Simplify tests uisng startFromFixture and minimizing visits

--- test/javascripts/turbolinks_test.coffee
+++ test/javascripts/turbolinks_test.coffee
@@ -133,16 +133,16 @@

   describe 'head asset tracking', ->
     it 'refreshes page when a data-turbolinks-track value matches but src changes', (done) ->
-      visit url: 'singleScriptInHeadTwo', ->
-        visit url: 'singleScriptInHeadWithDifferentSourceButSameName', ->
-          assert(Turbolinks.fullPageNavigate.called, 'Should perform a full page refresh.')
-          done()
+      startFromFixture('singleScriptInHead')
+      visit url: 'singleScriptInHeadWithDifferentSourceButSameName', ->
+        assert(Turbolinks.fullPageNavigate.called, 'Should perform a full page refresh.')
+        done()

     it 'does not refresh page when new data-turbolinks-track values encountered', (done) ->
-      visit url: 'singleScriptInHead', ->
-        visit url: 'twoScriptsInHead', ->
-          assert(Turbolinks.fullPageNavigate.notCalled, 'Should not perform a full page refresh.')
-          done()
+      startFromFixture('singleScriptInHead')
+      visit url: 'twoScriptsInHead', ->
+        assert(Turbolinks.fullPageNavigate.notCalled, 'Should not perform a full page refresh.')
+        done()

     it 'does not update the browser history stack when a conflict is detected', (done) ->
       startFromFixture('singleScriptInHead')
@@ -191,85 +191,70 @@
         linkTagInserted = sinon.spy()
         $(document).on 'page:after-link-inserted', linkTagInserted

-        visit url: 'noScriptsOrLinkInHead', ->
-          assertLinks([])
-          visit url: 'singleLinkInHead', ->
-            assertLinks(['foo.css'])
-            assert.equal(linkTagInserted.callCount, 1)
-            done()
-
-      it 'inserts link with a new href into empty head on navigation', (done) ->
-        visit url: 'noScriptsOrLinkInHead', ->
-          assertLinks([])
-          visit url: 'singleLinkInHead', ->
-            assertLinks(['foo.css'])
-            done()
-
-      it 'inserts link with a new href into existing head on navigation', (done) ->
         visit url: 'singleLinkInHead', ->
           assertLinks(['foo.css'])
-          visit url: 'twoLinksInHead', ->
-            assertLinks(['foo.css', 'bar.css'])
-            done()
-
-      it 'does not reinsert link with existing href into identical head on navigation', (done) ->
+          assert.equal(linkTagInserted.callCount, 1)
+          done()
+
+      it 'inserts link with a new href into empty head on navigation', (done) ->
         visit url: 'singleLinkInHead', ->
           assertLinks(['foo.css'])
-          visit url: 'singleLinkInHead', ->
-            assertLinks(['foo.css'])
-            done()
+          done()
+
+      it 'inserts link with a new href into existing head on navigation', (done) ->
+        startFromFixture('singleLinkInHead')
+        visit url: 'twoLinksInHead', ->
+          assertLinks(['foo.css', 'bar.css'])
+          done()
+
+      it 'does not reinsert link with existing href into identical head on navigation', (done) ->
+        startFromFixture('singleLinkInHead')
+        visit url: 'singleLinkInHead', ->
+          assertLinks(['foo.css'])
+          done()

     describe 'script tags', ->
       it 'dispatches page:after-script-inserted event when inserting a script on navigation', (done) ->
         scriptTagInserted = sinon.spy()
         $(document).on 'page:after-script-inserted', scriptTagInserted
-
-        visit url: 'noScriptsOrLinkInHead', ->
-          visit url: 'singleScriptInHead', ->
-            assert.equal(scriptTagInserted.callCount, 1)
-            done()
+        visit url: 'singleScriptInHead', ->
+          assert.equal(scriptTagInserted.callCount, 1)
+          done()

       it 'inserts script with a new src into empty head on navigation', (done) ->
-        visit url: 'noScriptsOrLinkInHead', ->
-          assertScripts([])
-          visit url: 'singleScriptInHead', ->
-            assertScripts(['foo.js'])
-            done()
-
-      it 'inserts script with a new src into existing head on navigation', (done) ->
         visit url: 'singleScriptInHead', ->
           assertScripts(['foo.js'])
-          visit url: 'twoScriptsInHead', ->
-            assertScripts(['foo.js', 'bar.js'])
-            done()
+          done()
+
+      it 'inserts script with a new src into existing head on navigation', (done) ->
+        startFromFixture('singleScriptInHead')
+        visit url: 'twoScriptsInHead', ->
+          assertScripts(['foo.js', 'bar.js'])
+          done()

       it 'does not insert duplicate script tag on navigation into identical upstream head', (done) ->
+        startFromFixture('singleScriptInHead')
         visit url: 'singleScriptInHead', ->
           assertScripts(['foo.js'])
-          visit url: 'singleScriptInHead', ->
-            assertScripts(['foo.js'])
-            done()
+          done()

       it 'does not insert duplicate script tag on navigation into superset upstream head', (done) ->
-        visit url: 'singleScriptInHead', ->
-          assertScripts(['foo.js'])
-          visit url: 'twoScriptsInHead', ->
-            assertScripts(['foo.js', 'bar.js'])
-            done()
-
-      it 'does not remove script when navigating to a page with an empty head', (done) ->
-        visit url: 'singleScriptInHead', ->
-          assertScripts(['foo.js'])
-          visit url: 'noScriptsOrLinkInHead', ->
-            assertScripts(['foo.js'])
-            done()
-
-      it 'does not remove script nodes when navigating to a page with less script tags', (done) ->
+        startFromFixture('singleScriptInHead')
         visit url: 'twoScriptsInHead', ->
           assertScripts(['foo.js', 'bar.js'])
-          visit url: 'singleScriptInHead', ->
-            assertScripts(['foo.js', 'bar.js'])
-            done()
+          done()
+
+      it 'does not remove script when navigating to a page with an empty head', (done) ->
+        startFromFixture('singleScriptInHead')
+        visit url: 'noScriptsOrLinkInHead', ->
+          assertScripts(['foo.js'])
+          done()
+
+      it 'does not remove script nodes when navigating to a page with less script tags', (done) ->
+        startFromFixture('twoScriptsInHead')
+        visit url: 'singleScriptInHead', ->
+          assertScripts(['foo.js', 'bar.js'])
+          done()

       describe 'executes scripts in the order they are present in the dom of the upstream document', ->
         beforeEach -> window.actualExecutionOrder = []
@@ -328,11 +313,10 @@
         done()

     it 'head assets are not removed during partial replace', (done) ->
-      visit url: 'singleLinkInHead', ->
+      startFromFixture('singleLinkInHead')
+      visit url: 'twoLinksInHead', options: {partialReplace: true, onlyKeys: ['turbo-area']}, ->
         assertLinks(['foo.css'])
-        visit url: 'twoLinksInHead', options: {partialReplace: true, onlyKeys: ['turbo-area']}, ->
-          assertLinks(['foo.css'])
-          done()
+        done()

     it 'script tags are evaluated when they are the subject of a partial replace', (done) ->
       visit url: 'inlineScriptInBody', options: {partialReplace: true, onlyKeys: ['turbo-area']}, ->


spec: Reading asar file should not leak fd

--- spec/asar-spec.coffee
+++ spec/asar-spec.coffee
@@ -8,6 +8,10 @@

   describe 'node api', ->
     describe 'fs.readFileSync', ->
+      it 'does not leak fd', ->
+        for i in [1..10000]
+          fs.readFileSync(path.join(process.resourcesPath, 'atom.asar', 'renderer', 'api', 'lib', 'ipc.js'))
+
       it 'reads a normal file', ->
         file1 = path.join fixtures, 'asar', 'a.asar', 'file1'
         assert.equal fs.readFileSync(file1).toString().trim(), 'file1'


Add specs for type data attribute

Add specs for type data attribute

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -95,14 +95,17 @@
       expect(tabBar.find('.tab:eq(0) .title').text()).toBe item1.getTitle()
       expect(tabBar.find('.tab:eq(0) .title')).not.toHaveAttr('data-name')
       expect(tabBar.find('.tab:eq(0) .title')).not.toHaveAttr('data-path')
+      expect(tabBar.find('.tab:eq(0)')).toHaveAttr('data-type', 'TestView')

       expect(tabBar.find('.tab:eq(1) .title').text()).toBe editor1.getTitle()
       expect(tabBar.find('.tab:eq(1) .title')).toHaveAttr('data-name', path.basename(editor1.getPath()))
       expect(tabBar.find('.tab:eq(1) .title')).toHaveAttr('data-path', editor1.getPath())
+      expect(tabBar.find('.tab:eq(1)')).toHaveAttr('data-type', 'TextEditor')

       expect(tabBar.find('.tab:eq(2) .title').text()).toBe item2.getTitle()
       expect(tabBar.find('.tab:eq(2) .title')).not.toHaveAttr('data-name')
       expect(tabBar.find('.tab:eq(2) .title')).not.toHaveAttr('data-path')
+      expect(tabBar.find('.tab:eq(2)')).toHaveAttr('data-type', 'TestView')

     it "highlights the tab for the active pane item", ->
       expect(tabBar.find('.tab:eq(2)')).toHaveClass 'active'


Fucking line-wrap, how does it work?

Fucking line-wrap, how does it work?

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -257,6 +257,12 @@
       chunkLen = chunks[n].length
       msg = "#{chunks[n][chunkLen - 1]}#{msg}"
       chunks[n] = chunks[n][...-1]
+
+  # If we somehow have some message left over, shove it
+  # into the last chunk.
+  if msg.length
+    chunks[chunks.length - 1] += msg
+
   return chunks.map (c) -> c.trim()

 formatForTextBubble = (msg, font="./ayp-template-images/arial.ttf", size=12, max=330) ->


Sim: Set timeout on API requests to the site.

--- server/auth.coffee
+++ server/auth.coffee
@@ -4,7 +4,7 @@

 request = require 'request'
 authHeaders = {AUTHUSER: process.env.AUTHUSER, AUTHTOKEN: process.env.AUTHTOKEN}
-request = request.defaults(json: true, headers: authHeaders)
+request = request.defaults(json: true, headers: authHeaders, timeout: 30 * 1000)

 config = require './config'
 redis = require './redis'


Defect DE16995 - Iteration summary app doesn't display test results from within Test Sets

--- iterationsummary/IterationSummaryAppSpec.coffee
+++ iterationsummary/IterationSummaryAppSpec.coffee
@@ -6,7 +6,7 @@

 describe 'Rally.apps.iterationsummary.IterationSummaryApp', ->
   helpers
-    _getContext: (initialValues) ->
+    getContext: (initialValues) ->
       globalContext = Rally.environment.getContext()

       Ext.create('Rally.app.Context', {
@@ -18,25 +18,30 @@
         }, initialValues)
       })

-    _createApp: (initialValues) ->
+    createApp: (initialValues) ->
       @container = Ext.create('Ext.Container', {
         renderTo:'testDiv'
       })

       app = Ext.create('Rally.apps.iterationsummary.IterationSummaryApp', {
-        context: @_getContext(initialValues)
+        context: @getContext(initialValues)
       })

       @container.add(app)
       @waitForComponentReady(app)

-    _stubApp: (config) ->
+    stubApp: (config) ->
       @stubPromiseFunction(Rally.apps.iterationsummary.IterationSummaryApp.prototype, 'getScheduleStates',
         (config.scheduleStates || ["Defined", "In-Progress", "Completed", "Accepted"]))
       @stub(Rally.apps.iterationsummary.IterationSummaryApp.prototype, 'getStartDate').returns(config.startDate || new Date())
       @stub(Rally.apps.iterationsummary.IterationSummaryApp.prototype, 'getEndDate').returns(config.endDate || new Date())
       @_stubIterationQuery(config.tzOffset || 0)
       @stub(Rally.util.Timebox, 'getToday').returns(config.today || new Date())
