Remove superfluous code

Remove superfluous code

--- spec/generalStepDefs.spec.coffee
+++ spec/generalStepDefs.spec.coffee
@@ -106,11 +106,11 @@
             Test: ->
               stubPage
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       afterEach ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       it 'should set the currentPage on the World', ->
         executeStep 'I am on the "Test" page', ->
@@ -228,11 +228,11 @@
               clear: sinon.stub()
               sendKeys: sinon.stub().resolves()
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       afterEach ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       it 'should clear and send the text to the field', ->
         executeStep 'I type "First" in the "Name" field', ->
@@ -283,11 +283,11 @@
             searchButton:
               click: sinon.stub()
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       afterEach ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       it 'should click the element', ->
         executeStep 'I click the "Search" button', ->
@@ -338,11 +338,11 @@
         world.currentPage =
           timeZoneSelect: $ 'select#timezone'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       beforeEach ->
         browser.driver.executeScript "fixtures.set('
@@ -383,11 +383,11 @@

       before ->
         world = new World ->
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       it 'should succeed if the page title matches the supplied title', ->
         executeStep 'the title should equal "Protractor Integration Test Page"', ->
@@ -420,11 +420,11 @@
         world.currentPage =
           button: $ 'button#testButton'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with an active element', ->

@@ -482,11 +482,11 @@
         world.currentPage =
           button: $ 'button#testButton'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with element present', ->

@@ -543,11 +543,11 @@
             Test: ->
               stubPage
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       afterEach ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       it 'should set the currentPage on the World', ->
         executeStep 'I should be on the "Test" page'
@@ -590,11 +590,11 @@
           testSpan: $ 'span#testSpan'
           testInput: $ 'input#testInput'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with a span', ->

@@ -653,11 +653,11 @@
         world.currentPage =
           timeZoneSelect: $ 'select#timezone'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       beforeEach ->
         browser.driver.executeScript "fixtures.set('
@@ -703,11 +703,11 @@
         world.currentPage =
           testSpan: element(By.css 'span#testSpan')
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with the element displayed', ->

@@ -787,11 +787,11 @@
         world.currentPage =
           testInput: $ 'input#testInput'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       beforeEach ->
         browser.driver.executeScript "fixtures.set('<input id=\"testInput\" placeholder=\"Test Placeholder\" />');"
@@ -847,11 +847,11 @@
         world.currentPage =
           testButton: $ 'button#testButton'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with enabled button', ->

@@ -915,11 +915,11 @@
         world.currentPage =
           timeZoneSelect: $ 'select#timezone'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       beforeEach ->
         browser.driver.executeScript "fixtures.set('
@@ -968,11 +968,11 @@
         world.currentPage =
           testCheckbox: $ 'input#testCheckbox'
         sandbox.stub(supportCodeLibrary, 'instantiateNewWorld').returns world
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       after ->
         sandbox.restore()
-        scenarioRunner = initScenarioRunner()
+        initScenarioRunner()

       describe 'with a selected checkbox', ->


Remove unnecessary commas

Remove unnecessary commas


Link to the application docs page

Link to the application docs page

And maybe to the channel as well (not sure if that works).

--- scripts/govuk_app_owners.coffee
+++ scripts/govuk_app_owners.coffee
@@ -10,4 +10,4 @@
       .get() (err, response, body) ->
         if response.statusCode == 200
           data = JSON.parse(body)
-          res.reply "#{application} is owned by #{data.team}"
+          res.reply "<#{data.links.html_url>}|#{data.app_name}> is owned by <#{data.team}>"


fix too many commas when building dot-source for taskOverview; fixes #343

--- app/assets/javascripts/admin/views/task/task_overview_view.coffee
+++ app/assets/javascripts/admin/views/task/task_overview_view.coffee
@@ -116,17 +116,17 @@
       lastActivityDays = moment().diff(user.lastActivity, 'days')
       color = @colorJet(Math.min(50, lastActivityDays) * (128 / 50) + 128)

-      @quoted(userName) + " [id="+ @quoted(user.id) + ", shape=box, fillcolor=" + @quoted(color) + "];"
-    )
-
-    svgTaskTypes = ''
-    svgProjects = ''
+      @quoted(userName) + " [id="+ @quoted(user.id) + ", shape=box, fillcolor=" + @quoted(color) + "]"
+    ).join(";")
+
+    svgTaskTypes = ""
+    svgProjects = ""

     if @doDrawTaskTypes()
-      svgTaskTypes = taskTypes.map( (taskType) => @quoted(taskType.summary) + ";")
+      svgTaskTypes = taskTypes.map( (taskType) => @quoted(taskType.summary)).join(";")

     if @doDrawProjects()
-      svgProjects = projects.map( (project) => @quoted(project.name) + ";" )
+      svgProjects = projects.map( (project) => @quoted(project.name)).join(";")

     svgTaskTypes + svgUsers + svgProjects
@@ -134,30 +134,30 @@

   buildEdges : (userInfos) ->

-    svgTaskTypeEdges = ''
-    svgFutureTaskTypesEdges = ''
-    svgProjectEdges = ''
+    svgTaskTypeEdges = ""
+    svgFutureTaskTypesEdges = ""
+    svgProjectEdges = ""

     if @doDrawTaskTypes()

       svgTaskTypeEdges = userInfos.map( (userInfo) =>
         { user, taskTypes } = userInfo
         taskTypes.map( (taskType) => @edge(user.name, taskType.summary))
-      )
+      ).join(";")

       svgFutureTaskTypesEdges  = "edge [ color=blue ];"
       svgFutureTaskTypesEdges += userInfos.map( (userInfo) =>
         { user, futureTaskType } = userInfo
         if(futureTaskType)
           @edge(user.name, futureTaskType.summary)
-      )
+      ).join(";")

     if @doDrawProjects()
       svgProjectEdges = userInfos.map( (userInfo) =>
         { user, projects } = userInfo
         projects.map( (project) => @edge(user.name, project.name))
-      )
+      ).join(";")

     svgTaskTypeEdges + svgProjectEdges + svgFutureTaskTypesEdges
@@ -225,5 +225,5 @@

   quoted : (str) -> '"' + str + '"'

-  edge : (a, b) -> @quoted(a) + "->" + @quoted(b) + ";"
-
+  edge : (a, b) -> @quoted(a) + "->" + @quoted(b)
+


Use an alert instead of the access_denied template

--- lib/router.coffee
+++ lib/router.coffee
@@ -34,13 +34,13 @@
         template: "editPatient"
         data: -> Patients.findOne @params._id

-requireLogin = (pause) ->
+requireLogin = ->
     if not Meteor.user()
         if Meteor.loggingIn()
             @render @loadingTemplate
         else
-            @render "accessDenied"
-        pause()
+            Alerts.add "Please log in to proceed."
+            @redirect "/"

 Router.onBeforeAction "loading"
 Router.onBeforeAction requireLogin, except: ["home", "accessDenied"]


cfp work

cfp work

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -91,8 +91,21 @@
       items: AgendaItem.all()
     onAfterAction: -> document.title = "Agenda | Reversim Summit 2015"

-  @route 'proposal1', _.extend(path: '/proposal/:id/:title*', proposalRouteConfig)
-  @route 'proposal2', _.extend(path: '/proposal/:id', proposalRouteConfig)
+#  @route 'proposal1', _.extend(path: '/proposal/:id/:title*', proposalRouteConfig)
+#  @route 'proposal2', _.extend(path: '/proposal/:id', proposalRouteConfig)
+
+  @route 'proposal',
+    path: '/proposal/:id'
+    waitOn: ->
+      Meteor.subscribe('proposals', {_id: @params.id})
+    template: 'proposal'
+    notFoundTemplate: 'notFound'
+    data: ->
+      proposal = Proposal.find(@params.id)
+      if not proposal then return null
+      document.title = "#{proposal.title} | Reversim Summit 2015"
+      {page: 'proposal', proposal: proposal}
+

   @route 'proposals',
     path: '/proposals/:limit?'
@@ -249,7 +262,7 @@
 proposalRouteConfig =
   waitOn: ->
     Meteor.subscribe('proposals', {_id: @params.id})
-  tempalte: 'proposal'
+  template: 'proposal'
   notFoundTemplate: 'notFound'
   data: ->
     proposal = Proposal.find(@params.id)


:eggplant::peach:

:eggplant::peach:

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -7,6 +7,9 @@
 Comic = require './comic'

 ## Application routes
+app.get '/dickbutt', (request, response) ->
+  response.redirect('/static/db.jpg')
+
 app.get '/', (request, response) ->
   Comic.latest (err, comic) =>
     # TODO: Better error handling


Small javascript bugfix.

Small javascript bugfix.

--- app/assets/javascripts/spree/backend/product_properties.js.coffee
+++ app/assets/javascripts/spree/backend/product_properties.js.coffee
@@ -15,7 +15,7 @@

   categoryDeleted: (category) ->
     category.node.remove()
-    @categories = @category.filter (c) -> c isnt category
+    @category_editors = @category_editors.filter (c) -> c isnt category

   fetchCategories: (payload) ->
     $.each payload, (index, item) =>


Make quotes consistent

Make quotes consistent

--- app/assets/javascripts/spree/backend/product_properties.js.coffee
+++ app/assets/javascripts/spree/backend/product_properties.js.coffee
@@ -16,7 +16,7 @@
     @node.sortable
       handle: '.category-handle',
       placeholder: 'ui-sortable-placeholder',
-      items: ".js-category-node"
+      items: '.js-category-node'
       start: (event, ui) ->
         ui.placeholder.height(ui.item.height())
         ui.placeholder.html "

"
@@ -38,17 +38,17 @@
     $.ajax
       type: 'POST'
       dataType: 'json'
-      url: "/api/property_categories"
+      url: '/api/property_categories'
       headers: 'x-spree-token': Spree.api_key
       data: payload

       success: (data) =>
-        show_flash("success", data.flash)
+        show_flash('success', data.flash)
       error: (data) =>
         if data.flash
-          show_flash("error", data.flash)
+          show_flash('error', data.flash)
         else
-          show_flash("error", "There was an error updating your properties.")
+          show_flash('error', 'There was an error updating your properties.')

   serialize: ->
     @category_editors.map (c) -> c.serialize()
@@ -57,7 +57,7 @@
   constructor: (@parent, @category) ->
     @properties = []

