fixed height problem, added no-hide when finished

--- client/app/MainApp/globalnotification.coffee
+++ client/app/MainApp/globalnotification.coffee
@@ -46,15 +46,18 @@

     @startTime = new Date Date.now()
     @endTime = @getOptions().targetDate
+    @done = no

     globalSticky = @getSingleton('windowController').stickyNotification
     if globalSticky
+      globalSticky.done = no
       globalSticky.show()
       globalSticky.setTitle @getOptions().title
       globalSticky.setContent @getOptions().content
       globalSticky.startTime = Date.now()
       globalSticky.endTime = @getOptions().targetDate
       globalSticky.adjustTimer @getOptions().duration
+

     else
       KDView.appendToDOMBody @
@@ -70,7 +73,7 @@

   recalculatePosition:()->

-    cachedWidth = @getWidth
+    cachedWidth = @getWidth()

     @$().css marginLeft : -cachedWidth/2

@@ -123,7 +126,7 @@
      """

   click:->
-    @hide()
+    @hide() unless @done

   viewAppended:->
     @setTemplate @pistachio()
@@ -175,6 +178,7 @@

       @recalculatePosition()
       if currentTime < 0
+        @done = yes
         clearInterval @notificationInterval
         @$('.slider-wrapper').addClass 'done'
     ,1000


registerform wip

registerform wip

--- client/app/MainApp/login/registerform.coffee
+++ client/app/MainApp/login/registerform.coffee
@@ -119,9 +119,11 @@
         validate      :
           event       : "blur"
           rules       :
+            required  : yes
             minLength : 8
           messages    :
-            minLength : "Password is required and should at least be 8 characters."
+            required  : "Password is required."
+            minLength : "Password should at least be 8 characters."
         change        : (input, event)=>
           if @kodingenUser
             @passwordConfirm.input.setValue input.getValue()
@@ -139,8 +141,8 @@
             required  : yes
             match     : @password.input
           messages    :
+            required  : "Password confirmation required!"
             match     : "Password confirmation doesn't match!"
-            match     : "Password confirmation is required!"
         focus         : (input, event)=>
           if @kodingenUser
             input.setValue @password.input.getValue()
@@ -248,6 +250,7 @@
     @parent.setClass "taller"
     @username.setClass "kodingen"
     @password.input.$().attr "placeholder", "Type your kodingen password"
+    @password.input.ruleChain = ["required"] # this is a hack, needs a better implementation
     @passwordConfirm.setHeight 0
     @$('p.kodingen-user-notification b').text "#{@username.input.getValue()}"
     @$('p.kodingen-user-notification').height 54
@@ -259,6 +262,7 @@
     @parent.unsetClass "taller"
     @username.unsetClass "kodingen"
     @password.input.$().attr "placeholder", "Create a password"
+    @password.input.ruleChain = ["required","minLength"] # this is a hack, needs a better implementation
     @$('p.kodingen-user-notification').height 0
     @passwordConfirm.setHeight 32


Implement #5

Implement #5

--- flow-router-title.coffee
+++ flow-router-title.coffee
@@ -1,4 +1,4 @@
-class FlowRouterTitle
+export class FlowRouterTitle
   constructor: (@router) ->
     _self = @
     hardCodedTitle = document.title or null
@@ -10,11 +10,13 @@
           hardCodedTitle = document.title
         document.title = newValue
         @curValue = newValue
+      return

     @titleHandler = (context, redirect, stop, data) =>
-      defaultTitle = null
-      _context     = _.extend context, {query: context.queryParams}
-      _arguments   = [context.params, context.queryParams, data]
+      defaultTitle      = null
+      _context          = _.extend context, {query: context.queryParams}
+      _arguments        = [context.params, context.queryParams, data]
+      _groupTitlePrefix = @_getParentPrefix @router._current.route.group, _context, _arguments

       if @router.globals.length
         for option in @router.globals
@@ -22,30 +24,27 @@
             defaultTitle = option.title

       if context.route?.group?.options
-        _routeTitle       = context.route.options?.title
-        _routeTitle       = _routeTitle.apply _context, _arguments if _.isFunction _routeTitle
+        _routeTitle = context.route.options?.title
+        _routeTitle = _routeTitle.apply _context, _arguments if _.isFunction _routeTitle

-        _groupTitle       = context.route.group.options?.title
-        _groupTitle       = _groupTitle.apply _context, _arguments if _.isFunction _groupTitle
-
-        _groupTitlePrefix = context.route.group.options?.titlePrefix
-        _groupTitlePrefix = _groupTitlePrefix.apply _context, _arguments if _.isFunction _groupTitlePrefix
+        _groupTitle = context.route.group.options?.title
+        _groupTitle = _groupTitle.apply _context, _arguments if _.isFunction _groupTitle

         unless _routeTitle
-          _title = _groupTitle or defaultTitle or hardCodedTitle
+          _title = _groupTitlePrefix + (_groupTitle or defaultTitle or hardCodedTitle)
         else if _routeTitle and _groupTitlePrefix
           _title = _groupTitlePrefix + _routeTitle
         else
-          _title = _routeTitle or defaultTitle or hardCodedTitle
+          _title = _groupTitlePrefix + (_routeTitle or defaultTitle or hardCodedTitle)

       else
         _title = context.route.options?.title or defaultTitle or hardCodedTitle
-
-      _title = _title.apply _context, _arguments if _.isFunction _title
+        _title = _title.apply _context, _arguments if _.isFunction _title

       if _.isString _title
         title.set _title
         context.context.title = _title if context?.context
+      return

     @router.triggers.enter [@titleHandler]

@@ -58,5 +57,18 @@
       else
         _self.titleHandler _context
       _orig.apply @, arguments
+      return

-`export { FlowRouterTitle }`+  _getParentPrefix: (group, _context, _arguments, i = 0) ->
+    prefix = ''
+    i++
+
+    if group
+      if group.options?.titlePrefix
+        if (_context.route.options?.title and i is 1) or i isnt 1
+          _gt     = group.options.titlePrefix
+          _gt     = _gt.apply _context, _arguments if _.isFunction _gt
+          prefix += _gt
+      prefix = @_getParentPrefix(group.parent, _context, _arguments, i) + prefix if group.parent
+
+    return prefix


[TEST] Make sure all operator tests are run

--- test/conditions.coffee
+++ test/conditions.coffee
@@ -1,3 +1,5 @@
+Promise = require "bluebird"
+
 describe "Liquid.Condition", ->
   context "if", ->
     it "renders on `true` variables", ->
@@ -39,29 +41,33 @@

     context "with operators", ->
       it "that evaluate to true renders", ->
-        renderTest 'X','{% if a == 42 %}X{% endif %}', a: 42
-        renderTest 'X','{% if a is 42 %}X{% endif %}', a: 42
+        Promise.all([
+          renderTest 'X','{% if a == 42 %}X{% endif %}', a: 42
+          renderTest 'X','{% if a is 42 %}X{% endif %}', a: 42

-        renderTest 'X','{% if a != 42 %}X{% endif %}', a: 41
-        renderTest 'X','{% if a isnt 42 %}X{% endif %}', a: 41
-        renderTest 'X','{% if a <> 42 %}X{% endif %}', a: 41
+          renderTest 'X','{% if a != 42 %}X{% endif %}', a: 41
+          renderTest 'X','{% if a isnt 42 %}X{% endif %}', a: 41
+          renderTest 'X','{% if a <> 42 %}X{% endif %}', a: 41

-        renderTest 'X','{% if a > 42 %}X{% endif %}', a: 43
-        renderTest 'X','{% if a >= 42 %}X{% endif %}', a: 43
-        renderTest 'X','{% if a >= 42 %}X{% endif %}', a: 42
+          renderTest 'X','{% if a > 42 %}X{% endif %}', a: 43
+          renderTest 'X','{% if a >= 42 %}X{% endif %}', a: 43
+          renderTest 'X','{% if a >= 42 %}X{% endif %}', a: 42

-        renderTest 'X','{% if a < 42 %}X{% endif %}', a: 41
-        renderTest 'X','{% if a <= 42 %}X{% endif %}', a: 41
-        renderTest 'X','{% if a <= 42 %}X{% endif %}', a: 42
+          renderTest 'X','{% if a < 42 %}X{% endif %}', a: 41
+          renderTest 'X','{% if a <= 42 %}X{% endif %}', a: 41
+          renderTest 'X','{% if a <= 42 %}X{% endif %}', a: 42

-        renderTest 'X','{% if a contains 2 %}X{% endif %}', a: [1,2,3]
-        renderTest 'X','{% if a contains "b" %}X{% endif %}', a: "abc"
+          renderTest 'X','{% if a contains 2 %}X{% endif %}', a: [1,2,3]
+          renderTest 'X','{% if a contains "b" %}X{% endif %}', a: "abc"
+        ])

       it "that evaluate to false doesn't render", ->
-        renderTest '','{% if a != 42 %}X{% endif %}', a: 42
+        Promise.all([
+          renderTest '','{% if a != 42 %}X{% endif %}', a: 42

-        renderTest '','{% if a contains 2 %}X{% endif %}'
-        renderTest '','{% if a contains 2 %}X{% endif %}', a: { indexOf: null }
+          renderTest '','{% if a contains 2 %}X{% endif %}'
+          renderTest '','{% if a contains 2 %}X{% endif %}', a: { indexOf: null }
+        ])

     context "with awful markup", ->
       it "renders correctly", ->


DNDUploader: Use machine instance of delegate

--- client/finder/commonviews/DNDUploader.coffee
+++ client/finder/commonviews/DNDUploader.coffee
@@ -181,19 +181,22 @@

   upload: (fileName, contents, relativePath)->

+    machine = @getMachine()
     folder  = if relativePath and relativePath isnt fileName
     then "#{@path}/#{relativePath}"
     else @path

     modalStack   = KDModalView.createStack lastToFirst: yes
-    fsFolderItem = FSHelper.createFileInstance path: folder, type: 'folder'
-    fsFileItem   = FSHelper.createFileInstance path: "#{folder}/#{fileName}"
+    fsFolderItem = FSHelper.createFileInstance { path: folder, type: 'folder', machine }
+    fsFileItem   = FSHelper.createFileInstance { path: "#{folder}/#{fileName}", machine }

     return if FSHelper.isUnwanted fsFolderItem.path
     return if FSHelper.isUnwanted fsFileItem.path, yes

     upload = =>
+
       fsFileItem.exists (err, exists)=>
+
         if not exists or fsFileItem.getLocalFileInfo().lastUploadedChunk?
         then @saveFile fsFileItem, contents
         else
@@ -227,3 +230,7 @@
       else upload()

     return fsFileItem
+
+  getMachine: ->
+
+    @getDelegate().controller.machines.first.machine


Save uom listing when enter is pressed in price

Fixes #37

--- client/skr/screens/sku-maint/SkuUomList.cjsx
+++ client/skr/screens/sku-maint/SkuUomList.cjsx
@@ -68,6 +68,7 @@
                 <LC.NumberInput type="number" label='Size' name='size' format='#,###'
                     model={@props.uom} xs=3 />
                 <LC.NumberInput type="text"  label='Price' name='price'
+                    align='right' onEnter={@props.onSave}
                     model={@props.uom} xs=5 />
             </BS.Row>
         </BS.Panel>
@@ -89,7 +90,8 @@
         <BS.Accordion>
             <BS.Panel className="sku-uom-edit">
                 {@props.uoms.map (uom) =>
-                    <UomEdit key={uom.cid} list={@props.uoms} uom={uom} />}
+                    <UomEdit onSave={@props.onSave}
+                        key={uom.cid} list={@props.uoms} uom={uom} />}
             </BS.Panel>
         </BS.Accordion>

@@ -147,9 +149,13 @@
             className='sm'
             id='sku-edit-uoms'
             className='sku-uoms-editing'
-            title={header} placement='left'
+            placement='left'
+            title={header}
         >
-            <EditBody ref='body' uoms={@state.editing} selected={@state.selected} />
+            <EditBody ref='body'
+                onSave={@onOk}
+                uoms={@state.editing} selected={@state.selected}
+            />
             <div className="panel-footer">
                 <BS.Button onClick={@onCancel}>
                     Cancel


Oscillator

Oscillator

--- test/oscillator.coffee
+++ test/oscillator.coffee
@@ -8,8 +8,8 @@
   equals o(0), 10
   equals o(30), 10
   equals o(15), -10
-  equals o(7.5).toFixed(6), 0 #=> 0
-  equals o(22.5).toFixed(6), 0 #=> 0
+  equals o(7.5).toFixed(6), 0
+  equals o(22.5).toFixed(6), 0

   o = Oscillator
     period: 30


:disposable :repeat :until :leave :continue

--- client/codemirror-apl-mode.coffee
+++ client/codemirror-apl-mode.coffee
@@ -94,14 +94,16 @@
         ok = 0
         switch kw = stream.match(/\w*/)?[0]?.toLowerCase()
           # see https://github.com/jashkenas/coffeescript/issues/2014 for the "multiline when" syntax
-          when 'class', 'for', 'hold', 'if', 'interface', 'namespace', 'property', 'repeat', 'section', 'select'
-          ,    'trap', 'while', 'with'
+          when 'class', 'disposable', 'for', 'hold', 'if', 'interface', 'namespace'
+          ,    'property', 'repeat', 'section', 'select', 'trap', 'while', 'with'
             state.kws.push kw; state.inds.push stream.indentation(); ok = 1
           when 'end'
             ok = state.kws.length > 0; (if ok then state.kws.pop(); state.inds.pop()); ok
-          when 'endclass', 'endfor', 'endhold', 'endif', 'endinterface', 'endnamespace', 'endproperty', 'endrepeat'
-          ,    'endsection', 'endselect', 'endtrap', 'endwhile', 'endwith'
-            i = state.kws.lastIndexOf kw[3..]; ok = i == state.kws.length - 1 >= 0
+          when 'endclass', 'enddisposable', 'endfor', 'endhold', 'endif', 'endinterface', 'endnamespace'
+          ,    'endproperty', 'endrepeat', 'endsection', 'endselect', 'endtrap', 'endwhile', 'endwith'
+          ,    'until'
+            kw0 = if kw == 'until' then 'repeat' else kw[3..] # corresponding opening keyword
+            i = state.kws.lastIndexOf kw0; ok = i == state.kws.length - 1 >= 0
             (if ok then state.kws.splice i; state.inds.splice i); ok
           when 'else'
             ok = state.kws[-1..][0] in ['if', 'select', 'trap']
@@ -111,7 +113,9 @@
             ok = state.kws[-1..][0] == 'for'
           when 'case', 'caselist'
             ok = state.kws[-1..][0] in ['select', 'trap']
-          when 'access', 'base', 'continue', 'field', 'goto', 'include', 'leave', 'return', 'until'
+          when 'leave', 'continue'
+            ok = state.kws[-1..][0] in ['for', 'while', 'continue']
+          when 'access', 'base', 'field', 'goto', 'include', 'return'
             ok = 1
           when 'implements'
             stream.match /\s+\w+/; ok = 1
@@ -126,10 +130,10 @@
         else 'apl-glb'
       else 'apl-err'

-  electricInput: /(?::else|:end|:andif|:orif|:case|\})$/ # when the user enters one of these, a re-indent is triggered
+  electricInput: /:(?:end|else|andif|orif|case|until|\})$/ # when the user enters one of these, a re-indent is triggered

   indent: (state, textAfter) ->
-    re = if state.dfnDepth then /^\s*\}/ else /^\s*:(?:end|else|andif|orif|case|caselist)/i
+    re = if state.dfnDepth then /^\s*\}/ else /^\s*:(?:end|else|andif|orif|case|until)/i
     state.inds[-1..][0] + config.indentUnit * !re.test textAfter

   fold: 'indent'


New upstream version of jQuery iScrubber.

--- client/js/jquery.iscrubber.coffee
+++ client/js/jquery.iscrubber.coffee
@@ -1,6 +1,7 @@
 ###
-  jQuery iScrubber plugin 1.0.0
-
+  jQuery iScrubber plugin 1.1.0
+
+  @preserve
   Created by Marco Martins
   https://github.com/skarface/iscrubber.git
 ###
@@ -11,41 +12,48 @@
     showItem: 1
     leaveToFirst: true

-  # Set the options.
+  ### Set the options ###
   options = $.extend({}, $.fn.iscrubber.defaultOptions, customOptions)

-  # scrub function
+  ### scrub function ###
   scrub = (elements, itemToShow) ->
-    elements.css('display', 'none')
-    $(elements[itemToShow-1]).css('display', 'block')
+    if options.hideWithClass
+      elements.addClass(options.hideWithClass)
+      $(elements[itemToShow - 1]).removeClass(options.hideWithClass)
+    else
+      elements.css('display', 'none')
+      $(elements[itemToShow - 1]).css('display', 'block')

   this.each ->
     $this = $(this)

-    # get elements
+    return if $this.data('iscrubber-enabled')
+    $this.data('iscrubber-enabled', true)
+
+    ### get elements ###
     elements = $this.find('li')

-    # set correct width from children and add minimal css require
+    ### set correct width from children and add minimal css require ###
     width = elements.first().width()
     $this.width(width).css('padding', 0)

-    # get trigger width => (scrubber width / number of children)
+    ### get trigger width => (scrubber width / number of children) ###
     trigger = width / $this.children().length

-    # show first element
+    ### show first element ###
     scrub(elements, options.showItem)

-    # bind event when mouse moves over scrubber
-    $this.mousemove (e) ->
-      # get x mouse position
+    ### bind event when mouse moves over scrubber ###
+    $this.on 'mousemove.iscrubber', (e) ->
+      ### get x mouse position ###
       x = e.pageX - $this.offset().left

-      # get the index of image to display on top
+      ### get the index of image to display on top ###
       index = Math.ceil(x/trigger)
       index = 1 if index == 0
       scrub(elements, index)

-    # bind event when mouse leaves scrubber
-    $this.mouseleave ->
+    ### bind event when mouse leaves scrubber ###
+    $this.on 'mouseleave.iscrubber', ->
       scrub(elements, options.showItem) if options.leaveToFirst is true


Save all course attributes (namely id)

Save all course attributes (namely id)

--- src/course/model.coffee
+++ src/course/model.coffee
@@ -73,7 +73,7 @@
   # The clone's attributes are persisted to the user once complete
   persist: (user) ->
     other = user.findOrCreateCourse(@ecosystem_book_uuid)
-    other.name = @to.course.name
+    _.extend(other, @to.course)
     other.periods = [ @to.period ]
     user.onCourseUpdate(other)


Return an exclusive endScreenRow from expandBufferRangeToLineBoundaries

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -124,7 +124,7 @@

     {startScreenRow, endScreenRow} = @expandBufferRangeToLineBoundaries(Range(Point.ZERO, Point(@buffer.getLineCount(), 0)))
     newLines = @buildSpatialScreenLines(0, @buffer.getLineCount())
-    oldRowExtent = endScreenRow - startScreenRow + 1
+    oldRowExtent = endScreenRow - startScreenRow
     newRowExtent = newLines.length
     @spliceDisplayIndex(startScreenRow, oldRowExtent, newLines)
     @emitter.emit 'did-change-sync', Object.freeze([{
@@ -159,7 +159,7 @@
     foldId = @foldsMarkerLayer.markRange(bufferRange, {invalidate: 'inside'}).id
     if @foldsMarkerLayer.findMarkers(containsRange: bufferRange).length is 1
       {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToLineBoundaries(bufferRange)
-      oldRowExtent = endScreenRow - startScreenRow + 1
+      oldRowExtent = endScreenRow - startScreenRow
       newScreenLines = @buildSpatialScreenLines(startBufferRow, endBufferRow)
       newRowExtent = newScreenLines.length
       @spliceDisplayIndex(startScreenRow, oldRowExtent, newScreenLines)
@@ -196,7 +196,7 @@
       foldMarker.destroy()
     combinedRange = Range(combinedRangeStart, combinedRangeEnd)
     {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToLineBoundaries(combinedRange)
-    oldRowExtent = endScreenRow - startScreenRow + 1
+    oldRowExtent = endScreenRow - startScreenRow
     newScreenLines = @buildSpatialScreenLines(startBufferRow, endBufferRow)
     newRowExtent = newScreenLines.length
     @spliceDisplayIndex(startScreenRow, oldRowExtent, newScreenLines)
@@ -305,7 +305,10 @@
     while @spatialLineIterator.isSoftWrappedAtEnd()
       @spatialLineIterator.moveToSuccessor()

-    {screenRow: @spatialLineIterator.getScreenRow(), bufferRow: @spatialLineIterator.getBufferEnd().row}
+    {
+      screenRow: @spatialLineIterator.getScreenRow() + 1,
+      bufferRow: @spatialLineIterator.getBufferEnd().row
+    }

   expandBufferRangeToLineBoundaries: (range) ->
     {screenRow: startScreenRow, bufferRow: startBufferRow} = @lineStartBoundaryForBufferRow(range.start.row)


Expand change region to  empty lines also for folds

Now that we have made the Patch inclusive, if
there's any empty line following the fold's extent
it will be blown away. By extending the change, we
ensure all the empty buffer lines get recomputed
correctly. This fixes a bunch of randomized test
failures.

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -52,6 +52,7 @@
     bufferRange = @buffer.clipRange(bufferRange)
     foldId = @foldsMarkerLayer.markRange(bufferRange).id
     if @foldsMarkerLayer.findMarkers(containsRange: bufferRange).length is 1
+      {newRange: bufferRange} = @expandChangeRegionToSurroundingEmptyLines(bufferRange, bufferRange)
       {bufferStart, bufferEnd} = @expandBufferRangeToScreenLineStarts(bufferRange)
       foldExtent = traversal(bufferEnd, bufferStart)
       {start, oldExtent} = @patch.spliceInput(bufferStart, foldExtent, foldExtent)
@@ -82,7 +83,9 @@
       for foldMarker in foldMarkers
         combinedRangeEnd = maxPoint(combinedRangeEnd, foldMarker.getEndPosition())
         foldMarker.destroy()
-      {bufferStart, bufferEnd} = @expandBufferRangeToScreenLineStarts(Range(combinedRangeStart, combinedRangeEnd))
+      combinedRange = Range(combinedRangeStart, combinedRangeEnd)
+      {newRange: combinedRange} = @expandChangeRegionToSurroundingEmptyLines(combinedRange, combinedRange)
+      {bufferStart, bufferEnd} = @expandBufferRangeToScreenLineStarts(combinedRange)
       foldExtent = traversal(bufferEnd, bufferStart)
       {start, oldExtent} = @patch.spliceInput(bufferStart, foldExtent, foldExtent)
       {screenNewEnd, screenLineLengths} = @computeTransformation(bufferStart.row, bufferEnd.row)


Bug fix: don't highlight in debugger for position bytecodes unless they're for YourCode.

--- coffee/RubyCodeHighlighter.coffee
+++ coffee/RubyCodeHighlighter.coffee
@@ -50,10 +50,10 @@
             @highlightedRange = [startLine, startCol, endLine, endCol]

       when 'position'
-        # currently just ignore bytecode[1], which is the path
-        @currentLine = parseInt bytecode[2]
-        @currentCol = parseInt bytecode[3]
-        @justChangedPosition = true
+        if bytecode[1] == 'YourCode'
+          @currentLine = parseInt bytecode[2]
+          @currentCol = parseInt bytecode[3]
+          @justChangedPosition = true

       when 'done'
         @currentLine = null


Wave 9 released, removed arc-170, sf tie, protectorate, and SC from unreleased expansions

--- coffeescripts/cards-common.coffee
+++ coffeescripts/cards-common.coffee
@@ -3,10 +3,6 @@

 exportObj.unreleasedExpansions = [
     "Heroes of the Resistance Expansion Pack"
-    "ARC-170 Expansion Pack"
-    "Special Forces TIE Expansion Pack"
-    "Protectorate Starfighter Expansion Pack"
-    "Shadow Caster Expansion Pack"
     "Sabine's TIE Fighter Expansion Pack"
     "Upsilon-class Shuttle Expansion Pack"
     "Quadjumper Expansion Pack"


cleanup fsFormFor directive

cleanup fsFormFor directive

--- src/fs/fsFormFor.coffee
+++ src/fs/fsFormFor.coffee
@@ -25,6 +25,12 @@
   template: (el, attrs) ->
     modelName = el.attr("model")
     action = el.attr("action")
+    formAttributes = {
+      name: 'form'
+      class: 'form-horizontal'
+      novalidate: ''
+    }
+    formAttributes[attr.name] = attr.value for attr in el.prop("attributes")

     inputReplacer = () ->
       input = $(this)
@@ -77,31 +83,4 @@
       .replaceWith(rowReplacer)
       .end().html()

-    hiddenJson = (model, attr) ->
-
-
-
-    """
-    <form name='form' class='form-horizontal' action='#{action}' novalidate>
-      #{html}
-    </form>
-    """
-
-angular
-.module('formstamp').directive 'fsSyncFormFor', ->
-  restrict: 'AE'
-  template: (el, attrs) ->
-    model = el.attr('model')
-    syncAttr = el.attr('sync-attr')
-
-    attributes = {}
-    attributes[attr.name] = attr.value for attr in el.prop("attributes")
-    attributes['fs-form-for'] = ''
-    delete attributes["fs-sync-form-for"]
-    res = $("<div />", attributes)
-
-    name = syncAttr || "_json"
-    res.html("<input type='hidden' value='{{#{model} | json}}' name='#{name}' />" +
-             el.html())
-        .get(0)
-        .outerHTML
+    $('<form>', formAttributes).html(html)[0].outerHTML


Refactored Force

Refactored Force

--- js/particle.coffee
+++ js/particle.coffee
@@ -18,14 +18,17 @@

       return null if deltaTime == 0

+      @force = new Vector(0, 0)
+
       for particle in Particle.particles
         delta = particle.position.subtract @position
         if delta.magnitude() > @mass + particle.mass
           gForce = @GRAVITATIONAL_CONSTANT * particle.mass / delta.squareMagnitude()
-          x = gForce * Math.cos(delta.angle()) * deltaTime
-          y = gForce * Math.sin(delta.angle()) * deltaTime
-          @velocity = new Vector(x + @velocity.x, y + @velocity.y)
+          x = gForce * Math.cos(delta.angle())
+          y = gForce * Math.sin(delta.angle())
+          @force = @force.add new Vector(x, y)

+      @velocity = @velocity.add @force.multiply(deltaTime)
       @position = @position.add @velocity.multiply(deltaTime)

     render: (context, scale) ->


Add instrumentor method to include a module in coverage report

--- src/instrumenter.coffee
+++ src/instrumenter.coffee
@@ -26,6 +26,8 @@
 estraverse = require 'estraverse'
 _ = require 'lodash'
 esprima = require 'esprima'
+path = require 'path'
+fs = require 'fs'

 class StructuredCode
     constructor: (code) ->
@@ -72,6 +74,18 @@
         @fixupLoc program, code.sourceMap
         @instrumentASTSync program, filename, code

+    # Used to ensure that a module is included in the code coverage report
+    # (even if it is not loaded during the test)
+    includeInCoverageReport: (filename) ->
+        filename = path.resolve(filename)
+        code = fs.readFileSync(filename, 'utf8')
+        @instrumentSync(code, filename)
+
+        # Setup istanbul's references for this module
+        eval("#{@getPreamble null}")
+
+        return
+
     fixupLoc: (program)->
         # TODO(Constellation)
         # calculate precise offset or attach in


Remove a useless function in frontend/room.coffee

--- src/frontend/room.coffee
+++ src/frontend/room.coffee
@@ -77,42 +77,6 @@
         that = this
         $('#videos').delegate 'li.thumbnail video', 'click', () ->
             that.checkZoom this, 'thumbnailVideo'
-
-    # Start animation.
-    startAnimation       : (elems, interval, speed) =>
-        i = elems.length
-        time = interval * 5
-        while i >= 0
-            $(elems[i]).delay(time).fadeIn speed
-            time -= interval
-            i--
-        i = elems.length
-        fadeOutTime = interval * 10
-        while i >= 0
-            $(elems[i]).delay(fadeOutTime).fadeOut speed
-            fadeOutTime -= interval
-            i--
-        $('div#endMessage').delay(interval * 10 + 4 * interval).fadeIn speed
-        $('div#endMessage').delay(interval * 10).fadeOut speed
-        $('div#body').append '
Click here if you want to report a bug.
'
-        $('div#body').append '
Here you can chat with you friends!
'
-        $('div#footer').append '
Click here to activate your camera.
'
-        $('div#body').append '
Here is a list of users currently in the room.
'
-        i = 0
-        time = interval * 5
-        while i < elems.length
-            $(elems[i]).delay(time).fadeOut speed
-            i++
-        $('div#endMessage').delay(time).fadeIn speed
-        $('div#endMessage').delay(time).fadeOut speed
-        i = 0
-        time = interval * 5
-        while i < elems.length
-            $(elems[i]).delay(time).fadeOut speed
-            i++
-        $('div#endMessage').delay(time).fadeIn speed
-        $('div#endMessage').delay(time).fadeOut speed
-        @startAnimation $("div[id$='Arrow']"), 1000, 400

     # Start the tutorial animation.
     startTutorial      : () =>


parser handles modal operations

parser handles modal operations

--- src/gerber-parser.coffee
+++ src/gerber-parser.coffee
@@ -130,10 +130,14 @@
       # check for coordinate operations
       # not an else if because G codes for mode set can go inline with
       # interpolate blocks
-      if op = block.match(/D0?[123]$/)?[0]
-        op = op[op.length-1]
-        op = if op is '1' then 'int' else if op is '2' then 'move' else 'flash'
-        coord = parseCoord block.match(reCOORD)?[0], @format
+      coord = parseCoord block.match(reCOORD)?[0], @format
+      if op = block.match(/D0?[123]$/)?[0] or Object.keys(coord).length
+        if op? then op = op[op.length-1]
+        op = switch op
+          when '1' then 'int'
+          when '2' then 'move'
+          when '3' then 'flash'
+          else 'last'
         c.op = { do: op }
         c.op[axis] = val for axis, val of coord
       # check for a tool change


Fixed a typo where cmd would appear in linux.cson

Fixes #2251 again

--- keymaps/linux.cson
+++ keymaps/linux.cson
@@ -69,8 +69,8 @@
   'ctrl-delete': 'editor:delete-to-end-of-word'
   'ctrl-home': 'core:move-to-top'
   'ctrl-end': 'core:move-to-bottom'
-  'cmd-shift-home': 'core:select-to-top'
-  'cmd-shift-end': 'core:select-to-bottom'
+  'ctrl-shift-home': 'core:select-to-top'
+  'ctrl-shift-end': 'core:select-to-bottom'

   # Sublime Parity
   'ctrl-a': 'core:select-all'


Update link_hints.coffee

Update link_hints.coffee

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -112,7 +112,7 @@
       HUD.show("Download link URL")
       @linkActivator = (link) ->
         DomUtils.simulateClick(link, {
-          altKey: KeyboardUtils.platform == "Mac",
+          altKey: true,
           ctrlKey: false,
           metaKey: false })
     else # OPEN_IN_CURRENT_TAB


update atom config

update atom config

--- .atom/config.cson
+++ .atom/config.cson
@@ -92,6 +92,7 @@
   "exception-reporting":
     userId: "53f5306d-b3ef-b804-5b38-b4b0dcb858c5"
   "go-outline":
+    showVariables: false
     viewMode: "package"
   "go-plus":
     autocomplete: {}


make sure only mapped variables are used

--- test/chinchilla/request_builder.spec.coffee
+++ test/chinchilla/request_builder.spec.coffee
@@ -138,7 +138,7 @@

       builder = new ChRequestBuilder(context, 'role', 'collection', 'query')
       builder.extractFrom(obj, 'member')
-      builder.mergeParams(foo: 'foo')
+      builder.mergeParams(foo: 'foo', bar: 'bar')

       $httpBackend.expectGET('http://um.mpx.com/roles/15?foo=foo').respond(null)


Test that save is only called once

Test that save is only called once

--- tests/batman/model/associations_test.coffee
+++ tests/batman/model/associations_test.coffee
@@ -29,11 +29,14 @@
       ok store instanceof @Store
       equal store.id, 1

-asyncTest "belongsTo associations are saved", 1, ->
+asyncTest "belongsTo associations are saved", 2, ->
   store = new @Store name: 'Zellers'
   product = new @Product name: 'Gizmo'
   product.set 'store', store
+
+  productSaveSpy = spyOn product, 'save'
   product.save (err, record) ->
+    equal productSaveSpy.callCount, 1
     equal record.get('store_id'), store.id
     QUnit.start()

@@ -44,11 +47,14 @@
       ok product instanceof @Product
       equal product.id, 1

-asyncTest "hasOne associations are saved", 1, ->
+asyncTest "hasOne associations are saved", 2, ->
   store = new @Store name: 'Zellers'
   product = new @Product name: 'Gizmo'
   store.set 'product', product
+
+  storeSaveSpy = spyOn store, 'save'
   store.save (err, record) ->
+    equal storeSaveSpy.callCount, 1
     equal product.get('store_id'), record.id
     QUnit.start()


Rename associations tests to be easier filtered in the browser tests

--- tests/batman/model/associations_test.coffee
+++ tests/batman/model/associations_test.coffee
@@ -1,14 +1,14 @@
 {createStorageAdapter, TestStorageAdapter, AsyncTestStorageAdapter} = if typeof require isnt 'undefined' then require './model_helper' else window
 helpers = if typeof require is 'undefined' then window.viewHelpers else require '../view/view_helper'

-QUnit.module "Associations"
+QUnit.module "Batman.Model Associations"

 asyncTest "support custom model namespaces and class names", 2, ->
   namespace = this
   class namespace.Walmart extends Batman.Model

   class Product extends Batman.Model
-    @belongsTo 'store',
+    @belongsTo 'store',
       namespace: namespace
       name: 'Walmart'
   productAdapter = createStorageAdapter Product, AsyncTestStorageAdapter,
@@ -57,13 +57,13 @@
     product  = store.get 'product'
     product._batman.state = 'loading'
     store.save (err, savedStore) ->
-      ok !err
+      ok !err
       QUnit.start()

 asyncTest "inline saving can be disabled", 1, ->
   namespace = this
   class @Store extends Batman.Model
-    @hasMany 'products',
+    @hasMany 'products',
       namespace: namespace
       saveInline: false
   @storeAdapter = createStorageAdapter @Store, AsyncTestStorageAdapter,
@@ -111,7 +111,7 @@
     equal arr[1].get('name'), "Product Two"
     QUnit.start()

-QUnit.module "belongsTo Associations"
+QUnit.module "Batman.Model belongsTo Associations"
   setup: ->
     namespace = this
     class @Store extends Batman.Model
@@ -158,7 +158,7 @@
     equal storedJSON.store_id, undefined

     @Product.find record.get('id'), (err, product2) ->
-      deepEqual product2.toJSON(), storedJSON
+      deepEqual product2.toJSON(), storedJSON
       QUnit.start()

 asyncTest "belongsTo associations render", 1, ->
@@ -186,7 +186,7 @@
       store: {name: "Inline Store"}
     QUnit.start()

-QUnit.module "hasOne Associations"
+QUnit.module "Batman.Model hasOne Associations"
   setup: ->
     namespace = this
     class @Store extends Batman.Model
@@ -243,7 +243,7 @@
       equal node[0].innerHTML, 'Product One'
       QUnit.start()

-QUnit.module "hasMany Associations"
+QUnit.module "Batman.Model hasMany Associations"
   setup: ->
     namespace = this

@@ -360,4 +360,3 @@
     addedProduct.save (err, savedProduct) =>
       ok store.get('products').has(savedProduct)
       QUnit.start()
-


tests/device: Update the expected RPi1 DT display name

See: https://github.com/balena-os/balena-raspberrypi/commit/b7019912a63708b0e083e6fa7ace8e036a460655
See: https://ci.appveyor.com/project/resin-io/resin-sdk/builds/28295305
Change-type: patch
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@balena.io>

--- tests/integration/models/device.spec.coffee
+++ tests/integration/models/device.spec.coffee
@@ -39,11 +39,11 @@

 			it 'should get the display name for a known slug', ->
 				promise = balena.models.device.getDisplayName('raspberry-pi')
-				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 and Zero)')
+				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 / Zero / Zero W)')

 			it 'should get the display name given a device type alias', ->
 				promise = balena.models.device.getDisplayName('raspberrypi')
-				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 and Zero)')
+				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 / Zero / Zero W)')

 			it 'should eventually be undefined if the slug is invalid', ->
 				promise = balena.models.device.getDisplayName('asdf')
@@ -52,7 +52,7 @@
 		describe 'balena.models.device.getDeviceSlug()', ->

 			it 'should eventually be the slug from a display name', ->
-				promise = balena.models.device.getDeviceSlug('Raspberry Pi (v1 and Zero)')
+				promise = balena.models.device.getDeviceSlug('Raspberry Pi (v1 / Zero / Zero W)')
 				m.chai.expect(promise).to.eventually.equal('raspberry-pi')

 			it 'should eventually be the slug if passing already a slug', ->


Remove unused variable

Remove unused variable

--- lib/linter-csound.coffee
+++ lib/linter-csound.coffee
@@ -25,7 +25,7 @@
       lintsOnChange: true

       lint: (editor) ->
-        return new Promise((resolve, reject) ->
+        return new Promise((resolve) ->
           if editor.getRootScopeDescriptor().getScopesArray()[0] is 'source.csound-document'
             documentProcessor.filePath = editor.getPath()
             documentProcessor.setInput(editor.getText())


Grab I*** warning messages

Grab I*** warning messages

For handling flake8-import-order plugin


Fix #39 (UNC paths) for lintOnSave

Fix #39 (UNC paths) for lintOnSave

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -113,7 +113,7 @@
       else
         @pylamaPath = pylamaPath
     else
-      if /^win/.test process.platform
+      if process.platform is 'win32'
         @pylamaPath = path.join path.dirname(__dirname), 'bin', 'pylama.bat'
       else
         @pylamaPath = path.join path.dirname(__dirname), 'bin', 'pylama.py'
@@ -198,7 +198,11 @@

   lintOnSave: (textEditor) =>
-    lintInfo = @makeLintInfo do textEditor.getPath
+    filePath = do textEditor.getPath
+    if process.platform is 'win32'
+      if filePath.slice(0, 2) == '\\\\'
+        return @lintOnFly textEditor
+    lintInfo = @makeLintInfo filePath
     @lintFile lintInfo, textEditor


UPDATE: main.coffee

UPDATE: main.coffee

--- generators/app/templates/app/_coffee/main.coffee
+++ generators/app/templates/app/_coffee/main.coffee
@@ -2,14 +2,18 @@
 # Require
 #######################################################
 require 'angular/angular.min'
-
-
+<% if (includeAngularRoute) { %>
+require 'angular-route/angular-route.min'
+<% } %>
+<% if (includeAngularResource) { %>
 require 'angular-resource/angular-resource.min'
-
-
+<% } %>
+<% if (includeAngularAnimate) { %>
 require 'angular-animate/angular-animate.min'
-
-
+<% } %>
+<% if (includeAngularClickOutside) { %>
+require 'angular-click-outside/angular-click-outside.js'
+<% } %>

 window._ = require 'underscore'
@@ -25,6 +29,3 @@
 # Wayfind Viewer App
 #######################################################
 app.controller  'MainCtrl', require './controllers/MainCtrl'
-
-
-


fix documentation - api key is needed when creating an instance of songkick

--- lib/node-songkick.coffee
+++ lib/node-songkick.coffee
@@ -4,7 +4,7 @@
 Module entry point. Usage:

 var sk = require("node-songkick");
-var Songkick = new sk();
+var Songkick = new sk("<YOUR API KEY>");

 Then, you can use the module like this:


use to.username

use to.username

--- lib/notifications.coffee
+++ lib/notifications.coffee
@@ -94,7 +94,7 @@
         ## if they regard the same message and haven't yet been seen by user.
         notification = Notifications.findOne
           type: 'messageUpdate'
-          to: to
+          to: to.username
           message: diff.id
           seen: false
         if notification?
@@ -104,7 +104,7 @@
         else
           notificationInsert
             type: 'messageUpdate'
-            to: to
+            to: to.username
             message: diff.id
             dates: [diff.updated]
             diffs: [diff._id]


Shorten month format (consistency with rest of Coauthor)

--- lib/notifications.coffee
+++ lib/notifications.coffee
@@ -393,7 +393,7 @@
           #  dates = "between #{diffs[0].updated} and #{diffs[diffs.length-1].updated}"
           #else
           updated = momentInUserTimezone diffs[diffs.length-1].updated, user
-          dates = "on #{updated.format 'ddd, MMMM D, YYYY [at] H:mm z'}"
+          dates = "on #{updated.format 'ddd, MMM D, YYYY [at] H:mm z'}"
           if msg.root?
             html += "
#{authorsHTML} #{verb} message #{linkToMessage msg, true, true} in the thread #{linkToMessage rootmsg, true, true} #{dates}:"
             text += "#{authorsText} #{verb} message #{linkToMessage msg, false, true} in the thread #{linkToMessage rootmsg, false, true} #{dates}:"


Fix bug in notifications when adding first tag

--- lib/notifications.coffee
+++ lib/notifications.coffee
@@ -650,7 +650,7 @@
                   else
                     '+'
                 tag
-            for tag in sortTags old.tags
+            for tag in sortTags old?.tags
               if escapeTag(tag.key) not of (msg.tags ? {})
                 tag.diff = '-'
                 tags.push tag


Fix typos in Export as PDF message (#308)

--- lib/pdf-converter.coffee
+++ lib/pdf-converter.coffee
@@ -39,7 +39,7 @@
     message = '''
       This feature is experimental.
       You must manually activate this feature in the package settings.
-      `asciidoctor-pdf` must be installed in you computer.
+      `asciidoctor-pdf` must be installed on your computer.
       '''
     atom.notifications.addWarning 'Export as PDF:', detail: message or '', dismissable: true


:racehorse: delay initialization so atom loads faster

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -14,12 +14,16 @@
   config: require('./config.coffee')

   activate: ->
-    GitHubApi ?= require 'github'
-    PackageManager ?= require './package-manager'
-    atom.commands.add 'atom-workspace', "sync-settings:backup", => @backup()
-    atom.commands.add 'atom-workspace', "sync-settings:restore", => @restore()
-    atom.commands.add 'atom-workspace', "sync-settings:view-backup", => @viewBackup()
-    @checkForUpdate()
+    # speedup activation by async initializing
+    # further optimization can be achieved by minimizing this file
+    setImmediate =>
+      # actual initialization after atom has loaded
+      GitHubApi ?= require 'github'
+      PackageManager ?= require './package-manager'
+      atom.commands.add 'atom-workspace', "sync-settings:backup", => @backup()
+      atom.commands.add 'atom-workspace', "sync-settings:restore", => @restore()
+      atom.commands.add 'atom-workspace', "sync-settings:view-backup", => @viewBackup()
+      @checkForUpdate()

   deactivate: ->


add methods pluck and toArray

add methods pluck and toArray

--- lib/client/javascripts/nali/collection.js.coffee
+++ lib/client/javascripts/nali/collection.js.coffee
@@ -51,13 +51,14 @@
     @adaptations.cancel.unshift cancel if cancel
     @

-  add: ( model ) ->
-    Array::push.call @, model
-    @adaptModel  model
-    @subscribeTo model, 'destroy', @onModelDestroyed
-    @reorder()
-    @trigger 'update.length.add', model
-    @trigger 'update.length', 'add', model
+  add: ( models... ) ->
+    for model in [].concat models...
+      Array::push.call @, model
+      @adaptModel  model
+      @subscribeTo model, 'destroy', @onModelDestroyed
+      @reorder()
+      @trigger 'update.length.add', model
+      @trigger 'update.length', 'add', model
     @

   remove: ( model ) ->
@@ -74,12 +75,18 @@
     @length = 0
     @

+  pluck: ( property ) ->
+    model[ property ] for model in @
+
   indexOf: ( model ) ->
     Array::indexOf.call @, model

   sort: ( sorter ) ->
     Array::sort.call @, sorter
     @
+
+  toArray: ->
+    Array::slice.call @, 0

   order: ( @ordering ) ->
     @reorder()


modal is considered closed too early in chrome. need to wait until the modal is not present at all

--- test-integration/helpers/ui/roster.coffee
+++ test-integration/helpers/ui/roster.coffee
@@ -45,6 +45,8 @@
     css: '.modal-body.teacher-edit-period-form .tutor-input input[type=text]'
   saveUpdate:
     css: '.modal-footer .-edit-period-confirm'
+  base:
+    css: '.teacher-edit-period-modal'

 class PeriodEditModal extends TestHelper
   constructor: (test, testElementLocator) ->
@@ -56,7 +58,7 @@
   waitUntilClose: =>
     # waits until the locator element is not present
     @test.driver.wait =>
-      @el.self().isPresent().then (isPresent) ->
+      @el.base().isPresent().then (isPresent) ->
         not isPresent

   close: =>


change frame

change frame

--- atmospheric/002-accumulated-heat/csmain.coffee
+++ atmospheric/002-accumulated-heat/csmain.coffee
@@ -36,18 +36,51 @@
             $("#msg").html('Initializting the terrain data')
             true

+        changeframe = (e) ->
+            canvas = $('#canvas').get(0)
+            width = canvas.width
+            height = canvas.height
+
+            x = y = 0
+            if e.pageX || e.pageY
+              x = e.pageX
+              y = e.pageY
+            else
+              x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
+              y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop
+            x -= canvas.offsetLeft
+            y -= canvas.offsetTop
+            x = x - width / 2
+            y = y - height / 2
+
+            if x > 0
+                frame = rotater.left(frame, - x / width * 2)
+            else
+                frame = rotater.right(frame, x / width * 2)
+
+            if y > 0
+                frame = rotater.up(frame, y / height * 2)
+            else
+                frame = rotater.down(frame, - y / height * 2)
+
         bean.add(
             $('#btnStart').get(0), 'click', (-> invoke())
         )
+        bean.add(
+            $('#canvas').get(0), 'click', changeframe
+        )

         time = 0
+        counter = 0
         evolve = ->
             return if map == null
             tao = planet.period / 30 #SI
             lights = starlight.evolve(au.fromSI_T(tao))
             data = accumulator.accumulate(tao, time, lights[0], lights[1])

-            viewer.paint(transformer.target(frame, (time / planet.period)), map, data)
+            if counter == 0
+                viewer.paint(transformer.target(frame, (time / planet.period)), map, data)
+            counter = (counter + 1) % 30
             time += tao
         handle = setInterval(evolve, 1000)


Match param and ivar names

Match param and ivar names

--- src/browser/auto-updater-win32.coffee
+++ src/browser/auto-updater-win32.coffee
@@ -10,8 +10,7 @@
 class AutoUpdater
   _.extend @prototype, EventEmitter.prototype

-  setFeedUrl: (url) ->
-    @updateUrl = url
+  setFeedUrl: (@updateUrl) ->

   quitAndInstall: ->
     updateDotExe = @getUpdateExePath()


uncoupling

uncoupling

--- src/liquid/block.coffee
+++ src/liquid/block.coffee
@@ -16,8 +16,8 @@
     while tokens.length > 0
       token = tokens.shift()

-      if Liquid.Block.IsTag.test(token)
-        if match = Liquid.Block.FullToken.exec(token)
+      if Block.IsTag.test(token)
+        if match = Block.FullToken.exec(token)
           # if we found the proper block delimitor just end parsing
           # here and let the outer block proceed


update example to use context functions added to run context

--- examples/browser.coffee
+++ examples/browser.coffee
@@ -27,7 +27,7 @@
 target_data = targets.flatMapLatest (t) -> bacon.fromPromise data t

 # graph the data
-_modules.graph.graph target_data
+run_context.graph.graph target_data

 # create an input field. it will reflect the current search
 i = text_input()


Fix android release gulp task for newest Cordova

--- gulp/tasks/deploy.coffee
+++ gulp/tasks/deploy.coffee
@@ -20,7 +20,7 @@

 # Android deployment
 androidDeployReleaseTasks = []
-androidReleaseFile = "platforms/android/ant-build/CordovaApp-release.apk"
+androidReleaseFile = "platforms/android/ant-build/MainActivity-release.apk"

 if GLOBALS.TESTFAIRY_API_KEY


Flesh out rendering tests to try and stress the will* and did* traversals.

--- tests/prof/tests/view/simple_view_rendering.coffee
+++ tests/prof/tests/view/simple_view_rendering.coffee
@@ -33,13 +33,8 @@
       node.innerHTML = simpleSource
       context = Batman(foo: 'bar')

-      view = new Batman.View
-        contexts: [context]
-        node: node
-      if view.on?
-        view.on 'ready', -> deferred.resolve()
-      else
-        view.ready -> deferred.resolve()
+      view = new Batman.View({context, node})
+      view.on 'ready', -> deferred.resolve()
       return
     ),{
       defer: true
@@ -51,13 +46,8 @@
       node.innerHTML = loopSource
       context = Batman(objects: new Batman.Set([0...100]...))

-      view = new Batman.View
-        contexts: [context]
-        node: node
-      if view.on?
-        view.on 'ready', -> deferred.resolve()
-      else
-        view.ready -> deferred.resolve()
+      view = new Batman.View({context, node})
+      view.on 'ready', -> deferred.resolve()
       return
     ),{
       defer: true
@@ -76,9 +66,7 @@
           baz: new Batman.Set([0...100]...)
           qux: new Batman.Set([0...100]...)

-      view = new Batman.View
-        contexts: [context]
-        node: node
+      view = new Batman.View({context, node})
       if view.on?
         view.on 'ready', -> deferred.resolve()
       else
@@ -89,4 +77,39 @@
       defer: true
     })

+  do ->
+    suite.add('deep tree rendering', ((deferred) ->
+      rootNode = node = document.createElement 'div'
+      for i in [0..100]
+        newNode = document.createElement 'div'
+        node.appendChild newNode
+        node = newNode
+
+      context = Batman()
+      view = new Batman.View({context, node})
+      view.on 'ready', -> deferred.resolve()
+      return
+    ),{
+      maxTime: 10
+      defer: true
+    })
+
+  do ->
+    suite.add('deep tree rendering with bindings', ((deferred) ->
+      rootNode = node = document.createElement 'div'
+      for i in [0..100]
+        newNode = document.createElement 'div'
+        newNode.setAttribute 'data-bind', 'foo'
+        node.appendChild newNode
+        node = newNode
+
+      context = Batman(foo: 'bar')
+      view = new Batman.View({context, node: rootNode})
+      view.on 'ready', -> deferred.resolve()
+      return
+    ),{
+      maxTime: 10
+      defer: true
+    })
+
   suite.run()


SplitView: initiation logic changed completely

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -32,88 +32,96 @@

   viewAppended:->

-    @_sanitizeSizes()
-
+    @_calculateSizes()
     @_createPanels()
+
     @_putPanels()
+    @_resizePanels()
+
     @_putViews()

-    if @getOptions().resizable and @panels.length
-      @_createResizers()
+    @_createResizer()  if @resizable and @panels[1]

     @listenWindowResize()
-
-  # CREATE/REMOVE PANELS
+    @parent?.on "PanelDidResize", KD.utils.debounce 10, @bound '_windowDidResize'
+
+
   _createPanels:->

-    panelCount = @getOptions().sizes.length
-    @panels    = [@_createPanel(0), @_createPanel(1)]
+    @_createPanel 0
+    @_createPanel 1 if @sizes[1]?
+

   _createPanel:(index)->

-    log @sizes[index],"////////"
-    {type, fixed} = @getOptions()
+    {fixed} = @getOptions()
     panel = new KDSplitViewPanel
       cssClass : "kdsplitview-panel panel-#{index}"
       index    : index
-      type     : type
+      type     : @type
       size     : @sizes[index]
       fixed    : !!fixed[index]
-      minimum  : @minimums[index]
-      maximum  : @maximums[index]

     panel.on "KDObjectWillBeDestroyed", => @_panelIsBeingDestroyed panel
     @emit "SplitPanelCreated", panel
+    @panels[index] = panel
+
     return panel

   _putPanels:->
-    for panel in @panels
-      @addSubView panel
-      if @getOptions().colored
-        panel.$().css backgroundColor : KD.utils.getRandomRGB()
-
-
+
+    @addSubView @panels[0]
+    @addSubView @panels[1]
+
+    if @getOptions().colored
+      @panels[0].setCss backgroundColor : KD.utils.getRandomRGB()
+      @panels[1].setCss backgroundColor : KD.utils.getRandomRGB()
+
+
+  _resizePanels:-> @resizePanel @sizes[0]

   _panelIsBeingDestroyed:(panel)->

-    index         = @getPanelIndex panel
-    o             = @getOptions()
-    @panels       = @panels.slice(0,index).concat(@panels.slice(index+1))
-    @sizes        = @sizes.slice(0,index).concat(@sizes.slice(index+1))
-    @panelsBounds = @panelsBounds.slice(0,index).concat(@panelsBounds.slice(index+1))
-
-    @minimums[index] = 0
-    @maximums[index] = @_getSize()
-    o.views[index]   = null  if o.views[index]?
-
-  # CREATE RESIZERS
-
-
-
-  _createResizer:(index)->
-
-    @addSubView resizer = new KDSplitResizer
-      cssClass : "kdsplitview-resizer #{@getOptions().type}"
-      type     : @getOptions().type
-      panel0   : @panels[index-1]
-      panel1   : @panels[index]
-
-    return resizer
-
-
-  # PUT VIEWS
+    {views}              = @getOptions()
+    {index}              = panel
+    @panels[index]       = null
+    @sizes[index]        = null
+    @minimums[index]     = null
+    @maximums[index]     = null
+    views[index]         = null
+
+
+  _createResizer:->
+
+    {type}   = @getOptions()
+    @resizer = @addSubView new KDSplitResizer
+      cssClass : "kdsplitview-resizer #{type}"
+      type     : @type
+      panel0   : @panels[0]
+      panel1   : @panels[1]
+
+    @_repositionResizer()
+
+
+  _repositionResizer:-> @resizer._setOffset @sizes[0]
+
+
   _putViews:->
-    @getOptions().views ?= []
-    for view,i in @getOptions().views
-      if view instanceof KDView
-        @setView view,i
-
-  # HELPERS
-  _sanitizeSizes:->
+
+    {views} = @getOptions()
+
+    return  unless views
+
+    @setView views[0], 0  if views[0]
+    @setView views[1], 1  if views[1]
+
+
+  _calculateSizes:->

     @_setMinsAndMaxs()
+
     {sizes} = @getOptions()
     ss      = @_getSize()
     s       = []
@@ -129,20 +137,21 @@
       else if sizes[1] and (not sizes[0] or sizes[0] is 'auto')
         s[0] = ss - s[1]

-
     @size  = ss
-    log s, sizes, '-----------'
     @sizes = s

   _sanitizeSize: (size) ->

-    size = if "number" is typeof size then size
-    else if /px$/.test size    then parseInt size, 10
+    return if "number" is typeof size
+      if 1 > size > 0
+      then @_getSize() * size
+      else size
+    else if /px$/.test size then parseInt size, 10
     else if /%$/.test size
     then @_getSize() / 100 * parseInt size, 10
-    else @_getSize() / 2
-    size
+    else null
+

   _setMinsAndMaxs:->

@@ -157,96 +166,112 @@
   _getSize:->

     if @size then @size
-    else if @getOptions().type is "vertical"
+    else if @isVertical()
     then @getWidth()
     else @getHeight()

   _setSize:(size)->
-    if @getOptions().type is "vertical" then @setWidth size else @setHeight size
+
+    if @isVertical()
+    then @setWidth size
+    else @setHeight size
+

   _getParentSize:->
-    type    = @getOptions().type
-    $parent = @$().parent()
-    if type is "vertical" then $parent.width() else $parent.height()
+
+    if @isVertical()
+      if @parent
+      then @parent.getWidth()
+      else window.innerWidth
+    else
+      if @parent
+      then @parent.getHeight()
+      else window.innerHeight

   _getLegitPanelSize: (size, index) ->

     min = @minimums[index] or 0
     max = @maximums[index] or @_getSize()
-    log min, max, size, Math.min Math.max(min, size), max
+
     return Math.min Math.max(min, size), max

-  _resizePanels:->
-
-    @_sanitizeSizes()
-
-  _repositionPanels:->
-
-    @_calculatePanelBounds()
-    @_setPanelPositions()
-
-  # EVENT HANDLING
-
-  _windowDidResize:(event)->
-
+  _windowDidResize: ->
+
+    @size = null
     @_setSize @_getParentSize()
+    @_calculateSizes()
     @_resizePanels()
-    @_repositionPanels()
-    @_setPanelPositions()

     # find a way to do that for when parent get resized and split reachs a min-width
     # if @getWidth() > @_getParentSize() then @setClass "min-width-reached" else @unsetClass "min-width-reached"
-    if @getOptions().resizable
-      @_repositionResizers()
-
-  mouseUp:(event)->
+    @_repositionResizer()  if @resizable
+
+  mouseUp: (event) ->
+
     @$().unbind "mousemove.resizeHandle"
     @_resizeDidStop event

-  _panelReachedMinimum:(panelIndex)->
-    @panels[panelIndex].emit "PanelReachedMinimum"
-    @emit "PanelReachedMinimum", panel : @panels[panelIndex]
-
-  _panelReachedMaximum:(panelIndex)->
-    @panels[panelIndex].emit "PanelReachedMaximum"
-    @emit "PanelReachedMaximum", panel : @panels[panelIndex]
+
+  _panelReachedMinimum:(index)->
+
+    panel = @panels[index]
+    panel.emit "PanelReachedMinimum"
+    @emit "PanelReachedMinimum", {panel}
+
+
+  _panelReachedMaximum:(index)->
+
+    panel = @panels[index]
+    panel.emit "PanelReachedMaximum"
+    @emit "PanelReachedMaximum", {panel}
+

   _resizeDidStart:(event)->
-    $('body').addClass "resize-in-action"
-    @emit "ResizeDidStart", orgEvent : event
-
-  _resizeDidStop:(event)->
-    @emit "ResizeDidStop", orgEvent : event
-    @utils.wait 300, ->
-      $('body').removeClass "resize-in-action"
-
-  ### PUBLIC METHODS ###
-
-  isVertical:-> @getOptions().type is "vertical"
-
-  getPanelIndex:(panel)->
-
-    for p,i in @panels
-      if p.getId() is panel.getId()
-        return i
-
-  hidePanel:(panelIndex,callback = noop)->
-
-    panel = @panels[panelIndex]
+
+    @emit "ResizeDidStart", event
+    document.body.classList.add "resize-in-action"
+
+
+
+  _resizeDidStop: do ->
+
+    unsetResizeInAction = KD.utils.throttle 1000, (view)->
+      document.body.classList.remove "resize-in-action"
+
+    (event)->
+
+      s1 = @sizes[0]/@_getSize()
+      s2 = @sizes[1]/@_getSize()
+
+      @setOption 'sizes', [s1, s2]
+      @emit "ResizeDidStop", event
+
+      unsetResizeInAction this
+
+
+  isVertical:-> @type is "vertical"
+
+
+  getPanelIndex: (panel)-> panel.index
+
+
+  hidePanel: (index, callback = noop)->
+
+    panel = @panels[index]
     panel._lastSize = panel._getSize()
-    @resizePanel 0,panelIndex,()=>
-      callback.call @,(panel : panel, index : panelIndex )
-
-  showPanel:(panelIndex,callback = noop)->
-
-    panel = @panels[panelIndex]
-    newSize = panel._lastSize or @getOptions().sizes[panelIndex] or 200
+    @resizePanel 0, index, callback.bind this, {panel, index}
+
+
+  showPanel:(index,callback = noop)->
+
+    panel           = @panels[index]
+    newSize         = panel._lastSize or @sizes[index] or 200
     panel._lastSize = null
-    @resizePanel newSize,panelIndex,()->
-      callback.call @,(panel : panel, index : panelIndex )
+    @resizePanel newSize, index, callback.bind this, {panel, index}
+

   resizePanel:(value = 0,panelIndex = 0,callback = noop)->


enter when editing cell in collection view does the right thing

triggers blur

addresses #221

--- client/collection_view.coffee
+++ client/collection_view.coffee
@@ -88,10 +88,12 @@
     $this = $(e.currentTarget)
     field_name = $this.data('field')
     type = Houston._get_type(field_name, get_current_collection())
-    input = 'text' #TODO schemaToInputType type fix on blur bug
+    input = 'text' # TODO schemaToInputType type fix on blur bug
     $this.removeClass('houston-collection-field')
     $this.html "<input type='#{input}' class='input-sm form-control' placeholder='#{type}' value='#{$this.text().trim()}'>"
     $this.find('input').select()
+    $this.find('input').on 'keydown', (event) ->
+      event.currentTarget.blur() if event.keyCode == 13
     $this.find('input').on 'blur', ->
       updated_val = $this.find('input').val()
       $this.html ""


Use data for payment id rather than res of IMP response

--- public/assets/coffee/order-id-payment.coffee
+++ public/assets/coffee/order-id-payment.coffee
@@ -33,24 +33,21 @@
           buyer_addr:   $info.data('address1')
           notice_url:   'https://test-share.theopencloset.net/webhooks/iamport'
         , (res) ->
-          data =
-            order_id: order_id
-            dump: JSON.stringify(res)
-            imp_uid: res.imp_uid
-            merchant_uid: res.merchant_uid
-            amount: res.paid_amount
-            status: res.status
-            pg_provider: res.pg_provider
-            pay_method: res.pay_method
-
           unless res.success
             $.growl.error({ title: '결제실패', message: res.error_msg })

-          payment_id = res.id
-          $.ajax "/payments/#{payment_id}",
+          $.ajax "/payments/#{data.id}",
             type: 'PUT'
             dataType: 'json'
-            data: data
+            data:
+              order_id: order_id
+              dump: JSON.stringify(res)
+              imp_uid: res.imp_uid
+              merchant_uid: res.merchant_uid
+              amount: res.paid_amount
+              status: res.status
+              pg_provider: res.pg_provider
+              pay_method: res.pay_method
             success: (data, textStatus, jqXHR) ->
             error: (jqXHR, textStatus, errorThrown) ->
             complete: (jqXHR, textStatus) ->


- Remove messy code, emit closeTopicsModal to close modal

--- client/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
+++ client/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
@@ -176,22 +176,13 @@

     super options,data

-    @titleLink = new KDCustomHTMLView
-      tagName     : 'a'
-      attributes  :
-        href      : '#'
-        class     : 'ttag'
-      pistachio   : '{{#(title)}}'
-    , data
+    @titleLink = new TagLinkView null, data

     @titleLink.registerListener
       KDEventTypes  : 'click'
       listener      : @
       callback      : (pubInst, event)=>
-        tag = @getData()
-        appManager.tell "Topics", "createContentDisplay", tag
-        event.stopPropagation()
-        no
+        @getDelegate().emit "closeTopicsModal"

   pistachio:->
     """


