move code into lib

move code into lib

modify test to use lib path

--- test/unit_domain_tree.coffee
+++ test/unit_domain_tree.coffee
@@ -1,5 +1,5 @@

-{DomainNames,Tree} = require('../')
+{DomainNames,Tree} = require('../lib')

 describe 'Tree', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -25,6 +25,6 @@
     x: 4
     y: 20

-  equals engine.first('type=testObj'), obj1
+  equals engine.first('type=testObj').l, obj1

 module()


fallback to old logic if no core steps are found

--- tutor/src/components/task/crumb-mixin.cjsx
+++ tutor/src/components/task/crumb-mixin.cjsx
@@ -102,8 +102,12 @@

       notCoreIndex = _.findLastIndex crumbs, (crumb) ->
         (crumb.type is 'step') and TaskStepStore.isCore(crumb.data.id)
-      if notCoreIndex isnt -1
-        notCore = crumbs[notCoreIndex]
+
+      notCore = if notCoreIndex is -1
+        _.find crumbs, (crumb) ->
+          (crumb.type is 'step') and not TaskStepStore.isCore(crumb.data.id)
+      else
+        crumbs[notCoreIndex]
       if notCore?
         crumbType = 'spacer'
         spacerCrumb =


Handle split error in collaboration pagination

refs VICE-2955
flag=none

Test Plan:
 - set up and enable google drive plugin
 - go to the collaborations page for a course
 - open browser console
 - create a collaboration with 50+ students added
 - after creation, edit the collaboration
 - while the user lists load in no error should show

Change-Id: Ibd7d56b0faf72b44c3458bf811683eb12a4d1491
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/296467
Tested-by: Service Cloud Jenkins <9144042a601061f88f1e1d7a1753ea3e2972119d@instructure.com>
Reviewed-by: Jason Gillett <12d37bea14b8257b31fd2f970455158adbd46adb@instructure.com>
Product-Review: Jason Gillett <12d37bea14b8257b31fd2f970455158adbd46adb@instructure.com>
QA-Review: Caleb Guanzon <54fa1f30d451e4f3c341a8f3c47c9215fd0be9c6@instructure.com>

--- ui/features/collaborations/backbone/views/MemberListView.coffee
+++ ui/features/collaborations/backbone/views/MemberListView.coffee
@@ -151,5 +151,6 @@
       $.when(@currentXHR).then(@onFetch)

   getNextPage: (header) ->
-    nextPage = filter(header.split(','), (l) -> l.match(/next/))[0]
+    if header
+      nextPage = filter(header.split(','), (l) -> l.match(/next/))[0]
     return if nextPage then nextPage.match(/http[^>]+/)[0] else false


Some is not allowed

Some is not allowed

--- workers/social/lib/social/models/app.coffee
+++ workers/social/lib/social/models/app.coffee
@@ -52,7 +52,7 @@
         'fetchRelativeReviews', 'approve'
       ]
       static        : [
-        'one', 'create', 'someWithRelationship', 'updateAllSlugs', 'some'
+        'one', 'create', 'someWithRelationship', 'updateAllSlugs'
       ]

     schema          :


Tweaks

Tweaks

--- sync.coffee
+++ sync.coffee
@@ -76,8 +76,8 @@
           req = request.del(url)

       req.end (err, res) ->
-        return options.error(model, err) if err
-        return options.error(model, new Error "Ajax failed with status #{res.status} for #{method} with: #{util.inspect(res.body)}") unless res.ok
+        return options.error(err) if err
+        return options.error(new Error "Ajax failed with status #{res.status} for #{method} with: #{util.inspect(res.body)}") unless res.ok
         options.success(JSONUtils.parse(res.body))
       return


Refactor test (change variable name)

Refactor test (change variable name)

--- dist-tools/test/bundle-transform.spec.coffee
+++ dist-tools/test/bundle-transform.spec.coffee
@@ -28,7 +28,7 @@

 # Bundle helpers

-bundleFile = bundleHelpers.servicesFile
+servicesFile = bundleHelpers.servicesFile
 header = 'var AWS = require("./core"); module.exports = AWS;'
 buildBundle = (services) ->
   data = [header]
@@ -47,12 +47,12 @@
 # Assertions

 assertBundle = (services, bundle, errMsg) ->
-  runTransform transform(services, true)(bundleFile), 'data', (err, data) ->
+  runTransform transform(services, true)(servicesFile), 'data', (err, data) ->
     expect(err).toEqual(null)
     expect(data).toEqual(bundle)

 assertBundleFailed = (services, errMsg) ->
-  runTransform transform(services, true)(bundleFile), 'data', (err) ->
+  runTransform transform(services, true)(servicesFile), 'data', (err) ->
     expect(err.message).toEqual(errMsg)

 describe 'bundle transformer', ->
@@ -71,14 +71,14 @@
       expect(data).toEqual 'foo'

   it 'returns bundle if file is aws.js', ->
-    runTransform transform(bundleFile), 'data', (e, data) ->
+    runTransform transform(servicesFile), 'data', (e, data) ->
       expect(e).toEqual(null)
       expect(data).toEqual(defaultBundle)

   it 'uses SERVICES environment variable if services not initialized', ->
     process.env.SERVICES = 's3,cloudwatch'
     bundle = buildBundle s3: ['2006-03-01'], cloudwatch: ['2010-08-01']
-    runTransform transform(bundleFile), 'data', (e, data) ->
+    runTransform transform(servicesFile), 'data', (e, data) ->
       expect(e).toEqual(null)
       expect(data).toEqual(bundle)

@@ -132,6 +132,6 @@
     waitsFor -> err || tr
     runs ->
       expect(err.message).toEqual('Missing modules: invalidmodule')
-      runTransform tr(bundleFile), 'data', (e, out) ->
+      runTransform tr(servicesFile), 'data', (e, out) ->
         expect(e.message).toEqual(err.message)
         expect(out).toEqual(null)


Send updated preferences to backend

Send updated preferences to backend

--- app/assets/javascripts/backend/animals.js.coffee
+++ app/assets/javascripts/backend/animals.js.coffee
@@ -282,11 +282,24 @@

       @updatePreferences = () =>

+        #[{group: {id:301, containers:[{id:176, position: 0}]}},{group: {id:302, containers:[{id:176, position: 0}]}},{group: {id:305, containers:[{id:169, position: 0},{id: 5678, position: 1}]}}]
+        json_data = []
+
+        ko.utils.arrayForEach @groups(), (g) =>
+          curContainers = ko.utils.arrayFilter @containers(), (c) =>
+            c.group_id() == g.id
+
+          curContainers = ko.utils.arrayMap curContainers, (c) =>
+            {id: c.id, position: c.position()}
+
+          json_data.push {group: {id: g.id, containers: curContainers}}
+
+
         $.ajax '/backend/animals/update_preferences',
 #          type: 'PUT',
           type: 'GET',
           dataType: 'JSON',
-          data: ko.toJSON @groups,
+          data: ko.toJSON json_data,
           success: (res) =>
             #nothing


lightbox will use thumbnails if provided. Resolves #319

--- app/assets/javascripts/components/light-box.cjsx
+++ app/assets/javascripts/components/light-box.cjsx
@@ -84,7 +84,7 @@

               <svg className="light-box-subject" width={125} height={125} viewBox={viewBox} >
                   <SVGImage
-                    src = {@state.first.location.standard}
+                    src = {if @state.first.location.thumbnail then @state.first.location.thumbnail else @state.first.location.standard}
                     width = {100}
                     height = {100}
                   />
@@ -95,7 +95,7 @@
                 {second.order}
                 <svg className="light-box-subject" width={125} height={125} viewBox={viewBox} >
                     <SVGImage
-                      src = {second.location.standard}
+                      src = {if second.location.thumbnail then second.location.thumbnail else second.location.standard}
                       width = {100}
                       height = {100}
                     />
@@ -108,7 +108,7 @@
                 {third.order}
                 <svg className="light-box-subject" width={125} height={125} viewBox={viewBox} >
                     <SVGImage
-                      src = {third.location.standard}
+                      src = {if third.location.thumbnail then third.location.thumbnail else third.location.standard}
                       width = {100}
                       height = {100}
                     />


Add Register Wiki and BO to atom projects

--- atom/atom.symlink/projects.cson
+++ atom/atom.symlink/projects.cson
@@ -18,3 +18,13 @@
   'paths': [
     '/Users/mkelly/Development/SK/SKBackofficeSDK'
   ]
+'SK Register Wiki':
+  'title': 'SK Register Wiki'
+  'paths': [
+    '/Users/mkelly/Dropbox/Development/Git/ipad-register.wiki'
+  ]
+'SK Backoffice':
+  'title': 'SK Backoffice'
+  'paths': [
+    '/Users/mkelly/Development/SK/backoffice'
+  ]


allow overriding range when unit=%

allow overriding range when unit=%

--- app/assets/javascripts/monitoring/util.js.coffee
+++ app/assets/javascripts/monitoring/util.js.coffee
@@ -17,7 +17,7 @@
   if !vals || vals.length == 0
     return

-  # draw footer
+  # draw footer: "Last Value: 20.32%" or "Last Value: 3541 GB"
   footer = $(s + " .footer")
   unit = metric.get("unit")
   unit_label = ""
@@ -43,10 +43,11 @@
   opts.width = gc.width()
   opts.height = gc.height()

+  # Fix the y-axis value range
   opts.includeZero = true # always incldue zero on y-axis
-
-  if unit == "%" || metric.get("range") == "1..100"
-    # set range if known
+  range = metric.get("range")
+  if unit == "%" && (!range? || range == "0..100")
+    # use percentage range, unless overriden in range var
     opts.valueRange = [ 0, 100 ]
     opts.yRangePad = 1


OX-484 repaired sorting, use jquery extend (copy)

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -598,7 +598,7 @@
     return null

-  getTrees : -> @trees
+  getTrees : -> $.extend(true, [], @trees).sort(@compareNames)

   # returns a list of nodes that are connected to the parent


fix format money in application.coffee

fix format money in application.coffee

Fix bug when adding values with comma as '1,000.00' replacing the comma for nothing as regular integer values.

--- app/assets/javascripts/shoppe/application.coffee
+++ app/assets/javascripts/shoppe/application.coffee
@@ -72,7 +72,7 @@
 # Format money values to 2 decimal places
 #
 window.formatMoneyField = ->
-  value = $(this).val()
+  value = $(this).val().replace /,/, ""
   $(this).val(parseFloat(value).toFixed(2)) if value.length

 #
@@ -116,4 +116,4 @@
 Mousetrap.bind 'escape', ->
   Nifty.Dialog.closeTopDialog()
   false
-  +


CamelCase variables.

CamelCase variables.

--- app/javascripts/modeling.coffee
+++ app/javascripts/modeling.coffee
@@ -16,12 +16,12 @@
             {
               label: 'FXL Ktrans'
               color: 'BurlyWood'
-              accessor: (session) -> session.modeling.fxlKTrans
+              accessor: (session) -> session.modeling.fxlKTrans.average
             }
             {
               label: 'FXR Ktrans'
               color: 'OliveDrab'
-              accessor: (session) -> session.modeling.fxrKTrans
+              accessor: (session) -> session.modeling.fxrKTrans.average
             }
           ]
       ve:
@@ -31,7 +31,7 @@
             {
               label: 'v_e'
               color: 'MediumSeaGreen'
-              accessor: (session) -> session.modeling.v_e
+              accessor: (session) -> session.modeling.vE.average
             }
           ]
       taui:
@@ -41,7 +41,7 @@
             {
               label: 'tau_i'
               color: 'PaleVioletRed'
-              accessor: (session) -> session.modeling.tau_i
+              accessor: (session) -> session.modeling.tauI.average
             }
           ]

@@ -86,15 +86,15 @@
             # @param current the current value
             # @param previous the previous value
             # @returns the percent change
-            percent_change = (current, previous) ->
+            percentChange = (current, previous) ->
               (current - previous)/previous * 100

             # Return an object with the change properties.
-            deltaKTrans_pct_change: percent_change(current.deltaKTrans, previous.deltaKTrans)
-            fxlKTrans_pct_change: percent_change(current.fxlKTrans, previous.fxlKTrans)
-            fxrKTrans_pct_change: percent_change(current.fxrKTrans, previous.fxrKTrans)
-            v_e_pct_change: percent_change(current.v_e, previous.v_e)
-            tau_i_pct_change: percent_change(current.tau_i, previous.tau_i)
+            deltaKTransPctChange: percentChange(current.deltaKTrans.average, previous.deltaKTrans.average)
+            fxlKTransPctChange: percentChange(current.fxlKTrans.average, previous.fxlKTrans.average)
+            fxrKTransPctChange: percentChange(current.fxrKTrans.average, previous.fxrKTrans.average)
+            vEPctChange: percentChange(current.vE.average, previous.vE.average)
+            tauIPctChange: percentChange(current.tauI.average, previous.tauI.average)

           # If this is not the first session's modeling object,
           # then extend the object with the percent change properties,


