Add marionette views for divisions.

Add marionette views for divisions.

--- src/views.coffee
+++ src/views.coffee
@@ -1004,10 +1004,11 @@
             event.preventDefault()
             # TODO: Add here functionality for leaving feedback.

-    class PositionDetailsView extends SMItemView
+    class PositionDetailsView extends SMLayout
         id: 'details-view-container'
         className: 'navigation-element'
         template: 'position'
+        regions: 'admin_divisions': '.admin-div-placeholder'
         events:
             'click .icon-icon-close': 'self_destruct'
         initialize: (opts) ->
@@ -1023,41 +1024,13 @@
                     lon: coords[0]
                     lat: coords[1]
                     unit_include: 'name,root_services,location'
-                    type: 'neighborhood,income_support_district,health_station_district,voting_district'
+                    type: 'neighborhood,income_support_district,health_station_district,rescue_district'
                     geometry: 'true'
                 reset: true

         render_admin_divs: ->
-            target_el = @$el.find '.admin-div-placeholder'
-            map =                            window.map_view.map
-            @div_list.each (div) ->
-                # Please don't hate me
-                html = "
"
-
-                html = "
#{i18n.t('district.' + div.get 'type')}: "
-                if div.get 'name'
-                    html += "#{p13n.get_translated_attr div.get 'name'}"
-                else
-                    html += "#{div.get 'origin_id'}"
-                html += "
"
-                if div.get 'unit'
-                    html += "
"
-                    html += "#{p13n.get_translated_attr div.get('unit').name}"
-                    html += "
"
-
-                html += "
"
-                el = $(html)
-                layer = null
-                el.on 'mouseenter', (ev) ->
-                    if layer
-                        map.removeLayer layer
-                    layer = L.geoJson div.get 'boundary'
-                    layer.addTo map
-                el.on 'mouseleave', (ev) ->
-                    if layer
-                        map.removeLayer layer
-                    layer = null
-                target_el.append el
+            @admin_divisions.show new DivisionListView
+                collection: @div_list

         self_destruct: ->
             @selected_position.clear()
@@ -1572,6 +1545,21 @@

     class SearchResultsView extends SMCollectionView
         itemView: SearchResultView
+
+    class DivisionListItemView extends SMItemView
+        events:
+            'click': 'handle_click'
+        tagName: 'li'
+        template: 'division-list-item'
+        handle_click: =>
+            if @model.has 'unit'
+                unit = new models.Unit(@model.get('unit'))
+                app.commands.execute 'setUnit', unit
+                app.commands.execute 'selectUnit', unit
+    class DivisionListView extends SMCollectionView
+        tagName: 'ul'
+        className: 'division-list'
+        itemView: DivisionListItemView

     class SearchLayoutView extends SMLayout
         className: 'search-results navigation-element'


Add plugin.defaultEnv property. #748

Add plugin.defaultEnv property. #748

--- src/watch.coffee
+++ src/watch.coffee
@@ -334,12 +334,18 @@
     joinConfig = config._normalized.join
     plugins    = getPlugins packages, config

+    plugins = plugins.filter (plugin) ->
+      plugin.optimize ?= plugin.minify if propIsFunction 'minify'
+      plugin.defaultEnv ?= if not config.optimize and typeof plugin.optimize is 'function'
+        'production'
+      else
+        '*'
+      plugin.defaultEnv is '*' or plugin.defaultEnv in config.env
+
     # Get compilation methods.
-    compilers  = plugins.filter(propIsFunction 'compile')
-    linters    = plugins.filter(propIsFunction 'lint')
-    optimizers = plugins.filter(propIsFunction 'optimize').concat(
-      plugins.filter(propIsFunction 'minify')
-    )
+    compilers  = plugins.filter propIsFunction 'compile'
+    linters    = plugins.filter propIsFunction 'lint'
+    optimizers = plugins.filter propIsFunction 'optimize'

     # Get plugin onCompile callbacks
     callbacks  = plugins.filter(propIsFunction 'onCompile').map((plugin) -> (args...) -> plugin.onCompile args...)


Tests: Fix failed test

Tests: Fix failed test

--- client/ide/test/views/tabview/ideview.test.coffee
+++ client/ide/test/views/tabview/ideview.test.coffee
@@ -227,22 +227,23 @@

     it 'should create the IDEEditorPane and call createPane_ method and emit change object', ->

-      uid        = mock.getMockMachine().uid
-      file       = createFile()
-      path       = file.path
-      content    = 'foo'
-      callback   = ->
-      eventFlag  = no
-      emitChange = yes
-      isReadOnly = no
-      change     = context: file: { content, path, machine: { uid } }
+      uid           = mock.getMockMachine().uid
+      file          = createFile()
+      path          = file.path
+      content       = 'foo'
+      callback      = ->
+      eventFlag     = no
+      emitChange    = yes
+      isReadOnly    = no
+      isActivePane  = yes
+      change        = context: file: { content, path, machine: { uid } }

       createPaneSpy = expect.spyOn ideView, 'createPane_'
       emitChangeSpy = expect.spyOn ideView, 'emitChange'
       expect.spyOn ideView, 'switchToEditorTabByFile'
       ideView.once 'NewEditorPaneCreated', -> eventFlag = yes

-      editorPane      = ideView.createEditorAfterFileCheck file, content, callback, emitChange, isReadOnly
+      editorPane      = ideView.createEditorAfterFileCheck file, content, callback, emitChange, isReadOnly, isActivePane
       [ ep, opt, fl ] = createPaneSpy.calls.first.arguments
       [ ed, ch ]      = emitChangeSpy.calls.first.arguments

@@ -250,7 +251,12 @@
       expect(ep).toBe editorPane
       expect(ed).toBe editorPane
       expect(fl).toBe file
-      expect(opt).toEqual { name: file.name, editor: editorPane, aceView: editorPane.aceView }
+      expect(opt).toEqual {
+        name          : file.name,
+        editor        : editorPane,
+        aceView       : editorPane.aceView,
+        isActivePane  : isActivePane
+      }
       expect(eventFlag).toBe yes
       expect(ch).toEqual change
       expect(editorPane.options.ideViewHash).toBe ideView.hash


Model: removed withLink

Model: removed withLink

--- coffee/src/model.coffee
+++ coffee/src/model.coffee
@@ -83,12 +83,6 @@
     # Make the request, allowing the user to override any Ajax options.
     $.ajax _.extend params, options

-
-  @withLink: (link) =>
-    link ?= ''
-    shell = acorn.LinkShell.bestMatchingShell(link)
-    @withShellData acorn.LinkShell.shellForLink link
-
   @withData: (data) =>
     if not data?
       data = acornid: 'new'


Remove undefined variable.

Remove undefined variable.

--- cms/static/coffee/spec/views/overview_spec.coffee
+++ cms/static/coffee/spec/views/overview_spec.coffee
@@ -49,7 +49,6 @@
         sinon.useFakeXMLHttpRequest()

     afterEach ->
-        @xhr.restore()
         delete window.analytics
         delete window.course_location_analytics


Don't allow old remote frames to bump new ones when received out of order

--- javascripts/leapjs.networking-0.1.coffee
+++ javascripts/leapjs.networking-0.1.coffee
@@ -66,6 +66,8 @@
   receiveRemoteFrame: (userId, frameData)->
     # don't add old frames
     return if @remoteFrames[userId] and (@remoteFrames[userId].timestamp > frameData.timestamp)
+    previousFrameData = @remoteFrames[userId]
+    return if previousFrameData and (previousFrameData.timestamp > frameData.timestamp)

     @remoteFrames[userId] = frameData


payment: don't send the plan interval from plan, pricing view state will automatically be used

--- client/Pricing/views/singleplan.coffee
+++ client/Pricing/views/singleplan.coffee
@@ -74,11 +74,9 @@
         { title, monthPrice, yearPrice } = @getOptions()
         planTitle = title.toLowerCase()

-        { planInterval } = @state
-
         @emit 'PlanSelected', {
           planTitle, monthPrice, yearPrice
-          reducedMonth, discount, planInterval
+          reducedMonth, discount
         }


KodingRouter: Fixed appManager.tell handleQuery

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -79,9 +79,13 @@

         @setPageTitle nicenames[app] ? app
         appManager = KD.getSingleton "appManager"
+
+        appManager.once "AppCreated", (appInstance) ->
+          if app.name is appInstance.name
+            appManager.tell app, 'handleQuery', query
+
         appManager.open app, (appInstance)=>
           appInstance.setOption "initialRoute", @getCurrentPath()
-        appManager.tell app, 'handleQuery', query

   handleNotFound:(route)->


Client: Derp. Call the function, don't use a ref.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -432,7 +432,7 @@
             "your team"

           # Capitalize the text if necessary
-          text = "#{text[0].toUpperCase}#{text[1...]}"  if index == 0
+          text = "#{text[0].toUpperCase()}#{text[1...]}"  if index == 0
           text
         else
           [text] = args.splice(0, 1)


replace detect with find

replace detect with find

--- client/shared/models/user_model.coffee
+++ client/shared/models/user_model.coffee
@@ -19,7 +19,7 @@
     (_.find(AppConfig.locales, (h) => h.key == @locale) or {}).name

   identityFor: (type) ->
-    _.detect @identities(), (i) -> i.identityType == type
+    _.find @identities(), (i) -> i.identityType == type

   membershipFor: (group) ->
     _.head @recordStore.memberships.find(groupId: group.id, userId: @id)


#288 Solved erratic bug when root node is replaced by child

--- client/src/app/game/board/board.coffee
+++ client/src/app/game/board/board.coffee
@@ -75,12 +75,15 @@
         treeJson = $.jstree._reference(element).get_json()
         if treeJson.length isnt 1
           scope.updateTree( boardConverter.convert(currentGame.version.board) ) # Refresh display from model
-          throw Error('Tree should have one and only one root')
+          throw Error 'Tree should have one and only one root'
         else
           treeJson = treeJson[0]
-          console.log treeJson
-          reverted = boardConverter.revert(treeJson)
-          currentGame.version.board = reverted
+          if treeJson.metadata.nodeId isnt 0
+            scope.updateTree( boardConverter.convert(currentGame.version.board) ) # Refresh display from model
+            throw Error 'Trying to replace root node'
+          else # OK
+            reverted = boardConverter.revert(treeJson)
+            currentGame.version.board = reverted

       scope.updateTree = (board) ->
         $(element).jstree


Update the minimum Serrano version that notifies in responses

--- coffee/cilantro/models/exporter.coffee
+++ coffee/cilantro/models/exporter.coffee
@@ -12,7 +12,7 @@

         # Versions greater than or equal to this version are considered to
         # support notification on completion.
-        minSerranoVersionProgressEnabled: [2, 0, 17]
+        minSerranoVersionProgressEnabled: [2, 0, 16]

         url: ->
             c.getSessionUrl('exporter')


loop in

loop in

--- collections/artwork_collections.coffee
+++ collections/artwork_collections.coffee
@@ -109,7 +109,7 @@
       url: @url() + '&artwork_id=' + artwork.id
       error: options.error
       success: (cols) =>
-        cols.each (c) => @get(c.id).artworks.add(artwork)
+        cols.each (c) => @get(c.id)?.artworks.add(artwork)
         options?.success?()

 module.exports.ArtworkCollection = ArtworkCollection


remove deselect after open

remove deselect after open

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -3700,8 +3700,6 @@
           gammaDir.expand()
           deltaFile = gammaDir.entries.children[1]

-          treeView.deselect()
-
           [dragStartEvent, dragEnterEvent, dropEvent] =
               eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir, treeView)
           console.log treeView.getSelectedEntries()
@@ -3775,9 +3773,6 @@
       beforeEach ->
         waitForWorkspaceOpenEvent ->
           atom.workspace.open(thetaFilePath)
-
-        runs ->
-          treeView.deselect()

       it "should move the directory to the hovered directory", ->
         # Dragging thetaDir onto alphaDir


