Added support for socket.io acknowledgments [Radagaisus] (solves #14)

This adds @ack to the scope of @on (on the server/zappa side).
The callback function that will receive the acknowledgment (on the client) must be provided as the last argument of emit() or send().

This is 4fba3ab304bbd104d005c3314c928917ada566c2

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -396,9 +396,10 @@
     for name, h of ws_handlers
       do (name, h) ->
         if name isnt 'connection' and name isnt 'disconnect'
-          socket.on name, (data) ->
+          socket.on name, (data, ack) ->
             ctx = build_ctx()
             ctx.data = data
+            ctx.ack = ack
             switch app.settings['databag']
               when 'this' then h.apply(data, [ctx])
               when 'param' then h.apply(ctx, [data])


`app.configure` doesn't exist in Express-4

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -365,11 +365,6 @@
             app.use a
           else
             use k, v for k, v of a
-    return
-
-  context.configure = (p) ->
-    if typeof p is 'function' then app.configure p
-    else app.configure k, v for k, v of p
     return

   context.settings = app.settings


pass results of parents to children

pass results of parents to children

--- src/zuvor.coffee
+++ src/zuvor.coffee
@@ -399,17 +399,26 @@
       valid = new Set(names).delete(options.pending).delete(options.done)
       options.pending.add valid
       Promise.all valid.keys().map (id) ->
-        # TODO pass in results of parents
-        promise = Promise.resolve options.callback id
+        parents = options.graph.parents(id)
+        if parents.length is 0
+          promise = Promise.resolve options.callback id
+        else
+          values = {}
+          for parent in parents
+            values[parent] = results[parent]
+          promise = Promise.resolve options.callback(id, values)
         promise.then (value) ->
           options.pending.delete id
           options.done.add id
           results[id] = value

+          keys = new Set(options.done.keys())
+            .delete(orderless)
+
           candidates = if options.reversed
-            options.graph.whereAllChildrenIn(options.done.keys())
+            options.graph.whereAllChildrenIn(keys)
           else
-            options.graph.whereAllParentsIn(options.done.keys())
+            options.graph.whereAllParentsIn(keys)

           # start all we can start now that have not been started
           next = new Set(candidates)


Updated player to allow bluetooth access to id.

--- game/lib/player.coffee
+++ game/lib/player.coffee
@@ -3,14 +3,14 @@

 class Player extends EventEmitter
   constructor: (options)->
-    @_id = options.id
+    @id = options.id
     @_ledPin = options.led
     @_buttonPin = options.button
     @_game = options.game

     @_game.on 'buttonPress', (pin)=>
       if pin == "#{@_buttonPin}"
-        console.log "Player #{@_id}'s button pressed!"
+        console.log "Player #{@id}'s button pressed!"
         @emit 'buttonPress'

     @on 'buttonPress', =>
@@ -40,7 +40,7 @@
   endTurn: ()->
     @_currentTurn.end = new Date()
     @_turns.push @_currentTurn
-    console.log "Player #{@_id}'s turn length: #{@_currentTurn.end - @_currentTurn.start}ms"
+    console.log "Player #{@id}'s turn length: #{@_currentTurn.end - @_currentTurn.start}ms"
     @setLED(false)
     @_currentTurn = null


app-updates: Fix for update app button. It was showing when user don't have the app.

--- client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -161,16 +161,17 @@
         appsController.updateUserApp app.manifest, =>
           @getSingleton("router").handleRoute "Develop"

+    @updateButton.hide()
+
     appsController.fetchApps (err, manifests) =>
       # user have the app, show just show open button
       if app.title in Object.keys manifests
         @installButton.hide()
         @openButton.show()

-      appName   = app.manifest.name
-      {version} = manifests[appName]
-      unless appsController.isAppUpdateAvailable appName, version
-        @updateButton.hide()
+      appName = app.manifest.name
+      if appsController.isAppUpdateAvailable appName, manifests[appName]?.version
+        @updateButton.show()

     {icns, name, version, authorNick} = app.manifest


Better deepClone

Better deepClone

--- src/6.object.coffee
+++ src/6.object.coffee
@@ -1,8 +1,10 @@
-# `deepAssign` and `deepAbsorb` must use deep clone when traverse isn't needed. If otherwise
-# using direct assignment, then it will have severe side-effects: when sources are more than 1,
-# the first or middle source's value may have been changed after the whole thing finishes.
-# `deepAssign` and `deepAbsorb` should only traverse non-array objects. If they also traverse
-# arrays, then it will look very unnatural. It's weird to merge arrays.
+# `deepAssign` and `deepAbsorb` should use "merge" only when both source and target
+# are non-array objects. It will look unnatural and weird to merge arrays.
+#
+# `deepAssign` and `deepAbsorb` must use deep clone when the source is a non-array object but
+# can't be merged. If otherwise using direct assignment, then it will have severe
+# side-effects: when sources are more than 1, the first or middle source's data
+# may have been changed after the whole thing finishes.

 Object.isObject = (x) -> typeof x in ["object", "function"] and x != null
 Object.isNormalObject = (x) -> Object.isObject(x) and typeof x != "function" and not Array.isArray(x)
@@ -53,14 +55,17 @@
     )
     subject
 Object.deepClone = (x) ->
-    target = if Array.isArray(x) then [] else {}
-    deepCopyFrom = (target, source) ->
-        Object.forEach(source, (key, value) ->
-            if Object.isObject(value)
-                target[key] = if Array.isArray(value) then [] else {}
-                deepCopyFrom(target[key], value)
-            else
-                target[key] = value
-        )
-    deepCopyFrom(target, x)
-    target
+    if Object.isObject(x)
+        target = if Array.isArray(x) then [] else {}
+        deepCopyFrom = (target, source) ->
+            Object.forEach(source, (key, value) ->
+                if Object.isObject(value)
+                    target[key] = if Array.isArray(value) then [] else {}
+                    deepCopyFrom(target[key], value)
+                else
+                    target[key] = value
+            )
+        deepCopyFrom(target, x)
+        target
+    else
+        x


Don't sync in unit tests

Don't sync in unit tests

--- src/Bootstrap.coffee
+++ src/Bootstrap.coffee
@@ -181,6 +181,9 @@
     # May need to think of a better way to handle this.
     # ATM, this will only try to create the tables, fail because they are already
     # created, but by trying will learn about the table structure.
+    if Salad.env is "testing"
+      return cb()
+
     App.sequelize.sync()
       .success =>
         cb()


xmpp/connection: readability

xmpp/connection: readability

--- src/xmpp/connection.coffee
+++ src/xmpp/connection.coffee
@@ -55,7 +55,7 @@
                 when "presence"
                     @_handlePresence stanza
                 when "message"
-                    unless stanza.attrs.type is "error"
+                    if stanza.attrs.type isnt "error"
                         @_handleMessage stanza
                     else if from.indexOf('@') < 0
                         unless missedServerTimeouts.hasOwnProperty(from)


Correcting typo.

Correcting typo.

--- largest-continuous-sum/largest-continuous-sum.coffee
+++ largest-continuous-sum/largest-continuous-sum.coffee
@@ -1,7 +1,7 @@
 largestContinuousSum = (array) ->
   return if !array or !array.length

-  currentSum = maximumSum = array.unshift()
+  currentSum = maximumSum = array.shift()

   # Checks the array of sums and compares them
   array.forEach (num) ->


Sign avatar urls with absolute bewit expiry (allows avatar to be cached)

--- lib/assets/javascripts/models/meta_profile.js.coffee
+++ lib/assets/javascripts/models/meta_profile.js.coffee
@@ -1,3 +1,5 @@
+AVATAR_TTL = 1376669460 + 3154000000 # 100 years from 2013-08-16 -0400
+
 TentStatus.Models.MetaProfile = class  MetaProfileModel extends Marbles.Model
   @model_name: 'meta_profile'
   @id_mapping_scope: ['entity']
@@ -36,7 +38,7 @@
   constructor: ->
     @on 'change:avatar_digest', (digest) =>
       if digest
-        @set('avatar_url', TentStatus.tent_client.getSignedUrl('attachment', entity: @get('entity'), digest: digest))
+        @set('avatar_url', TentStatus.tent_client.getSignedUrl('attachment', entity: @get('entity'), digest: digest, exp: AVATAR_EXP_TIMESTAMP))
       else
         @set('avatar_url', TentStatus.config.DEFAULT_AVATAR_URL)


turn off neighborhood borders for now. It's too noisy

--- source/javascripts/grid.coffee
+++ source/javascripts/grid.coffee
@@ -50,11 +50,11 @@
         context.fill()

       # Draw the neighborhoods
-      context.beginPath()
-      path(neighborhoods)
-      context.strokeStyle = '#fff'
-      context.lineWidth = 0.3
-      context.stroke()
+      # context.beginPath()
+      # path(neighborhoods)
+      # context.strokeStyle = '#fff'
+      # context.lineWidth = 0.3
+      # context.stroke()

       # Draw the bodies of water
       context.beginPath()


:white_check_mark: Add tests for buffer color properties

--- spec/color-scanner-spec.coffee
+++ spec/color-scanner-spec.coffee
@@ -30,6 +30,19 @@
       it 'returns the first buffer color match', ->
         expect(bufferColor).toBeDefined()

+      describe 'the resulting buffer color', ->
+        it 'has a text range', ->
+          expect(bufferColor.textRange).toEqual([8,12])
+
+        it 'has a buffer range', ->
+          expect(bufferColor.bufferRange).toEqual([[0, 8],[0, 12]])
+
+        it 'has a color', ->
+          expect(bufferColor.color).toBeColor('#ffffff')
+
+        it 'stores the matched text', ->
+          expect(bufferColor.match).toEqual('#fff')
+
       describe 'successive searches', ->
         it 'returns a buffer color for each match and then undefined', ->
           expect(scanner.search()).toBeDefined()


add static files route

add static files route

--- app/app.coffee
+++ app/app.coffee
@@ -1,3 +1,4 @@
+path = require 'path'
 express = require 'express'

 config = require '../config.json'
@@ -7,6 +8,8 @@

 app.use '/', site

+app.use '/static', express.static path.join process.cwd(), config.paths.static
+
 app.listen config.port, config.host, ->
 	unless config.host
 		console.log "Listening on *:#{config.port}"


Attach globally.

Attach globally.

--- src/bootstrap.coffee
+++ src/bootstrap.coffee
@@ -1,6 +1,6 @@
 # Like sands through the hourglass, so are the days of our lives.
 App = require 'app'
-atom = new App
+window.atom = new App

 require 'window'
 window.startup()


multi device support

multi device support

--- src/bootstrap.coffee
+++ src/bootstrap.coffee
@@ -9,7 +9,7 @@
 csrgen = Promise.promisify require 'csr-gen'
 request = Promise.promisify require 'request'

-registerDevice = (apiKey, userId, applicationId) ->
+registerDevice = (apiKey, userId, applicationId, deviceType) ->
 	# I'd be nice if the UUID matched the output of a SHA-256 function, but although the length limit of the CN
 	# attribute in a X.509 certificate is 64 chars, a 32 byte UUID (64 chars in hex) doesn't pass the certificate
 	# validation in OpenVPN This either means that the RFC counts a final NULL byte as part of the CN or that the
@@ -22,7 +22,7 @@
 			user: userId
 			application: applicationId
 			uuid: uuid
-			'device_type': 'Raspberry Pi'
+			'device_type': deviceType
 	).spread (response, body) ->
 		if response.statusCode != 201
 			throw new Error('Device registration to resin failed. body: ' + body + '. status code: ' + response.statusCode)
@@ -32,10 +32,11 @@
 module.exports = ->
 	# Load config file
 	userConfig = require('/boot/config.json')
+	userConfig.deviceType ?= 'Raspberry Pi'

 	new Promise (resolve, reject) ->
 		if not userConfig.uuid?
-			registerDevice(userConfig.apiKey, userConfig.userId, userConfig.applicationId)
+			registerDevice(userConfig.apiKey, userConfig.userId, userConfig.applicationId, userConfig.deviceType)
 			.then (uuid) ->
 				userConfig.uuid = uuid
 				resolve(uuid)


Split committee import up into separate jobs.

--- jobs/import-committees.coffee
+++ jobs/import-committees.coffee
@@ -4,7 +4,7 @@
 ifSuccessful = helpers.ifSuccessful

 soap = require 'soap'
