some test refactoring

some test refactoring

--- test/pagerv2_commands_test.coffee
+++ test/pagerv2_commands_test.coffee
@@ -55,16 +55,6 @@
       name: 'user_with_email',
       email_address: 'user@example.com'
     }
-    # room.robot.brain.data.pagerv2 = {
-    #   users: {
-    #     momo: {
-    #       id: 'momo',
-    #       name: 'momo',
-    #       email: 'momo@example.com',
-    #       pdid: 'AAAAA42'
-    #     }
-    #   }
-    # }

     room.receive = (userName, message) ->
       new Promise (resolve) =>
@@ -233,344 +223,230 @@
           expect(hubotResponse())
           .to.eql 'Regina Phalange is on call until Tuesday 22:00 (utc).'

-  # ------------------------------------------------------------------------------------------------
-  describe '".pd 120000"', ->
-    context 'when everything goes right,', ->
-      say 'pd 120000', ->
-        it 'warns that this duration does not make any sense', ->
-          expect(hubotResponse())
-          .to.eql 'Sorry you cannot set an override of more than 1 day.'
-
-  describe '".pd 120"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'AAAAA42'
-            }
+  # ================================================================================================
+  context 'caller is known', ->
+    beforeEach ->
+      room.robot.brain.data.pagerv2 = {
+        users: {
+          momo: {
+            id: 'momo',
+            name: 'momo',
+            email: 'momo@example.com',
+            pdid: 'PEYSGVF'
           }
         }
-        nock('https://api.pagerduty.com')
-        .get('/schedules/42')
-        .reply(200, require('./fixtures/schedule_get-ok.json'))
-        .post('/schedules/42/overrides')
-        .reply(200, require('./fixtures/override_create-ok.json'))
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd 120', ->
-        it 'says override is done', ->
-          expect(hubotResponse())
-          .to.eql 'Rejoice Aurelio Rice! momo is now on call.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd not me"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/schedules/42/overrides')
-        .reply(200, require('./fixtures/override_get-ok.json'))
-        .delete('/schedules/42/overrides/PQ47DCP')
-        .reply(200, require('./fixtures/override_get-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd not me', ->
-        it 'returns name of who is on call', ->
-          expect(hubotResponse())
-          .to.eql 'Ok, momo! Aurelio Rice override is cancelled.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd incident 1234"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents/1234')
-        .reply(200, require('./fixtures/incident_get-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd incident 1234', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'PT4KHLK (resolved) The server is on fire.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd sup"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents')
-        .reply(200, require('./fixtures/incident_list-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd sup', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'PT4KHLK (resolved) The server is on fire.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd ack"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents')
-        .reply(200, require('./fixtures/incident_list-ok.json'))
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd ack', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK acknowledged.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd ack PT4KHLK"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd ack PT4KHLK', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK acknowledged.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd res"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents')
-        .reply(200, require('./fixtures/incident_list-ok.json'))
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd res', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK resolved.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd res PT4KHLK"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd res PT4KHLK', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK resolved.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd assign all to me"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents')
-        .reply(200, require('./fixtures/incident_list-ok.json'))
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd assign all to me', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK assigned to momo.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd assign PT4KHLK to me"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .put('/incidents')
-        .reply(200, require('./fixtures/incident_manage-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd assign PT4KHLK to me', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK assigned to momo.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd snooze all"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .get('/incidents')
-        .reply(200, require('./fixtures/incident_list-ok.json'))
-        .post('/incidents/PT4KHLK/snooze')
-        .reply(200, require('./fixtures/incident_snooze-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd snooze all', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK snoozed.'
-
-  # ------------------------------------------------------------------------------------------------
-  describe '".pd snooze PT4KHLK"', ->
-    context 'when everything goes right,', ->
-      beforeEach ->
-        room.robot.brain.data.pagerv2 = {
-          users: {
-            momo: {
-              id: 'momo',
-              name: 'momo',
-              email: 'momo@example.com',
-              pdid: 'PEYSGVF'
-            }
-          }
-        }
-        nock('https://api.pagerduty.com')
-        .post('/incidents/PT4KHLK/snooze')
-        .reply(200, require('./fixtures/incident_snooze-ok.json'))
-
-      afterEach ->
-        room.robot.brain.data.pagerv2 = { }
-        nock.cleanAll()
-
-      say 'pd snooze PT4KHLK', ->
-        it 'returns details on the incident', ->
-          expect(hubotResponse())
-          .to.eql 'Incident PT4KHLK snoozed.'
+      }
+
+      afterEach ->
+        room.robot.brain.data.pagerv2 = { }
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd 120000"', ->
+      context 'when everything goes right,', ->
+        say 'pd 120000', ->
+          it 'warns that this duration does not make any sense', ->
+            expect(hubotResponse())
+            .to.eql 'Sorry you cannot set an override of more than 1 day.'
+
+    describe '".pd 120"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/schedules/42')
+          .reply(200, require('./fixtures/schedule_get-ok.json'))
+          .post('/schedules/42/overrides')
+          .reply(200, require('./fixtures/override_create-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd 120', ->
+          it 'says override is done', ->
+            expect(hubotResponse())
+            .to.eql 'Rejoice Aurelio Rice! momo is now on call.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd not me"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/schedules/42/overrides')
+          .reply(200, require('./fixtures/override_get-ok.json'))
+          .delete('/schedules/42/overrides/PQ47DCP')
+          .reply(200, require('./fixtures/override_get-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd not me', ->
+          it 'returns name of who is on call', ->
+            expect(hubotResponse())
+            .to.eql 'Ok, momo! Aurelio Rice override is cancelled.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd incident 1234"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents/1234')
+          .reply(200, require('./fixtures/incident_get-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd incident 1234', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'PT4KHLK (resolved) The server is on fire.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd sup"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents')
+          .reply(200, require('./fixtures/incident_list-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd sup', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'PT4KHLK (resolved) The server is on fire.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd ack"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents')
+          .reply(200, require('./fixtures/incident_list-ok.json'))
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd ack', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK acknowledged.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd ack PT4KHLK"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd ack PT4KHLK', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK acknowledged.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd res"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents')
+          .reply(200, require('./fixtures/incident_list-ok.json'))
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd res', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK resolved.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd res PT4KHLK"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd res PT4KHLK', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK resolved.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd assign all to me"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents')
+          .reply(200, require('./fixtures/incident_list-ok.json'))
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd assign all to me', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK assigned to momo.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd assign PT4KHLK to me"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .put('/incidents')
+          .reply(200, require('./fixtures/incident_manage-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd assign PT4KHLK to me', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK assigned to momo.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd snooze all"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .get('/incidents')
+          .reply(200, require('./fixtures/incident_list-ok.json'))
+          .post('/incidents/PT4KHLK/snooze')
+          .reply(200, require('./fixtures/incident_snooze-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd snooze all', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK snoozed.'
+
+    # ----------------------------------------------------------------------------------------------
+    describe '".pd snooze PT4KHLK"', ->
+      context 'when everything goes right,', ->
+        beforeEach ->
+          nock('https://api.pagerduty.com')
+          .post('/incidents/PT4KHLK/snooze')
+          .reply(200, require('./fixtures/incident_snooze-ok.json'))
+
+        afterEach ->
+          nock.cleanAll()
+
+        say 'pd snooze PT4KHLK', ->
+          it 'returns details on the incident', ->
+            expect(hubotResponse())
+            .to.eql 'Incident PT4KHLK snoozed.'


Add blank lines.

Add blank lines.

--- test/unit/registrationSpec.coffee
+++ test/unit/registrationSpec.coffee
@@ -40,9 +40,11 @@
             .to.exist
           expect(registration.scan, "The registration scan reference is incorrect")
             .to.equal(scan)
+
         it 'should set the registration number', ->
           expect(registration.number, "The scan number is missing").to.exist
           expect(registration.number, "The scan number is incorrect").to.equal(1)
+
         it 'should have a virtual title property', ->
           expect(registration.title, "The registration title is missing").to.exist
           expect(registration.title, "The registration title is incorrect")


:sparkles: 英語の文章が気になったので。

:sparkles: 英語の文章が気になったので。

--- src/renderer/components/index-component.coffee
+++ src/renderer/components/index-component.coffee
@@ -20,7 +20,7 @@

-            The more simple, the more Web3D is interesting.
+            The simpler Web3D,the more interesting.

@@ -31,7 +31,7 @@

Github

-
jThree is Open Source Project. Your contribution is welcome!

+
jThree hosts sources in Github.Your any contributions are welcome!

@@ -40,7 +40,7 @@

OverView

-
Tutorial, tags, tips. You can post sample codes and share.

+
Tutorials, tags, tips. Share your code and stock your idea.

           </Link>

@@ -49,7 +49,7 @@

Reference

-
jThree API reference. You can search classes, methods, properties...

+
jThree API reference. Search classes, methods, properties...

           </Link>

@@ -58,7 +58,7 @@

Forum

-
You can discuss with jThree community members. Official announcement is also here.

+
Discuss your problem with jThree community members. Official announcement will be posted here.


Pass the owning element to the popover

Pass the owning element to the popover

--- src/scripts/modules/media/header/header.coffee
+++ src/scripts/modules/media/header/header.coffee
@@ -20,7 +20,7 @@
         }

       return {currentPage: currentPage}
-
+
     regions:
       'button': '.info .btn'

@@ -34,7 +34,7 @@
     onRender: () ->
       @regions.button.append new BookPopoverView
         model: @model
-        owner: @
+        owner: @$el.find('.info .btn')

     toggleSummary: (e) ->
       $summary = @$el.find('.summary')


Fix spec failure

Fix spec failure

--- apps/about/test/templates.coffee
+++ apps/about/test/templates.coffee
@@ -12,7 +12,7 @@
   jade.compile(
     fs.readFileSync(filename),
     { filename: filename }
-  )(_.extend require('./fixture/content.json'), { sd: {}, crop: -> }, opts)
+  )(_.extend require('./fixture/content.json'), { asset: (->), sd: {}, crop: -> }, opts)

 describe 'About templates', ->


Fix DOM.getStyle in Firefox

Fix DOM.getStyle in Firefox

--- assets/javascripts/dom.js.coffee
+++ assets/javascripts/dom.js.coffee
@@ -99,7 +99,7 @@

   getStyle: (el, name) ->
     val = el.style[name]
-    val = DOM.getComputedStyle(el, name) if val.match(/^[\s\r\t\n]*$/)
+    val = DOM.getComputedStyle(el, name) if !val || val.match(/^[\s\r\t\n]*$/)
     val

   getComputedStyle: (el, name) ->


Temporarily remove broken offline special-casing

Certain callers expect the cached data to be an array, while others expect objects

For now, fail trying to hit atom.io instead of throwing an uncaught exception due to an unexpected data type.

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -92,6 +92,7 @@

   # This could use a better name, since it checks whether it's appropriate to return
   # the cached data and pretends it's null if it's stale and we're online
+  # FIXME: Why is this even a callback when everything is sync? We never return an error anyway
   fetchFromCache: (packagePath, options, callback) ->
     unless callback
       callback = options
@@ -106,10 +107,6 @@
     if cached? and (not @online() or options.force or (Date.now() - cached.createdOn < @expiry))
       cached ?= data: {}
       callback(null, cached.data)
-    else if not cached? and not @online()
-      # The user hasn't requested this resource before and there's no way for us
-      # to get it to them so just hand back an empty object so callers don't crash
-      callback(null, {})
     else
       # falsy data means "try to hit the network"
       callback(null, null)


some more coverage

some more coverage

--- test/cron_events_test.coffee
+++ test/cron_events_test.coffee
@@ -112,7 +112,7 @@

     context 'but job is not known', ->
       hubot 'cron start nojob'
-      it 'should complain about the period syntax', ->
+      it 'should complain about the inexistence of that job', ->
         expect(hubotResponse()).to.eql 'startJob: There is no such job named nojob'
       it 'should not have added a job in the jobs queue', ->
         expect(room.robot.cron.jobs.somejob).not.to.be.defined
@@ -125,3 +125,36 @@
         expect(room.robot.brain.data.cron.somejob.started).to.be.true
       it 'should have added a job in the jobs queue', ->
         expect(room.robot.cron.jobs.somejob).to.be.defined
+
+  # ---------------------------------------------------------------------------------
+  context 'user stops a job', ->
+    beforeEach ->
+      room.robot.brain.data.cron = {
+        somejob: {
+          cronTime: '* * * * *',
+          eventName: 'event1',
+          eventData: { },
+          started: true
+        }
+      }
+      room.robot.brain.emit 'loaded'
+
+      afterEach ->
+        room.robot.brain.data.cron = { }
+        room.robot.cron.jobs = { }
+
+    context 'but job is not known', ->
+      hubot 'cron stop nojob'
+      it 'should complain about the inexistence of that job', ->
+        expect(hubotResponse()).to.eql 'stopJob: There is no such job named nojob'
+      it 'should not have added a job in the jobs queue', ->
+        expect(room.robot.cron.jobs.somejob).not.to.be.defined
+
+    context 'and job exists', ->
+      hubot 'cron stop somejob'
+      it 'should not complain about the inexistence of that job', ->
+        expect(hubotResponse()).to.eql 'The job somejob is now paused.'
+      it 'should change brain to record it\'s not started', ->
+        expect(room.robot.brain.data.cron.somejob.started).to.be.false
+      it 'should not have added a job in the jobs queue', ->
+        expect(room.robot.cron.jobs.somejob).to.be.undefined


added reply callbacks

added reply callbacks

--- node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
+++ node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
@@ -1,33 +1,104 @@
 {EventEmitter} = require 'microemitter'
 redis = require 'redis'
+os = require 'os'

 class Kommunicator extends EventEmitter

   constructor:(@publicKey, @serviceGenericName, @serviceUniqueName, @host='localhost', @port=6380)->
+    @callbackid = 0
+    @waitingReplies = {}
+    @kiteList = {}
+    @commands =
+      'kite.who': ()->
+        [@getChannelNameForKite()]
+      'kite.join': (channelName, kiteName)->
+        if not @kiteList[channelName]
+          @kiteList[channelName] = []
+        if kiteName not in @kiteList[channelName]
+          @kiteList[channelName].push kiteName
+        console.log "clients", @kiteList
+        [true]
+
+  getChannelNameForKite:(genericName, uniqueName)->
+    gn = genericName or @serviceGenericName
+    "#{@publicKey}-kite-#{gn}-#{uniqueName or @serviceUniqueName}"
+
+  getChannelNameForGenericName:(genericName)->
+    "#{@publicKey}-kite-#{genericName or @serviceGenericName}"
+
+  getChannelNameForAll:()->
+    "#{@publicKey}-kite-all"
+
+  receivedCommand: (from, cmdid, cmd, args)->
+    console.log "received command", from, cmd, args
+
+    if cmd.indexOf('reply-') > -1
+      cmdid = cmd.split('reply-')[1]
+      console.log "received cmdid", cmdid, @waitingReplies[cmdid]
+      @waitingReplies[cmdid]?.apply(@, args)
+      return
+
+    console.log "@commands", @commands, cmd
+    fn = @commands[cmd]
+    ret = fn.apply(@, args)
+    @sendCommand(from, "reply-#{cmdid}", ret)
+
+  sendCommand: (channel, cmd, args, cb=false)->
+    @callbackid++;
+    data = {
+      cmd: cmd,
+      id: @callbackid,
+      args: args,
+      from: @getChannelNameForKite()
+    }
+    console.log ">>>>> publishing to channel", channel, cmd, args
+    if cb
+      @waitingReplies[@callbackid] = cb
+    console.log "waiting replies list", @waitingReplies
+    @mergenData.publish channel, JSON.stringify(data)

   connectToMessageBus: ()=>
-
     console.log @serviceUniqueName, @serviceGenericName, @publicKey
-

     @mergen = redis.createClient(@port, @host)
     @mergen.on "error", (err)->
       console.log("Error ",err)
-    @mergen.subscribe "#{@publicKey}-kite-all"
-    @mergen.subscribe "#{@publicKey}-kite-#{@serviceGenericName}"
-    @mergen.subscribe "#{@publicKey}-kite-#{@serviceGenericName}-#{@serviceUniqueName}"
+
+    @mergen.subscribe @getChannelNameForAll()
+    @mergen.subscribe @getChannelNameForGenericName()
+    @mergen.subscribe @getChannelNameForKite()

     @mergen.on "message", (channel, message)=>
-      console.log ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> channel", channel, "message", message
-      try
-        k = message.split(' ')
-        cmds[k[0]] k[1...], (ret)=>
-          @mergenData.publish "kite-#{k[k.length-1]}", "socketwrite " + ret
-      catch Error
-        console.trace Error
+      console.log ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> received channel", channel, "message", message
+      message = JSON.parse message
+      @receivedCommand message.from, message.id, message.cmd, message.args

     @mergen.on "subscribe", (channel, count)->
       console.log ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> connected to channel", channel
+
+    # we have a second connection because, redis client in node doesnt send any commands
+    # when it enters subscribe mode, though redis docs says you can publish if you are in subcribe
+    # mode
+    @mergenData = redis.createClient(@port, @host)
+    @mergenData.on "error", (err)->
+      console.log "error", err
+
+    data = {}
+    data.pid = process.pid
+    data.hostname = os.hostname()
+    data.serviceGenericName = @serviceGenericName
+    data.serviceUniqueName = @serviceUniqueName
+    data.startedAt = Date.now()
+
+    @mergenData.hset "#{@publicKey}-kites", "#{@serviceGenericName}-#{@serviceUniqueName}", data
+
+    @sendCommand @getChannelNameForGenericName(), "kite.who", [], (reply)->
+      console.log "!!!!!!!! received reply for me", reply
+      @commands['kite.join'].call(@, @getChannelNameForGenericName(), reply)
+
+    @sendCommand @getChannelNameForGenericName(), "kite.join", [@getChannelNameForGenericName(), @getChannelNameForKite()]
+
+    @connected = true

 module.exports = class Kite extends EventEmitter


Add specs for more fold corner cases.

Add specs for more fold corner cases.

--- spec/atom/renderer-spec.coffee
+++ spec/atom/renderer-spec.coffee
@@ -1,7 +1,7 @@
 Renderer = require 'renderer'
 Buffer = require 'buffer'

-fdescribe "Renderer", ->
+describe "Renderer", ->
   [renderer, buffer, changeHandler] = []
   beforeEach ->
     buffer = new Buffer(require.resolve 'fixtures/sample.js')
@@ -43,7 +43,7 @@

       describe "when there is a fold placeholder straddling the max length boundary", ->

-  describe "folding", ->
+  fdescribe "folding", ->
     describe "when folds are created and destroyed", ->
       describe "when a fold spans multiple lines", ->
         it "replaces the lines spanned by the fold with a single line containing a placeholder", ->
@@ -112,12 +112,101 @@
           expect(line5.text).toBe '      current = items.shift();'
           expect(renderer.lineForRow(8).text).toBe '    r... sort(left).concat(pivot).concat(sort(right));'

+        fit "allows the outer fold to start at the same location as the inner fold", ->
+          renderer.createFold([[4, 29], [7, 4]])
+          renderer.createFold([[4, 29], [9, 2]])
+          expect(renderer.lineForRow(4).text).toBe "    while(items.length > 0) {...};"
+
       describe "when a fold begins on the line on which another fold ends", ->
+        describe "when the second fold is created before the first fold", ->
+          it "renders a placeholder for both folds on the first line of the first fold", ->
+            fold1 = renderer.createFold([[7, 5], [8, 36]])
+            fold2 = renderer.createFold([[4, 29], [7, 4]])
+
+            [line4, line5] = renderer.linesForRows(4, 5)
+            expect(line4.text).toBe  '    while(items.length > 0) {...}...concat(sort(right));'
+            expect(line5.text).toBe '  };'
+
+            expect(changeHandler.callCount).toBe 2
+            [[event1], [event2]] = changeHandler.argsForCall
+            expect(event1.oldRange).toEqual [[7, 0], [8, 56]]
+            expect(event1.newRange).toEqual [[7, 0], [7, 28]]
+            expect(event2.oldRange).toEqual [[4, 0], [7, 28]]
+            expect(event2.newRange).toEqual [[4, 0], [4, 56]]
+            changeHandler.reset()
+
+            fold1.destroy()
+            [line4, line5] = renderer.linesForRows(4, 5)
+            expect(line4.text).toBe '    while(items.length > 0) {...}'
+            expect(line5.text).toBe '    return sort(left).concat(pivot).concat(sort(right));'
+
+            expect(changeHandler).toHaveBeenCalled()
+            [event] = changeHandler.argsForCall[0]
+            expect(event.oldRange).toEqual [[4, 0], [4, 56]]
+            expect(event.newRange).toEqual [[4, 0], [5, 56]]
+            changeHandler.reset()
+
+            fold2.destroy()
+            [line4, line5] = renderer.linesForRows(4, 5)
+            expect(line4.text).toBe '    while(items.length > 0) {'
+            expect(line5.text).toBe '      current = items.shift();'
+
+            expect(changeHandler).toHaveBeenCalled()
+            [event] = changeHandler.argsForCall[0]
+            expect(event.oldRange).toEqual [[4, 0], [4, 33]]
+            expect(event.newRange).toEqual [[4, 0], [7, 5]]
+
+        describe "when the second fold is created after the first fold", ->
+          it "renders a placeholder for both folds on the first line of the first fold", ->
+            fold1 = renderer.createFold([[4, 29], [7, 4]])
+            fold2 = renderer.createFold([[7, 5], [8, 36]])
+            [line4, line5] = renderer.linesForRows(4, 5)
+            expect(line4.text).toBe  '    while(items.length > 0) {...}...concat(sort(right));'
+            expect(line5.text).toBe '  };'
+
+            expect(changeHandler.callCount).toBe 2
+            [[event1], [event2]] = changeHandler.argsForCall
+            expect(event1.oldRange).toEqual [[4, 0], [7, 5]]
+            expect(event1.newRange).toEqual [[4, 0], [4, 33]]
+            expect(event2.oldRange).toEqual [[4, 0], [5, 56]]
+            expect(event2.newRange).toEqual [[4, 0], [4, 56]]
+            changeHandler.reset()
+
+            fold1.destroy()
+            [line4, line5] = renderer.linesForRows(4, 5)
+            [line7] = renderer.linesForRows(7, 7)
+            expect(line4.text).toBe '    while(items.length > 0) {'
+            expect(line5.text).toBe '      current = items.shift();'
+            expect(line7.text).toBe '    }...concat(sort(right));'
+
+            expect(changeHandler).toHaveBeenCalled()
+            [event] = changeHandler.argsForCall[0]
+            expect(event.oldRange).toEqual [[4, 0], [4, 56]]
+            expect(event.newRange).toEqual [[4, 0], [7, 28]]
+            changeHandler.reset()
+
+            fold2.destroy()
+            [line4, line5] = renderer.linesForRows(4, 5)
+            expect(line4.text).toBe '    while(items.length > 0) {'
+            expect(line5.text).toBe '      current = items.shift();'
+
+            expect(changeHandler).toHaveBeenCalled()
+            [event] = changeHandler.argsForCall[0]
+            expect(event.oldRange).toEqual [[7, 0], [7, 28]]
+            expect(event.newRange).toEqual [[7, 0], [8, 56]]

       describe "when a fold starts at the beginning of a line", ->
+        it "renders a placeholder at the beginning of the line", ->
+          renderer.createFold([[4, 0], [7, 4]])
+          expect(renderer.lineForRow(4).text).toBe '...}'

       describe "when a fold ends at the beginning of a line", ->
+        it "renders a placeholder at the beginning of the line", ->
+          renderer.createFold([[4, 29], [7, 0]])
+          expect(renderer.lineForRow(4).text).toBe '    while(items.length > 0) {...    }'

       describe "when a fold starts on the first line of the buffer", ->
-
-
+        it "renders the first line correctly when the fold is destroyed", ->
+          fold = renderer.createFold([[0, 14], [0, 27]])
+          fold.destroy()
+          expect(renderer.lineForRow(0).text).toBe 'var quicksort = function () {'


Delete hello-world_test.coffee

Delete hello-world_test.coffee

--- test/hello-world_test.coffee
+++ test/hello-world_test.coffee
@@ -1,19 +0,0 @@
-chai = require 'chai'
-sinon = require 'sinon'
-chai.use require 'sinon-chai'
-
-expect = chai.expect
-
-describe 'hello-world', ->
-  beforeEach ->
-    @robot =
-      respond: sinon.spy()
-      hear: sinon.spy()
-
-    require('../src/hello-world')(@robot)
-
-  it 'registers a respond listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/hello/)
-
-  it 'registers a hear listener', ->
-    expect(@robot.hear).to.have.been.calledWith(/orly/)


use new date function in specs

use new date function in specs

--- spec/new-post-view-spec.coffee
+++ spec/new-post-view-spec.coffee
@@ -59,7 +59,7 @@
         if fs.existsSync(pathToCreate)
           fileContents = fs.readFileSync(pathToCreate, {encoding: 'UTF-8'})

-          expect(fileContents).toBe dialog.fileContents('Jekyll New Post', dialog.generateDateString())
+          expect(fileContents).toBe dialog.fileContents('Jekyll New Post', dialog.generateDateString(new Date(), true))

           fs.unlinkSync(pathToCreate)
         else
@@ -98,7 +98,7 @@
         if fs.existsSync(pathToCreate)
           fileContents = fs.readFileSync(pathToCreate, {encoding: 'UTF-8'})

-          expect(fileContents).toBe dialog.fileContents('Jekyll New Post', dialog.generateDateString())
+          expect(fileContents).toBe dialog.fileContents('Jekyll New Post', dialog.generateDateString(new Date(), true))

           fs.unlinkSync(pathToCreate)
         else


Added mixpanel tracking for unsuccessful logins and sign ups.

--- app/assets/javascript/fancy_login/login_register_fancybox.coffee
+++ app/assets/javascript/fancy_login/login_register_fancybox.coffee
@@ -78,7 +78,6 @@
       mixingpanel_tracker.track "Login", {"action":"signin with google", "location":"login fancybox", "url": document.URL }

     $('#register_form').submit (e) =>
-      mixingpanel_tracker.track "Register", {"action":"register", "location":"login fancybox", "url": document.URL }
       @formSubmit e, $('#register_form').data("url")

     $('#loginbox-sign-up .btn-facebook').click (e) =>
@@ -206,6 +205,12 @@

       if has_errors
         $.fancybox.update()
+
+        if (form_id == "register_form")
+          mixingpanel_tracker.track "Register", {"action":"register unsuccessful", "location":"login fancybox", "url": document.URL }
+        else
+          mixingpanel_tracker.track "Login", {"action":"signin unsuccessful", "location":"login fancybox", "url": document.URL }
+
         return false

       # send the request


AnswerTypeController as Object

AnswerTypeController as Object

--- app/assets/javascripts/hanuman/controllers/answer_type.js.coffee
+++ app/assets/javascripts/hanuman/controllers/answer_type.js.coffee
@@ -1,3 +1,3 @@
 App.AnswerTypeController = Ember.ObjectController.extend({
-
+  # must define AnswerTypeController as an ObjectController for the belongsTo to work
 })


Refactored infinite scroll to support taxons. Closes #22

--- app/assets/javascripts/sprangular/controllers/productList.coffee
+++ app/assets/javascripts/sprangular/controllers/productList.coffee
@@ -14,12 +14,16 @@
   $scope.loadNextPage = ->
     $scope.fetching = true

-    Catalog.products($routeParams.search, $scope.page+1)
-      .then (newPage) ->
-        $scope.page++
-        $scope.fetching = false
-        $scope.products = $scope.products.concat(newPage)
-        $scope.loadingComplete = newPage.isLastPage
+    load = if taxon
+      Catalog.productsByTaxon(taxon.permalink, $scope.page+1)
+    else
+      Catalog.products($routeParams.search, $scope.page+1)
+
+    load.then (newPage) ->
+      $scope.page++
+      $scope.fetching = false
+      $scope.products = $scope.products.concat(newPage)
+      $scope.loadingComplete = newPage.isLastPage

   $scope.addToCart = (variant, qty) ->
     Cart.addVariant(variant, qty)


Updating

Updating

--- config.cson
+++ config.cson
@@ -77,7 +77,6 @@
     style: {}
   "tool-bar":
     position: "Left"
-    visible: false
   "tree-view":
     autoReveal: true
     hideVcsIgnoredFiles: false


Disable most "smart" features of language-markdown

--- config.cson
+++ config.cson
@@ -35,6 +35,9 @@
 			"welcome"
 		]
 		openEmptyEditorOnStart: false
+		packagesWithKeymapsDisabled: [
+			"language-markdown"
+		]
 		telemetryConsent: "no"
 		themes: [
 			"atom-light-ui"
@@ -61,7 +64,12 @@
 	"language-applescript":
 		autoCompile: false
 	"language-markdown":
+		addListItems: false
+		autoIncrementListItems: false
+		emphasisShortcuts: false
 		indentListItems: false
+		linkShortcuts: false
+		removeEmptyListItems: false
 	"markdown-preview":
 		grammars: [
 			"source.gfm"


force: true to ensure loadModel loads associations

--- client/lanes/models/Query.coffee
+++ client/lanes/models/Query.coffee
@@ -166,7 +166,7 @@
         value = this.get('value')
         value += '%' if 'like' == op
         value = parseFloat(value) if @field.type == "n"
-        param[ this.field.id ] = if 'eq' == op then value else { op: op, value: value }
+        param[ @field.id ] = if 'eq' == op then value else { op: op, value: value }
         param

@@ -294,7 +294,7 @@
         ! @clauses.findWhere( isValid: false )

     loadModel: (model, options = {}) ->
-        _.extend(options, _.result(this, 'syncOptions'))
+        _.extend(options, _.result(this, 'syncOptions'), force: true)
         model.withAssociations(options.include || [], options)

     loadSingle: (code, options = {}) ->


requested changes complete

requested changes complete

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -37,7 +37,6 @@
       return unless range? and range.isCollapsed()
       anchor = this._findAnchor(range)
       if anchor
-        @range = range
         this.setMode(anchor.href, false)
         this.show(anchor)
       else
@@ -55,11 +54,11 @@

   saveLink: ->
     url = this._normalizeURL(@textbox.value)
-    if @range?
+    if @range? and @range.isCollapsed()
       anchor = this._findAnchor(@range)
-      if anchor?
-        anchor.href = url
-    @quill.formatText(@range, 'link', url, 'user') if @range?
+      anchor.href = url if anchor?
+    else
+      @quill.formatText(@range, 'link', url, 'user') if @range?
     this.setMode(url, false)

   setMode: (url, edit = false) ->


fix setting description

fix setting description

--- lib/package/config.coffee
+++ lib/package/config.coffee
@@ -45,7 +45,7 @@
           type: 'string'
         order: 5
       externalProcessPort:
-        title: 'Port of external Julia process'
+        title: 'Port for communicating with the Julia process'
         type: 'string'
         description: '`random` will use a new port each time, or enter an integer to set the port statically.'
         default: 'random'


note that setting ARGS requires julia reboot

--- lib/package/config.coffee
+++ lib/package/config.coffee
@@ -19,7 +19,7 @@
       arguments:
         title: 'Arguments'
         type: 'array'
-        description: 'Set `ARGS` to the following entries.'
+        description: 'Set `ARGS` to the following entries. Requires restart of Julia process.'
         default: []
         items:
           type: 'string'


Revert "Go back to using node's console.* and std*.write"

This reverts commit 4e8db2c3be8f3c9a172fb87ad8739493ab3dfbb1.

It seems that we still have problems with Node.js's console.* calls, I'm
reverting this for now unitl we get a solution.

Fixes #3100.

--- atom/browser/lib/init.coffee
+++ atom/browser/lib/init.coffee
@@ -17,6 +17,25 @@
 # Electron's built-in libraries.
 globalPaths = Module.globalPaths
 globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
+
+if process.platform is 'win32'
+  # Redirect node's console to use our own implementations, since node can not
+  # handle console output when running as GUI program.
+  consoleLog = (args...) ->
+    process.log util.format(args...) + "\n"
+  streamWrite = (chunk, encoding, callback) ->
+    chunk = chunk.toString(encoding) if Buffer.isBuffer chunk
+    process.log chunk
+    callback() if callback
+    true
+  console.log = console.error = console.warn = consoleLog
+  process.stdout.write = process.stderr.write = streamWrite
+
+  # Always returns EOF for stdin stream.
+  Readable = require('stream').Readable
+  stdin = new Readable
+  stdin.push null
+  process.__defineGetter__ 'stdin', -> stdin

 # Don't quit on fatal error.
 process.on 'uncaughtException', (error) ->


Fix `process.std*.write`

Fix `process.std*.write`

- Support printing Buffer
- Don't add '\n' at the end the chunk

--- atom/browser/lib/init.coffee
+++ atom/browser/lib/init.coffee
@@ -21,10 +21,15 @@
 if process.platform is 'win32'
   # Redirect node's console to use our own implementations, since node can not
   # handle console output when running as GUI program.
-  print = (args...) ->
+  consoleLog = (args...) ->
     process.log util.format(args...) + "\n"
-  console.log = console.error = console.warn = print
-  process.stdout.write = process.stderr.write = print
+  streamWrite = (chunk, encoding, callback) ->
+    chunk = chunk.toString(encoding) if Buffer.isBuffer chunk
+    process.log chunk
+    callback() if callback
+    true
+  console.log = console.error = console.warn = consoleLog
+  process.stdout.write = process.stderr.write = streamWrite

   # Always returns EOF for stdin stream.
   Readable = require('stream').Readable


fix up PRESENTER_USER

fix up PRESENTER_USER

--- lib/server.storage.coffee
+++ lib/server.storage.coffee
@@ -44,6 +44,6 @@

 PRESENTER_USER = {id: "236886", provider: "github"}
 do ->
-  subscribe(channels.getUserEventChannelName(), publishEventsFromPresenterToSlaves)
+  subscribe(channels.getUserEventChannelName(PRESENTER_USER), publishEventsFromPresenterToSlaves)

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


fixed the display attribute when fade in or fade out

--- src/coffee/animationEffect.coffee
+++ src/coffee/animationEffect.coffee
@@ -51,6 +51,7 @@
   ###
   fadeIn:( el, callback )->
     animation = @createAnimation el
+    domUtils.show el
     animation.cssEffect 'eh-fade-in', callback

   ###
@@ -59,5 +60,7 @@
   ###
   fadeOut:( el, callback )->
     animation = @createAnimation el
-    animation.cssEffect 'eh-fade-out', callback
+    animation.cssEffect 'eh-fade-out', ->
+      domUtils.hide el
+      callback and callback()


clear output on enter

clear output on enter

--- app/client.coffee
+++ app/client.coffee
@@ -598,7 +598,7 @@
 # ---------------------------

 #socket = io.connect('http://ssl-webclient.roboime.com:8888/')
-socket = io.connect('http://127.0.0.1:8888')
+socket = io.connect()

 socket.on "vision_packet", (packet) ->
   updateVisionState packet
@@ -628,4 +628,5 @@
   obj.args.splice(obj.args.length - 1, 1)
   $("#cout").append "<div>" + command + "</div>"
   socket.emit "cmd_packet", obj
-
+  $(this).val('')
+


Revert "REMOVE ME hacks on hacks on hacks"

This reverts commit 35d9c3dfffa67a3bc3206e533f3b6ff8f35a6baa.

--- build/tasks/set-version-task.coffee
+++ build/tasks/set-version-task.coffee
@@ -5,9 +5,9 @@
   {spawn} = require('./task-helpers')(grunt)

   getVersion = (callback) ->
-    releasableBranches = ['stable', 'beta', 'upload-to-s3']
+    releasableBranches = ['stable', 'beta']
     channel = grunt.config.get('atom.channel')
-    shouldUseCommitHash = false # if channel in releasableBranches then false else true
+    shouldUseCommitHash = if channel in releasableBranches then false else true
     inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
     {version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
     if shouldUseCommitHash and inRepository


not yet ready for windows builds

not yet ready for windows builds

--- gulp/electron-build.coffee
+++ gulp/electron-build.coffee
@@ -32,11 +32,11 @@
               CFBundleName: packageJson.name
               CFBundleVersion: packageJson.version
               icon: path.join(paths.assets, 'logo.icns')
-          win:
-              "version-string": packageJson.version
-              "file-version": packageJson.version
-              "product-version": packageJson.version
-              "icon": path.join(paths.assets, 'logo.icns')
+          # win:
+          #     "version-string": packageJson.version
+          #     "file-version": packageJson.version
+          #     "product-version": packageJson.version
+          #     "icon": path.join(paths.assets, 'logo.icns')
       })
       .pipe gulp.dest("")


remove old ensure_access

remove old ensure_access

--- app/github.coffee
+++ app/github.coffee
@@ -187,33 +187,6 @@
               require_access_token: @props.require_access_token
               handle_token: (t) => @state.tokens.push t

-  ensure_access = (ctx, url) ->
-    unless url?
-      domain = github.default()
-      site = github.get_site domain
-    else
-      site = github.get_site_from_url url
-      domain = url.hostname()
-    if site? and site.requires_access_token and not site.access_token?
-      result = Q.defer()
-      ctx.text 'Please set a GitHub access token:'
-      input = ctx.input.text_input()
-      button = ctx.input.button('Save')
-      user_details = button.map(input).flatMapLatest (access_token) ->
-        Bacon.combineTemplate
-          user: Bacon.fromPromise http.get github.to_api_url(site, '/user').setQuery {access_token}
-          access_token: access_token
-        .changes()
-      user_details.onValue ({user, access_token}) =>
-        global_settings.user_settings.set 'github', 'githubs', domain, 'access_token', access_token
-        ctx.text "Logged in as #{user.name}"
-        result.resolve url?.setQuery {access_token}
-      user_details.onError =>
-        ctx.text "That access token didn't work. Try again?"
-      result.promise
-    else
-      Q url
-
   component_cmd 'gist', 'Loads a script from a gist', (ctx, gist, options={}) ->
     if arguments.length is 0
       @github.save_gist()


Add playground

Add playground


improve menus:

improve menus:

- create menu in atom-text-editor space
- nested menu for Hydrogen commands
- add more menus

--- menus/hydrogen.cson
+++ menus/hydrogen.cson
@@ -1,35 +1,203 @@
-'context-menu':
-  '.tree-view .file .name[data-name$=\\.ipynb]':
-    [{label: 'Import Notebook', command: 'hydrogen:import-notebook'}]
 'menu': [
   {
-    'label': 'Packages'
-    'submenu': [
-      'label': 'Hydrogen'
-      'submenu': [
-        {
-          'label': 'Run'
-          'command': 'hydrogen:run'
-        }, {
-          'label': 'Run and Move Down'
-          'command': 'hydrogen:run-and-move-down'
-        }, {
-          'label': 'Run Cell'
-          'command': 'hydrogen:run-cell'
-        }, {
-          'label': 'Run Cell and Move Down'
-          'command': 'hydrogen:run-cell-and-move-down'
-        }, {
-          'label': 'Run All'
-          'command': 'hydrogen:run-all'
-        }, {
-          'label': 'Run All Above'
-          'command': 'hydrogen:run-all-above'
-        }, {
-          'label': 'Connect to Remote Kernel'
-          'command': 'hydrogen:connect-to-remote-kernel'
+    label: 'Packages'
+    submenu: [
+      label: 'Hydrogen'
+      submenu: [
+        {
+          label: 'Start Local Kernel'
+          command: 'hydrogen:start-local-kenel'
+        }
+        {
+          label: 'Connect to Remote Kernel'
+          command: 'hydrogen:connect-to-remote-kernel'
+        }
+        {
+          label: 'Connect to Existing Kernel'
+          command: 'hydrogen:connect-to-existing-kernel'
+        }
+        {
+          label: 'Interrupt Kernel'
+          command: 'hydrogen:interrupt-kernel'
+        }
+        {
+          label: 'Restart Kernel'
+          command: 'hydrogen:restart-kernel'
+        }
+        {
+          label: 'Shutdown Kernel'
+          command: 'hydrogen:shutdown-kernel'
+        }
+        {
+          label: 'Toggle Kernel Monitor'
+          command: 'hydrogen:toggle-kernel-monitor'
+        }
+        {
+          label: 'Update Kernels'
+          commands: 'hydrogen:update-kernels'
+        }
+        {type: 'separator'}
+        {
+          label: 'Run'
+          command: 'hydrogen:run'
+        }
+        {
+          label: 'Run and Move Down'
+          command: 'hydrogen:run-and-move-down'
+        }
+        {
+          label: 'Run Cell'
+          command: 'hydrogen:run-cell'
+        }
+        {
+          label: 'Run Cell and Move Down'
+          command: 'hydrogen:run-cell-and-move-down'
+        }
+        {
+          label: 'Run All'
+          command: 'hydrogen:run-all'
+        }
+        {
+          label: 'Run All Above'
+          command: 'hydrogen:run-all-above'
+        }
+        {type: 'separator'}
+        {
+          label: 'Clear Results'
+          commands: 'hydrogen:clear-results'
+        }
+        {type: 'separator'}
+        {
+          label: 'Toggle Insepctor'
+          command: 'hydrogen:toggle-inspector'
+        }
+        {
+          label: 'Toggle Output Area'
+          command: 'hydrogen:toggle-output-area'
+        }
+        {
+          label: 'Toggle Watches'
+          command: 'hydrogen:toggle-watches'
+        }
+        {type: 'separator'}
+        {
+          label: 'Import Notebook'
+          command: 'hydrogen:import-notebook'
+        }
+        {
+          label: 'Export Notebook'
+          command: 'hydrogen:export-notebook'
         }
       ]
     ]
   }
 ]
+
+'context-menu':
+  '.tree-view .file .name[data-name$=\\.ipynb]': [
+    {type: 'separator'}
+    {
+      label: 'Hydrogen'
+      submenu: [
+        {
+          label: 'Import Notebook'
+          command: 'hydrogen:import-notebook'
+        }
+      ]
+    }
+    {type: 'separator'}
+  ]
+
+  'atom-text-editor:not([mini])': [
+    {type: 'separator'}
+    {
+      label: 'Hydrogen'
+      submenu: [
+        {
+          label: 'Start Local Kernel'
+          command: 'hydrogen:start-local-kenel'
+        }
+        {
+          label: 'Connect to Remote Kernel'
+          command: 'hydrogen:connect-to-remote-kernel'
+        }
+        {
+          label: 'Connect to Existing Kernel'
+          command: 'hydrogen:connect-to-existing-kernel'
+        }
+        {
+          label: 'Interrupt Kernel'
+          command: 'hydrogen:interrupt-kernel'
+        }
+        {
+          label: 'Restart Kernel'
+          command: 'hydrogen:restart-kernel'
+        }
+        {
+          label: 'Shutdown Kernel'
+          command: 'hydrogen:shutdown-kernel'
+        }
+        {
+          label: 'Toggle Kernel Monitor'
+          command: 'hydrogen:toggle-kernel-monitor'
+        }
+        {type: 'separator'}
+        {
+          label: 'Run'
+          command: 'hydrogen:run'
+        }
+        {
+          label: 'Run and Move Down'
+          command: 'hydrogen:run-and-move-down'
+        }
+        {
+          label: 'Run Cell'
+          command: 'hydrogen:run-cell'
+        }
+        {
+          label: 'Run Cell and Move Down'
+          command: 'hydrogen:run-cell-and-move-down'
+        }
+        {
+          label: 'Run All'
+          command: 'hydrogen:run-all'
+        }
+        {
+          label: 'Run All Above'
+          command: 'hydrogen:run-all-above'
+        }
+        {type: 'separator'}
+        {
+          label: 'Clear Result'
+          commands: 'hydrogen:clear-result'
+        }
+        {
+          label: 'Clear Results'
+          commands: 'hydrogen:clear-results'
+        }
+        {type: 'separator'}
+        {
+          label: 'Toggle Insepctor'
+          command: 'hydrogen:toggle-inspector'
+        }
+        {
+          label: 'Toggle Output Area'
+          command: 'hydrogen:toggle-output-area'
+        }
+        {
+          label: 'Toggle Watches'
+          command: 'hydrogen:toggle-watches'
+        }
+        {type: 'separator'}
+        {
+          label: 'Import Notebook'
+          command: 'hydrogen:import-notebook'
+        }
+        {
+          label: 'Export Notebook'
+          command: 'hydrogen:export-notebook'
+        }
+      ]
+    }
+    {type: 'separator'}
+  ]


Reject fails

Reject fails

--- src/backburner.coffee
+++ src/backburner.coffee
@@ -54,7 +54,7 @@
         try
             @_loopBodyFn
         catch e
-            @resolveWith this, e
+            @rejectWith this, e

 backburner.while = (loopTestFn, loopBodyFn, context) ->
     context ?= {}


strip extra comments and console.log statements

--- views/puzzle.coffee
+++ views/puzzle.coffee
@@ -105,20 +105,13 @@
   constructor: (puzzle_app) ->
     @puzzle = puzzle_app
     @all_pieces = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p"]
-    @unset_pieces = [] # FIXME Change to all_pieces.clone()
-    @unset_pieces[p] = @all_pieces[p] for p in [0..15]
-    # @unset_pieces = [] # FIXME Change to all_pieces.clone()
-    # @unset_pieces[p] = @puzzle.pz_status.all_pieces[p] for p in [0..15]
-    # @pieces_in_puzzle = 16

   start_new_puzzle: () ->
     @finished = false
-    @unset_pieces = [] # FIXME Change to all_pieces.clone()
-    @unset_pieces[p] = @all_pieces[p] for p in [0..15]
+    @unset_pieces = @all_pieces.slice(0, 15)
     @pieces_in_puzzle = 16
     @pieces_set = 0
-    console.log "CALL reset_mask() from start_new_puzzle"
     @puzzle.mask.reset_mask(@puzzle.grid)
     @puzzle.ui_status.reset()
     @puzzle.puzzle_view.reset()
@@ -141,7 +134,6 @@

   next_piece: () ->
-    console.log "CALL reset_mask() from next_piece"
     @puzzle.mask.reset_mask(@puzzle.grid)
     @puzzle.indicator.decrement()
     pc = Math.floor(@pieces_in_puzzle*Math.random())
@@ -153,7 +145,6 @@
   start_first_piece: () ->
     @puzzle.colors.new_rotation()
     @puzzle.indicator.start_indicator()
-    # @puzzle.mask.init_missing_pieces_mask()
     pc = Math.floor(@pieces_in_puzzle*Math.random())
     @sym = @unset_pieces[pc]
     @unset_pieces.splice(@unset_pieces.indexOf(@sym),1)
@@ -164,21 +155,16 @@
 class MissingPiecesMask

   constructor: (puzzle_app) ->
-  # constructor: (puzzle_app, grid) ->
     @puzzle = puzzle_app
     @puzzle_pattern = @puzzle.puzzle_pattern
-    # @grid = grid
     @hex_draw = @puzzle.hex_draw
-    # @reset_mask()
+

   reset_mask: (grid) ->
-    console.log " BEGIN  MissingPiecesMask#reset_mask()"
-    console.log "    grid size = #{grid.length}"
     @puzzle.puzzle_view.draw_photo()
     @grid = grid
     @get_next_color()
     @draw_mask(grid)
-    console.log " END    MissingPiecesMask#reset_mask()"

   get_next_color: () ->
@@ -186,8 +172,6 @@

   draw_mask: (grid) ->
-    console.log "     draw_mask() called from reset_mask()"
-    console.log "        grid size = #{grid.length}"
     @hex_draw.set_context("canvas")
     for bb in [1..10]
       for aa in [1..24]
@@ -198,18 +182,15 @@
 class PuzzlePiece

   constructor: (puzzle_app) ->
-    console.log "PuzzlePiece constructor"
     @puzzle = puzzle_app
     @hex_box = @puzzle.hex_box
     @pattern  = new PiecePattern(this)
-    console.log "    @pattern.img.id = #{@pattern.img.id}"
     @redraw = new PieceRedrawBuffer(this)
     @hexes = []
     @bounding_box = [0,0,0,0,]

   construct_piece: (sym) ->
-    console.log "PuzzlePiece construct_piece() sym = #{sym}"
     @sym = sym
     @hexes = @get_hexes()
     @hex_box.set_hex_box(sym)
@@ -237,17 +218,11 @@
     @photo_clip.width = @hex_box.width
     @photo_clip.height = @hex_box.height
     @photo_clip_context = @photo_clip.getContext('2d')
-
     xx = @hex_box.box_xy[0] - @puzzle.puzzle_view.puzzle_xy[0]
     yy = @hex_box.box_xy[1] - @puzzle.puzzle_view.puzzle_xy[1]
-
     photo = @puzzle.puzzle_view.photo
     @photo_clip_context.drawImage(photo,xx,yy,@hex_box.width,@hex_box.height,0,0,@hex_box.width,@hex_box.height)
-
-    console.log "CALL reset_mask() from cut_piece_from_photo"
     @puzzle.mask.reset_mask(@puzzle.grid)
-    # @mask = new MissingPiecesMask(@puzzle, @puzzle.grid)
-    # pm_img = document.getElementById("piece-mask")
     context = @pattern.img.getContext('2d')
     context.globalCompositeOperation = 'source-atop'
     context.drawImage(@photo_clip,0,0)
@@ -284,15 +259,6 @@

   in_bounding_box: (x,y) ->
     if x>@left && x<@right && y>@top && y<@bottom then return true else return false
-
-
-  # TODO Does this method belong in PiecePattern class?
-  # get_hexes: () ->
-  #   hexes = []
-  #   for bb in [1..10]
-  #     for aa in [1..24]
-  #       hexes.push([aa,bb]) if @puzzle.puzzle_pattern.grid[bb][aa] == @sym
-  #   return hexes

@@ -443,13 +409,11 @@

   set_context: (mode) ->
-    console.log "CALL set_context()  mode = #{mode}"
     @mode = mode
     @context = @puzzle_view.get_drawing_context(mode)

   set_context2: (img) ->
-    console.log "CALL set_context2()  img.id = #{img.id}"
     @context = img.getContext('2d')

@@ -491,7 +455,6 @@

   set_hex_box: (piece_symbol) ->
-    console.log "HexBox set_hex_box() piece_symbol = #{piece_symbol}"
     @reset_hexes(@get_hexes(piece_symbol))
     @get_box_metrics()

@@ -616,7 +579,6 @@

   decrement: () ->
     @write_message(@puzzle.pz_status.pieces_in_puzzle)
-    # console.log(@puzzle.pz_status.pieces_in_puzzle+" Pieces Left")

   write_message: (n) ->
@@ -663,7 +625,6 @@
     rr.push(@app_colors[i]) for i in [@start_number..15]
     rr.push(@app_colors[i]) for i in [0..@start_number-1] if @start_number > 0
     rr = rr.reverse() if @color_direction == "down"
-    # console.log(color) for color in rr
     return rr


add use configuratorView for element tab

--- BackofficeBundle/Resources/public/coffee/tab/tabViewFormLoader.coffee
+++ BackofficeBundle/Resources/public/coffee/tab/tabViewFormLoader.coffee
@@ -1,6 +1,5 @@
 tabViewFormLoad = (options) ->
   tabViewClass = appConfigurationView.getConfiguration(options.entityType, 'showTab')
-  elementTabViewClass = appConfigurationView.getConfiguration(options.entityType, 'editEntityTab')

   tabView = new tabViewClass(options)
   panels = getPanelsLink(options.element.get('links'))
@@ -11,6 +10,7 @@
         url: panel.link
         method: "GET"
         success: (response) ->
+          elementTabViewClass = appConfigurationView.getConfiguration(options.entityType+'_tab_'+panel.id, 'editEntityTab')
           view = new elementTabViewClass(
             html: response,
             entityType: options.entityType,


Xbox: turn off debugging and update output

--- bots/Xbox.coffee
+++ bots/Xbox.coffee
@@ -10,7 +10,7 @@
   pageSettings:
     loadImages:false, loadPlugins:false # don't load images or plugins
     userAgent:'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.84 Safari/537.22'
-  verbose:true; logLevel:'debug' # output logs at specified level
+  verbose:false; logLevel:'debug' # output logs at specified level
 )
 ARGS=[]; cliCount=0
 CLI=[{c:0,n:'email',d:''},{c:1,n:'password',d:''},
@@ -24,7 +24,6 @@
   else ARGS[arg.c] = arg.d # use the default value
 if '' in ARGS[0..2] # if the username or password was not set
   casper.echo 'argErr: username, password, and code cannot be empty'; casper.exit 1
-#if ARGS[4] < ARGS[3] then ARGS[4] = ARGS[3] # to make sure that maxTime >= minTime

 casper.echo 'Logging in...' # give immediate output to the user
 casper.start 'http://www.xbox.com/', ->
@@ -40,9 +39,14 @@
 ), ARGS[0], ARGS[1]

 casper.thenOpen 'https://live.xbox.com/en-US/RedeemCode'
+casper.echo "Entering code: #{ARGS[2]}"
 casper.then redeemCode = ->
   @sendKeys 'input.TokenTextBox', ARGS[2]
   @click '#RedeemCode'

+casper.then getStatus = ->
+  @echo @fetchText '#RedeemResults p'
+
 casper.run ->
+  @echo "Finished redeeming code."
   @exit 0


Prevent command duplication

Prevent command duplication

--- components/ConnectRuntime.coffee
+++ components/ConnectRuntime.coffee
@@ -95,7 +95,7 @@
       # TODO: Read basedir from graph?
       runtime.sendComponent 'list', 'noflo-ui-preview'
       @sendGraph runtime, editor
-      @subscribeEditor editor, runtime
+    @subscribeEditor editor, runtime

     runtime.on 'component', (message) ->
       definition =


Use "inside" marker invalidation strategy

--- lib/violation-view.coffee
+++ lib/violation-view.coffee
@@ -33,7 +33,25 @@
       selector: @find('.violation-border')

   trackEdit: ->
-    @marker = @editor.markScreenRange(@getCurrentScreenRange(), invalidation: 'inside')
+    # :persistent -
+    # Whether to include this marker when serializing the buffer. Defaults to true.
+    #
+    # :invalidate -
+    # Determines the rules by which changes to the buffer *invalidate* the
+    # marker. Defaults to 'overlap', but can be any of the following:
+    # * 'never':
+    #     The marker is never marked as invalid. This is a good choice for
+    #     markers representing selections in an editor.
+    # * 'surround':
+    #     The marker is invalidated by changes that completely surround it.
+    # * 'overlap':
+    #     The marker is invalidated by changes that surround the start or
+    #     end of the marker. This is the default.
+    # * 'inside':
+    #     The marker is invalidated by a change that touches the marked
+    #     region in any way. This is the most fragile strategy.
+    options = { invalidation: 'inside', persistent: false }
+    @marker = @editor.markScreenRange(@getCurrentScreenRange(), options)
     @marker.on 'changed', ({newHeadScreenPosition, newTailScreenPosition, isValid}) =>
       # Head and Tail: Markers always have a head and sometimes have a tail.
       # If you think of a marker as an editor selection, the tail is the part that's stationary


Added resortTabHandles method to sort handles when a handle index has changed.

--- components/tabs/KDTabView.coffee
+++ components/tabs/KDTabView.coffee
@@ -65,10 +65,24 @@

       newTabHandle.$().css maxWidth: @getOptions().maxHandleWidth

+      newTabHandle.on "HandleIndexHasChanged", @bound "resortTabHandles"
+
       return paneInstance
     else
       warn "You can't add #{paneInstance.constructor.name if paneInstance?.constructor?.name?} as a pane, use KDTabPaneView instead."
       false
+
+  resortTabHandles:->
+    #   {subViews}       = @holder
+    #   temp             = subViews[@draggedItemIndex]
+
+    #   subViews.splice(@draggedItemIndex, 1);
+    #   targetPos = if @isDraggingToLeft then Math.abs @passedHandleLength - @draggedItemIndex else @passedHandleLength + @draggedItemIndex
+    #   subViews.splice(targetPos, 0, temp);
+
+    #   names = ''
+    #   names += ', ' + $(subView.domElement).find('b').text() for subView in subViews
+    #   log names

   removePane:(pane)->
     pane.emit "KDTabPaneDestroy"


Added Scarlata to team list.

Added Scarlata to team list.

Kyle - just realised I missed off Claudia in the update. Can you have a look and see what you think of this text.

--- app/lib/en.coffee
+++ app/lib/en.coffee
@@ -1171,6 +1171,7 @@
       hart: 'Ross Hart - Astronomer, University of Nottingham
 PhD student.'
       kruk: 'Sandor Kruk - Astronomer, University of Oxford
 PhD student.'
       newnham: 'Lucy Newnham - Astronomer, University of Portsmouth
 PhD student.'
+      scarlata: 'Claudia Scarlata - Astronomer, University of Minnesota
 An expert in automated morphology measures, now working on efforts to combine machine learning and human classification.'

     dev:
       title: 'Zooniverse Development Team'


* remove unneeded spacer

* remove unneeded spacer

--- src/packer.coffee
+++ src/packer.coffee
@@ -129,7 +129,6 @@
   ###
   _getHeader : (env_header, strict_settings, cache_modules_settings) ->
     """
-
     // Generated by clinch #{@_clinch_verison_}
     (function() {
       #{@_getStrictLine strict_settings}


Fix github-merge to work against messages like `hubot merge github_user/repository/myfix into base`

This fix changes the regex used from:

  http://rubular.com/r/GYTfo8swxd

To:

  http://rubular.com/r/ajwxmrfDZ4

--- src/scripts/github-merge.coffee
+++ src/scripts/github-merge.coffee
@@ -22,7 +22,7 @@
   github = require("githubot")(robot)

   # http://rubular.com/r/vnnwHvt75L
-  robot.respond /merge ([-_\.0-9a-zA-Z]+)(\/([-_\.a-zA-z0-9\/]+))? into ([-_\.a-zA-z0-9\/]+)$/i, (msg) ->
+  robot.respond /merge ([-_\.0-9a-zA-Z]+\/[-_\.0-9a-zA-Z]+)(\/([-_\.a-zA-z0-9\/]+))? into ([-_\.a-zA-z0-9\/]+)$/i, (msg) ->
     app      = msg.match[1]
     head     = msg.match[3] || "master"
     base     = msg.match[4]


Stack skills

Stack skills

--- src/parser.coffee
+++ src/parser.coffee
@@ -171,12 +171,12 @@
     token = tokens.shift()
     switch token.type
       when 'list_start'
+        argumentsListStack.push argumentsList if argumentsList?
         argumentsList = []
-        argumentsListStack.push argumentsList

       when 'list_item_start', 'loose_item_start'
+        argumentStack.push argument if argument?
         argument = {}
-        argumentStack.push argument

       when 'text'
         argument.text ?= []
@@ -187,14 +187,12 @@
         argumentsList.push argument
         delete argument.text

-        argumentStack.pop()
-        argument = _.last argumentStack
+        argument = argumentStack.pop()

       when 'list_end'
         if argument?
           argument.arguments = argumentsList
-          argumentsListStack.pop()
-          argumentsList = _.last argumentsListStack
+          argumentsList = argumentsListStack.pop()
         else
           args = argumentsList


Adds an event handler which allows to modify the object send to s3.

--- app/assets/javascripts/s3_direct_upload.js.coffee
+++ app/assets/javascripts/s3_direct_upload.js.coffee
@@ -25,6 +25,7 @@
     progress_bar_target: null
     click_submit_target: null
     allow_multiple_files: true
+    before_upload: false

   $.extend settings, options

@@ -140,7 +141,10 @@
     content.unique_id  = file.unique_id if 'unique_id' of file
     content.relativePath = build_relativePath(file) if has_relativePath(file)
     content = $.extend content, settings.additional_data if settings.additional_data
-    content
+    if settings.before_upload
+      settings.before_upload(content)
+    else
+      content

   has_relativePath = (file) ->
     file.relativePath || file.webkitRelativePath


Show count of individual rankings in header of first column

--- app/assets/javascripts/views/question_page.coffee
+++ app/assets/javascripts/views/question_page.coffee
@@ -37,7 +37,9 @@

           @div id: 'columns', =>
             @div class: 'column', id: 'column1', =>
-              @h4 "Combined Ranking", class: 'collective list-header', outlet: 'column1Header'
+              @h4 "Combined Ranking", class: 'collective list-header hide', outlet: 'combinedRankingHeader'
+              @h4 "Individual Rankings", class: 'collective list-header hide', outlet: 'allRankingsHeader'
+              @h4 "New Answers", class: 'collective list-header hide', outlet: 'newAnswersHeader'

               @subview 'answerList', new Views.RelationView(
                 attributes: { class: 'collective answer-list' }
@@ -121,6 +123,11 @@
     @personalRanking.setRelation(Models.User.getCurrent().preferencesForQuestion(question))
     @updateVotingInstructions()

+
+    @subscriptions.add @question.rankings().onInsert => @updateAllRankingsHeader()
+    @subscriptions.add @question.rankings().onRemove => @updateAllRankingsHeader()
+    @updateAllRankingsHeader()
+
     @subscriptions.add @question.comments().onInsert => @updateDiscussionHeader()
     @subscriptions.add @question.comments().onRemove => @updateDiscussionHeader()
     @updateDiscussionHeader()
@@ -144,13 +151,17 @@
   show: ->
     $('#all-questions-link').show()
     super
+
+  showColumn1Header: (header) ->
+    @find('#column1 .list-header').hide()
+    header.show()

   showCombinedRanking: ->
     @fetchPromise.onSuccess =>
       @showAnswerList()
       @showPersonalRanking()
       @highlightLeftNavLink(@combinedRankingLink)
-      @column1Header.text("Combined Ranking")
+      @showColumn1Header(@combinedRankingHeader)
       @answerList.setRelation(@question.answers())

   showNewAnswers: ->
@@ -158,7 +169,7 @@
       @showAnswerList()
       @showPersonalRanking()
       @highlightLeftNavLink(@newAnswersLink)
-      @column1Header.text("New Answers")
+      @showColumn1Header(@newAnswersHeader)
       if newAnswers = @question.newAnswers()
         @answerList.setRelation(newAnswers)

@@ -168,7 +179,6 @@
       @highlightLeftNavLink(@individualRankingsLink)

       ranking = @question.rankings().find({userId})
-      @column1Header.text("Individual Rankings")

       @showAllRankings()
       @allRankings.find(".selected").removeClass('selected')
@@ -196,6 +206,7 @@
   showAllRankings: ->
     @answerList.hide()
     @allRankings.show()
+    @showColumn1Header(@allRankingsHeader)
     @allRankings.setRelation(@question.rankings())

   showAnswerList: ->
@@ -208,6 +219,13 @@
     @personalRanking.sortable('enable')
     @personalRanking.setRelation(Models.User.getCurrent().preferencesForQuestion(@question))
     @updateVotingInstructions()
+
+  updateAllRankingsHeader: ->
+    count = @question.rankings().size()
+    if count == 1
+      @allRankingsHeader.text("1 Individual Ranking")
+    else
+      @allRankingsHeader.text("#{count} Individual Rankings")

   updateAnswerPreference: (item) ->
     answerId = item.data('answer-id')


fix datetimepicker not opening

fix datetimepicker not opening

--- app/assets/javascripts/angular/worklogs/worklogs_controller.js.coffee
+++ app/assets/javascripts/angular/worklogs/worklogs_controller.js.coffee
@@ -21,9 +21,14 @@
     startingDay: 1

   $scope.open = ($event, timeframe)->
-    timeframe.opened = true
     $event.preventDefault()
     $event.stopPropagation()
+    timeframe.opened = true
+
+  $scope.openEnded = ($event, timeframe)->
+    $event.preventDefault()
+    $event.stopPropagation()
+    timeframe.openedEnded = true

   $scope.hstep = 1
   $scope.mstep = 10


handle twitter

handle twitter

--- scripts/amazon-ec.coffee
+++ scripts/amazon-ec.coffee
@@ -32,13 +32,12 @@

   search: (query, isKindle, page, callback, nothingCallBack) ->

-    binding = if isKindle then 'kindle' else 'not kindle'
     month = moment().add(-2,'M').format('MM-YYYY')

     this.operationHelper.execute('ItemSearch',{
       'SearchIndex': 'Books',
       'BrowseNode': comicNode,
-      'Power':"title-begins:#{query} and pubdate:after #{month} and binding:#{binding}",
+      'Power':"title-begins:#{query} and pubdate:after #{month} and binding:#{binding = if isKindle then 'kindle' else 'not kindle'}",
       'ResponseGroup':'Large',
       'Sort':'daterank',
       'ItemPage': page
@@ -68,7 +67,7 @@
         }

       if page < totalPages and page < 10
-        setTimeout(this.search, 5000, query,isKindle, page + 1,callback, nothingCallBack)
+        setTimeout((() -> this.search), 5000, query, isKindle, page + 1, callback, nothingCallBack)

     ).catch((err) ->
       console.log("error:", err)
@@ -80,12 +79,12 @@
 newReleaseSearch = (msg,query,isKindle) ->
   # 検索の実行
   amazonSearch.search(query, isKindle, 1,
-    ((item) -> msg.send "#{item.title}が見つかったよー。¥n発売日は #{item.releaseDate}だよー¥n#{item.url}"),
+    ((item) -> msg.send "#{item.title}が見つかったよー。\n発売日は #{item.releaseDate}だよー\n#{item.url}"),
     (() -> msg.send "#{query}は最近リリースされてないよー")
   )

 nexWeekSearch = (msg, query , isKindle) ->
-  nextWeek = moment().add(+1,'w').format('YYYY-MM-DD')
+  nextWeek = moment().add(+1,'w').add(+1,'d').format('YYYY-MM-DD')
   futureTimeSearch(msg,query,isKindle, nextWeek, "来週")

 tomorrowSearch = (msg, query , isKindle) ->
@@ -94,33 +93,36 @@

 futureTimeSearch = (msg, query, isKindle, time, timeWord) ->
   amazonSearch.search(query, isKindle , 1,
-    ((item) -> msg.send "#{item.title}が#{timeWord}発売されるよー¥n#{item.url}" if item.releaseDate is time)
+    ((item) -> msg.send "#{item.title}が#{timeWord}発売されるよー。\n #{item.url}" if item.releaseDate is time),
+    (() -> {})
   )

 module.exports = (robot) ->

   # クーロンで来週発売と明日発売の通知を設定
-#  send = (name,message) ->
-#    users = robot.brain.users()
-#
-#    i = 1
-#    while user = users[i]
-#      i++
-#      console.log(user)
-#      response = new robot.Response(robot, {user : {id : -1, name : user.name}, text : "none", done : false}, [])
-#      stores =  robot.brain.get(user.name)
-#
-#      for store in stores
-#        nexWeekSearch(response, store.title, store.kindle)
-#        tomorrowSearch(response, store.title, store.kindle)
-#
-#  # 起動時にクーロン設定
-#  # *(sec) *(min) *(hour) *(day) *(month) *(day of the week)
-#  new cronJob('*/30 * * * * *', () ->
-#    currentTime = new Date
-#    send ""
-#  ).start()
+  send = (name,message) ->
+    users = robot.brain.users()
+
+    console.log users
+
+    i = 1
+    while user = users[i]
+      i++
+      console.log(user.name)
+      response = new robot.Response(robot, {user : {id : -1, name : user.name, user:user.name}, text : "none", done : false}, [])
+      stores =  robot.brain.get(user.name)
+
+      for store in stores
+        nexWeekSearch(response, store.title, store.kindle)
+        tomorrowSearch(response, store.title, store.kindle)
+
+  # 起動時にクーロン設定
+  # *(sec) *(min) *(hour) *(day) *(month) *(day of the week)
+  new cronJob('*/30 * * * * *', () ->
+    currentTime = new Date
+    send ""
+  ).start()

   robot.respond /kindle最新刊(\S*) (\S+)$/i, (msg) ->
     newReleaseSearch msg, msg.match[2], true
@@ -129,9 +131,10 @@
     newReleaseSearch msg, msg.match[2], false

   robot.respond /kindle登録(\S*) (\S+)$/i, (msg) ->
+
     message = msg.match[2]
     # twitter とshell でプロパティが変わるため
-    user = if msg.hasOwnProperty('user') then msg.user.user else msg.envelope.user.name
+    user = if msg.envelope.user.hasOwnProperty('user') then msg.envelope.user.user else msg.envelope.user.name

     stores = robot.brain.get(user) ? []

@@ -139,7 +142,13 @@
     if !stores.filter((item) -> item.title is message).length
       stores.push({title: message, kindle: true})

+    # user のID は連番で登録する
+    i = 1
+    users = robot.brain.users()
+    while ( users[i] )
+      i++
     # 保存
+    robot.brain.userForId(i, name: user) if !robot.brain.userForName(user) # user がいなければ保存
     robot.brain.set user, stores
     robot.brain.save()

@@ -147,7 +156,7 @@

   robot.respond /登録内容(\S*)/i, (msg) ->
     # twitter とshell でプロパティが変わるため
-    user = if msg.hasOwnProperty('user') then msg.user.user else msg.envelope.user.name
+    user = if msg.envelope.user.hasOwnProperty('user') then msg.envelope.user.user else msg.envelope.user.name
     # 呼び出し
-    msg.send robot.brain.get(user)
-
+    stores = robot.brain.get(user)
+    msg.send "登録内容は" + stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title


test cleanup

test cleanup

--- test/coffeescripts/Settings_spec.js.coffee
+++ test/coffeescripts/Settings_spec.js.coffee
@@ -1,6 +1,6 @@
 #= require roofpig/Settings

-describe "Settings#constructor", ->
+describe "Settings", ->
   it "has defaults", ->
     empty_settings = new Settings({ data: -> null })
     expect(empty_settings.alg).to.equal("")
@@ -10,19 +10,21 @@

     expect(empty_settings.colors).to.exist

-describe "Settings#flag", ->
-  it "reads a move string", ->
-    settings = new Settings({ data: (name) -> {flags: "fast shiny"}[name] })
-    expect(settings.flags).to.equal("fast shiny")
-    expect(settings.flag("fast")).to.equal(true)
-    expect(settings.flag("slow")).to.equal(false)
+  describe "flags", ->
+    it "reads a move string", ->
+      settings = new Settings({ data: (name) -> {flags: "fast shiny"}[name] })
+      expect(settings.flags).to.equal("fast shiny")
+      expect(settings.flag("fast")).to.equal(true)
+      expect(settings.flag("slow")).to.equal(false)

   describe "prefs", ->
+    prefs = { hover: "3.3"}
+
     it "reads prefs and settings", ->
-      settings = new Settings({ data: (name) -> {flags: "fast shiny"}[name] }, { hover: "3.3"})
+      settings = new Settings({ data: (name) -> {flags: "fast shiny"}[name] }, prefs)
       expect(settings.flags).to.equal("fast shiny")
       expect(settings.hover).to.equal("3.3")

     it "settings override prefs", ->
-      settings = new Settings({ data: (name) -> {hover: "2.5"}[name] }, { hover: "3.3"})
-      expect(settings.hover).to.equal("2.5")
+      settings = new Settings({ data: (name) -> {hover: "2.5"}[name] }, prefs)
+      expect(settings.hover).to.equal("2.5")


Fix XSS error in OEmbed

Fix XSS error in OEmbed

--- packages/rocketchat-oembed/client/oembedUrlWidget.coffee
+++ packages/rocketchat-oembed/client/oembedUrlWidget.coffee
@@ -18,11 +18,11 @@
 Template.oembedUrlWidget.helpers
 	description: ->
 		description = getDescription this
-		return new Handlebars.SafeString description if _.isString description
+		return Blaze._escape(description) if _.isString description

 	title: ->
 		title = getTitle this
-		return new Handlebars.SafeString title if _.isString title
+		return Blaze._escape(title) if _.isString title

 	target: ->
 		if not this.parsedUrl?.host || !document?.location?.host || this.parsedUrl.host isnt document.location.host


Add temporary interval to print out buffer sizes

--- src/streammachine/stream.coffee
+++ src/streammachine/stream.coffee
@@ -87,12 +87,24 @@
                         dur = ( now.getTime() / 1000 - l.minuteTime.getTime() / 1000 )
                         @log.minute "", path:l.obj.reqPath, time:now, ua:l.obj.reqUA, duration:dur
                         l.minuteTime = now
-
+
             , 60*1000

         else if @mlog_timer && !opts.log_minutes
             clearInterval @mlog_timer
             @mlog_timer = null
+
+        # FIXME -- Temporarily poll buffer size for listeners every minute
+        @buf_timer = setInterval =>
+            all_buf = 0
+            for id,l of @_lmeta
+                conn = l.obj.req?.socket
+                all_buf += conn?.bufferSize || 0
+                @log.debug "Buffer size", bufferSize:conn?.bufferSize || "UNKNOWN"
+
+            @log.debug "All buffers: #{all_buf}"
+        , 60*1000
+

     #----------


Add subject modelings virtual property.

Add subject modelings virtual property.

--- src/subject/subject.data.coffee
+++ src/subject/subject.data.coffee
@@ -55,24 +55,52 @@
 ###
 ModelingResults =
   ###*
+   * Collects the modeling results into a [_modelings_] array,
+   * where _modelings_ is a {{source, protocol, results}},
+   * the ``source`` value is a {source type, source protocol}
+   * object, ``protocol`` is the modeling protocol, and
+   * ``results`` is an array of modeling results in session
+   * number order.
    * @method collect
    * @param subject {Object} the parent subject
-   * @return {Object[]} the modeling [{source, results}] array,
-   *   where each source value is a {type: id} object and the
-   *   results are the modeling results in session number
-   *   order.
+   * @return {Object} the
+   *   {_sourceType_: {_sourceProtocol_: {_modelingProtocol_: _results_}}}
+   *   associative object,
+   *   where the results are an array in session number order
   ###
   collect: (subject) ->
-    # Make the {protocol id: {source type: {source id: results}}}
-    # object.
-    assoc = subject.sessions.reduce(associate, {})
-    # Flatten into a [[{protocol, source, results}, ...], ...]
-    # array of arrays partitioned by protocol id.
-    modelingArrays = (
-      flattenBySource(pclId, srcAssoc) for pclId, srcAssoc of assoc
-    )
-    # Flatten into a [{protocol, source, results}, ...] array.
-    _.flatten(modelingArrays)
+    # The modeling result intensity value property path.
+    intensityPath = 'image.metadata.average_intensity'
+    # The grouped modelings.
+    grouped = {}
+    # Collect each session modeling result.
+    for session, i in subject.sessions
+      for modeling in session.modelings
+        # Convert the modeling source from {_type_: _protocol_}
+        # to [_type_, _protocol_].
+        source = _.toPairs(modeling.source)[0]
+        # The property path to the modeling result is the
+        # source followed by the modeling protocol followed
+        # by the session index.
+        path = _.flatten([source, modeling.protocol, i])
+        result = _.mapValues(modeling.result, intensityPath)
+        _.set(grouped, path, result)
+
+    # Regroup by the modeling source and protocol.
+    regrouped = []
+    for sourceType, rest of grouped
+      for sourceProtocol, rest2 of rest
+        for modelingProtocol, results of rest2
+          item =
+            source:
+              type: sourceType
+              protocol: sourceProtocol
+            protocol: modelingProtocol
+            results: results
+          regrouped.push(item)
+
+    # Return the regrouped array.
+    regrouped

 ###*
@@ -172,10 +200,10 @@
           if surgeries.length is 1 then surgeries[0] else null

       ###*
-       * The {{#crossLink "Modeling"}}{{/crossLink}} results array.
+       * The {{#crossLink "ModelingResults"}}{{/crossLink}}
+       * array.
        *
-       * @property modelings
-       * @return the modelings array
+       * @property modelings {Object[]}
       ###
       modelings:
         get: ->


add compound interface "oneClass" to jQuery hook

* usage: { (observer|subject)Interface: 'oneClass.some-class-name' }
* allows getting status of individual class on element
* allows toggling individual class on element

--- src/synapse/hooks/jquery.coffee
+++ src/synapse/hooks/jquery.coffee
@@ -201,6 +201,15 @@
             name: 'data'
             get: (key) -> @data(key)
             set: (key, value) -> @data(key, value)
+
+        # ### _oneClass.FOO_
+        # Reads or adds/removes a single class on the target element using
+        # jQuery hasClass for get and toggleClass for set
+        # (based on the truthiness of the set value)
+        interfaces.register
+            name: 'oneClass'
+            get: (key) -> @hasClass(key)
+            set: (key, value) -> @toggleClass(key, value)

     # Default DOM events. When a DOM element is declared the subject of a


Corrected broken reference to the socket.

--- src/pubsub.coffee
+++ src/pubsub.coffee
@@ -405,7 +405,7 @@
               logger.error "WebSocket error:", error

           # Received a record
-          sock.on 'message', (rec) =>
+          @sock.on 'message', (rec) =>
             logger.verbose "Received a record:", rec
             @recordCount += 1


signup button fix https://app.asana.com/0/3716548652471/7294481826334

--- workers/social/lib/social/render/homeintro.coffee
+++ workers/social/lib/social/render/homeintro.coffee
@@ -16,7 +16,12 @@
         <form>

<button type="button" class="kdbutton register gh-gray with-icon" id="kd-134">Sign up with GitHub</button>

or

-
<button type="button" class="kdbutton register orange" id="kd-135">Sign up with email</button>

+

+            <button onclick="location.href='#!/Register'" type="button" class="kdbutton register orange" id="kd-135">
+
+              Sign up with email
+            </button>
+

or check what Koding is:


Support for JSON reponse in browser impl added

--- impls/browser/index.coffee
+++ impls/browser/index.coffee
@@ -46,8 +46,11 @@

 	xhr.open opts.method, opts.url, true
 	xhr.setRequestHeader 'X-Expected-Type', Model.OBJECT
+	xhr.responseType = 'json'
 	xhr.onload = ->
-		response = utils.tryFunc JSON.parse, null, [xhr.response], xhr.response
+		response = xhr.response
+		if typeof response is 'string'
+			response = utils.tryFunc JSON.parse, null, [response], response
 		callback xhr.status, response

 	xhr.send()


Added debugOnly and betaOnly to F4 grammar

--- grammars/papyrus-fallout4.cson
+++ grammars/papyrus-fallout4.cson
@@ -30,7 +30,7 @@
 			}
 			{
 				name: "keyword.language.script-flag.papyrus.fallout4"
-				match: "(?i)(conditional|default|hidden|native|const)"
+				match: "(?i)(conditional|default|hidden|native|const|debugonly|betaonly)"
 			}
 		]
 		begin: "(?i)^\\s*(scriptname)\\s+([:\\w]+)(\\s+(extends)\\s+(\\S+))?"
@@ -151,7 +151,7 @@
 	}
 	{
 		name: "storage.modifier.papyrus.fallout4"
-		match: "(?i)\\b(global|hidden|native|const|conditional)\\b"
+		match: "(?i)\\b(global|hidden|native|const|conditional|debugonly|betaonly)\\b"
 	}
 	{
 		name: "variable.language.papyrus.fallout4"


rebase modifications

rebase modifications

--- BackofficeBundle/Resources/public/coffee/areaView.coffee
+++ BackofficeBundle/Resources/public/coffee/areaView.coffee
@@ -76,12 +76,7 @@
     $("ul.ui-model-blocks", @el).remove() if $("ul.ui-model-blocks", @el).children().length == 0
     if $("ul.ui-model-areas", @el).children().length == 0
       $("ul.ui-model-areas", @el).remove()
-      $("ul.ui-model-blocks", @el).sortable(
-        connectWith: "ul.ui-model-blocks",
-        start: (event, ui)->
-            ui.placeholder.css('height', ui.helper.css('height'))
-            ui.placeholder.css('width', ui.helper.css('width'))
-      ).disableSelection()
+      $("ul.ui-model-blocks", @el).sortable(connectWith: "ul.ui-model-blocks").disableSelection()
     this

   addAreaToView: (area, areaHeight) ->


Fix command line passing.

Fix command line passing.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -48,7 +48,7 @@
   sphere = new SphereClient options
   options.sphere_client = sphere
   mapping = new Mapping options
-  mapping = argv.standardShippingMethod
+  mapping.standardShippingMethod = argv.standardShippingMethod

   sphere.orders.last("#{argv.fetchHours}h").perPage(0).fetch().then (result) ->
     mapping.processOrders(result.body.results)


Initialisation intelligente de la position et possibilité de redéfinir la position avec centerX et centerY

--- js/game/core/Entity.coffee
+++ js/game/core/Entity.coffee
@@ -14,16 +14,18 @@
             @ctx = @parent.ctx

             # Position
-            @x = @y = 0
+            @x = 0 if not @x?
+            @y = 0 if not @y?

             # Dimensions
-            @width = @height = 0
+            @width = 0 if not @width?
+            @height = 0 if not @height?

             # Centre absolu de l'objet, en fonction de sa taille (read only)
             @get 'centerX', => @x + @width / 2
             @get 'centerY', => @y + @height / 2
-            @set 'centerX', =>
-            @set 'centerY', =>
+            @set 'centerX', (val) => @x = val - @width / 2
+            @set 'centerY', (val) => @y = val - @height / 2

             # Angle (en radians)
             @angle = 0


Spacing

Spacing

--- src/trix/text_controller.coffee
+++ src/trix/text_controller.coffee
@@ -138,13 +138,16 @@
   lockSelection: ->
     if @currentAttributes["href"]
       @expandSelectedRangeAroundCommonAttribute("href")
-    @textView.lockSelection()
+
     if selectedRange = @getSelectedRange()
       @text.addAttributeAtRange("selected", true, selectedRange)
+
+    @textView.lockSelection()

   unlockSelection: ->
     if selectedRange = @getSelectedRange()
       @text.removeAttributeAtRange("selected", selectedRange)
+
     @textView.unlockSelection()

   rangeIsCollapsed = ([startPosition, endPosition]) ->


Login: change / route to /Activity

Login: change / route to /Activity

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -390,7 +390,7 @@
       firstRoute = KD.getSingleton("router").visitedRoutes.first

       if firstRoute and /^\/(?:Reset|Register|Confirm)\//.test firstRoute
-        firstRoute = "/"
+        firstRoute = "/Activity"

       KD.getSingleton('appManager').quitAll()
       KD.getSingleton('router').handleRoute firstRoute or '/Activity', {replaceState: yes, entryPoint}


minor ux fix for vm deletion

minor ux fix for vm deletion

--- client/landing/app/MainApp/filetree/modals/vmdangermodalview.coffee
+++ client/landing/app/MainApp/filetree/modals/vmdangermodalview.coffee
@@ -52,4 +52,5 @@
       input.setValidationResult 'slugCheck', null
       @modalTabs.forms.dangerForm.buttons.confirmButton.enable()
     else
+      @modalTabs.forms.dangerForm.buttons.confirmButton.disable()
       input.setValidationResult 'slugCheck', 'Sorry, entered value does not match vm name!', showError


teams: var renaming for readability

teams: var renaming for readability

--- client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
@@ -4,7 +4,8 @@

 module.exports = class TeamUsernameTab extends KDTabPaneView

-  callback = (formData) ->
+  kallback = (formData) ->
+
     { storeNewTeamData, createTeam, joinTeam, getTeamData } = KD.utils
     { join } = getTeamData().signup

@@ -45,7 +46,7 @@
         tagName : 'h5'
         partial : 'please enter your Koding password'

-      wrapper.addSubView @form = new TeamLoginAndCreateTabForm { callback }
+      wrapper.addSubView @form = new TeamLoginAndCreateTabForm { callback : kallback }

     else

@@ -84,7 +85,7 @@
       return new KDNotificationView title : "Sorry, your group domain and your username can not be the same!"

     KD.utils.usernameCheck username,
-      success : -> callback formData
+      success : -> kallback formData
       error   : ({responseJSON}) =>

         {forbidden, kodingUser} = responseJSON


refactor(macUpload): Updated directive to be more Angular

--- src/directives/file_upload.coffee
+++ src/directives/file_upload.coffee
@@ -78,16 +78,16 @@
         applyCallback "progress", $event, $data

     if opts.dropZone?
-      $(document).on "drop dragover", (event) -> event.preventDefault()
+      angular.element(document).bind "drop dragover", (event) -> event.preventDefault()

       # Add and remove droppable class
       dragoverTimeout = null
       dropZone        = element.parents opts.dropZone

-      $(document).bind "dragover", (event) ->
+      angular.element(document).bind "dragover", (event) ->
         $timeout.cancel dragoverTimeout if dragoverTimeout?

-        node   = $(event.target).parents opts.dropZone
+        node   = angular.element(event.target).parents opts.dropZone
         method = if node.length then "addClass" else "removeClass"
         dropZone[method] "droppable"

@@ -96,7 +96,7 @@
           dropZone.removeClass "droppable"
         , 250, false

-    options.dropZone  = dropZone or $()
+    options.dropZone  = dropZone or angular.element()
     options.pasteZone = null

     if opts.options


Removed padding from day.

Removed padding from day.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -74,13 +74,13 @@
       if date && format != 'none'
         date = new Date(date)
         if format == 'long'
-          $(@).text(date.format("%B %d, %Y at %l:%M %p"))
+          $(@).text(date.format("%B %e, %Y at %l:%M %p"))
         else if format == 'time'
           $(@).text(date.format("%l:%M %p"))
         else if format == 'day'
-          $(@).text(date.format("%d %b"))
+          $(@).text(date.format("%e %b"))
         else if format == 'day_year'
-          $(@).text(date.format("%d %b %Y"))
+          $(@).text(date.format("%e %b %Y"))

   applyUserTitles: ->
     $('[data-behavior~=user_title]').each ->


Small updates to screen

Small updates to screen

--- src/screen.coffee
+++ src/screen.coffee
@@ -1,10 +1,14 @@
-define ->
+define ['./widget'] ->

   class Screen
     constructor: (options) ->
       @name = options.name || 'home'
-      @route = '/' + @name
-
-
+      @route = "/#{@name}"
+      @widgets = {}
+      @template = ""
+
     addWidget: (widget) ->
-
+
+    render: ->
+
+  Screen.$container = $('.screen-container')


use o to insert before

use o to insert before

--- app/assets/javascripts/lists.js.coffee
+++ app/assets/javascripts/lists.js.coffee
@@ -422,6 +422,8 @@
       App.mainList.view.newItem "indent"
     "shift+return" : ->
       App.mainList.view.newItem "previous"
+    "o"            : ->
+      App.mainList.view.newItem "previous"
     "backspace"    : ->
       App.mainList.view.deleteItem()
     "del"          : ->


fixed the sentiment analysis

fixed the sentiment analysis

--- scripts/sentiment.coffee
+++ scripts/sentiment.coffee
@@ -137,10 +137,10 @@
     output

   onlyNegative = (list) ->
-    _.filter(list || [], (x) -> x.score_average > 0)
+    _.filter(list || [], (x) -> (x.score_average < 0))

   onlyPositive = (list) ->
-    _.filter(list || [], (x) -> x.score_average < 0)
+    _.filter(list || [], (x) -> (x.score_average > 0))

   robot.hear /.*/, (msg)->
     # match everything and log it


Fetch printes suggestion list from microservice

--- app/assets/javascripts/print.js.coffee
+++ app/assets/javascripts/print.js.coffee
@@ -9,10 +9,28 @@
     $('#print_printer').val printer_name

   if $('#print_printer').length
-    $('#print_printer').chosen
-      no_results_text: 'No matches'
-      search_contains: true
-      width: '91%'
+
+    $.getJSON($('.printer-list').data('url'))
+    .success (printers) ->
+      option_tags = printers.map (printer) ->
+        $('<option/>')
+        .val(printer.name)
+        .html(printer.name)
+        .data('media', printer.media)
+        .data('location', printer.location)
+        .data('duplex', printer.duplex)
+
+      printer_suggestions = printers.slice(0, 10).map (printer) ->
+        $('<li/>').html($('<a/>').addClass('set-printer').attr('href', 'javascript:;').html(printer.name))
+
+      $('.printer-list').html(printer_suggestions)
+
+      $('#print_printer')
+      .html(option_tags)
+      .chosen
+        no_results_text: 'No matches'
+        search_contains: true
+        width: '91%'

     $('.get-pq-button').on 'click', ->
       $that = $(this)


Add findTask method to store.

Add findTask method to store.

--- app/assets/javascripts/store.js.coffee
+++ app/assets/javascripts/store.js.coffee
@@ -14,3 +14,10 @@
       if oldRecord = @getById(oldType, data.id)
         @dematerializeRecord(oldRecord)
     @_super @modelFor(modelType), data, _partial
+
+  # find any task in the store even when subclassed
+  findTask: (id) ->
+    matchingTask = _(@typeMaps).detect (tm) ->
+      tm.type.toString().match(/Task$/) and tm.idToRecord[id]
+    if matchingTask
+      matchingTask.idToRecord[id]