-    categoryEditorTemplate = _.template($("#category-editor-template").html())
+    categoryEditorTemplate = _.template($('#category-editor-template').html())
     @node = $("
").html(categoryEditorTemplate(category_editor: this))
     @parent.append @node

@@ -65,7 +65,7 @@
     @add_property_button = @node.find '.js-add-property'
     @property_rows = @node.find 'tbody'

-    @node.find("tbody").sortable
+    @node.find('tbody').sortable
       handle: '.handle',
       update: =>
         @sortProperties()
@@ -88,7 +88,7 @@
       @parent.trigger 'deleteCategory', this

   name: ->
-    @node.find(".js-cat-name").val()
+    @node.find('.js-cat-name').val()

   addProperty: (property) ->
     @properties.push new ProductPropertyEditor(this, @property_rows, property)
@@ -109,8 +109,8 @@

 class ProductPropertyEditor
   constructor: (@category_editor, @parent, @property) ->
-    propertyEditorTemplate = _.template($("#property-editor-template").html())
-    @node = $("
").html(propertyEditorTemplate(property_editor: this))
+    propertyEditorTemplate = _.template($('#property-editor-template').html())
+    @node = $('
').html(propertyEditorTemplate(property_editor: this))
     @parent.append @node

     @delete_button = @node.find '.js-delete-property'
@@ -119,10 +119,10 @@
       @parent.trigger 'deleteProperty', this

   key: ->
-    @node.find(".js-key").val()
+    @node.find('.js-key').val()

   value: ->
-    @node.find(".js-val").val()
+    @node.find('.js-val').val()

   serialize: ->
     { key: @key(), value: @value() }


Update deprecated ws server method

Update deprecated ws server method

--- lib/server.coffee
+++ lib/server.coffee
@@ -18,7 +18,7 @@
         else if @latex.server.openTab
           @latex.viewer.openViewerNewWindow()

-    @ws = ws.createServer server: @http
+    @ws = new ws.Server server: @http
     @ws.on "connection", (ws) =>
       ws.on "message", (msg) => @latex.viewer.wsHandler(ws, msg)
       ws.on "close", () => @latex.viewer.wsHandler(ws, '{"type":"close"}')


Nodejs Web Server: remove cookie domain

Nodejs Web Server: remove cookie domain

--- servers/lib/server/client.coffee
+++ servers/lib/server/client.coffee
@@ -31,9 +31,8 @@
     req.pendingCookies or= {}
     req.pendingCookies._csrf = csrfToken

-    domain  = '.koding.com'
     expires = new Date Date.now() + maxAge
-    res.cookie '_csrf', csrfToken, { expires, secure, domain }
+    res.cookie '_csrf', csrfToken, { expires, secure }

 generateFakeClientFromReq = (req, res, callback) ->


Remove Python 'pass' from Powershell keywords.

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -93,7 +93,7 @@
   }
   {
     'comment': 'keywords that alter flow from within a block'
-    'match': '\\b(?i:(break|continue|pass|throw|return|exit|yield))\\b'
+    'match': '\\b(?i:(break|continue|throw|return|exit|yield))\\b'
     'name': 'keyword.control.flow.powershell'
   }
   {


login prompt back in

login prompt back in

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -66,6 +66,8 @@
     # # if mobile device detected, go to verify mode
     # if window.matchMedia("(min-device-width: 320px)").matches and window.matchMedia("(max-device-width: 480px)").matches
     #   location.hash = "#/verify"
+
+    @loggedOutClassificationCount = 0

     window.classifier = @
     @recordedClickEvents = [] # array to store all click events
@@ -550,7 +552,15 @@
           $('.mini-course-option').prop 'checked', @courseEnabled

     # SEND CLASSIFICATION
-    @course.incrementCount()
+
+    if User.current?
+      @course.incrementCount()
+    else
+      @loggedOutClassificationCount += 1
+      if @loggedOutClassificationCount%5 == 0
+        @course.showPrompt()
+
+
     # console.log 'YOU\'VE MARKED ', @course.count, ' LIGHT CURVES!'

     @classification.annotate


Straight lines during drawing.

Straight lines during drawing.

--- packages/landsofillusions-assets/spriteeditor/tools/pencil.coffee
+++ packages/landsofillusions-assets/spriteeditor/tools/pencil.coffee
@@ -45,8 +45,8 @@

       else
         # Apply locked coordinate.
-        if lockedCoordinate = @lockedCoordinate()
-          currentPixelCoordinates = _.extend {}, currentPixelCoordinates, lockedCoordinate
+        if @drawStraight()
+          currentPixelCoordinates = _.extend {}, currentPixelCoordinates, @lockedCoordinate()

         # Draw bresenham line from last stroke coordinates (reset after end of stroke).
         bresenhamLine lastStrokeCoordinates.x, lastStrokeCoordinates.y, currentPixelCoordinates.x, currentPixelCoordinates.y, (x, y) => pixels.push {x, y}
@@ -96,6 +96,7 @@

     if event.which is AC.Keys.shift
       if @mouseState.leftButton
+        @lockedCoordinate null
         @drawStraight true

       else
@@ -116,10 +117,16 @@
   onMouseMove: (event) ->
     super arguments...

-    @currentPixelCoordinates
+    currentPixelCoordinates = @currentPixelCoordinates()
+
+    newPixelCoordinates =
       x: @mouseState.x
       y: @mouseState.y

+    return if EJSON.equals currentPixelCoordinates, newPixelCoordinates
+
+    @currentPixelCoordinates newPixelCoordinates
+
     @applyPencil()

   onMouseUp: (event) ->
@@ -127,6 +134,8 @@

     # End stroke.
     @lastStrokeCoordinates null
+
+    @drawStraight false

   applyPencil: ->
     return unless @mouseState.leftButton
@@ -139,14 +148,34 @@
     layerOrigin =
       x: layer?.origin?.x or 0
       y: layer?.origin?.y or 0
+
+    pixels = @pixels()
+    drawStraight = @drawStraight()
+
+    if drawStraight
+      lastPixelCoordinates = @lastPixelCoordinates()
+      lastNewPixel = _.last pixels
+
+      unless lockedCoordinate = @lockedCoordinate()
+        # Calculate which direction to lock to.
+        if lastNewPixel.x is lastPixelCoordinates.x
+          # Lock to vertical straight lines.
+          lockedCoordinate = x: lastPixelCoordinates.x
+
+        else
+          lockedCoordinate = y: lastPixelCoordinates.y
+
+        @lockedCoordinate lockedCoordinate

     for absolutePixel in @pixels()
       absoluteCoordinates = _.pick absolutePixel, ['x', 'y']
       @lastPixelCoordinates absoluteCoordinates
       @lastStrokeCoordinates absoluteCoordinates
-
-      # Create the new pixel in relative coordinates.
+
       pixel = _.clone absolutePixel
+      _.extend pixel, lockedCoordinate if drawStraight
+
+      # Pixel must be in relative coordinates.
       pixel.x -= layerOrigin.x
       pixel.y -= layerOrigin.y


fail when selenium unit tests fail

fail when selenium unit tests fail

--- test/run_selenium.coffee
+++ test/run_selenium.coffee
@@ -109,6 +109,11 @@
     .title().then (title) ->
       expect(title).to.be('lead.js test runner')
       browser.executeAsync('run(arguments[0])')
+        .then (results) ->
+          if results.failures > 0
+            Q.reject results
+          else
+            results

 module.exports = {
   start_tunnel


fix subtle timing bug

fix subtle timing bug

--- src/Landscape.coffee
+++ src/Landscape.coffee
@@ -440,7 +440,8 @@
   handle:(remote)->
     switch remote.action
       when 'spawn'
-        @spawn(remote.id, remote.name)
+        unless @players[remote.id]?
+          @spawn(remote.id, remote.name)
       when 'bail'
         @delPlayer(remote.id)
       when 'jump'


ContextMenuManager is Extended

ContextMenuManager is Extended

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -5,7 +5,7 @@
 CSON = require 'season'
 fs = require 'fs-plus'

-# Public: Provides a registry for commands that you'd like to appear in the
+# Extended: Provides a registry for commands that you'd like to appear in the
 # context menu.
 #
 # An instance of this class is always available as the `atom.contextMenu`


hid unimplementable part of the debugger (SI stack)

--- client/editor.coffee
+++ client/editor.coffee
@@ -21,12 +21,14 @@
           ◀

           aA
+

       '''


place cursor in the centre of the editor

--- client/editor.coffee
+++ client/editor.coffee
@@ -155,7 +155,8 @@

   updateSize: -> cm.setSize $e.width(), $e.parent().height() - $e.position().top - 28
   open: (ee) ->
-    cm.setValue ee.text; cm.setCursor ee.currentRow, (ee.currentColumn || 0); cm.focus()
+    cm.setValue ee.text; cm.focus()
+    cm.setCursor ee.currentRow, (ee.currentColumn || 0); cm.scrollIntoView null, $e.height() / 2
     for l in ee.lineAttributes?.stop then cm.setGutterMarker l, 'breakpoints', createBreakpointElement()
     return
   hasFocus: -> cm.hasFocus()
@@ -169,7 +170,7 @@
   highlight: (l) ->
     if hll? then cm.removeLineClass hll, 'background', 'highlighted'
     cm.addLineClass (hll = l), 'background', 'highlighted'
-    cm.setCursor l, 0
+    cm.setCursor l, 0; cm.scrollIntoView null, $e.height() / 2
     return
   getContainer: -> $e
   setDebugger: setDebugger


Write error to console when PDF does not exist

This is one of the things pointed out in #28: when the PDF file doesn't exist, we still call the viewer resulting in a (possible bizarre) error message. This pre-empts this by only calling the viewer if the PDF file exists.

--- lib/viewer.coffee
+++ lib/viewer.coffee
@@ -1,4 +1,4 @@
-{LTool,get_tex_root} = require './ltutils'
+{LTool, get_tex_root, is_file} = require './ltutils'
 {exec, execFile} = require 'child_process'
 path = require 'path'

@@ -112,5 +112,19 @@
     tex_exts = atom.config.get("latextools.texFileExtensions")
     if parsed_master.ext in tex_exts && parsed_current.ext in tex_exts
       master_path_no_ext = path.join(parsed_master.dir, parsed_master.name)
+
+      @ltConsole.show()  # ensure console is visible
       @ltConsole.addContent("Jump to #{row},#{col}")
-      @_jumpToPdf(current_file,master_path_no_ext + ".pdf",row,col)
+
+      pdf_file = master_path_no_ext + '.pdf'
+      if not is_file(pdf_file)
+        log_file = master_path_no_ext + '.log'
+        @ltConsole.addContent(
+          "Could not find PDF file #{pdf_file}. If no errors appeared from " +
+          "the build, please check your log file, #{log_file}",
+          file: log_file,
+          level: 'error'
+        )
+        return
+
+      @_jumpToPdf(current_file, pdf_file, row, col)


Disabled web socket stuff.

Disabled web socket stuff.

--- app/assets/javascripts/app/index.js.coffee
+++ app/assets/javascripts/app/index.js.coffee
@@ -61,6 +61,7 @@
     processData: false
     headers: {'X-Requested-With': 'XMLHttpRequest'}
     cache: false
+    async: true

   ajax: (params, defaults) ->
     $.ajax($.extend({}, @defaults, defaults, params))
@@ -203,26 +204,26 @@
       window.Session['UISeletion'] = @getSelected() + ''
     )

-    @ws = new WebSocket("ws://localhost:3001/");
+#    @ws = new WebSocket("ws://localhost:3001/");

     # Set event handlers.
-    @ws.onopen = ->
-      console.log("onopen")
-
-    @ws.onmessage = (e) ->
+#    @ws.onopen = ->
+#      console.log("onopen")
+
+#    @ws.onmessage = (e) ->
       # e.data contains received string.
-      console.log("onmessage: " + e.data)
-      eval e.data
-
-    Spine.bind 'ws:send', (data) =>
-      @log 'ws:send', data
-      @ws.send(data);
-
-    @ws.onclose = ->
-      console.log("onclose")
-
-    @ws.onerror = ->
-      console.log("onerror")
+#      console.log("onmessage: " + e.data)
+#      eval e.data
+
+#    Spine.bind 'ws:send', (data) =>
+#      @log 'ws:send', data
+#      @ws.send(data);
+
+#    @ws.onclose = ->
+#      console.log("onclose")
+
+#    @ws.onerror = ->
+#      console.log("onerror")

   getSelected: ->
     text = '';


Fix Facebook Share #108

Fix Facebook Share #108

--- app/assets/javascripts/social-share-button.coffee
+++ app/assets/javascripts/social-share-button.coffee
@@ -36,7 +36,7 @@
       when "douban"
         SocialShareButton.openUrl("http://shuo.douban.com/!service/share?href=#{url}&name=#{title}&image=#{img}&sel=#{desc}", 770, 470)
       when "facebook"
-        SocialShareButton.openUrl("http://www.facebook.com/sharer.php?u=#{url}", 555, 400)
+        SocialShareButton.openUrl("http://www.facebook.com/sharer/sharer.php?u=#{url}&display=popup&ref=plugin", 555, 400)
       when "qq"
         SocialShareButton.openUrl("http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=#{url}&title=#{title}&pics=#{img}&summary=#{desc}&site=#{appkey}")
       when "google_plus"


No ttl set, instead user permanent set -> pset()

--- src/RedisPort.coffee
+++ src/RedisPort.coffee
@@ -284,7 +284,7 @@
 			while not port? or port in ports
 				port = 10000 + Math.floor Math.random() * 55000

-			@set "services/#{role}", { host: @host, port: port, role: role }, (error, stat) =>
+			@pset "services/#{role}", { host: @host, port: port, role: role }, (error, stat) =>
 				cb error, port

 	# Free a service, simply deletes and emits a free event


support sort

support sort

--- app/assets/javascripts/monster/list.coffee
+++ app/assets/javascripts/monster/list.coffee
@@ -19,12 +19,26 @@
         edit: if @Event[name] && @Event[name].edit then @Event[name].edit else @Event.template(name,'edit')
     if @search is 'location'
       search = @$location.search()
-      @$.search = search.search if search.search
+      @$.search     = search.search       if search.search
+      @$.pagination = {page: search.page} if search.page
     @reindex() if @pull
     @$.$watch 'search', _.debounce(@update_search, 500) if @search
     @index_success null, @data() if @data
-    @$.destroy = @destroy
+    @$export 'sort',
+             'destroy'
+    @$.predicate =
+      name: 'id'
+      dir: 'asc'
     @reindex = _.debounce @reindex, 100
+  sort:(name)=>
+    dir = if @$.predicate.name is name
+      if @$.predicate.dir == 'desc' then 'asc' else 'desc'
+    else
+      'asc'
+    @$.predicate = {name: name, dir: dir}
+    @$location.search 'sort', "#{name},#{dir}"
+    @$.sortable = true
+    @reindex()
   update_search:(val,old)=>
     if old != val
       if @search is 'location'
@@ -34,6 +48,7 @@
           @$location.search 'search', null
       if @$.pagination
         @$.pagination.page = 1
+        @$location.search 'page', null
       @reindex() if @pull
   reindex:=>
     attrs = @attrs()
@@ -41,6 +56,13 @@
       attrs.page = @$.pagination.page
     if @search && @$.search
       attrs.search = @$.search
+    if @$.sortable
+      attrs.sort   = @$location.search().sort
+    if @$.pagination
+      if @$.pagination.page is 1
+        @$location.search 'page', null
+      else
+        @$location.search 'page', @$.pagination.page
     @Api[@table_name][@action] attrs
   destroy:(model)=>
     name = _.singularize @table_name


Sticky full content fix for animated transistion.

--- app/assets/javascripts/web/_site.js.coffee
+++ app/assets/javascripts/web/_site.js.coffee
@@ -84,8 +84,9 @@
       feedbin.hideLinkAction(url)

   changeContentView: (view) ->
-    currentView = $('[data-behavior~=content_option]:not(.hide)')
-    nextView = $("[data-behavior~=content_option][data-content-option=#{view}]")
+    id = feedbin.selectedEntry.id
+    currentView = $("[data-entry-id=#{id}] [data-behavior~=content_option]:not(.hide)")
+    nextView = $("[data-entry-id=#{id}] [data-behavior~=content_option][data-content-option=#{view}]")

     if view == 'extract'
       $('body').addClass('extract-active')


Fail more cleanly if runtime is not available

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -42,7 +42,7 @@
             done()
           client.on 'connectFailed', (err) ->
             tries--
-            chai.expect(tries).to.be.above 0
+            chai.expect(tries, 'Connection retry limit must not be exceeded').to.be.above 0
             setTimeout(
               ->
                 client.connect address, 'noflo'
@@ -92,6 +92,8 @@
       connection.once 'message', listener

     describe 'Runtime Protocol', ->
+      before ->
+        chai.expect(connection, 'Connection needs to be available').not.be.a 'null'
       describe 'requesting runtime metadata', ->
         it 'should provide it back', (done) ->
           connection.once 'message', (message) ->
@@ -104,6 +106,7 @@

     describe 'Graph Protocol', ->
       before ->
+        chai.expect(connection, 'Connection needs to be available').not.be.a 'null'
         chai.expect(capabilities, 'Graph protocol should be allowed for user').to.contain 'protocol:graph'
       describe 'adding a graph and nodes', ->
         it 'should provide the nodes back', (done) ->
@@ -581,6 +584,7 @@
     describe 'Network Protocol', ->
       # Set up a clean graph
       before (done) ->
+        chai.expect(connection, 'Connection needs to be available').not.be.a 'null'
         chai.expect(capabilities, 'Network protocol should be allowed for user').to.contain 'protocol:network'
         waitFor = 5  # set this to the number of commands below
         listener = (message) ->
@@ -764,6 +768,7 @@

     describe 'Component Protocol', ->
       before ->
+        chai.expect(connection, 'Connection needs to be available').not.be.a 'null'
         chai.expect(capabilities, 'Component protocol should be allowed for connection').to.contain 'protocol:component'
       describe 'on requesting a component list', ->
         it 'should receive some known components', (done) ->


Change default collector unit to steps [#146019673]

--- src/code/utils/time-units.coffee
+++ src/code/utils/time-units.coffee
@@ -19,7 +19,7 @@

   defaultUnit: "STEP"

-  defaultCollectorUnit: "DAY"
+  defaultCollectorUnit: "STEP"

   toString: (unit, plural) ->
     number = if plural then ".PLURAL" else ""


Update - reference updates

Update - reference updates

--- src/coffee/product-import.coffee
+++ src/coffee/product-import.coffee
@@ -1,6 +1,6 @@
 debug = require('debug')('sphere-product-sync-import')
 _ = require 'underscore'
-_.mixin require('underscore-mixins')
+_.mixin require 'underscore-mixins'
 Promise = require 'bluebird'
 {SphereClient, ProductSync} = require 'sphere-node-sdk'

@@ -99,7 +99,7 @@
       @_resolveProductCategories(product.categories)
       @_resolveReference(@client.taxCategories, 'taxCategory', product.taxCategory, "name=\"#{product.taxCategory?.id}\"")
     ]
-    .spread (prodTypeId, prodCatsIds, taxCatId) =>
+    .spread (prodTypeId, prodCatsIds, taxCatId) ->
       if prodTypeId
         product.productType =
           id: prodTypeId
@@ -109,7 +109,7 @@
           id: taxCatId
           typeId: 'tax-category'
       if prodCatsIds
-        product.categories = _.map prodCatsIds, (catId) =>
+        product.categories = _.map prodCatsIds, (catId) ->
           id: catId
           typeId: 'category'
       Promise.resolve product
@@ -121,7 +121,7 @@
       else
         Promise.all cats.map (cat) =>
           @_resolveReference(@client.categories, 'categories', cat, "externalId=\"#{cat.id}\"")
-        .then (result) -> resolve(result.filter (c) => c)
+        .then (result) -> resolve(result.filter (c) -> c)

   _resolveReference: (service, refKey, ref, predicate) ->
     new Promise (resolve) =>


Add method call to reset process summary.

--- src/coffee/product-import.coffee
+++ src/coffee/product-import.coffee
@@ -10,6 +10,7 @@
     @sync = new ProductSync
     @client = new SphereClient options
     @_resetCache()
+    @_resetSummary()

   _resetCache: ->
     @_cache =


:bug: fix pre-release bug for ] motion stoppable at last row screenwise-ly

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -328,7 +328,7 @@
       false
     else
       point.isEqual(@editor.clipScreenPosition(point)) and
-        ((point.row is 0) or (point.row is @getVimLastBufferRow()))
+        ((point.row is 0) or (point.row is @getVimLastScreenRow()))

 class MoveDownToEdge extends MoveUpToEdge
   @extend()


cleanup further

cleanup further

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -697,9 +697,6 @@
   scrolloff: 2
   defaultCount: 0

-  getCount: ->
-    super - 1
-
   moveCursor: (cursor) ->
     cursor.setBufferPosition(@getPoint())

@@ -716,7 +713,7 @@
     row = getFirstVisibleScreenRow(@editor)
     offset = @getScrolloff()
     offset = 0 if (row is 0)
-    offset = Math.max(@getCount(), offset)
+    offset = Math.max(@getCount(-1), offset)
     row + offset

 # keymap: M
@@ -741,7 +738,7 @@
     row = Math.min(@editor.getLastVisibleScreenRow(), vimLastScreenRow)
     offset = @getScrolloff() + 1
     offset = 0 if row is vimLastScreenRow
-    offset = Math.max(@getCount(), offset)
+    offset = Math.max(@getCount(-1), offset)
     row - offset

 # Scrolling


Possible correction for MoveToLineByPercent issue

It was overriding getRow, which wasn't actually called in the parent.

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -681,8 +681,8 @@
 # keymap: N% e.g. 10%
 class MoveToLineByPercent extends MoveToFirstLine
   @extend()
-  getRow: ->
-    percent = Math.min(100, @getCount())
+  getCount: ->
+    percent = Math.min(100, super + 1)
     Math.floor(@getVimLastScreenRow() * (percent / 100))

 class MoveToRelativeLine extends Motion


:memo:

:memo:

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -94,8 +94,8 @@
     if @isMode('visual') and cursorIsAtEndOfLineAtNonEmptyRow(cursor)
       # Avoid puting cursor on EOL in visual-mode as long as cursor's row was non-empty.
       swrap(selection).translateSelectionHeadAndClip('backward')
-
-    swrap(selection).translateSelectionEndAndClip('forward') # @inclusive effect
+    # to select @inclusive-ly
+    swrap(selection).translateSelectionEndAndClip('forward')

 # Used as operator's target in visual-mode.
 class CurrentSelection extends Motion


Remove event attaching from watcher handler.

Events should be attached once, and having event attaching
code within some watcher can cause big memory leaks attaching
same handlers more that one time.

--- app/coffee/modules/taskboard/charts.coffee
+++ app/coffee/modules/taskboard/charts.coffee
@@ -87,18 +87,20 @@

     link = ($scope, $el, $attrs) ->
         element = angular.element($el)
+
+        $scope.$on "resize", ->
+            redrawChart(element, $scope.stats.days)
+
+        $scope.$on "taskboard:graph:toggle-visibility", ->
+            $el.parent().toggleClass('open')
+
+            # fix chart overflow
+            timeout(100, -> redrawChart(element, $scope.stats.days))
+
         $scope.$watch 'stats', (value) ->
-            if $scope.stats?
-                redrawChart(element, $scope.stats.days)
-
-                $scope.$on "resize", ->
-                    redrawChart(element, $scope.stats.days)
-
-                $scope.$on "taskboard:graph:toggle-visibility", ->
-                    $el.parent().toggleClass('open')
-
-                    # fix chart overflow
-                    timeout(100, -> redrawChart(element, $scope.stats.days))
+            if not $scope.stats?
+                return
+            redrawChart(element, $scope.stats.days)

         $scope.$on "$destroy", ->
             $el.off()


Pinpointing server-side bug in creating comment.[4]

--- src/api/posts.coffee
+++ src/api/posts.coffee
@@ -82,7 +82,7 @@
 	# Atomically push Comment to CommentTree
 	CommentTree.findOneAndUpdate { _id: parent.comment_tree }, {$push: { docs : comment }}, (err, doc) ->
 		if err
-			logger.error(err, 'Failed to push comment to CommentTree')
+			logger.error('Failed to push comment to CommentTree', err)
 			return cb(err)
 		if not doc
 			logger.warn('CommentTree %s of parent %s not found. Failed to push comment.',


Update point

Update point

--- src/app/point.coffee
+++ src/app/point.coffee
@@ -6,7 +6,7 @@
 module.exports =
 class Point

-  # Public: Constructs a `Point` from a given object.
+  # Constructs a `Point` from a given object.
   #
   # object - This can be an {Array} (`[startRow, startColumn, endRow, endColumn]`) or an object `{row, column}`
   #
@@ -22,7 +22,7 @@

       new Point(row, column)

-  # Public: Identifies which `Point` is smaller.
+  # Identifies which `Point` is smaller.
   #
   # "Smaller" means that both the `row` and `column` values of one `Point` are less than or equal
   # to the other.
@@ -39,7 +39,7 @@
     else
       point2

-  # Public: Creates a new `Point` object.
+  # Creates a new `Point` object.
   #
   # row - A {Number} indicating the row (default: 0)
   # column - A {Number} indicating the column (default: 0)
@@ -47,13 +47,13 @@
   # Returns a {Point},
   constructor: (@row=0, @column=0) ->

-  # Public: Creates an identical copy of the `Point`.
+  # Creates an identical copy of the `Point`.
   #
   # Returns a duplicate {Point}.
   copy: ->
     new Point(@row, @column)

-  # Public: Adds the `column`s of two `Point`s together.
+  # Adds the `column`s of two `Point`s together.
   #
   # other - The {Point} to add with
   #
@@ -68,7 +68,7 @@

     new Point(row, column)

-  # Public: Moves a `Point`.
+  # Moves a `Point`.
   #
   # In other words, the `row` values and `column` values are added to each other.
   #
@@ -79,7 +79,7 @@
     other = Point.fromObject(other)
     new Point(@row + other.row, @column + other.column)

-  # Public: Creates two new `Point`s, split down a `column` value.
+  # Creates two new `Point`s, split down a `column` value.
   #
   # In other words, given a point, this creates `Point(0, column)` and `Point(row, column)`.
   #
@@ -103,7 +103,7 @@
   # * If the first `row` is less than `other.row`, returns `-1`.
   # * If the first `column` is greater than `other.column`, returns `1`.
   # * If the first `column` is less than `other.column`, returns `-1`.
-  #
+  #
   # Otherwise, returns `0`.
   compare: (other) ->
     if @row > other.row
@@ -118,7 +118,7 @@
       else
         0

-  # Public: Identifies if two `Point`s are equal.
+  # Identifies if two `Point`s are equal.
   #
   # other - The {Point} to compare against
   #
@@ -128,7 +128,7 @@
     other = Point.fromObject(other)
     @row == other.row and @column == other.column

-  # Public: Identifies if one `Point` is less than another.
+  # Identifies if one `Point` is less than another.
   #
   # other - The {Point} to compare against
   #
@@ -136,7 +136,7 @@
   isLessThan: (other) ->
     @compare(other) < 0

-  # Public: Identifies if one `Point` is less than or equal to another.
+  # Identifies if one `Point` is less than or equal to another.
   #
   # other - The {Point} to compare against
   #
@@ -144,7 +144,7 @@
   isLessThanOrEqual: (other) ->
     @compare(other) <= 0

-  # Public: Identifies if one `Point` is greater than another.
+  # Identifies if one `Point` is greater than another.
   #
   # other - The {Point} to compare against
   #
@@ -152,7 +152,7 @@
   isGreaterThan: (other) ->
     @compare(other) > 0

-  # Public: Identifies if one `Point` is greater than or equal to another.
+  # Identifies if one `Point` is greater than or equal to another.
   #
   # other - The {Point} to compare against
   #
@@ -160,23 +160,23 @@
   isGreaterThanOrEqual: (other) ->
     @compare(other) >= 0

-  # Public: Converts the {Point} to a String.
+  # Converts the {Point} to a String.
   #
   # Returns a {String}.
   toString: ->
     "#{@row},#{@column}"

-  # Public: Converts the {Point} to an Array.
+  # Converts the {Point} to an Array.
   #
   # Returns an {Array}.
   toArray: ->
     [@row, @column]

   ### Internal ###
-
+
   inspect: ->
     "(#{@row}, #{@column})"
-
+
   # Internal:
   serialize: ->
     @toArray()


Use `_.multiplyString` in building a tab token

--- src/app/token.coffee
+++ src/app/token.coffee
@@ -33,7 +33,7 @@

   buildTabToken: (tabLength) ->
     new Token(
-      value: new Array(tabLength + 1).join(" ")
+      value: _.multiplyString(" ", tabLength)
       scopes: @scopes
       bufferDelta: 1
       isAtomic: true


fix Icon Image download timing.

fix Icon Image download timing.

--- grails-app/assets/javascripts/chat.coffee
+++ grails-app/assets/javascripts/chat.coffee
@@ -285,8 +285,8 @@
         """

         $("#image#{iconIndex}").on 'error', (error) ->
-            $("#icon#{iconIndex}").html "<svg width='40' height='40' id='identicon#{iconIndex}'></svg>"
-            jdenticon.update("#identicon#{iconIndex}", sha1(msg['username']))
+            error.currentTarget.parentNode.innerHTML = "<svg width='40' height='40' id='identicon#{('' + error.timeStamp).replace(/\./, '_')}'></svg>"
+            jdenticon.update("#identicon#{('' + error.timeStamp).replace(/\./, '_')}", sha1(msg['username']))

     $('#area00').scrollTop(($("#area00")[0].scrollHeight))
     lastUser = msg['username']


Fix typo

Fix typo

--- client/src/daily-report/directives/select-goals/controller.coffee
+++ client/src/daily-report/directives/select-goals/controller.coffee
@@ -1,5 +1,5 @@
 angular.module 'Scrumble.daily-report'
-.controller 'SelectGoalsCtrl', ['$scope', '$mdToas', 'trelloCards', 'markdownGenerator', (
+.controller 'SelectGoalsCtrl', ['$scope', '$mdToast', 'trelloCards', 'markdownGenerator', (
   $scope
   $mdToast
   trelloCards


Add an "ignoreSelection" key to config

Add an "ignoreSelection" key to config

Add a checkbox to the package settings panel that will allow atom-script
to ignore selected code and always perform file based runs.

--- lib/script.coffee
+++ lib/script.coffee
@@ -20,6 +20,10 @@
       title: 'HTML escape console output'
       type: 'boolean'
       default: true
+    ignoreSelection:
+      title: 'Ignore selection (file-based runs only)'
+      type: 'boolean'
+      default: false
     scrollWithOutput:
       title: 'Scroll with output'
       type: 'boolean'


fix error on id modal

fix error on id modal

--- MediaAdminBundle/Resources/public/coffee/orchestraLib.coffee
+++ MediaAdminBundle/Resources/public/coffee/orchestraLib.coffee
@@ -5,7 +5,7 @@
 doCallBack = (editor, view) ->
   tinymce_button_mediamanager = (editor) ->
     $.extend true, view, extendView['orchestraMediaAbstractType'], extendView['orchestraWysiwygType']
-    selector = '.modal_media.' + editor.id
+    selector = '#modal_media_' + editor.id
     view.WysiwygTypeModal
       selector: selector
       input: editor.id


Send git to user private channel (by user.profile.room)

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -60,7 +60,7 @@
       robot.messageRoom user.name, welcomeMsg(user.name)
       robot.messageRoom '#presentaciones', "Hola @#{user.name}"
       add_nicks user.name
-      welcomeGif(user, "#{res.message.name}")
+      welcomeGif(user, user.profile.room)

   robot.respond /la bienvenida/i, (msg) ->
     msg.reply welcomeMsg('')


Speaker deck driver for prev/next

Speaker deck driver for prev/next

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -37,7 +37,7 @@
       slideInfo = new Firebase "#{baseFirebaseUrl}info/"
       slideInfo.set code

-      remoteQueu = new Firebase "#{baseFirebaseUrl}cmd/"
+      remoteQueu = new Firebase "#{baseFirebaseUrl}qc/"

       console.log remoteQueu

@@ -58,14 +58,14 @@
             end
           when 'next'
             remote.next
-          when 'prev'
+          when 'prev', 'previous'
             remote.previous
           when 'current'
             remote.getCurrentSlide
           else
             console.log "Not implement"
         #Okay, remove that command
-        r = new Firebase("https://qcommander.firebaseio-demo.com/command_queues/#{connection.get('token')}/".concat(snapshot.name()))
+        r = new Firebase("https://qcommander.firebaseio-demo.com//#{connection.get('token')}/qc/".concat(snapshot.name()))
         r.remove()
       this.showConnectionBoard()

@@ -109,11 +109,19 @@
       @driver = {}
       this.setupRemote
     # Based on url, load corresponding remote driver
-    setupRemote: () ->
+    setupRemote: () ->
+      @driver = switch
+        when @url.indexOf('http://speakerdeck.com/') > 1 then new SpeakerdeskRemote
+        when @url.indexOf('http://slideshare.net/') > 1  then new SlideshareRemote
+        when @url.indexOf('http://www.scribd.com/') > 1  then new ScribdRemote
+        else new RabbitRemote
+
     previous: () ->
-      @driver.jump(@driver.currentSlide() - 1)
+      #@driver.jump(@driver.currentSlide() - 1a
+      @driver.previous()
     next: () ->
-      @driver.jump(@driver.currentSlide() + 1)
+      #@driver.jump(@driver.currentSlide() + 1)
+      @driver.next()
     jump: (num) ->
       @driver.jump(num)

@@ -129,26 +137,27 @@
     jump: () ->
     next: () ->
     previous: () ->
-    getCurrentSlide: () ->
+    getCurrentSlideNumber: () ->
+    getCurrentSlideScreenshot: () ->

-  class SpeakerdeskRemote extends Remote
+
+  class SpeakerdeskRemote extends RemoteControlDriver
     constructor: () ->
       super
+      @container = $('.speakerdeck-iframe ').contents()
+      console? && console.log @container
+
+    getCurrentSlideScreenshot: () ->
+
     next: () ->
-      f = $('.speakerdeck-iframe');
-      console.log($('.controls > a.next'))
-      $('.controls > a.next').click();
-      $('.nav .btnNext').length && $('.nav .btnNext').click()
+      $('.overnav > .next', @container).click()
     previous: () ->
-      f = $('.speakerdeck-iframe');
-      $('.controls > a.prev').click();
-      $('.nav .btnPrevious').length && $('.nav .btnNext').click()
+      $('.overnav > .prev', @container).click()

-  class SlideshareRemote extends Remote
-  class ScribdRemote extends Remote
-  class RabbitRemote extends Remote
-
+  class SlideshareRemote extends RemoteControlDriver
+  class ScribdRemote extends RemoteControlDriver
+  class RabbitRemote extends RemoteControlDriver

   return {
     init: ()->


fix instance methods to prototype methods

--- gulp/assets/javascripts/accordions.coffee
+++ gulp/assets/javascripts/accordions.coffee
@@ -16,15 +16,15 @@

       @init()

-    open: =>
+    open: ->
       @lists.slideDown 300
       @change_method 'close'

-    close: =>
+    close: ->
       @lists.slideUp 300
       @change_method 'open'

-    change_method: (order) =>
+    change_method: (order) ->
       unless order is 'open'
         @button.removeClass 'open'
                .addClass 'close'
@@ -34,7 +34,7 @@
                .addClass 'open'
                .text 'OPEN'

-    init: =>
+    init: ->
       unless @default is 'open'
         @change_method 'close'
       else
@@ -42,4 +42,4 @@
         @lists.hide()

   $('.list--suit-process-toggle').each ->
-    new SuitProcedureAccordion $(@)+    new SuitProcedureAccordion $(@)


adds ability to pass a custom renderer function

--- src/components/table_view.coffee
+++ src/components/table_view.coffee
@@ -50,4 +50,8 @@

 Luca.components.TableView.renderColumn = (column, item, model, index)->
   cellValue = model.read( column.reader )
+
+  if _.isFunction( column.renderer )
+    cellValue = column.renderer.call @, cellValue, model, column
+
   make("td", {}, cellValue)


test-sauce command

test-sauce command

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -107,6 +107,7 @@
   grunt.loadTasks 'tasks'

   grunt.registerTask "default", ['sass', 'concat:css', 'coffee', 'peg-grammars', 'copy:parser', 'requirejs-optimize-config', 'requirejs', 'copy:dist']
+  grunt.registerTask 'test-sauce', ['connect', 'saucelabs-mocha']

   grunt.registerTask 'requirejs-optimize-config', 'Builds the mainConfigFile for r.js', ->
     config_script = grunt.file.read('build/requirejs_config.js')


Updating Gruntfile to get rid of mozilla-addon-sdk

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -941,17 +941,11 @@
       }
     }
   }
-  'mozilla-addon-sdk':
-    'latest':
-      options:
-        dest_dir: '.mozilla_addon_sdk/'
-
-  'mozilla-cfx-xpi':
-    'dist':
-      options:
-        'mozilla-addon-sdk': 'latest'
-        extension_dir: 'build/dist/firefox'
-        dist_dir: 'build/dist'
+  'jpm':
+    options:
+      src: 'build/dist/firefox/'
+      xpi: 'build/dist/'
+      debug: true

   vulcanize: {}
 }  # grunt.initConfig
@@ -1145,8 +1139,7 @@
 taskManager.add 'dist', [
   'build'
   'copy:dist'
-  'mozilla-addon-sdk'
-  'mozilla-cfx-xpi:dist'
+  'jpm:xpi'
 ]

 taskManager.add 'default', [
@@ -1164,7 +1157,7 @@
   grunt.loadNpmTasks 'grunt-exec'
   grunt.loadNpmTasks 'grunt-gitinfo'
   grunt.loadNpmTasks 'grunt-jasmine-chromeapp'
-  grunt.loadNpmTasks 'grunt-mozilla-addon-sdk'
+  grunt.loadNpmTasks 'grunt-jpm'
   grunt.loadNpmTasks 'grunt-string-replace'
   grunt.loadNpmTasks 'grunt-ts'
   grunt.loadNpmTasks 'grunt-vulcanize'


Added new tasks to gruntfile

Added new tasks to gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -57,7 +57,27 @@
       ajax:
         files: ['<%= source.ajax %>']
         tasks: ["coffee:ajax", "uglify:ajax"]
-
+      element:
+        files: ['<%= source.element %>']
+        tasks: ["coffee:element", "uglify:element"]
+      environment:
+        files: ['<%= source.environment %>']
+        tasks: ["coffee:environment", "uglify:environment"]
+      events:
+        files: ['<%= source.events %>']
+        tasks: ["coffee:events", "uglify:events"]
+      gestures:
+        files: ['<%= source.gestures %>']
+        tasks: ["coffee:gestures", "uglify:gestures"]
+      output:
+        files: ['<%= source.output %>']
+        tasks: ["coffee:output", "uglify:output"]
+      query:
+        files: ['<%= source.query %>']
+        tasks: ["coffee:query", "uglify:query"]
+      style:
+        files: ['<%= source.style %>']
+        tasks: ["coffee:style", "uglify:style"]

   grunt.loadNpmTasks "grunt-contrib-concat"
   grunt.loadNpmTasks "grunt-contrib-coffee"
@@ -66,4 +86,3 @@

   # Default task.
   grunt.registerTask 'default', ["concat", "coffee", "uglify"]
-


refactor karma files config now more inclusive easier to add files and reduce configuration

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -202,14 +202,7 @@
 						'./dist/scripts/libs/angular-route.js'
 						'./dist/scripts/libs/angular-resource.js'
 						'./bower_components/angular-mocks/angular-mocks.js'
-						'./dist/scripts/app.js'
-						'./dist/scripts/routes.js'
-						'./dist/scripts/views.js'
-						'./dist/scripts/controllers/**/*.js'
-						'./dist/scripts/directives/**/*.js'
-						'./dist/scripts/filters/**/*.js'
-						'./dist/scripts/interceptors/**/*.js'
-						'./dist/scripts/services/**/*.js'
+						'./dist/scripts/**/*.js'
 						'./test/scripts/**/*.{coffee,js}'
 					]
 					frameworks: [


Fix livereload for app JS

Fix livereload for app JS

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -275,9 +275,9 @@
       livereload:
         options:
           livereload: true
+          interrupt: true
         files: [
-          'public/css/buckets.css'
-          'public/js/{buckets,vendor}.css'
+          'public/{css,js}/*.{css,js}'
           'public/plugins/**/*.{css,js}'
         ]


uglify is failing

uglify is failing

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -218,7 +218,8 @@
     'ngtemplates'
     'copy'
     'concat'
-    'uglify'
+# FIXME: uglify seems to fail
+#    'uglify'
     'cssmin'
     'rev'
     'usemin'


Fix casing on JSON folder paths

Fix casing on JSON folder paths

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -24,9 +24,9 @@
 					}
 				]
 			jsonFile:
-				resourcesFiles: './test/json/one.json'
+				resourcesFiles: './test/JSON/one.json'
 			jsonFiles:
-				resourcesFiles: ['./test/json/one.json', './test/json/two.json']
+				resourcesFiles: ['./test/JSON/one.json', './test/JSON/two.json']
 		nodeunit:
 			tests: ['./test/*_test.js']


updated coffeelint files

updated coffeelint files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -20,7 +20,7 @@
 			options:
 				no_tabs: {level: 'ignore'}
 				indentation: {level: 'ignore'}
-			dev: ['app/client/**/*.coffee', 'app/server/**/*.coffee']
+			dev: ['app/*.coffee', 'app/client/**/*.coffee', 'app/server/**/*.coffee']

 		htmlhint:
 			all:


update Gruntfile, don't coffee tasks/**/*.coffee files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,6 +6,8 @@
           host: 'localhost'
           port: 2121
           passive: on
+          # type: 'binary'
+          # mkdirs: no
           # dryrun: on
           # ftpCommand: 'ftp'
           # encoding: 'utf-8'
@@ -37,7 +39,7 @@
           {
             expand: on
             src: [
-              'tasks/**/*.coffee'
+              # 'tasks/**/*.coffee'
               'test/**/*.coffee'
             ]
             ext: '.js'


Remove source maps along with JS and CSS files. [#89552722]

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -162,7 +162,8 @@
       when 'coffee'
         grunt.config 'coffee.build.src', sourcePath
       when 'coffeeDelete'
-        grunt.config 'clean.js.src', replaceExtension(sourcePath, '.js')
+        jsPath = replaceExtension(sourcePath, '.js')
+        grunt.config 'clean.js.src', [jsPath, jsPath + '.map']
       when 'cucumber'
         specs = if path.match(/\.feature$/) then [path] else [featureDir + featureFiles]
         grunt.config 'protractor.cucumber.options.args.specs', specs
@@ -173,4 +174,5 @@
       when 'sass'
         grunt.config 'sass.build.src', sourcePath
       when 'sassDelete'
-        grunt.config 'clean.css.src', replaceExtension(sourcePath, '.css')
+        cssPath = replaceExtension(sourcePath, '.css')
+        grunt.config 'clean.css.src', [cssPath, cssPath + '.map']


Updated tasks

Updated tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,6 +12,7 @@
                     'dev/public/assets/dist/modules/config.js': 'dev/public/assets/app/modules/config.coffee'
                     'api/controllers/api.js': 'dev/api/controllers/api.coffee'
                     'api/controllers/content.js': 'dev/api/controllers/content.coffee'
+                    'api/controllers/user.js': 'dev/api/controllers/user.coffee'
                     'api/models/user.js': 'dev/api/models/user.coffee'
                     'data/fixtures/fictitious/fixture.js': 'dev/data/fixtures/fictitious/fixture.coffee'
                     'data/configs/bootstrapper.js': 'dev/data/configs/bootstrapper.coffee'
@@ -20,11 +21,15 @@
                 options:
                     bare: true
                 files:
+                    'data/tests/index.js': 'dev/data/tests/index.coffee'
+                    'data/tests/api/controllers/user.js': 'dev/data/tests/api/controllers/user.coffee'
+                    'data/tests/tools/util.js': 'dev/data/tests/tools/util.coffee'
                     'var/test-public/modules/config.js': 'dev/var/test-public/modules/config.coffee'
         copy:
             dist:
                 files: [
                     { expand: true, flatten: true, filter: 'isFile', src: 'dev/public/*.html', dest: 'public/' }
+                    { expand: true, flatten: true, filter: 'isFile', src: 'dev/data/tests/*.json', dest: 'data/tests/' }
                     { expand: true, flatten: true, filter: 'isFile', src: 'dev/data/configs/*.json', dest: 'data/configs/' }
                 ]
         concat:
@@ -54,7 +59,8 @@
                     glob: []
                     resolvers: [{
                         files: [
-                            'dev/public/*.html'
+                            'dev/public/*.html',
+                            'dev/data/tests/*.json'
                         ]
                         tasks: ['copy:dist']
                         callback:
@@ -67,6 +73,9 @@
                             'dev/public/assets/app/*.coffee',
                             'dev/public/assets/app/modules/*.coffee',
                             'dev/var/test-public/modules/*.coffee'
+                            'dev/data/tests/*.coffee'
+                            'dev/data/tests/**/*.coffee'
+                            'dev/data/tests/**/**/*.coffee'
                         ]
                         tasks: ['coffee:*']
                         callback:


Ooops, change the repo name

Ooops, change the repo name

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -147,7 +147,7 @@
                 message: 'Built from %sourceCommit% on branch %sourceBranch%'
             pages:
                 options:
-                    remote: 'git@github.com:ukmadlz/no-service-brumjs-2016.git'
+                    remote: 'git@github.com:ukmadlz/no-service-seium-2016.git'
                     branch: 'gh-pages'


Allow grunt-bump to commit and create the version tag

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -57,10 +57,7 @@
       options:
         files: ['package.json'],
         updateConfigs: ['pkg'],
-        commit: false,
-        createTag: false,
         push: false,
-        pushTo: 'origin',
     clean:
       build: [ 'build/*' ]
       release: [ 'release/*' ]


Fix connect-mock require

Fix connect-mock require

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -33,7 +33,7 @@
     errorHandler
   ]

-  middlewares = middlewares.unshift(require('connect-open')({verbose: verbose})) if grunt.option 'mock'
+  middlewares = middlewares.unshift(require('connect-mock')({verbose: verbose})) if grunt.option 'mock'

   # Tasks
   config =


fix cleaning issue

fix cleaning issue

Signed-off-by: Joshua Rubin <5af9dd6785dff068189d7fd731f38e1d0699263c@zvelo.com>

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -7,7 +7,7 @@
       lib: [ "lib/*.js", "lib/**/*.js", "!lib/poly/*.js", "!lib/poly/**/*.js" ]
       amd: "amd"
       example: [ "example/public/js/*.js", "example/public/js/*.map" ]
-      test: [ "test/lib/**/*.js", "test/lib/**/**/*.js" ]
+      test: [ "test/lib" ]

     coffee:
       options:


Add the custom matchers as a spec helper so they actually work. [rev: matthew.gordon2]

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,6 +12,7 @@
   styles = 'bower_components/hp-autonomy-js-testing-utils/src/css/bootstrap-stub.css'
   serverPort = 8000
   host = "http://localhost:#{serverPort}/"
+  helpers = 'bower_components/hp-autonomy-js-testing-utils/src/js/jasmine-custom-matcher.js'

   grunt.initConfig
     pkg: grunt.file.readJSON 'package.json'
@@ -29,6 +30,7 @@
       test:
         src: sourcePath
         options:
+          helpers: helpers
           host: host
           keepRunner: true
           outfile: jasmineSpecRunner
@@ -40,6 +42,7 @@
       coverage:
         src: sourcePath
         options:
+          helpers: helpers
           host: host
           keepRunner: true
           outfile: coverageSpecRunner


added data to jade release

added data to jade release

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -67,6 +67,9 @@
       release:
         options:
           pretty: false
+          # you can provide specfic content to a template by using the src, dest params
+          data: (src, dest) ->
+            require './data/content.json'
         files: [
           expand: true
           src: 'pages/**/*.jade'


Add Grunt task to reset stuff

Add Grunt task to reset stuff

Helps with testing the install process. You probably shouldn't run this.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,6 +1,7 @@
 fs = require 'fs'
 path = require 'path'
 Mocha = require 'mocha'
+exec = require('child_process').exec

 module.exports = (grunt) ->

@@ -38,6 +39,15 @@
   # Default task.
   grunt.registerTask 'default', ['coffee', 'mocha:default']

+  grunt.registerTask 'nuke', "Nuke stuff. Don't run this unless you know what you're doing.", ->
+    steps = [
+      'rm -rf ~/.impromptu'
+      'brew uninstall redis cmake'
+      'git clean -fdx'
+    ]
+
+    exec steps.join ' && '
+
   # Task for running Mocha tests with coffee.
   grunt.registerMultiTask 'mocha', 'Run mocha unit tests.', ->
     done = @async()


Include plugins global for server tests

Include plugins global for server tests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -121,3 +121,4 @@
     # browser tests.
     GLOBAL.chai = require 'chai'
     GLOBAL.httprequest = require './src/index'
+    GLOBAL.httprequestplugins = require './src/plugins'


workaround for false error message on "view" while Sumatra is already running

--- lib/viewer.coffee
+++ lib/viewer.coffee
@@ -20,7 +20,7 @@
     @ltConsole.addContent("Executing " + command, br = true)

     exec command, {}, (err, stdout, stderr) =>
-      if err # weirdness
+      if err && !(err.code == 1 && !stderr) # when it is already running, Sumatra returns error code 1 but no error message, while "the jump" works just fine
         @ltConsole.addContent("ERROR #{err.code}: ", br=true)
         @ltConsole.addContent(line, br=true) for line in stderr.split('\n')


server: use url encoded body parser (fixes login regression)

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -74,7 +74,7 @@
     secret: "foo"
     resave: yes
     saveUninitialized: true
-  app.use bodyParser.json()
+  app.use bodyParser.urlencoded()
   app.use compression()
   # helmet:
   app.use helmet.xframe('sameorigin')
@@ -293,7 +293,7 @@
   return handleClientIdNotFound res, req unless clientId

   JUser.login clientId, { username, password }, (err, info) ->
-    return res.status(403).send  err.message  if err?
+    return res.status(403).send err.message  if err?
     # implementing a temporary opt-out for new koding:
     storageOptions =
       appId   : 'NewKoding'


Fixed deprecated 'web-preferences' & 'web-security'

--- lib/window.coffee
+++ lib/window.coffee
@@ -12,8 +12,8 @@
   createWindow: (options) ->
     options = _.extend options,
       show: false
-      'web-preferences':
-        'web-security': false
+      'webPreferences':
+        'webSecurity': false
         'plugins': true
     current = new BrowserWindow options
     if options.indexName?


Bugfix

Bugfix

--- components.coffee
+++ components.coffee
@@ -265,7 +265,9 @@
         for reactiveName of @reactives
             if reactiveName of @_reactiveComps
                 # It's a ReactiveVar
-                J.Dict._deepStop Tracker.nonreactive => @reactives[reactiveName].get()
+                val = Tracker.nonreactive => @reactives[reactiveName].get()
+                if val instanceof J.AutoDict or val instanceof J.AutoList or val instanceof J.AutoVar
+                    val.stop()
                 @_reactiveComps[reactiveName].stop()
             else
                 # It's an AutoDict


that's what `@currentUser` was supposed to be for :best:

--- resources/assets/coffee/_classes/store-supporter-tag.coffee
+++ resources/assets/coffee/_classes/store-supporter-tag.coffee
@@ -39,6 +39,10 @@
     @usernameInput = @el.querySelector('.js-username-input')
     @inputFeedback = @el.querySelector('.js-input-feedback')

+    @user =
+      username: @el.dataset.username
+      avatarUrl: @el.dataset.avatarUrl
+
     @initializeSlider()
     @initializeSliderPresets()
     @initializeUsernameInput()
@@ -69,16 +73,19 @@
   getUser: (username) =>
     $.post laroute.route('users.check-username-exists'), username: username
     .done (data) =>
-      @setUserInteraction(data?)
-      @updateUserDisplay(data)
-      @updateCart(data)
+      @user =
+        username: data.username
+        avatarUrl: data.avatar_url
+
     .fail (xhr) =>
-      @setUserInteraction(false)
-      @updateUserDisplay(null)
-      @updateCart(null)
+      @user = null
       if xhr.status == 401
         osu.popup osu.trans('errors.logged_out'), 'danger'
+
     .always =>
+      @setUserInteraction(@user?)
+      @updateCart(@user)
+      @updateUserDisplay()
       @searching = false

   calculate: (position) =>
@@ -115,10 +122,10 @@
     @durationElement.textContent = obj.durationText()
     @discountElement.textContent = "save #{obj.discount()}%"

-  updateUserDisplay: (user) =>
-    avatarUrl = if user
+  updateUserDisplay: =>
+    avatarUrl = if @user
                   @inputFeedback.textContent = ''
-                  user.avatar_url
+                  @user.avatarUrl
                 else
                   @inputFeedback.textContent = "This user doesn't exist"
                   ''


Add error handling on logout

Add error handling on logout

--- services/web/app/coffee/Features/User/UserController.coffee
+++ services/web/app/coffee/Features/User/UserController.coffee
@@ -133,8 +133,9 @@
 				SudoModeHandler.clearSudoMode(user._id)
 			cb()

-	logout : (req, res)->
+	logout : (req, res, next)->
 		UserController._doLogout req, (err) ->
+			return next(err) if err?
 			res.redirect '/login'

 	register : (req, res, next = (error) ->)->


Fixed issue #887 - Some notifications are marked as read initially.

--- app/assets/javascripts/app/controllers/widget/online_notification.coffee
+++ app/assets/javascripts/app/controllers/widget/online_notification.coffee
@@ -218,18 +218,19 @@
     for item in items
       if !@alreadyShown[item.id]
         @alreadyShown[item.id] = true
-        if @container.fetchedData
-          item = @prepareForObjectListItem(item)
-          if item.objectNative && item.objectNative.activityMessage
-            title = item.objectNative.activityMessage(item)
-          else
-            title = "Need objectNative in item #{item.object}.find(#{item.o_id})"
-          title = App.Utils.html2text(title.replace(/<.+?>/g, '"'))
-          @notifyDesktop(
-            url: item.link
-            title: title
-          )
-          App.OnlineNotification.play()
+        if !item.seen
+          if @container.fetchedData
+            item = @prepareForObjectListItem(item)
+            if item.objectNative && item.objectNative.activityMessage
+              title = item.objectNative.activityMessage(item)
+            else
+              title = "Need objectNative in item #{item.object}.find(#{item.o_id})"
+            title = App.Utils.html2text(title.replace(/<.+?>/g, '"'))
+            @notifyDesktop(
+              url: item.link
+              title: title
+            )
+            App.OnlineNotification.play()

   onClick: =>
     @container.hide()


Correct indentaiton of jQuery.payment code in checkout/payment code

--- frontend/app/assets/javascripts/store/checkout/payment.js.coffee
+++ frontend/app/assets/javascripts/store/checkout/payment.js.coffee
@@ -1,12 +1,12 @@
 Spree.ready ($) ->
   if ($ '#checkout_form_payment').is('*')

-      $(".cardNumber").payment('formatCardNumber')
-  $(".cardExpiry").payment('formatCardExpiry')
-  $(".cardCode").payment('formatCardCVC')
+    $(".cardNumber").payment('formatCardNumber')
+    $(".cardExpiry").payment('formatCardExpiry')
+    $(".cardCode").payment('formatCardCVC')

-  $(".cardNumber").change ->
-    $(this).parent().siblings(".ccType").val($.payment.cardType(@value))
+    $(".cardNumber").change ->
+      $(this).parent().siblings(".ccType").val($.payment.cardType(@value))

     ($ 'input[type="radio"][name="order[payments_attributes][][payment_method_id]"]').click(->
       ($ '#payment-methods li').hide()


updated list of rules to make a bit more sense

--- src/scripts/moscow-rules.coffee
+++ src/scripts/moscow-rules.coffee
@@ -27,10 +27,10 @@
   "Maintain a natural pace.",
   "Murphy is right.",
   "Never go against your gut; it is your operational antenna.",
-  "Once is an accident. Twice is coincidence. Three times is an enemy action. (taken from Ian Fleming's novel Goldfinger)",
   "Pick the time and place for action.",
   "There is no limit to a human being's ability to rationalize the truth.",
-  "Vary your pattern and stay within your cover."
+  "Vary your pattern and stay within your cover.",
+  "Once is an accident. Twice is coincidence. Three times is an enemy action."
 ]

 module.exports = (robot) ->


Show ports on the list command

Show ports on the list command

--- src/bin/noflo.coffee
+++ src/bin/noflo.coffee
@@ -33,6 +33,10 @@
               console.log ''
               console.log "#{component} (#{path})"
               console.log instance.description if instance.description
+              if instance.inPorts
+                console.log 'Inports:', _.keys(instance.inPorts).join ', '
+              if instance.outPorts
+                console.log 'Outports:', _.keys(instance.outPorts).join ', '
               process.exit 0 if todo is 0
         return


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -24,7 +24,7 @@
     Object.extend(I.clampData, data)

   ###*
-  Helper to clamp the `x` and `y` properties of the oboj
+  Helper to clamp the `x` and `y` properties of the object to be within a r

   @name clampToBounds
   @methodOf Clampable#


Set injected to false by default

Set injected to false by default

--- src/app/text-mate-grammar.coffee
+++ src/app/text-mate-grammar.coffee
@@ -176,7 +176,7 @@
     return scanner if scanner = @scannersByBaseGrammarName[baseGrammar.name]

     anchored = false
-    injected = true
+    injected = false
     regexes = []
     patterns = @getIncludedPatterns(baseGrammar)
     scopes = scopesFromStack(ruleStack)


Add comment explaining the need for appending `\n` when scanning lines with a TextMate grammar

--- src/app/text-mate-grammar.coffee
+++ src/app/text-mate-grammar.coffee
@@ -93,9 +93,11 @@
   getNextTokens: (stack, line, position) ->
     patterns = @getIncludedPatterns()

+    # Add a `\n` to appease patterns that contain '\n' explicitly
     return null unless result = @getScanner().findNextMatch(line + "\n", position)
     { index, captureIndices } = result

+    # Since the `\n' (added above) is not part of the line, truncate captures to the line's actual length
     lineLength = line.length
     captureIndices = captureIndices.map (value, index) ->
       value = lineLength if index % 3 != 0 and value > lineLength


collision

collision

--- src/collision.coffee
+++ src/collision.coffee
@@ -46,26 +46,26 @@
   Circle objects have x, y, and radius.

-     player = GameObject
-       x: 5
-       y: 5
-       radius: 10
-
-     enemy = GameObject
-       x: 10
-       y: 10
-       radius: 10
-
-     farEnemy = GameObject
-       x: 500
-       y: 500
-       radius: 30
-
-     Collision.circular(player, enemy)
-  => true
-
-     Collision.circular(player, farEnemy)
-  => false
+  player = GameObject
+    x: 5
+    y: 5
+    radius: 10
+
+  enemy = GameObject
+    x: 10
+    y: 10
+    radius: 10
+
+  farEnemy = GameObject
+    x: 500
+    y: 500
+    radius: 30
+
+  Collision.circular(player, enemy)
+  # => true
+
+  Collision.circular(player, farEnemy)
+  # => false

   @name circular


collision with regular bounds

collision with regular bounds

--- src/collision.coffee
+++ src/collision.coffee
@@ -220,8 +220,8 @@
           xw = target.width/2
           yw = target.height/2
           return Collision.rayRectangle source, direction,
-            xw:
-            yw:
+            xw: xw
+            yw: yw
         else
           error "Bounds object isn't a rectangle"


added underscore character to list of allowed characters in command line file names

--- lib/app.coffee
+++ lib/app.coffee
@@ -155,7 +155,7 @@
   for excludedOption in ["--python-expr", "--python-file", "--pmap", "--dynamic-library", "--all-destination", "--fail-destination", "--pass-destination", "--print-statistics", "--print-volume-statistics", "--xargs"]
     regexp = new RegExp(excludedOption + "=?[^\\s]*", "gi")
     options = options.replace(regexp, "")
-  filter = new RegExp("[^\\s\\=\\-\\/\\,\\.\\:0-9a-z" + additionalPermittedCharacters + "]", "gi")
+  filter = new RegExp("[^\\s\\=\\-\\/\\,\\.\\:0-9a-z_" + additionalPermittedCharacters + "]", "gi")
   options = options.replace(filter, "")
   options


properly throw error when your api key is wrong

--- lib/bot.coffee
+++ lib/bot.coffee
@@ -198,14 +198,12 @@
 }).startRTM()

 defaultBot.api.team.info {}, (err, response) ->
-  if err
-    console.error(err)
-  if response
+  if response?.ok
     controller.saveTeam(response.team, ->
       console.log "Saved the team information..."
     )
   else
-    throw new Error("Could not connect to the Slack API.")
+    throw new Error("Could not connect to the Slack API. Ensure your Slack API key is correct. (#{err})")

 controller.setupWebserver process.env.PORT || 3333, (err, expressWebserver) ->
   controller.createWebhookEndpoints(expressWebserver)


Use CourseStore to get name to display

Use CourseStore to get name to display

--- src/components/navbar/index.cjsx
+++ src/components/navbar/index.cjsx
@@ -17,6 +17,7 @@
   displayName: 'Navigation'

   mixins: [BindStoreMixin]
+  bindStore: CourseStore

   contextTypes:
     router: React.PropTypes.func
@@ -32,7 +33,7 @@
       {courseId} = @context.router.getCurrentParams()

       unless @state.course?.id is courseId
-        course = CourseListingStore.get(courseId)
+        course = CourseStore.get(courseId)
         @setState({course})

   # Also need to listen to when location finally updates.
@@ -51,8 +52,6 @@

   bindUpdate: ->
     @handleCourseChanges()
-
-  bindStore: CourseStore

   transitionToMenuItem: (routeName, params) ->
     @context.router.transitionTo(routeName, params)


Rename tag prefix to 'context-cnxmod'

Rename tag prefix to 'context-cnxmod'

--- src/components/tags/cnx-mod.cjsx
+++ src/components/tags/cnx-mod.cjsx
@@ -19,7 +19,7 @@
     <MultiInput
       {...@props}
       label='CNX Module'
-      prefix='content-cnxmod'
+      prefix='context-cnxmod'
       cleanInput={@cleanInput}
       validateInput={@validateInput}
       placeholder='#########-####-###-####-############'


Removed controller currentParams accessor

--- src/controller/controller.coffee
+++ src/controller/controller.coffee
@@ -176,5 +176,3 @@
   _viewClassForAction: (action) ->
     classPrefix = @get('routingKey').replace('/', '_')
     Batman.currentApp?[Batman.helpers.camelize("#{classPrefix}_#{action}_view")] || Batman.View
-
-  @accessor 'currentParams', -> Batman.currentApp.get('currentParams')


Add ngRight tests

Add ngRight tests

--- client/test/spec/directives/ngright.coffee
+++ client/test/spec/directives/ngright.coffee
@@ -1,16 +1,43 @@
 'use strict'

 describe 'Directive: ngRight', ->
-
-  # load the directive's module
   beforeEach module 'blicblockApp'

   scope = {}
+  compile = {}

-  beforeEach inject ($controller, $rootScope) ->
+  beforeEach inject ($controller, $compile, $rootScope) ->
     scope = $rootScope.$new()
+    compile = $compile

-  it 'should make hidden element visible', inject ($compile) ->
-    element = angular.element '<ng-right></ng-right>'
-    element = $compile(element) scope
-    expect(element.text()).toBe 'this is the ngRight directive'
+  it 'is triggered on right arrow keydown', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keydown')
+    e.which = 39
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on right arrow keypress', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keypress')
+    e.which = 39
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on letter a keydown', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keydown')
+    e.which = 100
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)
+
+  it 'is triggered on letter a keypress', ->
+    element = compile('

')(scope)
+    expect(scope.touched).toBeFalsy()
+    e = $.Event('keypress')
+    e.which = 100
+    $(element).trigger(e)
+    expect(scope.touched).toEqual(true)


Fixes #579

Fixes #579

--- lib/git.coffee
+++ lib/git.coffee
@@ -133,7 +133,7 @@
     else
       new Promise (resolve, reject) ->
         project = atom.project
-        directory = project.getDirectories().filter((d) -> d.contains(path))[0]
+        directory = project.getDirectories().filter((d) -> d.contains(path) or d.getPath() is path)[0]
         if directory?
           project.repositoryForDirectory(directory)
           .then (repo) ->


fix tests

fix tests

--- lib/nap.coffee
+++ lib/nap.coffee
@@ -45,7 +45,7 @@
   @_fileMtimeMap = {}
   @_preprocessedCache = {}
   @_fingerprintCache = { js: {}, jst: {}, css: {} }
-  @getNamespace = options.getNamespace if options.getNamespace
+  @getNamespace = options.getNamespace ? @defaultGetNamespace

   unless fs.existsSync process.cwd() + @publicDir
     throw new Error "The directory #{@publicDir} doesn't exist"
@@ -265,7 +265,7 @@
 # @param {String} filename The template filename
 # @return {String} namespace Processed template filename

-@getNamespace = (filename) ->
+@defaultGetNamespace = (filename) ->
     # Templates in a 'templates' folder are namespaced by folder after 'templates'
     if filename.indexOf('templates') > -1
       namespace = filename.split('templates')[-1..][0].replace /^\/|\..*/g, ''


Fixed 4 unit test failures

Fixed 4 unit test failures

--- lib/nap.coffee
+++ lib/nap.coffee
@@ -119,7 +119,7 @@
     fingerprint = '-' + fingerprintForPkg('jst', pkg) if @mode is 'production'
     src = (@cdnUrl ? @_assetsDir) + '/' + "#{pkg}#{fingerprint ? ''}.jst.js"
     src += '.jgz' if gzip
-    return if filename.match /\.map$/ then "" else "<script src='#{src}' type='text/javascript'></script>"
+    return "<script src='#{src}' type='text/javascript'></script>"

   expandAssetGlobs()


run: merge constrains

run: merge constrains

--- lib/run.coffee
+++ lib/run.coffee
@@ -19,7 +19,8 @@
     v = service.use[id] = module: v if typeof v is 'string'
     v.id ?= v.module
   service.commands ?= {}
-  context.config ?= {}
+  service.config ?= {}
+  service.config = merge {}, service.config... if Array.isArray service.config
   service.configure ?= []
   service.configure = [service.configure] unless Array.isArray service.configure
   service.constraints ?= {}
@@ -46,13 +47,11 @@
   @setMaxListeners 100
   process.on 'uncaughtException', (err) =>
     @emit 'error', err
+  config.config = merge {}, config.config... if Array.isArray config.config
   # Normalize nodes
   for id, node of config.nodes
     node.id ?= id
-    # node.host ?= id
-    # node.shortname ?= node.host.split('.')[0]
     node.config ?= {}
-    # node.config.id = id
     node.config.host ?= id
     node.config.shortname ?= node.config.host.split('.')[0]
   # Discover module inside parent project
@@ -65,12 +64,16 @@
     normalize_service service
     merge service, normalize_service service.module
   # Add auto loaded services
-  for _, service of config.services
+  load_children = (service) ->
     for id, use of service.use
       if use.implicit
-        use = merge require.main.require(use.module), use
+        module = config.services[use.id] or require.main.require(use.module)
+        use = merge module, use
         child = config.services[use.id] = normalize_service use
         merge child.constraints, service.constraints
+        load_children child
+  for _, service of config.services
+    load_children service
   # Graph ordering
   graph = tsort()
   for _, service of config.services


push and unshift Collection methods are aliases of appendModels and prependModels

--- src/marbles/collection.js.coffee
+++ src/marbles/collection.js.coffee
@@ -144,6 +144,9 @@

     models

+  push: (models...) =>
+    @appendModels(models)
+
   appendIds: (model_cids...) =>
     @model_ids = @model_ids.concat(model_cids)

@@ -171,6 +174,9 @@

     models

+  unshift: (models...) =>
+    @prependModels(models)
+
   prependIds: (model_cids...) =>
     @model_ids = model_cids.concat(@model_ids)

@@ -181,16 +187,6 @@
   last: =>
     return unless cid = _.last(@model_ids)
     @constructor.model.find({ cid: cid })
-
-  unshift: (models...) =>
-    for model in models
-      @model_ids.unshift(model.cid)
-    @model_ids.length
-
-  push: (models...) =>
-    for model in models
-      @model_ids.push(model.cid)
-    @model_ids.length

   models: (cids = @model_ids) =>
     models = []


low precision is enough

low precision is enough

--- src/renderer/canvas_webgl.coffee
+++ src/renderer/canvas_webgl.coffee
@@ -22,7 +22,7 @@

   # Private: Vertex Shader.
   @VERTEX_SHADER: """
-  precision highp float;
+  precision lowp float;

   uniform vec2 screen;
   uniform vec2 texture;
@@ -40,7 +40,7 @@

   # Private: Fragment Shader.
   @FRAGMENT_SHADER: """
-  precision highp float;
+  precision lowp float;

   uniform sampler2D texture_id;
   varying vec2 texture_coord;


add missing @fake method to NetworkManager

--- src/shared/NetworkManager.coffee
+++ src/shared/NetworkManager.coffee
@@ -91,5 +91,8 @@
   @fakeEmit: (name, data) ->
     @get().fakeEmit(name, data)

+  @fake: (data) ->
+    @get().fake(data)
+
   @emit: (data) ->
     @get().emit(data)


Fix file tab height discrepancies

Fix file tab height discrepancies

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -19,6 +19,8 @@
 {ProjectStore} = require('./project_store')
 {Icon, Tip, SAGE_LOGO_COLOR} = require('./r_misc')
 misc = require('misc')
+
+FILE_NAV_HEIGHT = '36px'

 default_file_tab_styles =
     width : 250
@@ -338,8 +340,8 @@

             <FreeProjectWarning project_id={@props.project_id} name={name} />
-            {

-
+            {

+
                     {[<FileTab
                         name       = {k}
                         label      = {v.label}
@@ -359,7 +361,7 @@
                     lockToContainerEdges={true}
                     distance    = {3 if not IS_MOBILE}
                     bsStyle     = "pills"
-                    style       = {display:'flex'}
+                    style       = {display:'flex', height:FILE_NAV_HEIGHT, overflowY:'hidden'}
                 >
                     {@file_tabs()}
                 </SortableNav>


Formalize various node types for consistency Fix various bugs

--- cilantro/static/cilantro/scripts/coffeescript/serrano/datacontext.coffee
+++ cilantro/static/cilantro/scripts/coffeescript/serrano/datacontext.coffee
@@ -3,37 +3,31 @@
     'backbone'
 ], (_, Backbone) ->

+    isBranch = (attrs) ->
+        (attrs.type is 'and' or attrs.type is 'or') and attrs.children?.length >= 2
+
+    isCondition = (attrs) ->
+        attrs.operator and attrs.id and attrs.value isnt undefined
+
+    isComposite = (attrs) ->
+        attrs.composite is true and attrs.id
+
+
     class DataContextNode extends Backbone.Model
         validate: (attrs) ->
-            delete @type
-
-            # Branch
-            if attrs.type isnt undefined
-                if attrs.type isnt 'and' and attrs.type isnt 'or'
-                    return 'Invalid logical operator between nodes'
-                if not attrs.children or attrs.children.length < 2
-                    return 'Branch nodes must contain two or more child nodes'
-                @type = 'branch'
-
-            # Condition
-            else if attrs.operator isnt undefined
-                if not attrs.id? or attrs.value is undefined
-                    return 'Condition nodes must have id, operator and value attributes'
-                @type = 'condition'
-
-            # Composite
-            else if attrs.composite is true
-                if not attrs.id?
-                    return 'Composite nodes must have an id attribute'
-                @type = 'composite'
-            else
-                for key, value of attrs
-                    if value isnt undefined
-                        return 'Unknown node type'
+            # Some kind of node
+            if isBranch(attrs) or isCondition(attrs) or isComposite attrs
+                return
+
+            # Check for all undefined values
+            for key, value of attrs
+                if value isnt undefined
+                    return 'Unknown node type'
+
             return

         toJSON: ->
-            if @type is 'branch'
+            if @isBranch()
                 json =
                     type: @get 'type'
                     children: _.map(@get('children'), (model) -> model.toJSON())
@@ -45,16 +39,36 @@
         siblings: ->
             if @root then false else _.without @parent.get('children'), @

+        # Proxy methods
+        isEmpty: ->
+            _.isEmpty @attributes
+
+        isBranch: ->
+            isBranch @attributes
+
+        isCondition: ->
+            isCondition @attributes
+
+        isComposite: ->
+            isComposite @attributes
+
         # Creates a new branch and adds this node along with one or more other
         # nodes to the branch
-        promote: (type, nodes...) ->
-            if type isnt 'and' and type isnt 'or'
-                throw new Error 'Type must be "and" or "or"'
+        promote: (nodes...) ->
+            if _.isString nodes[0]
+                type = nodes[0]
+                if type isnt 'and' and type isnt 'or'
+                    throw new Error 'Type must be "and" or "or"'
+                nodes.splice 0, 1
+            else
+                type = 'and'
+
             if nodes.length is 0
                 throw new Error 'At least one other node must be supplied'

             children = _.map [@toJSON(), nodes...], (attrs) =>
                 DataContextNode.parseAttrs attrs, @
+
             @clear slient: true
             @set type: type, children: children
             return @
@@ -83,7 +97,7 @@
             return @

         add: (nodes...) ->
-            if @type isnt 'branch'
+            if not @isBranch()
                 throw new Error 'Node is not a branch. Use "promote" to convert it into one'
             @attributes.children.push.apply @attributes.children, _.map nodes, (attrs) =>
                 DataContextNode.parseAttrs attrs, @
@@ -109,23 +123,20 @@
     DataContextNode.parseAttrs = (attrs, parent, callback) ->
         if not attrs
             node = new DataContextNode
-        # Existing node. Note this assumes
+        # Existing node
         else if attrs instanceof DataContextNode
             node = attrs
         # Branch
-        else if attrs.type is 'and' or attrs.type is 'or' and attrs.children?.length >= 2
+        else if isBranch attrs
             node = new DataContextNode type: attrs.type
-            children = _.map attrs.children, (_attrs) -> DataContextNode.parseAttrs _attrs, node
+            children = _.map attrs.children, (_attrs) -> DataContextNode.parseAttrs _attrs, node, callback
             node.set children: children
-            node.type = 'branch'
         # Condition
-        else if attrs.operator and attrs.id and attrs.value isnt undefined
+        else if isCondition attrs
             node = new DataContextNode attrs
-            node.type = 'condition'
         # Composite
-        else if attrs.composite is true and attrs.id
+        else if isComposite attrs
             node = new DataContextNode attrs
-            node.type = 'composite'
         else
             throw new Error 'Unknown node type'

@@ -147,9 +158,8 @@
             if @isNew() then super else @get 'url'

         parse: (response) =>
-            @nodes = {}
-
             if response
+                @nodes = {}
                 @node = DataContextNode.parseAttrs response.json, null, @cacheNode

             return response
@@ -160,26 +170,55 @@
                 attrs.json = @node.toJSON()
             return attrs

+        # Proxy methods
+        isEmpty: ->
+            @node.isEmpty()
+
+        isBranch: ->
+            @node.isBranch()
+
+        isCondition: ->
+            @node.isCondition()
+
+        isComposite: ->
+            @node.isComposite()
+
         cacheNode: (node) =>
             if node.id
                 if not (cache = @nodes[node.id])
                     cache = @nodes[node.id] = []
-                cache.push node
-
-        promote: (node, type, nodes...) ->
-            node.promote(type, nodes...)
+                # Don't add redundant cache
+                if cache.indexOf(node) is -1
+                    cache.push node
+
+        promote: (node, nodes...) ->
+            if node is null
+                @node.promote nodes...
+            else
+                node.promote nodes...
+            @set 'json', @node.toJSON()

         demote: (node) ->
             node.demote()
+            @set 'json', @node.toJSON()

         add: (node, nodes...) ->
-            node.add(nodes...)
+            if node is null
+                node = @node
+
+            if node.isEmpty()
+                node.set nodes[0].attributes or nodes[0]
+            else
+                node.add(nodes...)
+                @cacheNode node for node in node.get 'children'
+            @set 'json', @node.toJSON()

         remove: (node) ->
             node.remove()
-            # Unreference it
-            if (idx = (cache = @nodes[node.id]).indexOf(node)) >= 0
-                cache.splice(idx)
+            # Dereference it
+            if (cache = @nodes[node.id]) and (idx = cache.indexOf(node)) >= 0
+                cache.splice(idx, 1)
+            @set 'json', @node.toJSON()

     class DataContexts extends Backbone.Collection


webapp/desktop: basically fixing the new files notification box

--- src/smc-webapp/desktop_app.cjsx
+++ src/smc-webapp/desktop_app.cjsx
@@ -48,6 +48,8 @@

 HIDE_LABEL_THOLD = 6

+NAV_HEIGHT = 36
+
 FileUsePageWrapper = (props) ->
     styles =
         zIndex       : '10'
@@ -55,7 +57,7 @@
         position     : 'fixed'
         boxShadow    : '0 0 15px #aaa'
         border       : '2px solid #ccc'
-        top          : '43px'
+        top          : "#{NAV_HEIGHT - 2}px"
         background   : '#fff'
         right        : '2em'
         overflowY    : 'auto'
@@ -196,7 +198,7 @@

     render_right_nav: ->
         logged_in = @props.is_logged_in
-        <Nav id='smc-right-tabs-fixed' style={height:'36px', lineHeight:'20px', margin:'0', overflowY:'hidden'}>
+        <Nav id='smc-right-tabs-fixed' style={height:"#{NAV_HEIGHT}px", lineHeight:'20px', margin:'0', overflowY:'hidden'}>
             {@render_admin_tab() if logged_in and @props.groups?.includes('admin')}
             {@render_sign_in_tab() if not logged_in}
             <NavTab
@@ -222,7 +224,7 @@
             float      : 'right'
             padding    : '10px 7px'

-        <Nav style={height:"36px", margin:'0', overflow:'hidden'}>
+        <Nav style={height:"#{NAV_HEIGHT}px", margin:'0', overflow:'hidden'}>
             <NavTab
                 name           = {'projects'}
                 inner_style    = {padding:'0px'}
@@ -266,7 +268,7 @@
             display       : 'flex'
             marginBottom  : 0
             width         : '100%'
-            minHeight     : '36px'
+            minHeight     : "#{NAV_HEIGHT}px"
             position      : 'fixed'
             right         : 0
             zIndex        : '100'
@@ -274,7 +276,7 @@
             top           : top

         positionHackOffset = if @props.show_global_info then announce_bar_offset else 0
-        positionHackHeight = (36 + positionHackOffset) + 'px'
+        positionHackHeight = (NAV_HEIGHT + positionHackOffset) + 'px'

         <div ref="page" style={style} onDragOver={(e) -> e.preventDefault()} onDrop={@drop}>
             {<FileUsePageWrapper /> if @props.show_file_use}


webapp: fix syntax error in cad51ccc41e50fb

--- src/smc-webapp/misc_page.coffee
+++ src/smc-webapp/misc_page.coffee
@@ -1863,10 +1863,10 @@
     $("iframe:visible").css('pointer-events', 'auto')

 # for backward compatibility, and no circular import
-exports.open_popup_window = (...args) ->
-    require('./r_misc/open-browser-tab').open_popup_window(...args)
-exports.open_new_tab = (...args) ->
-    require('./r_misc/open-browser-tab').open_new_tab(...args)
+exports.open_popup_window = (args...) ->
+    require('./r_misc/open-browser-tab').open_popup_window(args...)
+exports.open_new_tab = (args...) ->
+    require('./r_misc/open-browser-tab').open_new_tab(args...)

 exports.get_cookie = (name) ->


payment: better currency formatting

payment: better currency formatting

--- client/app/Applications/Dashboard.kdapplication/views/productview.coffee
+++ client/app/Applications/Dashboard.kdapplication/views/productview.coffee
@@ -8,11 +8,11 @@
     product = @getData()

     title     = product.title
-    price     = (product.feeAmount / 100).toFixed 2
+    price     = @utils.formatMoney product.feeAmount / 100
     fmtPrice  =
       if product.priceIsVolatile
       then '(price is volatile)'
-      else "$#{ price }"
+      else "#{ price }"

     subscriptionType =
       if product.subscriptionType is 'single'


webapp/new: replace generic icons with proper ones for sage and jupyter

--- src/smc-webapp/project_new.cjsx
+++ src/smc-webapp/project_new.cjsx
@@ -152,11 +152,11 @@
         <div>
             <Row style={row_style}>
                 <Col sm={6}>
-                    <Tip icon='file-code-o' title='Sage worksheet' tip='Create an interactive worksheet for using the SageMath mathematical software, R, and many other systems.  Do sophisticated mathematics, draw plots, compute integrals, work with matrices, etc.'>
-                        <NewFileButton icon='file-code-o' name='Sage worksheet' on_click={@props.create_file} ext='sagews' />
-                    </Tip>
-                    <Tip icon='file-code-o' title='Jupyter notebook' tip='Create an interactive notebook for using Python, Julia, R and more.'>
-                        <NewFileButton icon='file-code-o' name='Jupyter notebook' on_click={@props.create_file} ext={'ipynb'} />
+                    <Tip icon='cc-icon-sagemath-bold' title='Sage worksheet' tip='Create an interactive worksheet for using the SageMath mathematical software, R, and many other systems.  Do sophisticated mathematics, draw plots, compute integrals, work with matrices, etc.'>
+                        <NewFileButton icon='cc-icon-sagemath-bold' name='Sage worksheet' on_click={@props.create_file} ext='sagews' />
+                    </Tip>
+                    <Tip icon='cc-icon-jupyter' title='Jupyter notebook' tip='Create an interactive notebook for using Python, Julia, R and more.'>
+                        <NewFileButton icon='cc-icon-jupyter' name='Jupyter notebook' on_click={@props.create_file} ext={'ipynb'} />
                     </Tip>
                 </Col>
                 <Col sm={6}>


add(wrong-sorting-test): test for sorting when it's not overrided

--- src/spec/fetchstock.spec.coffee
+++ src/spec/fetchstock.spec.coffee
@@ -56,3 +56,11 @@
       expect(result.body.results[0].quantityOnStock).toEqual 6
       done()
     .catch done
+
+  it 'should sort by id if it is not overrided', (done) ->
+    sortingParam = ''
+    @fetchStocks.client.inventoryEntries.sort = (path) ->
+      sortingParam = path
+    @fetchStocks.run()
+    expect(sortingParam).toEqual 'id'
+    done()


Add test for failed xpath resolve

Add test for failed xpath resolve

--- annotran/static/scripts/annotator/plugin/substitution.coffee
+++ annotran/static/scripts/annotator/plugin/substitution.coffee
@@ -95,8 +95,16 @@
     div.appendChild(document.createTextNode(substituteText))
     substituteText = div.innerHTML

-    # resolve the passed xpath
-    full_range = new xpathRange.SerializedRange(data).normalize(document.body)
+    full_range = null
+
+    try
+      # resolve the passed xpath
+      full_range = new xpathRange.SerializedRange(data).normalize(document.body)
+    catch
+      full_range = null
+
+    if full_range == null
+      return

     # map the start and end points
     start = full_range.start


[skip] Skipped timesheets for retired users (4m)

--- app/assets/javascripts/houston/reports/timesheet_view.coffee
+++ app/assets/javascripts/houston/reports/timesheet_view.coffee
@@ -73,7 +73,7 @@
   render: ->
     # for each subject, render a new stacked bar graph
     @$el.empty()
-    for timesheet in @timesheets
+    for timesheet in @timesheets when @users[timesheet.subject.id]
       @$el.append """

#{@users[timesheet.subject.id]}


fixed bug not being able to log when coming from github

--- app/assets/javascripts/views/log/log_time_github_view.coffee
+++ app/assets/javascripts/views/log/log_time_github_view.coffee
@@ -57,6 +57,8 @@

   initialize : ->

+    super()
+
     @repositoryModel = new RepositoryModel(id : @model.get("id"))
     @repositoryModel.fetch().done( =>
       repoName = @repositoryModel.get("name")


fix region links

fix region links

--- app/assets/javascripts/views/tables/TableRegionsChart.coffee
+++ app/assets/javascripts/views/tables/TableRegionsChart.coffee
@@ -52,7 +52,7 @@
         )
         onClick: (series) =>
           document.location.href = Routes.Regions.show
-            name: series.value.region
+            name: series.value.region.get('regionName')

       @graph.render()


refactor: Update atom config

refactor: Update atom config

--- configs/atom.cson
+++ configs/atom.cson
@@ -1,12 +1,7 @@
 "*":
   core:
     telemetryConsent: "limited"
-    themes: [
-      "atom-dark-ui"
-      "gruvbox-plus-syntax"
-    ]
   editor:
-    fontFamily: "Menlo"
     fontSize: 16
     preferredLineLength: 100
     showIndentGuide: true
@@ -14,8 +9,8 @@
     softWrap: true
   "exception-reporting":
     userId: "c3e419a6-250a-4e79-85f8-48fdda7d7718"
-  "linter-flow":
-    executablePath: "./node_modules/.bin/flow"
+  "ide-flowtype":
+    tryFlowBin: true
   "linter-stylelint":
     disableWhenNoConfig: true
     enableHtmlLinting: true
@@ -23,24 +18,6 @@
     showPanel: true
   "markdown-preview":
     useGitHubStyle: true
-  minimap:
-    plugins:
-      bookmarks: true
-      bookmarksDecorationsZIndex: 0
-      cursorline: true
-      cursorlineDecorationsZIndex: 0
-      "find-and-replace": true
-      "find-and-replaceDecorationsZIndex": 0
-      "git-diff": true
-      "git-diffDecorationsZIndex": 0
-      "highlight-selected": true
-      "highlight-selectedDecorationsZIndex": 0
-      linter: true
-      linterDecorationsZIndex: 0
-      pigments: true
-      pigmentsDecorationsZIndex: 0
-      selection: true
-      selectionDecorationsZIndex: 0
   "tree-view":
     hideIgnoredNames: true
   welcome: