JS: Narrow the scope of some of the stubbing for UI tests

--- tests_javascript/test_ui.coffee
+++ tests_javascript/test_ui.coffee
@@ -43,54 +43,54 @@
             'es'

     test 'Should render a flash', () ->
-        @sandbox.server.respondWith [
+        @sandbox.server.respondWith '/flash-test1', [
             200
             {'Content-Type': 'application/json'}
             JSON.stringify {flashed_messages: [
                 {message: 'Testing1', category: 'warning'}
             ]}
         ]
-        jQuery.get '/'
-        alerts = @flashesFixture.find '.alert'
+        jQuery.get '/flash-test1'
+        alerts = @fixture.find '.alert'
         assert.strictEqual alerts.length, 1
         assert.ok (alerts[0].innerText.indexOf 'Testing1') != -1
         @sandbox.clock.tick 2500
-        @sandbox.server.respondWith [
+        @sandbox.server.respondWith '/flash-test2', [
             200
             {'Content-Type': 'application/json'}
             JSON.stringify {flashed_messages: [
                 {message: 'Testing2', category: 'message'}
             ]}
         ]
-        jQuery.get '/'
-        alerts = @flashesFixture.find '.alert'
+        jQuery.get '/flash-test2'
+        alerts = @fixture.find '.alert'
         assert.strictEqual alerts.length, 2
         @sandbox.clock.tick 3000
-        alerts = @flashesFixture.find '.alert'
+        alerts = @fixture.find '.alert'
         assert.strictEqual alerts.length, 1
         @sandbox.clock.tick 3000
-        alerts = @flashesFixture.find '.alert'
+        alerts = @fixture.find '.alert'
         assert.strictEqual alerts.length, 0

     test 'Should update item counts in the navbar', () ->
-        @sandbox.server.respondWith [
+        @sandbox.server.respondWith '/navbar-test1', [
             200
             {'Content-Type': 'application/json'}
             JSON.stringify {nav_counts: {pending: 2, payouts: 4, personal: 6}}
         ]
-        jQuery.get '/'
+        jQuery.get '/navbar-test1'
         assert.strictEqual (@fixture.find '#badge-pending').text(), '2'
         assert.strictEqual (@fixture.find '#badge-payouts').text(), '4'
         assert.strictEqual (@fixture.find '#badge-personal').text(), '6'

     test 'Should clear item counts in navbar for 0', () ->
-        @sandbox.server.respondWith [
+        @sandbox.server.respondWith '/navbar-test2', [
             200
             {'Content-Type': 'application/json'}
             JSON.stringify {nav_counts: {pending: 2, payouts: 0, personal: 6}}
         ]
-        jQuery.get '/'
+        jQuery.get '/navbar-test2'
         assert.strictEqual (@fixture.find '#badge-payouts').text(), ''

     suite 'Localization', () ->


:lipstick:

:lipstick:

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -1467,7 +1467,7 @@
           expect(buffer.lineForRow(1)).toBe '  var qif (items.length <= 1) return items;'
           expect(editor.getCursorScreenPosition()).toEqual [1, 7]

-    describe "when return is pressed", ->
+    describe "return", ->
       describe "when the cursor is at the beginning of a line", ->
         it "inserts an empty line before it", ->
           editor.setCursorScreenPosition(row: 1, column: 0)
@@ -1499,7 +1499,7 @@
           expect(editor.lines.find('.line:eq(2)')).toHaveHtml ' '
           expect(editor.getCursorScreenPosition()).toEqual(row: 2, column: 0)

-    describe "when backspace is pressed", ->
+    describe "backspace", ->
       describe "when the cursor is on the middle of the line", ->
         it "removes the character before the cursor", ->
           editor.setCursorScreenPosition(row: 1, column: 7)
@@ -1565,7 +1565,7 @@
           editor.trigger 'backspace-to-beginning-of-word'
           expect(buffer.lineForRow(1)).toBe '  var sort = function(it) {'

-    describe "when delete is pressed", ->
+    describe "delete", ->
       describe "when the cursor is on the middle of a line", ->
         it "deletes the character following the cursor", ->
           editor.setCursorScreenPosition([1, 6])
@@ -1589,6 +1589,12 @@
           editor.setCursorScreenPosition([12, buffer.lineForRow(12).length])
           editor.trigger keydownEvent('delete')
           expect(buffer.lineForRow(12)).toBe '};'
+
+      describe "when multiple lines are removed from the buffer (regression)", ->
+        it "removes all of them from the dom", ->
+          buffer.change(new Range([6, 24], [12, 0]), '')
+          expect(editor.find('.line').length).toBe 7
+          expect(editor.find('.line:eq(6)').text()).toBe(buffer.lineForRow(6))

     describe "delete-to-end-of-word", ->
       describe "when no text is selected", ->
@@ -1615,7 +1621,7 @@
           editor.trigger 'delete-to-end-of-word'
           expect(buffer.lineForRow(1)).toBe '  var sort = function(it) {'

-    describe "when undo/redo events are triggered on the editor", ->
+    describe "undo/redo", ->
       it "undoes/redoes the last change", ->
         buffer.insert [0, 0], "foo"
         editor.trigger 'undo'
@@ -1679,12 +1685,6 @@

         expect(selections[0].getBufferRange()).toEqual [[1, 12], [1, 12]]
         expect(selections[1].getBufferRange()).toEqual [[1, 30], [1, 30]]
-
-    describe "when multiple lines are removed from the buffer (regression)", ->
-      it "removes all of them from the dom", ->
-        buffer.change(new Range([6, 24], [12, 0]), '')
-        expect(editor.find('.line').length).toBe 7
-        expect(editor.find('.line:eq(6)').text()).toBe(buffer.lineForRow(6))

   describe "when the editor is attached to the dom", ->
     it "calculates line height and char width and updates the pixel position of the cursor", ->


CredentialsController: add shared credential to credential list

--- client/app/lib/providers/resourcestatemodal/controllers/credentialscontroller.coffee
+++ client/app/lib/providers/resourcestatemodal/controllers/credentialscontroller.coffee
@@ -225,7 +225,22 @@
         break  if provider in ['aws', 'vagrant']
       provider ?= (Object.keys stack.credentials ? { aws : yes }).first

-      helpers._loadCredentials { provider }, stack.credentials, callback
+      helpers._loadCredentials { provider }, stack.credentials, (err, result) ->
+        return callback err  if err
+
+        { items, selectedItem } = result
+        return callback null, result  unless selectedItem
+
+        isAvailable = items.filter((item) -> item.identifier is selectedItem).length > 0
+        return callback null, result  if isAvailable
+
+        # try to add stack credential to the list of credentials
+        # if it's not there but it's shared with the team
+        remote.api.JCredential.one selectedItem, (err, credential) ->
+          return callback err, result  if err or not credential
+
+          result.items.unshift credential
+          callback null, result

     loadRequirements: (stack, callback) ->


Make the status bar global.

Make the status bar global.

--- spec/status-bar-spec.coffee
+++ spec/status-bar-spec.coffee
@@ -2,6 +2,7 @@
 StatusBar = require '../lib/status-bar'
 path = require 'path'
 os = require 'os'
+StatusBarView = require '../lib/status-bar-view'

 describe "StatusBar", ->
   [editor, statusBar, buffer] = []


The config value is automatically restored on each spec run.

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -53,13 +53,8 @@
     expect(editor.getText()).toBe "don't trim me \n"

   describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is true", ->
-    [originalConfigValue] = []
     beforeEach ->
-      originalConfigValue = atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")
-      expect(originalConfigValue).toBe true
-
-    afterEach ->
-      atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", originalConfigValue)
+      atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", true)

     it "removes the whitespace from all lines, excluding the current lines", ->
       editor.insertText "1  \n2  \n3  \n"
@@ -69,14 +64,8 @@
       expect(editor.getText()).toBe "1\n2  \n3  \n"

   describe "when 'whitespace.ignoreWhitespaceOnCurrentLine' is false", ->
-    [originalConfigValue] = []
     beforeEach ->
-      originalConfigValue = atom.config.get("whitespace.ignoreWhitespaceOnCurrentLine")
-      expect(originalConfigValue).toBe true
       atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", false)
-
-    afterEach ->
-      atom.config.set("whitespace.ignoreWhitespaceOnCurrentLine", originalConfigValue)

     it "removes the whitespace from all lines, including the current lines", ->
       editor.insertText "1  \n2  \n3  \n"


remove coffee

remove coffee

--- app/initialize.coffee
+++ app/initialize.coffee
@@ -1 +0,0 @@
-$ ->


Fix filename case

Fix filename case

--- test/Client_test.coffee
+++ test/Client_test.coffee
@@ -1,4 +1,4 @@
-Client = require("../src/client")
+Client = require("../src/Client")
 expect = require("chai").expect
 sinon = require("sinon")


Updates ImageParser tests

Updates ImageParser tests

--- test/ImageParser.coffee
+++ test/ImageParser.coffee
@@ -36,12 +36,15 @@

     parser.parse(path.join(testPath, 'testImage.jpg'), node)
     .then ->
+      # Expect an image property with the testImage key and exif data
       expect(node.hasProperty('images')).to.be.true
       imageProperty = node.getProperty 'images'

       image = imageProperty.testImage
-      expect(image.exifData).to.not.be.null
+      expect(image).not.to.be.null
+      expect(image.exifData).not.to.be.null

+      # Expect Actual data when requesting it
       image.getImageData()
       .then (imageData) ->
         expect(imageData.length).to.eql(9371)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -24,7 +24,8 @@
     frequency: 5
     alpha: 0.2

-  5.
+  5.times (n) ->
+    if

   player.update()
   equals player.I.alpha, 0.2


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -12,7 +12,8 @@
   equals player.I.flickerDuration, 30

 test 'it should be able to set one param at a time', ->
-  player = GameObje
+  player = GameObject()
+  player.include F

 test 'it should change the alpha value when the flicker duration has elapsed', ->
   player = GameObject


shy: test improvement

shy: test improvement

--- test/promise.shy.coffee
+++ test/promise.shy.coffee
@@ -55,10 +55,10 @@
       content: 'abc'
       shy: true
     ], (err, status) ->
-      status.should.be.true()
+      status.should.be.true() unless err
     .then (err, status) ->
-      status.should.be.true()
-      next()
+      status.should.be.true() unless err
+      next err


no need to update an answer with same answer

--- src/components/task-step/exercise/modes.cjsx
+++ src/components/task-step/exercise/modes.cjsx
@@ -171,6 +171,7 @@
     answerId: answer_id

   onAnswerChanged: (answer) ->
+    return if answer.id is @state.answerId
     @setState {answerId: answer.id}
     @props.onAnswerChanged?(answer)


consistent formatting

consistent formatting

--- test/test_helper.coffee
+++ test/test_helper.coffee
@@ -54,7 +54,6 @@
     return element

 class TableGUI
-
   constructor: (@element) ->
     @reload()

@@ -68,7 +67,6 @@

 class PaginationGUI
-
   constructor: (@element) ->
     @reload()


Reformatted unit tests

Reformatted unit tests

--- test/xml2js.test.coffee
+++ test/xml2js.test.coffee
@@ -55,16 +55,16 @@
     assert.equal r['listtest']['item'][2]['#'], 'Quux.')

   'test default text handling': skeleton(undefined, (r) ->
-      assert.equal r['whitespacetest']['#'], 'Line One Line Two')
+    assert.equal r['whitespacetest']['#'], 'Line One Line Two')

   'test disable trimming': skeleton({trim: false}, (r) ->
-      assert.equal r['whitespacetest']['#'], 'Line One Line Two')
+    assert.equal r['whitespacetest']['#'], 'Line One Line Two')

   'test disable normalize': skeleton({normalize: false}, (r) ->
-      assert.equal r['whitespacetest']['#'], 'Line One\n        Line Two')
+    assert.equal r['whitespacetest']['#'], 'Line One\n        Line Two')

   'test disable normalize and trim': skeleton({normalize: false, trim: false}, (r) ->
-      assert.equal r['whitespacetest']['#'], '\n        Line One\n        Line Two\n    ')
+    assert.equal r['whitespacetest']['#'], '\n        Line One\n        Line Two\n    ')

   'test root node eliminiation': skeleton({__xmlString: '<root></root>'}, (r) ->
     assert.deepEqual r, {})


Fixed: preconditions weren't checked anymore

--- tests/functional.coffee
+++ tests/functional.coffee
@@ -7,9 +7,9 @@

 {syncPath} = helpers.options

-describe.only "Functional Tests", ->
-
-    #before helpers.ensurePreConditions
+describe "Functional Tests", ->
+
+    before helpers.ensurePreConditions

     # Prepares the local system
     before filesHelpers.deleteAll


tests for PivotData

tests for PivotData

