temporarily change 1337 to 1338 for Seaport

--- library/nucleus/bundles.coffee
+++ library/nucleus/bundles.coffee
@@ -45,7 +45,7 @@
     @synopsis = "Final production environment for end users"
     @overrides = layout: library: "library", config: "config"
     @defaults = new Object env: {},  server: {}, secure: {}
-    @defaults.hub = host: "localhost", port: 1337, opts: {}
+    @defaults.hub = host: "localhost", port: 1338, opts: {}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.master = host: "localhost", http: 80, https: 443
     @defaults.server = host: "localhost", http: 80, https: 443
@@ -83,7 +83,7 @@
     @synopsis = "An environment between staging and production"
     @overrides = layout: library: "library", config: "config"
     @defaults = new Object env: {},  server: {}, secure: {}
-    @defaults.hub = host: "localhost", port: 1337, opts: {}
+    @defaults.hub = host: "localhost", port: 1338, opts: {}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.master = host: "localhost", http: 80, https: 443
     @defaults.server = host: "localhost", http: 80, https: 443
@@ -121,7 +121,7 @@
     @synopsis = "Unstable working environment for developers"
     @overrides = layout: library: "library", config: "config"
     @defaults = new Object env: {},  server: {}, secure: {}
-    @defaults.hub = host: "localhost", port: 1337, opts: {}
+    @defaults.hub = host: "localhost", port: 1338, opts: {}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.master = host: "localhost", http: 1080, https: 1043
     @defaults.server = host: "localhost", http: 1080, https: 1043
@@ -159,7 +159,7 @@
     @synopsis = "Isolated environment for running the tests"
     @overrides = layout: library: "library", config: "config"
     @defaults = new Object env: {},  server: {}, secure: {}
-    @defaults.hub = host: "localhost", port: 1337, opts: {}
+    @defaults.hub = host: "localhost", port: 1338, opts: {}
     @defaults.env = dirs: ["tmp", "var", "pub"], mode: 0o744
     @defaults.master = host: "localhost", http: 1080, https: 1043
     @defaults.server = host: "localhost", http: 1080, https: 1043


Minor syntactic touch ups in the bundles module

--- library/nucleus/bundles.coffee
+++ library/nucleus/bundles.coffee
@@ -49,7 +49,7 @@
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
-    @pushToRegistry(yes, @tag.toUpperCase())
+    @pushToRegistry yes, @tag.toUpperCase()
     @directory = "config"

 # One of the predefined scopes, baked into the framework. Scopes
@@ -65,7 +65,7 @@
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
-    @pushToRegistry(yes, @tag.toUpperCase())
+    @pushToRegistry yes, @tag.toUpperCase()
     @directory = "config"

 # One of the predefined scopes, baked into the framework. Scopes
@@ -81,7 +81,7 @@
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
-    @pushToRegistry(yes, @tag.toUpperCase())
+    @pushToRegistry yes, @tag.toUpperCase()
     @directory = "config"

 # One of the predefined scopes, baked into the framework. Scopes
@@ -97,5 +97,5 @@
     @defaults.log = request: {format: "dev", level: "debug"}
     @defaults.secure.key = "#{__dirname}/../../keys/key.pem"
     @defaults.secure.cert = "#{__dirname}/../../keys/cert.pem"
-    @pushToRegistry(yes, @tag.toUpperCase())
+    @pushToRegistry yes, @tag.toUpperCase()
     @directory = "config"


Fix logging highlights and undefinedes

Fix logging highlights and undefinedes

--- library/nucleus/routing.coffee
+++ library/nucleus/routing.coffee
@@ -53,9 +53,10 @@
         predicate = (routable) -> routable.matches parameters...
         recognized = _.find(@registry or [],  predicate) or null
         if recognized then constructor = recognized.constructor
-        inspected = (constructor?.nick or constructor?.name).underline
+        inspected = (constructor?.nick or constructor?.name)
+        identify = inspected.toString().underline
         @emit "recognized", recognized, parameters... if recognized?
-        matching = "Request #{incoming} matches #{inspected} service"
+        matching = "Request #{incoming} matches #{identify} service"
         logger.debug matching.grey if recognized?
         return recognized.process parameters... if recognized?
         logger.warn "No routable for #{incoming} request".yellow
@@ -74,7 +75,7 @@
         nick = routable?.constructor?.nick
         name = routable?.constructor?.name
         inspected = nick or name or undefined
-        identify = inspected.underline
+        identify = inspected.toString().underline
         duplicate = routable in (@registry or [])
         [matches, process] = [routable.matches, routable.process]
         goneMatches = "The #{inspected} has no valid matches method"
@@ -96,6 +97,7 @@
         nick = routable?.constructor?.nick
         name = routable?.constructor?.name
         inspected = nick or name or undefined
+        identify = inspected.toString().underline
         [matches, process] = [routable.matches, routable.process]
         goneMatches = "The #{inspected} has no valid matches method"
         goneProcess = "The #{inspected} has no valid process method"
@@ -103,5 +105,5 @@
         passProcess = _.isFunction(process) and process?.length is 3
         throw new Error goneMatches unless passMatches
         throw new Error goneProcess unless passProcess
-        logger.info "Fallback to #{inspected.underline} service".blue
+        logger.info "Fallback to #{identify} service".blue
         @fallback = routable; @emit "installed", routable; this


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -15,7 +15,7 @@
   y: 50
   width: 10
   height: 10
-  ve
+  velocity:

 enemy.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -33,7 +33,7 @@
 # => Point(-1, 0)

 # you can use this direction to set a velocity for your object.
-enemy.I.velocity =
+enemy.I.velocity = enemy.I.directio

 enemy.I.x
 # => 99


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -2,7 +2,7 @@
 The Follow module provides a simple method to set an object's
 velocity so that it will approach another object. This method
 relies on both objects having `bounds` methods. GameObject
-provides
+provides this method b

 player = GameObject


lint fixes

lint fixes

--- js/src/tree/TreeLoader.coffee
+++ js/src/tree/TreeLoader.coffee
@@ -35,8 +35,8 @@
       .then =>
         @objectApi.getObjectMetadata(id)
       .then (metadata) =>
-        {childObjectCount} = metadata
-        childIndices       = [0...childObjectCount]
+        { childObjectCount } = metadata
+        childIndices         = [0...childObjectCount]
         return Promise.all(_.map(childIndices, (index) =>
           childId = ObjectUtils.createChildId(id, index)

@@ -49,8 +49,8 @@
         children = _.compact(children)
         return new TreeNode(id, children)
       .catch (e) ->
-        {message, stack} = e
-        log.error('failed to load', {e, message, stack})
+        { message, stack } = e
+        log.error('failed to load', { e, message, stack })
         return undefined

   return TreeLoader


Only atom_open if node exists

Only atom_open if node exists

--- lib/nsync/custom-commands.coffee
+++ lib/nsync/custom-commands.coffee
@@ -9,7 +9,8 @@

   atom_open: ({path}) ->
     node = nsync.getNode(path)
-    atomHelper.open(node.localPath())
+    if node?
+      atomHelper.open(node.localPath())

   learn_submit: ({url}) ->
     new WebWindow(url, {resizable: false})


#88: prompt for access_token

#88: prompt for access_token

--- src/github.coffee
+++ src/github.coffee
@@ -11,8 +11,10 @@

 define (require) ->
   URI = require 'URIjs'
+  Q = require 'q'
   modules = require 'modules'
   http = require 'http'
+  global_settings = require 'settings'

   notebook = null
   require ['notebook'], (nb) ->
@@ -87,6 +89,7 @@

     cmd 'load', 'Loads a file from GitHub', (path, options={}) ->
       url = github.to_repo_url path
+      ensure_access @, url
       @async ->
         @text "Loading file #{path}"
         promise = github.get_repo_contents(url)
@@ -96,19 +99,39 @@
           @error response.statusText
         promise

+    ensure_access = (ctx, url) ->
+      unless url?
+        instance = settings.get 'githubs', github.default()
+      else
+        instance = github.get_github url
+      if instance? and instance.requires_access_token and not instance.access_token?
+        result = Q.defer()
+        ctx.text 'Please set a GitHub access token:'
+        input = ctx.input.text_input()
+        button = ctx.input.button('Save')
+        button.map(input).onValue (access_token) ->
+          domain = URI(url).hostname()
+          global_settings.user_settings.set 'github', 'githubs', domain, 'access_token', access_token
+          result.resolve()
+        result.promise
+      else
+        Q true
+
     cmd 'gist', 'Loads a script from a gist', (gist, options={}) ->
       if arguments.length is 0
         @github.save_gist()
       else
         url = github.to_gist_url gist
         @async ->
-          @text "Loading gist #{gist}"
-          promise = http.get url
-          promise.done (response) =>
-            for name, file of response.files
-              notebook.handle_file @, file, options
-          promise.fail (response) =>
-            @error response.statusText
+          authorized = ensure_access @, url
+          authorized.then =>
+            @text "Loading gist #{gist}"
+            promise = http.get url
+            promise.done (response) =>
+              for name, file of response.files
+                notebook.handle_file @, file, options
+            promise.fail (response) =>
+              @error response.statusText

     cmd 'save_gist', 'Saves a notebook as a gist', ->
       notebook = @export_notebook()
@@ -118,13 +141,15 @@
           'notebook.lnb':
             content: JSON.stringify notebook, undefined, 2
       @async ->
-        promise = github.save_gist gist
-        promise.done (result) =>
-          @html "#{result.html_url}"
-          lead_uri = URI window.location.href
-          lead_uri.fragment "/#{result.html_url}"
-          @html "#{lead_uri}"
-        promise.fail =>
-          @error 'Save failed. Make sure your access token is configured correctly.'
+        authorized = ensure_access @
+        authorized.then =>
+          promise = github.save_gist gist
+          promise.done (result) =>
+            @html "#{result.html_url}"
+            lead_uri = URI window.location.href
+            lead_uri.fragment "/#{result.html_url}"
+            @html "#{lead_uri}"
+          promise.fail =>
+            @error 'Save failed. Make sure your access token is configured correctly.'

     github


:fire: Remove redundant part of PanelView

--- lib/views/panel.coffee
+++ lib/views/panel.coffee
@@ -3,28 +3,8 @@
 class PanelView extends HTMLElement
   initialize:(@linter) ->
     @id = 'linter-panel'
-    @_decorations = []
-
-  empty: ->
-    return unless @_decorations.length
-    @_decorations.forEach (decoration) ->
-      try decoration.destroy()
-    @_decorations = []
-    @innerHTML = ''
-
-  update: ->
-    if @linter.views.messages.length
-      @render @linter.views.messages
-      @linter.views.setPanelVisibility(true)
-    else
-      @hide()
-
-  hide: ->
-    @empty()
-    @linter.views.setPanelVisibility(false)

   render: (messages) ->
-    @empty()
     messages.forEach (message) =>
       if @linter.views.scope is 'file'
         return unless message.currentFile


Debugging

Debugging

--- lib/compile.coffee
+++ lib/compile.coffee
@@ -272,11 +272,12 @@
         helpers.logVerbose 'match 4'
         updated = true

-        escapedMatch = escapeCurlyBraces match
-        escapedAttrVal = escapeBraces( attrVal
+        escapedMatch = escapeCurlyBraces( match
           .replace(/\{\{/g, "{{expression '")
           .replace(/\}\}/g, "'}}")
         )
+
+        escapedAttrVal = escapeBraces attrVal

         """#{escapedMatch.replace ' ' + attrName, ' data-' + attrName} #{attrName.substring(3)}="#{escapedAttrVal}" """
       )


Add mock.orderForm.addItem

Add mock.orderForm.addItem


Update crudify.coffee

Update crudify.coffee

--- lib/crudify.coffee
+++ lib/crudify.coffee
@@ -3,6 +3,8 @@

 # underscores is amazing, we'll be using this a lot.
 _ = require "underscore"
+
+extendify = require "./extended"

 # require your nedb installation
 ds = null
@@ -10,8 +12,7 @@
 # on unsupported request methods.
 crudify = (store, req, fn) ->

-  st = require "./extended"
-  ds = new st store
+  ds = new extendify store
   Schema = ds.Schema

   # listen for `req.param('id')` will fallback until it finds the id
@@ -155,4 +156,4 @@
         # boohoo, we've got trouble.
         fn error: "No items updated, please check your id", null

-module.exports = crudify+module.exports = crudify


Create root view before loading package

Create root view before loading package

--- src/packages/autocomplete/spec/autocomplete-spec.coffee
+++ src/packages/autocomplete/spec/autocomplete-spec.coffee
@@ -41,6 +41,7 @@
   miniEditor = null

   beforeEach ->
+    new RootView
     editor = new Editor(editSession: fixturesProject.buildEditSessionForPath('sample.js'))
     atom.loadPackage('autocomplete')
     autocomplete = new AutocompleteView(editor)


Use timeout to put in other callback and then update the StatusMessage

--- app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee
+++ app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee
@@ -1,4 +1,4 @@
-angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, StatusMessage) ->
+angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, StatusMessage, $timeout) ->
   new class AdminStripeElements

     # These are both set from the AdminStripeElements directive