implement new tooltips for add_points

implement new tooltips for add_points

--- src/add_points.coffee
+++ src/add_points.coffee
@@ -57,13 +57,6 @@
         xscale = prevchart.xscale()
         yscale = prevchart.yscale()

-        # individual tooltips
-        indtip = d3.tip()
-                   .attr('class', "d3-tip #{tipclass}")
-                   .html((d,i) -> indID[i])
-                   .direction('e')
-                   .offset([0,10+pointsize])
-        svg.call(indtip)

         pointGroup = svg.append("g").attr("id", "points")
         points =
@@ -78,9 +71,8 @@
                   .attr("fill", (d,i) -> pointcolor[group[i]])
                   .attr("stroke", pointstroke)
                   .attr("stroke-width", "1")
-                  .on("mouseover.paneltip", indtip.show)
-                  .on("mouseout.paneltip", indtip.hide)

+        indtip = d3panels.tooltip_create(d3.select("body"), points, {tipclass:tipclass}, (d,i) -> indID[i])

         # jitter missing values?
         if prevchart.xNA() or prevchart.yNA()
@@ -131,7 +123,7 @@
     # function to remove chart
     chart.remove = () ->
                       pointGroup.remove()
-                      indtip.destroy()
+                      d3panels.tooltip_destroy(indtip)
                       return null

     # return the chart function


Add Buffer.logLines()

Add Buffer.logLines()

--- src/app/buffer.coffee
+++ src/app/buffer.coffee
@@ -242,4 +242,9 @@
   backwardsScanInRange: (regex, range, iterator) ->
     @scanInRange regex, range, iterator, true

+  logLines: (start=0, end=@getLastRow())->
+    for row in [start..end]
+      line = @lineForRow(row)
+      console.log row, line, line.length
+
 _.extend(Buffer.prototype, EventEmitter)


Update cursor

Update cursor

--- src/app/cursor.coffee
+++ src/app/cursor.coffee
@@ -53,7 +53,7 @@

   ### Public ###

-  # Public: Moves a cursor to a given screen position.
+  # Moves a cursor to a given screen position.
   #
   # screenPosition - An {Array} of two numbers: the screen row, and the screen column.
   # options - An object with the following keys:
@@ -63,13 +63,13 @@
     @changePosition options, =>
       @editSession.setMarkerHeadScreenPosition(@marker, screenPosition, options)

-  # Public: Gets the screen position of the cursor.
+  # Gets the screen position of the cursor.
   #
   # Returns an {Array} of two numbers: the screen row, and the screen column.
   getScreenPosition: ->
     @editSession.getMarkerHeadScreenPosition(@marker)

-  # Public: Moves a cursor to a given buffer position.
+  # Moves a cursor to a given buffer position.
   #
   # bufferPosition - An {Array} of two numbers: the buffer row, and the buffer column.
   # options - An object with the following keys:
@@ -79,17 +79,17 @@
     @changePosition options, =>
       @editSession.setMarkerHeadBufferPosition(@marker, bufferPosition, options)

-  # Public: Gets the current buffer position.
+  # Gets the current buffer position.
   #
   # Returns an {Array} of two numbers: the buffer row, and the buffer column.
   getBufferPosition: ->
     @editSession.getMarkerHeadBufferPosition(@marker)

-  # Public: If the marker range is empty, the cursor is marked as being visible.
+  # If the marker range is empty, the cursor is marked as being visible.
   updateVisibility: ->
     @setVisible(@editSession.isMarkerRangeEmpty(@marker))

-  # Public: Sets the visibility of the cursor.
+  # Sets the visibility of the cursor.
   #
   # visible - A {Boolean} indicating whether the cursor should be visible
   setVisible: (visible) ->
@@ -98,25 +98,27 @@
       @needsAutoscroll ?= true if @visible and @isLastCursor()
       @trigger 'visibility-changed', @visible

-  # Public: Retrieves the visibility of the cursor.
+  # Retrieves the visibility of the cursor.
   #
   # Returns a {Boolean}.
   isVisible: -> @visible

-  # Public: Identifies what the cursor considers a "word" RegExp.
+  # Identifies what the cursor considers a "word" RegExp.
   #
   # Returns a {RegExp}.
   wordRegExp: ->
     nonWordCharacters = config.get("editor.nonWordCharacters")
     new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g")

-  # Public: Identifies if this cursor is the last in the {EditSession}.
+  # Identifies if this cursor is the last in the {EditSession}.
+  #
+  # "Last" is defined as the most recently added cursor.
   #
   # Returns a {Boolean}.
   isLastCursor: ->
     this == @editSession.getCursor()

-  # Public: Identifies if the cursor is surrounded by whitespace.
+  # Identifies if the cursor is surrounded by whitespace.
   #
   # "Surrounded" here means that all characters before and after the cursor is whitespace.
   #
@@ -126,84 +128,84 @@
     range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
     /^\s+$/.test @editSession.getTextInBufferRange(range)

-  # Public: Removes the setting for auto-scroll.
+  # Removes the setting for auto-scroll.
   clearAutoscroll: ->
     @needsAutoscroll = null

-  # Public: Deselects whatever the cursor is selecting.
+  # Deselects whatever the cursor is selecting.
   clearSelection: ->
     if @selection
       @selection.goalBufferRange = null
       @selection.clear() unless @selection.retainSelection

-  # Public: Retrieves the cursor's screen row.
+  # Retrieves the cursor's screen row.
   #
   # Returns a {Number}.
   getScreenRow: ->
     @getScreenPosition().row

-  # Public: Retrieves the cursor's screen column.
+  # Retrieves the cursor's screen column.
   #
   # Returns a {Number}.
   getScreenColumn: ->
     @getScreenPosition().column

-  # Public: Retrieves the cursor's buffer row.
+  # Retrieves the cursor's buffer row.
   #
   # Returns a {Number}.
   getBufferRow: ->
     @getBufferPosition().row

-  # Public: Retrieves the cursor's buffer column.
+  # Retrieves the cursor's buffer column.
   #
   # Returns a {Number}.
   getBufferColumn: ->
     @getBufferPosition().column

-  # Public: Retrieves the cursor's buffer row text.
+  # Retrieves the cursor's buffer row text.
   #
   # Returns a {String}.
   getCurrentBufferLine: ->
     @editSession.lineForBufferRow(@getBufferRow())

-  # Public: Moves the cursor up one screen row.
+  # Moves the cursor up one screen row.
   moveUp: (rowCount = 1) ->
     { row, column } = @getScreenPosition()
     column = @goalColumn if @goalColumn?
     @setScreenPosition({row: row - rowCount, column: column})
     @goalColumn = column

-  # Public: Moves the cursor down one screen row.
+  # Moves the cursor down one screen row.
   moveDown: (rowCount = 1) ->
     { row, column } = @getScreenPosition()
     column = @goalColumn if @goalColumn?
     @setScreenPosition({row: row + rowCount, column: column})
     @goalColumn = column

-  # Public: Moves the cursor left one screen column.
+  # Moves the cursor left one screen column.
   moveLeft: ->
     { row, column } = @getScreenPosition()
     [row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity]
     @setScreenPosition({row, column})

-  # Public: Moves the cursor right one screen column.
+  # Moves the cursor right one screen column.
   moveRight: ->
     { row, column } = @getScreenPosition()
     @setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true)

-  # Public: Moves the cursor to the top of the buffer.
+  # Moves the cursor to the top of the buffer.
   moveToTop: ->
     @setBufferPosition([0,0])

-  # Public: Moves the cursor to the bottom of the buffer.
+  # Moves the cursor to the bottom of the buffer.
   moveToBottom: ->
     @setBufferPosition(@editSession.getEofBufferPosition())

-  # Public: Moves the cursor to the beginning of the buffer line.
+  # Moves the cursor to the beginning of the buffer line.
   moveToBeginningOfLine: ->
     @setBufferPosition([@getBufferRow(), 0])

-  # Public: Moves the cursor to the beginning of the first character in the line.
+  # Moves the cursor to the beginning of the first character in the line.
   moveToFirstCharacterOfLine: ->
     position = @getBufferPosition()
     scanRange = @getCurrentLineBufferRange()
@@ -214,7 +216,7 @@
     newPosition = [position.row, 0] if newPosition.isEqual(position)
     @setBufferPosition(newPosition)

-  # Public: Moves the cursor to the beginning of the buffer line, skipping all whitespace.
+  # Moves the cursor to the beginning of the buffer line, skipping all whitespace.
   skipLeadingWhitespace: ->
     position = @getBufferPosition()
     scanRange = @getCurrentLineBufferRange()
@@ -224,25 +226,25 @@

     @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)

-  # Public: Moves the cursor to the end of the buffer line.
+  # Moves the cursor to the end of the buffer line.
   moveToEndOfLine: ->
     @setBufferPosition([@getBufferRow(), Infinity])

-  # Public: Moves the cursor to the beginning of the word.
+  # Moves the cursor to the beginning of the word.
   moveToBeginningOfWord: ->
     @setBufferPosition(@getBeginningOfCurrentWordBufferPosition())

-  # Public: Moves the cursor to the end of the word.
+  # Moves the cursor to the end of the word.
   moveToEndOfWord: ->
     if position = @getEndOfCurrentWordBufferPosition()
       @setBufferPosition(position)

-  # Public: Moves the cursor to the beginning of the next word.
+  # Moves the cursor to the beginning of the next word.
   moveToBeginningOfNextWord: ->
     if position = @getBeginningOfNextWordBufferPosition()
       @setBufferPosition(position)

-  # Public: Retrieves the buffer position of where the current word starts.
+  # Retrieves the buffer position of where the current word starts.
   #
   # options - A hash with one option:
   #           :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
@@ -263,7 +265,7 @@

     beginningOfWordPosition or currentBufferPosition

-  # Public: Retrieves the buffer position of where the current word ends.
+  # Retrieves the buffer position of where the current word ends.
   #
   # options - A hash with one option:
   #           :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
@@ -283,7 +285,7 @@

     endOfWordPosition ? currentBufferPosition

-  # Public: Retrieves the buffer position of where the next word starts.
+  # Retrieves the buffer position of where the next word starts.
   #
   # options - A hash with one option:
   #           :wordRegex - A {RegExp} indicating what constitutes a "word" (default: {wordRegExp})
@@ -301,7 +303,7 @@

     beginningOfNextWordPosition or currentBufferPosition

-  # Public: Gets the word located under the cursor.
+  # Gets the word located under the cursor.
   #
   # options - An object with properties based on {.getBeginningOfCurrentWordBufferPosition}.
   #
@@ -311,7 +313,7 @@
     endOptions = _.extend(_.clone(options), allowNext: false)
     new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions))

-  # Public: Retrieves the range for the current line.
+  # Retrieves the range for the current line.
   #
   # options - A hash with the same keys as {EditSession.bufferRangeForBufferRow}
   #
@@ -319,7 +321,7 @@
   getCurrentLineBufferRange: (options) ->
     @editSession.bufferRangeForBufferRow(@getBufferRow(), options)

-  # Public: Retrieves the range for the current paragraph.
+  # Retrieves the range for the current paragraph.
   #
   # A paragraph is defined as a block of text surrounded by empty lines.
   #
@@ -341,19 +343,19 @@

     new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])

-  # Public: Retrieves the characters that constitute a word preceeding the current cursor position.
+  # Retrieves the characters that constitute a word preceeding the current cursor position.
   #
   # Returns a {String}.
   getCurrentWordPrefix: ->
     @editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])

-  # Public: Identifies if the cursor is at the start of a line.
+  # Identifies if the cursor is at the start of a line.
   #
   # Returns a {Boolean}.
   isAtBeginningOfLine: ->
     @getBufferPosition().column == 0

-  # Public: Retrieves the indentation level of the current line.
+  # Retrieves the indentation level of the current line.
   #
   # Returns a {Number}.
   getIndentLevel: ->
@@ -362,13 +364,13 @@
     else
       @getBufferColumn()