--- tests/pivot_spec.coffee
+++ tests/pivot_spec.coffee
@@ -6,233 +6,325 @@
     ["Carol",   "female",   "yellow",    "1983-12-08",   102,        14]
 ]

-describe "$.pivot() with no rows/cols, default count aggregator, default TableRenderer",  ->
-    table = $("
").pivot fixtureData
-
-    it "renders a table", ->
-        expect table.find("table.pvtTable").length
-        .toBe  1
-
-    describe "its renderer output", ->
-
-        it "has the correct textual representation", ->
-            expect table.find("table.pvtTable").text()
-            .toBe ["Totals", "4"].join("")
-
-        it "has a correct grand total with data value", ->
-            expect table.find("td.pvtGrandTotal").text()
-            .toBe  "4"
-            expect table.find("td.pvtGrandTotal").data("value")
+
+describe "$.pivotUI()", ->
+    describe "with no rows/cols, default count aggregator, default TableRenderer",  ->
+        table = null
+
+        beforeEach (done) ->
+            table = $("
").pivotUI fixtureData, onRefresh: done
+        it "has all the basic UI elements", (done) ->
+            expect table.find("td.pvtAxisContainer").length
+            .toBe  3
+            expect table.find("td.pvtRendererArea").length
+            .toBe  1
+            expect table.find("td.pvtVals").length
+            .toBe  1
+            expect table.find("select.pvtRenderer").length
+            .toBe  1
+            expect table.find("select.pvtAggregator").length
+            .toBe  1
+            expect table.find("span.pvtAttr").length
+            .toBe  6
+            done()
+
+        it "reflects its inputs", (done) ->
+            expect table.find("td.pvtUnused span.pvtAttr").length
+            .toBe  6
+            expect table.find("select.pvtRenderer").val()
+            .toBe  "Table"
+            expect table.find("select.pvtAggregator").val()
+            .toBe  "Count"
+            done()
+
+        it "renders a table", (done) ->
+            expect table.find("table.pvtTable").length
+            .toBe  1
+            done()
+
+
+        describe "its renderer output", ->
+            it "has the correct type and number of cells", (done) ->
+                expect table.find("th.pvtTotalLabel").length
+                .toBe  1
+                expect table.find("td.pvtGrandTotal").length
+                .toBe  1
+                done()
+
+            it "has the correct textual representation", (done) ->
+                expect table.find("table.pvtTable").text()
+                .toBe ["Totals", "4"].join("")
+                done()
+
+            it "has a correct grand total with data value", (done) ->
+                expect table.find("td.pvtGrandTotal").text()
+                .toBe  "4"
+                expect table.find("td.pvtGrandTotal").data("value")
+                .toBe  4
+                done()
+
+    describe "with rows/cols, sum-over-sum aggregator, Heatmap renderer",  ->
+        table = null
+
+        beforeEach (done) ->
+            table = $("
").pivotUI fixtureData,
+                rows: ["gender"], cols: ["colour"]
+                aggregatorName: "Sum over Sum"
+                vals: ["successes", "trials"]
+                rendererName: "Heatmap"
+                onRefresh: done
+
+        it "has all the basic UI elements", (done) ->
+            expect table.find("td.pvtAxisContainer").length
+            .toBe  3
+            expect table.find("td.pvtRendererArea").length
+            .toBe  1
+            expect table.find("td.pvtVals").length
+            .toBe  1
+            expect table.find("select.pvtRenderer").length
+            .toBe  1
+            expect table.find("select.pvtAggregator").length
+            .toBe  1
+            expect table.find("span.pvtAttr").length
+            .toBe  6
+            done()
+
+        it "reflects its inputs", (done) ->
+            expect table.find("td.pvtUnused span.pvtAttr").length
             .toBe  4
-
-describe "$.pivotUI() with no rows/cols, default count aggregator, default TableRenderer",  ->
-    table = null
-
-    beforeEach (done) ->
-        table = $("
").pivotUI fixtureData, onRefresh: done
-    it "has all the basic UI elements", (done) ->
-        expect table.find("td.pvtAxisContainer").length
-        .toBe  3
-        expect table.find("td.pvtRendererArea").length
-        .toBe  1
-        expect table.find("td.pvtVals").length
-        .toBe  1
-        expect table.find("select.pvtRenderer").length
-        .toBe  1
-        expect table.find("select.pvtAggregator").length
-        .toBe  1
-        expect table.find("span.pvtAttr").length
-        .toBe  6
-        done()
-
-    it "reflects its inputs", (done) ->
-        expect table.find("td.pvtUnused span.pvtAttr").length
-        .toBe  6
-        expect table.find("select.pvtRenderer").val()
-        .toBe  "Table"
-        expect table.find("select.pvtAggregator").val()
-        .toBe  "Count"
-        done()
-
-    it "renders a table", (done) ->
-        expect table.find("table.pvtTable").length
-        .toBe  1
-        done()
-
-
-    describe "its renderer output", ->
-        it "has the correct type and number of cells", (done) ->
-            expect table.find("th.pvtTotalLabel").length
+            expect table.find("td.pvtRows span.pvtAttr").length
+            .toBe  1
+            expect table.find("td.pvtCols span.pvtAttr").length
+            .toBe  1
+            expect table.find("select.pvtRenderer").val()
+            .toBe  "Heatmap"
+            expect table.find("select.pvtAggregator").val()
+            .toBe  "Sum over Sum"
+            done()
+
+        it "renders a table", (done) ->
+            expect table.find("table.pvtTable").length
             .toBe  1
-            expect table.find("td.pvtGrandTotal").length
+            done()
+
+        describe "its renderer output", ->
+            it "has the correct type and number of cells", (done) ->
+                expect table.find("th.pvtAxisLabel").length
+                .toBe  2
+                expect table.find("th.pvtRowLabel").length
+                .toBe  2
+                expect table.find("th.pvtColLabel").length
+                .toBe  3
+                expect table.find("th.pvtTotalLabel").length
+                .toBe  2
+                expect table.find("td.pvtVal").length
+                .toBe  6
+                expect table.find("td.pvtTotal").length
+                .toBe  5
+                expect table.find("td.pvtGrandTotal").length
+                .toBe  1
+                done()
+
+            it "has the correct textual representation", (done) ->
+                expect table.find("table.pvtTable").text()
+                .toBe [
+                    "colour",   "blue", "red",  "yellow",   "Totals",
+                    "gender",
+                    "female",           "0.26", "0.14",     "0.20",
+                    "male",     "0.20",                     "0.20",
+                    "Totals",   "0.20", "0.26", "0.14",     "0.20"
+                    ].join("")
+                done()
+
+            it "has a correct spot-checked cell with data value", (done) ->
+                expect table.find("td.col0.row1").text()
+                .toBe  "0.20"
+                expect table.find("td.col0.row1").data("value")
+                .toBe  (12+30)/(103+112)
+                done()
+
+describe "$.pivot()", ->
+
+    describe "with no rows/cols, default count aggregator, default TableRenderer",  ->
+        table = $("
").pivot fixtureData
+
+        it "renders a table", ->
+            expect table.find("table.pvtTable").length
             .toBe  1
-            done()
-
-        it "has the correct textual representation", (done) ->
-            expect table.find("table.pvtTable").text()
-            .toBe ["Totals", "4"].join("")
-            done()
-
-        it "has a correct grand total with data value", (done) ->
-            expect table.find("td.pvtGrandTotal").text()
-            .toBe  "4"
-            expect table.find("td.pvtGrandTotal").data("value")
-            .toBe  4
-            done()
-
-describe "$.pivotUI() with specified rows/cols, sum-over-sum aggregator, Heatmap renderer",  ->
-    table = null
-
-    beforeEach (done) ->
-        table = $("
").pivotUI fixtureData,
-            rows: ["gender"], cols: ["colour"]
-            aggregatorName: "Sum over Sum"
-            vals: ["successes", "trials"]
-            rendererName: "Heatmap"
-            onRefresh: done
-
-    it "has all the basic UI elements", (done) ->
-        expect table.find("td.pvtAxisContainer").length
-        .toBe  3
-        expect table.find("td.pvtRendererArea").length
-        .toBe  1
-        expect table.find("td.pvtVals").length
-        .toBe  1
-        expect table.find("select.pvtRenderer").length
-        .toBe  1
-        expect table.find("select.pvtAggregator").length
-        .toBe  1
-        expect table.find("span.pvtAttr").length
-        .toBe  6
-        done()
-
-    it "reflects its inputs", (done) ->
-        expect table.find("td.pvtUnused span.pvtAttr").length
-        .toBe  4
-        expect table.find("td.pvtRows span.pvtAttr").length
-        .toBe  1
-        expect table.find("td.pvtCols span.pvtAttr").length
-        .toBe  1
-        expect table.find("select.pvtRenderer").val()
-        .toBe  "Heatmap"
-        expect table.find("select.pvtAggregator").val()
-        .toBe  "Sum over Sum"
-        done()
-
-    it "renders a table", (done) ->
-        expect table.find("table.pvtTable").length
-        .toBe  1
-        done()
-
-    describe "its renderer output", ->
-        it "has the correct type and number of cells", (done) ->
-            expect table.find("th.pvtAxisLabel").length
-            .toBe  2
-            expect table.find("th.pvtRowLabel").length
-            .toBe  2
-            expect table.find("th.pvtColLabel").length
-            .toBe  3
-            expect table.find("th.pvtTotalLabel").length
-            .toBe  2
-            expect table.find("td.pvtVal").length
-            .toBe  6
-            expect table.find("td.pvtTotal").length
-            .toBe  5
-            expect table.find("td.pvtGrandTotal").length
-            .toBe  1
-            done()
-
-        it "has the correct textual representation", (done) ->
+
+        describe "its renderer output", ->
+
+            it "has the correct textual representation", ->
+                expect table.find("table.pvtTable").text()
+                .toBe ["Totals", "4"].join("")
+
+            it "has a correct grand total with data value", ->
+                expect table.find("td.pvtGrandTotal").text()
+                .toBe  "4"
+                expect table.find("td.pvtGrandTotal").data("value")
+                .toBe  4
+
+    describe "with rows/cols, sum aggregator, derivedAttributes, filter and sorters",  ->
+        {sortAs, derivers, aggregators} = $.pivotUtilities
+        table = $("
").pivot fixtureData,
+            rows: ["gender"], cols: ["birthyear"], aggregator: aggregators["Sum"](["trialbins"])
+            filter: (record) -> record.name != "Nick"
+            derivedAttributes:
+                birthyear: derivers.dateFormat "birthday", "%y"
+                trialbins: derivers.bin "trials", 10
+            sorters: (attr) ->
+                if attr == "gender" then return sortAs(["male", "female"])
+
+        it "renders a table with the correct textual representation", ->
             expect table.find("table.pvtTable").text()
             .toBe [
-                "colour",   "blue", "red",  "yellow",   "Totals",
-                "gender",
-                "female",           "0.26", "0.14",     "0.20",
-                "male",     "0.20",                     "0.20",
-                "Totals",   "0.20", "0.26", "0.14",     "0.20"
+                "birthyear",    "1982",     "1983",     "Totals"
+                "gender",
+                "male",         "110.00",               "110.00"
+                "female",       "90.00",    "100.00",   "190.00"
+                "Totals",       "200.00",   "100.00",   "300.00"
                 ].join("")
-            done()
-
-        it "has a correct spot-checked cell with data value", (done) ->
-            expect table.find("td.col0.row1").text()
-            .toBe  "0.20"
-            expect table.find("td.col0.row1").data("value")
-            .toBe  (12+30)/(103+112)
-            done()
-
-describe "$.pivot() with rows/cols, sum aggregator, derivedAttributes, filter and sorters",  ->
-    {sortAs, derivers, aggregators} = $.pivotUtilities
-    table = $("
").pivot fixtureData,
-        rows: ["gender"], cols: ["birthyear"], aggregator: aggregators["Sum"](["trialbins"])
-        filter: (record) -> record.name != "Nick"
-        derivedAttributes:
-            birthyear: derivers.dateFormat "birthday", "%y"
-            trialbins: derivers.bin "trials", 10
-        sorters: (attr) ->
-            if attr == "gender" then return sortAs(["male", "female"])
-
-    it "renders a table with the correct textual representation", ->
-        expect table.find("table.pvtTable").text()
-        .toBe [
-            "birthyear",    "1982",     "1983",     "Totals"
-            "gender",
-            "male",         "110.00",               "110.00"
-            "female",       "90.00",    "100.00",   "190.00"
-            "Totals",       "200.00",   "100.00",   "300.00"
-            ].join("")
-
-describe "$.pivot() with rows/cols, fraction-of aggregator",  ->
-    {aggregators} = $.pivotUtilities
-    table = $("
").pivot fixtureData,
-        rows: ["gender"]
-        aggregator: aggregators["Sum as Fraction of Total"](["trials"])
-
-    it "renders a table with the correct textual representation", ->
-        expect table.find("table.pvtTable").text()
-        .toBe [
-            "gender",  "Totals"
-            "female",  "47.8%"
-            "male",    "52.2%"
-            "Totals",  "100.0%"
-            ].join("")
-
-describe "$.pivot() with rows/cols, custom aggregator, custom renderer with options",  ->
-    received_PivotData = null
-    received_rendererOptions = null
-
-    table = $("
").pivot fixtureData,
-        rows: ["name", "colour"], cols: ["trials", "successes"]
-        aggregator: ->
-            count2x: 0
-            push: -> @count2x +=2
-            value: -> @count2x
-            format: (x) -> "formatted " + x
-            label: "Count 2x"
-        renderer: (a,b) ->
-            received_PivotData = a
-            received_rendererOptions = b
-            return $("
").addClass(b.greeting).text("world")
-        rendererOptions: {greeting:"hithere"}
-
-    it "renders the custom renderer as per options", ->
-        expect table.find("div.hithere").length
-        .toBe  1
-
-    describe "its received PivotData object", ->
-        it "has correctly-ordered row and column keys", ->
-            expect received_PivotData.getRowKeys()
-            .toEqual [ [ 'Carol', 'yellow' ], [ 'Jane', 'red' ], [ 'John', 'blue' ], [ 'Nick', 'blue' ] ]
-            expect received_PivotData.getColKeys()
-            .toEqual [ [ 95, 25 ], [ 102, 14 ], [ 103, 12 ], [ 112, 30 ] ]
-
-        it "has a correct grand total value and format for custom aggregator", ->
-            agg = received_PivotData.getAggregator([],[])
-            val = agg.value()
-            expect(agg.label).toBe "Count 2x"
-            expect(val).toBe 8
-            expect(agg.format(val)).toBe "formatted 8"
+
+    describe "with rows/cols, fraction-of aggregator",  ->
+        {aggregators} = $.pivotUtilities
+        table = $("
").pivot fixtureData,
+            rows: ["gender"]
+            aggregator: aggregators["Sum as Fraction of Total"](["trials"])
+
+        it "renders a table with the correct textual representation", ->
+            expect table.find("table.pvtTable").text()
+            .toBe [
+                "gender",  "Totals"
+                "female",  "47.8%"
+                "male",    "52.2%"
+                "Totals",  "100.0%"
+                ].join("")
+
+    describe "with rows/cols, custom aggregator, custom renderer with options",  ->
+        received_PivotData = null
+        received_rendererOptions = null
+
+        table = $("
").pivot fixtureData,
+            rows: ["name", "colour"], cols: ["trials", "successes"]
+            aggregator: ->
+                count2x: 0
+                push: -> @count2x +=2
+                value: -> @count2x
+                format: (x) -> "formatted " + x
+            renderer: (a,b) ->
+                received_PivotData = a
+                received_rendererOptions = b
+                return $("
").addClass(b.greeting).text("world")
+            rendererOptions: {greeting:"hithere"}
+
+        it "renders the custom renderer as per options", ->
+            expect table.find("div.hithere").length
+            .toBe  1
+
+        describe "its received PivotData object", ->
+            it "has a correct grand total value and format for custom aggregator", ->
+                agg = received_PivotData.getAggregator([],[])
+                val = agg.value()
+                expect(val).toBe 8
+                expect(agg.format(val)).toBe "formatted 8"

 describe "$.pivotUtilities", ->
+
+    describe ".PivotData()", ->
+        sumOverSumOpts =
+            rows: [], cols: []
+            aggregator: $.pivotUtilities.aggregators["Sum over Sum"](["a","b"])
+            filter: -> true
+            sorters: ->
+
+        describe "with array-of-array input", ->
+            aoaInput =  [ ["a","b"], [1,2], [3,4] ]
+            pd = new $.pivotUtilities.PivotData aoaInput, sumOverSumOpts
+
+            it "has the correct grand total value", ->
+                expect pd.getAggregator([],[]).value()
+                .toBe (1+3)/(2+4)
+
+        describe "with array-of-object input", ->
+            aosInput =  [ {a:1, b:2}, {a:3, b:4} ]
+            pd = new $.pivotUtilities.PivotData aosInput, sumOverSumOpts
+
+            it "has the correct grand total value", ->
+                expect pd.getAggregator([],[]).value()
+                .toBe (1+3)/(2+4)
+
+        describe "with function input", ->
+            functionInput = (record) ->
+                record a:1, b:2
+                record a:3, b:4
+            pd = new $.pivotUtilities.PivotData functionInput, sumOverSumOpts
+
+            it "has the correct grand total value", ->
+                expect pd.getAggregator([],[]).value()
+                .toBe (1+3)/(2+4)
+
+        describe "with jQuery table element input", ->
+            tableInput = $ """
+

+

+
 a	b

+

+

+
 1	 2

+
 3	 4

+

+

+                """
+
+            pd = new $.pivotUtilities.PivotData tableInput, sumOverSumOpts
+
+            it "has the correct grand total value", ->
+                expect pd.getAggregator([],[]).value()
+                .toBe (1+3)/(2+4)
+
+
+        describe "with rows/cols, no filters/sorters, count aggregator", ->
+            pd = new $.pivotUtilities.PivotData fixtureData,
+                rows: ["name", "colour"],
+                cols: ["trials", "successes"],
+                aggregator: $.pivotUtilities.aggregators["Count"](),
+                filter: -> true
+                sorters: ->
+
+            it "has correctly-ordered row keys", ->
+                expect pd.getRowKeys()
+                .toEqual [ [ 'Carol', 'yellow' ], [ 'Jane', 'red' ], [ 'John', 'blue' ], [ 'Nick', 'blue' ] ]
+
+            it "has correctly-ordered col keys", ->
+                expect pd.getColKeys()
+                .toEqual [ [ 95, 25 ], [ 102, 14 ], [ 103, 12 ], [ 112, 30 ] ]
+
+            it "can be iterated over", ->
+                numNotNull = 0
+                numNull = 0
+                for r in pd.getRowKeys()
+                    for c in pd.getColKeys()
+                        if pd.getAggregator(r, c).value()?
+                            numNotNull++
+                        else
+                            numNull++
+                expect numNotNull
+                .toBe 4
+                expect numNull
+                .toBe 12
+
+            it "has a correct spot-checked aggregator", ->
+                agg = pd.getAggregator([ 'Carol', 'yellow' ],[ 102, 14 ])
+                val = agg.value()
+                expect(val).toBe 1
+                expect(agg.format(val)).toBe "1"
+
+            it "has a correct grand total aggregator", ->
+                agg = pd.getAggregator([],[])
+                val = agg.value()
+                expect(val).toBe 4
+                expect(agg.format(val)).toBe "4"
+
     describe ".naturalSort()", ->
         naturalSort = $.pivotUtilities.naturalSort

@@ -360,7 +452,7 @@
                 expect binner {x: {a:1}}
                 .toBeNaN()

-# TODO
-# full test of all aggregator templates?
-# locales?
-# input types?
+# todo
+# agg templates
+# date.parse -> date() ?
+# default options for PivotData, including filter


Fix test suite Windows issues on v0.10

Fix test suite Windows issues on v0.10

--- tests/write.spec.coffee
+++ tests/write.spec.coffee
@@ -30,11 +30,23 @@
 					@stream = stringToStream('Lorem ipsum dolor sit amet')

 				it 'should be able to write the stream to a device', (done) ->
+
+					# The operation takes some time on Windows for some reason.
+					# By increasing the default timeout (2s) we make sure we
+					# give Windows some space to do it's job.
+					this.timeout(8000)
+
 					emitter = imageWrite.write(@device.name, @stream)
 					emitter.on 'done', =>
-						filePromise = fs.readFileAsync(@device.name, encoding: 'utf8')
-						m.chai.expect(filePromise).to.eventually.equal('Lorem ipsum dolor sit amet')
-						done()
+						fs.readFileAsync(@device.name, encoding: 'utf8').then (contents) ->
+
+							# Windows, NodeJS v0.10, reads the file with some
+							# trailing null bytes in some cases.
+							# We make sure to remove them before checking the content.
+							contents = contents.replace(/\0/g, '')
+
+							m.chai.expect(contents).to.equal('Lorem ipsum dolor sit amet')
+							done()

 				describe 'given an error when writing the stream', ->


Revert accidental change.

Revert accidental change.

--- background_scripts/commands.coffee
+++ background_scripts/commands.coffee
@@ -65,7 +65,7 @@

         continue unless @availableCommands[vimiumCommand]

-        konsole.log("Mapping", key, "to", vimiumCommand)
+        console.log("Mapping", key, "to", vimiumCommand)
         @mapKeyToCommand(key, vimiumCommand)
       else if (lineCommand == "unmap")
         continue if (splitLine.length != 2)


Restore "whole volume" text in view mode

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -494,15 +494,16 @@
             when 'top'
               v = info.metric.options[v]
             when 'summary'
-              if Editing && info.d.global
+              if info.d.global
                 cell.classList.add('null')
                 cell.appendChild(document.createTextNode('Whole volume'))
-                del = cell.appendChild(document.createElement('a'))
-                del.className = 'button mini global-record white icon-text'
-                delicon = del.appendChild(document.createElement('span'))
-                delicon.className = 'icon trash'
-                del.appendChild(document.createTextNode('Remove'))
-                $(del).on 'click', $scope.$lift(clickGlobal)
+                if Editing
+                  del = cell.appendChild(document.createElement('a'))
+                  del.className = 'button mini global-record white icon-text'
+                  delicon = del.appendChild(document.createElement('span'))
+                  delicon.className = 'icon trash'
+                  del.appendChild(document.createTextNode('Remove'))
+                  $(del).on 'click', $scope.$lift(clickGlobal)
             else
               if info.metric.type == 'void' && info.d
                 cell.className = 'clickable' if Editing && Key.id != info.c


Make power operator compilation use proper AST nodes

--- nodes.coffee
+++ nodes.coffee
@@ -1669,10 +1669,9 @@
     @joinFragmentArrays parts, ''

   compilePower: (o) ->
-    left = @first.compileToFragments o, LEVEL_OP
-    right = @second.compileToFragments o, LEVEL_OP
-    parts = [@makeCode('Math.pow('), left, @makeCode(', '), right, @makeCode(')')]
-    @joinFragmentArrays parts, ''
+    # Make a Math.pow call
+    pow = new Value new Literal('Math'), [new Access new Literal 'pow']
+    new Call(pow, [@first, @second]).compileToFragments o

   toString: (idt) ->
     super idt, @constructor.name + ' ' + @operator


removing the (now-unused) inherits helper, and adding a helper for creating LiteralNodes

--- nodes.coffee
+++ nodes.coffee
@@ -36,12 +36,9 @@
   delete obj[key]
   val

-# Quickie inheritance convenience wrapper to reduce typing.
-inherit: (parent, props) ->
-  klass: del(props, 'constructor')
-  klass extends parent
-  (klass.prototype[name]: prop) for name, prop of props
-  klass
+# Quickie helper for a generated LiteralNode.
+literal: (name) ->
+  new LiteralNode(name)

 # Mark a node as a statement, or a statement only.
 statement: (klass, only) ->
@@ -82,7 +79,7 @@
   # If the code generation wishes to use the result of a complex expression
   # in multiple places, ensure that the expression is only ever evaluated once.
   compile_reference: (o) ->
-    reference: new LiteralNode(o.scope.free_variable())
+    reference: literal(o.scope.free_variable())
     compiled:  new AssignNode(reference, this)
     [compiled, reference]

@@ -387,7 +384,7 @@
   # Hooking one constructor into another's prototype chain.
   compile_node: (o) ->
     o.scope.assign('__extends', @code, true)
-    ref:  new ValueNode new LiteralNode '__extends'
+    ref:  new ValueNode literal('__extends')
     call: new CallNode ref, [@child, @parent]
     call.compile(o)

@@ -451,8 +448,8 @@
   # TODO: This generates pretty ugly code ... shrink it.
   compile_array: (o) ->
     name: o.scope.free_variable()
-    body: Expressions.wrap([new LiteralNode(name)])
-    arr:  Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, new LiteralNode(name))])
+    body: Expressions.wrap([literal(name)])
+    arr:  Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, literal(name))])
     (new ParentheticalNode(new CallNode(new CodeNode([], arr)))).compile(o)

@@ -515,7 +512,7 @@
     for prop in @properties
       if prop.variable and prop.variable.base.value is 'constructor'
         func: prop.value
-        func.body.push(new ReturnNode(new LiteralNode('this')))
+        func.body.push(new ReturnNode(literal('this')))
         constructor: new AssignNode(@variable, func)
       else
         if prop.variable
@@ -525,9 +522,9 @@

     if not constructor
       if @parent
-        applied: new ValueNode(@parent, [new AccessorNode(new LiteralNode('apply'))])
+        applied: new ValueNode(@parent, [new AccessorNode(literal('apply'))])
         constructor: new AssignNode(@variable, new CodeNode([], new Expressions([
-          new CallNode(applied, [new LiteralNode('this'), new LiteralNode('arguments')])
+          new CallNode(applied, [literal('this'), literal('arguments')])
         ])))
       else
         constructor: new AssignNode(@variable, new CodeNode())
@@ -571,7 +568,7 @@
     expr: expressions.unwrap()
     return expressions if expr.is_statement_only() or expr.contains (n) -> n.is_statement_only()
     Expressions.wrap([new CallNode(
-      new ValueNode(new LiteralNode(array), [new AccessorNode(new LiteralNode('push'))]), [expr]
+      new ValueNode(literal(array), [new AccessorNode(literal('push'))]), [expr]
     )])

 }
@@ -582,7 +579,7 @@

   wrap: (expressions, statement) ->
     func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
-    call: new CallNode(new ValueNode(func, [new AccessorNode(new LiteralNode('call'))]), [new LiteralNode('this')])
+    call: new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')])
     if statement then Expressions.wrap([call]) else call

 }
@@ -642,10 +639,10 @@
       [obj, idx]: [obj.value, obj.variable.base] if @variable.is_object()
       access_class: if @variable.is_array() then IndexNode else AccessorNode
       if obj instanceof SplatNode
-        val: new LiteralNode(obj.compile_value(o, val_var, @variable.base.objects.indexOf(obj)))
+        val: literal(obj.compile_value(o, val_var, @variable.base.objects.indexOf(obj)))
       else
-        idx: new LiteralNode(idx) unless typeof idx is 'object'
-        val: new ValueNode(new LiteralNode(val_var), [new access_class(idx)])
+        idx: literal(idx) unless typeof idx is 'object'
+        val: new ValueNode(literal(val_var), [new access_class(idx)])
       assigns.push(new AssignNode(obj, val).compile(o))
     code: assigns.join("\n")
     code += '\n' + @idt() + 'return ' + @variable.compile(o) + ';' if o.returns
@@ -715,7 +712,7 @@
   type: 'Splat'

   constructor: (name) ->
-    name: new LiteralNode(name) unless name.compile
+    name: literal(name) unless name.compile
     @children: [@name: name]

   compile_node: (o) ->
@@ -1001,7 +998,7 @@
   rewrite_switch: (o) ->
     assigner: @switcher
     if not (@switcher.unwrap() instanceof LiteralNode)
-      variable: new LiteralNode(o.scope.free_variable())
+      variable: literal(o.scope.free_variable())
       assigner: new AssignNode(variable, @switcher)
       @switcher: variable
     @condition: if @multiple


Fix #3361, make %% coerce right operand only once

Force coercion of right operand once before doing arithmetic with it in the `__modulo` utility function.

--- nodes.coffee
+++ nodes.coffee
@@ -2194,7 +2194,7 @@
   "

   modulo: -> """
-    function(a, b) { return (a % b + +b) % b; }
+    function(a, b) { b = +b; return (a % b + b) % b; }
   """

   # Shortcuts to speed up the lookup time for native functions.


fixes #1723: operator precedence in unbounded splice compilation

--- nodes.coffee
+++ nodes.coffee
@@ -1062,7 +1062,7 @@
         to = +to.compile(o) - +fromRef
         to += 1 unless exclusive
       else
-        to = to.compile(o) + ' - ' + fromRef
+        to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef
         to += ' + 1' unless exclusive
     else
       to = "9e9"


slightly less parentheticals.

slightly less parentheticals.

--- nodes.coffee
+++ nodes.coffee
@@ -1405,8 +1405,9 @@
     if expr instanceof Value and expr.isAtomic()
       expr.front = @front
       return expr.compile o
-    bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call)
     code = expr.compile o, LEVEL_PAREN
+    bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
+      (expr instanceof For and expr.returns))
     if bare then code else "(#{code})"

 #### For


改行コードを修正、住所とイメージURLを追加。

改行コードを修正、住所とイメージURLを追加。

--- scripts/recommend_lunch.coffee
+++ scripts/recommend_lunch.coffee
@@ -16,9 +16,11 @@
             random = Math.floor(Math.random() * total)
             console.log random

-            answer = "こんな所はどうでしょう？¥n¥n"
-            answer += data.businesses[random].name + "¥n"
-            answer += data.businesses[random].url
+            answer = "こんな所はどうでしょう？\n\n"
+            answer += data.businesses[random].name + "\n"
+            answer += data.businesses[random].url + "\n"
+            answer += data.businesses[random].location.display_address + "\n"
+            answer += data.businesses[random].image_url + "\n"

             msg.send answer
         .catch (err) ->


cleaning

cleaning

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,20 +1,3 @@
-#
-#module.exports = (grunt) ->
-#  grunt.initConfig nwjs:
-#    options:
-#      version: '0.12.0'
-#      buildDir: './build'
-#      platforms: [
-#        'osx'
-#        'win'
-#        'linux'
-#      ]
-#    src: ['./**', '!./build/**', '!./cache/**']
-#
-#  grunt.loadNpmTasks 'grunt-nw-builder'
-#  grunt.registerTask 'default', [ 'nwjs' ]
-
-#module.exports = (grunt) ->

 module.exports = (grunt) ->

@@ -48,16 +31,4 @@
       arch: 'x64'
       overwrite: true

-  grunt.registerTask 'default', [ 'electron' ]
-
-
-
-
-#  grunt.loadNpmTasks('grunt-electron-installer')
-#
-#  'create-windows-installer': {
-#    appDirectory: '/tmp/build/my-app',
-#    outputDirectory: '/tmp/build/installer',
-#    authors: 'My App Inc.',
-#    exe: 'myapp.exe'
-#  }+  grunt.registerTask 'build', [ 'electron' ]


Rajout d'une tache de copie pour quand le sync plante sur Windows

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -115,6 +115,14 @@
                     ]
                     dest: 'dist/'
                 }]
+            slides:
+                files: [{
+                    expand: true
+                    src: [
+                        'slides/**'
+                    ]
+                    dest: 'dist/'
+                }]

         clean:
             dist: ['dist/**']


Update build config for webpack2

Update build config for webpack2

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -34,14 +34,21 @@
             'microflo-emscripten': 'commonjs microflo-emscripten' # optional?
             'acorn': 'commonjs acorn' # optional?
           module:
-            loaders: [
-              { test: /\.coffee$/, loader: "coffee-loader" }
-              { test: /\.json$/, loader: "json-loader" }
-              { test: /\.fbp$/, loader: "fbp-loader" }
-              { test: /\.yaml$/, loader: "json-loader!yaml-include-loader" }
+            rules: [
+              test: /\.coffee$/
+              use: ["coffee-loader"]
+            ,
+              test: /\.fbp$/
+              use: ["fbp-loader"]
+            ,
+              test: /\.yaml$/
+              use: [
+                "json-loader"
+                "yaml-include-loader"
+              ]
             ]
           resolve:
-            extensions: ["", ".coffee", ".js"]
+            extensions: [".coffee", ".js"]
           node:
             fs: "empty"
         ignores: [


fix build

fix build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,7 +21,7 @@

     browserify:
       options:
-        alias: ['node_modules/tandem-core/src/tandem.coffee:tandem-core']
+        alias: ['node_modules/tandem-core/build/tandem-core.js:tandem-core']
         extensions: ['.js', '.coffee']
         transform: ['coffeeify']
       scribe:


New grunt task 'dev' to start server + watch

closes #95

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -13,7 +13,7 @@
   grunt.initConfig

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

     # Project metadata, used by the <banner> directive.
     meta:
@@ -23,37 +23,37 @@
               "  * Copyright (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>;" +
               " Licensed <%= _.pluck(pkg.licenses, 'type').join(', ') %> */\n"
     bumpup:
-      options:
-        dateformat: "YYYY-MM-DD HH:mm"
-        normalize: true
-        updateProps:
-          pkg: "package.json"
-      files: ["package.json", "bower.json", "fancytree.jquery.json"]
+        options:
+            dateformat: "YYYY-MM-DD HH:mm"
+            normalize: true
+            updateProps:
+                pkg: "package.json"
+        files: ["package.json", "bower.json", "fancytree.jquery.json"]

     checkrepo:
       beforeBump:
-        tag:
-          eq: "<%= pkg.version %>" # Check if highest repo tag == pkg.version
-#        tagged: false # Require last commit (HEAD) to be tagged
-        clean: true # // Require repo to be clean (no unstaged changes)
+          tag:
+              eq: "<%= pkg.version %>" # Check if highest repo tag == pkg.version
+  #        tagged: false # Require last commit (HEAD) to be tagged
+          clean: true # // Require repo to be clean (no unstaged changes)
       beforeRelease:
-        tag:
-          lt: "<%= pkg.version %>" # Check if highest repo tag is lower than pkg.version
-#        tagged: false # Require last commit (HEAD) to be tagged
-        clean: true # // Require repo to be clean (no unstaged changes)
+          tag:
+            lt: "<%= pkg.version %>" # Check if highest repo tag is lower than pkg.version
+#          tagged: false # Require last commit (HEAD) to be tagged
+          clean: true # // Require repo to be clean (no unstaged changes)

     clean:
-      build:
-        noWrite: true
-        src: [ "build" ]
+        build:
+            noWrite: true
+            src: [ "build" ]

     compress:
-      build:
-        options:
-          archive: "dist/<%= pkg.name %>-<%= pkg.version %>.zip"
-        files: [
-          {expand: true, cwd: "build/", src: ["**/*"], dest: ""}
-          ]
+        build:
+            options:
+                archive: "dist/<%= pkg.name %>-<%= pkg.version %>.zip"
+            files: [
+                {expand: true, cwd: "build/", src: ["**/*"], dest: ""}
+                ]

     concat:
         core:
@@ -67,24 +67,29 @@
             options:
                 stripBanners: true
             src: [
-              "<%= meta.banner %>"
-              "src/jquery.fancytree.js"
-              "src/jquery.fancytree.columnview.js"
-              "src/jquery.fancytree.dnd.js"
-              "src/jquery.fancytree.filter.js"
-              "src/jquery.fancytree.menu.js"
-              "src/jquery.fancytree.persist.js"
-              "src/jquery.fancytree.table.js"
-              "src/jquery.fancytree.themeroller.js"
-              ]
+                "<%= meta.banner %>"
+                "src/jquery.fancytree.js"
+                "src/jquery.fancytree.columnview.js"
+                "src/jquery.fancytree.dnd.js"
+                "src/jquery.fancytree.filter.js"
+                "src/jquery.fancytree.menu.js"
+                "src/jquery.fancytree.persist.js"
+                "src/jquery.fancytree.table.js"
+                "src/jquery.fancytree.themeroller.js"
+                ]
             dest: "build/<%= pkg.name %>-all.js"

     connect:
-        demo:
+        forever:
             options:
                 port: 8080
                 base: "./"
                 keepalive: true
+        dev: # pass on, so subsequent tastks (like watch) can start
+            options:
+                port: 8080
+                base: "./"
+                keepalive: false

     copy:
         build:
@@ -99,12 +104,12 @@
                 }]

     csslint:
