Do not show untrapped errors in the autocomplete list

--- lib/elixir-autocomplete-provider.coffee
+++ lib/elixir-autocomplete-provider.coffee
@@ -80,6 +80,8 @@

   createSuggestion = (serverSuggestion, prefix) ->
     [name, kind, signature, mod, desc, spec] = serverSuggestion.replace(/;/g, '\u000B').replace(/\\\u000B/g, ';').split('\u000B')
+
+    return "" if serverSuggestion.match(/^[\s\d]/)

     switch kind
       when 'private_function'
@@ -161,6 +163,8 @@
     }

   createSuggestionForModule = (serverSuggestion, name, prefix) ->
+    return "" if serverSuggestion.match(/^[\s\d]/)
+
     snippet = name.replace(/^:/, '')
     name = ':' + name if name.match(/^[^A-Z:]/)
     {


fix path

fix path

--- data/fetch.coffee
+++ data/fetch.coffee
@@ -67,7 +67,8 @@
   setTimeout ->
     console.log code
     url = "http://netrunnerdb.com/web/bundles/netrunnerdbcards/images/cards/en/#{code}.png"
-    request(url).pipe(fs.createWriteStream("img/#{code}.png"))
+    imgDir = if mongoUser "#{process.env.OPENSHIFT_DATA_DIR}/img" else "img"
+    request(url).pipe(fs.createWriteStream("#{imgDir}/#{code}.png"))
   , t

 request.get baseurl + "cards", (error, response, body) ->


restricted list update

restricted list update

--- data/fetch.coffee
+++ data/fetch.coffee
@@ -51,7 +51,7 @@

 baseurl = "http://netrunnerdb.com/api/"

-restricted = ["Philotic Entanglement"]
+restricted = ["Philotic Entanglement", "Eden Shard", "Eden Fragment"]

 selectFields = (fields, objectList) ->
   ((fields.reduce ((newObj, key) -> newObj[key] = obj[key]; newObj["limit"] = 1 if obj.title in restricted; newObj), {}) for obj in objectList)


Fix log

Fix log

--- src/daemon/app/env.coffee
+++ src/daemon/app/env.coffee
@@ -14,26 +14,26 @@

   find: (path, version) ->
     try
-      log.log path, "Looking for #{version}"
+      log.app.log path, "Looking for #{version}"
       for dir in fs.readdirSync(config.nvmDir).reverse()
         if minimatch dir, "v#{version}*"
           PATH = "#{config.nvmDir}/#{dir}/bin"
-          log.global.log path, "Using #{PATH}"
+          log.app.log path, "Using #{PATH}"
           return PATH
     catch
-      log.error path, "Can't find #{version} in #{config.nvmDir}"
+      log.app.error path, "Can't find #{version} in #{config.nvmDir}"

   nvmrc: (path) ->
     try
       version = @read "#{path}/.nvmrc"
-      log.global.log path, "Detected .nvmrc"
+      log.app.log path, "Detected .nvmrc"
       @find path, version
     catch

   nvmDefault: (path) ->
     try
       version = @read "#{config.nvmDir}/alias/default"
-      common.log path, "Detected ~/.nvm/alias/default"
+      log.app.log path, "Detected ~/.nvm/alias/default"
       @find path, version
     catch

@@ -42,7 +42,7 @@
     "/usr/local/bin:#{nodePath}"

   getPATH: (path) ->
-    PATH = @nvmrc path
+    PATH   = @nvmrc path
     PATH or= @nvmDefault path
     PATH or= @node()
     PATH


Allow overriding of options for an enum field in a FormView.

--- lib/ST/FormView.coffee
+++ lib/ST/FormView.coffee
@@ -29,8 +29,8 @@
         field.release()
       text: (attribute) ->
         @_add ST.TextFieldView.create(), attribute
-      enum: (attribute) ->
-        details = self.detailsFor attribute
+      enum: (attribute, details) ->
+        details ||= self.detailsFor attribute
         @_add ST.EnumFieldView.createWithValuesNull(details.values, details.null), attribute
       model: (attribute) ->
         details = self.detailsFor attribute


Set correct default options

Set correct default options

--- lib/activeroute.coffee
+++ lib/activeroute.coffee
@@ -19,9 +19,9 @@

 share.config = new ReactiveDict 'activeRouteConfig'
 share.config.set
-  active: 'active'
+  activeClass: 'active'
   caseSensitive: true
-  disabled: 'disabled'
+  disabledClass: 'disabled'

 test = (value, pattern) ->
   if Match.test pattern, RegExp


must not parse its own messages

must not parse its own messages

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -23,10 +23,11 @@
       @chatdriver.prepMeteorSubscriptions({uid: userid, roomid: RocketChatRoom}).then (arg) =>
         @robot.logger.info "subscription ready"
         @chatdriver.setupReactiveMessageList (newmsg) =>
-          @robot.logger.info "message receive callback"
-          user = @robot.brain.userForId newmsg.u._id, name: newmsg.u.username, room: newmsg.rid
-          text = new TextMessage(user, newmsg.msg, newmsg._id)
-          @robot.receive text
+          if newmsg.u._id isnt userid
+            @robot.logger.info "message receive callback"
+            user = @robot.brain.userForId newmsg.u._id, name: newmsg.u.username, room: newmsg.rid
+            text = new TextMessage(user, newmsg.msg, newmsg._id)
+            @robot.receive text
     @emit 'connected'

   send: (envelope, strings...) =>


Fail test on timeout (from queue_extraction)

--- test/support/redeye_suite.coffee
+++ test/support/redeye_suite.coffee
@@ -77,6 +77,7 @@
   die: ->
     @dispatcher.quit()
     @finish()
+    @assert.ok false, "Timed out, sad panda"

   # Terminate the last redis connection, ending the test
   finish: ->


:art: Change wording in spec

:art: Change wording in spec

--- spec/pane-view-spec.coffee
+++ spec/pane-view-spec.coffee
@@ -118,7 +118,7 @@
         paneModel.activateItem(view2)
         expect(pane.itemViews.find('#view-2').length).toBe 1

-    describe "when the active item implements ::getPath", ->
+    describe "when the new activeItem implements ::getPath", ->
       it "adds .has-file-path to the active item element", ->
         paneModel.activateItem(editor1)
         expect(pane.itemViews.find('atom-text-editor')).toHaveClass('has-file-path')


Migrate more specs

Migrate more specs

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -969,12 +969,13 @@

       runs ->
         atom.workspaceView.trigger 'tree-view:toggle'
-        treeView = atom.workspaceView.find(".tree-view")
-        dirView = treeView.root.entries.find('.directory:contains(test-dir)')
-        dirView.expand()
+        treeView = atom.workspaceView.find(".tree-view").view()
+        root = $(treeView.root)
+        dirView = $(treeView.root.entries).find('.directory:contains(test-dir):first')
+        dirView[0].expand()
         fileView = treeView.find('.file:contains(test-file.txt)')
-        dirView2 = treeView.root.entries.find('.directory:contains(test-dir2)')
-        dirView2.expand()
+        dirView2 = $(treeView.root.entries).find('.directory:contains(test-dir2):last')
+        dirView2[0].expand()
         fileView2 = treeView.find('.file:contains(test-file2.txt)')
         fileView3 = treeView.find('.file:contains(test-file3.txt)')

@@ -1007,8 +1008,8 @@
           storedPaths = JSON.parse(LocalStorage['tree-view:copyPath'])

           expect(storedPaths.length).toBe 2
-          expect(storedPaths[0]).toBe fileView2.getPath()
-          expect(storedPaths[1]).toBe fileView3.getPath()
+          expect(storedPaths[0]).toBe fileView2[0].getPath()
+          expect(storedPaths[1]).toBe fileView3[0].getPath()

     describe "tree-view:cut", ->
       LocalStorage = window.localStorage
@@ -1040,8 +1041,8 @@
           storedPaths = JSON.parse(LocalStorage['tree-view:cutPath'])

           expect(storedPaths.length).toBe 2
-          expect(storedPaths[0]).toBe fileView2.getPath()
-          expect(storedPaths[1]).toBe fileView3.getPath()
+          expect(storedPaths[0]).toBe fileView2[0].getPath()
+          expect(storedPaths[1]).toBe fileView3[0].getPath()

     describe "tree-view:paste", ->
       LocalStorage = window.localStorage
@@ -1241,7 +1242,7 @@

         runs ->
           treeView.trigger "tree-view:add-file"
-          addDialog = atom.workspaceView.find(".tree-view-dialog")
+          addDialog = atom.workspaceView.find(".tree-view-dialog").view()

       describe "when a file is selected", ->
         it "opens an add dialog with the file's current directory path populated", ->
@@ -1257,7 +1258,7 @@
             directoryModifiedHandler = jasmine.createSpy("directory-modified")
             dirView.on "tree-view:directory-modified", directoryModifiedHandler

-            dirView.directory.emit 'entry-removed', {name: 'deleted.txt'}
+            dirView[0].directory.emit 'entries-removed', {'deleted.txt': {}}
             expect(directoryModifiedHandler).toHaveBeenCalled()
             expect(treeView.find('.selected').text()).toBe path.basename(filePath)

@@ -1277,7 +1278,7 @@
                 expect(atom.workspace.getActivePaneItem().getPath()).toBe newPath

               waitsFor "tree view to be updated", ->
-                dirView.entries.find("> .file").length > 1
+                $(dirView[0].entries).find("> .file").length > 1

               runs ->
                 expect(treeView.find('.selected').text()).toBe path.basename(newPath)
@@ -1321,7 +1322,7 @@
           addDialog.cancel()
           dirView.click()
           treeView.trigger "tree-view:add-file"
-          addDialog = atom.workspaceView.find(".tree-view-dialog")
+          addDialog = atom.workspaceView.find(".tree-view-dialog").view()

           expect(addDialog).toExist()
           expect(addDialog.promptText.text()).toBeTruthy()
@@ -1333,20 +1334,20 @@
       describe "when the root directory is selected", ->
         it "opens an add dialog with no path populated", ->
           addDialog.cancel()
-          treeView.root.click()
+          root.click()
           treeView.trigger "tree-view:add-file"
-          addDialog = atom.workspaceView.find(".tree-view-dialog")
+          addDialog = atom.workspaceView.find(".tree-view-dialog").view()

           expect(addDialog.miniEditor.getText().length).toBe 0

       describe "when there is no entry selected", ->
         it "opens an add dialog with no path populated", ->
           addDialog.cancel()
-          treeView.root.click()
-          treeView.root.removeClass('selected')
+          root.click()
+          root.removeClass('selected')
           expect(treeView.selectedEntry()).toBeUndefined()
           treeView.trigger "tree-view:add-file"
-          addDialog = atom.workspaceView.find(".tree-view-dialog")
+          addDialog = atom.workspaceView.find(".tree-view-dialog").view()

           expect(addDialog.miniEditor.getText().length).toBe 0

@@ -1361,7 +1362,7 @@

         runs ->
           treeView.trigger "tree-view:add-folder"
-          addDialog = atom.workspaceView.find(".tree-view-dialog")
+          addDialog = atom.workspaceView.find(".tree-view-dialog").view()

       describe "when a file is selected", ->
         it "opens an add dialog with the file's current directory path populated", ->
@@ -1446,7 +1447,7 @@

           runs ->
             treeView.trigger "tree-view:move"
-            moveDialog = atom.workspaceView.find(".tree-view-dialog")
+            moveDialog = atom.workspaceView.find(".tree-view-dialog").view()

         afterEach ->
           waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
@@ -1476,8 +1477,8 @@
                 root.find('> .entries > .file:contains(renamed-test-file.txt)').length > 0

               runs ->
-                dirView = treeView.root.entries.find('.directory:contains(test-dir)')
-                dirView.expand()
+                dirView = $(treeView.root.entries).find('.directory:contains(test-dir)')
+                dirView[0].expand()
                 expect(dirView.entries.children().length).toBe 0

           describe "when the directories along the new path don't exist", ->
@@ -1527,8 +1528,8 @@
         beforeEach ->
           dotFilePath = path.join(dirPath, ".dotfile")
           fs.writeFileSync(dotFilePath, "dot")
-          dirView.collapse()
-          dirView.expand()
+          dirView[0].collapse()
+          dirView[0].expand()
           dotFileView = treeView.find('.file:contains(.dotfile)')

           waitsForFileToOpen ->
@@ -1536,7 +1537,7 @@

           runs ->
             treeView.trigger "tree-view:move"
-            moveDialog = atom.workspaceView.find(".tree-view-dialog")
+            moveDialog = atom.workspaceView.find(".tree-view-dialog").view()

         it "selects the entire file name", ->
           expect(moveDialog).toExist()
@@ -1561,7 +1562,7 @@

           runs ->
             treeView.trigger "tree-view:duplicate"
-            copyDialog = atom.workspaceView.find(".tree-view-dialog")
+            copyDialog = atom.workspaceView.find(".tree-view-dialog").view()

         afterEach ->
           waits 50 # The copy specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
@@ -1656,7 +1657,7 @@

           runs ->
             treeView.trigger "tree-view:duplicate"
-            copyDialog = atom.workspaceView.find(".tree-view-dialog")
+            copyDialog = atom.workspaceView.find(".tree-view-dialog").view()

         it "selects the entire file name", ->
           expect(copyDialog).toExist()
@@ -1679,7 +1680,7 @@
           runs ->
             editorView = atom.workspaceView.getActiveView()
             editorView.trigger "tree-view:duplicate"
-            copyDialog = atom.workspaceView.find(".tree-view-dialog")
+            copyDialog = atom.workspaceView.find(".tree-view-dialog").view()

         it "duplicates the current file", ->
           expect(copyDialog.miniEditor.getText()).toBe('tree-view.js')
@@ -1719,19 +1720,19 @@

         runs ->
           expect(fs.existsSync(temporaryFilePath)).toBeFalsy()
-          entriesCountBefore = treeView.root.entries.find('.entry').length
+          entriesCountBefore = $(treeView.root.entries).find('.entry').length
           fs.writeFileSync temporaryFilePath, 'hi'

-        waitsFor "directory view contens to refresh", ->
-          treeView.root.entries.find('.entry').length == entriesCountBefore + 1
+        waitsFor "directory view contents to refresh", ->
+          $(treeView.root.entries).find('.entry').length == entriesCountBefore + 1

         runs ->
-          expect(treeView.root.entries.find('.entry').length).toBe entriesCountBefore + 1
-          expect(treeView.root.entries.find('.file:contains(temporary)')).toExist()
+          expect($(treeView.root.entries).find('.entry').length).toBe entriesCountBefore + 1
+          expect($(treeView.root.entries).find('.file:contains(temporary)')).toExist()
           fs.removeSync(temporaryFilePath)

-        waitsFor "directory view contens to refresh", ->
-          treeView.root.entries.find('.entry').length == entriesCountBefore
+        waitsFor "directory view contents to refresh", ->
+          $(treeView.root.entries).find('.entry').length == entriesCountBefore

   describe "the hideVcsIgnoredFiles config option", ->
     describe "when the project's path is the repository's working directory", ->
@@ -1824,7 +1825,7 @@
       atom.project.getRepo().getPathStatus(modifiedFile)

       treeView.updateRoot()
-      treeView.root.entries.find('.directory:contains(dir)').expand()
+      $(treeView.root.entries).find('.directory:contains(dir)')[0].expand()

     describe "when the project is the repository root", ->
       it "adds a custom style", ->
@@ -1832,7 +1833,7 @@

     describe "when a file is modified", ->
       it "adds a custom style", ->
-        treeView.root.entries.find('.directory:contains(dir)').expand()
+        $(treeView.root.entries).find('.directory:contains(dir)')[0].expand()
         expect(treeView.find('.file:contains(b.txt)')).toHaveClass 'status-modified'

     describe "when a directory if modified", ->
@@ -1841,7 +1842,7 @@

     describe "when a file is new", ->
       it "adds a custom style", ->
-        treeView.root.entries.find('.directory:contains(dir2)').expand()
+        $(treeView.root.entries).find('.directory:contains(dir2)')[0].expand()
         expect(treeView.find('.file:contains(new2)')).toHaveClass 'status-added'

     describe "when a directory is new", ->
@@ -1890,9 +1891,9 @@

       runs ->
         atom.workspaceView.trigger 'tree-view:toggle'
-        treeView = atom.workspaceView.find(".tree-view")
-        dirView = treeView.root.entries.find('.directory:contains(test-dir)')
-        dirView.expand()
+        treeView = atom.workspaceView.find(".tree-view").view()
+        dirView = $(treeView.root.entries).find('.directory:contains(test-dir)')
+        dirView[0].expand()
         fileView1 = treeView.find('.file:contains(test-file1.txt)')
         fileView2 = treeView.find('.file:contains(test-file2.txt)')
         fileView3 = treeView.find('.file:contains(test-file3.txt)')


fix test bug.

fix test bug.

--- spec/vim-state-spec.coffee
+++ spec/vim-state-spec.coffee
@@ -304,7 +304,7 @@
         expect(vimState.submode).toEqual 'characterwise'
         expect(editorElement.classList.contains('command-mode')).toBe(false)

-        keydown('escape')
+        keydown('v')
         expect(vimState.mode).toEqual 'command'
         expect(editorElement.classList.contains('command-mode')).toBe(true)

@@ -313,7 +313,7 @@
         expect(vimState.submode).toEqual 'linewise'
         expect(editorElement.classList.contains('command-mode')).toBe(false)

-        keydown('escape')
+        keydown('V', shift: true)
         expect(vimState.mode).toEqual 'command'
         expect(editorElement.classList.contains('command-mode')).toBe(true)

@@ -322,7 +322,7 @@
         expect(vimState.submode).toEqual 'blockwise'
         expect(editorElement.classList.contains('command-mode')).toBe(false)

-        keydown('escape')
+        keydown('v', ctrl: true)
         expect(vimState.mode).toEqual 'command'
         expect(editorElement.classList.contains('command-mode')).toBe(true)

@@ -347,6 +347,12 @@
           expect(editorElement.classList.contains('visual-mode')).toBe(true)
           expect(vimState.submode).toEqual 'blockwise'
           expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+          keydown('v')
+          expect(editorElement.classList.contains('visual-mode')).toBe(true)
+          expect(vimState.submode).toEqual 'characterwise'
+          expect(editorElement.classList.contains('command-mode')).toBe(false)
+

         it "recover original range when shift from linewse to characterwise", ->
           keydown('v')


Add enable/disable buttons for the client console - disabled console displays no alerts, but still collects them

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
@@ -50,16 +50,36 @@
             """

-              <button class="btn btn-mini"> Clear Console</button>
+              <button data-action="clear" class="btn btn-mini"> Clear Console</button>
+              <button data-action="enable" class="btn btn-mini" disabled="true"> Enable Console</button>
+              <button data-action="disable" class="btn btn-mini"> Disable Console</button>
               """

           dom.body.prepend floatingConsole

           alertContainer = floatingConsole.findFirst ".alert-container"

-          floatingConsole.on "click", ".btn-mini", ->
+          floatingConsole.on "click", "[data-action=clear]", ->
             floatingConsole.hide()
             alertContainer.update ""
+
+          floatingConsole.on "click", "[data-action=disable]", ->
+
+            @attribute "disabled", true
+            floatingConsole.findFirst("[data-action=enable]").attribute "disabled", false
+
+            alertContainer.hide()
+
+            return false
+
+          floatingConsole.on "click", "[data-action=enable]", ->
+
+            @attribute "disabled", true
+            floatingConsole.findFirst("[data-action=disable]").attribute "disabled", false
+
+            alertContainer.show()
+
+            return false

         div = dom.create
           class: "alert #{className}"
@@ -75,7 +95,6 @@
         _.delay -> alertContainer.element.scrollTop = alertContainer.element.scrollHeight

         animating = false
-        removed = false

         runFadeout = ->
           return if animating
@@ -84,7 +103,8 @@

           div.fadeOut FADE_DURATION, ->
             dom.withReflowEventsDisabled ->
-              div.remove() unless removed
+              # Remove if not already removed
+              div.remove() if div.parent()

               # Hide the console after the last one is removed.
               unless floatingConsole.findFirst(".alert")


:arrow_up: Use the new bottom tab API in container

--- lib/ui/bottom-container.coffee
+++ lib/ui/bottom-container.coffee
@@ -12,9 +12,9 @@
     @subscriptions = new CompositeDisposable
     @emitter = emitter = new Emitter
     @tabs =
-      Line: new BottomTab().prepare('Line')
-      File: new BottomTab().prepare('File')
-      Project: new BottomTab().prepare('Project')
+      Line: BottomTab.create('Line')
+      File: BottomTab.create('File')
+      Project: BottomTab.create('Project')
     @status = new BottomStatus()
     Me = this


add unit test for getProjectDocsIfMatch

add unit test for getProjectDocsIfMatch

--- services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee
+++ services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee
@@ -252,6 +252,47 @@
 					.calledWith(new Error("doc updater returned failure status code: 500"))
 					.should.equal true

+	describe "getProjectDocsIfMatch", ->
+		beforeEach ->
+			@callback = sinon.stub()
+			@project_state_hash = "1234567890abcdef"
+
+		describe "successfully", ->
+			beforeEach ->
+				@doc0 =
+					_id: @doc_id
+					lines: @lines
+					v: @version
+				@docs = [ @doc0, @doc0, @doc0 ]
+				@body = JSON.stringify @docs
+				@request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body)
+				@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
+
+			it 'should get the documenst from the document updater', ->
+				url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc?state=#{@project_state_hash}"
+				@request.get.calledWith(url).should.equal true
+
+			it "should call the callback with the documents", ->
+				@callback.calledWithExactly(null, @docs).should.equal true
+
+		describe "when the document updater API returns an error", ->
+			beforeEach ->
+				@request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
+				@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
+
+			it "should return an error to the callback", ->
+				@callback.calledWith(@error).should.equal true
+
+		describe "when the document updater returns a conflict error code", ->
+			beforeEach ->
+				@request.get = sinon.stub().callsArgWith(1, null, { statusCode: 409 }, "Conflict")
+				@handler.getProjectDocsIfMatch @project_id, @project_state_hash, @callback
+
+			it "should return the callback with no documents", ->
+				@callback
+					.alwaysCalledWithExactly()
+					.should.equal true
+
 	describe "acceptChanges", ->
 		beforeEach ->
 			@change_id = "mock-change-id-1"


