Minimal layout differences in d3/storage.coffee

--- app/assets/javascripts/d3/storage.coffee
+++ app/assets/javascripts/d3/storage.coffee
@@ -40,12 +40,14 @@
       #Width and height
       @maximum_x = 120
       @maximum_y = 400
+
+      # Set to 200 in order to 'move it out of canvas'.
       tracker_value = if @gquery.future_value() < @maximum_x then @gquery.future_value() else 200

-      wind_curve  = filter_on_y_values wind_curve, @maximum_y
-      p2p_curve = filter_on_y_values p2p_curve, @maximum_y
-      p2g_curve = filter_on_y_values p2g_curve, @maximum_y
-      p2h_curve = filter_on_y_values p2h_curve, @maximum_y
+      wind_curve = filter_on_y_values wind_curve, @maximum_y
+      p2p_curve  = filter_on_y_values p2p_curve, @maximum_y
+      p2g_curve  = filter_on_y_values p2g_curve, @maximum_y
+      p2h_curve  = filter_on_y_values p2h_curve, @maximum_y

       @tracker = [{"x": tracker_value, "y": 0}, {"x": tracker_value, "y": @maximum_y}]

@@ -58,13 +60,10 @@
               .domain([0, @maximum_y])
               .range([@series_height, 0])

-      #Create SVG element
-      svgContainer = @svg
-
       #Create X axis
       xAxis = d3.svg.axis()
-                      .scale(xScale)
-                      .orient("bottom")
+                    .scale(xScale)
+                    .orient("bottom")

       #Define Y axis
       yAxis = d3.svg.axis()
@@ -72,26 +71,25 @@
                     .orient("left")
                     .tickValues([])
                     .tickSize(0)
-
+
       #Create xAxis
       xAxisGroup = @svg.append("g")
-                                 .attr("class", "x_axis")
-                                 .attr("transform", "translate(0,#{@series_height})")
-                                 .call(xAxis)
-
+                       .attr("class", "x_axis")
+                       .attr("transform", "translate(0,#{@series_height})")
+                       .call(xAxis)
+
       #Create Y axis
       yAxisGroup = @svg.append("g")
-                                 .attr("class", "y_axis")
-                                 .attr("transform", "translate(0,0)")
-                                 .call(yAxis)
-
+                       .attr("class", "y_axis")
+                       .call(yAxis)
+
       xAxisLabel = xAxisGroup.append("text")
-                               .attr("class","label")
-                               .attr("x", @width/2)
-                               .attr("y", 35)
-                               .text("#{I18n.t('output_elements.storage_chart.installed_capacity')}")
-                               .attr("text-anchor","middle")
-
+                             .attr("class","label")
+                             .attr("x", @width/2)
+                             .attr("y", 35)
+                             .text("#{I18n.t('output_elements.storage_chart.installed_capacity')}")
+                             .attr("text-anchor","middle")
+
       yAxisLabel = yAxisGroup.append("text")
                                .attr("class","label")
                                .attr("x", 0 - (@series_height/2))
@@ -99,105 +97,105 @@
                                .text("#{I18n.t('output_elements.storage_chart.total_cost')}")
                                .attr("text-anchor","middle")
                                .attr("transform", (d) -> "rotate(-90)" )
-
+
       #This is the accessor function we talked about above
       lineFunction = d3.svg.line()
                              .x( (d) ->  xScale(d.x) )
                              .y( (d) ->  yScale(d.y) )
                              .interpolate("basis")
-
+
       #The line SVG Path we draw
       @svg.append("path")
-               .attr("d", lineFunction(wind_curve))
-               .attr("stroke", "#00BFFF")
-               .attr("stroke-width", 3)
-               .attr("fill", "none")
-
-      @svg.append("path")
-               .attr("d", lineFunction(p2p_curve))
-               .attr("stroke", "#F08080")
-               .attr("stroke-width", 3)
-               .attr("fill", "none")
-
-      @svg.append("path")
-               .attr("d", lineFunction(p2g_curve))
-               .attr("stroke", "green")
-               .attr("stroke-width", 3)
-               .attr("fill", "none")
-
-      @svg.append("path")
-               .attr("d", lineFunction(p2h_curve))
-               .attr("stroke", "#8B008B")
-               .attr("stroke-width", 3)
-               .attr("fill", "none")
-
-      @svg.append("path")
-               .attr("id", "tracker")
-               .style("stroke-dasharray", ("3, 3"))
-               .attr("d", lineFunction(@tracker))
-               .attr("stroke", "red")
-               .attr("stroke-width", 2)
-               .attr("fill", "none")
-
+          .attr("d", lineFunction(wind_curve))
+          .attr("stroke", "#00BFFF")
+          .attr("stroke-width", 3)
+          .attr("fill", "none")
+
+      @svg.append("path")
+          .attr("d", lineFunction(p2p_curve))
+          .attr("stroke", "#F08080")
+          .attr("stroke-width", 3)
+          .attr("fill", "none")
+
+      @svg.append("path")
+          .attr("d", lineFunction(p2g_curve))
+          .attr("stroke", "green")
+          .attr("stroke-width", 3)
+          .attr("fill", "none")
+
+      @svg.append("path")
+          .attr("d", lineFunction(p2h_curve))
+          .attr("stroke", "#8B008B")
+          .attr("stroke-width", 3)
+          .attr("fill", "none")
+
+      @svg.append("path")
+          .attr("id", "tracker")
+          .style("stroke-dasharray", ("3, 3"))
+          .attr("d", lineFunction(@tracker))
+          .attr("stroke", "red")
+          .attr("stroke-width", 2)
+          .attr("fill", "none")
+
       @svg.append("g").append("text")
-               .attr("class", "indicative_label")
-               .attr("x", 320)
-               .attr('y', 0-300)
-               .text("#{I18n.t('output_elements.storage_chart.indicative')}")
-               .attr("transform", (d) -> "rotate(45)" )
-
+          .attr("class", "indicative_label")
+          .attr("x", 320)
+          .attr('y', 0-300)
+          .text("#{I18n.t('output_elements.storage_chart.indicative')}")
+          .attr("transform", (d) -> "rotate(45)" )
+
       legendGroup = @svg.append("g")
-                         .attr("class", "legend")
-                         .attr("transform", "translate(0,#{@series_height + legend_margin})")
-
+                        .attr("class", "legend")
+                        .attr("transform", "translate(0,#{@series_height + legend_margin})")
+
       legendItem = legendGroup.append("svg")
-
-      legendItem.append("rect")
-               .attr("x", 0)
-               .attr("y", 0)
-               .attr("width", "10")
-               .attr("height", "10")
-               .attr("fill", "#00BFFF")
-
-      legendItem.append("text")
-               .attr("class", "legend_label")
-               .attr("x", 15)
-               .attr("y", 10)
-               .text("#{I18n.t('output_elements.storage_chart.inland_wind')}")
-
-      legendItem.append("rect")
-               .attr("x", @width/2)
-               .attr("y", 0)
-               .attr("width", "10")
-               .attr("height", "10")
-               .attr("fill", "#F08080")
-
-      legendItem.append("text")
-               .attr("class", "legend_label")
-               .attr("x", @width/2 + 15)
-               .attr("y", 10)
-               .text("#{I18n.t('output_elements.storage_chart.electricity_storage')}")
-
+
+      legendItem.append("rect")
+                .attr("x", 0)
+                .attr("y", 0)
+                .attr("width", "10")
+                .attr("height", "10")
+                .attr("fill", "#00BFFF")
+
+      legendItem.append("text")
+                .attr("class", "legend_label")
+                .attr("x", 15)
+                .attr("y", 10)
+                .text("#{I18n.t('output_elements.storage_chart.inland_wind')}")
+
+      legendItem.append("rect")
+                .attr("x", @width/2)
+                .attr("y", 0)
+                .attr("width", "10")
+                .attr("height", "10")
+                .attr("fill", "#F08080")
+
+      legendItem.append("text")
+                .attr("class", "legend_label")
+                .attr("x", @width/2 + 15)
+                .attr("y", 10)
+                .text("#{I18n.t('output_elements.storage_chart.electricity_storage')}")
+
       legendItem.append("rect")
                .attr("x", 0)
                .attr("y", 15)
                .attr("width", "10")
                .attr("height", "10")
                .attr("fill", "green")
-
+
       legendItem.append("text")
                .attr("class", "legend_label")
                .attr("x", 15)
                .attr("y", 25)
                .text("#{I18n.t('output_elements.storage_chart.conversion_to_gas')}")
-
+
       legendItem.append("rect")
                .attr("x", @width/2)
                .attr("y", 15)
                .attr("width", "10")
                .attr("height", "10")
                .attr("fill", "#8B008B")
-
+
       legendItem.append("text")
                .attr("class", "legend_label")
                .attr("x", @width/2 + 15)
@@ -206,22 +204,22 @@

     refresh: =>
       tracker_value = if @gquery.future_value() < @maximum_x then @gquery.future_value() else 200
-
+
       tracker = [{"x": tracker_value, "y": 0}, {"x": tracker_value, "y": @maximum_y}]

       #Create scale functions
       xScale = d3.scale.linear()
-              .domain([0, @maximum_x])
-              .range([0, @width])
+                       .domain([0, @maximum_x])
+                       .range([0, @width])

       yScale = d3.scale.linear()
-              .domain([0, @maximum_y])
-              .range([@series_height, 0])
+                       .domain([0, @maximum_y])
+                       .range([@series_height, 0])

       lineFunction = d3.svg.line()
-                             .x( (d) ->  xScale(d.x) )
-                             .y( (d) ->  yScale(d.y) )
-                             .interpolate("basis")
+                           .x( (d) ->  xScale(d.x) )
+                           .y( (d) ->  yScale(d.y) )
+                           .interpolate("basis")

       d3.select("path#tracker")
-          .attr("d", lineFunction(tracker))+        .attr("d", lineFunction(tracker))


Updated documentation

Updated documentation

--- src/helpers/constants.coffee
+++ src/helpers/constants.coffee
@@ -109,16 +109,13 @@
                           If something is wrong with your punch, you can undo it by
                           doing `@ibizan undo`. You can also modify it manually using
                           the Ibizan spreadsheet, in the worksheet labeled 'Raw Data'.
-                          If you want to see how much time you've worked today, do
-                          `@ibizan today?`.\n
+                          If you want to see how much time you've worked, use
+                          `@ibizan today?` or `@ibizan week?`.\n
                           \n
                           If you make any manual changes to the spreadsheet, you should
-                          run `/sync` or `@ibizan sync`. Running this resyncs Ibizan with
-                          the spreadsheet.\n
-                          \n
-                          Due to some existing limitations in Google Sheets, changes
-                          made directly to the spreadsheet are not immediately
-                          reflected by Ibizan and must be followed up with a `/sync`.\n
+                          use `@ibizan sync`. Running this resyncs Ibizan with
+                          the spreadsheet. Note that Ibizan syncs automatically after
+                          editing or deleting rows.\n
                           \n
                           For more documentation, please check out
                           https://github.com/ibizan/ibizan.github.io/wiki"


show branch on org dashboard page

show branch on org dashboard page

--- js/inner/pages/org_dashboard.coffee
+++ js/inner/pages/org_dashboard.coffee
@@ -6,3 +6,4 @@
                new CI.inner.OrgSettingsCrumb(@username)]

     @title = @username
+    @show_branch = true


luca/basic no longer includes any containers outside of the base

--- app/assets/javascripts/luca/basic.coffee
+++ app/assets/javascripts/luca/basic.coffee
@@ -4,5 +4,5 @@
 #= require ./util
 #= require ./managers
 #= require ./core
-#= require ./containers
+#= require ./containers/container
 #= require ./framework


history.back after entering time

history.back after entering time