-  # Public: Identifies if the cursor is at the end of a line.
+  # Identifies if the cursor is at the end of a line.
   #
   # Returns a {Boolean}.
   isAtEndOfLine: ->
     @getBufferPosition().isEqual(@getCurrentLineBufferRange().end)

-  # Public: Retrieves the grammar's token scopes for the line.
+  # Retrieves the grammar's token scopes for the line.
   #
   # Returns an {Array} of {String}s.
   getScopes: ->


Make new appointment from existing work correctly and not just close the dialog.

--- app/assets/javascripts/appointments/appointments.controller.js.coffee
+++ app/assets/javascripts/appointments/appointments.controller.js.coffee
@@ -46,7 +46,7 @@
       , (rejected)->
         $scope.editAppointment(appt, rejected.$response.data)
     , (appointment) ->
-      if appointment && not appointment in ['backdrop click', 'escape key press']
+      if appointment && not (appointment in ['backdrop click', 'escape key press'])
         appointment = Appointments.$buildRaw(_.omit(appointment.$encode(), 'id'))
         $scope.editAppointment(appointment)


Temporary fix for tests.

Temporary fix for tests.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -1138,7 +1138,8 @@
   createHudElement: ->
     element = document.createElement("div")
     element.className = "vimiumReset vimiumHUD"
-    document.body.appendChild(element)
+    # FIXME(smblott) The "?" below is only inserted for the tests.  Fix!
+    document.body?.appendChild(element)
     element

   # Hide the HUD.


Better upgrade notification message.

Better upgrade notification message.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -1005,10 +1005,10 @@
     HUD.displayElement().style.display = ""

   showUpgradeNotification: (version) ->
-    HUD.upgradeNotificationElement().innerHTML = "Vimium has been updated to
+    HUD.upgradeNotificationElement().innerHTML = "Vimium has been upgraded to #{version}. See

-      #{version}.×"
+      what's new.×"
     links = HUD.upgradeNotificationElement().getElementsByTagName("a")
     links[0].addEventListener("click", HUD.onUpdateLinkClicked, false)
     links[1].addEventListener "click", (event) ->


Stop FindMode's keydown handler from using FindMode's scope and this

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -6,6 +6,7 @@
 #

 window.findModeQuery = { rawQuery: "", matchCount: 0 }
+window.findMode = null
 findModeQueryHasResults = false
 findModeAnchorNode = null
 findModeInitialRange = null
@@ -731,10 +732,10 @@
   FindModeHistory.saveQuery findModeQuery.rawQuery

 class FindMode extends Mode
-  constructor: (options = {}) ->
+  constructor: (@options = {}) ->
     @historyIndex = -1
     @partialQuery = ""
-    if options.returnToViewport
+    if @options.returnToViewport
       @scrollX = window.scrollX
       @scrollY = window.scrollY
     super
@@ -743,26 +744,25 @@
       exitOnClick: true

       keydown: (event) =>
-        window.scrollTo @scrollX, @scrollY if options.returnToViewport
+        window.scrollTo findMode.scrollX, findMode.scrollY if findMode.options.returnToViewport
         if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey
-          HUD.hide()
-          @exit()
+          findMode.exit()
           new PostFindMode if findModeQueryHasResults
         else if event.keyCode == keyCodes.enter
           handleEnterForFindMode()
-          @exit()
+          findMode.exit()
           new PostFindMode if findModeQueryHasResults
         else if event.keyCode == keyCodes.upArrow
-          if rawQuery = FindModeHistory.getQuery @historyIndex + 1
-            @historyIndex += 1
-            @partialQuery = findModeQuery.rawQuery if @historyIndex == 0
+          if rawQuery = FindModeHistory.getQuery findMode.historyIndex + 1
+            findMode.historyIndex += 1
+            findMode.partialQuery = findModeQuery.rawQuery if findMode.historyIndex == 0
             HUD.showFindMode rawQuery
         else if event.keyCode == keyCodes.downArrow
-          @historyIndex = Math.max -1, @historyIndex - 1
-          rawQuery = if 0 <= @historyIndex then FindModeHistory.getQuery @historyIndex else @partialQuery
+          findMode.historyIndex = Math.max -1, findMode.historyIndex - 1
+          rawQuery = if 0 <= findMode.historyIndex then FindModeHistory.getQuery findMode.historyIndex else findMode.partialQuery
           HUD.showFindMode rawQuery
         else if KeyboardUtils.isEscape event
-          @exit()
+          findMode.exit()
           handleEscapeForFindMode()
           new PostFindMode if findModeQueryHasResults

@@ -990,7 +990,7 @@
   # Save the selection, so performFindInPlace can restore it.
   findModeSaveSelection()
   window.findModeQuery = rawQuery: ""
-  findMode = new FindMode options
+  window.findMode = new FindMode options
   HUD.showFindMode()
   findMode


Added support for access tag

Added support for access tag

--- src/chalkboard.coffee
+++ src/chalkboard.coffee
@@ -90,8 +90,8 @@
 #                   Type            name       description
 returnRegex     = /\{([\w\|]+)}(?:\s+(.*))?/
 #                   Type        description
-NEW_LINE        = /\n\r?/
-cwd             = process.cwd()
+NEW_LINE = /\n\r?/
+cwd      = process.cwd()

 defaults =
   format:  "markdown"
@@ -250,7 +250,7 @@
               Cannot have multiple access specifier.
             """
           else
-            currentSection.access = key
+            currentSection.access = if key is "access" then value else key
             continue

         # For generic identifier


Also print 0 if stock is zero.

Also print 0 if stock is zero.

--- src/coffee/csv.coffee
+++ src/coffee/csv.coffee
@@ -31,7 +31,6 @@

   _getValue: (stock, mapping) ->
     value = access stock, mapping
-    return '' unless value

     value


can configure the api protocol

can configure the api protocol

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -14,6 +14,7 @@
   .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
   .describe('accessToken', 'an OAuth access token for the SPHERE.IO API')
   .describe('sphereHost', 'SPHERE.IO API host to connect to')
+  .describe('sphereProtocol', 'SPHERE.IO API protocol to connecto to')
   .describe('fetchHours', 'Number of hours to fetch modified orders')
   .describe('standardShippingMethod', 'Allows to define the fallback shipping method name if order has none')
   .describe('exportUnsyncedOnly', 'whether only unsynced orders will be exported or not')
@@ -113,6 +114,7 @@
     timeout: argv.timeout
     user_agent: "#{package_json.name} - #{package_json.version}"
   clientOptions.host = argv.sphereHost if argv.sphereHost
+  clientOptions.protocol = argv.sphereProtocol if argv.sphereProtocol

   exportType = if argv.csvTemplate then 'csv' else 'xml'


remove Mr.Async in comparison.coffee

remove Mr.Async in comparison.coffee

--- src/comparison.coffee
+++ src/comparison.coffee
@@ -1,6 +1,5 @@
 path = require 'path'
 _ = require 'underscore'
-mr = require 'Mr.Async'
 Q = require 'q'
 fs = require('fs')
 {resemble} = require 'resemble'


Fix bug with scrollOffset.x not registering

--- src/controller.coffee
+++ src/controller.coffee
@@ -1135,7 +1135,7 @@
         # The point was given in relation to the main canvas,
         # but we want it in relation to the palette canvas;
         # translate it.
-        point = new draw.Point point.x + PALETTE_WIDTH, point.y - @scrollOffset.y + @paletteScrollOffset.y - @paletteHeaderHeight
+        point = new draw.Point point.x + PALETTE_WIDTH - @scrollOffset.x, point.y - @scrollOffset.y + @paletteScrollOffset.y - @paletteHeaderHeight

         # Hit test as per normal.
         for block in @paletteBlocks
@@ -1153,12 +1153,12 @@
         switch
           when event.offsetX?
             return new draw.Point(
-              event.pageX - findPosLeft(track) - PALETTE_WIDTH,
+              event.pageX - findPosLeft(track) - PALETTE_WIDTH + @scrollOffset.x,
               event.pageY - findPosTop(track) + @scrollOffset.y
             )
           when event.layerX?
             return new draw.Point(
-              event.pageX - findPosLeft(track) - PALETTE_WIDTH,
+              event.pageX - findPosLeft(track) - PALETTE_WIDTH + @scrollOffset.x,
               event.pageY - findPosTop(track) + @scrollOffset.y
             )


Oops, fix syntax bug with previous commit

--- src/controller.coffee
+++ src/controller.coffee
@@ -2448,8 +2448,8 @@
               y: textElement.bounds[0].y - @scrollOffsets.main.y + translationVectors[i].y
             vector: translationVectors[i]

-      top = Math.max @aceEditor.getFirstVisibleRow(), 0
-      bottom = Math.min @aceEditor.getLastVisibleRow(), @view.getViewFor(@tree).lineLength - 1
+        top = Math.max @aceEditor.getFirstVisibleRow(), 0
+        bottom = Math.min @aceEditor.getLastVisibleRow(), @view.getViewFor(@tree).lineLength - 1

         treeView = @view.getViewNodeFor @tree
         lineHeight = @aceEditor.renderer.layerConfig.lineHeight


[DOC] Add documentation for trans.coffee.

--- src/core/trans.coffee
+++ src/core/trans.coffee
@@ -18,6 +18,8 @@
 class Translator
     constructor         : () ->

+    # Return the end of the host parameter.
+    # voicious.com -> com
     getDomain           : (host) ->
         location = undefined
         if host isnt undefined
@@ -25,6 +27,7 @@
             location = location[location.length - 1]
         return location

+    # Retrieve the corresponding translation depending on the end of the host parameter.
     getTrans            : (host, view) ->
         location = @getDomain host
         if location is 'fr'


more gitpages fiddling

more gitpages fiddling

--- core/layout.coffee
+++ core/layout.coffee
@@ -432,16 +432,19 @@
         )

       validator = ->
-        $.ajax({
-          url: '/validate/',
-          data: {url: window.location.pathname},
-          success: (data) ->
-            # we have a teem server!
-            showWarnings(data) if (data.length)
-            filereloader()
-          error: (err) ->
-            console.warn('Validation requires the Teem server')
-        }).always(finalcallback)
+        if DREEM_SERVER_AVAILABLE
+          $.ajax({
+            url: '/validate/',
+            data: {url: window.location.pathname},
+            success: (data) ->
+              # we have a teem server!
+              showWarnings(data) if (data.length)
+              filereloader()
+            error: (err) ->
+              console.warn('Validation requires the Teem server')
+          }).always(finalcallback)
+        else
+          finalcallback()

       # call the validator after everything loads
       loadIncludes(if test then finalcallback else validator)


Update HumanReadableLogsRules.coffee

Update HumanReadableLogsRules.coffee

Changed extraInfoURL links from /Errors:... to /Errors/...
Added math mode description to "Missing $ inserted." (humanReadableHint might be too long with this extra description)

--- services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
+++ services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
@@ -1,90 +1,90 @@
 define -> [
 		regexToMatch: /Misplaced alignment tab character \&/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Misplaced_alignment_tab_character_%26"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Misplaced_alignment_tab_character_%26"
 		humanReadableHint: """
 			You have placed an alignment tab character '&' in the wrong place. If you want to align something, you must write it inside an align environment such as \\begin{align} \u2026 \\end{align}, \\begin{tabular} \u2026 \\end{tabular}, etc. If you want to write an ampersand '&' in text, you must write \\& instead.
 		"""
 	,
 		regexToMatch: /Extra alignment tab has been changed to \\cr/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Extra_alignment_tab_has_been_changed_to_%5Ccr"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Extra_alignment_tab_has_been_changed_to_%5Ccr"
 		humanReadableHint: """
 			You have written too many alignment tabs in a table, causing one of them to be turned into a line break. Make sure you have specified the correct number of columns in your table.
 		"""
 	,
 		regexToMatch: /Display math should end with \$\$/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Display_math_should_end_with_$$."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Display_math_should_end_with_$$."
 		humanReadableHint: """
 			You have forgotten a $ sign at the end of 'display math' mode. When writing in display math mode, you must always math write inside $$ \u2026 $$. Check that the number of $s match around each math expression.
 		"""
 	,
 		regexToMatch: /Missing [{$] inserted./
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Missing_$_inserted"
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Missing_$_inserted"
 		humanReadableHint: """