@@ -13,7 +13,7 @@

       @stripe.createToken(@card, cardData).then (response) =>
         if(response.error)
-          StatusMessage.display 'error', response.error.message
+          $timeout -> StatusMessage.display 'error', response.error.message
           console.error(JSON.stringify(response.error))
         else
           secrets.token = response.token.id
@@ -29,7 +29,7 @@

       @stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
         if(response.error)
-          StatusMessage.display 'error', response.error.message
+          $timeout -> StatusMessage.display 'error', response.error.message
           console.error(JSON.stringify(response.error))
         else
           secrets.token = response.paymentMethod.id


Just enable keyboard bindings if widget is shown.

--- app/assets/javascripts/app/controllers/widget/online_notification.coffee
+++ app/assets/javascripts/app/controllers/widget/online_notification.coffee
@@ -57,7 +57,6 @@
     )

     $(window).on 'click.notifications', @hide
-    $(window).on 'keydown.notifications', @listNavigate

     @updateContent()

@@ -201,11 +200,13 @@
     @show()

   show: =>
+    $(window).on 'keydown.notifications', @listNavigate
     @shown = true
     @el.show()
     @updateHeight()

   hide: =>
+    $(window).off 'keydown.notifications'
     @shown = false
     @el.hide()


Adjust/add deprecated methods to make the old specs pass

--- src/keymap.coffee
+++ src/keymap.coffee
@@ -9,6 +9,8 @@
     @keyBindings = []
     @keystrokes = []

+  destroy: ->
+
   # Public: Add sets of key bindings grouped by CSS selector.
   #
   # source - A {String} (usually a path) uniquely identifying the given bindings
@@ -17,16 +19,19 @@
   #   keystroke patterns to commands.
   addKeyBindings: (source, keyBindingsBySelector) ->
     for selector, keyBindings of keyBindingsBySelector
-      # Verify selector is valid before registering any bindings
-      try
-        document.body.webkitMatchesSelector(selector)
-      catch
-        console.warn("Encountered an invalid selector adding keybindings from '#{source}': '#{selector}'")
-        continue
+      @addKeyBindingsForSelector(source, selector, keyBindings)

-      for keystroke, command of keyBindings
-        keyBinding = new KeyBinding(source, command, keystroke, selector)
-        @keyBindings.push(keyBinding)
+  addKeyBindingsForSelector: (source, selector, keyBindings) ->
+    # Verify selector is valid before registering any bindings
+    try
+      document.body.webkitMatchesSelector(selector)
+    catch
+      console.warn("Encountered an invalid selector adding keybindings from '#{source}': '#{selector}'")
+      return
+
+    for keystroke, command of keyBindings
+      keyBinding = new KeyBinding(source, command, keystroke, selector)
+      @keyBindings.push(keyBinding)

   handleKeyboardEvent: (event) ->
     @keystrokes.push(@keystrokeForKeyboardEvent(event))
@@ -77,10 +82,16 @@

   # Deprecated: Handle a jQuery keyboard event. Use {::handleKeyboardEvent} with
   # a raw keyboard event instead.
-  handleEvent: (event) ->
-    @handleKeyboardEvent(event.originalEvent ? event)
+  handleKeyEvent: (event) ->
+    event = event.originalEvent ? event
+    @handleKeyboardEvent(event)
+    false if event.defaultPrevented

   # Deprecated: Translate a jQuery keyboard event to a keystroke string. Use
   # {::keystrokeForKeyboardEvent} with a raw KeyboardEvent instead.
   keystrokeStringForEvent: (event) ->
     @keystrokeForKeyboardEvent(event.originalEvent ? event)
+
+  # Deprecated: Use addKeyBindings with a map from selectors to keybindings
+  bindKeys: (source, selector, keyBindings) ->
+    @addKeyBindingsForSelector(source, selector, keyBindings)


Tidied up some comments

Tidied up some comments

--- src/klarna.coffee
+++ src/klarna.coffee
@@ -81,6 +81,7 @@
           reject "'%s' not set", key
       break

+  # If no problems -> return original function
   return f

 parseError = (error, response, body) ->


re-number questions on new question

re-number questions on new question

--- app/assets/javascripts/angular/v2/controllers/instrument_questions_ctrl.js.coffee
+++ app/assets/javascripts/angular/v2/controllers/instrument_questions_ctrl.js.coffee
@@ -159,7 +159,16 @@
     else
       selectedQuestions = _.where($scope.questionSetQuestions, {selected: true})
       responseCount = 0
-      previousQuestionCount = $scope.instrumentQuestions.length
+      previousQuestion = _.last($scope.displayQuestions($scope.display))
+      if previousQuestion
+        previousQuestionCount = previousQuestion.number_in_instrument
+      else
+        previousDisplay = $scope.displays[$scope.display.position - 2]
+        if previousDisplay
+          lastQuestion = _.last($scope.displayQuestions(previousDisplay))
+          previousQuestionCount = lastQuestion.number_in_instrument
+        else
+          previousQuestionCount = 0
       angular.forEach $scope.questionSetQuestions, (q, i) ->
         if q.selected
           iq = new InstrumentQuestion()
@@ -172,8 +181,13 @@
             (data, headers) ->
               responseCount += 1
               if responseCount ==  selectedQuestions.length
-                $state.reload()
+                $scope.showNewQuestion = false
+                $scope.currentDisplay = null
+                iq.identifier = q.question_identifier
+                $scope.instrumentQuestions.push(iq)
+                $scope.renumberDisplaysAndQuestions()
             (result, headers) ->
+              alert(result.data.errors)
           )
           previousQuestionCount += 1


Fix \def processing: full-word match, some spacing workarounds

--- lib/formats.coffee
+++ lib/formats.coffee
@@ -249,9 +249,18 @@
   #for def, val of defs
   #  console.log "\\#{def} = #{val}"
   if 0 < _.size defs