+
+    stubTimeBoxInfo: (orientation) ->
+      timeOrientation: orientation
+      timeboxLength: 4
+      daysRemaining: 0

     _stubIterationQuery: (tzOffset = 0) ->
       @ajax.whenQuerying('iteration').respondWith([{
@@ -51,7 +56,7 @@
                 tzOffset: tzOffset * 60
       })

-    _prepareNoneAcceptedData: ->
+    prepareNoneAcceptedData: ->
       @ajax.whenQuerying('userstory').respondWith([{
           PlanEstimate:1.0
           ScheduleState:"Backlog"
@@ -191,7 +196,7 @@
         }
       ])

-    _prepareFiveDefectsData: ->
+    prepareFiveDefectsData: ->
       @ajax.whenQuerying('userstory').respondWith([
         {
           Summary:
@@ -261,7 +266,7 @@
         }
       ])

-    _prepareNoActiveDefectsData: ->
+    prepareNoActiveDefectsData: ->
       @ajax.whenQuerying('userstory').respondWith([
         {
           PlanEstimate:1.0
@@ -331,7 +336,7 @@
         }
       ])

-    _prepareSomeAcceptedData: ->
+    prepareSomeAcceptedData: ->
       @ajax.whenQuerying('userstory').respondWith([
         {
           PlanEstimate:1.0
@@ -472,7 +477,7 @@
         }
       ])

-    _prepareAllAcceptedData: ->
+    prepareAllAcceptedData: ->
       @ajax.whenQuerying('userstory').respondWith([
         {
           PlanEstimate:1.0
@@ -632,7 +637,7 @@
         }
       ])

-    _prepareSomeFailingTestsData: ->
+    prepareSomeFailingTestsData: ->
       @ajax.whenQuerying('userstory').respondWith([
         {
           Summary:
@@ -672,7 +677,7 @@
         }
       ])

-    _prepareTestSetData: (testSetSummaryVerdicts) ->
+    prepareTestSetData: (testSetSummaryVerdicts) ->
       @ajax.whenQuerying('userstory').respondWith()
       @ajax.whenQuerying('defectsuite').respondWith()
       @ajax.whenQuerying('defect').respondWith()
@@ -687,7 +692,6 @@

   beforeEach ->
     @_stubIterationQuery()
-    # @stubDefer()

   afterEach ->
     @container?.destroy()
@@ -707,57 +711,51 @@
     )
     displayStatusRowsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_displayStatusRows')

-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>

       expect(displayStatusRowsSpy).not.toHaveBeenCalled()

   it "calls wsapidatastore if models are available", ->
     displayStatusRowsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_displayStatusRows')

-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>

       @waitForCallback(displayStatusRowsSpy)

   it "getPostAcceptedState with no before or after state", ->
     @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith ["Defined", "In-Progress", "Completed", "Accepted"]
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       app._getPostAcceptedState().always (postAcceptedState) ->
         expect(postAcceptedState).toBeNull()

   it "getPostAcceptedState with only a before state", ->
     @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith ["Idea", "Defined", "In-Progress", "Completed", "Accepted"]
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       app._getPostAcceptedState().always (postAcceptedState) ->
         expect(postAcceptedState).toBeNull()

   it "getPostAcceptedState with only an after state", ->
     @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith ["Defined", "In-Progress", "Completed", "Accepted", "Released"]
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       app._getPostAcceptedState().always (postAcceptedState) ->
         expect(postAcceptedState).toBe "Released"

   it "getPostAcceptedState with a before and after state", ->
     @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith ["Idea", "Defined", "In-Progress", "Completed", "Accepted", "Really Really Done"]
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       app._getPostAcceptedState().always (postAcceptedState) ->
         expect(postAcceptedState).toBe "Really Really Done"

   it "does not aggregate testset and defectsuite data for HS subscriptions", ->

-    @_prepareFiveDefectsData()
-
-    # @_stubApp(
-    #   startDate:new Date(2011, 4, 6)
-    #   endDate:new Date(2011, 4, 20, 23, 59, 59)
-    #   today:new Date(2011, 5, 16)
-    # )
+    @prepareFiveDefectsData()

     @stub(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_isHsOrTeamEdition').returns true
     testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')
     defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')

     app = Ext.create('Rally.apps.iterationsummary.IterationSummaryApp',
-      context: @_getContext()
+      context: @getContext()
     )
     @waitForComponentReady(app).then ->

@@ -770,16 +768,37 @@
       expect(testsSpy).not.toHaveBeenCalled()

   it "does aggregate testset data for UE or EE subscriptions", ->
-    testPassingSelector = '.x4-component.header.testsPassing'
-
-    @_prepareTestSetData(Pass: 2, Fail: 1, Inconclusive: 1)
-    @_createApp({}).then (app) =>
-      @waitForVisible(css: testPassingSelector).then =>
-        expect(Ext.DomQuery.selectNode(testPassingSelector).innerHTML).toContain "50% Tests Passing"
+    @prepareTestSetData(Pass: 2, Fail: 1, Inconclusive: 1)
+    @createApp({}).then (app) =>
+      @waitForVisible(
+        css: '.x4-component.header.testsPassing'
+        text: "50% Tests Passing"
+      )
+
+  it "sets timeBoxInfo every time iteraton scope is changed", ->
+    @prepareTestSetData(Pass: 2, Fail: 1, Inconclusive: 1)
+    timeBoxInfoStub = @stub(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_calculateTimeBoxInfo')
+    timeBoxInfoStub.returns(@stubTimeBoxInfo('past'))
+
+    @createApp({}).then (app) =>
+      @waitForVisible(
+        css: '.timeboxStatusRow'
+        text: "50% Tests Passing"
+      ).then =>
+        expect(timeBoxInfoStub.callCount).toBe 1
+
+        timeBoxInfoStub.returns(@stubTimeBoxInfo('future'))
+        app.onScopeChange()
+        @waitForComponentReady(app).then =>
+          @waitForNotVisible(
+            css: '.timeboxStatusRow'
+            text: "50% Tests Passing"
+          ).then =>
+            expect(timeBoxInfoStub.callCount).toBe 2

   it "refreshes app on objectUpdate of artifacts", ->
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       addContentStub = @stub(app, 'addContent')

       messageBus = Rally.environment.getMessageBus()
@@ -789,16 +808,16 @@
       expect(addContentStub.callCount).toBe 5

   it "does not refresh app on objectUpdate of non-artifacts", ->
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       addContentSpy = @spy(app, 'addContent')

       Rally.environment.getMessageBus().publish(Rally.Message.objectUpdate, @mom.getRecord('Release'))

       expect(addContentSpy).not.toHaveBeenCalled()

-  it "only calculates timebox info once", ->
+  it "only fetches iteration schema once", ->
     httpGetSpy = @spy(Rally.env.IoProvider.prototype, 'httpGet')
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       expect(httpGetSpy).toHaveBeenCalledOnce()

       app.calculateTimeboxInfo().then ->
@@ -807,7 +826,7 @@
   it "only gets scheduleStates once", ->
     scheduleStates = ['Defined', 'In-Progress', 'Completed']
     ajaxRequest = @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith scheduleStates
-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>
       expect(ajaxRequest).toHaveBeenCalledOnce()

       ajaxRequest = @ajax.whenQueryingAllowedValues('userstory', 'ScheduleState').respondWith ['Defined', 'In-Progress', 'Accepted']
@@ -880,7 +899,7 @@
       }
     ])

-    @_stubApp({
+    @stubApp({
       startDate:new Date(2011, 4, 13),
       endDate:new Date(2011, 4, 20, 23, 59, 59),
       today:new Date(2011, 4, 14)
@@ -888,7 +907,7 @@

     acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-    @_createApp({}).then (app) =>
+    @createApp({}).then (app) =>

       summedPlanEstimate = 0
       Ext.Array.forEach(app.results.userstory, (s) ->
@@ -905,9 +924,9 @@

     it "displays no stats for future timebox", ->

-      @_prepareNoneAcceptedData()
-
-      @_stubApp({
+      @prepareNoneAcceptedData()
+
+      @stubApp({
         startDate:new Date(2051, 5, 15),
         endDate:new Date(2051, 5, 16, 23, 59, 59)
       })
@@ -916,7 +935,7 @@
       defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')
       testSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.then (configAcceptance) ->
           expect(!!configAcceptance.title).toBeFalsy()
@@ -935,9 +954,9 @@

     it "displays no alarming acceptance stats when timebox has just started", ->

-      @_prepareNoneAcceptedData()
-
-      @_stubApp(
+      @prepareNoneAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 13)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 14)
@@ -945,7 +964,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "0% Accepted"
@@ -955,9 +974,9 @@

     it "does not display defect stats when no active defects for current timebox", ->

-      @_prepareNoActiveDefectsData()
-
-      @_stubApp(
+      @prepareNoActiveDefectsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 13)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 14)
@@ -965,7 +984,7 @@

       defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configDefects = defectSpy.firstCall.returnValue
         expect(!!configDefects.title).toBeFalsy()
@@ -974,9 +993,9 @@

     it "displays defect stats for current timebox", ->

-      @_prepareFiveDefectsData()
-
-      @_stubApp(
+      @prepareFiveDefectsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 13)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 14)
@@ -984,7 +1003,7 @@

       defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configDefects = defectSpy.firstCall.returnValue
         expect(configDefects.title).toBe "5 Active Defects"
@@ -993,9 +1012,9 @@

     it "displays acceptance warning 5 days into a long timebox", ->

-      @_prepareNoneAcceptedData()
-
-      @_stubApp(
+      @prepareNoneAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 1)
         endDate:new Date(2011, 4, 28, 23, 59, 59)
         today:new Date(2011, 4, 20)
@@ -1003,7 +1022,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "0% Accepted"
@@ -1013,9 +1032,9 @@

     it "displays acceptance pending 5 days into a long timebox", ->

-      @_prepareSomeAcceptedData()
-
-      @_stubApp(
+      @prepareSomeAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 1)
         endDate:new Date(2011, 4, 28, 23, 59, 59)
         today:new Date(2011, 4, 20)
@@ -1023,7 +1042,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "48% Accepted"
@@ -1033,9 +1052,9 @@

     it "displays acceptance pending halfway through a short timebox", ->

-      @_prepareSomeAcceptedData()
-
-      @_stubApp(
+      @prepareSomeAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 2)
         endDate:new Date(2011, 4, 6, 23, 59, 59)
         today:new Date(2011, 4, 5)
@@ -1043,7 +1062,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "48% Accepted"
@@ -1053,9 +1072,9 @@

     it "displays acceptance error when all work not accepted from past timebox", ->

-      @_prepareSomeAcceptedData()
-
-      @_stubApp(
+      @prepareSomeAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 2)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 5)
@@ -1063,7 +1082,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "48% Accepted"
@@ -1073,9 +1092,9 @@

     it "displays defect error when active defects remain from past timebox", ->

-      @_prepareFiveDefectsData()
-
-      @_stubApp(
+      @prepareFiveDefectsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 13)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 14)
@@ -1083,7 +1102,7 @@

       defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configDefects = defectSpy.firstCall.returnValue
         expect(configDefects.title).toBe "5 Active Defects"
@@ -1092,9 +1111,9 @@

     it "does not display defect stats when no active defects for past timebox", ->

-      @_prepareNoActiveDefectsData()
-
-      @_stubApp(
+      @prepareNoActiveDefectsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 13)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 14)
@@ -1102,7 +1121,7 @@

       defectSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getDefectsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configDefects = defectSpy.firstCall.returnValue
         expect(!!configDefects.title).toBeFalsy()
@@ -1110,9 +1129,9 @@
         expect(!!configDefects.message).toBeFalsy()

     it "displays positive acceptance stats when all work accepted for past timebox", ->
-      @_prepareAllAcceptedData()
-
-      @_stubApp(
+      @prepareAllAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 2)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 5)
@@ -1120,7 +1139,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "100% Accepted"
@@ -1289,7 +1308,7 @@
         }
       ])