-			Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list here.
+			Check that your $'s match around math expressions. If they do, then you've probably used a symbol in normal text that needs to be in math mode. Symbols such as subscripts ( _ ), integrals ( \\int ), Greek letters ( \\alpha, \\beta, \\delta ), and modifiers (\\vec{x}, \\tilde{x} ) must be written in math mode. See the full list here.If you intended to use mathematics mode, then use $ … $ for 'inline math mode', $$...$$ for 'display math mode' or alternatively \begin{math} … \end{math}.
 		"""
 	,
 		regexToMatch: /(undefined )?[rR]eference(s)?.+(undefined)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_undefined_references."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references."
 		humanReadableHint: """
 			You have referenced something which has not yet been labelled. If you have labelled it already, make sure that what is written inside \\ref{...} is the same as what is written inside \\label{...}.
 		"""
 	,
 		regexToMatch: /Citation .+ on page .+ undefined on input line .+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
 		humanReadableHint: """
 			You have cited something which is not included in your bibliography. Make sure that the citation (\\cite{...}) has a corresponding key in your bibliography, and that both are spelled the same way.
 		"""
 	,
 		regexToMatch: /(Label .+)? multiply[ -]defined( labels)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:There_were_multiply-defined_labels."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_multiply-defined_labels."
 		humanReadableHint: """
 			You have used the same label more than once. Check that each \\label{...} labels only one item.
 		"""
 	,
 		regexToMatch: /`!?h' float specifier changed to `!?ht'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:%60!h%27_float_specifier_changed_to_%60!ht%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/%60!h%27_float_specifier_changed_to_%60!ht%27."
 		humanReadableHint: """
 			The float specifier 'h' is too strict of a demand for LaTeX to place your float in a nice way here. Try relaxing it by using 'ht', or even 'htbp' if necessary. If you want to try keep the float here anyway, check out the float package.
 		"""
 	,
 		regexToMatch: /No positions in optional float specifier/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:No_positions_in_optional_float_specifier."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier."
 		humanReadableHint: """
 			You have forgotten to include a float specifier, which tells LaTeX where to position your figure. Find out more about float specifiers here.
 		"""
 	,
 		regexToMatch: /Undefined control sequence/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:Undefined_control_sequence."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence."
 		humanReadableHint: """
 			The compiler is having trouble understanding a command you have used. Check that the command is spelled correctly. If the command is part of a package, make sure you have included the package in your preamble using \\usepackage{...}.
 		"""
 	,
 		regexToMatch: /File .+ not found/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:File_XXX_not_found_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX."
 		humanReadableHint: """
-			The compiler cannot find the file you want to include. Make sure that you have uploaded the file and specified the file location correctly.
+			The compiler cannot find the file you want to include. Make sure that you have uploaded the file and specified the file location correctly.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown graphics extension: \..+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_graphics_extension:_.gif."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif."
 		humanReadableHint: """
-			The compiler does not recognise the file type of one of your images. Make sure you are using a supported image format for your choice of compiler, and check that there are no periods (.) in the name of your image.
+			The compiler does not recognise the file type of one of your images. Make sure you are using a supported image format for your choice of compiler, and check that there are no periods (.) in the name of your image.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `H'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
 		humanReadableHint: """
 			The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `.+'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
 		humanReadableHint: """
 			You have used a float specifier which the compiler does not understand. You can learn more about the different float options available for placing figures here.
 		"""
 	,
 		regexToMatch: /LaTeX Error: \\math.+ allowed only in math mode/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors:LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
 		humanReadableHint: """
 			You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
 		"""


Add snippet to insert a character map

Add snippet to insert a character map


Clarify an if/else statement in up.util

Clarify an if/else statement in up.util

--- lib/assets/javascripts/up/util.js.coffee
+++ lib/assets/javascripts/up/util.js.coffee
@@ -789,13 +789,13 @@
     get = (key, fallback = undefined) ->
       storeKey = normalizeStoreKey(key)
       if entry = store[storeKey]
-        if !isFresh(entry)
+        if isFresh(entry)
+          log("Cache hit for %o", key)
+          entry.value
+        else
           log("Discarding stale cache entry for %o", key)
           remove(key)
           fallback
-        else
-          log("Cache hit for %o", key)
-          entry.value
       else
         log("Cache miss for %o", key)
         fallback


add a helper to BaseCollectionView

add a helper to BaseCollectionView

--- guts.coffee
+++ guts.coffee
@@ -300,6 +300,10 @@
     @_child_views = _.where @_child_views, (view) -> view isnt viewToRemove
     @$(viewToRemove.el).remove()

+  at: (index) =>
+    return @_child_views[index]
+
+
 # Export Guts
 class Guts
   @BasicModelView:     BasicModelView


suporte a chave zerada como checked...enabled

--- html.coffee
+++ html.coffee
@@ -25,7 +25,7 @@
   _convert_attributes: ->
     values = for key, value of @attributes
       value = if type(value) is 'array' then value.join " " else value
-      "#{key}=\"#{value}\""
+      if value then "#{key}=\"#{value}\"" else key
     @converted_attributes = if @attributes then " #{values.join " "}" else ""

   _convert_content: ->


Profile: user profile link is created with group slug prepended if the group is not koding

--- client/Main/CommonViews/linkviews/profilelinkview.coffee
+++ client/Main/CommonViews/linkviews/profilelinkview.coffee
@@ -29,12 +29,14 @@

   render: (fields) ->
     nickname = @getData().profile?.nickname
-    @setAttribute "href", "/#{nickname}"  if nickname
+    {slug} = KD.getSingleton("groupsController").getCurrentGroup()
+    href = if slug is "koding" then "/#{nickname}" else "/#{slug}/#{nickname}"
+    @setAttribute "href", href  if nickname
     super fields

   pistachio:->
     {profile} = @getData()
     if profile.firstName is "" and profile.lastName is ""
-      super "{{#(profile.nickname)}}"
+      super "{{#(profile.nickname)}}"
     else
       super "{{#(profile.firstName)+' '+#(profile.lastName)}}"


Typo.

Typo.

--- packages/core/documents/messages.coffee
+++ packages/core/documents/messages.coffee
@@ -19,7 +19,7 @@
       body: message

 # Auto-expire messages after MESSAGES_TTL seconds.
-Messages.Meta.collection._ensureIndex
+Message.Meta.collection._ensureIndex
   createdAt: 1
 ,
   expireAfterSeconds: MESSAGES_TTL


The @options must flow

The @options must flow

--- src/ThreeBSP.coffee
+++ src/ThreeBSP.coffee
@@ -143,7 +143,7 @@
       .invert()
       .clipTo(us)
       .invert()
-    new ThreeBSP us.build(them.allPolygons()), @matrix
+    new ThreeBSP us.build(them.allPolygons()), @matrix, @options

   intersect: (other) => @options.timer.doTask => other.withTimer @options.timer, =>
     [us, them] = [@tree.clone(), other.tree.clone()]
@@ -151,7 +151,7 @@
       .clipTo(us.invert())
       .invert()
       .clipTo(us.clipTo(them))
-    new ThreeBSP us.build(them.allPolygons()).invert(), @matrix
+    new ThreeBSP us.build(them.allPolygons()).invert(), @matrix, @options

 ##


system/sitemap.json working in node server which means search works

--- server/express/lib/server.coffee
+++ server/express/lib/server.coffee
@@ -342,7 +342,7 @@
           res.send('Favicon Saved')
         )
       else
-        mkdirp(argv.status, 0777, ->
+        mkdirp(argv.status, 0o0777, ->
           fs.writeFile(favLoc, buf, (e) ->
             if e then throw e
             res.send('Favicon Saved')
@@ -363,6 +363,40 @@
       res.send(files)
     )
   )
+
+  app.get('/system/sitemap.json', cors, (req, res) ->
+    fs.readdir(argv.db, (err, files) ->
+      sitemap = []
+      # used to make sure all of the files are read
+      # and processesed in the site map before responding
+      numFiles = files.length
+      for file in files
+        pagehandler.get(file, (e, page, status) ->
+          if e then throw e
+
+          # create synopsis
+          if (page.synopsis?)
+            synopsis = page.synopsis
+          else
+            synopsis = 'This page has no story.'
+          for item in page.story
+            if item.type == 'paragraph'
+              synopsis = item.text
+              break
+
+          sitemap.push({
+            slug     : page.title.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase(),
+            title    : page.title,
+            date     : page.journal.pop().date,
+            synopsis : synopsis
+          })
+          numFiles--
+          if numFiles == 0
+            res.json(sitemap)
+        )
+    )
+  )
+

   ##### Put routes #####


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/animated.coffee
+++ src/animated.coffee
@@ -112,7 +112,8 @@
     sprite = I.spriteLookup[frames[I.currentFrameIndex]]

     updateSprite(sprite)
-    I.activeAnimation
+    I.hflip = I.activeAnimation.transform?[I.currentFrameIndex].hflip
+    I.vflip = I.activeAnimation.transform?[I.currentFrameIndex].vflip

   find = (name) ->
     result = null
@@ -150,6 +151,8 @@

       I.currentFrameIndex = 0
       updateSprite(firstSprite)
+      I.hflip = I.activeAnimation.transform?[I.currentFrameIndex].hflip
+      I.vflip = I.activeAnimation.transform?[I.currentFrameIndex].vflip
     else
       warn "Could not find animation state '#{newState}'. The current transition will be ignored" if I.debugAnimation


fixed code style on servers.lib.server.client.coffee

--- servers/lib/server/client.coffee
+++ servers/lib/server/client.coffee
@@ -1,13 +1,13 @@
-{argv} = require 'optimist'
+{ argv } = require 'optimist'

-KONFIG = require('koding-config-manager').load("main.#{argv.c}")
-bongo  = require './bongo'
+KONFIG   = require('koding-config-manager').load("main.#{argv.c}")
+bongo    = require './bongo'

 handleError = (err, callback) ->
   console.error err
   return callback? err

-updateCookie = (req, res, session)->
+updateCookie = (req, res, session) ->
   { clientId }       = session

   # if we already have the same cookie in request, dont do anything
@@ -20,10 +20,10 @@

   res.cookie 'clientId', clientId, { maxAge, secure }

-generateFakeClientFromReq = (req, res, callback)->
+generateFakeClientFromReq = (req, res, callback) ->

-  {clientId} = req.cookies
-  {section} = req.params
+  { clientId } = req.cookies
+  { section }  = req.params

   # TODO change this with Team product
   groupName = 'koding'
@@ -32,11 +32,11 @@
   if not clientId and req.pendingCookies?.clientId
     clientId = req.pendingCookies.clientId

-  generateFakeClient {clientId, groupName, section}, (err, fakeClient, session) ->
+  generateFakeClient { clientId, groupName, section }, (err, fakeClient, session) ->

     return callback err  if err

-    {delegate} = fakeClient.connection
+    { delegate } = fakeClient.connection

     updateCookie req, res, session

@@ -45,7 +45,7 @@

 generateFakeClient = (options, callback) ->

-  {clientId, groupName, section} = options
+  { clientId, groupName, section } = options

   fakeClient    =
     context       :
@@ -61,44 +61,46 @@

   { JSession, JAccount } = bongo.models

-  JSession.fetchSession clientId, (err, response)->
+  JSession.fetchSession clientId, (err, response) ->

     return handleError err, callback  if err

     if not response or not response.session
-      return handleError new Error "Session is not set", callback
+      return handleError new Error 'Session is not set', callback

-    { session } = response
+    { session }             = response
     { username, groupName } = session

-    JAccount.one { "profile.nickname": username }, (err, account) ->
+    JAccount.one { 'profile.nickname': username }, (err, account) ->
       # we can ignore err here
-      prepareFakeClient fakeClient, {groupName, session, username, account}
+      prepareFakeClient fakeClient, { groupName, session, username, account }
       return callback null, fakeClient, session

 prepareFakeClient = (fakeClient, options) ->
-  {groupName, session, username, account, sessionToken} = options
+  { groupName, session, username, account, sessionToken } = options

-  {JAccount}      = bongo.models
+  { JAccount }      = bongo.models

   unless account
     account         = new JAccount
-    account.profile = nickname: username
+    account.profile = { nickname: username }
     account.type    = 'unregistered'

   fakeClient.sessionToken = sessionToken ? session.clientId

   # set username into context
-  fakeClient.context or= {}
+  fakeClient.context     or= {}
   fakeClient.context.group = groupName or fakeClient.context.group
   fakeClient.context.user  = session.username or fakeClient.context.user

   # create connection property
-  fakeClient.connection or= {}
+  fakeClient.connection         or= {}
   fakeClient.connection.delegate  = account or fakeClient.connection.delegate
   fakeClient.connection.groupName = groupName or fakeClient.connection.groupName

   fakeClient.impersonating = session.impersonating or false

-module.exports = { generateFakeClient: generateFakeClientFromReq, updateCookie}
+module.exports = { generateFakeClient: generateFakeClientFromReq, updateCookie }
+
+


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/debuggable.coffee
+++ src/debuggable.coffee
@@ -52,14 +52,13 @@
     if I.velocity?
       canvas.withTransform Matrix.translation(I.x, I.y), (canvas) ->
         thickness = 4
-        length = 10

         color = 'rgba(255, 0, 0, 0.5)'

         canvas.drawRect
           x: 0
           y: -thickness / 2
-          width: I.velocity.x * length
+          width: I.velocity.x
           height: thickness
           color: color


Add Droid Serif and Sans

Add Droid Serif and Sans

--- examples/Controls.cjsx
+++ examples/Controls.cjsx
@@ -77,6 +77,8 @@
   "EB Garamond"
   "Cabin"
   "Rufina"
+  "Droid Serif"
+  "Droid Sans"
 ]

 fonts = fonts.sort()


Removed CUI.error, CUI.debug, CUI.info and CUI.warn methods. See #45948

--- src/base/CUI.coffee
+++ src/base/CUI.coffee
@@ -820,23 +820,6 @@
 		p.url = p.url + p.path
 		p

-	@error: ->
-		console.warn("CUI.error is deprecated, use console.error.")
-		console.error.apply(console, arguments)
-
-	@debug: ->
-		console.warn("CUI.debug is deprecated, use console.debug.")
-		console.debug.apply(console, arguments)
-
-	@info: ->
-		console.warn("CUI.info is deprecated, use console.info.")
-		console.info.apply(console, arguments)
-
-	@warn: ->
-		console.warn("CUI.warn is deprecated, use console.warn.")
-		console.warn.apply(console, arguments)
-
-
 	@escapeAttribute: (data) ->
 		if CUI.util.isNull(data) or !CUI.util.isString(data)
 			return ""


CUI.dom.replace: empty & append content.

--- src/base/dom.coffee
+++ src/base/dom.coffee
@@ -293,6 +293,10 @@
 			node.insertBefore(content, node.firstChild)

 		return node
+
+	@replace: (node, content) ->
+		@empty(node)
+		@append(node, content)

 	@prepend: (node, content) ->
 		@__append(node, content, false)


add notification methods to application

add notification methods to application

--- core/application.coffee
+++ core/application.coffee
@@ -74,3 +74,15 @@

         show: (feature, options) ->
             @region.show feature, options
+
+        #methods for notification
+        success: (title, content) ->
+            alert content or title
+
+        info: (title, content) ->
+            content = title unless content
+            alert content or title
+
+        error: (title, content) ->
+            content = title unless content
+            alert content or title


hook linking for remote

hook linking for remote

--- packages/shrub-grunt/dox/dynamic.coffee
+++ packages/shrub-grunt/dox/dynamic.coffee
@@ -146,7 +146,7 @@
           matches[1]
         } hook [`#{
           matches[2]