:new: Add addLinter, deleteLinter and getLinters to linter-plus

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -37,6 +37,15 @@
         currentEditorLinter.destroy()
         @_editorLinters.delete currentEditorLinter

+  addLinter: (linter) ->
+    @linters.add(linter)
+
+  deleteLinter: (linter) ->
+    @linters.delete(linter)
+
+  getLinters: ->
+    @linters
+
   getProjectMessages: ->
     @_messagesProject


Add remember me option to the access

Add remember me option to the access

--- library/exposure/access.coffee
+++ library/exposure/access.coffee
@@ -90,7 +90,7 @@
     # container could be either a request or duplex socket or other
     # member that has the session storage installed under `session`.
     # The method also saves the session once it has authenticated.
-    authenticate: (container, entity, callback) ->
+    authenticate: (container, entity, rme, callback) ->
         noSave = "the session has not save function"
         noSession = "container has no session object"
         assert session = container?.session, noSession
@@ -101,5 +101,6 @@
             assert not _.isEmpty(content), "no content"
             session["x-authenticate-entity"] = content
             assert _.isFunction(session.save), noSave
+            session.cookie.maxAge = 2628000000 if rme
             session.save => @dereference container, ->
                 return callback undefined, content


Make duplex inherit from preflight (for Marshal support)

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -43,14 +43,14 @@
 {STATUS_CODES} = require "http"
 {remote, external} = require "./remote"
 {Barebones} = require "./skeleton"
-{Screenplay} = require "./visual"
+{Preflight} = require "./preflight"

 # This abstract base class can be used as either a direct parent or
 # a compount to the `Screenplay` abstract service. It provides the
 # unique ability of half duplex data exchange between the external
 # code that is executed on the call site via `Screenplay` facility
 # and an instance of the service that resides on the server site.
-module.exports.Duplex = class Duplex extends Screenplay
+module.exports.Duplex = class Duplex extends Preflight

     # This is a marker that indicates to some internal subsystems
     # that this class has to be considered abstract and therefore


