Pull Field options from Form context

Pull Field options from Form context

--- lib/elements.coffee
+++ lib/elements.coffee
@@ -15,38 +15,44 @@
   contextTypes:
     onChange: React.PropTypes.func
     setMessage: React.PropTypes.func
-    schema: React.PropTypes.shape
-      interactive: React.PropTypes.bool
-      input: React.PropTypes.component
+    schema: React.PropTypes.object

   getInitialState: ->
     value: @props.value
     invalid: false
     showIndicator: false

+  getFieldSchema: ->
+    @context.schema[@props.name]
+
   onChange: (value) ->
     @hideError()
     @setState {value}
-    @validate value if @context.schema.interactive
+    @validate value if @getFieldSchema().interactive
     @context.onChange @props.name, value

-  onFocus: -> @hideError()
+  onFocus: ->
+    @hideError()

-  hideError: -> @setState showIndicator: false, invalid: false
+  hideError: ->
+    @setState showIndicator: false, invalid: false

   onBlur: ->
-    if @state.value and not @context.schema.interactive
+    if @state.value and not @getFieldSchema().interactive
       @validate @state.value

   validate: (value) ->
-    validate.validateField @props.fieldData, value, (message) =>
+    validate.validateField
+      name: @props.name
+      schema: @getFieldSchema()
+    , value, (message) =>
       # All interactive fields will show an indicator on input.
       @setState showIndicator: true, invalid: message?
       @context.setMessage @props.name, message

   render: ->
     # Default to StringInput if no Input was given in the schema.
-    input = @context.schema.input or inputs.StringInput
+    input = @getFieldSchema().input or inputs.StringInput

     return input
       value: @state.value
@@ -68,6 +74,9 @@
   render: ->
     React.DOM.div className: "error-message", @props.message

+###*
+ * Wraps a submit button or anything with an onClick callback.
+###
 Submit = React.createClass
   displayName: "FormSubmit"


Better support for $.ajax stubbing

Better support for $.ajax stubbing

- compatible with jQuery.noConflict()
- returned deferred object is now chainable like in real $.ajax()
- add optional support for legacy 'success', 'error' and 'complete'

--- lib/assets/javascripts/fake_ajax_server.js.coffee
+++ lib/assets/javascripts/fake_ajax_server.js.coffee
@@ -2,15 +2,15 @@
 # =require sinon

 @FakeAjaxServer = class FakeAjaxServer
-  constructor: (@fakeServerResponse, @imediateServe = false)->
+  constructor: (@fakeServerResponse, @options = {})->
   start: ->
     @ajaxCallbacksQueue = []
-    @ajaxStub = sinon.stub $, 'ajax', @fakeAjax
-  stop: -> $.ajax.restore()
+    @ajaxStub = sinon.stub jQuery, 'ajax', @fakeAjax
+  stop: -> jQuery.ajax.restore()
   fakeAjax: =>
-    responseStub = sinon.stub(new jQuery.Deferred)
+    responseStub = @createFakeJqXHR()
     @ajaxCallbacksQueue.push [arguments, responseStub]
-    @processAllRequests() if @imediateServe
+    @processAllRequests() if @options.imediateServe
     responseStub
   processNextRequest: -> @fakeServerResponse.apply this, @ajaxCallbacksQueue.shift()[0]
   processAllRequests: -> @processNextRequest() while @ajaxCallbacksQueue.length
@@ -19,3 +19,8 @@
   ajaxResponse: (index = 0)-> @ajaxCallbacksQueue[index][1]
   ajaxSettings: (index = 0)-> @ajaxCallbacksQueue[index][0][0]

+  createFakeJqXHR: ->
+    xhr = jQuery.Deferred()
+    (xhr[m] = (->) for m in ['error', 'success', 'complete']) if @options.legacySupport
+    sinon.stub xhr, m, (-> xhr) for m, f of xhr when typeof f is 'function'
+    xhr


Catch and report YAML parsing issues

Catch and report YAML parsing issues

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -387,9 +387,14 @@
   postDir = path.join options.source, '_posts'
   permalinks = {}
   for filename in fs.readdirSync postDir
-    console.log filename
     if match = filename.match postMask
-      { data, content, ext } = getDataAndContent path.join postDir, filename
+      try
+        { data, content, ext } = getDataAndContent path.join postDir, filename
+      catch err
+        console.error "Error while trying to read #{filename}:".red
+        console.error err.toString()
+        console.error "Skipping post #{filename}".yellow
+        continue

       post = {}
       post[key] = data[key] for key of data


Added Case methods as well as a helper method that handles options for all cases.

--- src/fogbugz.coffee
+++ src/fogbugz.coffee
@@ -138,7 +138,7 @@
 		if err then callback(new Error('Error making new case')) else callback(null)
 	)

-# Used by all cases, some have editional info for method
+# Used by all cases, some have additional info for method
 CaseOptions = (options) ->
 	appendOptions = ''
 	if(options['ixBug']) then appendOptions += "&ixBug=#{options['ixBug']}"


minor fix. should call super always

minor fix. should call super always

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -433,6 +433,7 @@
   wise: 'linewise'

   initialize: ->
+    super
     @target = 'MoveToRelativeLine' if @isMode('normal')
     if @isMode('visual', 'characterwise')
       @stayOnLinewise = false


Remove logging of platform

Remove logging of platform

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -128,7 +128,6 @@
   #
   # See https://code.google.com/p/chromium/issues/detail?id=51024
   # See https://bugs.webkit.org/show_bug.cgi?id=19906
-  console.log process.platform
   if process.platform is 'linux'
     switch charCode
       when 186 then charCode = 59 # ";"


Be explicit and set sanitize to false

Be explicit and set sanitize to false

--- lib/renderer.coffee
+++ lib/renderer.coffee
@@ -7,7 +7,9 @@

 exports.toHtml = (text, filePath, callback) ->
   roaster ?= require 'roaster'
-  options = breaks: atom.config.get('markdown-preview.breakOnSingleNewline')
+  options =
+    sanitize: false
+    breaks: atom.config.get('markdown-preview.breakOnSingleNewline')

   roaster text, options, (error, html) =>
     if error


OPCE6: Test for Word.index function

OPCE6: Test for Word.index function

--- test/rte-test.coffee
+++ test/rte-test.coffee
@@ -229,7 +229,7 @@
     word.left.length.should.equals 0
     word.right[0].equals(sel).should.be.true

-  it 'should remove selection correctly [removeSel]', ->
+  it 'remove selection correctly [removeSel]', ->
     rte = new Rte "This is a test"
     sel = new Selection 0, 6, rte
     word = rte.getWord(0)
@@ -238,3 +238,9 @@

     word = rte.getWord(1)
     word.right.length.should.equals 1
+
+  it 'be able to get its index in RTE instance [index]', ->
+    rte = new Rte "This is a test"
+    rte.val()
+    word = rte.getWord( 3)
+    word.index(rte).should.equal 3


update size on load

update size on load

--- application.coffee
+++ application.coffee
@@ -913,4 +913,5 @@
   if (handler = shortcuts[keyCode])?
     e.preventDefault()
     handler(e)
+updateCanvasSize()
 redraw()


enh: wider area for events

enh: wider area for events

--- application.coffee
+++ application.coffee
@@ -865,10 +865,13 @@
 E("btn-reset").addEventListener "click", doReset
 E("btn-step").addEventListener "click", doStep
 #E("canvas").addEventListener "click", doCanvasClick
-E("canvas").addEventListener "mousedown", doCanvasMouseDown
-E("canvas").addEventListener "mouseup", doCanvasMouseUp
-E("canvas").addEventListener "mousemove", doCanvasMouseMove
-E("canvas").addEventListener "mousedrag", doCanvasMouseMove
+#
+mouseMoveReceiver = E("canvas-container")
+mouseMoveReceiver.addEventListener "mousedown", doCanvasMouseDown
+mouseMoveReceiver.addEventListener "mouseup", doCanvasMouseUp
+mouseMoveReceiver.addEventListener "mousemove", doCanvasMouseMove
+mouseMoveReceiver.addEventListener "mousedrag", doCanvasMouseMove
+
 E("btn-set-rule").addEventListener "click", doSetRule
 E("btn-set-rule-generic").addEventListener "click", (e)->
   doSetRuleGeneric()


:memo: Tweak Git error message

:memo: Tweak Git error message

--- src/install.coffee
+++ src/install.coffee
@@ -176,7 +176,7 @@

       The #{pack.name} package has module dependencies that cannot be installed without Git.