make router more easy to setup on a vm guest

--- router.coffee
+++ router.coffee
@@ -4,7 +4,7 @@
 fs = require('fs')

 # Port on which proxy listens
-proxyPort = 80
+proxyPort = 4000

 # Default port where requests are redirected
 defaultPort = 3000
@@ -14,10 +14,10 @@

 # HTTPS options
 # Server.key and Server.cert should be regenerated for each installation
-options =
-    https:
-        key: fs.readFileSync('/home/cozy/server.key', 'utf8'),
-        cert: fs.readFileSync('/home/cozy/server.crt', 'utf8')
+options = {}
+#    https:
+#        key: fs.readFileSync('/home/cozy/server.key', 'utf8'),
+#        cert: fs.readFileSync('/home/cozy/server.crt', 'utf8')

 # Proxy server that uses route table defined earlier


database

database

--- src/server/database_interface/index.coffee
+++ src/server/database_interface/index.coffee
@@ -8,16 +8,23 @@
 class DatabaseInterface
     #dbConfig is of type config.DatabaseConfig
     constructor : (dbConfig, callback) ->
+        @appCollection = 'applications'
+        @adminCollection  = 'admin_interface.users'
         # Ensures unique database for every user of the system
         # but will use the same database for multiple instances
         # of cloudbrowser run by the same user
