Use the optimized projects index for recent projects on the homepage

--- app/pages/home.cjsx
+++ app/pages/home.cjsx
@@ -109,7 +109,7 @@
             else

                 <Translate component="h5" content="home.recentProjects.altTitle" />
-                <PromiseRenderer promise={apiClient.type('projects').get(launch_approved: true, page_size: 4)}>{(projects) =>
+                <PromiseRenderer promise={apiClient.type('projects').get(launch_approved: true, page_size: 4, cards: true)}>{(projects) =>

                     {projects.map (project) ->


Fix moment.js loading errors

Fix moment.js loading errors

--- src/status.coffee
+++ src/status.coffee
@@ -560,7 +560,7 @@
     hour = @outOfOffice[hourOptionKey]
     advanceDay = hourOptionKey == "officeCloseHour" && @officeHoursOverlapDays()

-    if window.moment
+    if @outOfOffice["timezone"]? and momentTzLoaded()
       day = moment().tz(@outOfOffice["timezone"]).startOf("day").add hour, "h"
       day.add(1, "d") if advanceDay
       day.valueOf()
@@ -573,7 +573,7 @@
     @outOfOffice["officeOpenHour"] < @outOfOffice["officeCloseHour"]

   officeTimestamp: ->
-    if window.moment
+    if @outOfOffice["timezone"]? and momentTzLoaded()
       moment().tz(@outOfOffice["timezone"]).valueOf()
     else
       utcDate().getTime()
@@ -686,6 +686,9 @@
     now = new Date()
     new Date now.getTime() + now.getTimezoneOffset() * 60000

+  momentTzLoaded = ->
+    window.moment and window.moment().tz? and window.moment().tz("Etc/UTC")?
+
   deepMerge = (target, source) ->
     destination = {}


Read credentials from config file

Read credentials from config file

--- src/spec/product-type-importer.spec.coffee
+++ src/spec/product-type-importer.spec.coffee
@@ -5,8 +5,6 @@
 errMissingCredentials = 'Missing configuration in env variables'

 argv =
-  clientId: process.env.SPHERE_CLIENT_ID
-  clientSecret: process.env.SPHERE_CLIENT_SECRET
   projectKey: process.env.SPHERE_PROJECT_KEY
   logSilent: true


minor tweaks to the publish output

minor tweaks to the publish output

--- library/exposure/publish.coffee
+++ library/exposure/publish.coffee
@@ -121,10 +121,10 @@
     GET: (request, response, resource, domain, session) ->
         @push response, _.map request.records, (record) ->
             constructor = record.service.constructor
-            healthcare: record.healthcare or {}
-            qualified: record.service.qualified()
-            location: record.service.location()
-            identify: constructor.identify()
+            healthcare: record.healthcare or Object()
+            qualified: "#{record.service.qualified()}"
+            location: "#{record.service.location()}"
+            identify: constructor.identify().toString()
             patterns: _.map constructor.resources, "source"
             methods: _.map record.methods, (document, method) ->
                 relevant: document.relevant()


added tests for File

added tests for File

--- test/file-test.coffee
+++ test/file-test.coffee
@@ -1,4 +1,63 @@
+path = require 'path'
 file = require '../lib/file'

-describe 'JSFile', ->
-	before ->
+describe 'file', ->
+	beforeEach ->
+		process.chdir(path.resolve(__dirname, 'fixtures/file'))
+
+	describe 'File Class', ->
+		describe 'an instance', ->
+			describe 'name', ->
+				it 'should match it`s location relative to the base source directory', ->
+					f = new file.File('js', path.resolve('src/package/Class.coffee'), path.resolve('src'))
+					f.name.should.equal('package/Class')
+
+	describe 'JSFile Class', ->
+		describe 'an instance', ->
+			describe 'module name', ->
+				it 'should be lowercase', ->
+					f = new file.JSFile('js', path.resolve('src/package/Class.coffee'), path.resolve('src'))
+					f.module.should.equal('package/class')
+				it 'should convert camel case to "_"', ->
+					f = new file.JSFile('js', path.resolve('src/package/ClassCamelCase.coffee'), path.resolve('src'))
+					f.module.should.equal('package/class_camel_case')
+			describe 'with 2 dependencies', ->
+				it 'should result in a dependency count of 2', ->
+					f = new file.JSFile('js', path.resolve('src/main.coffee'), path.resolve('src'))
+					f.dependencies.length.should.equal(2)
+			describe 'with 1 commented out dependency', ->
+				it 'should result in a dependency count of 0', ->
+					f = new file.JSFile('js', path.resolve('src/package/Class.coffee'), path.resolve('src'))
+					f.dependencies.length.should.equal(0)
+			describe 'wrap()', ->
+				beforeEach ->
+					@f = new file.JSFile('js', path.resolve('src/main.coffee'), path.resolve('src'))
+					@c =
+						'''
+						var Class = require('./package/class');
+						var instance = new Class();
+						'''
+					@cw =
+						'''
+						require.module('main', function(module, exports, require) {
+						var Class = require('./package/class');
+						var instance = new Class();
+						});
+
+						'''
+					@cwm =
+						'''
+						require.module('main', function(module, exports, require) {
+						var Class = require('./package/class');
+						var instance = new Class();
+						});
+						require('main');
+						'''
+				it 'should wrap file contents in a module wrapper', ->
+					@f.wrap(@c).should.equal(@cw)
+				it 'should not wrap file contents in a module wrapper if already wrapped', ->
+					@f.wrap(@cw).should.equal(@cw)
+				it 'should include a bootstrap call if file is main entry point', ->
+					@f.main = true
+					@f.wrap(@c).should.equal(@cwm)
+


Created getMemberCards method to be called when requesting cards a user is a member of

--- src/trello.coffee
+++ src/trello.coffee
@@ -1,173 +1,186 @@
-# Description:
-#   Manage your Trello Board from Hubot!
-#
-# Dependencies:
-#   "node-trello": "latest"
-#
-# Configuration:
-#   HUBOT_TRELLO_KEY - Trello application key
-#   HUBOT_TRELLO_TOKEN - Trello API token
-#   HUBOT_TRELLO_BOARD - The ID of the Trello board you will be working with
-#
-# Commands:
-#   hubot trello new "<list>" <name> - Create a new Trello card in the list
-#   hubot trello list "<list>" - Show cards on list
-#   hubot trello move <shortLink> "<list>" - Move a card to a different list
-#   hubot trello add member <shortlink> "<first name of member>"
-#   hubot trello add comment <shortlink> "<comment>"
-#   hubot trello description <shortlink> "<discription>"
-#
-#
-# Author:
-#   jared barboza <jared.m.barboza@gmail.com>
-
-board = {}
-lists = {}
-members = {}
-
-Trello = require 'node-trello'
-
-trello = new Trello process.env.HUBOT_TRELLO_KEY, process.env.HUBOT_TRELLO_TOKEN
-
-# verify that all the environment vars are available
-ensureConfig = (out) ->
-  out "Error: Trello app key is not specified" if not process.env.HUBOT_TRELLO_KEY
-  out "Error: Trello token is not specified" if not process.env.HUBOT_TRELLO_TOKEN
-  out "Error: Trello board ID is not specified" if not process.env.HUBOT_TRELLO_BOARD
-  return false unless (process.env.HUBOT_TRELLO_KEY and process.env.HUBOT_TRELLO_TOKEN and process.env.HUBOT_TRELLO_BOARD)
-  true
-
-##############################
-# API Methods
-##############################
-
-createCard = (msg, list_name, cardName) ->
-  msg.reply "Sure thing boss. I'll create that card for you."
-  ensureConfig msg.send
-  id = lists[list_name.toLowerCase()].id
-  trello.post "/1/cards", {name: cardName, idList: id}, (err, data) ->
-    msg.reply "There was an error creating the card" if err
-    msg.reply "OK, I created that card for you. You can see it here: #{data.url}" unless err
-
-showCards = (msg, list_name) ->
-  msg.reply "Looking up the cards for #{list_name}, one sec."
-  ensureConfig msg.send
-  id = lists[list_name.toLowerCase()].id
-  msg.send "I couldn't find a list named: #{list_name}." unless id
-  if id
-    trello.get "/1/lists/#{id}", {cards: "open"}, (err, data) ->
-      msg.reply "There was an error showing the list." if err
-      msg.reply "Here are all the cards in #{data.name}:" unless err and data.cards.length == 0
-      msg.send "* [#{card.shortLink}] #{card.name} - #{card.shortUrl}" for card in data.cards unless err and data.cards.length == 0
-      msg.reply "No cards are currently in the #{data.name} list." if data.cards.length == 0 and !err
-
-getMemberId = (msg, member_name) ->
-  if (members[member_name.toLowerCase()]?)
-     id = members[member_name.toLowerCase()].id
-  msg.send "Unable to find person named: #{member_name}" unless id?
-  if id?
-     msg.send " * " + id
-
-addMember = (msg, card_id, member_name) ->
-  if (members[member_name.toLowerCase()]?)
-     id = members[member_name.toLowerCase()].id
-  msg.send "Unable to find person named: #{member_name}" unless id?
-  if id?
-     trello.put "/1/cards/#{card_id}/idMembers", {value: id}, (err, data) ->
-       msg.reply "Sorry captain, I couldn't add that member" if err
-       msg.reply "Success! #{member_name} was added" unless err
-
-moveCard = (msg, card_id, list_name) ->
-  ensureConfig msg.send
-  id = lists[list_name.toLowerCase()].id
-  msg.reply "I couldn't find a list named: #{list_name}." unless id
-  if id
-    trello.put "/1/cards/#{card_id}/idList", {value: id}, (err, data) ->
-      msg.reply "Sorry boss, I couldn't move that card after all." if err
-      msg.reply "Yep, ok, I moved that card to #{list_name}." unless err
-
-addDescription = (msg, card_id, desc) ->
-  ensureConfig msg.send
-  trello.put "/1/cards/#{card_id}/desc", {value: desc}, (err, data) ->
-      msg.reply "Sorry boss, I couldn't update that card after all." if err
-      msg.reply "Yep, ok, I updated that card for you." unless err
-
-addComment = (msg,card_id, comment, usr) ->
-  ensureConfig msg.send
-  trello.post "/1/cards/#{card_id}/actions/comments",{text: usr+" commented via Slack:"+comment}, (err, data) ->
-      msg.reply "Sorry, I was unable to do that." if err
-      msg.reply "Gladly! That comment has been added" unless err
-
-module.exports = (robot) ->
-  # fetch our board data when the script is loaded
-  ensureConfig console.log
-  trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}", (err, data) ->
-    board = data
-    trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}/lists", (err, data) ->
-      for list in data
-        lists[list.name.toLowerCase()] = list
-    trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}/members", (err, data) ->
-      for member in data
-        members[member.fullName.toLowerCase().split " ", 1] = member
-
-
-<<<<<<< HEAD
-  robot.respond /trello new ["“'‘]([^]+)["”'’](.*)/i, (msg) ->
-=======
-  robot.respond /trello new ["“'‘](.+)["”'’]\s(.*)/i, (msg) ->
->>>>>>> refs/remotes/hubot-scripts/master
-    ensureConfig msg.send
-    card_name = msg.match[2]
-    list_name = msg.match[1]
-
-    if card_name.length == 0
-      msg.reply "You must give the card a name"
-      return
-
-    if list_name.length == 0
-      msg.reply "You must give a list name"
-      return
-    return unless ensureConfig()
-
-    createCard msg, list_name, card_name
-
-  robot.respond /trello list ["“'‘](.+)["”'’]/i, (msg) ->
-    showCards msg, msg.match[1]
-
-  robot.respond /trello move (\w+) ["“'‘](.+)["”'’]/i, (msg) ->
-    moveCard msg, msg.match[1], msg.match[2]
-
-  robot.respond /trello comment (\w+) ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
-    addComment msg, msg.match[1], msg.match[2], msg.message.user.name
-
-  robot.respond /trello description (\w+) ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
-    addDescription msg, msg.match[1], msg.match[2]
-
-  robot.respond /trello list lists/i, (msg) ->
-    msg.reply "Here are all the lists on your board."
-    Object.keys(lists).forEach (key) ->
-      msg.send " * " + key
-
-  robot.respond /trello list members/i, (msg) ->
-    msg.reply "Here are all the members of the board"
-    Object.keys(members).forEach (member)->
-      msg.send " * " + member
-
-  robot.respond /trello list member id (\w+)/i, (msg) ->
-    msg.reply "The member id for "+ msg.match[1]+" is: "
-    getMemberId msg, msg.match[1]
-
-  robot.respond /trello add member (\w+) (\w+)/i, (msg) ->
-    addMember msg, msg.match[1], msg.match[2]
-
-  robot.respond /trello help/i, (msg) ->
-    msg.reply "Here are all the commands for me."
-    msg.send " *  trello new \"<ListName>\" <TaskName>"
-    msg.send " *  trello list \"<ListName>\""
-    msg.send " *  shows * [<card.shortLink>] <card.name> - <card.shortUrl>"
-    msg.send " *  trello move <card.shortlink> \"<ListName>\""
-    msg.send " *  trello list lists"
-    msg.send " *  trello list member id <name>"
-    msg.send " *  trello add member <card.shortLink> <Name>"
-    msg.send " *  trello comment <card.shortLink> <Comment>"
-    msg.send " *  trello description <card.shortLink> <Description>"
+# Description:
+#   Manage your Trello Board from Hubot!
+#
+# Dependencies:
+#   "node-trello": "latest"
+#
+# Configuration:
+#   HUBOT_TRELLO_KEY - Trello application key
+#   HUBOT_TRELLO_TOKEN - Trello API token
+#   HUBOT_TRELLO_BOARD - The ID of the Trello board you will be working with
+#
+# Commands:
+#   hubot trello new "<list>" <name> - Create a new Trello card in the list
+#   hubot trello list "<list>" - Show cards on list
+#   hubot trello move <shortLink> "<list>" - Move a card to a different list
+#   hubot trello add member <shortlink> "<first name of member>"
+#   hubot trello add comment <shortlink> "<comment>"
+#   hubot trello description <shortlink> "<discription>"
+#
+#
+# Author:
+#   jared barboza <jared.m.barboza@gmail.com>
+
+board = {}
+lists = {}
+members = {}
+
+Trello = require 'node-trello'
+
+trello = new Trello process.env.HUBOT_TRELLO_KEY, process.env.HUBOT_TRELLO_TOKEN
+
+# verify that all the environment vars are available
+ensureConfig = (out) ->
+  out "Error: Trello app key is not specified" if not process.env.HUBOT_TRELLO_KEY
+  out "Error: Trello token is not specified" if not process.env.HUBOT_TRELLO_TOKEN
+  out "Error: Trello board ID is not specified" if not process.env.HUBOT_TRELLO_BOARD
+  return false unless (process.env.HUBOT_TRELLO_KEY and process.env.HUBOT_TRELLO_TOKEN and process.env.HUBOT_TRELLO_BOARD)
+  true
+
+##############################
+# API Methods
+##############################
+
+createCard = (msg, list_name, cardName) ->
+  msg.reply "Sure thing boss. I'll create that card for you."
+  ensureConfig msg.send
+  id = lists[list_name.toLowerCase()].id
+  trello.post "/1/cards", {name: cardName, idList: id}, (err, data) ->
+    msg.reply "There was an error creating the card" if err
+    msg.reply "OK, I created that card for you. You can see it here: #{data.url}" unless err
+
+showCards = (msg, list_name) ->
+  msg.reply "Looking up the cards for #{list_name}, one sec."
+  ensureConfig msg.send
+  id = lists[list_name.toLowerCase()].id
+  msg.send "I couldn't find a list named: #{list_name}." unless id
+  if id
+    trello.get "/1/lists/#{id}", {cards: "open"}, (err, data) ->
+      msg.reply "There was an error showing the list." if err
+      msg.reply "Here are all the cards in #{data.name}:" unless err and data.cards.length == 0
+      msg.send "* [#{card.shortLink}] #{card.name} - #{card.shortUrl}" for card in data.cards unless err and data.cards.length == 0
+      msg.reply "No cards are currently in the #{data.name} list." if data.cards.length == 0 and !err
+
+getMemberId = (msg, member_name) ->
+  if (members[member_name.toLowerCase()]?)
+     id = members[member_name.toLowerCase()].id
+  msg.send "Unable to find person named: #{member_name}" unless id?
+  if id?
+     msg.send " * " + id
+
+addMember = (msg, card_id, member_name) ->
+  if (members[member_name.toLowerCase()]?)
+     id = members[member_name.toLowerCase()].id
+  msg.send "Unable to find person named: #{member_name}" unless id?
+  if id?
+     trello.put "/1/cards/#{card_id}/idMembers", {value: id}, (err, data) ->
+       msg.reply "Sorry captain, I couldn't add that member" if err
+       msg.reply "Success! #{member_name} was added" unless err
+
+getMemberCards = (msg, member_name) ->
+  if (members[member_name.toLowerCase()]?)
+     id = members[member_name.toLowerCase()].username
+  msg.send "Unable to find person named: #{user_name}" unless id?
+  if id?
+    trello.get "/1/search", {query: "@"+id, idBoards: board.id, modelTypes:cards, card_fields: name,shortLink,url}, (err, data) ->
+      msg.reply "So sorry, I got an error and cannot give you that information" if err
+      msg.reply "I got the following cards for you" unless err
+      msg.send board.id
+      msg.send id
+      for cards in data.cards
+        msg.send " * " + cards.name+ " | " + cards.url
+
+
+moveCard = (msg, card_id, list_name) ->
+  ensureConfig msg.send
+  id = lists[list_name.toLowerCase()].id
+  msg.reply "I couldn't find a list named: #{list_name}." unless id
+  if id
+    trello.put "/1/cards/#{card_id}/idList", {value: id}, (err, data) ->
+      msg.reply "Sorry boss, I couldn't move that card after all." if err
+      msg.reply "Yep, ok, I moved that card to #{list_name}." unless err
+
+addDescription = (msg, card_id, desc) ->
+  ensureConfig msg.send
+  trello.put "/1/cards/#{card_id}/desc", {value: desc}, (err, data) ->
+      msg.reply "Sorry boss, I couldn't update that card after all." if err
+      msg.reply "Yep, ok, I updated that card for you." unless err
+
+addComment = (msg,card_id, comment, usr) ->
+  ensureConfig msg.send
+  trello.post "/1/cards/#{card_id}/actions/comments",{text: usr+" commented via Slack:"+comment}, (err, data) ->
+      msg.reply "Sorry, I was unable to do that." if err
+      msg.reply "Gladly! That comment has been added" unless err
+
+module.exports = (robot) ->
+  # fetch our board data when the script is loaded
+  ensureConfig console.log
+  trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}", (err, data) ->
+    board = data
+    trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}/lists", (err, data) ->
+      for list in data
+        lists[list.name.toLowerCase()] = list
+    trello.get "/1/boards/#{process.env.HUBOT_TRELLO_BOARD}/members", (err, data) ->
+      for member in data
+        members[member.fullName.toLowerCase().split " ", 1] = member
+
+
+  robot.respond /trello new [""'‘](.+)["”'’]\s(.*)/i, (msg) ->
+    ensureConfig msg.send
+    card_name = msg.match[2]
+    list_name = msg.match[1]
+
+    if card_name.length == 0
+      msg.reply "You must give the card a name"
+      return
+
+    if list_name.length == 0
+      msg.reply "You must give a list name"
+      return
+    return unless ensureConfig()
+
+    createCard msg, list_name, card_name
+
+  robot.respond /trello list ["“'‘](.+)["”'’]/i, (msg) ->
+    showCards msg, msg.match[1]
+
+  robot.respond /trello move (\w+) ["“'‘](.+)["”'’]/i, (msg) ->
+    moveCard msg, msg.match[1], msg.match[2]
+
+  robot.respond /trello comment (\w+) ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
+    addComment msg, msg.match[1], msg.match[2], msg.message.user.name
+
+  robot.respond /trello description (\w+) ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
+    addDescription msg, msg.match[1], msg.match[2]
+
+  robot.respond /trello get (\w+)['’]s cards/i, (msg) ->
+    getMemberCards msg, msg.match[1]
+
+  robot.respond /trello list lists/i, (msg) ->
+    msg.reply "Here are all the lists on your board."
+    Object.keys(lists).forEach (key) ->
+      msg.send " * " + key
+
+  robot.respond /trello list members/i, (msg) ->
+    msg.reply "Here are all the members of the board"
+    Object.keys(members).forEach (member)->
+  msg.send " * " + member
+
+  robot.respond /trello list member id (\w+)/i, (msg) ->
+    msg.reply "The member id for "+ msg.match[1]+" is: "
+    getMemberId msg, msg.match[1]
+
+  robot.respond /trello add member (\w+) (\w+)/i, (msg) ->
+    addMember msg, msg.match[1], msg.match[2]
+
+  robot.respond /trello help/i, (msg) ->
+    msg.reply "Here are all the commands for me."
+    msg.send " *  trello new \"<ListName>\" <TaskName>"
+    msg.send " *  trello list \"<ListName>\""
+    msg.send " *  shows * [<card.shortLink>] <card.name> - <card.shortUrl>"
+    msg.send " *  trello move <card.shortlink> \"<ListName>\""
+    msg.send " *  trello list lists"
+    msg.send " *  trello list member id <name>"
+    msg.send " *  trello add member <card.shortLink> <Name>"
+    msg.send " *  trello comment <card.shortLink> <Comment>"
+    msg.send " *  trello description <card.shortLink> <Description>"


tested that main properly calls out to libraries

--- test/main_test.coffee
+++ test/main_test.coffee
@@ -1,4 +1,7 @@
 CineIO = require('../src/main')
+PlayStream = require('../src/play_stream')
+PublishStream = require('../src/publish_stream')
+ApiBridge = require('../src/api_bridge')

 describe 'CineIO', ->

@@ -49,6 +52,18 @@
       it 'requires a DOM Node', ->
         fn = -> CineIO.publish("FAKE_STREAM_ID", "FAKE_PASSWORD")
         expect(fn).to.throw(Error, "DOM node required.")
+      describe 'success', ->
+        beforeEach ->
+          @publishStub = sinon.stub PublishStream, 'new'
+
+        afterEach ->
+          @publishStub.restore()
+
+        it 'calls to PublishStream', ->
+          CineIO.publish("FAKE_STREAM_ID", 'FAKE_PASSWORD', 'fake-dom-id')
+          expect(@publishStub.calledOnce).to.be.true
+          args = @publishStub.firstCall.args
+          expect(args).to.deep.equal(["FAKE_STREAM_ID", 'FAKE_PASSWORD', 'fake-dom-id', {}])

   describe '.play', ->
     requiresInit('play')
@@ -58,6 +73,19 @@
       it 'requires a DOM Node', ->
         fn = -> CineIO.play("FAKE_STREAM_ID")
         expect(fn).to.throw(Error, "DOM node required.")
+
+      describe 'success', ->
+        beforeEach ->
+          @playStub = sinon.stub PlayStream, 'live'
+
+        afterEach ->
+          @playStub.restore()
+
+        it 'calls to PlayStream', ->
+          CineIO.play("FAKE_STREAM_ID", 'fake-dom-id')
+          expect(@playStub.calledOnce).to.be.true
+          args = @playStub.firstCall.args
+          expect(args).to.deep.equal(["FAKE_STREAM_ID", 'fake-dom-id', {}])

   describe '.playRecording', ->
     requiresInit('playRecording')
@@ -71,14 +99,53 @@
         fn = -> CineIO.playRecording("FAKE_STREAM_ID", "FAKE_RECORDING_NAME")
         expect(fn).to.throw(Error, "DOM node required.")

+      describe 'success', ->
+        beforeEach ->
+          @playRecordingStub = sinon.stub PlayStream, 'recording'
+
+        afterEach ->
+          @playRecordingStub.restore()
+
+        it 'calls to PlayStream', ->
+          CineIO.playRecording("FAKE_STREAM_ID", 'fakeRecording.mp4', 'fake-dom-id')
+          expect(@playRecordingStub.calledOnce).to.be.true
+          args = @playRecordingStub.firstCall.args
+          expect(args).to.deep.equal(["FAKE_STREAM_ID", 'fakeRecording.mp4', 'fake-dom-id', {}])
+
   describe '.getStreamDetails', ->
     requiresInit('getStreamDetails')
     describe 'after initialized', ->
       initCineIO()
       requiresStreamId('getStreamDetails')

+      describe 'success', ->
+        beforeEach ->
+          @getStreamDetailsStub = sinon.stub ApiBridge, 'getStreamDetails'
+
+        afterEach ->
+          @getStreamDetailsStub.restore()
+
+        it 'calls to the ApiBridge', ->
+          CineIO.getStreamDetails("FAKE_STREAM_ID", "the callback")
+          expect(@getStreamDetailsStub.calledOnce).to.be.true
+          args = @getStreamDetailsStub.firstCall.args
+          expect(args).to.deep.equal(["FAKE_STREAM_ID", 'the callback'])
+
   describe '.getStreamRecordings', ->
     requiresInit('getStreamRecordings')
     describe 'after initialized', ->
       initCineIO()
       requiresStreamId('getStreamRecordings')
+
+      describe 'success', ->
+        beforeEach ->
+          @getStreamRecordingsStub = sinon.stub ApiBridge, 'getStreamRecordings'
+
+        afterEach ->
+          @getStreamRecordingsStub.restore()
+
+        it 'calls to the ApiBridge', ->
+          CineIO.getStreamRecordings("FAKE_STREAM_ID", "the callback")
+          expect(@getStreamRecordingsStub.calledOnce).to.be.true
+          args = @getStreamRecordingsStub.firstCall.args
+          expect(args).to.deep.equal(["FAKE_STREAM_ID", 'the callback'])


Extend Command in Unlink class

Extend Command in Unlink class

--- src/unlink.coffee
+++ src/unlink.coffee
@@ -3,11 +3,12 @@
 CSON = require 'season'
 optimist = require 'optimist'

+Command = require './command'
+config = require './config'
 fs = require './fs'
-config = require './config'

 module.exports =
-class Unlink
+class Unlink extends Command
   @commandNames: ['unlink']

   constructor: ->
@@ -29,8 +30,6 @@
     options.alias('d', 'dev').boolean('dev').describe('dev', 'Unlink package from ~/.atom/dev/packages')
     options.boolean('hard').describe('hard', 'Unlink package from ~/.atom/packages and ~/.atom/dev/packages')
     options.alias('a', 'all').boolean('all').describe('all', 'Unlink all packages in ~/.atom/packages and ~/.atom/dev/packages')
-
-  showHelp: (argv) -> @parseOptions(argv).showHelp()

   getDevPackagePath: (packageName) -> path.join(@devPackagesPath, packageName)


Clean up test.

Clean up test.

--- test/index.coffee
+++ test/index.coffee
@@ -972,12 +972,11 @@
       # less than a month
       start = new Date(2000, 0, 1)
       end = new Date(2000, 1, 4)
-      range = Report.dateRange start, end
+      [range_start, range_end] = Report.dateRange start, end
       expected_start = moment.utc([2000, 0, 1]).toDate()
       expected_end = moment.utc([2000, 1, 1]).toDate()
-      range.length.should.equal 2
-      range[0].toString().should.equal expected_start.toString()
-      range[1].toString().should.equal expected_end.toString()
+      range_start.should.eql expected_start
+      range_end.should.eql expected_end

     it "should correctly return a range more than the period", ->
       # more than a month
@@ -995,9 +994,9 @@
       start = new Date(2000, 0, 1)
       end = new Date(2000, 0, 4)
       range = Report.dateRange start, end
-      expecte_period = moment.utc([2000, 0, 1]).toDate()
+      expected_period = moment.utc([2000, 0, 1]).toDate()
       range.length.should.equal 1
-      range[0].toString().should.equal expecte_period.toString()
+      range[0].toString().should.equal expected_period.toString()

   describe "#record", ->
     Report = new SparseReport simple_collection, {},


Tests running fix.

Tests running fix.

--- test/index.coffee
+++ test/index.coffee
@@ -1,17 +1,18 @@

 testutils = require './testutils.coffee'
 config = require './config.coffee'
+_ = require 'lodash'

 before testutils.checkDB

 describe 'Chat service.', ->

-  for state in config.states
-
-    testutils.setState state
+  _.forEach config.states, (state) ->

     describe "State: '#{state.state}', adapter: '#{state.adapter}'.", ->
+
+      before -> testutils.setState state

       describe 'Initialization', require './initialization.coffee'


Change scope of 'where' in tests

Change scope of 'where' in tests

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- test/items.coffee
+++ test/items.coffee
@@ -7,8 +7,8 @@
   withStats = items.filters.withStats
   OR = items.filters.or
   AND = items.filters.and
+  where = items.filters.where
   byProperty = items.sorts.byProperty
-

   hextechGunblade = items._list["Hextech Gunblade"]

@@ -69,8 +69,6 @@
           item.ap.should.equal 0 for item in list

       describe "where", ->
-        where = items.filters.where
-
         it "should check for equality if the parameter isn't a query object", ->
           find(where name: "Hextech Gunblade").should.eql [hextechGunblade]


optimization; return first set of solutions rather than doing an exhaustive search

--- src/system.coffee
+++ src/system.coffee
@@ -73,17 +73,18 @@
 		@solve: (x, expr, exprList) ->
 			if expr.vars.length is 1 and expr.vars[0] is x
 				return (y.approx() for y in expr.solve(x))
-			results = []
 			for e0 in exprList
 				common = expr.common(e0)
 				continue unless common.length > 0
 				for y in common
 					continue if y is x
+					results = []
 					for e1 in e0.solve(y)
 						results.push(@solve(x,
 							expr.sub([[y, e1]]),
 							exprList.filter((e2) -> e2 isnt e0))...)
-			return results
+					return results if results.length > 0
+			return []

 		solve: (x) ->
 			vars = @vars()


Cleaning up logging.

Cleaning up logging.

--- src/timber.coffee
+++ src/timber.coffee
@@ -31,7 +31,7 @@
         @module = module

     _write : (level, message) ->
-        console.log("#{@module} | #{level} | #{message}") if console
+        console.log([level, @module, message].join(" | ")) if console

     # Log a message at the debug level.
     #


Removed room feature

Removed room feature

--- src/tunnel.coffee
+++ src/tunnel.coffee
@@ -10,7 +10,7 @@
         host: '0.0.0.0'
         timeout: 2000

-    rooms: {}
+    connections: {}

     constructor: (options) ->
         @options = _.extend {}, @options, options
@@ -27,10 +27,9 @@

         connection.on 'message', _.bind @message, @, connection

-    disconnect: (cn, roomId, clientId)->
+    disconnect: (cn, clientId)->
         cn.on 'close', =>
-            delete @rooms[roomId][clientId]
-            delete @rooms[roomId] if _.isEmpty @rooms[roomId]
+            delete @connections[clientId]

     message: (cn, data)->
         mes = null
@@ -39,30 +38,24 @@

         if mes.host is 'tunnel'
             switch mes.pathname
-                when '/connect_room' then @connectRoom cn, mes.query
+                when '/connect' then @connect cn, mes.query

         else
             @tunnelMessage cn, mes, data

-    connectRoom: (cn, q)->
-        roomId   = q.room   or @getUID 'r'
+    connect: (cn, q)->
         clientId = q.client or @getUID 'c'
+        clientId = @getUID 'c' if @connections[clientId]

-        @rooms[roomId] = {} unless @rooms[roomId]
-        @rooms[roomId][clientId] = cn
+        @connections[clientId] = cn

         clearTimeout cn._startTO
-        @disconnect cn, roomId, clientId
-        @response cn, null, 'tunnel', '/created', room: roomId, client: clientId
+        @disconnect cn, clientId
+        @response cn, null, 'tunnel', '/created', client: clientId

     tunnelMessage: (cn, mes, data)->
-        return if not mes.host or not @rooms[mes.host] or not @rooms[mes.host][mes.auth]
-        room = @rooms[mes.host]
-        currentClient = mes.auth
-
-        for client of room
-            unless client is currentClient
-                room[client].send data
+        return if not mes.host or not @connections[mes.host]
+        @connections[mes.host].send data

     response: (cn, c, h, m, q)->
         return unless h


CADS-507 : Claim Self employment expenses for pensionExpenses and jobExpenses / childcare fixup charCount.

--- c3/app/assets/javascripts/s_self_employment/g_selfEmploymentPensionsAndExpenses.coffee
+++ c3/app/assets/javascripts/s_self_employment/g_selfEmploymentPensionsAndExpenses.coffee
@@ -1,11 +1,13 @@
 window.initEvents =(payPensionSchemeY, payPensionSchemeN, pensionExpenses,
                     haveExpensesForJobY, haveExpensesForJobN, jobExpenses) ->
+  $("#" + pensionExpenses).trigger("blur")
+  $("#" + jobExpenses).trigger("blur")

   if not $("#" + payPensionSchemeY).prop('checked')
     hidePensionExpenses(pensionExpenses)

   $("#" + payPensionSchemeY).on "click", ->
-    showPensionExpenses()
+    showPensionExpenses(pensionExpenses)

   $("#" + payPensionSchemeN).on "click", ->
     hidePensionExpenses(pensionExpenses)
@@ -14,13 +16,14 @@
     hideExpensesForJob(jobExpenses)

   $("#" + haveExpensesForJobY).on "click", ->
-    showHaveExpensesForJob()
+    showHaveExpensesForJob(jobExpenses)

   $("#" + haveExpensesForJobN).on "click", ->
     hideExpensesForJob(jobExpenses)

-showPensionExpenses = ->
+showPensionExpenses = (pensionExpenses) ->
+  $("#" + pensionExpenses).trigger("blur")
   $("#pensionExpenses").slideDown(0).attr 'aria-hidden', 'false'

 hidePensionExpenses = (pensionExpenses) ->
@@ -28,7 +31,8 @@
     	$("#" + pensionExpenses).val("")
   $("#pensionExpenses").slideUp(0, emptyPensionExpenses).attr 'aria-hidden', 'true', ->

-showHaveExpensesForJob = ->
+showHaveExpensesForJob = (jobExpenses) ->
+  $("#" + jobExpenses).trigger("blur")
   $("#jobExpenses").slideDown(0).attr 'aria-hidden', 'false'

 hideExpensesForJob = (jobExpenses) ->


log out job in tpdsupdate sender

log out job in tpdsupdate sender

GitOrigin-RevId: 8ff4665f62a1f36580c5864f2185ddf1a792f6cf

--- services/web/app/coffee/Features/ThirdPartyDataStore/TpdsUpdateSender.coffee
+++ services/web/app/coffee/Features/ThirdPartyDataStore/TpdsUpdateSender.coffee
@@ -44,7 +44,7 @@
 				logger.err err:err, "error queuing something in the tpdsworker, continuing anyway"
 				callback()
 			else
-				logger.log group:group, "successfully queued up job for tpdsworker"
+				logger.log group:group, job:job, "successfully queued up job for tpdsworker"
 				callback()

 	_addEntity: (options, callback = (err)->)->


add tests for .value setters/getters

add tests for .value setters/getters

--- tests/document.coffee
+++ tests/document.coffee
@@ -5,6 +5,9 @@
 assertEqual = (a, b, label) ->
 	if a isnt b
 		throw Error "#{label} (#{a?.toString()}) should equal (#{b?.toString()})"
+
+keys = (o) ->
+	[ i for i of o ]

 dom = require('../dom')
 global.document = dom.createDocument()
@@ -91,6 +94,30 @@
 assertEqual attr.value, "foo", "attr.value"
 assertEqual attr.nodeValue, "foo", "attr.nodeValue"

+input = document.createElement("input")
+input.value = 'bar'
+input.setAttribute('value', 'foo')
+assertEqual input.value, 'foo', 'input.value'
+assertEqual input.value, input.getAttribute('value'), 'input.getAttribute("value")'
+
+select = document.createElement("select")
+optionA = document.createElement("option")
+optionB = document.createElement("option")
+assertEqual optionA.constructor.name, "HTMLOptionElement"
+assertEqual optionA.constructor.__super__.constructor.name, "HTMLInputElement"
+optionA.value = '1'
+optionA.innerText = 'A'
+optionB.innerText = 'B'
+assertEqual optionA.value, '1', 'optionA.value'
+assertEqual optionB.value, 'B', 'optionB.value'
+select.appendChild(optionA)
+select.appendChild(optionB)
+assertEqual select.selectedIndex, 0, 'select.selectedIndex'
+assertEqual select.value, '1', 'select.value'
+select.selectedIndex = 1
+assertEqual select.selectedIndex, 1, 'select.selectedIndex * 2'
+assertEqual select.value, 'B', 'select.value'
+
 testSelector = (s, output) ->
 	x = document.querySelectorAll(s)
 	assertEqual x.toString(), output, s
@@ -100,7 +127,6 @@
 testSelector "p", '
,
'
 testSelector "div *", ',
,
,
'

-# console.log document.toString(true,true)
 console.log "All tests passed."

 # vim: ft=coffee


Add nick to users

Add nick to users

--- server/app/actors/user-actor.coffee
+++ server/app/actors/user-actor.coffee
@@ -25,6 +25,7 @@
     debug("Joing user #{@id} to room #{ev.data.room}")
     ev.socket.join(ev.data.room)
     @room = ev.data.room
+    @nick = ev.data.nick
     unbind(ev.socket, 'join')
     @manager.globalBus.push { type: "BROADCAST", room: ev.data.room, key: "user-joined", message: "User #{@id} joined to room #{ev.data.room}" }


Naming conflict since config refactor

Naming conflict since config refactor

--- server/packages/config/index.coffee
+++ server/packages/config/index.coffee
@@ -6,7 +6,7 @@
 url = require 'url'

 _ = require 'underscore'
-config = require 'config'
+configModule = require 'config'
 Promise = require 'bluebird'

 pkgman = require 'pkgman'
@@ -18,13 +18,13 @@
 	hostname: "//#{req.headers.host}"

 	# Is the server running in test mode?
-	testMode: if (config.get 'E2E')? then 'e2e' else false
+	testMode: if (configModule.get 'E2E')? then 'e2e' else false

 	# Execution environment, `production`, or...
-	environment: config.get 'NODE_ENV'
+	environment: configModule.get 'NODE_ENV'

 	# The list of enabled packages.
-	packageList: config.get 'packageList'
+	packageList: configModule.get 'packageList'

 # ## Implements hook `httpMiddleware`
 exports.$httpMiddleware = (http) ->


Added cformat and hash.

Added cformat and hash.

In python these are not methods on str, the syntax is built into the language which is not possible, or probably desirable in JS, so they have been tacked onto String.

--- pystr.coffee
+++ pystr.coffee
@@ -1,13 +1,13 @@
 # Some useful string functions ported from python.

-# Regular expressions are only used when finding parts at the front of the string, based on this post about a
+# Regular expressions are only used when finding parts at the front of the string, based on this post about a
 # [Faster JavaScript Trim](http://blog.stevenlevithan.com/archives/faster-trim-javascript).

 # Helper functions, these are not exposed.
-isArray = (a) -> Object::toString.apply(a) is '[object Array]'
-makeArray = (a) -> return a if isArray(a); return [a]
-isCaseable = (c) -> c.length and /[A-Za-z]/.test(c)
-endswith = (s, c) ->
+isArray = (a)-> Object::toString.apply(a) is '[object Array]'
+makeArray = (a)-> return a if isArray(a); return [a]
+isCaseable = (c)-> c.length and /[A-Za-z]/.test(c)
+endswith = (s, c)->
     for i in [c.length...0]
         return false if c.charAt(c.length-i) isnt s.charAt(s.length-i)
     return true
@@ -19,16 +19,16 @@
 String::upper = String::toUpperCase

 # Test if the string is lower and there is at least one cased character.
-String::islower = -> return true if @toString() is @lower() and isCaseable(@); return false
+String::islower = -> return true if "#{@}" is @lower() and isCaseable(@); return false

 # Test if the string is upper and there is at least one cased character.
-String::isupper = -> return true if @toString() is @upper() and isCaseable(@); return false
+String::isupper = -> return true if "#{@}" is @upper() and isCaseable(@); return false

 # Test if the string starts with a string.
 # With optional start, test the string beginning at that position.
 # With optional end, stop comparing the string at that position.
 # Prefix can also be an array of strings to try.
-String::startswith = (c, s, e) ->
+String::startswith = (c, s, e)->
     t = @slice(s, e)
     for a in makeArray(c)
         return true if new RegExp("^#{a}").test(t)
@@ -38,7 +38,7 @@
 # With optional start, test the string beginning at that position.
 # With optional end, stop comparing the string at that position.
 # Suffix can also be an array of strings to try.
-String::endswith = (c, s, e) ->
+String::endswith = (c, s, e)->
     t = @slice(s, e)
     for a in makeArray(c)
         return true if endswith(t, a)
@@ -51,64 +51,108 @@
 String::title = -> (word.capitalize() for word in @split(' ')).join(' ')

 # Test if the string is title and there is at least one cased character.
-String::istitle = -> return true if @toString() is @title() and isCaseable(@); return false
-
-# Return a copy of the string with leading whitespace removed,
+String::istitle = -> return true if "#{@}" is @title() and isCaseable(@); return false
+
+# Return a copy of the string with leading whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
-String::lstrip = (s) -> ss = '\\s\\s*'; ss = "(#{s})+" if s?; @.replace(new RegExp("^#{ss}"), '')
-
-# Return a copy of the string with trailing whitespace removed,
+String::lstrip = (s)-> ss = '\\s\\s*'; ss = "(#{s})+" if s?; @replace(new RegExp("^#{ss}"), '')
+
+# Return a copy of the string with trailing whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
-String::rstrip = (s) -> ss = '\\s*\\s'; ss = "(#{s})+" if s?; @.replace(new RegExp("#{ss}$"), '')
+String::rstrip = (s)-> ss = '\\s*\\s'; ss = "(#{s})+" if s?; @replace(new RegExp("#{ss}$"), '')
     #TODO: implement this without regex:
     #`re = new RegExp(ss); i = @length; continue while re.test(@charAt(--i)); @[0...i+1]`

-# Return a copy of the string with leading and trailing whitespace removed,
+# Return a copy of the string with leading and trailing whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
 # Uses the native `trim` if it's available and only whitespace is being stripped.
-String::strip = (s) -> return @trim() if not s and @trim; @lstrip(s).rstrip(s)
+String::strip = (s)-> return @trim() if not s and @trim; @lstrip(s).rstrip(s)

 # Return the number of non-overlapping occurrences of a string in the string.
 # Optional arguments start and end are interpreted as in slice notation.
-String::count = (c, s, e) -> @slice(s, e).match(new RegExp(c, 'g'))?.length or 0
+String::count = (c, s, e)-> @slice(s, e).match(new RegExp(c, 'g'))?.length or 0

 # Return the lowest index in the string where a string is found.
 # Optional arguments start and end are interpreted as in slice notation.
 # Return -1 if no match if found.
-String::find = (c, s, e) -> i = @slice(s, e).indexOf(c); return -1 if i is -1; i+(s or 0)
+String::find = (c, s, e)-> i = @slice(s, e).indexOf(c); return -1 if i is -1; i+(s or 0)

 # Test if all characters in the string are alphanumeric.
 # Use `A-Za-z` instead of `A-z` becuase `A-z` matches `-` for some reason...
-String::isalnum = -> @match(/[A-Za-z0-9]+/)?[0] is @toString()
+String::isalnum = -> @match(/[A-Za-z0-9]+/)?[0] is "#{@}"

 # Test if all characters in the string are alphabetic.
-String::isalpha = -> @match(/[A-Za-z]+/)?[0] is @toString()
+String::isalpha = -> @match(/[A-Za-z]+/)?[0] is "#{@}"

 # Test if all characters in the string are digits.
-String::isdigit = -> @match(/\d+/)?[0] is @toString()
+String::isdigit = -> @match(/\d+/)?[0] is "#{@}"

 # Test if all characters in the string are whitespace.
-String::isspace = -> @match(/\s+/)?[0] is @toString()
-
-# Return a string which is the concatenation of the strings in an array.
+String::isspace = -> @match(/\s+/)?[0] is "#{@}"
+
+# Return a string which is the concatenation of the strings in an array.
 # The separator between elements is the string.
-String::join = (a) -> a.join(@)
-
-# Test if the string contains a string.
+String::join = (a)-> a.join(@)
+
+# Return S left-justified in a string of length width. Padding is
+# done using the specified fill character (default is a space).
+String::ljust = (w, c=' ')-> [c,t]=["#{c}","#{@}"]; return t unless w and c; t="#{t}#{c}" while(t.length < w); t
+
+# Return S right-justified in a string of length width. Padding is
+# done using the specified fill character (default is a space)
+String::rjust = (w, c=' ')-> [c,t]=["#{c}","#{@}"]; return t unless w and c; t="#{c}#{t}" while(t.length < w); t
+
+# Pad a numeric string S with zeros on the left, to fill a field
+# of the specified width.  The string S is never truncated.
+String::zfill = (w)-> "#{@}".rjust(w, 0)
+
+# Test if the string contains any of the passed in strings.
 # Python strings do not have a 'contains' function, but javascript doesn't have a way of saying `'foo' in 'foobar'`.
 # With this method the python `in` check would be written like this: `'foobar'.contains('foo')`
-String::contains = (s) -> @indexOf(s) isnt -1
+String::contains = (s...)-> (return true if @indexOf(i) isnt -1) for i in s; return false
+
+# C style string formatting
+# `'%d %s and %d %s'.cformat(2, 'cats', 6, 'dogs')` --> '2 cats and 6 dogs'
+# `'%.2f liters'.cformat(2.2483545345)` --> '2.25 liters'
+# `'%02d:%02d.%02d'.cformat(9, 36, 28)` --> 09:36.28
+String::cformat = (args...)->
+    return '' unless @
+    pad = (v,l,c)-> "#{v}".zfill(l, c)
+    @replace(/%(s|\.?[0-9]*f|0?[0-9]*d)/g, (match, type)->
+        arg = args.shift()
+        if fmatch = type.match(/(\.?)([0-9]*)f/)
+            arg = parseFloat(arg)
+            if fmatch[1] is '.' and fmatch[2]
+                return "#{arg.toFixed(fmatch[2])}"
+            else
+                return "#{arg}"
+        if imatch = type.match(/(0?)([0-9]*)d/)
+            arg = parseInt(arg, 10)
+            if imatch.length > 1 and imatch[2]
+                return "#{arg}".rjust(imatch[2], imatch[1] or ' ')
+            else
+                return "#{arg}"
+        return "#{arg}"
+    )
+
+# Return an integer hash of the string
+String::hash = ()->
+    hash = 0
+    for i in [0...@length]
+        hash= (hash<<5)-hash+@charCodeAt(i)
+        hash= hash & hash # Convert to 32bit integer
+    return hash

 # TODO: implement these

-# S.center(width[, fillchar]) -> string
-#
+# S.center(width[, fillchar])-> string
+#
 # Return S centered in a string of length width. Padding is
 # done using the specified fill character (default is a space)
 # String::center = ->

-# S.decode([encoding[,errors]]) -> object
-#
+# S.decode([encoding[,errors]])-> object
+#
 # Decodes S using the codec registered for encoding. encoding defaults
 # to the default encoding. errors may be given to set a different error
 # handling scheme. Default is 'strict' meaning that encoding errors raise
@@ -117,8 +161,8 @@
 # able to handle UnicodeDecodeErrors.
 # String::decode = ->

-# S.encode([encoding[,errors]]) -> object
-#
+# S.encode([encoding[,errors]])-> object
+#
 # Encodes S using the codec registered for encoding. encoding defaults
 # to the default encoding. errors may be given to set a different error
 # handling scheme. Default is 'strict' meaning that encoding errors raise
@@ -127,69 +171,57 @@
 # codecs.register_error that is able to handle UnicodeEncodeErrors.
 # String::encode = ->

-# S.expandtabs([tabsize]) -> string
-#
+# S.expandtabs([tabsize])-> string
+#
 # Return a copy of S where all tab characters are expanded using spaces.
 # If tabsize is not given, a tab size of 8 characters is assumed.
 # String::expandtabs = ->

-# S.format(*args, **kwargs) -> unicode
+# S.format(*args, **kwargs)-> unicode
 # String::format = ->

-# S.index(sub [,start [,end]]) -> int
-#
+# S.index(sub [,start [,end]])-> int
+#
 # Like S.find() but raise ValueError when the substring is not found.
 # String::index = ->

-# S.ljust(width[, fillchar]) -> string
-#
-# Return S left-justified in a string of length width. Padding is
-# done using the specified fill character (default is a space).
-# String::ljust = ->
-
-# S.partition(sep) -> (head, sep, tail)
-#
+# S.partition(sep)-> (head, sep, tail)
+#
 # Search for the separator sep in S, and return the part before it,
 # the separator itself, and the part after it.  If the separator is not
 # found, return S and two empty strings.
 # String::partition = ->

-# S.replace (old, new[, count]) -> string
-#
+# S.replace (old, new[, count])-> string
+#
 # Return a copy of string S with all occurrences of substring
 # old replaced by new.  If the optional argument count is
 # given, only the first count occurrences are replaced.
 # String::replace = ->

-# S.rfind(sub [,start [,end]]) -> int
-#
+# S.rfind(sub [,start [,end]])-> int
+#
 # Return the highest index in S where substring sub is found,
 # such that sub is contained within s[start:end].  Optional
 # arguments start and end are interpreted as in slice notation.
-#
+#
 # Return -1 on failure.
 # String::rfind = ->

-# S.rindex(sub [,start [,end]]) -> int
-#
+# S.rindex(sub [,start [,end]])-> int
+#
 # Like S.rfind() but raise ValueError when the substring is not found.
 # String::rindex = ->

-# S.rjust(width[, fillchar]) -> string
-#
-# Return S right-justified in a string of length width. Padding is
-# done using the specified fill character (default is a space)
-# String::rjust = ->
-
-# S.rpartition(sep) -> (tail, sep, head)
-#
+# S.rpartition(sep)-> (tail, sep, head)
+#
 # Search for the separator sep in S, starting at the end of S, and return
 # the part before it, the separator itself, and the part after it.  If the
 # separator is not found, return two empty strings and S.
 # String::rpartition = ->

-# S.rsplit([sep [,maxsplit]]) -> list of strings
-#
+# S.rsplit([sep [,maxsplit]])-> list of strings
+#
 # Return a list of the words in the string S, using sep as the
 # delimiter string, starting at the end of the string and working
 # to the front.  If maxsplit is given, at most maxsplit splits are
@@ -197,38 +229,23 @@
 # is a separator.
 # String::rsplit = ->

-# S.split([sep [,maxsplit]]) -> list of strings
-#
-# Return a list of the words in the string S, using sep as the
-# delimiter string.  If maxsplit is given, at most maxsplit
-# splits are done. If sep is not specified or is None, any
-# whitespace string is a separator and empty strings are removed
-# from the result.
-# String::split = ->
-
-# S.splitlines([keepends]) -> list of strings
-#
+# S.splitlines([keepends])-> list of strings
+#
 # Return a list of the lines in S, breaking at line boundaries.
 # Line breaks are not included in the resulting list unless keepends
 # is given and true.
 # String::splitlines = ->

-# S.swapcase() -> string
-#
+# S.swapcase()-> string
+#
 # Return a copy of the string S with uppercase characters
 # converted to lowercase and vice versa.
 # String::swapcase = ->

-# S.translate(table [,deletechars]) -> string
-#
+# S.translate(table [,deletechars])-> string
+#
 # Return a copy of the string S, where all characters occurring
 # in the optional argument deletechars are removed, and the
 # remaining characters have been mapped through the given
 # translation table, which must be a string of length 256.
 # String::translate = ->
-
-# S.zfill(width) -> string
-#
-# Pad a numeric string S with zeros on the left, to fill a field
-# of the specified width.  The string S is never truncated.
-# String::zfill = ->


Check compiler version

Check compiler version

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -20,8 +20,11 @@

     @createQueues()

+    vers = @getVersion()
+    vers.then @checkComp
+
     @backendPromise =
-      @getVersion()
+      vers
       .then @getCaps
       .then (@caps) =>
         @backend = new GhcModiProcessReal @caps
@@ -65,12 +68,35 @@
             if error?
               error.stack = (new Error).stack
               return reject error
-            resolve (
-              /^ghc-mod version (\d+)\.(\d+)\.(\d+)(?:\.(\d+))?/.exec(stdout)
-              .slice(1, 5).map (i) -> parseInt i
-              )
-
-  getCaps: (vers) ->
+            vers = /^ghc-mod version (\d+)\.(\d+)\.(\d+)(?:\.(\d+))?/.exec(stdout).slice(1, 5).map (i) -> parseInt i
+            comp = /GHC (.+)$/.exec(stdout.trim())[1]
+            Util.debug "Ghc-mod #{vers} built with #{comp}"
+            resolve {vers, comp}
+
+  checkComp: ({comp}) ->
+    Util.getProcessOptions()
+    .then (opts) ->
+      opts1 = {}
+      for k, v of opts
+        opts1[k] = v
+      opts1.timeout = atom.config.get('haskell-ghc-mod.syncTimeout')
+      return opts1
+    .then (opts) ->
+      CP.execFile 'ghc', ['--version'], opts, (error, stdout, stderr) ->
+        if error?
+          error.stack = (new Error).stack
+          throw error
+        compv = /version (.+)$/.exec(stdout.trim())[1]
+        Util.debug "Ghc version #{compv}"
+        if compv isnt comp
+          warn = "
+            GHC version in your PATH '#{compv}' doesn't match with
+            GHC version used to build ghc-mod '#{comp}'. This will lead to
+            problems"
+          atom.notifications.addWarning warn
+          Util.warn warn
+
+  getCaps: ({vers}) ->
     caps =
       version: vers
       fileMap: false


Fixed Wheel Offset Bug

Fixed Wheel Offset Bug

--- js/camera.coffee
+++ js/camera.coffee
@@ -34,7 +34,10 @@
       @position = @position.add new Vector(-cos, sin) if Keyboard.isDown 68
       @position = @position.add new Vector(-sin, -cos) if Keyboard.isDown 83

-      @position = Wheel.getOffset().multiply(deltaTime).add(@position)
+      wheelOffset = Wheel.getOffset().multiply(deltaTime / @scale)
+      x = wheelOffset.x * Math.cos(@rotation) + wheelOffset.y * Math.sin(@rotation)
+      y = - wheelOffset.x * Math.sin(@rotation) + wheelOffset.y * Math.cos(@rotation)
+      @position = @position.add new Vector(x, y)

     render: (context, canvas) ->
       width = window.innerWidth


Remove search function

Remove search function

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -74,22 +74,6 @@
         # TODO errors by section
         @featuredErrors.append(new ErrorView(@packageManager, error))

-
-  search: (query) ->
-    if @resultsContainer.children().length is 0
-      @searchMessage.text("Searching for \u201C#{query}\u201D\u2026").show()
-
-    @packageManager.search(query, {packages: true})
-      .then (packages=[]) =>
-        if packages.length is 0
-          @searchMessage.text("No package results for \u201C#{query}\u201D").show()
-        else
-          @searchMessage.hide()
-        @addPackageViews(@resultsContainer, packages)
-      .catch (error) =>
-        @searchMessage.hide()
-        @searchErrors.append(new ErrorView(@packageManager, error))
-
   addPackageViews: (container, packages) ->
     container.empty()
     packageViews = []


Typos

Typos

--- index.coffee
+++ index.coffee
@@ -52,7 +52,7 @@
 	computed:

 		# Add the maxThreshold to the @inViewportThreshold prop so that the handler
-		# is fired for elements that are talled than the viewport
+		# is fired for elements that are taller than the viewport
 		inViewportThresholdWithMax: ->

 			# Support number and array thresholds
@@ -80,7 +80,7 @@
 		inViewportRoot: -> @reInitInViewportMixin()
 		inViewportThresholdWithMax: (now, old) ->

-			# In IE, this is kept getting retriggered, to doing a manual comparison
+			# In IE, this kept getting retriggered, so doing a manual comparison
 			# of old and new before deciding whether to take action.
 			@reInitInViewportMixin() unless now.toString() == old.toString()


Fix prepublish.

Fix prepublish.

--- index.coffee
+++ index.coffee
@@ -1,7 +1,7 @@
 Esprima = require('esprima')
 {SourceNode} = require("source-map")
-Walker = require('./lib/walker')
-{delimit} = require('./lib/helpers')
+Walker = require('./lib/walker.coffee')
+{delimit} = require('./lib/helpers.coffee')

 ###*
 # js2coffee() : js2coffee(source, [options])


Always call done

Always call done

--- index.coffee
+++ index.coffee
@@ -48,7 +48,7 @@
     ]

     spawn {cmd, args}, (error, result, code) ->
-      return done(error) if error?
+      done(error)

       # nupkgPath = path.join(outputDirectory, "#{metadata.name}.#{metadata.version}.nupkg")
       #


table button

table button

--- js/editor.coffee
+++ js/editor.coffee
@@ -14,11 +14,16 @@
       @div ".five.columns", ->
         @div ".toolbar", ->
          @i ".fa.fa-header.fa-lg", on: {click: @$.on.header}
+
          @i ".fa.fa-bold.fa-lg", on: {click: @$.on.bold}
          @i ".fa.fa-italic.fa-lg", on: {click: @$.on.italic}
          @i ".fa.fa-link.fa-lg", on: {click: @$.on.link}
          @i ".fa.fa-code.fa-lg", on: {click: @$.on.inlineCode}
          @i ".fa.fa-camera.fa-lg", on: {click: @$.on.inlineMedia}
+         @i ".fa.fa-superscript.fa-lg", on: {click: @$.on.superscript}
+         @i ".fa.fa-subscript.fa-lg", on: {click: @$.on.subscript}
+
+         @i ".fa.fa-table.fa-lg", on: {click: @$.on.table}

         @$.elems.textarea = @textarea ".editor",
          autocomplete: "off"
@@ -51,7 +56,7 @@

    @listen 'header', ->
     s = @editor.getSelection()
-    @editor.replaceSelection "\n\n##{s}\n"
+    @editor.replaceSelection "\n##{s}\n"
     {line} = @editor.getCursor()
     @editor.indentLine line - 1, 'prev'
     @editor.indentLine line, 'prev'
@@ -63,6 +68,22 @@
    @listen 'inlineCode', -> @wrapSelection '``', '``'
    @listen 'link', -> @wrapSelection '<<', '>>'
    @listen 'inlineMedia', -> @wrapSelection '[[', ']]'
+   @listen 'superscript', -> @wrapSelection '^^', '^^'
+   @listen 'subscript', -> @wrapSelection '__', '__'
+
+   @listen 'table', ->
+    s = @editor.getSelection()
+    @editor.replaceSelection "\n|||\ncol1|col2\n===\n1,1|1,2\n2,1|2,2\n#{s}"
+    {line} = @editor.getCursor()
+    @editor.indentLine line - 5, 'prev'
+    @editor.indentLine line - 4, 'prev'
+    @editor.indentLine line - 4, 'add'
+    @editor.indentLine line - 3, 'prev'
+    @editor.indentLine line - 2, 'prev'
+    @editor.indentLine line - 1, 'prev'
+    @editor.indentLine line, 'prev'
+    @editor.indentLine line, 'subtract'
+    @editor.focus()

    preview: ->
     text = @editor.getValue()


Don't need a separate transition end function.

--- js/flight.coffee
+++ js/flight.coffee
@@ -102,7 +102,7 @@
     # shortern the delay here to stop a gap appearing in android
     window.setTimeout =>
       @translate(@targetPage, "X", "0%")
-      @currentPage.addEventListener "webkitTransitionEnd", @finishSlide, false
+      @currentPage.addEventListener "webkitTransitionEnd", @finishTransition, false
       @resetState()
     , 5

@@ -129,24 +129,16 @@
        , 10

      window.setTimeout =>
-       @targetPage.addEventListener "webkitTransitionEnd", @finishSlideFromBottom, false
+       @targetPage.addEventListener "webkitTransitionEnd", @finishTransition, false
      , 15
      @resetState()

-
   # call on transition end
-  finishSlide: =>
+  finishTransition: =>
     @removeClass @currentPage, 'visible'
     @currentPage.style.display = "none"
-    @addClass @targetPage, 'visible'
-
+    @addClass @targetPage, 'visible'
     @currentPage.removeEventListener "webkitTransitionEnd", @finishTransition, false
-
-   finishSlideFromBottom: =>
-    @removeClass @currentPage, 'visible'
-    @currentPage.style.display = "none"
-    @addClass @targetPage, 'visible'
-    @targetPage.removeEventListener "webkitTransitionEnd", @finishTransition, false

    resetState: =>
     @back = false


xmpp connection: doc, serialize stanzas only once

--- src/xmpp/connection.coffee
+++ src/xmpp/connection.coffee
@@ -83,6 +83,7 @@
         unless stanza.attrs.from
             stanza.attrs.from = @jid

+        # Count serialized length (do not actually allocate yet)
         bytes = 0
         stanza.root().write (s) ->
             bytes += Buffer.byteLength(s)
@@ -90,12 +91,14 @@
             logger.warn "Stanza with #{bytes} bytes: #{stanza.toString().substr(0, 1024)}..."
             throw new errors.MaxStanzaSizeExceeded(bytes)

-        logger.trace ">> #{stanza.toString()}"
+        # Serialize once (for logging and sending)
+        s = stanza.toString()
+        logger.trace ">> #{s}"
         if stanza.attrs.to is @jid
             # Loopback short-cut
             @conn.emit 'stanza', stanza
         else
-            @conn.send stanza
+            @conn.send s

     ##
     # @param {Function} cb: Called with (errorStanza, resultStanza)


add_wordguess_chooseword_wordfragments

--- snippets/language-coursescript.cson
+++ snippets/language-coursescript.cson
@@ -341,3 +341,31 @@
         Pic(checked=true): $4
       TR: $5
     """
+  'WordGuess':
+    'prefix': 'wordguess'
+    'body': """
+      [TYPE WordGuess]
+      Audio: $1
+      Text: $2
+      TR: $3
+    """
+  'ChooseAllWords':
+    'prefix': 'chooseall'
+    'body': """
+      [TYPE ChooseAllWords]
+      Opts:
+        Text(checked=true): $1
+        Text(checked=true): $2
+        Text: $3
+        Text: $4
+        Text: $5
+        Text: $6
+    """
+  'WordFragments':
+    'prefix': 'wordfragmentsguess'
+    'body': """
+      [TYPE WordGuess]
+      Pic: $1
+      Audio: $2
+      Text: $3
+    """


use new textile parser option

use new textile parser option

--- js/weblog.coffee
+++ js/weblog.coffee
@@ -143,7 +143,7 @@
       contentElement = $("c#{id}")
       Xhr.load "articles/#{id}",
         onSuccess: (req)=>
-          @articles[id].content = textile(req.responseText)
+          @articles[id].content = textile(req.responseText, {breaks: false})
           contentElement.append @articles[id].content
           articleElement.removeClass('failure')
           articleElement.addClass('success')


bootstrapped bxUtil

bootstrapped bxUtil

--- bin/toolboxx.coffee
+++ bin/toolboxx.coffee
@@ -20,4 +20,5 @@
   'bxQueue'
   'bxSession'
   'bxSocket'
+  'bxUtil'
 ]


Remove `body` from keymap selector

Remove `body` from keymap selector

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -55,7 +55,7 @@
 '.github-Dialog input':
   'enter': 'core:confirm'

-'body .github-CommitView-coAuthorEditor':
+'.github-CommitView-coAuthorEditor':
   'enter': 'github:co-author:enter'
   'down': 'github:co-author:down'
   'up': 'github:co-author:up'


fix copy keybinding fixes #149

fix copy keybinding
fixes #149

--- keymaps/ink.cson
+++ keymaps/ink.cson
@@ -15,10 +15,10 @@
   'shift-space': 'unset!'

 '.platform-win32 .ink, .platform-linux .ink':
-  'ctrl-c': 'native!'
+  'ctrl-c': 'core:copy'

 '.platform-darwin .ink':
-  'cmd-c': 'native!'
+  'cmd-c': 'core:copy'

 'atom-text-editor:not([mini])':
   'escape': 'inline:clear-current'


Add debug logs for :nsis task

Add debug logs for :nsis task

--- tasks/pack.coffee
+++ tasks/pack.coffee
@@ -381,6 +381,7 @@

     # Create the installer
     (callback) ->
+      console.log 'creating installer'
       signParams = [
         '/t'
         'http://timestamp.verisign.com/scripts/timstamp.dll'
@@ -395,12 +396,16 @@
         .replace /%CHANNEL%/g, 'dev'
       releasesUrl = remoteReleasesUrl + '/RELEASES'

+      console.log 'creating check request'
       request {url: releasesUrl}, (err, res, body) ->
+        console.log 'request done'
+
         if err or not res or res.statusCode < 200 or res.statusCode >= 400
           console.log 'Creating installer without remote releases url', releasesUrl, 'because of',
             'error', err, 'statusCode', res and res.statusCode, 'body', res and res.body
           remoteReleasesUrl = undefined

+        console.log 'request ok, running winInstaller'
         winInstaller
           appDirectory: './build/win32'
           outputDirectory: './dist'
@@ -415,7 +420,12 @@
           setupExe: manifest.name + '-' + manifest.version + '-win32-setup-for-nsis.exe'
           noMsi: true
           arch: 'ia32'
-        .then callback, callback
+        .then ->
+          console.log 'winInstaller done'
+          callback()
+        .catch err ->
+          console.log 'winInstaller errored'
+          callback err

     # Run makensis
     applySpawn (process.env.MAKENSIS_PATH or 'makensis.exe'), ['build/resources/win/installer.nsi']


Remove hyperlink on ImageKey on the smug util pages

It's super annoying when trying to copypasta

--- _coffee/smugmug_api.coffee
+++ _coffee/smugmug_api.coffee
@@ -19,7 +19,7 @@
 fill_image_list = (images) ->
   ret = $("#smug-images").html()
   $(images).each (i, image) ->
-    ret += "
#{ i+1 }	#{ image["Title"] }	#{ image["FileName"] }	#{ image["ImageKey"] }
\n"
+    ret += "
#{ i+1 }	#{ image["Title"] }	#{ image["FileName"] }	#{ image["ImageKey"] }
\n"
   $("#smug-images").html(ret)

 fetch_album_list = (callback) ->


add dependency on other bindings defined on the same element

--- js/libs/custom-bindings.coffee
+++ js/libs/custom-bindings.coffee
@@ -32,7 +32,8 @@
 #   .item{data-bind: "text: $data"}
 # Approach here: https://github.com/knockout/knockout/wiki/Bindings%3A-runafter
 ko.bindingHandlers.equalizeWidth =
-  update: (el, valueAccessor) =>
+  update: (el, valueAccessor, allBindingsAccessor) =>
+    ko.toJS(allBindingsAccessor()) # wait for other bindings to finish
     $(el).css('visibility', 'hidden')
     setTimeout () ->
       selector = valueAccessor()


Fix issue #245

Fix issue #245

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -380,6 +380,7 @@
   execute: (count=1) ->
     _.times count, =>
       @editor.moveCursorToEndOfLine()
+      @editor.moveCursorLeft()

   select: (count=1) ->
     _.times count, =>


:bug: Stop flickering when adjusting margins

--- lib/suggestion-list-element.coffee
+++ lib/suggestion-list-element.coffee
@@ -252,11 +252,9 @@
     @uiProps.itemHeight ?= @selectedLi.offsetHeight
     @uiProps.paddingHeight ?= (parseInt(getComputedStyle(this)['padding-top']) + parseInt(getComputedStyle(this)['padding-bottom'])) ? 0

-    if atom.views.documentReadInProgress?
-      # Run in atom >= 0.193. Will batch write updates and run them immediately after this read
-      atom.views.updateDocument @updateUIForChangedProps.bind(this)
-    else
-      @updateUIForChangedProps()
+    # Update UI during this read, so that when polling the document the latest
+    # changes can be picked up.
+    @updateUIForChangedProps()

   updateUIForChangedProps: ->
     @scroller.style['max-height'] = "#{@maxVisibleSuggestions * @uiProps.itemHeight + @uiProps.paddingHeight}px"


No need to boot karma.

No need to boot karma.

--- test/unit/common/roman.service.spec.coffee
+++ test/unit/common/roman.service.spec.coffee
@@ -1,26 +1,24 @@
-`import "../helpers/boot.js"`
 `import chai, { expect } from "chai"`

 `import Roman from "src/common/roman.service.coffee"`

-# describe 'Unit Testing the Roman Service', ->
-#   describe 'romanize', ->
-#     it 'should convert a string or an integer to a roman numeral', ->
-#       ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
-#
-#       TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']
-#
-#       HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']
-#
-#       THOUSANDS = [1..10].map((i) -> new Array(i).join('M'))
-#
-#       for m in [0...THOUSANDS.length]
-#         for c in [0...HUNDREDS.length]
-#           for x in [0...TENS.length]
-#             for i in [0...ONES.length]
-#               n = m * 1000 + c * 100 + x * 10 + i
-#               if n
-#                 expected = THOUSANDS[m] + HUNDREDS[c] + TENS[x] + ONES[i]
-#                 actual = Roman.romanize(n)
-#                 expect(actual, "Romanized value for #{ n } is incorrect")
-#                   .to.equal(expected)
+describe 'Unit Testing the Roman Service', ->
+  it 'should convert a string or an integer to a roman numeral', ->
+    ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
+
+    TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']
+
+    HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']
+
+    THOUSANDS = [1..10].map((i) -> new Array(i).join('M'))
+
+    for m in [0...THOUSANDS.length]
+      for c in [0...HUNDREDS.length]
+        for x in [0...TENS.length]
+          for i in [0...ONES.length]
+            n = m * 1000 + c * 100 + x * 10 + i
+            if n
+              expected = THOUSANDS[m] + HUNDREDS[c] + TENS[x] + ONES[i]
+              actual = Roman.romanize(n)
+              expect(actual, "Romanized value for #{ n } is incorrect")
+                .to.equal(expected)


Adding tests demonstrating laziness.

Adding tests demonstrating laziness.

--- tests/batman/property/property_test.coffee
+++ tests/batman/property/property_test.coffee
@@ -44,6 +44,67 @@
   @object.get('foo')

   deepEqual @object.property('foo').sources.toArray(), []
+
+
+QUnit.module 'Batman.Property laziness'
+  setup: ->
+    @class = class extends Batman.Object
+    @object = new @class
+
+test "accessing a property with a cached value should not call the accessor", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').cached = yes
+  @object.property('foo').value = 12345
+
+  equal @object.get('foo'), 12345
+  equal spy.callCount, 0
+
+test "observing a property should call the accessor to populate its sources", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.observe 'foo', ->
+
+  equal spy.callCount, 1
+
+test "observing a property should not call the accessor if it has sources", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').sources = yes
+  @object.observe 'foo', ->
+
+  equal spy.callCount, 0
+
+test "observed properties should not call the accessor when cached", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').cached = yes
+  @object.property('foo').value = 'yup'
+  @object.property('foo').sources = new Batman.SimpleSet()
+  @object.observe 'foo', ->
+
+  equal spy.callCount, 0
+  @object.property('foo').fire()
+  equal spy.callCount, 0
+
+test "observed properties should call the accessor when changed", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').sources = new Batman.SimpleSet()
+  @object.observe 'foo', ->
+
+  equal spy.callCount, 0
+  @object.set 'foo', 12345
+  equal spy.callCount, 1
+
+test "setting a source property should not call the dependent accessor", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').sources = new Batman.SimpleSet(@object.property('bar'))
+
+  @object.set('bar', 12345)
+  equal spy.callCount, 0
+
+test "unsetting a source property should not call the dependent accessor", ->
+  @class.accessor 'foo', (spy = createSpy())
+  @object.property('foo').sources = new Batman.SimpleSet(@object.property('bar'))
+
+  @object.unset('bar', 12345)
+  equal spy.callCount, 0

 QUnit.module 'Batman.Property',


Add missing "new" operator to create Script objects

--- lib/cache.coffee
+++ lib/cache.coffee
@@ -142,7 +142,7 @@
         for r in res
           provisions[r._id] = {}
           provisions[r._id].md5 = crypto.createHash('md5').update(r.script).digest('hex')
-          provisions[r._id].script = vm.Script(r.script, {filename: r._id, timeout: 50})
+          provisions[r._id].script = new vm.Script(r.script, {filename: r._id, timeout: 50})

         if -- counter == 0
           computeHash()
@@ -163,7 +163,7 @@
         for r in res
           virtualParameters[r._id] = {}
           virtualParameters[r._id].md5 = crypto.createHash('md5').update(r.script).digest('hex')
-          virtualParameters[r._id].script = vm.Script(r.script, {filename: r._id, timeout: 50})
+          virtualParameters[r._id].script = new vm.Script(r.script, {filename: r._id, timeout: 50})

         if -- counter == 0
           computeHash()


タスク名を変更

タスク名を変更

--- lib/index.coffee
+++ lib/index.coffee
@@ -17,12 +17,10 @@

 module.exports = class Lamjet
   @setup: (gulp)->
-    # TODO: 「clean」に名称変更する。
-    gulp.task "myclean", (callback)->
+    gulp.task "clean", (callback)->
       del(["./out", "./out.zip"], callback)

-    # TODO: 「build」に名称変更する。
-    gulp.task "mybuild", ->
+    gulp.task "compile", ->
       gulp.src("./src/**/*.coffee")
         .pipe(coffee()).on("error", gutil.log)
         .pipe(gulp.dest("./out"))
@@ -42,8 +40,8 @@

     gulp.task "build-zip", (callback)->
       return runSequence(
-        ["myclean"],
-        ["mybuild"],
+        ["clean"],
+        ["compile"],
         ["copy-package-json"],
         ["install-dependencies"],
         ["archive-to-zip"],


clarify comment on piping

clarify comment on piping

--- lib/index.coffee
+++ lib/index.coffee
@@ -59,8 +59,9 @@
             .filter((obj) -> options.type is 'full' or type is options.type)
             .map((obj) -> if type is 'left' then [obj, null] else [null, obj])
             # Don't just pipe into out because we also pipe into out later and node streams don't
-            # handle multiple sources for a stream very well - see understream.combine for how it
-            # would need to be handled
+            # handle multiple sources for a stream very well. See understream.combine for how it
+            # would need to be handled if we wanted to do it (which we don't because it adds
+            # unnecessary complexity)
             .each((obj) -> out.push obj)
             .run cb_p
         (cb_p) ->


Fixed naming in lib wrapper

Fixed naming in lib wrapper

--- lib/index.coffee
+++ lib/index.coffee
@@ -2,4 +2,4 @@
   'Frame': require './frame'
   'Client': require './client'
   'Server': require './server'
-  'code': require './code'
+  'codes': require './codes'


Add description for CSV default settings

--- lib/tablr.coffee
+++ lib/tablr.coffee
@@ -100,22 +100,27 @@
       properties:
         columnDelimiter:
           title: 'Default Column Delimiter'
+          description: 'The default column delimiter to use when opening a CSV for the first time. You can write space characters code such as `\\t` instead of using the proper character.'
           type: 'string'
           default: ','
         rowDelimiter:
           title: 'Default Row Delimiter'
+          description: 'The default row delimiter to use when opening a CSV for the first time. You can write space characters code such as `\\r` instead of using the proper character. The `auto` value will let the CSV parser determine the proper separator to use depending on the file\'s content.'
           type: 'string'
           default: 'auto'
         quote:
           title: 'Default Quote Character'
+          description: 'The default quote character for quoted content.'
           type: 'string'
           default: '"'
         escape:
           title: 'Default Espace Character'
+          description: 'The default escape character for escaped quotes in quoted content.'
           type: 'string'
           default: '"'
         comment:
           title: 'Default Comment Character'
+          description: 'The default character that indicate a comment. Everything past this character in a line will be ignored. You can set it to `none` to disable the use of comments.'
           type: 'string'
           default: '#'


:bug: Prevent errors raised when opening a context menu during an edit

Fixes #7

--- lib/tablr.coffee
+++ lib/tablr.coffee
@@ -113,6 +113,7 @@
         ]
         created: (event) ->
           {pageX, pageY, target} = event
+          return unless target.getScreenColumnIndexAtPixelPosition?
           target.targetColumnForAlignment = target.getScreenColumnIndexAtPixelPosition(pageX, pageY)
           setTimeout (-> delete target.targetColumnForAlignment), 10
       }]


document tabs_view

document tabs_view

--- app/views/tabs_view.coffee
+++ app/views/tabs_view.coffee
@@ -1,5 +1,4 @@
-# The **TabsView** renders the tab subviews and handles
-# switching between them.
+# The **TabsView** renders the tab subviews and handles switching between them.

 module.exports = class TabsView extends Backbone.View

@@ -14,7 +13,7 @@
       .append(app.playlistNewView.render().el)
     this

-  # Switch to the requested tab (called from `BannerView`).
+  # Switch to the selected tab (called from `BannerView`).
   switchTo: (tab) ->
     @$('.tab').hide()
     switch tab


rename var, minor

rename var, minor

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -458,9 +458,9 @@
       startRow <= bufferRow <= endRow

 getBufferRangeForRowRange = (editor, rowRange) ->
-  [rangeStart, rangeEnd] = rowRange.map (row) ->
+  [startRange, endRange] = rowRange.map (row) ->
     editor.bufferRangeForBufferRow(row, includeNewline: true)
-  rangeStart.union(rangeEnd)
+  startRange.union(endRange)

 getTokenizedLineForRow = (editor, row) ->
   editor.tokenizedBuffer.tokenizedLineForRow(row)


Tweak

Tweak

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -132,7 +132,7 @@
   ##############################

   # @private
-  @dataId: (data) -> return if (data instanceof Backbone.Model) or _.isObject(data) then data.id else data
+  @dataId: (data) -> return if _.isObject(data) then data.id else data

   @dataIsSameModel: (data1, data2) ->
     return Utils.dataId(data1) is Utils.dataId(data2) if Utils.dataId(data1) or Utils.dataId(data2)


OPCE19: bug fixing, all operations of merging working with quill.js

TODO: implement functionality to support removing style within a selection

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -77,6 +77,23 @@
   # Get index of word in rte list
   index: ->
     return @rte._rte.words.indexOf @
+
+  getSelections: ->
+    uniq = (value, index, self) ->
+      self.indexOf(value) == index and value != @
+
+    ret = []
+    if @left and @left.left
+      ret.append(@left.left)
+    if @left and @left.right
+      ret.append(@left.left)
+    if @right and @right.right
+      ret.append(@right.right)
+    if @right and @right.left
+      ret.append(@right.left)
+
+    ret.filter uniq # return a value with unique elements
+

 # A class describing a selection with a style (bold, italic, …)
@@ -111,10 +128,9 @@
       @right = @rte.getWord retEnd.word
       @rightPos = retEnd.pos

-      @left.left.push @
-      @right.right.push @
-
-      @rte._rte.selections.push @
+      @bind @left, @right
+
+      @rte.pushSel @

     else throw new Error "Wrong set of parameters
@@ -196,8 +212,7 @@
   #
   # @param [Selection] selection the selection to compare to this
   atLeftOf: (selection) ->
-     @gt(selection.left, selection.leftPos, "right") and
-     @lt(selection.right, selection.rightPos, "left")
+     @gt(selection.left, selection.leftPos, "right")

   # Set the style to style
   #
@@ -209,7 +224,7 @@
     @lt(@right, @rightPos, "left")

-  # Try to merge the given selection with this selection
+  # Try to merge the given selection with this selection, keeping this selection
   #
   # @param [Selection] selection the selection to merge to
   #
@@ -223,44 +238,36 @@
       return                    # nothing to do

     # if they have two styles that differ
-    for key, val of @style
-      if not selection.style.hasOwnProperty(key)
+    keys = _.keys(@style)
+    for key in keys
+      if not(_.has(selection.style, key))
         return
-    for key, val of selection.style
-      if not @style.hasOwnProperty(key)
-        return
+    selection.setStyle @style
+
+    selToRemove = @
+    selToKeep = selection

     if @atLeftOf selection # remove @
-      selToRemove = @
-      selToKeep = selection
       left = @left
       leftPos = @leftPos
       right = selection.right
       rightPos = selection.rightPos
     else if selection.atLeftOf @ # remove selection
-      selToRemove = selection
-      selToKeep = @
       left = selection.left
       leftPos = selection.leftPos
       right = @right
       rightPos = selection.rightPos
     else if @.in selection # remove @
-      selToRemove = @
-      selToKeep = selection
       left = selection.left
       leftPos = selection.leftPos
       right = selection.right
       rightPos = selection.rightPos
     else if @equals selection # remove @
-      selToRemove = @
-      selToKeep = selection
       left = selection.left
       leftPos = selection.leftPos
       right = selection.right
       rightPos = selection.rightPos
     else if selection.in @ # remove selection
-      selToRemove = selection
-      selToKeep = @
       left = @left
       leftPos = @leftPos
       right = @right
@@ -282,9 +289,8 @@

   # Unbind selection from word
   unbind: ->
-    if @left and @right
-      @left.removeSel @, "left"
-      @right.removeSel @, "right"
+    @left.removeSel @, "left"
+    @right.removeSel @, "right"

     @left = null
     @right = null
@@ -347,7 +353,7 @@
       # reset styles when replacing content
       @_rte.words = []
       @_rte.style = []
-      @push(content)
+      @push content
     else
       # TODO: support breaks (br, new paragraph, …)
       (e.word for e in @_rte.words).join('')
@@ -444,7 +450,7 @@
   # be done with the word at right (if any)
   #
   merge: (index) ->
-    if 0 <= index < @_rte.words.length
+    if 0 <= index < @getWords(0).length
       word = @getWord(index).word.trimRight()
       @deleteWords index
       pos = absoluteFromRelative index, 0, @
@@ -547,13 +553,13 @@
           pos -= jump
     else if jump > 0
       while jump > 0
-        delta = pos + jump - @_rte.word[word].length + 1
+        delta = pos + jump - @getWord(word).word.length + 1
         if delta > 0
           word += 1
           jump -= delta
           pos = 0

-          if word >= @_rte.words.length
+          if word >= @getWords(0).length
             return null
         else
           pos += jump
@@ -568,19 +574,19 @@
     if not selection.isValid()
       throw new Error "Invalid selection, got", selection

-    selection.style = style or {}
+    selection.setStyle (style or {})

     # Link the boundary words to selection
     leftWord = selection.left or {right: []}
     rightWord = selection.right or {left: []}

     # Merge left…
-    for tmpSelection in leftWord.right when tmpSelection
-      tmpSelection.merge selection
+    for tmpSelection in leftWord.right when tmpSelection and tmpSelection != selection
+      selection.merge tmpSelection
     # …and right (only happens when selections are contiguous or overlapping
     # and have same style)
-    for tmpSelection in rightWord.left when tmpSelection
-      tmpSelection.merge selection
+    for tmpSelection in rightWord.left when tmpSelection and tmpSelection != selection
+      selection.merge tmpSelection

   # Apply a delta to the object
   # @see http://quilljs.com/docs/deltas/
@@ -596,6 +602,8 @@
       else if delta.delete?
         selection = new Selection position, (position + delta.delete), @
         @deleteSel selection
+        selection.unbind()
+        @removeSel selection

       else if delta.insert?
         @insert position, delta.insert
@@ -609,27 +617,18 @@
       if delta.attributes?
         @setStyle selection, delta.attributes

-      selectionList = @getSelections()
-      for sel in selectionList
-        sel.merge selection
+        selectionList = @getSelections()
+        for sel in selectionList when sel != selection and sel
+          sel.merge selection
+
+  # Add a  selection to the selection list
+  pushSel: (selection)->
+    @_rte.selections.push selection

   # Return all the selections bound to left and right, except self
   # @return [Array<Selection>] an array of selection
   getSelections: ->
-    uniq = (value, index, self) ->
-      self.indexOf(value) == index and value != @
-
-    ret = []
-    if @left and @left.left
-      ret.append(@left.left)
-    if @left and @left.right
-      ret.append(@left.left)
-    if @right and @right.right
-      ret.append(@right.right)
-    if @right and @right.left
-      ret.append(@right.left)
-
-    ret.filter uniq # return a value with unique elements
+    @_rte.selections or []

   # Remove a selection from selection list
   #
@@ -642,14 +641,10 @@
         array.splice index, 1
         break

-    # unbind selection
-    selection.unbind()
-
   garbageCollect: ->
     sels = @_rte.selections
     for index in [0..sels.length-1]
       for key, value of sels[index]
-        console.log sels[index], "has", key, value, "deleting!"
         if value == null or value == ""
           sels[index].unbind()
           @removeSel sels[index]


Fixed issue where initializeHotkeys wasn't called before actions were initialized

--- ribs.coffee
+++ ribs.coffee
@@ -3,6 +3,11 @@
     # Make `Ribs` globally accessible.
     root = window ? module.exports
     root.Ribs = {}
+
+    _keyboardManager = null
+
+    Ribs.getKeyboardManager = ->
+        _keyboardManager =? new Ribs.KeyboardManager()

     # `Ribs.List` is the primary Ribs component.
     class Ribs.List extends Backbone.View
@@ -50,12 +55,11 @@

             # Lazily construct global keyboard manager
-            Ribs.keyboardManager ?= new Ribs.KeyboardManager()
-            @keyboardManager = Ribs.keyboardManager
+            @keyboardManager = Ribs.getKeyboardManager()
+
+            @initializeHotKeys()

             super
-
-            @initializeHotKeys()

             @$el.addClass('ribs')

@@ -975,3 +979,4 @@
                         ul.append li
                     @$el.append h1, ul

+


IDE: ensureReadme: for softlayer vms always show README.md

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -466,6 +466,20 @@
     @activeTabView.emit 'MachineWebPageRequested', machineData

+  ensureReadme: (snapshot) ->
+
+    layout     = IDELayoutManager.convertSnapshotToFlatArray snapshot
+    readmePath = "/home/#{@mountedMachine.getOwner()}/README.md"
+
+    return @openReadme()  if layout.length is 0
+
+    hasReadme  = (layout.filter ({context}) ->
+      path = context.file?.path
+      path and FSHelper.plainPath(path) is readmePath).length > 0
+
+    @openReadme()  unless hasReadme
+
+
   openReadme: (machine) ->

     machine or= @mountedMachine
@@ -544,6 +558,9 @@
         else

           @fetchSnapshot (snapshot) =>
+
+            if @mountedMachine.provider is 'softlayer'
+              @ensureReadme snapshot

             # Just resurrect snapshot for host or without collaboration.
             # Because we need check the `@myWatchMap` and it is not possible here.


test: Add test case to correct for additional SPSSODescriptors

When we upgraded to xmlbuilder2, the method we were using to create XML
documents slightly changed. See 57b1486fffa651344fdf1bcecf5750cada0b42d4 and
c910edbb63c6057d8680fa96cddbd8dd8a75f93f for more context.

This change explicitly checks for the amount of nodes that refer to
SPSSODescriptors. Before we upgraded xmlbuilder, there should only be one. When
we upgraded to xmlbuilder2, it registered 5. With this change to
create_metadata, it is now 1 again.

--- test/saml2.coffee
+++ test/saml2.coffee
@@ -134,6 +134,13 @@
         assert(
           has_attribute logout_service, 'Location', 'https://sp.example.com/assert',
           "Expected to find an SingleLogoutService with location 'htps://sp.example.com/assert'")
+
+      it 'contains only one SPSSODescriptor', ->
+        sp_sso_descriptor = entity_descriptor.getElementsByTagNameNS(
+          'urn:oasis:names:tc:SAML:2.0:metadata', 'SPSSODescriptor')
+
+        assert.equal(
+          sp_sso_descriptor.length, 1, "Expected 1 SP SSO descriptor; found #{sp_sso_descriptor.length}")

     describe 'format_pem', ->
       it 'formats an unformatted private key', ->


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -105,10 +105,10 @@
         r = labelRadius
         if x <= 0
             r = -r
-        #return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"
+        return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"
         #return "translate(" + r + "," + y + ")"#(y/h * labelRadius) + ")"
         #return "translate(" + ((x/h * labelRadius)+r/4) + "," + y*my + ")"#(y/h * labelRadius) + ")"
-        return "translate(" + (x+r/2)*mx + "," + y*my + ")"
+        #return "translate(" + (x+r/2)*mx + "," + y*my + ")"
         )

         .append("svg:tspan").attr("class","cusipie-label")


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,9 +2,8 @@

     @accessor 'data'

-    @oldPieData = ''
-
     ready: ->
+        window.oldPieData = ''

     onData: (data) ->
         #$(@node).fadeOut().fadeIn()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/state.coffee
+++ test/state.coffee
@@ -1,7 +1,7 @@
 test "State events should check if event can be fired", ->
   car = {}

-  sm = StateMachine 'state', car, { initial: 'parked' }, (machine) ->
+  StateMachine 'state', car, { initial: 'parked' }, (machine) ->
     machine.event 'start', (event) ->
       event.transition({ from: 'parked', to: 'idling' })


Update event-based-data-object.coffee

Update event-based-data-object.coffee

--- lib/event-based-data-object.coffee
+++ lib/event-based-data-object.coffee
@@ -12,6 +12,9 @@

     onUpdate: (callback) ->
         @emitter.on 'update', callback
+
+    #addResaulsUpdateListener
+    #addDataSourceUpdateListener

         ###
   https://www.npmjs.com/package/event-kit


#22 clean language helper module.

#22 clean language helper module.

--- lib/helpers/language_helper.coffee
+++ lib/helpers/language_helper.coffee
@@ -1,58 +1,52 @@
 #global $, _
-((NS) ->
-  # Filename: helpers/culture_helper.coffee
-  "use strict"
-  NS = NS or {}
-  isInBrowser = typeof module is 'undefined' and typeof window isnt 'undefined'
-  ArgumentNullException = if isInBrowser then NS.Helpers.Exceptions.ArgumentNullException else require("./custom_exception").ArgumentNullException
-  ArgumentInvalidException= if isInBrowser then NS.Helpers.Exceptions.ArgumentInvalidException else require("./custom_exception").ArgumentInvalidException
-  languagesOptions = {
-    'fr-FR':{},
-    'en-GB':{}
-  }
-
-  #Define or extend a language in the application.
-  defineLanguage =(cultureCode, languageOptions, options) ->
-    options = options or {}
-    isExtend = options.isExtend or false
-    if not cultureCode?
-      throw new ArgumentNullException("cultureCode", cultureCode)
-    if not _.isString(cultureCode)?
-      throw new ArgumentInvalidException("cultureCode should be a string.", cultureCode)
-    if not languageOptions?
-      throw new ArgumentNullException("cultureCode", languageOptions)
-    if not _.isObject(languageOptions)?
-      throw new ArgumentInvalidException("languageOptions should be an object.", languageOptions)
-    if(not isExtend or not languageOptions[cultureCode]?)
-      languagesOptions[cultureCode] =  languageOptions
-    else
-      _.extend(languagesOptions[cultureCode], languageOptions)
+# Filename: helpers/language_helper.coffee
+"use strict"
+NS = NS or {}
+isInBrowser = typeof module is 'undefined' and typeof window isnt 'undefined'
+ArgumentNullException = if isInBrowser then NS.Helpers.Exceptions.ArgumentNullException else require("./custom_exception").ArgumentNullException
+ArgumentInvalidException= if isInBrowser then NS.Helpers.Exceptions.ArgumentInvalidException else require("./custom_exception").ArgumentInvalidException
+languagesOptions = {
+  'fr-FR':{},
+  'en-GB':{}
+}

-  #Define many languages in the application.
-  defineLanguages = (languages, options)->
-    if not _.isArray(languages)
-      throw new ArgumentInvalidException("Languages should be an array", languages)
-    defineLanguage(language.culture, language.options) for language in languages
-
-  # Get an array with all the application languages defined.
-  getCultures = ->
-    return _.keys(languagesOptions)
-
-  # Get the language information.
-  getLanguage = (cultureCode)->
-    if languagesOptions[cultureCode]?
-      return languagesOptions[cultureCode]
-    return undefined
+#Define or extend a language in the application.
+defineLanguage =(cultureCode, languageOptions, options) ->
+  options = options or {}
+  isExtend = options.isExtend or false
+  if not cultureCode?
+    throw new ArgumentNullException("cultureCode", cultureCode)
+  if not _.isString(cultureCode)?
+    throw new ArgumentInvalidException("cultureCode should be a string.", cultureCode)
+  if not languageOptions?
+    throw new ArgumentNullException("cultureCode", languageOptions)
+  if not _.isObject(languageOptions)?
+    throw new ArgumentInvalidException("languageOptions should be an object.", languageOptions)
+  if(not isExtend or not languageOptions[cultureCode]?)
+    languagesOptions[cultureCode] =  languageOptions
+  else
+    _.extend(languagesOptions[cultureCode], languageOptions)

-  mod = {
-    "getCultures": getCultures,
-    "defineLanguage": defineLanguage,
-    "defineLanguages": defineLanguages,
-    "getLanguage":getLanguage
-  }
-  if isInBrowser
-    NS.Helpers = NS.Helpers or {}
-    NS.Helpers.languageHelper = mod
-  else
-    module.exports = mod
-)(if typeof module is 'undefined' and typeof window isnt 'undefined' then window.Fmk else module.exports)+#Define many languages in the application.
+defineLanguages = (languages, options)->
+  if not _.isArray(languages)
+    throw new ArgumentInvalidException("Languages should be an array", languages)
+  defineLanguage(language.culture, language.options) for language in languages
+
+# Get an array with all the application languages defined.
+getCultures = ->
+  return _.keys(languagesOptions)
+
+# Get the language information.
+getLanguage = (cultureCode)->
+  if languagesOptions[cultureCode]?
+    return languagesOptions[cultureCode]
+  return undefined
+
+mod = {
+  "getCultures": getCultures,
+  "defineLanguage": defineLanguage,
+  "defineLanguages": defineLanguages,
+  "getLanguage":getLanguage
+}
+module.exports = mod


Detecting font load in CSS impl improved

--- impl/css/level0/loader/font.coffee
+++ impl/css/level0/loader/font.coffee
@@ -4,7 +4,43 @@

 log = log.scope 'Renderer', 'FontLoader'

+SAMPLE_TEXT = 'ABC¬Ō∑ę†ī¨^óĻ„‚ąś∂ń©ķ∆Żł…ĺ«żźć√ļńĶ≤≥÷Ń™€ßį§¶•Ľľ–≠ŕŘ‹›řŖŗ°Š‚—Īő'
+
 module.exports = (impl) ->
+	implUtils = impl.utils
+
+	detectLoad = (name) ->
+		elem1 = document.createElement 'span'
+		elem1.textContent = SAMPLE_TEXT
+		elem1.style.fontFamily = "sans-serif"
+		impl._hatchery.appendChild elem1
+
+		elem2 = document.createElement 'span'
+		elem2.textContent = SAMPLE_TEXT
+		elem2.style.fontFamily = "#{name}, sans-serif"
+		impl._hatchery.appendChild elem2
+
+		sync = ->
+			if elem1.offsetWidth isnt elem2.offsetWidth or implUtils.loadedFonts[name]
+				impl._hatchery.removeChild elem1
+				impl._hatchery.removeChild elem2
+				markAsLoaded name
+			else
+				requestAnimationFrame sync
+
+		requestAnimationFrame sync
+
+	emitLoadedSignal = (name) ->
+		unless implUtils.loadedFonts[name]
+			implUtils.onFontLoaded.emit name
+
+	markAsLoaded = (name) ->
+		unless implUtils.loadedFonts[name]
+			implUtils.loadedFonts[name] = true
+			implUtils.loadingFonts[name] = 0
+			implUtils.onFontLoaded.emit name
+		return
+
 	loadFont: (name, source, sources) ->
 		urlStr = ''
 		for source in sources
@@ -21,31 +57,30 @@
 			}
 		"""

-		impl.utils.loadingFonts[name] ?= 0
-		impl.utils.loadingFonts[name]++
+		implUtils.loadingFonts[name] ?= 0
+		implUtils.loadingFonts[name]++

 		append = ->
 			document.body.appendChild styles
+			detectLoad name

 			xhr = new XMLHttpRequest
 			xhr.open 'get', sources[0], true
 			xhr.onload = ->
-				if impl.utils.loadingFonts[name] is 1
+				if implUtils.loadingFonts[name] is 1
+					emitFontLoadedSignal = ->
+						emitLoadedSignal name
+					setTimeout emitFontLoadedSignal, 50
 					# probably
-					requestAnimationFrame ->
-						impl.utils.onFontLoaded.emit name
+					setTimeout emitFontLoadedSignal, 500
 					# maybe
-					setTimeout ->
-						impl.utils.onFontLoaded.emit name
-					, 1000
+					setTimeout emitFontLoadedSignal, 1000
 					# empty string
 					setTimeout ->
-						impl.utils.loadedFonts[name] = true
-						impl.utils.loadingFonts[name] = 0
-						impl.utils.onFontLoaded.emit name
+						markAsLoaded name
 					, 2000
 				else
-					impl.utils.loadingFonts[name]--
+					implUtils.loadingFonts[name]--
 			xhr.send()

 		if document.readyState isnt 'complete'


remove href of <a />

remove href of <a />

--- coffee/chosen.jquery.coffee
+++ coffee/chosen.jquery.coffee
@@ -272,7 +272,7 @@
     if item.disabled
       choice.addClass 'search-choice-disabled'
     else
-      close_link = $('<a />', { href: '#', class: 'search-choice-close', 'data-option-array-index': item.array_index })
+      close_link = $('<a />', { class: 'search-choice-close', 'data-option-array-index': item.array_index })
       close_link.click (evt) => this.choice_destroy_link_click(evt)
       choice.append close_link


vm: code style

vm: code style

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -96,7 +96,6 @@
             return acc
         , @getUsageTemplate()

-
   @calculateUsage$ = permit 'list all vms',
     success: (client, groupSlug, callback)->
       {delegate} = client.connection


document the sweet sweet danger room

document the sweet sweet danger room

--- test/tests.coffee
+++ test/tests.coffee
@@ -50,6 +50,7 @@
     super new Robot.Message(user, text)

 class Helper.Response extends Robot.Response
+  # This changes ever HTTP request to hit the danger server above
   http: (url) ->
     super(url).host('127.0.0.1').port(9001)


Fix #1

Fix #1

--- lib/Meaning.Credentials.coffee
+++ lib/Meaning.Credentials.coffee
@@ -16,7 +16,7 @@
   uri         = existsDefault options.uri, DEFAULT.URI
   secure      = existsDefault options.secure, DEFAULT.SECURE

-  credentials.url = DEFAULT.SECURE_MODES[secure]
+  credentials.url = DEFAULT.SECURE_MODE[secure]
   credentials.url += "://#{uri}"

   credentials


Fix broken test after inicndet UI updates

--- tests/features/step_definitions/curator_inbox_steps.coffee
+++ tests/features/step_definitions/curator_inbox_steps.coffee
@@ -52,4 +52,4 @@
       @client.waitForExist('.selectable-content')

     @Then /^I should see accepted or rejected incidents$/, ->
-      @client.waitForExist('.incident-table tbody tr')
+      @client.waitForExist('.incident-list li')


Hardcode #moocnantes hashtag

Hardcode #moocnantes hashtag

--- frontend/src/js/admin/admin.coffee
+++ frontend/src/js/admin/admin.coffee
@@ -116,7 +116,7 @@
                            $("#search", settings).val())
         else
             twitterFontana(transition: "scroll-down",
-                           'mooc')
+                           'moocnantes')

     isSignedOut = ->
         $(document.body).removeClass('signedIn')
@@ -129,7 +129,7 @@
             HTMLFontana()

     # Two Demo Fontanas
-    twitterFontana = (settings={}, q="mooc")->
+    twitterFontana = (settings={}, q="moocnantes")->
         if visualizer
             visualizer.stop()
         datasource = new Fontana.datasources.ProxyTwitterSearch(q, '/all')