DataForm: undo removeClass fix since Martin fixed it

in 37f18db25fa0805e7b5ee6f412b6d844326b6dcd

--- src/elements/DataForm/DataForm.coffee
+++ src/elements/DataForm/DataForm.coffee
@@ -205,9 +205,9 @@
 				icon: "svg-close"
 				appearance: "flat"
 				onMouseenter: =>
-					CUI.dom.addClass(hl.DOM, "cui-data-form-row--trash")
+					CUI.dom.addClass(hl, "cui-data-form-row--trash")
 				onMouseleave: =>
-					CUI.dom.removeClass(hl.DOM, "cui-data-form-row--trash")
+					CUI.dom.removeClass(hl, "cui-data-form-row--trash")
 				onClick: =>
 					@_onRowRemove?.call(@, data)
 					@__removeRow(data)


map size (chrome tested)

map size (chrome tested)

--- js/views/map.coffee
+++ js/views/map.coffee
@@ -28,8 +28,8 @@
       content = $("#map")
       viewHeight = $(window).height()
       viewWidth = $(window).width()
-      content.height viewHeight
-      content.width viewWidth
+      content.height viewHeight-60 #60 header
+      content.width viewWidth+20 #20 scroller TODO: check with other devices - chrome tested

     zoomIn: ->


Add hard-coded default NIH Inclusion Enrollment Report

Applies to all volumes, though won't work if metrics are missing

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -1324,6 +1324,15 @@
             $scope.pivot.load(state.pivot)
             return

+        volume.state['NIH Inclusion Enrollment Report'] ?=
+          key: "slot"
+          pivot:
+            cols: ["participant ethnicity", "participant gender"]
+            rows: ["participant race"]
+            rendererName: "Table"
+            aggregatorName: "Count"
+          filter: [{c: "slot", m: "top", op: "false"}]
+
         return
     ]
     link: ($scope, $element, $attrs) ->


Updated to use Atom 1.0 API selector

Updated to use Atom 1.0 API selector

--- keymaps/vertical-align.cson
+++ keymaps/vertical-align.cson
@@ -7,7 +7,7 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.platform-darwin .editor':
+'.platform-darwin atom-text-editor':
   'ctrl-cmd-/': 'vertical-align:align'
-'.platform-win32 .editor, .platform-linux .editor':
+'.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
   'ctrl-alt-/': 'vertical-align:align'


Fixed hook initialize in user

Fixed hook initialize in user

--- server/packages/user/index.coffee
+++ server/packages/user/index.coffee
@@ -55,7 +55,7 @@
 	]

 # ## Implements hook `initialize`
-exports.$initialize = -> (req, res, next) ->
+exports.$initialize = (config) ->

 	{models: User: User} = require 'server/jugglingdb'

@@ -86,8 +86,6 @@

 	passport.deserializeUser (id, done) -> User.find(id).nodeify done

-	next()
-
 # ## Implements hook `models`
 exports.$models = (schema) ->


Sorry @rso, nu refreshed 'ie ietsjes sneller

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -64,7 +64,7 @@
     clearTimeout @_currentTimeout if @_currentTimeout?
     delete @_currentTimeout
     callMyselfSoon = =>
-      @_currentTimeout = setTimeout _.bind(args.callee, this), 7000*1000
+      @_currentTimeout = setTimeout _.bind(args.callee, this), 7000

     @fetch


Set also placeholder when adding a fact relation

--- core/app/backbone/models/fact_relation.coffee
+++ core/app/backbone/models/fact_relation.coffee
@@ -2,6 +2,8 @@

   defaults:
     evidence_type: 'FactRelation'
+    opinions:
+      formatted_relevance: "?"

   setOpinion: (type) ->
     $.ajax


Renamed opinion to opinion instead of type

--- core/app/backbone/models/fact_relation.coffee
+++ core/app/backbone/models/fact_relation.coffee
@@ -8,7 +8,7 @@
       url: @url() + "/opinion/" + type
       success: (data) =>
         mp_track "Evidence: Opinionate",
-          type: type
+          opinion: type
           evidence_id: @id

         @set data


Fix the bug #136

Fix the bug #136

--- app/coffee/controllers/issues.coffee
+++ app/coffee/controllers/issues.coffee
@@ -65,10 +65,10 @@
         for counter in list
             element = constants[counter[0]]
             tag = {
-                "id": element.id,
-                "name": element.name,
-                "count": counter[1],
-                "type": type,
+                id: element.id,
+                name: element.name,
+                count: counter[1],
+                type: type,
                 color: element.color
             }
             tags.push(selectTagIfNotSelected(tag))
@@ -77,13 +77,13 @@

     generateTagsFromUsers = (list, type, scopeVar)->
         tags = []
-        for userCounter in $scope.filtersData.owners
+        for userCounter in list
             user = $scope.constants.users[userCounter[0]]
             tag = {
-                "id": user.id,
-                "name": gm.utils.truncate(user.full_name, 17),
-                "count": userCounter[1],
-                "type": type
+                id: user.id,
+                name: gm.utils.truncate(user.full_name, 17),
+                count: userCounter[1],
+                type: type
             }

             tags.push(selectTagIfNotSelected(tag))


PrivateMessages: fix user being stuck when sending a message for the first time

--- client/Social/Activity/views/privatemessagelistitem.coffee
+++ client/Social/Activity/views/privatemessagelistitem.coffee
@@ -25,6 +25,9 @@

     @setClass 'with-parent'
     firstReply = @commentBox.controller.getListItems().first
+
+    return unless firstReply
+
     if @getData().account._id is firstReply.getData().account._id
       firstReply.setClass 'consequent'


MachineSettingsGeneralView: Require ComputeErrorUsageModal.

--- client/app/lib/providers/machinesettingsgeneralview.coffee
+++ client/app/lib/providers/machinesettingsgeneralview.coffee
@@ -1,13 +1,14 @@
-kd                   = require 'kd'
-KDView               = kd.View
-Machine              = require './machine'
-showError            = require 'app/util/showError'
-KodingSwitch         = require '../commonviews/kodingswitch'
-KDLoaderView         = kd.LoaderView
-CustomLinkView       = require '../customlinkview'
-KDCustomHTMLView     = kd.CustomHTMLView
-KDHitEnterInputView  = kd.HitEnterInputView
-KDFormViewWithFields = kd.FormViewWithFields
+kd                     = require 'kd'
+KDView                 = kd.View
+Machine                = require './machine'
+showError              = require 'app/util/showError'
+KodingSwitch           = require '../commonviews/kodingswitch'
+KDLoaderView           = kd.LoaderView
+CustomLinkView         = require '../customlinkview'
+KDCustomHTMLView       = kd.CustomHTMLView
+KDHitEnterInputView    = kd.HitEnterInputView
+KDFormViewWithFields   = kd.FormViewWithFields
+ComputeErrorUsageModal = require './computeerrorusagemodal'

 module.exports = class MachineSettingsGeneralView extends KDView


payment: s/BuyVmModal/VmBuyModal/

payment: s/BuyVmModal/VmBuyModal/

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -363,7 +363,7 @@

       paymentInput = {}

-      modal = new BuyVmModal { descriptions, hostTypes, descPartial, plans }
+      modal = new VmBuyModal { descriptions, hostTypes, descPartial, plans }

       @dialogIsOpen = yes
       modal.once 'KDModalViewDestroyed', => @dialogIsOpen = no


vm controller: refactor and fix up the control flow

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -17,12 +17,12 @@
       options =
         withArgs : command

-    @fetchDefaultVmName (defaultVmName)=>
-      vmName = if options.vmName then options.vmName else defaultVmName
-      unless vmName
-        return callback message: 'There is no VM for this account.'
+    @fetchVmName options, (err, vmName) =>
+      return callback err  if err?
       options.correlationName = vmName
-      @kc.run options, callback
+      @kc.run options, (rest...) ->
+        log rest...
+        callback rest...

   _runWrapper:(command, vm, callback)->
     [callback, vm] = [vm, callback]  unless 'string' is typeof vm
@@ -100,9 +100,20 @@
       return ("#{vmTemplate}".replace '%d', i)
     return no

+  fetchVmName: (options, callback) ->
+    if options.vmName?
+    then @utils.defer -> callback null, options.vmName
+    else if KD.whoami().type is 'unregistered'
+    then @utils.defer -> callback null, 'guest'
+    else
+      @fetchDefaultVmName (defaultVmName) ->
+        if defaultVmName?
+        then callback null, defaultVmName
+        else callback message: 'There is no VM for this account.'
+
   fetchDefaultVmName:(callback=noop, force=no)->
     if @defaultVmName and not force
-      return callback @defaultVmName
+      return @utils.defer -> callback @defaultVmName

     {entryPoint}   = KD.config
     currentGroup   = if entryPoint?.type is 'group' then entryPoint.slug


initial s3 kite

initial s3 kite

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -26,6 +26,8 @@
           options.kiteName = "terminal"
         else if /^vm\./.test options.method
           options.kiteName = "fs"
+        else if /^s3\./.test options.method
+          options.kiteName = "s3"

       options.correlationName = vmName
       @fetchRegion vmName, (region)=>


removed word wrapping

removed word wrapping

--- client/main.coffee
+++ client/main.coffee
@@ -31,7 +31,7 @@
     @editor.setShowPrintMargin false
     HTMLmode = require("ace/mode/html").Mode
     @editor.getSession().setTabSize(2);
-    @editor.getSession().setUseWrapMode(true);
+    # @editor.getSession().setUseWrapMode(true);
     @editor.getSession().setMode(new HTMLmode())


Move all_expectations and related functions into new class AllExpectations.

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -9,11 +9,11 @@
     install_expects_method()
     convenience_mocks = build_convenience_mock_objects()
     run_test_function(test_function, convenience_mocks)
-    verify_all_expectations()
+    AllExpectations.verify_all_expectations()
   finally
     uninstall_expects_method()
     uninstall_all_mocked_methods()
-    forget_all_expectations()
+    AllExpectations.unregister_all_expectations()

 install_expects_method = ->
   Object.prototype.expects = expects
@@ -22,8 +22,9 @@
   delete Object.prototype.expects

 # TODO: write comment about uninstalling in reverse
+# TODO: move into AllExpectations
 uninstall_all_mocked_methods = ->
-  expectation.uninstall_mocked_method() for expectation in all_expectations by -1
+  expectation.uninstall_mocked_method() for expectation in AllExpectations._expectations by -1

 build_convenience_mock_objects = ->
   ( new Object() for i in [1..5] )
@@ -74,21 +75,28 @@

 #
-# all_expectations
-#
-
+# AllExpectations
+#
 # TODO: note about there not being that many expectations (i.e. typically fewer than 10?)
-all_expectations = []
-
-verify_all_expectations = ->
-  errors = find_all_errors()
-  throw new Error( errors ) unless errors == ""
-
-find_all_errors = ->
-  ( expectation.find_errors() for expectation in all_expectations ).join("")
-
-forget_all_expectations = ->
-  all_expectations.length = 0
+#
+class AllExpectations
+
+	@register_expectation: (expectation) ->
+		AllExpectations._expectations.push(expectation)
+
+	@verify_all_expectations: ->
+		errors = AllExpectations._find_all_errors()
+		throw new Error(errors) unless errors == ""
+
+	@unregister_all_expectations: ->
+		AllExpectations._expectations.length = 0
+
+	# private
+
+	@_expectations: []
+
+	@_find_all_errors: ->
+  	  ( expectation.find_errors() for expectation in AllExpectations._expectations ).join("")

 #
@@ -103,18 +111,20 @@
     throw expectation._throws if expectation._throws?
     expectation._returns

+# TODO: move into AllExpectations
 find_expectation = (object, method_name, args...) ->
-  for expectation in all_expectations when expectation.matches(object, method_name, args...)
+  for expectation in AllExpectations._expectations when expectation.matches(object, method_name, args...)
     return expectation
   undefined

+# TODO: move into AllExpectations
 check_for_duplicate_expectations = ->
   # TODO: use each with index and slice to avoid last element