-        dbName = "UID#{process.getuid()}-#{dbName}"
+        dbName = "UID#{process.getuid()}-#{dbConfig.dbName}"
         @dbClient = new Mongo.Db(dbName,
             new Mongo.Server("127.0.0.1", 27017, options:{auto_reconnect:true}))
         @dbClient.open (err, pClient) ->
             callback?(err)
         @mongoStore = new MongoStore({db:"#{dbName}_sessions"})
-        @appCollection = "applications"
+
+    findAdminUser : (searchKey,callback) ->
+        @findUser(searchKey,@adminCollection,callback)
+
+    addAdminUser : (users,callback) ->
+        @addUser(users,@adminCollection,callback)

     findUser : (searchKey, collectionName, callback) ->
         Async.waterfall [


Replaced subtitles with names with less "attitude"

--- enterprise/server/src/main/coffeescript/neo4j/webadmin/views/BaseView.coffee
+++ enterprise/server/src/main/coffeescript/neo4j/webadmin/views/BaseView.coffee
@@ -40,10 +40,10 @@

       render : ->
         $(@el).html @template( mainmenu : [
-          { label : "Dashboard",   subtitle:"Get a grip",url : "#",           current: location.hash is "" }
+          { label : "Dashboard",   subtitle:"Overview",url : "#",           current: location.hash is "" }
           { label : "Data browser",subtitle:"Explore and edit",url : "#/data/" ,    current: location.hash.indexOf("#/data/") is 0 }
           { label : "Console",     subtitle:"Power tool",url : "#/console/" , current: location.hash is "#/console/" }
-          { label : "Server info", subtitle:"Detailed information",url : "#/info/" ,    current: location.hash is "#/info/" } ] )
+          { label : "Server info", subtitle:"Details",url : "#/info/" ,    current: location.hash is "#/info/" } ] )

         if @mainView?
           @mainView.attach($("#contents"))