Fix ordering in the context deployment

Fix ordering in the context deployment

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -115,8 +115,8 @@
             return unless src = value.remote.source
             set = "context.%s = (#{src})()"
             installer += "\r\n#{format set, key}\r\n"
-        context.sources.push installer
-        context.sources.push invoker
+        context.sources.unshift invoker
+        context.sources.unshift installer
         return context

     # Get the contents of the resources at the established path. It


Bump max age time

Bump max age time

--- library/nucleus/bundles.coffee
+++ library/nucleus/bundles.coffee
@@ -45,7 +45,7 @@
     @defaults = server: {hostname: "localhost", port: 80}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.secure = port: 443, key: "key.pem", cert: "cert.pem"
-    @defaults.session = secret: "abcdefgh", cookie: maxAge: 60000
+    @defaults.session = secret: "abcdefgh", cookie: maxAge: 3600000
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
@@ -64,7 +64,7 @@
     @defaults = server: {hostname: "localhost", port: 80}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.secure = port: 443, key: "key.pem", cert: "cert.pem"
-    @defaults.session = secret: "abcdefgh", cookie: maxAge: 60000
+    @defaults.session = secret: "abcdefgh", cookie: maxAge: 3600000
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
@@ -83,7 +83,7 @@
     @defaults = server: {hostname: "localhost", port: 8081}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.secure = port: 1443, key: "key.pem", cert: "cert.pem"
-    @defaults.session = secret: "abcdefgh", cookie: maxAge: 60000
+    @defaults.session = secret: "abcdefgh", cookie: maxAge: 3600000
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
@@ -102,7 +102,7 @@
     @defaults = server: {hostname: "localhost", port: 8081}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.secure = port: 1443, key: "key.pem", cert: "cert.pem"
-    @defaults.session = secret: "abcdefgh", cookie: maxAge: 60000
+    @defaults.session = secret: "abcdefgh", cookie: maxAge: 3600000
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"


minor syntax conv changes

minor syntax conv changes

--- library/nucleus/content.coffee
+++ library/nucleus/content.coffee
@@ -82,7 +82,7 @@
         throw new Error invalid unless valid
         args = ["Content-Length", encoded.length]
         response.setHeader args... unless areSent
-        response.write encoded
+        return response.write encoded

 # This class is a content negotiation broker. It is instantiated by
 # the kernel and then can be used either directly or via middleware
@@ -99,11 +99,12 @@
         isObject = _.isObject content
         return unless isArray or isObject
         (request, response, content) =>
-            type = "Content-Type"
-            json = "application/json"
-            sent = response.headersSent
+            assert type = "Content-Type"
+            assert json = "application/json"
+            sent = response.headersSent or no
+            assert dump = JSON.stringify.bind JSON
             doesHtml = response.accepts /html/
             spaces = if doesHtml then 4 else null
             response.setHeader type, json unless sent
-            jsoned = (x) -> JSON.stringify x, null, spaces
-            @output response, jsoned content
+            jsoned = (x) -> dump x, undefined, spaces
+            return @output response, jsoned content


Non-string image URLs shouldn't cause crashes

--- src/Graphic/Graphic.coffee
+++ src/Graphic/Graphic.coffee
@@ -261,6 +261,9 @@
     {imageCache} = opts
     {url} = @imageComponent()

+    if not _.isString(url)
+      return  # TODO: error reporting?
+
     imageCache.get(url, (image) => @drawImage(opts, image))

   imageComponent: ->


add behaviral option(inclusive) for PairFinder to control allow or skip pair under cursor

--- lib/pair-finder.coffee
+++ lib/pair-finder.coffee
@@ -42,7 +42,8 @@

 class PairFinder
   constructor: (@editor, options={}) ->
-    {@allowNextLine, @allowForwarding, @pair} = options
+    {@allowNextLine, @allowForwarding, @pair, @inclusive} = options
+    @inclusive ?= true
     if @pair?
       @setPatternForPair(@pair)

@@ -57,7 +58,7 @@
     found = null

     # Quote is not nestable. So when we encounter 'open' while finding 'close',
-    # it is forwarding pair, so stoppable is not @allowForwarding
+    # it is forwarding pair, so stoppable unless @allowForwarding
     findingNonForwardingClosingQuote = (this instanceof QuoteFinder) and which is 'close' and not @allowForwarding
     scanner = scanEditorInDirection.bind(null, @editor, direction, @getPattern(), {from, @allowNextLine})
     scanner (event) =>
@@ -92,11 +93,15 @@
       when 'close'
         openState = @spliceStack(stack, eventState)
         unless openState?
-          return true
+          return @inclusive or eventState.range.start.isGreaterThan(from)

         if stack.length is 0
           openRange = openState.range
-          openRange.start.isEqual(from) or (@allowForwarding and openRange.start.row is from.row)
+          openStart = openRange.start
+          if @inclusive
+            openStart.isEqual(from) or (@allowForwarding and openStart.row is from.row)
+          else
+            openStart.isLessThan(from) or (@allowForwarding and openStart.isGreaterThan(from) and openStart.row is from.row)

   findCloseForward: (from) ->
     @findPair('close', 'forward', from)


cleanup

cleanup

--- lib/phabricator.coffee
+++ lib/phabricator.coffee
@@ -239,8 +239,6 @@
                 cb user.phid

   recordId: (user, id) ->