--- app/assets/javascripts/time_entry.coffee
+++ app/assets/javascripts/time_entry.coffee
@@ -22,6 +22,10 @@
     )
     .done( ->

+      setTimeout(
+        -> window.history.back()
+        1
+      )
       $alert.addClass("alert-success")
       $alert.text("Yeah! Your time got logged.")
       $alert.show()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -9,7 +9,7 @@
       width: 20
       height: 20

-    s
+    player.include ActiveBounds

 @see GameObject


Add link to settings in disk space warning

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -269,23 +269,16 @@
             return null
         quotas = @props.get_total_project_quotas(@props.project_id)
         project_status = @props.project_map?.get(@props.project_id)?.get('status')
-        console.log "Quotas?", quotas, "status?", project_status?.toJS()
         if not quotas?.disk_quota? or not project_status?
             return null
         else
-            rss = project_status.get('memory')?.get('rss')
-            if rss?
-                memory = Math.round(rss/1000)
             disk = project_status.get('disk_MB')
             if disk?
                 disk = Math.ceil(disk)
-        if disk
-            console.log "Disk is:", disk
+        #if not disk or quotas.disk_quota - 5 > disk
+        #    return null

         styles =
-            padding      : 3
-            paddingLeft  : 7
-            paddingRight : 7
             marginBottom : 0
             fontSize     : '13pt'
         dismiss_styles =
@@ -299,7 +292,7 @@
             position   : 'relative'
             height     : 0
         <Alert bsStyle='danger' style={styles}>
-            <Icon name='exclamation-triangle' /> WARNING: This project is running out of disk space. Please increase the quota or delete some files.
+            <Icon name='exclamation-triangle' /> WARNING: This project is running out of disk space. Please increase the quota in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings or delete some files.
         </Alert>
 # is_public below -- only show this tab if this is true


Check that prices in staged are also updated.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -108,6 +108,7 @@
     .then (result) =>
       console.log 5
       @masterProductId = result.id
+      @masterProductVersion = result.version
       data =
         actions: [
           { action: 'publish' }
@@ -162,6 +163,14 @@

       @client.products.byId(result.id).save(data)
     .then (result) =>
+      console.log 7.5
+      data =
+        actions: [
+          { action: 'setAttribute', variantId: 1, name: 'mastersku', value: 'alksvbkl;jsdvn' }
+        ]
+        version: @masterProductVersion
+      @client.products.byId(@masterProductId).save(data)
+    .then (result) =>
       console.log 8
       @priceSync.run()
     .then (msg) =>
@@ -180,6 +189,13 @@
       expect(price.channel.id).toBeDefined()
       expect(price.customerGroup).toBeUndefined()

+      price = result.masterData.staged.masterVariant.prices[0]
+      expect(price.value.currencyCode).toBe 'EUR'
+      expect(price.value.centAmount).toBe 9999
+      expect(price.channel.typeId).toBe 'channel'
+      expect(price.channel.id).toBeDefined()
+      expect(price.customerGroup).toBeUndefined()
+
       price = result.masterData.current.masterVariant.prices[1]
       expect(price.value.currencyCode).toBe 'EUR'
       expect(price.value.centAmount).toBe 8999
@@ -188,6 +204,14 @@
       expect(price.customerGroup.typeId).toBe 'customer-group'
       expect(price.customerGroup.id).toBeDefined()

+      price = result.masterData.staged.masterVariant.prices[1]
+      expect(price.value.currencyCode).toBe 'EUR'
+      expect(price.value.centAmount).toBe 8999
+      expect(price.channel.typeId).toBe 'channel'
+      expect(price.channel.id).toBeDefined()
+      expect(price.customerGroup.typeId).toBe 'customer-group'
+      expect(price.customerGroup.id).toBeDefined()
+
       price = result.masterData.current.variants[0].prices[0]
       expect(price.value.currencyCode).toBe 'EUR'
       expect(price.value.centAmount).toBe 20000
@@ -195,6 +219,13 @@
       expect(price.channel.id).toBeDefined()
       expect(price.customerGroup).toBeUndefined()

+      price = result.masterData.staged.variants[0].prices[0]
+      expect(price.value.currencyCode).toBe 'EUR'
+      expect(price.value.centAmount).toBe 20000
+      expect(price.channel.typeId).toBe 'channel'
+      expect(price.channel.id).toBeDefined()
+      expect(price.customerGroup).toBeUndefined()
+
       price = result.masterData.current.variants[0].prices[1]
       expect(price.value.currencyCode).toBe 'EUR'
       expect(price.value.centAmount).toBe 15000
@@ -203,12 +234,28 @@
       expect(price.customerGroup.typeId).toBe 'customer-group'
       expect(price.customerGroup.id).toBeDefined()

+      price = result.masterData.staged.variants[0].prices[1]
+      expect(price.value.currencyCode).toBe 'EUR'
+      expect(price.value.centAmount).toBe 15000
+      expect(price.channel.typeId).toBe 'channel'
+      expect(price.channel.id).toBeDefined()
+      expect(price.customerGroup.typeId).toBe 'customer-group'
+      expect(price.customerGroup.id).toBeDefined()
+
       price = result.masterData.current.variants[1].prices[0]
       expect(price.value.currencyCode).toBe 'EUR'
       expect(price.value.centAmount).toBe 99
       expect(price.channel.typeId).toBe 'channel'
       expect(price.channel.id).toBeDefined()
       expect(price.customerGroup).toBeUndefined()
+
+      price = result.masterData.staged.variants[1].prices[0]
+      expect(price.value.currencyCode).toBe 'EUR'
+      expect(price.value.centAmount).toBe 99
+      expect(price.channel.typeId).toBe 'channel'
+      expect(price.channel.id).toBeDefined()
+      expect(price.customerGroup).toBeUndefined()
+
       done()

     .fail (error) ->


Use done with argument instead of logging error.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -49,8 +49,7 @@
       Q.all(dels).then (v) ->
         done()
       .fail (err) ->
-        console.log err
-        done()
+        done(err)

   it 'do nothing', (done) ->
     @priceSync.run (msg) ->


Remove nesting. Use fat arrow for fail method to be able to close sftp client properly.

--- src/spec/sftphelpers.spec.coffee
+++ src/spec/sftphelpers.spec.coffee
@@ -51,12 +51,14 @@
     it 'should download file from remote server', (done) ->

       @sftp
-      @helpers.openSftp().then (sftp) =>
+      @helpers.openSftp()
+      .then (sftp) =>
         @sftp = sftp
-        @helpers.getFile(sftp, "#{REMOTE_ROOT}/#{TEST_FILE}", "./#{TEST_FILE}").then =>
-          expect(fs.existsSync(TEST_FILE)).toBe true
-          @helpers.close @sftp
-          done()
-      .fail (result) ->
+        @helpers.getFile(sftp, "#{REMOTE_ROOT}/#{TEST_FILE}", "./#{TEST_FILE}")
+      .then =>
+        expect(fs.existsSync(TEST_FILE)).toBe true
+        @helpers.close @sftp
+        done()
+      .fail (result) =>
         @helpers.close @sftp
         done(result)


Rename `updateCachedDiskContents` and `updateCachedDiskContentsAsync`

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -67,12 +67,12 @@
     @setPath(@project.resolve(filePath)) if @project

   loadSync: ->
-    @updateCachedDiskContents()
+    @updateCachedDiskContentsSync()
     @reload() if @loadFromDisk
     @text.clearUndoStack()

   load: ->
-    @updateCachedDiskContentsAsync().then =>
+    @updateCachedDiskContents().then =>
       @reload() if @loadFromDisk
       @text.clearUndoStack()
       this
@@ -117,14 +117,14 @@
   subscribeToFile: ->
     @file.on "contents-changed", =>
       @conflict = true if @isModified()
-      @updateCachedDiskContentsAsync().done =>
+      @updateCachedDiskContents().done =>
         if @conflict
           @emit "contents-conflicted"
         else
           @reload()

     @file.on "removed", =>
-      @updateCachedDiskContentsAsync().done =>
+      @updateCachedDiskContents().done =>
         @emitModifiedStatusChanged(@isModified())

     @file.on "moved", =>
@@ -150,12 +150,12 @@
     @emit 'reloaded'

   # Private: Rereads the contents of the file, and stores them in the cache.
-  updateCachedDiskContents: ->
+  updateCachedDiskContentsSync: ->
     @loaded = true
     @cachedDiskContents = @file?.read() ? ""

   # Private: Rereads the contents of the file, and stores them in the cache.
-  updateCachedDiskContentsAsync: ->
+  updateCachedDiskContents: ->
     Q(@file?.readAsync() ? "").then (contents) =>
       @loaded = true
       @cachedDiskContents = contents


:fire: ignoreInvisibles parameter to TextEditor

Now that the TextEditor doesn't depend on atom.config, showInvisibles
can always be configured independently of the config

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -127,7 +127,7 @@
       @softWrapped, @decorationManager, @selectionsMarkerLayer, @buffer, suppressCursorCreation,
       @mini, @placeholderText, lineNumberGutterVisible, @largeFileMode, @clipboard,
       @assert, grammar, @showInvisibles, @autoHeight, @scrollPastEnd, @editorWidthInChars,
-      @tokenizedBuffer, @ignoreInvisibles, @displayLayer
+      @tokenizedBuffer, @displayLayer
     } = params

     throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard?
@@ -190,7 +190,7 @@
   update: (params) ->
     {
       softTabs, tabLength, softWrapped, mini, placeholderText, lineNumberGutterVisible,
-      showInvisibles, ignoreInvisibles, editorWidthInChars, scrollPastEnd, autoHeight
+      showInvisibles, editorWidthInChars, scrollPastEnd, autoHeight
     } = params

     resetDisplayLayer = false
@@ -217,10 +217,6 @@

     if showInvisibles? and showInvisibles isnt @showInvisibles
       @showInvisibles = showInvisibles
-      resetDisplayLayer = true
-
-    if ignoreInvisibles? and ignoreInvisibles isnt @ignoreInvisibles
-      @setIgnoreInvisibles(ignoreInvisibles, false)
       resetDisplayLayer = true

     if editorWidthInChars? and editorWidthInChars isnt @editorWidthInChars
@@ -621,7 +617,6 @@
   setMini: (mini) ->
     if mini isnt @mini
       @mini = mini
-      @ignoreInvisibles = @mini
       @resetDisplayLayer()
       @emitter.emit 'did-change-mini', @mini
     @mini
@@ -2793,12 +2788,6 @@
     @showInvisibles = showInvisibles
     @resetDisplayLayer()

-  setIgnoreInvisibles: (ignoreInvisibles, resetDisplayLayer=true) ->
-    return if ignoreInvisibles is @ignoreInvisibles
-
-    @ignoreInvisibles = ignoreInvisibles
-    @resetDisplayLayer() if resetDisplayLayer
-
   # Returns an {Object} representing the current invisible character
   # substitutions for this editor. See {::setInvisibles}.
   getInvisibles: ->


Remove debugger breakpoint.

Remove debugger breakpoint.

--- lib/actions/add_slash_to_directory.coffee
+++ lib/actions/add_slash_to_directory.coffee
@@ -2,7 +2,6 @@
 endswith= /\/$/
 module.exports= addSlashToDirectory= (req,res,next) ->
   try
-    debugger
     url = req._.url
     unless endswith.test(url.decoded_path)
       res.statusCode = 301;


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -12,7 +12,21 @@
     yPosition: App.height / 2

   self = GameState(I).extend
+    ###*
+    Set your velocity to follow another object.

+

+    enemy.follow(player)
+
+    # => The enemy now has it's velocity attribute set in
+    # the direction of the player, with magnitude equal to
+    # the enemy's speed
+

+
+    @name follow
+    @methodOf Follow#
+    @param {GameObject} obj The object you want to follow
+    ###
     centerText: (canvas, text, options={}) ->
       font = options.font || I.font
       size = options.size || I.fontSize


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -7,7 +7,7 @@
   self = GameState(I).extend
     centerText: (canvas, options={}) ->
       font =
-      canvas.font "#{}"
+      canvas.font "#{siz}"

       canvas.centerText
         y: App.height / 2 - 20


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -7,7 +7,7 @@
   self = GameState(I).extend
     centerText: (canvas, options={}) ->
       font =
-      canvas.font "#{siz}"
+      canvas.font "#{size}px "

       canvas.centerText
         y: App.height / 2 - 20


up-close attribute closes a containing popup

--- lib/assets/javascripts/up/popup.js.coffee
+++ lib/assets/javascripts/up/popup.js.coffee
@@ -131,8 +131,13 @@
   )

   # Close the pop-up overlay when the user presses ESC.