-      @_stubApp({
+      @stubApp({
         scheduleStates:["Defined", "In-Progress", "Completed", "Accepted", "Released"],
         startDate:new Date(2011, 4, 2),
         endDate:new Date(2011, 4, 20, 23, 59, 59),
@@ -1298,7 +1317,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "100% Accepted"
@@ -1448,7 +1467,7 @@
         }
       ])

-      @_stubApp(
+      @stubApp(
         startDate:new Date(2011, 4, 2)
         endDate:new Date(2011, 4, 6, 23, 59, 59)
         today:new Date(2011, 4, 5)
@@ -1456,7 +1475,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "100% Accepted"
@@ -1466,9 +1485,9 @@

     it "displays gently scolding for past timebox with work accepted late", ->

-      @_prepareAllAcceptedData()
-
-      @_stubApp(
+      @prepareAllAcceptedData()
+
+      @stubApp(
         startDate:new Date(2011, 3, 2)
         endDate:new Date(2011, 3, 20, 23, 59, 59)
         today:new Date(2011, 4, 20)
@@ -1476,7 +1495,7 @@

       acceptanceSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getAcceptanceConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         acceptanceSpy.firstCall.returnValue.always (configAcceptance) ->
           expect(configAcceptance.title).toBe "100% Accepted"
@@ -1520,7 +1539,7 @@
               Fail: 0
       ])

-      @_stubApp(
+      @stubApp(
         startDate:new Date(2011, 4, 2)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 5)
@@ -1528,7 +1547,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(!!configTests.title).toBeFalsy()
@@ -1537,9 +1556,9 @@

     it "displays pending when less than halfway or 5 days", ->

-      @_prepareSomeFailingTestsData()
-
-      @_stubApp(
+      @prepareSomeFailingTestsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 6)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 8)
@@ -1547,7 +1566,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(configTests.title).toBe "33% Tests Passing"
@@ -1587,7 +1606,7 @@
               Fail: 3
       ])

-      @_stubApp(
+      @stubApp(
         startDate:new Date(2011, 4, 6)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 16)
@@ -1595,7 +1614,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(configTests.title).toBe "0% Tests Passing"
@@ -1605,9 +1624,9 @@

     it "displays pending if some failing tests during current timebox", ->

-      @_prepareSomeFailingTestsData()
-
-      @_stubApp(
+      @prepareSomeFailingTestsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 6)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 16)
@@ -1615,7 +1634,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(configTests.title).toBe "33% Tests Passing"
@@ -1655,7 +1674,7 @@
               Fail: 0
       ])