-    unless user.id?
-      user.id = user.name
     @data.users[user.id] ?= {
       name: "#{user.name}",
       id: "#{user.id}"
@@ -250,8 +248,6 @@

   retrieveId: (user, id = null) ->
-    unless user.id?
-      user.id = user.name
     @data.users[user.id] ?= {
       name: "#{user.name}",
       id: "#{user.id}"


Cleanup closed panes in the state

Cleanup closed panes in the state

--- lib/pinned-tabs.coffee
+++ lib/pinned-tabs.coffee
@@ -83,6 +83,9 @@
         if index < pinnedTabs.length
           pane.moveItem item, pinnedTabs.length
       , 1
+
+    atom.workspace.onDidDestroyPane ({pane}) =>
+      delete @state.data[pane.id] if @state.data[pane.id] != undefined

     atom.workspace.onWillDestroyPaneItem ({item, pane}) =>
       return if @state.data[pane.id] == undefined


:memo: Mention atom.project global

:memo: Mention atom.project global

--- src/git.coffee
+++ src/git.coffee
@@ -7,7 +7,7 @@
 # Public: Represents the underlying git operations performed by Atom.
 #
 # This class shouldn't be instantiated directly but instead by accessing the
-# global project instance and calling `getRepo()`.
+# `atom.project` global and calling `getRepo()`.
 #
 # ## Example
 #


Add Google Universal Analytics support In order to send the correct 'location', I used ga('set', 'location') method.

--- lib/assets/javascripts/google-analytics-turbolinks.js.coffee
+++ lib/assets/javascripts/google-analytics-turbolinks.js.coffee
@@ -2,7 +2,10 @@
   document.addEventListener 'page:change', (event) =>

     # Google Analytics
-    if window._gaq != undefined
+    if window.ga != undefined
+      ga('set', 'location', location.href.split('#')[0])
+      ga('send', 'pageview')
+    else if window._gaq != undefined
       _gaq.push(['_trackPageview'])
     else if window.pageTracker != undefined
       pageTracker._trackPageview();


Fixed issue with GET params not being generated properly

--- lib/observejs/app/assets/javascripts/observejs/xhr.js.coffee
+++ lib/observejs/app/assets/javascripts/observejs/xhr.js.coffee
@@ -30,6 +30,26 @@
       formData.append key, @value(key)

     formData
+
+  urlEncoded: (action) =>
+    parser = document.createElement('a')
+    parser.href = action
+    params = parser.search.substring(1).split('&')
+
+    for key in @keys()
+      params.push "#{key}=#{@value(key)}"
+
+    if @form?
+      for element in @form.elements
+        unless element.hasAttribute('name')
+          continue
+
+        type = element.getAttribute("type").toUpperCase() || 'TEXT'
+        if (type != 'FILE' && type != 'RADIO' && type != 'CHECKBOX') || element.checked
+          params.push "#{encodeURIComponent(element.name)}=#{encodeURIComponent(element.value)}"
+
+    parser.search = "?#{params.join("&")}"
+    parser.href

 class Response
   constructor: (@xhr) ->
@@ -70,6 +90,11 @@

     @method = el.getAttribute('method') || 'GET'

+  open: (method, action) =>
+    @request.open @method, action
+    @request.setRequestHeader 'accept', "*/*;q=0.5, #{@script}"
+    @request.setRequestHeader 'X-Requested-With', "XMLHttpRequest"
+
   send: =>
     action = @request.element.getAttribute('action') || @request.element.getAttribute('href')

@@ -77,27 +102,18 @@
       throw "Cannot send a request to the server if the element isn't a Form or if the element doesn't have the HREF attributes: <div href>"
       return

-    if /get/i.test(@method) && @data.any()
-      parser = document.createElement('a')
-      parser.href = action
-      params = parser.search.substring(1).split('&')

-      for key in @data.keys()
-        params.push "#{key}=#{@data.value(key)}"
+    if /post|put/i.test(@method)
+      @open(@method, action)

-      parser.search = "?#{params.join("&")}"
-
-      action = parser.href
-
-    @request.open @method, action
-    @request.setRequestHeader 'accept', "*/*;q=0.5, #{@script}"
-    @request.setRequestHeader 'X-Requested-With', "XMLHttpRequest"
-
-    if !/get/i.test(@method.test)
       token = document.querySelector('meta[name=csrf-token]').getAttribute('content')
       @request.setRequestHeader 'X-CSRF-Token', token

-    @request.send(@data.serialize())
+      @request.send(@data.serialize())
+    else
+      action = @data.urlEncoded(action)
+      @open(@method, action)
+      @request.send()

 ObserveJS.XHR = XHR


replace the on finish with on close in the finishLogic

--- lib/promiseSftp.coffee
+++ lib/promiseSftp.coffee
@@ -97,7 +97,7 @@
     promisifiedClientMethods = {}
     restartOffset = null

-
+
     # always-on event handlers
     sshClient.on 'error', (err) ->
       lastSshError = err
@@ -112,7 +112,7 @@
       if changePassword
         sshClient.removeListener('change password', changePassword)

-
+
     # common 'continue' logic
     continueLogicFactory = (clientContext, name) ->
       (args...) -> new Promise (resolve, reject) ->
@@ -135,7 +135,7 @@
     # common 'finish' logic
     finishLogic = (stream) ->
       continuePromise = new Promise (resolve, reject) ->
-        stream.once('finish', resolve)
+        stream.once('close', resolve)
       undefined

     # internal connect logic
@@ -248,7 +248,7 @@
         if connectOptions.keepalive && !connectOptions.keepaliveInterval
           connectOptions.keepaliveInterval = connectOptions.keepalive
         delete connectOptions.keepalive
-
+
         # now that everything is set up, we can connect
         _connect(STATUSES.CONNECTING)

@@ -274,7 +274,7 @@
           sshClient.end()

     @logout = @end
-
+
     @destroy = () ->
       if connectionStatus == STATUSES.NOT_YET_CONNECTED || connectionStatus == STATUSES.DISCONNECTED
         wasDisconnected = true
@@ -291,7 +291,7 @@
     @restart = (byteOffset) -> Promise.try () ->
       restartOffset = byteOffset
       undefined
-
+
     # methods listed in complexPassthroughMethods, which will get a common logic wrapper

     @list = (path='.') ->
@@ -352,7 +352,7 @@
           input = fs.createReadStream(input)
         input.pipe(stream)
         undefined
-
+
     @delete = promisifiedClientMethods.unlink

     @mkdir = (dirPath, recursive, attributes) =>
@@ -402,10 +402,10 @@
       promisifiedClientMethods.read(handle, buffer, offset, length, position)
       .spread (bytesRead, buffer, position) ->
         { bytesRead, buffer, position }
-
+
     @wait = () ->  # no-op, will perform wrapper logic only

-
+
     # common promise, connection-check, and reconnect logic
     commonLogicFactory = (name, finishType, handler) ->
       if finishType == 'continue'


Modify message when no language is selected

--- lib/script-view.coffee
+++ lib/script-view.coffee
@@ -139,7 +139,7 @@
     # Determine if no language is selected.
     if lang is 'Null Grammar' or lang is 'Plain Text'
       err = $$ ->
-        @p 'You must select a language in the lower left, or save the file
+        @p 'You must select a language in the lower right, or save the file
           with an appropriate extension.'

     # Provide them a dialog to submit an issue on GH, prepopulated with their


KodingClient: Change `Close` button with `Go to Login` button

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -261,7 +261,7 @@
     createStaticContentHandler = @bound 'createStaticContentDisplayHandler'

     registerHostNameAndKey = ->
-      $.cookie "register-to-koding-client", "yes"
+      $.cookie "register-to-koding-client", yes
       unless KD.isLoggedIn()
         message = "Please login before next step"
         modal = new KDBlockingModalView
@@ -270,11 +270,13 @@
           height       : "auto"
           overlay      : yes
           buttons      :
-            Close      :
-              style    : "modal-clean-gray"
-              callback : ->
+            "Go to Login" :
+              style       : "modal-clean-gray"
+              callback    : ->
                 modal.destroy()
-                KD.utils.wait 5000, window.location.pathname = "/"
+                KD.utils.wait 5000, window.location.pathname = "/Login"
+      else
+        location.href = "/"

     routes =


use lontUrl from server as adding url into list

--- client/app/main/main.controller.coffee
+++ client/app/main/main.controller.coffee
@@ -16,7 +16,7 @@

       $scope.urls.unshift
         shortenId: data.shortUrl.split(/[\/ ]+/).pop()
-        longUrl: $scope.longUrl
+        longUrl: data.longUrl
         createdAt: moment().format()
         clicked: 0


force display of loading indicator

force display of loading indicator

--- client/lanes/workspace/ScreenView.cjsx
+++ client/lanes/workspace/ScreenView.cjsx
@@ -24,7 +24,7 @@

     renderLoading: ->
         screen = Lanes.Screens.Definitions.all.findWhere(loading: true)
-        <LC.NetworkActivityOverlay isRequesting message="Loading #{screen.title}…" />
+        <LC.NetworkActivityOverlay forceOn model={screen} message="Loading #{screen.title}…" />

     render: ->
         <div className={"page-content #{@context.uistate.layout_size}"}>


change for me

change for me

--- lib/transformer.coffee
+++ lib/transformer.coffee
@@ -128,7 +128,7 @@
 class JavaScript extends Transformer
   command: 'babel-node'
   initialize: ->
-    @args = ["--presets=es2017", @sourcePath]
+    @args = ["--presets=stage-2", @sourcePath]

   compile: ->
     @command = 'babel'
@@ -140,7 +140,7 @@
     @args = ['run', @sourcePath]

 class Haskell extends Transformer
-  command: 'runhaskell'
+  command: 'stack'

 class OCaml extends Transformer
   compile: ->


Set limit back to a sensible level

Set limit back to a sensible level

--- plugins/audit/app/assets/javascripts/audit/reducers/events.coffee
+++ plugins/audit/app/assets/javascripts/audit/reducers/events.coffee
@@ -5,7 +5,7 @@
     total: 0
     offset: 0
     currentPage: 1
-    limit: 2
+    limit: 30
     items: []
     isFetching: false
     filterStartTime: ''


Fixes problenm with build_expression_from_javascript_object

--- math/build_expression_from_javascript_object_spec.coffee
+++ math/build_expression_from_javascript_object_spec.coffee
@@ -58,7 +58,22 @@
     expect(expression).toBeInstanceOf @components.expression
     expect(sub_exp).toBeInstanceOf @components.expression

-  it "uses object syntax with label 'open_expression' to signify an open expression", ->
-    expression = @builder open_expression: [10]
-    sub_exp = expression.first()
-    expect(sub_exp.isOpen()).toEqual(true)
+  describe "open expressions", ->
+    it "uses object syntax with label 'open_expression' to signify an open expression", ->
+      expression = @builder open_expression: [10]
+      sub_exp = expression.first()
+      expect(sub_exp.isOpen()).toEqual(true)
+
+    it "correctly constructs nested open expressions", ->
+      expression = @builder open_expression: open_expression: [10]
+
+      first_open = expression.first()
+      expect(first_open.isOpen()).toEqual(true)
+
+      second_open = first_open.first()
+      expect(second_open.isOpen()).toEqual(true)
+
+      ten = second_open.first()
+      expect(ten.value()).toEqual 10
+
+


Fix user name in log

Fix user name in log

--- src/api/visualizers.coffee
+++ src/api/visualizers.coffee
@@ -29,6 +29,6 @@
       return utils.logAndSetResponse this, 404, "Could not find visualizer with #{name}", 'info'

     this.body = "Successfully removed visualizer with name #{name}"
-    logger.info "User #{this.authenticated.name} removed visualizer #{name}"
+    logger.info "User #{this.authenticated.email} removed visualizer #{name}"
   catch e
     utils.logAndSetResponse this, 500, "Could not remove visualizer #{name} via the API #{e}", 'error'


bind vm is about to change

bind vm is about to change

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -84,8 +84,9 @@
       if data.actionstatus is "Success"
         @createDomain client,
           domain   : data.description
-          owner    : client.connection.delegate.getId()
           regYears : params.years
+          orderId  :
+            resellerClub : data.entityid
           , (err, model) =>
             callback err, model
       else
@@ -120,6 +121,10 @@
     domainManager.dnsManager.removeDNSRecordFromProxy record, callback

   @bindVM = secure ({connection:{delegate}}, params, callback)->
+    KD.remote.api.JVM.someData {name:params.vmName}, {hostname:1}, (err, vm)->
+      console.log vm
+
+    """
     record =
       mode          : "vm"
       username      : delegate.profile.nickname
@@ -128,13 +133,14 @@

     if params.state
       domainManager.dnsManager.registerNewRecordToProxy record, (response)=>
-        @update {"domain":params.domainName}, {'$push': {"vms":params.vmName}}
+        @update {"domain":params.domainName}, {'$push': {"hostnameAlias":params.vmName}}
         callback "Your domain is now connected to #{params.vmName} VM." if response?.host?

     else
       domainManager.dnsManager.removeDNSRecordFromProxy record, (response)=>
-        @update {"domain":params.domainName}, {'$pull': {"vms":params.vmName}}
+        @update {"domain":params.domainName}, {'$pull': {"hostnameAlias":params.vmName}}
         callback "Your domain is now disconnected from the #{params.vmName} VM." if response?.res?
+    """

   @updateWhiteList = (params, callback)->
     if params.op == 'addToSet'


Github: add more documentation make sure all required parameters provided

--- workers/social/lib/social/models/github.coffee
+++ workers/social/lib/social/models/github.coffee
@@ -1,5 +1,4 @@
 { Base, signature } = require 'bongo'
-KodingError         = require '../error'

 { argv }  = require 'optimist'
 GithubAPI = require 'github'
@@ -28,6 +27,13 @@
           (signature Object, Function)

+  ###*
+   * Inline hepler to initialize GithubAPI instance for provided `client`
+   *
+   * @param  {Object} client - Revived client object which includes required
+   *                           oauth info in `client.r.oauth.token`
+   * @return {Object}        - GithubAPI instance
+  ###
   initGithubFor = (client) ->

     { oauth: {token} } = client.r
@@ -44,6 +50,20 @@
     return gh

+  ###*
+   * Provides a bridge between node-gitub api with Koding client
+   * In the revive it checks and verifies the oauth requirements
+   * for provided `OAUTH_PROVIDER`. If fails it will return callback
+   * with missing oauth error.
+   *
+   * _param {Object} client     - this will be injected
+   * @param {Object} _options   - includes `method` and `options`
+   *                          	 	method will consist of `base.method` string
+   *                             	api: http://mikedeboer.github.io/node-github/
+   * @param {Function} callback - function to call when the request is
+   *                            	finished with an error as first argument
+   *                            	and result data as second argument.
+  ###
   @api = permit 'api access', success: revive

     shouldReviveProvider : no
@@ -51,8 +71,17 @@

   , (client, _options, callback) ->

+    cbErr = (message) -> callback {
+      message: message or 'Insufficient parameters provided'
+    }
+
+    # Make sure all required parameteres provided
     { method, options } = _options
+    return cbErr()  if not method
+
     [ base, method ]    = method.split '.'
+    return cbErr()  if not base or not method
+
     # Callback wrapper to add some more
     # functionality to default callback
     cb = (err, response) ->
@@ -77,7 +106,13 @@
     # Requires extensive debugging on Bongo.Base ~ GG
     (options ?= {}).per_page = 10

+    # Initialize Github api with client object
+    # client object includes required token
     gh = initGithubFor client
+
+    # Make sure provided base and method exists
+    unless gh[base]?[method]?
+      return cbErr "No such base:'#{base}' or method:'#{method}'"

     try
       # TODO We can add a whitelist of accepted methods/bases


adding author from rel but still broken

adding author from rel but still broken

--- src/md.coffee
+++ src/md.coffee
@@ -434,7 +434,13 @@
                 do after1
                 do after2
             # Block quotes (and similar elements) are relatively straight forward.
-            when 'BLOCKQUOTE', 'DD' then after = @pushLeft '> '
+            when 'BLOCKQUOTE', 'DD'
+              quoteMd = '> '
+              # Special case for Vanilla blockquotes with the "rel" attr
+              if @has ele, 'rel', no
+                rel = @attr ele, 'rel', @options.absolute
+                quoteMd = "@#{rel} said:\n" + quoteMd
+              after = @pushLeft quoteMd
             # Links on the other hand are probably the trickiest.
             when 'A'
               # Extract the link URL from `ele`.


Store string dates for easy modification of log.

--- src/log.coffee
+++ src/log.coffee
@@ -168,17 +168,24 @@
                 e.duration = @get_duration i, entries

     new_entry: (entry, save=true)->
-        {active, task, date, pause} = entry
+        {active, task, date_str, pause} = entry
+        if not date_str?
+            {date} = entry
+            entry.date_str = new Date(date).toISOString()
+        else
+            date = (new Date date_str).getTime()
+        entry.date = date
+        entry.index = undefined
         if @last_entry and entry.date < @last_entry.date
             # Avoid negative entries
             # TODO: try to reproduce bug that causes this
             entry.date = date = Math.max date, @last_entry.date
+            entry.date_str = new Date(date).toISOString()

         if pause?
             console.log "%c#{if pause then 'PAUSE' else 'PLAY'}
                 #{new Date(date).toLocaleString()}",
                 "color:#{if pause then 'red' else 'green'}"
-            entry.index = @entries.length
             @is_paused = pause
             @entries.push entry
             if save
@@ -188,7 +195,6 @@
         activity_changed = active != @is_active
         task_changed = task != @last_entry?.task
         if activity_changed or task_changed
-            entry.index = @entries.length
             if activity_changed
                 @last_activity_change = entry
             if entry.active and task_changed


Use png format for guide map background tile layer

--- src/map.coffee
+++ src/map.coffee
@@ -77,7 +77,7 @@
                         continuousWorld: false
                     new L.TileLayer ortoImageUrl, ortoImageOptions
                 else
-                    guideMapUrl = KYMPGeoserverUrl("avoindata:Karttasarja_PKS", "gif")
+                    guideMapUrl = KYMPGeoserverUrl("avoindata:Karttasarja_PKS", "png")
                     guideMapOptions =
                         maxZoom: 12
                         minZoom: 2


Add file header

Add file header

--- src/lib/AsCallback.coffee
+++ src/lib/AsCallback.coffee
@@ -1,3 +1,8 @@
+#     NoFlo - Flow-Based Programming for JavaScript
+#     (c) 2017 Flowhub UG
+#     NoFlo may be freely distributed under the MIT license
+#
+# asCallback helper embedding NoFlo components or graphs in other JavaScript programs.
 ComponentLoader = require('./ComponentLoader').ComponentLoader
 Network = require('./Network').Network
 IP = require('./IP')


[CHORE] Remove some dead code

[CHORE] Remove some dead code

--- src/liquid/context.coffee
+++ src/liquid/context.coffee
@@ -5,7 +5,7 @@

   constructor: (engine, environments = {}, outerScope = {}, registers = {}, rethrowErrors = false) ->
     @environments = Liquid.Helpers.flatten [environments]
-    @scopes = [outerScope or {}]
+    @scopes = [outerScope]
     @registers = registers
     @errors = []
     @rethrowErrors = rethrowErrors


state objects

state objects

--- src/cleaner/cleaner.coffee
+++ src/cleaner/cleaner.coffee
@@ -2,10 +2,66 @@
 #   remove end-of-line comments
 #   replace block comments with whitespace

-ready = 0
-commenting = 1
-quoting = 2
+class State
+  constructor: ->
+  handleEscaped: (prev, char) -> ['', @]
+  handlePlain: (char) -> ['', @, no]
+
+ready = new State()
+commenting = new State()
+comment_to_line = 2
+quoting = 3
 escape_stack = []
+
+class Ready
+  constructor: ->
+  handleEscaped: (prev, char) ->
+    switch char
+      when '*'
+        ['  ', commenting]
+      when '/'
+        ['', comment_to_line]
+      when '\''
+        [prev + char, quoting]
+      else
+        [prev + char, @]
+  handlePlain: (char) ->
+    switch char
+      when '/'
+        ['', @, char]
+      when '\''
+        [char, quoting, no]
+      else
+        [char, @, no]
+
+class Commenting
+  constructor: ->
+  handleEscaped: (prev, char) ->
+    if char is '/'
+      ['  ', ready]
+    else
+      ['  ', @]
+  handlePlain: (char) ->
+    if char is '*'
+      ['', @, char]
+    else
+      [' ', @, no]
+
+#class Quoting
+#  constructor: ->
+#  handleEscaped: (prev, char) ->
+#    # this is clearly a bug
+#    [prev + char, @]
+#  handlePlain: (char) ->
+#    when '\\'
+#      ['', @, char]
+#    when '\''
+#      [char, ready, no]
+#    else
+#      [char, @, no]
+
+ready = new Ready()
+commenting = new Commenting()

 state = ready

@@ -14,52 +70,15 @@
   for char in line.split ''
     if escape_stack.length
       prev = escape_stack.pop()
-      switch state
-        when quoting
-          clean += prev + char
-        when commenting
-          if char is '/'
-            state = ready
-            clean += '  '
-          else
-            clean += '  '
-        else # ready
-          switch char
-            when '*'
-              clean += '  '
-              state = commenting
-            when '/'
-              return clean
-            when '\''
-              clean += prev + char
-              state = quoting
-            else
-              clean += prev + char
+      [new_char, new_state] = state.handleEscaped prev, char
+      return clean if new_state is comment_to_line
+      clean += new_char
+      state = new_state
     else
-      switch state
-        when quoting
-          switch char
-            when '\\'
-              escape_stack.push char
-            when '\''
-              clean += char
-              state = ready
-            else
-              clean += char
-        when commenting
-          if char is '*'
-            escape_stack.push char
-          else
-            clean += ' '
-        else # ready
-          switch char
-            when '/'
-              escape_stack.push char
-            when '\''
-              clean += char
-              state = quoting
-            else
-              clean += char
+      [new_char, new_state, push_me] = state.handlePlain char
+      clean += new_char
+      state = new_state
+      escape_stack.push push_me if push_me
   clean

 cleanInput = (input) ->


Update TileMapView to scale/follow camera.

--- src/scene/views/tileMapView.coffee
+++ src/scene/views/tileMapView.coffee
@@ -20,38 +20,14 @@
   constructor : (@canvas, @tileMap) ->
     super(@canvas)
     @$el = $ @canvas
-
-    @$el.on 'mousewheel', (e) =>
-      if e.originalEvent.wheelDeltaY > 0
-        @cameraZoom -= 1
-        @cameraZoom = Math.max(1,@cameraZoom)
-      else
-        @cameraZoom += 1
-        @cameraZoom = Math.min(8,@cameraZoom)
-
-    @$el.on 'mousemove', (e) => @hoverPos = @screenToWorld(@canvasMousePosition(e)).truncate()
+    @camera = new Rect(0,0,9,9)
+    @cameraScale = 1.0
     # DEBUG CODE REMOVE TODO
     $(window).on 'keydown', (e) =>
       return @camera.point.x -= 1 if e.keyCode is 37 # Left
       return @camera.point.y -= 1 if e.keyCode is 38 # Up
       return @camera.point.x += 1 if e.keyCode is 39 # Right
       return @camera.point.y += 1 if e.keyCode is 40 # Down
-
-    #@camera.point.set 0, 0
-    #@camera.extent.set @tileMap.bounds.extent.x, @tileMap.bounds.extent.y
-
-  debugRender: () ->
-    # No interactivity yet, please.
-    if @hoverPos and false
-      renderPos = @worldToScreen(@hoverPos)
-      renderUnitSize = Screen.UNIT * @cameraZoom
-      @context.save()
-      @context.strokeStyle = "rgba(255,0,0,0.85)"
-      @context.fillStyle = "rgba(255,255,255,0.35)"
-      @context.strokeRect(renderPos.x, renderPos.y, renderUnitSize, renderUnitSize)
-      @context.fillRect(renderPos.x, renderPos.y, renderUnitSize, renderUnitSize)
-      @context.restore()
-    super()

   drawTile : (icon, x, y) =>
     coords = Data.sprites[icon];
@@ -61,22 +37,28 @@
     srcX = coords.x
     srcY = coords.y
     srcW = srcH = Screen.UNIT
-    dstX = x * Screen.UNIT * @cameraZoom
-    dstY = y * Screen.UNIT * @cameraZoom
-    dstW = dstH = Screen.UNIT  * @cameraZoom
+    dstX = x * Screen.UNIT * @cameraScale
+    dstY = y * Screen.UNIT * @cameraScale
+    dstW = dstH = Screen.UNIT  * @cameraScale
     @context.drawImage(image,srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH)

   render: () ->
+    # Pin camera zoom to match canvas size
+    @cameraScale = @screenToWorld(@$el.width()) / @camera.extent.x
     @context.save();
     @context.fillStyle = "rgb(0,0,0)"
     @context.fillRect(0, 0, @canvas.width, @canvas.height)

     clipRect = new Rect(@camera).clip @tileMap.bounds
+    # Adjust render position for camera.
+    worldTilePos = @worldToScreen(@tileMap.bounds.point,@cameraScale)
+    worldCameraPos = @worldToScreen(@camera.point,@cameraScale)
+    @context.translate(worldTilePos.x - worldCameraPos.x,worldTilePos.y - worldCameraPos.y)
+
     xStride = clipRect.point.x + clipRect.extent.x
     yStride = clipRect.point.y + clipRect.extent.y
-
     for y in [clipRect.point.y ... yStride]
-      for x in [@camera.point.x ... xStride]
+      for x in [clipRect.point.x ... xStride]
         tile = @tileMap.getTerrainIcon x, y
         @drawTile(tile, x, y) if tile
     for feature in @tileMap.map.features
@@ -84,4 +66,3 @@
       @drawTile(feature.icon, feature.x, feature.y) if feature.icon

     @context.restore()
-    super()


Add TODO

Add TODO

--- src/command_service.coffee
+++ src/command_service.coffee
@@ -9,9 +9,11 @@
   constructor: (@_aggregateRepository, @_readAggregateRepository) ->
     @aggregateCache = {}

-  createAggregate: (Aggregate) ->
+  # TODO support garbage-collector-callback which gets called in intervals to check if we can drop the cache-entry
+
+  createAggregate: (Aggregate, params) ->
     # create Aggregate
-    aggregate = new Aggregate
+    aggregate = new Aggregate params
     aggregate.create()

     @_handle 'create', aggregate


added confirmation to install-systemd-unit.coffee

--- scripts/install-systemd-unit.coffee
+++ scripts/install-systemd-unit.coffee
@@ -71,11 +71,11 @@

             log.info "\nFinal configuration:\n\n%s\n", unitStr.split('\n').map((l)->"   #{l}").join('\n')

-            fs.writeFile answers.path, unitStr, mode: 0o644, (err) ->
-                if err
-                    log.error err.stack
-                else
-                    log.info "Wrote:  #{answers.path.bold.cyan}\n\nNow enable and start the service using the #{'systemctl'.bold.green} command."
-
-
-
+            v = (s) -> /^[yn]/i.test s
+            inquirer.prompt [{name:'yes',message:'Write file? (y/n)',validate:v}], (confirm)->
+                if S(confirm.yes.toLowerCase()).startsWith 'y'
+                    fs.writeFile answers.path, unitStr, mode: 0o644, (err) ->
+                        if err
+                            log.error err.stack
+                        else
+                            log.info "Wrote:  #{answers.path.bold.cyan}\n\nNow enable and start the service using the #{'systemctl'.bold.green} command."


Removed old file

Removed old file

--- sublime/kissml.coffee
+++ sublime/kissml.coffee
@@ -1,17 +0,0 @@
-_ = require 'underscore'
-kissml = require 'kissml'
-
-parser = require 'kissml_pa
-printer = require './kissml_printer'
-tokenizer = require './kissml_tokenizer'
-
-module.exports.smack = (string, parser, printer) ->
-	tokenizer = opts.tokenizer or tokenizer.Create()
-	printer   = opts.printer   or printer.Create()
-	parser    = opts.parser    or parser.Create()
-
-	token  = tokenizer.frack string
-	tree   = parser.organize token
-	result = printer.walk tree
-
-	return result


Fix online.net konnector (#466)

Fix online.net konnector (#466)

--- server/konnectors/online_net.coffee
+++ server/konnectors/online_net.coffee
@@ -74,7 +74,7 @@
 logIn = (requiredFields, bills, data, next) ->

     formUrl = 'https://console.online.net/en/login?o=1'
-    loginUrl = 'https://console.online.net/login_check'
+    loginUrl = 'https://console.online.net/en/login_check'
     billUrl = "https://console.online.net/en/bill/list"
     userAgent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:36.0) ' + \
                 'Gecko/20100101 Firefox/36.0'


datarange should ignore nan's in min/max computation

--- static/coffee/common/ranges.coffee
+++ static/coffee/common/ranges.coffee
@@ -37,11 +37,13 @@
     columns = _.reduce(columns, ((x, y) -> return x.concat(y)), [])
     columns = _.filter(columns, (x) -> typeof(x) != "string")
     if not _.isArray(columns[0])
+      columns = _.reject(columns, (x) -> isNaN(x))
       [min, max] = [_.min(columns), _.max(columns)]
     else
       maxs = Array(columns.length)
       mins = Array(columns.length)
       for i in [0..columns.length-1]
+        columns[i] = _.reject(columns[i], (x) -> isNaN(x))
         maxs[i] = _.max(columns[i])
         mins[i] = _.min(columns[i])
       [min, max] = [_.min(mins), _.max(maxs)]


engine_selector

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -49,7 +49,9 @@
      engine.find ".speed=5"
   => [enemy]

-     # You may mix and match selectors. "Enemy
+     # You may mix and match selectors.
+     engine.find "Enemy.x=0"
+  => [enemy, boss] # doesn't return distantEnemy
   </pre></code>

   You may mix and match selectors. "Wall.x=0" to select all objects of class Wall with an x property of 0.


Fixed collab group

Fixed collab group

--- lib/atox-collabGroup.coffee
+++ lib/atox-collabGroup.coffee
@@ -18,13 +18,14 @@
       throw {"id": 2, "message": "Invalid JSON"} unless d.id? and d.collab is true
       @id = d.id
     else
-      @id = genID()
+      @id = @genID()
       @name = JSON.stringify {"collab": true, "id": @id}
       @aTox.TOX.setGCtitle {"gID": @gID, "title": @name}

     @peerlist = []

   genID: ->
+    t = ""
     chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$%&(){}[]_+-*/'#|;,:."
     t += chars[Math.floor(Math.random() * chars.length)] for i in [0..20]
     return t


Remove desktop non-auction legacy buy behavior

--- src/desktop/apps/artwork/components/commercial/view.coffee
+++ src/desktop/apps/artwork/components/commercial/view.coffee
@@ -8,7 +8,6 @@
 ArtworkInquiry = require '../../../../models/artwork_inquiry.coffee'
 Form = require '../../../../components/form/index.coffee'
 Serializer = require '../../../../components/form/serializer.coffee'
-PendingOrder = require '../../../../models/pending_order.coffee'
 analyticsHooks = require '../../../../lib/analytics_hooks.coffee'
 openMultiPageModal = require '../../../../components/multi_page_modal/index.coffee'
 openInquiryQuestionnaireFor = require '../../../../components/inquiry_questionnaire/index.coffee'
@@ -54,16 +53,6 @@
       .then (data) ->
         order = data?.createOrderWithArtwork?.orderOrError?.order
         location.assign("/order2/#{order.id}/shipping")
-
-    else
-      order = new PendingOrder
-      @form = new Form $form: @$('form'), model: order
-
-      @form.submit e, success: ->
-        location.assign "/order/#{order.id}/resume?token=#{order.get 'token'}"
-
-      analyticsHooks
-        .trigger 'order:item-added', "Artwork:#{order.get 'artwork_id'}"

   inquire: (e) =>
     e.preventDefault() if e


Fix issue where breadcrumbs with quotes would break other crumbs

--- src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
+++ src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
@@ -31,7 +31,7 @@
         _.map obj, (limit) ->
           # Values with spaces in them must have been surrounded by quote strings
           if /\s/g.test(limit.value) and not /"/g.test(limit.value)
-            limit.value = "\"#{limit[key]}\""
+            limit.value = "\"#{limit.value}\""

           return "#{limit.tag}:#{limit.value}" # Limit strings are in the format `limit:value`


Added script to handle resizing of container

--- docs/coffee/script.coffee
+++ docs/coffee/script.coffee
@@ -144,6 +144,21 @@
         $input.val $(this).data "command"
         $input.focus()

+    $(".resizable-handle").on "mousedown", (e) ->
+        startX = e.pageX
+        $resizable = $(this).closest ".resizable"
+        initialWidth = $resizable.outerWidth()
+        e.preventDefault()
+
+        $resizable.parent().on "mousemove", (e) ->
+            console.log startX, e.pageX
+            e.preventDefault()
+            $resizable.css "width", initialWidth + (e.pageX - startX)*2
+
+        $resizable.parent().on "mouseup", (e) ->
+            console.log "UP"
+            $resizable.parent().off "mousemove"
+


updated form manager spec to be both more clear and correct with the new error relay rules

--- spec/tests/form_manager_spec.coffee
+++ spec/tests/form_manager_spec.coffee
@@ -6,14 +6,7 @@
 describe "FormManager", ->
 	manager = submitter = validator = null
 	submitButton = null
-
-	reqwestCallback = null
-	reqwestResponse = null
-	reqwestSpy = (params)-> params[reqwestCallback].apply null, reqwestResponse
-
-	# these are a result of using Function::apply above
-	successResponse = -> reqwestResponse[0]
-	errorResponse = -> reqwestResponse[1]
+	reqwestSpy = null

 	callbacks =
 		validateErrorHappened: (form)->
@@ -29,14 +22,13 @@
 		validator = FormValidator.create()
 		manager = new FormManager(submitter, validator)

-		reqwestCallback = "success"
 		spyOn(callbacks, 'validateErrorHappened').andCallThrough()
 		spyOn(callbacks, 'submitSuccessHappened').andCallThrough()
 		spyOn(callbacks, 'submitErrorHappened').andCallThrough()
 		manager.validatedWithErrors.add callbacks.validateErrorHappened
 		manager.submittedWithSuccess.add callbacks.submitSuccessHappened
 		manager.submittedWithError.add callbacks.submitErrorHappened
-		spyOn(window, 'reqwest').andCallFake(reqwestSpy)
+		reqwestSpy = spyOn(window, 'reqwest')

 	describe '::create', ->

@@ -74,7 +66,7 @@

 				it "will attempt to submit the form", ->
 					spyOn(submitter, 'submit')
-					reqwestResponse = mocks.simpleSuccessResponse
+					reqwestSpy.andCallFake((params)-> params.success(mocks.simpleSuccessResponse))
 					submitButton.click()
 					expect(submitter.submit).toHaveBeenCalledWith(form, 'simple')

@@ -86,11 +78,11 @@
 				describe "and submission is successful", ->

 					beforeEach ->
-						reqwestResponse = mocks.simpleSuccessResponse
+						reqwestSpy.andCallFake((params)-> params.success(mocks.simpleSuccessResponse))
 						submitButton.click()

 					it "will call callbacks added to the submittedWithSuccess", ->
-						expect(callbacks.submitSuccessHappened).toHaveBeenCalledWith(form, successResponse())
+						expect(callbacks.submitSuccessHappened).toHaveBeenCalledWith(form, mocks.simpleSuccessResponse)

 					it "will re-enable the submit button", ->
 						expect(submitButton.disabled).toBeFalsy()
@@ -98,12 +90,11 @@
 				describe "and submission is not successful", ->

 					beforeEach ->
-						reqwestCallback = 'error'
-						reqwestResponse = mocks.errorResponse
+						reqwestSpy.andCallFake((params)-> params.error(mocks.errorXhr))
 						submitButton.click()

 					it "will call callbacks added to the submittedWithError", ->
-						expect(callbacks.submitErrorHappened).toHaveBeenCalledWith(form, errorResponse())
+						expect(callbacks.submitErrorHappened).toHaveBeenCalledWith(form, mocks.errorXhr)

 					it "will re-enable the submit button", ->
 						expect(submitButton.disabled).toBeFalsy()
@@ -127,12 +118,11 @@
 			expect(callbacks.validateErrorHappened).not.toHaveBeenCalled()

 		it "will no longer call the success callbacks", ->
-			reqwestResponse = mocks.simpleSuccessResponse
+			reqwestSpy.andCallFake((params)-> params.success(mocks.simpleSuccessResponse))
 			submitButton.click()
 			expect(callbacks.submitSuccessHappened).not.toHaveBeenCalled()

 		it "will no longer call the error callbacks", ->
-			reqwestCallback = 'error'
-			reqwestResponse = mocks.errorResponse
+			reqwestSpy.andCallFake((params)-> params.error(mocks.errorXhr))
 			submitButton.click()
 			expect(callbacks.submitErrorHappened).not.toHaveBeenCalled()


Don't run mathjax cleanup on boot

Don't run mathjax cleanup on boot

Sometimes mathjax isn't fully configured yet and it attempts to
access "this.queue" while it's still undefined

--- src/helpers/mathjax.coffee
+++ src/helpers/mathjax.coffee
@@ -59,7 +59,6 @@

   configuredCallback = ->
     window.MathJax.Hub.Configured()
-    cleanMathArtifacts()

   if window.MathJax?.Hub
     window.MathJax.Hub.Config(MATHJAX_CONFIG)


add comment about locking in redis migration

--- services/document-updater/app/coffee/RedisMigrationManager.coffee
+++ services/document-updater/app/coffee/RedisMigrationManager.coffee
@@ -97,6 +97,14 @@
 	exec: (callback) ->
 		# decide which client to use
 		project_id = getProjectId(@queueKey)
+		# Put a lock around finding and updating the queue to avoid time-of-check to
+		# time-of-use problems. When running in the "switch" phase we need a lock to
+		# guarantee the order of operations. (Example: docupdater A sees an old
+		# queue at t=t0 and pushes onto it at t=t1, project history clears the queue
+		# between t0 and t1, and docupdater B sees the empty queue, sets the
+		# migration flag and pushes onto the new queue at t2. Without a lock it's
+		# possible to have t2 < t1 if docupdater A is slower than B - then there
+		# would be entries in the old and new queues, which we want to avoid.)
 		LockManager.getLock project_id, (error, lockValue) =>
 			return callback(error) if error?
 			releaseLock = (args...) =>


Change rollout detection to account for changes in proportion preserving rolled out users

--- services/web/app/coffee/Features/Project/ProjectController.coffee
+++ services/web/app/coffee/Features/Project/ProjectController.coffee
@@ -257,7 +257,8 @@
 				timestamp = parseInt(user_id.toString().substring(0, 8), 16)
 				counter = parseInt(user_id.toString().substring(18, 24), 16)

-				if counter % 5000 != 0
+				rolloutProportion = 10
+				if counter % 1000 < rolloutProportion
 					# Don't show if user is not part of roll out
 					return cb(null, false)
 				userSignupDate = new Date(timestamp * 1000)


Stop propagation of input events to prevent react from doing extra work

React seems to be handling these events when they bubble to the root of
the document. We want to avoid wasting time on this.

--- src/input-component.coffee
+++ src/input-component.coffee
@@ -33,6 +33,7 @@
     not isEqual(newProps.style, @props.style)

   onInput: (e) ->
+    e.stopPropagation()
     valueCharCodes = punycode.ucs2.decode(@getDOMNode().value)
     valueLength = valueCharCodes.length
     replaceLastChar = valueLength is @lastValueLength


version tag

version tag

--- src/jasmine-fixture.coffee
+++ src/jasmine-fixture.coffee
@@ -1,5 +1,6 @@
 ###
-jasmine-fixture Makes injecting HTML snippets into the DOM easy & clean!
+jasmine-fixture @@VERSION@@
+Makes injecting HTML snippets into the DOM easy & clean!
 site: https://github.com/searls/jasmine-fixture
 ###
 (($) ->


Update Datastream view to ItemView

Update Datastream view to ItemView

--- src/scripts/workbench/views/datastream/show_view.js.coffee
+++ src/scripts/workbench/views/datastream/show_view.js.coffee
@@ -1,5 +1,5 @@
-class Workbench.Views.DatastreamShowView extends Backbone.View
-  template: JST["workbench/templates/datastream"]
+class Workbench.Views.DatastreamShowView extends Backbone.Marionette.ItemView
+  template: "workbench/templates/datastream"

   tagName: "li"
   className: "datastream"
@@ -25,14 +25,9 @@
         @latestView.setElement(@$("#latest_#{@model.id}")).render()
       )

-  remove: ->
+  onBeforeDestroy: ->
     @chartView.remove()
     @latestView.remove()
-    super()

-  render: ->
-    @$el.animate("min-height": 200).promise().done(=>
-      @$el.html(@template(@model.toJSON()))
-      @renderDeferred.resolve()
-    )
-    this
+  onRender: ->
+    @renderDeferred.resolve()


CADS-608 : Fix character count updates for employment page Pension / PayForThings / JobExpenses.

--- c3/app/assets/javascripts/s_employment/g_pensionAndExpenses.coffee
+++ c3/app/assets/javascripts/s_employment/g_pensionAndExpenses.coffee
@@ -1,12 +1,15 @@
 window.initEvents =(payPensionSchemeY, payPensionSchemeN, pensionExpenses,
                     payForThingsY,payForThingsN,payForThings,
                     haveExpensesForJobY, haveExpensesForJobN, jobExpenses) ->
+  $("#" + pensionExpenses).trigger("blur")
+  $("#" + payForThings).trigger("blur")
+  $("#" + jobExpenses).trigger("blur")

   if not $("#" + payPensionSchemeY).prop('checked')
     hidePensionExpenses(pensionExpenses)

   $("#" + payPensionSchemeY).on "click", ->
-    showPensionExpenses()
+    showPensionExpenses(pensionExpenses)

   $("#" + payPensionSchemeN).on "click", ->
     hidePensionExpenses(pensionExpenses)
@@ -15,7 +18,7 @@
     hidePayForThings(payForThings)

   $("#" + payForThingsY).on "click", ->
-    showPayForThings()
+    showPayForThings(payForThings)

   $("#" + payForThingsN).on "click", ->
     hidePayForThings(payForThings)
@@ -24,32 +27,32 @@
     hideExpensesForJob(jobExpenses)

   $("#" + haveExpensesForJobY).on "click", ->
-    showHaveExpensesForJob()
+    showHaveExpensesForJob(jobExpenses)

   $("#" + haveExpensesForJobN).on "click", ->
     hideExpensesForJob(jobExpenses)

-showPensionExpenses = ->
+showPensionExpenses = (pensionExpenses) ->
+  $("#" + pensionExpenses).trigger("blur")
   $("#pensionExpenses").slideDown(0).attr 'aria-hidden', 'false'

 hidePensionExpenses = (pensionExpenses) ->
-	emptyPensionExpenses = ->
-    	$("#" + pensionExpenses).val("")
-  	$("#pensionExpenses").slideUp(0, emptyPensionExpenses).attr 'aria-hidden', 'true', ->
+  $("#" + pensionExpenses).val("")
+  $("#pensionExpenses").slideUp(0).attr 'aria-hidden', 'true', ->

-showHaveExpensesForJob = ->
+showHaveExpensesForJob = (jobExpenses) ->
+  $("#" + jobExpenses).trigger("blur")
   $("#jobExpenses").slideDown(0).attr 'aria-hidden', 'false'

 hideExpensesForJob = (jobExpenses) ->
-	emptyExpensesForJob = ->
-   		$("#" + jobExpenses).val("")
-  $("#jobExpenses").slideUp(0, emptyExpensesForJob).attr 'aria-hidden', 'true', ->
+  $("#" + jobExpenses).val("")
+  $("#jobExpenses").slideUp(0).attr 'aria-hidden', 'true', ->

-showPayForThings = ->
+showPayForThings = (payForThings) ->
+  $("#" + payForThings).trigger("blur")
   $("#payForThings").slideDown(0).attr 'aria-hidden', 'false'

 hidePayForThings = (payForThings) ->
-	emptyPayForThings = ->
-    	$("#" + payForThings).val("")
-  $("#payForThings").slideUp(0, emptyPayForThings).attr 'aria-hidden', 'true', ->+  $("#" + payForThings).val("")
+  $("#payForThings").slideUp(0).attr 'aria-hidden', 'true', ->


client: stackwizard: removed scrollview class from config view

--- client/stacks/lib/views/stacks/onboarding/configurationview.coffee
+++ client/stacks/lib/views/stacks/onboarding/configurationview.coffee
@@ -26,6 +26,8 @@
     @tabView = new kd.TabView
       enableMoveTabHandle : no
       tabHandleContainer  : @tabHandleContainer
+
+    @tabView.unsetClass 'kdscrollview'

     @on 'PlusHandleClicked', @bound 'addPane'


Fix file uploads to work after error

Fix file uploads to work after error

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -322,6 +322,7 @@
   save_answer: (event) =>
     event.preventDefault()
     max_filesize = 2*1000*1000 #2MB
+    pre_can_upload_files = @can_upload_files
     if @child_state == 'initial'
       files = ""
       if @can_upload_files == true
@@ -353,6 +354,7 @@
             @find_assessment_elements()
             @rebind()
           else
+            @can_upload_files = pre_can_upload_files
             @gentle_alert response.error

       $.ajaxWithPrefix("#{@ajax_url}/save_answer",settings)


Remove debug code.

Remove debug code.

--- src/coffee/kopi/ui/animators.coffee
+++ src/coffee/kopi/ui/animators.coffee
@@ -20,7 +20,6 @@
     resetAnimation: -> this._animation = null

     _switch: (fromChild, toChild, options) ->
-      console.log "_switch", options
       cls = this.constructor
       self = this
       animateFn = ->


Add stub to fix unit tests

Add stub to fix unit tests

--- tests/unit_tests/test_chrome_stubs.coffee
+++ tests/unit_tests/test_chrome_stubs.coffee
@@ -59,6 +59,8 @@
     onHistoryStateUpdated:
       addListener: () ->
     onReferenceFragmentUpdated:
+      addListener: () ->
+    onCommitted:
       addListener: () ->

   windows:


Slightly more precise conversion to screen positions

--- src/lines-yardstick.coffee
+++ src/lines-yardstick.coffee
@@ -35,7 +35,9 @@
     return new Point(row, 0) unless lineNode? and line?

     iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT)
-    charIndex = 0
+    column = 0
+    previousColumn = 0
+    previousDistance = Infinity

     @tokenIterator.reset(line)
     while @tokenIterator.next()
@@ -57,19 +59,23 @@
           textNodeIndex = 0
           nextTextNodeIndex = textNodeLength

-        while nextTextNodeIndex <= charIndex
+        while nextTextNodeIndex <= column
           textNode = iterator.nextNode()
           textNodeLength = textNode.textContent.length
           textNodeIndex = nextTextNodeIndex
           nextTextNodeIndex = textNodeIndex + textNodeLength

-        indexWithinTextNode = charIndex - textNodeIndex
+        indexWithinTextNode = column - textNodeIndex
         left = @leftPixelPositionForCharInTextNode(lineNode, textNode, indexWithinTextNode)
-        break if left >= targetLeft
+        distance = Math.abs(targetLeft - left)

-        charIndex += charLength
+        return new Point(row, previousColumn) if distance > previousDistance

-    new Point(row, charIndex)
+        previousDistance = distance
+        previousColumn = column
+        column += charLength
+
+    new Point(row, column)

   pixelPositionForScreenPosition: (screenPosition, clip=true) ->
     screenPosition = Point.fromObject(screenPosition)


Fix a bug in raw tag parsing

Fix a bug in raw tag parsing

--- src/liquid/tags/raw.coffee
+++ src/liquid/tags/raw.coffee
@@ -3,16 +3,16 @@
 module.exports = class Raw extends Liquid.Block
   parse: (tokens) ->
     @nodelist or= []
-    @nodelist.pop() while nodelist.length() > 0
+    @nodelist.pop() while @nodelist.length > 0

     while tokens.length > 0
-      token = token.shift()
-      match = Liquid.FullToken.exec(token)
+      token = tokens.shift()
+      match = Liquid.Block.FullToken.exec(token)

-      if match and @blockDelimiter() == match[1]
+      if match and @blockDelimiter() is match[1]
         @endTag()
-        return
+        break
+      else if token.length > 0
+        @nodelist.push(token)

-    @nodelist.push(token) unless token.length == 0
-
-Liquid.Template.registerTag "raw", Raw+Liquid.Template.registerTag "raw", Raw


Fix tests

Fix tests

--- test/lib/Task/ActivityTask/Save/_3DCartSaveOrdersSpec.coffee
+++ test/lib/Task/ActivityTask/Save/_3DCartSaveOrdersSpec.coffee
@@ -76,7 +76,7 @@
     .then ->
       Commands.findOne(input.commandId)
       .then (command) ->
-        command.progressBars[0].total.should.be.equal(0)
+        should.not.exist(command.progressBars[0].total)
         command.progressBars[0].current.should.be.equal(1)

   it "should update existing objects", ->


cron modify only 12 PM

cron modify only 12 PM

--- scripts/cron-sample.coffee
+++ scripts/cron-sample.coffee
@@ -5,7 +5,7 @@
 moment = require 'moment-timezone'

 module.exports = (robot) ->
-  new cron '0 0 */1 * * *', () ->
+  new cron '0 0 12 * * *', () ->
     robot.send {room: '#bot_test'}, moment.tz('Asia/Tokyo').format 'Botのcronで時報 HH時をお知らせします。'
   ,null, true, 'Asia/Tokyo'


Start the remoteUpdater before loading content

--- scripts/gh-book/app.coffee
+++ scripts/gh-book/app.coffee
@@ -11,9 +11,10 @@
   'cs!gh-book/opf-file'
   'cs!gh-book/binary-file'
   'cs!gh-book/welcome-sign-in'
+  'cs!gh-book/remote-updater'
   'less!styles/main'
   'less!gh-book/gh-book'
-], ($, _, Backbone, Marionette, logger, session, allContent, mediaTypes, XhtmlFile, OpfFile, BinaryFile, WelcomeSignInView) ->
+], ($, _, Backbone, Marionette, logger, session, allContent, mediaTypes, XhtmlFile, OpfFile, BinaryFile, WelcomeSignInView, remoteUpdater) ->

   # Stop logging.
   logger.stop()
