Applied coffeelint on ci-hierarchy file.

--- coffee/fields/ci_hierarchy.coffee
+++ coffee/fields/ci_hierarchy.coffee
@@ -66,8 +66,8 @@
           selected_locId = this.getSelectedFieldVal($location_id, field_values)
         if $division_id
           selected_divId = this.getSelectedFieldVal($division_id, field_values)
-      this.populateCompanies(fd_view, selected_compId,
-                             selected_locId, selected_divId)
+      @populateCompanies(fd_view, selected_compId,
+                         selected_locId, selected_divId)

   getSelectedFieldVal: ($ele, fieldValues) ->
     do(name = '', selectedId='') =>
@@ -89,8 +89,8 @@
         fd_view.field.appendData($company_id, companies)
         if selected_compId && selected_compId != ''
           $company_id.val selected_compId
-          this.setSelectedCompAndPopulateLocs(fd_view, selected_compId,
-                                              selected_locId, selected_divId)
+          @setSelectedCompAndPopulateLocs(fd_view, selected_compId,
+                                          selected_locId, selected_divId)

   populateLocationsByCompanyId: (e) ->
     do(selected_company_id = $(e.currentTarget).val(),
@@ -101,10 +101,10 @@

   setSelectedCompAndPopulateLocs: (fd_view, selected_compId,
                                    selected_locId = '', selected_divId = '') ->
-    this.selected_comp = Formbuilder.options.COMPANY_HIERARCHY.getHashObject(
+    @selected_comp = Formbuilder.options.COMPANY_HIERARCHY.getHashObject(
       selected_compId)
-    this.clearSelectFields(fd_view.model.attributes.cid)
-    this.populateLocations(
+    @clearSelectFields(fd_view.model.attributes.cid)
+    @populateLocations(
       fd_view, this.selected_comp, selected_locId, selected_divId)

   populateLocations: (fd_view, selected_comp,
@@ -117,25 +117,25 @@
         locations = selected_comp.locations

       if($location_id && locations.length > 0)
-        this.addPlaceHolder($location_id, '--- Select ---')
-        this.appendData($location_id, locations)
+        @addPlaceHolder($location_id, '--- Select ---')
+        @appendData($location_id, locations)
         if selected_locId && selected_locId != ''
           $location_id.val selected_locId
-          this.setSelectedLocAndPopulateDivs(fd_view, selected_locId,
+          @setSelectedLocAndPopulateDivs(fd_view, selected_locId,
                                              selected_divId)

   populateDivisionsByLocId: (e) ->
     do(selected_location_id = $(e.currentTarget).val(),
       that = e.data.that,
-      fd_view = e.data.fd_view,
-      selected_loc = null
+      fd_view = e.data.fd_view
     ) =>
       that.setSelectedLocAndPopulateDivs(fd_view, selected_location_id)

   setSelectedLocAndPopulateDivs: (fd_view, selected_locId,
                                   selected_divId = '') ->
-    selected_loc = this.selected_comp.locations.getHashObject(selected_locId)
-    this.populateDivisions(fd_view, selected_loc, selected_divId)
+    do(selected_loc = null)=>
+      selected_loc = @selected_comp.locations.getHashObject(selected_locId)
+      @populateDivisions(fd_view, selected_loc, selected_divId)

   populateDivisions: (fd_view, selected_loc, selected_divId = '') ->
     do( divisions = [],
@@ -145,9 +145,9 @@
       if selected_loc
         divisions = selected_loc.divisions
       $division_id.empty()
-      this.addPlaceHolder($division_id, '--- Select ---')
+      @addPlaceHolder($division_id, '--- Select ---')
       if $division_id && divisions.length > 0
-        this.appendData($division_id, divisions)
+        @appendData($division_id, divisions)
         if selected_divId && selected_divId != ''
           $division_id.val selected_divId

@@ -158,9 +158,9 @@
   appendData: ($element, data) ->
     do(appendString = '') =>
       _.each data, (obj_hash) ->
-        appendString = "<option value='" + obj_hash.id + "'>"
-        appendString += obj_hash.name + "</option>"
-        $element.append appendString
+        @appendString = "<option value='" + obj_hash.id + "'>"
+        @appendString += obj_hash.name + "</option>"
+        $element.append @appendString

   addPlaceHolder: ($element, name) ->
     $element.html("<option value=''>"+ name + "</option>")
@@ -185,7 +185,7 @@
   evalCondition: (clicked_element, cid, condition, set_value) ->
     do(check_result = false, $comp = null, $loc = null, $div = null,
        comp_name = '', comp_id = '', loc_id = '', div_id = '',
-       loc_name = '', div_name = '', set_value_toLowerCase = ''
+       loc_name = '', div_name = '', _toLowerCase_set_val = ''
     ) =>
       $comp = clicked_element.find("#company_id_" + cid)
       $loc = clicked_element.find("#location_id_" + cid)


Fix fixture.

Fix fixture.

--- coffee/fixtures/behavior/e.coffee
+++ coffee/fixtures/behavior/e.coffee
@@ -1,3 +1,2 @@
 module.exports =
-  "compomnent e":
-    "does ee": -> makePromise -> "sorry I never return"+  "does ee": -> makePromise -> "sorry I never return"


Cleaned some stuff up

Cleaned some stuff up

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -76,7 +76,7 @@

 class MoveVertically extends Motion
   execute: (count=1) ->
-    {row, column} = @editor.getCursorScreenPosition()
+    {row, column} = @editor.getCursorBufferPosition()

     nextRow = @nextValidRow(count)

@@ -111,25 +111,22 @@
   #
   # count - The number of folded 'buffer' rows away from
   #         the current row.
-  # increment - The direction to move the cursor. Use -1
-  #             for moving up, 1 for moving down.
   #
   # Returns an integer row index.
-  nextValidRowWithIncrement: (count, increment) ->
+  nextValidRow: (count) ->
     {row, column} = @editor.getCursorBufferPosition()
-    e = @editor
-
-    maxRow = @editor.getLineCount()
+
+    maxRow = @editor.getLastBufferRow()
     minRow = 0

-    # For each count, subtract 1 row. Folded rows
-    # count as a single row.
-    _.times count, ->
-      if e.isFoldedAtBufferRow(row)
-        while e.isFoldedAtBufferRow(row)
-          row += increment
-      else
-        row += increment
+    # For each count, add 1 'directionIncrement' to
+    # row. Folded rows count as a single row.
+    _.times count, =>
+      if @editor.isFoldedAtBufferRow(row)
+        while @editor.isFoldedAtBufferRow(row)
+          row += @directionIncrement()
+      else
+        row += @directionIncrement()

     if row > maxRow
       maxRow
@@ -139,8 +136,12 @@
       row

 class MoveUp extends MoveVertically
-  nextValidRow: (count) ->
-    @nextValidRowWithIncrement(count, -1)
+  # Internal: The direction to move the cursor. Use -1
+  # for moving up, 1 for moving down.
+  #
+  # Returns -1
+  directionIncrement: ->
+    -1

   select: (count=1) ->
     _.times count, =>
@@ -148,8 +149,12 @@
       true

 class MoveDown extends MoveVertically
-  nextValidRow: (count) ->
-    @nextValidRowWithIncrement(count, 1)
+  # Internal: The direction to move the cursor. Use -1
+  # for moving up, 1 for moving down.
+  #
+  # Returns 1
+  directionIncrement: ->
+    1

   move: (count) ->
     @editor.moveCursorDown(count)


update routes

update routes

--- app/controllers/routes.coffee
+++ app/controllers/routes.coffee
@@ -4,7 +4,6 @@
 Playlist = require('../models/playlist').Playlist
 Track = require('../models/track').Track
 Getters = require './getter'
-Saver = require './saving_track'

 module.exports = (app) ->


ошибка с вызовом модуля который был удален

--- app/controllers/server.coffee
+++ app/controllers/server.coffee
@@ -2,8 +2,6 @@
 express = 													require('express')
 mongoose = 													require('mongoose')
 http = 														require('http')
-io	 = 														require('socket.io')
-sockjs	 = 													require('sockjs')
 path = 														require('path')
 mkpath = 													require('mkpath')
 colors = 													require('colors')


Preserve arrays

Preserve arrays

--- libraries/settings/Settings.coffee
+++ libraries/settings/Settings.coffee
@@ -4,7 +4,7 @@

 merge = (settings, defaults) ->
 	for key, value of settings
-		if typeof(value) == "object"
+		if typeof(value) == "object" and value not instanceof Array
 			defaults[key] = merge(settings[key], defaults[key] or {})
 		else
 			defaults[key] = value


Add title text to globe nav icon

Add title text to globe nav icon

--- app/partials/main-header.cjsx
+++ app/partials/main-header.cjsx
@@ -76,7 +76,7 @@
       <Link to="lab" className="main-nav-item nav-build"><Translate className="minor" content="mainNav.lab" /></Link>
       {if isAdmin()
         <Link to="admin" className="main-nav-item nav-build"><Translate className="minor" content="mainNav.admin" /></Link>}
-      <TriggeredModalForm trigger={}>
+      <TriggeredModalForm triggerProps={title: "Other Links"}trigger={}>

           <Translate content="mainNav.daily" />
           <Translate content="mainNav.blog" />


Fixing map rotation direction

Fixing map rotation direction

--- src/js/Game.coffee
+++ src/js/Game.coffee
@@ -26,12 +26,12 @@
     @selector      = $(@settings.selectorID)

     $('#game .left').bind 'mousedown', (event) =>
-      @map.rotateCW()
+      @map.rotateCCW()
       @selectBlock null
       @hideSelector()

     $('#game .right').bind 'mousedown', (event) =>
-      @map.rotateCCW()
+      @map.rotateCW()
       @selectBlock null
       @hideSelector()


improved lighting calc, but messed something else up

--- src/js/main.coffee
+++ src/js/main.coffee
@@ -79,9 +79,12 @@

   # Project takes some 3D coordinates and transforms them in 2D
   # coordinates using the transformation matrix
-  project: (coord, transMat) ->
+  project: (coord, transMat, worldMat, normal) ->
     point = Vec4.fromValues(coord[0], coord[1], coord[2], 1)
     point = Vec4.transformMat4(Vec4.create(), point, transMat)
+
+    point3DWorld = Vec3.transformMat4(Vec3.create(), coord, worldMat)
+    normal3DWorld = Vec3.transformMat4(Vec3.create(), normal, worldMat)

     # perspective divide
     w = point[3]
@@ -95,7 +98,11 @@
     # them again to have (0, 0) on top left
     x = ( point[0] * @width  + @width  / 2.0) >> 0
     y = (-point[1] * @height + @height / 2.0) >> 0
-    return Vec3.fromValues(x, y, point[2])
+    return {
+      screen: Vec3.fromValues(x, y, point[2])
+      world: point3DWorld
+      normal: normal3DWorld
+    }

   # drawPoint calls putPixel but does the clipping operation before
   drawPoint: (point, color) ->
@@ -179,42 +186,40 @@

       ### Wireframe, Bresenham's ###
       if WIREFRAME
-        for face in mesh.faces
+        for face, faceIndex in mesh.faces
           vertexA = mesh.vertices[face[0]]
           vertexB = mesh.vertices[face[1]]
           vertexC = mesh.vertices[face[2]]
-          pixelA = @project(vertexA, transformMatrix)
-          pixelB = @project(vertexB, transformMatrix)
-          pixelC = @project(vertexC, transformMatrix)
-          @drawBLine(pixelA, pixelB)
-          @drawBLine(pixelB, pixelC)
-          @drawBLine(pixelC, pixelA)
+          normal = mesh.normals[faceIndex]
+
+          projectedA = @project(vertexA, transformMatrix, worldMatrix, normal)
+          projectedB = @project(vertexB, transformMatrix, worldMatrix, normal)
+          projectedC = @project(vertexC, transformMatrix, worldMatrix, normal)
+          @drawBLine(projectedA.screen, projectedB.screen)
+          @drawBLine(projectedB.screen, projectedC.screen)
+          @drawBLine(projectedC.screen, projectedA.screen)
       else
         for face, faceIndex in mesh.faces
           vertexA = mesh.vertices[face[0]]
           vertexB = mesh.vertices[face[1]]
           vertexC = mesh.vertices[face[2]]
-          pixelA = @project(vertexA, transformMatrix)
-          pixelB = @project(vertexB, transformMatrix)
-          pixelC = @project(vertexC, transformMatrix)
-          # grayVal = (faceIndex % mesh.faces.length) / mesh.faces.length
-          # color = Vec4.fromValues(grayVal, grayVal, grayVal, 1)
           normal = mesh.normals[faceIndex]
-          centerPoint = Vec3.add(Vec3.create(), vertexA, vertexB)
-          Vec3.add(centerPoint, centerPoint, vertexC)
+
+          projectedA = @project(vertexA, transformMatrix, worldMatrix, normal)
+          projectedB = @project(vertexB, transformMatrix, worldMatrix, normal)
+          projectedC = @project(vertexC, transformMatrix, worldMatrix, normal)
+
+          centerPoint = Vec3.add(Vec3.create(), projectedA.world, projectedB.world)
+          Vec3.add(centerPoint, centerPoint, projectedC.world)
           Vec3.scale(centerPoint, centerPoint, 1/3)

-          light1 = Vec3.fromValues(0, 10, 20)
+          light1 = Vec3.fromValues(-10, 20, 30)
           Vec3.subtract(light1, light1, centerPoint)
           Vec3.normalize(light1, light1)

-          light2 = Vec3.fromValues(0, 10, -20)
-          Vec3.subtract(light2, light2, centerPoint)
-          Vec3.normalize(light2, light2)
-
-          grayVal = Math.max(0, Vec3.dot(normal, light1)) + Math.max(0, Vec3.dot(normal, light2))
+          grayVal = Math.max(0, Vec3.dot(projectedA.normal, light1))
           color = Vec4.fromValues(grayVal, grayVal, grayVal, 1)
-          @drawTriangle(pixelA, pixelB, pixelC, color)
+          @drawTriangle(projectedA.screen, projectedB.screen, projectedC.screen, color)

   _fillBottomFlatTriangle: (v1, v2, v3, color) ->
     invSlope1 = (v2[0] - v1[0]) / (v2[1] - v1[1])


Use editor font family in examples

Use editor font family in examples

--- lib/space-pen-extensions.coffee
+++ lib/space-pen-extensions.coffee
@@ -35,7 +35,6 @@
         @colorizedCodeBlock 'example-html', 'text.xml', beautifyHtml(html)

   colorizedCodeBlock: (cssClass, grammarScopeName, code) ->
-
     highlighter ?= new Highlights(registry: atom.syntax)
     highlightedHtml = highlighter.highlightSync
       fileContents: code
@@ -45,6 +44,8 @@
     # The `editor` class messes things up as `.editor` has absolutely positioned lines
     highlightedBlock.removeClass('editor')
     highlightedBlock.addClass(cssClass)
+    if fontFamily = atom.config.get('editor.fontFamily')
+      highlightedBlock.css('font-family', fontFamily)

     @subview '__', highlightedBlock


Removed redundant name setting. Made more obvious which parameters were being tested against. Introduced actual and expected more consistently.

--- lib/test_generators/page.coffee
+++ lib/test_generators/page.coffee
@@ -23,67 +23,46 @@
         done()

     it 'Cursor can chain limit with paging', (done) ->
-      ALBUM_NAME = 'Test1'
-      Utils.setAllNames MODEL_TYPE, ALBUM_NAME, (err) ->
+      LIMIT = 3
+      MODEL_TYPE.cursor({$page: true}).limit(LIMIT).toJSON (err, data) ->
         assert.ok(!err, 'no errors')
-
-        limit = 3
-        MODEL_TYPE.cursor({$page: true, name: ALBUM_NAME}).limit(limit).toJSON (err, data) ->
-          assert.ok(!err, 'no errors')
-          assert.ok(data.rows, 'cursor toJSON gives us models')
-          assert.equal(data.total_rows, MODELS_JSON.length, 'has the correct total_rows')
-          assert.equal(data.rows.length, limit, 'found models')
-          done()
+        assert.ok(data.rows, 'cursor toJSON gives us models')
+        assert.equal(data.total_rows, MODELS_JSON.length, 'has the correct total_rows')
+        assert.equal(LIMIT, data.rows.length, "Expected: #{LIMIT}, Actual: #{data.rows.length}")
+        done()

     it 'Cursor can chain limit without paging', (done) ->
-      ALBUM_NAME = 'Test1'
-      Utils.setAllNames MODEL_TYPE, ALBUM_NAME, (err) ->
+      LIMIT = 3
+      MODEL_TYPE.cursor({$page: false}).limit(LIMIT).toJSON (err, data) ->
         assert.ok(!err, 'no errors')
-
-        limit = 3
-        MODEL_TYPE.cursor({$page: false, name: ALBUM_NAME}).limit(limit).toJSON (err, json) ->
-          assert.ok(!err, 'no errors')
-          assert.equal(json.length, limit, 'found models')
-          done()
+        assert.equal(LIMIT, data.length, "Expected: #{LIMIT}, Actual: #{data.length}")
+        done()

     it 'Cursor can chain limit and offset with paging', (done) ->
-      ALBUM_NAME = 'Test2'
-      Utils.setAllNames MODEL_TYPE, ALBUM_NAME, (err) ->
+      LIMIT = 3; OFFSET = 1
+      MODEL_TYPE.cursor({$page: true}).limit(LIMIT).offset(OFFSET).toJSON (err, data) ->
         assert.ok(!err, 'no errors')
-
-        limit = 2; offset = 1
-        MODEL_TYPE.cursor({$page: true, name: ALBUM_NAME}).limit(limit).offset(offset).toJSON (err, data) ->
-          assert.ok(!err, 'no errors')
-          assert.ok(data.rows, 'cursor toJSON gives us models')
-          assert.equal(data.total_rows, MODELS_JSON.length, 'has the correct total_rows')
-          assert.equal(data.offset, offset, 'has the correct offset')
-          assert.equal(limit, data.rows.length, "Expected: #{limit}, Actual: #{data.rows.length}")
-          done()
+        assert.ok(data.rows, 'cursor toJSON gives us models')
+        assert.equal(data.total_rows, MODELS_JSON.length, 'has the correct total_rows')
+        assert.equal(OFFSET, data.offset, 'has the correct offset')
+        assert.equal(LIMIT, data.rows.length, "Expected: #{LIMIT}, Actual: #{data.rows.length}")
+        done()

     it 'Cursor can select fields with paging', (done) ->
-      ALBUM_NAME = 'Test3'
       FIELD_NAMES = ['id', 'name']
-
-      Utils.setAllNames MODEL_TYPE, ALBUM_NAME, (err) ->
+      MODEL_TYPE.cursor({$page: true}).select(FIELD_NAMES).toJSON (err, data) ->
         assert.ok(!err, 'no errors')
-
-        MODEL_TYPE.cursor({$page: true, name: ALBUM_NAME}).select(FIELD_NAMES).toJSON (err, data) ->
-          assert.ok(!err, 'no errors')
-          assert.ok(_.isArray(data.rows), 'cursor toJSON gives us models')
-          for json in data.rows
-            assert.equal(_.size(json), FIELD_NAMES.length, 'gets only the requested values')
-          done()
+        assert.ok(_.isArray(data.rows), 'cursor toJSON gives us models')
+        for json in data.rows
+          assert.equal(_.size(json), FIELD_NAMES.length, 'gets only the requested values')
+        done()

     it 'Cursor can select values with paging', (done) ->
-      ALBUM_NAME = 'Test4'
       FIELD_NAMES = ['id', 'name']
-      Utils.setAllNames MODEL_TYPE, ALBUM_NAME, (err) ->
+      MODEL_TYPE.cursor({$page: true}).values(FIELD_NAMES).toJSON (err, data) ->
         assert.ok(!err, 'no errors')
-
-        MODEL_TYPE.cursor({$page: true, name: ALBUM_NAME}).values(FIELD_NAMES).toJSON (err, data) ->
-          assert.ok(!err, 'no errors')
-          assert.ok(_.isArray(data.rows), 'cursor values is an array')
-          for json in data.rows
-            assert.ok(_.isArray(json), 'cursor item values is an array')
-            assert.equal(json.length, FIELD_NAMES.length, 'gets only the requested values')
-          done()
+        assert.ok(_.isArray(data.rows), 'cursor values is an array')
+        for json in data.rows
+          assert.ok(_.isArray(json), 'cursor item values is an array')
+          assert.equal(json.length, FIELD_NAMES.length, 'gets only the requested values')
+        done()


Store previous active element

Store previous active element

--- lib/tool-bar-button-view.coffee
+++ lib/tool-bar-button-view.coffee
@@ -1,5 +1,5 @@
 {CompositeDisposable} = require 'atom'
-{View} = require 'space-pen'
+{$, View} = require 'space-pen'

 module.exports = class ToolBarButtonView extends View
   @content: ->
@@ -22,9 +22,12 @@
     @on 'click', =>
       if not @hasClass 'disabled'
         if typeof options.callback is 'string'
-          atom.commands.dispatch atom.views.getView(atom.workspace.getActiveTextEditor()), options.callback
+          atom.commands.dispatch @getPreviouslyFocusedElement(), options.callback
         else
           options.callback(options.data)
+
+    @on 'mouseover', =>
+      @storeFocusedElement()

   setEnabled: (enabled) ->
     if enabled
@@ -34,3 +37,12 @@

   destroy: ->
     @subscriptions.dispose()
+
+  getPreviouslyFocusedElement: () ->
+    if @previouslyFocusedElement[0] and @previouslyFocusedElement[0] isnt document.body
+      @eventElement = @previouslyFocusedElement[0]
+    else
+      @eventElement = atom.views.getView(atom.workspace)
+
+  storeFocusedElement: ->
+    @previouslyFocusedElement = $(document.activeElement)


less logging

less logging

--- client/lib/plugin.coffee
+++ client/lib/plugin.coffee
@@ -61,7 +61,6 @@
   image:
     emit: (div, item) ->
       item.text ||= item.caption
-      wiki.log 'image', item
       div.append "
#{wiki.resolveLinks(item.text)}
"
     bind: (div, item) ->
       div.dblclick -> wiki.textEditor div, item


Begone

Begone

Signed-off-by: Philip Vieira <3ba3974e96044dcde818e6310ed799abe10671d4@vallin.se>

--- app/assets/javascripts/modules/cloud/extras.js.coffee
+++ app/assets/javascripts/modules/cloud/extras.js.coffee
@@ -17,7 +17,6 @@
         if @frame.hasClass('expanded') then @expandTrigger.find('span').text("less") else @expandTrigger.find('span').text("more")

       @faye.subscribe "/cloud/#{@cloud.cloudId}/presence", (data) =>
-        console.log data
         @userRefresh(data.users) if data.status == 'join'
         @userPurge(data) if data.status == 'leave'


Fixed #55

Fixed #55

--- demo/coffee/gmap.coffee
+++ demo/coffee/gmap.coffee
@@ -9,11 +9,17 @@
   legend_name ?= "glyph"
   plot_title ?= ""

+  xdr = Collections('Range1d').create()
+
+  ydr = Collections('Range1d').create()
+
   plot_model = Collections('GMapPlot').create(
     map_options:
       lat: 30.267153
       lng: -97.74306079999997
       zoom: 15
+    x_range: xdr.ref()
+    y_range: ydr.ref()
     canvas_width: dims[0]
     canvas_height: dims[1]
     outer_width: dims[0]


Beautified the code

Beautified the code

--- src/DivSugar.coffee
+++ src/DivSugar.coffee
@@ -65,7 +65,7 @@

     # start tasks
     updateTasks = =>
-      curTime = (new Date()).getTime()
+      curTime = new Date().getTime()
       deltaTime = curTime - @_lastUpdatedTime
       @_lastUpdatedTime = curTime
       @rootTask.update deltaTime


specify dep version

specify dep version

--- src/scripts/base36.coffee
+++ src/scripts/base36.coffee
@@ -2,7 +2,7 @@
 #   Base36 encoding and decoding
 #
 # Dependencies:
-#   "big-integer": "https://github.com/peterolson/BigInteger.js"
+#   "big-integer": "1.1.5"
 #
 # Configuration:
 #   None


update v0.0.6

update v0.0.6

--- countUp.coffee
+++ countUp.coffee
@@ -2,19 +2,13 @@
 #
 # countUp.js
 # by @inorganik
-# v 0.0.5
+# v 0.0.6
 #
 # Example:
 # numAnim = new countUp "SomeElementYouWantToAnimate", 99.99, 2, 1.5
 # numAnim.start()
 #
 ###
-
-window.requestAnimationFrame =
-  window.requestAnimationFrame or
-  window.mozRequestAnimationFrame or
-  window.webkitRequestAnimationFrame or
-  window.msRequestAnimationFrame

 # target = id of Html element where counting occurs
 # startVal = the value you want to start at
@@ -31,23 +25,20 @@
     'ms'
   ]

-  @doc = document.getElementById target
-
+  #toggle easing
+  @useEasing = true
+
+  @doc = document.getElementById target
   startVal = Number startVal
   endVal = Number endVal
   @countDown = if (startVal > endVal) then true else false
-
-  #toggle easing
-  @useEasing = true
-
   decimals = Math.max(0, decimals or 0)
   @dec = Math.pow(10, decimals)
   @duration = duration * 1000 or 2000
-
   @startTime = null
+  @remaining = null
   @frameVal = startVal
-
-  @.rAF = null
+  @rAF = null

   # make sure requestAnimationFrame and cancelAnimationFrame are defined
   # polyfill for browsers without native support
@@ -75,30 +66,28 @@

   # Robert Penner's easeOutExpo
   @easeOutExpo = (t, b, c, d) ->
-    # modified from:
-    # c * (-Math.pow(2, -10 * t / d) + 1) + b
-    # thanks to @lifthrasiir's "exact easing" commit
     c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b

   @count = (timestamp) ->
     @startTime = timestamp if @startTime is null

+    @timestamp = timestamp
+
     progress = timestamp - @startTime

     # to ease or not to ease is the question
-    if @.useEasing
-      if @.countDown
-        i = @.easeOutExpo progress, 0, startVal - endVal, @.duration
-        @.frameVal = startVal - i
+    if @useEasing
+      if @countDown
+        i = @easeOutExpo progress, 0, startVal - endVal, @duration
+        @frameVal = startVal - i
       else
-        @.frameVal = @.easeOutExpo(progress, startVal, endVal - startVal, @.duration)
-      console.log @.frameVal
+        @frameVal = @easeOutExpo(progress, startVal, endVal - startVal, @duration)
     else
-      if @.countDown
-        i = (startVal - endVal) * (progress / @.duration)
-        @.frameVal = startVal - i
+      if @countDown
+        i = (startVal - endVal) * (progress / @duration)
+        @frameVal = startVal - i
       else
-        @.frameVal = startVal + (endVal - startVal) * (progress / @.duration)
+        @frameVal = startVal + (endVal - startVal) * (progress / @duration)

     # decimal
     @frameVal = Math.round(@frameVal * @dec) / @dec
@@ -113,12 +102,13 @@
     @doc.innerHTML = @addCommas @frameVal.toFixed(decimals)

     # weather to continue
-    if progress < self.duration
-      self.rAF = requestAnimationFrame self.count
+    if progress < @duration
+      @rAF = requestAnimationFrame @count
     else
-      self.callback() if self.callback?
+      @callback() if @callback?

-  @start = () ->
+  @start = (callback) ->
+    @callback = callback
     # make sure endVal is a number
     requestAnimationFrame @count unless isNaN(endVal) and isNan(startVal) isnt null
     else
@@ -131,7 +121,13 @@

   @reset = () ->
     cancelAnimationFrame @rAF
-    @doc.innerHTML = startVal
+    @doc.innerHTML = @addCommas startVal.toFixed(decimals)
+
+  @resume = () ->
+    @startTime = null
+    @duration = @remaining
+    @startVal = @framVal
+    requestAnimationFrame @count

   @addCommas = (nStr) ->
     nStr += ''
@@ -144,3 +140,6 @@
       x1 = x1.replace(rgx, '$1' + ',' + '$2')

     x1 + x2
+
+  # format startVal on initialization
+  @doc.innerHTML = @addCommas startVal.toFixed(decimals)


v 1.1.0 (globalization)

v 1.1.0 (globalization)

--- countUp.coffee
+++ countUp.coffee
@@ -2,7 +2,7 @@
 #
 # countUp.js
 # by @inorganik
-# v 1.0.3
+# v 1.1.0
 #
 # Example:
 # numAnim = new countUp "SomeElementYouWantToAnimate", 99.99, 2, 1.5
@@ -11,12 +11,21 @@
 ###

 # target = id of Html element where counting occurs
-# startVal = the value you want to start at
-# endVal = the value you want to arrive at
-# decimals = number of decimal places in number, default 0
-# duration = duration in seconds, default 2
+# @startVal = the value you want to start at
+# @endVal = the value you want to arrive at
+# decimals = number of decimal places, default 0
+# duration = duration of animation in seconds, default 2
+# options = optional object of options (see below)

-countUp = (target, startVal, endVal, decimals, duration) ->
+countUp = (target, startVal, endVal, decimals, duration, options) ->
+
+  # default options
+  @options = option || {
+    useEasing: true # toggle easing
+    useGrouping: true # 1,000,000 vs 1000000
+    separator: ',' # character to use as a separator
+    decimal: '.' # character to use as a decimal
+  }

   lastTime = 0
   vendors = [
@@ -25,19 +34,17 @@
     'ms'
   ]

-  #toggle easing
-  @useEasing = true
   @doc = if (typeof target == 'string') then document.getElementById target else target
-  @startVal = Number startVal
-  endVal = Number endVal
-  @countDown = if (startVal > endVal) then true else false
-  decimals = Math.max(0, decimals or 0)
-  @dec = Math.pow(10, decimals)
-  @duration = duration * 1000 or 2000
+  @startVal = Number @startVal
+  @endVal = Number @endVal
+  @countDown = if (@startVal > @endVal) then true else false
   @startTime = null
   @remaining = null
   @frameVal = @startVal
   @rAF = null
+  @dec = Math.pow(10, decimals)
+  @decimals = Math.max(0, decimals or 0)
+  @duration = duration * 1000 or 2000

   # make sure requestAnimationFrame and cancelAnimationFrame are defined
   # polyfill for browsers without native support
@@ -75,30 +82,30 @@
     progress = timestamp - @startTime

     # to ease or not to ease is the question
-    if @useEasing
+    if @options.Easing
       if @countDown
-        i = @easeOutExpo progress, 0, @startVal - endVal, @duration
+        i = @easeOutExpo progress, 0, @startVal - @endVal, @duration
         @frameVal = @startVal - i
       else
-        @frameVal = @easeOutExpo(progress, @startVal, endVal - @startVal, @duration)
+        @frameVal = @easeOutExpo(progress, @startVal, @endVal - @startVal, @duration)
     else
       if @countDown
-        i = (startVal - endVal) * (progress / @duration)
+        i = (@startVal - @endVal) * (progress / @duration)
         @frameVal = @startVal - i
       else
-        @frameVal = @startVal + (endVal - @startVal) * (progress / @duration)
+        @frameVal = @startVal + (@endVal - @startVal) * (progress / @duration)

     # decimal
     @frameVal = Math.round(@frameVal * @dec) / @dec

     # don't go past enVal since progress can exceed duration in last grame
     if @countDown
-      @frameVal = if (@framVal < endVal) then endVal else @frameVal
+      @frameVal = if (@framVal < @endVal) then @endVal else @frameVal
     else
-      @frameVal = if (@framVal > endVal) then endVal else @frameVal
+      @frameVal = if (@framVal > @endVal) then @endVal else @frameVal

     # formate and print value
-    @doc.innerHTML = @addCommas @frameVal.toFixed(decimals)
+    @doc.innerHTML = @formatNumber @frameVal.toFixed(decimals)

     # weather to continue
     if progress < @duration
@@ -108,10 +115,10 @@

   @start = (callback) ->
     @callback = callback
-    # make sure endVal is a number
-    requestAnimationFrame @count unless isNaN(endVal) and isNan(startVal) isnt null
+    # make sure @endVal is a number
+    requestAnimationFrame @count unless isNaN(@endVal) and isNan(@startVal) isnt null
     else
-      console.log('countUp error: startVal or endVal is not a number')
+      console.log('countUp error: @startVal or @endVal is not a number')
       @doc.innerHTML = '--'
     false

@@ -121,7 +128,7 @@
   @reset = () ->
     @startTime = null
     cancelAnimationFrame @rAF
-    @doc.innerHTML = @addCommas @startVal.toFixed(decimals)
+    @doc.innerHTML = @formatNumber @startVal.toFixed(decimals)

   @resume = () ->
     @startTime = null
@@ -129,17 +136,18 @@
     @startVal = @framVal
     requestAnimationFrame @count

-  @addCommas = (nStr) ->
+  @formatNumber = (nStr) ->
     nStr += ''
     x = nStr.split('.')
     x1 = x[0]
-    x2 = if x.length > 1 then "." + x[1] else ''
+    x2 = if x.length > 1 then @options.decimal + x[1] else ''
     rgx = /(\d+)(\d{3})/

-    while rgx.test(x1)
-      x1 = x1.replace(rgx, '$1' + ',' + '$2')
+    if @options.useGrouping
+      while rgx.test(x1)
+        x1 = x1.replace(rgx, '$1' + @options.separator + '$2')

     x1 + x2

-  # format startVal on initialization
-  @doc.innerHTML = @addCommas @startVal.toFixed(decimals)
+  # format @startVal on initialization
+  @doc.innerHTML = @formatNumber @startVal.toFixed(decimals)


Convert section arrays to strings

Convert section arrays to strings

--- client/src/app/io.coffee
+++ client/src/app/io.coffee
@@ -791,6 +791,10 @@

           # Set game version and player IDs on events
           for event in ioData[circuitId].events
+            # If event section is array, change it to a dot.separated string
+            if event.section and _.isArray(event.section)
+              event.section = event.section.join(".")
+
             eventQueue.push _.extend event,
               gameVersion: options.metrics.gameVersionId
               player: playerId


clone site clears seo icons

clone site clears seo icons

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -653,14 +653,16 @@
               image: ''
               keywords: ''
               title: ''
-              icons: {
-                apple57x57: ''
-                apple72x72: ''
-                apple114x114: ''
-                apple144x144: ''
-                favicon: ''
-              }
             }
+            icons = {
+              apple57x57: ''
+              apple72x72: ''
+              apple114x114: ''
+              apple144x144: ''
+              favicon: ''
+            }
+            site.settings.icons = icons
+            site.settings.seo.icons = icons
             db.get('sites').insert site, (err, dst_site) ->
               exit err if err


#156 Fix black clear color

#156 Fix black clear color

--- app/assets/javascripts/oxalis/view/plane_view.coffee
+++ app/assets/javascripts/oxalis/view/plane_view.coffee
@@ -114,12 +114,21 @@
       viewport = [[0, @curWidth+20], [@curWidth+20, @curWidth+20], [0, 0], [@curWidth+20, 0]]
       @renderer.autoClear = true

+      setupRenderArea = (x, y, width, color) =>
+        @renderer.setViewport x, y, width, width
+        @renderer.setScissor  x, y, width, width
+        @renderer.enableScissorTest true
+        @renderer.setClearColor color, 1
+
+      setupRenderArea( 0, 0, @renderer.domElement.width, 0xffffff )
+      @renderer.clear()
+
       for i in constants.ALL_VIEWPORTS
         @trigger "renderCam", i
-        @renderer.setViewport(viewport[i][0] * f, viewport[i][1] * f, @curWidth * f, @curWidth * f)
-        @renderer.setScissor(viewport[i][0] * f, viewport[i][1] * f, @curWidth * f, @curWidth * f)
-        @renderer.enableScissorTest(true)
-        @renderer.setClearColor(constants.PLANE_COLORS[i], 1);
+        setupRenderArea(
+          viewport[i][0] * f, viewport[i][1] * f, @curWidth * f,
+          constants.PLANE_COLORS[i]
+        )
         @renderer.render @scene, @camera[i]

       @flycam.hasChanged = false


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -19,7 +19,7 @@
       } = meterData

       if not I[name] || not I["max#{name.capitalize()}"]
-
+        warn ""
       ratio = I[name] / I["max#{name.capitalize()}"]

       if showName || text


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -22,7 +22,7 @@
         I[name] = 100

       if not I["max#{name.capitalize()}"]
-
+        I["max#{name.capitalize()}"] =

       ratio = I[name] / I["max#{name.capitalize()}"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -50,7 +50,7 @@
     I.meters.each (meterData) ->
       {
         borderColor,
-        bord
+        borderWidth,
         color,
         font,
         height,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -154,7 +154,7 @@
       width: 100

     if not I[name]
-      I[name] = 100
+      I[name] ?= 100

     if not I["max#{name.capitalize()}"]
       if I[name]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -130,7 +130,7 @@
     borderColor - Color of the meter's border
     borderWidth - Width of the meter's border
     color - Color of the meter's inner rectangle
-    name
+    nameColor -
   ###
   meter: (options={}) ->
     Object.reverseMerge options,


fix deactivation

fix deactivation

--- lib/ink.coffee
+++ lib/ink.coffee
@@ -29,7 +29,7 @@
         http.get "http://data.junolab.org/hit?id=#{id}&app=ink"

   deactivate: ->
-    results.deactivate()
+    Result.deactivate()
     Console.deactivate()

   provide: ->


Documentation

Documentation

--- src/movable.coffee
+++ src/movable.coffee
@@ -13,17 +13,17 @@

    player.include(Movable)

-=> `player velocity: {x: 0, y: 0} | player position: {x: 0, y: 0}`
+=> `player velocity is {x: 0, y: 0} | player position is {x: 0, y: 0}`

    player.update()
-=> `player velocity: {x: 1, y: 0} | player position: {x: 1, y: 0}`
+=> `player velocity is {x: 1, y: 0} | player position is {x: 1, y: 0}`

    player.update()
-=> `player velocity: {x: 2, y: 0} | player position: {x: 3, y: 0}`
+=> `player velocity is {x: 2, y: 0} | player position is {x: 3, y: 0}`

    # we've hit our maxSpeed so our velocity won't increase
    player.update()
-=> `player velocity: {x: 2, y: 0} | player position: {x: 5, y: 0}`
+=> `player velocity is {x: 2, y: 0} | player position is {x: 5, y: 0}`
 </pre></code>

 @name Movable


Identify cycles in peerDependencies with Tarjan's algorithm

--- src/npm2nix.coffee
+++ src/npm2nix.coffee
@@ -40,98 +40,99 @@

 writePkg = finalizePkgs = undefined
 do ->
-  findCycles = (name, spec, pkg) ->
-    index = 0
-    node = { name: name, spec: spec }
-    path = []
-    indices= []
-    pkg.cycles = []
-    loop
-      cycleDetected = false
-      for el in path
-        if el.name is node.name
-          cycleDetected = true
-          if el is path[0]
-            pkg.cycles.push path.slice 0
-      unless cycleDetected
-        peerDeps = packageSet[node.name][node.spec].peerDependencies or {}
-        if Object.keys(peerDeps).length > index
-          path.push node
-          indices.push index
-          name = Object.keys(peerDeps)[index]
-          node = { name: name, spec: peerDeps[name] }
-          index = 0
-          continue
-        else if path.length is 0
-          break
-      node = path.pop()
-      index = indices.pop() + 1
-  cycleMembers = {}
+  index = 0
+  stack = []
+  strongConnect = (pkg) ->
+    pkg.tarjanIndex = index
+    pkg.tarjanLowLink = index
+    index += 1
+    stackIndex = stack.length
+    stack.push pkg
+
+    peerDeps = pkg.peerDependencies or {}
+    for name, spec of peerDeps
+      otherPkg = packageSet[name][spec]
+
+      unless 'tarjanIndex' of otherPkg
+        strongConnect otherPkg
+        pkg.tarjanLowLink = otherPkg.tarjanLowLink if otherPkg.tarjanLowLink < pkg.tarjanLowLink
+      else
+        if otherPkg in stack
+          pkg.tarjanLowLink = otherPkg.tarjanIndex if otherPkg.tarjanIndex < pkg.tarjanLowLink
+
+    if pkg.tarjanLowLink is pkg.tarjanIndex
+      pkg.scc = stack[stackIndex..]
+      stack = stack[...stackIndex]
+
+
+  known = {}

   stream = fs.createWriteStream args.output
   stream.write "{ self, fetchurl, lib }:\n\n{"
   writePkg = (name, spec, pkg) ->
-    if name of cycleMembers and pkg.version of cycleMembers[name]
-      stream.write """
-      \n  full."#{escapeNixString name}"."#{escapeNixString spec}" = self.full."#{escapeNixString cycleMembers[name][pkg.version].name}"."#{escapeNixString cycleMembers[name][pkg.version].spec}";
-      """
-    else
-      findCycles name, spec, pkg
-      pkgs = [ pkg ]
-      names = [ name ]
-      specs = [ spec ]
-      seen = {}
-      seen[name] = true
-      count = 0
-      for cycle in pkg.cycles
-        for node in cycle
-          cycleMembers[node.name] ?= {}
-          cycleMembers[node.name][packageSet[node.name][node.spec].version] = { name: name, spec: spec }
-          unless seen[node.name]
-            pkgs.push packageSet[node.name][node.spec]
-            names.push node.name
-            specs.push node.spec
-            count += 1
-          seen[node.name] = true
-      stream.write "\n  full.\"#{escapeNixString name}\".\"#{escapeNixString spec}\" = lib.makeOverridable self.buildNodePackage {"
-      stream.write "\n    name = \"#{escapeNixString names[0]}-#{escapeNixString pkgs[0].version}\";\n    src = ["
-      for idx in [0..count]
-        pk = pkgs[idx]
-        stream.write """
-        \n      (#{if pk.patchLatest then 'self.patchLatest' else 'fetchurl'} {
-                url = "#{pk.dist.tarball}";
-                #{if 'shasum' of pk.dist then 'sha1' else 'sha256'} = "#{pk.dist.shasum ? pk.dist.sha256sum}";
-              })
-        """
-      stream.write "\n    ];\n    buildInputs ="
-      for idx in [0..count]
-        stream.write "\n      "
-        stream.write "++ " unless idx is 0
-        stream.write "(self.nativeDeps.\"#{escapeNixString names[idx]}\".\"#{escapeNixString specs[idx]}\" or [])"
-      stream.write ";\n    deps = ["
-      seenDeps = {}
-      for idx in [0..count]
-        for nm, spc of pkgs[idx].dependencies or {}
-          unless seenDeps[nm]
-            spc = spc.version if spc instanceof Object
-            if spc is 'latest' or spc is ''
-              spc = '*'
-            stream.write "\n      self.full.\"#{escapeNixString nm}\".\"#{escapeNixString spc}\""
-          seenDeps[nm] = true
-      stream.write "\n    ];\n    peerDependencies = ["
-      for idx in [0..count]
-        for nm, spc of pkgs[idx].peerDependencies or {}
-          unless seenDeps[nm] or seen[nm]
-            spc = spc.version if spc instanceof Object
-            if spc is 'latest' or spc is ''
-              spc = '*'
-            stream.write "\n      self.full.\"#{escapeNixString nm}\".\"#{escapeNixString spc}\""
-          seenDeps[nm] = true
-      stream.write "\n    ];\n    passthru.names = [ #{("\"#{escapeNixString nm}\"" for nm in names).join " "} ];\n  };"
+    stream.write """
+    \n  by-spec.\"#{escapeNixString name}\".\"#{escapeNixString spec}\" =
+        self.by-version.\"#{escapeNixString name}\".\"#{escapeNixString  pkg.version}\";
+    """
+    unless name of known and pkg.version of known[name]
+      known[name] ?= {}
+      known[name][pkg.version] = true
+      unless 'tarjanIndex' of pkg
+        strongConnect pkg
+      if 'scc' of pkg
+        names = [ ]
+        count = -1
+        cycleDeps = {}
+        for pk in pkg.scc
+          unless name of known and pk.version of known[name]
+            stream.write """
+            \n  by-version."#{escapeNixString pk.name}"."#{escapeNixString pk.version}" = self.by-version."#{escapeNixString name}"."#{escapeNixString pkg.version}";
+            """
+            known[pk.name] ?= {}
+            known[pk.name][pk.version] = true
+          names.push pk.name
+          cycleDeps[pk.name] = true
+          count += 1
+
+        stream.write "\n  by-version.\"#{escapeNixString name}\".\"#{escapeNixString pkg.version}\" = lib.makeOverridable self.buildNodePackage {"
+        stream.write "\n    name = \"#{escapeNixString names[0]}-#{escapeNixString pkg.scc[0].version}\";\n    src = ["
+        for idx in [0..count]
+          pk = pkg.scc[idx]
+          stream.write """
+          \n      (#{if pk.patchLatest then 'self.patchLatest' else 'fetchurl'} {
+                  url = "#{pk.dist.tarball}";
+                  #{if 'shasum' of pk.dist then 'sha1' else 'sha256'} = "#{pk.dist.shasum ? pk.dist.sha256sum}";
+                })
+          """
+        stream.write "\n    ];\n    buildInputs ="
+        for idx in [0..count]
+          stream.write "\n      "
+          stream.write "++ " unless idx is 0
+          stream.write "(self.nativeDeps.\"#{escapeNixString names[idx]}\" or [])"
+        stream.write ";\n    deps = ["
+        seenDeps = {}
+        for idx in [0..count]
+          for nm, spc of pkg.scc[idx].dependencies or {}
+            unless seenDeps[nm]
+              spc = spc.version if spc instanceof Object
+              if spc is 'latest' or spc is ''
+                spc = '*'
+              stream.write "\n      self.by-version.\"#{escapeNixString nm}\".\"#{packageSet[nm][spc].version}\""
+            seenDeps[nm] = true
+        stream.write "\n    ];\n    peerDependencies = ["
+        for idx in [0..count]
+          for nm, spc of pkg.scc[idx].peerDependencies or {}
+            unless seenDeps[nm] or cycleDeps[nm]
+              spc = spc.version if spc instanceof Object
+              if spc is 'latest' or spc is ''
+                spc = '*'
+              stream.write "\n      self.by-version.\"#{escapeNixString nm}\".\"#{packageSet[nm][spc].version}\""
+            seenDeps[nm] = true
+        stream.write "\n    ];\n    passthru.names = [ #{("\"#{escapeNixString nm}\"" for nm in names).join " "} ];\n  };"

     if fullNames[name] is spec
       stream.write """
-      \n  "#{escapeNixString name}" = self.full."#{escapeNixString name}"."#{escapeNixString spec}";
+      \n  "#{escapeNixString name}" = self.by-version."#{escapeNixString name}"."#{pkg.version}";
       """

   finalizePkgs = ->
@@ -145,21 +146,25 @@
   fetcher = new PackageFetcher()
   fs.readFile args.packageList, (err, json) ->
     if err?
-      console.error "Error reading file #{file}: #{err}"
+      console.error "Error reading file #{args.packageList}: #{err}"
       process.exit 1
     try
       packages = JSON.parse json
     catch error
-      console.error "Error parsing JSON file #{file}: #{error}"
+      console.error "Error parsing JSON file #{args.packageList}: #{error}"
       process.exit 3

+    packageByVersion = {}
     pkgCount = 0
     fetcher.on 'fetching', ->
       pkgCount += 1
     fetcher.on 'fetched', (name, spec, pkg) ->
       pkgCount -= 1
+      packageByVersion[name] ?= {}
+      unless pkg.version of packageByVersion[name]
+        packageByVersion[name][pkg.version] = pkg
       packageSet[name] ?= {}
-      packageSet[name][spec] = pkg
+      packageSet[name][spec] = packageByVersion[name][pkg.version]
       if pkgCount is 0
         names = (key for key, val of packageSet).sort()
         for name in names


Removing unneeded anonymous functions; leftovers from when we passed a second argument to except_cb in the bytecode loop.

--- src/opcodes.coffee
+++ src/opcodes.coffee
@@ -74,7 +74,7 @@
       # Initialize @method_spec.class and rerun opcode.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().initialize_class rs, @method_spec.class,