-  $(document).on('keydown', (event) ->
+  up.on('keydown', 'body', (event) ->
     if event.keyCode == 27
+      close()
+  )
+
+  up.on('click', '[up-close]', (event, $element) ->
+    if $element.closest('.up-popup')
       close()
   )


feat: completed record a game

feat: completed record a game

--- app/controllers/record_controller.coffee
+++ app/controllers/record_controller.coffee
@@ -8,11 +8,11 @@
   gameTwoAway: null
   gameThreeHome: null
   gameThreeAway: null
-  pendingHome: []
-  pendingAway: []
+  pendingRounds: []
   pendingHomeScore: 0
   pendingAwayScore: 0
-  pendingPerson: null
+  pendingHomePerson: null
+  pendingAwayPerson: null
   error: null
   authPerson: Ember.computed.alias('controllers.auth.person')
   game: Ember.computed.alias('controllers.game')
@@ -39,6 +39,9 @@
       @set('mustConfirm', false)
       homePerson = @get('authPerson')
       awayPerson = @get('awayPersonSelect')
+      @set('pendingHomePerson', homePerson)
+      @set('pendingAwayPerson', awayPerson)
+      debugger
       awayName = awayPerson.get('name')
       h = []
       a = []
@@ -51,15 +54,15 @@
       homeScore = 0
       awayScore = 0
       thirdPlayed = false
-
+      rounds = []
       if homePerson != null && awayPerson != null
         if homePerson.get('id') != awayPerson.get('id')
           # game = @get('game').createGame(homePerson, awayPerson)
           if h[0] == a[0]
-            @set('error', 'Game 1: Scores cannot be equal')
+            @set('error', 'Game 1: Score cannot be equal')
             return
           if h[1] == a[1]
-            @set('error', 'Game 2: Scores cannot be equal')
+            @set('error', 'Game 2: Score cannot be equal')
             return
           if (h[0]+1) == a[0] or h[0] == (a[0]+1)
             @set('error', 'Game 1: Must win by 2')
@@ -70,7 +73,7 @@
           if h[2] != 0 or a[2] != 0
             thirdPlayed = true
             if h[2] == a[2]
-              @set('error', 'Game 3: Scores cannot be equal')
+              @set('error', 'Game 3: Score cannot be equal')
               return
             if (h[2]+1) == a[2] or h[2] == (a[2]+1)
               @set('error', 'Game 3: Must win by 2')
@@ -80,15 +83,33 @@
             homeScore++
           else
             awayScore++
+          rounds.push(
+            index: 2
+            homeScore: h[0]
+            awayScore: a[0]
+            isComplete: true
+          )
           if h[1] > a[1]
             homeScore++
           else
             awayScore++
+          rounds.push(
+            index: 2
+            homeScore: h[1]
+            awayScore: a[1]
+            isComplete: true
+          )
           if thirdPlayed
             if h[2] > a[2]
               homeScore++
             else
               awayScore++
+            rounds.push(
+              index: 3
+              homeScore: h[2]
+              awayScore: a[2]
+              isComplete: true
+            )
           if homeScore == awayScore
             @set('error', 'Cannot tie the match, enter 3rd game.')
             return
@@ -98,13 +119,10 @@
             @set('confirm', "You beat " + awayName + " " + homeScore + " to " + awayScore)
           else
             @set('confirm', awayName + " beat you " + homeScore + " to " + awayScore)
-          @set('pendingHome', h)
-          @set('pendingAway', a)
+          @set('pendingRounds', rounds)
           @set('pendingHomeScore', homeScore)
           @set('pendingAwayScore', awayScore)
-          @set('pendingPerson', awayPerson)
           setTimeout( ->
-            console.log "focus!"
             $("#confirmGame").focus()
           , 200)
           return
@@ -118,7 +136,25 @@
       @set('confirm', null)
       @set('mustConfirm', false)
     confirmGame: ->
-
+      rounds = @get('pendingRounds')
+      homeScore = @get('pendingHomeScore')
+      awayScore = @get('pendingAwayScore')
+      homePerson = @get('pendingHomePerson')
+      console.log homePerson
+      debugger
+      awayPerson = @get('pendingAwayPerson')
+      completedGame = @get('store').createRecord('completedGame',
+      home: homePerson
+      away: awayPerson
+      createdAt: new Date()
+      startedAt: new Date()
+      completedAt: new Date()
+      homeScore: homeScore
+      awayScore: awayScore
+      rounds: rounds
+      )
+      completedGame.save()
+      @transitionToRoute("completedGames")
     cancelConfirm: ->
       @set('confirm', null)
       @set('mustConfirm', false)


fix app coffee

fix app coffee

--- js/app.coffee
+++ js/app.coffee
@@ -9,16 +9,15 @@
       @apiKey = key
     return

-  @ = (, ) ->
+  @$get = ($q, $http) ->
     self = this
     {
       getWeatherForecast: (city) ->
-        d = .defer()
-        (
+        d = $q.defer()
+        $http(
           method: 'GET'
           url: self.getUrl('forecast', city)
           cache: true).success((data) ->
-          console.log data
           d.resolve data.forecast.simpleforecast
           return
         ).error (err) ->
@@ -26,8 +25,8 @@
           return
         d.promise
       getCityDetails: (query) ->
-        d = .defer()
-        (
+        d = $q.defer()
+        $http(
           method: 'GET'
           url: 'http://autocomplete.wunderground.com/aq?query=' + query).success((data) ->
           d.resolve data.RESULTS
@@ -56,15 +55,15 @@
   WeatherProvider.setApiKey '$API_KEY'
   return
 ).config([
-  ''
-  () ->
-    .when('/',
+  '$routeProvider'
+  ($routeProvider) ->
+    $routeProvider.when('/',
       templateUrl: 'templates/home.html'
       controller: 'MainCtrl').when('/settings',
       templateUrl: 'templates/settings.html'
       controller: 'SettingsCtrl').otherwise redirectTo: '/'
     return
-]).directive('autoFill', (, Weather) ->
+]).directive('autoFill', ($timeout, Weather) ->
   {
     restrict: 'EA'
     scope:
@@ -85,13 +84,13 @@
           val = ele.val()
           if val.length < minKeyCount
             if timer
-              .cancel timer
+              $timeout.cancel timer
             scope.reslist = null
             return
           else
             if timer
-              .cancel timer
-            timer = ((->
+              $timeout.cancel timer
+            timer = $timeout((->
               scope.autoFill()(val).then (data) ->
                 if data and data.length > 0
                   scope.reslist = data
@@ -104,37 +103,36 @@
         # Hide the reslist on blur
         input.bind 'blur', (e) ->
           scope.reslist = null
-          scope.()
+          scope.$digest()
           return
         return

   }
-).controller('MainCtrl', (, , Weather, UserService) ->
-  .date = {}
+).controller('MainCtrl', ($scope, $timeout, Weather, UserService) ->
+  $scope.date = {}

   updateTime = ->
-    .date.tz = new Date((new Date).toLocaleString('en-US', timeZone: .user.timezone))
-     updateTime, 1000
+    $scope.date.tz = new Date((new Date).toLocaleString('en-US', timeZone: $scope.user.timezone))
+    $timeout updateTime, 1000
     return

-  .weather = {}
-  .user = UserService.user
-  Weather.getWeatherForecast(.user.location).then (data) ->
-    .weather.forecast = data
+  $scope.weather = {}
+  $scope.user = UserService.user
+  Weather.getWeatherForecast($scope.user.location).then (data) ->
+    $scope.weather.forecast = data
     return
   updateTime()
   return
-).controller 'SettingsCtrl', (, , Weather, UserService) ->
-  .user = UserService.user
+).controller 'SettingsCtrl', ($scope, $location, Weather, UserService) ->
+  $scope.user = UserService.user

-  .save = ->
+  $scope.save = ->
     UserService.save()
-    .path '/'
+    $location.path '/'
     return

-  .fetchCities = Weather.getCityDetails
+  $scope.fetchCities = Weather.getCityDetails
   return

 # ---
 # generated by js2coffee 2.2.0
-


Revert "Remove setInterval spy. It's now spied in the spec helper w/ setTimeout"

This reverts commit 930f1d7f018bb9949b0ee0e4ca7330a8a4ce0ec7.

I actually don't want to globally spy on setInterval because it interferes with
expected behaviors and eventually I'd like both of these to be opt-in.

--- spec/editor-view-spec.coffee
+++ spec/editor-view-spec.coffee
@@ -624,6 +624,8 @@
         editorView.attachToDom(heightInLines: 5)
         editorView.scrollToBottom()

+        spyOn(window, 'setInterval').andCallFake ->
+
         # start
         editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [12, 0])
         originalScrollTop = editorView.scrollTop()
@@ -671,6 +673,8 @@
           editorView.vScrollMargin = 0
           editorView.attachToDom(heightInLines: 5)
           editorView.scrollToBottom()
+
+          spyOn(window, 'setInterval').andCallFake ->

           editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [12, 0])
           originalScrollTop = editorView.scrollTop()


:white_check_mark: Add test to verify implementation

--- spec/editor-view-spec.coffee
+++ spec/editor-view-spec.coffee
@@ -3045,3 +3045,12 @@
       editorView.pixelPositionForScreenPosition([0, editor.getTabLength()])
       editorView.pixelPositionForScreenPosition([0, editor.getTabLength() + 1])
       expect(editorView.measureToColumn.callCount).toBe 0
+
+  describe "grammar data attributes", ->
+    it "adds and updates the grammar data attribute based on the current grammar", ->
+      editorView.attachToDom()
+      editor.setGrammar(atom.syntax.grammarForScopeName('text.plain'))
+      expect(editorView.attr('data-grammar')).toEqual 'text.plain'
+
+      editor.setGrammar(atom.syntax.grammarForScopeName('source.javascript'))
+      expect(editorView.attr('data-grammar')).toEqual 'source.javascript'


fix selected path

fix selected path

--- lib/controllers/config-controller.coffee
+++ lib/controllers/config-controller.coffee
@@ -173,9 +173,8 @@
   runProcessFromTreeView: ->
     selected = document.querySelectorAll(".tree-view .selected > .list-item > .name, .tree-view .selected > .name")
     return unless selected.length > 0
-
-    selectedPaths = el.dataset.path for el in selected
-    @runProcessWithFile(selectedPaths[0]);
+
+    @runProcessWithFile(selected[0].dataset.path);

   runProcessWithFile: (filePath) ->
     if @config.singular


Replace magic ID strings with helper functions.

--- src/bootstrap-elm.coffee
+++ src/bootstrap-elm.coffee
@@ -34,12 +34,12 @@
 loadAsCurrentDoc = (doc) ->
   app.ports.loadAsCurrentDoc.send doc

-  setUpEditor "edit-title", doc.title, false, (mutations, node) ->
+  setUpEditor getTitleElem, doc.title, false, (mutations, node) ->
     sync.getDoc(doc.id).done (doc) ->
       doc.title = node.textContent
       sync.saveDoc(doc).done -> app.ports.setTitle.send doc.title

-  setUpEditor "edit-description", doc.description, false, (mutations, node) ->
+  setUpEditor getDescriptionElem, doc.description, false, (mutations, node) ->
     sync.getDoc(doc.id).done (doc) ->
       doc.description = node.textContent
       sync.saveDoc(doc).done -> app.ports.setDescription.send doc.description
@@ -48,8 +48,6 @@

 setUpChapter = (chapter) ->
   chapterId = chapter.id
-  headingEditorElemId = "edit-chapter-heading-#{chapterId}"
-  bodyEditorElemId    = "edit-chapter-body-#{chapterId}"

   # If you delete the chapter's body and heading, assume you want it gone.
   # (If you didn't, you can just re-add it; there couldn't have been data loss.)
@@ -60,10 +58,10 @@
       # positives while the chapter is being created, causing it to be deleted.
       (!mutations.some ({addedNodes, removedNodes}) ->
         addedNodes.length > 0 || removedNodes.length > 0) &&
-      !document.getElementById(headingEditorElemId).textContent.match(/\S/) &&
-      !document.getElementById(bodyEditorElemId).textContent.match(/\S/)
-
-  editorHeadingPromise = setUpEditor headingEditorElemId, chapter.heading, false, (mutations, node) ->
+      !getChapterHeadingElem(chapterId).textContent.match(/\S/) &&
+      !getChapterBodyElem(chapterId).textContent.match(/\S/)
+
+  editorHeadingPromise = setUpEditor (-> getChapterHeadingElem chapterId), chapter.heading, false, (mutations, node) ->
     sync.getCurrentDoc().done (doc) ->
       heading = node.textContent

@@ -83,7 +81,7 @@

   editorBodyPromise = new Promise (resolve, reject) ->
     sync.getSnapshot(chapter.snapshotId).done (snapshot) ->
-      setUpEditor bodyEditorElemId, snapshot.html, true, (mutations, node) ->
+      setUpEditor (-> getChapterBodyElem chapterId), snapshot.html, true, (mutations, node) ->
         snapshotId    = chapter.snapshotId
         html          = node.innerHTML
         text          = node.textContent
@@ -174,8 +172,8 @@
     editors.set elem, editor
     editor

-setUpEditor = (id, html, enableRichText, onMutate) ->
-  whenPresent((-> document.getElementById id), 10000)
+setUpEditor = (getElem, html, enableRichText, onMutate) ->
+  whenPresent(getElem, 10000)
     .then ((elem) -> getEditorFor(elem, enableRichText, onMutate).writeHtml html, true),
           ((err)  -> console.error "Could not write after 10,000 attempts:", id, html)

@@ -185,9 +183,21 @@
 scrollToChapterId = (chapterId) ->
   editorFrame    = document.getElementById("editor-frame")
   editorHeader   = document.getElementById("editor-header")
-  chapterHeading = document.getElementById("edit-chapter-heading-#{chapterId}")
+  chapterHeading = getChapterHeadingElem chapterId

   editorFrame.scrollTop = chapterHeading.offsetTop - editorHeader.offsetHeight
+
+getTitleElem = ->
+  document.getElementById "edit-title"
+
+getDescriptionElem = ->
+  document.getElementById "edit-description"
+
+getChapterHeadingElem = (id) ->
+  document.getElementById "edit-chapter-heading-#{id}"
+
+getChapterBodyElem = (id) ->
+  document.getElementById "edit-chapter-body-#{id}"

 deleteChapter = (chapter) ->
   sync.deleteChapter(chapter).done (newChapters) ->


Remove dead code

Remove dead code

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -28,7 +28,6 @@
     @emitter = new Emitter
     @visibleHighlights = {}
     @characterWidthsByScope = {}
-    @rangesByDecorationId = {}
     @lineDecorationsByScreenRow = {}
     @lineNumberDecorationsByScreenRow = {}
     @customGutterDecorationsByGutterNameAndScreenRow = {}
@@ -1192,7 +1191,6 @@
         @decorations.push({decoration, range})

   updateLineDecorations: ->
-    @rangesByDecorationId = {}
     @lineDecorationsByScreenRow = {}
     @lineNumberDecorationsByScreenRow = {}
     @customGutterDecorationsByGutterNameAndScreenRow = {}
@@ -1215,20 +1213,6 @@
         delete tileState.highlights[id] unless @visibleHighlights[tileId]?[id]?

     return
-
-  removeFromLineDecorationCaches: (decoration) ->
-    @removePropertiesFromLineDecorationCaches(decoration.id, decoration.getProperties())
-
-  removePropertiesFromLineDecorationCaches: (decorationId, decorationProperties) ->
-    if range = @rangesByDecorationId[decorationId]
-      delete @rangesByDecorationId[decorationId]
-
-      gutterName = decorationProperties.gutterName
-      for row in [range.start.row..range.end.row] by 1
-        delete @lineDecorationsByScreenRow[row]?[decorationId]
-        delete @lineNumberDecorationsByScreenRow[row]?[decorationId]
-        delete @customGutterDecorationsByGutterNameAndScreenRow[gutterName]?[row]?[decorationId] if gutterName
-      return

   addToLineDecorationCaches: (decoration, range) ->
     marker = decoration.getMarker()
@@ -1241,8 +1225,6 @@
     else
       return if properties.onlyEmpty
       omitLastRow = range.end.column is 0
-
-    @rangesByDecorationId[decoration.id] = range

     for row in [range.start.row..range.end.row] by 1
       continue if properties.onlyHead and row isnt marker.getHeadScreenPosition().row


Sort locals alphabetically

Sort locals alphabetically

--- lib/Components/CallStackPane.coffee
+++ lib/Components/CallStackPane.coffee
@@ -137,7 +137,10 @@
         return frame.arguments.concat(frame.locals)

     root: () ->
-      TreeView("Locals", (() -> builder2.loadLocals().map(builder.value)), isRoot:true)
+      sortLocals = (locals) ->
+        locals.sort((a,b) -> a.name.localeCompare(b.name));
+        return locals;
+      TreeView("Locals", (() -> builder2.loadLocals().then(sortLocals).map(builder.value)), isRoot:true)

   LocalsPane = () ->
     state = builder2.root()


Removed freezing of application, and added an ajaxprefilter for adding login and api_key if attached to application

--- app/application.coffee
+++ app/application.coffee
@@ -9,9 +9,6 @@
       Backbone.history.start
         pushState: true
         root: config.approot
-
-      # Freeze the object
-      Object.freeze? this

     @addInitializer (options) =>
       # All navigation that is relative should be passed through the navigate
@@ -47,6 +44,18 @@
       Router = require 'lib/router'
       @router = new Router()

+    @addInitializer (options) =>
+      # Add an ajax prefilter
+      # to add user_id and api_key
+      application = this
+      $.ajaxPrefilter (options, originalOptions, jqXHR) ->
+        if application.user_id and application.api_key
+          login_data = {
+            user_id: application.user_id,
+            api_key: application.api_key
+          }
+          options.data = $.param($.extend(originalOptions.data, login_data))
+
     @start()

 module.exports = new Application()


restored back to original Gruntfile

restored back to original Gruntfile

--- bokehjs/Gruntfile.coffee
+++ bokehjs/Gruntfile.coffee
@@ -200,7 +200,7 @@
       server:
         options:
           port: 8000,
-          base: 'docs/'
+          base: '.'

     qunit:
       all:


change tag's size

change tag's size

--- views/titlesList.cjsx
+++ views/titlesList.cjsx
@@ -65,11 +65,11 @@
     if title isnt @props.activeTitle
       @props.handleTitleChange title
   render: ->
-

+

         <Input type='text'
                value={@state.filterKey}
-               placeholder={__ "Keywords"}
+               placeholder={__ 'Keywords'}
                hasFeedback
                ref='keywords'
                onChange={@handleKeyWordChange} />
@@ -88,7 +88,7 @@
                         {
                           if @state.showData[title].tags.length != 0
                             for tag, tagIndex in @state.showData[title].tags
-                              <Label style={display: 'inline-block', margin: 5}
+
                                {tag}


fixed test, related to c294224

fixed test, related to c294224

--- test/Hifo.coffee
+++ test/Hifo.coffee
@@ -122,7 +122,8 @@
 			obj1 = { value: 1 }
 			instance.add obj1
 			instance.add { value: 2 }
-			obj1.value = 3
+			obj1.value = 3   # even if the object has been messed with, the reference stays the same.
+			instance.add obj1
 			instance.add obj1

 			appearances = 0
@@ -137,6 +138,10 @@
 			instance.add obj1
 			instance.add obj2
 			assert instance.data.indexOf(obj1) > instance.data.indexOf obj2
+
+		it 'should not `push` and `pop` too low values', () ->
+			# todo: use ECMAScript 2016 `Array.prototype.observe`.
+			assert true


Rate-limit calls to invite api

Rate-limit calls to invite api

--- services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee
@@ -2,6 +2,7 @@
 AuthenticationController = require('../Authentication/AuthenticationController')
 AuthorizationMiddlewear = require('../Authorization/AuthorizationMiddlewear')
 CollaboratorsInviteController = require('./CollaboratorsInviteController')
+RateLimiterMiddlewear = require('../Security/RateLimiterMiddlewear')

 module.exports =
 	apply: (webRouter, apiRouter) ->
@@ -13,24 +14,40 @@
 		# invites
 		webRouter.post(
 			'/project/:Project_id/invite',
+			RateLimiterMiddlewear.rateLimit({
+				endpointName: "invite-to-project"
+				params: ["Project_id"]
+				maxRequests: 200
+				timeInterval: 60 * 10
+			}),
+			AuthenticationController.requireLogin(),
 			AuthorizationMiddlewear.ensureUserCanAdminProject,
 			CollaboratorsInviteController.inviteToProject
 		)

 		webRouter.get(
 			'/project/:Project_id/invite',
+			AuthenticationController.requireLogin(),
 			AuthorizationMiddlewear.ensureUserCanAdminProject,
 			CollaboratorsInviteController.getAllInvites
 		)

 		webRouter.delete(
 			'/project/:Project_id/invite/:invite_id',
+			AuthenticationController.requireLogin(),
 			AuthorizationMiddlewear.ensureUserCanAdminProject,
 			CollaboratorsInviteController.revokeInvite
 		)

 		webRouter.post(
 			'/project/:Project_id/invite/:invite_id/resend',
+			RateLimiterMiddlewear.rateLimit({
+				endpointName: "resend-invite"
+				params: ["Project_id"]
+				maxRequests: 200
+				timeInterval: 60 * 10
+			}),
+			AuthenticationController.requireLogin(),
 			AuthorizationMiddlewear.ensureUserCanAdminProject,
 			CollaboratorsInviteController.resendInvite
 		)


added jslove task

added jslove task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,65 +1,5 @@
 # Build configurations
 module.exports = (grunt) ->
-	# Compiles all CoffeeScript files in the project to JavaScript then deletes all CoffeeScript files.
-	# Used for those that desire plain old JavaScript.
-	# Enter the following command at the command line to execute this build task:
-	# grunt jslove
-	# grunt.registerTask 'jslove', [
-	# 	'coffee:jslove'
-	# 	'clean:jslove'
-	# ]
-
-	# Deletes dist and temp directories.
-	# The temp directory is used during the build process.
-	# The dist directory contains the artifacts of the build.
-	# These directories should be deleted before subsequent builds.
-	# These directories are not committed to source control.
-	# clean:
-	# 	working:
-	# 		src: [
-	# 			'./dist/'
-	# 			'./dist_test/'
-	# 			'./.temp/'
-	# 		]
-	# 	# Used for those that desire plain old JavaScript.
-	# 	jslove:
-	# 		src: [
-	# 			'**/*.coffee'
-	# 			'!**/node_modules/**'
-	# 		]
-
-	# Compile CoffeeScript (.coffee) files to JavaScript (.js).
-	# coffee:
-	# 	scripts:
-	# 		files: [
-	# 			cwd: './.temp/'
-	# 			src: 'scripts/**/*.coffee'
-	# 			dest: './.temp/'
-	# 			expand: true
-	# 			ext: '.js'
-	# 		,
-	# 			cwd: './test/'
-	# 			src: 'scripts/**/*.coffee'
-	# 			dest: './dist_test/'
-	# 			expand: true
-	# 			ext: '.js'
-	# 		]
-	# 		options:
-	# 			sourceMap: true
-	# 	# Used for those that desire plain old JavaScript.
-	# 	jslove:
-	# 		files: [
-	# 			cwd: './'
-	# 			src: [
-	# 				'**/*.coffee'
-	# 				'!**/node_modules/**'
-	# 			]
-	# 			dest: './'
-	# 			expand: true
-	# 			ext: '.js'
-	# 		]
-	# 		options: '<%= coffee.scripts.options %>'
-
 	grunt.initConfig
 		# Deletes dist and .temp directories
 		# The .temp directory is used during the build process
@@ -553,4 +493,13 @@
 	grunt.registerTask 'test', [
 		'build'
 		'karma'
+	]
+
+	# Compiles all CoffeeScript files in the project to JavaScript then deletes all CoffeeScript files
+	# Used for those that desire plain old JavaScript
+	# Enter the following command at the command line to execute this build task:
+	# grunt jslove
+	grunt.registerTask 'jslove', [
+		'coffee:jslove'
+		'clean:jslove'
 	]


Add build target for making production assets

Also fix whitespace

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,134 +1,134 @@
 module.exports = ( grunt ) ->

-	path = require( 'path' )
+  path = require( 'path' )

-	grunt.initConfig
-		pkg: grunt.file.readJSON( 'package.json' )
+  grunt.initConfig
+    pkg: grunt.file.readJSON( 'package.json' )

-		# Watch files and run the appropriate task on that file when it is
-		# changed.
-		watch:
-			components:
-				files: [ 'frontend/components/**/*.js' ]
-				tasks: [ 'copy:components', 'coffee:requirejs', 'requirejs' ]
+    # Watch files and run the appropriate task on that file when it is
+    # changed.
+    watch:
+      components:
+        files: [ 'frontend/components/**/*.js' ]
+        tasks: [ 'copy:components', 'coffee:requirejs', 'requirejs' ]

-			scripts:
-				files: [ 'frontend/coffeescript/**/*.coffee' ]
-				tasks: [ 'copy:components', 'coffee:requirejs', 'requirejs' ]
+      scripts:
+        files: [ 'frontend/coffeescript/**/*.coffee' ]
+        tasks: [ 'copy:components', 'coffee:requirejs', 'requirejs' ]

-			styles:
-				files: [ 'frontend/sass/**/*.scss' ]
-				tasks: [ 'compass:dist' ]
+      styles:
+        files: [ 'frontend/sass/**/*.scss' ]
+        tasks: [ 'compass:dist' ]

-			images:
-				files: [ 'frontend/img/**/*' ]
-				tasks: [ 'copy:img' ]
+      images:
+        files: [ 'frontend/img/**/*' ]
+        tasks: [ 'copy:img' ]

-		# Copy the appropriate bower components to our <vendor> folder
-		bower:
-			install:
-				options:
-					targetDir: '<%= pkg.static_dir %>'
-					layout: (type, component) ->
-						renamedType = type
-						if type == 'js'
-							renamedType = 'js/vendor'
-						else if type == 'css'
-							renamedType = 'css'
-						else if type == 'font'
-							renamedType = 'font'
-						return renamedType
+    # Copy the appropriate bower components to our <vendor> folder
+    bower:
+      install:
+        options:
+          targetDir: '<%= pkg.static_dir %>'
+          layout: (type, component) ->
+            renamedType = type
+            if type == 'js'
+              renamedType = 'js/vendor'
+            else if type == 'css'
+              renamedType = 'css'
+            else if type == 'font'
+              renamedType = 'font'
+            return renamedType

-		# Compile all javascript and place into our intermediary folder for
-		# RequireJS optimization
-		coffee:
-			requirejs:
-				options:
-					bare: true
-				expand: true
-				cwd: 'frontend/coffeescript'
-				src: [ '**/*.coffee' ]
-				dest: 'build/js'
-				ext: '.js'
+    # Compile all javascript and place into our intermediary folder for
+    # RequireJS optimization
+    coffee:
+      requirejs:
+        options:
+          bare: true
+        expand: true
+        cwd: 'frontend/coffeescript'
+        src: [ '**/*.coffee' ]
+        dest: 'build/js'
+        ext: '.js'

-		compass:
-			dist:
-				options:
-					sassDir: 'frontend/sass'
-					cssDir: '<%= pkg.static_dir %>/css'
-					outputStyle: 'compressed'
+    compass:
+      dist:
+        options:
+          sassDir: 'frontend/sass'
+          cssDir: '<%= pkg.static_dir %>/css'
+          outputStyle: 'compressed'

-		copy:
-			css:
-				expand: true
-				cwd: 'frontend/css'
-				src: [ '**/*' ]
-				dest: '<%= pkg.static_dir %>/css'
+    copy:
+      css:
+        expand: true
+        cwd: 'frontend/css'
+        src: [ '**/*' ]
+        dest: '<%= pkg.static_dir %>/css'

-			# Javascript components specifically go into an intermediary folder
-			# due to a two-stage build-process with Require-JS
-			components:
-				expand: true
-				cwd: 'frontend/components'
-				src: [ '**/*.js' ]
-				dest: 'build/js/vendor'
+      # Javascript components specifically go into an intermediary folder
+      # due to a two-stage build-process with Require-JS
+      components:
+        expand: true
+        cwd: 'frontend/components'
+        src: [ '**/*.js' ]
+        dest: 'build/js/vendor'

-			favicon:
-				expand: true
-				cwd: 'frontend/favicon'
-				src: [ '**/*' ]
-				dest: '<%= pkg.static_dir %>/favicon'
+      favicon:
+        expand: true
+        cwd: 'frontend/favicon'
+        src: [ '**/*' ]
+        dest: '<%= pkg.static_dir %>/favicon'

-			img:
-				expand: true
-				cwd: 'frontend/img'
-				src: [ '**/*.png', '**/*.jpg' ]
-				dest: '<%= pkg.static_dir %>/img'
+      img:
+        expand: true
+        cwd: 'frontend/img'
+        src: [ '**/*.png', '**/*.jpg' ]
+        dest: '<%= pkg.static_dir %>/img'

-		requirejs:
-			compile:
-				options:
-					appDir: 'build'
-					mainConfigFile: 'build/js/common.js'
-					dir: '<%= pkg.static_dir %>'
-					keepBuildDir: true
-					optimize: 'none'
-					optimizeCss: 'none'
-					modules: [ {
-						name: '../common'
-						include: [ 'jquery',
-								   'app/dashboard/main',
-								   'app/dashboard/views',
-								   'app/viz/main',
-								   'app/viz/views',
-								   'app/webform/main',
-								   'app/webform/views' ]
-					}]
+    requirejs:
+      compile:
+        options:
+          appDir: 'build'
+          mainConfigFile: 'build/js/common.js'
+          dir: '<%= pkg.static_dir %>'
+          keepBuildDir: true
+          optimize: if grunt.option('target') and grunt.option('target') is 'prod' then 'uglify' else 'none'
+          optimizeCss: 'none'
+          modules: [ {
+            name: '../common'
+            include: [ 'jquery',
+                   'app/dashboard/main',
+                   'app/dashboard/views',
+                   'app/viz/main',
+                   'app/viz/views',
+                   'app/webform/main',
+                   'app/webform/views' ]
+          }]

-	grunt.loadNpmTasks( 'grunt-bower-task' )
-	grunt.loadNpmTasks( 'grunt-contrib-watch' )
-	grunt.loadNpmTasks( 'grunt-contrib-requirejs' )
-	grunt.loadNpmTasks( 'grunt-contrib-coffee' )
-	grunt.loadNpmTasks( 'grunt-contrib-compass' )
-	grunt.loadNpmTasks( 'grunt-contrib-copy' )

-	grunt.registerTask( 'default', [ 'watch' ] )
+  grunt.loadNpmTasks( 'grunt-bower-task' )
+  grunt.loadNpmTasks( 'grunt-contrib-watch' )
+  grunt.loadNpmTasks( 'grunt-contrib-requirejs' )
+  grunt.loadNpmTasks( 'grunt-contrib-coffee' )
+  grunt.loadNpmTasks( 'grunt-contrib-compass' )
+  grunt.loadNpmTasks( 'grunt-contrib-copy' )

-	grunt.registerTask( 'build', [ # Run through javascript compilation process
-								   'bower',
-								   'copy:components',
-								   'coffee:requirejs',
-								   'requirejs',
+  grunt.registerTask( 'default', [ 'watch' ] )

-								   # Compile SCSS
-								   'compass:dist',
+  # Run through Javascript/CSS compilation process,
+  # add --target=prod to minify Javascript assets.
+  grunt.registerTask( 'build', [
+                   'bower',
+                   'copy:components',
+                   'coffee:requirejs',
+                   'requirejs',

-								   # Finally copy oher basic components over to
-								   # <static> folder
-								   'copy:components',
-								   'copy:css',
-								   'copy:favicon',
-								   'copy:img',
+                   # Compile SCSS
+                   'compass:dist',

-								   # Now, begin watching for new changes
-								   'watch' ] )
+                   # Finally copy oher basic components over to
+                   # <static> folder
+                   'copy:components',
+                   'copy:css',
+                   'copy:favicon',
+                   'copy:img' ] )


Better commit message

Better commit message

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -77,7 +77,7 @@
       options:
         base: 'browser'
         clone: 'gh-pages'
-        message: 'Updating'
+        message: "Release #{pkg.name} #{process.env.TRAVIS_TAG}"
         repo: repo
         user:
           name: 'NoFlo bot'


removed spec-e2e

removed spec-e2e

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,9 +8,5 @@
         options:
           helpers: 'spec/helpers/**/*.{js,coffee}'
           specs: 'spec/**/*.{js,coffee}'
-      e2e:
-        options:
-          helpers: 'spec-e2e/helpers/**/*.{js,coffee}'
-          specs: 'spec-e2e/**/*.{js,coffee}'

-  g.registerTask 'default', ['spec:unit', 'spec:e2e']
+  g.registerTask 'default', ['spec:unit']


fixed watch to run coffee:scripts instead of jslove

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -279,7 +279,7 @@
 			scripts:
 				files: './src/scripts/**/*.coffee'
 				tasks: [
-					'coffee'
+					'coffee:scripts'
 					'copy:scripts'
 				]
 			styles:


Prevent definition errors in top level expression and make sure not defined are correctly marked

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -195,6 +195,9 @@
       deferrable: yes
       _defer: undefined

+  bareDefine: ->
+    @definePattern()
+
   defineNonDeferrablePattern: (pattern) ->
     definition = @definePattern pattern
     definition.deferrable = no
@@ -235,13 +238,13 @@
     !!@_scope().definition

   isAtDefinition: ->
-    (definition = @_currentDefinition()) and definition.inside is 0
+    (definition = @_currentDefinition()) and definition.pattern and definition.inside is 0

   isAtSimpleDefinition: ->
     @isAtDefinition() and @definitionName()

-  isAtDeferrableDefinition: ->
-    @isAtDefinition() and @_currentDefinition().deferrable
+  # isAtDeferrableDefinition: ->
+  #   @isAtDefinition() and @_currentDefinition().deferrable

   # isInsideSimpleDefinition: ->
   #   (definition = @_currentDefinition())
@@ -557,7 +560,7 @@
   if not paramNames
     # log "deferring in known call #{operator.symbol}"
     ctx.doDefer operator, operator.symbol
-    return deferredExpression()
+    return assignCompile ctx, call, deferredExpression()
   positionalParams = filter ((param) -> not (lookupInMap labeledArgs, param)), paramNames
   nonLabeledArgs = map _snd, filter (([label, value]) -> not label), args

@@ -1018,8 +1021,16 @@
   assigns: assigns ? []

 topLevelExpression = (ctx, expression) ->
+  ctx.bareDefine()
   compiled = expressionCompile ctx, expression
-  (irDefinition expression.tea, compiled)
+  deferred = ctx.deferReason()
+  ctx.leaveDefinition()
+  if deferred
+    [expression, dependencyName] = deferred
+    malformed expression, "#{dependencyName} is not defined"
+    undefined
+  else
+    (irDefinition expression.tea, compiled)

 topLevel = (ctx, form) ->
   if (terms = _terms form).length % 2 == 0
@@ -4077,11 +4088,12 @@

 compileCtxAstToJs = (compileFn, ctx, ast) ->
   ir = compileFn ctx, ast
-  jsIr = translateIr ctx, ir
-  js = (if Array.isArray jsIr
-      translateStatementsToJs
-    else
-      translateToJs) jsIr
+  if ir
+    jsIr = translateIr ctx, ir
+    js = (if Array.isArray jsIr
+        translateStatementsToJs
+      else
+        translateToJs) jsIr
   {ctx, ast, js}

 astizeList = (source) ->


Update Atom

Update Atom

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -19,6 +19,7 @@
     disabledPackages: [
       "spell-check"
       "whitespace"
+      "atom-perforce"
     ]
     audioBeep: false
     themes: [


Set Atom editor to not exclude VCS ignored paths.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -46,6 +46,7 @@
       "nuclide-blame"
       "linter-markdown"
     ]
+    excludeVcsIgnoredPaths: false
     packagesWithKeymapsDisabled: [
       "jumpy"
     ]


show tabs

show tabs

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -2,9 +2,13 @@
   welcome:
     showOnStartup: false
   editor:
-    fontFamily: "Consolas, Inconsolata, Monaco, Courier New, Courier"
-    fontSize: 14
+    fontSize: 12
+    fontFamily: "Droid Sans Mono, Monaco, Inconsolata, Consolas, Courier New, Courier"
+    tabLength: 4
+    softTabs: false
     invisibles:
+      cr: ""
+      eol: ""
       tab: "→"
     showInvisibles: true
   core:


add a bunch of key defs for reference, and customize a bunch for navigation and switching modes

--- home/.atom/keymap.cson
+++ home/.atom/keymap.cson
@@ -29,6 +29,7 @@
 #
 # NOTE: CAPS LOCK should be mapped to Control
 #
+
 'atom-workspace':
   # Close Pane
   'ctrl-\'': 'core:close'
@@ -66,18 +67,76 @@
   'ctrl-shift-t': 'editor:add-selection-below'

   # Pane Splitting
-  'ctrl alt-t':   'pane:split-down-and-copy-active-item'
-  'ctrl alt-h':   'pane:split-left-and-copy-active-item'
+  'ctrl alt-t':  'pane:split-down-and-copy-active-item'
+  'ctrl alt-h':  'pane:split-left-and-copy-active-item'
   'ctrl alt-n':  'pane:split-right-and-copy-active-item'
-  'ctrl alt-c':     'pane:split-up-and-copy-active-item'
+  'ctrl alt-c':  'pane:split-up-and-copy-active-item'

 #####################################################
 # VIM MODE PLUS!!!
 #####################################################
+'atom-workspace atom-text-editor:not([mini]), atom-text-editor.vim-mode-plus':
+  # Deleting - because the delete key is too far away, and mac laptops don't have one
+  'ctrl-d':       'core:delete'
+  'ctrl-shift-d': 'editor:delete-to-end-of-word'
+  'alt-shift-d':  'editor:delete-line'
+
+  # Enter Visual Mode
+  'ctrl-j': 'vim-mode-plus:activate-characterwise-visual-mode'
+  # Enter Normal Mode
+  'ctrl-u': 'vim-mode-plus:activate-normal-mode'
+  # Enter Insert Mode
+  'ctrl-i': 'vim-mode-plus:activate-insert-mode'
+
+##############################################
+################ INSERT MODE #################
 'atom-text-editor.vim-mode-plus.insert-mode':
-  # Close pane. This was originally delete to beginning of word
-  'ctrl-w': 'core:close'
-
+  # Copy - because for some reason, this was bound to something else
+  'ctrl-c': 'core:copy'
+
+  # Exit Insert Mode - Escape is too far away.
+  'ctrl-u': 'vim-mode-plus:activate-normal-mode'
+
+##############################################
+################ VISUAL MODE #################
+'atom-text-editor.vim-mode-plus.visual-mode':
+  # Exit Visual Mode - Escape is too far away.
+  'ctrl-u': 'vim-mode-plus:activate-normal-mode'
+  # Enter Insert Mode
+  'ctrl-i': 'vim-mode-plus:activate-insert-mode'
+
+  # select the entire block with the same indentation
+  'i i': 'vim-mode-plus:inner-indentation'
+
+  # select the entire block within the current fold-level
+  'i f': 'vim-mode-plus:inner-fold'
+
+  # Select outer wrapping characters
+  'o s': 'vim-mode-plus:a-any-pair'
+  'o q': 'vim-mode-plus:a-any-quote'
+  'o {': 'vim-mode-plus:a-curly-bracket'
+  'o }': 'vim-mode-plus:a-curly-bracket'
+  'o [': 'vim-mode-plus:a-square-bracket'
+  'o ]': 'vim-mode-plus:a-square-bracket'
+  'o <': 'vim-mode-plus:a-angle-bracket'
+  'o >': 'vim-mode-plus:a-angle-bracket'
+  'o (': 'vim-mode-plus:a-parenthesis'
+  'o )': 'vim-mode-plus:a-parenthesis'
+
+  # Select inside wrapping characters
+  'i s': 'vim-mode-plus:inner-any-pair'
+  'i q': 'vim-mode-plus:inner-any-quote'
+  'i {': 'vim-mode-plus:inner-curly-bracket'
+  'i }': 'vim-mode-plus:inner-curly-bracket'
+  'i [': 'vim-mode-plus:inner-square-bracket'
+  'i ]': 'vim-mode-plus:inner-square-bracket'
+  'i <': 'vim-mode-plus:inner-angle-bracket'
+  'i >': 'vim-mode-plus:inner-angle-bracket'
+  'i (': 'vim-mode-plus:inner-parenthesis'
+  'i )': 'vim-mode-plus:inner-parenthesis'
+
+##############################################
+############### NOT INSERT MODE ##############
 'atom-text-editor.vim-mode-plus:not(.insert-mode)':
   # Character Navigation
   'c':   'core:move-up'
@@ -85,9 +144,17 @@
   'h':   'core:move-left'
   'n':   'core:move-right'

+  # Character Selection
+  'shift-c': 'core:select-up'
+  'shift-h': 'core:select-left'
+  'shift-n': 'core:select-right'
+  'shift-t': 'core:select-down'
+
   # Word Navigation
-  'shift-n': 'editor:move-to-end-of-word'
-  'shift-h': 'editor:move-to-beginning-of-word'
+  'm':       'editor:move-to-next-subword-boundary'
+  'b':       'editor:move-to-previous-subword-boundary'
+  'shift-m': 'editor:select-to-next-subword-boundary'
+  'shift-b': 'editor:select-to-previous-subword-boundary'

   # Line Navigation
   '\' n': 'editor:move-to-end-of-screen-line'
@@ -105,13 +172,12 @@
   'r': 'pane:show-previous-item'
   'l': 'pane:show-next-item'

-
-  # Word Navigation
-  'b': 'editor:move-to-beginning-of-word'
-  'm': 'editor:move-to-end-of-word'
-
   # shortcut for : (so you don't have to type shift)
   ';': 'ex-mode:open'
+
+  # Because vim-search is actually kinda lame
+  '/':       'find-and-replace:show'
+  'ctrl-/':  'project-find:show'

 ###############################################


using build instead of tmp

using build instead of tmp

--- client/gulpfile.coffee
+++ client/gulpfile.coffee
@@ -12,14 +12,14 @@
 path = require 'path'

 gulp.task 'clean', ->
-  del.sync [ 'tmp', 'build', 'dist' ]
+  del.sync [ 'build', 'dist' ]

 gulp.task 'fonts', ->
   gulp.src('node_modules/bootstrap/fonts/*')
     .pipe gulp.dest('dist/fonts')

 gulp.task 'copy', ['jade'], ->
-  gulp.src('tmp/templates/index.html')
+  gulp.src('build/templates/index.html')
     .pipe gulp.dest('dist')
     .pipe connect.reload()

@@ -27,10 +27,10 @@
   gulp.src 'app/src/**/*.jade'
     .pipe jade
       pretty: true
-    .pipe gulp.dest 'tmp/templates'
+    .pipe gulp.dest 'build/templates'

 gulp.task 'templates', ['jade'], ->
-  gulp.src ['tmp/templates/**/*.html', '!tmp/templates/index.html']
+  gulp.src ['build/templates/**/*.html', '!build/templates/index.html']
     .pipe ngTemplates
       filename: 'templates.js'
       module: 'linkr'
@@ -48,10 +48,10 @@
   gulp.src('app/src/**/*.coffee')
     .pipe(coffee({bare: true})
     .on('error', gutil.log))
-    .pipe(gulp.dest('tmp/js'))
+    .pipe(gulp.dest('build/js'))

 gulp.task 'scripts', ['coffee'], ->
-  gulp.src('tmp/js/app.js')
+  gulp.src('build/js/app.js')
     .pipe(browserify({}))
     .pipe(gulp.dest('dist'))
     .pipe connect.reload()


Includes: Removed deleted sources from includes.

--- client/includes.coffee
+++ client/includes.coffee
@@ -740,8 +740,6 @@
   "libs/codemirror/lib/codemirror.css",
   "app/CommonViews/workspace/panes/collaborativepane.coffee",
   "app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee",
-  "app/CommonViews/workspace/panes/collaborativeterminalpane.coffee",
-  "app/CommonViews/workspace/panes/collaborativeclientterminalpane.coffee",
   "app/CommonViews/workspace/panes/sharableterminalpane.coffee",
   "app/CommonViews/workspace/panes/sharableclientterminalpane.coffee",
   "app/CommonViews/workspace/panes/collaborativefinderpane.coffee",


修正Table新出现的无法使用的严重BUG

修正Table新出现的无法使用的严重BUG

--- src/widget/list/table.coffee
+++ src/widget/list/table.coffee
@@ -313,7 +313,7 @@
 					columnConfig =
 						caption: propertyDef._caption
 						property: propertyDef._property
-					@_applyPropertyDefProperties(columnConfig, propertyDef)
+					@_applyPropertyDefProperties?(columnConfig, propertyDef)
 					columnConfigs.push(columnConfig)
 				@set("columns", columnConfigs)
 			else
@@ -339,7 +339,7 @@
 							else
 								template = "input-column"
 							column.set("template", template)
-						@_applyPropertyDefProperties(column, propertyDef)
+						@_applyPropertyDefProperties?(column, propertyDef)

 		$fly(window).resize () =>
 			if @_fixedHeaderVisible


Fix date reactivity, e.g. in History view

--- client/lib/date.coffee
+++ client/lib/date.coffee
@@ -36,7 +36,7 @@
 export FormatDate = React.memo ({date, prefix}) ->
   useTracker ->
     formatDate date, prefix
-  , []
+  , [date, prefix]
 FormatDate.displayName = 'FormatDate'

 Template.registerHelper 'formatDate', (date, kw) ->


Support string config of older getUserMedia spec.

--- src/jpeg_camera_html5.coffee
+++ src/jpeg_camera_html5.coffee
@@ -61,8 +61,7 @@
           ]

       that = this
-
-      navigator.getUserMedia get_user_media_options,
+      success =
         (stream) ->
           that._remove_message()

@@ -72,6 +71,7 @@
             that.video.src = stream

           that._wait_for_video_ready()
+      failure =
         (error) ->
           # XXX Receives NavigatorUserMediaError object and searches for
           # constant name matching error.code. With the current specification
@@ -83,6 +83,12 @@
             that._got_error key
             return
           that._got_error "UNKNOWN ERROR"
+
+      # XXX In an older spec first parameter was a string
+      try
+        navigator.getUserMedia get_user_media_options, success, failure
+      catch
+        navigator.getUserMedia "video", success, failure

     _engine_play_shutter_sound: ->
       return unless @shutter_buffer


Ghc-mod 5.7 is a maintenance release, no new features

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -172,7 +172,7 @@
       caps.typeConstraints = true
       caps.browseParents = true
       caps.interactiveCaseSplit = true
-    if atLeast([5, 7]) or atom.config.get('haskell-ghc-mod.experimental')
+    if atom.config.get('haskell-ghc-mod.experimental')
       caps.importedFrom = true
     Util.debug JSON.stringify(caps)
     return caps


Added parens for clarity

Added parens for clarity

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -198,13 +198,13 @@
     @totalPackages.text(@packages.user.length + @packages.core.length + @packages.dev.length)

   updateFilteredSectionCounts: ->
-    deprecated = @notHiddenCardsLength @deprecatedPackages
+    deprecated = @notHiddenCardsLength(@deprecatedPackages)
     @updateSectionCount(@deprecatedPackagesHeader, @deprecatedCount, deprecated, @packages.deprecated.length)

-    community = @notHiddenCardsLength @communityPackages
+    community = @notHiddenCardsLength(@communityPackages)
     @updateSectionCount(@communityPackagesHeader, @communityCount, community, @packages.user.length)

-    core = @notHiddenCardsLength @corePackages
+    core = @notHiddenCardsLength(@corePackages)
     @updateSectionCount(@corePackagesHeader, @coreCount, core, @packages.core.length)

     dev = @notHiddenCardsLength @devPackages


Fix for selection spanning into empty lines

--- lib/markdown-table-formatter.coffee
+++ lib/markdown-table-formatter.coffee
@@ -88,4 +88,4 @@

     selectionsRanges = editor.getSelectedBufferRanges()

-    editor.setTextInBufferRange(range, normtable(editor.getTextInBufferRange(range))) for range in selectionsRanges
+    editor.setTextInBufferRange(range, normtable(editor.getTextInBufferRange(range).trim())) for range in selectionsRanges


saving character set data to local storage works once more and T now toggles the tile dialog

--- web-editor/app.coffee
+++ web-editor/app.coffee
@@ -17,6 +17,7 @@
 # - should be able to choose different shared colors #1 and #2 in sprite mode
 # - copy and paste should use hover target rather than selected character
 # - bug: try noticing mouseOut in the editor and picking up the brush
+# - be able to export an array for lookup of changeable color to use per character

 # FEATURES
@@ -760,7 +761,7 @@
   save_to_local_storage = ->
     save = ( data, name ) ->
       localStorage[ name] = JSON.stringify( data )
-    save  data, 'data'
+    save  character_set.data, 'data'
     save  Color::id_for, 'changeable_colors'
     save  tile_palette.data, 'tile_design_data'
     save  world.data, 'world_data'
@@ -818,7 +819,7 @@
       when K_X then blank()
       when K_C then copy_from_index = selected_character_code
       when K_V then selected_character().copy_from  copy_from_index
-      when K_T then $('#tile_palette_dialog').fadeIn 'fast'
+      when K_T then $('#tile_palette_dialog').fadeToggle 'fast'
       when K_G then world.choose_tile()
       when K_W then world.pan_view 'up'
       when K_A then world.pan_view 'left'


Fix #52 to prevent spitting an error message when whitespace package is not active

--- lib/notational-velocity-view.coffee
+++ lib/notational-velocity-view.coffee
@@ -105,9 +105,12 @@
     if filePath
       atom.workspace.open(filePath).then (editor) ->
         save = ->
-          atom.packages.deactivatePackage 'whitespace'
+          isWhiteSpaceActive = atom.packages.isPackageActive 'whitespace'
+          if isWhiteSpaceActive
+            atom.packages.deactivatePackage 'whitespace'
           editor.save()
-          atom.packages.activatePackage 'whitespace'
+          if isWhiteSpaceActive
+            atom.packages.activatePackage 'whitespace'
         debouncedSave = _.debounce save, 1000
         editor.onDidStopChanging () ->
           debouncedSave() if editor.isModified()


Refactor schedulePopulateList

Refactor schedulePopulateList

--- lib/notational-velocity-view.coffee
+++ lib/notational-velocity-view.coffee
@@ -14,7 +14,7 @@
     if !fs.existsSync(@rootDirectory)
       throw new Error("The given directory #{@rootDirectory} does not exist. "
         + "Set the note directory to the existing one from Settings.")
-    @prevFilterQuery = ''
+    @skipPopulateList = false
     @prevCursorPosition = 0
     @documentsLoaded = false
     @docQuery = new DocQuery(@rootDirectory, {recursive: true})
@@ -41,7 +41,7 @@
     editor = @filterEditorView.model
     currCursorPosition = editor.getCursorBufferPosition().column
     if titleItem != undefined && @prevCursorPosition < currCursorPosition
-      @prevFilterQuery = titleItem.title
+      @skipPopulateList = true
       editor.setText(filterQuery + titleItem.title.slice(filterQuery.length))
       editor.selectLeft(titleItem.title.length - filterQuery.length)
     @prevCursorPosition = currCursorPosition
@@ -146,8 +146,6 @@
       @setError(@getEmptyMessage(@docQuery.documents.length, filteredItems.length))

   schedulePopulateList: ->
-    # We can skip it when we are just moving the position of the cursor.
-    currFilterQuery = @getFilterQuery()
-    if @prevFilterQuery != currFilterQuery
+    if !@skipPopulateList
       super
-    @prevFilterQuery = currFilterQuery
+    @skipPopulateList = false


fix pandoc parser math

fix pandoc parser math

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -1041,7 +1041,7 @@
         else
           tagStart = @mathInlineDelimiters[0][0]
           tagEnd = @mathInlineDelimiters[0][1]
-        mathElement.innerHTML = tagStart + mathElement.innerText.trim() + tagEnd
+        mathElement.innerHTML = tagStart + mathElement.innerText.trim().replace(/^\$\$/, '').replace(/\$\$$/, '') + tagEnd
         if displayMode and mathElement.nextElementSibling?.tagName == 'BR'
           mathElement.nextElementSibling.remove()

@@ -1228,7 +1228,7 @@
       else
         tagStart = @mathInlineDelimiters[0][0]
         tagEnd = @mathInlineDelimiters[0][1]
-      $math.html(tagStart + $math.text().trim() + tagEnd)
+      $math.html(tagStart + $math.text().trim().replace(/^\$\$/, '').replace(/\$\$$/, '') + tagEnd)

       if displayMode and $math.next()?[0]?.name == 'br'
         $math.next().remove()


graph -> Graph

graph -> Graph


Fix new coffeescript issues

Fix new coffeescript issues

--- client/ide/fs/fsitem.coffee
+++ client/ide/fs/fsitem.coffee
@@ -315,14 +315,11 @@
       kontrol = KD.getSingleton 'kontrol'
       kontrol.getKite \
         if vm?
-        then {
           name              : 'oskite'
           correlationName   : vm.hostnameAlias
           region            : vm.region
-        }
-        else {
+        else
           name              : 'oskite'
           correlationName   : vmName
-        }
     else
       KD.getSingleton('vmController').getKiteByVmName vmName


Switched fourth level group to the good-converting level instead of the bad one (oops).

--- app/models/User.coffee
+++ app/models/User.coffee
@@ -121,7 +121,7 @@
   # ... and stopped working well until another update on March 10, so maybe March 11+...
   # ... and another round, and then basically it just isn't completing well, so we pause the test until we can fix it.
   getFourthLevelGroup: ->
-    return 'signs-and-portents'
+    return 'forgetful-gemsmith'
     return @fourthLevelGroup if @fourthLevelGroup
     group = me.get('testGroupNumber') % 8
     @fourthLevelGroup = switch group


Remove console.log

Remove console.log

--- lib/actor/hactor.coffee
+++ lib/actor/hactor.coffee
@@ -564,7 +564,6 @@
     @hMessage {Object} hMessage to check
   ###
   validateFilter: (hMessage) ->
-    console.log "return : ",hFilter.checkFilterValidity(hMessage, @filter, {actor:@actor})
     return hFilter.checkFilterValidity(hMessage, @filter, {actor:@actor})

   ###*


Add REPL scoping tests

Add REPL scoping tests

--- test/repl.coffee
+++ test/repl.coffee
@@ -80,3 +80,14 @@
   input.emitLine '  1 + 1'
   input.emit 'keypress', null, ctrlV
   eq '2', output.lastWrite()
+
+testRepl "variables in scope are preserved", (input, output) ->
+  input.emitLine 'a = 1'
+  input.emitLine 'do -> a = 2'
+  input.emitLine 'a'
+  eq '2', output.lastWrite()
+
+testRepl "existencial assignment of previously declared variable", (input, output) ->
+  input.emitLine 'a = null'
+  input.emitLine 'a ?= 42'
+  eq '42', output.lastWrite()


added preview field to the package settings

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -35,6 +35,10 @@
       type: 'integer'
       default: 1
       minimum: 1
+    zzzpreview:
+      title: 'Preview'
+      description: 'This field does nothing, it\'s just here to preview your cursor'
+      type: 'string'

   activate: ->
     @subs = new CompositeDisposable()
@@ -43,6 +47,7 @@
     @subs.add atom.config.observe 'block-cursor.secondaryColor', @applySecondaryColor.bind @
     @subs.add atom.config.observe 'block-cursor.pulseDuration', @applyPulseDuration.bind @
     @subs.add atom.config.observe 'block-cursor.cursorThickness', @applyCursorThickness.bind @
+    atom.config.set 'block-cursor.zzzpreview', 'The quick brown fox jumps over the lazy dog'

   deactivate: ->
     @subs.dispose()


Corrected operator and typo

Corrected operator and typo

--- test/integration/connection-test.coffee
+++ test/integration/connection-test.coffee
@@ -1041,7 +1041,7 @@
 	request.on('row', (columns) ->
 		test.strictEqual(Object.keys(columns).length, 1)
 		# The current ARITHABORT default setting in Tedious is OFF
-		test.strictEqual(columns[0].value, if setting === true then 1 else 0)
+		test.strictEqual(columns[0].value, if setting is true then 1 else 0)
 	)

 	connection = new Connection(config)
@@ -1060,5 +1060,5 @@
 exports.testArithAbortOn = (test) ->
 	testArithAbort(test, true)

-exports.testArithAbortOf = (test) ->
+exports.testArithAbortOff = (test) ->
 	testArithAbort(test, false)


Add ._* to ignored file names

Add ._* to ignored file names

Macs generate ._ files when saving to a filesystem other then HFS
In shared environments this is a big problem

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -9,7 +9,7 @@
     properties:
       ignoredNames:
         type: 'array'
-        default: [".git", ".hg", ".svn", ".DS_Store", "Thumbs.db"]
+        default: [".git", ".hg", ".svn", ".DS_Store", "._*", "Thumbs.db"]
         items:
           type: 'string'
       excludeVcsIgnoredPaths:


Set autoHideMenuBar on properties object

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -189,7 +189,7 @@
         description: 'Increase/decrease the editor font size when pressing the Ctrl key and scrolling the mouse up/down.'

 if process.platform in ['win32', 'linux']
-  module.exports.core.autoHideMenuBar =
+  module.exports.core.properties.autoHideMenuBar =
     type: 'boolean'
     default: false
     description: 'Automatically hide the menu bar. This is only supported on Linux and Windows'


(fix) Fix pdf download functions.

(fix) Fix pdf download functions.

--- app/scripts/api.coffee
+++ app/scripts/api.coffee
@@ -28,6 +28,6 @@
   create:
     group: (members) -> post '/group', members
   urlOf:
-    correctedExercise: (id) -> "#{api.address}/correction/pdf/#{@id}"
-    submittedExercise: (id) -> "#{api.address}/solution/pdf/#{@id}"
+    correctedExercise: (id) -> "#{address}/correction/pdf/#{id}"
+    submittedExercise: (id) -> "#{address}/solution/pdf/#{id}"
   address: address


fix a error

fix a error

--- lib/build/android-build-view.coffee
+++ lib/build/android-build-view.coffee
@@ -6,6 +6,7 @@

 {getResourcePath} = require '../utils/utils'

+url = require 'url'

 KEYS = ['title', 'version', 'build', 'keystore', 'alias'
 'keypass', 'aliaspass', 'repository_url', 'scheme', 'content_src']


Try to fix #95

Try to fix #95

Apparently, ghc-mod returns empty string on `ghc-mod root`

--- lib/ghc-mod/ghc-modi-process-real.coffee
+++ lib/ghc-mod/ghc-modi-process-real.coffee
@@ -28,7 +28,7 @@
           Util.getRootDir buffer
         else
           [path] = res.stdout.split(EOL)
-          unless Util.isDirectory(path)
+          unless path and Util.isDirectory(path)
             warn "ghc-mod returned non-directory while getting project root dir"
             Util.getRootDir buffer
           else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -20,9 +20,6 @@
 test "text fades out", ->
   text = FloatingTextEffect()

-  text.update(1)
-  text.trigger "afterUpdate", 1
-
   equals text.I.alpha, 1

   text.update(0.25)


Fix default_callback wrong method names

Fix default_callback wrong method names

--- app/assets/javascripts/action_cable_notifications/default_callbacks.coffee
+++ app/assets/javascripts/action_cable_notifications/default_callbacks.coffee
@@ -10,7 +10,7 @@

   create: (packet, collection) ->
     fields = packet.data
-    collection.insert(fields)
+    collection.create(fields)

   update: (packet, collection) ->
     collection.update({id: packet.id}, packet.data)
@@ -24,7 +24,7 @@
       collection.upsert({id: fields.id}, fields)

   destroy: (packet, collection) ->
-    collection.remove({id: packet.id})
+    collection.destroy({id: packet.id})

   error: (packet, collection) ->
     console.error "[#{packet.cmd}]: #{packet.error}"


EDSC-189: Fix errors selecting spatial constraints

--- app/assets/javascripts/modules/map/leaflet-plugins/interpolation.js.coffee
+++ app/assets/javascripts/modules/map/leaflet-plugins/interpolation.js.coffee
@@ -101,7 +101,7 @@
   # Overrides the default projectLatLngs in Polyline and Polygon to project and interpolate the
   # path instead of just projecting it
   projectLatlngs = ->
-    interpolated = interpolate(@_map, @_latlngs, @_holes, @_interpolationFn)
+    interpolated = projectPath(@_map, @_latlngs, @_holes, @_interpolationFn)
     @_originalPoints = interpolated.boundary
     @_holePoints = interpolated.holes


Dragging of stereonet

Dragging of stereonet

--- attitude/display/assets/stereonet.coffee
+++ attitude/display/assets/stereonet.coffee
@@ -7,7 +7,8 @@
     @height = @width
     @center = [@width/2, @height/2]

-    @projection = d3.geo.azimuthalEquidistant()
+    #d3.geo.azimuthalEquidistant()
+    @projection = d3.geo.azimuthalEqualArea()
       .clipAngle 90-1e-3
       .scale 150
       .translate @center
@@ -16,6 +17,28 @@
     @path = d3.geo.path()
       .projection @projection

+    dragstarted = (d) ->
+      #stopPropagation prevents dragging to "bubble up" which triggers same event for all elements below this object
+      d3.event.sourceEvent.stopPropagation()
+      d3.select @
+        .classed 'dragging', true
+
+    dragged = =>
+      @projection.rotate [d3.event.x, -d3.event.y]
+      @svg.selectAll('path').attr d: @path
+
+    dragended = (d) ->
+      d3.select @
+        .classed 'dragging', false
+
+    @drag = d3.behavior.drag()
+      .origin =>
+        r = @projection.rotate()
+        {x: r[0], y: -r[1]}
+      .on 'drag', dragged
+      .on 'dragstart', dragstarted
+      .on 'dragend', dragended
+
     graticule = d3.geo.graticule()

     @svg = d3.select el[0][0]
@@ -23,6 +46,7 @@
         .attr "viewBox", "0,0,500,500"
         .attr "width", @width
         .attr "height", @height
+        .call @drag

     @svg.append "path"
       .datum graticule
@@ -57,7 +81,6 @@
       .datum data
       .attr
         class: "nominal #{newClass}"
-        #stroke: if main then 'rgb(255,0,0)' else 'rgb(100,100,100)'

   draw: =>
     @frame.selectAll 'path'


sorted results on client side

sorted results on client side

--- client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
+++ client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
@@ -141,7 +141,25 @@
       partial  : noItemFoundText or @getOptions().noItemFoundText or "There is no item found."
     @noItemFound.hide()

-  loadFeed:(filter = @selection)->
+  sortByKey: (arr, key) ->
+    key = key.split(".")
+    len = key.length
+    arr.sort (a, b) ->
+      i = 0
+      while i < len
+        a = a[key[i]]
+        b = b[key[i]]
+        i++
+      if a < b
+        1
+      else if a > b
+        -1
+      else
+        0
+
+    arr
+
+  loadFeed:(filter = @selection)=>

     options    = @getFeedOptions()
     selector   = @getFeedSelector()
@@ -157,6 +175,7 @@
           unless err
             if items.length is 0 and listController.getItemCount() is 0
               @noItemFound.show()
+            items = @sortByKey(items, filter.activeSort)
             listController.instantiateListItems items
             @emitCountChanged listController.itemsOrdered.length, filter.name
             if items.length is options.limit and listController.scrollView.getScrollHeight() <= listController.scrollView.getHeight()


new(save-action): add license property

new(save-action): add license property

--- src/Actions.coffee
+++ src/Actions.coffee
@@ -5,6 +5,7 @@
 GeoLocation = require "./GeoLocation"
 { EDIT }    = require "./constants/Form"
 { initialize, stopSubmit } = require "redux-form"
+LICENSE_NAME = "CC0-1.0"

 Actions =

@@ -121,6 +122,7 @@
   saveEntry: (e) ->
     saveFunc = if e?.id then WebAPI.saveEntry else WebAPI.saveNewEntry
     (dispatch, getState) ->
+      e.license ?= LICENSE_NAME
       saveFunc e, (err, res) ->
         if err
           dispatch stopSubmit EDIT.id, { _error: err }


Don't run commands on buffer without URI

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -203,6 +203,7 @@
         {name, typeSignature, symbolType, parent}

   getTypeInBuffer: (buffer, crange) =>
+    return Promise.resolve null unless buffer.getUri()?
     crange = Util.tabShiftForRange(buffer, crange)
     @queueCmd 'typeinfo',
       interactive: true
@@ -234,6 +235,7 @@
         throw new Error "No type"

   doCaseSplit: (buffer, crange) =>
+    return Promise.resolve [] unless buffer.getUri()?
     crange = Util.tabShiftForRange(buffer, crange)
     @queueCmd 'typeinfo',
       interactive: @caps?.interactiveCaseSplit ? false
@@ -261,6 +263,7 @@

   getInfoInBuffer: (editor, crange) =>
     buffer = editor.getBuffer()
+    return Promise.resolve null unless buffer.getUri()?
     {symbol, range} = Util.getSymbolInRange(editor, crange)

     @queueCmd 'typeinfo',
@@ -289,6 +292,7 @@

   doCheckOrLintBuffer: (cmd, buffer, fast) =>
     return Promise.resolve [] if buffer.isEmpty()
+    return Promise.resolve [] unless buffer.getUri()?

     # A dirty hack to make lint work with lhs
     olduri = uri = buffer.getUri()


Last bit of song timing functionality.

Last bit of song timing functionality.

--- javascripts/models/song.coffee
+++ javascripts/models/song.coffee
@@ -16,6 +16,28 @@
         @loadSheet sheet
         break
     delete @rawSheets
+
+  # Returns the beat at a time.
+  #
+  # @param [Number] time the time, expressed in seconds
+  # @return [Number] the beat number; can be negative and/or fractional
+  beatAtTime: (time) ->
+    # Binary search for the base beat.
+    low = 0
+    high = @lastBeat
+    while low <= high
+      middle = (low + high) >> 1
+      if time < @beatTime[middle]
+        high = middle - 1
+      else if time > @beatTime[middle]
+        low = middle + 1
+      else
+        break
+
+    # High will be -1 if the time is before the song's start.
+    high = 0 if high < 0
+
+    high + (time - @beatTime[high]) * @beatBpm[high] / 60.0

   # Processes the data pertaining to synchronizing notes to the audio file.
   loadSyncData: (data) ->
@@ -41,7 +63,7 @@
   # Processes @rawBpms and @rawPauses into @beatBpm, @beatTime, and @beatPause.
   computeBeatMaps: ->
     @rawBpms.sort()
-    @rawBpms.push({beat: @lastBeat, bpms: 1})
+    @rawBpms.push({beat: @lastBeat + 1, bpms: 1})
     @beatBpm = []
     beat0 = null
     bpm0 = 0
@@ -56,6 +78,7 @@
     @beatPause = {}
     time = 0
     pauseIndex = 0
+    console.log @rawPauses
     for beat in [0..@lastBeat]
       while @rawPauses[pauseIndex] && @rawPauses[pauseIndex].beat < beat
         pause = @rawPauses[pauseIndex]


added createSID with checking for ID collisions in the pool

--- modules/http/lib/sessions/Registry.coffee
+++ modules/http/lib/sessions/Registry.coffee
@@ -21,6 +21,12 @@
             @_pool[id]?.touch()
             @_pool[id]

+        ##
+        createSID: ()->
+            if @_pool[sid = floyd.tools.strings.uuid()]
+                return @createSID()
+
+            return sid

         ##
         _observe: ()->


new organic example: Matrix class (initialisation from List of Lists, transposition, multiplication, printing)

--- tests.coffee
+++ tests.coffee
@@ -4166,6 +4166,72 @@
   "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz "

   # ---------------------------------------------------------------------------
+  # Matrix class: initialisation from List of Lists, transposition,
+  # multiplication, printing.
+
+  """
+  Matrix = Class new
+  Matrix answer:
+  ﹍﹍initWith (array)
+  ﹍by:
+  ﹍﹍@array = array
+  ﹍﹍@rows = array length
+  ﹍﹍@columns = array[0] length
+
+  Matrix answer:
+  ﹍﹍print
+  ﹍by:
+  ﹍﹍console print "["
+  ﹍﹍for i from 0 to @rows - 1:
+  ﹍﹍﹍console print "["
+  ﹍﹍﹍for j from 0 to @columns - 1:
+  ﹍﹍﹍﹍console print @array[i][j]
+  ﹍﹍﹍﹍if j != @columns - 1:
+  ﹍﹍﹍﹍﹍console print ","
+  ﹍﹍﹍console print "]"
+  ﹍﹍﹍if i != @rows - 1:
+  ﹍﹍﹍﹍console print ","
+  ﹍﹍console print "]"
+
+  Matrix answer:
+  ﹍﹍transpose
+  ﹍by:
+  ﹍﹍transposed = []
+  ﹍﹍for i from 0 to @columns - 1:
+  ﹍﹍﹍transposed[i] = []
+  ﹍﹍﹍for j from 0 to @rows - 1:
+  ﹍﹍﹍﹍transposed[i][j] = @array[j][i]
+  ﹍﹍Matrix new initWith transposed
+
+  Matrix answer:
+  ﹍﹍multiply (other)
+  ﹍by:
+  ﹍﹍result = []
+  ﹍﹍for i from 0 to @rows - 1:
+  ﹍﹍﹍result[i] = []
+  ﹍﹍﹍for j from 0 to other.columns - 1:
+  ﹍﹍﹍﹍sum = 0
+  ﹍﹍﹍﹍for k from 0 to @columns - 1:
+  ﹍﹍﹍﹍﹍sum = sum + @array[i][k] * other.array[k][j]
+  ﹍﹍﹍﹍result[i][j] = sum
+  ﹍﹍Matrix new initWith result
+
+  myMatrix = Matrix new initWith [[1,2,3],[4,5,6],[7,8,9]]
+  myMatrix print
+  console print " "
+  myMatrix transpose print
+
+  console print " "
+  a = Matrix new initWith [[1,2],[3,4]]
+  b = Matrix new initWith [[-3,-8,3],[-2,1,4]]
+  (a multiply b) print
+
+  """
+
+  "[[1,2,3],[4,5,6],[7,8,9]] [[1,4,7],[2,5,8],[3,6,9]] [[-7,-6,11],[-17,-20,25]]"
+
+
+  # ---------------------------------------------------------------------------
   # a macro
   """
   a = '( (1...3) (console print x + y + z) x y z)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -46,7 +46,7 @@
   position method.

-  player = Core
+  player = GameObject
     x: 50
     y: 40


Corrent cursor handling

Corrent cursor handling

--- bokehjs/src/coffee/core/ui_events.coffee
+++ bokehjs/src/coffee/core/ui_events.coffee
@@ -130,13 +130,13 @@
         if view?
           if view.model.cursor?
             cursor = view.model.cursor()
-          if active_inspectors?
+          if active_inspectors
             # override event_type to cause inspectors to clear overlays
             event_type = "move:exit"

         # the event happened on the plot frame but off a renderer
         else if @_hit_test_frame(e.bokeh.sx, e.bokeh.sy)
-          if active_inspectors?
+          if active_inspectors
             cursor = "crosshair"

         @plot_view.set_cursor(cursor)


- [grunt] watch scripts folder for changes and run duo to compile scripts

--- tasks/grunt-contrib-watch.coffee
+++ tasks/grunt-contrib-watch.coffee
@@ -30,6 +30,9 @@
     nunjucksPartials:
       files: ['<%= path.source.layouts %>{,**/}_*.nj']
       tasks: ['nunjucks']
+    scripts:
+      files: ['<%= path.source.scripts %>/{,**}*.js']
+      tasks: ['shell:duo']
     scss:
       files: [
         '<%= path.source.styles %>/{,**/}*.scss'


added TODO for folder watching

added TODO for folder watching

--- src/builder.coffee
+++ src/builder.coffee
@@ -1,5 +1,6 @@
 # TODO: protect against source folder as out target during watch routine
 # TODO: add version checking if present in config?
+# TODO: add folder watching to pick up new files (wait for a better node.js implementation)

 fs = require 'fs'
 path = require 'path'


mixer to mixedice reference

mixer to mixedice reference

--- src/capsule.coffee
+++ src/capsule.coffee
@@ -1,8 +1,8 @@
-udefine 'capsule', ['jquery', 'mixer', 'eventmap'], ($, mixer, EventMap) ->
+udefine 'capsule', ['jquery', 'mixedice', 'eventmap'], ($, mixedice, EventMap) ->

   class Capsule
     constructor: (fn, elem) ->
-      mixer [@, Capsule::], new EventMap()
+      mixedice [@, Capsule::], new EventMap()

       [fn, elem] = [null, fn] if fn instanceof $


Utils: add isFake field to fake Social Message.

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -741,6 +741,7 @@
     isoNow    = now.toISOString()

     fakeObject         =
+      isFake           : yes
       on               : -> this
       watch            : -> this
       body             : body


Tidied up a warning message

Tidied up a warning message

--- lib/atom-slime.coffee
+++ lib/atom-slime.coffee
@@ -73,7 +73,7 @@

   tryToConnect: (i) ->
     if i > @maxConnectionAttempts
-      atom.notifications.addWarning("Couldn't connect to Lisp", detail:"Did you set the Slime location in settings?")
+      atom.notifications.addWarning("Couldn't connect to Lisp!", detail:"Did you start a Lisp swank server?")
       return false
     promise = @swank.connect()
     promise.then (=> @swankConnected()), ( => setTimeout ( => @tryToConnect(i + 1)), 200)


Made a typo converting BBOX to string on the search area button

--- src/client/search-app.coffee
+++ src/client/search-app.coffee
@@ -116,7 +116,7 @@

     # Adjust UI button
     bbox = bounds.toBBOX(4).split ','
-    text = "N: #{bbox[3]}, S: #{bbox[1]}, E: #{bbox[0]}, W: #{bbox[2]}"
+    text = "N: #{bbox[3]}, S: #{bbox[1]}, E: #{bbox[2]}, W: #{bbox[0]}"
     $('#define-map-area span').html "Defined Area: #{text}"
     $('#define-map-area').toggleClass 'action-button-highlight' if changeHighlight
     $('#define-map-area div').addClass 'hidden'


Reset the X position to the starting X after line wrapping is done (unless continued)

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -8,7 +8,7 @@
         @wordSpacing = options.wordSpacing is 0
         @columns     = options.columns or 1
         @columnGap   = options.columnGap ? 18 # 1/4 inch
-        @lineWidth   =  (options.width - (@columnGap * (@columns - 1))) / @columns
+        @lineWidth   = (options.width - (@columnGap * (@columns - 1))) / @columns
         @startX      = @document.x
         @startY      = @document.y
         @column      = 1
@@ -157,6 +157,8 @@
         if options.continued is yes
             @continuedX = textWidth
             @document.y = y
+        else
+            @document.x = @startX

     nextSection: (options) ->
         @emit 'sectionEnd', options, this


Remove unused methods from Fold

Remove unused methods from Fold

--- src/app/fold.coffee
+++ src/app/fold.coffee
@@ -14,9 +14,6 @@

   destroy: ->
     @renderer.destroyFold(this)
-
-  getRange: ->
-    throw "Don't worry about this yet -- sobo"

   getBufferDelta: ->
     new Point(@endRow - @startRow + 1, 0)
@@ -44,12 +41,4 @@
     return row if row < oldRange.start.row

     deltaFromOldRangeEndRow = row - oldRange.end.row
-    newRange.end.row + deltaFromOldRangeEndRow
-
-  compare: (other) ->
-    other
-    # startComparison = @start.compare(other.start)
-    # if startComparison == 0
-    #   other.end.compare(@end)
-    # else
-    #   startComparison
+    newRange.end.row + deltaFromOldRangeEndRow