-  return if all_expectations.length < 2
-  for outer in [0..all_expectations.length-2]
-    for inner in [outer+1..all_expectations.length-1]
-      if all_expectations[outer].equals( all_expectations[inner] )
-        throw_duplicate_expectation("#{all_expectations[outer]._method_name}(#{all_expectations[outer]._args})")
+  return if AllExpectations._expectations.length < 2
+  for outer in [0..AllExpectations._expectations.length-2]
+    for inner in [outer+1..AllExpectations._expectations.length-1]
+      if AllExpectations._expectations[outer].equals( AllExpectations._expectations[inner] )
+        throw_duplicate_expectation("#{AllExpectations._expectations[outer]._method_name}(#{AllExpectations._expectations[outer]._args})")

 throw_unknown_expectation = (expectation) ->
   throw new Error( "#{expectation} does not match any expectations" )
@@ -136,7 +146,7 @@
     @_throws = undefined
     @_called = no
     @_install_mock_method()
-    all_expectations.push(@)
+    AllExpectations.register_expectation(@)

   #
   # mock (mock) ->


Extract method _save_throws().

Extract method _save_throws().

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -71,7 +71,7 @@
   #
   throws: (error) ->
     _check_throws_usage(@, error)
-    @_throws = error
+    _save_throws(@, error)
     @

   #
@@ -245,6 +245,9 @@

 _save_returns = (expectation, value) ->
   expectation._returns = value
+
+_save_throws = (expectation, error) ->
+  expectation._throws = error

 _set_throws_for_current_expectation = (mock, error) ->
   _current_expectation(mock)._throws = error


Remove delay in sending scratch space updates

--- client/tawk.coffee
+++ client/tawk.coffee
@@ -189,11 +189,6 @@
 dom.GROUP = ->
   gid = @props.gid
   members = tawk['group/' + gid].members or []
-
-  group_info = tawk['/group/' + gid]
-  group_editing = tawk['editing-' + gid]
-  if not group_editing.timer
-    group_editing.text = (if group_info.text == undefined then 'This is your group scratch space' else group_info.text)

   divSize = group_size(members.length or 1) # ghost group is size 1

@@ -237,15 +232,9 @@
           borderBottomRightRadius: '15px'
           outline: 'none'
           border: '1px solid #aaa'
-        value: group_editing.text
+        value: (if tawk['/group' + gid].text? then tawk['/group' + gid].text else 'This is your group scratch space')
         onChange: (e) ->
-          group_editing.text = e.target.value
-          if group_editing.timer
-            clearTimeout group_editing.timer
-          group_editing.timer = setTimeout ->
-            group_editing.timer = null
-            group_info.text = group_editing.text
-          , 500
+          tawk['/group' + gid].text = e.target.value

 dom.GROUP.refresh = ->
   gid = @props.gid


add a failing test for a bug found in Alfred

form for binds do not update their context's bindings

--- tests/batman/view_test.coffee
+++ tests/batman/view_test.coffee
@@ -523,6 +523,21 @@
     @User = class User extends MockClass
       name: 'default name'