@@ -139,6 +140,7 @@
               repoName: repoName
               branch:   branch

+            remoteUpdater.stop()
             allContent.reload()
             @goWorkspace()

@@ -146,14 +148,20 @@
           # TODO: Move this into the controller
           goWorkspace: () ->
             setDefaultRepo()
-            allContent.load()
-            .fail((err) => alert('There was a problem loading the repo. Are you pointing to a valid book?'))
-            .done () => controller.goWorkspace()
+            remoteUpdater.start()
+            .fail((err) => alert('There was a problem starting the remote updater. Are you pointing to a valid book?'))
+            .done () =>
+              allContent.load()
+              .fail((err) => alert('There was a problem loading the repo. Are you pointing to a valid book?'))
+              .done () => controller.goWorkspace()
           goEdit: (id)    ->
             setDefaultRepo()
-            allContent.load()
-            .fail((err) => alert('There was a problem loading the repo. Are you pointing to a valid book?'))
-            .done () => controller.goEdit(id)
+            remoteUpdater.start()
+            .fail((err) => alert('There was a problem starting the remote updater. Are you pointing to a valid book?'))
+            .done () =>
+              allContent.load()
+              .fail((err) => alert('There was a problem loading the repo. Are you pointing to a valid book?'))
+              .done () => controller.goEdit(id)

         Backbone.history.start