Add terms to displayedQuery rather than query

--- app/assets/javascripts/rglossa/components/cwb-multiword.coffee
+++ app/assets/javascripts/rglossa/components/cwb-multiword.coffee
@@ -94,11 +94,15 @@

   addTerm: ->
-    query = @get('query')
-    return unless query
+    newTerm = App.CwbMultiwordTerm.create
+      word: ''
+      min: null
+      max: null
+      isLast: true

-    query += ' ""'
-    @set('query', query)
+    dq = @get('displayedQuery')
+    dq[dq.length-1].set('isLast', false)
+    dq.pushObject(newTerm)

   removeTerm: (term) ->


show module names on draft state assignment index

fixes CNVS-12216

test plan
- as a teacher
- in a course with 11+ modules
- with 1+ assignment in each module
- visit the assignment index page
- all module names should be listed (instead of 'Module')

Change-Id: I57b5f1219105e21db6fe48407422fc9385773bdf
Reviewed-on: https://gerrit.instructure.com/34083
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Anna Koalenz <8df3af669343a6e717fd7441a9deea414b21645f@instructure.com>
Reviewed-by: Cameron Sutter <853bcc0cb8f5834b7805e8eb9a762054be43c731@instructure.com>
Product-Review: Liz Abinante <ce3305c9be70e1a5a9ff49df19e1a5b24d5a47f8@instructure.com>

--- app/coffeescripts/collections/AssignmentGroupCollection.coffee
+++ app/coffeescripts/collections/AssignmentGroupCollection.coffee
@@ -5,7 +5,8 @@
   'underscore'
   'i18n!assignments'
   'compiled/collections/SubmissionCollection'
-], ($, Backbone, AssignmentGroup, _, I18n, SubmissionCollection) ->
+  'compiled/collections/ModuleCollection'
+], ($, Backbone, AssignmentGroup, _, I18n, SubmissionCollection, ModuleCollection) ->

   PER_PAGE_LIMIT = 50

@@ -23,9 +24,12 @@
         include: ["assignments"]

     loadModuleNames: ->
-      $.get(ENV.URLS.context_modules_url).then (modules) =>
+      modules = new ModuleCollection([], {course_id: @course.id})
+      modules.loadAll = true
+      modules.fetch()
+      modules.on 'fetched:last', =>
         moduleNames = {}
-        for m in modules
+        for m in modules.toJSON()
           moduleNames[m.id] = m.name

         for assignment in @assignments()


Fix exception in template helper, iframe login

`useIframe` makes method call to `rocketchat-iframe-login` package, which throws if that package is disabled.
Resolved with package enabled conditional.

--- packages/rocketchat-ui-master/master/main.coffee
+++ packages/rocketchat-ui-master/master/main.coffee
@@ -97,10 +97,12 @@
 			return false

 	useIframe: ->