-      You need install Git and make it available on your path environment variable in order to install this package.
+      You need to install Git and add it to your path environment variable in order to install this package.

     """


Print error on authentication failure (401)

--- src/jenkins.coffee
+++ src/jenkins.coffee
@@ -80,6 +80,8 @@
     req.get() (err, res, body) ->
         if err
           msg.send "Jenkins says: #{err}"
+        else if res.statusCode == 401
+          msg.send "Invalid credentials"
         else
           response = ""
           try
@@ -127,6 +129,8 @@
             req.get() (err, res, body) ->
                 if err
                   msg.send "Jenkins says: #{err}"
+                else if res.statusCode == 401
+                  msg.send "Invalid credentials"
                 else
                   response = ""
                   try
@@ -159,6 +163,8 @@
     req.get() (err, res, body) ->
         if err
           msg.send "Jenkins says: #{err}"
+        else if res.statusCode == 401
+          msg.send "Invalid credentials"
         else
           response = ""
           try
@@ -186,6 +192,8 @@
         response = ""
         if err
           msg.send "Jenkins says: #{err}"
+        else if res.statusCode == 401
+          msg.send "Invalid credentials"
         else
           try
             content = JSON.parse(body)


Improve error handling

Improve error handling

--- lib/uploader.coffee
+++ lib/uploader.coffee
@@ -6,9 +6,8 @@

 class Uploader extends EventEmitter
   #FIXME: 0 records?
-  constructor: ({accessKey, secretKey, region, stream, objectName, objectParams, bucket, partSize, maxBufferSize, verbose}, @cb) ->
+  constructor: ({accessKey, secretKey, region, stream, objectName, objectParams, bucket, partSize, maxBufferSize}, @cb) ->
     super()
-
     aws.config.update
       accessKeyId:     accessKey
       secretAccessKey: secretKey
@@ -19,11 +18,7 @@
     @objectParams.Bucket ?= bucket
     @objectParams.Key    ?= objectName

-    @maxBufferSize        = maxBufferSize # TODO
-    @partSize             = partSize or 5242880 # 5MB
-    @verbose             ?= false
-
-
+    @maxBufferSize = maxBufferSize # TODO

     if not @objectParams.Bucket then throw new Error "Bucket must be given"

@@ -35,12 +30,13 @@
     @partNumber      = 1
     @parts           = []
     @uploadedParts   = {}
+    @partSize        = partSize or 5242880 # 5MB
     @currentChunk    = new Buffer 0

     @on 'error', (err) => @failed = true

-    @setStreamHandlers stream
+    @handleStream stream
     process.nextTick => @initiateTransfer()

@@ -58,7 +54,7 @@
       @emit 'initiated', @uploadId

-  setStreamHandlers: (stream) ->
+  handleStream: (stream) ->
     stream.on 'data', (chunk) =>
       if typeof(chunk) is 'string' then chunk = new Buffer chunk, 'utf-8'
       @currentChunk = Buffer.concat [@currentChunk, chunk]
@@ -94,32 +90,44 @@
   uploadChunks: ->
     async.forEach @parts, (chunk, next) =>
       if chunk.progress or chunk.finished
-        return next()
+        next()
+      else
+        if not chunk.partNumber
+          chunk.partNumber = @partNumber
+          @partNumber += 1

-      currentPartNumber = @partNumber
+        chunk.progress = true

-      chunk.progress = true
-      @partNumber++
+        @client.uploadPart
+          Body:       chunk
+          Bucket:     @objectParams.Bucket
+          Key:        @objectName
+          PartNumber: chunk.partNumber.toString()
+          UploadId:   @uploadId
+        , (err, data) =>

-      @client.uploadPart
-        Body:       chunk
-        Bucket:     @objectParams.Bucket
-        Key:        @objectName
-        PartNumber: currentPartNumber.toString()
-        UploadId:   @uploadId
-      , (err, data) =>
-        chunk.progress = false
-        chunk.finished = true
+          if not chunk.progress
+            callbackCalled = true

-        @uploadedParts[currentPartNumber] = data?.ETag
+          chunk.progress = false
+          chunk.finished = if err then false else true

-        if err then @emit 'error', err
-        @emit 'uploaded', etag: data?.ETag
+          if err
+            if not callbackCalled
+              next err
+            else
+              console.error 'This callback was already called, WTF; chunk', chunk
+            @emit 'error', err
+          else
+            @uploadedParts[chunk.partNumber] = data.ETag

-        next err
+            @emit 'uploaded', etag: data.ETag
+
+            return next()

     , (err) =>
-      if err then console.error 'Upload failed', err
+      if err
+        console.error 'Cannot upload chunks', err
       @pruneParts()

@@ -142,8 +150,13 @@
       if @parts.length is 0
         @finishJob()
       else
-        process.stdout.write('W') if @verbose
-        setTimeout (=> @pruneParts()), 500
+        setTimeout (=>
+          # for race condition about failed parts
+          if @parts.length > 0
+            @uploadChunks()
+          else
+            @pruneParts()
+        ), 500

   finishJob: ->


Adds brewing & dibs commands to coffee.coffee and abstracts status-emoji

--- scripts/coffee.coffee
+++ scripts/coffee.coffee
@@ -1,14 +1,44 @@
 # Description:
-#   What do I need to make X cups of coffee?
+#   Kira Caffienation Support Tools
 #
 # Commands:
 #   hubot coffee [cups] - What you need to make [cups] cups of coffee.
+#   hubot brewing - Start a new brewing lineup
+#   hubot fresh pot - Announce that a pot is finished
+#   hubot fp - Alias for: hubot fresh pot
+#   hubot dibs - Reserve your spot in line for a current brew

 # water = 3/16 * cups
 # scoops = .75 * cups + 2

+StatusEmoji = require('./status-emoji')
+
+dibsLimit = process.env.HUBOT_COFFEE_DIBS_LIMIT || 1
+
+freshPots = [
+  "http://stream1.gifsoup.com/view6/3131142/chug-coffee-o.gif",
+  "http://a4.files.blazepress.com/image/upload/MTI4OTg3NDE5MTAwODc4MDk4.gif",
+  "http://38.media.tumblr.com/tumblr_m8wmtj5uqw1qzqwamo1_500.gif",
+  "https://images.encyclopediadramatica.se/5/51/I_dont_like_coffee.gif",
+  "http://spoonuniversity.com/wp-content/uploads/2015/07/anigif_enhanced-19737-1400112478-17.gif",
+  "http://i.huffpost.com/gadgets/slideshows/335861/slide_335861_3385763_free.gif",
+  "http://queenmobs.com/wp-content/uploads/2015/07/coffee-sylvester-cat.gif",
+  "http://www.meltingmama.net/.a/6a00d8345190c169e20192aa97b3c0970d-pi",
+  "https://s-media-cache-ak0.pinimg.com/originals/7c/f0/bc/7cf0bc80401a35b6413fcb7685e87bdc.jpg",
+  "http://ak-hdl.buzzfed.com/static/2015-07/7/13/enhanced/webdr12/anigif_enhanced-24754-1436291259-3.gif",
+  "http://i.imgur.com/vnuJQ4S.gif",
+  "http://25.media.tumblr.com/tumblr_m5bndgzQsI1qic1zfo1_500.gif",
+  "https://38.media.tumblr.com/tumblr_m5bnrxFuDj1qic1zfo1_500.gif",
+  "https://38.media.tumblr.com/tumblr_mash62P7pW1ro71p1o1_500.gif",
+  "https://thelordofthenerds.files.wordpress.com/2014/07/colbert-loves-coffee-o.gif",
+  "http://cdn1.theodysseyonline.com/files/2015/06/26/635709513164674737586848637_coffee-talk.gif",
+  "http://cdn1.theodysseyonline.com/files/2015/07/10/635720909538639483647877704_coffee-Elf.gif",
+]
+
 module.exports = (robot) ->
+  statusEmoji = new StatusEmoji
+
   robot.respond /coffee (\d+)$/i, (msg) ->
     cups = msg.match[1]

@@ -16,3 +46,75 @@
     scoops = (0.75 * cups) + 2

     msg.send "You need #{scoops} level scoops and #{water} L of water."
+
+  brewing = {}
+
+  robot.respond /brewing\s*$/i, (msg) ->
+    if isBrewing()
+      msg.send "A brew is already underway by (@#{brewing.barista})! #{statusEmoji.random('failure')}\n" +
+        freeSpots()
+      return
+
+    createBrew msg
+
+  robot.respond /(fresh[ -]pot|fp)\s*$/i, (msg) ->
+    return handleNoBrew msg if not isBrewing()
+
+    endBrew msg
+
+  robot.respond /dibs\s*$/i, (msg) ->
+    return handleNoBrew msg if not isBrewing()
+
+    dibber = robot.brain.usersForFuzzyName(msg.message.user['name'])[0].name
+
+    if dibber in brewing.dibs
+      msg.send "Nice try @#{dibber}, you already grabbed a spot! #{statusEmoji.random('failure')}"
+      return
+
+    if brewing.dibs.length >= dibsLimit
+      msg.send "Sorry! All spots have been claimed. #{statusEmoji.random('failure')}"
+      return
+
+    brewing.dibs.push(dibber)
+
+    threadedMsg(msg).send "Ok @#{dibber}, you grabbed :coffee: ##{brewing.dibs.length}! #{statusEmoji.random('success')}"
+
+  isBrewing = ->
+    return brewing.dibs?
+
+  threadedMsg = (msg) ->
+    # Ensure that the vote response sticks to the original thread
+    metadata = msg.envelope.metadata || {}
+    metadata.room = brewing.messageMetadata.room if brewing.messageMetadata.room
+    metadata.thread_id = brewing.messageMetadata.thread_id if brewing.messageMetadata.thread_id
+    metadata.message_id = brewing.messageMetadata.message_id if brewing.messageMetadata.message_id
+    msg.envelope.metadata = metadata
+    msg
+
+  handleNoBrew = (msg) ->
+    msg.send "There is no brew happening! #{statusEmoji.random('failure')}"
+
+  createBrew = (msg) ->
+    barista = robot.brain.usersForFuzzyName(msg.message.user['name'])[0].name
+    brewing =
+      barista: barista
+      dibs: [barista],
+      messageMetadata: msg.envelope.message.metadata,
+
+    msg.send "@team Brew started by @#{brewing.barista}! #{statusEmoji.random('success')}\n" +
+      "To grab a spot use: #{robot.alias}dibs\n" +
+      "To end the brew use: #{robot.alias}fresh pot"
+
+  endBrew = (msg) ->
+
+    teamNotify = brewing.dibs.length < dibsLimit
+
+    threadedMsg(msg).send "#{if teamNotify then '@team: ' else ''}Fresh Pot!!! #{msg.random freshPots}" +
+      "The dibbed spots are…\n" +
+      (":coffee: ##{(parseInt index)+1}: @#{dibber}#{if dibber == brewing.barista then ' (barista)' else ''}" for index, dibber of brewing.dibs).join("\n")
+
+    brewing = {}
+
+  freeSpots = ->
+    remaining = dibsLimit - brewing.dibs.length
+    "#{remaining} dib#{if remaining == 1 then '' else 's'}"


keep the logs clean corey

keep the logs clean corey

--- src/jscocoa.coffee
+++ src/jscocoa.coffee
@@ -5,7 +5,6 @@
   if not @outArgument2?
     OSX.JSCocoa.createClass_parentClass_('JSCocoaOutArgument2', 'JSCocoaOutArgument')

-  console.log(OSX.JSCocoaOutArgument2)
   o = OSX.JSCocoaOutArgument2.instance
   o.isOutArgument = true
   if args.length == 2


Add summation-summit for stimulating simulation

--- server/queues/scoring/getTwoGames.coffee
+++ server/queues/scoring/getTwoGames.coffee
@@ -80,7 +80,7 @@

 # Sampling by level: we pick a level, then find a human and ogre session for that level, one at random, one biased towards recent submissions.
 #ladderLevelIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span']  # Let's not give any extra simulations to old ladders.
-ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum', 'ace-of-coders', 'wakka-maul', 'power-peak', 'cross-bones', 'the-battle-of-sky-span']
+ladderLevelIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum', 'ace-of-coders', 'wakka-maul', 'power-peak', 'cross-bones', 'summation-summit', 'the-battle-of-sky-span']
 backgroundLadderLevelIDs = _.without ladderLevelIDs, 'zero-sum', 'ace-of-coders'
 sampleByLevel = (options, callback) ->
   levelID = options.levelID or _.sample(if options.background then backgroundLadderLevelIDs else ladderLevelIDs)


Add underscore as an allowed character

Add underscore as an allowed character

--- scripts/deploy.coffee
+++ scripts/deploy.coffee
@@ -1,5 +1,5 @@
 # Description:
-#  Kick of a jenkins deploy build
+#  Kick of a jenkins deploy build
 #
 # Commands:
 #   hubot deploy <target> - Deploy master to the given envirnonment
@@ -27,8 +27,8 @@

 module.exports = (robot) ->
-
-  robot.respond /deploy\s*([a-zA-Z0-9-/']+\s)(to\s+)?(.+)/i, (msg) ->
+
+  robot.respond /deploy\s*([_a-zA-Z0-9-/']+\s)(to\s+)?(.+)/i, (msg) ->
     branch     = msg.match[1] or null
     target     = msg.match[3] or null
     branch     = branch.replace /^\s+|\s+$/g, ""


haraheのsendは1回だけにする

haraheのsendは1回だけにする

メッセージの形式も変更

--- scripts/harahe.coffee
+++ scripts/harahe.coffee
@@ -32,10 +32,12 @@
           client.get req, (data, response) ->
             parseString data, (err, result) ->
               items = result['response']['rest'][0]
-              msg.send "Recommend: #{items['name']}"
-              msg.send "Category: #{items['category']}"
-              msg.send "Address: #{items['address']}"
-              msg.send "URL: #{items['url']}"
-              setTimeout (-> msg.send "Image: #{items['image_url'][0]['shop_image1']}"), 500
+              msg.send """
+              #{items['name']}
+
+              カテゴリ: #{items['category']}
+              住所: #{items['address']}
+              #{items['url']}
+              """
         catch
           msg.send "Not Found..."


Fixed access.

Fixed access.

--- iced/content_script.iced
+++ iced/content_script.iced
@@ -255,7 +255,7 @@
 # =======================================================

 createNewUser = (token, callback) ->
-    await $.get "#{ROOT_URL}accessToken=#{token}", defer data
+    await $.get "#{ROOT_URL}?accessToken=#{token}", defer data
     console.log "Create new json: #{data}"
     json = JSON.parse data
     callback json.id


Push logic ticks to scene.logic_ticks.

Push logic ticks to scene.logic_ticks.

--- engine/logic_block.coffee
+++ engine/logic_block.coffee
@@ -1,4 +1,5 @@

+#only tick must write in the scene
 class LogicBlock
     constructor: (@context, scene_name)->
         @objets = @context.objects
@@ -7,13 +8,10 @@

         @context.on_scene_ready scene_name, =>
             @init @context.scenes[scene_name]
-        @context.on_scene_ready scene_name, =>
-            @context.scenes[scene_name].pre_draw_callbacks.push(@sensors.bind(@))
-        @context.on_scene_ready scene_name, =>
-            @context.scenes[scene_name].pre_draw_callbacks.push(@actuators.bind(@))

-    init: (scene) => return
-    sensors: (scene, frame_duration)-> return
-    actuators: (scene, frame_duration)-> return
+        if @tick? then @context.on_scene_ready scene_name, =>
+            @context.scenes[scene_name].logic_ticks.push(@tick.bind(@))
+
+    init: (@scene)->

 module.exports = {LogicBlock}


Now it fully works

Now it fully works

--- app/config.coffee
+++ app/config.coffee
@@ -16,6 +16,7 @@
   onAuthentication: (accessToken, refreshToken, profile, done) =>
     debug 'Authenticated', accessToken
     profileId = profile?.id
+    fakeSecret = 'google-authenticator'
     authenticatorUuid = @meshbluJSON.uuid
     authenticatorName = @meshbluJSON.name
     deviceModel = new DeviceAuthenticator authenticatorUuid, authenticatorName, meshblu: @meshbluConn, meshbludb: @meshbludb
@@ -37,10 +38,10 @@
       if foundDevice
         foundDevice?.id = profileId
         return done null, foundDevice
-      deviceModel.create query, device, profileId, accessToken, deviceCreateCallback
+      deviceModel.create query, device, profileId, fakeSecret, deviceCreateCallback

     debug 'device query', query
-    deviceModel.findVerified query, accessToken, deviceFindCallback
+    deviceModel.findVerified query, fakeSecret, deviceFindCallback

   register: =>
     passport.use new GoogleStrategy googleOauthConfig, @onAuthentication


Fix returnToViewport support for FindMode

This was broken by the move to taking input in an iframe, since the
frontend was no longer getting keydown events for text changes, and so
the viewport wasn't being scrolled back to its original position until
the mode was exiting.

--- content_scripts/hud.coffee
+++ content_scripts/hud.coffee
@@ -35,6 +35,7 @@
     @hudUI.postMessage {name: "updateMatchesCount", matchCount, showMatchText}

   search: (data) ->
+    window.scrollTo findMode.scrollX, findMode.scrollY if findMode.options.returnToViewport
     findModeQuery.rawQuery = data.query
     updateFindModeQuery()
     performFindInPlace()


hide rollbar deploy logging

hide rollbar deploy logging

--- controllers/deploys.coffee
+++ controllers/deploys.coffee
@@ -30,7 +30,7 @@
       attr = _.clone req.query
       attr.teamId = team._id

-      rollbar.reportMessage("received modulus deploy hook: #{team.slug}")
+      # rollbar.reportMessage("received modulus deploy hook: #{team.slug}")

       # save the deploy in the db
       deploy = new Deploy attr


Add elixir packages

Add elixir packages

--- atom/packages.cson
+++ atom/packages.cson
@@ -3,6 +3,7 @@
   "atom-react-native-autocomplete"
   "auto-indent"
   "auto-update-packages"
+  "autocomplete-elixir"
   "autocomplete-modules"
   "emmet-simplified"
   "expand-region"
@@ -15,6 +16,7 @@
   "indent-guide-improved"
   "js-hyperclick"
   "language-babel"
+  "language-elixir"
   "language-javascript-jsx"
   "language-plantuml"
   "linter"


Add PHP class snippets

Add PHP class snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -334,6 +334,30 @@
     'prefix': 'f'
     'body': 'function ${1:fname}($2) {\n\t$3\n}'

+  'class':
+    'prefix': 'class'
+    'body': 'class ${1:Class} {\n\t$2\n}'
+
+  'constructor':
+    'prefix': 'ctor'
+    'body': 'function __construct($1) {\n\t$2\n}'
+
+  'parent constructor':
+    'prefix': 'pctor'
+    'body': 'parent::__construct($1);'
+
+  'public':
+    'prefix': 'public'
+    'body': 'public '
+
+  'private':
+    'prefix': 'private'
+    'body': 'private '
+
+  'protected':
+    'prefix': 'protected'
+    'body': 'protected '
+
   'if':
     'prefix': 'if'
     'body': 'if ( ${1:condition} ) {\n\t$2\n} $3'


Revise misleading PHP snippet comments

Revise misleading PHP snippet comments

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -289,7 +289,7 @@
     'prefix': 'class'
     'body': 'class ${1:ClassName}(${2:object}):\n\t${3:pass}'

-# PHP blocks/statements (meant to be used within PHP source blocks)
+# PHP code (within PHP blocks)

 '.source.php':

@@ -361,7 +361,7 @@
     'prefix': 'vd'
     'body': 'var_dump( $1 );'

-# Inline PHP Blocks (meant to be intermixed with HTML)
+# PHP markup (outside PHP blocks)

 '.text.html.php':


Add new JS snippets

Add new JS snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -4,16 +4,38 @@
     'body': 'const ${1:module} = require(\'${1:module}\')$0'
   'Function':
     'prefix': 'fun'
-    'body': 'function ${1:function_name} (${2:argument}) {\n\t${0:// body...}\n}'
+    'body': """
+      function ${1:function_name} (${2:argument}) {
+        ${0:// body...}
+      }
+    """
   'Tape test':
     'prefix': 'test'
-    'body': 'test(\'$1\', function (assert) {\n\tassert.ok($0)\n\tassert.end()\n})'
+    'body': """
+      test(\'$1\', function (assert) {
+        assert.ok($0)
+        assert.end()
+      })
+    """
   'Transform stream':
     'prefix': 'transform'
-    'body': 'new stream.Transform({${1:\n\tobjectMode: true,}${2:\n\ttransform (obj, _, cb) \{${3:\n\t\t//body...}\n\t\t$0cb()\}\n\t}\n})'
+    'body': """
+      new stream.Transform({
+        ${1:objectMode: true,}
+        ${2:transform (obj, _, cb) \{
+          $0
+          cb()\}
+        }
+      })
+    """
   'Callback':
     'prefix': 'cb'
-    'body': 'function (err, ${1:res}) {\n\tif (err) return console.error(err)\n\t$0\n}'
+    'body': """
+      function (err, ${1:res}) {
+        if (err) return console.error(err)
+        $0
+      }
+    """
   'log':
     'prefix': 'log'
     'body': 'console.log($1)$0'


Add missing semicolon to to.equal snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -269,7 +269,7 @@

   'to equal':
     'prefix': 'toequal'
-    'body': 'to.equal($1)$2'
+    'body': 'to.equal($1);$2'

   'not to equal':
     'prefix': 'nottoequal'


Don't assume extension for Jekyll include snippet

Filename could be .html or .md

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -845,7 +845,7 @@

   'include':
     'prefix': 'include'
-    'body': '{% include ${1:filename}.html $2%}'
+    'body': '{% include ${1:filename.html} $2%}'

   'if':
     'prefix': 'if'


doc'ing logging

doc'ing logging

--- src/logging.coffee
+++ src/logging.coffee
@@ -1,5 +1,5 @@
 ###*
-  @name util
+  @name Logging

   log "Testing123"


Remove debug code in app.coffee

Remove debug code in app.coffee

--- file/acp/be.bastelstu.wcf.nodePush/app.coffee
+++ file/acp/be.bastelstu.wcf.nodePush/app.coffee
@@ -1,6 +1,6 @@
 ### Copyright Information
 # @author	Tim Düsterhus
-# @copyright	2012-2013 Tim Düsterhus
+# @copyright	2012-2014 Tim Düsterhus
 # @license	BSD 3-Clause License <http://opensource.org/licenses/BSD-3-Clause>
 # @package	be.bastelstu.wcf.nodePush
 ###
@@ -179,7 +179,6 @@
 				chroot '/', config.user, config.group
 				logger.log "notice", "New User ID: #{process.getuid()}, New Group ID: #{process.getgid()}"
 			catch e
-				console.log e
 				logger.log "emerg", e, 'Cowardly refusing to keep the process alive as root.'
 				process.exit 1


:lipstick:

:lipstick:

--- src/browser/atom-protocol-handler.coffee
+++ src/browser/atom-protocol-handler.coffee
@@ -34,8 +34,7 @@

       if relativePath.indexOf('assets/') is 0
         assetsPath = path.join(app.getHomeDir(), '.atom', relativePath)
-        if fs.statSyncNoException(assetsPath).isFile?()
-          filePath = assetsPath
+        filePath = assetsPath if fs.statSyncNoException(assetsPath).isFile?()

       unless filePath
         for loadPath in @loadPaths


Update ekko-lightbox.coffee

Update ekko-lightbox.coffee

Updating coffee script to reflect changes

--- ekko-lightbox.coffee
+++ ekko-lightbox.coffee
@@ -140,8 +140,7 @@
 		img = new Image()
 		if !onLoadShowImage? || onLoadShowImage == true
 			img.onload = =>
-				width = img.width
-				@checkImageDimensions(img)
+				width = @checkImageDimensions(img)
 				@modal_body.html img
 				@resize width
 			img.onerror = =>
@@ -168,7 +167,7 @@
 		w = $(window)
 		if (img.width + (@padding.left + @padding.right + 20)) > w.width()
 			img.width = w.width() - (@padding.left + @padding.right + 20) #+ 20 because of the drop shadow
-		@
+		img.width

 }

@@ -194,4 +193,4 @@
 			gallery_parent_selector : $this.attr('data-parent')
 		})
 		.one 'hide', ->
-			$this.is(':visible') && $this.focus()+			$this.is(':visible') && $this.focus()


Add date and time simple types to those that open the DateControl

Avocado supports all three so it is possible we will see any of them and we should use the DateControl for all three of them.

--- src/coffee/cilantro/ui/field/form.coffee
+++ src/coffee/cilantro/ui/field/form.coffee
@@ -26,7 +26,7 @@
             controls.NumberControl
         else if type is 'boolean'
             controls.BooleanControl
-        else if type is 'datetime'
+        else if type is 'datetime' or type is 'date' or type is 'time'
             controls.DateControl


Improved: tests logging

Improved: tests logging

--- tests/helpers.coffee
+++ tests/helpers.coffee
@@ -27,17 +27,17 @@

 helpers.clearDB = (db) -> (done) ->
     @timeout 10000
-    console.log "Clear DB"
+    console.log "Clearing DB..."
     db.destroy (err) ->
-        console.log "Database destroyed"
+        console.log "\t-> Database destroyed!"
         if err and err.error isnt 'not_found'
             console.log "db.destroy err : ", err
             return done err

         setTimeout ->
-            console.log "Expecting a little ..."
+            console.log "Waiting a bit..."
             db.create (err) ->
-                console.log "Database created"
+                console.log "\t-> Database created"
                 console.log "db.create err : ", err if err
                 done err
         , 1000


small fix to argument reading

small fix to argument reading

--- sharejs-server.coffee
+++ sharejs-server.coffee
@@ -11,10 +11,12 @@

 # Using special options from https://github.com/share/ShareJS/blob/master/src/server/index.coffee
 options = _.extend {
-  db: {type: 'mongo'} # Default option is Mongo because Meteor provides it
   staticPath: null # Meteor is serving up this application
   # rest: null # disable REST interface?
 }, Meteor.settings.sharejs?.options
+
+# Default option is Mongo because Meteor provides it
+options.db.type ?= 'mongo'

 switch options.db.type
   when 'mongo'


remove silly and no longer necessary log message

--- sharejs-server.coffee
+++ sharejs-server.coffee
@@ -1,8 +1,6 @@
 # Creates a (persistent) ShareJS server
 # Based on https://github.com/share/ShareJS/wiki/Getting-started
 Future = Npm.require('fibers/future')
-
-logPrefix = "ShareJS:"

 ShareJS = ShareJS || {}
 # See docs for options. Uses mongo by default to enable persistence.
@@ -31,12 +29,10 @@
     connection._withDb (db) -> future.return(db)
     options.db.client = future.wait()

-    Meteor._debug logPrefix, "Using Meteor's mongo for persistence."
-
     if options.accounts_auth?
       options.auth = new MeteorAccountsAuthHandler(options.accounts_auth, options.db.client).handle
   else
-    Meteor._debug logPrefix, "using unsupported db type " + options.db.type + ", falling back to in-memory."
+    Meteor._debug "ShareJS: using unsupported db type " + options.db.type + ", falling back to in-memory."

 # Declare the path that ShareJS uses to Meteor
 RoutePolicy.declare('/channel/', 'network');


Refactored to extract pagination formatting of the result into extra method

--- src/controllers/mixins/pagination.coffee
+++ src/controllers/mixins/pagination.coffee
@@ -15,23 +15,26 @@
               callback.apply @, [err, result.rows]

         json: (data) ->
-          if @params.action isnt "index"
-            return _json.call @, data
+          if @params.action is "index"
+            data = @_buildPaginationResult data

-          totalPages = Math.ceil(@paginationTotal / @params.limit)
-          currentPage = Math.ceil(@params.offset / @params.limit)
-
-          response =
-            total: @paginationTotal
-            page: currentPage
-            totalPages: totalPages
-            limit: @params.limit
-            offset: @params.offset
-            items: data
-
-          _json.call @, response
+          _json.call @, data

   InstanceMethods:
+    _buildPaginationResult: (data) ->
+      totalPages = Math.ceil(@paginationTotal / @params.limit)
+      currentPage = Math.ceil(@params.offset / @params.limit)
+
+      response =
+        total: @paginationTotal
+        page: currentPage
+        totalPages: totalPages
+        limit: @params.limit
+        offset: @params.offset
+        items: data
+
+      response
+
     applyPaginationDefaults: ->
       @params.limit or= 20
       @params.limit = parseInt @params.limit, 10


Force native event handler to be registered for activation commands

We use onWillDispatch internally to manage activation commands,
activating packages *before* the activation command starts dispatching.
This means we need to explicitly tell the command registry to subscribe
to the command in question.

--- src/package.coffee
+++ src/package.coffee
@@ -335,6 +335,7 @@
     for selector, commands of @getActivationCommands()
       for command in commands
         do (selector, command) =>
+          atom.commands.commandRegistered(command)
           @activationCommandSubscriptions.add(atom.commands.onWillDispatch (event) =>
             return unless event.type is command
             currentTarget = event.target


fix modal test

fix modal test

--- karma/test-modals.coffee
+++ karma/test-modals.coffee
@@ -7,6 +7,7 @@
   FaqService = null
   LabelService = null
   MessageService = null
+  UserService = null

   $scope = null
   modalInstance = null
@@ -15,13 +16,14 @@
   beforeEach(() ->
     module('cases')

-    inject((_$controller_, _$rootScope_, _$q_, _MessageService_, _FaqService_, _LabelService_) ->
+    inject((_$controller_, _$rootScope_, _$q_, _MessageService_, _FaqService_, _LabelService_, _UserService_) ->
       $controller = _$controller_
       $rootScope = _$rootScope_
       $q = _$q_
       FaqService = _FaqService_
       LabelService = _LabelService_
       MessageService = _MessageService_
+      UserService = _UserService_
     )

     $scope = $rootScope.$new()
@@ -61,16 +63,33 @@
     $controller('AssignModalController', {$scope: $scope, $uibModalInstance: modalInstance, title: "Title", prompt: "OK?", partners: [test.moh, test.who]})

     expect($scope.fields.assignee).toEqual(test.moh)
+    expect($scope.fields.user).toEqual({id: null, name: "-- Anyone --"})
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}])

     $scope.fields.assignee = test.who
-    $scope.form = {$valid: true}
-    $scope.ok()
-
-    expect(modalInstance.close).toHaveBeenCalledWith(test.who)
-
-    $scope.cancel()
-
-    expect(modalInstance.dismiss).toHaveBeenCalledWith(false)
+    $scope.fields.user = test.user1
+    $scope.form = {$valid: true}
+    $scope.ok()
+
+    expect(modalInstance.close).toHaveBeenCalledWith({assignee: test.who, user: test.user1})
+
+    $scope.cancel()
+
+    expect(modalInstance.dismiss).toHaveBeenCalledWith(false)
+  )
+
+  it('AssignModalController.refreshUserList', () ->
+    usersForPartner = spyOnPromise($q, $scope, UserService, 'fetchInPartner')
+
+    $controller('AssignModalController', {$scope: $scope, $uibModalInstance: modalInstance, title: "Title", prompt: "OK?", partners: [test.moh, test.who]})
+
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}])
+
+    $scope.refreshUserList()
+    usersForPartner.resolve([test.user1])
+
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}, test.user1])
+    expect(UserService.fetchInPartner).toHaveBeenCalledWith(test.moh, false)
   )

   it('ComposeModalController', () ->
@@ -189,17 +208,35 @@

     expect($scope.fields.summary).toEqual({val: "Hello", maxLength: 10})
     expect($scope.fields.assignee).toEqual({val: test.moh})
+    expect($scope.fields.user).toEqual({val: {id: null, name: "-- Anyone --"}})
+    expect($scope.partners).toEqual([test.moh, test.who])
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}])

     $scope.fields.summary.val = "Interesting"
     $scope.fields.assignee.val = test.who
-    $scope.form = {$valid: true}
-    $scope.ok()
-
-    expect(modalInstance.close).toHaveBeenCalledWith({summary: "Interesting", assignee: test.who})
-
-    $scope.cancel()
-
-    expect(modalInstance.dismiss).toHaveBeenCalledWith(false)
+    $scope.fields.user.val = test.user1
+    $scope.form = {$valid: true}
+    $scope.ok()
+
+    expect(modalInstance.close).toHaveBeenCalledWith({summary: "Interesting", assignee: test.who, user: test.user1})
+
+    $scope.cancel()
+
+    expect(modalInstance.dismiss).toHaveBeenCalledWith(false)
+  )
+
+  it('NewCaseModalController.refreshUserList', () ->
+    usersForPartner = spyOnPromise($q, $scope, UserService, 'fetchInPartner')
+
+    $controller('NewCaseModalController', {$scope: $scope, $uibModalInstance: modalInstance, summaryInitial: "Hello", summaryMaxLength: 10, partners: [test.moh, test.who]})
+
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}])
+
+    $scope.refreshUserList()
+    usersForPartner.resolve([test.user1])
+
+    expect($scope.users).toEqual([{id: null, name: "-- Anyone --"}, test.user1])
+    expect(UserService.fetchInPartner).toHaveBeenCalledWith(test.moh, false)
   )

   it('NoteModalController', () ->


Tweak config

Tweak config

--- etc/atom/config.cson
+++ etc/atom/config.cson
@@ -71,6 +71,7 @@
   editor:
     fontFamily: "Source Code Pro"
     invisibles: {}
+    lineHeight: 1.3
     nonWordCharacters: "/\\\\()\\\"':,.;<>~!@#$%^&*|+=[]{}`?-…_"
     showIndentGuide: true
     softWrapAtPreferredLineLength: true
@@ -95,5 +96,6 @@
     autoToggle: true
   tabs:
     usePreviewTabs: true
+  "tree-view": {}
   welcome:
     showOnStartup: false


duplicated - move outside condition

duplicated - move outside condition

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -146,16 +146,14 @@
     @f = 10

   draw_scene: ->
+    @clear()
+    @draw_the_sun()
     unless @empty
-      @clear()
-      @draw_the_sun()
       @redraw_buildings()
       @redraw_colissions()
       @redraw_gorillas()
     else
       @empty = false
-      @clear()
-      @draw_the_sun()
       @draw_buildings()
       @draw_gorillas()


Updated shallow() snippet

Updated shallow() snippet

--- snippets/shallow.cson
+++ snippets/shallow.cson
@@ -2,5 +2,5 @@
   "shallow()":
     "prefix": "shallow"
     "body": """