Fix a javascript error

Fix a javascript error

--- app/coffeescripts/bundles/course_settings.coffee
+++ app/coffeescripts/bundles/course_settings.coffee
@@ -37,9 +37,10 @@

     parseTree = ($obj, arr) ->
       for content in arr
-        $obj.append "
#{content.label}
"
-        if content.children.length
-          $obj.find('li').append( parseTree($("

"), content.children) )
+        $li = $("
#{content.label}
")
+        if content.children.length > 0
+          $li.append( parseTree($("

"), content.children) )
+        $obj.append $li
       return $obj

     $("#pick_up_from_accounts").bind 'click', (e) ->


remove unused variable

remove unused variable

--- editor.coffee
+++ editor.coffee
@@ -163,7 +163,6 @@

 # Add an input cell above the last input cell
 run_in_info_context = (current_cell, code) ->
-  last = contexts[contexts.length - 1]
   cell = create_input_cell code
   if current_cell?
     current_cell.$el.before cell.$el


Destroy new classification objects after they're saved

--- app/pages/project/classify.cjsx
+++ app/pages/project/classify.cjsx
@@ -139,7 +139,7 @@
     console?.info 'Completed classification', @state.classification
     @state.classification.save().then (classification) =>
       console?.log 'Saved classification', classification.id
-      classification.uncache()
+      classification.destroy()

   loadAnotherSubject: ->
     @getCurrentWorkflowID(@props).then (workflowID) =>


Set the top/right offsets based on workflow position

Since Cilantro usually resides within another container, we cannot
simply use fixed top and right offsets. Instead, this commit takes into
account the position of the results workflow and sets the fixed offsets
based off the results workflow initially. Additionally, when the window
resizes, the fixed offsets of the context panel are recalculated.

--- src/coffee/cilantro/ui/workflows/results.coffee
+++ src/coffee/cilantro/ui/workflows/results.coffee
@@ -82,6 +82,7 @@

         initialize: ->
             $(document).on 'scroll', @onPageScroll
+            $(window).resize @onWindowResize

             @monitors = {}

@@ -93,6 +94,18 @@
             @areFiltersManuallyHidden = false
             # Flag indicating the current visibility of the context panel
             @areFiltersHidden = false
+
+        onWindowResize: =>
+            @updateContextPanelOffsets()
+
+        updateContextPanelOffsets: =>
+            # Find the bounds of the results workflow to properly fix the
+            # position of the context/filter panel.
+            @workflowTopOffset = @$el.position().top
+            @workflowRightOffset = window.innerWidth - (@$el.position().left + @$el.width())
+
+            @ui.contextContainer.css('top', @workflowTopOffset)
+            @ui.contextContainer.css('right', @workflowRightOffset)

         toggleContextPanelButtonClicked: =>
             if @areFiltersHidden
@@ -372,13 +385,16 @@

             # If there is already something fixed to the top, record the height
             # of it so we can account for it in our scroll calculations later.
-            if not @topNavbarHeight
+            if not @topNavbarHeight?
                 topElement = $('.navbar-fixed-top')
                 if topElement.length
                     @topNavbarHeight = topElement.height()
                 else
                     @topNavbarHeight = 0

+            if not @workflowTopOffset?
+                @updateContextPanelOffsets()
+
         showExportOptions: ->
             $('.export-options-modal .alert-block').hide()
             @ui.exportOptions.modal('show')


Implement minimum split size

Implement minimum split size

--- src/Control.coffee
+++ src/Control.coffee
@@ -52,8 +52,9 @@
   system = engine.s 'control'
   angle = Math.atan2 system.y, system.x
   for entity in system.entities
-    newEntity = engine.e engine.aa 'blobSplit', entity, null, angle
-    newEntity.c 'control', {}
+    if entity.c('blob').weight > 100
+      newEntity = engine.e engine.aa 'blobSplit', entity, null, angle
+      newEntity.c 'control', {}

 module.exports =
   component: ControlComponent


fixed typo with critical

fixed typo with critical

--- addon/utils/importance.coffee
+++ addon/utils/importance.coffee
@@ -2,7 +2,7 @@
 Importance =
   DivineMandate: -25
   LifeOrDeath: -20
-  MissionCritial: -15
+  MissionCritical: -15
   HeavyConsequences: -10
   VeryImportant: -5
   Important: 0


Added more checks to validate tests

Added more checks to validate tests

--- test.coffee
+++ test.coffee
@@ -120,6 +120,7 @@
       it 'runs a supplied validation function on the data', (done) ->
         validationRun = no
         validate = (opData, snapshot, callback) ->
+          assert.deepEqual snapshot, {v:1, data:'', type:otTypes.text.uri}
           validationRun = yes
           callback()

@@ -128,7 +129,10 @@
           done()

       it 'does not submit if validation fails', (done) -> @create =>
-        validate = (opData, snapshot, callback) -> callback 'no you!'
+        validate = (opData, snapshot, callback) ->
+          assert.deepEqual opData.op, ['hi']
+          callback 'no you!'
+
         @collection.submit @docName, {v:1, op:['hi'], validate}, (err) =>
           assert.equal err, 'no you!'


Added failing test case for the `yield` precedence

--- test/generators.coffee
+++ test/generators.coffee
@@ -79,3 +79,25 @@

   y = x.next 1
   ok y.value is 3 and y.done is true
+
+test "symbolic operators has precedence over the `yield`", ->
+
+  symbolic   = '+ - * / << >> & | || && ** ^ \\ or and'.split ' '
+  compound   = ("#{op}=" for op in symbolic)
+  relations  = '< > == != <= >= is isnt'.split ' '
+
+  operators  = []
+  operators.push symbolic...
+  operators.push compound...
+  operators.push relations...
+
+  allOf = (gen) -> ref.value until (ref = gen.next()).done
+
+  values = [0, 1, 2, 3]
+
+  for op in operators
+    generator = CoffeeScript.eval "(arr) -> yield i #{op} 2 for i in arr"
+    transform = CoffeeScript.eval "(i) -> i #{op} 2"
+    expected = values.map transform
+    actual = allOf generator values
+    arrayEq actual, expected


Fix render order

Fix render order

--- app/views/home-page-view.coffee
+++ app/views/home-page-view.coffee
@@ -172,3 +172,6 @@

     for entity in lvl.entityObjects
       entity.render(@ctx, sx, sy)
+
+    # always draw the player on top of everything again
+    lvl.player.render(@ctx, sx, sy)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -282,17 +282,17 @@
         path.transition()
             .duration(1500)
             #.attrTween("d", pieTween)
-            .attrTween("d", (d,i,a) ->
-                x = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
+            .attrTween("d", (d,i) ->
+                i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
                 return (t) ->
-                    b = x(t)
+                    b = i(t)
                     return arc(b)
             )

         path.exit()
             .transition()
             .duration(300)
-            .attrTween("d", (d,i,a) ->
+            .attrTween("d", (d,i) ->
                 i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
                 return (t) ->
                     b = i(t)


Agent pie: edit path exit tween function

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -162,15 +162,15 @@
             .attrTween('d', (d,i) ->
                 s0 = 2 * Math.PI
                 e0 = 2 * Math.PI
-                x = d3.interpolate({
+                exitInterpolater = d3.interpolate({
                     startAngle: d.startAngle
                     endAngle: d.endAngle
-                },
+                },{
                     startAngle: s0
-                    endAngle: e0)
-                (t) ->
-                    b = x(t)
-                    return arc b
+                    endAngle: e0
+                })
+                return (t) ->
+                    return arc(exitInterpolater(t))
             )
             .remove()


 Merge branch 'master.seperateLog' into 'master'

Master.Seperate Log

--- workers/log/lib/log/models/errorlog.coffee
+++ workers/log/lib/log/models/errorlog.coffee
@@ -7,7 +7,7 @@
   signature
 } = require 'bongo'

-uniqInt = 4
+uniqInt = 15

 module.exports = class JErrorLog extends ElasticSearch
   @share()


JVM: Cleanup

JVM: Cleanup

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -217,32 +217,6 @@
         then console.error err  unless err.code is 11000
         else updateRelationship domainObj

-  @fixUserDomains = permit 'change bundle',
-    success: (client, callback)->
-
-      unless client.context.group is "koding"
-        return callback new KodingError "You are not Koding admin."
-
-      JDomain = require './domain'
-      JUser   = require './user'
-
-      JVM.each {}, {}, (err, vm) =>
-        return callback err  if err
-        return callback null, null  unless vm
-        {nickname, groupSlug, uid, type} = @parseAlias vm.hostnameAlias
-        hostnameAliases = JVM.createAliases {
-          nickname, type, uid, groupSlug
-        }
-        [vmUser] = vm.users.filter (u) -> u.owner is yes
-        if vmUser?
-          JUser.one { _id: vmUser.id }, (err, user) =>
-            if not err and user
-              user.fetchAccount 'koding', (err, account) =>
-                console.log "WORKING ON VM FOR #{nickname} - #{hostnameAliases[0]}"
-                if not err and account
-                  @ensureDomainSettings {account, vm, type, nickname, groupSlug}
-                  @createDomains account, hostnameAliases, hostnameAliases[0]
-
   @ensureDomainSettings = ({account, vm, type, nickname, groupSlug, stack})->
     domain = 'kd.io'
     if type in ['user', 'expensed']


vm: implement JVM.fetchVmsByContext()

vm: implement JVM.fetchVmsByContext()

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -1,31 +1,75 @@
 {Model} = require 'bongo'

 module.exports = class JVM extends Model
+
+  {permit} = require './group/permissionset'

   @share()

   @trait __dirname, '../traits/protected'

   @set
-    softDelete        : yes
-    permissions       :
-      'sudoer'        : []
-    schema            :
-      ip              :
-        type          : String
-        default       : -> null
-      ldapPassword    :
-        type          : String
-        default       : -> null
-      name            : String
-      users           : Array
-      groups          : Array
-      isEnabled       :
-        type          : Boolean
-        default       : yes
-      shouldDelete    :
-        type          : Boolean
-        default       : no
+    softDelete          : yes
+    permissions         :
+      'sudoer'          : []
+      'list all vms'    : ['member','moderator']
+      'list default vm' : ['member','moderator']
+    sharedMethods       :
+      static            : ['fetchVmsByContext']
+      instance          : []
+    schema              :
+      ip                :
+        type            : String
+        default         : -> null
+      ldapPassword      :
+        type            : String
+        default         : -> null
+      name              : String
+      users             : Array
+      groups            : Array
+      isEnabled         :
+        type            : Boolean
+        default         : yes
+      shouldDelete      :
+        type            : Boolean
+        default         : no
+
+  @fetchVmsByContext = permit 'list all vms',
+    success: (client, options, callback) ->
+      [callback, options] = [options, callback]  unless callback
+
+      options ?= {}
+      options.limit = Math.min options.limit ? 10, 10
+
+      JGroup = require './group'
+
+      {context, connection:{delegate}} = client
+
+      slug = context.group ? 'koding'
+
+      delegate.fetchUser (err, user) ->
+        return callback err  if err
+
+        JGroup.one {slug}, (err, group) ->
+          return callback err  if err
+
+          selector =
+            groups  : { $elemMatch: id: group.getId() }
+            users   : { $elemMatch: id: user.getId() }
+
+          JVM.someData selector, { name: 1 }, options, (err, cursor)->
+            return callback err  if err
+
+            cursor.toArray (err, arr)->
+              return callback err  if err
+              callback null, arr.map (vm)-> vm.name
+
+    # TODO: let's implement something like this:
+    # failure: (client, callback) ->
+    #   @fetchDefaultVmByContext client, (err, vm)->
+    #     return callback err  if err
+    #     callback null, [vm]
+

   do ->


fixes to gruntfile

fixes to gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -13,7 +13,7 @@
       app  : ".tmp/app.coffee"
       test : ".tmp/test.coffee"

-    concat :
+    concat :
       app :
         src  : "<%=src.app%>"
         dest : "<%=concat_src.app%>"
@@ -29,12 +29,12 @@
         files:
           "test/test.js" : "<%=concat_src.test%>"

-    bgShell :
-      mocha :
+    bgShell :
+      mocha :
         cmd : 'mocha'

-    watch :
-      main :
+    watch :
+      main :
         files : ["<%=src.app%>","<%=src.test%>"]
         tasks : ["default"]


Update gruntfile.coffee

Update gruntfile.coffee

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -15,6 +15,7 @@
       compile:
         options:
           pretty: false
+          basedir: __dirname
         files:
           'test/index.html': 'test/index.jade'

@@ -22,6 +23,9 @@
       server:
         options:
           port: '3000'
+          base: './'
+          open:
+            target: 'http://localhost:<%= connect.server.options.port %>/test/'

     watch:
       options:


Patching a Foundation bug w/ tests

Patching a Foundation bug w/ tests

--- app/assets/javascripts/darkswarm/all.js.coffee
+++ app/assets/javascripts/darkswarm/all.js.coffee
@@ -13,6 +13,7 @@

 $ ->
   # Hacky fix for issue - http://foundation.zurb.com/forum/posts/2112-foundation-5100-syntax-error-in-js
-  Foundation.set_namespace = function() {};
+  Foundation.set_namespace = ->
+    null
   $(document).foundation()
   $(document).foundation({reveal: {animation: 'fade'}})


Use querySelectorAll in getLineElement

Use querySelectorAll in getLineElement

querySelector only returns a single element, which won't work.

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -214,7 +214,7 @@
     @getLineElement(row).remove()

   getLineElement: (row) ->
-    @lines.find("div.line:eq(#{row})")
+    $(@lines[0].querySelectorAll("div.line")[row])

   toggleSoftWrap: ->
     @setSoftWrap(not @softWrap)


added comment for todo

added comment for todo

--- server/components/ddurl/index.coffee
+++ server/components/ddurl/index.coffee
@@ -7,6 +7,9 @@
   ddurl = {}
   ddurl.shorten = (longUrl, callback) ->
     return callback 400, new Error 'Bad Request'  unless validUrl.isUri longUrl
+
+    # Todo:
+    # Check same longurl.

     Url.create
       'longUrl': longUrl


Fix faves on dynamically loaded comments

Fixes #43

--- app/assets/javascripts/redcursor_app.js.coffee
+++ app/assets/javascripts/redcursor_app.js.coffee
@@ -62,7 +62,7 @@
   #   a.minLength = 1

-  $(".fave").click ->
+  $(".fave").live 'click', ->
     self = $(this)
     post = self.find(".favorite").attr("data-post-id")
     $.ajax(url:"/posts/"+post+"/fave", dataType: "json", type: "post").done (msg) ->
@@ -74,17 +74,6 @@
         self.find(".off").show()
     return false

-  $("#title").click ->
-    if $(this).hasClass("active")
-      $(".toolbar").removeClass("active")
-      $(".title-toolbar").removeClass("active")
-      $(this).removeClass("active")
-    else
-      $(".toolbar").addClass("active")
-      $(".title-toolbar").addClass("active")
-      $(this).addClass("active")
-      $(".toolbar input.search-field").focus() if !mobile
-
   refreshPosts = () ->
     last_id = $('.post').last().attr("id").replace(/^post_/, ''.replace(/^post_/, ''))
     ourl = document.location.href.replace(/#.*$/, '')


table sorting, iterate trough array instead of object bub in IE8

--- app/assets/javascripts/backbone_js/ladas_table_sorting.js.coffee
+++ app/assets/javascripts/backbone_js/ladas_table_sorting.js.coffee
@@ -49,7 +49,7 @@

     new_order_by_val = ""
-    for index,element of order_by_array
+    for element in order_by_array
       if (new_order_by_val != "")
         new_order_by_val += ","


saveState and reset button now work

saveState and reset button now work

--- app/assets/javascripts/effective_datatables/initialize.js.coffee
+++ app/assets/javascripts/effective_datatables/initialize.js.coffee
@@ -10,7 +10,7 @@
         autoWidth: false
         deferRender: true
         order: datatable.data('default-order')
-        ajax: datatable.data('source')
+        sAjaxSource: datatable.data('source')
         pagingType: 'simple_numbers'
         lengthMenu: [[10, 25, 50, 100, 250, 1000, -1], [10, 25, 50, 100, 250, 1000, 'All']]
         fnServerParams: (aoData, a, b) ->
@@ -61,8 +61,8 @@

 $ -> initializeDataTables()
 $(document).on 'page:change', -> initializeDataTables()
-resetTable = (event) ->
+resetTable = (event) ->
   $('table[data-effective-datatables-table]').DataTable().state.clear()
   window.location.reload()
-
+
 $(document).on 'click', '[data-effective-datatables-reset-filters]', resetTable


Remove logo animation.

Remove logo animation.

--- app/assets/javascripts/itsf/backend/application/itsf-logo.coffee
+++ app/assets/javascripts/itsf/backend/application/itsf-logo.coffee
@@ -1,24 +0,0 @@
-rotation = ->
-  $('#navigation-logo > .itsf-logo-38').rotate
-    angle: 0
-    animateTo: 360
-    callback: rotation
-  return
-
-$ ->
-  $('a').click ->
-    rotation()
-
-  $('a *').click ->
-    rotation()
-
-  $('input[type="submit"]').click ->
-    rotation()
-
-  $('#navigation-logo > .itsf-logo-38').rotate bind:
-    mouseover: ->
-      $(this).rotate animateTo: 180
-      return
-    mouseout: ->
-      $(this).rotate animateTo: 0
-      return


Fix coffeelint errors

Fix coffeelint errors

--- api/resources/schedule.coffee
+++ api/resources/schedule.coffee
@@ -134,12 +134,16 @@
                     id: section.direction

                   if not directionDetail
-                    errors.push "Invalid #{name} section direction: #{section.direction}"
+                    errors.push "Invalid #{name} section " +
+                      "direction: #{section.direction}"

                   else
                     stops = section.stops
-                    if not stops or not util.isArray(stops) or not stops.length > 0
-                      errors.push "#{name} section must contain at least one stop"
+                    if not stops or
+                    not util.isArray(stops) or
+                    not stops.length > 0
+                      errors.push "#{name} section must contain " +
+                        "at least one stop"

                     else
                       for stop in stops


Removed trailing semicolon (unnecessary in coffeescript).

--- app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
+++ app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
@@ -92,7 +92,7 @@

     # There seems to be a bug in Bootstrap in which the backdrop can remain. See
     # http://competa.com/blog/2013/11/how-to-stop-twitter-bootstrap-modal-dialogs-breaking-on-browser-history-navigation-in-angularjs/
-    $('.modal-backdrop').remove();
+    $('.modal-backdrop').remove()

     subscriptionSvc.subscribe $scope.subscription_url
     $scope.subscription_url = null


Correct express depreciation

Correct express depreciation

--- server/controllers/konnectors.coffee
+++ server/controllers/konnectors.coffee
@@ -16,7 +16,7 @@
             if err
                 next err
             else if not konnector?
-                res.send 404
+                res.sendStatus 404
             else
                 konnector.injectEncryptedFields()


Update index.coffee

Update index.coffee

--- examples/hibiki/index.coffee
+++ examples/hibiki/index.coffee
@@ -18,9 +18,9 @@
   ]

   # Create a sprite from the sprite sheet, add 'idle'
-  # animation from first row of the spritesheet, and
-  # add 'walk' from second row, both at 24 frames
-  # per second.
+  # animation from first row of the spritesheet at 18
+  # frames per second, and add 'walk' from second row,
+  # at 24 frames per second.
   sprite = new Sprite spriteSheet
   sprite.addAnimation {id: 'idle', row: 0, fps: 18}
   sprite.addAnimation {id: 'walk', row: 1, fps: 24}


Input coffee to class fix

Input coffee to class fix

--- app/js/canvas/Input.coffee
+++ app/js/canvas/Input.coffee
@@ -23,10 +23,12 @@
         return

       return
+
     orientationChange: (callback) ->
       # Callback returns if orientation of screen is changed
       _orientationChange callback
       return
+
     mobile: (callback) ->
       touchendCoords = {}
       # Callback returns when screen is touched and when screen touch ends
@@ -44,6 +46,7 @@
         callback touchendCoords, false
         return
       return
+
     mouse_action: (callback) ->
       # Callback returns on mouse down
       @canvas.addEventListener 'mousedown', ((event) ->
@@ -52,6 +55,7 @@
         return
       ), false
       return
+
     mouse_move: (callback) ->
       # Callback returns when mouse is moved
       @canvas.addEventListener 'mousemove', ((event) ->
@@ -71,14 +75,6 @@
     callback keyCode, pressed, e
     return

-  ###*
-  * Used for getting touch screen coordinates
-  * @param {Event} Event
-  * @param {Function} Callback function
-  * @param {Boolean} If the screen is being touched
-  * @return {Function} callback({Object} X & Y touch coordinates, {Boolean} pressed)
-  ###
-
   _mobileInput = (e, callback, pressed) ->
     coords = {}
     if pressed
@@ -87,13 +83,6 @@
     callback coords, pressed
     return

-  ###*
-  * Used for getting mouse click coordinates
-  * @param {Event} Event
-  * @param {Function} Callback function
-  * @return {Function} callback({Object} X & Y mouse coordinates)
-  ###
-
   _mouseInput = (e, callback) ->
     coords = {}
     coords.x = e.pageX - (canvas.offsetLeft)
@@ -101,15 +90,9 @@
     callback coords
     return

-  ###*
-  * Performs the callback function when screen orientation change is detected
-  * @param {Function} Callback function
-  * @return {Function} callback()
-  ###
-
   _orientationChange = (callback) ->
     window.addEventListener 'orientationchange', (->
       callback()
       return
     ), false
-    return+    return


update: Total hours

update: Total hours

--- app/assets/javascripts/staffplans/show/views/user_view.js.coffee
+++ app/assets/javascripts/staffplans/show/views/user_view.js.coffee
@@ -88,15 +88,20 @@
     ww = _.groupBy _.compact(_.flatten(ww)), (w) ->
       "#{w.get('year')}-#{w.get('cweek')}"

+    # Total hours for each week
+    _.each ww, (hours, key) ->
+      ww[key] = _.reduce hours, (m, o) ->
+        m.actual += (parseInt(o.get('actual_hours'), 10) or 0)
+        m.estimated += (parseInt(o.get('estimated_hours'), 10) or 0)
+        m
+      , {actual: 0, estimated: 0}
+
     # Draw
     weekHourCounters = @$( '.week-hour-counter li' )
     _.each dateRange, (date, idx) ->
       # Map week to

-      whichHours = if date.weekHasPassed then 'actual_hours' else 'estimated_hours'
       li = weekHourCounters.eq(idx)
-      total = _.reduce ww["#{date.year}-#{date.mweek}"], (m, o) ->
-        m + (parseInt(o.get(whichHours), 10) or 0)
-      , 0
+      total = ww["#{date.year}-#{date.mweek}"][if date.weekHasPassed then 'actual' else 'estimated']
       li
         .height(total)
         .html("" + total + "")


+ Overly lengtly comment changed.

+ Overly lengtly comment changed.

--- app/lib/world/world.coffee
+++ app/lib/world/world.coffee
@@ -35,7 +35,7 @@
     @frames = [new WorldFrame(@, 0)]

   getSeed: ->
-    #Packs all methods in all thangs in one big array, then sorts and joins them. Then returns a hash value of the result.
+    #Generates a seed that depends on the usercode
     @hashString((methods for thangID, methods of @userCodeMap).reduce(((ret, methods) ->
       ret.push method for methodID, method of methods
     ), []).sort().join())


Changement limit log

Changement limit log

--- app/scripts/controllers/LoggedRfLogCtrl.coffee
+++ app/scripts/controllers/LoggedRfLogCtrl.coffee
@@ -15,7 +15,7 @@
     $scope.query =
       filter: '',
       order: '-date',
-      limit: 10,
+      limit: 50,
       page: 1

     success = (logs) ->


remove whitespace from adapter

remove whitespace from adapter

--- src/adapter.coffee
+++ src/adapter.coffee
@@ -21,7 +21,6 @@
   # Sort the data of an adapter by a given column. As soon as the data is sorted
   # an ready event has to be triggered on the Adapter
   sort: (column) ->
-
     $(this).trigger 'ready'

   # Paginate the data to a given page


Always open support site in new tab

Always open support site in new tab

--- src/concept-coach/error-notification.cjsx
+++ src/concept-coach/error-notification.cjsx
@@ -47,7 +47,7 @@

Server error encountered

           An unexpected error has occured.  Please
-          visit <a target="zendesk"
+          visit  our support site  so we can help to diagnose and correct the issue.


_getVisiblePosition (used by pageX and pageY of views/controls) doesn't add double scroll position anymore, as the root of the problem (the event x/y) works now.

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -218,7 +218,7 @@
   _getVisibleLeftPosition: (_id)->
     _elem = @_elements[_id]
     x = 0
-    while _elem != document
+    while _elem != document.body
       x += _elem.offsetLeft - _elem.scrollLeft
       _elem = _elem.parentNode
     x
@@ -229,13 +229,13 @@
   _getVisibleTopPosition: (_id)->
     _elem = @_elements[_id]
     y = 0
-    while _elem != document
+    while _elem != document.body
       y += _elem.offsetTop - _elem.scrollTop
       _elem = _elem.parentNode
     y

   ###
-  Returns the visible position of the element as a [ left, top ] tuble
+  Returns the visible position of the element as a [ left, top ] tuple
   ###
   getVisiblePosition: (_id)->
     [ @_getVisibleLeftPosition(_id), @_getVisibleTopPosition(_id) ]


Fix job.fail() with compound players.

Fix job.fail() with compound players.

--- application/chrome/content/wesabe/download/CompoundPlayer.coffee
+++ application/chrome/content/wesabe/download/CompoundPlayer.coffee
@@ -53,7 +53,7 @@
       wesabe.info "Could not complete job with ", @currentPlayer, " (", status, " ", result, ")"

       # if we can't, just report the last failure
-      startNextPlayer(=> job_fail_original(status, result))
+      startNextPlayer(=> job_fail_original.call(@job, status, result))

     @job.__defineGetter__ 'page', =>
       @currentPlayer.page


adds python 2.7.6 to doc'd versions

adds python 2.7.6 to doc'd versions


TrollMode: Update CheckFlags method to check account.isExempt when

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -284,6 +284,8 @@
   isMyPost: (post) -> post.account._id is KD.whoami().getId()

   checkFlag:(flagToCheck, account = KD.whoami())->
+    # exempt information is removed from globalFlags
+    return account.isExempt if flagToCheck is 'exempt'
     if account.globalFlags
       if 'string' is typeof flagToCheck
         return flagToCheck in account.globalFlags


pubnub: add toggling to client side

pubnub: add toggling to client side

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -269,6 +269,14 @@
       return callback {message: "Account not found", name: "NotFound"} unless owner
       callback null, owner

+  togglePubnub: ->
+    isPubnubEnabled = localStorage.isPubnubEnabled or KD.config.pubnub.enabled
+    localStorage.isPubnubEnabled = !isPubnubEnabled
+    location.reload()
+
+  isPubnubEnabled: ->
+    localStorage.isPubnubEnabled or KD.config.pubnub.enabled
+

 Object.defineProperty KD, "defaultSlug",
   get:->


Update fr.coffee

Update fr.coffee

"Ignorer 'toutes" qui convenait pour les infos de mises à jour est remplacé par "Tout ignorer" car l'option peut aussi concerner les messages notifiés.

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -17,7 +17,7 @@
     "memory consumption"        : "Utilisation mémoire"
     "disk consumption"          : "Utilisation disque"
     "you have no notifications" : "Vous n'avez aucune notification"
-    "dismiss all"               : "Ignorer toutes"
+    "dismiss all"               : "Tout ignorer"
     "add application"         : "Ajouter l'application ?"
     "install"                   : "Installer"
     "your app"                 : "Votre application !"


Updates spec

Updates spec

--- test/theme.spec.coffee
+++ test/theme.spec.coffee
@@ -1,7 +1,7 @@
 fs = require 'fs'
 sass = require 'node-sass'

-describe 'syntaxhighlighter-theme', ->
+describe 'theme-base', ->
   it 'compiles ok', (done) ->
     fs.readFile "#{__dirname}/../scss/theme.scss", 'utf8', (err, content) ->
       css = sass.renderSync data: content


Quick jQueryMobilization of the nearby cam list.

--- prod/src/coffee-script/node/server.coffee
+++ prod/src/coffee-script/node/server.coffee
@@ -30,7 +30,6 @@

 # Socket.io connections
 at connection: ->
-    send 'message', text: 'Hello!'

 # Default client-side code to include.
 client ->
@@ -51,15 +50,12 @@
                 $('div[data-role="content"]')
                     .append $('<p>').text "#{msg.message.text}"
             if msg.currentPosition
-                $('div[data-role="content"]')
-                    .append $('<p>').text """It looks like you're at
+                $('p#stats').text """It looks like you're at
 (#{msg.currentPosition.position.coords.latitude}, #{msg.currentPosition.position.coords.longitude}), give or take #{msg.currentPosition.position.coords.accuracy} feet and that there's at least #{msg.currentPosition.cameras.length} cameras nearby."""
-                list = $('<ol>')
+
                 for camera in msg.currentPosition.cameras
-                    list.append $('<li>')
-                        .append($('<h3>').text camera.name)
-                        .append($('<img>').attr 'src', camera.url)
-                $('div[data-role="content"]').append list
+                    $('ul#cam-list').append($('script#cam-item').tmpl(camera))
+                $('ul#cam-list').listview 'refresh'

         socket.connect()

@@ -118,4 +114,16 @@
                 div 'data-role': 'header', ->
                     h1 'traPHIcam.com'
                 div 'data-role': 'content', ->
-                    @content
+                    p id: 'stats'
+                    ul
+                        id: 'cam-list'
+                        'data-role': 'listview'
+                        class: 'ui-listview'
+        # Camera list item jQueryMobile template.
+        script id: 'cam-item', type: 'text/x-jquery-tmpl', ->
+            li class: 'ui-li-has-thumb ui-btn ui-btn-icon-right ui-li ui-btn-down-c ui-btn-up-c cam-li', 'data-theme': 'c', ->
+                div class: 'ui-btn-inner ui-li', ->
+                    div class: 'ui-btn-text', ->
+                        a class: 'ui-link-inherit', ->
+                            img class: 'ui-li-thumb', src: '${url}', alt: 'M.I.A.'
+                            h3 class: 'ui-li-heading', '${name}'


Routes: Remove comment out codes.

Routes: Remove comment out codes.

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -9,84 +9,10 @@
 dataProvider = require 'app/userenvironmentdataprovider'

-# loadWorkspace = (workspace, options) ->
-
-#   {machine, machineLabel, username} = options
-
-#   username or= nick()
-#   machine  or= getMachine machineLabel, username
-
-#   loadIDE { machine, workspace, username }
-
-
-# findWorkspace = (options, callback) ->
-
-#   {machineLabel, workspaceSlug, username} = options
-
-#   kallback = (workspaces) ->
-
-#     username ?= nick()
-
-#     for machine in globals.userMachines
-#       unless machine instanceof Machine
-#         machine = new Machine machine: remote.revive machine
-
-#       sameLabel = (machine.slug or machine.label) is machineLabel
-#       sameOwner = machine.getOwner() is username
-
-#       if sameLabel and sameOwner
-#         machineUId = machine.uid
-#         break
-
-#     for workspace in workspaces
-#       continue  unless workspace.machineUId is machineUId
-#       continue  unless workspace.slug is workspaceSlug
-
-#       return callback workspace
-
-#     return callback null
-
-#   if username
-#   then filterWorkspacesByUsername username, kallback
-#   else filterOwnWorkspaces kallback
-
-
-# filterWorkspacesByUsername = (username, callback) ->
-
-#   remote.cacheable username, (err, [account]) ->
-#     if err
-#       console.error err
-#       callback []
-
-#     originId = account.getId()
-
-#     callback globals.userWorkspaces.filter (workspace) ->
-#       originId is workspace.originId
-
-
-# filterOwnWorkspaces = (callback) ->
-
-#   callback globals.userWorkspaces.filter (workspace) ->
-#     workspace.originId is whoami()._id
-
-
 selectWorkspaceOnSidebar = (data) ->

   mainView = kd.getSingleton 'mainView'
   mainView.activitySidebar.selectWorkspace data
-
-
-# getMachine = (label, username = nick()) ->
-
-#   for m in globals.userMachines
-
-#     unless m instanceof Machine
-#       m = new Machine machine: remote.revive m
-
-#     sameUser  = m.getOwner() is username
-#     sameLabel = (m.slug or m.label) is label
-
-#     return m  if sameLabel and sameUser

 getLatestWorkspace = ->
@@ -109,14 +35,6 @@
 checkWorkspace = (machineLabel, workspaceSlug, channelId) ->

   return dataProvider.validateCollaborationWorkspace machineLabel, workspaceSlug, channelId
-
-  # for workspace in globals.userWorkspaces
-  #   sameLabel = workspace.machineLabel is machineLabel
-  #   sameSlug = workspace.slug is workspaceSlug
-  #   sameChannelId = not channelId or workspace.channelId is channelId
-
-  #   if sameLabel and sameSlug and sameChannelId
-  #     return workspace

 loadIDE = (data) ->
@@ -174,18 +92,6 @@
     router.handleRoute "/IDE/koding-vm-0/my-workspace"

-  # router = kd.getSingleton 'router'
-
-  # for machine in globals.userMachines when machine.isMine()
-  #   return routeToMachineWorkspace machine
-
-  # kd.singletons.computeController.fetchMachines (err, machines) ->
-
-  #   if err or not machines.length
-  #   then router.handleRoute "/IDE/koding-vm-0/my-workspace"
-  #   else routeToMachineWorkspace machines.first
-
-
 routeToMachineWorkspace = (machine) ->

   latestWorkspace = getLatestWorkspace()
@@ -197,15 +103,6 @@
       workspaceSlug = latestWorkspace.workspaceSlug

   kd.getSingleton('router').handleRoute "/IDE/#{machine.slug}/#{workspaceSlug}"
-
-  # if latestWorkspace = getLatestWorkspace()
-  #   {machineLabel} = latestWorkspace
-  #   if machineLabel is machine.label
-  #     workspaceSlug = latestWorkspace.workspaceSlug
-
-  # workspaceSlug ?= 'my-workspace'
-
-  # kd.getSingleton('router').handleRoute "/IDE/#{machine.slug}/#{workspaceSlug}"

 routeToLatestWorkspace = ->
@@ -230,28 +127,6 @@
         routeToMachineWorkspace machine
       else
         routeToFallback()
-
-
-  # router = kd.getSingleton 'router'
-
-  # if latestWorkspace = getLatestWorkspace()
-  #   {machineLabel, workspaceSlug, channelId} = latestWorkspace
-  # else
-  #   return routeToFallback()
-
-  # if channelId
-  #   for workspace in globals.userWorkspaces when workspace.channelId is channelId
-  #     return router.handleRoute "/IDE/#{channelId}"
-  #   routeToFallback()
-
-  # else if machineLabel and workspaceSlug
-  #   return routeToFallback()  unless machine = getMachine machineLabel
-
-  #   findWorkspace {workspaceSlug, machineLabel}, (workspace) ->
-
-  #     if workspace
-  #     then router.handleRoute "/IDE/#{machineLabel}/#{workspaceSlug}"
-  #     else routeToMachineWorkspace machine

 loadCollaborativeIDE = (id) ->
@@ -283,42 +158,6 @@
       return console.error e

-  # kd.singletons.socialapi.cacheable 'channel', id, (err, channel) ->
-
-  #   return routeToLatestWorkspace()  if err
-
-  #   try
-
-  #     [workspace] = globals.userWorkspaces.filter (w) -> w.channelId is channel.id
-
-  #     return routeToLatestWorkspace()  unless workspace
-
-  #     [machine] = globals.userMachines.filter (m) -> m.uid is workspace.machineUId
-
-  #     query = socialApiId: channel.creatorId
-  #     remote.api.JAccount.some query, {}, (err, account) =>
-
-  #       return throw new Error err  if err
-
-  #       username  = account.first.profile.nickname
-  #       channelId = channel.id
-
-  #       return loadIDE { machine, workspace, username, channelId }
-
-  #   catch e
-
-  #     console.error e
-  #     return routeToLatestWorkspace()
-
-
-# refreshWorkspaces = (callback) ->
-
-#   {mainView, computeController} = kd.singletons
-
-#   computeController.ready ->
-#     mainView.activitySidebar.fetchWorkspaces callback
-
-
 module.exports = -> lazyrouter.bind 'ide', (type, info, state, path, ctx) ->

   globals.userMachines = []
@@ -339,16 +178,6 @@
           if machine then routeToMachineWorkspace machine
           else routeToLatestWorkspace()

-      # {machineLabel} = info.params
-      # # we assume that if machineLabel is all numbers it is the channelId - SY
-      # if /^[0-9]+$/.test machineLabel
-      #   refreshWorkspaces -> loadCollaborativeIDE machineLabel
-      # else if machine = getMachine machineLabel
-      #   routeToMachineWorkspace machine
-      # else
-      #   routeToLatestWorkspace()
-
-
     when 'workspace'
       { params } = info
       params.username = username = nick()
@@ -361,23 +190,3 @@
           routeToMachineWorkspace machine
         else
           routeToLatestWorkspace()
-
-      # {params} = info
-      # {machineLabel} = params
-      # params.username = nick()
-
-      # refreshWorkspaces ->
-
-      #   findWorkspace params, (workspace) ->
-
-      #     if workspace
-      #       return loadWorkspace workspace, params
-      #     else
-      #       for machine in globals.userMachines when machine.slug is machineLabel
-      #         break
-
-      #       if machine
-      #       then routeToMachineWorkspace machine
-      #       else loadWorkspace null, params
-
-      #     routeToLatestWorkspace()


spreadsheet: separate records into separate categories

In a simple way.  We may need an additional layer to group by category
if we want more complex representations.

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -210,7 +210,7 @@
       for r in records
         updateRange s = r.segment = Segment.make(r.segment)
         for o, i in t
-          break unless o.some(overlaps)
+          break unless o[0].record.category != r.record.category || o.some(overlaps)
         t[i] = [] unless i of t
         t[i].push(r)
         $scope.selectRecord(r) if `r.id == targetRecord`


Removed old nonceStore tests

Removed old nonceStore tests

--- test/NonceStore.coffee
+++ test/NonceStore.coffee
@@ -1,41 +0,0 @@
-NonceStore  = require '../lib/nonce-store'
-should            = require 'should'
-
-
-describe 'NonceStore [Interface Class]', () ->
-
-  before ()=>
-    @store = new NonceStore('consumer_key')
-
-
-  # Standard nonce tests
-  #
-  #-- do not change below this line--
-
-  describe 'NonceStore', () =>
-    it 'should have extend NonceStore', () =>
-      should.exist(@store.isNonceStore)
-      @store.isNonceStore().should.be.ok
-
-  describe '.isNew', () =>
-    it 'should exist', () =>
-      should.exist(@store.isNew)
-
-    it 'should return Not Implemented', (done) =>
-      @store.isNew undefined, undefined, (err, valid)->
-        err.should.not.equal null
-        err.message.should.match /NOT/i
-        valid.should.equal false
-        done()
-
-
-  describe '.setUsed', () =>
-    it 'should exist', () =>
-      should.exist(@store.setUsed)
-
-    it 'should return Not Implemented', (done) =>
-      @store.setUsed undefined, undefined, (err, valid)->
-        err.should.not.equal null
-        err.message.should.match /NOT/i
-        valid.should.equal false
-        done()


Revert "Rename arrow function (af) snippet to 'fa'"

This reverts commit 936df3654970005972c806d098db095334cba032.

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -190,7 +190,7 @@
     'body': 'function ($1) {\n\t$2\n}$3'

   'arrow function':
-    'prefix': 'fa'
+    'prefix': 'af'
     'body': '($1) => {\n\t$2\n}$3'

   'immediately-invoked function expression':


Convert scan parameter to an integer.

Convert scan parameter to an integer.

--- app/javascripts/routes.coffee
+++ app/javascripts/routes.coffee
@@ -58,11 +58,6 @@
                 controller:  'SubjectListCtrl'

           # The subject state.
-          # TODO - child states always fetch the subject, even if
-          # called in a context with the fetched subject. Can this
-          # be avoided? Does the ui-router go function have an
-          # option to supply a subject argument and thereby forego
-          # the subject fetch resolution?
           .state 'quip.subject',
             url: '/:collection/subject/{subject:[0-9]+}?id'
             resolve:
@@ -99,6 +94,7 @@
                 controller:  'SubjectDetailCtrl'

           # The session state.
+          # The session state parameter is the session number.
           .state 'quip.subject.session',
             url: '/session/{session:[0-9]+}'
             resolve:
@@ -108,29 +104,50 @@
                   throw ReferenceError.new("Subject #{ subject.number } does" +
                                            " not have a session #{ number }")
                 # Grab the subject session embedded object.
-                sess = subject.sessions[number - 1]
-                # The session must have a detail foreign key.
-                if not sess.detail?
-                  throw ReferenceError.new("Subject #{ subject.number }" +
-                                           " Session #{ sess.number } does" +
-                                           " not have detail")
-
-                # Fetch the session detail.
-                Router.getSessionDetail(sess)
+                session = subject.sessions[number - 1]
+                # If the session has a scans array, then the detail was already
+                # fetched and we are done. Otherwise, fetch the detail.
+                if session.scans?
+                  session
+                else
+                  # The session must have a detail foreign key.
+                  if not session.detail?
+                    throw ReferenceError.new("Subject #{ subject.number }" +
+                                             " Session #{ session.number } does" +
+                                             " not have detail")
+                  # Fetch the session detail.
+                  Router.getSessionDetail(session)
             views:
               'main@':
                 templateUrl: '/partials/session-detail.html'
                 controller:  'SessionDetailCtrl'

           # The scan state.
+          # The scan state parameter is the scan number.
           .state 'quip.subject.session.scan',
             abstract: true
             url: '/scan/:scan'
             resolve:
               scan: (session, $stateParams, Router) ->
-                Router.getScan(session, $stateParams.scan)
+                number = parseInt($stateParams.scan)
+                Router.getScan(session, number)
+
+          # The scan volume state.
+          # The volume state parameter is the volume number.
+          .state 'quip.subject.session.scan.volume',
+            url: '/volume/{volume:[1-9][0-9]*}'
+            resolve:
+              volume: (scan, $stateParams, Router) ->
+                number = parseInt($stateParams.volume)
+                Router.getVolume(scan, number)
+            views:
+              'main@':
+                templateUrl: '/partials/volume.html'
+                controller:  'VolumeCtrl'

           # The registration state.
+          # The registration state parameter is the registration
+          # resource name.
           .state 'quip.subject.session.scan.registration',
             abstract: true
             url: '/registration/:registration'
@@ -138,29 +155,18 @@
               registration: (scan, $stateParams, Router) ->
                 Router.getRegistration(scan, $stateParams.registration)

-          # The scan image detail page.
-          .state 'quip.subject.session.scan.image',
-            url: '/image/{timePoint:[1-9][0-9]*}'
+          # The registration volume state.
+          # The volume state parameter is the volume number.
+          .state 'quip.subject.session.scan.registration.volume',
+            url: '/volume/{volume:[1-9][0-9]*}'
             resolve:
-              image: (scan, $stateParams, Router) ->
-                timePoint = parseInt($stateParams.timePoint)
-                Router.getImageDetail(scan, timePoint)
+              volume: (registration, $stateParams, Router) ->
+                number = parseInt($stateParams.volume)
+                Router.getVolume(registration, number)
             views:
               'main@':
-                templateUrl: '/partials/image-detail.html'
-                controller:  'ImageDetailCtrl'
-
-          # The registration image detail page.
-          .state 'quip.subject.session.scan.registration.image',
-            url: '/image/{timePoint:[1-9][0-9]*}'
-            resolve:
-              image: (registration, $stateParams, Router) ->
-                timePoint = parseInt($stateParams.timePoint)
-                Router.getImageDetail(registration, timePoint)
-            views:
-              'main@':
-                templateUrl: '/partials/image-detail.html'
-                controller:  'ImageDetailCtrl'
+                templateUrl: '/partials/volume.html'
+                controller:  'VolumeCtrl'

         # Redirect an URL with trailing slash to an URL without it.
         $urlRouterProvider.rule ($injector, $location) ->


singool: input settings automatically from model.

--- public/js/lib/singool/helpers/form.coffee
+++ public/js/lib/singool/helpers/form.coffee
@@ -62,13 +62,18 @@
       out += @input field, options
     out

-  input: (field, options) ->
-    _options =
-      type: 'text'
-      label: field
-      value: null
+  input: (field, options = {}) ->
+    if _.isEmpty(options) and @model
+      _options = @model.schema[field]
+    else
+      _options =
+        type: 'text'
+        label: field
+        value: null
+
     if @model and @model.get field
       _options.value = @model.get field
+
     options = _.defaults options, _options
     out = @[options.type] field, options
     out


If a write fails, don't assume it's a conflict.

A failed write attempts to reload the repo, causing an error if another is
already running. This change helps do less unnecessary reloads.

--- scripts/app.coffee
+++ scripts/app.coffee
@@ -164,18 +164,23 @@
         promise.resolve(val)
       )
       .fail (err) =>
-        # Probably a conflict because of a remote change.
+        # Possibly a conflict because of a remote change.
         # Resolve the changes and save again
         #
         # Reload all the models (merging local changes along the way)
         # and, at the same time get the new lastSeenSha
-        remoteUpdater.pollUpdates().then () =>
-          # Probably a patch/cache problem.
-          # Clear the cache and try again
-          session.getClient().clearCache?()
-          writeFiles(models, commitText)
-          .fail((err) => promise.reject(err))
-          .done (val) => promise.resolve(val)
+        # TODO: Not sure what code gihub sends back for a conflict, but it's
+        # gotta be one of these.
+        if err.status == 422 or err.status == 409
+          remoteUpdater.pollUpdates().then () =>
+            # Probably a patch/cache problem.
+            # Clear the cache and try again
+            session.getClient().clearCache?()
+            writeFiles(models, commitText)
+            .fail((err) => promise.reject(err))
+            .done (val) => promise.resolve(val)
+        else
+          promise.reject(err)

       return promise


add responses

add responses

--- scripts/ask.coffee
+++ scripts/ask.coffee
@@ -1,6 +1,6 @@
 module.exports = (robot) ->
     robot.respond /ask/i, (res) ->
-        console.log MSG
+        console.log res
         responses = [
             "Yes, definitely.",
             "No, definitely not.",
@@ -21,6 +21,7 @@
             "Signs point to yes.",
             "Signs point to no.",
             "I'm sorry, Dave, I cannot answer that question.",
+            "Aw hell nah."
         ]
         random = responses[Math.floor(Math.random() * responses.length)]


Add note about blotches, closes #67

Add note about blotches, closes #67

--- app/lib/tutorial_steps.coffee
+++ app/lib/tutorial_steps.coffee
@@ -38,7 +38,7 @@
     nextOn: 'first-drag': '.subject-container'

   new Step
-    content: 'Now adjust the size of the other radius to match the deposit.'
+    content: 'Now adjust the size of the other radius to match the deposit. Some blotches won\'t be quite circular. Just surround them as best you can.'
     attachment: x: 'left', to: '.subject-container', at: x: 'left'
     nextOn: 'drag-radius2': '.subject-container'


Trigger change event when setting option via #set

The #set is called when pre-selecting an option given in
@config.selected_id.

--- coffee/selectorablium.coffee
+++ coffee/selectorablium.coffee
@@ -108,6 +108,7 @@
         return false if text is false

       @_insertOptionElement value, text
+      @$el.trigger("change")
       @_hide()
       return true


tune temporisation

tune temporisation

--- test/phabs_test.coffee
+++ test/phabs_test.coffee
@@ -14,12 +14,12 @@

 describe 'hubot-phabs module', ->

-  hubotHear = (message, userName='momo') ->
+  hubotHear = (message, userName='momo', tempo=20) ->
     beforeEach (done) ->
       room.messages = []
       room.user.say userName, message
       room.messages.shift()
-      setTimeout (done), 20
+      setTimeout (done), tempo

   hubot = (message, userName='momo') ->
     hubotHear "@hubot #{message}", userName


Eliminate redundancies

Eliminate redundancies

--- lib/status-bar-manager.coffee
+++ lib/status-bar-manager.coffee
@@ -1,13 +1,13 @@
 {Disposable, CompositeDisposable} = require 'atom'

 ContentsByMode =
-  'insert': ["status-bar-vim-mode-insert", "Insert"]
-  'insert.replace': ["status-bar-vim-mode-insert", "Replace"]
-  'normal': ["status-bar-vim-mode-normal", "Normal"]
-  'visual': ["status-bar-vim-mode-visual", "Visual"]
-  'visual.characterwise': ["status-bar-vim-mode-visual", "Visual"]
-  'visual.linewise': ["status-bar-vim-mode-visual", "Visual Line"]
-  'visual.blockwise': ["status-bar-vim-mode-visual", "Visual Block"]
+  'insert':               ["insert", "Insert"]
+  'insert.replace':       ["insert", "Replace"]
+  'normal':               ["normal", "Normal"]
+  'visual':               ["visual", "Visual"]
+  'visual.characterwise': ["visual", "Visual"]
+  'visual.linewise':      ["visual", "Visual Line"]
+  'visual.blockwise':     ["visual", "Visual Block"]

 module.exports =
 class StatusBarManager
@@ -21,15 +21,14 @@

   initialize: (@statusBar) ->

-  update: (currentMode, currentSubmode) ->
-    currentMode = currentMode + "." + currentSubmode if currentSubmode?
-    if newContents = ContentsByMode[currentMode]
+  update: (mode, submode) ->
+    mode += ".#{submode}" if submode?
+    if newContents = ContentsByMode[mode]
       [klass, text] = newContents
-      @element.className = klass
+      @element.className = "status-bar-vim-mode-#{klass}"
       @element.textContent = text

   # Private
-
   attach: ->
     @tile = @statusBar.addRightTile(item: @container, priority: 20)


Add keybinding for toggle command

Add keybinding for toggle command

--- keymaps/tree-ignore.cson
+++ keymaps/tree-ignore.cson
@@ -8,6 +8,5 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps

-# TODO
-# '.workspace':
-#   'ctrl-alt-o': 'tree-ignore:toggle'
+'.workspace':
+  'shift-cmd-h': 'tree-ignore:toggle'


Flow layout.fillHeight support added

Flow layout.fillHeight support added

--- impl/base/level1/flow.coffee
+++ impl/base/level1/flow.coffee
@@ -18,6 +18,7 @@
 elementsX = new TypedArray.Uint32 64
 elementsY = new TypedArray.Uint32 64
 elementsCell = new TypedArray.Uint32 64
+rowsFills = new TypedArray.Uint8 64

 updateItem = (item) ->
 	unless effectItem = item._effectItem
@@ -70,11 +71,16 @@
 		elementsX = new TypedArray.Uint32 maxLen
 		elementsY = new TypedArray.Uint32 maxLen
 		elementsCell = new TypedArray.Uint32 maxLen
+		rowsFills = new TypedArray.Uint8 maxLen
+	else
+		for i in [0...maxLen] by 1
+			rowsFills[i] = 0

 	# tmp vars
 	width = height = column = row = x = y = right = rowSpan = maxCell = 0

 	# calculate children positions
+	rowsFillsSum = 0
 	for child, i in children
 		# omit not visible
 		if not child._visible
@@ -82,6 +88,8 @@

 		margin = child._margin
 		layout = child._layout
+		childWidth = child._width
+		childHeight = child._height

 		if layout
 			unless layout._enabled
@@ -89,10 +97,14 @@

 			if layout._fillWidth and not autoWidth
 				child.width = maxColumn - (if margin then margin.horizontal else 0)
+			if layout._fillHeight and not autoHeight
+				rowsFills[i]++
+				rowsFillsSum++
+				childHeight = 0

 		if column is 0
 			x = 0
-		else if column + columnSpacing + child.width + (if margin then margin._left else 0) > maxColumn
+		else if column + columnSpacing + childWidth + (if margin then margin._left else 0) > maxColumn
 			column = 0
 			row = height
 			x = 0
@@ -110,7 +122,7 @@
 		if margin and (includeBorderMargins or row > 0)
 			y += margin._top

-		right = x + child.width
+		right = x + childWidth

 		elementsX[i] = x
 		elementsY[i] = y
@@ -124,7 +136,7 @@
 			if includeBorderMargins and column > width
 				width = column

-		y += child._height
+		y += childHeight
 		rowSpan = rowSpacing
 		if margin
 			rowSpan += margin._bottom
@@ -136,6 +148,18 @@

 	if includeBorderMargins
 		height = Math.max height, height+rowSpan-rowSpacing
+
+	# expand filled rows
+	if rowsFillsSum > 0
+		freeHeightSpace = effectItem._height - height - topPadding - bottomPadding
+		perCell = freeHeightSpace / rowsFillsSum
+
+		yShift = 0
+		for child, i in children
+			elementsY[i] += yShift
+			if rowsFills[i]
+				yShift += cellsHeight[elementsCell[i]] = child.height = perCell * rowsFills[i]
+		height += freeHeightSpace

 	# set children positions
 	switch alignH
@@ -209,18 +233,20 @@
 update = ->
 	data = @_impl

+	if data.pending or not @_effectItem?._visible
+		return
+
 	if data.updatePending
 		data.loops++
 	else
 		data.loops = 0

-	if not data.pending and @_effectItem?._visible
-		data.pending = true
-		queue.push @
-
-		unless pending
-			setImmediate updateItems
-			pending = true
+	data.pending = true
+	queue.push @
+
+	unless pending
+		setImmediate updateItems
+		pending = true
 	return

 updateSize = ->


ComputeController: cosmetic fixes

ComputeController: cosmetic fixes

--- client/Main/providers/computecontroller.coffee
+++ client/Main/providers/computecontroller.coffee
@@ -503,9 +503,8 @@
     if @plans
       KD.utils.defer => callback @plans
     else
-      KD.remote.api.ComputeProvider.fetchPlans
-        provider: "koding"
-      , (err, plans)=>
+      KD.remote.api.ComputeProvider
+        .fetchPlans provider: "koding", (err, plans)=>
           if err? then warn err
           else @plans = plans
           callback plans
@@ -671,7 +670,7 @@
     .nodeify callback

-  fetchPlanCombo:(provider, callback)->
+  fetchPlanCombo: (provider, callback)->

     [callback, provider] = [provider, callback]  unless callback?
     provider ?= "koding"


MachineStateModal: do not handle update events while its busy

--- client/Main/providers/machinestatemodal.coffee
+++ client/Main/providers/machinestatemodal.coffee
@@ -59,6 +59,8 @@

   updateStatus: (event, task) ->

+    return  if @_busy
+
     {status, percentage, error} = event

     if status is @state
@@ -128,11 +130,13 @@
     KD.utils.wait 500, => @buildViews()

-  showBusy: (message = "Loading...") ->
+  showBusy: (message) ->
+
+    @_busy = message?

     @container.destroySubViews()

-    @createStateLabel message
+    @createStateLabel message ? "Loading..."
     @createLoading()

@@ -289,6 +293,8 @@

   buildViews: (response)->
+
+    return  if @_busy

     if response?.State?
       @state = response.State


Revert auto build mechanism back for NotInitialized VMs

--- client/Main/providers/machinestatemodal.coffee
+++ client/Main/providers/machinestatemodal.coffee
@@ -135,6 +135,7 @@

       if currentState is NotInitialized
         @buildViews State: currentState
+        KD.utils.defer => @turnOnMachine()
         return

       @triggerEventTimer 0


resurrection.teamwork: check for existence of playgrounds manifest

--- client/Teamwork/Views/teamworkdashboard.coffee
+++ client/Teamwork/Views/teamworkdashboard.coffee
@@ -64,7 +64,7 @@
   hide: -> @unsetClass "active"

   createPlaygrounds: (manifests) ->
-    manifests.forEach (manifest) =>
+    manifests?.forEach (manifest) =>
       @setClass "ready"
       @playgrounds.addSubView view = new KDCustomHTMLView
         cssClass  : "tw-playground-item"


Fixed initial wizard position.

Fixed initial wizard position.

--- app/views/home_view.coffee
+++ app/views/home_view.coffee
@@ -67,12 +67,13 @@
   createWizard: (scale=3.7) ->
     spriteOptions = thangID: "Beginner Wizard", resolutionFactor: scale
     @wizardSprite = new WizardSprite @wizardType, spriteOptions
-    @wizardSprite.update()
     wizardDisplayObject = @wizardSprite.displayObject
     wizardDisplayObject.x = 70
     wizardDisplayObject.y = 120
     wizardDisplayObject.scaleX = wizardDisplayObject.scaleY = scale
     wizardDisplayObject.scaleX *= -1
+    @wizardSprite.queueAction 'idle'
+    @wizardSprite.update()
     @stage.addChild wizardDisplayObject
     @stage.update()


Make touch emulator less prone to state bugs

By continously listening to events, rather then binding and unbinding all the time.

--- framer/Extras/TouchEmulator.coffee
+++ framer/Extras/TouchEmulator.coffee
@@ -52,8 +52,10 @@
 		@wrap = @context.domEventManager.wrap

 		@wrap(document).addEventListener("mousedown", @mousedown, true)
-		@wrap(document).addEventListener("mousemove", @mousemovePosition, true)
+		@wrap(document).addEventListener("mousemove", @mousemove, true)
+		@wrap(document).addEventListener("mouseup", @mouseup, true)
 		@wrap(document).addEventListener("keydown", @keydown, true)
+		@wrap(document).addEventListener("keyup", @keyup, true)
 		@wrap(document).addEventListener("mouseout", @mouseout, true)

 		@isMouseDown = false
@@ -83,8 +85,6 @@
 			@showTouchCursor()
 			@touchPointLayer.midX = @point.x
 			@touchPointLayer.midY = @point.y
-			@wrap(document).addEventListener("keyup", @keyup, true)
-			@wrap(document).addEventListener("mousemove", @mousemove, true)

 		if event.keyCode is @keyPanCode
 			@isPanKeyDown = true
@@ -96,8 +96,6 @@
 			cancelEvent(event)
 			@isPinchKeyDown = false
 			@hideTouchCursor()
-
-			@wrap(document).removeEventListener("mousemove", @mousemove, true)

 		if event.keyCode is @keyPanCode
 			cancelEvent(event)
@@ -107,15 +105,12 @@

 	mousedown: (event) =>
-
+
 		cancelEvent(event)

 		@isMouseDown = true
 		@target = event.target

-		@wrap(document).addEventListener("mousemove", @mousemove, true)
-		@wrap(document).addEventListener("mouseup", @mouseup, true)
-
 		if @isPinchKeyDown
 			dispatchTouchEvent("touchstart", @target, event, @touchPointDelta)
 		else
@@ -124,12 +119,12 @@
 		@touchPointLayer.image = @touchPointerImageActive

 	mousemove: (event) =>
-
-		cancelEvent(event)

 		@point =
 			x: event.pageX
 			y: event.pageY
+
+		cancelEvent(event)

 		@startPoint ?= @point
 		@centerPoint ?= @point
@@ -149,10 +144,11 @@
 				@touchPointLayer.midX = @touchPoint.x
 				@touchPointLayer.midY = @touchPoint.y

-		if (@isPinchKeyDown or @isPanKeyDown) and @touchPointDelta
-			dispatchTouchEvent("touchmove", @target, event, @touchPointDelta)
-		else
-			dispatchTouchEvent("touchmove", @target, event)
+		if @isMouseDown
+			if (@isPinchKeyDown or @isPanKeyDown) and @touchPointDelta
+				dispatchTouchEvent("touchmove", @target, event, @touchPointDelta)
+			else
+				dispatchTouchEvent("touchmove", @target, event)

 	mouseup: (event) =>

@@ -204,10 +200,6 @@
 			y: event.pageY

 	endMultiTouch: ->
-
-		@wrap(document).removeEventListener("mousemove", @mousemove, true)
-		@wrap(document).removeEventListener("mouseup", @mouseup, true)
-
 		@isMouseDown = false
 		@touchPointLayer.image = @touchPointerImage
 		@hideTouchCursor()


no new hero sections in old edit

no new hero sections in old edit

--- client/apps/edit/components/content/index.coffee
+++ client/apps/edit/components/content/index.coffee
@@ -28,10 +28,11 @@
   render: ->
     div {className: 'edit-section-layout'},
       if @props.article.get('hero_section') != null or @props.channel.hasFeature 'hero'
-        HeroSection {
-          section: @props.article.heroSection
-          channel: @props.channel
-        }
+        unless @props.article.heroSection.get('type') in ['split', 'text']
+          HeroSection {
+            section: @props.article.heroSection
+            channel: @props.channel
+          }

       HeaderSection {
         article: @props.article


FinderTreeController: Moved code to bottom.

--- client/ide/finder/idefindertreecontroller.coffee
+++ client/ide/finder/idefindertreecontroller.coffee
@@ -4,17 +4,6 @@

   cmCreateTerminal:  (node) -> @createTerminal  node
-
-
-  createWorkspace: (node) ->
-    folder       = node.getData()
-    name         = folder.name
-    machineUId   = folder.machine.uid
-    machineLabel = folder.machine.slug or folder.machine.label
-    rootPath     = FSHelper.plainPath folder.path
-    options      = { name, machineUId, rootPath, machineLabel }
-
-    KD.getSingleton('mainView').activitySidebar.createNewWorkspace options

   createTerminal: (node) ->
@@ -48,3 +37,16 @@

     node = @nodes["[#{machineUId}]#{rootPath}"]
     @deleteFiles [node] if node
+
+
+  createWorkspace: (node) ->
+
+    folder       = node.getData()
+    name         = folder.name
+    machineUId   = folder.machine.uid
+    machineLabel = folder.machine.slug or folder.machine.label
+    rootPath     = FSHelper.plainPath folder.path
+    eventObj     = this
+    options      = { name, machineUId, rootPath, machineLabel, eventObj }
+
+    KD.getSingleton('mainView').activitySidebar.createNewWorkspace options


remove selectable nodes

remove selectable nodes

--- src/scripts/controllers/editorController.coffee
+++ src/scripts/controllers/editorController.coffee
@@ -17,7 +17,6 @@
 		nodes = svg.append('svg:g').selectAll('g')

 		# mouse event vars
-		selectedNode = null
 		mouseDownEdge = null
 		mouseDownNode = null
 		mouseUpNode = null
@@ -62,9 +61,7 @@
 				.style('marker-start', (edge) -> if edge.left then 'url(#startArrow)' else '')
 				.style('marker-end', (edge) -> if edge.right then 'url(#endArrow)' else '')
 				.on 'mousedown', (edge) ->
-					# select link
 					mouseDownEdge = edge
-					selectedNode = null
 					restart()

 			# remove old links
@@ -92,13 +89,6 @@
 				d3.select(this).attr 'transform', '' # unenlarge target node

 			.on 'mousedown', (node) ->
-				# select node
-				mouseDownNode = node
-				if mouseDownNode == selectedNode
-					selectedNode = null
-				else
-					selectedNode = mouseDownNode
-
 				# reposition drag line
 				drag_line.style('marker-end', 'url(#endArrow)').classed('hidden', false).attr('d', 'M' + mouseDownNode.x + ',' + mouseDownNode.y + 'L' + mouseDownNode.x + ',' + mouseDownNode.y)
 				restart()
@@ -136,8 +126,6 @@
 						net.addEdge(edge)
 						$scope.$apply() # Quick save net to storage

-				# select new link
-				selectedNode = null
 				restart()

 			# show node text


Read correct var

Read correct var

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -517,7 +517,7 @@

     nodeNames = Object.keys(@nodes).sort()
     for name in nodeNames
-      node = @nodes[id]
+      node = @nodes[name]
       graph.processes[name] =
         component: node.component
         metadata: node.metadata or {}


Allow multiple ui listeners

Allow multiple ui listeners

This allows both the drop down menu and the filename input to both
listen to the UI events.

--- src/code/ui.coffee
+++ src/code/ui.coffee
@@ -91,6 +91,7 @@

   constructor: (@client)->
     @menu = null
+    @listenerCallbacks = []

   init: (options) ->
     options = options or {}
@@ -101,7 +102,12 @@
       @menu = new CloudFileManagerUIMenu options, @client

   # for React to listen for dialog changes
-  listen: (@listenerCallback) ->
+  listen: (callback) ->
+    @listenerCallbacks.push callback
+
+  listenerCallback: (evt) ->
+    for callback in @listenerCallbacks
+      callback evt

   appendMenuItem: (item) ->
     @listenerCallback new CloudFileManagerUIEvent 'appendMenuItem', item


add missing keymap

add missing keymap

--- keymaps/edit-in-new-tab.cson
+++ keymaps/edit-in-new-tab.cson
@@ -1,2 +1,3 @@
 'atom-text-editor':
-  'ctrl-shift-t': 'selection:move-to-new-tab'+  'ctrl-shift-t': 'selection:edit-in-new-tab'
+  'ctrl-alt-shift-t': 'selection:move-to-new-tab'


Changed octal to decimal for escaped bell character

--- src/command.coffee
+++ src/command.coffee
@@ -136,7 +136,7 @@
   catch err
     CoffeeScript.emit 'failure', err, task
     return if CoffeeScript.listeners('failure').length
-    return printLine err.message + '\007' if o.watch
+    return printLine err.message + '\7' if o.watch
     printWarn err instanceof Error and err.stack or "ERROR: #{err}"
     process.exit 1


much simpler implementation

much simpler implementation

--- src/compile.coffee
+++ src/compile.coffee
@@ -124,7 +124,6 @@

   helper: (ast, model, controller) ->
     dynamic = new DynamicNode(ast)
-    renderedViews = []
     render = (model=model, controller=controller) ->
       fragment = Serenade.document.createDocumentFragment()
       children = compile(ast.children, model, controller)
@@ -133,10 +132,8 @@
     helperFunction = Serenade.Helpers[ast.command] or throw SyntaxError "no helper #{ast.command} defined"
     context = { render, model, controller }
     update = ->
-      view.remove() for view in renderedViews
       args = ast.arguments.map((a) -> if a.bound then model[a.value] else a.value)
-      renderedViews = normalize(helperFunction.apply(context, args))
-      dynamic.replace [new Node(ast, element) for { fragment: element } in renderedViews]
+      dynamic.replace([normalize(ast, helperFunction.apply(context, args))])
     for argument in ast.arguments when argument.bound is true
       dynamic.bindEvent(model["#{argument.value}_property"], update)
     update()
@@ -206,21 +203,20 @@
     dynamic

 # turn a single element, document fragment, compiled view or string, or an
-# array of any of these into objects which conform to the interface of a
-# compiled view.
-normalize = (val) ->
+# array of any of these into Nodes.
+normalize = (ast, val) ->
   return [] unless val
   reduction = (aggregate, element) ->
     if typeof(element) is "string"
       div = Serenade.document.createElement("div")
       div.innerHTML = element
-      aggregate.push({ fragment: child, remove: -> }) for child in div.childNodes
+      aggregate.push(new Node(ast, child)) for child in div.childNodes
     else if element.nodeName is "#document-fragment"
-      aggregate.push({ fragment: child, remove: -> }) for child in element.childNodes
+      aggregate.push(new Node(ast, child)) for child in element.childNodes
     else if element instanceof CompiledView
-      aggregate.push(element)
+      aggregate = aggregate.concat(element.nodes)
     else
-      aggregate.push({ fragment: element, remove: -> })
+      aggregate.push(new Node(ast, element))
     aggregate
   [].concat(val).reduce(reduction, [])


Declare correct octal

Declare correct octal

--- src/creator.coffee
+++ src/creator.coffee
@@ -17,7 +17,7 @@
   mkdirDashP: (path) ->
     Path.exists path, (exists) ->
       unless exists
-        Fs.mkdir path, '0755', (err) ->
+        Fs.mkdir path, 0o0755, (err) ->
           throw err if err

   # Copy the contents of a file from one place to another


tiny CoffeeScript tweaks

tiny CoffeeScript tweaks

--- src/decking.coffee
+++ src/decking.coffee
@@ -343,9 +343,9 @@
       docker.buildImage tarball, options, (err, res) ->
         return done err if err

-        if res.headers["content-type"] == "application/json"
+        if res.headers["content-type"] is "application/json"
           res
-            .pipe(JSONStream.parse 'stream')
+            .pipe(JSONStream.parse "stream")
             .pipe(process.stdout)
         else
           res


small changes to kodingrouter

small changes to kodingrouter

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -123,7 +123,7 @@
       else
         @loadContent name, section, slug, route, query

-  clear:(route="/#{KD.config.groupEntryPoint}", replaceState=yes)->
+  clear:(route="/#{KD.config.groupEntryPoint ? ''}", replaceState=yes)->
     super route, replaceState

   createLinks =(names, fn)->


better tests for data attrs

better tests for data attrs

--- test/twine_test.coffee
+++ test/twine_test.coffee
@@ -1,6 +1,585 @@
 require('pbind')

 suite "Twine", ->
+  setupView = (html, context) ->
+    rootNode.innerHTML = html
+    Twine.reset(context, rootNode).bind().refreshImmediately()
+    rootNode.children[0]
+
+  triggerEvent = (node, eventName) ->
+    event = document.createEvent("HTMLEvents")
+    event.initEvent eventName, false, true
+    node.dispatchEvent event
+
+  rootNode = undefined
+  setup ->
+    @clock.tick 100
+    rootNode = document.createElement("div")
+
+  suite "data-bind attribute", ->
+    test "should bind basic keypaths", ->
+      testView = "

"
+      node = setupView(testView, key: "value")
+      assert.equal node.innerHTML, "value"
+
+    test "should bind compound keypaths", ->
+      testView = "

"
+      node = setupView(testView, key: {nested: { more: "value"}})
+      assert.equal node.innerHTML, "value"
+
+    test "should bind array accessor keypaths", ->
+      testView = "<input data-bind=\"key[0]\">"
+      node = setupView(testView, context = key: ["value"])
+      assert.equal node.value, "value"
+
+      context.key[0] = "new"
+      Twine.refreshImmediately()
+      assert.equal node.value, "new"
+
+      node.value = "value"
+      triggerEvent node, "change"
+      assert.equal node.value, "value"
+
+    test "should bind 'true' keyword keypaths", ->
+      testView = "

"
+      node = setupView(testView, key: "true")
+      assert.equal node.innerHTML, "true"
+
+    test "should bind 'false' keyword keypaths", ->
+      testView = "

"
+      node = setupView(testView, key: "false")
+      assert.equal node.innerHTML, "false"
+
+    test "should bind undefined keyword keypath", ->
+      testView = "

"
+      node = setupView(testView, key: "undefined")
+      assert.equal node.innerHTML, ""
+
+    test "should bind null keyword keypath", ->
+      testView = "

"
+      node = setupView(testView, key: "null")
+      assert.equal node.innerHTML, ""
+
+    test "should load data from the DOM if not defined", ->
+      testView = "
value
"
+      setupView(testView, context = {})
+      assert.equal context.key.nested, "value"
+
+    test "should set the value of input[type=text] elements", ->
+      testView = "<input type=\"text\" data-bind=\"key\">"
+      node = setupView(testView, context = key: "value")
+      assert.equal node.value, "value"
+
+      context.key = "new"
+      Twine.refreshImmediately()
+      assert.equal node.value, "new"
+
+    test "should set the checked value of input[type=checkbox] elements", ->
+      testView = "<input type=\"checkbox\" data-bind=\"key\">"
+      node = setupView(testView, context = key: "value")
+      assert.isTrue node.checked
+
+      context.key = ""
+      Twine.refreshImmediately()
+      assert.isFalse node.checked
+
+    test "should set the checked state of input[type=radio] elements", ->
+      testView = "<input type=\"radio\" data-bind=\"key\" value=\"one\"><input type=\"radio\" data-bind=\"key\" value=\"two\">"
+      setupView(testView, context = key: "two")
+      assert.isFalse rootNode.children[0].checked
+      assert.isTrue rootNode.children[1].checked
+
+      context.key = "one"
+      Twine.refreshImmediately()
+      assert.isTrue rootNode.children[0].checked
+      assert.isFalse rootNode.children[1].checked
+
+    test "should get the value of input[type=radio] elements", ->
+      testView = "<input type=\"radio\" name=\"group\" data-bind=\"key\" value=\"one\">" + "<input type=\"radio\" name=\"group\" data-bind=\"key\" value=\"two\" checked>"
+      setupView(testView, context = {})
+      assert.equal context.key, "two"
+
+      rootNode.children[0].checked = true
+      triggerEvent rootNode.children[0], "change"
+      assert.equal context.key, "one"
+
+    test "should get the selected state of the right child in select elements", ->
+      testView = """
+                  <select data-bind="key">
+                    <option value="one">one</option>
+                    <option value="two" selected>two</option>
+                    <option value="three">three</option>
+                  </select>
+                 """
+
+      setupView(testView, context = {})
+      select = rootNode.children[0]
+      options = select.children
+
+      assert.equal context.key, 'two'
+
+      options[0].selected = true
+      triggerEvent select, 'change'
+      assert.equal context.key, 'one'
+
+      options[2].selected = true
+      triggerEvent select, 'change'
+      assert.equal context.key, 'three'
+
+    test "should set the selected state of the right child in select elements", ->
+      testView = """
+                  <select data-bind="key">
+                    <option value="one">one</option>
+                    <option value="two" selected>two</option>
+                    <option value="three">three</option>
+                  </select>
+                 """
+
+      setupView(testView, context = {})
+      select = rootNode.children[0]
+      options = select.children
+
+      assert.isTrue options[1].selected
+
+      context.key = 'one'
+      Twine.refreshImmediately()
+      assert.isTrue options[0].selected
+
+      context.key = 'three'
+      Twine.refreshImmediately()
+      assert.isTrue options[2].selected
+
+    test "should work with arbitrary javascript", ->
+      testView = "

"
+      node = setupView(testView, key: 4)
+      assert.equal node.innerHTML, "8"
+
+    test "should work with arbitrary javascript that looks like a keypath", ->
+      testView = "

"
+      node = setupView(testView, {})
+      assert.equal node.innerHTML, "2.34"
+
+    test "should escape weird characters", ->
+      testView = "

"
+      node = setupView(testView, key: "


Fixed merged file

Fixed merged file

--- core/app/backbone/views/evidence/evidenceish_heading_view.coffee
+++ core/app/backbone/views/evidence/evidenceish_heading_view.coffee
@@ -1,6 +0,0 @@
-class window.EvidenceishHeadingView extends Backbone.Marionette.ItemView
-  className: 'comment-post-heading'
-  template: 'evidence/evidenceish_heading'
-
-  onRender: ->
-    UserPopoverContentView.makeTooltip @, @model


:memo: Shorten comment

:memo: Shorten comment

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -429,10 +429,8 @@
       lastInput = e.originalEvent.data
       @editor.insertText(lastInput)

-      # Return true if the last input was a space
-      # This prevents parent elements from scrolling
       if lastInput is ' '
-        true
+        true # Prevents parent elements from scrolling when a space is typed
       else
         @hiddenInput.val(lastInput)
         false


Collaboration: Show participants modal when participants button clicked.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -625,6 +625,7 @@

   createStatusBar: (splitViewPanel) ->
     splitViewPanel.addSubView @statusBar = new IDE.StatusBar
+    @statusBar.on 'ParticipantsModalRequired', @bound 'showParticipantsModal'

   createFindAndReplaceView: (splitViewPanel) ->
     splitViewPanel.addSubView @findAndReplaceView = new AceFindAndReplaceView
@@ -797,3 +798,8 @@

       when 'PaneRemoved'
         map.delete paneHash
+
+
+  showParticipantsModal: ->
+    host = @collaborationHost or KD.nick()
+    new IDE.ParticipantsModal { @participants, @realTimeDoc, @rtm, host }


Collaboration: Fixed syncing participants list.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -733,7 +733,12 @@
       if not @participants
         @addParticipant yes  if @amIHost
       else
-        if @participants.indexOf(nickname) is -1
+        isInList = no
+
+        @participants.asArray().forEach (participant) =>
+          isInList = yes  if participant.nickname is nickname
+
+        if not isInList
           log 'acetz: I am not in the participants list, adding myself'
           @addParticipant()
         else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -146,9 +146,7 @@
       I.active = false
   }

-  modules = .concat(I.includedModules)
-
-  modules.each (Module) ->
+  GameObject.defaultModules.each (Module) ->
     self.include Module

   self


First commit in brach

First commit in brach

--- server/controllers/mobileapi/mobileapi.coffee
+++ server/controllers/mobileapi/mobileapi.coffee
@@ -1,7 +1,6 @@
 mongoose = require "mongoose"
 User     = mongoose.model "User"
 _        = require "underscore"
-

 exports.usersLogin = (req, res) ->


cimsec customization

cimsec customization

Former-commit-id: 041fb411d809dfa63110bd5bfe554c6db41662a5 [formerly 177e4ceb7ef85331727cf2bcadd697c64a4469b7] [formerly 066e02b9e061b81fd5ac490ca49123ae91fa573d [formerly 9ad48ee6775f675bfad89eaa8913e3b12453e9ed]] [formerly 9ffd2c2c45cb2c77677e43d51c6da5413013dd1e [formerly e8358872457b609a1d205b3e0980e10581519ff0] [formerly 4c9787ccc8270dd917e80f11b845c223430e130a [formerly 4c9787ccc8270dd917e80f11b845c223430e130a [formerly 952321fb99fd63a7bb6a40830955e50cacbe79ec]]]]
Former-commit-id: d6ed620a878637e7a6c41c553593653e9a378c91 [formerly 5ec244fcf0d53abe99e3aeda574d79df2e35b6cc] [formerly b524b31d16b90e250a26072a8026e1857e817d39 [formerly 242dfda6c6dc9bdf638daa48ccd7db0248900c9e]]
Former-commit-id: 059367a4fa603e17267ee44e75fca33e5574ab7d [formerly 0d19e2cf7d68198de881cc5498928c7aa60ac160]
Former-commit-id: cf7bd69be021077c942e7baa896cec964e4ce829
Former-commit-id: 17237251d753cfddfe7cc06f4ddfc21905d09b95

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -280,6 +280,19 @@
     support_sub: ''
     oppose_sub: ''

+effective_ineffective =
+  individual:
+    support: 'Effective'
+    oppose: 'Ineffective'
+    support_sub: ''
+    oppose_sub: ''
+
+  group:
+    support: 'Effective'
+    oppose: 'Ineffective'
+    support_sub: ''
+    oppose_sub: ''
+

 desacuerdo_acuerdo =
   individual:
@@ -388,6 +401,8 @@
 customizations['us'] =
   show_proposer_icon: true

+customizations['cimsec'] =
+  slider_pole_labels : effective_ineffective

 customizations['villagb'] =


.

Former-commit-id: 512d253145bff2e9e207aee9e62f814abd5bd21d [formerly 002ebb415adb28f7672833ec072b041278f62512] [formerly dc052c734eee612fe98c316213bab3f881b84e36 [formerly 0cd1d11bdffdb889f2f84fe99bf25709b2926cfb]] [formerly 17bc5d2d1d3e77657b050f1ee23e8daa1b42bb39 [formerly 1a62e32e04ba62c44ede6d3e08aeb8b01e3cd1c3] [formerly 6137aed10395b3bb469446885f8deca5d4cbd872 [formerly 6137aed10395b3bb469446885f8deca5d4cbd872 [formerly f38f64fbe8b716cfdfe7afd432d66f1eee4edf2b]]]]
Former-commit-id: 71dc644118cca973fcc679d511fe2cfbef4a4f13 [formerly af439b938efabc8ed185db46f67421862ac7c915] [formerly 34686757f3f2583980eaf7bd59ed65a847bfab7b [formerly f56f7dc37187c9693e0d060689432b0e9af41a23]]
Former-commit-id: d644aaf097f243fdd3ac89caa4f370b768413164 [formerly 86fde2acda71e96b72b5fd44e7d94bcb59d6e78a]
Former-commit-id: 48a72ac9b16b0303d9412b7cf7ae14a7312ec989
Former-commit-id: 5828d9509f24a7a2b136ad28a08ca405d045d775

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -1696,7 +1696,6 @@
   collapse_descriptions_at: 600

 customizations.bitcoinfoundation = customizations['bitcoinfoundationarchive'] =
-  show_new_proposal_button: false

   auth:
     user_questions : [


Pass the key event’s keyIdentifier through for modifier keys

This is necessary for keyup events. A ‘Meta’ keyup event will have
`metaKey` property set to false because the key was just lifted.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -115,20 +115,20 @@
   key = keyForKeyboardEvent(event, dvorakQwertyWorkaroundEnabled)

   keystroke = ''
-  if event.ctrlKey
+  if event.ctrlKey or key is 'Control'
     keystroke += 'ctrl'
-  if event.altKey
+  if event.altKey or key is 'Alt'
     keystroke += '-' if keystroke
     keystroke += 'alt'
-  if event.shiftKey
+  if event.shiftKey or key is 'Shift'
     # Don't push 'shift' when modifying symbolic characters like '{'
     unless /^[^A-Za-z]$/.test(key)
       keystroke += '-' if keystroke
       keystroke += 'shift'
-  if event.metaKey
+  if event.metaKey or key is 'Meta'
     keystroke += '-' if keystroke
     keystroke += 'cmd'
-  if key?
+  if key? and not KeyboardEventModifiers.has(key)
     keystroke += '-' if keystroke
     keystroke += key

@@ -163,16 +163,16 @@
     switch key
       when 'ctrl'
         keyIdentifier = 'Control'
-        ctrl = true
+        ctrl = true if eventType isnt 'keyup'
       when 'alt'
         keyIdentifier = 'Alt'
-        alt = true
+        alt = true if eventType isnt 'keyup'
       when 'shift'
         keyIdentifier = 'Shift'
-        shift = true
+        shift = true if eventType isnt 'keyup'
       when 'cmd'
         keyIdentifier = 'Meta'
-        cmd = true
+        cmd = true if eventType isnt 'keyup'
       else
         keyIdentifier = key[0].toUpperCase() + key[1..]

@@ -232,7 +232,7 @@
   if process.platform in ['linux', 'win32']
     keyIdentifier = translateKeyIdentifierForWindowsAndLinuxChromiumBug(keyIdentifier)

-  return null if KeyboardEventModifiers.has(keyIdentifier)
+  return keyIdentifier if KeyboardEventModifiers.has(keyIdentifier)

   charCode = charCodeFromKeyIdentifier(keyIdentifier)


Add config.paths.packageConfig.

Add config.paths.packageConfig.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -52,6 +52,7 @@
   server.listen parseInt port, 10
   server.on 'listening', callback
   logger.info "application started on http://localhost:#{port}/"
+  server

 exports.startServer = (config, callback = (->)) ->
   if config.server.path
@@ -93,6 +94,7 @@
   config.paths.build ?= config.buildPath ? join 'root', 'public'
   config.paths.app ?= join 'root', 'app'
   config.paths.config = configPath ? join 'root', 'config'
+  config.paths.packageConfig ?= join 'root', 'package.json'
   config.paths.assets ?= join 'app', 'assets'
   config.paths.test ?= join 'root', 'test'
   config.paths.vendor ?= join 'root', 'vendor'
@@ -103,23 +105,6 @@
   # Alias deprecated config params.
   config.rootPath = config.paths.root
   config.buildPath = config.paths.build
-
-  # config.paths        ?= {}
-  # config.paths.root   ?= config.rootPath ? '.'
-  # config.paths.build  ?= config.buildPath ? join 'root', 'public'
-  # config.paths.app    ?= join 'root', 'app'
-  # config.paths.config  = configPath ? join 'root', 'config'
-  # config.paths.assets ?= join 'app', 'assets'
-  # config.paths.test   ?= join 'root', 'test'
-  # config.paths.vendor ?= join 'root', 'vendor'
-  # config.server       ?= {}
-  # config.server.path  ?= null
-  # config.server.port  ?= 3333
-  # config.server.run   ?= no
-  # # Alias deprecated config params.
-  # config.rootPath      = config.paths.root
-  # config.buildPath     = config.paths.build
-
   replaceSlashes config if process.platform is 'win32'
   config

@@ -141,7 +126,7 @@

 exports.loadPlugins = (config, callback) ->
   rootPath = sysPath.resolve config.rootPath
-  fs.readFile (sysPath.join rootPath, 'package.json'), (error, data) ->
+  fs.readFile config.paths.packageConfig, (error, data) ->
     return callback error if error?
     deps = Object.keys JSON.parse(data).dependencies
     try


Add some padding at the bottom of the example page

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -8,7 +8,7 @@
     margin: 10

   render: ->
-

+

react-image-grid

       Code on Github

Create a perfect image grid every time whatever the size of the


Remove unused alias: bindOnce

Remove unused alias: bindOnce

--- source/bind.coffee
+++ source/bind.coffee
@@ -41,7 +41,7 @@
       bind__EventString(object, events, resolveCallback(object, hash[events]), context, once)
     return

-  for own k, v of { bind: false, bindOnce: true }
+  for own k, v of { on: false, once: true }
     do (method = k, once = v) ->

       PS[method] = (events, callback, context) ->
@@ -54,7 +54,6 @@
           else
             bind__EventMap(this, events, context ? callback ? this, once)
         this
-
-  PS.on   = PS.bind
-  PS.once = PS.bindOnce
+
+  PS.bind = PS.on
   return


Don't hire same agent twice

Don't hire same agent twice

--- source/desk.coffee
+++ source/desk.coffee
@@ -45,7 +45,9 @@

   hireAgent: (agentD) ->
     @agent = new Agent(this.x, this.y+1, agentD)
-    Agent.potentialAgents.pop()
+    Agent.potentialAgents = Agent.potentialAgents.filter((agent) ->
+      agent != agentD
+    )

   infoScreen: () ->
     ui = Game.state.ui