-
+ObjectId = require('mongodb').ObjectID
 committeesSvcUri = "./wsdl/Committees.svc.xml"

 persistCommitteeMembers = (members, committee, session, db, callback) ->
@@ -55,19 +55,42 @@
       persistCommitteeMembers(members, committeeDetails, session, db, callback)
       callback()

-module.exports = (jobs, db) ->
+module.exports = (jobs, db) -> soap.createClient committeesSvcUri, (err, client) ->
+  throw err if err
+
+  jobs.process 'import committee for session', 20, (job, callback) ->
+    # Ensure object IDs are in the correct format.
+    job.data.session._id = new ObjectId(job.data.session._id)
+
+    getCommitteeForSessionArgs =
+      CommitteeId: job.data.committeeBrief.Id
+      SessionId: job.data.session.assemblyId
+
+    client.CommitteeService.BasicHttpBinding_CommitteeFinder.GetCommitteeForSession getCommitteeForSessionArgs, (err, result, raw) ->
+      if err
+        callback(err)
+        return
+
+      persistCommittee(result.GetCommitteeForSessionResult, job.data.session, db, callback)
+
+  jobs.process 'import committees for session', 4, (job, callback) ->
+    getCommitteesArgs =
+      SessionId: job.data.session.assemblyId
+
+    client.CommitteeService.BasicHttpBinding_CommitteeFinder.GetCommitteesBySession getCommitteesArgs, (err, result, raw) ->
+      if err
+        callback(err)
+        return
+
+      result.GetCommitteesBySessionResult.CommitteeListing.forEach (committeeBrief) ->
+        jobs.create("import committee for session", committeeBrief: committeeBrief, session: job.data.session).save()
+
+      callback()
+
   jobs.process 'import committees', (job, callback) ->
-    soap.createClient committeesSvcUri, (err, client) -> ifSuccessful err, callback, ->
       db.collection("sessions").find().toArray (err, results) -> ifSuccessful err, callback, ->
         results.forEach (session) ->
-          getCommitteesArgs =
-            SessionId: session.assemblyId
+          job.log "Kueing up import for session job for " + session._id
+          jobs.create("import committees for session", session: session).save()

-          client.CommitteeService.BasicHttpBinding_CommitteeFinder.GetCommitteesBySession getCommitteesArgs, (err, result, raw) -> ifSuccessful err, callback, ->
-            result.GetCommitteesBySessionResult.CommitteeListing.forEach (committeeBrief) ->
-              getCommitteeForSessionArgs =
-                CommitteeId: committeeBrief.Id
-                SessionId: session.assemblyId
-
-              client.CommitteeService.BasicHttpBinding_CommitteeFinder.GetCommitteeForSession getCommitteeForSessionArgs, (err, result, raw) -> ifSuccessful err, callback, ->
-                persistCommittee(result.GetCommitteeForSessionResult, session, db, callback)
+        callback()


Improve format string highlighting for some themes