-        }`](#{
+        }`](../#{
           path.dirname(@filename).split('/').map(-> '..').join '/'
         }/hooks##{
           matches[2].toLowerCase()


Only redirect if has overleaf setting

Only redirect if has overleaf setting

--- services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee
+++ services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee
@@ -17,7 +17,7 @@
 				logger.err {err, token, userId},
 					"[TokenAccess] error finding project with higher access"
 				return next(err)
-			if !projectExists
+			if !projectExists and settings.overleaf
 				logger.log {token, userId},
 					"[TokenAccess] no project found for this token"
 				# Project does not exist, but may be unimported - try it on v1


export the actual sheet value to the parent by calling props.onChange

--- src/Spreadsheet.coffee
+++ src/Spreadsheet.coffee
@@ -21,7 +21,17 @@
       dispatcher.replaceCells @props.cells

   updateCells: ->
-    @setState cells: cellStore.getCells()
+    newCells = cellStore.getCells()
+    @setState cells: newCells
+
+    array = []
+    for r in mori.clj_to_js(newCells)
+      row = []
+      for c in r
+        row.push c.raw
+      array.push row
+
+    @props.onChange (c.raw for c in r for r in mori.clj_to_js newCells) if @props.onChange

   componentWillReceiveProps: (nextProps) ->
     if nextProps.cells and nextProps.cells.length


avoid unnecessary trailing if

avoid unnecessary trailing if

--- core/app/backbone/factlink/react_text_area.coffee
+++ core/app/backbone/factlink/react_text_area.coffee
@@ -24,7 +24,8 @@
   componentDidMount: ->
     if @props.storageKey?
       storedText = safeLocalStorage.getItem(@props.storageKey)
-      @updateText storedText if storedText
+      if storedText
+        @updateText storedText

   _onChange: (e)->
     @updateText e.target.value


:bug: wildcard(*) was not expanded correctly when query words include single-char query

--- lib/get-filter-spec-for-query.coffee
+++ lib/get-filter-spec-for-query.coffee
@@ -15,7 +15,7 @@
   segments.join('')

 getRegExpForWord = (word, {wildcard, sensitivity}={}) ->
-  if wildcard ? true
+  if word.length > 1 # don't expand wildcard for sole `*`.
     pattern = expandWildCard(word)
   else
     pattern = _.escapeRegExp(word)
@@ -28,15 +28,11 @@
 module.exports = (query, options={}) ->
   include = []
   exclude = []
-
-  words = _.compact(query.split(/\s+/))
   {negateByEndingExclamation} = options
   delete options.negateByEndingExclamation
+  words = _.compact(query.split(/\s+/))
   for word in words
-    if word.length is 1
-      options.wildcard = false
-      include.push(getRegExpForWord(word, options))
-    else if word.startsWith('!')
+    if word.startsWith('!')
       exclude.push(getRegExpForWord(word[1...], options))
     else if negateByEndingExclamation and word.endsWith('!')
       exclude.push(getRegExpForWord(word[...-1], options))


skip broken symbols

skip broken symbols

--- src/ui.coffee
+++ src/ui.coffee
@@ -33,9 +33,10 @@

   for route in db.routes
     for id in route.segments
-      segment = segmentMap.get(id)
-      segment.properties.symbols = [] unless segment.properties.symbols?
-      segment.properties.symbols.push(route.symbol)
+      if route.symbol?
+        segment = segmentMap.get(id)
+        segment.properties.symbols = [] unless segment.properties.symbols?
+        segment.properties.symbols.push(route.symbol)

   width = 1
   height = 1
@@ -205,6 +206,7 @@
       .enter().append('g')
         .attr('class', 'symbol segmentSymbol')
         .each (d) ->
+          return unless d.properties.symbols
           for i in d3.range(0, d.properties.symbols.length)
             dx = - d3.round(13 / 2 * (d.properties.symbols.length - 1))
             g = d3.select(@).append('g')


member-provider - Fixed it no longer being able to cope with the operators -> and :: spanning multiple lines.

--- lib/providers/member-provider.coffee
+++ lib/providers/member-provider.coffee
@@ -18,7 +18,7 @@
     ###
     fetchSuggestions: ({editor, bufferPosition, scopeDescriptor, prefix}) ->
         # Autocompletion for class members, i.e. after a ::, ->, ...
-        @regex = /(?:(?:[a-zA-Z0-9_]+)(?:\(.*\))?(?:->|::))+([a-zA-Z0-9_]*)/g
+        @regex = /(?:(?:[a-zA-Z0-9_]*)\s*(?:\(.*\))?\s*(?:->|::)\s*)+([a-zA-Z0-9_]*)/g

         prefix = @getPrefix(editor, bufferPosition)
         return unless prefix.length


「lambda-config.js」の生成処理を整理

「lambda-config.js」の生成処理を整理

--- bin/lamjet.coffee
+++ bin/lamjet.coffee
@@ -41,15 +41,17 @@
         return Promise.resolve(result)
       .then (result)-> writeArtifact("package.json", result.body)

-  makePackageJson(functionName: defaultFunctionName)
+  makeLambdaConfigJs = ->
+    return readTemplate("lambda-config.js")
+      .then (result)-> writeArtifact("lambda-config.js", result.body)
+
+  Promise.resolve()
+    .then (result)-> makePackageJson(functionName: defaultFunctionName)
     .then (result)->
       console.log(JSON.stringify({then: result}, null, 2))
-
-  lambdaConfigJs     = fs.readFileSync(path.join(templatePath, "lambda-config.js"), encoding: "utf8")
-  lambdaConfigJsPath = path.join(path.resolve(), "lambda-config.js")
-  console.log lambdaConfigJs
-  console.log lambdaConfigJsPath
-  fs.writeFileSync(lambdaConfigJsPath, lambdaConfigJs, encoding: "utf8", flag: "w")
+    .then (result)-> makeLambdaConfigJs()
+    .then (result)->
+      console.log(JSON.stringify({then: result}, null, 2))

   gitignore     = fs.readFileSync(path.join(templatePath, "gitignore"), encoding: "utf8")
   gitignorePath = path.join(path.resolve(), ".gitignore")


:lipstick: Change test description

:lipstick: Change test description

--- spec/palette-element-spec.coffee
+++ spec/palette-element-spec.coffee
@@ -1,7 +1,7 @@
 Color = require '../lib/color'
 Palette = require '../lib/palette'

-describe 'PaletteElement', ->
+fdescribe 'PaletteElement', ->
   [palette, paletteElement, workspaceElement, pigments, project] = []

   beforeEach ->
@@ -43,7 +43,7 @@
       expect(paletteElement.querySelectorAll('li').length).not.toEqual(0)
       expect(paletteElement.querySelectorAll('li').length).toEqual(palette.tuple().length)

-    it 'binds colors with palette variables', ->
+    it 'binds colors with project variables', ->
       projectVariables = project.getColorVariables()

       li = paletteElement.querySelector('li')


Use stubModel

Use stubModel

--- spec/storage-manager-spec.coffee
+++ spec/storage-manager-spec.coffee
@@ -202,17 +202,17 @@

       project.set('task_ids', [task.id, task2.id, task3.id])

-      taskExpectation = base.data.stub "tasks", include: ['project': [{ 'tasks': ['assignees'] }]], only: task.id, name: "task", response: (stub) ->
-        stub.results = [task]
+      taskExpectation = base.data.stubModel "task", task.id, include: ['project': [{ 'tasks': ['assignees'] }]], response: (stub) ->
+        stub.result = task
         stub.associated.project = [project]
         stub.recursive = true

-      projectExpectation = base.data.stub "projects", include: ['tasks': ['assignees']], only: project.id, name: "projects", response: (stub) ->
+      projectExpectation = base.data.stub "projects", only: project.id, include: ['tasks': ['assignees']], response: (stub) ->
         stub.results = [project]
         stub.associated.tasks = [task, task2, task3]
         stub.recursive = true