-    r = ///\\(#{_.keys(defs).join '|'})\s*///g
-    while 0 <= tex.search(r)
-      tex = tex.replace r, (match, def) -> defs[def]
+    r = ///
+      ({?)     # allow {\macro} notation to force space after macro
+      \\(#{_.keys(defs).join '|'})
+      (?![a-zA-z])  # ensure full-word match
+      \s*      # consume spaces after \macro, like TeX does
+      (?:{})?  # allow \macro{} notation to force space after macro
+      (}?)     # allow {\macro} notation to force space after macro
+    ///g
+    while r.test tex
+      tex = tex.replace r, (match, left, def, right) ->
+        left = right = '' if left and right
+        left + defs[def] + right
   tex

 texAlign =


Fix displaying following status on ProfileView

--- client/landing/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
+++ client/landing/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
@@ -54,6 +54,16 @@
       ]
     , memberData

+    unless memberData.followee
+      KD.whoami().isFollowing? memberData.getId(), "JAccount", (following) =>
+        memberData.followee = following
+        if memberData.followee
+          #@followButton.setClass 'following-btn following-topic'
+          @followButton.setState "Unfollow"
+        else
+          @followButton.setState "Follow"
+          #@followButton.unsetClass 'following-btn following-topic'
+
     @skillTags = @putSkillTags()

     @followers = new KDView


preserve center and zoom when resizing

preserve center and zoom when resizing

--- src/ui/canvas.coffee
+++ src/ui/canvas.coffee
@@ -1,11 +1,6 @@
 app.canvas = (options) ->
   map = options.map
   bbox = map.db.bbox