-		return RocketChat.iframeLogin.reactiveEnabled.get()
+		iframeEnabled = (typeof RocketChat.iframeLogin isnt "undefined")
+		return (iframeEnabled and RocketChat.iframeLogin.reactiveEnabled.get())

 	iframeUrl: ->
-		return RocketChat.iframeLogin.reactiveIframeUrl.get()
+		iframeEnabled = (typeof RocketChat.iframeLogin isnt "undefined")
+		return (iframeEnabled and RocketChat.iframeLogin.reactiveIframeUrl.get())

 	subsReady: ->
 		routerReady = FlowRouter.subsReady('userData', 'activeUsers')


mistake by pilot card "Guri"

mistake by pilot card "Guri"

--- coffeescripts/cards-de.coffee
+++ coffeescripts/cards-de.coffee
@@ -387,7 +387,7 @@
             name: "Prinz Xizor"
             text: """Sobald du verteidigst, darf ein freundliches Schiff in Reichweite 1 ein nicht-negiertes %HIT% oder %CRIT% an deiner Stelle nehmen."""
         "Guri":
-            text: """Wenn du zu Beginn der Kampfphase in Reichweite 1 zu einem freundlichen Schiff bist, darfst du 1 Fokusmarker auf dein Schiff legen."""
+            text: """Wenn du zu Beginn der Kampfphase in Reichweite 1 zu einem feindlichen Schiff bist, darfst du 1 Fokusmarker auf dein Schiff legen."""
         "Black Sun Vigo":
             name: "Vigo der Schwarzen Sonne"
         "Black Sun Enforcer":


Typo

Typo

fixes #510

--- coffeescripts/cards-en.coffee
+++ coffeescripts/cards-en.coffee
@@ -896,7 +896,7 @@
         'Rage':
             text: """Action: Assign 1 focus token to your ship and receive 2 stress tokens.  Until the end of the round, when attacking, you may reroll up to 3 attack dice."""
         "Attanni Mindlink":
-            text: """Limit 2 per quad. %SCUMONLY%%LINEBREAK%Each time you are assigned a focus or stress token, each other friendly ship with Attanni Mindlink must also be assigned the same type of token if it does not already have one."""
+            text: """Limit 2 per squad. %SCUMONLY%%LINEBREAK%Each time you are assigned a focus or stress token, each other friendly ship with Attanni Mindlink must also be assigned the same type of token if it does not already have one."""
         "Boba Fett":
             text: """%SCUMONLY%%LINEBREAK%After performing an attack, if the defender was dealt a faceup Damage card, you may discard this card to choose and discard 1 of the defender's Upgrade cards."""
         "Dengar":


Update cards-es.coffee

Update cards-es.coffee

Missed to delete a couple lines from the copy-pasting while fixing the previous error :P

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -653,7 +653,6 @@
             ship: "T-70 Ala-X"
             text: '''The first time you remove a shield token from your ship each round, assign 1 evade token to your ship.'''
         "Unspoiled PS7 T-70 X-Wing Pilot":
-            name: "Novato del Esc. Azul"
             ship: "T-70 Ala-X"
         # TIE/fo
         '"Omega Ace"':
@@ -682,7 +681,6 @@
             text: '''Enemy ships that you have locked cannot modify any dice when attacking you or defending against your attacks.'''
         "Unspoiled PS7 TIE/fo Pilot":
             ship: "Caza TIE/fo"
-            text: '''Enemy ships that you have locked cannot modify any dice when attacking you or defending against your attacks.'''
         "Unspoiled PS4 TIE/fo Pilot":
             ship: "Caza TIE/fo"


Map is showing again.  Need viewport and sprite sync systems.

--- src/javascript/game/ecs2_spike.coffee
+++ src/javascript/game/ecs2_spike.coffee
@@ -1,11 +1,11 @@
 PIXI = require 'pixi.js'
 _ = require 'lodash'
+Immutable = require 'immutable'

 Mousetrap = require '../vendor/mousetrap_wrapper'
 KeyboardController = require '../input/keyboard_controller'
 GamepadController = require('../input/gamepad_controller')

-#XXX EntityStore = require '../ecs/entity_store'
 EntityStore = require '../ecs2/entity_store'

 SystemRegistry = require '../ecs/system_registry'
@@ -97,8 +97,12 @@
     @setupSpriteConfigs()

     @setupInput()
-
-    @setupMap(MapData.areas.a, @layers.map)
+    # TODO: update sprite configs to immutable structure.
+    # TODO: somethign better than sneaking data into 'cheatsies'
+    @input.setIn ['cheatsies','spriteConfigs'], @spriteConfigs
+
+    map = @input.getIn(['cheatsies','map'])
+    @setupMap(MapData.areas.a, @layers.map, map.get('tileWidth'),map.get('tileHeight'))

     @timeDilation = 1

@@ -138,11 +142,20 @@
     }

   setupInput: ->
-    @input =
+    @input = Immutable.fromJS
       controllers:
         player1: {}
         player2: {}
         admin: {}
+      dt: 0
+      cheatsies:
+        map:
+          tileGrid: "NOT SET"
+          tileWidth: 16
+          tileHeight: 16
+        spriteConfigs:
+          {}
+

     @keyboardController = new KeyboardController
       bindings:
@@ -211,10 +224,7 @@
       # 'skree_action'
       # 'skree_velocity'
       'gravity_system'
-      ['map_physics_system',
-        tileGrid: @mapTileGrid
-        tileWidth: @mapTileWidth
-        tileHeight: @mapTileHeight]
+      'map_physics_system',

       # 'bullet_system'

@@ -314,9 +324,24 @@
     ac = @adminController.update()
     @handleAdminControls(ac) if ac?

-    @input.controllers.player1 = p1in
-    @input.controllers[@adminMovers[@adminMoversIndex]] = ac
+    # @input.controllers.player1 = p1in
+    # @input.controllers[@adminMovers[@adminMoversIndex]] = ac
     # @input.controllers.player2 = @p2Controller.update()
+
+    @input = @input
+      .setIn(['controllers','player1'], Immutable.fromJS(p1in))
+      .set('dt', dt*@timeDilation)
+
+    # input
+    #   dt
+    #   controllers
+    #     player1
+    #   cheatsies
+    #     map
+    #       tileGrid
+    #       tileWidth
+    #       tileHeight
+    #     spriteConfigs

     # TODO @systemsRunner.run(@estore, dt*@timeDilation, @input) unless @paused

@@ -352,15 +377,9 @@
         @adminMoversIndex = 0

-  setupMap: (map, container) ->
-    @mapTileHeight = 16
-    @mapTileWidth = 16
-
-    @roomWidth = 16
-    @roomHeight = 15
-
-    @roomWidthPx = @roomWidth * @mapTileWidth
-    @roomHeightPx = @roomHeight * @mapTileHeight
+  setupMap: (map, container, mapTileWidth, mapTileHeight) ->
+    roomWidth = 16
+    roomHeight = 15

     getMapTileSprite = (n) ->
       if n?
@@ -370,26 +389,26 @@

     divRem = (numer,denom) -> [Math.floor(numer/denom), numer % denom]

-    mapRowCount = map.length * @roomHeight
-    mapColCount = map[0].length * @roomWidth
+    mapRowCount = map.length * roomHeight
+    mapColCount = map[0].length * roomWidth

     tileGrid = []
     for r in [0...mapRowCount]
       tileRow = []
       tileGrid.push tileRow
       for c in [0...mapColCount]