+asyncTest 'it should update to a binding upon its creation', 2, ->
+  source = '''
+  <form data-formfor-user="instanceOfUser">
+    <input type="text" data-bind="user.name">
+  </form>
+  '''
+  context = new Batman.Object()
+
+  node = render source, context, (node) =>
+    equals $('input', node).val(), ""
+    context.set 'instanceOfUser', new @User
+    delay =>
+      equals $('input', node).val(), "default name"
+
+
 asyncTest 'it should pull in objects for form rendering', 1, ->
   source = '''
   <form data-formfor-user="instanceOfUser">


published date cannot be null

published date cannot be null

--- api/apps/articles/model/index.coffee
+++ api/apps/articles/model/index.coffee
@@ -119,7 +119,7 @@

 @present = (article) =>
   scheduled = if (date = article?.scheduled_publish_at) then moment(date).toISOString() else null
-  published = if (date = article?.published_at) then moment(date).toISOString() else null
+  published = if (date = article?.published_at) then moment(date).toISOString() else moment().toISOString()
   _.extend article,
     id: article?._id?.toString()
     _id: undefined


return instance when exiting constructor early

--- maskew.coffee
+++ maskew.coffee
@@ -39,7 +39,7 @@
 class window.Maskew

   constructor: (@_el, @angle, @_options = {}) ->
-    return @_el unless hasSupport
+    return @ unless hasSupport
     return new Maskew @_el, @angle unless @ instanceof Maskew

     @_options.touch       or= false


:speedboat: reducing points after scaling to speed up rendering

--- src/Renderer.coffee
+++ src/Renderer.coffee
@@ -19,7 +19,7 @@
     labelMargin: 5

     #"poi_label", "water",
-    drawOrder: ["water", "admin", "building", "road", "place_label", "poi_label", "housenum_label"]
+    drawOrder: ["admin", "building", "road", "place_label", "poi_label", "housenum_label"]

     icons:
       car: "🚗"
@@ -122,8 +122,7 @@
     unless style = @styler.getStyleFor layer, feature, 14
       return false

-    toDraw = for points in feature.points
-      [point.x/scale, point.y/scale] for point in points
+    toDraw = (@_scaleAndReduce points, scale for points in feature.points)

     color = style.paint['line-color'] or style.paint['fill-color'] or style.paint['text-color']

@@ -159,5 +158,22 @@
             if @labelBuffer.writeIfPossible text, x, point[1], (@config.layers[layer]?.margin or @config.labelMargin)
               @canvas.text text, x, point[1], colorCode, false

+  _scaleAndReduce: (points, scale) ->
+    lastX = null
+    lastY = null
+    scaled = []
+
+    for point in points
+      x = Math.floor point.x/scale
+      y = Math.floor point.y/scale
+
+      if lastX isnt x or lastY isnt y
+        lastY = y
+        lastX = x
+        scaled.push [x, y]
+
+    scaled
+
+
   notify: (text) ->
     @_write "\r\x1B[K"+text


Resource fix ups

Resource fix ups

--- src/Resource.coffee
+++ src/Resource.coffee
@@ -12,7 +12,7 @@
   Resource = ({@url, params:@_params, model, collection, transform})->
     @transform = transform or idfunc
     @Model = (model or Model).extend ModelInstance
-    @Collection = (collection or Collection).extend CollectionInstance
+    @Collection = (collection or Collection).extend CollectionInstance @Model
     return

   Resource.extend = extend
@@ -24,6 +24,7 @@
     return

   Resource::parseCollectionResponse = (response,collection)->
+    collection.remove collection.toArray()
     jsonObjs = JSON.parse response
     collection.add (@transform obj for obj in jsonObjs)
     return
@@ -52,8 +53,8 @@
         return
     inst

-  Resource::query = (params,success,error)->
-    inst = new @Collection @
+  Resource::query = (params,success,error,inst)->
+    inst ?= new @Collection @
     http
       method: 'GET'
       url: @genUrl params, false
@@ -109,6 +110,7 @@
           (status,response,isSuccess)=>
             if isSuccess
               @_setStatus 'deleted'
+              @destroy()
               success?()
             else
               @_setStatus 'error'
@@ -125,6 +127,8 @@
           else 'PUT'
         url: @_res.genUrl params, false
         data: JSON.stringify @_a
+        headers:
+          'Content-Type': 'application/json;charset=UTF-8'
         (status,response,isSuccess)=>
           if isSuccess
             @_res.parseModelResponse response, @
@@ -138,15 +142,25 @@
       @_setStatus 'saving'
       return

-  CollectionInstance =
+  CollectionInstance = (ModelType)->
     constructor: (@_res)->
       Collection.call @
       @_status = 'loading'
       return

+    Model: ModelType
     _setStatus: setStatus
     status: getStatus

     requery: (params)->
+      @_res.query params, undefined, undefined, @
+      @
+
+    _toM: (o)->
+      o =
+        if o instanceof @Model then o
+        else new @Model @_res, o, false
+      o._setParent @
+      o

   Resource


update doc

update doc

--- scripts/pagerv2_commands.coffee
+++ scripts/pagerv2_commands.coffee
@@ -20,7 +20,7 @@
 #   hubot pd noc now             - creates a noc  override until the end of current oncall
 #   hubot pd not noc             - cancels a noc override if any
 #
-#   hubot pd <#>                 - gives more information about incident number <number>
+#   hubot pd incident <#>        - gives more information about incident number <number>
 #   hubot pd sup|inc|incidents   - lists currently unresolved incidents
 #
 #   hubot pd ack [all]           - acknowledges any unack incidents
@@ -143,7 +143,6 @@
         res.send e
       res.finish()

-  # TODO
   #   hubot pd incident <number> - gives more information about incident number <number>
     robot.respond /pd (?:inc |incident )(\d+|[A-Z0-9]{7})\s*$/, (res) ->
       [ _, incident ] = res.match


Tests:IDE:Terminal: Increased timer to terminate open sessions.

--- tests/src/ide/terminal.coffee
+++ tests/src/ide/terminal.coffee
@@ -44,7 +44,7 @@
   openNewTerminalMenu(browser)

   browser
-    .pause 30000 # required
+    .pause 60000 # required, wait to terminate all open sessions
     .assert.elementNotPresent   'li.terminate-all' # Assertion


CUI: Run getPathToScript at include time, to safely determine script path.

--- src/base/CUI.coffee
+++ src/base/CUI.coffee
@@ -19,7 +19,6 @@

 		@CSS = new CUI.CSSLoader()

-		@getPathToScript()
 		trigger_viewport_resize = =>
 			console.info("CUI: trigger viewport resize.")
 			Events.trigger
@@ -83,12 +82,12 @@

 	@getPathToScript: ->
 		if not @pathToScript
-			for s, idx in DOM.matchSelector(document.documentElement, "script")
-				if m = s.src.match("(.*/)cui\.js$")
-					@pathToScript = m[1]
-					@script = s
-					break
-			assert(@pathToScript, "CUI", "Could not determine script path.")
+			scripts = document.getElementsByTagName('script')
+			cui_script = scripts[scripts.length - 1]
+			if m = cui_script.src.match("(.*/).*?\.js$")
+				@pathToScript = m[1]
+			else
+				assert(@pathToScript, "CUI", "Could not determine script path.")

 		@pathToScript

@@ -912,6 +911,10 @@
 	CUI.__downloadDataElement = nodes[1]
 	CUI.DOM.append(document.body, nodes)

+
+CUI.getPathToScript()
+
+
 if not window.addEventListener
 	alert("Your browser is not supported. Please update to a current version of Google Chrome, Mozilla Firefox or Internet Explorer.")
 else


Remove wayward debugger.

Remove wayward debugger.

--- server/battle_controller.coffee
+++ server/battle_controller.coffee
@@ -40,7 +40,6 @@
   transitionToNextState: ->
     if @battle.areAllRequestsCompleted()
       if @battle.replacing
-        debugger
         @battle.performReplacements()
         @beginTurn()
       else


Refactor / removed unused methods

Refactor / removed unused methods

--- server/game/game_methods.coffee
+++ server/game/game_methods.coffee
@@ -17,22 +17,16 @@
     updateTurns: (gameId, turns) ->
         Games.update gameId, { $set: { turns: turns } }

-    # update player stats
-    updateBoard: (gameId, board) ->
-        Games.update gameId, { $set: { board: board } }
-
     # initialize a new game
     createGame: (opponentId) ->
         game = GameFactory.create [Meteor.userId(), opponentId]
         Games.insert game

-    # update game document
-    updateGame: (game) ->
-        Games.update game._id, game
-
+    # update game fields
     updateFields: (gameId, field) ->
         Games.update gameId, { $set: field }

+    # prepend game events to a game log
     log: (gameId, log) ->
         game = Games.findOne gameId

@@ -46,6 +40,7 @@
             }
         }

+    # update user scores
     updateScores: (winner, loser, tie = no) ->
         if tie
             winnerField = loserField =
@@ -63,6 +58,6 @@
         )

         Meteor.users.update(
-            { _id: loser },
+            { _id: loser },
             { $inc: loserField }
         )


webserver: inject myPermissionsAndRoles handler

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -77,6 +77,7 @@
 app.post '/-/analytics/track'                    , require './handlers/analytics/track'
 app.post '/-/analytics/page'                     , require './handlers/analytics/page'

+app.get  '/-/my/permissionsAndRoles'             , require './handlers/myPermissionsAndRoles'
 app.get  '/-/google-api/authorize/drive'         , require './handlers/authorizedrive'
 app.get  '/-/auth/check/:key'                    , require './handlers/authkeycheck'
 app.post '/-/support/new', bodyParser.json()     , require './handlers/supportnew'


Refs #68; Tested Epoch.Chart.SVG resize events.

--- tests/unit/core/charts.coffee
+++ tests/unit/core/charts.coffee
@@ -262,6 +262,30 @@
         assert.equal chart.svg.attr('width'), containerWidth
         assert.equal chart.svg.attr('height'), containerHeight

+    describe 'dimensionsChanged', ->
+      [width, height, chart] = [200, 100, null]
+
+      before (done) ->
+        d3.select(doc.body).append('div').attr('id', 'svgResize').style
+          width: width + 'px'
+          height: height + 'px'
+        chart = new Epoch.Chart.SVG { el: '#svgResize' }
+        done()
+
+      after (done) ->
+        d3.select('#svgResize').remove()
+        done()
+
+      it 'should resize the SVG element when the width option is changed', ->
+        newWidth = width + 500
+        chart.option 'width', newWidth
+        assert.equal chart.svg.attr('width'), newWidth
+
+      it 'should resize the SVG element when the height option is changed', ->
+        newHeight = height + 500
+        chart.option 'height', newHeight
+        assert.equal chart.svg.attr('height'), newHeight
+
   describe 'Canvas', ->
     [containerWidth, containerHeight] = [1000, 280]
     container = null


improve formatting of Space.Objects unit tests

--- tests/unit/object.unit.coffee
+++ tests/unit/object.unit.coffee
@@ -6,39 +6,25 @@
   describe 'extending', ->

     it 'creates and returns a subclass', ->
-
       Space.Object.extend(@namespace, 'MyClass')
       expect(@namespace.MyClass).to.extend Space.Object

     it 'applies the arguments to the super constructor', ->
-
       [first, second, third] = ['first', 2, {}]
       spy = sinon.spy()
-
       Space.Object.extend @namespace, 'Base', {
         Constructor: -> spy.apply this, arguments
       }
       @namespace.Base.extend(@namespace, 'Extended')
       instance = new @namespace.Extended first, second, third
-
       expect(spy).to.have.been.calledWithExactly first, second, third
       expect(spy).to.have.been.calledOn instance

     it 'allows to extend the prototype', ->
-
-      First = Space.Object.extend {
-        first: 1,
-        get: (property) -> @[property]
-      }
-
-      Second = First.extend {
-        second: 2,
-        get: -> First::get.apply this, arguments
-      }
-
+      First = Space.Object.extend first: 1, get: (property) -> @[property]
+      Second = First.extend second: 2, get: -> First::get.apply this, arguments
       class Third extends Second
         get: (property) -> super property
-
       instance = new Third()
       expect(instance.get('first')).to.equal 1
       expect(instance.get('second')).to.equal 2
@@ -54,9 +40,7 @@

     it 'forwards any number of arguments to the constructor', ->
       Base = Space.Object.extend Constructor: (@first, @second) ->
-
       instance = Base.create 1, 2
-
       expect(instance.first).to.equal 1
       expect(instance.second).to.equal 2


bug fix

bug fix

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -49,7 +49,7 @@
 ko.bindingHandlers.attr.update = (element, value_accessor, all_bindings_accessor, view_model, binding_context) ->
   new_value_accessor = ->
     value = ko.unwrap(value_accessor());
-    value[k] = JSON.stringify(ko.unwrap(v)) for k, v of value when !Utils.isString(v)
+    value[k] = JSON.stringify(val) for k, v of value when (val = ko.unwrap(v)) and !Utils.isString(val)
     value
   original_update(element, new_value_accessor, all_bindings_accessor, view_model, binding_context)


Fix removeItems

Fix removeItems

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -225,8 +225,13 @@

   # Sends a request to remove items from the OrderForm.
   removeItems: (items, expectedOrderFormSections = @_allOrderFormSections) =>
-    item.quantity = 0 for item in items
-    @updateItems(items, expectedOrderFormSections)
+    itemsToRemove = []
+    for item, i in items
+      itemsToRemove.push({
+        index: item.index,
+        quantity: 0
+      })
+    @updateItems(itemsToRemove, expectedOrderFormSections)

   # Sends a request to remove all items from the OrderForm.
   removeAllItems: (expectedOrderFormSections = @_allOrderFormSections) =>


Propagate SpriteParser's auto-insignificant-tween skipping fix to SpriteBuilder too

--- app/lib/sprites/SpriteBuilder.coffee
+++ app/lib/sprites/SpriteBuilder.coffee
@@ -28,11 +28,17 @@
     anim.initialize(mode ? createjs.MovieClip.INDEPENDENT, startPosition ? 0, loops ? true, labels)
     for tweenData in animData.tweens
       tween = createjs.Tween
+      stopped = false
       for func in tweenData
         args = _.cloneDeep(func.a)
         @dereferenceArgs(args, locals)
-        tween = tween[func.n](args...)
-      anim.timeline.addTween(tween)
+        if tween[func.n]
+          tween = tween[func.n](args...)
+        else
+          # If we, say, skipped a shadow get(), then the wait() may not be present
+          stopped = true
+          break
+      anim.timeline.addTween(tween) unless stopped

     anim.nominalBounds = new createjs.Rectangle(animData.bounds...)
     if animData.frameBounds


Fixed the _quoteString and encodeObject bugs

--- js/core/util/util_methods/util_methods.coffee
+++ js/core/util/util_methods/util_methods.coffee
@@ -116,7 +116,7 @@
     ]
     # Returns a quoted string (escapes quotation marks and places quotes within)
     _quoteString: (_str)->
-      for _substitution in @_stringSubstritutions
+      for _substitution in @_stringSubstitutions
         [ _substFrom, _substTo ] = _substitution
         _str = _str.replace( _substFrom, _substTo )
       '"'+_str+'"'
@@ -227,7 +227,7 @@
       encodeObject: (_obj)->
         return 'null' unless _obj?
         _type = @typeChr(_obj)
-        unless _obj
+        unless _type
           console.log 'WARNING: encode of '+(typeof _obj)+' not possible'
           return 'null'
         return String(_obj) if _type == 'b' or _type == 'n'
@@ -272,7 +272,7 @@
               _cloned[_key] = _value
           else
             for _key, _value of _obj
-              _cloned[_key] = @cloneObject( _item )
+              _cloned[_key] = @cloneObject( _value )
           return _cloned
         _obj
   for _methodName, _methodValue of _encodeDecodeMethods


remove useless `pass is 0` test since that arg is not used any more

also remove warnings about variables with no values. In practice this
is a false positive far too often.

--- lib/assets/javascripts/jax/material.js.coffee
+++ lib/assets/javascripts/jax/material.js.coffee
@@ -156,18 +156,10 @@
       gl = context.gl
       gl.blendFunc GL_ONE, GL_ONE
       gl.depthFunc GL_EQUAL
-    @shader.set context, @assigns, pass is 0
+    @shader.set context, @assigns
     return true

   drawBuffers: (context, mesh, pass = 0) ->
-    unless Jax.environment is Jax.PRODUCTION or @_alreadyWarned
-      # in development mode, check that all variables are assigned, and warn if not.
-      # Only do this once (per material).
-      @_alreadyWarned = true
-      for name, variable of @shader.variables(context)
-        if variable.value is undefined
-          console.log "Warning: material #{@name} did not set a value for shader variable #{variable.name}"
-
     if (buffer = mesh.getIndexBuffer()) && buffer.length
       buffer.bind context if pass is 0
       context.gl.drawElements mesh.draw_mode, buffer.length, buffer.dataType, 0


remove expires headers, http 1.0 died a long time ago

--- server.coffee
+++ server.coffee
@@ -66,7 +66,6 @@
         four_oh_four(resp, "Content-Length exceeded")
       else
         newHeaders =
-          'expires'                : srcResp.headers['expires']
           'content-type'           : srcResp.headers['content-type']
           'cache-control'          : srcResp.headers['cache-control']
           'content-length'         : content_length


Fix issue with not having a default tab when none explicitly set

--- app/scripts/directives/tabset.coffee
+++ app/scripts/directives/tabset.coffee
@@ -18,6 +18,7 @@
       if tab.active and tab isnt selectedTab
         tab.active = false
         console.log tab
+      return
     # Set the selected tab as active
     selectedTab.active = true
     return
@@ -28,6 +29,7 @@
       newTab.active = true
     else if newTab.active
       self.select newTab
+    return

   self.removeTab = (tab) ->
     index = tabs.indexOf tab
@@ -77,7 +79,7 @@
   transclude: true
   replace:    true
   scope:
-    active:   '@'
+    active:   '=?'
     src:      '@'
   compile: (element, attributes, transclude) ->
     postlink = (scope, element, attributes, controller) ->


return more variable info

return more variable info

--- src/debugger.coffee
+++ src/debugger.coffee
@@ -74,4 +74,27 @@
                         value: @rt.makeValueString(val)
         ret

+Debugger::allVariables = ->
+  usedName = new Set
+  ret = []
+  scopeName = 'global'
+  scopeIndex = i = ref = 0
+  while i < @rt.scope.length #changed to step from 0 upwards so thsat it can remember the last scopeName captured form the function
+    ref1 = @rt.scope[scopeIndex]
+    for name of ref1
+      val = ref1[name]
+      if @rt.scope[scopeIndex].$name.indexOf('function') > -1  # if name is something like "function main" or "function foo"
+        scopeName = @rt.scope[scopeIndex].$name.replace('function ', '') #store the name of the function as that will be the scope used while inside the function
+      if typeof val == 'object' and 't' of val and 'v' of val
+        if !usedName.has(name)
+          usedName.add name
+          ret.push
+            scopelevel: scopeIndex #add the level 0 to ...
+            scopename: scopeName # add the scopeName
+            gentype: val.t.type #add the general type e.g. primitive
+            name: name
+            type: @rt.makeTypeString(val.t)
+            value: @rt.makeValueString(val)
+    scopeIndex = i += 1
+  ret
 module.exports = Debugger


:art: Rename function to indicate ownership

--- src/integrations/atom/projects.coffee
+++ src/integrations/atom/projects.coffee
@@ -5,10 +5,10 @@

 module.exports =
 class Projects
-  constructor: (searchBus) ->
+  constructor: (searchStream) ->
     @tasks = {}
-    searchProp = searchBus.skipDuplicates().toProperty('')
-    @matchedItemsProp = Bacon.combineWith(@filterItemsBySearch, @createItemsProp(), searchProp)
+    searchProp = searchStream.skipDuplicates().toProperty('')
+    @matchedItemsProp = Bacon.combineWith @filterItemsBySearch, @createItemsProp(), searchProp

   filterItemsBySearch: (items, searchStr) ->
     return items unless searchStr


Fix: Trigger TextFieldView enter submit on keydown instead of keyup to prevent conflict with forms displayed by ModelFieldView.

--- lib/ST/TextFieldView.coffee
+++ lib/ST/TextFieldView.coffee
@@ -30,6 +30,7 @@
     else
       @_inputElement.val @_placeholder
       @_inputElement.addClass 'placeholder'
+    @_inputElement.bind   'keydown', @method('keyDown')
     @_inputElement.bind   'keyup change', @method('inputChanged')
     @_inputElement.focus  @method('inputFocus')
     @_inputElement.blur   @method('inputBlur')
@@ -48,16 +49,16 @@
       input = @_inputElement[0]
       input.blur() if input && input.blur

-  @method 'inputChanged', (e) ->
-    if e && e.which && e.which == 13
-      @trigger 'submit'
-    else
-      oldValue = @_value
-      newValue = @_inputElement.val()
-      unless oldValue == newValue
-        @_value = newValue
-        @_value = $.trim @_value if @_autoTrim
-        @_changed 'value', oldValue, newValue
+  @method 'keyDown', (e) ->
+    @trigger 'submit' if e && e.which && e.which == 13
+
+  @method 'inputChanged', ->
+    oldValue = @_value
+    newValue = @_inputElement.val()
+    unless oldValue == newValue
+      @_value = newValue
+      @_value = $.trim @_value if @_autoTrim
+      @_changed 'value', oldValue, newValue

   @method 'inputFocus', ->
     if @_inputElement.val() == @_placeholder


Include acceleration in position integration

It appears to be more accurate, and the boxes bounce less


tweak debug exports

tweak debug exports

--- lib/browserify.entry.coffee
+++ lib/browserify.entry.coffee
@@ -10,8 +10,12 @@
   require "rxjs-jquery"

   window.app = app = require "./app"
-  app.core = require("./app.core")
-  app.net = require("./app.net")
-  app.log = require("./utils").log
-  app.faye = require("./app.net").fayeClient
+  app.ns =
+    utils: require('./utils')
+    core: require("./app.core")
+    ui: require("./app.ui")
+    net: require("./app.net")
+    auth: require("./app.auth")
+    log: require("./utils").log
+    faye: require("./app.net").fayeClient
   app.main()


do not check for ios sim in linux etc

do not check for ios sim in linux etc

--- spec/core/connect.spec.coffee
+++ spec/core/connect.spec.coffee
@@ -50,6 +50,10 @@
         expect( packaged ).toBeTruthy()

     it "kills iOS simulator", =>
+      unless process.platform == "darwin"
+        console.log "skipping because not in os x"
+        return
+
       killediOS = false
       waitsFor =>
         killediOS = @session.stdout.match("Killed iOS Simulator")


Fix spec

Fix spec

--- spec/image-editor-spec.coffee
+++ spec/image-editor-spec.coffee
@@ -14,6 +14,9 @@

   describe ".activate()", ->
     it "registers a project opener that handles image file extension", ->
+      atom.workspaceView = new WorkspaceView()
+      atom.workspace = atom.workspaceView.model
+
       waitsForPromise ->
         atom.packages.activatePackage('image-view')


Configuration de "file-icons"

Configuration de "file-icons"

Les icônes sont colorés uniquement en cas de modification

--- atom/config.cson
+++ atom/config.cson
@@ -14,6 +14,8 @@
     showIndentGuide: true
     showInvisibles: true
     tabLength: 2
+  "file-icons":
+    onChanges: true
   "flex-tool-bar": {}
   "isotope-ui":
     backgroundGradient: true


Changes to atom's config.cson

Changes to atom's config.cson

--- atom/config.cson
+++ atom/config.cson
@@ -1,9 +1,13 @@
 "*":
+  "atom-beautify":
+    general:
+      _analyticsUserId: "016f8b6e-eda5-4d72-acc8-57bcde20fdff"
   core:
     autoHideMenuBar: true
     disabledPackages: [
       "exception-reporting"
       "linter-perl"
+      "perltidy"
     ]
     projectHome: "/home/caramelomartins/Projects"
     themes: [


Update Elixir formatter for 1.6.0 final

Update Elixir formatter for 1.6.0 final

--- atom/config.cson
+++ atom/config.cson
@@ -4,9 +4,8 @@
     fuzzyMatch: true
     helmDirSwitch: true
   "atom-elixir-formatter":
-    elixirExecutable: "/Users/randy/.asdf/installs/elixir/1.6.0-rc.0/bin/elixir"
+    elixirExecutable: "/Users/randy/.asdf/installs/elixir/1.6.0/bin/elixir"
     formatOnSave: "whenFormatterFilePresent"
-    showErrorNotifications: false
   "autocomplete-modules":
     babelPluginModuleAlias: true
     webpack: true


add more housing unit stats

add more housing unit stats


remove trailing comma

remove trailing comma

--- webpack.config.coffee
+++ webpack.config.coffee
@@ -19,7 +19,7 @@
       './resources/styles/tutor.less'
     ]
     qa: [
-      './src/qa.coffee',
+      './src/qa.coffee'
     ]

   output:


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -66,7 +66,7 @@
 Triggered before the object should be drawn. A canvas is passed as
 the first argument. This does not apply the current transform.

-@name beforeDraw
+@name beforeTransform
 @methodOf Drawable#
 @event
 @param {PowerCanvas} canvas A reference to the canvas to draw on.
@@ -74,9 +74,9 @@

 ###*
 Triggered after the object should be drawn. A canvas is passed as
-the first argument. This does not apply the current transform.
+the first argument. This applies the current transform.

-@name afterDraw
+@name afterTransform
 @methodOf Drawable#
 @event
 @param {PowerCanvas} canvas A reference to the canvas to draw on.


Signin page uses query param to pick register/signin state

--- _source/client/templates/cards/on-board/components/profile.signin.coffee
+++ _source/client/templates/cards/on-board/components/profile.signin.coffee
@@ -5,7 +5,6 @@
   @card "Apollos.profile.onBoard"

   url: -> "sign-in"
-

   vars: -> [

@@ -40,6 +39,11 @@

     # bind email field changes to update parent if valid
     self = @
+
+    showRegister = Apollos.Router._current.queryParams.register is "true"
+
+    if showRegister
+      self.hasAccount.set false

     self.autorun ->
       children = {}


[fix] Fixed tagging feedback on IE (4m)

[fix] Fixed tagging feedback on IE (4m)

--- app/assets/javascripts/houston/feedback/helpers/jquery_extensions.coffee
+++ app/assets/javascripts/houston/feedback/helpers/jquery_extensions.coffee
@@ -16,7 +16,7 @@
       .typeahead
         source: tags
         updater: (item)->
-          @$element.val().replace(/[^,]*$/, ' ').trimLeft() + item + ', '
+          @$element.val().replace(/[^,]*$/, ' ').replace(/^\s+/, '') + item + ', '
         matcher: (item)->
           tquery = extractor(@query)
           return false unless tquery


fix li ssh, which i broke earlier

fix li ssh, which i broke earlier

--- code/linode.coffee
+++ code/linode.coffee
@@ -140,7 +140,7 @@
   # Returns an instance given its name.
   # TODO: put err in to config
   @get: (name, callback) ->
-    @list no, (err, list) ->
+    @list yes, (err, list) ->
       k = _.find list, (n) ->
         n.name == name
       throw "instance not found" unless k?


Remove duplicate filePath

Remove duplicate filePath

--- spec/image-editor-view-spec.coffee
+++ spec/image-editor-view-spec.coffee
@@ -61,7 +61,6 @@

     beforeEach ->
       atom.workspaceView.attachToDom()
-      filePath = atom.project.resolve('binary-file.png')

       waitsForPromise ->
         atom.packages.activatePackage('image-view')


Update specs to pass again

Update specs to pass again

* what spie can do seems to have changed, replaced by a simple
  counter - which is better either way

--- spec/javascripts/board_spec.coffee
+++ spec/javascripts/board_spec.coffee
@@ -214,11 +214,11 @@
       expect(get_last_move(board)).toBeNull()

   describe 'all_fields_do', ->
-    spie = null
+    call_count = null

     verify_calls_to_color = (stone_color, number)->
-      all_fields_do(board, (x, y, color)-> spie() if color == stone_color)
-      expect(spie.calls.length).toEqual number
+      all_fields_do(board, (x, y, color)-> call_count++ if color == stone_color)
+      expect(call_count).toEqual number

     all_fields_board = ->
       board_string = """
@@ -230,11 +230,11 @@

     beforeEach ->
       board = all_fields_board()
-      spie = jasmine.createSpy('blubb')
+      call_count = 0

     it 'calls the function for every field', ->
-      all_fields_do(board, -> spie())
-      expect(spie.calls.length).toEqual 9
+      all_fields_do(board, -> call_count++)
+      expect(call_count).toEqual 9

     it 'has all the black stones', ->
       verify_calls_to_color(BLACK, 4)


IMAGE ME

IMAGE ME

--- botart.coffee
+++ botart.coffee
@@ -54,7 +54,7 @@
       response.on 'data', (chunk) ->
         data += chunk
       response.on 'end', ->
-        callback JSON.parse(data)
+        callback JSON.parse(data) if callback
     else
       console.log "#{response.statusCode}: #{path}"
       response.setEncoding('utf8')
@@ -83,15 +83,10 @@
       handler(message)

 log = (message) ->
-  if message.topic
-    console.log "#{message.topic.name} >> #{message.user.username}: #{message.message}"
-  else
-    console.log "botart >> #{message.user.username}: #{message.message}"
+  console.log "#{message.topic.name} >> #{message.user.username}: #{message.message}"

 say = (topic, message) ->
-  data = qs.stringify { message: message }
-  post "/api/topics/#{topic}/messages/create.json", data, (body) ->
-    log body.message
+  post "/api/topics/#{topic}/messages/create.json", qs.stringify(message: message)

 listen = ->
   get '/api/live.json', (body) ->


Remove unused code

Remove unused code

--- server/methods/updateMessage.coffee
+++ server/methods/updateMessage.coffee
@@ -1,5 +1,3 @@
-updateMessage = (originalMessage, message) ->
-
 Meteor.methods
 	updateMessage: (message) ->
 		if not Meteor.userId()
@@ -41,4 +39,4 @@

 		# Meteor.defer ->
 		# 	RocketChat.callbacks.run 'afterSaveMessage', ChatMessage.findOne(message.id)
-		+


added note about rollback

added note about rollback

--- services/document-updater/app/coffee/RedisMigrationManager.coffee
+++ services/document-updater/app/coffee/RedisMigrationManager.coffee
@@ -81,7 +81,8 @@
 #  - deploy project history with the "old queue" pointing to the new redis and
 #    the "new queue" to the old redis to clear the new queue before processing
 #    the new queue (i.e. add a rollback:true property in new_project_history in
-#    the project-history settings)
+#    the project-history settings via the environment variable
+#    MIGRATION_PHASE="rollback").
 #  - projects will now clear gradually from the new redis back to the old redis
 #  - get a list of all the projects in the new redis and flush them, which will
 #    cause the new queues to be cleared and the old redis to be used for those


Update date

Update date

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -236,7 +236,7 @@
 					return cb()
 				timestamp = user_id.toString().substring(0,8)
 				userSignupDate = new Date( parseInt( timestamp, 16 ) * 1000 )
-				if userSignupDate > new Date("2017-08-02")
+				if userSignupDate > new Date("2017-08-09")
 					# Don't show for users who registered after it was released
 					return cb(null, false)
 				timeout = setTimeout cb, 500


only recalc smoothed points on addPoint

only recalc smoothed points on addPoint

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -79,13 +79,13 @@

   addPoint: (x, y) ->
     @points.push(@lcState.makePoint(x, y))
+    @smoothedPoints = LC.bspline(LC.bspline(LC.bspline(@points)))

   draw: (ctx) ->
-    smoothedPoints = LC.bspline(LC.bspline(LC.bspline(@points)))
-    return unless smoothedPoints.length
+    return unless @smoothedPoints.length
     ctx.beginPath()
-    ctx.moveTo(smoothedPoints[0].x, smoothedPoints[0].y)
-    _.each _.rest(smoothedPoints), (p) ->
+    ctx.moveTo(@smoothedPoints[0].x, @smoothedPoints[0].y)
+    _.each _.rest(@smoothedPoints), (p) ->
       ctx.strokeStyle = p.color
       ctx.lineWidth = p.size
       ctx.lineTo(p.x, p.y)


Ensure gracefully cleanup of tmp folder

Ensure gracefully cleanup of tmp folder

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -126,6 +126,8 @@
       fileRegex: argv.sftpFileRegex
       logger: logger

+    tmp.setGracefulCleanup()
+
     createTmpDir()
     .then (tmpPath) ->
       logger.info "Tmp folder created at #{tmpPath}"


fix(logger): remove maxArray limit when logging

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -4,6 +4,7 @@
 Csv = require 'csv'
 Promise = require 'bluebird'
 fs = Promise.promisifyAll require('fs')
+util = require('util')
 {ProjectCredentialsConfig} = require 'sphere-node-utils'
 Importer = require './import'
 Exporter = require './export'
@@ -86,6 +87,11 @@
             clientId: client_id
             clientSecret: client_secret
   @run: (argv) ->
+
+    _consoleWarnAllResults = (result) ->
+      # print out full response array by passing util.inspect
+      # with maxArrayLength (default is 100)
+      console.warn util.inspect(result, { maxArrayLength: null })

     _subCommandHelp = (cmd) ->
       program.emit(cmd, null, ['--help'])
@@ -180,7 +186,7 @@
           # params: importManager (filePath, isArchived)
           importer.importManager opts.in || opts.csv, opts.zip
             .then (result) ->
-              console.warn result
+              _consoleWarnAllResults result
               process.exit 0
             .catch (err) ->
               console.error err
@@ -238,7 +244,7 @@
               importer.continueOnProblems = opts.continueOnProblems
               importer.changeState(publish, remove, filterFunction)
             .then (result) ->
-              console.warn result
+              _consoleWarnAllResults result
               process.exit 0
             .catch (err) ->
               if err.stack then console.error(err.stack)
@@ -338,7 +344,7 @@
               exporter.exportFull(opts.out, not opts.current)
             )
             .then (result) ->
-              console.warn result
+              _consoleWarnAllResults result
               process.exit 0
             .catch (err) ->
               if err.stack then console.error(err.stack)
@@ -387,7 +393,7 @@
           exporter = new Exporter options
           exporter.createTemplate(opts.languages, opts.out, opts.all)
           .then (result) ->
-            console.warn result
+            _consoleWarnAllResults result
             process.exit 0
           .catch (err) ->
             console.error err


:shirt:

:shirt:

--- src/main-process/atom-application.coffee
+++ src/main-process/atom-application.coffee
@@ -188,7 +188,7 @@
         window.browserWindow.removeListener 'blur', blurHandler
       window.browserWindow.webContents.once 'did-finish-load', => @saveState(false)

-  getAllWindows: () =>
+  getAllWindows: =>
     @windows.all().slice()

   getLastFocusedWindow: (predicate) =>
@@ -669,9 +669,9 @@
     if parsedUrl.host is 'core'
       predicate = require('../core-uri-handlers').windowPredicate(parsedUrl)
       bestWindow = @getLastFocusedWindow (win) ->
-        !win.isSpecWindow() && predicate(win)
-
-    bestWindow ?= @getLastFocusedWindow (win) -> !win.isSpecWindow()
+        not win.isSpecWindow() and predicate(win)
+
+    bestWindow ?= @getLastFocusedWindow (win) -> not win.isSpecWindow()
     if bestWindow?
       bestWindow.sendURIMessage url
       bestWindow.focus()
@@ -686,7 +686,7 @@
       windowDimensions = @getDimensionsForNewWindow()
       win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env})
       @windows.addWindow(win)
-      win.on 'window:loaded', =>
+      win.on 'window:loaded', ->
         win.sendURIMessage url

   findPackageWithName: (packageName, devMode) ->
@@ -910,8 +910,8 @@
     @windows.splice(currentIndex, 1) if currentIndex > -1

   getLastFocusedWindow: (predicate) =>
-    predicate ?= (win) => true
+    predicate ?= (win) -> true
     @windows.find(predicate)

-  all: () =>
+  all: =>
     @windows


Fix backtrace with protobufjs

Fix backtrace with protobufjs

--- drivers/javascript/util.coffee
+++ drivers/javascript/util.coffee
@@ -110,7 +110,8 @@
                 if frame.type is "POS"
                     parseInt frame.pos
                 else
-                    frame.opt
+                    # protobufjs returns Long object that we need to convert to number
+                    frame.pos.toInt()
         new ErrClass msg, root, bt

 module.exports.deconstructDatum = deconstructDatum


fix example

fix example

--- example/examTabPane5.cjsx
+++ example/examTabPane5.cjsx
@@ -1,6 +1,7 @@
 React = require "react"
 {
-  Input, RadioGroup, Button, Options
+  Input, Radio, TextArea, CheckBox
+  RadioGroup, Button, Options
 } = require "../src/photon.coffee"

 module.exports = React.createClass
@@ -25,13 +26,13 @@
           placeholder="Email" label="Email Address" />
         <Input ref="password" type="password"
           placeholder="Password" label="password" />
-        <Input ref="description" type="textarea" rows=3 label="Description" />
+        <TextArea ref="description" rows=3 label="Description" />
         <Options ref="options" items={options} />
-        <Input ref="cb1" type="checkbox" label="This is a checkbox" />
-        <Input ref="cb2" type="checkbox" label="This is a checkbox too" />
+        <CheckBox ref="cb1" label="This is a checkbox" />
+        <CheckBox ref="cb2" label="This is a checkbox too" />
         <RadioGroup ref="radios" name="radios">
-          <Input type="radio" checked label="Keep your options open" />
-          <Input type="radio" label="More options" />
+          <Radio checked label="Keep your options open" />
+          <Radio label="More options" />
         </RadioGroup>
         <div className="form-actions pull-right">
           <Button form type="submit"


better

better

--- lib/eslogger.coffee
+++ lib/eslogger.coffee
@@ -260,7 +260,7 @@
     content = @html_head(room)
     nav = " - Day before"
     if diffdays isnt 0
-      nav += " - Day after"
+      nav += " - Day after"
     content += """

             #{start_date} until #{stop_date} - Times are UTC (now is #{time} UTC)


Refactor writePage function into a few smaller chunks

--- src/generate.coffee
+++ src/generate.coffee
@@ -182,62 +182,76 @@
     # Run conversion
     convertContent(ext, page.content, mergedPlugins.converters)
       .then (result) ->
-        log.verbose "generate", "Processing %s (%s)", page.title, page.url
-
-        page.content = result.content
-        newExt = result.ext or page.ext
-        paginator = page.paginator or {}
-
-        # Pretty URLs don't get extensions
-        if config.pretty_urls and newExt is '.html'
-          page.url = helpers.stripExtension page.url
-        else if newExt isnt ext
-          # Update page url with new extension
-          page.url = (helpers.stripExtension page.url) + newExt
-
-        # Strip out index.html
-        if path.basename(page.url, '.html') is 'index'
-          page.url = path.dirname page.url
-
-        # Set up correct path / URL
-        outpath = path.join config.destination, page.url
-        unless path.extname page.url
-          if newExt is '.html'
-            outpath = path.join config.destination, page.url, 'index.html'
-
-        context.setLocals 'page', page
-        context.setLocals 'paginator', paginator
-
-        # Content may contain liquid directives (such as a post listing). Process
-        # now before layout
-        render = tinyliquid.compile page.content, liquidOptions
-        Q.nfcall render, context
-      .then ->
-        log.silly "generate", "Rendered page content for %s", page.url
+        processConvertedPage(result, page, config)
+      .then (outputPath) ->
+        outpath = outputPath
+        # Content may contain liquid directives (such as a post listing)
+        # Process now before layout
+        renderLiquidPostContent(page, context, liquidOptions)
+      .then (content) ->
+        log.silly "generate", "Rendered page content for %s: %s", page.url, content
         page.content = context.clearBuffer().toString()
-
-        # If there's no layout, then we're done
-        unless page.layout and page.layout of compiledLayouts
-          log.verbose "generate", "Writing file without layout: %s", outpath
-          return Q.nfcall fs.outputFile, outpath, page.content
-
-        log.verbose "generate", "Applying layout %s to %s", page.layout, page.url
-        template = compiledLayouts[page.layout]
-        context.setLocals 'content', page.content
-        Q.nfcall template, context
-      .then ->
+        renderPostLayout(page, compiledLayouts, context)
+      .then (contents) ->
         # Write file
         log.verbose "generate", "Writing page: %s", outpath
-        Q.nfcall fs.outputFile, outpath, context.clearBuffer()
-
-          # TODO: Re-enable the error checking that happens on tinyliquid
-          # compilation
-          #.fail (err) ->
-            #log.verbose "generate", "Tinyliquid compile error: %s", err.message
-            #throw new Error "Error while processing #{page.url}: #{err.message}"
-          #.fail (err) ->
-            #log.verbose "generate", "Tinyliquid compile error: %s", err.message
-            #throw new Error "Error while processing #{page.url}: #{err.message}"
+        Q.nfcall(fs.outputFile, outpath, contents)
+
+processConvertedPage = (result, page, config) ->
+  log.verbose "generate", "Processing %s (%s)", page.title, page.url
+
+  ext = path.extname page.path
+  page.content = result.content
+  newExt = result.ext
+
+  # Pretty URLs don't get extensions
+  if config.pretty_urls and newExt is '.html'
+    page.url = helpers.stripExtension page.url
+  else if newExt isnt ext
+    # Update page url with new extension
+    page.url = (helpers.stripExtension page.url) + newExt
+
+  # Strip out index.html
+  if path.basename(page.url, '.html') is 'index'
+    page.url = path.dirname page.url
+
+  # Set up correct path / URL
+  outpath = path.join config.destination, page.url
+  unless path.extname page.url
+    if newExt is '.html'
+      outpath = path.join config.destination, page.url, 'index.html'
+
+  outpath
+
+renderLiquidPostContent = (page, context, liquidOptions) ->
+  context.setLocals 'page', page
+  context.setLocals 'paginator', page.paginator or {}
+
+  render = tinyliquid.compile page.content, liquidOptions
+  Q.nfcall(render, context)
+    .then(
+      -> return context.clearBuffer().toString()
+      (err) ->
+        log.warn "generate", "Tinyliquid compile error: %s", err.message
+        throw new Error "Liquid error from #{page.url}: #{err.message}"
+    )
+
+renderPostLayout = (page, compiledLayouts, context) ->
+  # If there's no layout, then we just return the content
+  unless page.layout and page.layout of compiledLayouts
+    log.verbose "generate", "No layout for: %s", page.path
+    return page.content
+
+  log.verbose "generate", "Applying layout %s to %s", page.layout, page.url
+  template = compiledLayouts[page.layout]
+  context.setLocals 'content', page.content
+  Q.nfcall(template, context)
+    .then(
+      -> context.clearBuffer()
+      (err) ->
+        log.warn "generate", "Tinyliquid template error: %s", err.message
+        throw new Error "Liquid error from #{page.url}: #{err.message}"
+    )

 writeFiles = (bundle) ->
   log.verbose "generate", "Writing static files: %j", bundle.site.static_files


Fixed Java not running on Windows

Fixed Java not running on Windows

Windows users still need to add jdk path (...\jdk1.x.x_xx\bin) to their system environment variables.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -141,12 +141,20 @@
       args: (context) -> [context.filepath]

   Java:
-    "File Based":
-      command: "bash"
-      args: (context) ->
-        className = context.filename.replace /\.java$/, ""
-        args = ['-c', "javac -d /tmp '#{context.filepath}' && java -cp /tmp #{className}"]
-        return args
+    if GrammarUtils.OperatingSystem.isWindows()
+      "File Based":
+        command: "cmd"
+        args: (context) ->
+          className = context.filename.replace /\.java$/, ""
+          args = ["/c javac -Xlint #{context.filename} && start cmd /k java #{className}"]
+          return args
+    else
+      "File Based":
+        command: "bash"
+        args: (context) ->
+          className = context.filename.replace /\.java$/, ""
+          args = ['-c', "javac -d /tmp '#{context.filepath}' && java -cp /tmp #{className}"]
+          return args

   JavaScript:
     "Selection Based":


Implemented AXS (SBX) opcode.

Implemented AXS (SBX) opcode.

--- emulator/core/units/cpu.coffee
+++ emulator/core/units/cpu.coffee
@@ -596,6 +596,9 @@
     RRA: (address) =>
         @$addValueToAccumulator @ROR address

+    AXS: (address) => # Also known as SBX
+        @registerX = @compareRegisterAndMemory @accumulator & @registerX, address
+
     ###########################################################
     # Instruction helper functions
     ###########################################################
@@ -629,6 +632,7 @@
         result = register - operand
         @carryFlag = result >= 0 # Unsigned comparison (bit 8 is actually the result sign)
         @$updateZeroAndNegativeFlag result # Not a signed comparison
+        result & 0xFF

     branchIf: (condition, address) ->
         if condition
@@ -1015,6 +1019,8 @@
         @registerOperation 0x63, @RRA, @indirectXMode, 1, 1 # 8 cycles (undocumented operation)
         @registerOperation 0x73, @RRA, @indirectYMode, 1, 1 # 8 cycles (undocumented operation)

+        @registerOperation 0xCB, @AXS, @immediateMode, 0, 0 # 2 cycles (undocumented operation)
+
     registerOperation: (operationCode, instruction, addressingMode, emptyReadCycles, emptyWriteCycles) ->
         @operationsTable[operationCode] =
             instruction: instruction


changes

changes


Add button in log-view to toggle case sensitivity

--- lib/log-view.coffee
+++ lib/log-view.coffee
@@ -38,6 +38,8 @@
             @button outlet: 'filterButton', class: 'btn', 'Filter'
           @div class: 'btn-group', =>
             @button outlet: 'tailButton', class: 'btn btn-icon icon-arrow-down'
+          @div class: 'btn-group', =>
+            @button outlet: 'caseSensistiveButton', class: 'btn', 'Aa'
           @div class: 'btn-group btn-group-level', =>
             @button outlet: 'levelVerboseButton', class: 'btn log-verbose', 'V'
             @button outlet: 'levelInfoButton', class: 'btn log-info', 'I'
@@ -69,6 +71,8 @@
       title: "Filter Log Lines"
     @disposables.add atom.tooltips.add @tailButton,
       title: "Tail On File Changes"
+    @disposables.add atom.tooltips.add @caseSensistiveButton,
+      title: "Toggle case sensitivity"
     @disposables.add atom.tooltips.add @levelVerboseButton,
       title: "Toggle Verbose Level"
     @disposables.add atom.tooltips.add @levelInfoButton,
@@ -92,6 +96,7 @@

     @filterButton.on 'click', => @confirm()
     @tailButton.on 'click', => @toggleTail()
+    @caseSensistiveButton.on 'click', => @toggleCaseSensitivity()
     @levelVerboseButton.on 'click', => @toggleButton('verbose')
     @levelInfoButton.on 'click', => @toggleButton('info')
     @levelDebugButton.on 'click', => @toggleButton('debug')
@@ -116,6 +121,11 @@
     @updateButtons()
     @tail()

+  toggleCaseSensitivity: ->
+    atom.config.set('language-log.caseInsensitive', !atom.config.get('language-log.caseInsensitive'))
+    @updateButtons()
+    @confirm()
+
   toggleButton: (level) ->
     @settings[level] = if @settings[level] then false else true
     @updateButtons()
@@ -123,6 +133,7 @@

   updateButtons: ->
     @tailButton.toggleClass('selected', atom.config.get('language-log.tail'))
+    @caseSensistiveButton.toggleClass('selected', !atom.config.get('language-log.caseInsensitive'))
     @levelVerboseButton.toggleClass('selected', @settings.verbose)
     @levelInfoButton.toggleClass('selected', @settings.info)
     @levelDebugButton.toggleClass('selected', @settings.debug)


Return silently if no WebSockets support

--- client.coffee
+++ client.coffee
@@ -4,7 +4,9 @@

 connect = (opts={}) ->
   # if user is running mozilla then use it's built-in WebSocket
-  window.WebSocket ||= window.MozWebSocket
+  WebSocket = window.WebSocket || window.MozWebSocket
+
+  return unless WebSocket

   connection = new WebSocket('ws://' + (opts.host || document.domain || 'localhost') + ':'+(opts.port || 8081))


Fixed issue with button not updating due to checking messages

Which doesn't exist anymore. Also fixed an issue with innerText in
Firefox which doesn't update the button's text.

--- lib/ecrire/app/assets/javascripts/admin/editor/navigation/save.js.coffee
+++ lib/ecrire/app/assets/javascripts/admin/editor/navigation/save.js.coffee
@@ -9,7 +9,7 @@
     @on 'click', @button, @save
     @on 'beforeunload', window, @confirm

-    @button.innerText = @button.getAttribute('persisted')
+    @button.textContent = @button.getAttribute('persisted')
     @refresh()
     @cache()
     @interval = setInterval(@refresh, 1000)
@@ -28,7 +28,7 @@
       if refresh
         cache = PostBody.instance.toString()
         @button.setAttribute('disabled', 'disabled')
-        @button.innerText = @button.getAttribute('persisted')
+        @button.textContent = @button.getAttribute('persisted')
       cache
     @cache()

@@ -42,10 +42,7 @@
     xhr.send()

   saved: (e) =>
-    if e.MessageHTML
-      event = new CustomEvent('Editor:message', { bubbles: true})
-      event.MessageHTML = e.MessageHTML
-      @element().dispatchEvent(event)
+    if e.UpdatedAtTime
       @time.setAttribute('time', e.UpdatedAtTime)
       @refresh()
       @cache(true)
@@ -54,8 +51,8 @@
     return unless @cache()?
     if @cache() != PostBody.instance.toString()
       @button.removeAttribute('disabled')
-      @button.innerText = @button.getAttribute('dirty')
+      @button.textContent = @button.getAttribute('dirty')
     else
       @button.setAttribute('disabled', 'disabled')
-      @button.innerText = @button.getAttribute('persisted')
+      @button.textContent = @button.getAttribute('persisted')


Activity: sidebar sections' all/new link routes added

--- client/Social/Activity/routes.coffee
+++ client/Social/Activity/routes.coffee
@@ -17,14 +17,11 @@
         router.loadContent name, section, slug, route, query, passOptions

-  handleChannel = (type, slug) ->
+  handleChannel = (type, slug, callback) ->
+
     {appManager} = KD.singletons
-    appManager.open 'Activity', (app) -> app.getView().open type, slug
-
-
-  handleNewMessage = (type, slug) ->
-    {appManager} = KD.singletons
-    appManager.open 'Activity', (app) -> app.getView().showNewMessageModal()
+    callback    ?= (app) -> app.getView().open type, slug
+    appManager.open 'Activity', callback

   KD.registerRoutes 'Activity',
@@ -37,7 +34,14 @@
     '/:name?/Activity/Post/:slug?' : ({params:{name, slug}, query}) ->
       handleChannel 'post', slug

-    '/:name?/Activity/Message/New' : handleNewMessage
+    '/:name?/Activity/Message/New' : ->
+      handleChannel null, null, (app) -> app.getView().showNewMessageModal()
+
+    '/:name?/Activity/Topic/All' : ({params:{name, slug}, query}) ->
+      handleChannel null, null, (app) -> app.getView().showAllTopics()
+
+    '/:name?/Activity/Post/All' : ({params:{name, slug}, query}) ->
+      handleChannel null, null, (app) -> app.getView().showAllPosts()

     '/:name?/Activity/Message/:slug?' : ({params:{name, slug}, query}) ->
       handleChannel 'message', slug


Fix indent for tips

Fix indent for tips


add  snippet (#159)

add  snippet (#159)

--- snippets/language-coursescript.cson
+++ snippets/language-coursescript.cson
@@ -1829,3 +1829,100 @@
       Text: $4
       Text: $5
    """
+  'AIXPA2PReveal':
+    'prefix': 'aixpa2preveal'
+    'body':"""
+      [TYPE AIXPA2PReveal]
+      Video(stream=true): $1
+      Video(stream=true): $2
+      Video(stream=true): $3
+      Video(stream=true): $4
+      Video(stream=true): $5
+      Pic(background=true): $6
+      QuestionPart:
+        Pic: $7
+        coordinate: x=$8,y=$9
+        Opts:
+        Mix:
+          Pic: $10
+          coordinate: x=$11,y=$12
+          Pic(sr_start=true): $13
+          Pic(sr_end=true): $14
+      \"""
+      display_layout: REGULAR
+      \"""
+    """
+  'AIXA2PMole':
+    'prefix': 'aixa2pmole'
+    'body':"""
+      [TYPE AIXA2PMole]
+      Video(stream=true): $1
+      Video(stream=true): $2
+      Video(stream=true): $3
+      Video(stream=true): $4
+      Video(stream=true): $5
+      Pic(background=true): $6
+      QuestionPart:
+        Audio: $7
+        Opts:
+        Mix:
+          Audio(sound_effect=true): $8
+          Animation:
+          Pic(checked=true): $9
+          Pic: $10
+        Mix:
+          Audio(sound_effect=true): $11
+          Animation:
+          Pic(checked=true): $12
+          Pic: $13
+        Mix:
+          Audio(sound_effect=true): $14
+          Animation:
+          Pic(checked=true): $15
+          Pic: $16
+        Mix:
+          Audio(sound_effect=true): $17
+          Animation:
+          Pic(checked=true): $18
+          Pic: $19
+      \"""
+      display_layout: REGULAR
+      \"""
+    """
+  'AIXT2PMole':
+    'prefix': 'aixt2pmole'
+    'body':"""
+      [TYPE AIXT2PMole]
+      Video(stream=true): $1
+      Video(stream=true): $2
+      Video(stream=true): $3
+      Video(stream=true): $4
+      Video(stream=true): $5
+      Pic(background=true): $6
+      QuestionPart:
+        Text: $7
+        Opts:
+        Mix:
+          Audio(sound_effect=true): $8
+          Animation:
+          Pic(checked=true): $9
+          Pic: $10
+        Mix:
+          Audio(sound_effect=true): $11
+          Animation:
+          Pic(checked=true): $12
+          Pic: $13
+        Mix:
+          Audio(sound_effect=true): $14
+          Animation:
+          Pic(checked=true): $15
+          Pic: $16
+        Mix:
+          Audio(sound_effect=true): $17
+          Animation:
+          Pic(checked=true): $18
+          Pic: $19
+      \"""
+      display_layout: REGULAR
+      \"""
+    """


:lipstick:

:lipstick:

--- spec/atom/root-view-spec.coffee
+++ spec/atom/root-view-spec.coffee
@@ -43,8 +43,7 @@
       describe "when there is a project", ->
         it "shows the FileFinder when it is not on screen and hides it when it is", ->
           runs ->
-            $('#jasmine-content').append(rootView)
-
+            rootView.attachToDom()
             expect(rootView.find('.file-finder')).not.toExist()

           waitsForPromise ->


remove unnecessary assignment

remove unnecessary assignment

$.extend extends @myChart.options (target) with data.options.

--- bar_chart.coffee
+++ bar_chart.coffee
@@ -45,6 +45,6 @@
     if @myChart
       if data.labels then @myChart.data.labels = data.labels
       if data.datasets then @myChart.data.datasets = data.datasets
-      if data.options then @myChart.options = $.extend(@myChart.options, data.options)
+      if data.options then $.extend(@myChart.options, data.options)

       @myChart.update()


Change random operations generation strategy, in dev mode.

--- server/lib/weboob-mock.coffee
+++ server/lib/weboob-mock.coffee
@@ -137,12 +137,11 @@
     operations = []
     count = 3
     i = count
-    account = selectRandomAccount uuid
     while i--
-        operations.push generateOne account
+        operations.push generateOne selectRandomAccount uuid
         count++
-    while rand(0, 100) > 80 and count < 8
-        operations.push generateOne account
+    while rand(0, 100) > 70 and count < 8
+        operations.push generateOne selectRandomAccount uuid
         count++
     console.log 'generated', count, 'operations'
     operations


Remove stdout noise

Remove stdout noise

--- spec/jasmine-test-runner.coffee
+++ spec/jasmine-test-runner.coffee
@@ -48,8 +48,6 @@
     fileBase = path.basename(process.env.TEST_JUNIT_XML_PATH, '.xml')

     jasmineEnv.addReporter new JasmineJUnitReporter(outputDir, true, false, fileBase, true)
-  else
-    process.stdout.write "No JUnit XML\n"

   jasmineEnv.setIncludedTags([process.platform])


Add specs

Add specs

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -653,6 +653,32 @@
         assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '.', code: 'NumpadDecimal', modifierState: {NumLock: true}})), '.')
         assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '', code: 'NumpadDecimal', modifierState: {NumLock: false}})), 'delete')

+    describe "when numlock is on", ->
+      it "translates numpad digits using KeyboardEvent.code", ->
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '1', code: 'Numpad0', modifierState: {NumLock: true}})), 'numpad0')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '2', code: 'Numpad1', modifierState: {NumLock: true}})), 'numpad1')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '3', code: 'Numpad2', modifierState: {NumLock: true}})), 'numpad2')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '4', code: 'Numpad3', modifierState: {NumLock: true}})), 'numpad3')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '5', code: 'Numpad4', modifierState: {NumLock: true}})), 'numpad4')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '6', code: 'Numpad5', modifierState: {NumLock: true}})), 'numpad5')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '6', code: 'Numpad6', modifierState: {NumLock: true}})), 'numpad6')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '7', code: 'Numpad7', modifierState: {NumLock: true}})), 'numpad7')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '8', code: 'Numpad8', modifierState: {NumLock: true}})), 'numpad8')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '9', code: 'Numpad9', modifierState: {NumLock: true}})), 'numpad9')
+
+    describe "when numlock is off", ->
+      it "doesn't translate numpad digits", ->
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '1', code: 'Numpad0', modifierState: {NumLock: false}})), 'insert')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '2', code: 'Numpad1', modifierState: {NumLock: false}})), 'end')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '3', code: 'Numpad2', modifierState: {NumLock: false}})), 'down')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '4', code: 'Numpad3', modifierState: {NumLock: false}})), 'pagedown')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '5', code: 'Numpad4', modifierState: {NumLock: false}})), 'left')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '6', code: 'Numpad5', modifierState: {NumLock: false}})), 'clear')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '6', code: 'Numpad6', modifierState: {NumLock: false}})), 'right')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '7', code: 'Numpad7', modifierState: {NumLock: false}})), 'home')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '8', code: 'Numpad8', modifierState: {NumLock: false}})), 'up')
+        assert.equal(keymapManager.keystrokeForKeyboardEvent(buildKeydownEvent({key: '9', code: 'Numpad9', modifierState: {NumLock: false}})), 'pageup')
+
     describe "when the Dvorak QWERTY-⌘ layout is in use on macOS", ->
       it "uses the US layout equivalent when the command key is held down", ->
         mockProcessPlatform('darwin')


--- spec/notice/message_spec.coffee
+++ spec/notice/message_spec.coffee
@@ -42,6 +42,18 @@
             property1: 'value1'

         done()
+
+
+    it 'predefined default values are overridden', (done) ->
+
+        Message = message
+            properties:
+                prop: 1
+
+        m = new Message prop: 2
+        m.prop.should.eql 2
+        done()
+

 xdescribe 'Message', ->


Fix spurious rename

Fix spurious rename

--- spec/decoration-manager-spec.coffee
+++ spec/decoration-manager-spec.coffee
@@ -51,7 +51,7 @@
       layer1Marker.destroy()
       expect(->
         decorationManager.decorateMarker(layer1Marker, {type: 'overlay', item: document.createElement('div')})
-      ).toThrow("Cannot decorate a destroyed layer1Marker")
+      ).toThrow("Cannot decorate a destroyed marker")
       expect(decorationManager.getOverlayDecorations()).toEqual []

     it "does not allow destroyed marker layers to be decorated", ->


test(promise): add test covering chaining of promises

--- specs/units/promise.spec.coffee
+++ specs/units/promise.spec.coffee
@@ -39,3 +39,51 @@

     describe '::isPending', ->
       itsReturn -> should equal true
+
+    context 'when chained using then', ->
+      given 'firstPromise', -> new spectacular.Promise
+      given 'secondPromise', -> @firstPromise.then ->
+      given 'thirdPromise', -> @secondPromise.then ->
+
+      context 'when the first promise is fulfilled', ->
+        before -> @firstPromise.resolve()
+
+        the 'secondPromise', -> @secondPromise.should be 'fulfilled'
+        the 'thirdPromise', -> @thirdPromise.should be 'fulfilled'
+
+      context 'when the first promise is rejected', ->
+        before -> @firstPromise.reject('message')
+
+        the 'secondPromise', -> @secondPromise.should be 'rejected'
+        the 'thirdPromise', -> @thirdPromise.should be 'rejected'
+
+      context 'and one of the factory return a promise', ->
+        given 'firstPromise', -> new spectacular.Promise
+        given 'returnedPromise', -> new spectacular.Promise
+        given 'secondPromise', -> @firstPromise.then => @returnedPromise
+        given 'thirdPromise', -> @secondPromise.then ->
+
+        context 'when the first promise is fulfilled', ->
+          before -> @firstPromise.resolve()
+
+          the 'secondPromise', -> @secondPromise.shouldnt be 'fulfilled'
+          the 'thirdPromise', -> @thirdPromise.should be 'pending'
+
+          context 'and the returned promise is fulfilled', ->
+            before -> @returnedPromise.resolve()
+
+            the 'secondPromise', -> @secondPromise.should be 'fulfilled'
+            the 'thirdPromise', -> @thirdPromise.should be 'fulfilled'
+
+          context 'and the returned promise is rejected', ->
+            before -> @returnedPromise.reject('message')
+
+            the 'secondPromise', -> @secondPromise.should be 'rejected'
+            the 'thirdPromise', -> @thirdPromise.should be 'rejected'
+
+        context 'when the first promise is rejected', ->
+          before -> @firstPromise.reject('message')
+
+          the 'secondPromise', -> @secondPromise.should be 'rejected'
+          the 'thirdPromise', -> @thirdPromise.should be 'rejected'
+


Remove trailing semicolon

Remove trailing semicolon

--- spec/pane-spec.coffee
+++ spec/pane-spec.coffee
@@ -1127,7 +1127,7 @@

     it "restores the correct item when it doesn't implement getURI() and some items weren't deserialized", ->
       unserializable = {}
-      pane.addItem(unserializable, 0);
+      pane.addItem(unserializable, 0)
       pane.items[2].getURI = null
       pane.activateItemAtIndex(2)
       newPane = Pane.deserialize(pane.serialize(), atom)


docco now handles // -style javascript comments as well.

--- docco.coffee
+++ docco.coffee
@@ -92,8 +92,8 @@
   save docs_text, code_text
   sections

-# Does the line begin with a comment?
-comment_matcher: /^\s*#\s?/
+# Does the line begin with a comment? Handle `#` and `//` -style comments.
+comment_matcher: /^\s*(#|\/\/)\s?/

 # Compute the destination HTML path for an input source file.
 destination: (filepath) ->


Redraw view when content has loaded

Redraw view when content has loaded

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -31,7 +31,7 @@
       super()
       @hideProgress = options.hideProgress

-      @listenTo(@model, 'change:currentPage removeNode moveNode add:contents', @render)
+      @listenTo(@model, 'change:loaded change:currentPage removeNode moveNode add:contents', @render)

     events:
       'click .next': 'nextPage'


Fix CJS require in performance test

Fix CJS require in performance test

--- performance/PerformanceTestCases.coffee
+++ performance/PerformanceTestCases.coffee
@@ -1,4 +1,4 @@
-Bacon = (require "../src/bacon").Bacon
+Bacon = (require "../src/bacon").default

 class Generator
   constructor: ->


Revert "Revert "Add root node to POST contributions/""

This reverts commit cffb43d652f57fa64f7bfc630d214f33206ddae0.

--- src/app/config/restangular.coffee
+++ src/app/config/restangular.coffee
@@ -14,3 +14,13 @@
     if operation is "getList"
       return response.data[what]

+  RestangularProvider.setRequestInterceptor (element, operation, what, url) ->
+    if operation is "post"
+      # Add singular root node to element
+      newElem = {}
+      newElem[what.substring(0, what.length-1)] = element
+      return newElem
+    if operation is "update"
+      # Trim the 's' off so we can reference the singular root note name
+      # This is a total hack and won't work for resources like "people"
+      return response.data[what.substring(0, what.length-1)]


prevent cdn cache

prevent cdn cache

--- firefox/lib/lixian.coffee
+++ firefox/lib/lixian.coffee
@@ -13,6 +13,15 @@

 current_timestamp = ->
 	new Date().getTime()
+
+
+no_cache = (url) ->
+	if url.indexOf '?' == -1
+		url += '?'
+	else
+		url += '&'
+	url += 'nocache=' + current_timestamp()
+

 ################################################################################
 # utils
@@ -423,7 +432,7 @@
 			type_id = 4
 		page = page_index + 1
 		p = 1 # XXX: what is it?
-		url = "/interface/showtask_unfresh?type_id=#{type_id}&page=#{page}&tasknum=#{page_size}&p=#{p}&interfrom=task"
+		url = no_cache "/interface/showtask_unfresh?type_id=#{type_id}&page=#{page}&tasknum=#{page_size}&p=#{p}&interfrom=task"
 		return url

 	parse_rebuild: (text) ->


do this more elegantly (again)

do this more elegantly (again)


Added hash scan failure condition

Added hash scan failure condition

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

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

-        msg.send summary
+          msg.send summary
+
+        else
+          msg.send "VirusTotal URL Analysis: #{vt_json.verbose_msg}"

   robot.respond /virustotal url (.*)/i, (msg) ->
     url = msg.match[1].toLowerCase()


Add function to add a custom logo (based on a layer)


Some basic heuristics for resolving overlapping highlights.

--- client/lib/pdf-text-highlight.coffee
+++ client/lib/pdf-text-highlight.coffee
@@ -10,10 +10,19 @@
     @_$highlightsLayer = @_$textLayer.prev('.highlights-layer')
     @_highlightsCanvas = @_$highlightsLayer.prev('.highlights-canvas').get(0)

+    @_area = null
     @_hover = null
     @_$highlight = null

     @_createHighlight()
+
+  _computeArea: (segments) =>
+    @_area = 0
+
+    for segment in segments
+      @_area += segment.width * segment.height
+
+    return # Don't return the result of the for loop

   _precomputeHover: (segments) =>
     # For now compute simply a bounding box
@@ -74,6 +83,15 @@
     context = @_highlightsCanvas.getContext('2d')
     context.clearRect 0, 0, @_highlightsCanvas.width, @_highlightsCanvas.height

+  _sortHighlights: =>
+    @_$highlightsLayer.find('.highlights-layer-highlight').detach().sort(
+      (a, b) =>
+        # Heuristics, we put smaller highlights later in DOM tree which means they will have higher z-index
+        # The motivation here is that we want higher the highlight which leaves more area to the user to select the other highlight by not covering it
+        # TODO: Should we improve here? For example, compare size of (A-B) and size of (B-A), where A-B is A with (A intersection B) removed
+        $(b).data('highlight')._area - $(a).data('highlight')._area
+    ).appendTo(@_$highlightsLayer)
+
   _createHighlight: =>
     scrollLeft = $(window).scrollLeft()
     scrollTop = $(window).scrollTop()
@@ -97,6 +115,7 @@
       width: rect.width
       height: rect.height

+    @_computeArea segments
     @_precomputeHover segments

     @_$highlight = $('<div/>').addClass('highlights-layer-highlight').append(
@@ -116,7 +135,11 @@
         @_$highlight.removeClass 'hovered'
         @_hideHover()

+    @_$highlight.data 'highlight', @
+
     @_$highlightsLayer.append @_$highlight
+
+    @_sortHighlights()

   # React to changes in the underlying annotation
   annotationUpdated: =>


Enhances the unit tests

Enhances the unit tests


add some constraints on modifying patches

--- src/smc-util/syncstring_schema.coffee
+++ src/smc-util/syncstring_schema.coffee
@@ -173,6 +173,19 @@
             check_hook : (db, obj, account_id, project_id, cb) ->
                 # this verifies that user has write access to these patches
                 db._user_set_query_patches_check(obj, account_id, project_id, cb)
+            before_change : (database, old_val, new_val, account_id, cb) ->
+                if old_val?
+                    if old_val.sent? and new_val.sent? and new_val.sent != old_val.sent
+                        cb("you may not change the sent time once it is set")
+                        return
+                    if old_val.user? and new_val.user? and old_val.user != new_val.user
+                        cb("you may not change the author of a patch from #{old_val.user} to #{new_val.user}")
+                        return
+                    if old_val.patch? and new_val.patch? and old_val.patch != new_val.patch
+                        cb("you may not change a patch")
+                        return
+                else
+                    cb()

 schema.patches.project_query = schema.patches.user_query


Fix: effectively destroy bank access when no accounts are bound to it

--- server/controllers/bankaccounts.coffee
+++ server/controllers/bankaccounts.coffee
@@ -75,12 +75,14 @@
                     return

                 if accounts.length is 0
-                    BankAccess.find account.bankAccess.id, (err, access) ->
+                    console.log '\t\tNo other bank account bound to this access!'
+                    BankAccess.find account.bankAccess, (err, access) ->
                         if err?
                             callback err
                             return

                         if not access?
+                            console.log '\t\tAccess not found?'
                             callback()
                             return

@@ -88,6 +90,7 @@
                         console.log "\t\t-> Access destroyed"
                         callback()
                 else
+                    console.log '\t\tAt least one other bank account bound to this access.'
                     callback()

     async.series requests, (err, results) ->


Remove unused local var in event stream actions

--- app/assets/javascripts/event_stream_actions.js.coffee
+++ app/assets/javascripts/event_stream_actions.js.coffee
@@ -12,7 +12,6 @@
   updated: (esData)->
     Ember.run =>
       if esData.task
-        phaseId = esData.task.phase_id
         taskId = esData.task.id
         # This is an ember bug.  A task's phase needs to be notified that the other side of
         # the hasMany relationship has changed via set.  Simply loading the updated task into the store


fixing force 2d

fixing force 2d

--- app/assets/javascripts/spree/frontend/lookbook.coffee
+++ app/assets/javascripts/spree/frontend/lookbook.coffee
@@ -23,7 +23,11 @@

     @lookbook = lookbook

+
+
     @force2d = force2d
+
+    console.log '@force2d', @force2d

     @stage = @lookbook.querySelector(".stage")
     @looks = @stage.querySelectorAll(".slide")
@@ -266,10 +270,13 @@
   if(document.querySelector('#lookbook') != null)
     use2d = if window.location.pathname == "/art" then true else false

-    lookbook = new Lookbook(document.querySelector('#lookbook', use2d))
-
-
-
-
-
-
+    console.log 'use2d', use2d
+
+
+    lookbook = new Lookbook(document.querySelector('#lookbook'), use2d)
+
+
+
+
+
+


Fix elm-format tests, support older version format

--- src/beautifiers/elm-format.coffee
+++ src/beautifiers/elm-format.coffee
@@ -15,7 +15,11 @@
       installation: "https://github.com/avh4/elm-format#installation-"
       version: {
         args: ['--help']
-        parse: (text) -> text.match(/elm-format-\d+.\d+ (\d+\.\d+\.\d+)/)[1]
+        parse: (text) ->
+          try
+            return text.match(/elm-format-\d+.\d+ (\d+\.\d+\.\d+)/)[1]
+          catch
+            return text.match(/elm-format (\d+\.\d+\.\d+)/)[1]
       }
     }
   ]


add the family feud nope

add the family feud nope

--- scripts/no-no-no.coffee
+++ scripts/no-no-no.coffee
@@ -1,6 +1,7 @@
 nopes = [
   "http://mlkshk.com/r/MCR5.gif",
-  "http://i.imgur.com/zCtbl.gif"
+  "http://i.imgur.com/zCtbl.gif",
+  "http://mlkshk.com/r/P8AK.gif"
 ]

 module.exports = (robot) ->


state change events

state change events

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -52,7 +52,7 @@
   self.bind "update", ->
     I.updating = true

-    I.objects.invoke ""
+    I.objects.invoke "trigger"

     [I.objects, toRemove] = I.objects.partition (object) ->
       object.update()


fix(feedback-window): Close correctly when in fullscreen mode

- Now shows main window when feedback window is closed/hidden
- Fixes #615

--- src/browser/window-manager.coffee
+++ src/browser/window-manager.coffee
@@ -157,7 +157,7 @@
   Feedback window
   ###

-  showFeedbackWindow: ({x, y, width, height, params}) ->
+  showFeedbackWindow: ({x, y, width, height, params}) =>
     if @feedbackWindow
       @feedbackWindow.show()
     else
@@ -177,11 +177,12 @@
         event.preventDefault()

       # Disable window close, hide instead
-      w.on 'close', (event) ->
+      w.on 'close', (event) =>
         unless global.application.quitting
           event.preventDefault()
           w.hide()
-      w.on 'closed', (event) ->
+          @showMainWindow()
+      w.on 'closed', (event) =>
         @feedbackWindow = null

       w.webContents.on('new-window', onOpenURL)


fix(window-manager): Stop bindng window state to config when quitting

--- src/browser/window-manager.coffee
+++ src/browser/window-manager.coffee
@@ -15,6 +15,7 @@
       @ensurePrimaryWindowOnscreen()

   ensurePrimaryWindowOnscreen: ->
+    return if global.application.quitting
     hasToken = @config.get('nylas.token')
     if hasToken
       @showMainWindow()


ihmmm

ihmmm

--- config/prod.coffee
+++ config/prod.coffee
@@ -3,7 +3,7 @@

 deepFreeze = require 'koding-deep-freeze'

-version = fs.readFileSync nodePath.join(__dirname, '../.revision'), 'utf-8'
+version = "0.9.1c" # fs.readFileSync nodePath.join(__dirname, '../.revision'), 'utf-8'

 # STAGING
 mongo = 'beta_koding_user:lkalkslakslaksla1230000@localhost:27017/beta_koding?auto_reconnect'


fix(editor): handle getAttachment exception

--- app/modules/components/wysiwyg/wysiwyg.service.coffee
+++ app/modules/components/wysiwyg/wysiwyg.service.coffee
@@ -101,8 +101,11 @@
                     if match && match.length == 2
                         tokens = match[1].split(":")

-                        promise = @.getCachedAttachment(tokens).then (url) =>
+                        promise = @.getCachedAttachment(tokens)
+                        .then (url) =>
                             e.setAttribute(tag.attr, url)
+                        .catch () =>
+                            console.warn('attachment ref not found', e.getAttribute(tag.attr))

                         promises.push(promise)


Added trigger for warning against using devs

--- scripts/triggers.coffee
+++ scripts/triggers.coffee
@@ -23,6 +23,7 @@
   '!pb'           : "Please avoid using pastebin.com as it is slow and forces others to look at ads.  Please use #{pasteUrl}.  Thanks!"
   '!rules'        : "You may review our room rules at http://goo.gl/Tl77U2"
   '!docs'         : "The !docs syntax is deprecated, please use `Liona show <user> docs for <topic>`"
+  '!usedev'       : "Only use the dev version you don't mind potentially needing to change your app on updates if the API or concepts change. Stability is not guaranteed."

   # Helpers
   '!ugt'          : "It is always morning when someone comes into a channel. We call that Universal Greeting Time http://www.total-knowledge.com/~ilya/mips/ugt.html"


Add environment variable for response length

--- scripts/watsonqa.coffee
+++ scripts/watsonqa.coffee
@@ -41,13 +41,14 @@
         uri_h = process.env.TRAVELQABOT_URI_SERVER ? "http://localhost:3000"
         uri_d = process.env.TRAVELQABOT_URI_DIR ? "/d"
         link = "#{uri_h}#{uri_d}/#{id}"
+        response_length = process.env.TRAVELQABOT_RESPONSE_LENGTH ? "90"

         watson_answer = JSON.parse(response_string)[0]
         if watson_answer?
           q = watson_answer.question;
           if q
             if q.evidencelist?.length? > 0
-              msg.send "#{q.evidencelist[0].text.substring(0, 90)} #{link}"
+              msg.send "#{q.evidencelist[0].text.substring(0, response_length)} #{link}"
             else if q.errorNotifications? > 0
               msg.send "Error: #{q.errornotifications[0].text}"
             else


draw gorilla on top of a random building


differentiation actually works now

differentiation actually works now

--- coffeequate/src/operators/Pow.coffee
+++ coffeequate/src/operators/Pow.coffee
@@ -396,4 +396,5 @@
 			if @children.right.evaluate?() == 0
 				return new Constant(0)
 			return new Mul(new Pow(@children.left, new Add(@children.right, new Constant(-1))),
-										 @children.differentiate(variable))
+										 @children.left.differentiate(variable),
+										 @children.right).expandAndSimplify()


Responsive videos classes

Responsive videos classes

fixes #70

--- ekko-lightbox.coffee
+++ ekko-lightbox.coffee
@@ -224,7 +224,7 @@
 		width = @checkDimensions width
 		height = width / aspectRatio
 		@resize width
-		@lightbox_body.html ''
+		@lightbox_body.html '

'
 		@options.onContentLoaded.call(@)
 		if @modal_arrows #hide the arrows when showing video
 			@modal_arrows.css 'display', 'none'
@@ -235,7 +235,7 @@
 		width = @checkDimensions width
 		height = width / aspectRatio
 		@resize width
-		@lightbox_body.html ''
+		@lightbox_body.html '

'
 		@options.onContentLoaded.call(@)
 		if @modal_arrows #hide the arrows when showing video
 			@modal_arrows.css 'display', 'none'
@@ -244,7 +244,7 @@
 		width = @$element.data('width') || 612
 		width = @checkDimensions width
 		@resize width
-		@lightbox_body.html ''
+		@lightbox_body.html '

'
 		@options.onContentLoaded.call(@)
 		if @modal_arrows #hide the arrows when showing video
 			@modal_arrows.css 'display', 'none'


Consent to atom telemetry

Consent to atom telemetry

--- .config/atom/config.cson
+++ .config/atom/config.cson
@@ -22,6 +22,7 @@
       "sort-lines"
       "csslint"
     ]
+    telemetryConsent: "limited"
   csslint:
     hideOnNoErrors: true
   editor:


Renamed Feed to RSS

Renamed Feed to RSS

--- .metalsmith/metal.coffee
+++ .metalsmith/metal.coffee
@@ -99,7 +99,7 @@
     .use filename()
     .use feed
       collections: ['guide', 'faq']
-      output: 'feed.xml'
+      output: 'rss.xml'
       metadata: template: 'feed.toffee'
     .use templates 'toffee'
     .build callback


Added comment

Added comment

--- src/wrathnet/expansions/expansion.coffee
+++ src/wrathnet/expansions/expansion.coffee
@@ -9,5 +9,6 @@
 # this library is licensed. See the LICENSE file for the full license.
 #

+# Denotes an expansion
 class WrathNet.expansions.Expansion


menu: Fix some todos

menu: Fix some todos

--- src/js/menus.coffee
+++ src/js/menus.coffee
@@ -80,6 +80,11 @@
   menu.sections []
   menu.sections sections

+appendItemToSection = (menu, sectionIndex, newItem) ->
+  newItemPosition = (menu.section sectionIndex).items.length
+
+  menu.item sectionIndex, newItemPosition, newItem
+
 isNextEpisodeForItemAired = (item) ->
   return false unless item.next_episode?
   if item.next_episode.season > item.seasons.length
@@ -174,7 +179,7 @@
               return
             trakttv.fetchShowProgress data.showID,
               (err, show) =>
-                # TODO: if err, reset the checked flag and subtitles
+                # TODO: display notification
                 return if err?
                 if not isNextEpisodeForItemAired(show)
                   return
@@ -190,8 +195,7 @@
                   false
                 )

-                # TODO: use a function to add items
-                @menu.item e.sectionIndex, e.section.items.length, newItem
+                appendItemToSection(@menu, e.sectionIndex, newItem)

     @menu.on 'select', (e) =>
@@ -373,7 +377,6 @@

             episodesMenu.on 'select', (e) ->
               data = e.item.data
-              # TODO: colorize this card
               detailedItemCard = createDefaultCard
                 title: showTitle
                 subtitle: "Season #{data.seasonNumber} Ep. #{data.episodeNumber}"


Include only nodes being part of ways

Include only nodes being part of ways

--- webpack/src/index.coffee
+++ webpack/src/index.coffee
@@ -3,9 +3,22 @@
 require('leaflet/dist/leaflet.css')
 require('leaflet')

+overpass_query = (latlng) ->
+  lat = latlng.lat
+  lng = latlng.lng
+  """
+  [out:json];
+  way(around:100,#{lat},#{lng})->.ways;
+  node(around:100,#{lat},#{lng})->.nodes;
+  .ways >->.way_nodes;
+  node.nodes.way_nodes;
+  out;
+  """
+
 findClosestTo = (origin, parsed) ->
   minDistance = 1000
   found = null
+  debugger
   for element in parsed.elements
     elemLatLng = new L.LatLng(element.lat, element.lon)
     if origin.distanceTo(elemLatLng) < minDistance
@@ -21,7 +34,8 @@

   map.on('click', (ev) ->
     latlng = ev.latlng
-    fetch("http://overpass-api.de/api/interpreter?data=[out:json];node(around:100,#{latlng.lat},#{latlng.lng});out;")
+    # TODO: handle no nodes returned
+    fetch("http://overpass-api.de/api/interpreter?data=#{overpass_query(latlng)}")
       .then((response) => response.text())
       .then((body) =>
         found = findClosestTo(latlng, JSON.parse(body))


Skip all the additional loop logic.

Skip all the additional loop logic.

--- src/view/bindings/form_binding.coffee
+++ src/view/bindings/form_binding.coffee
@@ -21,17 +21,15 @@
     selectedNodes = Batman.DOM.querySelectorAll(@node, selectors.join(', '))
     attributeName = "data-addclass-#{@errorClass}"

-    for selectedNode in selectedNodes
+    for selectedNode in selectedNodes when not selectedNode.getAttribute(attributeName)
       binding = selectedNode.getAttribute('data-bind')
       field = binding.substr(binding.indexOf(attribute) + attribute.length + 1)

       index = field.indexOf('|')
       field = field.substr(0, index) if index != -1
-
       field = field.trim()

-      if not selectedNode.getAttribute(attributeName)
-        selectedNode.setAttribute(attributeName, "#{attribute}.errors.#{field}.length")
+      selectedNode.setAttribute(attributeName, "#{attribute}.errors.#{field}.length")

     errorsNode = Batman.DOM.querySelector(@node, '.errors')
     errorsNode.setAttribute('data-showif', "#{attribute}.errors.length") if errorsNode and !errorsNode.getAttribute('data-showif')