-
-  map.width = 1
-  map.height = 1
-  map.sc = 1
-  map.tr = [0, 0]

   extent = {
     w: d3.max([-bbox[0], bbox[2]]) * 2
@@ -63,16 +58,30 @@
       .attr('class', 'actionbar')

   resize = ->
+    if map.sc?
+      oldCenter = [map.width / 2, map.height / 2]
+
     map.width = parseInt(d3.select('body').style('width'))
     map.height = parseInt(d3.select('body').style('height'))
+    minScale = d3.min([map.width / extent.w, map.height / extent.h])
+
+    if map.sc?
+      sc = map.sc
+      tr = map.tr
+      center = projection.invert(oldCenter)
+
+    else
+      sc = minScale
+      tr = [0, 0]
+      center = [0, 0]

     svg.select('.zoomrect')
         .attr('width', map.width)
         .attr('height', map.height)

-    updateProjection(d3.min([map.width / extent.w, map.height / extent.h]), [0, 0])
-    map.centerAt([0, 0], map.sc)
-    zoom.scaleExtent([map.sc, 3])
+    updateProjection(sc, tr)
+    map.centerAt(center, map.sc)
+    zoom.scaleExtent([minScale, 3])

     actionbar.attr('transform', "translate(0, #{map.height - app.ACTIONBAR_HEIGHT})")
     actionbar.select('.background').attr('width', map.width)


fix ajax requests not returning proper error instance

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -14,14 +14,15 @@
     async.retry 3, (step) ->
       $.ajax({url: url, timeout: 10000})
         .fail (err) ->
-          step err
+          return step err
         .done (body) ->
           step null, body

     , (err, results) ->
       if err
         window.log.error(err)
-        return done(err)
+        return done(new Error(err))
+
       return done null, results


:fire: Remove g flag from regex

:fire: Remove g flag from regex

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -61,7 +61,7 @@
       regex = xcache.get(rawRegex)
     else
       xcache.set(rawRegex, regex = XRegExp(rawRegex))
-    for line in data.split(/\r?\n/g)
+    for line in data.split(/\r?\n/)
       match = XRegExp.exec(line, regex)
       if match
         options.baseReduction = 1 unless options.baseReduction


Guard against empty jQuery array

Guard against empty jQuery array

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -193,7 +193,7 @@

 # Trigger attach event when views are added to the DOM
 callAttachHook = (element) ->
-  return unless element instanceof jQuery
+  return unless element instanceof jQuery and element[0]
   onDom = element.isOnDom?()

   elementsWithHooks = []


Throw exception when ::command is called on any jQuery object

And delete pre-existing definitions.

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -190,17 +190,6 @@

   end: ->
     @prevObject ? jQuery(null)
-
-  # Public: Register a command handler on this element.
-  #
-  # This method registers a command listener for this element on the Atom
-  # command registry
-  #
-  # * `commandName` A namespaced {String} describing the command, such as
-  #   `find-and-replace:toggle`.
-  # * `handler` A {Function} to execute when the command is triggered.
-  command: (commandName, handler) ->
-    super(commandName, handler)

   # Public: Preempt events registered with jQuery's `::on`.
   #
@@ -411,11 +400,6 @@
 $.fn.trueWidth = ->
   @[0].getBoundingClientRect().width

-$.fn.command = (eventName, handler) ->
-  if @length > 0
-    atom.commands.add @[0], eventName, (event) =>
-      handler.call(this, $.event.fix(event))
-
 $.fn.iconSize = (size) ->
   @width(size).height(size).css('font-size', size)

@@ -435,7 +419,7 @@
     docs at https://atom.io/docs/api/latest/CommandRegistry#instance-add.
   """

-View::command = ->
+$.fn.command = (eventName, handler) ->
   throw new Error """
     `command` is no longer available. Please subscribe to commands via
     `atom.commands.add`. See the docs at


change key bind to chrome/xbox360

change key bind to chrome/xbox360

--- plugins/gamepadprocedure.coffee
+++ plugins/gamepadprocedure.coffee
@@ -117,8 +117,8 @@
         padbuttons[2] = buttons[2].pressed
         padbuttons[3] = buttons[3].pressed

-        padbuttons[4] = buttons[4].pressed
-        padbuttons[5] = buttons[5].pressed
+        padbuttons[4] = buttons[6].pressed
+        padbuttons[5] = buttons[4].pressed

         padbuttons[6] = buttons[8].pressed
         padbuttons[7] = buttons[9].pressed


sort feed chronologically

sort feed chronologically

--- components/lightbox/client/lightbox_view.coffee
+++ components/lightbox/client/lightbox_view.coffee
@@ -40,6 +40,9 @@
       type: 'block'
       object_id: @model.id

+    feed.comparator = (group) ->
+      new Date group.models[0].get('created_at')
+
     new SmallFeedView
       el: @$ "#lightbox__feed_inner"
       collection: feed
@@ -47,6 +50,7 @@
     new NewCommentView
       el: @$ "#lightbox__new-comment"
       collection: feed
+      block_id: @model.id
       autoRender: true

     IconicJS().inject 'img.iconic'


spinner: make sure isAppended is up to date

--- app/_src/helpers/spinner.coffee
+++ app/_src/helpers/spinner.coffee
@@ -44,6 +44,7 @@

 	removeEl: ->
 		@$spinner.remove() if @$spinner.length
+		@isAppended = false

 	show: =>
 		@appendEl() unless @isAppended
@@ -54,9 +55,9 @@
 		, BEFORE_HIDE_DURATION_MS * 1.2

 	hide: (isToBeRemoved) =>
-		return @animateOut(isToBeRemoved) if @hasDeffered isnt true
+		return @animateOut(isToBeRemoved) unless @hasDeffered
 		$.when( @deffered.promise() ).done =>
-			@animateOut(isToBeRemoved)
+			@animateOut isToBeRemoved

 	animateOut: (isToBeRemoved) =>
 		TweenLite.to @$spinner, .3, {


fix this.key

fix this.key

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -64,17 +64,18 @@
 			this.start = new Date()
 			this.key = key
 			this.sampleRate = sampleRate
+
 		done:->
 			timeSpan = new Date - this.start
 			statsd.timing(buildKey(this.key), timeSpan, this.sampleRate)
-			if !summaries[key]?
+			if !summaries[this.key]?
 				summary = new client.Summary({
-					name: "#{name}_#{key}",
-					help: key,
+					name: "#{name}_#{this.key}",
+					help: this.key,
 					maxAgeSeconds: 600,
 					ageBuckets: 10
 				})
-			summaries[key].observe(timeSpan)
+			summaries[this.key].observe(timeSpan)
 			return timeSpan

 	gauge : (key, value, sampleRate = 1)->


additional test

additional test

--- src/spec/helpers/project-credentials-config.spec.coffee
+++ src/spec/helpers/project-credentials-config.spec.coffee
@@ -41,3 +41,16 @@
     .fail (error) ->
       done(error)
     .done()
+
+  it "should throw an error if credentials are not defined", (done) ->
+    ProjectCredentialsConfig.create
+      lookupFiles: ['data/config/config3']
+    .then (config) ->
+      try
+        expect(config.forProjectKey('non-existing-project'))
+        done("Error not thrown")
+      catch error
+        done()
+    .fail (error) ->
+      done(error)
+    .done()


Add bulk method from range tracker.

Add bulk method from range tracker.

--- app/coffee/RangesTracker.coffee
+++ app/coffee/RangesTracker.coffee
@@ -100,6 +100,21 @@
 			change = @getChange(change_id)
 			return if !change?
 			@_removeChange(change)
+
+		removeChangeIds: (change_to_remove_ids) ->
+			return if !change_to_remove_ids?.length > 0
+			i = @changes.length
+			remove_change_id = {}
+			for change_id in change_to_remove_ids
+				remove_change_id[change_id] = true
+
+			while (i--)
+				if remove_change_id[@changes[i].id]
+					delete remove_change_id[@changes[i].id]
+					removed_change = @changes.splice(i, 1)[0]
+					@_markAsDirty removed_change, "change", "removed"
+					if Object.keys(remove_change_id).length == 0
+						break

 		validate: (text) ->
 			for change in @changes


Bug regarding selection window movement corrected

--- assets/coffeescript/main.coffee
+++ assets/coffeescript/main.coffee
@@ -139,7 +139,9 @@
     find_closest_sample: (index, offset, direction) ->
         cur_sample = @reading.samples[index]
         # find the sandwiching samples
-        index += direction while (prev_sample = cur_sample; cur_sample = @reading.samples[index + direction]) and !(prev_sample.time * direction <= offset * direction < cur_sample.time * direction)
+        while (prev_sample = cur_sample; cur_sample = @reading.samples[index + direction]) and
+                !(offset * direction < cur_sample.time * direction)
+            index += direction
         # choose the closer one
         if cur_sample and (offset - prev_sample.time) * direction > (cur_sample.time - offset) * direction
             index += direction


KD: Add list of allowedApps a.k.a internal koding apps

--- core/KD.coffee
+++ core/KD.coffee
@@ -225,4 +225,5 @@

 KD.enableLogs() if not KD.config?.suppressLogs

+KD.config.allowedApps = ['account']
 prettyPrint = noop


Use camelCase property.

Use camelCase property.

--- app/javascripts/modeling.coffee
+++ app/javascripts/modeling.coffee
@@ -5,7 +5,7 @@
     # The common modeling x-axis specification.
     x =
       label: 'Visit Date'
-      accessor: (session) -> session.acquisition_date.valueOf()
+      accessor: (session) -> session.acquisitionDate.valueOf()

     # The chart-specific y-axis specifications.
     y =
@@ -16,12 +16,12 @@
             {
               label: 'FXL Ktrans'
               color: 'BurlyWood'
-              accessor: (session) -> session.modeling.fxl_k_trans
+              accessor: (session) -> session.modeling.fxlKTrans
             }
             {
               label: 'FXR Ktrans'
               color: 'OliveDrab'
-              accessor: (session) -> session.modeling.fxr_k_trans
+              accessor: (session) -> session.modeling.fxrKTrans
             }
           ]
       ve:
@@ -90,9 +90,9 @@
               (current - previous)/previous * 100

             # Return an object with the change properties.
-            delta_k_trans_pct_change: percent_change(current.delta_k_trans, previous.delta_k_trans)
-            fxl_k_trans_pct_change: percent_change(current.fxl_k_trans, previous.fxl_k_trans)
-            fxr_k_trans_pct_change: percent_change(current.fxr_k_trans, previous.fxr_k_trans)
+            deltaKTrans_pct_change: percent_change(current.deltaKTrans, previous.deltaKTrans)
+            fxlKTrans_pct_change: percent_change(current.fxlKTrans, previous.fxlKTrans)
+            fxrKTrans_pct_change: percent_change(current.fxrKTrans, previous.fxrKTrans)
             v_e_pct_change: percent_change(current.v_e, previous.v_e)
             tau_i_pct_change: percent_change(current.tau_i, previous.tau_i)


How to rename files, the git way

How to rename files, the git way

--- SingularityUI/app/views/ExpandableTableSubview.coffee
+++ SingularityUI/app/views/ExpandableTableSubview.coffee
@@ -1,109 +0,0 @@
-View = require './view'
-
-# Reusable view for paginable tables
-#
-# You feed it a (server-side paginable) collection
-# and a template and it works its magic
-#
-# If it's provided with a `.page-header h1` it can also be
-# expanded to fit the entire page and shrunk back down after
-class ExpandableTableSubview extends View
-
-    buttonsTemplate: require './templates/tableSubviewButtons'
-
-    expanded: false
-
-    events: ->
-        _.extend super,
-            'click [data-action="next-page"]': 'nextPage'
-            'click [data-action="previous-page"]': 'previousPage'
-            'click [data-action="expand"]': 'expand'
-            'click [data-action="shrink"]': 'shrink'
-
-    initialize: ({ @collection, @template }) ->
-        @listenTo @collection, 'sync', @render
-
-    render: ->
-        # If we've already rendered stuff and now we're trying to render
-        # an empty collection (`next` returned an empty list)
-        if @collection.length is 0 and not @$el.is ':empty'
-            # Disable the next button and don't render anything
-            @$('[data-action="next-page"]').attr 'disabled', true
-            @collection.currentPage -= 1
-            return
-
-        # For after the render
-        haveButtons = @$('.table-subview-buttons').length
-
-        @$el.html @template
-            synced:  @collection.synced
-            data:    _.pluck @collection.models, 'attributes'
-
-        # Stop right here if we don't need to append the expand links and the buttons
-        return if @collection.length isnt @collection.atATime and not haveButtons
-
-        # Append expand / shrink link
-        $header = @$('.page-header h1')
-        if $header.length
-            $header.find('small').remove()
-            if not @expanded
-                $header.append 'more at once'
-            else if @expanded
-                $header.append 'fewer at once'
-
-        # Append next / previous page buttons
-        hasPrevButton = @collection.currentPage isnt 1
-        hasNextButton = @collection.length is @collection.atATime
-
-        @$el.append @buttonsTemplate {hasPrevButton, hasNextButton}
-
-    nextPage: ->
-        @collection.currentPage += 1 unless @collection.length isnt @collection.atATime
-        @collection.fetch()
-
-    previousPage: ->
-        @collection.currentPage -= 1 unless @collection.currentPage is 1
-        @collection.fetch()
-
-    expand: ->
-        @expanded = true
-
-        # Container dimensions
-        containerOffset = @$el.offset().top
-        containerHeight = @$el.height()
-        # Table dimensions
-        $table = @$('table')
-        tableOffset = $table.offset().top
-        tableHeight = $table.height()
-
-        # Figure out spaces
-        spaceAboveTable = containerOffset - tableOffset
-        spaceUnderTable = containerHeight - spaceAboveTable - tableHeight
-
-        $firstRow = $ @$('tbody tr')[0]
-        firstRowHeight = $firstRow.height()
-
-        pageHeight = $(window).height()
-
-        # Since it's fixed. 1.5 for a little margin
-        navHeight = app.views.nav.$el.height() * 1.5
-        # Take away the stuff above and under the table from the size of the page
-        availableSpace = pageHeight - spaceAboveTable - spaceUnderTable - navHeight
-        # How many rows d'ya think we can fit in?
-        canFit = Math.floor availableSpace / firstRowHeight
-
-        # - 1 just in case
-        @collection.atATime = canFit - 1
-        @collection.currentPage = 1
-
-        @collection.fetch().done =>
-            $(window).scrollTop @$el.offset().top - navHeight
-
-    shrink: ->
-        @expanded = false
-
-        @collection.atATime = 5
-        @collection.currentPage = 1
-        @collection.fetch()
-
-module.exports = ExpandableTableSubview


Fix admin page

Fix admin page

[#95961470]

--- review_front/app/components/permission-cell.coffee
+++ review_front/app/components/permission-cell.coffee
@@ -5,13 +5,13 @@
   classNames: ['permissions', 'text-center']
   classNameBindings: ['permission.isAllowed:success:danger']

-  permission: (() ->
+  permission: Ember.computed('project', 'user', ->
     user = @get('user')
     project = @get('project')
-    permission = user.get('permission').find (item) ->
+    permission = user.get('permissions').find (item) ->
       item.get('project.id') == project.get('id')
     permission ||= @createPermission(user, project)
-  ).property('project', 'user')
+  )

   createPermission: (user, project) ->
     permission = @store.createRecord('permission', {


Remove use of fs-plus shims

Remove use of fs-plus shims

--- spec/status-bar-spec.coffee
+++ spec/status-bar-spec.coffee
@@ -54,7 +54,7 @@
   describe "when the buffer content has changed from the content on disk", ->
     it "disables the buffer modified indicator on save", ->
       filePath = path.join(os.tmpdir(), "atom-whitespace.txt")
-      fs.writeSync(filePath, "")
+      fs.writeFileSync(filePath, "")
       atom.rootView.openSync(filePath)
       editor = atom.rootView.getActiveView()
       expect(StatusBar.fileInfo.bufferModified.text()).toBe ''
@@ -108,7 +108,7 @@

   describe "git branch label", ->
     beforeEach ->
-      fs.remove(path.join(os.tmpdir(), '.git')) if fs.isDirectorySync(path.join(os.tmpdir(), '.git'))
+      fs.removeSync(path.join(os.tmpdir(), '.git')) if fs.isDirectorySync(path.join(os.tmpdir(), '.git'))
       atom.rootView.attachToDom()

     it "displays the current branch for files in repositories", ->
@@ -131,29 +131,29 @@

     beforeEach ->
       projectPath = atom.project.resolve('git/working-dir')
-      fs.move(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))
+      fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))
       atom.project.setPath(projectPath)
       filePath = atom.project.resolve('a.txt')
       newPath = atom.project.resolve('new.txt')
-      fs.writeSync(newPath, "I'm new here")
+      fs.writeFileSync(newPath, "I'm new here")
       ignorePath = path.join(projectPath, '.gitignore')
-      fs.writeSync(ignorePath, 'ignored.txt')
+      fs.writeFileSync(ignorePath, 'ignored.txt')
       ignoredPath = path.join(projectPath, 'ignored.txt')
-      fs.writeSync(ignoredPath, '')
+      fs.writeFileSync(ignoredPath, '')
       atom.project.getRepo().getPathStatus(filePath)
       atom.project.getRepo().getPathStatus(newPath)
-      originalPathText = fs.read(filePath)
+      originalPathText = fs.readFileSync(filePath, 'utf8')
       atom.rootView.attachToDom()

     afterEach ->
-      fs.writeSync(filePath, originalPathText)
-      fs.remove(newPath)
-      fs.remove(ignorePath)
-      fs.remove(ignoredPath)
-      fs.move(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'))
+      fs.writeFileSync(filePath, originalPathText)
+      fs.removeSync(newPath)
+      fs.removeSync(ignorePath)
+      fs.removeSync(ignoredPath)
+      fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'))

     it "displays the modified icon for a changed file", ->
-      fs.writeSync(filePath, "i've changed for the worse")
+      fs.writeFileSync(filePath, "i've changed for the worse")
       atom.project.getRepo().getPathStatus(filePath)
       atom.rootView.openSync(filePath)
       expect(StatusBar.git.gitStatusIcon).toHaveClass('icon-diff-modified')
@@ -171,16 +171,16 @@
       expect(StatusBar.git.gitStatusIcon).toHaveClass('icon-diff-ignored')

     it "updates when a status-changed event occurs", ->
-      fs.writeSync(filePath, "i've changed for the worse")
+      fs.writeFileSync(filePath, "i've changed for the worse")
       atom.project.getRepo().getPathStatus(filePath)
       atom.rootView.openSync(filePath)
       expect(StatusBar.git.gitStatusIcon).toHaveClass('icon-diff-modified')
-      fs.writeSync(filePath, originalPathText)
+      fs.writeFileSync(filePath, originalPathText)
       atom.project.getRepo().getPathStatus(filePath)
       expect(StatusBar.git.gitStatusIcon).not.toHaveClass('icon-diff-modified')

     it "displays the diff stat for modified files", ->
-      fs.writeSync(filePath, "i've changed for the worse")
+      fs.writeFileSync(filePath, "i've changed for the worse")
       atom.project.getRepo().getPathStatus(filePath)
       atom.rootView.openSync(filePath)
       expect(StatusBar.git.gitStatusIcon).toHaveText('+1')


shells/youtube: embed src is now always https

--- coffee/src/shells/youtube.shell.coffee
+++ coffee/src/shells/youtube.shell.coffee
@@ -55,7 +55,7 @@

   embedLink: (options) =>
     # see https://developers.google.com/youtube/player_parameters for options
-    "http://www.youtube.com/embed/#{@youtubeId()}?" +
+    "https://www.youtube.com/embed/#{@youtubeId()}?" +
          '&fs=1' +
        # '&modestbranding=1' +
          '&iv_load_policy=3' +


Add varying enemy spawn frequency depending on selected level

--- src/script/EnemyManager.coffee
+++ src/script/EnemyManager.coffee
@@ -4,12 +4,17 @@
 class EnemyManager

   maxEnemies: 15
-  spawnFrequency: 3 #seconds

   constructor: ->
     @enemiesOnScreen = 0
     enemies = game.add.group()
     enemies.enableBody = true
+
+    switch currentLevel
+      when 'one'   then @spawnFrequency = 4 #seconds
+      when 'two'   then @spawnFrequency = 3 #seconds
+      when 'three' then @spawnFrequency = 2 #seconds
+      when 'four'  then @spawnFrequency = 1 #second

     game.time.events.loop(Phaser.Timer.SECOND * @spawnFrequency, @spawnWrapper, this)


Atom spec no longer closes all open windows

This makes life easier when you're running the spec suite while actually using atom to edit itself.

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -1,37 +1,32 @@
 Atom = require 'atom'
 fs = require 'fs'
+_ = require 'underscore'

 describe "Atom", ->
-  closeAllWindows = ->
-    window.close() for window in atom.windows
-    waitsFor "there to be no windows", ->
-      atom.windows.length == 0
-
   beforeEach ->
     spyOn(Atom.prototype, "setUpKeymap")

   describe ".open(path)", ->
-    beforeEach ->
-      closeAllWindows()
+    newWindow = null

     afterEach ->
-      closeAllWindows()
+      newWindow?.close()

     describe "when opening a file", ->
       it "displays it in a new window with the contents of the file loaded", ->
         filePath = null

         filePath = require.resolve 'fixtures/sample.txt'
-        expect(atom.windows.length).toBe 0
+        previousWindowCount = atom.windows.length

         atom.open filePath

         waitsFor "window to open", ->
-          atom.windows.length > 0
+          atom.windows.length > previousWindowCount

         runs ->
-          expect(atom.windows.length).toBe 1
-          newWindow = atom.windows[0]
+          expect(atom.windows.length).toBe previousWindowCount + 1
+          newWindow = _.last(atom.windows)
           expect(newWindow.rootView.activeEditor().buffer.getPath()).toEqual filePath
           expect(newWindow.rootView.activeEditor().buffer.getText()).toEqual fs.read(filePath)


Activity: blur the input view after submission

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -146,7 +146,7 @@
   reset: (unlock = yes) ->

     @input.empty()
-    @input.blur()
+    @input.setBlur()
     @embedBox.resetEmbedAndHide()

     if unlock then @unlockSubmit()


activity: deprecate duplicate call to embedly box reset procedure

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -205,7 +205,6 @@
     return @showError err  if err

     @reset yes
-    @embedBox.resetEmbedAndHide()
     @emit "Submit", activity

     KD.mixpanel "Status update create, success", { length: activity?.body?.length }


AccountCredentialList: Add a comment line

--- client/account/lib/accountcredentiallist.coffee
+++ client/account/lib/accountcredentiallist.coffee
@@ -116,6 +116,7 @@
     new AccountCredentialEditModal { provider, credential }, data

+  # Move this method to controller.
   verify: (item) ->

     credential  = item.getData()


activity: add new ensureMessage action

activity: add new ensureMessage action

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -96,6 +96,30 @@
     fetchingMap[messageId] = no

+ensureMessage = (messageId) ->
+
+  message = kd.singletons.reactor.evaluate ['MessagesStore', messageId]
+
+  if message
+    return new Promise (resolve) -> resolve { message: message.toJS() }
+
+  new Promise (resolve) ->
+    loadMessage(messageId).then ({ message }) ->
+      channelId = message.initialChannelId
+      putLoaderMarker channelId, messageId, { position: 'before', autoload: yes }
+      putLoaderMarker channelId, messageId, { position: 'after', autoload: yes }
+      resolve { message }
+
+
+putLoaderMarker = (channelId, messageId, options) ->
+
+  dispatch actionTypes.ACTIVATE_LOADER_MARKER,
+    channelId : channelId
+    messageId : messageId
+    position  : options.position
+    autoload  : options.autoload
+
+
 ###*
  * Action to load message with given slug.
  *
@@ -411,6 +435,7 @@
 module.exports = {
   loadMessages
   loadMessageBySlug
+  ensureMessage
   createMessage
   likeMessage
   unlikeMessage


docs: Added missing doc

docs: Added missing doc

--- client/app/lib/providers/snapshothelpers.coffee
+++ client/app/lib/providers/snapshothelpers.coffee
@@ -24,7 +24,16 @@

 ###*
- * Create a new vm from the given snapshotId.
+ * Create a new vm from the given snapshotId, going directly to reinit
+ * for hobbyist's, and showing the new machine modal for all other
+ * users.
+ *
+ * @param {Machine} machine - The machine to reinit a snapshot onto, in
+ *  the event that it's needed (hobbyists).
+ * @param {String} snapshotId - The snapshotId to create the machine from
+ * @param {Function()} callback - Called once the process has begin, *not*
+ *  when the process is entirely done. (Eg, after plan confirmations have
+ *  taken place, etc).
 ###
 newVmFromSnapshot = (machine, snapshotId, callback = kd.noop) ->


Fixes null link

Fixes null link

--- client/apps/edit/components/layout/index.coffee
+++ client/apps/edit/components/layout/index.coffee
@@ -163,7 +163,6 @@
           @article.replaceLink(findText, @getNewLinkFromHits(valid_results) || text)
           return cb()
     ), (err, result) =>
-      console.log 'herererere'
       @article.sections.trigger 'change:autolink'
       $('#autolink-status').removeClass('searching').html('Auto-Link')
       $('#edit-content__overlay').removeClass('disabled')
@@ -175,7 +174,7 @@
     result = results[0]
     name = result._source.name
     link = @findLinkFromResult(result)
-    "#{name}"
+    return "#{name}" if link

   findLinkFromResult: (result) ->
     switch result._type


any live cell with exactly three live neighbors should become a live cell

--- spec/lib/grid-spec.coffee
+++ spec/lib/grid-spec.coffee
@@ -51,3 +51,11 @@
     Given -> @grid.live 4,5
     When -> @grid = @grid.generate()
     Then -> expect(@grid.cell(4,4)).toBe false
+
+  describe 'with any dead cell with exactly three live neighbors becomes a live cell', ->
+
+    Given -> @grid.live 4,3
+    Given -> @grid.live 5,4
+    Given -> @grid.live 4,5
+    When -> @grid.generate()
+    Then -> expect(@grid.cell(4,4)).toBe true


Removed canvas and contexts from grid spec.

--- spec/lib/grid_spec.coffee
+++ spec/lib/grid_spec.coffee
@@ -1,7 +1,6 @@
 "use strict"

 Grid = require "./../../lib/grid"
-Canvas = require "canvas"

 describe "getCenterXY", ->
   it "expects tile options", ->
@@ -10,10 +9,8 @@
     expect(grid.tileSize).toBeDefined()
     expect(grid.pointyTiles).toBeDefined()

-  it "calculates pixel coordinates for canvas based on tile and grid options", ->
-    canvas = new Canvas(1,1)
-    context = canvas.getContext("2d")
-    grid = new Grid(context)
+  it "calculates pixel coordinates", ->
+    grid = new Grid()

     grid.tileSpacing = 0
     grid.tileSize = 100
@@ -33,9 +30,7 @@

 describe "hexagonCoordinates", ->
   beforeEach ->
-    @canvas = new Canvas(0,0)
-    @context = @canvas.getContext("2d")
-    @grid = new Grid(@context)
+    @grid = new Grid()

   it "expects grid options", ->
     expect(@grid.withOrigin).toBeDefined()


:lipstick:

:lipstick:

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -218,7 +218,7 @@
       snippets.loaded = false
       snippets.loadAll()

-      waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
+      waitsFor "all snippets to load", 5000, -> snippets.loaded

       runs ->
         expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
@@ -232,7 +232,7 @@
       snippets.loaded = false
       snippets.loadAll()

-      waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
+      waitsFor "all snippets to load", 5000, -> snippets.loaded

       runs ->
         snippet = syntax.getProperty(['.source.js'], 'snippets.fun')


Remove unused requires

Remove unused requires

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -6,8 +6,6 @@
 Editor = require 'editor'
 _ = require 'underscore'
 fs = require 'fs'
-AtomPackage = require 'atom-package'
-TextMatePackage = require 'text-mate-package'

 describe "Snippets extension", ->
   [buffer, editor] = []


Revert "Remove firmware update check in connecting vc"

This reverts commit ed4d42de7ebd74ebc3d784105764f2b842528d69.

--- app/src/controllers/onboarding/device/onboarding_device_connecting_view_controller.coffee
+++ app/src/controllers/onboarding/device/onboarding_device_connecting_view_controller.coffee
@@ -25,20 +25,15 @@

   _navigateContinue: ->
     @_stopTimer()
-    ledger.app.wallet.getState (state) =>
-      if state == ledger.wallet.States.LOCKED
-        ledger.app.router.go '/onboarding/device/pin'
+    ledger.app.wallet?.isFirmwareUpdateAvailable (isAvailable) =>
+      if isAvailable
+        ledger.app.router.go '/onboarding/device/update'
       else
-        ledger.app.router.go '/onboarding/management/welcome'
-#    ledger.app.wallet?.isFirmwareUpdateAvailable (isAvailable) =>
-#      if isAvailable
-#        ledger.app.router.go '/onboarding/device/update'
-#      else
-#        ledger.app.wallet.getState (state) =>
-#          if state == ledger.wallet.States.LOCKED
-#            ledger.app.router.go '/onboarding/device/pin'
-#          else
-#            ledger.app.router.go '/onboarding/management/welcome'
+        ledger.app.wallet.getState (state) =>
+          if state == ledger.wallet.States.LOCKED
+            ledger.app.router.go '/onboarding/device/pin'
+          else
+            ledger.app.router.go '/onboarding/management/welcome'

   _navigateForged: ->
     @_stopTimer()
@@ -46,13 +41,12 @@
       if error?
         ledger.app.router.go '/onboarding/device/forged'
       else
-        @_navigateContinue()
-#        ledger.app.wallet?.isFirmwareOverwriteOrUpdateAvailable (isAvailable) =>
-#          if isAvailable and not ledger.fup.versions.Nano.CurrentVersion.Beta
-#            ledger.app.setExecutionMode(ledger.app.Modes.FirmwareUpdate)
-#            ledger.app.router.go '/update/index', {hidePreviousButton: yes}
-#          else
-#            @_navigateContinue()
+        ledger.app.wallet?.isFirmwareOverwriteOrUpdateAvailable (isAvailable) =>
+          if isAvailable and not ledger.fup.versions.Nano.CurrentVersion.Beta
+            ledger.app.setExecutionMode(ledger.app.Modes.FirmwareUpdate)
+            ledger.app.router.go '/update/index', {hidePreviousButton: yes}
+          else
+            @_navigateContinue()

   _navigateError: ->
     @_stopTimer()


fix: autohide wasn't enabled when state.enabled === undefined, ie when starting it for the first time

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -43,7 +43,8 @@
     treeView = treeViewPkg.mainModule.createView()
     treeViewEl = atom.views.getView treeView
     # enable if was enabled in previous session
-    @enabled = state.enabled
+    state.enabled ?= true
+    {@enabled} = state
     @enable() if @enabled

     # respond to changes in the config


use atom.config.get instead of keeping a reference to the config object

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -17,6 +17,10 @@
   new Promise (resolve) ->
     process.nextTick ->
       resolve()
+
+getConfig = (key) -> atom.config.get "autohide-tree-view.#{key}"
+
+setConfig = (key, value) -> atom.config.set "autohide-tree-view.#{key}", value

 class AutohideTreeView
   config:
@@ -70,9 +74,7 @@
     SubAtom ?= require 'sub-atom'
     @disposables = new SubAtom()

-    # keep a reference to the package config
-    @conf = atom.config.get 'autohide-tree-view'
-    {pushEditor} = @conf
+    pushEditor = getConfig 'pushEditor'

     # wait until the tree view package is activated
     atom.packages.activatePackage 'tree-view'
@@ -81,28 +83,31 @@
       @handleEvents()
       # start with pushEditor = true, we'll change it back later
       # activating the package looks much better this way
-      @conf.pushEditor = true
+      setConfig 'pushEditor', true
       @update()
     .then =>
       # reset the pushEditor setting to the user value
-      @conf.pushEditor = pushEditor
+      setConfig 'pushEditor', pushEditor
       @update()
     .catch error

   deactivate: ->
     # deactivating looks better with pushEditor = true
-    @conf.pushEditor = true
+    pushEditor = getConfig 'pushEditor'
+    setConfig 'pushEditor', true
     # the stylesheet will be removed before the animation is finished
     # so set minWidth on the element
     @treeViewEl.style.minWidth = '1px'
     # apply new pushEditor value, then show the tree view
     @update().then =>
+      # reset the pushEditor setting to the user value
+      setConfig 'pushEditor', pushEditor
       # show the tree view
       @show 0
     .then =>
       # finally dispose of everything
       @disposables.dispose()
-      [@disposables, @conf, @visible] = []
+      [@disposables, @visible] = []
     .catch error

   registerTreeView: (treeViewPkg) ->
@@ -122,9 +127,6 @@
       [@treeView, @treeViewEl] = []

   handleEvents: ->
-    # keep config reference updated
-    @disposables.add atom.config.onDidChange 'autohide-tree-view', ({newValue}) => @conf = clone newValue
-
     # changes to these settings should trigger an update
     @disposables.add atom.config.onDidChange 'autohide-tree-view.pushEditor', => @update()
     @disposables.add atom.config.onDidChange 'autohide-tree-view.hiddenWidth', => @update()
@@ -202,17 +204,17 @@
   # updates styling on the .tree-view-resizer and the panel element
   update: ->
     promiseNextTick().then =>
-      if @conf.pushEditor
+      if getConfig('pushEditor')
         @treeViewEl.style.position = 'relative'
         atom.views.getView(@treeView.panel)?.style.width = ''
       else
         @treeViewEl.style.position = 'absolute'
-        atom.views.getView(@treeView.panel)?.style.width = "#{@conf.hiddenWidth}px"
+        atom.views.getView(@treeView.panel)?.style.width = "#{getConfig('hiddenWidth')}px"
       @resize()
     .catch error

   # show the tree view
-  show: (delay = @conf.showDelay, disableHoverEvents = false) ->
+  show: (delay = getConfig('showDelay'), disableHoverEvents = false) ->
     # disable hover events on the tree view when not triggered
     # by a hover event
     @disableHoverEvents() if disableHoverEvents
@@ -228,14 +230,14 @@
       @treeView.focus() if finished

   # hide the tree view
-  hide: (delay = @conf.hideDelay) ->
+  hide: (delay = getConfig('hideDelay')) ->
     @visible = false
     # enable hover events again
     @enableHoverEvents()
     # focus the element that was focused before the tree view
     # was opened
     @recoverFocus()
-    @animate @conf.hiddenWidth, delay
+    @animate getConfig('hiddenWidth'), delay
     .then (finished) =>
       # hide the tree view content
       @treeView.scroller[0].style.display = 'none' if finished
@@ -271,12 +273,12 @@

   # enable hover events on the tree view
   enableHoverEvents: ->
-    if @conf.showOn.match 'hover'
+    if getConfig('showOn').match 'hover'
       @treeViewEl.classList.add 'autohide-hover'

   # disable hover events on the tree view
   disableHoverEvents: ->
-    if @conf.showOn.match 'hover'
+    if getConfig('showOn').match 'hover'
       @treeViewEl.classList.remove 'autohide-hover'

   # resolves true if animation finished, false if animation cancelled
@@ -284,7 +286,7 @@
     # get the initial width of the element
     initialWidth = @treeViewEl.clientWidth
     # calculate the animation duration
-    duration = Math.abs targetWidth - initialWidth / (@conf.animationSpeed or Infinity)
+    duration = Math.abs targetWidth - initialWidth / (getConfig('animationSpeed') or Infinity)

     # cancel any current animation
     if @currentAnimation? and @currentAnimation.playState isnt 'finished'


Improve dark background colors

Improve dark background colors

--- .Gruntfile.coffee
+++ .Gruntfile.coffee
@@ -77,13 +77,13 @@
             'table-bg-hover':             '@gray-dark'
             'table-border-color':         '@gray-dark'
             'state-success-text':         '#fff'
-            'state-success-bg':           'darken(@brand-success, 25%)'
+            'state-success-bg':           'darken(@brand-success, 35%)'
             'state-info-text':            '#fff'
-            'state-info-bg':              'darken(@brand-info, 35%)'
+            'state-info-bg':              'darken(@brand-info, 45%)'
             'state-warning-text':         '#fff'
-            'state-warning-bg':           'darken(@brand-warning, 35%)'
+            'state-warning-bg':           'darken(@brand-warning, 45%)'
             'state-danger-text':          '#fff'
-            'state-danger-bg':            'darken(@brand-danger, 25%)'
+            'state-danger-bg':            'darken(@brand-danger, 35%)'
             'modal-content-bg':           'lighten(@body-bg, 10%)'
             'modal-header-border-color':  '@gray-dark'
             'input-bg':                   '@gray-darker'


[atom] removing atom-ctags

[atom] removing atom-ctags

--- .atom/config.cson
+++ .atom/config.cson
@@ -1,10 +1,5 @@
 "*":
-  "atom-ctags":
-    autoBuildTagsWhenActive: true
   core:
-    disabledPackages: [
-      "symbols-view"
-    ]
     ignoredNames: [
       ".git"
       ".hg"
@@ -14,6 +9,7 @@
       "Thumbs.db"
       "CVS"
     ]
+    projectHome: "$HOME/Code"
     themes: [
       "atom-material-ui"
       "atom-material-syntax-dark"


atom: update config

atom: update config

--- .atom/config.cson
+++ .atom/config.cson
@@ -40,7 +40,6 @@
       "minimap"
       "pretty-json"
       "tool-bar-main"
-      "activate-power-mode"
     ]
     packagesWithKeymapsDisabled: [
       "git-time-machine"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -181,8 +181,8 @@

     ###*
     Returns a new matrix that corresponds this matrix multiplied by a
-    a scaling matrix.
-    @name scale
+    a skew matrix.
+    @name skew
     @methodOf Matrix#
     @see Matrix.skew
     @param {Number} sx


[benchmarks] cleanup

[benchmarks] cleanup

--- benchmarks/memory/memory.coffee
+++ benchmarks/memory/memory.coffee
@@ -32,7 +32,6 @@
     server.send({type: 'gc'})
     server.send({type: 'memory'})
     server.once 'message', (msg) ->
-        # Results holds the memory usage for a given number of browsers.
         results = [msg.data.heapUsed / 1024]
         console.log("0: #{msg.data.heapUsed / 1024}")
         server.send({type: 'memory'})


fix(search): Don't show loading dots forever if requests fail

--- src/search-subscription.coffee
+++ src/search-subscription.coffee
@@ -32,6 +32,13 @@
     resultCount = 0
     resultIds = []

+    resultReturned = =>
+      # Don't emit a "result" until we have at least one thread to display.
+      # Otherwise it will show "No Results Found"
+      if resultIds.length > 0 or resultCount is @_accountIds.length
+        query = DatabaseStore.findAll(Thread).where(id: resultIds).order(Thread.attributes.lastMessageReceivedTimestamp.descending())
+        @replaceQuery(query)
+
     @_accountIds.forEach (aid) =>
       NylasAPI.makeRequest
         method: 'GET'
@@ -39,15 +46,15 @@
         accountId: aid
         json: true
         returnsModel: true
+
       .then (threads) =>
         return unless @_termsVersion is termsVersion
         resultCount += 1
         resultIds = resultIds.concat _.pluck(threads, 'id')
+        resultReturned()

-        # Don't emit a "result" until we have at least one thread to display.
-        # Otherwise it will show "No Results Found"
-        if resultIds.length > 0 or resultCount is @_accountIds.length
-          query = DatabaseStore.findAll(Thread).where(id: resultIds).order(Thread.attributes.lastMessageReceivedTimestamp.descending())
-          @replaceQuery(query)
+      .catch (err) =>
+        resultCount += 1
+        resultReturned()

 module.exports = SearchSubscription


Fix Chase identification process.

Fix Chase identification process.

--- application/chrome/content/wesabe/fi-scripts/com/chase/identification.coffee
+++ application/chrome/content/wesabe/fi-scripts/com/chase/identification.coffee
@@ -85,15 +85,16 @@
       log.warn "Could not answer delivery method question, suspending job"
       action.collectIdentificationCodeDeliveryMethods()

-#      job.suspend "suspended.missing-answer.auth.identification.delivery-method",
-#            title: "Confirm Your Identity"
-#           header: "Chase needs a one-time Identification Code to confirm that you own the accounts before Wesabe can access them. Once you have the code we'll ask you to enter it on the next screen."
-#        questions: [
-#           type: "choice"
-#          label: "How should Chase send you the Identification Code?"
-#            key: "identificationCodeDeliveryMethod"
-#     persistent: false
-#        choices: tmp.identificationCodeDeliveryMethods]
+      job.suspend "suspended.missing-answer.auth.identification.delivery-method",
+        title: "Confirm Your Identity"
+        header: "Chase needs a one-time Identification Code to confirm that you own the accounts before Wesabe can access them. Once you have the code we'll ask you to enter it on the next screen."
+        questions: [
+          type: "choice"
+          label: "How should Chase send you the Identification Code?"
+          key: "identificationCodeDeliveryMethod"
+          persistent: false
+          choices: tmp.identificationCodeDeliveryMethods
+        ]

     confirmIdentificationCode: ->
       page.click e.identification.continueButton
@@ -105,14 +106,15 @@
         page.click e.identification.continueButton
       else
         log.warn "Could not answer identification code question, suspending job"
-#        job.suspend "suspended.missing-answer.auth.identification.code",
-#            title: "Identification Code"
-#           header: "Please enter the identification code you received from Chase. If you didn't receive an identification code please cancel and try again."
-#        questions: [
-#           type: "number",
-#          label: "Identification Code",
-#            key: "identificationCode",
-#     persistent: false]
+        job.suspend "suspended.missing-answer.auth.identification.code",
+          title: "Identification Code"
+          header: "Please enter the identification code you received from Chase. If you didn't receive an identification code please cancel and try again."
+          questions: [
+            type: "number",
+            label: "Identification Code",
+            key: "identificationCode",
+            persistent: false
+          ]

   elements:
     identification:
@@ -125,8 +127,7 @@

         contact:
           indicator: [
-            '//td[@class="steptexton"][contains(string(.), "Identification Code")]'
-            '//form[@name="frmOTPDeliveryMode"]'
+            '//*[@class="instrtexthead"][contains(string(.), "Get your Identification Code")]'
           ]

         confirmation:
@@ -136,8 +137,7 @@

         codeEntry:
           indicator: [
-            '//td[@class="steptexton"][contains(string(.), "Enter Code")]'
-            '//form[@name="frmValidateOTP"]'
+            '//*[@class="instrtexthead"][contains(string(.), "Enter your Identification Code")]'
           ]

         legalAgreements:


Fixed history and set discussion XBlock to always use async ajax requests

--- common/static/coffee/src/discussion/main.coffee
+++ common/static/coffee/src/discussion/main.coffee
@@ -17,7 +17,7 @@
       discussion = new Discussion(threads, {pages: thread_pages, sort: sort_preference})
       course_settings = new DiscussionCourseSettings(element.data("course-settings"))
       if Backbone.History.started
-        DiscussionUtil.route_prefix = DiscussionUtil.getHistoryPath(window.location.pathname, Backbone.History.root)
+        DiscussionUtil.route_prefix = DiscussionUtil.getHistoryPath(window.location.pathname, Backbone.history.root)
       new DiscussionRouter({discussion: discussion, course_settings: course_settings})
       if !Backbone.History.started
         Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"})


JNewStatusUpdate: check if teaser is exists for activity

--- client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -156,19 +156,23 @@
       @newActivityArrivedList[id] = true

   newActivityArrived:(activity)->
-
-    @logNewActivityArrived(activity)
-
-    return unless @_state is 'public'
-    unless @isMine activity
-      # if realtime update is newmember item
-      # instead of adding a new item we update the
-      # latest inserted member bucket or create a new one
-      if activity instanceof KD.remote.api.CNewMemberBucketActivity
-        @updateNewMemberBucket activity
-      else
-        view = @addHiddenItem activity, 0
-        @activityHeader?.newActivityArrived()
+    if activity and 'function' is typeof activity.fetchTeaser
+      activity.fetchTeaser (err, teaser)=>
+        if teaser
+          @logNewActivityArrived(activity)
+
+          return unless @_state is 'public'
+          unless @isMine activity
+            # if realtime update is newmember item
+            # instead of adding a new item we update the
+            # latest inserted member bucket or create a new one
+            if activity instanceof KD.remote.api.CNewMemberBucketActivity
+              @updateNewMemberBucket activity
+            else
+              view = @addHiddenItem activity, 0
+              @activityHeader?.newActivityArrived()
+    else
+      log "discarding activity", activity

   updateNewMemberBucket:(memberAccount)->
     for item in @itemsOrdered


simplify the convert method

simplify the convert method

--- lib/closure-conversion.coffee
+++ lib/closure-conversion.coffee
@@ -149,69 +149,20 @@
                                 env = env.parent
                 n

-
-#
-#   This pass walks the tree and moves all function expressions to the toplevel.
-#
-#   at the point where this pass runs there are a couple of assumptions:
-#
-#   1. there are no function declarations anywhere in the program.  They have all
-#      been converted to 'var X = %makeClosure(%env_Y, function (%env) { ... })'
-#
-#   2. There are no free variables in the function expressions.
-#
-class LambdaLift extends NodeVisitor
-        constructor: (module) ->
-                super
-                @functions = []
-                @mappings = []
-
-        currentMapping: -> if @mappings.length > 0 then @mappings[0] else {}
-
-        visitProgram: (program) ->
-                super
-                program.body = @functions.concat program.body
-                program
-
-        visitFunctionDeclaration: (n) ->
-                n.body = @visit n.body
-                n
-
-        visitFunctionExpression: (n) ->
-                if n.id?.name?
-                        global_name = genGlobalFunctionName n.id.name
-                        @currentMapping()[n.id.name] = global_name
-                else
-                        global_name = genAnonymousFunctionName()
-
-                n.type = syntax.FunctionDeclaration
-                n.id =
-                        type: syntax.Identifier
-                        name: global_name
-
-                @functions.push n
-
-                new_mapping = deep_copy_object @currentMapping()
-
-                @mappings.unshift new_mapping
-                n.body = @visit n.body
-                @mappings = @mappings.slice(1)
-
-                n.params.unshift create_identifier "%env_#{n.ejs_env.parent.id}"
-
-                return {
-                        type: syntax.Identifier,
-                        name: global_name
-                }
-
 create_identifier = (x) -> type: syntax.Identifier, name: x
 create_string_literal = (x) -> type: syntax.Literal, value: x, raw: "\"#{x}\""

 # this should move to echo-desugar.coffee
+
+# convert all function declarations to variable assignments
+# with named function expressions.
+#
+# i.e. from:
+#   function foo() { }
+# to:
+#   var foo = function foo() { }
+#
 FuncsToVars = class FuncsToVars extends NodeVisitor
-        constructor: (module) ->
-                super
-
         visitFunctionDeclaration: (n) ->
                 if n.toplevel
                         super
@@ -229,6 +180,97 @@
                                 kind: "var"
                         }

+
+# hoists all vars to the start of the enclosing function,
+# replacing any initializer with an assignment expression.
+#
+# i.e. from
+#    {
+#       ....
+#       var x = 5;
+#       ....
+#    }
+# to
+#    {
+#       var x;
+#       ....
+#       x = 5;
+#       ....
+#    }
+#
+class HoistVars extends NodeVisitor
+        constructor: () ->
+                super
+                @function_stack = []
+
+        visitFunction: (n) ->
+                @function_stack.unshift { func: n, vars: [] }
+                n = super
+                n.body.body = @function_stack[0].vars.concat n.body.body
+                @function_stack.shift()
+                n
+
+        visitForIn: (n) ->
+                if n.left.type is syntax.VariableDeclaration
+                        @function_stack[0].vars.push
+                                type: syntax.VariableDeclaration
+                                declarations: [{
+                                        type: syntax.VariableDeclarator
+                                        id: create_identifier n.left.declarations[0].id.name
+                                        init: null
+                                }],
+                                kind: "var"
+                        n.left.type = syntax.Identifier
+                        n.left.name = n.left.declarations[0].id.name
+                        delete n.left.declarations
+                        delete n.left.kind
+
+                n.right = @visit n.right
+                n.body = @visit n.body
+                n
+
+        visitVariableDeclaration: (n) ->
+                if n.kind is "var"
+                        # vars are hoisted to the containing function's toplevel scope
+                        for i in [0...n.declarations.length]
+                                @function_stack[0].vars.push
+                                        type: syntax.VariableDeclaration
+                                        declarations: [{
+                                                type: syntax.VariableDeclarator
+                                                id: create_identifier n.declarations[i].id.name
+                                                init: null
+                                                }],
+                                        kind: "var"
+
+                        # now we need convert the initializers to assignment expressions
+                        assignments = []
+                        for i in [0...n.declarations.length]
+                                if n.declarations[i].init?
+                                        assignments.push
+                                                type: syntax.ExpressionStatement
+                                                expression:
+                                                        type: syntax.AssignmentExpression
+                                                        left: create_identifier n.declarations[i].id.name
+                                                        right: n.declarations[i].init
+                                                        operator: "="
+
+                        # now return the new assignments, which will replace the original variable
+                        # declaration node.
+                        if assignments.length == 1
+                                return assignments[0]
+
+                        return {
+                                type: syntax.BlockStatement
+                                body: assignments
+                        }
+                n
+
+# this class really doesn't behave like a normal NodeVisitor, as it modifies the tree in-place
+# check the free() function near the top of the file for the actual implementation.
+class ComputeFree extends NodeVisitor
+        visit: (n) ->
+                free n
+                n

 # 1) allocates the environment at the start of the n
 # 2) adds mappings for all .closed variables
@@ -238,13 +280,9 @@
                 @mappings = []

         currentMapping: -> if @mappings.length > 0 then @mappings[0] else {}
-
+
         visitIdentifier: (n) ->
-                if n.ejs_substitute?
-                        if not @currentMapping().hasOwnProperty n.name
-                                debug.log "missing mapping for #{n.name}"
-                                throw "InternalError 2"
-
+                if @currentMapping().hasOwnProperty n.name
                         return @currentMapping()[n.name]
                 n

@@ -308,6 +346,17 @@
                                         kind: n.kind
                                 }
                 rv
+
+        visitProperty: (n) ->
+                # we don't visit property keys here
+                n.value = @visit n.value
+                n
+
+        visitFunctionBody: (n) ->
+                # we use this instead of calling super in visitFunction because we don't want to visit parameters
+                # during this pass, or they'll be substituted with an %env.
+                n.body = @visit n.body
+                n

         visitFunction: (n) ->
                 if n.ejs_env.closed.empty()
@@ -321,14 +370,15 @@
                                                 value: null
                                 }],
                                 kind: "var"