-        [rr,tr] = divRem(r, @roomHeight)
-        [rc,tc] = divRem(c, @roomWidth)
+        [rr,tr] = divRem(r, roomHeight)
+        [rc,tc] = divRem(c, roomWidth)
         roomType = map[rr][rc]
         room = MapData.roomTypes[roomType]
         tileType = room[tr][tc]
         if tileType?
           tile =
             type: tileType
-            x: c * @mapTileWidth
-            y: r * @mapTileHeight
-            width: @mapTileWidth
-            height: @mapTileHeight
+            x: c * mapTileWidth
+            y: r * mapTileHeight
+            width: mapTileWidth
+            height: mapTileHeight

           sprite = getMapTileSprite(tile.type)
           if sprite?


Add grouping in to fullscale

Add grouping in to fullscale

--- components/ToFullscale.coffee
+++ components/ToFullscale.coffee
@@ -74,6 +74,7 @@
     out: 'url'
     async: true
     forwardGroups: true
+    group: true
   , (url, groups, out, callback) ->
     newUrl = url
     unless url


Linting fix

Linting fix

--- src/lib/nodejs/ComponentLoader.coffee
+++ src/lib/nodejs/ComponentLoader.coffee
@@ -17,7 +17,7 @@
 # We allow components to be un-compiled CoffeeScript
 CoffeeScript = require 'coffee-script'
 if typeof CoffeeScript.register != 'undefined'
-    CoffeeScript.register()
+  CoffeeScript.register()
 babel = require 'babel-core'

 # Disable NPM logging in normal NoFlo operation


[fix] enable pairing buttons enabling regardless of whether group was already paired

--- js/admin.coffee
+++ js/admin.coffee
@@ -98,10 +98,12 @@
   $(".enable-pairing").on('click', (e) ->
     e.preventDefault()
     year = $(this).data('year')
-    selector = $(".btn.pairing.#{year}")
-    if selector.hasClass("disabled") && selector.data("paired") == false
-      selector.removeClass("disabled")
-    else selector.addClass("disabled")
+    $(".btn.pairing.#{year}").each((ind,val)->
+      if $(this).hasClass("disabled") && $(this).data("paired") == false
+        $(this).removeClass("disabled")
+      else $(this).addClass("disabled")
+    )
+
   )


refactor

refactor

--- app/assets/javascripts/select2_helper.js.coffee
+++ app/assets/javascripts/select2_helper.js.coffee
@@ -1,37 +1,46 @@
 $(document).on 'ready page:change', () ->
+  jQuery.fn.extend
+    select2_single: (params) ->
+      default_params =
+        width: '100%'
+        initSelection: (element, callback) ->
+          callback
+            id: element.val()
+            text: element.data('init') || ''
+        ajax:
+          url: '/select2/results'
+          dataType: 'json'
+          data: (term, page) ->
+            term: term
+            page: page
+            model: $(this).data('model')
+            column: $(this).data('column')
+          results: (data, page) -> data
+      $.extend default_params, params
+      this.select2 default_params
+    select2_multiple: (params) ->
+      default_params =
+        multiple: true
+        width: '100%'
+        initSelection: (element, callback) ->
+          $(element).val('')
+          callback $(element).data('init')
+        ajax:
+          url: '/select2/results'
+          dataType: 'json'
+          data: (term, page) ->
+            term: term
+            page: page
+            model: $(this).data('model')
+            column: $(this).data('column')
+          results: (data, page) ->
+            data
+      $.extend default_params, params
+      this.select2 default_params
   $('.select2').select2
     width: '100%'
-  $('.select2-single').select2
-    width: '100%'
-    initSelection: (element, callback) ->
-      callback
-        id: element.val()
-        text: element.data('init') || ''
-    ajax:
-      url: '/select2/results'
-      dataType: 'json'
-      data: (term, page) ->
-        term: term
-        page: page
-        model: $(this).data('model')
-        column: $(this).data('column')
-      results: (data, page) -> data
-  $('.select2-multiple').select2
-    multiple: true
-    width: '100%'
-    initSelection: (element, callback) ->
-      $(element).val('')
-      callback $(element).data('init')
-    ajax:
-      url: '/select2/results'
-      dataType: 'json'
-      data: (term, page) ->
-        term: term
-        page: page
-        model: $(this).data('model')
-        column: $(this).data('column')
-      results: (data, page) ->
-        data
+  $('.select2-single').select2_single()
+  $('.select2-multiple').select2_multiple()
   $('.select2-tags-array').select2
     tags: []
     width: '100%'


change in coffee

change in coffee

--- js/index.coffee
+++ js/index.coffee
@@ -26,6 +26,7 @@
   alterPage = (name, url, description, score) ->
     $('#name').text(name)
     $('#url').text(url)
+    $('#url').attr('href', url)
     $('#desc').text(description)
     animateScore(score)


Make build status observable

Make build status observable

--- js/inner.coffee
+++ js/inner.coffee
@@ -215,10 +215,10 @@
       author_email: null
       author_date: null
       start_time: null
-      stop_time: null,
+      stop_time: null
+      status: null,

       ["build_num"
-       "status"
        "committer_name"
        "committer_email"
        "why"
@@ -232,7 +232,7 @@
       "#{@project_path()}/#{@build_num}"

     @style = @komp =>
-      klass = switch @status
+      klass = switch @status()
         when "failed"
           "important"
         when "infrastructure_fail"
@@ -260,7 +260,7 @@
       return result

-    @status_words = @komp => switch @status
+    @status_words = @komp => switch @status()
       when "infrastructure_fail"
         "circle bug"
       when "timedout"
@@ -272,7 +272,7 @@
       when "not_running"
         "not running"
       else
-        @status
+        @status()

     @committer_mailto = @komp =>
       if @committer_email
@@ -339,23 +339,33 @@
       @build_channel.bind('newAction', (json) => @newAction json)
       @build_channel.bind('updateAction', (json) => @updateAction json)
       @build_channel.bind('appendAction', (json) => @appendAction json)
+      @build_channel.bind('updateStatus', (json) => @updateStatus json)
+
+  fillActions: (step, index) =>
+    # fills up steps and actions such that step and index are valid
+
+    for i in [0..step-1]
+      if not @steps()[i]?
+        @steps.setIndex(step, new Step({}))
+
+    # actions can arrive out of order when doing parallel. Fill up the other indices so knockout doesn't bitch
+    for i in [0..index-1]
+      if not @steps()[step].actions()[i]?
+        @steps()[step].actions.setIndex(i, new ActionLog({}))

   newAction: (json) =>
-    if not @steps()[json.step]?
-      @steps.setIndex(json.step, new Step({}))
-
-    # actions can arrive out of order when doing parallel. Fill up the other indices so knockout doesn't bitch
-    for i in [0..json.index-1]
-      if not @steps()[json.step].actions()[i]?
-        @steps()[json.step].actions.setIndex(i, new ActionLog({}))
-
+    @fillActions(json.step, json.index)
     @steps()[json.step].actions.setIndex(json.index, new ActionLog(json.log))

   updateAction: (json) =>
+    @fillActions(json.step, json.index)
     @steps()[json.step].actions()[json.index].updateObservables(json.log)

   appendAction: (json) =>
     @steps()[json.step].actions()[json.index].out.push(json.out)
+
+  updateStatus: (json) =>
+    @status(json.status)

   # TODO: CSRF protection
   retry_build: (data, event) =>


Make lightning the default plan

Make lightning the default plan

--- js/inner.coffee
+++ js/inner.coffee
@@ -435,7 +435,7 @@
       price: 0
     ]

-    @individualPlan = ko.observable(@plans[4])
+    @individualPlan = ko.observable(@plans[2])

     # team billing
     @teamPlan = ko.observable(null)


Sort available builds alphabetically

Sort available builds alphabetically

--- js/inner.coffee
+++ js/inner.coffee
@@ -297,7 +297,7 @@
       else
         -1
     else
-      if l.vcs_url().toLowerCase() < r.vcs_url().toLowerCase() then 1 else -1
+      if l.vcs_url().toLowerCase() > r.vcs_url().toLowerCase() then 1 else -1

   checkbox_title: =>


Remove unused code

Remove unused code

--- js/outer.coffee
+++ js/outer.coffee
@@ -1,13 +1,3 @@
-queryParams = () ->
-  res = {}
-  params = window.location.search.substring(1).split("&")
-  for p in params
-    [k,v] = p.split("=")
-    res[k] = v
-  res
-
-window.queryParams = queryParams
-
 class CircleViewModel
   constructor: ->
     @ab = (new ABTests(ab_test_definitions)).ab_tests


Extended shema for user's profile.

Extended shema for user's profile.

--- app/lib/collections/schemas/user-profile.coffee
+++ app/lib/collections/schemas/user-profile.coffee
@@ -4,5 +4,8 @@
     label: 'Full name'
     min: 2
     max: 140
+  photo:
+    type: Mongo.ObjectID
+    label: 'Photo'

 Namespace 'schemas', UserProfile:schema


shopify callback added

shopify callback added

--- server.coffee
+++ server.coffee
@@ -13,7 +13,7 @@
     res.set('Content-Type','text/html')
     .sendFile('index.html',root:'.')

-app.get '/shopify/:callback' (req,res) ->
+app.get '/shopify/:callback', (req,res) ->
     res.status(200).send('All is well with callback ')

 app.use '/shopify-proxy', (req, res, next) ->


redirect to https on production

redirect to https on production

--- server.coffee
+++ server.coffee
@@ -41,6 +41,16 @@

 allIdentities = {}

+
+if process.env.NODE_ENV == 'production'
+  forceHttps = (req, res, next) ->
+    isHttps = req.headers["x-forwarded-proto"] == 'https'
+    return next() if isHttps
+    host = req.headers.host
+    res.redirect("https://" + host + req.url)
+  app.use forceHttps
+
+
 if secretKey
   app.get '', (req, res)->
     res.render('index', publicKey: publicKey)


Changed output to show data.

Changed output to show data.

--- server.coffee
+++ server.coffee
@@ -30,7 +30,7 @@
   console.log('eventName '+ eventName)
   console.log('channelName '+ channelName)
   console.log('socket_id '+ socket_id)
-  console.log('data '+ data)
+  console.log(data)

 # when a new person connects ->
   # send current state


Missing check.

Missing check.

--- server.coffee
+++ server.coffee
@@ -22,6 +22,8 @@
         console.log "Exception from Tracker afterFlush function:", e.stack || e.message

   autorun: (f) ->
+    throw new Error 'Tracker.autorun requires a function argument' unless typeof f is 'function'
+
     c = new Tracker.Computation(f, Tracker.currentComputation, privateObj)

     if Tracker.active


Updated log_mapper

Updated log_mapper

--- bokehjs/src/coffee/mapper/1d/log_mapper.coffee
+++ bokehjs/src/coffee/mapper/1d/log_mapper.coffee
@@ -5,7 +5,7 @@
 ], (Backbone, HasProperties) ->

-  class LinearMapper extends HasProperties
+  class LogMapper extends HasProperties
     initialize: (attrs, options) ->
       super(attrs, options)

@@ -19,10 +19,40 @@
       return scale * x + offset

     v_map_to_target: (xs) ->
-      [scale, offset] = @get('mapper_state')
+      [scale, offset, inter_scale, inter_offset] = @get('mapper_state')
+
+      intermediate = new Float64Array(xs.length)
       result = new Float64Array(xs.length)
+
+      if inter_scale == 0
+        intermediate = xs.map (i) -> i * 0
+      else
+
+        try
+          mask1 = xs.map (i) -> i <= 0
+          mask2 = xs.map (i) -> isNaN(i)
+
+          mask = (mask1[i] | mask2[i] for i in [0...xs.length])
+
+          mask = mask.reduce (x, y) -> x || y
+
+          if mask == 1
+            xs[mask] = 1
+
+          intermediate = xs.map (i) -> (Math.log(i) - inter_offset) / inter_scale
+
+          for x, idx in intermediate
+            if isNaN(intermediate[idx])
+              throw "NaN"
+            if isFinite(intermediate[idx]) == False
+              throw "Infinite"
+
+        catch error
+          intermediate = xs.map (i) -> i * 0
+
       for x, idx in xs
-        result[idx] = scale * x + offset
+        result[idx] = intermediate[idx] * scale + offset
+
       return result

     map_from_target: (xprime) ->
@@ -36,6 +66,32 @@
         result[idx] = (xprime - offset) / scale
       return result