--- grammars/rust.cson
+++ grammars/rust.cson
@@ -243,7 +243,7 @@
       }
       {
         'comment': 'Format string thing'
-        'name': 'punctuation.section.embedded.format.rust'
+        'name': 'punctuation.section.embedded.meta.tag.format.rust'
         'begin': "{"
         'end': "}"
         'patterns': [


Speedup tests by using observe.

Speedup tests by using observe.

--- tests.coffee
+++ tests.coffee
@@ -1,3 +1,5 @@
+WAIT_FOR_DATABASE_TIMEOUT = 1000 # ms
+
 # The order of documents here tests delayed definitions

 # Just to make sure things are sane
@@ -252,30 +254,34 @@
   Meteor.publish null, ->
     SpecialPost.documents.find()

-  DirectCollection.command
-    profile: 2
-
-  systemProfile = new DirectCollection 'system.profile'
+  Future = Npm.require 'fibers/future'

   Meteor.methods
     'wait-for-database': ->
-      peerdbNamespaces = ("meteor.#{ document.Meta.collection._name }" for document in Document.list)
-      try
-        systemProfile.findEach
-          ns:
-            $in: peerdbNamespaces
-          op:
-            $in: ['insert', 'update', 'remove']
-        ,
-          tailable: true
-          awaitdata: true
-          numberOfRetries: 2
-        ,
-          (document) ->
-            # Ignoring
-            # TODO: We could also measure things like number of requests made and test if they match what it should be
-      catch error
-        throw error unless /tailable cursor timed out/.test "#{ error }"
+      future = new Future()
+      timeout = null
+      newTimeout = ->
+        Meteor.clearTimeout timeout if timeout
+        timeout = Meteor.setTimeout ->
+          timeout = null
+          future.return() unless future.isResolved()
+        , WAIT_FOR_DATABASE_TIMEOUT
+      newTimeout()
+      handles = []
+      for document in Document.list
+        do (document) ->
+          initializing = true
+          handles.push document.documents.find({}).observeChanges
+            added: (id, fields) ->
+              newTimeout() unless initializing
+            changed: (id, fields) ->
+              newTimeout()
+            removed: (id) ->
+              newTimeout()
+          initializing = false
+      future.wait()
+      for handle in handles
+        handle.stop()

 waitForDatabase = (test, expect) ->
   Meteor.call 'wait-for-database', expect (error) ->
@@ -8607,7 +8613,7 @@
 if Meteor.isServer and Document.instances is 1
   testAsyncMulti 'peerdb - exception while processing', [
     (test, expect) ->
-      Log._intercept 4 # Four to see if we catch more than expected
+      Log._intercept 3

       IdentityGenerator.documents.insert
         source: 'exception'


fix(token): drag and dropping a token no longer shows the plus sign. fixes T3390.

Test Plan: tested manually. all tests remain green.

Reviewers: bengotow

Maniphest Tasks: T3390

Differential Revision: https://phab.nylas.com/D1988

--- src/components/tokenizing-text-field.cjsx
+++ src/components/tokenizing-text-field.cjsx
@@ -106,6 +106,8 @@
     json = Utils.serializeRegisteredObjects(@props.item)
     event.dataTransfer.setData('nylas-token-item', json)
     event.dataTransfer.setData('text/plain', @props.item.toString())
+    event.dataTransfer.dropEffect = "move"
+    event.dataTransfer.effectAllowed = "move"
     @setState(dragging: true)

   _onDragEnd: (event) =>


Исправил баг Opera 10.50 который не мог получить высоту панели с тумблерами

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -217,6 +217,7 @@
     thumbnails = headerHeight = 0

     if @dimensions.thumbnails isnt false and not @thumbnails.hasClass('hide')
+      @dimensions.thumbnails = @thumbnails.height() if @dimensions.thumbnails is 0
       thumbnails = @dimensions.thumbnails

     headerHeight = @dimensions.header if title


Remove loggers

Remove loggers

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -146,10 +146,8 @@

     componentWillMount: ->
         who = getSegments()[1]
-        console.log "who: #{ who }"

         peer = new Peer who, key: PEER_KEY, debug: 2
-        console.log "new Peer()"

         @connections = []

@@ -157,15 +155,10 @@
             alert error.type

         peer.on "open", (peerId) =>
-            console.log "peer.on"
-            console.log "peerId: #{ peerId }"

             if who == "x"
-                console.log "acting as #{ who }, host"

                 peer.on "connection", (connection) =>
-                    console.log "new connection"
-                    console.log "peer #{ connection.peer } just connected to host #{ peerId }"

                     @connections.push connection

@@ -173,40 +166,33 @@

                     peerIds = @connectionsToPeerIds @connections

-                    console.log "connected peers: #{ peerIds }"

                     connection.on "open", =>
                         connectionsWithoutNewConnection = _.filter @connections, (c) -> c.peer != connection.peer
                         peerIdsWithoutNewConnection = @connectionsToPeerIds connectionsWithoutNewConnection

                         if peerIdsWithoutNewConnection.length
-                            console.log "sending connections to #{ connection.peer }"

                             connection.send type: "newConnection", peerIds: peerIdsWithoutNewConnection

             if who != "x"
-                console.log "acting as #{ who }, normal peer"

                 peer.on "connection", (connection) =>

-                    console.log "connection from #{ connection.peer }"

                     connection.on "open", =>

-                        console.log "connection from #{ connection.peer } open"

                         @connections.push connection

                         @listenForMessage connection

                 connection = peer.connect "x"
-                console.log "connecting to x, host"

                 connection.on "error", (error) =>
                     alert error

                 connection.on "open", =>
-                    console.log "connection to #{ connection.peer } open"

                     @connections.push connection

@@ -216,10 +202,8 @@
                         if data.type == "newConnection"
                             peerIds = data.peerIds

-                            console.log "new connections to connect to: #{ peerIds }"

                             _.forEach peerIds, (peerId) =>
-                                console.log "connecting to #{ peerId }"

                                 connection = peer.connect peerId

@@ -227,7 +211,6 @@
                                     alert error.type

                                 connection.on "open", =>
-                                    console.log "connection to #{ connection.peer } open"

                                     @connections.push connection

@@ -248,13 +231,11 @@
             message: message

         _.forEach @connections, (c) ->
-            console.log "sending message to #{ c.peer }"
             c.send data

     listenForMessage: (connection) ->
         connection.on "data", (data) =>
             if data.type == "message"
-                console.log "new message from #{ connection.peer }"

                 messages = @state.messages
                 messages.push data.message


Add logging

Add logging

--- src/circle-ci.coffee
+++ src/circle-ci.coffee
@@ -137,6 +137,8 @@
           msg.send "Cleared build cache for #{project}"

   robot.router.post "/hubot/circle", (req, res) ->
+    console.log "Received circle webhook callback"
+
     query = querystring.parse url.parse(req.url).query
     res.end JSON.stringify {
        received: true #some client have problems with an empty response
@@ -146,9 +148,13 @@
     user.room = query.room if query.room
     user.type = query.type if query.type

+    console.log "Received CircleCI payload: #{util.inspect(req.body.payload)}"
+
     try
       robot.send user, formatBuildStatus(req.body.payload)

+      console.log "Send CircleCI build status message"
+
     catch error
       console.log "circle hook error: #{error}. Payload: #{util.inspect(req.body.payload)}"


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -18,7 +18,7 @@
   ###
   @name clamp
   @methodOf Clampable#
-  @param {Object}
+  @param {Object} data
   ###
   clamp: (data) ->
     Object.extend(I.clampData, data)


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -18,7 +18,7 @@
   ###
   @name clamp
   @methodOf Clampable#
-  @param
+  @param {Object}
   ###
   clamp: (data) ->
     Object.extend(I.clampData, data)


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -26,7 +26,7 @@
   ###*
   @name clampToBounds
   @methodOf Clampable#
-  @param {Rectangle} [bounds] The bounds to clamp the object's position
+  @param {Rectangle} [bounds] The bounds to clamp the object's position within.
   ###
   clampToBounds: (bounds) ->
     bounds ||= Rectangle x: 0, y: 0, width: App.width, height: App.height


selection is trigger by click instead of mousedown


use 'ruletype' in app.coffee for clarity

--- assets/js/app.coffee
+++ assets/js/app.coffee
@@ -29,8 +29,8 @@
     my: "top center"
     at: "bottom center"

-  # Annotation families as defined in `_config.yml`
-  families:
+  # Annotation rule types as defined in `_config.yml`
+  ruletypes:
     required: "Required"
     permitted: "Permitted"
     forbidden: "Forbidden"
@@ -45,20 +45,20 @@
   initTooltips: ->

     # Dynamically add annotations as title attribute to rule list items
-    for family, rules of window.annotations
+    for ruletype, rules of window.annotations
       for rule in rules
-        $(".license-rules ul.license-#{family} li.#{rule["tag"]}").attr "title", rule["description"]
+        $(".license-rules ul.license-#{ruletype} li.#{rule["tag"]}").attr "title", rule["description"]

     # Init tooltips on all rule list items
-    for family, label of @families
-      $(".license-#{family} li").qtip
+    for ruletype, label of @ruletypes
+      $(".license-#{ruletype} li").qtip
         content:
           text: false
           title:
             text: label
         position: @qtip_position
         style:
-          classes: "qtip-shadow qtip-#{family}"
+          classes: "qtip-shadow qtip-#{ruletype}"

     false


CUI.dom.remove: Fixed passing a node with a ".DOM" property.

--- src/base/dom.coffee
+++ src/base/dom.coffee
@@ -324,15 +324,14 @@
 	@getAttribute: (node, key) ->
 		node.getAttribute(key)

-	@remove: (element) ->
-		if not element
+	@remove: (_node) ->
+		node = (_node?.DOM or _node)
+
+		if not node
 			return null

-		if element.DOM
-			element.parentNode?.removeChild(element.DOM)
-		else
-			element.parentNode?.removeChild(element)
-		element
+		node.parentNode?.removeChild(node)
+		node

 	@empty: (node) ->
 		if not node


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -93,8 +93,9 @@
         else
           eventCallbacks[event] = []
     else if namespace
-      eventCallbacks[event] = eventCallbacks.select (callback) ->
-        !callback.__PIXIE?[namespace]?
+
+        eventCallbacks[event] = eventCallbacks.select (callback) ->
+          !callback.__PIXIE?[namespace]?

     return this


Remove slashes before check

Remove slashes before check

--- kites/applications/api.coffee
+++ kites/applications/api.coffee
@@ -159,7 +159,7 @@
               manifestIsSafe = no
               callback new KodingError "Parsing manifest failed.", e
             if manifestIsSafe
-              if appRootPath isnt normalizeUserPath username, manifest.path
+              if appRootPath.replace(/^\/|\/$/g, '') isnt normalizeUserPath(username, manifest.path).replace(/^\/|\/$/g, '')
                 callback new KodingError "Paths are different in manifest, exiting."
                 log.error appRootPath, normalizeUserPath username, manifest.path
               else


calculateTop #347

calculateTop #347

--- app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
@@ -128,13 +128,18 @@
     return (left + right) / 2

+  calculateTop : (chainCount, top, mode) ->
+
+    if mode == @MODE_NORMAL or chainCount < 3
+      return top + chainCount * @nodeDistance
+    else if mode == @MODE_NOCHAIN
+      return top + 2 * @nodeDistance
+
+
   # Calculate the top of the children
-  calculateChildTreeTop : (mode, chainCount, top) ->
-
-    if mode == @MODE_NORMAL or chainCount < 3
-      return top + (chainCount + 1) * @nodeDistance
-    else if mode == @MODE_NOCHAIN
-      return top + 3 * @nodeDistance
+  calculateChildTop : (chainCount, top, mode) ->
+
+    return @calculateTop(chainCount, top, mode) + @nodeDistance

   clearBackground : ->
@@ -195,7 +200,7 @@

   drawCommentChain : (decision, left, right, top, mode) ->

-    topChild = @calculateChildTreeTop(mode, decision.chainCount, top) - @nodeDistance
+    topChild = @calculateTop(decision.chainCount, top, mode)
     extent = @drawTreeWithWidths(decision.node, left, right, topChild, mode)
     return extent.middle

@@ -203,7 +208,7 @@
   drawBranch : (decision, left, right, top, mode) ->

     branchMiddle = @calculateBranchMiddle(decision, left, right)
-    topChildren = @calculateChildTreeTop(mode, decision.chainCount, top)
+    topChildren = @calculateChildTop(decision.chainCount, top, mode)
     leftTree = @drawTreeWithWidths(decision.node.children[0], left,  branchMiddle, topChildren, mode)
     rightTree = @drawTreeWithWidths(decision.node.children[1], branchMiddle, right, topChildren, mode)

@@ -276,6 +281,7 @@
       chainCount,
       isBranch: tree.children.length > 1
       isLeaf: tree.children.length == 0
+      isComment: @nodeIdHasComment(tree.id)
     }


Better key management

Better key management

--- src/cacheitem.coffee
+++ src/cacheitem.coffee
@@ -9,14 +9,20 @@
   fields: [
     "url"
     "token"
-    "lastModified"
     "etag"
+    "body"
     "createdAt"
     "updatedAt"
   ]

+CacheItem.pkey = () ->
+  "urlAndToken"
+
+makeKey = (url, token) ->
+  "#{url}|#{token}"
+
 CacheItem.beforeCreate = (props, callback) ->
-  props.urlAndToken = "#{token}|#{url}"
+  props.urlAndToken = makeKey props.url, props.token
   props.createdAt = props.updatedAt = (new Date()).toISOString()
   callback null, props

@@ -26,20 +32,19 @@

 CacheItem::beforeSave = (callback) ->
   if !@urlAndToken
-    @urlAndToken = "#{@token}|#{@url}"
+    @urlAndToken = makeKey @url, @token
   @updatedAt = (new Date()).toISOString()
   if !@createdAt
     @createdAt = @updatedAt
   callback null

 CacheItem.byUrlAndToken = (url, token, callback) ->
-  urlAndToken = "#{token}|#{url}"
+  urlAndToken = makeKey url, token
   CacheItem.get urlAndToken, (err, cacheItem) ->
-    if err
-      if err.name == "NoSuchThingError"
-        callback null, null
-      else
-        callback err
+    if err && err.name == "NoSuchThingError"
+      callback null, null
+    else if err
+      callback err
     else
       callback null, cacheItem


Update list.coffee

Update list.coffee

--- src/cmd/list.coffee
+++ src/cmd/list.coffee
@@ -10,6 +10,6 @@
              .map((s) -> s['Data']['Series'][0])
              .forEach (s) -> printSeries(s, false)
     else
-      return error("No Series Added Yet. Try `tv lookup <title>` first. And then `tv add <id>`")
+      return error("No Series Added Yet. Try `tvcl lookup <title>` first. And then `tvcl add <id>`")

 module.exports = list


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -25,7 +25,7 @@
       min: 0
       max: 100

-  # Score
+  # Score can only be positive
   player.clamp
     score:
       min: 0


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -3,7 +3,7 @@
 GameObject's properties.

-# Hea
+# Health reg
 player = GameObject
   health: 50


Fix Message signature via API

Fix Message signature via API

--- app/src/controllers/wallet/message/wallet_message_index_dialog_view_controller.coffee
+++ app/src/controllers/wallet/message/wallet_message_index_dialog_view_controller.coffee
@@ -10,12 +10,11 @@
     super
     @_isEditable = @params.editable || no
     chrome.app.window.current().show()
-    @view.derivationPath.val("m/" + @params.path)
-    @view.message.val(@message)
-
+    @view.derivationPath.val("m/" + Api.cleanPath(@params.path))
+    @view.message.val(@params.message)
     unless @_isEditable
-      @view.message.attr("read-only", on)
-      @view.derivationPath.attr("read-only", on)
+      @view.message.attr("readonly", on)
+      @view.derivationPath.attr("readonly", on)

   cancel: ->
     unless @_isEditable


Remove undesired console.log

Remove undesired console.log

--- packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee
+++ packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee
@@ -19,8 +19,6 @@

 RocketChat.API.v1.addAuthMethod ->
-	console.log @request.method, @request.url
-
 	headerToken = @request.headers['authorization']
 	getToken = @request.query.access_token


update to use trees

update to use trees

--- lib/eval.coffee
+++ lib/eval.coffee
@@ -23,12 +23,9 @@
     editor = atom.workspace.getActiveTextEditor()
     for sel in editor.getSelections()
       comm.msg 'eval', @evalData(editor, sel), ({start, end, result}) =>
-        [header, [body]] = result
         @ink?.results.showForLines editor, start-1, end-1,
           content:
-            @ink.results.collapsible
-              header: header
-              body: body
+            @ink.tree.fromJson(result)[0]
         notifications.show "Evaluation Finished"

   evalAll: ->


Unload package before upgrade

Unload package before upgrade

--- src/app/package-manager.coffee
+++ src/app/package-manager.coffee
@@ -45,6 +45,7 @@
   activateOnSuccess = !atom.isPackageDisabled(name)
   activateOnFailure = atom.isPackageActive(name)
   atom.deactivatePackage(name) if atom.isPackageActive(name)
+  atom.unloadPackage(name) if atom.isPackageLoaded(name)

   apm = require.resolve '.bin/apm'
   apmProcess = spawn(apm, ['install', "#{name}@#{version}"])


componentWillUnmount should not be called manually

--- resources/assets/coffee/react/beatmaps/paginator.coffee
+++ resources/assets/coffee/react/beatmaps/paginator.coffee
@@ -34,7 +34,6 @@
   componentDidMount: =>
     Timeout.set 0, @throttledAutoPagerOnScroll
     $(window).on 'scroll.paginator', @throttledAutoPagerOnScroll
-    $(document).on 'turbolinks:before-cache.paginator', @componentWillUnmount

   componentWillUnmount: =>


append vars

append vars

--- honey/index.coffee
+++ honey/index.coffee
@@ -88,9 +88,11 @@
     __pub_tmpl_comp: "#{pubTemplate}component/"
     #__pub_tmpl_module: "#{pubTemplate}module"
     __pub_tmpl_ui: "#{pubTemplate}ui"
-    __pub_tmpl_widget: "#{pubTemplate}widget"
+    __pub_tmpl_widget: "#{pubTemplate}widget/"
     #全局组件的css
     __pub_css_comp: if isProduction then pubCssCompProduction else "/imgotv-pub/css/component/"
+    #demo图片的地址
+    __img_demo: if isProduction then "http://image-demo.lab.hunantv.com/#{project_name}/" else "/image-demo/"

   #把变量加到global中去
   jsonData = silky.data.json


improve poplist scrolling for chrome

improve poplist scrolling for chrome

--- addon/lib/eui-poplist/list-view.coffee
+++ addon/lib/eui-poplist/list-view.coffee
@@ -11,13 +11,12 @@
   rowHeight: Ember.computed.alias 'controller.listRowHeight'

   setup: (->
-    # Prevents mouse scroll events from passing through to the div
-    # behind the poplist when listView is scrolled to the end.
-    @.$().bind('mousewheel.emberui', (e) =>
-      e.preventDefault()
-      scrollTo = @get('scrollTop') + (e.originalEvent.wheelDelta * -1)
+    # Prevents chrome from scrolling the page via momentum when scrolling the list
+    @.$().bind('scroll.emberui', (e) =>
+      scrollTop = $(window).scrollTop()

-      @scrollTo(scrollTo)
+      Ember.run.next @, ->
+        $(window).scrollTop(scrollTop)
     )

     # Firefox
@@ -25,6 +24,7 @@
       e.cancelBubble = true
     )
   ).on 'didInsertElement'
+

   breakdown: (->
     @.$().unbind('mousewheel.emberui DOMMouseScroll.emberui')


Don't complain if the window is not there

--- tilde/zephyros.coffee
+++ tilde/zephyros.coffee
@@ -119,6 +119,7 @@

 moveWindow = (fn) ->
   win = api.focusedWindow()
-  frame = win.screen().frameWithoutDockOrMenu()
-  fn win, frame if fn
-  win.setFrame frame
+  if win?
+    frame = win.screen().frameWithoutDockOrMenu()
+    fn win, frame if fn
+    win.setFrame frame


clean up whitespace

clean up whitespace

--- services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee
+++ services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee
@@ -32,11 +32,11 @@
 		methodWithLock.withoutLock = methodWithoutLock
 		methodWithLock
 	else
-		# handle case with separate setup and locked stages
+		# handle case with separate setup and locked stages
 		wrapWithSetup = methodWithoutLock.beforeLock  # a function to set things up before the lock
 		mainTask = methodWithoutLock.withLock # function to execute inside the lock
 		methodWithLock = wrapWithSetup (project_id, args..., callback) ->
-			LockManager.runWithLock(LOCK_NAMESPACE, project_id, (cb) ->
+			LockManager.runWithLock(LOCK_NAMESPACE, project_id, (cb) ->
 				mainTask(project_id, args..., cb)
 			callback)
 		methodWithLock.withoutLock = wrapWithSetup mainTask
@@ -160,9 +160,9 @@
 				return callback(err)
 			TpdsUpdateSender.addFile {project_id:project_id, file_id:fileRef._id, path:result?.path?.fileSystem, project_name:project.name, rev:fileRef.rev}, (err) ->
 				return callback(err) if err?
-				callback(null, result, project)
-
-	addFile: wrapWithLock
+				callback(null, result, project)
+
+	addFile: wrapWithLock
 		beforeLock: (next) ->
 			(project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback) ->
 				ProjectEntityUpdateHandler.uploadFile project_id, folder_id, fileName, fsPath, linkedFileData, userId, (error, fileRef, fileStoreUrl) ->
@@ -178,7 +178,7 @@
 				]
 				DocumentUpdaterHandler.updateProjectStructure project_id, userId, {newFiles}, (error) ->
 					return callback(error) if error?
-					callback(null, fileRef, folder_id)
+					callback(null, fileRef, folder_id)

 	replaceFile: wrapWithLock
 		beforeLock: (next) ->
@@ -258,9 +258,9 @@
 					callback null, doc, !existingDoc?

 	# FIXME: this method needs to be changed, currently it needs a lock around an upload to S3 because
-	# there is no way to know in advance if the file is a replace or an insert (at least you have to
+	# there is no way to know in advance if the file is a replace or an insert (at least you have to
 	# take the lock to find that out and do any operation in that lock).  In the new scheme any upsert
-	# should always create the new file (outside the lock) in S3 first and then make the changes to the
+	# should always create the new file (outside the lock) in S3 first and then make the changes to the
 	# project in a lock, marking the old file as deleted if it exists.
 	upsertFile: wrapWithLock (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback = (err, file, isNewFile)->)->
 		ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) ->
@@ -373,7 +373,7 @@
 					path: file.path
 					url: FileStoreHandler._buildUrl(project_id, file.file._id)

-				DocumentUpdaterHandler.resyncProjectHistory project_id, docs, files, callback
+				DocumentUpdaterHandler.resyncProjectHistory project_id, docs, files, callback
 	_cleanUpEntity: (project, entity, entityType, path, userId, callback = (error) ->) ->
 		if(entityType.indexOf("file") != -1)
 			self._cleanUpFile project, entity, path, userId, callback


added timefreeze to recurly wrapper, don't think it really works but never know *double shurg*

--- services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
+++ services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
@@ -4,6 +4,7 @@
 querystring = require 'querystring'
 RecurlyWrapper = require "../../../../app/js/Features/Subscription/RecurlyWrapper"
 Settings = require "settings-sharelatex"
+tk = require("timekeeper")

 fixtures =
 	"subscriptions/44f83d7cba354d5b84812419f923ea96":
@@ -107,7 +108,11 @@
 		Settings.defaultPlanCode =
 			collaborators: 0
 			versioning: false
-
+		tk.freeze(Date.now())
+
+	afterEach ->
+		tk.reset()
+
 	describe "sign", ->
 		before (done) ->
 			RecurlyWrapper.sign({


Rename editSession to editor

Rename editSession to editor

--- lib/link.coffee
+++ lib/link.coffee
@@ -1,10 +1,10 @@
 module.exports =
   activate: ->
     atom.workspaceView.command 'link:open', ->
-      editSession = atom.workspaceView.getActivePaneItem()
-      return unless editSession?
+      editor = atom.workspaceView.getActivePaneItem()
+      return unless editor?

-      token = editSession.tokenForBufferPosition(editSession.getCursorBufferPosition())
+      token = editor.tokenForBufferPosition(editor.getCursorBufferPosition())
       return unless token?

       unless @selector?


re-order prefs; add some descriptions

re-order prefs; add some descriptions

--- lib/main.coffee
+++ lib/main.coffee
@@ -7,47 +7,68 @@
       description: 'Lint files while typing, without the need to save them'
       type: 'boolean'
       default: true
-    showErrorPanel:
-      title: 'Show Error Panel at the bottom'
-      type: 'boolean'
-      default: true
-    showErrorTabLine:
-      title: 'Show Line tab in Bottom Panel'
-      type: 'boolean'
-      default: false
-    showErrorTabFile:
-      title: 'Show File tab in Bottom Panel'
-      type: 'boolean'
-      default: true
-    showErrorTabProject:
-      title: 'Show Project tab in Bottom Panel'
-      type: 'boolean'
-      default: true
-    showErrorInline:
-      title: 'Show Inline Tooltips'
-      descriptions: 'Show inline tooltips for errors'
-      type: 'boolean'
-      default: true
-    underlineIssues:
-      title: 'Underline Issues'
-      type: 'boolean'
-      default: true
+      order: 1
+
     ignoredMessageTypes:
       title: "Ignored message Types"
       type: 'array'
       default: []
       items:
         type: 'string'
+      order: 2
+
+    showErrorInline:
+      title: 'Show Inline Tooltips'
+      description: 'Show inline tooltips for errors'
+      type: 'boolean'
+      default: true
+      order: 3
+    underlineIssues:
+      title: 'Underline Issues'
+      type: 'boolean'
+      default: true
+      order: 3
+
+    showErrorPanel:
+      title: 'Show Error Panel at the Bottom'
+      description: 'Show a list of errors in a bottom panel'
+      type: 'boolean'
+      default: true
+      order: 4
+
+    displayLinterInfo:
+      title: 'Display Linter Info in Status Bar'
+      description: 'Whether to show any linter information in the status bar'
+      type: 'boolean'
+      default: true
+      order: 5
+    showErrorTabLine:
+      title: 'Show Line tab in Status Bar'
+      type: 'boolean'
+      default: false
+      order: 5
+    showErrorTabFile:
+      title: 'Show File tab in Status Bar'
+      type: 'boolean'
+      default: true
+      order: 5
+    showErrorTabProject:
+      title: 'Show Project tab in Status Bar'
+      type: 'boolean'
+      default: true
+      order: 5
     statusIconScope:
       title: "Scope of messages to show in status icon"
       type: 'string'
       enum: ['File', 'Line', 'Project']
       default: 'Project'
+      order: 5
     statusIconPosition:
-      title: 'Position of Status Icon on Bottom Bar'
+      title: 'Position of Status Icon in Status Bar'
       enum: ['Left', 'Right']
       type: 'string'
       default: 'Left'
+      order: 5

   activate: (@state) ->
     LinterPlus = require('./linter.coffee')


Use a config schema

Use a config schema

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,8 +1,10 @@
 GitHubFile  = require './github-file'

 module.exports =
-  configDefaults:
-    includeLineNumbersInUrls: true
+  config:
+    includeLineNumbersInUrls:
+      default: true
+      type: 'boolean'

   activate: ->
     return unless atom.project.getRepo()?


WIP #3 now work

WIP #3 now work

--- lib/main.coffee
+++ lib/main.coffee
@@ -42,7 +42,6 @@
     @subscriptions.add @observeTextEditors()
     @subscriptions.add @observeActivePaneItem()
     @subscriptions.add @observeOnWillDestroyPaneItem()
-    # @subscriptions.add @extendSymbolsView()

   deactivate: ->
     for editorID, disposables of @editorSubscriptions
@@ -70,9 +69,9 @@
       view.openTag = withAudit openTag
       openTag

+    @openTags = {}
     atom.packages.onDidActivatePackage (pack) ->
       return unless pack.name is 'symbols-view'
-      console.log "CALLED"
       main = pack.mainModule

       @openTags['FileView']    = extendOpenTag main.createFileView()
@@ -80,100 +79,92 @@
       @openTags['GoToView']    = extendOpenTag main.createGoToView()
       @openTags['GoBackView']  = extendOpenTag main.createGoBackView()

-      # fileView = main.createProjectView()
-      # fileView = main.createGoToView()
-
-      # fileView.openTag = (params...) ->
-      #   console.log "Before"
-      #   console.log @stack
-      #   console.log @constructor.name
-      #   openTag params...
-      #   console.log "After"
-      #
-      console.log result
-      # libPath = path.join(atom.packages.resolvePackagePath('symbols-view') , 'lib', 'symbols-view')
-      # SymbolsView = require libPath
-      # _openTag = SymbolsView::openTag
-      # SymbolsView::openTag = (params...) ->
-      #   # if @constructor
-      #   # setTimeout =>
-      #   #   console.log "before", @panel.isVisible()
-      #   # , 300
-      #   console.log "Before"
-      #   console.log @stack
-      #   console.log @constructor.name
-      #   _openTag.call(this, params...)
-      #   console.log "After"
-      #   console.log @stack
-      #   # setTimeout =>
-      #   #   console.log "after", @panel.isVisible()
-      #   # , 300
-
+  # For better handling symbols-view.
+  # -------------------------
   observeModalPanel: ->
+    withPanel = (panel, {onDidShow, onDidHide}) =>
+      [oldEditor, oldPoint, newEditor, newPoint] = []
+      panelSubscription = panel.onDidChangeVisible (visible) =>
+        if visible
+          @lock()
+          {editor: oldEditor, point: oldPoint} = onDidShow()
+        else
+          # Final position is set after some delay from panel hidden.
+          setTimeout =>
+            {editor: newEditor, point: newPoint} = onDidHide()
+
+            # FIXME: oldPoint sometimes became undefined, so need to guard
+            if oldPoint and newPoint and @needRemember(oldPoint, newPoint)
+              @history.add oldEditor, oldPoint, oldEditor.getURI(), dumpMessage: "[Cursor moved] save history"
+            @unLock()
+          , 300
+
+      @subscriptions.add panel.onDidDestroy =>
+        panelSubscription.dispose()
+
     symbolsViewHandlers =
-      FileView: (panel) ->
-        @withPanel panel,
+      FileView: (panel) =>
+        withPanel panel,
           # At the timing symbol-views panel show, first item in symobls
           # already selected(this mean cursor position have changed).
           # So we can't use TexitEditor::getCursorBufferPosition(), fotunately,
           # FileView serialize buffer state initaially, we use this.
-          onShow: =>
-            console.log "Shown FileView"
+          onDidShow: =>
+            @debug "Shown FileView"
             editor = @getEditor()
             point  = panel.getItem().initialState?.bufferRanges[0].start
             {editor, point}
-          onHide: =>
-            console.log "Hidden FileView"
-            editor = @getEditor()
-            point  = editor.getCursorBufferPosition()
-            {editor, point}
-
-      ProjectView: (panel) ->
-        @withPanel panel,
-          onShow: =>
-            console.log "Shown ProjectView"
-            editor = @getEditor()
-            point  = editor.getCursorBufferPosition()
-            {editor, point}
-          onHide: =>
-            console.log "Hidden ProjectView"
-            editor = @getEditor()
-            point  = editor.getCursorBufferPosition()
+          onDidHide: =>
+            @debug "Hidden FileView"
+            editor = @getEditor()
+            point  = @getPosition editor
+            {editor, point}
+
+      ProjectView: (panel) =>
+        withPanel panel,
+          onDidShow: =>
+            @debug "Shown ProjectView"
+            editor = @getEditor()
+            point  = @getPosition editor
+            {editor, point}
+          onDidHide: =>
+            @debug "Hidden ProjectView"
+            editor = @getEditor()
+            point  = @getPosition editor
             {editor, point}

     atom.workspace.panelContainers['modal'].onDidAddPanel ({panel, index}) =>
-      item = panel.getItem()
-      name = item.constructor.name
       # [CAUTION] Simply checking constructor name is not enough.
       # e.g. ProjectView is also used in `fuzzy-finder`.
       # return unless name in ['GoToView', 'GoBackView', 'FileView', 'ProjectView']
-      return unless (name in ['FileView', 'ProjectView']) and
-        typeof(item.openTag) is 'function'
-
-      symbolsViewHandlers[name]?.bind(this)(panel)
+      item = panel.getItem()
+      name = item.constructor.name
+      if name in ['FileView', 'ProjectView'] and typeof(item.openTag) is 'function'
+        symbolsViewHandlers[name]?(panel)

   observeTextEditors: ->
     onDidChangePath = (editor) =>
-      orgURI = editor.getURI()
+      oldURI = editor.getURI()

       editor.onDidChangePath =>
         newURI = editor.getURI()
-        @history.rename orgURI, newURI
-        @lastEditor.rename orgURI, newURI
-        orgURI = newURI
+        @history.rename oldURI, newURI
+        @lastEditor.rename oldURI, newURI
+        oldURI = newURI

     onDidChangeCursorPosition = (editor) =>
       editor.onDidChangeCursorPosition ({oldBufferPosition, newBufferPosition}) =>
         return if @isLocked() # for performance
         return if editor.hasMultipleCursors()
-        return unless URI = editor.getURI()
-        return if oldBufferPosition.row is newBufferPosition.row # for performance.
+        return if oldBufferPosition.row is newBufferPosition.row # for performance
+        return unless @needRemember(oldBufferPosition, newBufferPosition)
+
         setTimeout =>
           # When symbols-view's modal panel shown, we lock() but its delayed.
-          # So checking lock state here is important.
+          # So we need lock state again here after some delay.
           return if @isLocked()
-          if @needRemember(oldBufferPosition, newBufferPosition)
-            @history.add editor, oldBufferPosition, URI, dumpMessage: "[Cursor moved] save history"
+          # @debug "Move #{oldBufferPosition.toString()} -> #{newBufferPosition.toString()}"
+          @history.add editor, oldBufferPosition, editor.getURI(), dumpMessage: "[Cursor moved] save history"
         , 300

     onDidDestroy = (editor) =>
@@ -182,6 +173,7 @@
         delete @editorSubscriptions[editor.id]

     atom.workspace.observeTextEditors (editor) =>
+      return unless editor.getURI()
       @editorSubscriptions[editor.id] = new CompositeDisposable
       @editorSubscriptions[editor.id].add onDidChangePath(editor)
       @editorSubscriptions[editor.id].add onDidChangeCursorPosition(editor)
@@ -208,56 +200,34 @@
         LastEditor.saveDestroyedEditor item

   needRemember: (oldBufferPosition, newBufferPosition) ->
-    # console.log [oldBufferPosition, newBufferPosition]
     # Line number delata exceeds or not.
     Math.abs(oldBufferPosition.row - newBufferPosition.row) > @rowDeltaToRemember

   # Helpers
   #-----------------
-  withPanel: (panel, {onShow, onHide}) ->
-    [oldEditor, oldPoint, newEditor, newPoint] = []
-    panelSubscription = panel.onDidChangeVisible (visible) =>
-      if visible
-        @lock panel.getItem().constructor.name
-        {editor: oldEditor, point: oldPoint} = onShow()
-      else
-        # ProjectView delayed changing cursor position after panel hidden(),
-        setTimeout =>
-          {editor: newEditor, point: newPoint} = onHide()
-
-          # FIXME: oldPoint sometimes became undefined, so need to guard
-          if oldPoint and newPoint and @needRemember(oldPoint, newPoint)
-            @history.add oldEditor, oldPoint, oldEditor.getURI(), dumpMessage: "[Cursor moved] save history"
-          @unLock()
-        , 300
-
-    @subscriptions.add panel.onDidDestroy ->
-      panelSubscription.dispose()
-
   lock:     -> @locked = true
   unLock:   -> @locked = false
   isLocked: -> @locked

   jump: (direction) ->
     # Settings tab is not TextEditor instance.
-    return unless activeEditor = @getEditor()
-    return unless entry = @history["get#{direction}"]()
+    return unless editor = @getEditor()
+    return unless entry  = @history["get#{direction}"]()

     if direction is 'Prev' and @history.isNewest()
-      point = activeEditor.getCursorBufferPosition()
-      URI   = activeEditor.getURI()
-      @history.pushToHead activeEditor, point, URI
+      point = @getPosition()
+      URI   = editor.getURI()
+      @history.pushToHead editor, point, URI

     @lock()

     {URI, point} = entry
-
-    if activeEditor.getURI() is URI # Same pane.
+    if editor.getURI() is URI # Same pane.
       # Intentionally disable `autoscroll` to set cursor position middle of
       # screen afterward.
-      activeEditor.setCursorBufferPosition point, autoscroll: false
+      editor.setCursorBufferPosition point, autoscroll: false
       # Adjust cursor position to middle of screen.
-      activeEditor.scrollToCursorPosition()
+      editor.scrollToCursorPosition()
       @land direction
     else
       # Jump to different pane
@@ -278,6 +248,9 @@
   getEditor: ->
     atom.workspace.getActiveTextEditor()

+  getPosition: (editor=@getEditor())->
+    editor.getCursorBufferPosition()
+
   dump: ->
     console.log @isLocked()
     @history.dump '', true


overhaul config

overhaul config

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,9 +1,20 @@
 # Dependencies
 {exec} = require 'child_process'
+os = require 'os'
+
+if os.platform() is 'win32'
+  which  = "where"
+else
+  which  = "which"

 module.exports = InnoSetupCore =
+  config:
+    pathToISCC:
+      title: "Path To ISCC"
+      description: "Specify the full path to `ISCC.exe`"
+      type: "string"
+      default: ""
   subscriptions: null
-  which: null

   activate: (state) ->
     {CompositeDisposable} = require 'atom'
@@ -40,23 +51,14 @@
         exec "\"ISCC\" \"#{script}\"", (error, stdout, stderr) ->
           if error isnt null
             # isccBin error from stdout, not error!
-            atom.notifications.addError(script, detail: error, dismissable: true)
+            atom.notifications.addError("**#{script}**", detail: error, dismissable: true)
           else
             atom.notifications.addSuccess("Compiled successfully", detail: stdout, dismissable: false)
     else
       # Something went wrong
       atom.beep()
-      if atom.config.get('language-innosetup.debug') is true
-        console.log "[language-innosetup] Scope: #{scope}"

   getPath: (callback) ->
-    os = require 'os'
-
-    if os.platform() is 'win32'
-      which  = "where"
-    else
-      which  = "which"
-
     # If stored, return pathToISCC
     pathToISCC = atom.config.get('language-innosetup.pathToISCC')
     if pathToISCC?


Deprecation warn fix: use es6's promise.then instead of Q.done

--- lib/main.coffee
+++ lib/main.coffee
@@ -61,6 +61,6 @@
       when 'down' then pane.splitDown()
       when 'right' then pane.splitRight()

-    atom.workspace.open(filePath, searchAllPanes: false).done ->
+    atom.workspace.open(filePath, searchAllPanes: false)

   deactivate: ->


Allow overriding error_reporting from php.ini

Some users have a non-default `error_reporting` level which hides most errors, making `linter-php` appear to do nothing. Default to overriding the php.ini setting and forcing `E_ALL`.

--- lib/main.coffee
+++ lib/main.coffee
@@ -8,6 +8,12 @@
       type: 'string'
       title: 'PHP Executable Path'
       default: 'php' # Let OS's $PATH handle the rest
+    errorReporting:
+      type: 'boolean'
+      title: 'Error Reporting Level Override'
+      description: 'Force overriding the `error_reporting` setting from php.ini ' +
+        'to `E_ALL` if php.ini is setup to ignore most errors.'
+      default: true

   _testBin: ->
     title = 'linter-php: Unable to determine PHP version'
@@ -30,6 +36,9 @@
       (executablePath) =>
         @executablePath = executablePath
         @_testBin()
+    @subscriptions.add atom.config.observe 'linter-php.errorReporting',
+      (value) =>
+        @errorReporting = value

   deactivate: ->
     @subscriptions.dispose()
@@ -48,6 +57,7 @@
         parameters.push('--syntax-check')
         parameters.push('--define', 'display_errors=On')
         parameters.push('--define', 'log_errors=Off')
+        parameters.push('--define', 'error_reporting=E_ALL') if @errorReporting
         text = textEditor.getText()
         [projectPath] = atom.project.relativizePath(filePath)
         cwd = if projectPath? then projectPath else path.dirname(filePath)


Keep default as false

Keep default as false

--- lib/main.coffee
+++ lib/main.coffee
@@ -3,7 +3,7 @@
 module.exports =
   config:
     createInDevMode:
-      default: true
+      default: false
       type: 'boolean'

   activate: ->


Fix linter errors

Fix linter errors

Fix a few minor code style issues exposed by `coffeelint`.

--- lib/main.coffee
+++ lib/main.coffee
@@ -46,8 +46,8 @@
       lintOnFly: true
       lint: (textEditor) =>
         filePath = textEditor.getPath()
-        if @disableWhenNoJshintrcFileInPath and !helpers.findFile(filePath, '.jshintrc')
-            return []
+        if @disableWhenNoJshintrcFileInPath and not helpers.findFile(filePath, '.jshintrc')
+          return []

         text = textEditor.getText()
         parameters = ['--reporter', reporter, '--filename', filePath]


Revise config option description.

Revise config option description.

--- lib/main.coffee
+++ lib/main.coffee
@@ -12,7 +12,7 @@
     resolvePathsRelativeToConfig:
       title: 'Resolve paths in configuration relative to config file'
       type: 'boolean'
-      description: '(and not relative to project root)'
+      description: 'Instead of the default where paths are resolved relative to the project root'
       default: 'false'
     configFile:
       title: '.sass-lint.yml Config File'


implement 'i can see a root index page'

implement 'i can see a root index page'

--- integration_test/cobalt.coffee
+++ integration_test/cobalt.coffee
@@ -8,7 +8,7 @@
 _ = require 'underscore'

 host = 'boxecutor-int-test-0.scraperwiki.net'
-baseurl = "http://#{host}/"
+baseurl = "http://#{host}"

 cobalt_api_key = process.env.COTEST_USER_API_KEY
 boxname = 'cotest/' + String(Math.random()).replace(/\./,'')
@@ -53,7 +53,18 @@
     it 'I can see my git repo'
     it 'I cannot see any other box'
   describe 'When I publish some files', ->
-    it 'I can see a root index page'
+    before (done) ->
+      cmd = "echo -n Testing > http/index.html"
+      ssh = "ssh #{boxname}@#{host} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -o PreferredAuthentications=publickey -o LogLevel=ERROR -i #{sshkey_prv_path} '#{cmd}'"
+      exec ssh, (err, stdout_, stderr_) ->
+          done()
+
+    it 'I can see a root index page', (done) ->
+      response = request.get "#{baseurl}/#{boxname}/http/", (err, resp, body) ->
+        resp.should.have.status 200
+        body.should.equal 'Testing'
+        done()
+
     it 'I can see normal files'
     it 'I can see an index page for subdirectories'
     it 'I can follow my own symlinks'


Popovers now hide and show correctly

Popovers now hide and show correctly

--- app/assets/javascripts/users.js.coffee
+++ app/assets/javascripts/users.js.coffee
@@ -24,28 +24,29 @@
   )
   $('.comment-user-name').click((e)->
     if isVisible
-      console.log("isVisible")
-      if not $(this).hasClass('popover-visible')
-        console.log("there is a popup visible and but not this one")
-        $('.comment-user-name').popover('hide')
-        $('.comment-user-name').removeClass('popover-visible')
-        console.log("remove all popups1")
-        $(this).popover('toggle')
-        console.log("show this popup and add class visible")
-        $(this).addClass('popover-visible')
-        isVisible = true
-        clickedAway = false
-      else
-        console.log("clicked popup was visible")
-        $('.comment-user-name').popover('hide')
-        $('.comment-user-name').removeClass('popover-visible')
-        console.log("remove all popups2")
-        isVisible = false
-        clickedAway = false
+      $(this).addClass("selected")
+      $('.comment-user-name').each ->
+        if $(this).hasClass("selected")
+          if $(this).hasClass("popover-visible")
+            toggle_popover(false, $(this))
+            #$(this).popover('hide')
+            #$(this).removeClass('popover-visible')
+            isVisible = false
+          else
+            toggle_popover(true, $(this))
+            #$(this).popover('show')
+            #$(this).addClass('popover-visible')
+            isVisible = true
+        else
+          toggle_popover(false, $(this))
+          #$(this).popover('hide')
+          #$(this).removeClass('popover-visible')
+      $(this).removeClass("selected")
+      clickedAway = false
     else
-      console.log("non-visible, showing popover")
-      $(this).popover('show')
-      $(this).addClass('popover-visible')
+      #$(this).popover('show')
+      #$(this).addClass('popover-visible')
+      toggle_popover(true, $(this))
       clickedAway = false
       isVisible = true
     e.preventDefault()
@@ -53,13 +54,21 @@

   $(document).click((e)->
     if(isVisible & clickedAway)
-      console.log("visible and clickedAway - hiding popover")
+      toggle_popover(false, $('.comment-user-name'))
       #$('.comment-user-name').popover('hide')
       #$('.comment-user-name').removeClass('popover-visible')
       isVisible = false
       clickedAway = false
     else
-      console.log("clickedAway = true")
-      console.log("-------------------")
       clickedAway = true
   )
+
+  toggle_popover = (show, elem) ->
+    #elem.each ->
+    if show
+      elem.popover('show')
+      elem.addClass('popover-visible')
+    else
+      elem.popover('hide')
+      elem.removeClass('popover-visible')
+


remove a console.log

remove a console.log

--- app/assets/javascripts/views.js.coffee
+++ app/assets/javascripts/views.js.coffee
@@ -166,7 +166,6 @@
       ich.post_new topic

     expand_text_area: (e)->
-      console.log e
       $(e.currentTarget).css('height', '100px')

     events: ->


fix bad ID being passed on animal selector

--- app/controllers/animal_selector.coffee
+++ app/controllers/animal_selector.coffee
@@ -5,6 +5,7 @@
 User = require 'zooniverse/lib/models/user'
 AnalyticsLogger = require 'lib/analytics'
 columnize = require 'lib/columnize'
+Subject = require 'models/subject'
 AnimalDetails = require './animal_details'
 getTutorialSubject = require 'lib/get_tutorial_subject'
 getPhysicallyAdjacentSibling = require 'lib/get_physically_adjacent_sibling'
@@ -140,7 +141,7 @@
     setTimeout details.show, 125

   onClickClearFilters: ->
-    AnalyticsLogger.logEvent User.current?.zooniverse_id,@zooniverseId,'clear',''
+    AnalyticsLogger.logEvent User.current?.zooniverse_id,Subject.current?.zooniverseId,'clear',''
     @clearFilters()

   clearFilters: =>
@@ -149,7 +150,7 @@
     @searchInput.trigger 'keydown'

   onClickStartTutorial: ->
-    AnalyticsLogger.logEvent User.current?.zooniverse_id,@zooniverseId,'tutorial',''
+    AnalyticsLogger.logEvent User.current?.zooniverse_id,Subject.current?.zooniverseId,'tutorial',''
     getTutorialSubject().select()

 module.exports = AnimalSelector


don't force message over exception but include it as description

--- libraries/logger/logging-manager.coffee
+++ libraries/logger/logging-manager.coffee
@@ -14,15 +14,14 @@
 	captureException: (attributes, message, level) ->
 		# handle case of logger.error "message"
 		if typeof attributes is 'string'
-			attributes = {err: attributes}
+			attributes = {err: new Error(attributes)}
 		# extract any error object
 		error = attributes.err or attributes.error
-		# use our log message as the title when available
+		# include our log message in the error report
 		if not error?
 			error = {message: message} if typeof message is 'string'
 		else if message?
-			error.exception = error.message
-			error.message = message
+			attributes.description = message
 		# report the error
 		if error?
 			# capture attributes and use *_id objects as tags


Better naming for stored points

Better naming for stored points

--- framer/LayerDraggable2.coffee
+++ framer/LayerDraggable2.coffee
@@ -121,22 +121,22 @@
 			t: touchEvent.timeStamp

 		# Store original layer position
-		@_position =
+		@_layerStartPoint =
 			x: @layer.x
 			y: @layer.y

-		# if @bounce
-		# 	@_position = @_constrainPosition(@_position, @constraints, 1 / @overdragScale)
+		if @bounce
+			@_layerStartPoint = @_constrainPosition(@_layerStartPoint, @constraints, 1 / @overdragScale)

 		# Store start cursor position
-		@_start =
+		@_cursorStartPoint =
 			x: touchEvent.clientX
 			y: touchEvent.clientY

 		# Store cursor/layer offset
-		@_offset =
-			x: touchEvent.clientX - @_position.x
-			y: touchEvent.clientY - @_position.y
+		@_layerCursorOffset =
+			x: touchEvent.clientX - @_layerStartPoint.x
+			y: touchEvent.clientY - @_layerStartPoint.y

 		# Store the current layer scale so we can correct movement for it
 		@_screenScale =
@@ -166,8 +166,8 @@
 			t: touchEvent.timeStamp

 		delta =
-			x: touchEvent.clientX - @_start.x
-			y: touchEvent.clientY - @_start.y
+			x: touchEvent.clientX - @_cursorStartPoint.x
+			y: touchEvent.clientY - @_cursorStartPoint.y

 		# Correct for current drag speed and scale
 		correctedDelta =
@@ -178,8 +178,8 @@
 		# Pixel align all moves
 		if @pixelAlign
 			point =
-				x: parseInt(@_start.x + correctedDelta.x - @_offset.x)
-				y: parseInt(@_start.y + correctedDelta.y - @_offset.y)
+				x: parseInt(@_cursorStartPoint.x + correctedDelta.x - @_layerCursorOffset.x)
+				y: parseInt(@_cursorStartPoint.y + correctedDelta.y - @_layerCursorOffset.y)

 		# Constraints
 		point = @_constrainPosition(point, @_constraints) if @_constraints
@@ -189,11 +189,11 @@
 		# if @directionLock
 		# 	@_updateDirectionLock(correctedDelta) if ! @_directionIsLocked

-		# 	@layer.x = @_position.x if @_xAxisLock
-		# 	@layer.y = @_position.y if @_yAxisLock
+		# 	@layer.x = @_layerStartPoint.x if @_xAxisLock
+		# 	@layer.y = @_layerStartPoint.y if @_yAxisLock
 		# else
-		# 	@layer.x = @_position.x
-		# 	@layer.y = @_position.y
+		# 	@layer.x = @_layerStartPoint.x
+		# 	@layer.y = @_layerStartPoint.y

 		_.extend(@layer, @updatePosition(point))
@@ -216,7 +216,7 @@
 		# # Set _isDragging after DragEnd is fired, so that calls to calculateVelocity()
 		# # still returns dragging velocity - both in case the user calls calculateVelocity(),
 		# # (which would return a stale value before the simulation had finished one tick)
-		# # and because @_startSimulation currently calls calculateVelocity().
+		# # and because @_start currently calls calculateVelocity().
 		@_isDragging = false


Add expectations to context-menu specs

Add expectations to context-menu specs

--- spec/context-menu-manager-spec.coffee
+++ spec/context-menu-manager-spec.coffee
@@ -22,8 +22,16 @@
         '.selector':
           'parent':
             'child-1': 'child-1:trigger'
-            'child-2': 'child-1:trigger'
+            'child-2': 'child-2:trigger'
           'parent-2': 'parent-2:trigger'
+
+      expect(contextMenu.definitions['.selector'].length).toBe 2
+      expect(contextMenu.definitions['.selector'][0].label).toEqual 'parent'
+      expect(contextMenu.definitions['.selector'][0].submenu.length).toBe 2
+      expect(contextMenu.definitions['.selector'][0].submenu[0].label).toBe 'child-1'
+      expect(contextMenu.definitions['.selector'][0].submenu[0].command).toBe 'child-1:trigger'
+      expect(contextMenu.definitions['.selector'][0].submenu[1].label).toBe 'child-2'
+      expect(contextMenu.definitions['.selector'][0].submenu[1].command).toBe 'child-2:trigger'

     describe 'dev mode', ->
       it 'loads',  ->


fix windows npm path

fix windows npm path

--- libs/npm.coffee
+++ libs/npm.coffee
@@ -5,7 +5,7 @@
 GLOBAL_NPM_BIN = fs.realpathSync which.sync 'npm'

 if process.platform is "win32"
-    GLOBAL_NPM_PATH = fs.path.join GLOBAL_NPM_BIN, 'node_modules/npm'
+    GLOBAL_NPM_PATH = fs.path.join GLOBAL_NPM_BIN, '../node_modules/npm'
 else
     GLOBAL_NPM_PATH = fs.path.join GLOBAL_NPM_BIN, '../..'


Test that total is updated when lines change

--- spec/skr/models/SalesOrderSpec.coffee
+++ spec/skr/models/SalesOrderSpec.coffee
@@ -1,7 +1,19 @@
 describe "Skr.Models.SalesOrder", ->

-    xit "must have a customer to save", (done)->
-        model = new Skr.Models.SalesOrder()
-        Lanes.Test.ModelSaver.perform(model, done).then (save)->
-            expect(save.error).toHaveBeenCalled()
-            expect(model.errors.customer).toContain("is not set")
+
+    it "it calculates total", ->
+        so = new Skr.Models.SalesOrder()
+        so.lines.add(sku_code: 'TEST', price: 2.12, qty:1)
+        expect(so.total).toEqual(jasmine.any(_.bigDecimal))
+        expect(so.total.toString()).toEqual('2.12')
+        so.order_total = 1.42
+        expect(so.total.toString()).toEqual('1.42')
+        expect(so.total).toEqual(jasmine.any(_.bigDecimal))
+
+    it 'updates total when lines change', ->
+        so = new Skr.Models.SalesOrder()
+        line = so.lines.add(sku_code: 'TEST', price: 2.12, qty:1)
+        line.price = 3.42
+        expect(so.total.toString()).toEqual('3.42')
+        line.price = 233.111
+        expect(so.total.toString()).toEqual('233.111')


Allow to specify crossOrigin attribute

Allow to specify crossOrigin attribute

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -1004,6 +1004,8 @@
     # See https://github.com/enyo/dropzone/pull/226
     img = document.createElement "img"

+    img.crossOrigin = file.crossOrigin if file.crossOrigin
+
     img.onload = =>
       file.width = img.width
       file.height = img.height


bugfix

bugfix

--- src/frinkiac.coffee
+++ src/frinkiac.coffee
@@ -83,7 +83,7 @@

 module.exports = (robot) ->
   robot.respond /(simpsons (search|me)|frinkiac) (.*)/i, (msg) ->
-    query = msg.match[2].split('|')
+    query = msg.match[3].split('|')
     customCaption = query[1]

     axios(getRequestConfig('search', {q: query[0]}))


fix hammer bug

fix hammer bug

--- testApp/app/controllers/applicationController.coffee
+++ testApp/app/controllers/applicationController.coffee
@@ -1,25 +1,29 @@
-# always put everything inside PhoneGap deviceready

 document.addEventListener "deviceready", ()->

-  $(".opensLayer").hammer().on "tap", () ->
+  $(".opensLayer").hammer().on "tap", (e) ->
+    return if e.gesture.srcEvent.type == "mousedown"

     layer = new steroids.views.WebView { location: @getAttribute("data-location")  }
     steroids.layers.push layer

-  $(".opensModal").hammer().on "tap", () ->
+
+  $(".opensModal").hammer().on "tap", (e) ->
+    return if e.gesture.srcEvent.type == "mousedown"

     layer = new steroids.views.WebView { location: @getAttribute("data-location") }
     steroids.modal.show { layer: layer }

-  $(".closesModal").hammer().on "tap", () ->
+  $(".closesModal").hammer().on "tap", (e) ->
+    return if e.gesture.srcEvent.type == "mousedown"

     steroids.modal.hide()

-  $(".performsTest").hammer().on "tap", () ->
+  $(".performsTest").hammer().on "tap", (e) ->
+    return if e.gesture.srcEvent.type == "mousedown"
+
     eval "#{STEROIDS.controllerName}.#{@getAttribute("data-test")}()"
-    return false


tackled #75: defect CORS proxy

tackled #75: defect CORS proxy

--- src/g/config.coffee
+++ src/g/config.coffee
@@ -6,5 +6,5 @@
   defaults:
     registerMouseHover: false,
     registerMouseClicks: true,
-    importProxy: "http://www.corsproxy.com/"
+    importProxy: "https://cors-anywhere.herokuapp.com/"
     eventBus: true


add wildcard to the end of completion find queries

--- src/graphite.coffee
+++ src/graphite.coffee
@@ -215,7 +215,7 @@

     transform_response: (response) ->
       if settings.get('type') == 'lead'
-        _.map response.results, ({name, start, step, values}) ->
+        _.map response, ({name, start, step, values}) ->
           target: name
           datapoints: _.map values, (v, i) ->
             [v, start + step * i]
@@ -232,7 +232,7 @@

     # returns a promise
     complete: (query) ->
-      graphite.find(query)
+      graphite.find(query + '*')
       .then ({query, result}) ->
         graphite.parse_find_response query, result


Added article to placeholder options.

Added article to placeholder options.

--- app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee
+++ app/assets/javascripts/app/controllers/_ui_element/ticket_perform_action.coffee
@@ -365,6 +365,11 @@
           display: 'Ticket'
         },
         {
+          prefix: 'article'
+          object: 'TicketArticle'
+          display: 'Article'
+        },
+        {
           prefix: 'user'
           object: 'User'
           display: 'Current User'


Add spinner when transitioning back to aru controller to notify reloading

--- app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
@@ -47,6 +47,7 @@
     )
     @set('controllers.annualReportUpload.currentError', null)
     @set('controllers.annualReportUpload.allErrorsCollapsed', null)
+    $('.validation-errors-loading').show()

   unsavedChanges: (->
     @get('changedRowsCount') > 0


restored count of modified records

restored count of modified records

--- app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
@@ -39,7 +39,7 @@

   clearModifiedFlags: ->
     @beginPropertyChanges()
-    @get('content').forEach (shipment) ->
+    Trade.SandboxShipment.all().forEach (shipment) ->
       shipment.set('_modified', false)
     @endPropertyChanges()

@@ -48,7 +48,13 @@
     annualReportUpload.set('errorMessage', "")
     annualReportUploadId = annualReportUpload.get('id')
     @transitionToRoute('annual_report_upload', annualReportUploadId)
-
+
+  unsavedChanges: (->
+    @get('changedRowsCount') > 0
+  ).property('changedRowsCount')
+  changedRowsCount: (->
+    Trade.SandboxShipment.all().filterBy('_modified', true).length
+  ).property('content', 'currentShipment')

   actions:


Do not check statusMessage

Do not check statusMessage

--- src/jira-bug.coffee
+++ src/jira-bug.coffee
@@ -49,12 +49,11 @@
         .auth(auth)
         .post(issue) (err, res, body) ->
           try
-            if res.statusCode is 201 and res.statusMessage is "Created"
+            if res.statusCode is 201
               json = JSON.parse body
               msg.reply "Issue created: #{jiraUrl}/browse/#{json.key}"
-              console.log "statusCode:", res.statusCode, "statusMessage:", res.statusMessage, "err:", err, "body:", body
             else
               msg.reply "Unable to file issue please notify @ndaversa"
-              console.log "statusCode:", res.statusCode, "statusMessage:", res.statusMessage, "err:", err, "body:", body
+              console.log "statusCode:", res.statusCode, "err:", err, "body:", body
           catch error
             msg.reply "Unable to file issue: #{error}"


Make test use word under cursor

Make test use word under cursor

--- src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee
+++ src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee
@@ -271,37 +271,37 @@
         expect(finder.list.find("li:contains(tree-view.js)")).not.toExist()

   describe "fuzzy find by content under cursor", ->
+    editor = null
+
     beforeEach ->
+      editor = rootView.getActiveEditor()
       rootView.attachToDom()
       spyOn(rootView.project, "getFilePaths").andCallThrough()

     it "opens the fuzzy finder window when there are multiple matches", ->
-      cursor = rootView.getActiveEditor().getCursor()
-      spyOn(cursor, "getCurrentWord").andReturn("sample")
+      editor.setText("sample")
       rootView.trigger 'fuzzy-finder:find-under-cursor'

       waitsFor ->
         finder.list.children('li').length > 0

       runs ->
-        expect(rootView.find('.fuzzy-finder')).toExist()
+        expect(finder).toBeVisible()
         expect(rootView.find('.fuzzy-finder input:focus')).toExist()

     it "opens a file directly when there is a single match", ->
-      cursor = rootView.getActiveEditor().getCursor()
-      spyOn(cursor, "getCurrentWord").andReturn("sample.txt")
+      editor.setText("sample.txt")
+      rootView.trigger 'fuzzy-finder:find-under-cursor'

       openedPath = null
       spyOn(rootView, "open").andCallFake (path) ->
         openedPath = path

-      rootView.trigger 'fuzzy-finder:find-under-cursor'
-
       waitsFor ->
         openedPath != null

       runs ->
-        expect(rootView.find('.fuzzy-finder')).not.toExist()
+        expect(finder).not.toBeVisible()
         expect(openedPath).toBe "sample.txt"

   describe "opening a path into a split", ->


Don't let review panel entries go off the top of the screen

--- services/web/public/coffee/ide/review-panel/directives/reviewPanelSorted.coffee
+++ services/web/public/coffee/ide/review-panel/directives/reviewPanelSorted.coffee
@@ -43,15 +43,36 @@
 					previous_focused_entry_index = focused_entry_index

 					sl_console.log "focused_entry_index", focused_entry_index
-
+
+					# As we go backwards, we run the risk of pushing things off the top of the editor.
+					# If we go through the entries before and assume they are as pushed together as they
+					# could be, we can work out the 'ceiling' that each one can't go through. I.e. the first
+					# on can't go beyond the toolbar height, the next one can't go beyond the bottom of the first
+					# one at this minimum height, etc.
+					heights = (entry.$layout_el.height() for entry in entries_before)
+					previousMinTop = TOOLBAR_HEIGHT
+					min_tops = []
+					for height in heights
+						min_tops.push previousMinTop
+						previousMinTop += PADDING + height
+					min_tops.reverse()
+
 					line_height = 15

-					# Put the focused entry exactly where it wants to be
-					focused_entry_top = Math.max(TOOLBAR_HEIGHT, focused_entry.scope.entry.screenPos.y)
+					# Put the focused entry as close to where it wants to be as possible
+					focused_entry_top = Math.max(previousMinTop, focused_entry.scope.entry.screenPos.y)
 					focused_entry.$box_el.css(top: focused_entry_top)
 					focused_entry.$indicator_el.css(top: focused_entry_top)
 					focused_entry.$callout_el.css(top: focused_entry_top + line_height, height: 0)

+					positionLayoutEl = ($callout_el, original_top, top) ->
+						if original_top <= top
+							entry.$callout_el.removeClass("rp-entry-callout-inverted")
+							entry.$callout_el.css(top: original_top + line_height, height: top - original_top)
+						else
+							entry.$callout_el.addClass("rp-entry-callout-inverted")
+							entry.$callout_el.css(top: top + line_height + 1, height: original_top - top)
+
 					previousBottom = focused_entry_top + focused_entry.$layout_el.height()
 					for entry in entries_after
 						original_top = entry.scope.entry.screenPos.y
@@ -60,23 +81,21 @@
 						previousBottom = top + height
 						entry.$box_el.css(top: top)
 						entry.$indicator_el.css(top: top)
-						entry.$callout_el.removeClass("rp-entry-callout-inverted")
-						entry.$callout_el.css(top: original_top + line_height, height: top - original_top)
+						positionLayoutEl(entry.$callout_el, original_top, top)
 						sl_console.log "ENTRY", {entry: entry.scope.entry, top}
-
+
 					previousTop = focused_entry_top
 					entries_before.reverse() # Work through backwards, starting with the one just above
-					for entry in entries_before
+					for entry, i in entries_before
 						original_top = entry.scope.entry.screenPos.y
 						height = entry.$layout_el.height()
 						original_bottom = original_top + height
 						bottom = Math.min(original_bottom, previousTop - PADDING)
-						top = bottom - height
+						top = Math.max(bottom - height, min_tops[i])
 						previousTop = top
 						entry.$box_el.css(top: top)
 						entry.$indicator_el.css(top: top)
-						entry.$callout_el.addClass("rp-entry-callout-inverted")
-						entry.$callout_el.css(top: top + line_height + 1, height: original_top - top)
+						positionLayoutEl(entry.$callout_el, original_top, top)
 						sl_console.log "ENTRY", {entry: entry.scope.entry, top}

 				scope.$applyAsync () ->


don't need the url in html

don't need the url in html

--- src/atom/modal-selector.coffee
+++ src/atom/modal-selector.coffee
@@ -62,8 +62,8 @@
     else
       items = @list
     $('#modal-selector ul').empty()
-    for {name, url} in items[0..10]
-      $('#modal-selector ul').append "
#{name}
"
+    for {name} in items[0..10]
+      $('#modal-selector ul').append "
#{name}
"
     $('#modal-selector input').focus()
     $('#modal-selector li:first').addClass 'selected'


extract messageId fix

extract messageId fix

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -753,11 +753,11 @@
 			div

 		getMessageId: ->
-			id = /[0-9a-f]{16}/.exec window.location.hash
+			id = /\/([0-9a-f]{16})/.exec window.location.hash
 			if id is null
 				throw __msg 'errorNotWithinAConversation'
 			else
-				id[0]
+				id[1]

 		onSchedule: (props, loadingIcon, resetIcon) =>
 			try


allow uppercase mailIds

allow uppercase mailIds

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -448,7 +448,7 @@
       if @isPreviewPaneEnabled()
         id = @selectedConversationId
       else
-        id = /\/([0-9a-f]{16})/.exec window.location.hash
+        id = /\/([0-9a-f]{16})/i.exec window.location.hash
         id = id?[1]

       if id is null


Formatting

Formatting

--- lib/layervault/nodes/file.coffee
+++ lib/layervault/nodes/file.coffee
@@ -19,11 +19,11 @@
       md5 = opts.md5
     else if opts.localPath?
       service = new Md5Service(opts.localPath)
-      service.calculate (md5) => @delete {md5: md5}, cb
+      service.calculate (md5) => @delete { md5: md5 }, cb
     else
       throw "Deleting a file requires the md5 of the latest revision"

-    @api.delete @nodePath, {md5: md5}, cb.bind(@)
+    @api.delete @nodePath, { md5: md5 }, cb.bind(@)

   move: (to, cb) -> @api.post("#{@nodePath}/move", { to: to }, cb.bind(@))
   rename: @move


remove docs option dependencies from client

--- lib/atom-ternjs-client.coffee
+++ lib/atom-ternjs-client.coffee
@@ -10,7 +10,6 @@
   config:
     sort: false
     guess: false
-    docs: false
     urls: false
     origins: false
     caseInsensitive: false
@@ -21,8 +20,6 @@
     @registerEvents()

   registerEvents: ->
-    @disposables.push atom.config.observe 'atom-ternjs.docs', =>
-      @config.docs = atom.config.get('atom-ternjs.docs')
     @disposables.push atom.config.observe 'atom-ternjs.sort', =>
       @config.sort = atom.config.get('atom-ternjs.sort')
     @disposables.push atom.config.observe 'atom-ternjs.guess', =>
@@ -51,9 +48,9 @@
         includeKeywords: true
         sort: @config.sort
         guess: @config.guess
-        docs: @config.docs and @config.documentation
-        urls: @config.docs and @config.urls
-        origins: @config.docs and @config.origins
+        docs: @config.documentation
+        urls: @config.urls
+        origins: @config.origins
         lineCharPositions: true
         caseInsensitive: @config.caseInsensitive
     )


return empty array if no type is given

return empty array if no type is given

--- lib/atom-ternjs-helper.coffee
+++ lib/atom-ternjs-helper.coffee
@@ -114,9 +114,9 @@
         obj.leftLabel = obj.type.replace(/fn\(.{0,}\)/, '').replace(' : ', '')

     if !obj.type.endsWith(')') or obj.type.match(/\(\)$/)
-      obj.rightLabel = obj.rightLabelDoc = obj.type.replace(/( : .+)/, '')
+      obj.rightLabel = obj.type.replace(/( : .+)/, '')
     else
-      obj.rightLabel = obj.rightLabelDoc = obj.type
+      obj.rightLabel = obj.type

     if obj.rightLabel.startsWith('fn')
       if atom.config.get('atom-ternjs.useSnippets')
@@ -127,13 +127,11 @@
       obj._typeSelf = 'function'

     if obj.name
-      obj.rightLabelDoc = obj.rightLabel.replace(/^fn/, obj.name)
       if obj.leftLabel is obj.name
         obj.leftLabel = null
         obj.rightLabel = null

     if obj.leftLabel is obj.rightLabel
-      obj.rightLabelDoc = null
       obj.rightLabel = null

     obj
@@ -146,6 +144,7 @@
     "#{name}(#{suggestionParams.join(',')})"

   extractParams: (type) ->
+    return [] unless type
     start = 0
     params = []
     inside = 0


use Promise.resolve instead of promiseNextTick function

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -4,11 +4,6 @@
 # generic error logging function
 error = (e) ->
   console.error e.message, '\n', e.stack
-
-# process.nextTick that returns a promise
-promiseNextTick = ->
-  new Promise (resolve) ->
-    process.nextTick resolve

 getConfig = (key) -> atom.config.get "autohide-tree-view.#{key}"

@@ -180,7 +175,7 @@

   # updates styling on the .tree-view-resizer and the panel element
   update: (pushEditor = getConfig('pushEditor'))->
-    promiseNextTick().then =>
+    Promise.resolve().then =>
       if pushEditor
         @treeViewEl.style.position = 'relative'
         atom.views.getView(@treeView.panel)?.style.width = ''
@@ -223,7 +218,7 @@

   # resize the tree view when its contents might change size
   resize: ->
-    promiseNextTick().then =>
+    Promise.resolve().then =>
       if @visible then @show 0  else @hide 0
     .catch error


registerTreeView registers a disposable

registerTreeView registers a disposable

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -95,7 +95,6 @@
       @show 0
     .then =>
       @disposables.dispose()
-      @disposeTreeView()
       [@disposables, @conf, @visible] = []
     .catch error

@@ -104,13 +103,13 @@
     @treeViewEl = @treeView.element
     @treeViewEl.classList.add 'autohide', 'autohide-hover'

-  disposeTreeView: ->
-    @treeViewEl.classList.remove 'autohide', 'autohide-hover'
-    @treeViewEl.style.position = ''
-    @treeViewEl.style.minWidth = ''
-    @treeViewEl.parentNode?.style?.width = ''
-    @treeViewEl.querySelector('.tree-view-scroller').style.display = ''
-    [@treeView, @treeViewEl] = []
+    @disposables.add new Disposable =>
+      @treeViewEl.classList.remove 'autohide', 'autohide-hover'
+      @treeViewEl.style.position = ''
+      @treeViewEl.style.minWidth = ''
+      @treeViewEl.parentNode?.style?.width = ''
+      @treeViewEl.querySelector('.tree-view-scroller').style.display = ''
+      [@treeView, @treeViewEl] = []

   observe: ->
     # observe config


Close #1923; Prevent erros update outgoing webhooks with empty channel

--- packages/rocketchat-integrations/server/methods/outgoing/updateOutgoingIntegration.coffee
+++ packages/rocketchat-integrations/server/methods/outgoing/updateOutgoingIntegration.coffee
@@ -17,7 +17,12 @@
 		if integration.urls.length is 0
 			throw new Meteor.Error 'invalid_urls', '[methods] updateOutgoingIntegration -> urls is required'

-		if integration.channel?.trim() isnt '' and integration.channel[0] not in ['@', '#']
+		if _.isString(integration.channel)
+			integration.channel = integration.channel.trim()
+		else
+			integration.channel = undefined
+
+		if integration.channel? and integration.channel[0] not in ['@', '#']
 			throw new Meteor.Error 'invalid_channel', '[methods] updateOutgoingIntegration -> channel should start with # or @'

 		if not integration.token? or integration.token?.trim() is ''
@@ -36,7 +41,7 @@
 			throw new Meteor.Error 'invalid_integration', '[methods] updateOutgoingIntegration -> integration not found'

-		if integration.channel?.trim() isnt ''
+		if integration.channel?
 			record = undefined
 			channelType = integration.channel[0]
 			channel = integration.channel.substr(1)


#104 - Dates before 1900/01/01 of DateTime input parameters resulted in "Out of bounds" error

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -137,7 +137,7 @@
         threeHundredthsOfSecond = Math.floor(threeHundredthsOfSecond)

         buffer.writeUInt8(8)
-        buffer.writeUInt32LE(days)
+        buffer.writeInt32LE(days)
         buffer.writeUInt32LE(threeHundredthsOfSecond)
       else
         buffer.writeUInt8(0)


add exception message about ejs/esprima not supporting destructuring assignments yet

--- lib/closure-conversion.coffee
+++ lib/closure-conversion.coffee
@@ -1614,7 +1614,14 @@

                 n.declarations = decls
                 n
-
+
+        visitAssignmentExpression: (n) ->
+                if n.left.type is ObjectPattern or n.left.type is ArrayPattern
+                        throw new Error "EJS doesn't support destructuring assignments yet (issue #16)"
+                        throw new SyntaxError "cannot use destructuring with assignment operators other than '='" if n.operator isnt "="
+                else
+                        n
+
 passes = [
         DesugarClasses
         DesugarDestructuring


better working dir defaults

better working dir defaults

--- lib/connection/process.coffee
+++ lib/connection/process.coffee
@@ -3,6 +3,13 @@
 net = require 'net'

 module.exports =
+  workingDir: ->
+    paths = atom.workspace.project.getDirectories()
+    if paths.length == 1
+      paths[0].path
+    else
+      process.env.HOME || process.env.USERPROFILE
+
   jlpath: -> atom.config.get("julia-client.juliaPath")
   # TODO: this is very naïve.
   jlargs: -> atom.config.get("julia-client.juliaArguments").split ' '
@@ -34,16 +41,15 @@
     @cmds?.dispose()

   spawnJulia: (port, cons) ->
-    switch process.platform
-      when 'win32'
-        @useWrapper = parseInt(child_process.spawnSync("powershell", ["$PSVersionTable.PSVersion.Major"]).output[1].toString()) > 2
-        if @useWrapper
-          @proc = child_process.spawn("powershell", ["-ExecutionPolicy", "bypass", "& \"#{__dirname}\\spawnInterruptibleJulia.ps1\" -port #{port} -jlpath \"#{@jlpath()}\" -jloptions \"#{@jlargs().join(' ')}\""])
-        else
-          cons.c.out "PowerShell version < 3 encountered. Running without wrapper (interrupts won't work)."
-          @proc = child_process.spawn(@jlpath(), [@jlargs()..., '-e', "import Atom; @sync Atom.connect(#{port})"])
+    if process.platform == 'win32'
+      if parseInt(child_process.spawnSync("powershell", ["$PSVersionTable.PSVersion.Major"]).output[1].toString()) > 2
+        @proc = child_process.spawn("powershell", ["-ExecutionPolicy", "bypass", "& \"#{__dirname}\\spawnInterruptibleJulia.ps1\" -port #{port} -jlpath \"#{@jlpath()}\" -jloptions \"#{@jlargs().join(' ')}\""], cwd: @workingDir())
+        return
       else
-        @proc = child_process.spawn(@jlpath(), [@jlargs()..., '-e', "import Atom; @sync Atom.connect(#{port})"])
+        cons.c.out "PowerShell version < 3 encountered. Running without wrapper (interrupts won't work)."
+
+    @proc = child_process.spawn(@jlpath(), [@jlargs()..., '-e', "import Atom; @sync Atom.connect(#{port})"], cwd: @workingDir())
+    return

   interruptJulia: ->
     switch process.platform


Use Q() as it returns a resolved promise

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -29,10 +29,7 @@
     @emit('cleared')

   search: (pattern, paths, onlyRunIfChanged = false)->
-    if onlyRunIfChanged and pattern? and paths? and pattern == @pattern and _.isEqual(paths, @searchedPaths)
-      deferred = Q.defer()
-      deferred.resolve()
-      return deferred.promise
+    return Q() if onlyRunIfChanged and pattern? and paths? and pattern == @pattern and _.isEqual(paths, @searchedPaths)

     @clear()
     @active = true


Fix whitespace

Fix whitespace

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -25,7 +25,7 @@
 misc = require('smc-util/misc')
 {ActivityDisplay, DeletedProjectWarning, DirectoryInput, Icon, Loading, ProjectState, SAGE_LOGO_COLOR
  SearchInput, TimeAgo, ErrorDisplay, Space, Tip, LoginLink, Footer} = require('./r_misc')
-{FileTypeSelector} = require('./project_new')
+{FileTypeSelector}    = require('./project_new')
 {BillingPageLink}     = require('./billing')
 {human_readable_size} = require('./misc_page')
 {MiniTerminal}        = require('./project_miniterm')
@@ -143,8 +143,8 @@
         @props.bordered != next.border

     render_icon : ->
-        ext  = misc.filename_extension(@props.name)
-        name = file_associations[ext]?.icon ? 'file'
+        ext   = misc.filename_extension(@props.name)
+        name  = file_associations[ext]?.icon ? 'file'
         style =
             color         : if @props.mask then '#bbbbbb'
             verticalAlign : 'sub'
@@ -344,12 +344,12 @@

 NoFiles = rclass
     propTypes :
-        actions     : rtypes.object.isRequired
+        actions       : rtypes.object.isRequired
         create_folder : rtypes.func.isRequired
         create_file   : rtypes.func.isRequired
-        public_view : rtypes.bool
-        file_search : rtypes.string
-        current_path: rtypes.string
+        public_view   : rtypes.bool
+        file_search   : rtypes.string
+        current_path  : rtypes.string

     displayName : 'ProjectFiles-NoFiles'

@@ -462,18 +462,18 @@
     displayName : 'ProjectFiles-FileListing'

     propTypes :
-        listing       : rtypes.array.isRequired
-        file_map      : rtypes.object.isRequired
-        file_search   : rtypes.string
+        listing             : rtypes.array.isRequired
+        file_map            : rtypes.object.isRequired
+        file_search         : rtypes.string
+        checked_files       : rtypes.object
+        current_path        : rtypes.string
+        page_number         : rtypes.number
+        page_size           : rtypes.number
+        public_view         : rtypes.bool
+        actions             : rtypes.object.isRequired
+        create_folder       : rtypes.func.isRequired
+        create_file         : rtypes.func.isRequired
         selected_file_index : rtypes.number
-        checked_files : rtypes.object
-        current_path  : rtypes.string
-        page_number   : rtypes.number
-        page_size     : rtypes.number
-        public_view   : rtypes.bool
-        actions       : rtypes.object.isRequired
-        create_folder : rtypes.func.isRequired
-        create_file   : rtypes.func.isRequired

     getDefaultProps : ->
         file_search : ''
@@ -1468,9 +1468,9 @@
     displayName : 'ProjectFiles-ProjectFilesNew'

     propTypes :
-        file_search  : rtypes.string.isRequired
-        current_path : rtypes.string
-        actions      : rtypes.object.isRequired
+        file_search   : rtypes.string.isRequired
+        current_path  : rtypes.string
+        actions       : rtypes.object.isRequired
         create_folder : rtypes.func.isRequired
         create_file   : rtypes.func.isRequired

@@ -1534,15 +1534,15 @@
         account :
             other_settings : rtypes.immutable
         "#{name}" :
-            current_path       : rtypes.string
-            activity           : rtypes.object
-            page_number        : rtypes.number
-            file_action        : rtypes.string
-            file_search        : rtypes.string
-            show_hidden        : rtypes.bool
-            sort_by_time       : rtypes.bool
-            error              : rtypes.string
-            checked_files      : rtypes.immutable
+            current_path        : rtypes.string
+            activity            : rtypes.object
+            page_number         : rtypes.number
+            file_action         : rtypes.string
+            file_search         : rtypes.string
+            show_hidden         : rtypes.bool
+            sort_by_time        : rtypes.bool
+            error               : rtypes.string
+            checked_files       : rtypes.immutable
             file_creation_error : rtypes.string
             selected_file_index : rtypes.number

@@ -1701,18 +1701,18 @@
             </div>
         else if listing?
             <FileListing
-                listing       = {listing}
-                selected_file_index = {@props.selected_file_index}
-                page_size     = {@file_listing_page_size()}
-                page_number   = {@props.page_number}
-                file_map      = {file_map}
-                file_search   = {@props.file_search}
-                checked_files = {@props.checked_files}
-                current_path  = {@props.current_path}
-                public_view   = {public_view}
-                actions       = {@props.actions}
-                create_file   = {@create_file}
-                create_folder = {@create_folder} />
+                listing             = {listing}
+                page_size           = {@file_listing_page_size()}
+                page_number         = {@props.page_number}
+                file_map            = {file_map}
+                file_search         = {@props.file_search}
+                checked_files       = {@props.checked_files}
+                current_path        = {@props.current_path}
+                public_view         = {public_view}
+                actions             = {@props.actions}
+                create_file         = {@create_file}
+                create_folder       = {@create_folder}
+                selected_file_index = {@props.selected_file_index} />
         else
             <div style={fontSize:'40px', textAlign:'center', color:'#999999'} >
                 <Loading />
@@ -1771,16 +1771,16 @@
             <Row>
                 <Col sm=3>
                     <ProjectFilesSearch
-                        key                = {@props.current_path}
-                        file_search        = {@props.file_search}
-                        actions            = {@props.actions}
-                        current_path       = {@props.current_path}
-                        selected_file      = {visible_listing?[@props.selected_file_index]}
-                        selected_file_index= {@props.selected_file_index}
+                        key                 = {@props.current_path}
+                        file_search         = {@props.file_search}
+                        actions             = {@props.actions}
+                        current_path        = {@props.current_path}
+                        selected_file       = {visible_listing?[@props.selected_file_index]}
+                        selected_file_index = {@props.selected_file_index}
                         file_creation_error = {@props.file_creation_error}
                         num_files_displayed = {visible_listing?.length}
-                        create_file        = {@create_file}
-                        create_folder      = {@create_folder} />
+                        create_file         = {@create_file}
+                        create_folder       = {@create_folder} />
                 </Col>
                 {@render_new_file() if not public_view}
                 <Col sm={if public_view then 6 else 4}>