-          (->resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          (->resume_cb(undefined, undefined, true, false)), except_cb
     return

 class root.DynInvokeOpcode extends root.InvokeOpcode
@@ -152,7 +152,7 @@
         # this opcode.
         cdesc = util.typestr2descriptor @str_constant.value
         rs.async_op (resume_cb, except_cb) =>
-          rs.get_cl().resolve_class(rs, cdesc, ((cls)=>resume_cb cls.get_class_object(rs), undefined, true), ((e_cb)->except_cb e_cb))
+          rs.get_cl().resolve_class(rs, cdesc, ((cls)=>resume_cb cls.get_class_object(rs), undefined, true), except_cb)
         return
       else
         rs.push @constant.value
@@ -348,7 +348,7 @@
     unless cls?
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().initialize_class rs, @class,
-          ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
       return

     new_execute = (rs) ->
@@ -606,12 +606,12 @@
         # Initialize cls_type and rerun opcode.
         rs.async_op (resume_cb, except_cb) =>
           rs.get_cl().initialize_class rs, cls_type,
-            ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+            ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
     else
       # Initialize @field_spec.class and rerun opcode.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().initialize_class rs, @field_spec.class,
-          ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
     return
   }
   179: new root.FieldOpcode 'putstatic', {execute: (rs)->
@@ -634,12 +634,12 @@
         # Initialize cls_type and rerun opcode.
         rs.async_op (resume_cb, except_cb) =>
           rs.get_cl().initialize_class rs, cls_type,
-            ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+            ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
     else
       # Initialize @field_spec.class and rerun opcode.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().initialize_class rs, @field_spec.class,
-          ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
     return
   }
   180: new root.FieldOpcode 'getfield', { execute: (rs) ->
@@ -668,7 +668,7 @@
       # Alright, tell this class's ClassLoader to load the class.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().resolve_class rs, @field_spec.class,
-          (=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          (=>resume_cb(undefined, undefined, true, false)), except_cb
     return
   }
   181: new root.FieldOpcode 'putfield', { execute: (rs) ->
@@ -701,7 +701,7 @@
       # Alright, tell this class's ClassLoader to load the class.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().resolve_class rs, @field_spec.class,
-          (=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          (=>resume_cb(undefined, undefined, true, false)), except_cb
     return
   }
   182: new root.DynInvokeOpcode 'invokevirtual'
@@ -730,7 +730,7 @@
           else
             obj = new JavaObject(rs, class_file)
           resume_cb(obj, undefined, true)
-        rs.get_cl().initialize_class rs, @class, success_fn, ((e_cb)->except_cb(e_cb))
+        rs.get_cl().initialize_class rs, @class, success_fn, except_cb
   }
   188: new root.NewArrayOpcode 'newarray', { execute: (rs) -> rs.push rs.heap_newarray @element_type, rs.pop() }
   189: new root.ClassOpcode 'anewarray', { execute: (rs) ->
@@ -745,7 +745,7 @@
       # Load @class and rerun opcode.
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().resolve_class rs, @class,
-          ((class_file)=>resume_cb(undefined, undefined, true, false)), ((e_cb)->except_cb(e_cb))
+          ((class_file)=>resume_cb(undefined, undefined, true, false)), except_cb
     return
   }
   190: new root.Opcode 'arraylength', { execute: (rs) -> rs.push rs.check_null(rs.pop()).array.length }
@@ -769,7 +769,7 @@
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().resolve_class rs, @class, (()->
           resume_cb undefined, undefined, true, false
-        ), ((e_cb)->except_cb(e_cb))
+        ), except_cb
   }
   193: new root.ClassOpcode 'instanceof', { execute: (rs) ->
     @cls = rs.get_cl().get_resolved_class @class, true
@@ -784,7 +784,7 @@
       rs.async_op (resume_cb, except_cb) =>
         rs.get_cl().resolve_class rs, @class, (()->
           resume_cb undefined, undefined, true, false
-        ), ((e_cb)->except_cb(e_cb))
+        ), except_cb
   }
   194: new root.Opcode 'monitorenter', { execute: (rs)->
     monitor = rs.pop()


Add a cancel() function to the promise

Add a cancel() function to the promise

--- src/project.coffee
+++ src/project.coffee
@@ -275,17 +275,14 @@
       excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths')
       exclusions: atom.config.get('core.ignoredNames')

-    @cancelScan()
-
-    @scanTask = Task.once require.resolve('./scan-handler'), @getPath(), regex.source, searchOptions, =>
-      @scanTask = null
+    task = Task.once require.resolve('./scan-handler'), @getPath(), regex.source, searchOptions, =>
       deferred.resolve()

-    @scanTask.on 'scan:result-found', (result) =>
+    task.on 'scan:result-found', (result) =>
       iterator(result) unless @isPathModified(result.filePath)

     if _.isFunction(options.onPathsSearched)
-      @scanTask.on 'scan:paths-searched', (numberOfPathsSearched) ->
+      task.on 'scan:paths-searched', (numberOfPathsSearched) ->
         options.onPathsSearched(numberOfPathsSearched)

     for buffer in @buffers.getValues() when buffer.isModified()
@@ -294,13 +291,11 @@
       buffer.scan regex, (match) -> matches.push match
       iterator {filePath, matches} if matches.length > 0

-    deferred.promise
-
-  # Public: Cancels the current scan task if there is one running.
-  cancelScan: ->
-    if @scanTask?
-      @scanTask.terminate()
-      @scanTask = null
+    promise = deferred.promise
+    promise.cancel = ->
+      task.terminate()
+      deferred.reject('cancelled')
+    promise

   # Public: Performs a replace across all the specified files in the project.
   #


Update Promise.all to return the results of each promise

Results are passed as an array argument to Promise.then() callbacks.
Results are ordered based on the order of the
promises given to Promise.all() as argument.

--- src/promise.coffee
+++ src/promise.coffee
@@ -1,24 +1,42 @@
 define ->
+  UID = 1
+
   class Promise
     @all: (promises) ->
       promise_all = new Promise()
       rejected = false

+      results = {}
       done_count = promises.length
       if done_count is 0
         promise_all.resolve(true)
         return promise_all

-      success = -> promise_all.resolve(true) if --done_count is 0
+      create_ordered_array = (results)->
+        arr = []
+        for promise_id, data of results
+          arr[data.order] = data.result
+        return arr
+
+      success = (result)->
+        results[this._id].result = result
+        if --done_count is 0
+          promise_all.resolve(create_ordered_array(results))
       fail = ->
         unless rejected
           promise_all.reject(false)
           rejected = true

-      promise.then(success, fail) for promise in promises
+      i = 0
+      for promise in promises
+        results[promise._id] = {order: i++}
+        promise.then(success.bind(promise), fail)
+
+
       promise_all

     constructor: ->
+      @_id = UID++
       @state = 'pending'
       @_thens = []


body can be both element or string

body can be both element or string

--- tutor/src/components/tutor-dialog.cjsx
+++ tutor/src/components/tutor-dialog.cjsx
@@ -8,7 +8,7 @@
   title:         React.PropTypes.string.isRequired
   onOk:          React.PropTypes.func.isRequired
   onCancel:      React.PropTypes.func.isRequired
-  body:          React.PropTypes.element.isRequired
+  body:          React.PropTypes.any.isRequired
   show:          React.PropTypes.bool
   buttons:       React.PropTypes.arrayOf( React.PropTypes.element )
   okBtnText:     React.PropTypes.string


Mary Tyler Moore == VICTORY.

Mary Tyler Moore == VICTORY.

--- src/victory.coffee
+++ src/victory.coffee
@@ -40,7 +40,8 @@
   "http://www.velocityindiana.org/wp-content/uploads/2014/08/bff.gif",
   "http://i.kinja-img.com/gawker-media/image/upload/s--_dYUH9jW--/18vvnw5taib2ogif.gif",
   "https://31.media.tumblr.com/e844e0925dbd8699ddb68fb2408d61b6/tumblr_mqrjr2oH0G1r3kc9vo1_250.gif",
-  "http://i.kinja-img.com/gawker-media/image/upload/s--0wAEcaN4--/c_fit,fl_progressive,q_80,w_636/bdbtzjrhyyuarpfbqksn.gif"
+  "http://i.kinja-img.com/gawker-media/image/upload/s--0wAEcaN4--/c_fit,fl_progressive,q_80,w_636/bdbtzjrhyyuarpfbqksn.gif",
+  "http://media.giphy.com/media/vpybhig8QFLOM/giphy.gif"
 ]

 module.exports = (robot) ->