Base PaneContainer::getActivePane on the model's active pane

--- src/pane-container.coffee
+++ src/pane-container.coffee
@@ -30,8 +30,9 @@
     @subscribe @model, 'surrendered-focus', @onSurrenderedFocus

   viewForModel: (model) ->
-    viewClass = model.getViewClass()
-    model._view ?= new viewClass(model)
+    if model?
+      viewClass = model.getViewClass()
+      model._view ?= new viewClass(model)

   serializeParams: ->
     model: @model.serialize()
@@ -98,7 +99,7 @@
     @find('.pane:has(:focus)').view()

   getActivePane: ->
-    @find('.pane.active').view() ? @find('.pane:first').view()
+    @viewForModel(@model.activePane)

   getActivePaneItem: ->
     @model.activePaneItem


We've moved phabricator over to https!  So let's save a redirect.

Auditors: james

Test plan:
None

--- scripts/phabricator.coffee
+++ scripts/phabricator.coffee
@@ -21,7 +21,7 @@
     if robot.fromSelf msg
         return
     robot.fancyMessage({
-        msg: "http://phabricator.khanacademy.org/" + msg.match[2],
+        msg: "https://phabricator.khanacademy.org/" + msg.match[2],
         room: msg.envelope.room,
         from: "Phabot Rabbit",
         message_format: "text"


- Make titleLinks clickable in more tags window

--- client/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
+++ client/app/Applications/Topics.kdapplication/Views/TopicsListItemView.coffee
@@ -167,17 +167,38 @@
     @addSubView @skillList, '.profile-meta'

   _addLocationsList: ->
-    @locationList = new TopicsLocationView {}, @getData().locations
+    @locationList = new TopicsLocationView {}, @getData().locations
     @addSubView @locationList, '.personal'

 class ModalTopicsListItem extends TopicsListItemView
+
+  constructor:(options,data)->
+
+    super options,data
+
+    @titleLink = new KDCustomHTMLView
+      tagName     : 'a'
+      attributes  :
+        href      : '#'
+        class     : 'ttag'
+      pistachio   : '{{#(title)}}'
+    , data
+
+    @titleLink.registerListener
+      KDEventTypes  : 'click'
+      listener      : @
+      callback      : (pubInst, event)=>
+        tag = @getData()
+        appManager.tell "Topics", "createContentDisplay", tag
+        event.stopPropagation()
+        no

   pistachio:->
     """

-        {span.ttag{> @titleLink}}
+        {{> @titleLink}}


downcase header name

downcase header name

--- src/podroller/core.coffee
+++ src/podroller/core.coffee
@@ -256,10 +256,10 @@
         # -- make a request to the preroll server -- #

         console.log "making a preroll request"
-
+
         opts =
             host:       @options.preroll.server
-            path:       [@options.preroll.path,@options.preroll.key,key,"?static=1" if req.headers['Consistent-Preroll']?].join("/")
+            path:       [@options.preroll.path,@options.preroll.key,key,"?static=1" if req.headers['consistent-preroll']?].join("/")

         conn = req.connection


coffeelint fixing

coffeelint fixing

--- server/src/Component/Builtin/Database/CouchMuffin.coffee
+++ server/src/Component/Builtin/Database/CouchMuffin.coffee
@@ -136,7 +136,7 @@
     #   @param {number} [options.replicate_to=0]
     #   Ensures this operation is replicated to this many nodes
     # @param {function} callback Called after the operation (error, result)
-    # @param {function} callback Called when the operation is completed (error, result)
+    # @param {function} callback Called when the operation is completed (error, result)
     save: (params, callback) ->
         @_method = 'save'
         id = params.id


added singlePost

added singlePost

--- server/publications.coffee
+++ server/publications.coffee
@@ -1,3 +1,6 @@
+Meteor.publish 'singlePost', (slug) ->
+  Post.find({slug: slug})
+
 Meteor.publish 'posts', ->
   Post.find()


<debug> Test - Adaptating createDriver method to new requirements.

--- tests/helpers.coffee
+++ tests/helpers.coffee
@@ -73,6 +73,7 @@

                 fileData =
                     path: os.tmpdir() + "/" + extName
+                    originalFilename : extName

                 Driver.create fileData, (err, driver) ->
                     store.driver = driver


First argument is an event, then eventKey

--- src/components/performance-forecast/teacher-student.cjsx
+++ src/components/performance-forecast/teacher-student.cjsx
@@ -30,7 +30,7 @@

   bindStore: PerformanceForecast.TeacherStudent.store

-  onSelectStudent: (roleId) ->
+  onSelectStudent: (ev, roleId) ->
     {courseId} = @props
     PerformanceForecast.TeacherStudent.actions.load(courseId, {roleId})
     @setState({roleId})


Full name for Administrator

Full name for Administrator

--- src/gui/tabs/info/index.html.coffee
+++ src/gui/tabs/info/index.html.coffee
@@ -81,4 +81,4 @@
       td @n.UserDomain
     row ->
       th 'Role'
-      td if @a then 'Admin' else 'User'
+      td if @a then 'Administrator' else 'User'


incoming request handler: handle authentication failure

--- src/incoming-message-handler.coffee
+++ src/incoming-message-handler.coffee
@@ -1,4 +1,14 @@
 handleAuth = require './auth/auth.coffee'
+
+mungeCallbacks = (callbacks, n) ->
+  # FIXME: this is an ugly hack; there must be a better way to implement it:
+  for own k, c of callbacks
+    if (c.join '.') is '0.responseCallback'
+      callbacks[k] = [n]
+    if c[1] is 'withArgs'
+      # since we're rewriting the protocol for the withArgs case, we need to remove everything up to withArgs
+      callbacks[k] = c.slice 2
+  callbacks

 module.exports = (proto, message) ->

@@ -15,25 +25,20 @@
   if args.length > 0
     [{ withArgs, responseCallback, kite, authentication: auth }] = args

-  handleAuth(auth, @key).then (token) ->
+  if !withArgs? and !responseCallback?
+    @emit 'debug', "Handling a normal dnode message"
+    return proto.handle req

-    if !withArgs? and !responseCallback?
-      # it's a normal dnode protocol message.
-      proto.handle req
-      return
+  @emit 'debug', "Authenticating request"

-    # it's a kite protocol message.
+  handleAuth(method, auth, @key).then (token) =>
+    @emit 'debug', "Authentication passed"
+
     withArgs ?= []

     withArgs = [withArgs]  unless Array.isArray withArgs

-    # FIXME: this is an ugly hack; there must be a better way to implement it:
-    for own k, c of callbacks
-      if (c.join '.') is '0.responseCallback'
-        callbacks[k] = [withArgs.length]
-      if c[1] is 'withArgs'
-        # since we're rewriting the protocol for the withArgs case, we need to remove everything up to withArgs
-        callbacks[k] = c.slice 2
+    mungeCallbacks callbacks, withArgs.length

     proto.handle {
       method
@@ -41,3 +46,15 @@
       links
       callbacks
     }
+
+  .catch (err) =>
+    @emit 'debug', "Authentication failed"
+
+    mungeCallbacks callbacks, 1
+
+    proto.handle {
+      method: 'kite.echo'
+      arguments: [err, responseCallback]
+      links
+      callbacks
+    }


Resolved #6, #8 and tests for stops admin

--- src/tests/cucumber/features/step_definitions/uc41.coffee
+++ src/tests/cucumber/features/step_definitions/uc41.coffee
@@ -11,5 +11,5 @@
     },
       roles: ['root']

-  @Given /^Stop "([^"]*)" is created$/, ()->
-    server.call "assureStop", "testStop-1"
+  @Given /^Stop "([^"]*)" is created$/, (stopTitle)->
+    server.call "assureStop", stopTitle


change acceptance test to use redis history client

--- test/acceptance/coffee/ApplyingUpdatesToADocTests.coffee
+++ test/acceptance/coffee/ApplyingUpdatesToADocTests.coffee
@@ -4,7 +4,7 @@
 expect = chai.expect
 async = require "async"
 Settings = require('settings-sharelatex')
-rclient_web = require("redis-sharelatex").createClient(Settings.redis.web)
+rclient_history = require("redis-sharelatex").createClient(Settings.redis.history)
 rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
 Keys = Settings.redis.documentupdater.key_schema

@@ -49,10 +49,10 @@
 				done()

 		it "should push the applied updates to the track changes api", (done) ->
-			rclient_web.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
+			rclient_history.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
 				throw error if error?
 				JSON.parse(updates[0]).op.should.deep.equal @update.op
-				rclient_web.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
+				rclient_history.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
 					throw error if error?
 					result.should.equal 1
 					done()
@@ -82,9 +82,9 @@
 				done()

 		it "should push the applied updates to the track changes api", (done) ->
-			rclient_web.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
+			rclient_history.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
 				JSON.parse(updates[0]).op.should.deep.equal @update.op
-				rclient_web.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
+				rclient_history.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
 					result.should.equal 1
 					done()

@@ -127,12 +127,12 @@
 						done()

 			it "should push the applied updates to the track changes api", (done) ->
-				rclient_web.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
+				rclient_history.lrange "UncompressedHistoryOps:#{@doc_id}", 0, -1, (error, updates) =>
 					updates = (JSON.parse(u) for u in updates)
 					for appliedUpdate, i in @updates
 						appliedUpdate.op.should.deep.equal updates[i].op

-					rclient_web.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
+					rclient_history.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, result) =>
 						result.should.equal 1
 						done()


Disabled failing tests that were browserify-specific

--- test/spec/plugins/localization_and_defaults.tests.coffee
+++ test/spec/plugins/localization_and_defaults.tests.coffee
@@ -237,8 +237,10 @@
     assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
     done()

-  # TODO: fix these tests in browserify
-  it.skip '2. internals test (Coffeescript inheritance)', (done) ->
+  it '2. internals test (Coffeescript inheritance)', (done) ->
+    # TODO: fix these tests in browserify
+    return done() if typeof process != 'undefined' and process.browser # TODO: skip on AMD until have time to troubleshoot
+
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager

@@ -309,8 +311,10 @@
     assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
     done()

-  # TODO: fix these tests in browserify
-  it.skip '3. internals test (Javascript inheritance)', (done) ->
+  it '3. internals test (Javascript inheritance)', (done) ->
+    # TODO: fix these tests in browserify
+    return done() if typeof process != 'undefined' and process.browser # TODO: skip on AMD until have time to troubleshoot
+
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager

@@ -874,8 +878,10 @@
       assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
       done()

-  # TODO: fix these tests in browserify
   it.skip '3. internals test (Coffeescript inheritance)', (done) ->
+    # TODO: fix these tests in browserify
+    return done() if typeof process != 'undefined' and process.browser # TODO: skip on AMD until have time to troubleshoot
+
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager


Loads saved port mapping from moirai

Loads saved port mapping from moirai

--- src/coffee/hardware.coffee
+++ src/coffee/hardware.coffee
@@ -92,12 +92,33 @@
     hw_driver_list_ports: (ports) ->
         Hardware.ports = (JSON.parse(port) for port in ports)
         $('#hw-setup-pinmap').removeAttr 'unresolved'
+        Connection.send 'HW_PORT_MAPPING_GET'
+
+    hw_port_mapping_get: (ms) ->
+        $('#hw-setup-pinmap tbody').html ''
+        ms = (JSON.parse(m) for m in ms)
+        ms.sort( (a, b) -> a.map - b.map )
+        for m in ms
+            hw_add_port()
+            row = $($('#hw-setup-pinmap tbody tr').last())
+            select = $(row.find('.hw-port-name'))
+            if select.get(0).MaterialTextfield?
+                select.get(0).MaterialTextfield.change m.id
+            else
+                select.val m.id
+            row.find('.hw-port-alias').get(0).MaterialTextfield.change m.alias
+            $(row.find(".hw-port-digana input[value=#{m.digital}]")).parent().get(0).MaterialRadio.check()
+            $(row.find(".hw-port-inout input[value=#{m.input}]")).parent().get(0).MaterialRadio.check()
+            if m.pwn
+                $(row.find(".hw-port-pwm")).get(0).MaterialSwitch.on()
+            else
+                $(row.find(".hw-port-pwm")).get(0).MaterialSwitch.off()
+            row.find('.hw-port-value').get(0).MaterialTextfield.change m.value
+        enforce_port_integrity row

     hw_driver_is_connected: (status) ->
         status = JSON.parse(status)
-        console.log status
         if status
-            console.log 'listing_ports'
             Connection.send 'HW_DRIVER_LIST_PORTS'

 loading_tag = '

'


syntax

syntax

--- src/replay/catalog.coffee
+++ src/replay/catalog.coffee
@@ -136,7 +136,7 @@
   buffer = File.readFileSync(filename)
   parts = buffer.toString('utf8').split('\n\n')
   if parts.length > 2
-    parts0 = new Buffer(parts[0], 'utf8'),
+    parts0 = new Buffer(parts[0], 'utf8')
     parts1 = new Buffer(parts[1], 'utf8')
     body = buffer.slice(parts0.length + parts1.length + 4)
   return [parts[0], parts[1], body || '']


using buffer length for calculating respone body

--- src/replay/catalog.coffee
+++ src/replay/catalog.coffee
@@ -136,7 +136,9 @@
   buffer = File.readFileSync(filename)
   parts = buffer.toString('utf8').split('\n\n')
   if parts.length > 2
-    body = buffer.slice(parts[0].length + parts[1].length + 4)
+    parts0 = new Buffer(parts[0], 'utf8'),
+    parts1 = new Buffer(parts[1], 'utf8')
+    body = buffer.slice(parts0.length + parts1.length + 4)
   return [parts[0], parts[1], body || '']

 # Parse headers from header_lines.  Optional argument `only` is an array of


refactor(macModal): Updated mac-modal service to work with jqLite

--- src/services/modal.coffee
+++ src/services/modal.coffee
@@ -94,7 +94,8 @@
             viewScope              = $rootScope.$new()
             viewScope.modal        = this
             viewScope.closeOverlay = ($event) =>
-              if options.overlayClose and angular.element($event.target).hasClass("modal-overlay")
+              if options.overlayClose and
+                  angular.element($event.target).hasClass("modal-overlay")
                 @hide()

             if options.controller
@@ -103,8 +104,9 @@

             angular.extend options.attributes, {id}
             element = angular.element(@modalTemplate).attr options.attributes
-            angular.element(".modal-content-wrapper", element).html template
-            angular.element("body").append $compile(element) viewScope
+            wrapper = angular.element element[0].getElementsByClassName("modal-content-wrapper")
+            wrapper.html template
+            angular.element(document.body).append $compile(element) viewScope

             showModal element

@@ -140,18 +142,21 @@
       element = modalObject.element
       options = modalObject.options

-      modal  = $(".modal", element).attr "style", ""
+      modal  = angular.element(element[0].getElementsByClassName "modal").attr "style", ""
       height = modal.outerHeight()
       width  = modal.outerWidth()

       css =
-        if $(window).height() > height
+        if angular.element(window).height() > height
           marginTop:  -height / 2
         else
           top: options.topOffset
       css.marginLeft = -width / 2

-      modal.css css
+      angular.forEach css, (value, key) ->
+        if not isNaN(+value) and angular.isNumber +value
+          value = "#{value}px"
+        modal.css key, value

     #
     # @name hide
@@ -189,10 +194,10 @@
       options       = @opened.options

       if options.keyboard
-        $(document)[action] "keydown", escapeKeyHandler
+        angular.element(document)[action] "keydown", escapeKeyHandler

       if options.resize
-        $(window)[action] "resize", resizeHandler
+        angular.element(window)[action] "resize", resizeHandler

     #
     # @name register