-      taskWithAssigneesExpectation = base.data.stub "tasks", only: [task.id, task2.id, task3.id], include: ['assignees'], name: "tasks", response: (stub) ->
+      taskWithAssigneesExpectation = base.data.stub "tasks", only: [task.id, task2.id, task3.id], include: ['assignees'], response: (stub) ->
         stub.results = [task]
         stub.associated.users = [user]


spec: require from lib

spec: require from lib

--- spec/underscore-plus-spec.coffee
+++ spec/underscore-plus-spec.coffee
@@ -1,4 +1,4 @@
-_ = require '../src/underscore-plus'
+_ = require '../lib/underscore-plus.js'

 describe "underscore extensions", ->
   describe "::adviseBefore(object, methodName, advice)", ->


lodheatmap: small simplification

lodheatmap: small simplification

--- src/lodheatmap.coffee
+++ src/lodheatmap.coffee
@@ -109,7 +109,7 @@
         for chr in data.chrname
             for pos,i in data.posByChr[chr]
                 for lod,j in data.lodByChr[chr][i]
-                    if lod >= zthresh or lod <= -zthresh
+                    if Math.abs(lod) >= zthresh
                         cells.push({lod: lod, chr:chr, pos:pos, posindex:+i, lodindex:+j})
         calc_chrcell_rect(cells, xmid_scaled, ymid_scaled)


:art:

:art:

--- src/masked_input.cjsx
+++ src/masked_input.cjsx
@@ -21,7 +21,7 @@
     return null unless @props.mask?

     @buffer =
-      for char, idx in @props.mask.split('') # TODO: Do we need to split?
+      for char, idx in @props.mask
         if @translations[char] # TODO: Should we just call getPattern instead of translations?
           @firstNonMaskIdx ?= idx
           @getFormat(idx)
@@ -33,7 +33,22 @@

   componentDidUpdate: ->
     @setCursorPos(@cursorPos) if @cursorPos?
-    # @cursorPos = null
+
+  render: ->
+    props = {}
+    if @props.mask?
+      props.onChange = @handleChange
+      props.onKeyPress = @handleKeyPress
+      props.onKeyDown = @handleKeyDown
+      props.onFocus = @handleFocus
+      props.value =
+        # TODO: Is this right? Should the initial state transform the @props.value?
+        if @props.value? && @props.value isnt @state.value
+          @maskedValue(@props.value)
+        else
+          @state.value
+
+    <input {...@props} {...props} />

   translations:
     '9': /\d/
@@ -213,20 +228,4 @@
     @cursorPos = i
     @buffer.join('')

-  render: ->
-    props = {}
-    if @props.mask?
-      props.onChange = @handleChange
-      props.onKeyPress = @handleKeyPress
-      props.onKeyDown = @handleKeyDown
-      props.onFocus = @handleFocus
-      props.value =
-        # TODO: Is this right? Should the initial state transform the @props.value?
-        if @props.value? && @props.value isnt @state.value
-          @maskedValue(@props.value)
-        else
-          @state.value
-
-    <input {...@props} {...props} />
-
 module.exports = MaskedInput


chore(lint): Fix lint errors within base Meshweaver

--- src/meshweaver.coffee
+++ src/meshweaver.coffee
@@ -105,7 +105,7 @@

   ensureValidationSummaryItem: (key, error) =>
     $validationSummary = @self.$('.validation-summary').first()
-    $item = $('ul', $validationSummary).find("."+key)
+    $item = $('ul', $validationSummary).find("." + key)
     unless $item.length
       $item = $(document.createElement "li")
         .addClass(key)