-                        super
+                        @visitFunctionBody n
                 else
                         # okay, we know we need a fresh environment in this function

                         env_name = "%env_#{n.ejs_env.id}"

+                        prepends = []
                         # insert environment creation (at the start of the function body)
-                        n.body.body.unshift
+                        prepends.push {
                                 type: syntax.VariableDeclaration,
                                 declarations: [{
                                         type: syntax.VariableDeclarator
@@ -338,9 +388,10 @@
                                                 properties: []
                                 }],
                                 kind: "var"
+                        }

                         if n.ejs_env.parent?
-                                n.body.body.splice 1, 0, {
+                                prepends.push {
                                         type: syntax.ExpressionStatement
                                         expression:
                                                 type: syntax.AssignmentExpression,
@@ -357,7 +408,7 @@
                         # we need to push assignments of any closed over parameters into the environment at this point
                         for param in n.params
                                 if n.ejs_env.closed.member param.name
-                                        n.body.body.splice 1, 0, {
+                                        prepends.push {
                                                 type: syntax.ExpressionStatement
                                                 expression:
                                                         type: syntax.AssignmentExpression,
@@ -405,7 +456,8 @@
                                         property: create_identifier sym

                         @mappings.unshift new_mapping
-                        super
+                        @visitFunctionBody n
+                        n.body.body = prepends.concat n.body.body
                         @mappings = @mappings.slice(1)

                 # we need to replace function declarations of the form:
@@ -464,38 +516,75 @@
                 n.callee = create_identifier "%invokeClosure"
                 n

-
+#
+#   This pass walks the tree and moves all function expressions to the toplevel.
+#
+#   at the point where this pass runs there are a couple of assumptions:
+#
+#   1. there are no function declarations anywhere in the program.  They have all
+#      been converted to 'var X = %makeClosure(%env_Y, function (%env) { ... })'
+#
+#   2. There are no free variables in the function expressions.
+#
+class LambdaLift extends NodeVisitor
+        constructor: (module) ->
+                super
+                @functions = []
+                @mappings = []
+
+        currentMapping: -> if @mappings.length > 0 then @mappings[0] else {}
+
+        visitProgram: (program) ->
+                super
+                program.body = @functions.concat program.body
+                program
+
+        visitFunctionDeclaration: (n) ->
+                n.body = @visit n.body
+                n
+
+        visitFunctionExpression: (n) ->
+                if n.id?.name?
+                        global_name = genGlobalFunctionName n.id.name
+                        @currentMapping()[n.id.name] = global_name
+                else
+                        global_name = genAnonymousFunctionName()
+
+                n.type = syntax.FunctionDeclaration
+                n.id =
+                        type: syntax.Identifier
+                        name: global_name
+
+                @functions.push n
+
+                new_mapping = deep_copy_object @currentMapping()
+
+                @mappings.unshift new_mapping
+                n.body = @visit n.body
+                @mappings = @mappings.slice(1)
+
+                n.params.unshift create_identifier "%env_#{n.ejs_env.parent.id}"
+
+                return {
+                        type: syntax.Identifier,
+                        name: global_name
+                }
+
+passes = [
+        FuncsToVars,
+        HoistVars,
+        ComputeFree,
+        LocateEnvVisitor,
+        SubstituteVariables,
+        LambdaLift
+        ]

 exports.convert = (tree) ->
         debug.log "before:"
         debug.log -> escodegen.generate tree

-        funcs_to_vars = new FuncsToVars tree
-        tree2 = funcs_to_vars.visit tree
-
-        debug.log "after FuncsToVars:"
-        debug.log -> escodegen.generate tree2
-
-        # first we decorate the tree with free variable usage
-        free tree2
-
-        # traverse the tree accumulating info about which function level defined a given variable.
-        locate_envs = new LocateEnvVisitor
-        tree3 = locate_envs.visit tree2
-
-        debug.log "after LocateEnvVisitor:"
-        debug.log -> escodegen.generate tree3
-        substitute_vars = new SubstituteVariables tree3
-        tree4 = substitute_vars.visit tree3
-
-        debug.log "after SubstituteVariables:"
-        debug.log -> escodegen.generate tree4
-
-        lambda_lift = new LambdaLift tree4
-        tree5 = lambda_lift.visit tree4
-
-        debug.log "after LambdaLift:"
-        debug.log -> escodegen.generate tree5
-
-        tree5
-        +        passes.forEach (passType) ->
+                pass = new passType()
+                tree = pass.visit tree
+
+        tree


Remap the even and odd classes of filtered items.

--- app/assets/javascripts/alchemy/alchemy.base.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.base.js.coffee
@@ -72,6 +72,11 @@
       else
         display = "none"
       $this.css display: display
+    $items = $(selector)
+    # Fix the even and odd classes
+    $items.removeClass('even odd')
+    $items.filter(':visible:even').addClass('even')
+    $items.filter(':visible:odd').addClass('odd')

   # Shows spinner while loading images and
   # fades the image after its been loaded