Make images and dimensions configurable when initializing plugin

--- storyturtle.coffee
+++ storyturtle.coffee
@@ -7,25 +7,60 @@
 localStorage = @localStorage

 storyturtle = {}
+config = {}
+
+$.fn.storyturtle = (options) ->
+  config = $.extend true,
+    images: {} # Maps image type to URL
+    board:
+      width: 300
+      height: 300
+    editor:
+      rows: 15
+      cols: 30
+    controls:
+      width: 300
+      height: 25
+    feature: storyturtle.feature
+  , options
+  @each ->
+    storyturtle.init $(@)
+
+storyturtle.feature = (type, cb) ->
+  if type of config.images
+    img = new Image()
+    img.src = config.images[type]
+    $(img).load ->
+      feature = $("<img>", src:img.src)
+      feature.css
+        width:30
+        height:30
+      cb feature
+
 storyturtle.init = (game) ->
   game.hide()
-    .width(300)
-    .height(350)
+    .width(
+      if config.board.width > config.controls.width
+        config.board.width
+      else
+        config.controls.width)
+    .height(config.board.height+config.controls.height)

   editor = $("<textarea>",
-    rows: 15
-    cols: 30)
+    rows: config.editor.rows
+    cols: config.editor.cols)
     .hide()
     .val(game.text())
     .appendTo(game.text(""))

   board = $("<div>")