-      ${1:wrapper} = shallow(${2:<App/>});
+      ${1:wrapper} = shallow(<${2:App}/>);
       """


:lipstick: Destructure tag from argv

:lipstick: Destructure tag from argv

--- src/publish.coffee
+++ src/publish.coffee
@@ -213,7 +213,7 @@
   run: (options) ->
     {callback} = options
     options = @parseOptions(options.commandArgs)
-    tag = options.argv.tag
+    {tag} = options.argv
     [version] = options.argv._

     metadataPath = path.resolve('package.json')


#1077 - Group and its children are hidden after being grouped again

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -198,9 +198,9 @@

         $target.toggleClass("survey__row--selected")
         if $target.hasClass('survey__row--group')
-          $target.find('li').toggleClass("survey__row--selected", $target.hasClass("survey__row--selected"))
-
-        $group = $target.parents('.survey__row')
+          $target.find('li.survey__row, li.survey__row--group').toggleClass("survey__row--selected", $target.hasClass("survey__row--selected"))
+
+        $group = $target.parent().closest('.survey__row')
         if $group.length > 0
           @select_group_if_all_items_selected($group)

@@ -210,9 +210,12 @@
     select_group_if_all_items_selected: ($group) ->
       $rows = $group.find('.survey__row')
       $group.toggleClass('survey__row--selected', $rows.length == $rows.filter('.survey__row--selected').length)
+      $group = $group.parent().closest('.survey__row')
+      if $group.length > 0
+        @select_group_if_all_items_selected($group)

     questionSelect: (evt)->
-      @activateGroupButton(@selectedRows().length > 0)
+      @activateGroupButton(@$el.find('.survey__row--selected').length > 0)
       return

     activateGroupButton: (active=true)->
@@ -538,6 +541,8 @@
       rows = []
       @$el.find('.survey__row--selected').each (i, el)=>
         $el = $(el)
+        if $el.parents('li.survey__row--group.survey__row--selected').length > 0
+          return
         rowId = $el.data("rowId")
         matchingRow = false
         findMatch = (row)->


arranged and formatted nonContextGlobals list in REPL

--- repl.coffee
+++ repl.coffee
@@ -33,7 +33,11 @@

 # The REPL context; must be visible outside `run` to allow for tab completion
 sandbox = Script.createContext()
-nonContextGlobals = ['clearInterval', 'Buffer', 'setTimeout', 'console', 'process', 'clearTimeout', 'setInterval']
+nonContextGlobals = [
+  'Buffer', 'console', 'process'
+  'setInterval', 'clearInterval'
+  'setTimeout', 'clearTimeout'
+]
 sandbox[g] = global[g] for g in nonContextGlobals
 sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox


add failing test

add failing test

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -832,6 +832,12 @@
         odd = src.map((x) -> x % 2)
         src.takeWhile(odd)
       [1,1])
+  describe "works with never-ending Property", ->
+    expectPropertyEvents(
+      -> repeat(1, [1, error("wat"), 2, 3])
+        .toProperty()
+        .takeWhile(lessThan(3))
+      [1, error("wat"), 2])

 describe "Property.takeUntil", ->
   describe "takes elements from source until an event appears in the other stream", ->


just one failing test left

just one failing test left

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -1342,7 +1342,7 @@
           s.end()
         p
       ["b", error()])
-describe.skip "works with synchronous source", ->
+ describe "works with synchronous source", ->
    expectStreamEvents(
      -> Bacon.fromArray([1,2,3]).toProperty(0).changes()
      [1,2,3])


Use the getStream API

Use the getStream API

--- spec/Component.coffee
+++ spec/Component.coffee
@@ -1429,6 +1429,26 @@
         c.inPorts.in.attach sin1
         sin1.post new noflo.IP 'data', 'eh'

+      it 'should get full stream when it has a single packet stream and it should clear it', (done) ->
+        c = new noflo.Component
+          inPorts:
+            eh:
+              datatype: 'string'
+          outPorts:
+            canada:
+              datatype: 'string'
+          process: (input, output) ->
+            return unless input.hasStream 'eh'
+            stream = input.getStream 'eh'
+            packetTypes = stream.map (ip) -> [ip.type, ip.data]
+            chai.expect(packetTypes).to.eql [
+              ['data', 'moose']
+            ]
+            chai.expect(input.has('eh')).to.equal false
+            done()
+
+        c.inPorts.eh.attach sin1
+        sin1.post new noflo.IP 'data', 'moose'
       it 'should get full stream when it has a full stream, and it should clear it', (done) ->
         c = new noflo.Component
           inPorts:
@@ -1439,16 +1459,23 @@
               datatype: 'string'
           process: (input, output) ->
             return unless input.hasStream 'eh'
-            originalBuf = input.buffer.get 'eh'
             stream = input.getStream 'eh'
-            afterStreamBuf = input.buffer.get 'eh'
-            chai.expect(stream).to.eql originalBuf
-            chai.expect(afterStreamBuf).to.eql []
+            packetTypes = stream.map (ip) -> [ip.type, ip.data]
+            chai.expect(packetTypes).to.eql [
+              ['openBracket', null]
+              ['openBracket', 'foo']
+              ['data', 'moose']
+              ['closeBracket', 'foo']
+              ['closeBracket', null]
+            ]
+            chai.expect(input.has('eh')).to.equal false
             done()

         c.inPorts.eh.attach sin1
         sin1.post new noflo.IP 'openBracket'
+        sin1.post new noflo.IP 'openBracket', 'foo'
         sin1.post new noflo.IP 'data', 'moose'
+        sin1.post new noflo.IP 'closeBracket', 'foo'
         sin1.post new noflo.IP 'closeBracket'

     describe 'with a simple ordered stream', ->


Add separate specs for sync/async caching

--- spec/cson-spec.coffee
+++ spec/cson-spec.coffee
@@ -166,24 +166,38 @@
         runs ->
           expect(CSON.readFileSync(csonPath)).toEqual object

-  describe "when a cache directory is set", ->
-    it "caches the contents of the compiled CSON files", ->
-      samplePath = path.join(__dirname, 'fixtures', 'sample.cson')
-      cacheDir = temp.mkdirSync('cache-dir')
-      CSON.setCacheDir(cacheDir)
-      CoffeeScript = require 'coffee-script'
-      spyOn(CoffeeScript, 'eval').andCallThrough()
-
-      expect(CSON.readFileSync(samplePath)).toEqual {a: 1, b: c: true}
-      expect(CoffeeScript.eval.callCount).toBe 1
-      CoffeeScript.eval.reset()
-      expect(CSON.readFileSync(samplePath)).toEqual {a: 1, b: c: true}
-      expect(CoffeeScript.eval.callCount).toBe 0
-
-      CoffeeScript.eval.reset()
-      sample = null
-      CSON.readFile samplePath, (error, object) -> sample = object
-      waitsFor -> sample?
-      runs ->
-        expect(sample).toEqual {a: 1, b: c: true}
+  describe "caching", ->
+    describe "synchronous reads", ->
+      it "caches the contents of the compiled CSON files", ->
+        samplePath = path.join(__dirname, 'fixtures', 'sample.cson')
+        cacheDir = temp.mkdirSync('cache-dir')
+        CSON.setCacheDir(cacheDir)
+        CoffeeScript = require 'coffee-script'
+        spyOn(CoffeeScript, 'eval').andCallThrough()
+
+        expect(CSON.readFileSync(samplePath)).toEqual {a: 1, b: c: true}
+        expect(CoffeeScript.eval.callCount).toBe 1
+        CoffeeScript.eval.reset()
+        expect(CSON.readFileSync(samplePath)).toEqual {a: 1, b: c: true}
         expect(CoffeeScript.eval.callCount).toBe 0
+
+    describe "asynchronous reads", ->
+      it "caches the contents of the compiled CSON files", ->
+        samplePath = path.join(__dirname, 'fixtures', 'sample.cson')
+        cacheDir = temp.mkdirSync('cache-dir')
+        CSON.setCacheDir(cacheDir)
+        CoffeeScript = require 'coffee-script'
+        spyOn(CoffeeScript, 'eval').andCallThrough()
+
+        sample = null
+        CSON.readFile samplePath, (error, object) -> sample = object
+        waitsFor -> sample?
+        runs ->
+          expect(sample).toEqual {a: 1, b: c: true}
+          expect(CoffeeScript.eval.callCount).toBe 1
+          CoffeeScript.eval.reset()
+          sample = null
+          CSON.readFile samplePath, (error, object) -> sample = object
+        waitsFor -> sample?
+        runs ->
+          expect(CoffeeScript.eval.callCount).toBe 0


Update tests - Fix #37

Update tests - Fix #37

--- spec/jira.spec.coffee
+++ spec/jira.spec.coffee
@@ -6,12 +6,13 @@
     makeUrl = (path, altBase) ->
         base = 'rest/api/2/'
         base = 'rest/greenhopper/2/' if altBase?
-        url.format
+        decodeURIComponent(
+            url.format
                 protocol: 'http:'
                 hostname: 'localhost'
                 auth: 'test:test'
                 port: 80
-                pathname: "#{base}#{path}"
+                pathname: "#{base}#{path}")

     beforeEach ->


Basic filter for executing specific tests

--- spec/main-spec.coffee
+++ spec/main-spec.coffee
@@ -9,7 +9,17 @@
   obj = obj[parts.shift()] while parts.length
   obj

+argv = process.argv.slice(2)
+spec = argv.indexOf('--spec')
+spec = unless spec is -1
+  argv[spec + 1]
+else
+  null
+
 glob.sync("#{__dirname}/**/*.json").forEach (file) ->
+  if spec isnt null
+    return if file.indexOf(spec) is -1
+
   JSON.parse(fs.readFileSync(file)).forEach (suite) ->
     describe "#{suite.description} (#{file.replace(__dirname + '/', '')})", ->
       suite.tests.forEach (test) ->


support SMC_STORAGE environment variable in compute daemon

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -76,7 +76,9 @@
 BUCKET  = process.env.SMC_BUCKET
 ARCHIVE = process.env.SMC_ARCHIVE

-if misc.startswith(require('os').hostname(), 'compute')   # my official deploy: TODO -- should be moved to conf file.
+if process.env.SMC_STORAGE?
+    STORAGE = process.env.SMC_STORAGE
+else if misc.startswith(require('os').hostname(), 'compute')   # my official deploy: TODO -- should be moved to conf file.
     STORAGE = 'storage0-us'
 else
     STORAGE = ''


Maybe this is better? I really hope no one is pulling these. Also should be doing this in beta-* branches.

--- src/50_servers.coffee
+++ src/50_servers.coffee
@@ -1,14 +1,15 @@
 debug = require('debug')('rupert:servers')

 module.exports = (config, app)->
-    if config.tls
+    tls = config.find('tls', 'TLS', false)
+    if tls
         debug("Configuring TLS")
-        config.set('tls', {}) if config.tls is true
+        config.tls = {} if config.tls is true
         require('./51_secure')(config, app)
         .then (_)->
             app.servers = _
             app.server = _.https
-            app.url = process.env.URL = config.HTTPS_URL
+            app.url = process.env.URL = config.find('url', 'URL', config.HTTPS_URL)
             app
     else
         debug("No TLS")
@@ -16,5 +17,5 @@
         .then (_)->
             app.servers = _
             app.server = _.http
-            app.url = process.env.URL = config.HTTP_URL
+            app.url = process.env.URL = config.find('url', 'URL', config.HTTP_URL)
             app


Removing brittspace

Removing brittspace

--- lib/twitter_cldr/js/mustache/utilities.coffee
+++ lib/twitter_cldr/js/mustache/utilities.coffee
@@ -2,7 +2,7 @@
 # http://www.apache.org/licenses/LICENSE-2.0

 class TwitterCldr.Utilities
-  # This function was adapted from the Mozilla JS reference:
+  # This function was adapted from the Mozilla JS reference:
   # https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode
   @from_char_code: (code_point) ->
     if code_point > 0xFFFF


Fix: support for consumers of the REPL *module* being able to opt into using the global context via option `.useGlobal`.

Note that, at least for now, CoffeeScript's own REPL *CLI* still uses a
non-global context, rendering modules such as `color`, which attempt to
modify the prototypes of JavaScript primitives, ineffective. By
contrast, node's own CLI does use the global context.

--- repl.coffee
+++ repl.coffee
@@ -27,7 +27,11 @@
         new Assign (new Value new Literal '_'), ast, '='
       ]
       js = ast.compile bare: yes, locals: Object.keys(context)
-      cb null, vm.runInContext(js, context, filename)
+      result = if context is global
+        vm.runInThisContext js, filename
+      else
+        vm.runInContext js, context, filename
+      cb null, result
     catch err
       # AST's `compile` does not add source code information to syntax errors.
       updateSyntaxError err, input


cleaning up an unimplemented test

cleaning up an unimplemented test

--- src/components/buttons/test/buttonbar.test.coffee
+++ src/components/buttons/test/buttonbar.test.coffee
@@ -13,9 +13,6 @@
     it 'should instantiate without error', ->
       buttonBar = new KDButtonBar
       assert.ok buttonBar
-
-    it 'should create new buttons supplied as options', ->
-      buttonOptions = [{ foo: 'bar' }]

   describe 'createButton', ->
     beforeEach ->


An udefine module on CommonJS does not always export

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -54,20 +54,20 @@

       result = resolveModule factory, depArr

-      module.exports = result if hasModule
-
       # Set dependency if it does not exist
       unless Object.hasOwnProperty.call udefine.modules[platform], name
         udefine.modules[platform][name] = result
-
+
+    # Automatically inject if property is set
+    unless Object.hasOwnProperty.call udefine.inject.modules, name
+      if udefine.autoInject
+        udefine.inject.add name, {root, name} if udefine.env.globals
+        if udefine.env.commonjs
+          udefine.inject.add name, {root: module.exports, name}

     # Inject result into defined namespace
     if Object.hasOwnProperty.call udefine.inject.modules, name
       injectObject = udefine.inject.modules[name]
-    else
-      if udefine.autoInject and udefine.env.globals
-        injectObject = {root, name}
-
       {root: injectRoot, name: injectName} = injectObject

       udefine.inject(injectRoot, injectName)(result)


Fixes udefine reference in browsers

Fixes udefine reference in browsers

--- src/udefine.coffee
+++ src/udefine.coffee
@@ -7,7 +7,7 @@
 # TODO: Binding to global on Node.js is not the best idea (I know it's bad
 #  practice), but what are the alternatives? (Putting it into a requirable
 #  module is not really an for small-scale projects)
-do (root = if hasModule then global ? this) ->
+do (root = if hasModule then global else this) ->
   root.udefine or= (name, deps, factory) ->
     throw new Error 'A udefine module needs to have a name' unless name?


Make the REPL use the global context to be consistent with the node REPL.

This will make packages that modify prototypes - e.g. 'colors', 'sugar'
- work as expected.

To verify that the `node` REPL uses the global context, execute `global
=== module.exports.repl.context`.

Note: Tests pass, except `cluster.coffee`, which, however, failed even
before these modifications.

--- repl.coffee
+++ repl.coffee
@@ -9,6 +9,8 @@
   prompt: 'coffee> ',
   historyFile: path.join process.env.HOME, '.coffee_history' if process.env.HOME
   historyMaxInputSize: 10240
+  # Make the REPL use the global context by default so as to (a) be consistent with the `node` REPL and, therefore, (b) make packages that modify prototypes - e.g., 'colors', 'sugar' - work as expected.
+  useGlobal: yes
   eval: (input, context, filename, cb) ->
     # XXX: multiline hack.
     input = input.replace /\uFF00/g, '\n'
@@ -27,7 +29,7 @@
         new Assign (new Value new Literal '_'), ast, '='
       ]
       js = ast.compile bare: yes, locals: Object.keys(context)
-      cb null, vm.runInContext(js, context, filename)
+      cb null, if context is global then vm.runInThisContext(js, filename) else vm.runInContext(js, context, filename)
     catch err
       # AST's `compile` does not add source code information to syntax errors.
       updateSyntaxError err, input


resolved merge

resolved merge

--- repl.coffee
+++ repl.coffee
@@ -32,7 +32,7 @@
   backlog = ''
   try
     val = CoffeeScript.eval code, bare: on, globals: on, filename: 'repl'
-    console.log val if val isnt undefined
+    process.stdout.write val + '\n' if val isnt undefined
   catch err
     error err
   repl.prompt()


fix for heavy typo

fix for heavy typo

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -86,7 +86,7 @@
     return '1498876634'
   else if slot in ['special', 'secondary']
     return '2465295065'
-  else if slot is 'heay'
+  else if slot is 'heavy'
     return '953998645'
   else
     return null


Further bounties debugging

Further bounties debugging

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -60,7 +60,8 @@
       for vendorHash in vendors
         getVendorDetails(res, vendorHash).then (vendorDetails) ->

-          console.log vendorDetails.saleItemCategories
+          console.log 'vendorDetails ='
+          console.log vendorDetails

           for category in vendorDetails.saleItemCategories
             if category.categoryIndex in Constants.BOUNTIES_CATEGORY_INDICES
@@ -363,10 +364,10 @@
         console.log 'Error getting vendor details: ' + vendorHash
         return deferred.reject()

-    vendorDetails = {
+    vendorDetails =
       vendorName: response.definitions.vendorDetails[vendorHash].summary.vendorName,
       salesItemCategories: response.data.saleItemCategories
-    }
+
     deferred.resolve(vendorDetails)

   deferred.promise


Refactor - Removing constants etc.

Refactor - Removing constants etc.


clean views on destroy

clean views on destroy

--- lib/data-atom-controller.coffee
+++ lib/data-atom-controller.coffee
@@ -18,7 +18,7 @@
       @currentViewState.view.show() if @currentViewState && @currentViewState.isShowing

    destroy: ->
-      # TODO destroy views
+      @currentViewState = null

    serialize: ->


Start collapsed

Start collapsed

--- lib/deprecation-cop-view.coffee
+++ lib/deprecation-cop-view.coffee
@@ -79,7 +79,7 @@
       self = this
       for deprecation in deprecations
         @list.append $$ ->
-          @li class: 'deprecation list-nested-item', =>
+          @li class: 'deprecation list-nested-item collapsed', =>
             @div class: 'deprecation-info list-item', =>
               @span class: 'text-highlight', deprecation.getOriginName()
               @span " (called #{deprecation.getCallCount()} times)"


added Jeremy's fierce logo maker

added Jeremy's fierce logo maker

--- scripts/fierce.coffee
+++ scripts/fierce.coffee
@@ -56,7 +56,7 @@

 module.exports = (robot) ->
   robot.respond /(fierce|fierce me)(.*)/i, (msg) ->
-    msg.send "The next Fierce publication will be Fierce" + words[Math.floor(Math.random() * words.length)] + words[Math.floor(Math.random() * words.length)]
+    msg.send "http://www.mutatedcreativity.com/dev/f/logo/?pub=Fierce" + words[Math.floor(Math.random() * words.length)] + words[Math.floor(Math.random() * words.length)] + "&ext=.png"

   robot.respond /(find) in (project|dir)/i, (msg) ->
     msg.send 'find . -type f | xargs grep -i "sharethis"'


protect against blank url

protect against blank url

--- js/inner/project.coffee
+++ js/inner/project.coffee
@@ -347,10 +347,13 @@
     false # dont bubble the event up

   create_settings_build: (data, event) =>
-    $.ajax
-      type: "POST"
-      event: event
-      url: "/api/v1/project/#{@project_name()}/tree/#{@settings_branch()}"
+    url = "/api/v1/project/#{@project_name()}"
+    if not _.isEmpty(@settings_branch())
+      url += "/tree/#{@settings_branch()}"
+    $.ajax
+      type: "POST"
+      event: event
+      url: url
       success: (data) =>
         VM.visit_local_url data.build_url
     false # dont bubble the event up


Rename variable 'object' to 'methods' (hopefully a bit clearer).

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -258,13 +258,13 @@
     expectation._throws = error

   _install_mock_method = (expectation) ->
-    object = if is_class(expectation._object) then expectation._object.prototype else expectation._object
-    original_method = object[ expectation._method_name ]
-    object[ expectation._method_name ] = MockedMethodBuilder.build_mocked_method(expectation._method_name)
+    methods = if is_class(expectation._object) then expectation._object.prototype else expectation._object
+    original_method = methods[ expectation._method_name ]
+    methods[ expectation._method_name ] = MockedMethodBuilder.build_mocked_method(expectation._method_name)
     if original_method?
-      expectation.uninstall_mocked_method = -> object[ expectation._method_name ] = original_method
+      expectation.uninstall_mocked_method = -> methods[ expectation._method_name ] = original_method
     else
-      expectation.uninstall_mocked_method = -> delete object[ expectation._method_name ]
+      expectation.uninstall_mocked_method = -> delete methods[ expectation._method_name ]

   _throw_args_usage = ->
     throw new Error( "you need to supply at least one argument to args(), e.g. my_mock.expects('my_method').args(42)" )


Change 'true' to 'yes'.

Change 'true' to 'yes'.

--- src/TinyMockJS.coffee
+++ src/TinyMockJS.coffee
@@ -91,7 +91,7 @@
     expectation = find_expectation(@, method_name, args...)
     throw_unknown_expectation("#{method_name}(#{args})") unless expectation?
     check_for_duplicate_expectations()
-    expectation.called = true
+    expectation.called = yes
     throw expectation._throws if expectation._throws?
     expectation._returns


hide date from logs fix

hide date from logs fix

--- lib/actions/views/logs.coffee
+++ lib/actions/views/logs.coffee
@@ -43,7 +43,7 @@
       data: data
     if data.logs
       for log in data.logs
-        renderLog(log, 0)
+        renderLog(log, 0, ignore_date)
     showMore()

 this.renderLog = (data, level, ignore_date) ->


:scissors: console.log

:scissors: console.log

--- lib/merge-conflicts-view.coffee
+++ lib/merge-conflicts-view.coffee
@@ -43,7 +43,6 @@
       found = false
       for listElement in @pathList.children()
         li = $(listElement)
-        console.log "Testing listElement [#{li.data('path')}] = [#{p}]"
         if li.data('path') is p
           found = true


Output recall.

Output recall.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -124,8 +124,8 @@
         if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])
             recall = robot.brain.get 'winners'

-            savescore = ["#{username} has #{points[username]}" for username in recall]
-            msg.send "Top points: #{savescore}"
+            # savescore = ["#{username} has #{points[username]}" for username in recall]
+            msg.send "#{recall}"

             # Outputs an inspection of recall
             msg.send "#{Util.inspect(recall)}"


I see shit on the screen!!

I see shit on the screen!!

--- canvas/game.coffee
+++ canvas/game.coffee
@@ -110,7 +110,6 @@
   class Atom extends Indexable
     @flavor_count = 6

-    @atom_for_shape = {}
     @max_bonds = 2

     constructor: (pos, @flavor_index, @sprite, @space) ->
@@ -124,7 +123,7 @@
       @space.addBody(body)
       @space.addShape(@shape)

-      Atom.atom_for_shape[@shape] = this
+      @shape.atom = this
       # atom => joint
       @bonds = new Map()
       @marked_for_deletion = false
@@ -183,7 +182,7 @@
       @space.remove(@shape)
       if not @rogue
         @space.remove(@shape.body)
-      delete Atom.atom_for_shape[@shape]
+      delete @shape.atom
       @sprite.delete()
       @sprite = null

@@ -369,11 +368,11 @@
           # loop over every object in the space and apply an impulse
           for shape in @space.shapes
             vector = shape.body.position - bomb.shape.body.position
-            dist = vector.get_length()
+            dist = vector.length()
             direction = vector.normalized()
             power = 6000
             damp = 1 - dist / 800
-            shape.body.apply_impulse(direction * power * damp)
+            shape.body.applyImpulse(direction.scale(power).scale(damp))

           # explosion animation
           sprite = new Engine.Sprite("bombsplode", batch:@game.batch, zorder:@game.group_fg)
@@ -396,7 +395,7 @@
       @computeAtomPointedAt()

       # update physics
-      step_count = Math.floor(dt / (1 / game_fps))
+      step_count = Math.floor(dt / (1 / 60))
       if step_count < 1
         step_count = 1
       delta = dt / step_count
@@ -592,16 +591,18 @@

       @control_state = []
       for btn, ctrl of @controls
-        @control_state[ctrl] = @engine.buttonState(btn)
-
-      feet_start = @man.body.position - @man_size / 2 + new Vec2d(1, -1)
-      feet_end = new Vec2d(feet_start.x + @man_size.x - 2, feet_start.y - 2)
+        @control_state[ctrl] = @game.engine.buttonState(btn)
+
+      feet_start = @man.body.position.minus(@man_size.scale(0.5)).offset(1, -1)
+      feet_end = feet_start.offset(@man_size.x - 2, -2)
       bb = new cp.BB(feet_start.x, feet_end.y, feet_end.x, feet_start.y)
-      ground_shapes = @space.bb_query(bb)
+      ground_shapes = []
+      @space.bbQuery bb, -1, null, (shape) ->
+        ground_shapes.push shape
       grounded = ground_shapes.length > 0

       grounded_move_force = 1000
-      not_moving_x = abs(@man.body.velocity.x) < 5.0
+      not_moving_x = Math.abs(@man.body.velocity.x) < 5.0
       air_move_force = 200
       grounded_move_boost = 30
       air_move_boost = 0
@@ -612,12 +613,12 @@
       move_right = @control_state[Control.MoveRight] and not @control_state[Control.MoveLeft]
       if move_left
         if @man.body.velocity.x >= -max_speed and @man.body.position.x - @man_size.x / 2 - 5 > 0
-          @man.body.apply_impulse(new Vec2d(-move_force, 0), new Vec2d(0, 0))
+          @man.body.applyImpulse(new Vec2d(-move_force, 0), new Vec2d(0, 0))
           if @man.body.velocity.x > -move_boost and @man.body.velocity.x < 0
             @man.body.velocity.x = -move_boost
       else if move_right
         if @man.body.velocity.x <= max_speed and @man.body.position.x + @man_size.x / 2 + 3 < @size.x
-          @man.body.apply_impulse(new Vec2d(move_force, 0), new Vec2d(0, 0))
+          @man.body.applyImpulse(new Vec2d(move_force, 0), new Vec2d(0, 0))
           if @man.body.velocity.x < move_boost and @man.body.velocity.x > 0
             @man.body.velocity.x = move_boost

@@ -635,11 +636,11 @@
         animation_name = "jump"
         @sprite_man.setAnimation(negate + animation_name)
         @man.body.velocity.y = 100
-        @man.body.apply_impulse(new Vec2d(0, 2000), new Vec2d(0, 0))
+        @man.body.applyImpulse(new Vec2d(0, 2000), new Vec2d(0, 0))
         # apply a reverse force upon the atom we jumped from
         power = 1000 / ground_shapes.length
         for shape in ground_shapes
-          shape.body.apply_impulse(new Vec2d(0, -power), new Vec2d(0, 0))
+          shape.body.applyImpulse(new Vec2d(0, -power), new Vec2d(0, 0))
         @playSfx('jump')

       # point the man+arm in direction of mouse
@@ -677,15 +678,15 @@
           @let_go_of_fire_main = false
           @claw_in_motion = true
           @sprite_claw.visible = true
-          body = new cp.Body(mass=5, moment=1000000)
+          body = new cp.Body(5, 1000000)
           body.position = new Vec2d(@point_start)
-          body.angle = @point_vector.get_angle()
+          body.angle = @point_vector.angle()
           body.velocity = @man.body.velocity + @point_vector * @claw_shoot_speed
           @claw = new cp.CircleShape(body, @claw_radius, new Vec2d())
           @claw.friction = 1
           @claw.elasticity = 0
           @claw.collision_type = Collision.Claw
-          @claw_joint = new cp.SlideJoint(@claw.body, @man.body, new Vec2d(0, 0), new Vec2d(0, 0), 0, @size.get_length())
+          @claw_joint = new cp.SlideJoint(@claw.body, @man.body, new Vec2d(0, 0), new Vec2d(0, 0), 0, @size.length())
           @claw_joint.max_bias = max_bias
           @space.addBody(body)
           @space.addShape(@claw)
@@ -694,7 +695,7 @@
           @playSfx('shoot_claw')

         if @sprite_claw.visible
-          claw_dist = (@claw.body.position - @man.body.position).get_length()
+          claw_dist = @claw.body.position.minus(@man.body.position).length()

         if @control_state[Control.FireMain] and @claw_in_motion
           if claw_dist < @min_claw_dist + 8
@@ -738,7 +739,7 @@
         # move the atom closer to the ray gun
         vector = @point_start - @ray_atom.shape.body.position
         delta = vector.normalized() * 1000 * dt
-        if delta.get_length() > vector.get_length()
+        if delta.length() > vector.length()
           # just move the atom to final location
           @ray_atom.shape.body.position = @point_start
         else
@@ -806,16 +807,17 @@

       @bond_queue = []

-    clawHitSomething: (space, arbiter) =>
+    clawHitSomething: (arbiter, space) =>
       if @claw_attached
         return
       # bolt these bodies together
-      [claw, shape] = arbiter.shapes
-      pos = arbiter.contacts[0].position
-      shape_anchor = pos - shape.body.position
-      claw_anchor = pos - claw.body.position
-      claw_delta = claw_anchor.normalized() * -(@claw_radius + 8)
-      @claw.body.position += claw_delta
+      claw = arbiter.a
+      shape = arbiter.b
+      pos = new Vec2d(arbiter.contacts[0].p)
+      shape_anchor = pos.minus(shape.body.position)
+      claw_anchor = pos.minus(claw.body.position)
+      claw_delta = claw_anchor.normalized().scale(-(@claw_radius + 8))
+      @claw.body.position.add(claw_delta)
       @claw_pins_to_add = [
         new cp.PinJoint(claw.body, shape.body, claw_anchor, shape_anchor),
         new cp.PinJoint(claw.body, shape.body, new Vec2d(0, 0), new Vec2d(0, 0)),
@@ -826,8 +828,9 @@

       @playSfx("claw_hit")

-    atomHitAtom: (space, arbiter) =>
-      [atom1, atom2] = [Atom.atom_for_shape[shape] for shape in arbiter.shapes]
+    atomHitAtom: (arbiter, space) =>
+      atom1 = arbiter.a.atom
+      atom2 = arbiter.b.atom
       # bond the atoms together
       if atom1.flavor_index is atom2.flavor_index
         @bond_queue.append([atom1, atom2])
@@ -890,7 +893,7 @@
         # find the coords at the wall
         slope = @point_vector.y / (@point_vector.x+0.00000001)
         y_intercept = @point_start.y - slope * @point_start.x
-        @point_end = @point_start + @size.get_length() * @point_vector
+        @point_end = @point_start + @size.length() * @point_vector
         if @point_end.x > @size.x
           @point_end.x = @size.x
           @point_end.y = slope * @point_end.x + y_intercept
@@ -922,19 +925,19 @@

     moveSprites: =>
       # drawable things
-      drawDrawable = =>
+      drawDrawable = (drawable) =>
         drawable.sprite.pos = drawable.shape.body.position.plus(@pos)
-        drawable.sprite.rotation = -drawable.shape.body.rotation_vector.get_angle_degrees()
+        drawable.sprite.rotation = -drawable.shape.body.rot.angle()
         true
       @atoms.each drawDrawable
       @bombs.each drawDrawable
       @rocks.each drawDrawable

       @sprite_man.pos = @man.body.position.plus(@pos)
-      @sprite_man.rotation = -@man.body.rotation_vector.get_angle_degrees()
+      @sprite_man.rotation = -@man.body.rot.angle()

       @sprite_arm.pos = @arm_pos.plus(@pos)
-      @sprite_arm.rotation = -(@mouse_pos - @man.body.position).get_angle_degrees()
+      @sprite_arm.rotation = -@mouse_pos.minus(@man.body.position).angle()
       if @mouse_pos.x < @man.body.position.x
         @sprite_arm.rotation += 180

@@ -942,39 +945,43 @@

       if @sprite_claw.visible
         @sprite_claw.pos = @claw.body.position.plus(@pos)
-        @sprite_claw.rotation = -@claw.body.rotation_vector.get_angle_degrees()
-
-    drawPrimitives: =>
+        @sprite_claw.rotation = -@claw.body.rot.angle()
+
+    drawPrimitives: (context) =>
       # draw a line from gun hand to @point_end
       if not @game_over
-        @drawLine(@point_start + @pos, @point_end + @pos, [0, 0, 0, 0.23])
+        @drawLine(context, @point_start.plus(@pos), @point_end.plus(@pos), [0, 0, 0, 0.23])

         # draw a line from gun to claw if it's out
         if @sprite_claw.visible
-          @drawLine(@point_start + @pos, @sprite_claw.position, [1, 1, 0, 1])
+          @drawLine(context, @point_start.plus(@pos), @sprite_claw.position, [1, 1, 0, 1])

         # draw lines for bonded atoms
         @atoms.each (atom) =>
           if atom.marked_for_deletion
             return
           atom.bonds.each (other, joint) =>
-            @drawLine(@pos + atom.shape.body.position, @pos + other.shape.body.position, [0, 0, 1, 1])
+            @drawLine(context, @pos.plus(atom.shape.body.position), @pos.plus(other.shape.body.position), [0, 0, 1, 1])
             true
           true

         if @game.debug
           if @claw_pins
             for claw_pin in @claw_pins
-              @drawLine(@pos + claw_pin.a.position + claw_pin.anchr1, @pos + claw_pin.b.position + claw_pin.anchr2, [1, 0, 1, 1])
+              @drawLine(context, @pos.plus(claw_pin.a.position).plus(claw_pin.anchr1), @pos.plus(claw_pin.b.position).plus(claw_pin.anchr2), [1, 0, 1, 1])

         # lazer
         if @lazer_line?
           [start, end] = @lazer_line
-          @drawLine(start + @pos, end + @pos, [1, 0, 0, 1])
-
-    drawLine: (p1, p2, color) =>
-      pyglet.gl.glColor4f(color[0], color[1], color[2], color[3])
-      pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ['v2f', [p1[0], p1[1], p2[0], p2[1]]])
+          @drawLine(context, start.plus(@pos), end.plus(@pos), [1, 0, 0, 1])
+
+    drawLine: (context, p1, p2, color) =>
+      context.strokeStyle = "rgb(#{color[0]}, #{color[1]}, #{color[2]}, #{color[3]})"
+      context.beginPath()
+      context.moveTo(p1.x, p1.y)
+      context.lineTo(p2.x, p2.y)
+      context.closePath()
+      context.stroke()

   class Game
     constructor: (@gw, @engine, @server) ->
@@ -1099,12 +1106,11 @@
       for tank in @tanks
         tank.moveSprites()

-      @batch.draw()
+      @engine.draw @batch

       for tank in @tanks
-        tank.drawPrimitives()
+        tank.drawPrimitives(context)

-
       if @fps_display
         context.fillText "#{@engine.fps} fps", 0, engine.size.y

@@ -1254,7 +1260,7 @@

     onMouseDown: (click_pos) =>
       if click_pos.distanceTo(@start_pos) < @click_radius
-        @gw.play(server_on=false)
+        @gw.play(false)
         return
       else if click_pos.distanceTo(@credits_pos) < @click_radius
         @gw.credits()
@@ -1289,7 +1295,8 @@

   # monkey patch chipmunk's vector, giving it the same API as ours.
-  cp.Vect = Vec2d
+  for prop, val of Vec2d.prototype
+    cp.Vect.prototype[prop] = val

   canvas = document.getElementById("game")
   engine = new Engine(canvas)


add configuration var for FIREBASE_URL

add configuration var for FIREBASE_URL

--- lib/pegg-firebase-client.coffee
+++ lib/pegg-firebase-client.coffee
@@ -10,10 +10,11 @@

 FIREBASE_SECRET = process.env.FIREBASE_SECRET or fail "cannot have an empty FIREBASE_SECRET"
 FIREBASE_UID = process.env.FIREBASE_UID or fail "cannot have an empty FIREBASE_UID"
+FIREBASE_URL = process.env.FIREBASE_URL or fail "cannot have an empty FIREBASE_URL"

 class PeggFirebaseClient extends Firebase
   constructor: ->
-    super 'https://pegg.firebaseio.com'
+    super FIREBASE_URL
     tokenGenerator = new FirebaseTokenGenerator FIREBASE_SECRET
     token = tokenGenerator.createToken {uid: FIREBASE_UID}#, {admin: true, expires: 2272147200}
     @authWithCustomToken token, (error, auth) =>


Typofix

Typofix

--- lib/rubocop-auto-correct.coffee
+++ lib/rubocop-auto-correct.coffee
@@ -86,7 +86,7 @@
     projectConfigPath = projectPath + configFile
     homeConfigPath = fs.getHomeDirectory() + configFile
     return ['--config', projectConfigPath] if (fs.existsSync(projectConfigPath))
-    retrun ['--config', homeConfigPath] if (fs.existsSync(homeConfigPath))
+    return ['--config', homeConfigPath] if (fs.existsSync(homeConfigPath))
     []

   rubocopCommand: ->


Added `page.reload()`

Added `page.reload()`

--- shim.coffee
+++ shim.coffee
@@ -45,7 +45,7 @@
   obj

 pageWrap = (page) -> mkwrap page,
-  ['open','close','includeJs','sendEvent','release','uploadFile','close','goBack','goForward']
+  ['open','close','includeJs','sendEvent','release','uploadFile','close','goBack','goForward','reload']
   injectJs: (js, cb=->) -> cb page.injectJs js
   evaluate: (fn, cb=(->), args...) -> cb page.evaluate.apply(page, [fn].concat(args))
   render: (file, cb=->) -> page.render file; cb()


Change windows to ctrl-alt-t

Change windows to ctrl-alt-t

--- keymaps/split-diff.cson
+++ keymaps/split-diff.cson
@@ -7,9 +7,10 @@
 '.split-diff-ui':
   'ctrl-alt-,': 'split-diff:copy-to-left'
   'ctrl-alt-.': 'split-diff:copy-to-right'
+
 '.platform-darwin atom-workspace':
   'ctrl-alt-t': 'split-diff:toggle'
 '.platform-win32 atom-workspace':
-  'ctrl-alt-d': 'split-diff:toggle'
+  'ctrl-alt-t': 'split-diff:toggle'
 '.platform-linux atom-workspace':
   'ctrl-alt-d': 'split-diff:toggle'


actually submit data for post

actually submit data for post

--- src/api/loader.coffee
+++ src/api/loader.coffee
@@ -13,7 +13,8 @@

   {data, change} = eventData
   apiSetting = _.pick(setting, 'url', 'method')
-  apiSetting.data = change if _.includes(METHODS_WITH_DATA, apiSetting.method)
+  if _.includes(METHODS_WITH_DATA, apiSetting.method)
+    apiSetting.data = change or data

   if isLocal
     apiSetting.url = "#{interpolate(apiSetting.url, data)}/#{apiSetting.method}.json"


Fixed merge error.

Fixed merge error.

--- lib/goto/function-goto.coffee
+++ lib/goto/function-goto.coffee
@@ -101,7 +101,7 @@
             if thrownWhenDescription
                 throwsDescription += ' ' + thrownWhenDescription

-            throwsDescription += ""
+            throwsDescription += "
"

         if throwsDescription.length > 0
             description += "

"


Don't set height on mini editor explicitly. Let 'height: auto' do it.

We're not sure why this is here, and can't see a problem when we remove
it.

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -734,7 +734,6 @@
     @lineHeight = lineRect.height
     @charWidth = charRect.width
     @charHeight = charRect.height
-    @height(@lineHeight) if @mini
     fragment.remove()

   updateLayerDimensions: ->


Added one search test with $page

Added one search test with $page

--- lib/test_generators/page.coffee
+++ lib/test_generators/page.coffee
@@ -12,6 +12,7 @@
   express = require 'express'
   _ = require 'underscore'

+  Utils = require 'backbone-node/utils'
   RestController = require '../../rest_controller'

   describe 'RestController', ->
@@ -126,3 +127,23 @@
             assert.ok(_.isArray(json), 'cursor item values is an array')
             assert.equal(json.length, FIELD_NAMES.length, 'gets only the requested values')
           done()
+
+    it 'Ensure the correct value is returned', (done) ->
+      Utils.getAt MODEL_TYPE, 2, (err, model) ->
+        assert.ok(!err, 'no errors')
+        assert.ok(!!model, 'model')
+
+        app = express(); app.use(express.bodyParser())
+        controller = new RestController(app, {model_type: MODEL_TYPE, route: ROUTE})
+
+        request(app)
+          .get("/#{ROUTE}")
+          .query({$page: true, name: model.get('name')})
+          .set('Accept', 'application/json')
+          .end (err, res) ->
+            assert.ok(!err, 'no errors')
+            assert.ok(!!data = res.body, 'got data')
+            assert.equal(data.total_rows, 1, 'has the correct total_rows')
+            assert.equal(data.rows.length, 1, 'has the correct row.length')
+            assert.deepEqual(expected = JSON.stringify(model.toJSON()), actual = JSON.stringify(data.rows[0]), "Expected: #{util.inspect(expected)}. Actual: #{util.inspect(actual)}")
+            done()


Fix variable interpolation

Fix variable interpolation

--- shared/src/model/user.coffee
+++ shared/src/model/user.coffee
@@ -41,7 +41,7 @@
       resp
     axios(
       method: 'PUT',
-      url: URLs.construct('accounts_api', "contact_infos", @id, "{type}.json")
+      url: URLs.construct('accounts_api', "contact_infos", @id, "#{type}.json")
       withCredentials: true
       data: data
     ).catch(afterRequest).then(afterRequest)


Yet another fix for the remoting

Yet another fix for the remoting

--- library/visualize/remote.coffee
+++ library/visualize/remote.coffee
@@ -97,7 +97,7 @@
     # piece. That is, either a normal class or a function, so on.
     # If the sourcing argument is not supplied, fetch the current.
     sourcing: (sourcing) ->
-        correct = not _.isEmpty sourcing
+        correct = _.isFunction sourcing
         return @$sourcing unless sourcing?
         invalid = "Sourcing is not correct"
         throw new Error invalid unless correct


remove unused constant

remove unused constant

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -7,8 +7,6 @@
 [GitHubApi, PackageManager] = []

 # constants
-syncUri = 'sync-settings:/'
-
 DESCRIPTION = 'Atom configuration storage operated by http://atom.io/packages/sync-settings'
 REMOVE_KEYS = ["sync-settings"]


set startedBy property directly when creating the instance document

--- server/cluster.coffee
+++ server/cluster.coffee
@@ -64,6 +64,7 @@
     bigboatCompose = YAML.load appDef.bigboatCompose

     Instances.upsert {name: instance}, $set:
+      startedBy: user._id
       images: (service.image for serviceName, service of dockerCompose)
       app:
         name: app


Gamuza grammar (mixed Lua with C++)

Gamuza grammar (mixed Lua with C++)

--- grammars/gamuza.cson
+++ grammars/gamuza.cson
@@ -3,100 +3,80 @@
 'fileTypes': ['ga']

 'patterns': [
-    {
-        # numbers: 4, 7.2, .0983
-        'match': '\\b(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))\\b'
-        'name': 'constant.numeric.ga'
-    },
-    {
-        'match': '\\b(?<!\\$)0(b|B)[01]+\\b(?!\\$)'
-        'name': 'constant.numeric.binary.ga'
-    },
-    {
-        'match': '\\b(?<!\\$)0(o|O)?[0-7]+\\b(?!\\$)'
-        'name': 'constant.numeric.octal.ga'
-    },
-    {
-        # HEX numbers: 0x000020
-        'match': '\\b(?<!\\$)0(x|X)[0-9a-fA-F]+\\b(?!\\$)'
-        'name': 'constant.numeric.hexadecimal.ga'
-    },
-    {
-        'captures':
-          '1':
-            'name': 'keyword.control.ga'
-          '2':
-            'name': 'entity.name.function.scope.ga'
-          '3':
-            'name': 'entity.name.function.ga'
-          '4':
-            'name': 'punctuation.definition.parameters.begin.ga'
-          '5':
-            'name': 'variable.parameter.function.ga'
-          '6':
-            'name': 'punctuation.definition.parameters.end.ga'
-        'match': '\\b(function)(?:\\s+([a-zA-Z_.:]+[.:])?([a-zA-Z_]\\w*)\\s*)?(\\()([^)]*)(\\))'
-        'name': 'meta.function.ga'
-    },
-    {
-        'match': '(?<![\\d.])\\s0x[a-fA-F\\d]+|\\b\\d+(\\.\\d+)?([eE]-?\\d+)?|\\.\\d+([eE]-?\\d+)?'
-        'name': 'constant.numeric.ga'
-    },
-    {
-        # Strings Single
-        'begin': '\''
-        'beginCaptures':
-          '0':
-            'name': 'punctuation.definition.string.begin.ga'
-        'end': '\''
-        'endCaptures':
-          '0':
-            'name': 'punctuation.definition.string.end.ga'
-        'name': 'string.quoted.single.ga'
-        'patterns': [
-          {
-            'match': '\\\\.'
-            'name': 'constant.character.escape.ga'
-          }
-        ]
-    },
-    {
-        # Strings Double
-        'begin': '\"'
-        'beginCaptures':
-          '0':
-            'name': 'punctuation.definition.string.begin.ga'
-        'end': '\"'
-        'endCaptures':
-          '0':
-            'name': 'punctuation.definition.string.end.ga'
-        'name': 'string.quoted.double.ga'
-        'patterns': [
-          {
-            'match': '\\\\.'
-            'name': 'constant.character.escape.ga'
-          }
-        ]
-    },º
-    {
-    'begin': '(?<=\\.cdef)\\s*(\\[(=*)\\[)'
-    'beginCaptures':
-      '0':
-        'name': 'string.quoted.other.multiline.ga'
-      '1':
-        'name': 'punctuation.definition.string.begin.ga'
-    'contentName': 'meta.embedded.ga'
-    'end': '(\\]\\2\\])'
-    'endCaptures':
-      '0':
-        'name': 'string.quoted.other.multiline.ga'
-      '1':
-        'name': 'punctuation.definition.string.end.ga'
-    'patterns': [
-      {
-        'include': 'source.c'
-      }
-    ]
+  {
+      # numbers: 4, 7.2, .0983
+      'match': '\\b(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))\\b'
+      'name': 'constant.numeric.ga'
+  },
+  {
+      'match': '\\b(?<!\\$)0(b|B)[01]+\\b(?!\\$)'
+      'name': 'constant.numeric.binary.ga'
+  },
+  {
+      'match': '\\b(?<!\\$)0(o|O)?[0-7]+\\b(?!\\$)'
+      'name': 'constant.numeric.octal.ga'
+  },
+  {
+      # HEX numbers: 0x000020
+      'match': '\\b(?<!\\$)0(x|X)[0-9a-fA-F]+\\b(?!\\$)'
+      'name': 'constant.numeric.hexadecimal.ga'
+  },
+  {
+      'captures':
+        '1':
+          'name': 'keyword.control.ga'
+        '2':
+          'name': 'entity.name.function.scope.ga'
+        '3':
+          'name': 'entity.name.function.ga'
+        '4':
+          'name': 'punctuation.definition.parameters.begin.ga'
+        '5':
+          'name': 'variable.parameter.function.ga'
+        '6':
+          'name': 'punctuation.definition.parameters.end.ga'
+      'match': '\\b(function)(?:\\s+([a-zA-Z_.:]+[.:])?([a-zA-Z_]\\w*)\\s*)?(\\()([^)]*)(\\))'
+      'name': 'meta.function.ga'
+  },
+  {
+      'match': '(?<![\\d.])\\s0x[a-fA-F\\d]+|\\b\\d+(\\.\\d+)?([eE]-?\\d+)?|\\.\\d+([eE]-?\\d+)?'
+      'name': 'constant.numeric.ga'
+  },
+  {
+      # Strings Single
+      'begin': '\''
+      'beginCaptures':
+        '0':
+          'name': 'punctuation.definition.string.begin.ga'
+      'end': '\''
+      'endCaptures':
+        '0':
+          'name': 'punctuation.definition.string.end.ga'
+      'name': 'string.quoted.single.ga'
+      'patterns': [
+        {
+          'match': '\\\\.'
+          'name': 'constant.character.escape.ga'
+        }
+      ]
+  },
+  {
+      # Strings Double
+      'begin': '\"'
+      'beginCaptures':
+        '0':
+          'name': 'punctuation.definition.string.begin.ga'
+      'end': '\"'
+      'endCaptures':
+        '0':
+          'name': 'punctuation.definition.string.end.ga'
+      'name': 'string.quoted.double.ga'
+      'patterns': [
+        {
+          'match': '\\\\.'
+          'name': 'constant.character.escape.ga'
+        }
+      ]
   },
   {
     'begin': '(?<!--)\\[(=*)\\['


Simplification for model_name

Simplification for model_name

--- sync.coffee
+++ sync.coffee
@@ -9,11 +9,8 @@
 module.exports = class AjaxSync

   constructor: (@model_type) ->
-    throw new Error("Missing url for model") unless @url = _.result(@model_type.prototype, 'url')
-
-    # publish methods and sync on model
-    @model_type.model_name = Utils.parseUrl(@url).model_name unless @model_type.model_name # model_name can be manually set
-    throw new Error('Missing model_name for model') unless @model_type.model_name
+    @model_type.model_name = Utils.findOrGenerateModelName(@model_type)
+    throw new Error("Missing url for model: #{@model_type}") unless @url = _.result(@model_type.prototype, 'url')
     @schema = new Schema(@model_type)
     @request = require 'superagent'


Add i18n when env.coffee isn't required

Add i18n when env.coffee isn't required

--- views/components/ship-parts/slotitems-data.coffee
+++ views/components/ship-parts/slotitems-data.coffee
@@ -1,5 +1,17 @@
-__ = window.i18n.data.__.bind(i18n.data)
-__n = window.i18n.data.__n.bind(i18n.data)
+if window.i18n?.data?
+  __ = window.i18n.data.__.bind(i18n.data)
+else
+  path = require 'path-extra'
+  i18n = new (require 'i18n-2')
+    locales: ['en-US', 'ja-JP', 'zh-CN', 'zh-TW']
+    defaultLocale: 'zh-CN'
+    directory: path.join ROOT, 'i18n', 'data'
+    updateFiles: false
+    indent: "\t"
+    extension: '.json'
+    devMode: false
+  i18n.setLocale(window.language)
+  __ = i18n.__.bind(i18n)

 types =
   "api_taik": "HP"


Fix typo in *getStats’ reply

Fix typo in *getStats’ reply

--- handlers/core.coffee
+++ handlers/core.coffee
@@ -74,7 +74,7 @@
 					api.replyTo message, __("""
 						Statistics:
 						Up since: %1$s (%2$d seconds)