Remove a leftover CoffeeScript file. (#400)

--- src/message-io.coffee
+++ src/message-io.coffee
@@ -1,126 +0,0 @@
-'use strict';
-
-tls = require('tls')
-crypto = require('crypto')
-
-require('./buffertools')
-EventEmitter = require('events').EventEmitter
-isPacketComplete = require('./packet').isPacketComplete
-packetLength = require('./packet').packetLength
-packetHeaderLength = require('./packet').HEADER_LENGTH
-Packet = require('./packet').Packet
-TYPE = require('./packet').TYPE
-
-StreamParser = require('./stream-parser')
-
-class ReadablePacketStream extends StreamParser
-  constructor: ->
-    super()
-
-  parser: ->
-    while true
-      header = yield @readBuffer(packetHeaderLength)
-      length = header.readUInt16BE(2)
-      data = yield @readBuffer(length - packetHeaderLength)
-
-      @push(new Packet(Buffer.concat([header, data])))
-
-    undefined
-
-class MessageIO extends EventEmitter
-  constructor: (@socket, @_packetSize, @debug) ->
-    @packetStream = new ReadablePacketStream()
-    @packetStream.on 'data', (packet) =>
-      @logPacket('Received', packet)
-      @emit 'data', packet.data()
-      @emit 'message' if packet.isLast()
-
-    @socket.pipe(@packetStream)
-
-    @packetDataSize = @_packetSize - packetHeaderLength
-
-  packetSize: (packetSize) ->
-    if arguments.length > 0
-      @debug.log('Packet size changed from #{@_packetSize} to #{packetSize}')
-      @_packetSize = packetSize
-      @packetDataSize = @_packetSize - packetHeaderLength
-
-    @_packetSize
-
-  startTls: (credentialsDetails) ->
-    credentials = if tls.createSecureContext
-      tls.createSecureContext(credentialsDetails)
-    else
-      crypto.createCredentials(credentialsDetails)
-
-    @securePair = tls.createSecurePair(credentials)
-    @tlsNegotiationComplete = false
-
-    @securePair.on 'secure', =>
-      cipher = @securePair.cleartext.getCipher()
-      @debug.log('TLS negotiated (#{cipher.name}, #{cipher.version})')
-
-      @emit('secure', @securePair.cleartext)
-      @encryptAllFutureTraffic()
-
-    @securePair.encrypted.on 'data', (data) =>
-      @sendMessage(TYPE.PRELOGIN, data)
-
-    # On Node >= 0.12, the encrypted stream automatically starts spewing out
-    # data once we attach a `data` listener. But on Node <= 0.10.x, this is not
-    # the case. We need to kick the cleartext stream once to get the
-    # encrypted end of the secure pair to emit the TLS handshake data.
-    @securePair.cleartext.write('')
-
-  encryptAllFutureTraffic: () ->
-    @socket.unpipe(@packetStream)
-    @securePair.encrypted.removeAllListeners('data')
-
-    @socket.pipe(@securePair.encrypted)
-    @securePair.encrypted.pipe(@socket)
-
-    @securePair.cleartext.pipe(@packetStream)
-
-    @tlsNegotiationComplete = true
-
-  tlsHandshakeData: (data) ->
-    @securePair.encrypted.write(data)
-
-  # TODO listen for 'drain' event when socket.write returns false.
-  # TODO implement incomplete request cancelation (2.2.1.6)
-  sendMessage: (packetType, data, resetConnection) ->
-    if data
-      numberOfPackets = (Math.floor((data.length - 1) / @packetDataSize)) + 1
-    else
-      numberOfPackets = 1
-      data = new Buffer 0
-
-    for packetNumber in [0..numberOfPackets - 1]
-      payloadStart = packetNumber * @packetDataSize
-      if packetNumber < numberOfPackets - 1
-        payloadEnd = payloadStart + @packetDataSize
-      else
-        payloadEnd = data.length
-      packetPayload = data.slice(payloadStart, payloadEnd)
-
-      packet = new Packet(packetType)
-      packet.last(packetNumber == numberOfPackets - 1)
-      packet.resetConnection(resetConnection)
-      packet.packetId(packetNumber + 1)
-      packet.addData(packetPayload)
-
-      @sendPacket(packet, packetType)
-
-  sendPacket: (packet, packetType) =>
-    @logPacket('Sent', packet);
-
-    if @securePair && @tlsNegotiationComplete
-      @securePair.cleartext.write(packet.buffer)
-    else
-      @socket.write(packet.buffer)
-
-  logPacket: (direction, packet) ->
-    @debug.packet(direction, packet)
-    @debug.data(packet)
-
-module.exports = MessageIO


checking in generated js

checking in generated js

--- src/middleware.coffee
+++ src/middleware.coffee
@@ -47,9 +47,6 @@
         pathname = pathname.substring options.replacePrefix.length
       if options.prefix and 0 is pathname.indexOf options.prefix
         pathname = pathname.substring options.prefix.length
-    if /\.js$/.test pathname
-      if options.replacePrefix and 0 is pathname.indexOf options.replacePrefix
-        pathname = pathname.substring options.replacePrefix.length
       jsPath = path.join dest, pathname
       coffeePath = path.join src, pathname.replace '.js', '.coffee'
       # Ignore ENOENT to fall through as 404


re-implement `_set_lanesizes()` using more general logic, new API


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/oscillator.coffee
+++ src/oscillator.coffee
@@ -4,7 +4,7 @@
 @name Oscillator
 @constructor
 @param {Number} amplitude How much to scale the oscillator function value
-@param {Number}
+@param {Number} period Ho
 ###
 Oscillator = (options={}) ->
   {amplitude, period, offset} = options


Fixing syntax error in coffeescript

Fixing syntax error in coffeescript

--- maproulette/static/js/maproulette.coffee
+++ maproulette/static/js/maproulette.coffee
@@ -189,7 +189,7 @@
   dlg = $('

').addClass("dlg")
   dlg.apppend("
")
   for c in challenges
-    s = "
#{c.title}
"
+    s = """"
#{c.title}
"""
     dlg.append(s)
   dlg.append("
")
   dlg.append(makeButton("Close", "dlgClose()"))


Characters: fixed monsters inserting

Characters: fixed monsters inserting

--- init.coffee
+++ init.coffee
@@ -276,40 +276,28 @@
 		[15,"Грязевой голем",21,300,20,100,0,50,2500,0,0,5,16]
 	]

-	dbConnection.query.sync(dbConnection, "TRUNCATE monster_prototypes", [])
-	for i in prototypes
-		dbConnection.query.sync(
-			dbConnection
-			"INSERT INTO monster_prototypes "+
-				"(id, name, level, power, agility, defense, intelligence, accuracy, "+
-				"health_max, mana_max, energy, initiative_min, initiative_max) "+
-				"VALUES "+
-				"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"
-			i
-		)
-
-	console.log('Inserting monsters...')
-
 	locs = dbConnection.query.sync(dbConnection, "SELECT id FROM locations").rows
 	if (locs.length == 0)
 		throw new Error("No locations found. Forgot unify data?")

-	dbConnection.query.sync(dbConnection, "TRUNCATE monsters", [])
-	prototypesFromDB = dbConnection.query.sync(dbConnection, "SELECT * FROM monster_prototypes").rows
-	for i in [0...50]
-		soul = prototypesFromDB.sample()
-		dbConnection.query.sync(
-			dbConnection
-			"INSERT INTO monsters "+
-				"(id, prototype, location, health, mana, effects,"+
-				" attack_chance, initiative) "+
-				"VALUES "+
-				"($1, $2, $3, $4, $5, $6, $7, $8)"
-			[
-				i, soul.id, locs.sample().id, soul.health_max, soul.mana_max, null,
-				Number.random(25), Number.random(soul.initiative_min, soul.initiative_max)
-			]
-		)
+	console.log('Inserting monsters...')
+
+	dbConnection.query.sync(dbConnection, "DELETE FROM characters WHERE player IS NULL", [])
+	for i in prototypes
+		for j in [1..5]
+			dbConnection.query.sync(
+				dbConnection
+				"INSERT INTO characters "+
+					"(name, level, power, agility, defense, intelligence, accuracy, "+
+					"health_max, mana_max, energy, "+
+					"location, health, mana, attack_chance, initiative) "+
+					"VALUES "+
+					"($1, $2, $3, $4, $5, $6, $7, "+
+					" $8, $9, $10, "+
+					" $11, $12, $13, $14, $15)"
+				i.slice(1, i.length-2) # cut id (first) and minitiative_min|max (last two)
+					.concat(locs.sample().id, i[8], i[9], Number.random(25), Number.random(i[11], i[12]))
+			)

 	console.log('Done.')


Fixes test in parter2 as well

Fixes test in parter2 as well

--- desktop/apps/partner2/test/client/articles.coffee
+++ desktop/apps/partner2/test/client/articles.coffee
@@ -18,6 +18,7 @@
         sd: ARTSY_EDITORIAL_CHANNEL: '123'
       $.fn.imagesLoaded = sinon.stub()
       $.fn.waypoint = sinon.stub()
+      $.fn.fillwidthLite = sinon.stub()
       @ArticlesAdapter = benv.requireWithJadeify(
         (resolve __dirname, '../../client/articles'), ['articleTemplate', 'jsonldTemplate']
       )


remove .from constructor

remove .from constructor

--- unitary.coffee
+++ unitary.coffee
@@ -44,13 +44,6 @@
     @.x = x
     @.y = y

-  @fromVector: ((c) ->
-    c.prototype = Point.prototype
-    return c
-  )((A) ->
-    @.x = A.x
-    @.y = A.y
-  )
   moveTo: (x, y) ->
     return new Point(x, y).setStrokeColor(@.strokeColor).setFillColor(@.fillColor)
   move: (dx, dy) ->
@@ -70,13 +63,6 @@
     super()
     @.x = x
     @.y = y
-  @from: ((c) ->
-    c.prototype = Vector.prototype
-    return c
-  )((A, B) ->
-    @.x = B.x - A.x
-    @.y = B.y - A.y
-  )
   add: (CD) ->
     return new Vector(@.x + CD.x, @.y + CD.y)
   minus: (CD) ->


:bug: Change the Linux key binding to Ctrl+u

Applies to #5

--- keymaps/encoding-selector.cson
+++ keymaps/encoding-selector.cson
@@ -5,4 +5,4 @@
   'ctrl-shift-u': 'encoding-selector:show'

 '.platform-linux .editor':
-  'ctrl-shift-u': 'encoding-selector:show'
+  'ctrl-u': 'encoding-selector:show'


Tests: check for empty responses too.

Tests: check for empty responses too.

--- test/api._coffee
+++ test/api._coffee
@@ -65,15 +65,20 @@
         delete exp.content  # so it's not deep-equaled below
         expect(act).to.not.have.key 'dzi'
         expect(act).to.not.have.key 'error'
-    if act.dzi
+    else if act.dzi
         expect(act).to.not.have.key 'content'
         expectDzi act.dzi, exp.dzi
         delete exp.dzi  # so it's not deep-equaled below
         expect(act).to.not.have.key 'error'
-    if act.error
+    else if act.error
         expect(act).to.not.have.key 'content'
         expect(act).to.not.have.key 'dzi'
         _expectStringMatching act.error, /.+/
+    else
+        throw new AssertionError "
+            Expected response to have one of `content`, `dzi`, or `error`;
+            none found: #{JSON.stringify act, null, 4}
+        "

     _expectPartial act, exp


collaboration: controller remove obselete code

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -827,24 +827,6 @@
       sinkrow.dash queue, callback

-  getWorkspaceName: (callback) -> callback @workspaceData.name
-
-
-  createWorkspace: (options = {}) ->
-
-    name         = options.name or 'My Workspace'
-    rootPath     = "/home/#{nick()}"
-    {label, uid} = @mountedMachine
-
-    return remote.api.JWorkspace.create
-      name         : name
-      label        : options.label        or label
-      machineUId   : options.machineUId   or uid
-      machineLabel : options.machineLabel or label
-      rootPath     : options.rootPath     or rootPath
-      isDefault    : name is 'My Workspace'
-
-
   updateWorkspace: (options = {}) ->

     return remote.api.JWorkspace.update @workspaceData._id, { $set : options }


Keep workspace channel id as it is

Keep workspace channel id as it is

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -60,7 +60,7 @@

   unsetSocialChannel: ->

-    @channelId = @socialChannel = @workspaceData.channelId = null
+    @channelId = @socialChannel = null

   deletePrivateMessage: (callback = kd.noop) ->


Rename event handler parameter

Rename event handler parameter

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -290,9 +290,9 @@
     @socialChannel.on 'AddedToChannel', @bound 'participantAdded'

-  participantAdded: (socialAccount) =>
-
-    socialHelpers.fetchAccount socialAccount, (err, account) =>
+  participantAdded: (participant) ->
+
+    socialHelpers.fetchAccount participant, (err, account) =>

       return throwError err  if err
       return  unless account


The server error message is a dictionary now, and it provides the xhr object.

Closes #118

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -121,6 +121,8 @@
     # If the file doesn't match the file type.
     dictInvalidFileType: "You can't upload files of this type."

+    # If the server response was invalid.
+    dictResponseError: "Server responded with {{statusCode}} code."

     # If `done()` is called without argument the file is accepted
     # If you call it with an error message, the file is rejected
@@ -507,7 +509,6 @@
     if file.size > @options.maxFilesize * 1024 * 1024
       done "File is too big (" + (Math.round(file.size / 1024 / 10.24) / 100) + "MB). Max filesize: " + @options.maxFilesize + "MB"
     else
-      # Add file size check here.
       @options.accept.call this, file, done

   addFile: (file) ->
@@ -620,7 +621,7 @@
     xhr.open @options.method, @options.url, true

     handleError = =>
-      @errorProcessing file, xhr.responseText || "Server responded with #{xhr.status} code."
+      @errorProcessing file, xhr.responseText || @options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr

     xhr.onload = (e) =>
       unless 200 <= xhr.status < 300
@@ -684,11 +685,11 @@

   # Called internally when processing is finished.
   # Individual callbacks have to be called in the appropriate sections.
-  errorProcessing: (file, message) ->
+  errorProcessing: (file, message, xhr) ->
     @filesProcessing = without(@filesProcessing, file)
     file.processing = no
     @processQueue()
-    @emit "error", file, message
+    @emit "error", file, message, xhr
     @emit "complete", file


add download links

add download links

--- script.coffee
+++ script.coffee
@@ -1,8 +1,8 @@
 md ="""
 Download It
 ===
-Grab the [development version](https://raw.github.com/calvinmetcalf/communist/master/dist/communist.js)
-or [production version](https://raw.github.com/calvinmetcalf/communist/master/dist/communist.min.js) from the dist folder.
+To use, downlaod the [development version](https://raw.github.com/calvinmetcalf/communist/master/dist/communist.js)
+or [production version](https://raw.github.com/calvinmetcalf/communist/master/dist/communist.min.js) of the script and use it with your project.
 __Important:__ you have to host it yourself, you will have issues if your link to one on a seperate domain.

 API


Add comments to script

Add comments to script

--- src/ac-webhooks.coffee
+++ src/ac-webhooks.coffee
@@ -1,5 +1,6 @@
 # Description:
-#  Listens for ac webhooks and post in chat
+#  Listens for ac webhooks and post in chat, Currently only interested in time
+#  logs
 #
 # Dependencies:
 #   None
@@ -19,14 +20,16 @@

   robot.router.post "/hubot/ac-webhooks", (req, res) ->

+    # Get the data out of the request body
     data = JSON.parse req.body['data']
-    console.log data
     data = data['data']
     timeRecord = data['time_record']

+    # Handle billability
     billableStatus = 'billable'
     if timeRecord['billable_status'] != 1 then billableStatus = 'non billable'

+    # Collect other time record values
     userName = timeRecord['user_name']
     time = timeRecord['value']
     projectType = timeRecord['parent_type']
@@ -36,16 +39,23 @@
     projectName = timeRecord['project_name']
     taskName = timeRecord['task_name']

+    # if time was logged against a task we handle slightly differently from if
+    # time was logged against a project
     taskMessage = ''
     if typeof taskName != 'undefined' then taskMessage = 'task ' + taskName + ' for the '

+    # Build the final message to put into the chat room
     message = userName + ' logged ' + time + ' ' + billableStatus + ' hours against the ' +
       taskMessage + 'project ' + projectName + ' with the description ' + summary

+    # We need to know which chat room to log to, set that up
     user = {}
     user.room = process.env['HUBOT_CAMPFIRE_ROOMS'].split(',')[0]

+    # Send the message
     robot.send user, message
+
+    # Terminate the request, return true status for whoever called the endpoint
     res.end '{"success":true}'


when there is a reasonable amount of alternative captions received attempt to combine them

--- src/frinkiac.coffee
+++ src/frinkiac.coffee
@@ -36,7 +36,7 @@
   string.replace /^\s*|\s*$/g, ''

 addLineBreaks = (str) ->
-  wordCount = 5
+  wordCount = 4
   newString = ''
   str.split(' ').forEach (word, i) ->
     i++
@@ -47,6 +47,17 @@
 getImageUrl = (episode, timestamp, caption) ->
   encodedUrl = encode(addLineBreaks(trimWhitespace(caption)))
   "https://frinkiac.com/meme/#{episode}/#{timestamp}.jpg?lines=#{encodedUrl}"
+
+combineCaptions = (captions) ->
+  if captions.length <= 3
+    newCaption = ''
+    captions.forEach (caption, i) ->
+      newCaption += caption.Content
+      unless i == (captions.length - 1)
+        newCaption += '\n'
+    newCaption
+  else
+    captions[0].Content

 module.exports = (robot) ->
   robot.respond /(simpsons search|frinkiac) (.*)/i, (msg) ->
@@ -65,7 +76,7 @@
           else
             axios(getRequestConfig('caption', {e: episode, t: timestamp}))
               .then (response) ->
-                msg.send getImageUrl(episode, timestamp, response.data.Subtitles[0].Content)
+                msg.send getImageUrl(episode, timestamp, combineCaptions(response.data.Subtitles))

         else
           console.log("D'oh! I couldn't find anything for `#{query[0]}`.");


Added a negate operation to CSM parameters

--- src/api/api.csm.coffee
+++ src/api/api.csm.coffee
@@ -174,6 +174,8 @@
         @update "(#{@serialize()}) - #{arg}.0"
       else
         @update "(#{@serialize()}) - #{arg}"
+    neg: () ->
+      @update "-(#{@serialize()})"

   window.range = (description, defaultArg, start, end, step) ->
     paramIndex = globalParamIndex


Add cmd/ctrl+num key

Add cmd/ctrl+num key

--- views/app.cjsx
+++ views/app.cjsx
@@ -50,6 +50,10 @@
   handleCtrlOrCmdTabKeyDown: ->
     @setState
       key: 0
+  handleCtrlOrCmdNumberKeyDown: (num) ->
+    if num <= components.length + tabbedPlugins.length
+      @setState
+        key: num - 1
   handleTabKeyDown: ->
     @setState
       key: (@state.key + 1) % (components.length + tabbedPlugins.length)
@@ -62,10 +66,15 @@
         setTimeout ->
           lockedTab = false
         , 200
-        if e.ctrlKey || e.metaKey
+        if e.ctrlKey or e.metaKey
           @handleCtrlOrCmdTabKeyDown()
         else
           @handleTabKeyDown()
+      else if e.ctrlKey or e.metaKey
+        if e.keyCode >= 49 and e.keyCode <= 57
+          @handleCtrlOrCmdNumberKeyDown(e.keyCode - 48)
+        else if e.keyCode is 48
+          @handleCtrlOrCmdNumberKeyDown 10
   render: ->
     ### FIXME
     # Animation disabled


Add tests for updates that change <in-reply-to/>

And right now they fail :)

--- test/post.coffee
+++ test/post.coffee
@@ -518,6 +518,82 @@
                     content: "Updated test post E6", id: "test-E-6"
                 server.doTest publishEl, "got-iq-publish-E-8", cb, testErrorIq "modify", "not-acceptable"
             ], done
+
+        it "should not allow adding an <in-reply-to/>", (done) ->
+            async.series [(cb) ->
+                # Publish E9
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-9", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E9", id: "test-E-9"
+                server.doTest publishEl, "got-iq-publish-E-9", cb, testPublishResultIq
+
+            , (cb) ->
+                # Publish E10
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-10", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E10", id: "test-E-10"
+                server.doTest publishEl, "got-iq-publish-E-10", cb, testPublishResultIq
+
+            , (cb) ->
+                # Update E10 to be in-reply-to E9
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-11", "/user/picard@enterprise.sf/posts",
+                    content: "Updated test post E10", id: "test-E-10", in_reply_to: "test-E-9"
+                server.doTest publishEl, "got-iq-publish-E-11", cb, testErrorIq "modify", "not-acceptable"
+            ], done
+
+        it "should not allow changing <in-reply-to/>", (done) ->
+            async.series [(cb) ->
+                # Publish E12
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-12", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E12", id: "test-E-12"
+                server.doTest publishEl, "got-iq-publish-E-12", cb, testPublishResultIq
+
+            , (cb) ->
+                # Publish E13
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-13", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E13", id: "test-E-13"
+                server.doTest publishEl, "got-iq-publish-E-13", cb, testPublishResultIq
+
+            , (cb) ->
+                # Publish E14 as reply to E13
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-14", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E14", id: "test-E-14", in_reply_to: "test-E-13"
+                server.doTest publishEl, "got-iq-publish-E-14", cb, testPublishResultIq
+
+            , (cb) ->
+                # Update E14 to be in-reply-to E12
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-15", "/user/picard@enterprise.sf/posts",
+                    content: "Updated test post E14", id: "test-E-14", in_reply_to: "test-E-12"
+                server.doTest publishEl, "got-iq-publish-E-15", cb, testErrorIq "modify", "not-acceptable"
+            ], done
+
+        it "should not allow removing <in-reply-to/>", (done) ->
+            async.series [(cb) ->
+                # Publish E16
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-16", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E16", id: "test-E-16"
+                server.doTest publishEl, "got-iq-publish-E-16", cb, testPublishResultIq
+
+            , (cb) ->
+                # Publish E17 as reply to E16
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-17", "/user/picard@enterprise.sf/posts",
+                    content: "Test post E17", id: "test-E-17", in_reply_to: "test-E-16"
+                server.doTest publishEl, "got-iq-publish-E-17", cb, testPublishResultIq
+
+            , (cb) ->
+                # Update E17 to not be a reply
+                publishEl = server.makePublishIq "picard@enterprise.sf", "buddycloud.example.org",
+                    "publish-E-18", "/user/picard@enterprise.sf/posts",
+                    content: "Updated test post E17", id: "test-E-17"
+                server.doTest publishEl, "got-iq-publish-E-18", cb, testErrorIq "modify", "not-acceptable"
+            ], done
     # }}}
 # }}}
 # {{{ Retrieving posts


fix last jump when rotating

fix last jump when rotating

--- framer/Defaults.coffee
+++ framer/Defaults.coffee
@@ -30,7 +30,8 @@
 		orientation: "portrait"
 		keyboard: false
 		animationOptions:
-			curve: "spring(400,40,0,0.06)"
+			time: .2
+			curve: "ease-out"
 	LayerDraggable:
 		momentum: true
 		momentumOptions:


start serf agent automatically

start serf agent automatically

--- test/serf.coffee
+++ test/serf.coffee
@@ -1,22 +1,24 @@
 'use strict'

 {assert} = require 'chai'
+{spawn} = require 'child_process'
 Serf = require '../'

-###
-Please start two serf agents by
-
-```
-$ serf agent -node=agent-one -bind=127.0.0.1:7946
-$ serf agent -node=agent-two -bind=127.0.0.1:7947 -rpc-addr=127.0.0.1:7374
-```
-###
-
 describe 'basic test', ->
+  procs = []
   client = undefined

   before (done) ->
-    client = Serf.connect {port: 7373}, done
+    procs.push(spawn 'serf', ['agent', '-node=agent-one', '-bind=127.0.0.1:7946'])
+    procs.push(spawn 'serf', ['agent', '-node=agent-two', '-bind=127.0.0.1:7947', '-rpc-addr=127.0.0.1:7374'])
+
+    setTimeout ->
+      client = Serf.connect {port: 7373}, done
+    , 500
+
+  after () ->
+    procs.forEach (proc) ->
+      do proc.kill

   it 'should stats', (done) ->
     client.stats {}, (result) ->


Round test value. Improve test across browsers since no formal reset is in place

--- test/spec.coffee
+++ test/spec.coffee
@@ -12,7 +12,8 @@
       firstHeadingRect = headings[1].getBoundingClientRect()
       secondHeadingRect = headings[2].getBoundingClientRect()
       distance = secondHeadingRect.top - firstHeadingRect.height
-      expect(distance - lineHeight).to.eql(firstHeadingRect.top)
+      expect(Math.round(distance - lineHeight))
+      .to.eql(Math.round(firstHeadingRect.top))
     )
   )
 )


Nodejs: metrics: remove unused require

Nodejs: metrics: remove unused require

--- node_modules_koding/koding-datadog/lib/adapters/socialworker.coffee
+++ node_modules_koding/koding-datadog/lib/adapters/socialworker.coffee
@@ -1,6 +1,5 @@
 { argv }  = require 'optimist'
 KONFIG    = require('koding-config-manager').load("main.#{argv.c}")
-Timer     = require '../timer'
 DogStatsD = require '../dogstatsd'

 module.exports = socialWorker = {
@@ -26,14 +25,14 @@

   sendMetrics : (opts) ->

-    tags            = socialWorker.populateTags()
-    metricName      = socialWorker.generateMetricName opts
-    memoryUsage     = process.memoryUsage()
-    dogStatsDClient = DogStatsD.getClient()
+    tags                         = socialWorker.populateTags()
+    metricName                   = socialWorker.generateMetricName opts
+    dogStatsDClient              = DogStatsD.getClient()
+    { rss, heapTotal, heapUsed } = process.memoryUsage()

-    dogStatsDClient.gauge "#{metricName}.memory.rss", memoryUsage.rss, tags
-    dogStatsDClient.gauge "#{metricName}.memory.heapTotal", memoryUsage.heapTotal, tags
-    dogStatsDClient.gauge "#{metricName}.memory.heapUsed", memoryUsage.heapUsed, tags
+    dogStatsDClient.gauge "#{metricName}.memory.rss",       rss, tags
+    dogStatsDClient.gauge "#{metricName}.memory.heapTotal", heapTotal, tags
+    dogStatsDClient.gauge "#{metricName}.memory.heapUsed",  heapUsed, tags

 }


test git

test git


Copy other fields to related publish function.

--- server.coffee
+++ server.coffee
@@ -22,8 +22,16 @@
           oldRelatedPublish = relatedPublish

           relatedPublish = publish._recreate()
-          # TODO: Test how recursive @related works
-          relatedPublish.related = publish.related
+
+          # We copy overridden methods if they exist
+          for own key, value of publish when key in ['added', 'changed', 'removed', 'ready', 'stop', 'error']
+            relatedPublish[key] = value
+
+          # If there are any extra fields which do not exist in recreated related publish
+          # (because they were added by some other code), copy them over
+          # TODO: This copies also @related, test how recursive @related works
+          for own key, value of publish when key not of relatedPublish
+            relatedPublish[key] = value

           relatedPublishAdded = relatedPublish.added
           relatedPublish.added = (collectionName, id, fields) ->


Remove location from personal database

Remove location from personal database

--- server.coffee
+++ server.coffee
@@ -32,6 +32,11 @@
 		newVersion = 12
 		Db.backend.remove('collectionRegistered')

+	if version <13
+		newVersion = 13
+		for userId in Plugin.userIds()
+			Db.personal(userId).remove 'location'
+
 	# Write new version to the database
 	if newVersion isnt version
 		log '[onUpgrade()] Upgraded from version '+version+' to '+newVersion+'.'


Annotate ._isResourceHref() as private.

Annotate ._isResourceHref() as private.

--- src/OVirtResponseHydrator.coffee
+++ src/OVirtResponseHydrator.coffee
@@ -280,6 +280,8 @@
   #
   # @return [Boolean]
   #
+  # @private
+  #
   _isResourceHref: (subject) ->
     /[\w\/]+\/\w+-\w+-\w+-\w+-\w+$/.test subject


Each line gets a separate publish

Each line gets a separate publish

--- watcher.coffee
+++ watcher.coffee
@@ -2,6 +2,7 @@
 fs = require 'fs'
 path = require 'path'
 optimist = require 'optimist'
+crypto = require 'crypto'
 {spawn} = require 'child_process'

 argv = require('optimist')
@@ -40,13 +41,17 @@
 mqtt.createClient port, host, (err, client) ->
   timeout = 5000
   client.connect
-    clientId: "watcher_#{Math.floor(Math.random * 65535)}"
     keepalive: timeout

   setTimeout =>
     client.pingreq()
   , timeout

+  publishLines = (data) ->
+    for line in data.split('\n') when line isnt ""
+      client.publish
+        topic: topic
+        payload: line

   client.on 'connack', (packet) ->
     if packet.returnCode isnt 0
@@ -55,6 +60,4 @@
     console.log 'Client connected'

     fileWatcher(file).on 'data', (data) ->
-      client.publish
-        topic: topic
-        payload: data
+      publishLines data


Specs for the displayDuration setting

Specs for the displayDuration setting

--- test/test.coffee
+++ test/test.coffee
@@ -146,6 +146,14 @@
         @$carousel.circular({transitionDelay: 250})
         expect(@$carousel.circular('settings').transitionDelay).to.equal 250

+      it 'the display duration defaults to 4000ms', ->
+        @$carousel.circular()
+        expect(@$carousel.circular('settings').displayDuration).to.equal 4000
+
+      it 'the transition delay can be overriden', ->
+        @$carousel.circular({displayDuration: 1250})
+        expect(@$carousel.circular('settings').displayDuration).to.equal 1250
+
       it 'autoStart is true by default', ->
         _resume = sinon.spy @$carousel.circular('_internals'), 'resume'
         @$carousel.circular()


Added memory leak tests

Added memory leak tests

--- test/test.coffee
+++ test/test.coffee
@@ -851,3 +851,29 @@
   it 'Fails to apply an operation to a document that was deleted and recreated'

   it 'correctly namespaces pubsub operations so other collections dont get confused'
+
+  describe 'cleanup', ->
+    it 'does not leak streams when clients subscribe & unsubscribe from documents', (done) -> @create =>
+      assert.strictEqual 0, @client.numStreams
+      @collection.subscribe @docName, 1, (err, stream) =>
+        throw new Error err if err
+        assert.strictEqual 1, @client.numStreams
+        stream.destroy()
+        assert.strictEqual 0, @client.numStreams
+        done()
+
+    it 'does not leak streams from bulkSubscribe', (done) -> @create2 'x', => @create2 'y', =>
+      assert.strictEqual 0, @client.numStreams
+      bs = {}
+      bs[@cName] = {x:1, y:1}
+      @client.bulkSubscribe bs, (err, streams) =>
+        throw new Error err if err
+        assert.strictEqual 2, @client.numStreams
+        streams[@cName].x.destroy()
+        streams[@cName].y.destroy()
+        assert.strictEqual 0, @client.numStreams
+        assert.strictEqual 0, Object.keys(@client.streams).length
+        done()
+
+
+


Reduce isLoggedIn() check using Coffee more beautifully

--- src/Lidsys/Application/Controller/assets/lidsys/app-models.js.coffee
+++ src/Lidsys/Application/Controller/assets/lidsys/app-models.js.coffee
@@ -6,4 +6,4 @@
     setUser: (@user) ->
     getUser: -> @user

-    isLoggedIn: -> !!(@user && @user.userId)
+    isLoggedIn: -> !!@user?.userId