-    .width(300)
-    .height(300)
+    .width(config.board.width)
+    .height(config.board.height)
     .appendTo(game)

   speaker = $("<div>")
-    .width(300)
+    .width(config.controls.width)
+    .height(config.controls.height)
     .appendTo(game)

   play = $("<a>", href:'#')
@@ -37,7 +72,8 @@
     .css(float: "right")

   controls = $("<div>")
-    .width(300)
+    .width(config.controls.width)
+    .height(config.controls.height)
     .append(play, edit)
     .appendTo(game)

@@ -47,19 +83,17 @@
     left:0
     top:0

-  create = (cb, name, type, x, y)->
+  create = (cb, name, type, x, y) ->
     feature = features[name]
     feature.remove if feature
-    feature = $("<img>", src: "images/#{type}.png")
-    feature.css
-      position: 'absolute'
-      left:x
-      top:y
-      width:30
-      height:30
-    feature.appendTo board
-    features[name] = feature
-    cb()
+    config.feature type, (feature) ->
+      feature.css
+        position: 'absolute'
+        left:x
+        top:y
+      feature.appendTo board
+      features[name] = feature
+      cb()

   move = (cb, name, x, y)->
     feature = features[name]
@@ -89,7 +123,7 @@

   grammer =
     create:
-      match: /(\w+)\s+is\s+an?\s+(\w+)\s+at\s+([123]?\d?\d)\s+([123]?\d?\d)/
+      match: /(\w+)\s+is\s+an?\s+(\w+)\s+at\s+(\d+)\s+(\d+)/
       handle: (match, cb) ->
         create(cb
           match[1]
@@ -103,7 +137,7 @@
         feature.remove() if feature
         cb()
     move:
-      match: /(\w+)\s+moves\s+to\s+([123]?\d?\d)\s+([123]?\d?\d)/
+      match: /(\w+)\s+moves\s+to\s+(\d+)\s+(\d+)/
       handle: (match, cb)->
         move(
           cb
@@ -191,7 +225,3 @@
     false

   game.show()
-
-$.fn.storyturtle = ->
-  @each ->
-    storyturtle.init $(@)


fixup remainder by comments :(

fixup remainder by comments :(

--- tutor/test/components/task.spec.coffee
+++ tutor/test/components/task.spec.coffee
@@ -209,7 +209,8 @@
   #     # .then(taskChecks.checkRecoveryContent)
   #     .then(_.delay(done, 1800)).catch(done)

-  it 'should continue even if task has only a single step', (done) ->
+  # This test no longer works since we'll always have at least some spaced practice
+  xit 'should continue even if task has only a single step', (done) ->
     TaskActions.reset()
     model = _.clone(VALID_MODEL)
     model.steps = _.clone(VALID_MODEL.steps)
@@ -227,8 +228,16 @@
     # run a full step through and check each step
     taskActions
       .clickContinue(@result)
-      .then(taskActions.completeSteps)
-      .then(taskChecks.checkIsCompletePage)
-      .then( ->
-        done()
-      , done)
+      .then(taskActions.completeThisStep)
+      .then(taskActions.advanceStep)
+      .then(taskActions.completeThisStep)
+      .then(taskActions.advanceStep)
+      .then(taskActions.clickContinue)
+      .then(taskActions.clickContinue)
+      .then(taskActions.completeThisStep)
+      .then(taskActions.advanceStep)
+      .then(taskActions.completeThisStep)
+#      .then(taskChecks.checkIsCompletePage) # FIXME - why cant we advance past the prior exercise ?
+      .then( ->
+        done()
+      , done)


Don't show decimals for percentVoted

Don't show decimals for percentVoted

--- angular/core/models/poll_model.coffee
+++ angular/core/models/poll_model.coffee
@@ -63,7 +63,7 @@
         0

     percentVoted: ->
-      100 * @stancesCount / @communitySize() if @communitySize() > 0
+      (100 * @stancesCount / @communitySize()).toFixed(0) if @communitySize() > 0

     undecidedCount: ->
       if @isActive()


Replaces number-of-parents data option by target.

--- app/assets/javascripts/base.js.coffee
+++ app/assets/javascripts/base.js.coffee
@@ -15,10 +15,10 @@
     regexp = new RegExp($(this).data('id'), 'g')
     target = $(this)

-    if $(this).data('number-of-parents') == 3
-      target = target.parent().parent().parent()
-
-    target.after($(this).data('fields').replace(regexp, time))
+    if $(this).data('target')
+      target = $('#' + $(this).data('target'))
+
+    target.before($(this).data('fields').replace(regexp, time))
     event.preventDefault()

   $('input[data-autocomplete]').each (k, v) ->


cleaned request code

cleaned request code

--- app/assets/javascripts/card.js.coffee
+++ app/assets/javascripts/card.js.coffee
@@ -7,6 +7,6 @@
 jQuery ->
   $(".request-btn").click(->
     if $("#sign_out").length > 0
-      index = $(this).parent().parent().parent().parent().parent()
+      index = $(this).parents(".proposal")
       $(index).find(".request").toggleClass("hidden")
   )


Handle empty sort keys, displayed as "-"

Using "-" causes errors when sorting, so it's converted to an empty
string instead.

--- app/assets/javascripts/core.js.coffee
+++ app/assets/javascripts/core.js.coffee
@@ -70,7 +70,12 @@
     )

 window.jQuery.fn.dataTableExt.oSort["sort-key-pre"] = (a) ->
-    return $(a).data("sort-key")
+    key = $(a).data("sort-key")
+
+    if key == "-"
+        return ""
+    else
+        return key

 window.jQuery.fn.dataTableExt.oSort["sort-key-asc"] = window.jQuery.fn.dataTableExt.oSort["string-asc"]
 window.jQuery.fn.dataTableExt.oSort["sort-key-desc"] = window.jQuery.fn.dataTableExt.oSort["string-desc"]


Refactor: simplify user_patch json structure creation

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -57,10 +57,11 @@
 #

 save_data = ->
-  user_patch = {}
-  user_patch.name = user_name().text()              # TODO: does this need to be HTML, SQL, etc. escaped?
-  user_patch.title = user_title().text()            # TODO: does this need to be HTML, SQL, etc. escaped?
-  user_patch.highlights = highlights_view_to_model(user_highlights().html())  # TODO: does this need to be HTML, SQL, etc. escaped?
+  user_patch = {
+    name: user_name().text(),              # TODO: does this need to be HTML, SQL, etc. escaped?
+    title: user_title().text(),            # TODO: does this need to be HTML, SQL, etc. escaped?
+    highlights: highlights_view_to_model(user_highlights().html())  # TODO: does this need to be HTML, SQL, etc. escaped?
+  }
   $.ajax({
     url: "/users/#{user.id}.json",
     type: 'PUT',


add translation

add translation

--- app/assets/javascripts/i18n.js.coffee
+++ app/assets/javascripts/i18n.js.coffee
@@ -34,6 +34,7 @@
       silent:           'I\'m silent'
       ringing:          'I\'m receiving a call'
       talking:          'I\'m currently talking'
+      unknown:          'Availability unknown'
     customers:
       customer_is:      'customer is'
       customers_are:    'customers are'
@@ -188,6 +189,7 @@
       silent:           'Ich spreche nicht'
       ringing:          'Ich werde grade angerufen'
       talking:          'Ich spreche grade'
+      unknown:          'Bereitschaft unbekannt'
     customers:
       customer_is:      'Kunde ist'
       customers_are:    'Kunden sind'


Refactor labs.js map code, again

Refactor labs.js map code, again

--- app/assets/javascripts/labs.js.coffee
+++ app/assets/javascripts/labs.js.coffee
@@ -144,13 +144,12 @@
           window.labs.push(lab)

     map.on 'zoomend', ->
-      console.log allLabs
       currentZoom = map.getZoom()
-      LeafIcon = L.Icon.extend(options: iconSize: [
-          currentZoom
-          currentZoom
+      scaledIcon = L.Icon.extend(options: iconSize: [
+          10
+          10
           ])
-      allLabs.setIcon LeafIcon
+      allLabs.setIcon scaledIcon
       return

     windowHeight = ->


map fix

map fix

--- app/assets/javascripts/labs.js.coffee
+++ app/assets/javascripts/labs.js.coffee
@@ -45,7 +45,7 @@
     location = [$('#lab-map').data('latitude'), $('#lab-map').data('longitude')]
     labmap = L.map('lab-map', { scrollWheelZoom: false, zoomControl: false, loadingControl: true }).setView(location, 14 )
     new L.Control.Zoom({ position: 'topright' }).addTo(labmap)
-    L.tileLayer('https://{s}.tiles.mapbox.com/v3/examples.map-9ijuk24y/{z}/{x}/{y}.png', { attribution: osmAttrib }).addTo(labmap)
+    L.tileLayer('https://{s}.tiles.mapbox.com/v3/johnrees.i8b9pofn/{z}/{x}/{y}.png', { attribution: osmAttrib }).addTo(labmap)
     icon = L.icon({
       iconUrl: window.mapIcons[$('#lab-map').data('kind-name')]
       iconSize:     [35, 35]
@@ -93,7 +93,7 @@

     window.map = map
     new L.Control.Zoom({ position: 'topleft' }).addTo(map)
-    L.tileLayer('https://{s}.tiles.mapbox.com/v3/examples.map-9ijuk24y/{z}/{x}/{y}.png', { attribution: osmAttrib, maxZoom: 14 }).addTo(map)
+    L.tileLayer('https://{s}.tiles.mapbox.com/v3/johnrees.i8b9pofn/{z}/{x}/{y}.png', { attribution: osmAttrib, maxZoom: 14 }).addTo(map)
     navigator.geolocation.getCurrentPosition((position)->
       map.setView([position.coords.latitude, position.coords.longitude], 4)
     )


app: remove unnecessary api_call

app: remove unnecessary api_call

Since the chart is loaded last and makes an API call this one is superfluous

--- app/assets/javascripts/lib/app.coffee
+++ app/assets/javascripts/lib/app.coffee
@@ -41,7 +41,6 @@
       # thus continue the else part
     else
       @load_user_values()
-      @call_api()

   setup_fce: =>
     @update_fce_checkboxes()


MainView: broken push

MainView: broken push

--- client/Main/maincontroller/mainviewcontroller.coffee
+++ client/Main/maincontroller/mainviewcontroller.coffee
@@ -33,7 +33,7 @@

     mainController.on "ToggleChatPanel", -> mainView.chatPanel.toggle()

-    if KD.checkFlag 'super-admin' then
+    if KD.checkFlag 'super-admin'
     then KDView.setElementClass body, 'add', 'super'
     else KDView.setElementClass body, 'remove', 'super'


Fix ModelViewSelection specs

Fix ModelViewSelection specs

--- spec/model-view-selection-spec.coffee
+++ spec/model-view-selection-spec.coffee
@@ -7,14 +7,16 @@
 describe "ModelViewSelection", ->
   beforeEach ->
     @trigger = jasmine.createSpy('trigger')
+
     @items = []
     @items.push(new Thread(id: "#{ii}")) for ii in [0..99]

     @view = new ModelView()
-    @view._pages =
-      "0":
-        items: @items
-        loaded: true
+    @view.indexOfId = jasmine.createSpy('indexOfId').andCallFake (id) =>
+      _.findIndex(@items, _.matcher({id}))
+    @view.get = jasmine.createSpy('get').andCallFake (idx) =>
+      @items[idx]
+
     @selection = new ModelViewSelection(@view, @trigger)

   it "should initialize with an empty set", ->


Client: Activity: Tests: Updated getComponents method of allprivatechannels route

--- client/activity/lib/routes/allprivatechannels.coffee
+++ client/activity/lib/routes/allprivatechannels.coffee
@@ -10,4 +10,5 @@

   getComponents: (state, callback) ->

-    helper.renderWithBackgroundChannel BrowsePrivateChannelsModal, callback
+    helper.renderWithBackgroundChannel BrowsePrivateChannelsModal.Container, callback
+


DefineStackView: group the missing/required fields information in preview section

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -400,15 +400,17 @@

   createReportFor = (data, type) ->

-    if data.length > 0
+    if (Object.keys data).length > 0
       console.warn "#{type.capitalize()} for preview requirements: ", data

-      issueList = ''
-      for issue in data
-        issueList += " - #{issue}\n"
-
-      issues = "> Following #{type} found while generating
-                preview for this template: \n#{issueList}"
+      issues = ''
+      for issue of data
+        if issue is 'userInput'
+          issues += " - These variables: `#{data[issue]}`
+                        will be requested from user.\n"
+        else
+          issues += " - These variables: `#{data[issue]}`
+                        couldn't find in `#{issue}` data.\n"
     else
       issues = ''

@@ -425,8 +427,8 @@
     availableData = { group, account, custom }

     requiredData  = requirementsParser template
-    errors        = []
-    warnings      = []
+    errors        = {}
+    warnings      = {}

     fetchUserData = (callback) ->

@@ -437,7 +439,8 @@
         for field in data

           if type is 'userInput'
-            warnings.push "Variable `#{field}` will be requested from user."
+            warnings.userInput ?= []
+            warnings.userInput.push field
             continue

           if content = jspath.getAt availableData[type], field
@@ -446,7 +449,8 @@
               else ///\${var.koding_#{type}_#{field}}///g
             template = template.replace search, content
           else
-            errors.push "Variable `#{field}` not found in `#{type}` data."
+            errors[type] ?= []
+            errors[type].push field

       @createPreviewModal { errors, warnings, template }


minor: copy fix [ci skip]

minor: copy fix [ci skip]

--- client/app/lib/providers/computecontroller.ui.coffee
+++ client/app/lib/providers/computecontroller.ui.coffee
@@ -181,7 +181,7 @@
           title   : "Reinitialize Stack?"
           message : "
             If you choose to proceed, this stack and all the VMs will be
-            re-initialized from latest revision of this stack.
+            re-initialized from the latest revision of this stack.
             You will lose all of your existing files, workspaces, VMs and all
             of your data.
           "


TailerPane: Remove unnecessary tailFile bound.

--- client/ide/lib/views/filefinder/idefilefinder.coffee
+++ client/ide/lib/views/filefinder/idefilefinder.coffee
@@ -89,7 +89,6 @@
     @listController.getView().on 'ItemWasAdded', (item) =>
       item.once 'viewAppended', =>
         item.child.on 'FileNeedsToBeOpened', @bound 'openFile'
-        item.child.on 'FileNeedsToBeTailed', @bound 'tailFile'

     @content.addSubView @listController.getView()


landing: put extra line back

landing: put extra line back

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -345,6 +345,7 @@

     return groupName

+
   checkIfGroupExists: (groupName, callback) ->

     $.ajax


Change event is no longer listened for

Change event is no longer listened for

--- coffee/codesearch.coffee
+++ coffee/codesearch.coffee
@@ -56,5 +56,4 @@
         return

     jQuery('input[type="search"]').keyup search
-    jQuery('input[type="search"]').change search
     return


table height change

table height change

--- coffee/directives.coffee
+++ coffee/directives.coffee
@@ -47,7 +47,7 @@
     templateUrl: 'attendee-table.html'
     link: (scope, element, attr) ->
       scope.eventIds = Object.keys(scope.events)
-      scope.tableHeight = if window.innerHeight then "#{window.innerHeight - 150}px" else '500px'
+      scope.tableHeight = if window.innerHeight then "#{window.innerHeight - 40}px" else '350px'
       scope.columnWidth = (80 / scope.eventIds.length) - 0.1
       scope.rsvpMeta = rsvpMeta


errors: added TypeError

errors: added TypeError

--- coffee/src/errors.coffee
+++ coffee/src/errors.coffee
@@ -15,6 +15,9 @@
 NotSupportedError = (method, extra) ->
   throw new Error "#{method} not supported. #{extra ? ''}"

+TypeError = (variable, expectedType) ->
+  throw new Error "Type error: `#{variable}` is not of type #{expectedType}."
+
 UndefinedShellError = (shell) ->
   throw new Error "Attempt to construct undefined shell #{shell}"

@@ -26,5 +29,6 @@
 acorn.errors.MissingParameterError = MissingParameterError
 acorn.errors.NotImplementedError = NotImplementedError
 acorn.errors.NotSupportedError = NotSupportedError
+acorn.errors.TypeError = TypeError
 acorn.errors.UndefinedShellError = UndefinedShellError
 acorn.errors.URLError = URLError


add possibility to not escape values

add possibility to not escape values

--- app/scripts/directives.coffee
+++ app/scripts/directives.coffee
@@ -65,25 +65,32 @@

 korpApp.directive "escaper", () ->
     link : ($scope, elem, attr) ->
-        doNotEscape = ["*=", "!*="]
-        escape = (val) ->
-            if $scope.orObj.op not in doNotEscape
-                regescape(val)
-            else
-                val
-
-        unescape = (val) ->
-            if $scope.orObj.op not in doNotEscape
-                unregescape(val)
-            else
-                val
+
+        if $scope.escape == false
+            escape = (val) ->
+                return val
+            unescape = (val) ->
+                return val
+        else
+            doNotEscape = ["*=", "!*="]
+            escape = (val) ->
+                if $scope.orObj.op not in doNotEscape
+                    regescape(val)
+                else
+                    val
+
+            unescape = (val) ->
+                if $scope.orObj.op not in doNotEscape
+                    unregescape(val)
+                else
+                    val

         $scope.input = unescape $scope.model
         $scope.$watch "input", () ->
-            $scope.model = escape($scope.input)
+            $scope.model = escape $scope.input

         $scope.$watch "orObj.op", () ->
-            $scope.model = escape($scope.input)
+            $scope.model = escape $scope.input

 korpApp.directive "tokenValue", ($compile, $controller, extendedComponents) ->


Fix undefined description bug

Fix undefined description bug

--- src/smc-webapp/chat/actions.coffee
+++ src/smc-webapp/chat/actions.coffee
@@ -156,6 +156,7 @@
             end_of_context_index = end_of_mention_index + CONTEXT_SIZE

             # Add relevant ellpises depending on size of full message
+            description = ""
             if mention.get('plainTextIndex') != 0
                 description = "... "
             description += @store.get('message_plain_text').slice(end_of_mention_index, end_of_context_index).trim()


JSListView update

JSListView update

--- JSKit/04_JSListView.coffee
+++ JSKit/04_JSListView.coffee
@@ -104,7 +104,7 @@
 		return num

 	setSelect:(select)->
-		$(@_viewSelector+"_select").select(select)
+		$(@_viewSelector+"_select").val(select)

 	sortReflection:->
 		if (@_style == "JSListStyleTypeSortable")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -185,9 +185,8 @@

   hsl = color.toHsl()

-  log hsl
-
-  equal hsl[0], 22
+  # why won't this value round in #toHsl???
+  equal hsl[0].round(), 22
   equal hsl[1].toFixed(2), 0.38
   equal hsl[2].toFixed(2), 0.13
   equal hsl[3], 1.0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -193,8 +193,6 @@
   equal hsl[3], 1.0

 test "#hue", ->
-  # fix hue conversion rounding errors
-
   color = Color2(34, 54, 239)

   color2 = color.hue(60)
@@ -205,8 +203,6 @@
   equal color2.a, 1.0

 test "#hue$", ->
-  # fix hue conversion rounding errors
-
   color = Color2(34, 54, 239)

   color.hue$(60)
@@ -217,8 +213,6 @@
   equal color.a, 1.0

 test "#complement", ->
-  # fix hue conversion rounding errors
-
   color = Color2(10, 30, 50)

   color2 = color.complement()
@@ -229,8 +223,6 @@
   equal color2.a, 1.0

 test "#complement$", ->
-  # fix hue conversion rounding errors
-
   color = Color2(10, 30, 50)

   color.complement$()


:racehorse: Cache text in TokenIterator

:racehorse: Cache text in TokenIterator

--- src/token-iterator.coffee
+++ src/token-iterator.coffee
@@ -53,6 +53,8 @@
         else
           @screenEnd = @screenStart + tag
           @bufferEnd = @bufferStart + tag
+
+        @text = @line.text.substring(@screenStart, @screenEnd)
         return true

     false
@@ -68,8 +70,7 @@

   getScopes: -> @scopes

-  getText: ->
-    @line.text.substring(@screenStart, @screenEnd)
+  getText: -> @text

   isSoftTab: ->
     @line.specialTokens[@index] is SoftTab


Add Author and Notes sections to documentation header

--- src/assembla.coffee
+++ src/assembla.coffee
@@ -29,6 +29,11 @@
 #   hubot space <space> - look up a space by id or wiki_name [assembla]
 #   ticket #<ticket> - show ticket on default space [assembla]
 #
+# Notes:
+#   None
+#
+# Author:
+#   benwtr

 SPACE = process.env.HUBOT_ASSEMBLA_SPACE
 SSH_DEPLOY_ACTION_ID = process.env.HUBOT_ASSEMBLA_DEPLOY_ACTION_ID


Removed no longer used shortcut

Removed no longer used shortcut

--- core/app/backbone/contrib/backbone_react_glue.coffee
+++ core/app/backbone/contrib/backbone_react_glue.coffee
@@ -4,8 +4,6 @@
     throw "You haven't defined component, silly developer" unless options.component
     @on 'show', ->
       React.renderComponent(@options.component, @el)
-
-window.R = React.DOM

 define_tag = (tag) ->
   return unless {}.hasOwnProperty.call(React.DOM, tag) and tag != 'injection'


Add link to followed username

Add link to followed username

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -135,7 +135,7 @@
             _span ["feed-activity-description"],
               Factlink.Global.t.followed
             " "
-            _span ["feed-activity-username"],
+            _a ["feed-activity-username", href: followed_user.link(), rel:"backbone"],
               _img ["avatar-image feed-activity-followed-avatar", src: followed_user.avatar_url(32), style: {height: '32px', width: '32px'}]
               " "
               followed_user.get('name')


msgflo-newrelic-jobstats: Fix not all roles showing up [major]

New Relic has a default LIMIT of 10 when using FACET, so if
there were more than 10 roles, the ones with the smallest values
were getting hidden.

--- src/utils/newrelic.coffee
+++ src/utils/newrelic.coffee
@@ -7,7 +7,8 @@
   insights = new Insights options
   return callback new Error 'Missing app name' if not app

-  q = "SELECT average(duration), stddev(duration), percentile(duration, 90) FROM MsgfloJobCompleted FACET role SINCE #{options.since} ago WHERE outport != 'error' AND appName = '#{app}'"
+  limit = 100 # maximum number of role supported
+  q = "SELECT average(duration), stddev(duration), percentile(duration, 90) FROM MsgfloJobCompleted FACET role SINCE #{options.since} ago WHERE outport != 'error' AND appName = '#{app}' LIMIT #{limit}"
   insights.query q, (err, body) ->
     return callback err if err


Mandatory count template in catalogs.

Mandatory count template in catalogs.

--- client/lib/catalog.coffee
+++ client/lib/catalog.coffee
@@ -110,16 +110,16 @@
         ]
         limit: Session.get variables.limit

-    templates.count?.documentsCount = ->
+    templates.count.documentsCount = ->
       Session.get variables.count

-    templates.count?.countDescription = ->
+    templates.count.countDescription = ->
       documentClass.verboseNameWithCount Session.get(variables.count)

-    templates.count?.noDocuments = ->
+    templates.count.noDocuments = ->
       Session.get(variables.ready) and not Session.get(variables.count)

-    templates.count?.documentsFilter = ->
+    templates.count.documentsFilter = ->
       Session.get variables.filter

     templates.loading.documentsLoading = ->


Display an error for cancelled jobs as well.

--- client/publication.coffee
+++ client/publication.coffee
@@ -656,7 +656,7 @@
     transform: null # So that we don't have any complications passing it to FlashMessage.error

   # Only the latest job is pushed to the client, so index 0
-  return unless publication?.jobs?[0]?.status is 'failed'
+  return unless publication?.jobs?[0]?.status in ['failed', 'cancelled']

   FlashMessage.error "The last job associated with the publication has failed.", {template: 'failedJobLink', data: publication}, false, false # Don't display the stack


rename API to peacemarkerapi

rename API to peacemarkerapi

--- client/src/backend.coffee
+++ client/src/backend.coffee
@@ -53,21 +53,21 @@

     getPlaces: (callback) ->
         request
-            .get('/api/peacemarker')
+            .get('/api/peacemarkerapi')
             .set('Accept', 'application/json')
             .end (err, res) ->
                 callback err, res.body

     createPlaces: (peacemarker, callback) ->
         request
-            .post('/api/peacemarker')
+            .post('/api/peacemarkerapi')
             .send(peacemarker)
             .end (err, res) ->
                 callback err, res.body

     putPlacesid: (peacemarker, callback) ->
         request
-            .put("/api/peacemarker/#{peacemarker.id}")
+            .put("/api/peacemarkerapi/#{peacemarker.id}")
             .send(peacemarker)
             .set('Accept', 'application/json')
             .end (err, res) ->
@@ -75,5 +75,5 @@

     deletePlacesid: (peacemarker, callback) ->
         request
-            .del("/api/peacemarker/#{peacemarker.id}")
+            .del("/api/peacemarkerapi/#{peacemarker.id}")
             .end callback


maybe printing something out would be nice...


Settings controller doesn't need to be ObjectController

--- app/controllers/settings/index.coffee
+++ app/controllers/settings/index.coffee
@@ -1,6 +1,6 @@
 `import Ember from 'ember'`

-Controller = Ember.ObjectController.extend
+Controller = Ember.Controller.extend
   settings: Ember.computed.alias('model.settings')

   settingsChanged: (->


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -71,18 +71,17 @@
   @param {Function} [callback] The listener to remove.
   ###
   unbind: (events, callback) ->
-    events.split(" ").each (namespacedEvent) ->
-      [event, namespace] = namespacedEvent.split(".")
+    [event, namespace] = namespacedEvent.split(".")

-      if event
-        eventCallbacks[event] ||= []
-
-        if callback
-          eventCallbacks[event].remove(callback)
-        else
-          eventCallbacks[event] = []
-      else if namespace
-        ;
+    if event
+      eventCallbacks[event] ||= []
+
+      if callback
+        eventCallbacks[event].remove(callback)
+      else
+        eventCallbacks[event] = []
+    else if namespace
+      ;

   ###*
   Calls all listeners attached to the specified event.


Trigger initial tutorial step changes off the body

--- app/controllers/tutorial_steps.coffee
+++ app/controllers/tutorial_steps.coffee
@@ -8,7 +8,7 @@
     '''
     attachment: to: '.main-pair'
     onLeave: ->
-      $(document).trigger 'enter-tutorial-step', 1
+      $('body').trigger 'enter-tutorial-step', 1

   new Step
     title: 'Subject'
@@ -20,7 +20,7 @@
     '''
     attachment: arrowDirection: 'right', x: 'right', to: '.classifier img.subject', at: x: 'left'
     onLeave: ->
-      $(document).trigger 'enter-tutorial-step', 2
+      $('body').trigger 'enter-tutorial-step', 2
       console.log 'here'

   new Step


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -33,6 +33,7 @@
   rightEnemy = GameObject
     x: 100
     y: 50
+    width: 10
     height: 10


Use jquery for Post.activate_posts ajax

Use jquery for Post.activate_posts ajax

--- app/javascript/src/legacy/post.coffee
+++ app/javascript/src/legacy/post.coffee
@@ -135,23 +135,21 @@
         return
 )
     return
-  activate_posts: (post_ids, finished) ->
-    notice 'Activating ' + post_ids.length + (if post_ids.length == 1 then ' post' else ' posts')
-    params = {}
-    params['post_ids[]'] = post_ids
-    new (Ajax.Request)('/post/activate.json',
-      requestHeaders: 'X-CSRF-Token': jQuery('meta[name=csrf-token]').attr('content')
-      parameters: params
-      onComplete: (resp) ->
-        resp = resp.responseJSON
-        if resp.success
-          if finished
-            finished resp
-        else
-          notice 'Error: ' + resp.reason
-        return
-)
-    return
+
+  activate_posts: (postIds, finished) ->
+    notice "Activating #{postIds.length} #{if postIds.length == 1 then 'post' else 'posts'}"
+
+    jQuery.ajax '/post/activate.json',
+      data:
+        post_ids: postIds
+      dataType: 'json'
+      method: 'POST'
+
+    .done finished
+
+    .fail (xhr) =>
+      notice "Error: #{xhr.responseJSON?.reason ? 'unknown error'}"
+
   activate_all_posts: ->
     post_ids = []
     Post.posts.each (pair) ->


A bit more relaxed text selection detection

--- app/js/jail_iframe/util/create.coffee
+++ app/js/jail_iframe/util/create.coffee
@@ -10,5 +10,5 @@
     text, siteUrl, siteTitle

 FactlinkJailRoot.textSelected = ->
-  # At least 4 words of at least 2 characters
-  /(\w{2,}[\s-_&\/#%.,;:!()]+){4}/.test window.document.getSelection().toString()
+  # At least 3 words of at least 2 characters, separated by at most 6 non-letter chars
+  /(\w{2,}\W{1,6}){3}/.test window.document.getSelection().toString()


increase timeout

increase timeout

--- test/zorium.coffee
+++ test/zorium.coffee
@@ -873,7 +873,7 @@
       window.location.pathname.should.be '/test'
       root.isEqualNode(htmlToNode(result1)).should.be true
       done()
-    , 30
+    , 60

   it 'passes params', ->
     class App


OMG crazy test

OMG crazy test

--- www/js/globals.coffee
+++ www/js/globals.coffee
@@ -32,6 +32,7 @@
     SEARCH_NUMBER_VENUES_WITH_PHOTO: 6 # Number of venues to get photos for when searching
     TOKEN: undefined # Set in app.coffee
   GLOBALS.AUTH_URL = "https://foursquare.com/oauth2/authenticate?client_id=#{GLOBALS.CLIENT_ID}&response_type=token&redirect_uri=#{window.location.origin}"
+  alert window.location.origin

   # Determine the locale; fallback to en-US if not available.
   # HACK: Better way for this, I assume?


Renamend name of log in init.coffee

Renamend name of log in init.coffee

--- node/init.coffee
+++ node/init.coffee
@@ -75,12 +75,12 @@
 				return callback() if not error?

 				# Show error
-				log.status 'ackee', 'Loading SSL, database or generating cookieToken failed', error
+				log.status 'init', 'Loading SSL, database or generating cookieToken failed', error
 				return false

 		(callback) ->

-			log.status 'ackee', 'Setting server configuration'
+			log.status 'init', 'Setting server configuration'

 			# App configuration
 			app.use morgan('dev') if process.env.npm_package_config_debug is 'true'
@@ -104,7 +104,7 @@

 		(callback) ->

-			log.status 'ackee', "Starting server"
+			log.status 'init', 'Starting server'

 			# Start http server
 			http = http.createServer app
@@ -128,7 +128,7 @@

 					"""
 			text +=	"		=> https://localhost:#{ process.env.npm_package_config_portSSL }" if ssl?.key? and ssl.cert?
-			log.status 'ackee', text
+			log.status 'init', text

 	]


sprite class detail

sprite class detail

--- src/sprite.coffee
+++ src/sprite.coffee
@@ -1,4 +1,10 @@
 ###*
+The Sprite class provides a way to load images for use in games.
+
+By default, images are loaded asynchronously. A proxy object is
+returned immediately but though it has a draw method it will not
+draw anything to the screen until the image has been loaded.
+
 @name Sprite
 @constructor
 ###


Update sprite.coffee

Update sprite.coffee

make a few minor adjustment to the Sprite class

--- src/sprite.coffee
+++ src/sprite.coffee
@@ -7,13 +7,14 @@
 class Sprite
     #closures
     _list = []
+    _canvas = document.getElementById 'gh-canvas'
     _sortRule = (sp1, sp2) ->
         sp1._dis.level - sp2._dis.level
     _bounds =
-        top: document.getElementById('gh-canvas').height / 2
-        bottom: -document.getElementById('gh-canvas').height / 2
-        right: document.getElementById('gh-canvas').width / 2
-        left: -document.getElementById('gh-canvas').width / 2
+        top: _canvas.height / 2
+        bottom: -_canvas.height / 2
+        right: _canvas.width / 2
+        left: -_canvas.width / 2
     _boundaryCallback = (boundAction, side) ->
         #return appropriate callback function
         switch boundAction
@@ -119,7 +120,7 @@
         for own key, value of env.SPRITE_DEFAULT_CONFIG
             config[key] ?= value

-        #filter out magnitudes and angles of vectors
+        #process config object
         magnitudes = {}
         angles = {}
         for own key, value of config
@@ -129,20 +130,26 @@
             else if key.match /(^posAngle$|^motAngle$|^accAngle$)/
                 delete config[key]
                 angles[key] = value
+            else if key.match /^ba_all$/
+                delete config[key]
+                config.ba_top = value
+                config.ba_bottom = value
+                config.ba_right = value
+                config.ba_left = value

         #used to track asynchronous _update
         @_updateID = null

         #create primary objects
-        @_dis = {}
         @_pos = {}
         @_mot = {}
         @_acc = {}
+        @_dis = {}
         @_bas = {}

         #create secondary objects
         @_dis.image = new Image()
-        @_dis.context = document.getElementById('gh-canvas').getContext '2d'
+        @_dis.context = _canvas.getContext '2d'

         #set this sprite's configuration
         #"virtually" calls child's set method in derived classes
@@ -451,7 +458,7 @@
                     if Greenhorn.isDown[KEYS[token]]
                         @emit event
             #fire 'isUp' event
-            else if event.match /^isUp:(\w+|\d)$/
+            else if event.match /^isUp:(\w+|\d)/
                 token = event.split(':')[1].toUpperCase()
                 if token.match /-/
                     _emit = false
@@ -545,6 +552,16 @@
                     when 'ne'
                         if @get(tokens[0]) != tokens[2]
                             @emit event
+            else if event.match /^\w+-(eq|ne)-\w+/
+                tokens = event.split '-'
+                tokens[2] = eval tokens[2]
+                switch tokens[1]
+                    when 'eq'
+                        if @get(tokens[0]) is tokens[2]
+                            @emit event
+                    when 'ne'
+                        if @get(tokens[0]) isnt tokens[2]
+                            @emit event
         this

     #debugging
@@ -567,7 +584,11 @@
             width: #{Math.round @_dis.width}
             height: #{Math.round @_dis.height}
             visible: #{@_dis.visible}
-            boundAction: #{@_dis.boundAction}
+        bound actions:
+            top: #{@_bas.top.ba}
+            bottom: #{@_bas.bottom.ba}
+            right: #{@_bas.right.ba}
+            left: #{@_bas.left.ba}
         """
     log: ->
         console.log @report()


Reintroduce default value for argument in LinkHints::activateLink

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -359,7 +359,7 @@
   # When only one hint remains, activate it in the appropriate way.  The current frame may or may not contain
   # the matched link, and may or may not have the focus.  The resulting four cases are accounted for here by
   # selectively pushing the appropriate HintCoordinator.onExit handlers.
-  activateLink: (linkMatched, userMightOverType) ->
+  activateLink: (linkMatched, userMightOverType = false) ->
     @removeHintMarkers()

     if linkMatched.isLocalMarker


made string names overridable (maybe--missing tests)

--- marktab.coffee
+++ marktab.coffee
@@ -1,6 +1,6 @@
 class Marktab
 	# private instance variables
-	stringDefaults =
+	stringNameDefaults =
 		1: "e"
 		2: "B"
 		3: "G"
@@ -16,7 +16,7 @@
 	chordPattern = /\(([0-9]:[0-9])+\)/
 	riffPattern = /\[[0-9]:[0-9]\s[0-9rhp\/\\]+\]/

-	constructor: (@lines = []) ->
+	constructor: (@lines = [], @stringNames = stringNameDefaults) ->
 		# @lines contains an array of parsed lines, ready to be output
 		this

@@ -160,7 +160,7 @@
 		tabMap = this.normalizeTabMap(tabMap)
 		for stringNum in [1..6]
 			notes = tabMap[stringNum]
-			line = stringDefaults[stringNum] + "|-"
+			line = @stringNames[stringNum] + "|-"
 			for note in notes
 				line += (note || '-')
 				if note < 10 || !note


Parsing HTML as a document obviates the need to trim whitespace

--- src/trix/models/html_parser.coffee
+++ src/trix/models/html_parser.coffee
@@ -18,7 +18,6 @@
     try
       @createHiddenContainer()
       html = sanitizeHTML(@html)
-      html = trimInsignificantWhitespace(html)
       @container.innerHTML = html
       walker = walkTree(@container, usingFilter: nodeFilter)
       @processNode(walker.currentNode) while walker.nextNode()
@@ -198,9 +197,6 @@

     body.innerHTML

-  trimInsignificantWhitespace = (html) ->
-    html.replace(/^\s*<meta[^>]*>/i, "")
-
   isExtraBR = (element) ->
     previousSibling = element.previousElementSibling
     tagName(element) is "br" and


Clarify block appending methods

Clarify block appending methods

--- src/trix/models/html_parser.coffee
+++ src/trix/models/html_parser.coffee
@@ -53,11 +53,11 @@
     if @isBlockElement(element) and not @isBlockElement(element.firstChild)
       attributes = getBlockAttributes(element)
       unless elementContainsNode(@currentBlockElement, element) and arraysAreEqual(attributes, @currentBlock.attributes)
-        @currentBlock = @appendBlockForAttributes(attributes, element)
+        @currentBlock = @appendBlockForAttributesWithElement(attributes, element)
         @currentBlockElement = element

     else if @currentBlockElement and not elementContainsNode(@currentBlockElement, element) and not @isBlockElement(element)
-        @currentBlock = @appendBlockForAttributes({})
+        @currentBlock = @appendEmptyBlock()
         @currentBlockElement = null

   isExtraBR: (element) ->
@@ -107,12 +107,15 @@
           unless element.parentNode.firstChild is element
             @appendStringWithAttributes(" ")

-  appendBlockForAttributes: (attributes, element) ->
+  appendBlockForAttributesWithElement: (attributes, element) ->
     @blockElements.push(element)
     block = blockForAttributes(attributes)
     @blocks.push(block)
     block

+  appendEmptyBlock: ->
+    @appendBlockForAttributesWithElement({}, null)
+
   appendStringWithAttributes: (string, attributes) ->
     @appendPiece(pieceForString(string, attributes))

@@ -121,7 +124,7 @@

   appendPiece: (piece) ->
     if @blocks.length is 0
-      @appendBlockForAttributes({})
+      @appendEmptyBlock()
     @blocks[@blocks.length - 1].text.push(piece)

   appendStringToTextAtIndex: (string, index) ->


Fix wrong call to debounce method.

Fix wrong call to debounce method.

--- app/coffee/utils.coffee
+++ app/coffee/utils.coffee
@@ -44,5 +44,5 @@
 utils.safeDebounced = (scope, timeout, func) ->
     wrapper = ->
         gm.safeApply(scope, func)
-    utils.debounce(timeout, wrapper)
+    utils.debounced(timeout, wrapper)


Allow null treeTitle

Allow null treeTitle

--- server/materials/downloadMaterials.coffee
+++ server/materials/downloadMaterials.coffee
@@ -40,7 +40,7 @@
     process: (material) ->
         oldMaterial = await Material.findById(material._id)
         if oldMaterial?.force
-            logger.info("Will not overwrite forces material #{material._id}")
+            logger.info("Will not overwrite forced material #{material._id}")
             material.content = oldMaterial.content
             material.title = oldMaterial.title
             material.force = oldMaterial.force
@@ -155,6 +155,8 @@
     makeTree: (material) ->
         if material.type in ["label", "epigraph", "link"]
             return null
+        if material.treeTitle == null
+            return null
         if material.type.startsWith("sub.")
             return null
         if material.type == "news"


@nodoc for cacheOrderForm, make it _

@nodoc for cacheOrderForm, make it _

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -72,7 +72,8 @@

 		@version = 'VERSION'

-	cacheOrderForm: (data) =>
+	# @nodoc
+	_cacheOrderForm: (data) =>
 		@orderFormId = data.orderFormId
 		@orderForm = data

@@ -96,7 +97,7 @@
 				contentType: 'application/json; charset=utf-8'
 				dataType: 'json'
 				data: JSON.stringify(checkoutRequest)
-			.done @cacheOrderForm
+			.done @_cacheOrderForm

 	# Sends an OrderForm attachment to the current OrderForm, possibly updating it.
 	# @param attachmentId [String] the name of the attachment you're sending.
@@ -121,7 +122,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify(attachment)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 		if options.abort and options.subject
 			@_subjectToJqXHRMap[options.subject]?.abort()
@@ -153,7 +154,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify(updateItemsRequest)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 	# Sends a request to add an offering to the OrderForm.
 	# @param offeringId [String, Number] the id of the offering.
@@ -179,7 +180,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify(updateItemsRequest)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 	# Sends a request to update the items in the OrderForm. Items that are omitted are not modified.
 	# @param items [Array] an array of objects representing the items in the OrderForm.
@@ -199,7 +200,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify(updateItemsRequest)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm
 		.done => @_requestingItem = undefined

 	# Sends a request to remove items from the OrderForm.
@@ -233,7 +234,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify couponCodeRequest
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 	# Sends a request to remove the discount coupon from the OrderForm.
 	# @param expectedOrderFormSections [Array] (default = *all*) an array of attachment names.
@@ -252,7 +253,7 @@
 			contentType: 'application/json; charset=utf-8'
 			dataType: 'json'
 			data: JSON.stringify(checkoutRequest)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 	# Sends a request to calculates shipping for the current OrderForm, given an address object.
 	# @param address [Object] an address object
@@ -304,7 +305,7 @@
 			contentType: 'application/json; charset=utf-8',
 			dataType: 'json',
 			data: JSON.stringify(transactionRequest)
-		.done @cacheOrderForm
+		.done @_cacheOrderForm

 	# Sends a request to retrieve the orders for a specific orderGroupId.
 	# @param orderGroupId [String] the ID of the order group.


Add props to existing Clay if it exists

Add props to existing Clay if it exists

--- src/clay_sdk.coffee
+++ src/clay_sdk.coffee
@@ -4,7 +4,14 @@
   else if typeof define is 'function' and define.amd
     define(definition)
   else
-    context[name] = definition()
+    if context[name]
+      Clay = definition()
+      for key, val of Clay
+        if typeof val is 'function'
+          context[name][key] = -> val.apply Clay, arguments
+        else context[name][key] = val
+    else
+      context[name] = definition()
 )('Clay', this, ->

   Promiz = require 'promiz'


Port type annotations

Port type annotations

--- components/Jsonify.coffee
+++ components/Jsonify.coffee
@@ -10,13 +10,13 @@
     @raw = false

     @inPorts =
-      in: new noflo.Port()
-      raw: new noflo.Port()
+      in: new noflo.Port 'object'
+      raw: new noflo.Port 'boolean'
     @outPorts =
-      out: new noflo.Port()
+      out: new noflo.Port 'string'

     @inPorts.raw.on 'data', (raw) =>
-      @raw = raw is 'true'
+      @raw = String(raw) is 'true'

     @inPorts.in.on 'begingroup', (group) =>
       @outPorts.out.beginGroup group


don't prefer online version

don't prefer online version

--- config/application.coffee
+++ config/application.coffee
@@ -99,7 +99,6 @@
     generate:
       options:
         basePath: './dist'
-        preferOnline: true
         hash: true
         verbose: false
         timestamp: false