-						Memory Usage: %3$d} KiB
+						Memory Usage: %3$d KiB
 						""",
 						config.upSince,
 						process.uptime(),


began writing tag loading.

began writing tag loading.

--- client/main.coffee
+++ client/main.coffee
@@ -54,6 +54,8 @@
     tagName: 'li'
     className: 'post'
     template: Templates.post
+    tags: []
+    vote: null
     events: ->

     initialize: ->
@@ -78,9 +80,7 @@
       $(@el).find('#content').autosize()

     renderInnerContents: =>
-      $(@el).find('#votebox').votebox({votesnum:@model.get('score'), callback: @model.maketag})
       @renderPostContent()
-      $(@el).find('#tagbox').tagbox({callback: @model.maketag})
       unless postCollection.where({parent: @id}).length > 0
         $(@el).find('#omnipost').omnipost({removeOnSubmit: true, callback: @model.respond})

@@ -106,6 +106,11 @@
       $(@el).html(@template)
       $(@el).find('.inner-question').attr('id', @model.get('id'))
       @renderInnerContents()
+      tags = tagCollection.where({parent: @model.get('id')})
+      for tag in tags
+        @addTag(tag)
+      $(@el).find('#votebox').votebox(vote: @vote, votesnum:@model.get('score'), callback: @model.maketag)
+      $(@el).find('#tagbox').tagbox(callback: @model.maketag, tags: @tags)
       return $(@el)

     addChild:(child) =>
@@ -122,22 +127,16 @@
       else
         base = $(@el).find('#response:first')
         base.prepend(child.render())
-
-   class TagView extends Backbone.View
-    tagName: 'p'
-    className: 'tag'
-    template: Templates.post
-    events: ->
-
-    initialize: ->
-      @id = @model.get('parent')
-
-    render: ->
-      tagdiv = $("
#{@model.get('title')}
")
-      tagdiv.css('background-image', 'url("/images/tagOutline.png")')
-      tagdiv.css('background-repeat', 'no-repeat')
-      tagdiv.css('background-size', '100% 100%')
-      $(@el).append(tagdiv)
+
+    addTag:(tag) =>
+      t = tag.get('title')
+      if t[0] != ','
+        @tags += t
+      if t == ',correct'
+        @vote = true
+      else if t == ',incorrect'
+        @vote = false
+

   class StreamView extends Backbone.View
     initialize: ->
@@ -149,9 +148,7 @@
       postCollection.bind('add', @addOne, this)
       postCollection.bind('reset', @addAll, this)
       postCollection.bind('all', @render, this)
-      tagCollection.bind('add', @addTag, this)
       @reset()
-      tagCollection.bind('reset', @addAllTags, this)

     reset: =>
       $.getJSON('/stream', ( (data) ->
@@ -194,12 +191,6 @@
       item.destroy()
     deleteAll: ->
       postCollection.each(@deleteOne)
-
-    addTag: (item) ->
-      tag = new TagView(model: item)
-
-    addAllTags: ->
-      tagCollection.each(@addTag)

     setTopicCreatorVisibility: =>
       if @topic_creator_showing


Make cosmetic improvements to the output

--- lib/barista_test.coffee
+++ lib/barista_test.coffee
@@ -7,9 +7,14 @@
 # Parse our command-line arguments.
 test_file_paths = process.argv[1...process.argv.length]

-# Set up a basic test environment.  This code is based on the code at
-# http://github.com/jashkenas/coffee-script/blob/master/Cakefile ,
-# which implements the standard Coffee
+# ANSI color codes.  Borrowed from
+# http://github.com/jashkenas/coffee-script/blob/master/Cakefile , which
+# implements a test runner for internal use by CoffeeScript.
+red   = '\033[0;31m'
+green = '\033[0;32m'
+reset = '\033[0m'
+
+# Set up a basic test environment.  Based on the standard Cakefile.
 passed = failed = 0
 helpers.extend global, assert_module
 global.ok = (args...) ->
@@ -19,8 +24,8 @@

 # Called when the tests are done.
 testsDone = ->
-  tag = if failed > 0 then "ERR" else "OK"
-  sys.puts "#{tag}: #{passed} passed, #{failed} failed"
+  tag = if failed > 0 then "#{red}ERR" else "#{green}OK"
+  sys.puts "\n#{tag}:#{reset} #{passed} passed, #{failed} failed"
   if failed > 0
     process.exit(1)

@@ -32,8 +37,9 @@
     files_left -= 1
     try
       coffee.run data.toString(), fileName: path
+      sys.print "."
     catch err
       failed += 1
-      sys.puts err.stack.toString()
+      sys.puts "\n#{err.stack.toString()}"
     if files_left is 0
       testsDone()


wipe db before seed

wipe db before seed

--- seed.coffee
+++ seed.coffee
@@ -52,11 +52,11 @@
   category: 'Interests'
 ]

-createBoard = (board, cb) ->
-  try
+mongo.wipe ->
+  createBoard = (board, cb) ->
     Board.create board, -> cb()

-async.forEach boards, createBoard, (err) ->
-  console.log err if err?
-  console.log 'Done'
-  #process.exit()+  async.forEach boards, createBoard, (err) ->
+    console.log err if err?
+    console.log 'Done'
+    #process.exit()


write less rules to the maintained stylesheet

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -48,24 +48,15 @@
     workspaceView = atom.views.getView atom.workspace
     workspaceView.className = workspaceView.className.replace /block-cursor-(block|bordered-box|i-beam|underline)/, ''
     workspaceView.classList.add "block-cursor-#{cursorType}"
-    @applyPrimaryColor()
-    @applySecondaryColor()

   applyPrimaryColor: (color) ->
     color ?= atom.config.get 'block-cursor.primaryColor'
-    property = switch @cursorType
-      when 'block' then 'background-color'
-      else 'border-color'
-    value = color.toRGBAString()
-    @updateStylesheet primarySelector, property, value
+    @updateStylesheet primarySelector, 'background-color', color.toRGBAString()
+    @updateStylesheet primarySelector, 'border-color', color.toRGBAString()

   applySecondaryColor: (color) ->
-    color ?= atom.config.get 'block-cursor.secondaryColor'
-    property = switch @cursorType
-      when 'block' then 'background-color'
-      else 'border-color'
-    value = color.toRGBAString()
-    @updateStylesheet secondarySelector, property, value
+    @updateStylesheet secondarySelector, 'background-color', color.toRGBAString()
+    @updateStylesheet secondarySelector, 'border-color', color.toRGBAString()

   applyPulse: (duration) ->
     @updateStylesheet primarySelector, 'transition-duration', "#{duration}ms"


Adding back in a comment accidentally deleted.

--- hogan-express.coffee
+++ hogan-express.coffee
@@ -81,6 +81,9 @@
           if (! lcontext.lambdaVals?) then lcontext.lambdaVals = {}
           if (! lcontext.lambdaVals[name]?) then lcontext.lambdaVals[name] = {}

+          # getting the context right here is important
+          # it must account for "locals" and values in the current context
+
           #  ... particually interesting when applying within a list
           lctx= {}
           lctx = lctx extends opt._locals if opt._locals


add ajax to do sign out

add ajax to do sign out

--- src/components.coffee
+++ src/components.coffee
@@ -13,6 +13,12 @@
   throw new Error(JSON.stringify(msgs...))

 App = React.createClass
+
+  logout: ->
+    $.ajax('/accounts/logout', {method: 'DELETE'})
+    .always ->
+      window.location.href = '/'
+
   render: ->

@@ -37,7 +43,7 @@

-                Sign out!
+                <button className='btn btn-link' onClick={@logout}>Sign out!</button>


add license note on top of components.coffee

--- src/components.coffee
+++ src/components.coffee
@@ -1,3 +1,25 @@
+    ###
+    Copyright (c) 2013 Cotton Hou
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+    ###
+
     class State

         constructor: (@done = false) ->


Change ship.id to ship.shipId

Change ship.id to ship.shipId

--- views/records.coffee
+++ views/records.coffee
@@ -25,14 +25,14 @@
     # Side effect: will always delete temp json file (even if returns null)
     # Format: {
     #   map: {
-    #     name: "2-5"
+    #     name: e.g. "2-5"
     #     rank: undefined | 1 | 2 | 3           # 1 for easy, 3 for hard
     #     hp: undefined | [<now_remaining>, <max>]  # undefined after cleared
     #   }
     #   time: <Unix Time Milliseconds>
     #   deck: [
-    #     {
-    #       id: 8902
+    #     {     # One ship
+    #       shipId: <int>
     #       consumption: [<resupplyFuel>, <resupplyAmmo>, <resupplyBauxite>,
     #         <repairFuel>, <repairSteel>]
     #     }, ...
@@ -40,7 +40,7 @@
     #   deck2: <Same as deck>
     #   reinforcements: [
     #     {
-    #       id: [4702, 1056, ...]
+    #       shipId: [<int>, ...]
     #       consumption: [<fuel>, <ammo>, 0, <bauxite>]     # Total only
     #     }, ...
     #   ]
@@ -82,13 +82,14 @@
       @result_.deck2 = @fleetConsumption_(@record_.deck2)
     if @record_.reinforcements?
       @result_.reinforcements = for reinforcement in @record_.reinforcements
-        id: reinforcement.deck
+        shipId: reinforcement.deck.map((i) -> window._ships[i].api_ship_id)
         consumption: (sum4(@shipExpeditionConsumption_ id for id in reinforcement.deck))
-
     @result_

   fleetConsumption_: (deck) ->
-    ({id: ship.id, consumption: @shipConsumption_ ship} for ship in deck)
+    (for ship in deck
+       id: window._ships[ship.id].api_ship_id,
+       consumption: @shipConsumption_ ship)

   shipConsumption_: (recordShip) ->
     nowShip = window._ships[recordShip.id]


Fix coffee syntax bug

Fix coffee syntax bug

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -5,7 +5,7 @@
 {getItemAt} = require('./parse.coffee')

 # iOS CSS hover hack
-document.addEventListener 'touchstart', ->, false
+document.addEventListener('touchstart', (->), false)

 if not document.getElementById '#editor'
   return


Even simpler

Even simpler

--- coffee/vex.coffee
+++ coffee/vex.coffee
@@ -160,7 +160,7 @@
         ids = vex.getAllVexes().map(-> $(@).data().vex.id).toArray()
         return false unless ids?.length

-        id = ids.sort().shift()
+        id = Math.max ids...
         $lastVex = vex.getVexByID id
         return false if $lastVex.data().vex.escapeButtonCloses isnt true