implemented fix for nested arrays in table data

--- prometheus_samples/app/assets/javascripts/prometheus.coffee
+++ prometheus_samples/app/assets/javascripts/prometheus.coffee
@@ -41,7 +41,7 @@
             $(this).hide()
             $(label).show()
        )
-
+
   nullCheck: (val)->
     if val == null
       return ""
@@ -80,65 +80,71 @@
           when "even" then evenOdd = "odd"
           else evenOdd = "odd"
         row = "
"
-
+
         headers = $(ele).find("th")
-        jQuery.each(headers, (k, eles) ->
-           data_options = []
-           try
-              try
-                data_options = ($(eles).attr("data-options").split(" "))
-              catch ex
-                values = eles
-              try
-                property = $(eles).attr("data-val").split(".")
-              catch ex
-              unless property?
-                return null
-              nested = obj
-              for i in [0..property.length - 1]
+        if obj.constructor.toString().indexOf("Array") is -1
+          obj = [obj]
+        jQuery.each(obj, (k, obj) ->
+          jQuery.each(headers, (k, eles) ->
+             data_options = []
+             try
+                try
+                  data_options = ($(eles).attr("data-options").split(" "))
+                catch ex
+                  values = eles
+                try
+                  property = $(eles).attr("data-val").split(".")
+                catch ex
+                unless property?
+                  return null
+                nested = obj
+
+                for i in [0..property.length - 1]
                   nested = nested[property[i]]
-              isNull = nested?
-
-              nested = "" unless isNull
-              undefinedCount++ unless isNull
-              if $(eles).attr("data-val")
-                  input_name = Prometheus.getInputName(eles, property)
-                  input_id =  Prometheus.getInputId(eles, property, row_count)
-                  input_name = "#{input_name}[#{row_count}]"
-                  input_type = Prometheus.getInputType(eles)
-                  change = Prometheus.getOnChange(eles)
-                  classes = Prometheus.setColumnClasses(data_options)
-                  cls = Prometheus.getClasses(eles)
-                  row += ""
-                  row += "#{nested}"
-                  if input_type == "select"
-                    row += Prometheus.addTableSelect(input_name, row_count, val, eles, property, data, nested, change, blur)
-                  else
-                    row += Prometheus.addTableInput(input_name, input_id, nested, property, change, blur)
-              if $(eles).attr("data-href")
-                  row += Prometheus.addTableLink(eles)
-           catch ex
-                row += Prometheus.addEmptyColumn
-                undefinedCount++
-
-          )
-        row += "
"
-        if undefinedCount < headers.length
-          row_count++
-          if not success
-            if not appendData
-              $(ele).find("tbody").html("")
-              success = 1
-          $(ele).find("tbody").append(row)
-          $(ele).find("input").hide()
-          $(ele).find("select").hide()
+
+                isNull = nested?
+
+                nested = "" unless isNull
+                undefinedCount++ unless isNull
+                if $(eles).attr("data-val")
+                    input_name = Prometheus.getInputName(eles, property)
+                    input_id =  Prometheus.getInputId(eles, property, row_count)
+                    input_name = "#{input_name}[#{row_count}]"
+                    input_type = Prometheus.getInputType(eles)
+                    change = Prometheus.getOnChange(eles)
+                    classes = Prometheus.setColumnClasses(data_options)
+                    cls = Prometheus.getClasses(eles)
+                    row += ""
+                    row += "#{nested}"
+                    if input_type == "select"
+                      row += Prometheus.addTableSelect(input_name, row_count, property, eles, property, data, nested, change, blur)
+                    else
+                      row += Prometheus.addTableInput(input_name, input_id, nested, property, change, blur)
+                if $(eles).attr("data-href")
+                    row += Prometheus.addTableLink(eles)
+             catch ex
+                  row += Prometheus.addEmptyColumn
+                  undefinedCount++
+
+            )
+          row += "
"
+          if undefinedCount < headers.length
+            row_count++
+            if not success
+              if not appendData
+                $(ele).find("tbody").html("")
+                success = 1
+            $(ele).find("tbody").append(row)
+            $(ele).find("input").hide()
+            $(ele).find("select").hide()
+        )
       )
     )
     Prometheus.bindEditables()
-
+

   getOnChange: (eles) ->
-    change = ""
+    change = ""
     try
       if $(eles).attr("data-change")
         change = $(eles).attr("data-change")
@@ -150,57 +156,57 @@
   getInputType: (eles) ->
     input_type = undefined
     try
-      if $(eles).attr("data-input")
+      if $(eles).attr("data-input")
           input_type = $(eles).attr("data-input").toString()
     catch ex
       input_type = "text"
     return input_type
-
+
   getInputName: (eles, property) ->
     if $(eles).attr("data-name")
         input_name = $(eles).attr("data-name")
-    else if property.length > 1
+    else if property.length > 1
         input_name = property.join("_")
     else
         input_name = property.toString()
-
+
   getInputId: (eles, property, row_count) ->
     if $(eles).attr("data-id")
         input_id = $(eles).attr("data-id")
     else
         input_id = property.join("_")
         input_id = "#{input_id}_#{row_count}"
-
+
   getId: (eles) ->
     ele_id = ""
     if $(eles).attr("data-id")
       ele_id = $(eles).attr("data-id")
     return ele_id
-
+
   getClasses: (eles) ->
     if $(eles).attr("data-class")
       cls = $(eles).attr("data-class")
     else
       cls = ""
     return cls
-
+
   addTableLink: (eles) ->
     row = ""
     row += " "
     row += "	"
     return row
-
+
   addTableInput: (name, id, nested, property, change, blur) ->
     unless nested?
       return null
     row  = "<input name='#{name}' id='#{id}' value='#{nested}' onblur='#{blur}' class='hidden #{property.join("_")} editable' onchange=\"#{change}\"/>"
     row += "	"
     return row
-
+
   addEmptyColumn: ->
     row = "	"
     return row
-
+
   addTableSelect: (input_name, row_count, val, eles, property, data, nested,change, blur) ->
     row = ""
     select_options = data
@@ -220,7 +226,7 @@
     row += "</select>"
     row += "	"
     return row
-
+
   setColumnClasses: (options) ->
     classes = []
     jQuery.each(options, (i, val) ->
@@ -230,7 +236,7 @@
         return ""
     )
     return classes.join(" ")
-
+
   bindData: (data, container) ->
     if not srcBound
       @bindSources(data, container, false)
@@ -252,7 +258,7 @@
       data = [data]
     if(data)
       @bindSelectSources(data, container)
-
+
   bindValues: (data, container, append) ->
      if append
        appendData = append
@@ -264,19 +270,19 @@
        data = [data]
      if(data)
        @bindTables(data, container)
-
+
        @bindSelects(data, container)
-
+
        @bindInputs(data, container)
-
+
        @bindHiddens(data, container)
-
+
        @bindCheckboxes(data, container)
-
+
        @bindLists(data, container)
-
+
        @bindLabels(data, container)
-
+
        @bindSpan(data, container)

        @bindEditables()
@@ -286,7 +292,7 @@
     labels = $(container).find("span[data-val]")
     jQuery.each(labels, (i, ele) ->
             jQuery.each(data, (i, obj) ->
-              if $(ele).attr("data-val")
+              if $(ele).attr("data-val")
                 property = $(ele).attr("data-val").split(".")
                 try
                   data_options = ""
@@ -345,20 +351,20 @@
     if Prometheus.getBoolean(name)
       input.attr("name", name)
     return input
-
+
   getOnBlur: (ele) ->
     if $(ele).attr("data-blur")
       return ""
     else
       return ""
-
+
   bindLists: (data, container) ->
     lists = $(container).find("ul[data-val]")
     jQuery.each(lists, (i, ele) ->
             success = 0
             title = ""
             list_count = $(ele).find("li").length
-            if $(ele).attr("data-val")
+            if $(ele).attr("data-val")
               jQuery.each(data, (i, obj) ->
                 property = $(ele).attr("data-val").split(".")
                 change = Prometheus.getOnChange(ele)
@@ -376,7 +382,7 @@
                   input = ""
                   if $(ele).attr("data-name")
                     name = "#{$(ele).attr('data-name')}[#{list_count}]"
-                  else
+                  else
                     name = "#{property.join('_')}[#{list_count}]"
                   # TODO figure out a better way to blur and get rid of this
                   blur = undefined
@@ -401,13 +407,13 @@
                   $(ele).find("input").hide()
                 catch ex
               )
-    )
+    )

   bindLabels: (data, container) ->
     labels = $(container).find("label[data-val]")
     jQuery.each(labels, (i, ele) ->
             jQuery.each(data, (i, obj) ->
-              if $(ele).attr("data-val")
+              if $(ele).attr("data-val")
                 property = $(ele).attr("data-val").split(".")
                 try
                   data_options = ""
@@ -452,7 +458,7 @@
               value = ""
               objects = []
               src = $(ele).attr("data-src").split(".")
-              jQuery.each(data, (i, val) ->
+              jQuery.each(data, (i, val) ->
                 val = val
                 jQuery.each(src, (i, str) ->
                   try
@@ -477,21 +483,21 @@
                   $(ele).append(option)
               )
     )
-
-
+
+
   getRootArray: (data) ->
-    while data[0].constructor.toString().indexOf("Array") is not -1
+    while data[0].constructor.toString().indexOf("Array") is not -1
       data = data[0]
     return data
-
+
   bindSelects: (data, container) ->
     selects = $(container).find("select[data-val]")
-    obj = data
+    obj = data
     jQuery.each(selects, (i, ele) ->
-      if $(ele).attr("data-val")
+      if $(ele).attr("data-val")
         property = $(ele).attr("data-val").split(".")
         jQuery.each(obj, (i, val) ->
-          try
+          try
             for i in [0..property.length - 1]
               val = val[property[i]]
             if val?
@@ -499,12 +505,12 @@
               $("##{$(ele).attr("id")} option:contains('#{val}')").attr("selected", "selected");
           catch ex
         )
-    )
-
+    )
+
   bindInputs: (data, container) ->
        inputs = $(container).find("input:text[data-val]")
        jQuery.each(inputs, (i, ele) ->
-                   if $(ele).attr("data-val")
+                   if $(ele).attr("data-val")
                      jQuery.each(data, (i, obj) ->
                        property = $(ele).attr("data-val").split(".")
                        try
@@ -515,11 +521,11 @@
                      )

        )
-
+
   bindHiddens: (data, container) ->
        inputs = $(container).find("input:hidden[data-val]")
        jQuery.each(inputs, (i, ele) ->
-                   if $(ele).attr("data-val")
+                   if $(ele).attr("data-val")
                      jQuery.each(data, (i, obj) ->
                        property = $(ele).attr("data-val").split(".")
                        try
@@ -530,12 +536,12 @@
                      )

        )
-
-
+
+
   bindCheckboxes: (data, container) ->
        checkboxes = $(container).find("input:checkbox[data-val]")
        jQuery.each(checkboxes, (i, ele) ->
-              if $(ele).attr("data-val")
+              if $(ele).attr("data-val")
                 jQuery.each(data, (i, obj) ->
                   property = $(ele).attr("data-val").split(".")
                   try
@@ -545,9 +551,10 @@
                   catch ex
                 )
        )
-
+
   getBoolean: (obj) ->
     if obj is 0 or obj is "false" or obj is "" or obj is "0" or obj is null or obj is undefined or obj is "undefined"
       return false
     else
-      return true+      return true
+


Fixed bug on forward message modal

Fixed bug on forward message modal

--- static/coffee/modals.coffee
+++ static/coffee/modals.coffee
@@ -136,7 +136,7 @@

     if $scope.form.$valid
       urn = {scheme: $scope.fields.urn.scheme, path: $scope.fields.urn.path, urn: ($scope.fields.urn.scheme + ':' + $scope.fields.urn.path)}
-      $modalInstance.close({text: $scope.fields.text, urn: urn})
+      $modalInstance.close({text: $scope.fields.text.val, urn: urn})

   $scope.cancel = () -> $modalInstance.dismiss('cancel')


atom: add ipdb snippet

atom: add ipdb snippet

--- atom/atom.symlink/snippets.cson
+++ atom/atom.symlink/snippets.cson
@@ -1,5 +1,8 @@
 # Python
 '.source.python':
+  'ipdb.set_trace()':
+    'prefix': 'ipdb'
+    'body': 'import ipdb; ipdb.set_trace()'
   'wdb.set_trace()':
     'prefix': 'wdb'
     'body': 'import wdb; wdb.set_trace()'


fix eventlogger tests to use name with dashes

--- services/real-time/test/unit/coffee/EventLoggerTests.coffee
+++ services/real-time/test/unit/coffee/EventLoggerTests.coffee
@@ -11,9 +11,11 @@
 		tk.freeze(new Date(@start))
 		@EventLogger = SandboxedModule.require modulePath, requires:
 			"logger-sharelatex": @logger = {error: sinon.stub()}
-		@id_1 = "abc-1"
+			"metrics-sharelatex": @metrics = {inc: sinon.stub()}
+		@channel = "applied-ops"
+		@id_1 = "random-hostname:abc-1"
 		@message_1 = "message-1"
-		@id_2 = "abc-2"
+		@id_2 = "random-hostname:abc-2"
 		@message_2 = "message-2"

 	afterEach ->
@@ -21,32 +23,54 @@

 	describe 'checkEventOrder', ->

-		it 'should accept events in order', ->
-			@EventLogger.checkEventOrder(@id_1, @message_1)
-			status = @EventLogger.checkEventOrder(@id_2, @message_2)
-			expect(status).to.be.undefined
+		describe 'when the events are in order', ->
+			beforeEach ->
+				@EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+				@status = @EventLogger.checkEventOrder(@channel, @id_2, @message_2)

-		it 'should return "duplicate" for the same event', ->
-			@EventLogger.checkEventOrder(@id_1, @message_1)
-			status = @EventLogger.checkEventOrder(@id_1, @message_1)
-			expect(status).to.equal "duplicate"
+			it 'should accept events in order', ->
+				expect(@status).to.be.undefined

-		it 'should log an error for out of order events', ->
-			@EventLogger.checkEventOrder(@id_1, @message_1)
-			@EventLogger.checkEventOrder(@id_2, @message_2)
-			status = @EventLogger.checkEventOrder(@id_1, @message_1)
-			expect(status).to.be.undefined
+			it 'should increment the valid event metric', ->
+				@metrics.inc.calledWith("event.#{@channel}.valid", 1)
+					.should.equal.true

-		it 'should flush old entries', ->
-			@EventLogger.MAX_EVENTS_BEFORE_CLEAN = 10
-			@EventLogger.checkEventOrder(@id_1, @message_1)
-			for i in [1..8]
-				status = @EventLogger.checkEventOrder(@id_1, @message_1)
-				expect(status).to.equal "duplicate"
-			# the next event should flush the old entries aboce
-			@EventLogger.MAX_STALE_TIME_IN_MS=1000
-			tk.freeze(new Date(@start + 5 * 1000))
-			# because we flushed the entries this should not be a duplicate
-			@EventLogger.checkEventOrder('other-1', @message_2)
-			status = @EventLogger.checkEventOrder(@id_1, @message_1)
-			expect(status).to.be.undefined+		describe 'when there is a duplicate events', ->
+			beforeEach ->
+				@EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+				@status = @EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+
+			it 'should return "duplicate" for the same event', ->
+				expect(@status).to.equal "duplicate"
+
+			it 'should increment the duplicate event metric', ->
+				@metrics.inc.calledWith("event.#{@channel}.duplicate", 1)
+					.should.equal.true
+
+		describe 'when there are out of order events', ->
+			beforeEach ->
+				@EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+				@EventLogger.checkEventOrder(@channel, @id_2, @message_2)
+				@status = @EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+
+			it 'should return "out-of-order" for the event', ->
+				expect(@status).to.equal "out-of-order"
+
+			it 'should increment the out-of-order event metric', ->
+				@metrics.inc.calledWith("event.#{@channel}.out-of-order", 1)
+					.should.equal.true
+
+		describe 'after MAX_STALE_TIME_IN_MS', ->
+			it 'should flush old entries', ->
+				@EventLogger.MAX_EVENTS_BEFORE_CLEAN = 10
+				@EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+				for i in [1..8]
+					status = @EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+					expect(status).to.equal "duplicate"
+				# the next event should flush the old entries aboce
+				@EventLogger.MAX_STALE_TIME_IN_MS=1000
+				tk.freeze(new Date(@start + 5 * 1000))
+				# because we flushed the entries this should not be a duplicate
+				@EventLogger.checkEventOrder(@channel, 'other-1', @message_2)
+				status = @EventLogger.checkEventOrder(@channel, @id_1, @message_1)
+				expect(status).to.be.undefined


Catches all creation and closed dates

Catches all creation and closed dates

--- initial_populate.coffee
+++ initial_populate.coffee
@@ -8,6 +8,10 @@
 rightnow = 'donelist'

 t = new trello "appkey", "usertoken"
+
+# Sort dates in reverse (oldest first)
+sortOldest = (a, b) ->
+  a - b

 # Trello resolver
 tresolve = (held, resolve, reject) ->
@@ -31,12 +35,12 @@
   filterParams =
     filter: "all"
     format: "list"
-    actions: "updateCard:closed,createCard"
-    fields: "closed,idList,name,dateLastActivity",
+    actions: "updateCard:closed,createCard,copyCard,convertToCardFromCheckItem,moveCardToBoard"
+    fields: "closed,name,dateLastActivity",
     limit: 1000

   filterParams.before = before.toISOString()
-  filterParams.since = since.toISOString()
+  filterParams.since = since.toISOString() if since?

   # Return gettrello promise
   gettrello {}, "/1/boards/targetboard/cards", filterParams
@@ -44,8 +48,6 @@

 # Insert a card into the database
 insertCard = (card) ->
-  if card.id == '5473baac31efae1dfb099c94'
-    console.log card
   (success, error) ->
     # Database connection
     pg.connect
@@ -54,11 +56,11 @@
       host: 'localhost'
       ssl: false,
       (err, client, done) ->
-        handleError = (err) ->
-
+        dates = []
         # Grab the creation date
         creation_date = null
-        creation_date = action.date for action in card.actions when action.type == 'createCard'
+        creation_date = action.date for action in card.actions when action.type == 'createCard' || action.type == 'convertToCardFromCheckItem' || action.type == 'copyCard' || action.type == 'moveCardToBoard'
+        dates.push new Date(creation_date)

         # If the card is closed, find the date and whether it was in 'rightnow'
         closed_date = null
@@ -71,24 +73,26 @@
             if action.data.list.id == rightnow
               finished = true
             closed_date = action.date
+            dates.push new Date(closed_date)
           catch error
             console.error "Could not access data, ", error
             console.error (util.inspect card, {depth: null, color: true})

         # Insert into database
         q = client.query
-          text: "SELECT trello.insert_card($1, $2, $3, $4, $5, $6)"
-          name: 'cardinsert'
-          values: [card.id, card.name, card.closed, finished, creation_date, closed_date]
-        , (err, result) ->
-          if err
-            # Close connection
-            done(client)
-            error err
-            return
-          # Release the database connection
-          done()
-          success()
+            text: "SELECT trello.insert_card($1, $2, $3, $4, $5, $6)"
+            name: 'cardinsert'
+            values: [card.id, card.name, card.closed, finished, creation_date, closed_date]
+          , (err, result) ->
+            if err
+              # Close connection
+              done(client)
+              error err
+              return
+            # Release the database connection
+            done()
+            dates.sort sortOldest
+            success(dates[0])

 # Recurse for optional depth iterations
 processCards = (cards) ->
@@ -121,12 +125,13 @@

 requests = []
 for d, i in dates
-  do (d) ->
+  do (d, i) ->
     requests.push(processCards(getCards(d[0], d[1])).then (val) ->
         new Promise (resolve, reject) ->
           Promise.all(val).then((success) ->
             console.log "[=] (" + i + ") " + d[0] + " - " + d[1] + ":\t" + success.length + " cards"
-            resolve()
+            success.sort sortOldest
+            resolve(success[0])
           , (fail) ->
             reject(fail)
           )


More comment compliance

More comment compliance

--- javascripts/tres.coffee
+++ javascripts/tres.coffee
@@ -23,7 +23,7 @@
 # ---

 # Tres.Device handles/exposes device events (orientation change, accellerometer,
-# etc), screen width, and other hardware-related goodies.
+# etc), screen width, and other hardware-related goodies
 class Device
   constructor: ->
     _.extend @, Backbone.Events
@@ -38,18 +38,20 @@
 # ---

 # Tres.Screen is the base screen class. So you'll pretty have one of these for
-# each "route" in your app.
+# each "route" in your app
 class Tres.Screen extends Backbone.View

   # Defaults to using sections for screens
   tagName: 'section'

-  # Ensure one can still declare events in a view without getting in the way of the defaults.
+  # Ensure one can still declare events in a view without getting in the
+  # way of the defaults
   __events:
     "click a[href]:not([href^='http://'])": 'touchLink'

-    # Provide a convenience method `submit` which gets fired when you submit a form in a screen.
-    # You can still trap forms normally. This is just a shortcut in case you have 1 form in a screen.
+    # Provide a convenience method `submit` which gets fired when you
+    # submit a form in a screen. You can still trap forms normally.
+    # This is just a shortcut in case you have 1 form in a screen
     "submit form": '__submit'

   events: {}
@@ -142,7 +144,8 @@
   # Adds one record to the list, wrapping it in a Tres.List
   __add: (model) ->
     tag = @entry?.tagName or @_tagMap[@$el.get(0).tagName]
-    template = new Tres.ListEntry _.extend(@entry, { tagName : tag, model : model })
+    template = new Tres.ListEntry
+      _.extend(@entry, { tagName : tag, model : model })
     model.template = template
     @$el.append template.render().el

@@ -225,7 +228,7 @@

 # ---

-# Just a regular Backbone.Router for now. By default you'll have one for application
+# Just a regular Backbone.Router for now. You'll have one for application
 class Router extends Backbone.Router

 Tres.Router = new Router


check if req.url matched those predefined upstream apps to determine passing handling to upstream app or next handler

--- api/models/Upstream.coffee
+++ api/models/Upstream.coffee
@@ -67,7 +67,13 @@

   # return http-proxy-middleware instance with existing saved upstream settings
   middleware: (req, res, next) ->
-    @proxy req, res, next
+    paths = _.keys @router
+    matchedOne = _.some paths, (path) ->
+      req.url.match new RegExp "^#{path}"
+    if matchedOne
+      @proxy req, res, next
+    else
+      next()

   # reload router settings
   reload: ->


Add special character specs to ScriptOptions

--- spec/script-options-spec.coffee
+++ spec/script-options-spec.coffee
@@ -4,9 +4,10 @@
   beforeEach ->
     @scriptOptions = new ScriptOptions()
     @dummyEnv =
-      NODE_ENV: 'test',
-      SCRIPT_USER: 'johndoe'
-    @dummyEnvString = "SCRIPT_ENV='test';SCRIPT_USER=true;_TEST_VAR='123'"
+      SCRIPT_CI: true
+      SCRIPT_ENV: "'test'"
+      _NUMBERS: '"123"'
+    @dummyEnvString = "SCRIPT_CI=true;SCRIPT_ENV='test';_NUMBERS=\"123\""

   describe 'getEnv', ->
     it 'should default to an empty env object', ->
@@ -23,11 +24,20 @@
       mergedEnv = @scriptOptions.mergedEnv(@dummyEnv)
       expect(mergedEnv).toEqual(@dummyEnv)

-    it 'should merge custom options', ->
-      @scriptOptions.env = "VAR_1=one;VAR_2='t w o';VAR_3=\"three\""
+    it 'should retain the original environment', ->
+      @scriptOptions.env = "TEST_VAR_1=one;TEST_VAR_2=\"two\";TEST_VAR_3='three'"
       mergedEnv = @scriptOptions.mergedEnv(@dummyEnv)
-      expect(mergedEnv.VAR_1).toEqual('one')
-      expect(mergedEnv.VAR_2).toEqual('t w o')
-      expect(mergedEnv.VAR_3).toEqual('three')
-      expect(mergedEnv.NODE_ENV).toEqual('test')
-      expect(mergedEnv.SCRIPT_USER).toEqual('johndoe')
+      expect(mergedEnv.SCRIPT_CI).toMatch('true')
+      expect(mergedEnv.SCRIPT_ENV).toMatch('test')
+      expect(mergedEnv._NUMBERS).toMatch('123')
+      expect(mergedEnv.TEST_VAR_1).toMatch('one')
+      expect(mergedEnv.TEST_VAR_2).toMatch('two')
+      expect(mergedEnv.TEST_VAR_3).toMatch('three')
+
+    it 'should support special character values', ->
+      @scriptOptions.env = "TEST_VAR_1=o-n-e;TEST_VAR_2=\"nested\\\"doublequotes\\\"\";TEST_VAR_3='nested\\'singlequotes\\';TEST_VAR_4='s p a c e s'"
+      mergedEnv = @scriptOptions.mergedEnv(@dummyEnv)
+      expect(mergedEnv.TEST_VAR_1).toMatch('o-n-e')
+      expect(mergedEnv.TEST_VAR_2).toMatch('nested"doublequotes"')
+      expect(mergedEnv.TEST_VAR_2).toMatch('nested\'singlequotes\'')
+      expect(mergedEnv.TEST_VAR_4).toMatch('s p a c e s')


teams: removed group slug injection to routes

since we use subdomains this is not necessary anymore.

--- client/app/lib/kodingrouter.coffee
+++ client/app/lib/kodingrouter.coffee
@@ -56,13 +56,6 @@
       return KodingAppsController.loadInternalApp name, (err, res) =>
         return kd.warn err  if err
         kd.utils.defer => @handleRoute route, options
-
-    if entryPoint?.slug and entryPoint.type is "group"
-      entrySlug = "/" + entryPoint.slug
-      # if incoming route is prefixed with groupname or entrySlug is the route
-      # also we dont want koding as group name
-      if not ///^#{entrySlug}///.test(route) and entrySlug isnt '/koding'
-        route =  entrySlug + route

     return @handleRoute @getDefaultRoute()  if /<|>/.test route
     super route, options


client: support: don't override chatlio settings

--- client/app/lib/setupchatlio.coffee
+++ client/app/lib/setupchatlio.coffee
@@ -48,19 +48,8 @@
     # configure the client so it doesn't look shitty
     _chatlio.configure
       titleColor                : '#56A2D3'
-      titleFontColor            : '#fff'
-      onlineTitle               : 'How can we help you?'
-      offlineTitle              : 'Contact Us'
+      titleFontColor            : '#FFFFFF'
       agentLabel                : if shouldTalkToKodingSupport then 'Koding Support' else "#{team.title} Support"
-      browserSideAuthorLabel    : 'You'
-      onlineMessagePlaceholder  : 'Type message here...'
-      offlineGreeting           : 'Sorry we are away, but we would love to hear from you and chat soon!'
-      offlineEmailPlaceholder   : 'Email'
-      offlineMessagePlaceholder : 'Your message here'
-      offlineNamePlaceholder    : 'Name (optional but helpful)'
-      offlineSendButton         : 'Send'
-      offlineThankYouMessage    : 'Thanks for your message. We will be in touch soon!'
-      autoResponseMessage       : 'Question? Just type it below and we are online and ready to answer.'

     # these to identify the user talking
     # taken from user's koding account


change experiment to version 3

change experiment to version 3

--- app/lib/experiments.coffee
+++ app/lib/experiments.coffee
@@ -9,7 +9,7 @@
   Define the active experiment here by using a string which exists in http://experiments.zooniverse.org/active_experiments
   If no experiments should be running right now, set this to null, false or ""
   ###
-  ACTIVE_EXPERIMENT: "SerengetiMessagingExperiment2"
+  ACTIVE_EXPERIMENT: "SerengetiMessagingExperiment3"
   #ACTIVE_EXPERIMENT: "SerengetiBlanksExperiment1"
   ###
   The URL of the experiment server to use


test($filter):add test for `contains` build-in filter operation.

--- test/odata.query.filter.functions.coffee
+++ test/odata.query.filter.functions.coffee
@@ -26,6 +26,26 @@
       s = server.listen PORT, ->
         PORT = s.address().port
         done()
+
+  describe '[contains]', ->
+    it 'should filter items', (done) ->
+      request("http://localhost:#{PORT}")
+        .get("/book?$filter=contains(title,'i')")
+        .expect(200)
+        .end (err, res) ->
+          return done(err)  if(err)
+          res.body.value.length.should.greaterThan(0)
+          res.body.value.should.matchEach((item) -> item.title.indexOf('i') >= 1)
+          done()
+    it 'should filter items when it has extra spaces in query string', (done) ->
+      request("http://localhost:#{PORT}")
+        .get("/book?$filter=contains(title,'Visual Studio')")
+        .expect(200)
+        .end (err, res) ->
+          return done(err)  if(err)
+          res.body.value.length.should.greaterThan(0)
+          res.body.value.should.matchEach((item) -> item.title.indexOf('Visual Studio') >= 0)
+          done()

   describe '[indexof]', ->
     it 'should filter items', (done) ->


Add time capsule with a message for prefix-less future

--- lib/autoprefixer.coffee
+++ lib/autoprefixer.coffee
@@ -8,6 +8,16 @@
   Object.prototype.toString.apply(obj) == '[object Object]'

 cache = { }
+
+timeCapsule = (result, prefixes) ->
+  return if prefixes.browsers.selected.length == 0
+  return if prefixes.add.selectors.length > 0
+  return if Object.keys(prefixes.add).length > 2
+
+  result.warn(
+    'Greetings, space traveller. ' +
+    'We are in the golden age of prefix-less CSS, ' +
+    'where Autoprefixer is no longer needed for your stylesheet.')

 module.exports = postcss.plugin 'autoprefixer', (reqs...) ->
   if reqs.length == 1 and isPlainObject(reqs[0])
@@ -32,6 +42,7 @@

   plugin = (css, result) ->
     prefixes = loadPrefixes(from: css.source?.input.file)
+    timeCapsule(result, prefixes)
     prefixes.processor.remove(css)      if options.remove != false
     prefixes.processor.add(css, result) if options.add != false


remove constants.coffee that should be .gitignore'd

--- src/app/config/constants.coffee
+++ src/app/config/constants.coffee
@@ -1,9 +0,0 @@
-window.Cobudget.Config.Constants =
-  {
-    environment: 'development'
-    apiEndpoint: 'http://localhost:3000'
-    googClient: '@@googClient'
-    googApiKey: '@@googApiKey'
-  }
-
-


changes view code

changes view code

--- lib/changes-view.coffee
+++ lib/changes-view.coffee
@@ -10,12 +10,20 @@

 BaseTemplate = """

-

+

+      Unstaged changes
+      <button class="btn btn-xs">Stage all</button>
+

-

+

+      Staged changes
+      <button class="btn btn-xs">Unstage all</button>
+

-

-
+
Commit message

+

+      <atom-text-editor class="commit-description" data-placeholder="Enter the commit message describing your changes." style="height: 120px"></atom-text-editor>
+<button class="btn btn-commit">Commit</button>

@@ -31,21 +39,47 @@
 class ChangesView extends HTMLElement
   createdCallback: ->
     atom.commands.add 'git-experiment-changes-view .data',
-      'core:move-down': => @moveSelectionDown()
-      'core:move-up': => @moveSelectionUp()
+      'core:move-down': =>
+        console.log this
+        @moveSelectionDown()
+      'core:move-up': =>
+        console.log this
+        @moveSelectionUp()
+
+    atom.commands.add 'git-experiment-changes-view atom-text-editor',
+      'core:confirm': (event) =>
+        @commit()

     @changes = new GitChanges()

     @innerHTML = BaseTemplate
     @unstagedChangesNode = @querySelector('.unstaged.files')
     @stagedChangesNode = @querySelector('.staged.files')
+    @commitNode = @querySelector('.commit')
     @diffsNode = @querySelector('.diffs')
+    @commitMessageNode = @querySelector('atom-text-editor')
+    @commitMessageModel = @commitMessageNode.getModel()
+
+    @commitMessageModel.setSoftWrapped(true)
+    @commitMessageModel.setPlaceholderText(@commitMessageNode.dataset['placeholder'])
+
+    @selectedPath = @selectedState = null

     $(@).on 'click', '.change', (e) =>
       el = e.currentTarget
       path = el.dataset['path']
       state = el.dataset['state']
       @renderChangeDetail(path, state)
+
+    $(@).on 'click', '.column-header.unstaged .btn', => @stageAll()
+    $(@).on 'click', '.column-header.staged .btn', => @unstageAll()
+
+    $(window).on 'blur', =>
+      @watch() if atom.workspace.getActivePaneItem() == @
+
+    $(window).on 'focus', =>
+      @renderChanges() if atom.workspace.getActivePaneItem() == @
+      @unwatch()

     $(@).on 'click', '.change .btn', (e) =>
       el = $(e.target).closest('.change').get(0)
@@ -56,26 +90,21 @@
       else
         @changes.unstagePath(path)

-      console.log path, state, promise
       promise.then =>
         @renderChanges()

       e.stopPropagation()

+    $(@).on 'click', '.btn-commit', =>
+      @commit()
+
     @changeTemplate = TemplateHelper.addTemplate(this, ChangeSummaryTemplateString)

-    $(window).on 'focus', =>
-      @renderChanges() if atom.workspace.getActivePane() == @
-
     atom.workspace.onDidChangeActivePaneItem (pane) =>
-      if pane == @
-        @renderChanges()
-        @watch()
-      else
-        @unwatch()
+      @renderChanges() if pane == @
+

     @renderChanges()
-    @watch()

   getTitle: ->
     'Commit Changes'
@@ -104,9 +133,10 @@
            bit & codes.WT_TYPECHANGE

   renderChanges: ->
-    change.classList.add('old') for change in @querySelectorAll('.change')
-
     @changes.getStatuses().then (statuses) =>
+      @stagedChangesNode.innerHTML = ''
+      @unstagedChangesNode.innerHTML = ''
+
       statuses.forEach (status) =>
         if @changeIsUnstaged(status)
           @renderChangeSummary(status, 'unstaged')
@@ -114,28 +144,18 @@
         if @changeIsStaged(status)
           @renderChangeSummary(status, 'staged')

-      change.remove() for change in @querySelectorAll('.change.old')
-
-      unstagedCount = @querySelectorAll('.unstaged .change').length
-      stagedCount = @querySelectorAll('.staged .change').length
-
-      @updateColumnHeader(unstagedCount, 'unstaged')
-      @updateColumnHeader(stagedCount, 'staged')
-
-      @unstagedChangesNode.firstElementChild?.click()
-      @unstagedChangesNode.focus()
+      @querySelector('.change')?.click() unless @querySelector('.change.selected')
+      @diffsNode.innerHTML = '' unless @querySelector('.change.selected')

   renderChangeSummary: (change, state) =>
-    if existingChange = @querySelector(".change[data-path='#{change.path()}'][data-state='#{state}']")
-      existingChange.classList.remove('old')
-    else
-      changeNode = TemplateHelper.renderTemplate(@changeTemplate)
-      changeNode.querySelector('.path').textContent = change.path()
-      changeNode.firstElementChild.dataset['path'] = change.path()
-      changeNode.firstElementChild.dataset['state'] = state
-      changeNode.querySelector('button').textContent = if state == 'unstaged' then 'Stage' else 'Unstage'
-      node = if state == 'staged' then @stagedChangesNode else @unstagedChangesNode
-      node.appendChild(changeNode)
+    changeNode = TemplateHelper.renderTemplate(@changeTemplate)
+    changeNode.querySelector('.path').textContent = change.path()
+    changeNode.firstElementChild.dataset['path'] = change.path()
+    changeNode.firstElementChild.dataset['state'] = state
+    changeNode.firstElementChild.classList.add('selected') if @selectedPath == change.path() and @selectedState == state
+    changeNode.querySelector('button').textContent = if state == 'unstaged' then 'Stage' else 'Unstage'
+    node = if state == 'staged' then @stagedChangesNode else @unstagedChangesNode
+    node.appendChild(changeNode)

   renderChangeDetail: (path, state) ->
     diffsNode = @diffsNode
@@ -144,12 +164,12 @@
     @selectChange @querySelector("[data-path='#{path}'][data-state='#{state}']")

     @changes.getPatch(path, state).then (patch) =>
-      console.log patch
-      setImmediate ->
-        patchView = new PatchView
-        patchView.setPatch(patch)
-        diffsNode.appendChild(patchView)
-        diffsNode.style.webkitTransform = 'scale(1)' # fixes redraw issues
+      if patch
+        setImmediate ->
+          patchView = new PatchView
+          patchView.setPatch(patch)
+          diffsNode.appendChild(patchView)
+          diffsNode.style.webkitTransform = 'scale(1)' # fixes redraw issues

   selectChange: (el) ->
     return unless el
@@ -158,6 +178,9 @@
       commitNode.classList.remove('selected')

     el.classList.add('selected')
+
+    @selectedPath = el.dataset['path']
+    @selectedState = el.dataset['state']

     if @unstagedChangesNode.offsetHeight + @unstagedChangesNode.scrollTop - el.offsetTop - el.offsetHeight < 0
       el.scrollIntoView(false) # off the bottom of the scroll
@@ -169,6 +192,24 @@

   moveSelectionDown: ->
     @querySelector(".change.selected").nextElementSibling?.click()
+
+  stageAll: =>
+    paths = $(@).find('.unstaged .change').map( ->
+      this.dataset['path']
+    ).get()
+
+    @changes.stageAllPaths(paths).then =>
+      @renderChanges()
+    false
+
+  unstageAll: =>
+    paths = $(@).find('.staged .change').map( ->
+      this.dataset['path']
+    ).get()
+
+    @changes.unstageAllPaths(paths).then =>
+      @renderChanges()
+    false

   unwatch: ->
     if @watchSubscription?
@@ -183,4 +224,9 @@
           @renderChanges()
         , 10)

+  commit: ->
+    commitPromise = @changes.commit(@commitMessageModel.getText()).then =>
+      @commitMessageModel.setText('')
+      @renderChanges()
+
 module.exports = document.registerElement 'git-experiment-changes-view', prototype: ChangesView.prototype


Removed problematic line

Removed problematic line

--- SingularityUI/app/application.coffee
+++ SingularityUI/app/application.coffee
@@ -2,8 +2,6 @@

 User = require 'models/User'
 State = require 'models/State'
-
-RequestsAll  =require "collections/RequestsAll"

 RequestsActive = require 'collections/RequestsActive'
 RequestsCooldown = require 'collections/RequestsCooldown'


fix cursor wrap problem

fix cursor wrap problem

fixes #6

--- lib/clang-format.coffee
+++ lib/clang-format.coffee
@@ -39,7 +39,7 @@
           editor.setText(@getReturnedFormattedText(stdout))
           returnedCursorPos = @getReturnedCursorPosition(stdout)
           convertedCursorPos = @convertReturnedCursorPosition(editor, returnedCursorPos)
-          editor.setCursorScreenPosition(convertedCursorPos)
+          editor.setCursorBufferPosition(convertedCursorPos)

   getEndJSONPosition: (text) ->
     for i in [0..(text.length-1)]


Add custom URL option, drop screen_name and count options. Migrating over to Twitter API v.1.1.

--- coffee/jquery.tweetscroller.coffee
+++ coffee/jquery.tweetscroller.coffee
@@ -6,10 +6,9 @@
   pluginName = 'tweetscroller'
   document = window.document
   defaults =
+    url: ''
     tweet: '.tweet'
     template: '#tweet-template'
-    username: 'divshot'
-    count: 50
     height: 600
     speed: 30 # ms, 30-80 ms recommended, slower = better performance
     autoplay: false # CPU intensive
@@ -30,7 +29,7 @@
       @getTweets()

     getTweets: ->
-      $.getJSON "https://api.twitter.com/1/favorites.json?count=#{@options.count}&screen_name=#{@options.username}&callback=?", (data) =>
+      $.getJSON @options.url, (data) =>
         $(@element).append('

')
         @board = $(@element).find('.tweetscroller')


set queries if project was selected

set queries if project was selected

--- coffee/services/dataAdapter.coffee
+++ coffee/services/dataAdapter.coffee
@@ -241,6 +241,8 @@
     setQueries: (url, queries) ->
       if not url? or not queries? then return
       @_data[url].queries = queries
+      if @selectedProject and @selectedProject.url is url
+        @queries = queries
       Log.debug("setQueries: #{url}")

   return new DataAdapter


linkify markdown-it

linkify markdown-it

--- lib/carte/client/views/card.cjsx
+++ lib/carte/client/views/card.cjsx
@@ -2,7 +2,7 @@
 React = require('react')
 Edit = require('./edit')
 ModalTrigger = require('react-bootstrap/lib/ModalTrigger')
-markdownIt = require('markdown-it')()
+markdownIt = require('markdown-it')(linkify: true)

 module.exports = React.createClass
   displayName: 'Card'


use seed

use seed

--- lib/carte/client/views/list.cjsx
+++ lib/carte/client/views/list.cjsx
@@ -45,7 +45,7 @@

   randomParam: ()->
     query = $.extend {}, @props.cards.query
-    query = $.extend query, {sort_order: 'random', page: 1}
+    query = $.extend query, {sort_order: 'random', page: 1, seed: new Date().getTime()}
     $.param(query)

   pageParam: (page)->


"Directory permissions"... sortof! :D

"Directory permissions"... sortof! :D

--- browser/node.coffee
+++ browser/node.coffee
@@ -428,6 +428,8 @@
       stat.mtime = (new Date).getTime()
       stat.is_file = false
       stat.is_directory = true
+      #XXX: Shhhh...
+      stat.mode = 0o644
       stat
     else
       file = fs_state.open path, 'r'
@@ -501,13 +503,13 @@

   readdirSync: (path) ->
     dir_contents = fs_state.list(path)
-    throw "Could not read directory '#{path}'" unless dir_contents?
+    throw "Could not read directory '#{path}'" unless dir_contents? and path != ''
     return dir_contents

   unlinkSync: (path) -> throw "Could not unlink '#{path}'" unless fs_state.rm(path)
   rmdirSync: (path) -> throw "Could not delete '#{path}'" unless fs_state.rm(path, true)

-  existsSync: (path) -> fs_state.is_file(path) or fs_state.is_directory(path)
+  existsSync: (path) -> path != '' and (fs_state.is_file(path) or fs_state.is_directory(path))

   mkdirSync: (path) -> throw "Could not make directory #{path}" unless fs_state.mkdir path


Handle potential network start errors

Handle potential network start errors

--- spec/Network.coffee
+++ spec/Network.coffee
@@ -130,7 +130,8 @@
     it 'should send some initials when started', (done) ->
       chai.expect(n.initials).not.to.be.empty
       cb = done
-      n.start()
+      n.start (err) ->
+        return done err if err

     it 'should contain two processes', ->
       chai.expect(n.processes).to.not.be.empty
@@ -252,7 +253,8 @@
         nw.loader.components.Cb = -> cb
         nw.connect (err) ->
           return done err if err
-          nw.start()
+          nw.start (err) ->
+            return done err if err
       , true

     it 'should not send default values to nodes with an edge', (done) ->
@@ -270,7 +272,8 @@
         nw.loader.components.Merge = Merge
         nw.connect (err) ->
           return done err if err
-          nw.start()
+          nw.start (err) ->
+            return done err if err
       , true

     it 'should not send default values to nodes with IIP', (done) ->
@@ -286,7 +289,8 @@
         nw.loader.components.Merge = Merge
         nw.connect (err) ->
           return done err if err
-          nw.start()
+          nw.start (err) ->
+            return done err if err
       , true

   describe "Nodes are added first, then edges, then initializers (i.e. IIPs), and in order of definition order within each", ->
@@ -383,7 +387,8 @@
           n = nw
           nw.connect (err) ->
             return done err if err
-            nw.start()
+            nw.start (err) ->
+              return done err if err
         , true
       , 10
     it 'should allow removing the IIPs', (done) ->
@@ -405,7 +410,8 @@
         done()
       g.addInitial cb, 'Callback', 'callback'
       g.addInitial 'Baz', 'Repeat', 'in'
-      n.start()
+      n.start (err) ->
+        return done err if err

     describe 'on stopping', ->
       it 'processes should be running before the stop call', ->


Remove unused method.

Remove unused method.

--- src/code/views/node-view.coffee
+++ src/code/views/node-view.coffee
@@ -68,10 +68,6 @@
 module.exports = NodeView = React.createClass

   displayName: "NodeView"
-
-  renableDragging: ->
-
-    $elem.draggable dragOpts

   componentDidUpdate: ->
     handle = '.img-background'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animated.coffee
+++ test/animated.coffee
@@ -406,7 +406,7 @@
     data: animationData
     includedModules: ["Animated"]

-  equals animation.I.currentFrameIndex, animation.I.activeAnimation.frames.first(), "Animation should default to initial sprite"
+  equals animation.I.currentFrameIndex, 0, "should default to initial frame position of 0"

   animation.update()


stupid bug fixed

stupid bug fixed

--- lib/Context.coffee
+++ lib/Context.coffee
@@ -160,7 +160,7 @@

             # extend context with methods from config. add @method._super for each
             @_process config,
-                next(key, value)=>
+                each: (key, value, next)=>
                     if typeof value is 'function'

                         _orig_super = @[key]


added support for config.configure function

--- lib/Context.coffee
+++ lib/Context.coffee
@@ -119,10 +119,10 @@
         ## base configuration
         ##
         configure: (config)->
-
+
             if typeof config is 'string'
                 config = new floyd.Config floyd.tools.objects.resolve config
-
+
             config.id ?= @__ctxID()

             config.type ?= 'floyd.Context'
@@ -264,6 +264,11 @@
                     @_getAuthManager().authorize config.TOKEN

+            if typeof config?.configure is 'function'
+                config = config.configure.apply @, [config]
+
+            ##
+
             return config

@@ -416,8 +421,10 @@
         ##
         ##
         lookup: (name, identity, done)->
-
-            __ident = identity.id
+
+            if !(__ident = identity.id) || !identity.token
+                console.log '2. parameter is not identity', @ID
+                throw new Error '2. parameter is not identity'

             _children = 0
             _parent = !!(@parent && @parent.lookup)


Everything!

Everything!

--- test/appveyor.coffee
+++ test/appveyor.coffee
@@ -16,6 +16,7 @@
   uri = url.parse api
   data = JSON.stringify data
   uri.method = 'POST'
+  uri.path = path
   uri.pathname = path
   uri.headers =
     'Content-Type': 'application/json'


dont catch error

dont catch error

--- tentacle-transformer.coffee
+++ tentacle-transformer.coffee
@@ -22,16 +22,9 @@

   toJSON: () =>
     return unless @buffer?.remaining() > 0
-
-    try
-      decoded = @MicrobluProto.decodeDelimited @buffer
-      return null if !decoded
-      @buffer.compact()
-
-      return JSON.parse decoded.encodeJSON()
-
-    catch error
-      console.log('transformer error:',error.message)
-      return null
+    decoded = @MicrobluProto.decodeDelimited @buffer
+    return null if !decoded
+    @buffer.compact()
+    return JSON.parse decoded.encodeJSON()

 module.exports = TentacleTransformer


Improving CLI reporting with simple msgs.

--- build/bumper.coffee
+++ build/bumper.coffee
@@ -25,9 +25,7 @@
 		process.exit()

 new_version = "#{major}.#{minor}.#{patch}"
-
-console.log version
-console.log new_version
+console.log "Updating version to #{new_version} in files:"

 # updating README
 filepath = path.join __dirname, "../README.md"
@@ -35,10 +33,12 @@
 search = />\sVersion\s[0-9]+.[0-9]+.[0-9]+/
 replace = '> Version ' + new_version
 fs.writeFileSync filepath, (contents.replace search, replace)
+console.log '• README.md'

 # updating package.json
 filepath = path.join __dirname, "../package.json"
 contents = fs.readFileSync filepath, 'utf-8'
 search = /"version":\s"[0-9]+.[0-9]+.[0-9]+"/
 replace = "\"version\": \"#{new_version}\""
-fs.writeFileSync filepath, (contents.replace search, replace)+fs.writeFileSync filepath, (contents.replace search, replace)
+console.log '• package.json'


Add missing mock() test, and add pre-condition test.

--- test/TinyMockJS.test.coffee
+++ test/TinyMockJS.test.coffee
@@ -7,6 +7,9 @@

   it "Object does not have the property or method 'expects'", ->
     should.not.exist(Object.prototype.expects)
+
+  it "Object does not have the property or method 'my_expects'", ->
+    should.not.exist(Object.prototype.my_expects)

   it "Object does not have the property or method 'my_method'", ->
     should.not.exist(Object.prototype.my_method)
@@ -55,6 +58,10 @@
   it "Adds expects() to Object so that it is available on all objects", ->
     mock ->
       should.exist(Object.prototype.expects)
+
+  it "Adds the expects method name that was passed as an option to mock()", ->
+    mock expects_method_name: "my_expects", ->
+      should.exist(Object.prototype.my_expects)

   it "Removes expects() from Object after running the passed function", ->
     mock ->
@@ -73,6 +80,14 @@
     catch error
       # ignore
     should.not.exist(Object.prototype.expects)
+
+  it "Removes the expects method name that was passed as an option to mock() when the passed function throws an exception", ->
+    try
+      mock expects_method_name: "my_expects", ->
+        throw new Error("an error")
+    catch error
+      # ignore
+    should.not.exist(Object.prototype.my_expects)

   it "does not eat exceptions thrown by the passed function", ->
     (->


A and U goto archive and unread pages

A and U goto archive and unread pages

--- javascripts/chromepaper.js.coffee
+++ javascripts/chromepaper.js.coffee
@@ -73,12 +73,22 @@
       selectedArticle = $(this)
       showSelectedArticle()

+  gotoArchive = () ->
+    console.log(window.location)
+    window.location = window.location.origin + "/archive"
+
+  gotoUnread = () ->
+    console.log(window.location)
+    window.location = window.location.origin + "/u"
+
   bindEvents = () ->
     $(document).bind('keydown', 'j', selectNextArticle)
     $(document).bind('keydown', 'k', selectPreviousArticle)
     $(document).bind('keydown', 't', openTextArticle)
     $(document).bind('keydown', 'o', openOriginalArticle)
     $(document).bind('keydown', 'a', archiveSelectedArticle)
+    $(document).bind('keydown', 'shift+a', gotoArchive)
+    $(document).bind('keydown', 'shift+u', gotoUnread)
     $(articleContainer).click(articleClicked)
     $(archiveLink).click(articleArchived)


Fixed Postman import for requests without "order".

It sounds like *some* Postman collections have no "order" set, in which case we need to insert requests in an incertain order instead of not insterting them at all.

--- PostmanImporter.coffee
+++ PostmanImporter.coffee
@@ -106,17 +106,30 @@
         # Create a Paw Group
         pawRootGroup = context.createRequestGroup postmanCollection["name"]

-        # Add Postman folders
-        if postmanCollection["folders"]
-            for postmanFolder in postmanCollection["folders"]
-                pawGroup = @createPawGroup context, postmanRequestsById, postmanFolder
+        # If we have either "folders" or "order"
+        if postmanCollection["folders"]? || postmanCollection["order"]?

-                # Add group to root
-                pawRootGroup.appendChild pawGroup
+            # Add Postman folders
+            if postmanCollection["folders"]?
+                for postmanFolder in postmanCollection["folders"]
+                    pawGroup = @createPawGroup context, postmanRequestsById, postmanFolder

-        # Add Postman requests in root
-        if postmanCollection["order"]
-            for postmanRequestId in postmanCollection["order"]
+                    # Add group to root
+                    pawRootGroup.appendChild pawGroup
+
+            # Add Postman requests in root
+            if postmanCollection["order"]?
+                for postmanRequestId in postmanCollection["order"]
+                    # Create a Paw request
+                    pawRequest = @createPawRequest context, postmanRequestsById, postmanRequestId
+
+                    # Add request to root group
+                    pawRootGroup.appendChild pawRequest
+
+        # If the collection does not have "folders" or "order"
+        # add all requests in root
+        else
+            for postmanRequestId, postmanRequest of postmanRequestsById
                 # Create a Paw request
                 pawRequest = @createPawRequest context, postmanRequestsById, postmanRequestId


Add initial text insertion

Add initial text insertion

--- lib/gamboge-view.coffee
+++ lib/gamboge-view.coffee
@@ -42,6 +42,7 @@
   buffer: null
   $ghostText: null

+  lastChangeWasPredictionInsert: false
   predictionMarker: null

   # The model.
@@ -60,10 +61,10 @@

   registerEvents: ->
     console.log 'Registering events...'
-    # Updates the prediction model.
-    # Invoked 300ms after last buffer change.
+    # Updates the prediction model. Invoked 300ms after last buffer change.
     # TODO: use `onDidChange` instead?
     @editor.onDidStopChanging =>
+      return @resetChangeIgnorance() if @lastChangeWasPredictionInsert

       text = @getTextForCursorContext()

@@ -74,6 +75,7 @@
         return unless predictions?
         @predictionList.setPredictions predictions

+    # TODO: Is this needed?
     @predictionList.onDidChangePredictions =>
       console.log did_prediction_change: @predictionList

@@ -83,7 +85,9 @@
       console.log show_text_for: @predictionList.current()

     @subscribeToCommand @editorView, 'gamboge:complete', =>
+      @completeTokens n: 1
     @subscribeToCommand @editorView, 'gamboge:complete-all', =>
+      @completeTokens all: yes

     @subscribeToCommand @editorView, 'gamboge:next-prediction', =>
       @predictionList.next()
@@ -95,6 +99,36 @@
       console.log { changed_index: event }
       {target} = event
       @showGhostText target.current()
+
+  completeTokens: ({n, all}) ->
+    {entropy, tokens} = @predictionList.current()
+    n = tokens.length if all
+    insertTokens = tokens.slice(0, n)
+
+    insertText = GambogeView.textFromTokens insertTokens
+    cursorPosition = @editor.getCursorBufferPosition()
+
+    @ignoreNextChange()
+    @editor.setTextInBufferRange([cursorPosition, cursorPosition], insertText)
+
+
+
+  @specialTokens:
+    '<NEWLINE>': (editor) -> '\n'
+    '<NL>':      (editor) -> '\n'
+    '<INDENT>':           -> ''
+    'DEDENT':             -> ''
+
+  # TODO: Extract the method into its own module!
+  # TODO: Make it take in the current indent level. Do a bunch of indentation
+  # thing!
+  @textFromTokens: (tokens) ->
+    text = ""
+    for token in tokens
+      text +=
+        if token of GambogeView.specialTokens then GambogeView.specialTokens[token]()
+        else " #{token}"
+    text

   # Using markers, shows the GhostText.
@@ -140,13 +174,20 @@
     @predictionMarker = null
     @unshowGhostText()

-
   unshowGhostText: ->
     return unless @$ghostText?
     @editorView.find('.gamboge-ghost').remove()
     console.log @$ghostText
     @$ghostText = null
     @editorView.removeClass('.gamboge')
+
+  # Next change event was our fault...
+  ignoreNextChange: ->
+    @lastChangeWasPredictionInsert = true
+
+  resetChangeIgnorance: ->
+    console.log last_change_ignored: true
+    @lastChangeWasPredictionInsert = false

   # Gets a whole bunch of text prior to the cursor.
   getTextForCursorContext: ->


fix typo

fix typo

--- content_scripts/mode_visual.coffee
+++ content_scripts/mode_visual.coffee
@@ -70,7 +70,7 @@
     else
       @selection.modify @alterMethod, direction, granularity

-  # Return a simple camparable value which depends on various aspects of the selection.  This is used to
+  # Return a simple comparable value which depends on various aspects of the selection.  This is used to
   # detect, after a movement, whether the selection has changed.
   hashSelection: ->
     range = @selection.getRangeAt(0)


fix browsertest bundle

fix browsertest bundle

--- browsertest/jquery-tests.coffee
+++ browsertest/jquery-tests.coffee
@@ -1,6 +1,5 @@
 sinon = require 'sinon'
 Bacon = require '../dist/Bacon'
-require '../src/jquery'
 expect = require('chai').expect
 $.fn.asEventStream = Bacon.$.asEventStream


More robust

More robust

--- src/6.number.coffee
+++ src/6.number.coffee
@@ -22,7 +22,7 @@
     if radix != 10
         fractionalSize = 0
     x = @valueOf()
-    if integerSize > 80 or fractionalSize > 20 or x >= 1e21
+    if integerSize > 80 or fractionalSize > 20 or x >= 1e21 or x <= -1e21
         fail("Number or argument too large")
     s =
         if radix == 10
@@ -32,7 +32,7 @@
                 t
             else
                 if t[ePos + 1] == "+"
-                    fail("Number too large")
+                    fail("Number too large") # Redundant but needed to be robust
                 else
                     # JavaScript shows any number < 0.000001 in exponential form, so we
                     # use `toFixed` to disable the exponential form.


add setting for Atom snippet format

add setting for Atom snippet format

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -37,6 +37,16 @@
       type: "boolean"
       default: true
       order: 4
+    atomDefaultSyntax:
+      title: "Atom Default Syntax"
+      description: "Specify your preferred default syntax for Atom snippets"
+      default: "CSON"
+      type: "string"
+      enum: [
+        "CSON",
+        "JSON"
+      ],
+      order: 5
   subscriptions: null
   meta: "Generated with Atomizr – https://atom.io/packages/atomizr"


:minibus: LinterViews::removeBubble -> LinterViews::_removeBubble

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -46,7 +46,7 @@
     @_bottomStatus.count = counts.project

   updateBubble: (point)->
-    @removeBubble()
+    @_removeBubble()
     return unless @_showBubble
     return unless @_messages.length
     point = point || @linter.activeEditor.getCursorBufferPosition()
@@ -61,11 +61,6 @@
         item: @_renderBubble(message)
       })
       break
-
-  removeBubble: ->
-    return unless @_bubble
-    @_bubble.destroy()
-    @_bubble = null

   # consumed in views/bottom-tab-{file, project}
   changeTab: (Tab)->
@@ -97,7 +92,12 @@
   deactivate: ->
     @_panel.removeDecorations()
     @_panelWorkspace.destroy()
-    @removeBubble()
+    @_removeBubble()
+
+  _removeBubble: ->
+    return unless @_bubble
+    @_bubble.destroy()
+    @_bubble = null

   _renderBubble: (message)->
     bubble = document.createElement 'div'
@@ -110,7 +110,7 @@
   _renderPanel: ->
     @_panel.innerHTML = ''
     @_removeDecorations()
-    @removeBubble()
+    @_removeBubble()
     if not @_messages.length
       return @setPanelVisibility(false)
     @setPanelVisibility(true)


Fix issue #5148 - Taiga events send auth message before subscriptions

--- app/coffee/modules/events.coffee
+++ app/coffee/modules/events.coffee
@@ -204,7 +204,6 @@
     ###########################################
     onOpen: ->
         @.connected = true
-        @.startHeartBeatMessages()

         @log.debug("WebSocket connection opened")
         token = @auth.getToken()
@@ -215,6 +214,7 @@
         }

         @.sendMessage(message)
+        @.startHeartBeatMessages()
         @.notifications()

     onMessage: (event) ->


fix typo

fix typo

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -125,7 +125,7 @@
   updateScroll: (top) =>
     minimapHeight = @miniScrollView.outerHeight()
     scrollViewHeight = @scrollView.outerHeight()
-    # Need scroll-top value when in find-replace or in Vim mode(`gg`, `shift+g`).
+    # Need scroll-top value when in find pane or on Vim mode(`gg`, `shift+g`).
     # Or we can find a better solution.
     if top?
       overlayY = top


Revert "Honor `created()` function for an item in a submenu."

This reverts commit 9917ed39aa30e00e3300638e5da5a4dd6559b6b4.

Sorry, apparently I don't know how to use Git anymore.
I'm just trying to put this up for review and it went straight
to master instead.

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -136,9 +136,12 @@

       for itemSet in matchingItemSets
         for item in itemSet.items
-          itemForEvent = @cloneItemForEvent(item, event)
-          if itemForEvent
-            MenuHelpers.merge(currentTargetItems, itemForEvent, itemSet.specificity)
+          continue if item.devMode and not @devMode
+          item = Object.create(item)
+          if typeof item.shouldDisplay is 'function'
+            continue unless item.shouldDisplay(event)
+          item.created?(event)
+          MenuHelpers.merge(currentTargetItems, item, itemSet.specificity)

       for item in currentTargetItems
         MenuHelpers.merge(template, item, false)
@@ -146,19 +149,6 @@
       currentTarget = currentTarget.parentElement

     template
-
-  # Returns an object compatible with `::add()` or `null`.
-  cloneItemForEvent: (item, event) ->
-    return null if item.devMode and not @devMode
-    item = Object.create(item)
-    if typeof item.shouldDisplay is 'function'
-      return null unless item.shouldDisplay(event)
-    item.created?(event)
-    if Array.isArray(item.submenu)
-      item.submenu = item.submenu
-        .map((item) => @cloneItemForEvent(item, event))
-        .filter((item) -> item isnt null)
-    return item

   convertLegacyItemsBySelector: (legacyItemsBySelector, devMode) ->
     itemsBySelector = {}


Verify path is string before reading

Verify path is string before reading

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -135,7 +135,7 @@
     @getMarkdownSource().then (source) => @renderMarkdownText(source) if source?

   getMarkdownSource: ->
-    if @file?
+    if @file? and typeof @file.path is 'string'
       @file.read()
     else if @editor?
       Promise.resolve(@editor.getText())


Bug fix : Didn't check if there was a location in tests

--- test/hTwitterAdapter.coffee
+++ test/hTwitterAdapter.coffee
@@ -76,7 +76,7 @@
       hActor.onMessage = (hMessage) =>
         count++
         hMessage.type.should.be.equal("hTweet")
-        if hMessage.payload.location[0] > -2.5 and hMessage.payload.location[0] < 7.2 and hMessage.payload.location[1] > 43.3 and hMessage.payload.location[1] < 50.6
+        if hMessage.payload.location and hMessage.payload.location[0] > -2.5 and hMessage.payload.location[0] < 7.2 and hMessage.payload.location[1] > 43.3 and hMessage.payload.location[1] < 50.6
           if count is 1
             done()


Fix History::serializeParams

Fix History::serializeParams

--- src/marbles/history.js.coffee
+++ src/marbles/history.js.coffee
@@ -53,7 +53,7 @@
     query = []
     for key,val of params
       val = val.join(',') if _.isArray(val)
-      continue if val?.match(/^[\s\r\t\n]*$/)
+      continue if val?.match?(/^[\s\r\t\n]*$/)
       query.push "#{key}=#{encodeURIComponent(val)}"
     "?" + query.join("&")


See #63, #64. Add backwards comptability for fix when used with newer Atom.

--- lib/preview-view.coffee
+++ lib/preview-view.coffee
@@ -10,7 +10,15 @@
 version  = pkg.version
 # Get Atom internal modules
 resourcePath = atom.config.resourcePath
-EditorView = require path.resolve resourcePath, 'src', 'editor-view'
+try
+  # Try to get specifically the ReactEditorView
+  # For backwards compatibilities with previous Atom versions
+  # v0.123.0 and earlier
+  ReactEditorView = require path.resolve resourcePath, 'src', 'react-editor-view'
+catch e
+  # Catch error
+  # It will error on Atom versions v0.124.0 and later
+EditorView = ReactEditorView ? require path.resolve resourcePath, 'src', 'editor-view'
 TextBuffer = require path.resolve resourcePath, 'node_modules', 'text-buffer'
 Editor = require path.resolve resourcePath, 'src', 'editor'


Less brackets.

Less brackets.

--- lib/price-import.coffee
+++ lib/price-import.coffee
@@ -9,24 +9,24 @@
 class PriceImport extends ProductImport

   constructor: (@logger, options = {}) ->
-    super(@logger, options)
+    super @logger, options
     @skuOfPerformedPrices = []
     @sync.config [{type: 'prices', group: 'white'}].concat(['base', 'references', 'attributes', 'images', 'variants', 'metaAttributes'].map (type) -> {type, group: 'black'})

   _processBatches: (prices) ->
     batchedList = _.batchList(prices, 30) # max parallel elements to process
     Promise.map batchedList, (pricesToProcess) =>
-      skus = @_extractUniqueSkus(pricesToProcess)
-      predicate = @_createProductFetchBySkuQueryPredicate(skus)
+      skus = @_extractUniqueSkus pricesToProcess
+      predicate = @_createProductFetchBySkuQueryPredicate skus
       @client.productProjections
-      .where(predicate)
-      .staged(true)
+      .where predicate
+      .staged true
       .all()
       .fetch()
       .then (results) =>
         debug "Fetched products: %j", results
         queriedEntries = results.body.results
-        wrappedProducts = @_wrapPricesIntoProducts(pricesToProcess, queriedEntries)
+        wrappedProducts = @_wrapPricesIntoProducts pricesToProcess, queriedEntries
         console.log "Wrapped #{_.size prices} price(s) into #{_.size wrappedProducts} existing product(s)."
         @_createOrUpdate wrappedProducts, queriedEntries
         .then (results) =>


add API integration tests for password reset endpoint

--- test/integration/api.coffee
+++ test/integration/api.coffee
@@ -47,7 +47,7 @@
     @toolName = "int-test-#{String(Math.random()*Math.pow(2,32))[0..6]}"
   context "When I'm not logged in", ->

-    describe 'Sign up', ->
+    describe 'I can sign up', ->
       context 'POST /api/<username>', ->
         before (done) ->
           request.post
@@ -67,6 +67,61 @@
           @body.should.include
             shortName: 'tabbytest'
             displayName: 'Tabatha Testerson'
+
+    describe 'I can request a password reset email', ->
+      context 'POST /api/<shortname>/set-password/', ->
+        before (done) ->
+          request.post
+            uri: "#{serverURL}/api/ickletest/reset-password"
+          , (err, res, body) =>
+            @response = res
+            @body = parseJSON body
+            done()
+
+        it 'returns a HTTP 200 status', ->
+          @response.statusCode.should.equal 200
+
+        it 'returns a JSON string with a success message', ->
+          @body.should.not.have.property 'error'
+          @body.should.have.property 'success'
+          @body.success.should.equal 'A password reset link has been emailed to ickletest'
+
+    describe 'I can’t request a password reset email for a user that doesn’t exist', ->
+      context 'POST /api/i-do-not-exist/set-password/', ->
+        before (done) ->
+          request.post
+            uri: "#{serverURL}/api/i-do-not-exist/reset-password"
+          , (err, res, body) =>
+            @response = res
+            @body = parseJSON body
+            done()
+
+        it 'returns a HTTP 404 status', ->
+          @response.statusCode.should.equal 404
+
+        it 'returns a JSON string with an error message', ->
+          @body.should.not.have.property 'success'
+          @body.should.have.property 'error'
+          @body.error.should.equal 'That username could not be found'
+
+    describe 'I get a sensible error message when a user doesn’t have a password token', ->
+      context 'POST /api/ehg/set-password/', ->
+        before (done) ->
+          request.post
+            uri: "#{serverURL}/api/ehg/reset-password"
+          , (err, res, body) =>
+            @response = res
+            @body = parseJSON body
+            done()
+
+        it 'returns a HTTP 500 status', ->
+          @response.statusCode.should.equal 500
+
+        it 'returns a JSON string with an error message', ->
+          @body.should.not.have.property 'success'
+          @body.should.have.property 'error'
+          @body.error.should.equal 'Something went wrong: token not found'
+

   context "When I have set my password", ->
     before (done) ->


Added case sensitive search back if you include a capital letter

--- lib/motions/search-motion.coffee
+++ lib/motions/search-motion.coffee
@@ -51,7 +51,8 @@
   scan: ->
     term = @input.characters
     mod = 'g'
-    if atom.config.get 'vim-mode.useSmartcaseForSearch'
+    usingSmartcase = atom.config.get 'vim-mode.useSmartcaseForSearch'
+    if usingSmartcase && !term.match('[A-Z]')
       mod += 'i'
     if term.indexOf('\\c') != -1
       term = term.replace('\\c','')


fix for URL with space in between

fix for URL with space in between

--- lib/open-in-browsers-view.coffee
+++ lib/open-in-browsers-view.coffee
@@ -52,7 +52,7 @@
     else
       editor = atom.workspace.getActiveTextEditor()
       fpath = editor.getPath()
-    exec "#{cmd} #{fpath}" if fpath
+    exec "#{cmd} \"#{fpath}\"" if fpath
     return false

   # Returns an object that can be retrieved when package is activated


removed warning about no inputs

removed warning about no inputs

--- src/mist-ninja-builder.coffee
+++ src/mist-ninja-builder.coffee
@@ -95,7 +95,6 @@
     if statement.main_inputs.length
       statement.main_inputs =
         MistGlobber.doAllGlobs statement.main_inputs, @mistDir
-      # TODO error on no inputs (but only if globs were supplied)

     if statement.foreach
       for inp in statement.main_inputs


BUG-FIX Getting template property has to be done from prototype

--- src/modules/controller.coffee
+++ src/modules/controller.coffee
@@ -23,12 +23,12 @@
       # Inherit injector
       @injector = @parent.injector

-    if opts.template and @template is undefined
-      @template = opts.template
-
-    if @template is true
-      @template = @$el.html()
-      @$el.empty()
+    switch @template
+      when undefined
+        @template = opts.template if opts.template
+      when true
+        @template = @$el.html()
+        @$el.empty()

     @params = opts.params or {}

@@ -87,8 +87,8 @@
       buffer = _.sortBy(buffer, (b)-> - b.controller.prototype.orderPriority ) if sortHelpers

       for item in buffer
-        helper = new item.controller(el: el, parent: @, mainAttr: item.value)
-        stop = true if helper.template
+        stop = true if item.controller.prototype.template
+        new item.controller el: el, parent: @, mainAttr: item.value

     @_parseNode child for child in node.childNodes unless stop or not node
     undefined


No more slidey animations

No more slidey animations

--- app/javascripts/SearchBox.coffee
+++ app/javascripts/SearchBox.coffee
@@ -29,11 +29,6 @@
         @addressDict[address.street].push(address)

       $("input#streetName").keyup(@updateView)
-      # $("input#streetName").focus ()->
-      #   $(".searchResults").slideDown()
-      # $("input#streetName").blur ()->
-      #   $(".searchResults").slideUp() unless $("input#streetName").val() != ""
-

   capitaliseFirstLetter: (string) ->
     string = string.toLowerCase()


Fix wall collision coalescing code

Fix wall collision coalescing code

--- app/lib/world/world_utils.coffee
+++ app/lib/world/world_utils.coffee
@@ -72,8 +72,12 @@
 module.exports.now = (if window?.performance?.now? then (-> window.performance.now()) else (-> new Date()))

 module.exports.consolidateThangs = consolidateThangs = (thangs) ->
+  # We can gain a performance increase by consolidating all regular walls into a minimal covering, non-intersecting set a la Gridmancer.
   debug = false
-  isStructural = (t) -> t.stateless and t.collides and t.collisionCategory is 'obstacles' and t.shape in ['box', 'sheet'] and t.restitution is 1.5 and (t.pos.x - t.width / 2 >= 0) and (t.pos.y - t.height / 2 >= 0)
+  isStructural = (t) ->
+    t.stateless and t.collides and t.collisionCategory is 'obstacles' and t.shape in ['box', 'sheet'] and  # Can only do wall-like obstacle Thangs.
+    t.spriteName isnt 'Ice Wall' and t.restitution is 1.0 and  # Fixed restitution value on 2016-03-15, but it causes discrepancies, so disabled for Kelvintaph levels.
+    (t.pos.x - t.width / 2 >= 0) and (t.pos.y - t.height / 2 >= 0)  # Grid doesn't handle negative numbers, so don't coalesce walls below/left of 0, 0.
   structural = _.remove thangs, isStructural
   return unless structural.length
   rightmost = _.max structural, (t) -> t.pos.x + t.width / 2
@@ -101,7 +105,9 @@
     thang.pos.y = rect.y
     thang.width = rect.width
     thang.height = rect.height
+    thang.destroyBody()
     thang.createBodyDef()
+    thang.createBody()
     dissection.push thang

   dissectRectangles grid, addStructuralThang, false, debug


:memo: Add a few comments

:memo: Add a few comments

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -224,11 +224,15 @@
     else
       if droppedPath = dataTransfer.getData('text/plain')
         atom.workspace.open(droppedPath).then (item) =>
+          # Move the item from the pane it was opened on to the target pane
+          # where it was dropped onto
           toPane = $(event.target).closest('.pane').view()
           toIndex = @getDropTargetIndex(event)
           activePane = atom.workspaceView.getActivePaneView()
           activeItemIndex = activePane.getActiveItemIndex()
           @moveItemBetweenPanes(activePane, activeItemIndex, toPane, toIndex, item)
+
+          # Let window where the drag started know that the tab was dropped
           BrowserIpc.sendChannel(fromProcessId, fromRoutingId, 'tab:dropped', fromIndex, fromPaneIndex)

         atom.focus()


:art: Rewrite conditional

:art: Rewrite conditional

--- lib/themes-panel.coffee
+++ lib/themes-panel.coffee
@@ -166,15 +166,15 @@
       @parents('.settings-view').view()?.showPanel(@activeSyntaxTheme, {back: 'Themes', pack: @activeSyntaxTheme})

   toggleCurrentThemeButtons: ->
-    if not atom.config.get(@activeUiTheme)?
+    if atom.config.get(@activeUiTheme)?
+      @currentUiThemeSettings.show()
+    else
       @currentUiThemeSettings.hide()
+
+    if atom.config.get(@activeSyntaxTheme)?
+      @currentSyntaxThemeSettings.show()
     else
-      @currentUiThemeSettings.show()
-
-    if not atom.config.get(@activeSyntaxTheme)?
       @currentSyntaxThemeSettings.hide()
-    else
-      @currentSyntaxThemeSettings.show()

   # Populate the theme menus from the theme manager's active themes


Moving path handling to Node cross-platform functions

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -1,9 +1,9 @@
-
+p = require('path')

 module.exports = {
   basename: (path) ->
-    return path.replace(/^.*\//, '')
+    return p.basename(path)

   dirname: (path) ->
-    return path.replace(/[^\/]+$/, '')
+    return p.dirname(path)
 }


Verify rangeFromLineNumber inputs

Verify rangeFromLineNumber inputs

Verify that lineNumber and colStart are numbers with values of at least
0.

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -44,7 +44,7 @@
         spawnedProcess = new BufferedNodeProcess({command, args, options, stdout, stderr, exit})
       else
         spawnedProcess = new BufferedProcess({command, args, options, stdout, stderr, exit})
-      spawnedProcess.onWillThrowError(({error, handle}) =>
+      spawnedProcess.onWillThrowError(({error, handle}) ->
         return reject(error) if error and error.code is 'ENOENT'
         handle()
         if error.code is 'EACCES'
@@ -58,7 +58,8 @@

   rangeFromLineNumber: (textEditor, lineNumber, colStart) ->
     throw new Error('Provided text editor is invalid') unless textEditor?.getText?
-    if typeof lineNumber is 'undefined' or lineNumber isnt lineNumber
+    if typeof lineNumber isnt 'number' or lineNumber isnt lineNumber or lineNumber < 0 or
+    typeof colStart isnt 'number' or colStart isnt colStart or colStart < 0
       return [[0, 0], [0, 1]]
     lineLength = textEditor.getBuffer().lineLengthForRow(lineNumber)
     throw new Error('Column start greater than line length') if colStart > lineLength


Don't re-store the last result

Don't re-store the last result

--- lib/history.coffee
+++ lib/history.coffee
@@ -40,7 +40,7 @@

   store: ->
     text = @miniEditor.getText()
-    return if not text
+    return if not text or text is @getLast()
     @scratch = null
     @items.push(text)
     @index = @items.length - 1


fix and generalize tooltips

fix and generalize tooltips

--- lib/util/tooltip.coffee
+++ lib/util/tooltip.coffee
@@ -1,3 +1,5 @@
+{CompositeDisposable, Emitter} = require 'atom'
+
 # Tooltip
 #
 # Tooltips that support arbitrary HTML content.
@@ -22,14 +24,25 @@

 module.exports =
 class Tooltip
-  constructor: (@parent, content, {@cond, @delay}={}) ->
+  constructor: (@parent, content, {@cond, @delay, @clas}={}) ->
     @cond  = (-> true) unless @cond?
     @delay = 150       unless @delay?
-
+    @emitter = new Emitter()
     @view = @tooltipView content
     document.body.appendChild @view
+
+    # remember old mouse listeners
+    @oldOnMouseOver = @parent.onmouseover
+    @oldOnMouseOut = @parent.onmouseout
+
     @showOnHover()
     this
+
+  onDidShow: (f) ->
+    @emitter.on('didShow', f)
+
+  onDidHide: (f) ->
+    @emitter.on('didHide', f)

   hide_: ->
     @view.classList.add 'dontshow'
@@ -37,18 +50,24 @@

   hide: ->
     @view.classList.add 'dontshow'
+    @emitter.emit 'didHide'
     setTimeout (=> @hide_()), 100

   show: ->
     @view.style.display = 'block'
+    @emitter.emit 'didShow'
     setTimeout ( => @view.classList.remove 'dontshow'), 20

   destroy: ->
-    document.body.removeChild @view
+    if document.body.contains @view
+      document.body.removeChild @view
+    @parent.onmouseover = @oldOnMouseOver
+    @parent.onmouseout = @oldOnMouseOut

   tooltipView: (content) ->
     tt = document.createElement 'div'
     tt.classList.add 'ink-tooltip', 'dontshow'
+    if @clas then tt.classList.add @clas
     tt.style.display = 'none'
     if content then tt.appendChild content
     tt
@@ -63,11 +82,12 @@
     @parent.onmouseout = =>
       clearTimeout showTimer
       hideTimer = setTimeout (=> @hide()), @delay
-    @view.onmouseover  = => clearTimeout hideTimer
-    @view.onmouseout   = => hideTimer = setTimeout (=> @hide()), @delay
+    @view.onmouseover  = =>
+      clearTimeout hideTimer
+    @view.onmouseout   = =>
+      hideTimer = setTimeout (=> @hide()), @delay

   positionOverlay: ->
     bounding = @parent.getBoundingClientRect()
-    @view.style.bottom   = bounding.height + 'px'
+    @view.style.bottom   = document.documentElement.clientHeight - bounding.top + 'px'
     @view.style.left     = bounding.left   + 'px'
-    @view.style.minWidth = bounding.width  + 'px'


Tweak error check in malformed json test

--- test/surveyor.coffee
+++ test/surveyor.coffee
@@ -58,7 +58,8 @@
       fs.unlinkSync './manifest/test_malformed.json'
       for error in err
         if error.file is "test_malformed.json"
-          done() if error.error.type is "unexpected_token"
+          assert.deepEqual error.error, new Error 'SyntaxError: Unexpected token l'
+          done()
   it 'should prune jobs no longer in the manifest', (done) ->
     rand1 = Math.floor(Math.random() * (1 << 24)).toString(16)
     model.slaves[rand1] =


Disable pandoc citation autocompletion by default

--- lib/latexer.coffee
+++ lib/latexer.coffee
@@ -33,7 +33,7 @@
     autocomplete_pandoc_markdown_citations:
       description: "For example: [see @doe99, pp. 33-35; also @smith04, chap. 1]"
       type: "boolean"
-      default: true
+      default: false


Fix root directory determination

Fix root directory determination

--- lib/manager.coffee
+++ lib/manager.coffee
@@ -10,10 +10,10 @@
     @disable_watcher = atom.config.get "atom-latex.disable_watcher"
     @watched = []
   rootDir: ->
-    # Collect all open TextEditors with  LaTeX grammar
+    # Collect all open TextEditors with LaTeX grammar
     texEditors = (editor for editor in atom.workspace.getTextEditors()\
                     when editor.getGrammar().scopeName.match(/text.tex.latex/))
-    if atom.workspace.getActiveTextEditor()? # An active editor is open
+    if atom.workspace.getActiveTextEditor() in texEditors # An active TeXeditor is open
       return atom.project.relativizePath(atom.workspace.getActiveTextEditor().getPath())[0]
     else if texEditors.length > 0   # First open editor with LaTeX grammar
       return atom.project.relativizePath(texEditors[0].getPath())[0]


Add rando gif salute to welcome-to-zithub

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -11,6 +11,7 @@
 # Author:
 #   Francho

+hubot = require 'hubot'

 module.exports = (robot) ->
   welcomeMsg = (nick) ->
@@ -36,6 +37,10 @@
   robot.brain.on 'loaded', =>
     robot.brain.data.nicks ||= []

+  saluteWithRandomGif = (res) ->
+    msg = new hubot.TextMessage(res.message.user, robot.name + ' gif me hello')
+    robot.receive msg
+
   robot.enter (res) ->
     user = res.message.user
     robot.logger.debug "User enter #{user.name}"
@@ -45,3 +50,4 @@
         return
       add_nicks user.name
       robot.messageRoom user.name, welcomeMsg(user.name)
+      saluteWithRandomGif res


Store tabText in atom.

Store tabText in atom.

This is a combination of tabsOrSpaces and tabSize. I imagine things like these will be stored in a settings object on `atom` at some point.

--- src/atom/app.coffee
+++ src/atom/app.coffee
@@ -6,14 +6,16 @@
 class App
   keymap: null
   windows: null
+  tabText: null

   constructor: (@loadPath, nativeMethods)->
     @windows = []
     @setupKeymap()
+    @tabText = "  "

   setupKeymap: ->
     @keymap = new GlobalKeymap()
-
+
     $(document).on 'keydown', (e) => @keymap.handleKeyEvent(e)

   open: (url) ->


Use new interface to instantiate jsdom

Use new interface to instantiate jsdom

--- test/planerHtml.test.coffee
+++ test/planerHtml.test.coffee
@@ -10,7 +10,7 @@
       FetchExternalResources: false,
       ProcessExternalResources: false
     }
-    @dom = jsdom.jsdom('')
+    @dom = new jsdom.JSDOM().window.document

   it 'should return an empty body when given an empty body', ->
     msgBody = ''


Change title universe feeding

Change title universe feeding

--- BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
+++ BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
@@ -2,7 +2,7 @@

 renderPageTitle = ->
   $('#title-logo').addClass($('nav li.active:first > a > i').attr('class').replace('fa-lg', ''))
-  $('#title-universe').text($('nav li.active:first > a > span').text())
+  $('#title-universe').text($('.breadcrumb li:nth-child(2)').text())
   $('#title-functionnality').text('> ' + $('.breadcrumb li:last').text())


Remove Q

Remove Q

--- client/apps/queue/routes.coffee
+++ client/apps/queue/routes.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore'
 query = require './query.coffee'
-Q = require 'bluebird-q'
 Lokka = require('lokka').Lokka
 Transport = require('lokka-transport-http').Transport
 { API_URL } = process.env


comment removed, linted

comment removed, linted

--- client/coffeescript/main.coffee
+++ client/coffeescript/main.coffee
@@ -39,17 +39,11 @@
     return "#{window.instance}#{url}"

 Router = new Router()
-###
-@addthis_config =
-{
-   // ... members go here
-}
-###
 Meteor.startup( ->
   # Get User ID
   Meteor.call('get_user_id', (err, res) ->
     Session.set('user_id', res)
-  )
+  )

   Backbone.history.start( pushState: true ) #!SUPPRESS no_headless_camel_case
 )


removed calls to meteor method for user id

--- client/coffeescript/main.coffee
+++ client/coffeescript/main.coffee
@@ -39,9 +39,7 @@
 Router = new Router()
 Meteor.startup( ->
   # Get User ID
-  Meteor.call('get_user_id', (err, res) ->
-    Session.set('user_id', res)
-  )
+  Session.set('user_id', @userId)

   Backbone.history.start( pushState: true ) #!SUPPRESS no_headless_camel_case
 )


IDE: Removed commented code.

IDE: Removed commented code.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -546,7 +546,6 @@

     else if component is 'searchResult'
     then """Search results for #{data.searchText}"""
-    # then """ #{data.stats.numberOfSearchedFiles} for "#{data.searchText}", #{data.stats.numberOfMatches} found """

     else if typeof data is 'string' then data


Collaboration: Handle user presence in chat view.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -982,14 +982,13 @@
         targetUser  = participant.nickname
         targetIndex = index

-
       unless targetUser
         return warn 'Unknown user in collaboration, we should handle this case...'

       if actionType is 'join'
-        log targetUser, 'joined'
+        @chat.emit 'ParticipantJoined', targetUser
       else
-        log targetUser, 'left'
+        @chat.emit 'ParticipantLeft', targetUser

         # check the user is still at same index, so we won't remove someone else.
         user = @participants.get targetIndex


Set json property for xhr lib

Set json property for xhr lib

--- client/lanes/models/Sync.coffee
+++ client/lanes/models/Sync.coffee
@@ -31,7 +31,7 @@
         options.url ||= _.result(model, "url")
         if _.contains(['create', 'update', 'patch'], method)
             isSave = true
-            options.data ||= model.dataForSave(options)
+            options.json ||= model.dataForSave(options)
         model.requestInProgress = options
         model.trigger("request", model, method, options)
         return new _.Promise (resolve, reject) ->
@@ -67,7 +67,7 @@
         _.defaults(options, {
             method: methodMap[method]
             dataType: "json"
-            data: query
+            json: query
         })

         # Ensure that we have a URL.
@@ -75,10 +75,12 @@
         options.url += '.json'
         unless _.isEmpty(query)
             options.url += '?' + Lanes.lib.objToParam(query)
-        if options.data and !_.isString(options.data)
-            options.originalData = options.data
-            options.data = JSON.stringify( options.data )
+
+        # if options.data and !_.isString(options.data)
+        #     options.originalData = options.data
+        #     options.json = options.data
         options.headers ||= {}
+        # options.json = true
         options.headers['X_CSRF_TOKEN'] = Lanes.config.csrf_token
         options.contentType = "application/json"

@@ -89,7 +91,7 @@
                 if err
                     reject(error:err, body:options.xhr.responseText)
                 else
-                    resolve(JSON.parse(body))
+                    resolve(body)
             )
         )


WIP: removing expression compilation

WIP: removing expression compilation

--- client/src/app/gamEvolve.coffee
+++ client/src/app/gamEvolve.coffee
@@ -51,13 +51,8 @@
       else
         @bindings[bindingName] = bindingValue

-  compileExpression: (expression) -> GE.compileExpression(expression, @constants.evaluator)
-
   # Pins are optional
-  evaluateFunction: (f, pins) -> f(@memory, @io, @constants.assets, @constants.transformers, @bindings, pins)
-
-  # Pins are optional
-  evaluateExpression: (expression, pins) -> @evaluateFunction(@compileExpression(expression), pins)
+  evaluateExpressionFunction: (f, pins) -> f(@memory, @io, @constants.assets, @constants.transformers, @bindings, pins)

 GE.extensions =
   IMAGE: ["png", "gif", "jpeg", "jpg"]
@@ -196,12 +191,12 @@
       direction: "in"
   return pinDefs

-# Returns an object mapping pin expression names to their values
+# Returns an object mapping pin expression names to their functions
 # Pin expressions is an object that contains 'in' and 'out' attributes
-GE.evaluateInputPinExpressions = (path, evaluationContext, pinDefs, pinExpressions) ->
-  evaluatedPins = {}
-
-  # TODO: don't evaluate output pinexpressions here!
+GE.compileInputPinExpressions = (path, evaluationContext, pinDefs, pinExpressions) ->
+  compiledPins = {}
+
+  # TODO: don't compile output pin expressions here!
   for pinName, pinOptions of pinDefs
     # Resolve pin expression value. If the board doesn't specify a value, use the default, it it exists. Otherwise, throw exception for input values
     if pinOptions.direction in ["in", "inout"] and pinExpressions.in?[pinName]
@@ -213,6 +208,19 @@
     else if pinOptions.direction in ["in", "inout"]
       throw GE.makeExecutionError("Missing input pin expression for pin '#{pinName}'", path)

+    try
+      compiledPins[pinName] = evaluationContext.compileExpression(pinValue)
+    catch error
+      throw GE.makeExecutionError("Error compiling the input pin expression expression '#{pinValue}' for pin '#{pinName}': #{error.stack}", path)
+  return compiledPins
+
+# Returns an object mapping pin expression names to their values
+# Pin expressions is an object that contains 'in' and 'out' attributes
+GE.evaluateInputPinExpressions = (path, pinFunctions) ->
+  evaluatedPins = {}
+
+  # TODO: don't evaluate output pin expressions here!
+  for pinName, pinFunction of pinFunctions
     try
       evaluatedPins[pinName] = evaluationContext.evaluateExpression(pinValue)
     catch error
@@ -382,13 +390,13 @@
   result = new GE.ChipVisitorResult(GE.signals.DONE)
   GE.transformersLogger = GE.makeLogFunction(path, result.logMessages)

-  for dest, src of chip.emitter
+  for dest, expressionFunction of chip.emitter
     evaluationContext = new GE.EvaluationContext(constants, bindings)

     try
-      outputValue = evaluationContext.evaluateExpression(src)
+      outputValue = evaluationContext.evaluateExpressionFunction(expressionFunction)
     catch error
-      throw GE.makeExecutionError("Error evaluating the output pin expression value expression '#{src}' for emitter chip: #{error.stack}", path)
+      throw GE.makeExecutionError("Error executing the output pin expression '#{expressionFunction}' --> '#{dest}' for emitter chip: #{error.stack}", path)

     [parent, key] = GE.getParentAndKey(evaluationContext, dest.split("."))
     parent[key] = outputValue


ensure the email from address is always correct

--- server/controllers/mails.coffee
+++ server/controllers/mails.coffee
@@ -113,7 +113,7 @@
                 if instance?[0]?.value.domain?
                     domain = instance[0].value.domain
                 else
-                    domain = ""
+                    domain = 'your.cozy.io'

                 # retrieves and slugifies the username if it exists
                 if users?[0]?.value.public_name?
@@ -121,6 +121,8 @@
                     displayName = displayName.toLowerCase()
                                              .replace ' ', '-'
                     displayName += "-"
+                else
+                    displayName = ''

                 mailOptions =
                     to: body.to


manage error not found in bulk_docs

manage error not found in bulk_docs

--- server/middlewares/utils.coffee
+++ server/middlewares/utils.coffee
@@ -72,7 +72,13 @@
                 # Document deletion:
                 #   Get doc and check docType of current document
                 db.get doc._id, (err, doc) ->
-                    checkPermissions req, doc.docType, cb
+                    if err? and err.error is 'not_found'
+                        cb()
+                    else if err
+                        console.log err
+                        cb err
+                    else
+                        checkPermissions req, doc.docType, cb
             else
                 # Manage in request
                 checkPermissions req, doc.docType, cb


Listen to createTeamTaskProgress event (for supervisors)

--- scripts/providers/contest.coffee
+++ scripts/providers/contest.coffee
@@ -56,12 +56,14 @@
             teamProvider.on 'qualifyTeam', @onQualifyTeam

             identity = identityProvider.getIdentity()
-            if identity.role is 'team'
+            if _.contains ['admin', 'manager', 'team'], identity.role
                 @onCreateTeamTaskProgress = (e) =>
                     options = JSON.parse e.data
                     teamTaskProgress = new TeamTaskProgressModel options
                     ndx = _.findIndex @teamTaskProgressEntries, teamId: options.teamId, taskId: options.taskId
-                    if teamTaskProgress.teamId == identity.id and ndx == -1
+                    if ndx == -1
+                        if identity.role == 'team' and identity.id != options.teamId
+                            return
                         @teamTaskProgressEntries.push teamTaskProgress
                         @trigger 'createTeamTaskProgress', [teamTaskProgress]


Add setManualPrice and removeManualPrice

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -370,6 +370,27 @@
       url: @_getAddToCartURL()
       data: JSON.stringify addToCartRequest

+  # Sends a request to change the price of an item, updating manualPrice on the orderForm
+  # Only possible if allowManualPrice is true
+  setManualPrice: (itemIndex, manualPrice) =>
+    setManualPriceRequest =
+      price: manualPrice
+
+    @_updateOrderForm
+      url: @_manualPriceURL(itemIndex)
+      type: 'PUT'
+      contentType: 'application/json; charset=utf-8'
+      dataType: 'json'
+      data: JSON.stringify setManualPriceRequest
+
+  # Sends a request to remove the manualPrice of an item, updating manualPrice on the orderForm
+  removeManualPrice: (itemIndex) =>
+    @_updateOrderForm
+      url: @_manualPriceURL(itemIndex)
+      type: 'DELETE'
+      contentType: 'application/json; charset=utf-8'
+      dataType: 'json'
+
   # URL BUILDERS

   _getOrderFormId: =>
@@ -427,6 +448,9 @@
   _getAddToCartURL: =>
     @_getOrderFormURL() + '/items'

+  _manualPriceURL: (itemIndex) =>
+    @_getOrderFormURL() + '/items/' + itemIndex + '/price'
+
   _getOrdersURL: (orderGroupId) =>
     HOST_URL + '/api/checkout/pub/orders/order-group/' + orderGroupId


servers: add endpoint

servers: add endpoint

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -67,6 +67,7 @@
 app.post '/-/teams/early-access'                 , require './handlers/earlyaccess'
 app.post '/-/teams/verify-domain'                , require './handlers/verifyslug'
 app.post '/-/teams/invite-by-csv'                , require('./handlers/invitetoteambycsv').handler
+app.post '/-/teams/invite-by-csv-analyze'        , require('./handlers/invitetoteambycsv2').handler
 app.get  '/-/teams/check-team-invitation'        , require './handlers/teaminvitationchecker'

 # fetches last members of team


init: rename logs to log

init: rename logs to log

--- lib/commands/init.coffee
+++ lib/commands/init.coffee
@@ -219,7 +219,7 @@
     rl.write "Vagrant file created\n"
   .write
     header: 'Log Directory'
-    destination: "#{params.path}/logs/.gitignore"
+    destination: "#{params.path}/log/.gitignore"
     content: """
     *
     !.gitignore
@@ -231,7 +231,7 @@
   .execute
     cmd: """
     git init
-    git add .gitignore package.json cache/.gitignore conf/config.coffee logs/.gitignore conf/VagrantFile
+    git add .gitignore package.json cache/.gitignore conf/config.coffee log/.gitignore conf/VagrantFile
     git commit -m 'Project initialization'
     """
     cwd: "#{params.path}"


Show correct zebra

Show correct zebra

--- src/gui/tabs/remote/html.coffee
+++ src/gui/tabs/remote/html.coffee
@@ -6,13 +6,32 @@
     for k of rec
       return
     true
+  single = (rec)->
+    for k of rec
+      return if result
+      result = k
+    result += ' ' if result
+  n = 0
   do it = (tree = @, prefix = '')->
     for k, v of tree
       k = Number k
       leaf = empty v.down
-      div prefix, k
+      myN = ++n
+      div
+        class: if myN & 1 then 'odd' else 'even'
+        -> input
+          type: 'checkbox'
+          disabled: leaf
+        ' '
+        single v.dists
+        prefix
+        k
+        ' '
+        -> i v.best.$[0].join '.'
       if leaf
         continue
       div
         class: 'indent hide'
         -> it v.down, "#{prefix}#{k}."
+      if n - myN & 1
+        n++


Fix setting ss value in new metric in key column

--- app/assets/volume/spreadsheet.coffee
+++ app/assets/volume/spreadsheet.coffee
@@ -780,7 +780,7 @@
           info.v = v
           rcm = Data[info.c][info.metric.id]
           if info.c == Key.id || info.c == 'asset'
-            rcm[info.n][info.i] = v
+            arr(rcm, info.n)[info.i] = v
             generateText(info)
           else
             for i, n of Depends[info.d]


adding test case for colliding fuzzy matches

--- test/diff_test.coffee
+++ test/diff_test.coffee
@@ -74,6 +74,9 @@
     it "should return [..., ['+', <added item>], ...] for two arrays when the second array has an extra value", ->
       assert.deepEqual [[' '], ['+', { foo: 20 }], [' ']], diff([{ foo: 10 }, { foo: 30 }], [{ foo: 10 }, { foo: 20 }, { foo: 30 }])

+    it "should return [..., ['+', <added item>], ...] for two arrays when the second array has a new but nearly identical object added", ->
+      assert.deepEqual [[' '],[ '+', { name: 'Foo', a: 3, b: 1, c: 1 }], [' ']], diff([{ "name": "Foo", "a": 3, "b": 1 },{ foo: 10 }], [{ "name": "Foo", "a": 3, "b": 1 },{ "name": "Foo", "a": 3, "b": 1, "c": 1 },{ foo: 10 }])
+
     it "should return [..., ['~', <diff>], ...] for two arrays when an item has been modified (note: involves a crazy heuristic)", ->
       assert.deepEqual [[' '], ['~', { foo: { __old: 20, __new: 21 } }], [' ']], diff([{ foo: 10, bar: { bbbar: 10, bbboz: 11 } }, { foo: 20, bar: { bbbar: 50, bbboz: 25 } }, { foo: 30, bar: { bbbar: 92, bbboz: 34 } }], [{ foo: 10, bar: { bbbar: 10, bbboz: 11 } }, { foo: 21, bar: { bbbar: 50, bbboz: 25 } }, { foo: 30, bar: { bbbar: 92, bbboz: 34 } }])


remove unused require

remove unused require

--- spec/javascripts/spec.js.coffee
+++ spec/javascripts/spec.js.coffee
@@ -1,5 +1,4 @@
 #= require_tree ./helpers
 #= require ../../app/assets/javascripts/application
 #= require ./factories
-#= require_tree ./fixtures
 #= require_tree ./backbone


predefined transport used the wrong url variable

--- source/pubsub_http/pubsub.coffee
+++ source/pubsub_http/pubsub.coffee
@@ -7,7 +7,7 @@
 create_ajax_transport = ( url = '/pubsub' )->
     ( obj, cb, message_url = url )->
         $.ajax(
-            url: url,
+            url: message_url,
             type: 'POST',
             contentType: 'application/json;charset=UTF-8',
             data: JSON.stringify obj )


Rename class

Rename class

--- lib/double-click-tree-view.coffee
+++ lib/double-click-tree-view.coffee
@@ -1,6 +1,6 @@
 'use strict'

-class CharyTreeView
+class DoubleClickTreeView
   activate: ->
     atom.packages.activatePackage('tree-view').then (treeViewPkg) =>
       @treeView = treeViewPkg.mainModule.createView()
@@ -21,4 +21,4 @@
   entryDoubleClicked: (e) ->
     @originalEntryClicked(e)

-module.exports = new CharyTreeView()
+module.exports = new DoubleClickTreeView()


:shirt: Fix linter errors

:shirt: Fix linter errors

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -34,7 +34,7 @@
     @previouslyFocusedElement = $(document.activeElement)
     @panel.show()
     @message.text("Enter #{mode} path")
-    if @mode == 'package'
+    if @mode is 'package'
       @setPathText("my-package")
     else
       @setPathText("my-theme-syntax", [0, 8])


Working on tests.

Working on tests.

--- test/bone.io.coffee
+++ test/bone.io.coffee
@@ -2,7 +2,7 @@
 express = require('express.io')
 rack = require('asset-rack')
 global.window = global
-global.$ = {}
+global.$ = ->
 require('../bone.io')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/bounded.coffee
+++ test/bounded.coffee
@@ -18,7 +18,8 @@
     y: 20

   obj.include(Bounded)
+  bounds = obj.bounds(5, 10)

-  equals obj.bounds(2, 5).x, 5
-  equals obj.bounds().y, 10
+  equals bounds.x, 0
+  equals bounds.y, 30