+    _get_safe_scale: (orig_start, orig_end) ->
+      if orig_start < 0
+        start = 0
+      else
+        start = orig_start
+
+      if orig_end < 0
+        end = 0
+      else
+        end = orig_end
+
+      if start == end
+        if start == 0
+          start = 1
+          end = 10
+        else
+          log_val = Math.log(start) / Math.log(10)
+          start = Math.pow(10, Math.floor(log_val))
+
+          if Math.ceil(log_val) != Math.floor(log_val)
+            end = Math.pow(10, Math.ceil(log_val))
+          else
+            end = Math.pow(10, Math.ceil(log_val) + 1)
+
+      return [start, end]
+
     _mapper_state: () ->
       #
       #  (t1 - t0)       (t1 - t0)
@@ -48,14 +104,30 @@
       source_end   = @get('source_range').get('end')
       target_start = @get('target_range').get('start')
       target_end   = @get('target_range').get('end')
-      scale = (target_end - target_start)/(source_end - source_start)
-      offset = -(scale * source_start) + target_start
-      return [scale, offset]

-  class LinearMappers extends Backbone.Collection
-    model: LinearMapper
+      #scale = (target_end - target_start)/(source_end - source_start)
+      #offset = -(scale * source_start) + target_start
+
+      screen_range = target_start - target_end
+
+      [start, end] = @_get_safe_scale(source_start, source_end)
+
+      if start == 0
+        inter_scale = Math.log(end)
+        inter_offset = 0
+      else
+        inter_scale = Math.log(end) - Math.log(start)
+        inter_offset = Math.log(start)
+
+      scale = screen_range
+      offset = target_start
+      return [scale, offset, inter_scale, inter_offset]
+
+
+  class LogMappers extends Backbone.Collection
+    model: LogMapper

   return {
-    "Model": LinearMapper,
-    "Collection": new LinearMappers()
+    "Model": LogMapper,
+    "Collection": new LogMappers()
   }


added popupClass() API method

added popupClass() API method

--- assets/javascripts/app/timeline/timeline.coffee
+++ assets/javascripts/app/timeline/timeline.coffee
@@ -8,6 +8,7 @@
     _parsedData: null
     _selectedItem: null
     _rootDOMElement: null
+    _popupClass: AddEventView

     addToLane: (chart, item) ->
       index = item.lane
@@ -327,10 +328,9 @@
       @draw(true)

     addItem: (item) =>
-      console.log 'adding item', item
+      #console.log 'adding item', item
       @_data.push item
       @_parsedData = @parseData @_data
-      console.log @_parsedData
       @draw()

     nextItem: =>
@@ -355,7 +355,7 @@
         @centerElement el

     onAddUserNote: =>
-      @popup = new AddEventView(@rootDOMElement())
+      @popup = new @_popupClass(@rootDOMElement())
       $popup = $('.addEventWrapper')
       bounds = $('#timeline-toolbar')[0].getBoundingClientRect()
       left = bounds.width + bounds.left - 5
@@ -365,7 +365,6 @@
       @popup.on 'addEventCancel', @onAddEventCancel

     onAddEventSave: (data) =>
-      console.log 'onAddEventSave', data
       @addItem data
       @popup.off 'addEventSave', @onAddEventSave
       @popup.off 'addEventCancel', @onAddEventCancel
@@ -374,7 +373,6 @@
       @popup = null

     onAddEventCancel: =>
-      console.log 'onAddEventCancel'
       @popup.off 'addEventSave', @onAddEventSave
       @popup.off 'addEventCancel', @onAddEventCancel
       $('#timeline-add-note').removeClass('toggled')
@@ -417,6 +415,8 @@
     selectedItem: (item) => if item? then @_selectedItem = item else @_selectedItem

     rootDOMElement: (el) => if el? then @_rootDOMElement = el else @_rootDOMElement
+
+    popupClass: (cls) => if cls? then @_popupClass = cls else @_popupClass

   T = new Timeline()
   @exports =
@@ -431,4 +431,5 @@
     reset: T.reset
     selectedItem: T.selectedItem
     rootDOMElement: T.rootDOMElement
+    popupClass: T.popupClass
   @exports


Fill quantity with purchase order service or cost

--- app/assets/javascripts/backend/purchase_process/reconciliation.js.coffee
+++ app/assets/javascripts/backend/purchase_process/reconciliation.js.coffee
@@ -267,13 +267,20 @@

     _fillPurchaseOrderItem: (lastLineForm, checkboxLine, itemId, itemQuantity, itemConditionning, itemConditionningQuantity) ->
       variantId = $(checkboxLine).find('.variant').attr('data-id')
+      variantType = $(checkboxLine).attr('data-variant-type')

       $(lastLineForm).find('.purchase-item-attribute').val(itemId)

       $(lastLineForm).find('.item-block.merchandise .parcel-item-variant').first().selector('value', variantId)
       $(lastLineForm).find('.hidden.purchase-item-attribute').val(itemId)
-      $(lastLineForm).find('.nested-fields.storing-fields:first .storing-quantifier .storing-quantity').val(itemQuantity)
-      $(lastLineForm).find('.nested-fields.storing-fields:first .conditionning-quantity').val(itemConditionningQuantity)
-      $(lastLineForm).find('.nested-fields.storing-fields:first .conditionning').val(itemConditionning)
+
+      if variantType == "service" || variantType == "cost"
+        $(lastLineForm).find('.item-quantifier-population .total-quantity').val(itemQuantity)
+        $(lastLineForm).find('.buttons button[data-validate="item-form"]').removeAttr('disabled')
+      else
+        $(lastLineForm).find('.nested-fields.storing-fields:first .storing-quantifier .storing-quantity').val(itemQuantity)
+        $(lastLineForm).find('.nested-fields.storing-fields:first .conditionning-quantity').val(itemConditionningQuantity)
+        $(lastLineForm).find('.nested-fields.storing-fields:first .conditionning').val(itemConditionning)
+

 ) ekylibre, jQuery


Avoid highlighting commands under a continued line

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -468,7 +468,7 @@
 			# Concealed newline
 			name: "constant.character.escape.newline.roff"
 			begin: "(\\\\)$\\n?"
-			end:   "^"
+			end:   "^(?:[.'])?"
 			beginCaptures:
 				1: name: "punctuation.definition.escape.roff"
 		}


Check for whitespace before starting a string

Something like abc"xyz is considered a single string in Groff: the quote
mark doesn't start a word-group in the middle of an unquoted string.

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -76,13 +76,13 @@
 	string:
 		patterns: [{
 			name: "string.quoted.double.empty.roff",
-			match: '(")(")'
+			match: '(?<=(?<=[^\\\\]|^)\\s|^)(")(")(?=\\s|$)'
 			captures:
 				1: {name: "punctuation.definition.string.begin.roff"}
 				2: {name: "punctuation.definition.string.end.roff"}
 		},{
 			name: "string.quoted.double.roff",
-			begin: '"(?!")'
+			begin: '(?<=(?<=[^\\\\]|^)\\s|^)"(?!")'
 			end:   '(?<!")"(?!")|(?<!\\\\)$|(?=\\\\\")'
 			beginCaptures: {0: {name: "punctuation.definition.string.begin.roff"}}
 			endCaptures:   {0: {name: "punctuation.definition.string.end.roff"}}


Add config for Atom's linter ui.

Add config for Atom's linter ui.

--- files/.atom/config.cson
+++ files/.atom/config.cson
@@ -28,6 +28,11 @@
   "linter-eslint":
     disableWhenNoEslintrcFileInPath: true
     useGlobalEslint: true
+  "linter-ui-default":
+    alwaysTakeMinimumSpace: true
+    panelHeight: 208
+    panelRepresents: "Entire Project"
+    showPanel: true
   "markdown-preview": {}
   minimap:
     plugins:


Use buttons data-actions in NumberFieldUpdater component

It will survive if we change icons framework, also it is more
samantically correct.

--- backend/app/assets/javascripts/spree/backend/number_field_updater.coffee
+++ backend/app/assets/javascripts/spree/backend/number_field_updater.coffee
@@ -19,19 +19,19 @@

   toggleReadOnly = (id, show) ->
     toggleButtonVisibility('edit', id, show)
-    toggleButtonVisibility('trash', id, show)
+    toggleButtonVisibility('remove', id, show)
     cssDisplay = if show then 'block' else 'none'
     $("#number-update-#{id} span").css('display', cssDisplay)

   toggleForm = (id, show) ->
     toggleButtonVisibility('cancel', id, show)
-    toggleButtonVisibility('check', id, show)
+    toggleButtonVisibility('save', id, show)
     cssDisplay = if show then 'block' else 'none'
     $("#number-update-#{id} input[type='number']").css('display', cssDisplay)

-  toggleButtonVisibility = (buttonIcon, id, show) ->
+  toggleButtonVisibility = (buttonAction, id, show) ->
     cssDisplay = if show then 'inline-block' else 'none'
-    $(".fa-#{buttonIcon}[data-id='#{id}']").css('display', cssDisplay)
+    $("[data-action='#{buttonAction}'][data-id='#{id}']").css('display', cssDisplay)

   resetInput = (id) ->
     tableCell = $("#number-update-#{id}")


Swiping events changes

Swiping events changes

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -58,7 +58,9 @@
             fingers = touches.length
             if fingers == GESTURE.fingers
                 CURRENT_TOUCH = _fingersPosition(touches, fingers)
-                _trigger "swiping" if _isSwipe(event)
+                is_swipe = _isSwipe(event)
+                if is_swipe then GESTURE.prevSwipe = true
+                _trigger "swiping" if (is_swipe or GESTURE.prevSwipe is true)
                 if fingers == 2
                     _captureRotation()
                     _capturePinch()
@@ -80,7 +82,7 @@
             if GESTURE.taps is 2 and GESTURE.gap
                 _trigger "doubleTap"
                 _cleanGesture()
-            else if _isSwipe()
+            else if _isSwipe() or GESTURE.prevSwipe
                 _trigger "swipe"
                 swipe_direction = _swipeDirection(FIRST_TOUCH[0].x, CURRENT_TOUCH[0].x, FIRST_TOUCH[0].y, CURRENT_TOUCH[0].y)
                 _trigger "swipe" + swipe_direction


remove more components not yet ready

remove more components not yet ready

these are not ready for integration

--- js/src/service/ShareProcessingService.coffee
+++ js/src/service/ShareProcessingService.coffee
@@ -1,49 +0,0 @@
-define 'kryptnostic.share-processing-service', [
-  'require'
-  'kryptnostic.logger'
-  'kryptnostic.deflating-marshaller'
-  'kryptnostic.crypto-service-loader'
-  'kryptnostic.encrypted-search-object-key'
-], (require) ->
-
-  Logger                   = require 'kryptnostic.logger'
-  CryptoServiceLoader      = require 'kryptnostic.crypto-service-loader'
-  DeflatingMarshaller      = require 'kryptnostic.deflating-marshaller'
-  EncryptedSearchObjectKey = require 'kryptnostic.encrypted-search-object-key'
-
-  log = Logger.get('ShareProcessingService')
-
-  #
-  # Service to process incoming shares.
-  # Author: rbuckheit
-  #
-
-  class ShareProcessingService
-
-    constructor: ->
-      @cryptoServiceLoader = new CryptoServiceLoader()
-      @marshaller          = new DeflatingMarshaller()
-
-    # processes all incoming shares, registering bridge keys for each.
-    processShares: (shares) ->
-      Promise.resolve()
-      .then =>
-        Promise.all(shares.map( (share) => @processShare(share) ))
-      .then (encryptedSearchObjectKeys) =>
-        @sharingClient.registerSearchKeys(searchObjectKeys)
-
-    # downloads encrypted document key, decrypts it, and generates a new bridge key.
-    # this key will be registered with the server.
-    processShare: (share) ->
-      Promise.resolve()
-      .then =>
-        @cryptoServiceLoader.getObjectCryptoService(id, { expectMiss : false })
-      .then (cryptoService) =>
-        { id } = share
-
-        blockCiphertext = share.sharingKey
-        marshalled      = cryptoService.decrypt(blockCiphertext)
-        sharingKey      = @marshaller.unmarshall(marshalled)
-
-        bridgeKey = @fheEngine.getBridgeKey({ sharingKey })
-        return new EncryptedSearchObjectKey { id, key : bridgeKey }


don't need this

don't need this

--- lib/atom-ternjs-type.coffee
+++ lib/atom-ternjs-type.coffee
@@ -150,12 +150,6 @@
         })
         @setPosition()

-  hide: ->
-    @view.classList.remove('active')
-
-  show: ->
-    @view.classList.add('active')
-
   destroy: ->
     @destroyOverlay()
     @view?.destroy()


Amazing what a little : can do

Amazing what a little : can do

--- src/scripts/search-virus-total.coffee
+++ src/scripts/search-virus-total.coffee
@@ -34,7 +34,7 @@
       .post(data) (err, res, body) ->
         vt_json = JSON.parse(body)

-        if vt_json.response_code == 1:
+        if vt_json.response_code == 1
           summary = """ VirusTotal Result: #{vt_json.resource}
           - Scanned at: #{vt_json.scan_date}
           - Results:    #{vt_json.positives}/#{vt_json.total}