-#      options:
-#              csslintrc: ".csslintrc"
-      strict:
-        options:
-            import: 2
-        src: ["src/**/*.css"]
+  #      options:
+  #              csslintrc: ".csslintrc"
+        strict:
+            options:
+                import: 2
+            src: ["src/**/*.css"]

     cssmin:
         build:
@@ -136,37 +141,37 @@
         all: ["demo/**/*.html", "doc/**/*.html", "test/**/*.html"]

     jsdoc:
-      build:
-        src: ["src/*.js", "doc/README.md"]
-        # http://usejsdoc.org/about-configuring-jsdoc.html#example
+        build:
+            src: ["src/*.js", "doc/README.md"]
+            # http://usejsdoc.org/about-configuring-jsdoc.html#example
+            options:
+                destination: "doc/jsdoc_grunt"
+#                template: "bin/jsdoc3-moogle",
+                verbose: true
+
+    jshint:
         options:
-          destination: "doc/jsdoc_grunt"
-#                    template: "bin/jsdoc3-moogle",
-          verbose: true
-
-    jshint:
-      options:
-        # Linting according to http://contribute.jquery.org/style-guide/js/
-        jshintrc: ".jshintrc"
-      beforeConcat: [
-        "Gruntfile.js"
-        "src/*.js"
-        "3rd-party/**/jquery.fancytree.*.js"
-        "test/unit/*.js"
-        ]
-      afterConcat: [
-        "<%= concat.core.dest %>"
-        "<%= concat.all.dest %>"
-        ]
+            # Linting according to http://contribute.jquery.org/style-guide/js/
+            jshintrc: ".jshintrc"
+        beforeConcat: [
+            "Gruntfile.js"
+            "src/*.js"
+            "3rd-party/**/jquery.fancytree.*.js"
+            "test/unit/*.js"
+            ]
+        afterConcat: [
+            "<%= concat.core.dest %>"
+            "<%= concat.all.dest %>"
+            ]

     less:
         development:
             options:
-#             paths: ["src/"]
-#             report: "min"
-              compress: false
-              yuicompress: false
-#              optimization: 10
+#               paths: ["src/"]
+#               report: "min"
+                compress: false
+                yuicompress: false
+#               optimization: 10
             files: [
                 {expand: true, cwd: "src/", src: "**/ui.fancytree.less", dest: "src/", ext: ".fancytree.css"}
             ]
@@ -226,9 +231,9 @@
 #                , expand: true
 #                , cwd: "build/"
                 sourceMap:
-                  (path) -> path.replace(/.js/, ".js.map")
+                    (path) -> path.replace(/.js/, ".js.map")
                 sourceMappingURL:
-                  (path) -> path.replace(/^build\//, "") + ".map"
+                    (path) -> path.replace(/^build\//, "") + ".map"
 #                  , sourceMapIn: function(path) { return path.replace(/^build\//, "")}
 #                  , sourceMapRoot: "/" //function(path) { return path.replace(/^build\//, "")}
                 sourceMapPrefix: 1 # strip 'build/' from paths
@@ -262,56 +267,37 @@
   grunt.loadNpmTasks "grunt-tagrelease"
   grunt.loadNpmTasks "grunt-text-replace"

-  # from jquery:
-  #   grunt.loadNpmTasks( "grunt-compare-size"
-  #   grunt.loadNpmTasks( "grunt-git-authors"
-  #   grunt.loadNpmTasks( "grunt-contrib-watch"
-  #   grunt.loadNpmTasks( "grunt-jsonlint"
-  # from hammer.js
-  #   grunt.loadNpmTasks 'grunt-tagrelease'
-
-  # Task for updating the pkg config property. Needs to be run after
-  # bumpup so the next tasks in queue can work with updated values.
-  # grunt.registerTask "updatePkg", () ->
-  #   grunt.config.set "pkg", grunt.file.readJSON("package.json")
-
-
-  grunt.registerTask "server", ["connect:demo"]
+  grunt.registerTask "server", ["connect:forever"]
+  grunt.registerTask "dev", ["connect:dev", "watch"]
   grunt.registerTask "test", [
-    "jshint:beforeConcat",
-    # "csslint",
-    "qunit:develop"
+      "jshint:beforeConcat",
+      # "csslint",
+      "qunit:develop"
   ]
   # grunt.registerTask("makejsdoc", ["jsdoc"]
   grunt.registerTask "travis", ["test"]
   grunt.registerTask "default", ["test"]
-  # grunt.registerTask "bump", [
-  #   "checkrepo:beforeBump"
-  #   "bumpup:build"
-  #   "updatePkg"
-  #   # "replace:bump"
-  #   ]
   grunt.registerTask "build", [
-    "exec:tabfix"
-    "test"
-    "clean:build"
-    "copy:build"
-    "concat"
-    # "cssmin:build"
-    "replace:build"
-    "jshint:afterConcat"
-    "uglify"
-    "qunit:build"
-    "compress:build"
-    # "clean:build"
-    ]
+      "exec:tabfix"
+      "test"
+      "clean:build"
+      "copy:build"
+      "concat"
+      # "cssmin:build"
+      "replace:build"
+      "jshint:afterConcat"
+      "uglify"
+      "qunit:build"
+      "compress:build"
+      # "clean:build"
+      ]
   grunt.registerTask "release", [
-    "checkrepo:beforeRelease"
-    "build"
-    "tagrelease"
-    "bumpup:prerelease"
-    ]
+      "checkrepo:beforeRelease"
+      "build"
+      "tagrelease"
+      "bumpup:prerelease"
+      ]
   grunt.registerTask "upload", [
-    "build"
-    "exec:upload"
-    ]
+      "build"
+      "exec:upload"
+      ]