-      @_stubApp(
+      @stubApp(
         startDate:new Date(2011, 4, 6)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 4, 16)
@@ -1663,7 +1682,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(configTests.title).toBe "100% Tests Passing"
@@ -1673,9 +1692,9 @@

     it "displays error if any failing tests during past timeboxes", ->

-      @_prepareSomeFailingTestsData()
-
-      @_stubApp(
+      @prepareSomeFailingTestsData()
+
+      @stubApp(
         startDate:new Date(2011, 4, 6)
         endDate:new Date(2011, 4, 20, 23, 59, 59)
         today:new Date(2011, 5, 16)
@@ -1683,7 +1702,7 @@

       testsSpy = @spy(Rally.apps.iterationsummary.IterationSummaryApp.prototype, '_getTestsConfigObject')

-      @_createApp({}).then (app) =>
+      @createApp({}).then (app) =>

         configTests = testsSpy.firstCall.returnValue
         expect(configTests.title).toBe "33% Tests Passing"


Doc in methods

Doc in methods

--- source/core/output.coffee
+++ source/core/output.coffee
@@ -1,7 +1,7 @@
 ###
-@TODO
+HTML Renderer

-@namespace Atoms.COre
+@namespace Atoms.Core
 @class Output

 @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
@@ -13,16 +13,31 @@
   method: "append"
   ifs   : []

+  ###
+  Insert content to the end of each element in the set of matched elements.
+  @method append
+  ###
   append: -> @render "append"

+  ###
+  Insert content to the beginning of each element in the set of matched elements.
+  @method prepend
+  ###
   prepend: -> @render "prepend"

+  ###
+  Set the HTML contents of each element in the set of matched elements.
+  @method html
+  ###
   html: -> @render "html"

+  ###
+  Render element with the instance @template and @attributes.
+  @method render
+  ###
   render: ->
     throw "No template defined." unless @template?
     throw "No parent assigned." unless @attributes.parent?
-
     @_setIfBindings() if @ifs.length > 0
     @el = Atoms.$ Atoms.Core.render(@template)(@attributes)
     @parent = Atoms.$ @attributes.parent


Fix redirect from global is not on app subdomain

--- assets/javascripts/routers/posts.js.coffee
+++ assets/javascripts/routers/posts.js.coffee
@@ -52,7 +52,7 @@

   globalFeed: =>
     unless TentStatus.isAppSubdomain()
-      return @index()
+      return @navigate('/', {trigger: true})
     TentStatus.setPageTitle "Tent.is Global Feed"
     @view = new TentStatus.Views.GlobalFeed


Handle dones

Handle dones

--- spec/IframeRuntime.coffee
+++ spec/IframeRuntime.coffee
@@ -83,8 +83,11 @@
             runtime.removeListener 'error', receiveError
             runtime.removeListener 'execution', receive
             done()
+            done = ->
         receiveError = (err) ->
+          console.log err
           done err
+          done = ->
         runtime.on 'network', (msg) ->
           return unless msg.command is 'error'
           receiveError new Error msg.payload.message


:bug: Invoke `reload` instead of non-existant `restart`

Fix #12665

--- spec/atom-reporter.coffee
+++ spec/atom-reporter.coffee
@@ -173,7 +173,7 @@
     listen document, 'click', '.stack-trace', (event) ->
       event.currentTarget.classList.toggle('expanded')

-    @reloadButton.addEventListener('click', -> require('electron').ipcRenderer.send('call-window-method', 'restart'))
+    @reloadButton.addEventListener('click', -> require('electron').ipcRenderer.send('call-window-method', 'reload'))

   updateSpecCounts: ->
     if @skippedCount


Use getActiveTextEditor instead of getActiveEditor

--- spec/autosave-spec.coffee
+++ spec/autosave-spec.coffee
@@ -14,7 +14,7 @@
       atom.workspace.open('sample.js')

     runs ->
-      initialActiveItem = atom.workspace.getActiveEditor()
+      initialActiveItem = atom.workspace.getActiveTextEditor()

     waitsForPromise ->
       atom.project.open('sample.coffee').then (o) ->
@@ -108,7 +108,7 @@
           atom.workspace.open()

         runs ->
-          pathLessItem = atom.workspace.getActiveEditor()
+          pathLessItem = atom.workspace.getActiveTextEditor()
           spyOn(pathLessItem, 'save').andCallThrough()
           pathLessItem.setText('text!')
           expect(pathLessItem.getUri()).toBeFalsy()


After deleting a chapter, focus on the previous element. (Better than nothing...)

--- src/bootstrap-elm.coffee
+++ src/bootstrap-elm.coffee
@@ -200,6 +200,10 @@
   document.getElementById "edit-chapter-body-#{id}"

 deleteChapter = (chapter) ->
+  # TODO focus on the END of the previous sibling, once I've figured out
+  # how to do that reliably (e.g. when the user presses the up arrow...)
+  getChapterHeadingElem(chapter.id).previousSibling.focus()
+
   sync.deleteChapter(chapter).done (newChapters) ->
     app.ports.setChapters.send newChapters


another feature request in issue #4 unvealed a bug with flushing on end in paused state

--- src/buffer-stream.coffee
+++ src/buffer-stream.coffee
@@ -64,7 +64,7 @@
         @emit('drain') if @paused
         @paused = off
         @emit('resume') if @size is 'none'
-        @flush() if not @enabled or @size is 'none'
+        @flush() if not @enabled or @finished
         if @finished
             @emit('end')
             @emit('close')


Rearrange cloudstorm.js.coffee

Rearrange cloudstorm.js.coffee

--- src/cloudstorm.js.coffee
+++ src/cloudstorm.js.coffee
@@ -10,10 +10,13 @@
   'cloudStorm.time'
   'cloudStorm.datetime'
   'cloudStorm.enum'
+  'cloudStorm.field'
+  'cloudStorm.form'
   'cloudStorm.index'
   'cloudStorm.index.sidePanel'
   'cloudStorm.itemList'
   'cloudStorm.main'
+  'cloudStorm.menu'
   'cloudStorm.number'
   'cloudStorm.resourceInput'
   'cloudStorm.textfield'


refactor(productImport): refactor to reduce dependence on another class.

Rather than referring twice to @validator.states, now assign this to @states following validator.fetchResources.

--- src/coffee/import.coffee
+++ src/coffee/import.coffee
@@ -112,6 +112,7 @@

     @validator.fetchResources(@resourceCache)
     .then (resources) =>
+      @states = @validator.states
       @resourceCache = resources

       if _.isString(csv) || csv instanceof Buffer
@@ -418,7 +419,7 @@
   update: (product, existingProduct, id2SameForAllAttributes, header, rowIndex, publish) ->
     product.categoryOrderHints = @_mergeCategoryOrderHints existingProduct, product
     if (not product.state? and not existingProduct.state? and @defaultState?)
-      product.state = { typeId : "state", id : @validator.states.key2id[@defaultState] }
+      product.state = { typeId : "state", id : @states.key2id[@defaultState] }
     allSameValueAttributes = id2SameForAllAttributes[product.productType.id]
     config = [
       { type: 'base', group: 'white' }
@@ -504,7 +505,7 @@

   create: (product, rowIndex, publish = false) ->
     if (not product.state? and @defaultState?)
-      product.state = { typeId : "state", id : @validator.states.key2id[@defaultState] }
+      product.state = { typeId : "state", id : @states.key2id[@defaultState] }
     if @dryRun
       Promise.resolve "[row #{rowIndex}] DRY-RUN - create new product."
     else if @updatesOnly


Return what function returns from measure

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -99,9 +99,10 @@

   measure: (description, fn) ->
     start = new Date().getTime()
-    fn()
+    value = fn()
     result = new Date().getTime() - start
     console.log description, result
+    value

 window[key] = value for key, value of windowAdditions
 window.startup()


Allow for slack buttons to toggle for watching and assignment

--- src/adapters/slack.coffee
+++ src/adapters/slack.coffee
@@ -17,8 +17,8 @@
       catch e
         return

-      response = @onButtonActions payload
-      res.json response
+      @onButtonActions(payload).then ->
+        res.json payload.original_message

     # Since the slack client used by hubot-slack has not yet been updated
     # to the latest version, we do not get events for reactions.
@@ -137,44 +137,50 @@
               user: msg.user

   onButtonActions: (payload) ->
-    key = payload.callback_id
-    user = payload.user
-    channel = payload.channel
-    msg = payload.original_message
-
-    for action in payload.actions
+    Promise.all payload.actions.map (action) => @handleButtonAction payload, action
+
+  handleButtonAction: (payload, action) ->
+    return new Promise (resolve, reject) =>
+      key = payload.callback_id
+      user = payload.user
+      msg = payload.original_message
+      envelope =
+        robot: emit: -> #Discard emitted messages, slack attachments are enough communication
+        message: user: user
+
       switch action.name
         when "rank"
-          Jira.Rank.forTicketKeyByDirection key, "up",
-            robot: @robot
-            message:
-              room: channel
-              user: user
+          Jira.Rank.forTicketKeyByDirection key, "up", envelope
           msg.attachments.push text: "<@#{user.id}|#{user.name}> ranked this ticket to the top"
+          resolve()
         when "watch"
-          Jira.Watch.forTicketKeyForPerson key, user.name,
-            robot: @robot
-            message:
-              room: channel
-              user: user
-          msg.attachments.push text: "<@#{user.id}|#{user.name}> is now watching this ticket"
+          Jira.Create.fromKey(key)
+          .then (ticket) =>
+            watchers = Utils.lookupChatUsersWithJira ticket.watchers
+            if _(watchers).findWhere(id: user.id)
+              msg.attachments.push text: "<@#{user.id}|#{user.name}> has stopped watching this ticket"
+              Jira.Watch.forTicketKeyRemovePerson key, null, envelope
+            else
+              msg.attachments.push text: "<@#{user.id}|#{user.name}> is now watching this ticket"
+              Jira.Watch.forTicketKeyForPerson key, user.name, envelope
+            resolve()
         when "assign"
-          Jira.Assign.forTicketKeyToPerson key, user.name,
-            robot: @robot
-            message:
-              room: channel
-              user: user
-          msg.attachments.push text: "<@#{user.id}|#{user.name}> is now assigned to this ticket"
+          Jira.Create.fromKey(key)
+          .then (ticket) =>
+            assignee = Utils.lookupChatUserWithJira ticket.fields.assignee
+            if assignee and assignee.id is user.id
+              Jira.Assign.forTicketKeyToUnassigned key, envelope
+              msg.attachments.push text: "<@#{user.id}|#{user.name}> has unassigned themself"
+            else
+              Jira.Assign.forTicketKeyToPerson key, user.name, envelope
+              msg.attachments.push text: "<@#{user.id}|#{user.name}> is now assigned to this ticket"
+            resolve()
         when "devready", "inprogress"
           result = Utils.fuzzyFind action.value, Config.maps.transitions, ['jira']
           if result
-            Jira.Transition.forTicketKeyToState key, result.name,
-              robot: @robot
-              message:
-                room: channel
-                user: user
             msg.attachments.push text: "<@#{user.id}|#{user.name}> transitioned this ticket to #{result.jira}"
-    return msg
+            Jira.Transition.forTicketKeyToState key, result.name, envelope
+          resolve()

   getTicketKeyInChannelByTs: (channel, ts) ->
     switch channel[0]


Add some missing variable declarations

Add some missing variable declarations

--- src/rebuild.coffee
+++ src/rebuild.coffee
@@ -5,6 +5,7 @@

 config = require './apm'
 Command = require './command'
+fs = require './fs'
 Install = require './install'

 module.exports =
@@ -13,7 +14,8 @@

   constructor: ->
     super()
-    @atomNodeDirectory = path.join(config.getAtomDirectory(), '.node-gyp')
+    @atomDirectory = config.getAtomDirectory()
+    @atomNodeDirectory = path.join(@atomDirectory, '.node-gyp')
     @atomNpmPath = require.resolve('npm/bin/npm-cli')

   parseOptions: (argv) ->


fixed plaintext function

fixed plaintext function

--- src/reports.coffee
+++ src/reports.coffee
@@ -128,9 +128,10 @@
   User.find { weeklyReport: true }, callback

 plainTemplate = (report) ->
+  text = ''
   for data in report.data
     do (data) ->
-      " \r\n \r\n <---------- Start Channel  #{data.channel.name} ---------------------------> \r\n \r\n
+      text += " \r\n \r\n <---------- Start Channel  #{data.channel.name} ---------------------------> \r\n \r\n
                 Channel Name: #{data.channel.name} \r\n
                 Channel Load: #{ data.data[0].load } transactions  \r\n
                 Ave response time: #{ data.data[0].avgResp } \r\n
@@ -143,6 +144,8 @@
               \r\n
               \r\n
             "
+  text
+
 htmlTemplate = (report) ->
   text = "
     <html>
@@ -197,10 +200,8 @@
   agenda.every "1 days", 'send daily channel metrics'

 exports.setupAgenda = setupAgenda
-
-if process.env.NODE_ENV == "test"
-  exports.sendReports = sendReports
-  exports.fetchDailySubscribers = fetchDailySubscribers
-  exports.fetchWeeklySubscribers = fetchWeeklySubscribers
-  exports.fetchChannelReport = fetchChannelReport
-  exports.sendUserEmail = sendUserEmail
+exports.sendReports = sendReports
+exports.fetchDailySubscribers = fetchDailySubscribers
+exports.fetchWeeklySubscribers = fetchWeeklySubscribers
+exports.fetchChannelReport = fetchChannelReport
+exports.sendUserEmail = sendUserEmail


ugh I think it's finally fixed this time

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -72,7 +72,7 @@
     obj = @string_pool[oref] unless obj #megahack
     throw "undefined heap reference: #{oref}" unless obj
     name = field_spec.sig.name
-    obj[name] = @init_field(@field_lookup(field_spec)) if name[obj] is undefined
+    obj[name] = @init_field(@field_lookup(field_spec)) if obj[name] is undefined
     @push obj[name]
     @push null if field_spec.sig.type in ['J','D']


removed heatmap hack

removed heatmap hack

--- src/requester/druidRequester.coffee
+++ src/requester/druidRequester.coffee
@@ -17,7 +17,6 @@
         }
       else
         postfix = ''
-        postfix += 'heatmap' if query.queryType is 'heatmap' # Druid is f-ed
         postfix += '?pretty' if context?.pretty
         param = {
           method: 'POST'


fixed the jenkins-notifier script

fixed the jenkins-notifier script

--- src/scripts/jenkins-notifier.coffee
+++ src/scripts/jenkins-notifier.coffee
@@ -29,9 +29,10 @@

     res.end('')

-    user = {}
-    user.room = query.room if query.room
-    user.type = query.type if query.type
+    envelope = {}
+    envelope.user = {}
+    envelope.room = query.room if query.room
+    envelope.user.type = query.type if query.type

     try
       for key of req.body
@@ -40,16 +41,16 @@
       if data.build.phase == 'FINISHED'
         if data.build.status == 'FAILURE'
           if data.name in @failing
-            build = "STILL"
+            build = "is still"
           else
-            build = "STARTED"
-          robot.send user, "#{build} FAILING: #{data.name} ##{data.build.number} (#{encodeURI(data.build.full_url)})"
+            build = "started"
+          robot.send envelope, "#{data.name} build ##{data.build.number} #{build} failing (#{encodeURI(data.build.full_url)})"
           @failing.push data.name unless data.name in @failing
         if data.build.status == 'SUCCESS'
           if data.name in @failing
             index = @failing.indexOf data.name
             @failing.splice index, 1 if index isnt -1
-            robot.send user, "BUILD RESTORED: #{data.name} ##{data.build.number} (#{encodeURI(data.build.full_url)})"
+            robot.send envelope, "#{data.name} build was restored ##{data.build.number} (#{encodeURI(data.build.full_url)})"

     catch error
       console.log "jenkins-notify error: #{error}. Data: #{req.body}"


improve file listing message

improve file listing message

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -671,7 +671,11 @@
         # that we know our relation to this project, namely so that
         # get_my_group is defined.
         id = misc.uuid()
-        @set_activity(id:id, status:"scanning '#{misc.trunc_middle(path,30)}'")
+        if path
+            status = "Loading file list - #{misc.trunc_middle(path,30)}"
+        else
+            status = "Loading file list"
+        @set_activity(id:id, status:status)
         my_group = undefined
         the_listing = undefined
         async.series([


Allow creation of nested folders from file listing

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -43,7 +43,7 @@
     'cs'   : ['exe']
     'tex'  : 'aux bbl blg fdb_latexmk glo idx ilg ind lof log nav out snm synctex.gz toc xyc'.split(' ')

-BAD_FILENAME_CHARACTERS       = '\\/'
+BAD_FILENAME_CHARACTERS       = '\\'
 BAD_LATEX_FILENAME_CHARACTERS = '\'"()"~%'
 BANNED_FILE_TYPES             = ['doc', 'docx', 'pdf', 'sws']


new class name for content header

new class name for content header

--- js/app.coffee
+++ js/app.coffee
@@ -6,7 +6,8 @@

   header =
     $("")
-      .append(HAML.header(args))
+      .addClass('middle-head')
+      .append(HAML.inner_header(args))

   footer =
     $("")


neo4j: fix bug where 0 was turning up false and wasn't setting newMemberBucketIndex

--- utils/graph/graph_decorator_test.coffee
+++ utils/graph/graph_decorator_test.coffee
@@ -70,7 +70,7 @@
   response.isFull     = true
   response.from       = overview.first.createdAt.last
   response.to         = overview.last.createdAt.first
-  response.newMemberBucketIndex = newMemberBucketIndex  if newMemberBucketIndex
+  response.newMemberBucketIndex = newMemberBucketIndex  if newMemberBucketIndex?

   return response


Fix line numbering of source files

Fix line numbering of source files

--- src/scripts/views/gui/SourceFileMarkup.coffee
+++ src/scripts/views/gui/SourceFileMarkup.coffee
@@ -12,7 +12,7 @@
     code = @sourceFile.getRawSourceCode()
     breakpointCallback = @sourceFile.getBreakpointCallback()

-    @formatted_code = $("

")
+    @formatted_code = $("

")
     cnt = @sourceFile.getOffset()

     for line in code.split("\n")


Added a regressive test for e87592059bab141d73e7dda18bc66a74637019e2

--- test/server/functional/user.spec.coffee
+++ test/server/functional/user.spec.coffee
@@ -20,7 +20,7 @@
     expect(user.isEmailSubscriptionEnabled('adventurerNews')).toBeTruthy()
     expect(user.isEmailSubscriptionEnabled('generalNews')).toBeFalsy()
     done()
-
+
   it 'maintains the old subs list if it\'s around', (done) ->
     user = new User()
     user.set 'emailSubscriptions', ['tester']
@@ -33,7 +33,7 @@
     GLOBAL.mc =
       lists:
         subscribe: callback
-
+
   it 'uses emails to determine what to send to MailChimp', (done) ->
     makeMC (params) ->
       expect(JSON.stringify(params.merge_vars.groupings[0].groups)).toBe(JSON.stringify(['Announcements']))
@@ -41,7 +41,7 @@

     user = new User({emailSubscriptions:['announcement'], email:'tester@gmail.com'})
     User.updateMailChimp(user)
-
+
 describe 'POST /db/user', ->

   createAnonNameUser = (done)->
@@ -191,7 +191,16 @@
         form.append('_id', String(admin._id))
         form.append('email', joe.get('email').toUpperCase())

-  it 'works', (done) ->
+  it 'does not care if you include your existing name', (done) ->
+    unittest.getNormalJoe (joe) ->
+      req = request.put getURL(urlUser+'/'+joe._id), (err, res) ->
+        expect(res.statusCode).toBe(200)
+        done()
+      form = req.form()
+      form.append('_id', String(joe._id))
+      form.append('name', 'Joe')
+
+  it 'accepts name and email changes', (done) ->
     unittest.getNormalJoe (joe) ->
       req = request.put getURL(urlUser), (err, res) ->
         expect(res.statusCode).toBe(200)
@@ -204,7 +213,6 @@
       form.append('_id', String(joe._id))
       form.append('email', 'New@email.com')
       form.append('name', 'Wilhelm')
-

 describe 'GET /db/user', ->


Coding convention

Coding convention

--- test/spec/sync/flat/unique.tests.coffee
+++ test/spec/sync/flat/unique.tests.coffee
@@ -60,13 +60,13 @@
     it 'Handles a find unique query on one field', (done) ->
       Flat.cursor({$unique: 'name'}).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         done()

     it 'Handles a find unique query on one field and gives the correct result with sort', (done) ->
       Flat.cursor({$unique: 'name'}).select('created_at', 'updated_at').sort('-updated_at').limit(1).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, 1, "finds no extra results")
+        assert.equal(results.length, 1, 'finds no extra results')
         retrieved_clone = results[0]
         assert.equal(retrieved_clone.created_at, null, "loaded model has no created_at")
         assert.equal(retrieved_clone.updated_at.getTime(), new_updated_at.getTime(), "finds the correct model")
@@ -75,7 +75,7 @@
     it 'Handles a find unique query on name with $select', (done) ->
       Flat.cursor({$unique: ['name'], $select: ['id']}).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         for result in results
           assert.equal(_.keys(result).length, 1, "finds only the $selected field")
           assert.equal(_.keys(result)[0], 'id', "finds only the $selected field")
@@ -84,7 +84,7 @@
     it 'Handles a find unique query on name with select (chaining)', (done) ->
       Flat.cursor().unique('name').select('id').toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         for result in results
           assert.equal(_.keys(result).length, 1, "finds only the $selected field")
           assert.equal(_.keys(result)[0], 'id', "finds only the $selected field")
@@ -93,7 +93,7 @@
     it 'Handles a find unique query on name with $select name', (done) ->
       Flat.cursor({$unique: ['name'], $select: ['name']}).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         for result in results
           assert.equal(_.keys(result).length, 1, "finds only the $selected field")
           assert.equal(_.keys(result)[0], 'name', "finds only the $selected field")
@@ -102,7 +102,7 @@
     it 'Handles a find unique query on name with $values', (done) ->
       Flat.cursor({$unique: ['name'], $values: ['id']}).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         for result in results
           assert.ok(!_.isObject(result), "finds only the $selected field")
         done()
@@ -110,7 +110,7 @@
     it 'Handles a find unique query on name with $values name', (done) ->
       Flat.cursor({$unique: ['name'], $values: ['name']}).toJSON (err, results) ->
         assert.ifError(err)
-        assert.equal(results.length, BASE_COUNT, "finds no extra results")
+        assert.equal(results.length, BASE_COUNT, 'finds no extra results')
         for result in results
           assert.ok(!_.isObject(result), "finds only the $selected field")
         done()
@@ -118,14 +118,14 @@
     it 'Handles a find unique query with count', (done) ->
       Flat.count {$unique: 'name'}, (err, result) ->
         assert.ifError(err)
-        assert.equal(result, BASE_COUNT, "finds no extra results")
+        assert.equal(result, BASE_COUNT, 'finds no extra results')
         done()

     # TODO: test more edge cases
     it 'Handles a find unique query with count on empty collection', (done) ->
       Empty.count {$unique: 'name'}, (err, result) ->
         assert.ifError(err)
-        assert.equal(result, 0, "finds no extra results")
+        assert.equal(result, 0, 'finds no extra results')
         done()


Subscribes to the user channel for room notifications

--- src/coffee/control/index.coffee
+++ src/coffee/control/index.coffee
@@ -1,12 +1,19 @@
 angular.module 'app.control'

 .controller 'app.control.index', [
-  '$scope', '$state', '$stateParams', '$facebook', '$kinvey', 'me', 'users', 'rooms',
-  ($scope, $state, $stateParams, $facebook, $kinvey, me, users, rooms) ->
+  '$scope', '$state', '$stateParams', '$facebook', '$kinvey', 'PubNub', 'me', 'users', 'rooms',
+  ($scope, $state, $stateParams, $facebook, $kinvey, PubNub, me, users, rooms) ->

     $scope.me = me
     $scope.users = users
     $scope.rooms = rooms
+
+    PubNub.ngSubscribe
+      channel: me._id
+
+    $scope.$on (PubNub.ngMsgEv me._id), (event, payload) ->
+      room = new $kinvey.Room payload
+      $scope.rooms.push room

     $scope.logout = ->
       $facebook.logout().then ->


style(util/setCurrentProfile:tests): fix indentation

--- test/util/setCurrentProfile.spec.coffee
+++ test/util/setCurrentProfile.spec.coffee
@@ -25,12 +25,13 @@
 	it "returns an Error when writeFile fails", ( done ) ->
 		triedToWriteThis = {}
 		setCurrentProfile = proxyquire "../../lib/util/setCurrentProfile",
-		"./getMinecraftPath": -> pathToFixtures
-		"fs":
-			writeFile: ( filename, json, callback ) ->
-				filename.should.equal pathToTheFixture
-				triedToWriteThis = JSON.parse json
-				callback "fake-result"
+			"./getMinecraftPath": ->
+				pathToFixtures
+			"fs":
+				writeFile: ( filename, json, callback ) ->
+					filename.should.equal pathToTheFixture
+					triedToWriteThis = JSON.parse json
+					callback "fake-result"

 		setCurrentProfile fakeNewProfile, ( err ) ->
 			expect( err ).to.equal "fake-result"
@@ -39,12 +40,13 @@
 	it "blindly rewrites current profile with specified object", ( done ) ->
 		triedToWriteThis = {}
 		setCurrentProfile = proxyquire "../../lib/util/setCurrentProfile",
-		"./getMinecraftPath": -> pathToFixtures
-		"fs":
-			writeFile: ( filename, json, callback ) ->
-				filename.should.equal pathToTheFixture
-				triedToWriteThis = JSON.parse json
-				callback()
+			"./getMinecraftPath": ->
+				pathToFixtures
+			"fs":
+				writeFile: ( filename, json, callback ) ->
+					filename.should.equal pathToTheFixture
+					triedToWriteThis = JSON.parse json
+					callback()

 		setCurrentProfile fakeNewProfile, ( err ) ->
 			expect( err ).to.equal undefined


Adding adds to top of list

Adding adds to top of list

--- assets/javascripts/views/awards_page.js.coffee
+++ assets/javascripts/views/awards_page.js.coffee
@@ -25,7 +25,7 @@
     @listenTo @zonkFormView.model, 'sync', @onZonkSaved

   onZonkSaved: (model) =>
-    @zonkAwards.add model
+    @zonkAwards.add model, at: 0
     @render()

   onZonksFetched: (collection) =>


general cleanup/updating of builtin.string

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -35,7 +35,7 @@
       .__mul__(
         new Int Math.floor(delta, 2))
       .value
-    if delta % 2 == 0
+    if delta % 2 is 0
       new (type(@)) pad + @value + pad
     else
       new (type(@)) pad + @value + pad + fillchar
@@ -63,7 +63,7 @@
     index = slicedStr.lastIndexOf(suffix)
     if index < 0
       return false
-    return (index + suffix.length) == end
+    return (index + suffix.length) is end

   expandtabs: -> return

@@ -71,23 +71,23 @@
     index = @value.indexOf(sub)
     withinBounds = start <= index < end
     if withinBounds
-      return index
-    else
-      return -1
+      return new Int index
+    else
+      return new Int -1

   format: -> return

-  index: (sub, start = 0, end = @__len__()) ->
+  index: (sub, start = 0, end = @__len__().value) ->
     substr = @value.slice(start, end)
     index = substr.indexOf(sub)
-    if index == -1
+    if index is -1
       raise new ValueError "substring not found"
     else
-      return index
+      return new Int index

   isalnum: ->
     alnums = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
-    if @__len__() == 0
+    if @__len__().value is 0
       return false
     else
       for c in @value
@@ -97,7 +97,7 @@

   isalpha: ->
     alphas = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-    if @__len__() == 0
+    if @__len__().value is 0
       return false
     else
       for c in @value
@@ -107,7 +107,7 @@

   isdigit: ->
     digits = '0123456789'
-    if @__len__() == 0
+    if @__len__().value is 0
       return false
     else
       for c in @value
@@ -120,7 +120,7 @@
   islower: ->
     lowers = 'abcdefghijklmnopqrstuvwxyz'
     uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-    if @__len__() == 0
+    if @__len__().value is 0
       return false
     else
       containsLower = false
@@ -133,7 +133,7 @@

   isspace: ->
     spaces = '\t\n\x0b\x0c\r '
-    if @__len__() == 0
+    if @__len__().value is 0
       return false
     else
       for c in @value
@@ -151,7 +151,7 @@
       firstChar = new String(word[0])
       if not firstChar.isupper()
         return false
-      if word.length == 1
+      if word.length is 1
         continue
       if not firstChar.islower()
         return false
@@ -162,7 +162,7 @@
   isupper: ->
     lowers = 'abcdefghijklmnopqrstuvwxyz'
     uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-    if @__len__() == 0
+    if @__len__().value == 0
       return false
     else
       containsUpper = false
@@ -176,14 +176,14 @@
   join: (iterable) -> return

   ljust: (width, fillchar = ' ') ->
-    if width <= @__len__()
-      return @value
-    delta = width - @__len__()
-    pad = (new Str('')).__mul__(delta)
-    return @value + pad
+    if width <= @__len__().value
+      return @
+    delta = width - @__len__().value
+    pad = (new Str('')).__mul__(delta).value
+    return new Str(@value + pad)

   lower: ->
-    return @value.toLowerCase()
+    return new Str(@value.toLowerCase())

   lstrip: (chars) -> return

@@ -202,23 +202,23 @@
     else
       return @split(old).join(replacement)

-  rfind: (sub, start = 0, end = @__len__()) ->
+  rfind: (sub, start = 0, end = @__len__().value) ->
     lastIndex = @__slice__(start, end).lastIndexOf(sub)
     return lastIndex

-  rindex: (sub, start = 0, end = @__len__()) ->
+  rindex: (sub, start = 0, end = @__len__().value) ->
     lastIndex = substr.lastIndexOf(sub)
-    if lastIndex == -1
+    if lastIndex is -1
       raise new ValueError "substring not found"
     else
-      return lastIndex
+      return new Int lastIndex

   rjust: (width, fillchar = ' ') ->
-    if width <= @__len__()
+    if width <= @__len__().value
       return @value
-    delta = width - @__len__()
-    pad = (new Str('')).__mul__(delta)
-    return pad + @value
+    delta = width - @__len__().value
+    pad = (new Str('')).__mul__(delta).value
+    return new Str(pad + @value)

   rpartition: (sep) ->
     splitValues = @rsplit(sep, 1)
@@ -233,7 +233,7 @@
   rstrip: -> return
   split: -> return
   splitlines: -> return
-  startswith: (prefix, start = 0, end = @__len__()) ->
+  startswith: (prefix, start = 0, end = @__len__().value) ->
     return @value.slice(start, end).indexOf(prefix) == 0

   strip: -> return
@@ -254,23 +254,23 @@
         result += lowerCaseChar
       else
         result += c
-    return result
+    return new Str result

   title: -> return
   translate: -> return

   upper: ->
-    return @value.toUpperCase()
+    return new Str @value.toUpperCase()

   zfill: (width) ->
-    if width <= @__len__()
-      return @value
+    if width.value <= @__len__().value
+      return @
     zeroStr = new Str('0')
-    if @value.length == 0
+    if @__len__().value is 0
       return zeroStr.__mul__(width)
-    zeroCount = width - @__len__()
-    if @value[0] == '-'
+    zeroCount = width.value - @__len__().value
+    if @value[0] is '-'
       remaining = @value.slice(1)
-      return '-' + zeroStr.__mul__(zeroCount) + remaining
-    else
-      return zeroStr.__mul__(zeroCount) + remaining
+      return new Str '-' + zeroStr.__mul__(zeroCount).value + remaining
+    else
+      return new Str zeroStr.__mul__(zeroCount).value + remaining


Remove debug output

Remove debug output

--- js/app.coffee
+++ js/app.coffee
@@ -69,7 +69,6 @@
     @query_results = ko.observableArray([])

   authGitHubSlideDown: =>
-    console.log("authGitHubSlideDown")
     mixpanel.track("Auth GitHub Modal Why Necessary")
     $(".why_authenticate_github_modal").slideDown()


Added theme setting to presets and fixed item delay for gallery preset.

--- src/coffee/presets.coffee
+++ src/coffee/presets.coffee
@@ -35,6 +35,7 @@
       repeating: false
       alwaysShowCaption: true
       visibleItems: 4
+      theme: "dark"
       itemProperties:
         delay: 0
         size:
@@ -67,6 +68,7 @@
         x: 0
         y: 0
       scaling: 1
+      theme: "page"
       visibleItems: 1
       controls:
         margin:
@@ -118,6 +120,7 @@
         itemPadding: 2
       # Standard rondell options
       visibleItems: 4
+      theme: "dark"
       cropThumbnails: true
       center:
         top: 145
@@ -130,7 +133,7 @@
           x: 10
           y: 255
       itemProperties:
-        delay: 10
+        delay: 0
         sizeFocused:
           width: 480
           height: 280
@@ -227,6 +230,7 @@
           * (r.itemProperties.size.width + r.special.itemPadding)

     slider:
+      theme: 'slider'
       visibleItems: 1
       fadeTime: 1000
       opacityMin: 0.01


Clean up and dedupe hashbang snippets

Clean up and dedupe hashbang snippets

--- src/configs/snippets.cson
+++ src/configs/snippets.cson
@@ -1,16 +1,27 @@
 # Universal
 "*":
-	"Node Script":
+	Node:
 		prefix: "node"
-		body: '#!/usr/bin/env node --es_staging\n"use strict";\n\n'
+		body: """
+			\\#!/usr/bin/env node --es_staging
+			"use strict";
+			\n\n"""

 	Perl:
 		prefix: "pl"
-		body: "#!/usr/bin/env perl\nuse strict;\nuse warnings;\nuse autodie;\n\n$1"
+		body: """
+			\\#!/usr/bin/env perl
+			use strict;
+			use warnings;
+			use autodie;
+			use v5.14;
+			\n"""

 	Ruby:
 		prefix: "rb"
-		body: "#!/usr/bin/env ruby\n\n$1"
+		body: """(?x)
+			\\#!/usr/bin/env ruby
+			\n\n"""

 	"Boilerplate HTML":
 		prefix: "htmldoc"
@@ -263,9 +274,6 @@

 # Perl
 ".source.perl":
-	Hashbang:
-		prefix: "pl"
-		body: "#!/usr/bin/env perl\nuse strict;\nuse warnings;\nuse autodie;\n\n$1"
 	"Project Boilerplate":
 		prefix: "perl"
 		body: """
@@ -333,9 +341,6 @@

 # Ruby
 ".source.ruby":
-	Executable:
-		prefix: "rb"
-		body: "#!/usr/bin/env ruby\n\n$1"
 	Read:
 		prefix: "read"
 		body: 'data = File.read("${1:file.name}")'


:lipstick:

:lipstick:

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -593,8 +593,6 @@

     {screenLines, regions} = @buildScreenLines(startBufferRow, endBufferRow + bufferDelta)
     @screenLines[startScreenRow...endScreenRow] = screenLines
-    screenDelta = screenLines.length - (endScreenRow - startScreenRow)
-
     @rowMap.spliceRegions(startBufferRow, endBufferRow - startBufferRow, regions)
     @findMaxLineLength(startScreenRow, endScreenRow, screenLines)

@@ -603,7 +601,7 @@
     changeEvent =
       start: startScreenRow
       end: endScreenRow - 1
-      screenDelta: screenDelta
+      screenDelta: screenLines.length - (endScreenRow - startScreenRow)
       bufferDelta: bufferDelta

     if options.delayChangeEvent


Use ISO8601 format for timestamps

Use ISO8601 format for timestamps

--- lib/sensu-dashboard/assets/javascripts/handlebars_helpers.coffee
+++ lib/sensu-dashboard/assets/javascripts/handlebars_helpers.coffee
@@ -20,4 +20,5 @@
   return Handlebars.helpers.strip(linkified)

 Handlebars.registerHelper "formatTimestamp", (text) ->
-    return Date(text*100)
+  date = new Date(text*1000)
+  return date.toISOString()


PPU code refactoring.

PPU code refactoring.

--- emulator/core/units/ppu.coffee
+++ emulator/core/units/ppu.coffee
@@ -5,7 +5,10 @@
 FRAME_BUFFER_WIDTH  = require("../common/constants").VIDEO_WIDTH
 FRAME_BUFFER_HEIGHT = require("../common/constants").VIDEO_HEIGHT

-# Cycle/scanlines flags
+###########################################################
+# PPU cycle/scanlines flags
+###########################################################
+
 F_RENDER    = 1 <<  1 # Rendering cycle
 F_FETCH_NT  = 1 <<  2 # Cycle where nametable byte is fetched
 F_FETCH_AT  = 1 <<  3 # Cycle where attribute byte is fetched
@@ -27,7 +30,10 @@
 F_VB_END    = 1 << 19 # Cycle where VBlank ends
 F_SKIP      = 1 << 20 # Cycle which is skipped during odd frames

+###########################################################
 # Tables containing flags for all cycles/scanlines
+###########################################################
+
 cycleFlagsTable = (0 for [0..340])
 scanlineFlagsTable = (0 for [0..261])

@@ -58,8 +64,7 @@
 cycleFlagsTable[i]    |= F_SHIFT_BG for i in [1..336] when not (256 < i < 321)
 scanlineFlagsTable[i] |= F_SHIFT_BG for i in [0..239]

-#cycleFlagsTable[65]   |= F_EVAL_SP
-cycleFlagsTable[257]  |= F_EVAL_SP
+cycleFlagsTable[65]   |= F_EVAL_SP
 scanlineFlagsTable[i] |= F_EVAL_SP for i in [0..261] when not (239 < i < 261)

 cycleFlagsTable[i]    |= F_CLIP_LEFT for i in [1..8]
@@ -92,19 +97,21 @@
 cycleFlagsTable[338]    |= F_SKIP
 scanlineFlagsTable[261] |= F_SKIP

+###########################################################
+# Sprite data for curently rendered scanline
+###########################################################
+
 class Sprite

     constructor: ->
-        @address = 0
-        @height = 0
-        @rowNumber = 0
-        @x = 0
-        @horizontalFlip = false
-        @paletteNumber = 0
-        @inFront = false
-        @zeroSprite = false
-        @patternRow0 = 0
-        @patternRow1 = 0
+        @x = 0                  # X position on scanline
+        @zeroSprite = false     # Whether this is a first sprite from OAM
+        @horizontalFlip = false # Whether is flipped horizontally
+        @paletteNumber = 0      # Palette number for rendering
+        @inFront = false        # Rendering priority
+        @patternRowAddress = 0  # Base address of sprite pattern row
+        @patternRow0 = 0        # Pattern row (bit 0)
+        @patternRow1 = 0        # Pattern row (bit 1)

 ###########################################################
 # Picture processing unit
@@ -118,7 +125,7 @@
         @ppuMemory = ppuMemory
         @cpu = cpu
         @ntscMode = true
-        @colorEmphasis = 0
+        @colorEmphasis = 0 # Color palette BGR emphasis bits

     ###########################################################
     # Power-up state initialization
@@ -131,43 +138,43 @@
         @resetVariables()

     resetOAM: ->
-        @primaryOAM = (0 for [0..0x100])  # 256B
-        @secondaryOAM = (new Sprite for [0..7])
-        @spriteCount = 0
-        @spriteNumber = 0
-        @spriteScanline = (0 for [0..261])
-        @spriteScanlinePixels = (null for [0..261])
+        @primaryOAM = (0 for [0...0x100])       # Sprite data - 256B (64 x 4B sprites)
+        @secondaryOAM = (new Sprite for [0..7]) # Sprite data for rendered scanline (up to 8 sprites)

     resetRegisters: ->
-        @setControl 0       #  8-bit PPUCTRL register
-        @setMask 0          #  8-bit PPUMASK register
-        @setStatus 0        #  8-bit PPUSTATUS register
-        @oamAddress = 0     # 15-bit OAMADDR register
-        @tempAddress = 0    # 15-bit 'Loopy T' register
-        @vramAddress = 0    # 15-bit 'Loopy V' register
-        @vramReadBuffer = 0 #  8-bit VRAM read buffer
-        @writeToogle = 0    #  1-bit 'Loopy W' register
-        @fineXScroll = 0    #  3-bit 'Loopy X' register
-        @patternBuffer0 = 0 # 16-bit pattern (bit 0) shift buffer
-        @patternBuffer1 = 0 # 16-bit pattern (bit 1) shift buffer
-        @paletteBuffer0 = 0 #  8-bit palette (bit 0) shift buffer
-        @paletteBuffer1 = 0 #  8-bit palette (bit 1) shift buffer
-        @patternBufferNext0 = 0
-        @patternBufferNext1 = 0
-        @paletteLatchNext0 = 0
-        @paletteLatchNext1 = 0
-        @paletteLatch0 = 0  #  1-bit palette (bit 0) latch register
-        @paletteLatch1 = 0  #  1-bit palette (bit 1) latch register
+        @setControl 0           #  8-bit PPUCTRL register
+        @setMask 0              #  8-bit PPUMASK register
+        @setStatus 0            #  8-bit PPUSTATUS register
+        @oamAddress = 0         # 15-bit OAMADDR register
+        @tempAddress = 0        # 15-bit 'Loopy T' register
+        @vramAddress = 0        # 15-bit 'Loopy V' register
+        @vramReadBuffer = 0     #  8-bit VRAM read buffer
+        @writeToogle = 0        #  1-bit 'Loopy W' register
+        @fineXScroll = 0        #  3-bit 'Loopy X' register
+        @patternBuffer0 = 0     # 16-bit pattern (bit 0) shift buffer
+        @patternBuffer1 = 0     # 16-bit pattern (bit 1) shift buffer
+        @paletteBuffer0 = 0     #  8-bit palette (bit 0) shift buffer
+        @paletteBuffer1 = 0     #  8-bit palette (bit 1) shift buffer
+        @paletteLatch0 = 0      #  1-bit palette (bit 0) latch register
+        @paletteLatch1 = 0      #  1-bit palette (bit 1) latch register
+        @patternBufferNext0 = 0 # Next value for pattern (bit 0) shift buffer
+        @patternBufferNext1 = 0 # Next value for pattern (bit 1) shift buffer
+        @paletteLatchNext0 = 0  # Next value for palette (bit 0) latch register
+        @paletteLatchNext1 = 0  # Next value for palette (bit 1) latch register

     resetVariables: ->
-        @scanline = 261         # Total 262 scanlines (0..261)
-        @cycle = 0              # Total 341 cycles per scanline (0..340)
-        @cycleFlags = 0         # Flags for current cycle
-        @renderedSprite = null  # Currently rendered sprite
+        @scanline = 261         # Current scanline - from total 262 scanlines (0..261)
+        @cycle = 0              # Current cycle - from total 341 cycles per scanline (0..340)
+        @cycleFlags = 0         # Flags for current cycle/scanline
         @suppressVBlank = false # Whether to suppress VBlank flag setting
         @supressNMI = false     # Whether to supress NMI generation
         @nmiDelay = 0           # Number of cycles after which NMI is generated
         @oddFrame = false       # Whether odd frame is being rendered
+        @renderedSprite = null  # Currently rendered sprite
+        @spriteCount = 0        # Total number of sprites on current scanline
+        @spriteNumber = 0       # Number of currently fetched sprite
+        @spriteCache = (0 for [0..261])         # Preprocesed sprite data for current scanline (cycle -> sprite rendered on that cycle)
+        @spritePixelCache = (null for [0..261]) # Prerendered sprite pixels for current scanline (cycle -> sprite pixel rendered on that cycle)

     ###########################################################
     # External configuration
@@ -254,14 +261,14 @@
         value

     getStatus: ->
-        @spriteScalineOverflow << 5 | # S[5] Set when there is more than 8 sprites on current scanline
-        @spriteZeroHit         << 6 | # S[6] Set when a nonzero pixel of sprite 0 overlaps a nonzero background pixel
-        @vblankFlag            << 7   # S[7] Set at the start of the VBlank
+        @spriteOverflow << 5 | # S[5] Set when there is more than 8 sprites on current scanline
+        @spriteZeroHit  << 6 | # S[6] Set when a nonzero pixel of sprite 0 overlaps a nonzero background pixel
+        @vblankFlag     << 7   # S[7] Set at the start of the VBlank

     setStatus: (value) ->
-        @spriteScalineOverflow = (value >>> 5) & 1 # S[5]
-        @spriteZeroHit         = (value >>> 6) & 1 # S[6]
-        @vblankFlag            = (value >>> 7)     # S[7]
+        @spriteOverflow = (value >>> 5) & 1 # S[5]
+        @spriteZeroHit  = (value >>> 6) & 1 # S[6]
+        @vblankFlag     = (value >>> 7)     # S[7]

     ###########################################################
     # OAM access ($2003 - address / $2004 - data)
@@ -517,9 +524,8 @@
             @setFramePixel @$readPalette colorAddress

     renderFramePixel: ->
-        @renderedSprite = null
         backgroundColor = @renderBackgroundPixel()
-        spriteColor = @$renderSpritePixel()
+        spriteColor = @renderSpritePixel()
         if (spriteColor & 0x03) and (backgroundColor & 0x03)
             @spriteZeroHit ||= @renderedSprite.zeroSprite                    # Both bagckground and sprite pixels are visible
             if @renderedSprite.inFront then spriteColor else backgroundColor # Choose target by rendering priority
@@ -569,17 +575,6 @@
     #    Y = bit 1 of YYYYY
     #    X = bit 1 of XXXXX

-    renderBackgroundPixel: ->
-        return 0 if @$isBackgroundPixelInvisible()
-        colorBit0   = ((@patternBuffer0 << @fineXScroll) >> 15) & 0x1
-        colorBit1   = ((@patternBuffer1 << @fineXScroll) >> 14) & 0x2
-        paletteBit0 = ((@paletteBuffer0 << @fineXScroll) >>  5) & 0x4
-        paletteBit1 = ((@paletteBuffer1 << @fineXScroll) >>  4) & 0x8
-        return paletteBit1 | paletteBit0 | colorBit1 | colorBit0
-
-    isBackgroundPixelInvisible: ->
-        not @backgroundVisible or @backgroundClipping and (@cycleFlags & F_CLIP_LEFT)
-
     fetchNametable: ->
         @addressBus = 0x2000 | @vramAddress & 0x0FFF
         patternNumer = @ppuMemory.read @addressBus # Nametable byte fetch
@@ -616,6 +611,19 @@
         @patternBuffer1 = (@patternBuffer1 << 1)
         @paletteBuffer0 = (@paletteBuffer0 << 1) | @paletteLatch0
         @paletteBuffer1 = (@paletteBuffer1 << 1) | @paletteLatch1
+
+    renderBackgroundPixel: ->
+        if @$isBackgroundPixelVisible()
+            colorBit0   = ((@patternBuffer0 << @fineXScroll) >> 15) & 0x1
+            colorBit1   = ((@patternBuffer1 << @fineXScroll) >> 14) & 0x2
+            paletteBit0 = ((@paletteBuffer0 << @fineXScroll) >>  5) & 0x4
+            paletteBit1 = ((@paletteBuffer1 << @fineXScroll) >>  4) & 0x8
+            paletteBit1 | paletteBit0 | colorBit1 | colorBit0
+        else
+            0
+
+    isBackgroundPixelVisible: ->
+        @backgroundVisible and not (@backgroundClipping and (@cycleFlags & F_CLIP_LEFT))

     ###########################################################
     # Sprite rendering
@@ -634,84 +642,79 @@
     #   C = color palette number
     # Byte 3 = x screen coordinate

+    evaluateSprites: ->
+        @spriteNumber = 0
+        @spriteCount = 0
+        @spriteOverflow = 0
+        height = if @bigSprites then 16 else 8
+        bottomY = @scanline
+        topY = Math.max 0, bottomY - height
+        for spriteY, address in @primaryOAM by 4 when topY < spriteY <= bottomY
+            patternTableAddress = @spPatternTableAddress
+            patternNumber = @primaryOAM[address + 1]
+            if @bigSprites
+                patternTableAddress = (patternNumber & 1) << 12
+                patternNumber &= 0xFE
+            attributes = @primaryOAM[address + 2]
+            rowNumber = bottomY - spriteY
+            rowNumber = height - rowNumber - 1 if attributes & 0x80 # Vertical flip
+            if rowNumber >= 8 # Overflow to next pattern
+                rowNumber -= 8
+                patternNumber++
+            patternAddress = patternTableAddress + (patternNumber << 4)
+            sprite = @secondaryOAM[@spriteCount++]
+            sprite.x = @primaryOAM[address + 3]
+            sprite.zeroSprite = address is 0
+            sprite.horizontalFlip = attributes & 0x40
+            sprite.paletteNumber = 0x10 | (attributes & 0x03) << 2
+            sprite.inFront = (attributes & 0x20) is 0
+            sprite.patternRowAddress = patternAddress + rowNumber
+            if @spriteCount is 8
+                @spriteOverflow = 1
+                break
+        undefined
+
+    fetchSpriteLow: ->
+        if @spriteNumber < @spriteCount
+            sprite = @secondaryOAM[@spriteNumber]
+            @addressBus = sprite.patternRowAddress
+            sprite.patternRow0 = @ppuMemory.read @addressBus
+
+    fetchSpriteHigh: ->
+        if @spriteNumber < @spriteCount
+            sprite = @secondaryOAM[@spriteNumber++]
+            @addressBus = sprite.patternRowAddress + 8
+            sprite.patternRow1 = @ppuMemory.read @addressBus
+
     prerenderSprites: ->
-        for i in [0...@spriteScanline.length]
-            @spriteScanline[i] = null
-            @spriteScanlinePixels[i] = 0
-        for spriteNumber in [0...@spriteCount]
-            sprite = @secondaryOAM[spriteNumber]
+        for i in [0...@spriteCache.length]
+            @spriteCache[i] = null
+            @spritePixelCache[i] = 0
+        for i in [0...@spriteCount]
+            sprite = @secondaryOAM[i]
             for columnNumber in [0..7]
                 x = sprite.x + columnNumber + 1
                 break if x > FRAME_BUFFER_WIDTH
-                continue if x < 1 or @spriteScanline[x]
+                continue if x < 1 or @spriteCache[x]
                 columnNumber ^= 0x07 unless sprite.horizontalFlip
                 colorBit0 = ((sprite.patternRow0 >>> columnNumber) & 1)
                 colorBit1 = ((sprite.patternRow1 >>> columnNumber) & 1) << 1
                 colorNumber = colorBit1 | colorBit0
                 if colorNumber
-                    @spriteScanlinePixels[x] = sprite.paletteNumber | colorNumber
-                    @spriteScanline[x] = sprite
+                    @spriteCache[x] = sprite
+                    @spritePixelCache[x] = sprite.paletteNumber | colorNumber
         undefined

     renderSpritePixel: ->
-        if @$isSpritePixelInvisible()
+        if @$isSpritePixelVisible()
+            @renderedSprite = @spriteCache[@cycle]
+            @spritePixelCache[@cycle]
+        else
+            @renderedSprite = null
             0
-        else
-            @renderedSprite = @spriteScanline[@cycle]
-            @spriteScanlinePixels[@cycle]
-
-    isSpritePixelInvisible: ->
-        not @spritesVisible or @spriteClipping and (@cycleFlags & F_CLIP_LEFT)
-
-    evaluateSprites: ->
-        @spriteNumber = 0
-        @spriteCount = 0
-        @spriteScalineOverflow = 0
-        height = if @bigSprites then 16 else 8
-        bottomY = @scanline
-        topY = Math.max 0, bottomY - height
-        for spriteY, address in @primaryOAM by 4 when topY < spriteY <= bottomY
-            sprite = @secondaryOAM[@spriteCount++]
-            sprite.address = address
-            sprite.height = height
-            sprite.rowNumber = bottomY - spriteY
-            if @spriteCount is 8
-                @spriteScalineOverflow = 1
-                break
-        undefined
-
-    fetchSpriteLow: ->
-        if @spriteNumber < @spriteCount
-            @sprite = @secondaryOAM[@spriteNumber]
-            address = @sprite.address
-            patternNumber = @primaryOAM[address + 1]
-            patternTableAddress = @spPatternTableAddress
-            if @bigSprites
-                patternTableAddress = (patternNumber & 1) << 12
-                patternNumber &= 0xFE
-            attributes = @primaryOAM[address + 2]
-            rowNumber = @sprite.rowNumber
-            height = @sprite.height
-            rowNumber = height - rowNumber - 1 if attributes & 0x80 # Vertical flip
-            if rowNumber >= 8
-                rowNumber -= 8
-                patternNumber++
-            patternAddress = patternTableAddress + (patternNumber << 4)
-            @addressBus = patternAddress + rowNumber
-            @sprite.x = @primaryOAM[address + 3]
-            @sprite.horizontalFlip = attributes & 0x40
-            @sprite.paletteNumber = 0x10 | (attributes & 0x03) << 2
-            @sprite.inFront = (attributes & 0x20) is 0
-            @sprite.zeroSprite = address is 0
-            @sprite.patternRow0 = @ppuMemory.read @addressBus
-        else
-            @sprite = null
-
-    fetchSpriteHigh: ->
-        if @sprite
-            @addressBus += 8
-            @sprite.patternRow1 = @ppuMemory.read @addressBus
-            @spriteNumber++
+
+    isSpritePixelVisible: ->
+        @spritesVisible and not (@spriteClipping and (@cycleFlags & F_CLIP_LEFT))

     ###########################################################
     # Debug rendering


emailsettings: fix notification delay information in email notification settings

--- client/Account/views/emailnotifications.coffee
+++ client/Account/views/emailnotifications.coffee
@@ -83,13 +83,15 @@
       marketing        :
         title          : 'When Koding has member updates (like privacy updates, inactive account notices, new offers and campaigns)'
       pmNotificationDelay:
-        title          : 'Send me an email after'
+        title          : ''

     view = this

     @addSubView @list = new KDCustomHTMLView tagName : 'ul'

     for own flag, field of fields
+
+      return @["#{flag}FieldAdded"]? frequency[flag]  if field.title is null or field.title is ""

       @list.addSubView field.formView = new KDCustomHTMLView tagName : 'li'

@@ -110,6 +112,7 @@
       field.formView.addSubView title
       field.formView.addSubView fieldSwitch
       fields[flag].formView.addSubView fields[flag].loader
+
       @["#{flag}FieldAdded"]? frequency[flag]


kite/newkite.coffee: fix optional arguments to method

--- client/landing/app/MainApp/kite/newkite.coffee
+++ client/landing/app/MainApp/kite/newkite.coffee
@@ -50,6 +50,12 @@

   # Call a method on the connected Kite.
   tell: (method, args, cb) ->
+    # some methods doesn't need any argument(s), therefore you can omit args
+    # completely. If you omit args, then you'll going to pass cb as the second
+    # argument to the function, therefore we'll assign it back to cb and make
+    # args empty.
+    [args, cb] = [[], args] unless cb
+
     if not Array.isArray(args)
       args = [args]


widths are no longer needed

widths are no longer needed

--- client/lanes/components/grid/EditingMixin.cjsx
+++ client/lanes/components/grid/EditingMixin.cjsx
@@ -15,7 +15,6 @@

     listenNetworkEvents: true
     getDefaultProps: -> editors: {}
-    getInitialState: -> widths: @props.grid.columnWidths()
     topOffset:       -> @props.topOffset + (@props.rowIndex * @props.rowHeight)

     renderControls: ->


remove add_rendering

remove add_rendering

--- src/context.coffee
+++ src/context.coffee
@@ -191,11 +191,7 @@
         @renderable_list_builder.add_component component

       add_rendered: (rendered) ->
-        @add_rendering -> rendered
-
-      # adds a function that can render.
-      # the function will be called in the current context.
-      add_rendering: (rendering) -> @add_renderable _lead_render: @keeping_context(rendering)
+        @add_renderable _lead_render: -> rendered

       render: render


Removing FIXME.

Removing FIXME.

--- src/element.coffee
+++ src/element.coffee
@@ -121,9 +121,6 @@
     # Return an array of points that when joined create a convex polygon
     # that fully enclosed the element.
     getAxisAlignedBoundingBox : () ->
-        # FIXME: not sure if an array of points is the best way to represent
-        # this, though it might be the easiest way to extend to non-axis
-        # aligned shapes.
         throw new Error("Not Implemented error")


Array.isArray → instanceof Array (IE fix)

--- src/geodist.coffee
+++ src/geodist.coffee
@@ -50,7 +50,7 @@

   coords = []

-  if Array.isArray(point)
+  if point instanceof Array
     coords = point
   else if point.lat? and point.lon?
     coords = [point.lat, point.lon]


Defaults in Atoms

Defaults in Atoms

--- source/class/atom.coffee
+++ source/class/atom.coffee
@@ -15,7 +15,9 @@
   @include Atoms.Core.Output

   constructor: (@attributes) ->
+    @default = {} unless @default
     super
+    @attributes = Atoms.Core.Helper.mix @attributes, @default
     @constructor.type = "Atom"

     do @scaffold
@@ -30,9 +32,7 @@

   bindUpdate: (model) =>
     if model.uid is @entity.uid
-
       attributes = @entity.parse?() or @entity.attributes()
-      # attributes = @entity.attributes()
       @attributes[attribute] = @entity[attribute] for attribute of attributes
       do @refresh


Fix missed snake_case to camelCase, causing bug where projects couldn't be restored

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -425,7 +425,7 @@
 			for projectId in projectIds
 				queuedHttp {
 					method: "POST"
-					url: "/project/#{project_id}/restore"
+					url: "/project/#{projectId}/restore"
 					headers:
 						"X-CSRF-Token": window.csrfToken
 				}


Read non-200 download response as JSON error

--- src/install.coffee
+++ src/install.coffee
@@ -180,7 +180,15 @@
           callback("Unable to download #{packageUrl}: #{error.message}")
         writeStream.on 'close', -> callback(null, filePath)
       else
-        callback("Unable to download #{packageUrl}: #{response.statusCode}")
+        chunks = []
+        response.on 'data', (chunk) -> chunks.push(chunk)
+        response.on 'end', ->
+          try
+            error = JSON.parse(Buffer.concat(chunks))
+            message = error.message ? error.error ? error
+            callback("Unable to download #{packageUrl}: #{response.statusCode} #{message}")
+          catch
+            callback("Unable to download #{packageUrl}: #{response.statusCode}")

   # Get the path to the package from the local cache.
   #


Fix for ENOENT problem

Fix for ENOENT problem

--- gulp/tasks/clean.coffee
+++ gulp/tasks/clean.coffee
@@ -2,5 +2,5 @@
 del = require('del')
 config = require('../config')

-gulp.task 'clean', ->
-  del([config.publicDir])
+gulp.task 'clean', (cb) ->
+  del([config.publicDir], cb)


Extracted drawing of datasets (points and ranges) into their own methods, as we were triggering a bug when using the minified versions

Signed-off-by: Marko Locher <9efe9a5a1da5f41a4eb7599f2715dc24abf5bbc8@locher.at>

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -146,33 +146,35 @@
             el = @svg.select("g.datasets #dataset-#{dataset}")
             d = @data[dataset]

-            # ranges
-            el.selectAll('path').remove()
-            r = el.selectAll('path')
-                .data(d.ranges)
+            drawRanges(el, d.ranges, { index: d.index, color: d.color })
+            drawPoints(el, d.points, { index: d.index, color: d.color })
+
+        drawRanges = (element, data, options) =>
+            element.selectAll('path').remove()
+            r = element.selectAll('path')
+                .data(data)

             r.enter().append('path')
                 .attr('d',
                     d3.svg.line()
-                        .x( (d) => @scales.x(d) )
-                        .y( -5 * d.index )
+                        .x( (a) => @scales.x(new Date(a)) )
+                        .y( -5 * options.index )
                         .interpolate('linear')
                     )
-                .attr('stroke', d.color)
+                .attr('stroke', options.color)

             r.exit().remove()

-            # points
-            el.selectAll('circle').remove()
-            p = el.selectAll('circle')
-                .data(d.points)
-                .remove()
+        drawPoints = (element, data, options) =>
+            element.selectAll('circle').remove()
+            p = element.selectAll('circle')
+               .data(data)

             p.enter().append('circle')
-                    .attr('cx', (d) => @scales.x( new Date(d) ) )
-                    .attr('cy', "#{-5 * d.index}")
-                    .attr('fill', d.color)
-                    .attr('r', 2)
+                .attr('cx', (a) => @scales.x(new Date(a)) )
+                .attr('cy', -5 * options.index )
+                .attr('fill', options.color)
+                .attr('r', 2)

             p.exit().remove()


Fix percentiles grid

Fix percentiles grid

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -628,28 +628,14 @@
     rowCount = frame.rows

     inspectPercentiles = ->
-      percentiles = frame.default_pctiles
-      percentileValues = column.pctiles
-
-      variables = [
-        Flow.Data.Variable 'percentile', TNumber
-        Flow.Data.Variable 'value', TNumber #TODO depends on type of variable?
+      vectors = [
+        createVector 'percentile', TNumber, frame.default_pctiles
+        createVector 'value', TNumber, column.pctiles
       ]

-      Record = Flow.Data.Record variables
-      rows = for percentile, i in percentiles
-        row = new Record()
-        row.percentile = percentile
-        row.value = percentileValues[i]
-        row
-
-      Flow.Data.Table
-        label: 'percentiles'
+      createDataframe 'percentiles', vectors, (sequence frame.default_pctiles.length), null,
         description: "Percentiles for column '#{column.label}' in frame '#{frameKey}'."
-        variables: variables
-        rows: rows
-        meta:
-          origin: "getColumnSummary #{stringify frameKey}, #{stringify columnName}"
+        origin: "getColumnSummary #{stringify frameKey}, #{stringify columnName}"

     inspectDistribution = ->
       minBinCount = 32
@@ -790,6 +776,7 @@
     if column.type is 'int' or column.type is 'real'
       inspections.summary = inspectSummary
       inspections.distribution = inspectDistribution
+      inspections.percentiles = inspectPercentiles
     else
       inspections.domain = inspectDomain


Add percentiles table to scan(column-summary)

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -245,6 +245,39 @@
       data: getData

   extendColumnSummary = (frameKey, frame, columnName) ->
+    __getPercentiles = null
+    getPercentiles = ->
+      return __getPercentiles if __getPercentiles
+
+      column = head frame.columns
+      percentiles = frame.default_pctiles
+      percentileValues = column.pctiles
+
+      columns = [
+        name: 'percentile'
+        type: Flow.Data.Real
+      ,
+        name: 'value'
+        type: Flow.Data.Real #TODO depends on type of column?
+      ]
+
+      Row = Flow.Data.createCompiledPrototype map columns, (column) -> column.name
+      rows = for percentile, i in percentiles
+        row = new Row()
+        row.percentile = percentile
+        row.value = percentileValues[i]
+        row
+
+      __getPercentiles = Flow.Data.Table
+        name: 'percentiles'
+        label: 'Percentiles'
+        description: "Percentiles for column '#{column.label}' in frame '#{frameKey}'."
+        columns: columns
+        rows: rows
+        meta:
+          scan: "scan 'percentiles', getColumnSummary #{stringify frameKey}, #{stringify columnName}"
+
+
     __getDistribution = null
     getDistribution = ->
       return __getDistribution if __getDistribution
@@ -332,6 +365,7 @@

     __getSummary = null
     getSummary = ->
+      return __getSummary if __getSummary

       columns = [
         name: 'mean'
@@ -430,6 +464,7 @@
       summary: getSummary
       distribution: getDistribution
       domain: getDomain
+      percentiles: getPercentiles

   requestFrame = (frameKey, go) ->
     _.requestFrame frameKey, (error, frame) ->


make sure gulp watches images too

make sure gulp watches images too

--- gulp/tasks/watch.coffee
+++ gulp/tasks/watch.coffee
@@ -10,6 +10,7 @@
 # files to watch
 js = path.join(config.js.src, '**/**/*.{coffee,cjsx,js}')
 css = path.join(config.css.src, '**/**/*.{sass,scss}')
+images = path.join(config.images.src, '**/**/*.svg')

 gulp.task 'watch', ['sync'], ->
   log.info 'Watching assets for changes...'
@@ -20,4 +21,7 @@
   watch css, (files, cb) ->
     gulp.start 'css', cb

+  watch images, (files, cb) ->
+    gulp.start 'images', cb
+
   return


larger default wait time

larger default wait time

--- vue/tests/e2e/helpers/pageHelper.coffee
+++ vue/tests/e2e/helpers/pageHelper.coffee
@@ -117,5 +117,5 @@
     page.click('.auth-signup-form__legal-accepted .v-input--selection-controls__input')
     page.click '.auth-signup-form__submit'

-  waitFor: (selector, wait = 5000) ->
+  waitFor: (selector, wait = 8000) ->
     test.waitForElementVisible(selector, wait) if selector?


download bing image of the day

download bing image of the day

--- src/coffee/app.coffee
+++ src/coffee/app.coffee
@@ -1 +1,51 @@
 console.log "Hay there, you can find me @aberigle"
+
+class API
+
+  path : ""
+
+  constructor: (@path) ->
+
+  execute: (message = {}, path, method, callback) ->
+    xhr = new XMLHttpRequest
+    xhr.onreadystatechange = (event) ->
+      if xhr.readyState is 4
+        callback? event.target
+
+    xhr.open method, @path + path, true
+    # xhr.setRequestHeader("x-requested-with", "XMLHttpRequest")
+    if method in ["POST","PUT", "PATCH"] then xhr.send JSON.stringify message
+    else xhr.send()
+
+  _get: (path, callback) -> @execute null, path, "GET", callback
+
+  _post: (message, path, callback) -> @execute message, path, "POST", callback
+
+  _put: (message, path, callback) -> @execute message, path, "PUT", callback
+
+  _del: (path, callback) -> @execute null, path, "DELETE", callback
+
+class BingAPI extends API
+
+  constructor : -> super "http://image-a-day.herokuapp.com/"
+
+  getImage : (location, callback) ->
+    attrs =
+      format   : "js"
+      idx      : 0
+      n        : 1
+      location : location
+    attrString = "?"
+    attrString += "#{key}=#{attrs[key]}&" for key of attrs when attrs[key]?
+    @_get "/image", (response) -> callback? JSON.parse response.response
+
+aberigle = {}
+aberigle.api = new BingAPI
+
+window.onload = ->
+  aberigle.api.getImage navigator.language, (response) ->
+    document.body.style.backgroundImage = "url(#{response.imageUrl})"
+    link = copyright.firstChild
+    link.href = response.copyrightLink
+    link.text = response.copyright
+    console.log response


be friendly with node 0.10

be friendly with node 0.10

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -2,7 +2,9 @@
 Importer = require '../lib/csv/importer'
 Exporter = require '../lib/csv/exporter'
 package_json = require '../package.json'
+Promise = require 'bluebird'
 {ProjectCredentialsConfig,ExtendedLogger} = require 'sphere-node-utils'
+

 yargs = require 'yargs'
   .usage 'Usage: $0 <command> [options]'


Improve support of @v in rules

Improve support of @v in rules

--- src/coffeemake.coffee
+++ src/coffeemake.coffee
@@ -331,7 +331,6 @@
           @debug "Rule #{r.target} <- #{r.deps[0] ? "''"} matches! DONE"

   performAction: ->
-    @v = @vars
     if !@makefiles? and @defaultMakefile?
       @makefiles = [ @defaultMakefile ]
     if @makefiles?
@@ -373,6 +372,7 @@
       @runWithArgs(args, setup)

   runWithArgs: (args, setup)->
+    @v = @vars
     @processOptions(args).setup(setup).performAction()

   runStandalone: ({ process })->


add Collection#from_json

add Collection#from_json

--- src/collection.coffee
+++ src/collection.coffee
@@ -174,6 +174,14 @@
   size: () ->
     @_array.length

+  from_json: (json) ->
+    j = JSON.stringify(json)
+    if Sirius.Utils.is_array(j)
+      for jj in j then @_array.push(@_klass.from_json(jj))
+    else
+      @_array.push(@_klass.from_json(json))
+    @
+
   # convert collection to json
   # @return [JSON]
   to_json: () ->