wspace

wspace

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -166,18 +166,14 @@

   grunt.loadNpmTasks('grunt-contrib-clean')
-
   grunt.loadNpmTasks('grunt-snocketsify')
   grunt.loadNpmTasks('grunt-contrib-stylus')
   grunt.loadNpmTasks('grunt-contrib-jade')
-
   grunt.loadNpmTasks('grunt-contrib-cssmin')
   grunt.loadNpmTasks('grunt-contrib-uglify')
-
   grunt.loadNpmTasks('grunt-hashify')
   grunt.loadNpmTasks('grunt-contrib-compress')
   # grunt.loadNpmTasks('grunt-contrib-imagemin')
-
   grunt.loadNpmTasks('grunt-contrib-watch')

   grunt.registerTask('default', [


Docs: Fix BEM style names in headings.

Docs: Fix BEM style names in headings.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -190,6 +190,10 @@

 	saveHtml = (name, html) ->
+		html = (html
+			# Fix BEM style names in headings
+			.replace(/(<h\d[^>]*>)(.*?)(<\/h\d>)/g, (m, open, contents, close) -> open + contents.replace(/<\/?strong>/g, '__') + close)
+		)
 		template = grunt.file.read 'docs_src/template.html'
 		html = grunt.template.process template, data: html: html, page: name, menu: docsMenu
 		grunt.file.write "docs/#{name}.html", html


got rid of grunt

got rid of grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,48 +0,0 @@
-module.exports = (grunt) ->
-
-  # Project configuration.
-  grunt.initConfig {
-    pkg: grunt.file.readJSON('package.json')
-
-    coffee: {
-      options: { }
-      compile: {
-        files: [
-          {
-            expand: true      # Enable dynamic expansion.
-            cwd: 'src/'       # Src matches are relative to this path.
-            src: ['*.coffee'] # Actual pattern(s) to match.
-            dest: 'target/'   # Destination path prefix.
-            ext: '.js'        # Dest filepaths will have this extension.
-          }
-          {
-            expand: true
-            cwd: 'src/driver/'
-            src: ['*.coffee']
-            dest: 'target/'
-            ext: '.js'
-          }
-        ]
-      }
-    }
-
-    watch: {
-      coffee: {
-        files: ['src/*.coffee', 'src/driver/*.coffee']
-        tasks: ['coffee']
-      }
-    }
-
-    compass: {
-
-    }
-  }
-
-  grunt.loadNpmTasks('grunt-contrib-watch')
-  grunt.loadNpmTasks('grunt-contrib-coffee')
-  grunt.loadNpmTasks('grunt-contrib-uglify')
-  grunt.loadNpmTasks('grunt-contrib-compass')
-
-  # Default task(s).
-  grunt.registerTask('default', ['coffee'])
-  return


Allow gruntfile to set build-dir from the command line

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -2,13 +2,14 @@
 fs = require 'fs'
 path = require 'path'

-BUILD_DIR = '/tmp/atom-build/atom-shell'
-APP_NAME = 'Atom.app'
-CONTENTS_DIR = path.join(BUILD_DIR, APP_NAME, 'Contents')
-APP_DIR = path.join(CONTENTS_DIR, 'Resources', 'app')
-INSTALL_DIR = path.join('/Applications', APP_NAME)
-
 module.exports = (grunt) ->
+  APP_NAME = "Atom.app"
+  BUILD_DIR = grunt.option('build-dir') ? "/tmp/atom-build/"
+  SHELL_APP_DIR = path.join(BUILD_DIR, APP_NAME)
+  CONTENTS_DIR = path.join(SHELL_APP_DIR, 'Contents')
+  APP_DIR = path.join(CONTENTS_DIR, "Resources", "app")
+  INSTALL_DIR = path.join('/Applications', APP_NAME)
+
   exec = (command, args, options, callback) ->
     if grunt.util._.isFunction(args)
       options = args
@@ -213,7 +214,7 @@
         commands.push (callback) ->
           args = [
             version
-            'resources/mac/app-Info.plist'
+            'resources/mac/atom-Info.plist'
             'Atom.app/Contents/Info.plist'
           ]
           exec('script/generate-info-plist', args, env: {BUILT_PRODUCTS_DIR: BUILD_DIR}, callback)
@@ -230,13 +231,13 @@

   grunt.registerTask 'codesign', 'Codesign the app', ->
     done = @async()
-    args = ["-f", "-v", "-s", "Developer ID Application: GitHub", "#{path.join(BUILD_DIR, APP_NAME)}"]
+    args = ["-f", "-v", "-s", "Developer ID Application: GitHub", SHELL_APP_DIR]
     exec "codesign", args, (error) -> done(!error?)

   grunt.registerTask 'install', 'Install the built application', ->
     rm INSTALL_DIR
     mkdir path.dirname(INSTALL_DIR)
-    cp path.join(BUILD_DIR, APP_NAME), INSTALL_DIR
+    cp SHELL_APP_DIR, INSTALL_DIR

   grunt.registerTask 'bootstrap', 'Bootstrap modules and atom-shell', ->
     done = @async()
@@ -259,4 +260,4 @@
   grunt.registerTask('compile', ['coffee', 'less', 'cson'])
   grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
   grunt.registerTask('ci', ['clean', 'bootstrap', 'build', 'test'])
-  grunt.registerTask('default', 'build')
+  grunt.registerTask('default', ['build'])


Organizing Gruntfile

Organizing Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,8 +1,8 @@
-path = require("path")
+path = require('path')
 exec = require('child_process').exec
-cheerio = require("cheerio")
-chai = require("chai")
-lrSnippet = require("grunt-contrib-livereload/lib/utils").livereloadSnippet
+cheerio = require('cheerio')
+chai = require('chai')
+lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet
 folderMount = (connect, point) -> connect.static path.resolve(point)

 module.exports = (grunt) ->
@@ -10,68 +10,68 @@
 	grunt.initConfig
 		resourceToken: '$resourcesUrl$'
 		pkg: grunt.file.readJSON('package.json')
-		clean: ["build", "deploy"]
+		clean: ['build', 'deploy']
 		copy:
 			main:
 				expand: true
-				cwd: "src/"
-				src: ["**", "!includes/**", "!coffee/**", "!**/*.less"]
-				dest: "build/"
+				cwd: 'src/'
+				src: ['**', '!includes/**', '!coffee/**', '!**/*.less']
+				dest: 'build/'

 			debug:
-				src: ["src/index.html"]
-				dest: "build/index.debug.html"
+				src: ['src/index.html']
+				dest: 'build/index.debug.html'

 			deploy:
 				expand: true
-				cwd: "build/"
-				src: ["**", "!includes/**", "!coffee/**", "!**/*.less"]
-				dest: "deploy/<%= meta.commit %>/"
+				cwd: 'build/'
+				src: ['**', '!includes/**', '!coffee/**', '!**/*.less']
+				dest: 'deploy/<%= meta.commit %>/'

 			env:
 				expand: true
-				cwd: "deploy/<%= meta.commit %>/"
-				src: ["index.html", "index.debug.html"]
-				dest: "deploy/<%= meta.env %>/"
+				cwd: 'deploy/<%= meta.commit %>/'
+				src: ['index.html', 'index.debug.html']
+				dest: 'deploy/<%= meta.env %>/'

 		coffee:
 			main:
 				expand: true
-				cwd: "src/coffee"
-				src: ["**/*.coffee"]
-				dest: "build/js/"
-				ext: ".js"
+				cwd: 'src/coffee'
+				src: ['**/*.coffee']
+				dest: 'build/js/'
+				ext: '.js'

 			test:
 				expand: true
-				cwd: "test/coffee"
-				src: ["**/*.coffee"]
-				dest: "build/test/js/"
-				ext: ".js"
+				cwd: 'test/coffee'
+				src: ['**/*.coffee']
+				dest: 'build/test/js/'
+				ext: '.js'

 		less:
 			main:
 				files:
-					"build/style/main.css": "src/style/main.less"
+					'build/style/main.css': 'src/style/main.less'

 		useminPrepare:
-			html: "build/index.html"
+			html: 'build/index.html'

 		usemin:
-			html: "build/index.html"
+			html: 'build/index.html'

 		'string-replace':
 			deploy:
 				files:
-					"deploy/<%= meta.env %>/index.html": ["deploy/<%= meta.env %>/index.html"]
-					"deploy/<%= meta.env %>/index.debug.html": ["deploy/<%= meta.env %>/index.debug.html"]
+					'deploy/<%= meta.env %>/index.html': ['deploy/<%= meta.env %>/index.html']
+					'deploy/<%= meta.env %>/index.debug.html': ['deploy/<%= meta.env %>/index.debug.html']

 				options:
 					replacements: [
-						pattern: /src="/ig
+						pattern: /src='/ig
 						replacement: 'src="<%= resourceToken %>/<%= pkg.name %>/<%= meta.commit %>/'
 					,
-						pattern: /href="/ig
+						pattern: /href='/ig
 						replacement: 'href="<%= resourceToken %>/<%= pkg.name %>/<%= meta.commit %>/'
 					]

@@ -80,79 +80,87 @@
 				options:
 					port: 9001
 					middleware: (connect, options) ->
-						[lrSnippet, folderMount(connect, "build/")]
+						[lrSnippet, folderMount(connect, 'build/')]

 		regarde:
 			dev:
-				files: ["src/**/*.html", "src/**/*.coffee", "src/**/*.js", "src/**/*.less"]
-				tasks: ["dev", "livereload"]
+				files: ['src/**/*.html', 'src/**/*.coffee', 'src/**/*.js', 'src/**/*.less']
+				tasks: ['dev', 'livereload']

 			prod:
-				files: ["src/**/*.html", "src/**/*.coffee", "src/**/*.js", "src/**/*.less"]
-				tasks: ["prod", "livereload"]
+				files: ['src/**/*.html', 'src/**/*.coffee', 'src/**/*.js', 'src/**/*.less']
+				tasks: ['prod', 'livereload']

 			test:
-				files: ["src/**/*.html", "src/**/*.coffee", "src/**/*.js", "src/**/*.less", "test/**/*.coffee"]
-				tasks: ["test"]
+				files: ['src/**/*.html', 'src/**/*.coffee', 'src/**/*.js', 'src/**/*.less', 'test/**/*.coffee']
+				tasks: ['test']
 				spawn: true

 		simplemocha:
 			options:
 				timeout: 3000
 				ignoreLeaks: false
-				ui: "bdd"
-				reporter: "spec"
+				ui: 'bdd'
+				reporter: 'spec'

 			all:
-				src: "build/test/js/**/*.js"
+				src: 'build/test/js/**/*.js'

-	grunt.registerTask "deploy-version", ->
+	# Looks for the commit hash in a GIT_COMMIT env var, or tries calling git.
+	grunt.registerTask 'deploy-version', ->
 		if process.env.GIT_COMMIT
-			grunt.config "meta.commit", new String(process.env.GIT_COMMIT)
-			grunt.log.writeln "Version set by environment variable GIT_COMMIT to: " + grunt.config("meta.commit")
+			grunt.config 'meta.commit', new String(process.env.GIT_COMMIT)
+			grunt.log.writeln 'Version set by environment variable GIT_COMMIT to: ' + grunt.config('meta.commit')
 		else
 			done = @async()
-			exec "git rev-parse --verify HEAD", (err, stdout, stderr) ->
+			exec 'git rev-parse --verify HEAD', (err, stdout, stderr) ->
 				if err
-					grunt.log.writeln "Failed to set version by git."
+					grunt.log.writeln 'Failed to set version by git.'
 					done()
 					return
-				grunt.config "meta.commit", stdout.replace("\n", "")
-				grunt.log.writeln "Version set by git commit to: " + grunt.config("meta.commit")
+				grunt.config 'meta.commit', stdout.replace('\n', '')
+				grunt.log.writeln 'Version set by git commit to: ' + grunt.config('meta.commit')
 				done()

-	grunt.loadNpmTasks "grunt-contrib-connect"
-	grunt.loadNpmTasks "grunt-contrib-concat"
-	grunt.loadNpmTasks "grunt-contrib-livereload"
-	grunt.loadNpmTasks "grunt-contrib-copy"
-	grunt.loadNpmTasks "grunt-contrib-clean"
-	grunt.loadNpmTasks "grunt-contrib-coffee"
-	grunt.loadNpmTasks "grunt-contrib-less"
-	grunt.loadNpmTasks "grunt-contrib-uglify"
-	grunt.loadNpmTasks "grunt-contrib-cssmin"
-	grunt.loadNpmTasks "grunt-regarde"
-	grunt.loadNpmTasks "grunt-simple-mocha"
-	grunt.loadNpmTasks "grunt-usemin"
-	grunt.loadNpmTasks "grunt-string-replace"
+	grunt.loadNpmTasks 'grunt-contrib-connect'
+	grunt.loadNpmTasks 'grunt-contrib-concat'
+	grunt.loadNpmTasks 'grunt-contrib-livereload'
+	grunt.loadNpmTasks 'grunt-contrib-copy'
+	grunt.loadNpmTasks 'grunt-contrib-clean'
+	grunt.loadNpmTasks 'grunt-contrib-coffee'
+	grunt.loadNpmTasks 'grunt-contrib-less'
+	grunt.loadNpmTasks 'grunt-contrib-uglify'
+	grunt.loadNpmTasks 'grunt-contrib-cssmin'
+	grunt.loadNpmTasks 'grunt-regarde'
+	grunt.loadNpmTasks 'grunt-simple-mocha'
+	grunt.loadNpmTasks 'grunt-usemin'
+	grunt.loadNpmTasks 'grunt-string-replace'

-	###
-	Remote tasks
-	###
-	grunt.registerTask "remote", "Run Remote proxy server", ->
-		require "coffee-script"
-		require("remote")()
+	grunt.registerTask 'default', ['dev-watch']

-	grunt.registerTask "dev", ["clean", "copy:main", "coffee", "less"]
-	grunt.registerTask "prod", ["dev", "copy:debug", "useminPrepare", "concat", "uglify", "cssmin", "usemin"]
-	grunt.registerTask "default", ["dev", "livereload-start", "connect", "regarde:dev"]
-	grunt.registerTask "devmin", ["prod", "livereload-start", "connect", "regarde:prod"]
-	grunt.registerTask "test", ["dev", "simplemocha"]
-	grunt.registerTask "devtest", ["test", "regarde:test"]
-	grunt.registerTask "deploy", ->
+	# Dev
+	grunt.registerTask 'dev', ['clean', 'copy:main', 'coffee', 'less']
+	grunt.registerTask 'dev-watch', ['dev', 'livereload-start', 'connect', 'regarde:dev']
+
+	# Prod - minifies files
+	grunt.registerTask 'prod', ['dev', 'copy:debug', 'useminPrepare', 'concat', 'uglify', 'cssmin', 'usemin']
+	grunt.registerTask 'prod-watch', ['prod', 'livereload-start', 'connect', 'regarde:prod']
+
+	# Test
+	grunt.registerTask 'test', ['dev', 'simplemocha']
+	grunt.registerTask 'test-watch', ['test', 'regarde:test']
+
+	# Deploy - creates deploy folder structure
+	grunt.registerTask 'deploy', ->
 		env = this.args[0]
-		console.log "Deploying to environment:", env
-		grunt.config "meta.env", env
-		grunt.task.run ["prod", "simplemocha", "deploy-version", "copy:deploy", "copy:env", "string-replace:deploy"]
+		console.log 'Deploying to environment:', env
+		grunt.config 'meta.env', env
+		grunt.task.run ['prod', 'simplemocha', 'deploy-version', 'copy:deploy', 'copy:env', 'string-replace:deploy']

 	# Example usage of deploy task
-	grunt.registerTask "deploy-example", ["deploy:master"]+	grunt.registerTask 'deploy-example', ['deploy:master']
+
+	#	Remote task
+	grunt.registerTask 'remote', 'Run Remote proxy server', ->
+		require 'coffee-script'
+		require('remote')()


Copy non-compiled files to build directory

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -109,6 +109,26 @@
     rm BUILD_DIR
     mkdir path.dirname(BUILD_DIR)
     cp 'atom-shell', BUILD_DIR
+
+    mkdir APP_DIR
+
+    fs.createReadStream('atom.sh').pipe(fs.createWriteStream(path.join(APP_DIR, 'atom.sh')))
+    fs.createReadStream('package.json').pipe(fs.createWriteStream(path.join(APP_DIR, 'package.json')))
+
+    directories = [
+      'benchmark'
+      'dot-atom'
+      'node_modules'
+      'spec'
+      'vendor'
+    ]
+    for directory in directories
+      cp directory, path.join(APP_DIR, directory)
+
+    cp 'src', path.join(APP_DIR, 'src'), filter: /.+\.(cson|coffee|less)$/
+    cp 'static', path.join(APP_DIR, 'static'), filter: /.+\.less$/
+    cp 'themes', path.join(APP_DIR, 'themes'), filter: /.+\.(cson|less)$/
+
     grunt.task.run('compile')

   grunt.registerTask 'install', 'Install the built application', ->


We compressed... yo.

We compressed... yo.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -14,7 +14,7 @@
     sass:
       options:
         sourceMap: 'none'
-        style: 'compact'
+        style: 'compressed'
       dist:
         files:
           './static/stylesheets/live.css': './avalonstar/assets/stylesheets/live.scss',


fix banner

fix banner

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -90,12 +90,12 @@
           options:
             banner: """
 /**
-* <%= pkg.name %> v<%= pkg.version %>
-* <%= pkg.homepage %>
-*
-* Copyright Ubiquitous Entertainment Inc.
-* Released under the MIT license.
-*/\n\n
+ * <%= pkg.name %> v<%= pkg.version %>
+ * <%= pkg.homepage %>
+ *
+ * Copyright Ubiquitous Entertainment Inc.
+ * Released under the MIT license.
+ */\n\n
 """

       uglify:


chore: fix typo in method name

chore: fix typo in method name

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -10,29 +10,29 @@
     dist: "dist"
     dev: "dev"

-  loadMoule = (name) ->
+  loadModule = (name) ->
     require("./build/config/#{name}")(grunt, appConfig)

   grunt.initConfig
     appConfig: appConfig
     pkg: grunt.file.readJSON("package.json")

-    watch:         loadMoule "watch"
-    coffee:        loadMoule "coffee"
-    less:          loadMoule "less"
-    concat:        loadMoule "concat"
-    useminPrepare: loadMoule "usemin_prepare"
-    usemin:        loadMoule "usemin"
-    htmlmin:       loadMoule "htmlmin"
-    copy:          loadMoule "copy"
-    coffeelint:    loadMoule "coffeelint"
-    ngtemplates:   loadMoule "ngtemplates"
-    bower:         loadMoule "bower"
-    karma:         loadMoule "karma"
-    clean:         loadMoule "clean"
-    connect:       loadMoule "connect"
-    shell:         loadMoule "shell"
-    devUpdate:     loadMoule "devUpdate"
+    watch:         loadModule "watch"
+    coffee:        loadModule "coffee"
+    less:          loadModule "less"
+    concat:        loadModule "concat"
+    useminPrepare: loadModule "usemin_prepare"
+    usemin:        loadModule "usemin"
+    htmlmin:       loadModule "htmlmin"
+    copy:          loadModule "copy"
+    coffeelint:    loadModule "coffeelint"
+    ngtemplates:   loadModule "ngtemplates"
+    bower:         loadModule "bower"
+    karma:         loadModule "karma"
+    clean:         loadModule "clean"
+    connect:       loadModule "connect"
+    shell:         loadModule "shell"
+    devUpdate:     loadModule "devUpdate"

   grunt.renameTask "regarde", "watch"


Add missing symlink point

Add missing symlink point

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -64,7 +64,7 @@

     symlink:
       eventric:
-        target: '.'
+        target: '..'
         link: 'node_modules/eventric'
         options:
           overwrite: true


Only log warning if error occurs

Only log warning if error occurs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -156,9 +156,10 @@
       test:
         command: "#{path.join(contentsDir, 'MacOS', 'Atom')} --test --resource-path=#{__dirname}"
         options:
-          stdout: false
-          stderr: false
-          callback: (error, stdout, stderr) -> grunt.warn('Specs failed')
+          stdout: true
+          stderr: true
+          callback: (error, stdout, stderr) ->
+            grunt.warn('Specs failed') if error?

   grunt.loadNpmTasks('grunt-coffeelint')
   grunt.loadNpmTasks('grunt-lesslint')


Deploy to DPC

Deploy to DPC

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -89,7 +89,7 @@
                 message: 'Built from %sourceCommit% on branch %sourceBranch%'
             pages:
                 options:
-                    remote: 'git@github.com:analytic-engine/lambda-days.2015.git'
+                    remote: 'git@github.com:analytic-engine/DPC.2015.git'
                     branch: 'gh-pages'


grunfile

grunfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -23,11 +23,14 @@
           "site.css": [ "css/main.less" ]
       release:
         options:
+          paths: ["bower_components/bootstrap/less"]
           compress: true
+        files:
+          "site.css": [ "css/main.less" ]

     coffeelint:
       app: ["{,*/}*.coffee"]

   grunt.registerTask "run", ["watch"]
-  grunt.registerTask "default", ["run"]
+  grunt.registerTask "default", ["less:release"]


fix Gruntfile (new API)

fix Gruntfile (new API)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,12 +25,12 @@

     pathfinder:
       requirejs:
-        files:
+        paths:
           js: ['test/dir/**/*.js']
         output: 'tmp/requirejs_output.js'
         template: 'test/requirejs_template.js'
       importless:
-        files:
+        paths:
           styles: ['test/dir/**/*.{css,less}']
         output: 'tmp/importless_output.less'
         template: 'test/importless_template.less'


feat(Gruntfile.coffee): More information in SauceLabs test task

The tags for unit tests now always contain the current SAUCE_USERNAME,
 and a `build` option was added per SauceLab's recommendation in order
 to generate the correct badge.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -499,9 +499,10 @@
                     tunneled: true
                     concurrency: 3
                     identifier: process.env.TRAVIS_JOB_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
-                    tags: [ process.env.TRAVIS_BRANCH || "local :: " + process.env.SAUCE_USERNAME ]
+                    tags: [ process.env.SAUCE_USERNAME+"@"+process.env.TRAVIS_BRANCH || process.env.SAUCE_USERNAME+"@local"]
                     testname: 'Unit Tests'
                     detailedError: false
+                    build: process.env.TRAVIS_BUILD_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
                     browsers: browsers
                     ## onTestComplete: (status, page, config, browser) ->
                     ##     done = @async()
@@ -511,7 +512,7 @@
                     ##         res = JSON.parse res
                     ##         res.browser = config

-                    ##         grunt.log.debug '[%s] Results: %j', config.prefix, res
+                    ##         grunt.log.debug '[%s] Results: %j', config.prefix, res

                     ##         data =
                     ##             'custom-data':
@@ -578,7 +579,7 @@
             Details on contributing pull requests found here: \n
             https://github.com/Widen/fine-uploader/blob/master/CONTRIBUTING.md\n
             '''
-
+
     # Travis' own test
     # ----------
     grunt.registerTask 'travis-sauce', 'Run tests on Saucelabs', [
@@ -617,7 +618,7 @@
         'connect:root_server'
     ]

-
+
     # Build
     # ----------
     # @verify


refactoring known routes

refactoring known routes

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,13 +3,14 @@
 	crypto = require 'crypto'

 	routes =
-		'/signer': (req, res, next) ->
-			signer = crypto.createSign "RSA-SHA1"
-			signer.update req.params.baseString
-			signature = signer.sign grunt.file.read("test/rsakeys/cert.priv.key"), "base64"
-			res.setHeader "Content-Type", "application/json; charset=UTF-8"
-			res.end JSON.stringify({signature: signature})
-			return
+		POST:
+			'/signer': (req, res, next) ->
+				signer = crypto.createSign "RSA-SHA1"
+				signer.update req.params.baseString
+				signature = signer.sign grunt.file.read("test/rsakeys/cert.priv.key"), "base64"
+				res.setHeader "Content-Type", "application/json; charset=UTF-8"
+				res.end JSON.stringify({signature: signature})
+				return

 	helpers =
 		middlewares:
@@ -27,7 +28,7 @@
 					next()
 				return
 			known_route_service: (req, res, next) ->
-				service = routes[req.url]
+				service = routes[req.method]?[req.url]
 				return next() unless service?
 				service req, res, next
 				return


Check local storage on load; use explicit condition syntax.

--- services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
+++ services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
@@ -19,7 +19,7 @@
 			openSubView: $scope.SubViews.CUR_FILE
 			overview:
 				loading: false
-				docsCollapsedState: {}
+				docsCollapsedState: JSON.parse(localStorage("docs_collapsed_state:#{$scope.project_id}")) or {}
 			dropdown:
 				loading: false
 			commentThreads: {}
@@ -28,13 +28,11 @@
 			rendererData: {}
 			loadingThreads: false

-		$scope.$on "project:joined", () ->
-			$scope.reviewPanel.overview.docsCollapsedState = JSON.parse(localStorage("docs_collapsed_state:#{$scope.project_id}")) or {}
-
 		window.addEventListener "beforeunload", () ->
 			collapsedStates = {}
 			for doc, state of $scope.reviewPanel.overview.docsCollapsedState
-				collapsedStates[doc] = state if state is true
+				if state
+					collapsedStates[doc] = state
 			valToStore = if Object.keys(collapsedStates).length > 0 then JSON.stringify(collapsedStates) else null
 			localStorage("docs_collapsed_state:#{$scope.project_id}", valToStore)


Added macro examples

Added macro examples

also added new function assignment shorthand approach - see use of
map(token)

--- notes.coffee
+++ notes.coffee
@@ -23,3 +23,15 @@
 #question: should always put after expression to make parsing of dictionary literal easier?
 var x = 2 ** 2 //Math.pow()
 default const
+
+
+type callback  = (err: boolean, result: string) -> void
+
+graph = graph :: a -> b
+constraint = eq :: a + b ** 2
+username = "Domenic Denicola";
+tag = "& is a fun tag";
+macro require -> arguments[0].split(",").map(token) -> string :: "var ${token} = require(${token})"
+html (strings, values...) -> return output where for(i,s in strings) output += s + encodeURIComponent(values[i])
+console.log(html :: <b>${username} says</b>: ${tag})
+require :: express, esprima, redis


Add: Link editor opens when InfoWindow is displaying

--- src/main/webapp/lib/views/MapLinkView.coffee
+++ src/main/webapp/lib/views/MapLinkView.coffee
@@ -82,7 +82,7 @@
   # publish polyline google events
   _publishGoogleEvents: ->
     gme = google.maps.event
-    @dblclckHandler = gme.addListener(@link, 'dblclick', (evt) =>
+    @dblclckHandler = gme.addListener(@link, 'dblclick', (evt) =>
       @model.set_editor_show(true)
       evt.stop()
     )
@@ -99,12 +99,14 @@

   # this is the rollover window for the link
   _createInfoWindow: ->
-    google.maps.event.addListener(@link, 'mouseover', (e) =>
+    gme = google.maps.event
+    @mOHandler = gme.addListener(@link, 'mouseover', (e) =>
       if(not @_isInfoWindowOpen())
         @iWindow.setContent(@getLinkRollOverInfo())
-        @iWindow.setPosition(e.latLng)
-        trackMouseoverTimeout = setTimeout((() => @iWindow.open($a.map)), 1000)
-        google.maps.event.addListenerOnce(@link, 'mouseout', (e) =>
+        l = e.latLng
+        @iWindow.setPosition(new google.maps.LatLng(l.lat(),l.lng()+0.0001))
+        trackMouseoverTimeout = setTimeout(( => @iWindow.open($a.map)), 1000)
+        gme.addListenerOnce(@link, 'mouseout', (e) =>
           window.clearTimeout(trackMouseoverTimeout);
           @iWindow.close()
         );


For 'sequential' assignment, at least pick a treatment randomly.

--- lib/connections.coffee
+++ lib/connections.coffee
@@ -193,7 +193,7 @@

   # Create a new experiment
   # TODO find a treatment
-  treatmentId = undefined
+  treatmentId = _.sample Batches.findOne(active: true).treatmentIds
   treatment = Treatments.findOne(treatmentId).name
   newId = TurkServer.Experiment.create treatment,
     assignable: true


chore: Fix tabbing

chore: Fix tabbing

--- lib/grunt/tasks.coffee
+++ lib/grunt/tasks.coffee
@@ -5,50 +5,54 @@

 module.exports = (grunt) ->

-  _ = grunt.util._
-  grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint']
+    _ = grunt.util._
+    grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint']

-  grunt.registerTask 'minify', 'Minify the source javascript and css', [
-    'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3',
-    'uglify:jqueryS3', 'uglify:all']
+    grunt.registerTask 'minify', 'Minify the source javascript and css', [
+        'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3',
+        'uglify:jqueryS3', 'uglify:all']

-  grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', ->
-    util.startKarma.call util, @data, true, @async()
+    grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', ->
+        util.startKarma.call util, @data, true, @async()

-  grunt.registerMultiTask 'autotest', "Re-run karma tests on changes", ->
-    util.startKarma.call util, @data, false, @async()
+    grunt.registerMultiTask 'autotest', "Re-run karma tests on changes", ->
+        util.startKarma.call util, @data, false, @async()

-  grunt.registerTask 'check_pull_req', '', ->
-    util.checkPullRequest()
+    grunt.registerTask 'check_pull_req', '', ->
+        util.checkPullRequest()

-  grunt.registerMultiTask 'saucetests', 'Run Karma tests on sauce', ->
-    self = @
-    done = @async()
-    count = 0
-    results = []
-    success = true
-    # we can only run 3 simultaneous browsers on SauceLabs
-    grunt.util.async.forEachSeries @data.browsers, (browserSet, next) ->
-      browsers = browserSet.join ','
-      port = util.sauceLabsAvailablePorts.pop()
-      args = ["node_modules/karma/bin/karma", "start", self.data.configFile,
-          "--browsers=#{browsers}", "--port=#{port}", "--single-run=true"]
+        active_jobs = (jobs) ->
+            jobs.filter (job) ->
+                job.status == 'in-progress'

-      grunt.util.spawn
-        cmd: 'node'
-        grunt: false
-        args: args
-        opts:
-          stdio: 'inherit'
-      , (error, result, code) ->
-        results[count] = code || 0
-        count++
-        if code != 0
-          success = false
-        next()
-    , (err) ->
-      if err?
-        done err
-      else
-        done success
+    grunt.registerMultiTask 'saucetests', 'Run Karma tests on sauce', ->
+        self = @
+        done = @async()
+        count = 0
+        results = []
+        success = true
+        # we can only run 3 simultaneous browsers on SauceLabs
+        grunt.util.async.forEachSeries @data.browsers, (browserSet, next) ->
+            browsers = browserSet.join ','
+            port = util.sauceLabsAvailablePorts.pop()
+            args = ["node_modules/karma/bin/karma", "start", self.data.configFile,
+                    "--browsers=#{browsers}", "--port=#{port}", "--single-run=true"]

+            grunt.util.spawn
+                cmd: 'node'
+                grunt: false
+                args: args
+                opts:
+                    stdio: 'inherit'
+            , (error, result, code) ->
+                results[count] = code || 0
+                count++
+                if code != 0
+                    success = false
+                next()
+        , (err) ->
+            if err?
+                done err
+            else
+                done success
+


changed narrative model content field to TEXT type

--- server/models/narrative.coffee
+++ server/models/narrative.coffee
@@ -5,7 +5,7 @@
     type: Sequelize.STRING
     allowNull: false
   content:
-    type: Sequelize.STRING
+    type: Sequelize.TEXT
     allowNull: false
   id:
     type: Sequelize.STRING


:art: Remove a redundant comment

:art: Remove a redundant comment

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -17,7 +17,6 @@
     @h = H
     @linters = [] # Values are pushed here from Main::consumeLinter

-    # Bubble
     @_subscriptions.add atom.config.observe 'linter.showErrorInline', (showBubble) =>
       @views.showBubble = showBubble
     @_subscriptions.add atom.config.observe 'linter-plus.lintOnFly', (value) =>


provide a means of making nodevisitors that don't actually copy the tree

--- lib/nodevisitor.coffee
+++ lib/nodevisitor.coffee
@@ -10,14 +10,12 @@
                         rv.push y
         rv

-hasOwn = Object.prototype.hasOwnProperty
-
 exports.NodeVisitor = class NodeVisitor
-        constructor: ->
+        constructor: (@dontcopy) ->

         shallowCopy: (o) ->
                 new_o = Object.create Object.getPrototypeOf o
-                new_o[x] = o[x] for x of o when hasOwn.apply o, [x]
+                new_o[x] = o[x] for x in Object.getOwnPropertyNames o
                 new_o

         visitProgram: (n) ->
@@ -218,7 +216,7 @@
                 debug.indent()
                 debug.log "#{n.type}>"

-                new_n = @shallowCopy n
+                new_n = if @dontcopy then n else @shallowCopy n
                 rv = null
                 switch new_n.type
                         when syntax.Program              then rv = @visitProgram new_n


Be more literate

Be more literate

--- lib/script-view.coffee
+++ lib/script-view.coffee
@@ -180,7 +180,7 @@
     )

   getCwd: ->
-    if not @run_options.cmd_cwd? || @run_options.cmd_cwd == ''
+    if not @run_options.cmd_cwd? or @run_options.cmd_cwd is ''
       atom.project.getPath()
     else
       @run_options.cmd_cwd


:fire: unused

:fire: unused

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -51,10 +51,6 @@
     else
       head

-  getNormalizedHeadScreenPosition: (selection) ->
-    bufferPosition = @getNormalizedHeadBufferPosition(selection)
-    @editor.screenPositionForBufferPosition(bufferPosition)
-
   resetState: ->
     @selectSucceeded = null


:bug: Make it work on Windows again

:bug: Make it work on Windows again

--- lib/typechecker.coffee
+++ lib/typechecker.coffee
@@ -8,6 +8,7 @@
   ActiveFile = Editor?.getPath()
   ScrollSubscription = null
   ScrollTimeout = null
+  Path = require 'path'
   class TypeChecker
     @activate:->
       return unless not Main.Status.TypeChecker
@@ -17,7 +18,7 @@
           clearTimeout ScrollTimeout
           ScrollTimeout = setTimeout(@OnScroll.bind(this),100)
       Subscriptions.push atom.workspace.observeTextEditors (editor)=>
-        Grammar = editor.getGrammar()
+	    Grammar = editor.getGrammar()
         return unless Grammar or Grammar.name isnt 'PHP' or Grammar.name isnt 'Hack' or Grammar.name isnt 'C++'
         editor.buffer.onDidSave (info)=>
           @Lint()
@@ -47,12 +48,17 @@
       ScrollSubscription?.dispose()
     @Lint:->
       Main.V.H.exec(['--json'],null,ActiveFile).then (result)=>
-        try
-          result = JSON.parse(result.stderr.substr(result.stderr.indexOf('{')))
-        catch error
-          console.log "Invalid JSON"
-          console.log result
+        if result.stderr.substr(0,5) is 'Error'
+          setTimeout ->
+            throw new Error(result.stderr.substr(7)); # Throw an error that can not be caught
+          ,0
+          return ;
+        result = JSON.parse(result.stderr.substr(result.stderr.indexOf('{')))
         Errors = result.errors
+        if Main.V.H.config.type is 'remote'
+          Errors.forEach (ErrorEntry)->
+            ErrorEntry.message.forEach (ErrorMessageEntry)->
+              ErrorMessageEntry.path = ErrorMessageEntry.path.replace(Main.V.H.config.remoteDir, atom.project.getPath()).split('/').join(Path.sep)
         try
           @ProcessErrors()
         catch error
@@ -74,7 +80,9 @@
           Color = if LeFirst then 'red' else 'blue'
           LeErrors.push new Main.V.TE(I,TraceEntry.path,TraceEntry.line,TraceEntry.start,TraceEntry.end,Color,Error.message)
           LeFirst = false
-      try setTimeout @OnScroll.bind(this),70
+      setTimeout =>
+		try @OnScroll.bind(this)
+	  ,70
     @OnScroll:->
       RowStart = EditorView.getFirstVisibleScreenRow()
       RowEnd = EditorView.getLastVisibleScreenRow()


Update gulpfile.coffee

Update gulpfile.coffee

if you have templates in subdirectories this will also pull those into the cache!

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -38,7 +38,7 @@
     scripts: ["app/js/app.{coffee,js}", "app/js/**/*.{coffee,js}"] # All .js and .coffee files, starting with app.coffee or app.js
     styles: "app/css/**/*.{scss,css}" # css and scss files
     pages: "app/pages/*.{html,jade,md,markdown}" # All html, jade,and markdown files that can be reached directly
-    templates: "app/templates/*.{html,jade,md,markdown}" # All html, jade, and markdown files used as templates within the app
+    templates: "app/templates/**/*.{html,jade,md,markdown}" # All html, jade, and markdown files used as templates within the app
     images: "app/img/*.{png,jpg,jpeg,gif}" # All image files
     static: "app/static/*.*" # Any other static content such as the favicon


fixed template watch task

fixed template watch task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -53,9 +53,6 @@
   css      :
     main   : 'assets/css/' + dist.name + '.css'
     vendor : []
-  templates:
-    main   : ['**.hbs'
-              'partials/**/**.hbs']

 banner = [ "/**"
            " * <%= pkg.name %> - <%= pkg.description %>"


Fix build script

Fix build script

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -25,7 +25,7 @@
   gulp.src paths.source.js
     .pipe gulp.dest paths.dest.lib

-gulp.task 'build', ['clean', 'coffee']
+gulp.task 'build', ['clean', 'js']

 gulp.task 'default', ['build']

@@ -35,4 +35,4 @@
     .pipe gulp.dest(paths.dest.root)

 gulp.task 'watch', ['build'], ->
-  gulp.watch paths.source.coffee, ['coffee']
+  gulp.watch paths.source.js, ['js']


remove "use strict" as it is not necessary

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -291,7 +291,6 @@
     #
     # necessary to add a callback to pipe (which is used to signal end of task)
     gulpCallback = (obj) ->
-        "use strict"
         stream = new Stream.Transform({objectMode: true})
         stream._transform = (file, unused, callback) ->
             obj()


increase jpg quality from 80 to 85

increase jpg quality from 80 to 85

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -180,7 +180,7 @@
 gulp.task 'image_min', ->
   options = {
     resize: [1440,1440],
-    quality: 80,
+    quality: 85,
     progressive: true,
     compressionLevel: 6,
     sequentialRead: true,


Workaround issue when upgrading from v1.5.9 or older that causes it to use old app files.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -18,6 +18,7 @@
 Q          = require 'q'
 Stream     = require 'stream'
 spawn      = require('child_process').spawn
+glob       = require('glob')

 #
 #
@@ -323,6 +324,16 @@
             console.error(error)
         .then (appPaths) ->
             if appPaths?.length > 0
+                # Work around issue when upgrading from an older version that causes
+                # it to use the old app directory, rather than the new app.asar file.
+                # This is due to electron checking for the app dir first, then the asar.
+                # See: https://github.com/electron/electron/blob/master/lib/browser/init.ts#L84
+                files = glob.sync "#{appPaths[0]}/**/app.asar", {nodir: true}
+                if files.length > 0
+                    resPath = path.dirname files[0]
+                    fs.mkdirSync "#{resPath}/app", {recursive: true}
+                    fs.writeFileSync "#{resPath}/app/package.json", ""
+
                 if process.env.NO_ZIP
                     cb()
                     return deferred.resolve()


Removed gulp file reference that I copied

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -7,139 +7,3 @@
     .pipe plumber()
     .pipe cjsx({ bare: true })
     .pipe gulp.dest('./lib')
-
-#var gulp = require('gulp');
-#var util = require('gulp-util');
-#var argv = require('yargs')
-  #.alias('o', 'optimize')
-  #.argv;
-
-#if (argv.optimize) {
-  #process.env.NODE_ENV = 'production';
-#} else {
-  #process.env.NODE_ENV = 'development';
-#}
-
-#// load plugins
-#var plugins = require('gulp-load-plugins')();
-
-#gulp.task('styles', ['fonts'], function () {
-  #return gulp.src(['assets/styles/style.less', 'assets/styles/style-guide.less'])
-    #.pipe(plugins.sourcemaps.init())
-    #.pipe(plugins.plumber())
-    #.pipe(plugins.less())
-    #.pipe(plugins.autoprefixer("last 2 versions"))
-    #.pipe(plugins.sourcemaps.write())
-    #.pipe(plugins.csslint('assets/styles/csslint.json'))
-    #.pipe(plugins.csslint.reporter())
-    #.pipe(plugins.if(argv.optimize, plugins.minifyCss()))
-    #.pipe(plugins.size({title: 'styles', showFiles: true}))
-    #.pipe(gulp.dest('public/styles'));
-#});
-
-#gulp.task('fonts', function() {
-  #return gulp.src(['./node_modules/font-awesome/fonts/*', './node_modules/bootstrap/fonts/*'])
-    #.pipe(plugins.changed('public/fonts'))
-    #.pipe(gulp.dest('public/fonts'))
-    #.pipe(plugins.size({title: 'fonts', showFiles: true}));
-#});
-
-#// Hack to enable configurable watchify watching
-#var watching = false;
-#gulp.task('enable-watch-mode', function() { watching = true; })
-
-#gulp.task('scripts', plugins.watchify(function (watchify) {
-  #var source = require('vinyl-source-stream');
-
-  #var stream = gulp.src('assets/scripts/bundles/*.cjsx')
-    #.pipe(watchify({
-      #watch: watching,
-      #verbose: true,
-      #extensions: ['.coffee', '.cjsx'],
-      #debug: true,
-      #setup: function(bundle) {
-        #bundle.transform(require('coffee-reactify'));
-
-        #if (argv.optimize) {
-          #bundle.transform(require('envify'));
-        #}
-        #bundle.transform(require('brfs'));
-      #},
-    #}))
-    #.pipe(plugins.plumber())
-    #.pipe(plugins.rename({suffix: "-bundle", extname: ".js"}))
-    #.pipe(plugins.streamify(plugins.size({title: 'bundle', showFiles: true})))
-    #.pipe(gulp.dest('public/scripts'));
-
-    #if (argv.optimize) {
-      #stream = stream
-        #.pipe(plugins.rename({suffix: '-min'}))
-        #.pipe(plugins.streamify(plugins.uglify()))
-        #.pipe(plugins.streamify(plugins.size({title: 'bundle-min', showFiles: true})))
-        #.pipe(gulp.dest('public/scripts'));
-    #}
-
-    #return stream;
-#}));
-
-#gulp.task('images', function () {
-    #return gulp.src('assets/images/**/*')
-    #.pipe(plugins.changed('public/images'))
-        #.pipe(gulp.dest('public/images'))
-        #.pipe(plugins.size({title: 'images', showFiles: true}));
-#});
-
-#gulp.task('clean', function () {
-  #return gulp.src(['public'], { read: false }).pipe(plugins.clean());
-#});
-
-#gulp.task('build', ['styles', 'scripts', 'images']);
-
-#gulp.task('default', ['clean'], function () {
-  #gulp.start('build');
-#});
-
-#gulp.task('connect', ['build'], function () {
-  #require('coffee-script/register');
-  #require('./server').gulpStartServer({
-      #server: {
-        #port: 9000,
-        #views: {
-          #path: 'views',
-          #extension: 'jade',
-          #compileWith: 'jade'
-        #},
-        #base: ''
-      #},
-      #watch: {
-        #compiledDir: __dirname + '/public'
-      #},
-      #liveReload: {
-        #enabled: false
-      #}
-    #}, function(server, app) {
-      #app.use(require('connect-livereload')({ port: 35729 }));
-    #}
-   #);
-#});
-
-#gulp.task('serve', ['build', 'connect'], function () {
-  #//require('opn')('http://localhost:9000');
-#});
-
-#gulp.task('watch', ['enable-watch-mode', 'connect', 'serve'], function () {
-  #var reloadServer = plugins.livereload();
-  #//this is needed to start the live reload server immediately
-  #reloadServer.changed();
-
-  #// watch for changes
-  #gulp.watch([
-    #'public/**/*',
-  #]).on('change', function (file) {
-    #reloadServer.changed(file.path);
-  #});
-
-  #//TODO: this is triggering twice sometimes on save
-  #gulp.watch('assets/styles/**/*.less', ['styles']);
-  #gulp.watch('assets/images/**/*', ['images']);
-#});


feat(funnel): add delay token

feat(funnel): add delay token

--- scripts/gitter-activity-notifier.coffee
+++ scripts/gitter-activity-notifier.coffee
@@ -10,7 +10,7 @@
     gitter = new Gitter(process.env.HUBOT_GITTER2_TOKEN)
     store = {}
     timeoutHandle = null
-    timeoutDuration = 4000
+    timeoutDuration = process.env.GITTER_ACTIVITY_FUNNEL_DELAY || 10000;

     primg = '![](https://goo.gl/hjqlaA)  '
     commentimg = '![](https://goo.gl/8IdEJl)  '


Fix Github username check case-sensitiveness (#476)

--- server/konnectors/githubcommits.coffee
+++ server/konnectors/githubcommits.coffee
@@ -133,10 +133,11 @@
                         log.info "Commit #{commit.sha} not saved: no metadata."
                     callback()

-                else if commit.author.login isnt username
+                else if commit.author.login.toLowerCase() isnt
+                username.toLowerCase()
                     log.info "Commit #{commit.sha} not saved: " + \
                              "user is not author (#{commit.author.login})."
-                    callback('bad credentials')
+                    callback('parsing error')

                 else
                     log.info "Saving commit #{commit.sha}..."


Add HipChat msg for Stripe payments

Add HipChat msg for Stripe payments

--- server/payments/payment_handler.coffee
+++ server/payments/payment_handler.coffee
@@ -307,6 +307,7 @@
         if err
           @logPaymentError(req, 'Stripe incr db error. '+err)
           return @sendDatabaseError(res, err)
+        @sendPaymentHipChatMessage user: req.user, payment: payment
         @sendCreated(res, @formatEntity(req, payment))
       )
     )