deal with upstream errors

deal with upstream errors

--- feed/server.coffee
+++ feed/server.coffee
@@ -21,14 +21,20 @@

     if not geocaches? or not lastFetch? or new Date - lastFetch > 60000
         debug 'fetching geocaches'
-        apiRes = yield request
-            .get "#{server}/geocaches"
-            .accept 'application/json'
-            .query
-                full: 1
-                maxAge: 14
-        geocaches = apiRes.body
-        lastFetch = new Date
+        try
+            apiRes = yield request
+                .get "#{server}/geocaches"
+                .accept 'application/json'
+                .query
+                    full: 1
+                    maxAge: 14
+            geocaches = apiRes.body
+            lastFetch = new Date
+        catch e
+            console.error "Unable to get upstream geocaches: #{e}"
+            res.status 500
+            res.send 'Failed to get upstream data'
+            return

     xml = template
         self: self
@@ -38,4 +44,4 @@
     res.send xml

 app.set 'x-powered-by', false
-app.listen 8080
+app.listen 8081


minor just moving "evalFirstListElementAndTurnRestIntoMessage" function

--- fizzylogo_ListPrimitiveClass.coffee
+++ fizzylogo_ListPrimitiveClass.coffee
@@ -85,6 +85,13 @@

     toBeReturned.printForList = toBeReturned.print

+    toBeReturned.evalFirstListElementAndTurnRestIntoMessage = (theContext) ->
+      firstElement = @firstElement()
+      console.log "           " + indentation() + "evaling element " + firstElement.value
+      evaledFirstElement = (firstElement.eval theContext).returned
+      restOfMessage = @skipNextMessageElement theContext
+      return [evaledFirstElement, restOfMessage]
+
     # it's like eval but it does it in a new
     # context and keeps track of hum much the
     # current message is consumed.
@@ -181,12 +188,6 @@
         throw "no first element, array is empty"
       return @elementAt 0

-    toBeReturned.evalFirstListElementAndTurnRestIntoMessage = (theContext) ->
-      firstElement = @firstElement()
-      console.log "           " + indentation() + "evaling element " + firstElement.value
-      evaledFirstElement = (firstElement.eval theContext).returned
-      restOfMessage = @skipNextMessageElement theContext
-      return [evaledFirstElement, restOfMessage]

     toBeReturned.skipNextMessageElement = (theContext) ->
       theContext.programCounter++


npm is now handled through scripts

npm is now handled through scripts

--- gulp.coffee
+++ gulp.coffee
@@ -26,18 +26,6 @@
       u: 'tdd'
       istanbul: {}
     }))
-
-  distNpm: (cb) ->
-    all.task.dist.npm({
-      output: {
-        dist: 'lib'
-      }
-      extras: [
-        'bower_components'
-      ]
-    })(cb)
-
-    return
 }

 all.task.exportTasks(tasks)


Don't need to duplicate node,

Don't need to duplicate node,

* Caused a flicker
* Can display none after duration of tranisition
* Pad delay with 300ms incase some target pages take time to render

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -14,7 +14,7 @@
   back: false

   transitionAnimation: true
-  speed: 0.4
+  speed: 0.3
   mainMenu: '#main-menu'
   menuCloseButton: '#close-menu-btn'

@@ -102,22 +102,18 @@
   slide: (targetPage, currentPage) ->
     targetPage.style.display = "-webkit-box"
     targetPage.style.overflow = "visible"
+    currentPage.style.overflow = "visible"
     screenWidth = window.innerWidth + 'px'
-
-    currentPageClone = currentPage.cloneNode true
-    currentPage.parentNode.appendChild currentPageClone
-    currentPageClone.style.overflow = "visible"
-    currentPage.style.display = "none"

     if @back
       @translate(targetPage, "X", "-" + screenWidth, "0ms")
       window.setTimeout =>
-        @translate(currentPageClone, "X", "100%")
+        @translate(currentPage, "X", "100%")
       , 0
     else
       @translate(targetPage,"X", screenWidth, "0ms")
       window.setTimeout =>
-        @translate(currentPageClone, "X", "-100%")
+        @translate(currentPage, "X", "-100%")
       , 0

     window.setTimeout =>
@@ -126,9 +122,9 @@
     , 0

     # delay node removal time to speed
-    delay = @speed * 1000
+    delay = (@speed * 1000) + 300
     window.setTimeout =>
-      currentPageClone.parentNode.removeChild currentPageClone
+      currentPage.style.display = "none"
     , delay

   # Private: Perform a slide from bottom transition.


More JSDoc

More JSDoc

--- src/coffee/oauth2.coffee
+++ src/coffee/oauth2.coffee
@@ -1,10 +1,20 @@
-_ = require('underscore')._
+_ = require 'underscore'
 querystring = require 'querystring'
 request = require 'request'
 Logger = require './logger'

+###*
+ * Creates a new OAuth2 instance, used to connect to https://auth.sphere.io
+ * @class OAuth2
+###
 class OAuth2

+  ###*
+   * Initialize the class
+   * @constructor
+   * @param {Object} [opts] A JSON object containg configuration options
+   * @throws {Error} if credentials are missing
+  ###
   constructor: (opts = {}) ->
     config = opts.config
     throw new Error('Missing credentials') unless config
@@ -25,11 +35,16 @@
     @logger.debug @_options, 'New OAuth object'
     return

+  ###*
+   * Retrieve an `access_token` to be able to access the HTTP API
+   * @param {Function} callback A function fulfilled with `error, response, body` arguments.
+  ###
   getAccessToken: (callback) ->
     params =
       grant_type: 'client_credentials'
       scope: "manage_project:#{@_options.config.project_key}"

+    # TODO: use querystring or mixins ?
     payload = querystring.stringify(params)
     request_options =
       uri: "https://#{@_options.config.client_id}:#{@_options.config.client_secret}@#{@_options.host}#{@_options.accessTokenUrl}"
@@ -45,6 +60,12 @@
     @logger.debug request_options, 'Retrieving access_token...'
     @_doRequest(request_options, callback)

+  ###*
+   * Execute the request using the underling `request` module
+   * @link https://github.com/mikeal/request
+   * @param {Object} options A JSON object containing all required options for the request
+   * @param {Function} callback A function fulfilled with `error, response, body` arguments.
+  ###
   _doRequest: (options, callback) ->
     request options, (e, r, b) =>
       @logger.error e if e


Flash bar hideable via touch

Flash bar hideable via touch

--- app/assets/javascripts/components/flash_bar.coffee
+++ app/assets/javascripts/components/flash_bar.coffee
@@ -1,4 +1,4 @@
 $(document)
-  .on("click", ".flash-bar-show", ->
+  .on("click touchstart", ".flash-bar-show", ->
     $(this).removeClass("flash-bar-show")
   )


fix bug cant navigate to yestoday in agendaDay view

--- app/assets/javascripts/controllers/calendar.coffee
+++ app/assets/javascripts/controllers/calendar.coffee
@@ -52,7 +52,7 @@
     # set defaultView, because when we change slotEventOverlap
     # ui-calendar will trigger rerender calendar
     $scope.calendarConfig.defaultView = view.type
-    today = moment().startOf('day')
+    today = moment().utc().startOf('day')
     if today > view.start and today <= view.end and today != view.start
       $scope.calendarConfig.defaultDate = today
     else


Include pension_calculator as a tool id in addition to pensions_calculator

--- app/assets/javascripts/syndication/tools.js.coffee
+++ app/assets/javascripts/syndication/tools.js.coffee
@@ -232,6 +232,16 @@
       include_ga: false
       title: "Pensions calculator"

+    pension_calculator:
+      en:
+        path: "en/tools/pension-calculator"
+      cy:
+        path: "cy/tools/cyfrifiannell-pensiwn"
+      width: "100%"
+      height: "800px"
+      include_ga: false
+      title: "Pension calculator"
+
     health_check:
       en:
         path: "en/tools/health-check"


Tweak grouping relics

Tweak grouping relics

--- app/assets/javascripts/widgets/direction.js.coffee
+++ app/assets/javascripts/widgets/direction.js.coffee
@@ -84,8 +84,7 @@
   window.marker_clusterer = new MarkerClusterer gmap, [],
     maxZoom: 18
     styles: styles
-    gridSize: 50
-    averageCenter: true
+    gridSize: 40

 renderResults = (search_results, last = true, callback) ->
   $.each search_results, ->


Fix bug with Components not saving/loading from backups from 07cfe47

--- app/views/editor/level/thangs/ThangsTabView.coffee
+++ app/views/editor/level/thangs/ThangsTabView.coffee
@@ -90,6 +90,8 @@
     @listenToOnce(@componentCollection, 'sync', ->
       for component in @componentCollection.models
         component.url = "/db/level.component/#{component.get('original')}/version/#{component.get('version').major}"
+        component.saveBackups = true
+        component.loadFromBackup()
         @supermodel.registerModel(component)
     )
     @level = options.level


inject into eventSourceable after checking for existence

--- source/server/infrastructure/router.coffee
+++ source/server/infrastructure/router.coffee
@@ -87,8 +87,8 @@
                        #{@eventSourceable}<#{aggregateId}>\n"), message)
     try
       eventSourceable = @repository.find @eventSourceable, aggregateId
+      throw Router.ERRORS.cannotHandleMessage(message) if !eventSourceable?
       @injector.injectInto(eventSourceable)
-      throw Router.ERRORS.cannotHandleMessage(message) if !eventSourceable?
       eventSourceable = @_handleDomainErrors(-> eventSourceable.handle message)
       @repository.save(eventSourceable) if eventSourceable?
     catch error


Add core.fileEncoding config schema

Add core.fileEncoding config schema

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -35,6 +35,47 @@
       destroyEmptyPanes:
         type: 'boolean'
         default: true
+      fileEncoding:
+        description: 'Default character set encoding to use when reading and writing files.'
+        type: 'string'
+        default: 'utf8'
+        enum: [
+          'cp437',
+          'eucjp',
+          'euckr',
+          'gbk',
+          'iso88591',
+          'iso885910',
+          'iso885913',
+          'iso885914',
+          'iso885915',
+          'iso885916',
+          'iso88592',
+          'iso88593',
+          'iso88594',
+          'iso88595',
+          'iso88596',
+          'iso88597',
+          'iso88597',
+          'iso88598',
+          'koi8r',
+          'koi8u',
+          'macroman',
+          'shiftjis',
+          'utf16be',
+          'utf16le',
+          'utf8',
+          'windows1250',
+          'windows1250',
+          'windows1251',
+          'windows1253',
+          'windows1254',
+          'windows1255',
+          'windows1256',
+          'windows1257',
+          'windows1258',
+          'windows866'
+        ]

   editor:
     type: 'object'


missing function

missing function

--- workers/social/lib/social/models/messages/post.coffee
+++ workers/social/lib/social/models/messages/post.coffee
@@ -160,7 +160,7 @@
                 callback createKodingError err
               else queue.next()
           ->
-            delegate.update $set: 'meta.modifiedAt': new Date, -> queue.next()
+            delegate.updateMetaModifiedAt ()-> queue.next()
           ->
             delegate.addContent activity, (err)-> queue.next(err)
           ->


:new: Add test for multiple bufferModifying linters

--- spec/buffer-modifying-provider-spec.coffee
+++ spec/buffer-modifying-provider-spec.coffee
@@ -8,6 +8,14 @@
   it 'is triggered before other linters', ->
     linter = getModuleMain()
     last = null
+    bufferModifying =
+      grammarScopes: ['*']
+      scope: 'file'
+      lintOnFly: false
+      modifiesBuffer: true
+      lint: ->
+        last = 'bufferModifying'
+        return []
     normalLinter =
       grammarScopes: ['*']
       scope: 'file'
@@ -16,16 +24,54 @@
       lint: ->
         last = 'normal'
         return []
-    bufferModifying =
+    linter.addLinter(bufferModifying)
+    linter.addLinter(normalLinter)
+    waitsForPromise ->
+      linter.getActiveEditorLinter().lint(false).then ->
+        expect(last).toBe('normal')
+  it 'runs in parallel', ->
+    linter = getModuleMain()
+    activeEditor = atom.workspace.getActiveTextEditor()
+    wasTriggered = false
+    first =
       grammarScopes: ['*']
       scope: 'file'
       lintOnFly: false
       modifiesBuffer: true
       lint: ->
-        last = 'bufferModifying'
+        wasTriggered = true
+        activeEditor.setText('first')
         return []
+    second =
+      grammarScopes: ['*']
+      scope: 'file'
+      lintOnFly: false
+      modifiesBuffer: true
+      lint: ->
+        expect(activeEditor.getText()).toBe('first')
+        activeEditor.setText('second')
+        return []
+    third =
+      grammarScopes: ['*']
+      scope: 'file'
+      lintOnFly: false
+      modifiesBuffer: true
+      lint: ->
+        expect(activeEditor.getText()).toBe('second')
+        activeEditor.setText('third')
+        return []
+    normalLinter =
+      grammarScopes: ['*']
+      scope: 'file'
+      lintOnFly: false
+      modifiesBuffer: false
+      lint: ->
+        expect(activeEditor.getText()).toBe('third')
+        return []
+    linter.addLinter(first)
+    linter.addLinter(second)
+    linter.addLinter(third)
     linter.addLinter(normalLinter)
-    linter.addLinter(bufferModifying)
     waitsForPromise ->
       linter.getActiveEditorLinter().lint(false).then ->
-        expect(last).toBe('normal')+        expect(wasTriggered).toBe(true)


:white_check_mark: for Compact spaces #279.

--- spec/operator-transform-string-spec.coffee
+++ spec/operator-transform-string-spec.coffee
@@ -313,6 +313,43 @@

     it "g-g- DashCase the line of text, won't move cursor", ->
       ensure 'l g - g -', text: 'vim-mode\natom_text_editor\n', cursor: [0, 1]
+
+  describe 'CompactSpaces', ->
+    beforeEach ->
+      set
+        cursorBuffer: [0, 0]
+
+    describe "basic behavior", ->
+      it "compats multiple space into one", ->
+        set
+          text: 'var0   =   0; var10   =   10'
+          cursor: [0, 0]
+        ensure 'g space $',
+          text: 'var0 = 0; var10 = 10'
+      it "don't apply compaction for leading and trailing space", ->
+        set
+          text: """
+          ___var0   =   0; var10   =   10___
+          ___var1   =   1; var11   =   11___
+          ___var2   =   2; var12   =   12___
+
+          ___var4   =   4; var14   =   14___
+          """.replace(/_/g, ' ')
+          cursor: [0, 0]
+        ensure 'g space i p',
+          text: """
+          ___var0 = 0; var10 = 10___
+          ___var1 = 1; var11 = 11___
+          ___var2 = 2; var12 = 12___
+
+          ___var4   =   4; var14   =   14___
+          """.replace(/_/g, ' ')
+      it "but it compact spaces when target all text is spaces", ->
+        set
+          text: '01234    90'
+          cursor: [0, 5]
+        ensure 'g space w',
+          text: '01234 90'

   describe 'surround', ->
     beforeEach ->


Removing duplication in tests

Removing duplication in tests

--- tests/unit/orderedItemsUseCaseTests.coffee
+++ tests/unit/orderedItemsUseCaseTests.coffee
@@ -1,22 +1,21 @@
 OrderedItemsUseCase = window.Todo.UseCases.OrderedItemsUseCase

 describe 'OrderedItemsUseCase', ->
-  it 'Should call display on presenter', ->
+  fakeItemsPresenter = null
+  fakeItemsGateway = null
+  orderedItemsUseCase = null
+
+  beforeEach ->
     fakeItemsPresenter = new FakeItemsPresenter
     fakeItemsGateway = new FakeItemsGateway
     orderedItemsUseCase = new OrderedItemsUseCase fakeItemsPresenter, fakeItemsGateway

     orderedItemsUseCase.getAllItems()

+  it 'Should call display on presenter', ->
     assert.isTrue fakeItemsPresenter.display.calledOnce

   it 'Should call getAllItems on gateway', ->
-    fakeItemsPresenter = new FakeItemsPresenter
-    fakeItemsGateway = new FakeItemsGateway
-    orderedItemsUseCase = new OrderedItemsUseCase fakeItemsPresenter, fakeItemsGateway
-
-    orderedItemsUseCase.getAllItems()
-
     assert.isTrue fakeItemsGateway.getAllItems.calledOnce

   class FakeItemsPresenter


add area highlighting and move data mapping to seperate function

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -1,9 +1,12 @@
 #= require mappings

-width = 800
+width  = 800
 height = 800
 vis    = null
+places = null
+neighborhoods = null

+fill = d3.scale.log().range ['#6cd7ff', "#006bbb"]
 projection = d3.geo.albers().rotate([120])
 path = d3.geo.path().projection(projection)

@@ -18,37 +21,10 @@
   s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)
   t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]

-  projection.scale(s)
-    .translate t
+  projection.scale(s).translate(t)

-  names = neighborhoods.features
-    .sort((a, b) -> d3.ascending(a.properties.NAME, b.properties.NAME))
-    .map (nh) -> nh.properties.NAME
+  loadCensusData(mapDataToNeighborhoods)

-  loadCensusData (data) ->
-    nhoods = {}
-    for hood in neighborhoods.features
-      name = hood.properties.NAME
-      shared = hood.properties.SHARED
-
-      if !shared?
-        current = nhoods[name] = {}
-        current[2010] = data[2010].filter((d) -> d.NEIGHBORHOOD == name)[0]
-        current[2000] = data[2000].filter((d) -> d.NEIGHBORHOOD == name)[0]
-
-        for key, ids of window.__mappings
-          try
-            from = d3.sum(ids[0].map (id) -> current[2000][id])
-            to   = d3.sum(ids[1].map (id) -> current[2010][id])
-            change = to - from
-            growth = parseFloat ((change / from) * 100).toFixed(2)
-            current[key] = [from, to, change, growth]
-          catch e
-            console.log "ERROR!!!! #{e} #{name} #{key}"
-        delete current[2000]
-        delete current[2010]
-
-    console.log nhoods
   vis.append('pattern')
     .attr('id', 'hatch')
     .attr('patternUnits', 'userSpaceOnUse')
@@ -69,9 +45,55 @@
     .attr('d', path)

 $ ->
-  vis = d3.select(document.body).append('svg')
+  vis = d3.select('.js-map').append('svg')
     .attr('width', width)
     .attr('height', height)
+
+  menu = d3.select('.js-menu').on 'change', (d) ->
+    key = d3.event.target.value
+    highlight key
+
+  for key of __mappings
+    menu.append('option')
+      .attr('value', key)
+      .text(key)
+
+highlight = (key) ->
+  [min, max] = d3.extent places, (d) -> d.value[key][1]
+  fill.domain [min, max]
+  vis.selectAll('.neighborhood:not(.shared):not(.unclaimed)')
+    .style('fill', (d) ->
+      name = d.properties.NAME
+      place = places.filter((p) -> p.key == name)[0]
+      console.log place, name
+      fill(place.value[key][0])
+    )
+
+mapDataToNeighborhoods = (data) ->
+  nhoods = {}
+  for hood in neighborhoods.features
+    name = hood.properties.NAME
+    shared = hood.properties.SHARED
+
+    if !shared?
+      current = nhoods[name] = {}
+      current[2010] = data[2010].filter((d) -> d.NEIGHBORHOOD == name)[0]
+      current[2000] = data[2000].filter((d) -> d.NEIGHBORHOOD == name)[0]
+
+      for key, ids of window.__mappings
+        try
+          from = d3.sum(ids[0].map (id) -> current[2000][id])
+          to   = d3.sum(ids[1].map (id) -> current[2010][id])
+          change = to - from
+          growth = parseFloat ((change / from) * 100).toFixed(2)
+          current[key] = [from, to, change, growth]
+        catch e
+          console.log "ERROR!!!! #{e} #{name} #{key}"
+
+      delete current[2000]
+      delete current[2010]
+
+  places = d3.entries(nhoods).filter (nh) -> !shouldExcludeNeighborhood(nh.key)

 loadCensusData = (callback) ->
   out  = {}
@@ -85,4 +107,7 @@
           row[key] = parseFloat val if key isnt 'NEIGHBORHOOD'
         row
       out[years[index]] = data
-      callback(out) if index == 1+      callback(out) if index == 1
+
+shouldExcludeNeighborhood = (name) ->
+  name.toLowerCase().indexOf('unclaimed') != -1


don't ask

don't ask

--- scripts/transport.coffee
+++ scripts/transport.coffee
@@ -5,7 +5,7 @@
     url = "https://api.api.ai/api/query?v=20150910&lang=en&sessionId="+createUID()+"&query="+encodeURI(res.match[1])
     robot.logger.debug('url: '+url)
     robot.http(url)
-        .headers(Accept: 'application/json', Authorization: 'Bearer 0491d2a8e09349ce83ca35ab3e257a03')
+        .headers(Accept: 'application/json', Authorization: 'Bearer '+TODO)
         .get() (err, res, body) ->
             robot.logger.debug('response: '+body)
          #res.match[1]


fix up option parsing in cli

fix up option parsing in cli

--- src/command.coffee
+++ src/command.coffee
@@ -30,14 +30,16 @@
 '''

 OPTIONS = [
-  [ 'o', 'out', '     specify an output directory' ]
-  [ 'q', 'quiet', '   do not print warnings and messages' ]
-  [ 'p', 'pretty', '  align SVG output prettily' ]
-  [ 'd', 'drill', '   process following file as an NC (Excellon) drill file' ]
+  [ 'o', 'out', '         specify an output directory' ]
+  [ 'q', 'quiet', '       do not print warnings and messages' ]
+  [ 'p', 'pretty', '      align SVG output prettily' ]
+  [ 'd', 'drill', '       process following file as an NC (Excellon) drill file' ]
   [ 'a', 'append-ext', '  append .svg rather than replace the extension' ]
-  [ 'v', 'version', ' display version information' ]
-  [ 'h', 'help', '    display this help text' ]
+  [ 'v', 'version', '     display version information' ]
+  [ 'h', 'help', '        display this help text' ]
 ]
+STRING_OPTS  = [ 'out', 'drill']
+BOOLEAN_OPTS = [ 'quiet', 'pretty', 'append-ext', 'version', 'help' ]

 printOptions = ->
   console.log 'Options:'
@@ -57,6 +59,8 @@
 run = ->
   argv = parseArgs process.argv.slice(2), {
     alias: getOptions()
+    string: STRING_OPTS
+    boolean: BOOLEAN_OPTS
   }
   fileList = argv._


Updated navbar for post competition

Updated navbar for post competition

--- web/coffee/navbar.coffee
+++ web/coffee/navbar.coffee
@@ -50,10 +50,14 @@
 userLoggedInNoCompetition =
   Team: "/team"
   Chat: "/chat"
-  About: "/about"
-  FAQ: "/faq"
-  Learn: "/learn"
-  Sponsors: "/sponsors"
+  Scoreboard: "/scoreboard"
+  Help:
+    Chat: "/chat"
+    Learn: "/learn"
+  About:
+    FAQ: "/faq"
+    About: "/about"
+    Sponsors: "/sponsors"
   Account:
     Manage: "/account"
     Logout: "#"


name change GameEditor -> PlayerCodeEditor, no functionality change

--- web/scripts/index.coffee
+++ web/scripts/index.coffee
@@ -15,11 +15,11 @@
             usedAtStart: 1
         }
     }
-    editor = new GameEditor levelOne, commands
+    editor = new PlayerCodeEditor levelOne, commands
     window.Editor = editor # For testing only, puts editor in global namespace.
     return

-class GameEditor
+class PlayerCodeEditor
     constructor: (@codeText, @commands) ->
         @editor = ace.edit "editor"
         @editSession = @editor.getSession()
@@ -35,7 +35,7 @@

     getCommandFromLine: (line) ->
         re = /^(.+)\(/
-        re.exec(line)[1]
+        return re.exec(line)[1]

     setUpInsertCommands: ->
         ###
@@ -148,10 +148,10 @@
         This is a wrapper for the functions which are tied to buttons.
         It restores focus to the editor after the button has been pressed.
         ###
-        gameEditor = @
+        playerCodeEditor = @
         return ->
-            func.call gameEditor
-            gameEditor.editor.focus()
+            func.call playerCodeEditor
+            playerCodeEditor.editor.focus()
             return

     usesCurrentRow: (func) ->
@@ -159,11 +159,11 @@
         This is a wrapper for the functions which need to know the current row.
         It retrieves the current row and passes it to the function.
         ###
-        gameEditor = @
+        playerCodeEditor = @
         return ->
-            currentRow = gameEditor.editor.getCursorPosition().row
+            currentRow = playerCodeEditor.editor.getCursorPosition().row
             arguments[arguments.length++] = currentRow
-            func.apply gameEditor, arguments
+            func.apply playerCodeEditor, arguments
             return

     usesCurrentPosition: (func) ->
@@ -171,12 +171,12 @@
         This is a wrapper for the functions which need to know the cursor's row and column
         It retrieves the current row and the current column and passes them to the function.
         ###
-        gameEditor = @
+        playerCodeEditor = @
         return ->
-            cursorPosition = gameEditor.editor.getCursorPosition()
+            cursorPosition = playerCodeEditor.editor.getCursorPosition()
             arguments[arguments.length++] = cursorPosition.row
             arguments[arguments.length++] = cursorPosition.column
-            func.apply gameEditor, arguments
+            func.apply playerCodeEditor, arguments
             return

     editsText: (func) ->
@@ -184,9 +184,9 @@
         This is a wrapper for functions which edit the text in the editor directly.
         It gets a reference to the text and passes it to the function.
         ###
-        gameEditor = @
+        playerCodeEditor = @
         return ->
-            text = gameEditor.editSession.getDocument()
+            text = playerCodeEditor.editSession.getDocument()
             arguments[arguments.length++] = text
-            func.apply gameEditor, arguments
+            func.apply playerCodeEditor, arguments
             return


CoffeeLint is complaining, but code is correct.

See https://github.com/clutchski/coffeelint/issues/308

--- server/lib/worker.coffee
+++ server/lib/worker.coffee
@@ -68,7 +68,7 @@
       while job = JobQueue.Meta.collection.getWork _.keys Job.types
         try
           try
-            j = new Job.types[job.type](job.data)
+            j = new Job.types[job.type] job.data
             j._id = job._doc._id
             j._runId = job._doc.runId
             result = j.run()


Add defensive code on decorations methods

--- lib/mixins/decoration-management.coffee
+++ lib/mixins/decoration-management.coffee
@@ -91,6 +91,7 @@
   #
   # Returns a `Decoration` object.
   decorateMarker: (marker, decorationParams) ->
+    return unless marker?
     marker = @getMarker(marker.id)

     if !decorationParams.scope? and decorationParams.class?
@@ -163,6 +164,7 @@
   #
   # decoration - The `Decoration` to remove.
   removeDecoration: (decoration) ->
+    return unless decoration?
     {marker} = decoration
     return unless decorations = @decorationsByMarkerId[marker.id]

@@ -186,6 +188,7 @@
   #
   # marker - The `marker` for which removing decorations.
   removeAllDecorationsForMarker: (marker) ->
+    return unless marker?
     decorations = @decorationsByMarkerId[marker.id].slice()
     for decoration in decorations
       @emitter.emit 'did-remove-decoration', {marker, decoration}
@@ -197,6 +200,7 @@
   #
   # marker - The `marker` for which removing decorations.
   removedAllMarkerDecorations: (marker) ->
+    return unless marker?
     @decorationMarkerChangedSubscriptions[marker.id].dispose()
     @decorationMarkerDestroyedSubscriptions[marker.id].dispose()


:white_check_mark: Add tests covering contrast with variables

--- spec/color-parser-spec.coffee
+++ spec/color-parser-spec.coffee
@@ -492,3 +492,31 @@
   itParses('contrast(#bbbbbb, rgb(20,20,20))').asColor(20,20,20)
   itParses('contrast(#333333, rgb(20,20,20), rgb(140,140,140))').asColor(140,140,140)
   itParses('contrast(#666666, rgb(20,20,20), rgb(140,140,140), 13%)').asColor(140,140,140)
+
+  itParses('contrast(@base)').withContext({
+    '@base': asColor '#bbbbbb'
+  }).asColor(0,0,0)
+  itParses('contrast(@base)').withContext({
+    '@base': asColor '#333333'
+  }).asColor(255,255,255)
+  itParses('contrast(@base, @dark)').withContext({
+    '@base': asColor '#bbbbbb'
+    '@dark': asColor 'rgb(20,20,20)'
+  }).asColor(20,20,20)
+  itParses('contrast(@base, @dark, @light)').withContext({
+    '@base': asColor '#333333'
+    '@dark': asColor 'rgb(20,20,20)'
+    '@light': asColor 'rgb(140,140,140)'
+  }).asColor(140,140,140)
+  itParses('contrast(@base, @dark, @light, @threshold)').withContext({
+    '@base': asColor '#666666'
+    '@dark': asColor 'rgb(20,20,20)'
+    '@light': asColor 'rgb(140,140,140)'
+    '@threshold': '13%'
+  }).asColor(140,140,140)
+
+  itParses('contrast(@base)').asInvalid()
+  itParses('contrast(@base)').asInvalid()
+  itParses('contrast(@base, @dark)').asInvalid()
+  itParses('contrast(@base, @dark, @light)').asInvalid()
+  itParses('contrast(@base, @dark, @light, @threshold)').asInvalid()


made it so the clear output button actually works

--- lib/views/terminal/terminal-view.coffee
+++ lib/views/terminal/terminal-view.coffee
@@ -9,7 +9,7 @@
       @hideBtn = @element.querySelector '#pros-terminal-hide'
       @hideBtn.onclick = => @hide()
       @clearBtn = @element.querySelector '#pros-terminal-clear-output'
-      @clearBtn.onclicl = => @clearOutput
+      @clearBtn.onclick = => @clearOutput()

     show: =>
       @panel ?= atom.workspace.addBottomPanel className: 'PROSTerminal', item: this


Reenable insertNewlineBelow() specs

Reenable insertNewlineBelow() specs

--- spec/edit-session-spec.coffee
+++ spec/edit-session-spec.coffee
@@ -1326,7 +1326,7 @@
               expect(cursor2.getBufferPosition()).toEqual [8,0]

       describe ".insertNewlineBelow()", ->
-        xdescribe "when the operation is undone", ->
+        describe "when the operation is undone", ->
           it "places the cursor back at the previous location", ->
             editSession.setCursorBufferPosition([0,2])
             editSession.insertNewlineBelow()
@@ -1334,7 +1334,7 @@
             editSession.undo()
             expect(editSession.getCursorBufferPosition()).toEqual [0,2]

-        xit "inserts a newline below the cursor's current line, autoindents it, and moves the cursor to the end of the line", ->
+        it "inserts a newline below the cursor's current line, autoindents it, and moves the cursor to the end of the line", ->
           editSession.setAutoIndent(true)
           editSession.insertNewlineBelow()
           expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"


:lipstick: for readability

:lipstick: for readability

--- spec/text-objects-spec.coffee
+++ spec/text-objects-spec.coffee
@@ -52,6 +52,9 @@
   selectedScreenRange = ->
       expect editor.getSelectedScreenRange()

+  selectedScreenRanges = ->
+      expect editor.getSelectedScreenRanges()
+
   selectedBufferRange = ->
       expect editor.getSelectedBufferRange()

@@ -67,31 +70,58 @@
   classListContains = (klass) ->
     expect editorElement.classList.contains(klass)

-  describe "the 'iw' text object", ->
-    beforeEach ->
-      text "12345 abcde ABCDE"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keystroke 'diw'
-
-      text().toBe "12345  ABCDE"
-      cursor().toEqual [0, 6]
-      register('"').toBe 'abcde'
+  ensure = (spec={}) ->
+    if spec.text?
+      text().toBe spec.text
+    if spec.cursor?
+      cursor().toEqual spec.cursor
+    if spec.register?
+      register('"').toBe spec.register
+
+    if spec.selectedBufferRange?
+      selectedBufferRange().toEqual spec.selectedBufferRange
+    if spec.selectedBufferRanges?
+      selectedBufferRanges().toEqual spec.selectedBufferRanges
+
+    if spec.selectedScreenRange?
+      selectedScreenRange().toEqual spec.selectedScreenRange
+    if spec.selectedScreenRanges?
+      selectedScreenRanges().toEqual spec.selectedScreenRanges
+
+    notEnsureNormalMode = [
+      spec.selectedBufferRange, spec.selectedBufferRanges
+      spec.selectedScreenRange, spec.selectedScreenRanges
+    ].some((bool) -> bool)
+    unless notEnsureNormalMode
       classListContains('operator-pending-mode').toBe(false)
       classListContains('normal-mode').toBe(true)

+  ensureKeystroke = (_keystroke, spec={}) ->
+    keystroke(_keystroke)
+    ensure(spec)
+
+  describe "the 'iw' text object", ->
+    beforeEach ->
+      text "12345 abcde ABCDE"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensureKeystroke 'diw',
+        text:     "12345  ABCDE"
+        cursor:   [0, 6]
+        register: 'abcde'
+
     it "selects inside the current word in visual mode", ->
-      keystroke 'viw'
-      selectedScreenRange().toEqual [[0, 6], [0, 11]]
+      ensureKeystroke 'viw',
+        selectedScreenRange: [[0, 6], [0, 11]]

     it "works with multiple cursors", ->
       addCursor [0, 1]
-      keystroke 'viw'
-      selectedBufferRanges().toEqual [
-        [[0, 6], [0, 11]]
-        [[0, 0], [0, 5]]
-      ]
+      ensureKeystroke 'viw',
+        selectedBufferRanges: [
+          [[0, 6], [0, 11]]
+          [[0, 0], [0, 5]]
+        ]

   describe "the 'iW' text object", ->
     beforeEach ->
@@ -99,17 +129,14 @@
       cursor [0, 9]

     it "applies operators inside the current whole word in operator-pending mode", ->
-      keystroke 'diW'
-
-      text().toBe "12(45  ABCDE"
-      cursor().toEqual [0, 6]
-      register('"').toBe "ab'de"
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'diW',
+        text:     "12(45  ABCDE"
+        cursor:   [0, 6]
+        register: "ab'de"

     it "selects inside the current whole word in visual mode", ->
-      keystroke 'viW'
-      selectedScreenRange().toEqual [[0, 6], [0, 11]]
+      ensureKeystroke 'viW',
+        selectedScreenRange: [[0, 6], [0, 11]]

   describe "the 'i(' text object", ->
     beforeEach ->
@@ -117,29 +144,25 @@
       cursor [0, 9]

     it "applies operators inside the current word in operator-pending mode", ->
-      keystroke 'di('
-      text().toBe "()"
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di(',
+        text: "()"
+        cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'di('
-      text().toBe "( something in here and in () )"
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di(',
+        text: "( something in here and in () )"
+        cursor: [0, 28]

     it "works with multiple cursors", ->
       text "( a b ) cde ( f g h ) ijk"
       cursorBuffer [0, 2]
       addCursor [0, 18]
-      keystroke 'vi('
-      selectedBufferRanges().toEqual [
-        [[0, 1],  [0, 6]]
-        [[0, 13], [0, 20]]
-      ]
+      ensureKeystroke 'vi(',
+        selectedBufferRanges: [
+          [[0, 1],  [0, 6]]
+          [[0, 13], [0, 20]]
+        ]

   describe "the 'i{' text object", ->
     beforeEach ->
@@ -147,19 +170,15 @@
       cursor [0, 9]

     it "applies operators inside the current word in operator-pending mode", ->
-      keystroke 'di{'
-      text().toBe "{}"
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di{',
+        text: "{}"
+        cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'di{'
-      text().toBe "{ something in here and in {} }"
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di{',
+        text: "{ something in here and in {} }"
+        cursor: [0, 28]

   describe "the 'i<' text object", ->
     beforeEach ->
@@ -167,19 +186,15 @@
       cursor [0, 9]

     it "applies operators inside the current word in operator-pending mode", ->
-      keystroke 'di<'
-      text().toBe "<>"
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di<',
+        text: "<>"
+        cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'di<'
-      text().toBe "< something in here and in <> >"
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di<',
+        text: "< something in here and in <> >"
+        cursor: [0, 28]

   describe "the 'it' text object", ->
     beforeEach ->
@@ -187,19 +202,15 @@
       cursor [0, 5]

     it "applies only if in the value of a tag", ->
-      keystroke 'dit'
-      text().toBe "<something></something><again>"
-      cursor().toEqual [0, 11]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'dit',
+        text: "<something></something><again>"
+        cursor: [0, 11]

     it "applies operators inside the current word in operator-pending mode", ->
       cursor [0, 13]
-      keystroke 'dit'
-      text().toBe "<something></something><again>"
-      cursor().toEqual [0, 11]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'dit',
+        text: "<something></something><again>"
+        cursor: [0, 11]

   describe "the 'ip' text object", ->
     beforeEach ->
@@ -207,16 +218,14 @@
       cursor [2, 2]

     it "applies operators inside the current paragraph in operator-pending mode", ->
-      keystroke 'yip'
-      text().toBe "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
-      cursor().toEqual [1, 0]
-      register('"').toBe "Paragraph-1\nParagraph-1\nParagraph-1\n"
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'yip',
+        text: "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
+        cursor: [1, 0]
+        register: "Paragraph-1\nParagraph-1\nParagraph-1\n"

     it "selects inside the current paragraph in visual mode", ->
-      keystroke 'vip'
-      selectedScreenRange().toEqual [[1, 0], [4, 0]]
+      ensureKeystroke 'vip',
+        selectedScreenRange: [[1, 0], [4, 0]]

   describe "the 'ap' text object", ->
     beforeEach ->
@@ -224,16 +233,14 @@
       cursor [3, 2]

     it "applies operators around the current paragraph in operator-pending mode", ->
-      keystroke 'yap'
-      text().toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
-      cursor().toEqual [2, 0]
-      register('"').toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n"
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'yap',
+        text: "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
+        cursor: [2, 0]
+        register: "Paragraph-1\nParagraph-1\nParagraph-1\n\n"

     it "selects around the current paragraph in visual mode", ->
-      keystroke 'vap'
-      selectedScreenRange().toEqual [[2, 0], [6, 0]]
+      ensureKeystroke 'vap',
+        selectedScreenRange: [[2, 0], [6, 0]]

   describe "the 'i[' text object", ->
     beforeEach ->
@@ -241,19 +248,15 @@
       cursor [0, 9]

     it "applies operators inside the current word in operator-pending mode", ->
-      keystroke 'di['
-      text().toBe "[]"
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di[',
+        text: "[]"
+        cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'di['
-      text().toBe "[ something in here and in [] ]"
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di[',
+        text: "[ something in here and in [] ]"
+        cursor: [0, 28]

   describe "the 'i\'' text object", ->
     beforeEach ->
@@ -261,30 +264,24 @@
       cursor [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      keystroke "di'"
-      text().toBe "''here' ' and over here"
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke "di'",
+        text: "''here' ' and over here"
+        cursor: [0, 1]

     # I don't like old behavior, that was not in Vim and furthermore, this is counter intuitive.
     # Simply selecting area between quote is that normal user expects.
     # it "applies operators inside the next string in operator-pending mode (if not in a string)", ->
     it "[Changed behavior] applies operators inside area between quote", ->
       cursor [0, 29]
-      keystroke "di'"
-      text().toBe "' something in here and in '' ' and over here"
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke "di'",
+        text: "' something in here and in '' ' and over here"
+        cursor: [0, 28]

     it "makes no change if past the last string on a line", ->
       cursor [0, 39]
-      keystroke "di'"
-      text().toBe "' something in here and in 'here' ' and over here"
-      cursor().toEqual [0, 39]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke "di'",
+        text: "' something in here and in 'here' ' and over here"
+        cursor: [0, 39]

   describe "the 'i\"' text object", ->
     beforeEach ->
@@ -292,26 +289,21 @@
       cursor [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      keystroke 'di"'
-      text().toBe '""here" " and over here'
-      cursor().toEqual [0, 1]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di"',
+        text: '""here" " and over here'
+        cursor: [0, 1]

     it "[Changed behavior] applies operators inside area between quote", ->
       cursor [0, 29]
-      keystroke 'di"'
-      text().toBe '" something in here and in "" " and over here'
-      cursor().toEqual [0, 28]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'di"',
+        text: '" something in here and in "" " and over here'
+        cursor: [0, 28]

     it "makes no change if past the last string on a line", ->
       cursor [0, 39]
-      keystroke 'di"'
-      text().toBe '" something in here and in "here" " and over here'
-      cursor().toEqual [0, 39]
-      classListContains('operator-pending-mode').toBe(false)
+      ensureKeystroke 'di"',
+        text: '" something in here and in "here" " and over here'
+        cursor: [0, 39]

   describe "the 'aw' text object", ->
     beforeEach ->
@@ -319,28 +311,26 @@
       cursor [0, 9]

     it "applies operators from the start of the current word to the start of the next word in operator-pending mode", ->
-      keystroke 'daw'
-      text().toBe "12345 ABCDE"
-      cursor().toEqual [0, 6]
-      register('"').toBe "abcde "
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'daw',
+        text: "12345 ABCDE"
+        cursor: [0, 6]
+        register: "abcde "

     it "selects from the start of the current word to the start of the next word in visual mode", ->
-      keystroke 'vaw'
-      selectedScreenRange().toEqual [[0, 6], [0, 12]]
+      ensureKeystroke 'vaw',
+        selectedScreenRange: [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
       text "12345\nabcde ABCDE"
       cursor [0, 3]
-      keystroke 'vaw'
-      selectedBufferRange().toEqual [[0, 0], [0, 5]]
+      ensureKeystroke 'vaw',
+        selectedBufferRange: [[0, 0], [0, 5]]

     it "doesn't span special characters", ->
       text "1(345\nabcde ABCDE"
       cursor [0, 3]
-      keystroke 'vaw'
-      selectedBufferRange().toEqual [[0, 2], [0, 5]]
+      ensureKeystroke 'vaw',
+        selectedBufferRange: [[0, 2], [0, 5]]

   describe "the 'aW' text object", ->
     beforeEach ->
@@ -348,22 +338,20 @@
       cursor [0, 9]

     it "applies operators from the start of the current whole word to the start of the next whole word in operator-pending mode", ->
-      keystroke 'daW'
-      text().toBe "12(45 ABCDE"
-      cursor().toEqual [0, 6]
-      register('"').toBe "ab'de "
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'daW',
+        text: "12(45 ABCDE"
+        cursor: [0, 6]
+        register: "ab'de "

     it "selects from the start of the current whole word to the start of the next whole word in visual mode", ->
-      keystroke 'vaW'
-      selectedScreenRange().toEqual [[0, 6], [0, 12]]
+      ensureKeystroke 'vaW',
+        selectedScreenRange: [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
       text "12(45\nab'de ABCDE"
       cursor [0, 4]
-      keystroke 'vaW'
-      selectedBufferRange().toEqual [[0, 0], [0, 5]]
+      ensureKeystroke 'vaW',
+        selectedBufferRange: [[0, 0], [0, 5]]

   describe "the 'a(' text object", ->
     beforeEach ->
@@ -371,19 +359,15 @@
       cursor [0, 9]

     it "applies operators around the current parentheses in operator-pending mode", ->
-      keystroke 'da('
-      text().toBe ""
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da(',
+        text: ''
+        cursor: [0, 0]

     it "applies operators around the current parentheses in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'da('
-      text().toBe "( something in here and in  )"
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da(',
+        text: "( something in here and in  )"
+        cursor: [0, 27]

   describe "the 'a{' text object", ->
     beforeEach ->
@@ -391,19 +375,15 @@
       cursor [0, 9]

     it "applies operators around the current curly brackets in operator-pending mode", ->
-      keystroke 'da{'
-      text().toBe ""
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da{',
+        text: ''
+        cursor: [0, 0]

     it "applies operators around the current curly brackets in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'da{'
-      text().toBe "{ something in here and in  }"
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da{',
+        text: "{ something in here and in  }"
+        cursor: [0, 27]

   describe "the 'a<' text object", ->
     beforeEach ->
@@ -411,19 +391,15 @@
       cursor [0, 9]

     it "applies operators around the current angle brackets in operator-pending mode", ->
-      keystroke 'da<'
-      text().toBe ""
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da<',
+        text: ''
+        cursor: [0, 0]

     it "applies operators around the current angle brackets in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'da<'
-      text().toBe "< something in here and in  >"
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da<',
+        text: "< something in here and in  >"
+        cursor: [0, 27]

   describe "the 'a[' text object", ->
     beforeEach ->
@@ -431,19 +407,15 @@
       cursor [0, 9]

     it "applies operators around the current square brackets in operator-pending mode", ->
-      keystroke 'da['
-      text().toBe ""
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da[',
+        text: ''
+        cursor: [0, 0]

     it "applies operators around the current square brackets in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'da['
-      text().toBe "[ something in here and in  ]"
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da[',
+        text: "[ something in here and in  ]"
+        cursor: [0, 27]

   describe "the 'a\'' text object", ->
     beforeEach ->
@@ -451,19 +423,15 @@
       cursor [0, 9]

     it "applies operators around the current single quotes in operator-pending mode", ->
-      keystroke "da'"
-      text().toBe "here' '"
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke "da'",
+        text: "here' '"
+        cursor: [0, 0]

     it "applies operators around the current single quotes in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke "da'"
-      text().toBe "' something in here and in  '"
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke "da'",
+        text: "' something in here and in  '"
+        cursor: [0, 27]

   describe "the 'a\"' text object", ->
     beforeEach ->
@@ -471,16 +439,12 @@
       cursor [0, 9]

     it "applies operators around the current double quotes in operator-pending mode", ->
-      keystroke 'da"'
-      text().toBe 'here" "'
-      cursor().toEqual [0, 0]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da"',
+        text: 'here" "'
+        cursor: [0, 0]

     it "applies operators around the current double quotes in operator-pending mode (second test)", ->
       cursor [0, 29]
-      keystroke 'da"'
-      text().toBe '" something in here and in  "'
-      cursor().toEqual [0, 27]
-      classListContains('operator-pending-mode').toBe(false)
-      classListContains('normal-mode').toBe(true)
+      ensureKeystroke 'da"',
+        text: '" something in here and in  "'
+        cursor: [0, 27]


Decouple string-matching patterns from parameter matching

Having a rule to match unquoted identifiers makes it impossible to reuse
the quoted-string patterns in other rules. Since we don't really need to
highlight barewords outside a request statement, a specific group's been
added to contain patterns used in request-lines.

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -76,12 +76,11 @@

-	# Double/single-quoted strings (used in requests)
+	# Double/single-quoted strings
 	strings:
 		patterns: [
 			{include: "#single-quoted-string"},
-			{include: "#double-quoted-string"},
-			{name: "string.unquoted.roff", begin: "(?=[A-Za-z_])", end: "(?=[^\\w])"}
+			{include: "#double-quoted-string"}
 		]

 	"single-quoted-string":
@@ -90,7 +89,10 @@
 		end:   "'"
 		beginCaptures: {0: {name: "punctuation.definition.string.begin.roff"}}
 		endCaptures:   {0: {name: "punctuation.definition.string.end.roff"}}
-		patterns: [{include: "#single-quoted-includes"}]
+		patterns: [{include: "#escapes"}, {
+			match: "[^']*[^'\\n\\r\\\\]$"
+			name: "invalid.illegal.string.roff"
+		}]

 	"double-quoted-string":
 		name: "string.quoted.double.roff",
@@ -98,22 +100,20 @@
 		end:   '"'
 		beginCaptures: {0: {name: "punctuation.definition.string.begin.roff"}}
 		endCaptures:   {0: {name: "punctuation.definition.string.end.roff"}}
-		patterns: [{include: "#double-quoted-includes"}]
-
-
-	# Inclusions for 'single-quoted strings'
-	"single-quoted-includes":
-		patterns: [{include: "#escapes"}, {
-			match: "[^']*[^'\\n\\r\\\\]$"
-			name: "invalid.illegal.string.roff"
-		}]
-
-	# Inclusions for "double-quoted strings"
-	"double-quoted-includes":
 		patterns: [{include: "#escapes"}, {
 			match: '[^"]*[^"\\n\\r\\\\]$'
 			name: "invalid.illegal.string.roff"
 		}]
+
+
+
+
+	# Request parameters
+	params:
+		patterns: [
+			{include: "#strings"},
+			{name: "string.unquoted.roff", begin: "(?=[A-Za-z_])", end: "(?=[^\\w])"}
+		]

@@ -220,7 +220,7 @@
 				1: {name: "entity.name.function.roff"}
 			patterns: [
 				{ include: "#main"    }
-				{ include: "#strings" }
+				{ include: "#params"  }
 			]
 		}, {

@@ -232,7 +232,7 @@
 				1: {name: "entity.name.function.roff"}
 			patterns: [
 				{ include: "#main"    }
-				{ include: "#strings" }
+				{ include: "#params"  }
 			]
 		}, {

@@ -244,7 +244,7 @@
 				1: {name: "entity.name.function.roff"}
 			patterns: [
 				{ include: "#main"    }
-				{ include: "#strings" }
+				{ include: "#params"  }
 			]

 		}]


targets: save a few pixels

targets: save a few pixels

--- app/assets/javascripts/d3/targets.coffee
+++ app/assets/javascripts/d3/targets.coffee
@@ -22,7 +22,7 @@
       @namespace.series.push @success_query, @value_query, @target_query

       @scale = d3.scale.linear()
-      @axis = d3.svg.axis().ticks(4).orient('bottom')
+      @axis = d3.svg.axis().tickSize(2, 0).ticks(4).orient('bottom')

     max_value: =>
       max = 0
@@ -52,10 +52,10 @@

     draw: =>
       margins =
-        top: 40
-        bottom: 100
-        left: 30
-        right: 30
+        top: 15
+        bottom: 5
+        left: 20
+        right: 20

       @width = (@container_node().width()   || 490) - (margins.left + margins.right)
       @height = (@container_node().height() || 402) - (margins.top + margins.bottom)
@@ -73,7 +73,7 @@
         .enter()
         .append('svg:g')
         .attr('class', 'target')
-        .attr('transform', (d, i) -> "translate(0, #{i * 40})")
+        .attr('transform', (d, i) -> "translate(0, #{i * 33})")

       # labels first
       @items.append("svg:text")
@@ -96,14 +96,14 @@
       @current_values = @blocks.append("svg:rect")
         .attr('class', 'current_value')
         .attr('y', -10)
-        .attr('height', 10)
+        .attr('height', 8)
         .attr('width', 0)
         .attr('fill', '#66ccff')

       @future_values = @blocks.append("svg:rect")
         .attr('class', 'future_value')
-        .attr('y', 0)
-        .attr('height', 10)
+        .attr('y', -1)
+        .attr('height', 8)
         .attr('width', 0)
         .attr('fill', '#0080ff')

@@ -117,7 +117,7 @@

       @axis = @blocks.append('svg:g')
         .attr("class", 'x_axis')
-        .attr('transform', 'translate(0.5,10.5)')
+        .attr('transform', 'translate(0.5, 7.5)')

     refresh: =>
       t.update_scale() for t in @targets


Don't like the two slashes in the output

--- grunt/amd_tamer.coffee
+++ grunt/amd_tamer.coffee
@@ -17,5 +17,5 @@
       src: '*.js'
       dest: 'dist/files/'
       rename: (dest, src) ->
-        "#{dest}/snowflake.renderer.#{src}"
+        "#{dest}snowflake.renderer.#{src}"
     }]


tweaking polylines

tweaking polylines

--- app/assets/transit/lib/views/plan.coffee
+++ app/assets/transit/lib/views/plan.coffee
@@ -56,16 +56,16 @@
   render_map: =>
     @clean_up()
     itinerary = Transit.plan.get('itineraries').first()
-    colors = {'BUS': 'blue', 'WALK': 'black'}
+    colors = {'BUS': '#025d8c', 'WALK': 'black'}
     for leg in itinerary.get('legs')
-      @draw_polyline(leg.legGeometry.points, colors[leg.mode] ? 'green')
+      @draw_polyline(leg.legGeometry.points, colors[leg.mode] ? '#1693a5')
     @map.addLayer(@plan_route)

   draw_polyline: (points, color) =>
     points = decodeLine(points)
     latlngs = (new L.LatLng(point[0], point[1]) for point in points)
-    polyline = new L.Polyline(latlngs, color: color)
+    polyline = new L.Polyline(latlngs, color: color, opacity: 0.6, clickable: false)
     @plan_route.addLayer(polyline)
     # zoom the map to the polyline
     #@map.fitBounds(new L.LatLngBounds(latlngs))


Reorganize gamepad display tables

Reorganize gamepad display tables

--- app/client/components/GamepadItem.coffee
+++ app/client/components/GamepadItem.coffee
@@ -21,7 +21,7 @@
     axes: _.map this.props.gamepad.axes, (axis) ->
       numeral(axis).format('0.00000')
     buttons: _.map this.props.gamepad.buttons, (button) ->
-      numeral(button.value).format('0.00')
+      numeral(button.value).format('0')

   renderHeader: ->

@@ -46,87 +46,70 @@
         {this.renderHeader()}
         <Button bsSize="small" onClick={this.openModal} style={{float: 'right'}}>Details</Button>

-      <Modal show={this.state.showModal} onHide={this.closeModal} bsSize="large">
+      <Modal show={this.state.showModal} onHide={this.closeModal}>
         <Modal.Header closeButton>
           <Modal.Title>{this.renderHeader()}</Modal.Title>
         </Modal.Header>
         <Modal.Body>
-          <Grid fluid={true}>
-            <Row>
-              <Col lg={9} md={12}>
-
-              </Col>
-              <Col lg={3} md={12}>
-

-

-

-                              Button 0: {values.buttons[0]}
-

-

-                              Button 1: {values.buttons[1]}
-

-

-                              Button 2: {values.buttons[2]}
-

-

-                              Button 3: {values.buttons[3]}
-

-

-                              Button 4: {values.buttons[4]}
-

-

-                              Button 5: {values.buttons[5]}
-

-

-                              Button 6: {values.buttons[6]}
-

-

-                              Button 7: {values.buttons[7]}
-

-

-                              Button 8: {values.buttons[8]}
-

-

-                              Button 9: {values.buttons[9]}
-

-

-                              Button 10: {values.buttons[10]}
-

-

-                              Button 11: {values.buttons[11]}
-

-

-                              Button 12: {values.buttons[12]}
-

-

-                              Button 13: {values.buttons[13]}
-

-

-                              Button 14: {values.buttons[14]}
-

-

-                              Button 15: {values.buttons[15]}
-

-

-                              Button 16: {values.buttons[16]}
-

-

-                              Axis 0: {values.axes[0]}
-

-

-                              Axis 1: {values.axes[1]}
-

-

-                              Axis 2: {values.axes[2]}
-

-

-                              Axis 3: {values.axes[3]}
-

-

-

-              </Col>
-            </Row>
-          </Grid>
+
+

+

+                Button
+                0
+                1
+                2
+                3
+                4
+                5
+                6
+                7
+                8
+                9
+                10
+                11
+                12
+                13
+                14
+                15
+                16
+

+

+                Value
+                {values.buttons[0]}
+                {values.buttons[1]}
+                {values.buttons[2]}
+                {values.buttons[3]}
+                {values.buttons[4]}
+                {values.buttons[5]}
+                {values.buttons[6]}
+                {values.buttons[7]}
+                {values.buttons[8]}
+                {values.buttons[9]}
+                {values.buttons[10]}
+                {values.buttons[11]}
+                {values.buttons[12]}
+                {values.buttons[13]}
+                {values.buttons[14]}
+                {values.buttons[15]}
+                {values.buttons[16]}
+

+

+

+

+                Axis
+                0
+                1
+                2
+                3
+

+

+                Value
+                {values.axes[0]}
+                {values.axes[1]}
+                {values.axes[2]}
+                {values.axes[3]}
+

+

         </Modal.Body>
       </Modal>
     </ListGroupItem>


fix wysiwyg translation

fix wysiwyg translation

--- app/coffee/modules/common/wisiwyg.coffee
+++ app/coffee/modules/common/wisiwyg.coffee
@@ -130,169 +130,6 @@
             else
                 return cursorPosition

-        markdownSettings =
-            nameSpace: "markdown"
-            onShiftEnter: {keepDefault:false, openWith:"\n\n"}
-            onEnter:
-                keepDefault: false,
-                replaceWith: () -> "\n"
-                afterInsert: (data) ->
-                    lines = data.textarea.value.split("\n")
-                    cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
-                    newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
-                    lastLine = lines[cursorLine - 1]
-
-                    # unordered list -
-                    match = lastLine.match /^(\s*- ).*/
-
-                    if match
-                        emptyListItem = lastLine.match /^(\s*)\-\s$/
-
-                        if emptyListItem
-                            nline = cursorLine - 1
-                            replace = null
-                        else
-                            nline = cursorLine
-                            replace = "#{match[1]}"
-
-                        markdownCaretPositon = addLine(data.textarea, nline, replace)
-
-                    # unordered list *
-                    match = lastLine.match /^(\s*\* ).*/
-
-                    if match
-                        emptyListItem = lastLine.match /^(\s*\* )$/
-
-                        if emptyListItem
-                            nline = cursorLine - 1
-                            replace = null
-                        else
-                            nline = cursorLine
-                            replace = "#{match[1]}"
-
-                        markdownCaretPositon = addLine(data.textarea, nline, replace)
-
-                    # ordered list
-                    match = lastLine.match /^(\s*)(\d+)\.\s/
-
-                    if match
-                        emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/
-
-                        if emptyListItem
-                            nline = cursorLine - 1
-                            replace = null
-                        else
-                            nline = cursorLine
-                            replace = "#{match[1] + (parseInt(match[2], 10) + 1)}. "
-
-                        markdownCaretPositon = addLine(data.textarea, nline, replace)
-
-                    setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon
-
-            markupSet: [
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.H1_BUTTON")
-                    key: "1"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.H1_SAMPLE_TEXT")
-                    closeWith: (markItUp) -> markdownTitle(markItUp, "=")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.H2_BUTTON")
-                    key: "2"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.H2_SAMPLE_TEXT")
-                    closeWith: (markItUp) -> markdownTitle(markItUp, "-")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.H3_BUTTON")
-                    key: "3"
-                    openWith: "### "
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.H3_SAMPLE_TEXT")
-                },
-                {
-                    separator: "---------------"
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON")
-                    key: "B"
-                    openWith: "**"
-                    closeWith: "**"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON_SAMPLE_TEXT")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
-                    key: "I"
-                    openWith: "_"
-                    closeWith: "_"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.STRIKE_BUTTON")
-                    key: "S"
-                    openWith: "~~"
-                    closeWith: "~~"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.STRIKE_SAMPLE_TEXT")
-                },
-                {
-                    separator: "---------------"
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_BUTTON")
-                    openWith: "- "
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_SAMPLE_TEXT")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_BUTTON")
-                    openWith: (markItUp) -> markItUp.line+". "
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_SAMPLE_TEXT")
-                },
-                {
-                    separator: "---------------"
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.PICTURE_BUTTON")
-                    key: "P"
-                    openWith: "!["
-                    closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.PICTURE_SAMPLE_TEXT")
-                    beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
-                    afterInsert:(markItUp) -> urlFormatting(markItUp)
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.LINK_BUTTON")
-                    key: "L"
-                    openWith: "["
-                    closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.LINK_SAMPLE_TEXT")
-                    beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
-                    afterInsert:(markItUp) -> urlFormatting(markItUp)
-                },
-                {
-                    separator: "---------------"
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_BUTTON")
-                    openWith: "> "
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_SAMPLE_TEXT")
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_BUTTON")
-                    openWith: "```\n"
-                    placeHolder: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_SAMPLE_TEXT")
-                    closeWith: "\n```"
-                },
-                {
-                    separator: "---------------"
-                },
-                {
-                    name: $translate.instant("COMMON.WYSIWYG.PREVIEW_BUTTON")
-                    call: preview
-                    className: "preview-icon"
-                },
-            ]
-            afterInsert: (event) ->
-                target = angular.element(event.textarea)
-                $model.$setViewValue(target.val())
-
         prepareUrlFormatting = (markItUp) ->
             regex = /(<<<|>>>)/gi
             result = 0
@@ -339,13 +176,184 @@

             return "\n"+heading+"\n"

-        element.markItUp(markdownSettings)
+        renderMarkItUp = () ->
+            markdownSettings =
+                nameSpace: "markdown"
+                onShiftEnter: {keepDefault:false, openWith:"\n\n"}
+                onEnter:
+                    keepDefault: false,
+                    replaceWith: () -> "\n"
+                    afterInsert: (data) ->
+                        lines = data.textarea.value.split("\n")
+                        cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
+                        newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
+                        lastLine = lines[cursorLine - 1]
+
+                        # unordered list -
+                        match = lastLine.match /^(\s*- ).*/
+
+                        if match
+                            emptyListItem = lastLine.match /^(\s*)\-\s$/
+
+                            if emptyListItem
+                                nline = cursorLine - 1
+                                replace = null
+                            else
+                                nline = cursorLine
+                                replace = "#{match[1]}"
+
+                            markdownCaretPositon = addLine(data.textarea, nline, replace)
+
+                        # unordered list *
+                        match = lastLine.match /^(\s*\* ).*/
+
+                        if match
+                            emptyListItem = lastLine.match /^(\s*\* )$/
+
+                            if emptyListItem
+                                nline = cursorLine - 1
+                                replace = null
+                            else
+                                nline = cursorLine
+                                replace = "#{match[1]}"
+
+                            markdownCaretPositon = addLine(data.textarea, nline, replace)
+
+                        # ordered list
+                        match = lastLine.match /^(\s*)(\d+)\.\s/
+
+                        if match
+                            emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/
+
+                            if emptyListItem
+                                nline = cursorLine - 1
+                                replace = null
+                            else
+                                nline = cursorLine
+                                replace = "#{match[1] + (parseInt(match[2], 10) + 1)}. "
+
+                            markdownCaretPositon = addLine(data.textarea, nline, replace)
+
+                        setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon
+
+                markupSet: [
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.H1_BUTTON")
+                        key: "1"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.H1_SAMPLE_TEXT")
+                        closeWith: (markItUp) -> markdownTitle(markItUp, "=")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.H2_BUTTON")
+                        key: "2"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.H2_SAMPLE_TEXT")
+                        closeWith: (markItUp) -> markdownTitle(markItUp, "-")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.H3_BUTTON")
+                        key: "3"
+                        openWith: "### "
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.H3_SAMPLE_TEXT")
+                    },
+                    {
+                        separator: "---------------"
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON")
+                        key: "B"
+                        openWith: "**"
+                        closeWith: "**"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON_SAMPLE_TEXT")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
+                        key: "I"
+                        openWith: "_"
+                        closeWith: "_"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.STRIKE_BUTTON")
+                        key: "S"
+                        openWith: "~~"
+                        closeWith: "~~"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.STRIKE_SAMPLE_TEXT")
+                    },
+                    {
+                        separator: "---------------"
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_BUTTON")
+                        openWith: "- "
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_SAMPLE_TEXT")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_BUTTON")
+                        openWith: (markItUp) -> markItUp.line+". "
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_SAMPLE_TEXT")
+                    },
+                    {
+                        separator: "---------------"
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.PICTURE_BUTTON")
+                        key: "P"
+                        openWith: "!["
+                        closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.PICTURE_SAMPLE_TEXT")
+                        beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
+                        afterInsert:(markItUp) -> urlFormatting(markItUp)
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.LINK_BUTTON")
+                        key: "L"
+                        openWith: "["
+                        closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.LINK_SAMPLE_TEXT")
+                        beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
+                        afterInsert:(markItUp) -> urlFormatting(markItUp)
+                    },
+                    {
+                        separator: "---------------"
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_BUTTON")
+                        openWith: "> "
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_SAMPLE_TEXT")
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_BUTTON")
+                        openWith: "```\n"
+                        placeHolder: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_SAMPLE_TEXT")
+                        closeWith: "\n```"
+                    },
+                    {
+                        separator: "---------------"
+                    },
+                    {
+                        name: $translate.instant("COMMON.WYSIWYG.PREVIEW_BUTTON")
+                        call: preview
+                        className: "preview-icon"
+                    },
+                ]
+                afterInsert: (event) ->
+                    target = angular.element(event.textarea)
+                    $model.$setViewValue(target.val())
+
+            element
+                .markItUpRemove()
+                .markItUp(markdownSettings)
+
+        renderMarkItUp()
+
+        unbind = $rootscope.$on "$translateChangeEnd", renderMarkItUp

         element.on "keypress", (event) ->
             $scope.$apply()

         $scope.$on "$destroy", ->
             $el.off()
+            unbind()

     return {link:link, require:"ngModel"}


GameObject: Fix children of clones not being parented to clone.

--- engine/gameobject.coffee
+++ engine/gameobject.coffee
@@ -443,7 +443,10 @@
     clone: (scene=this.scene, recursive=false) ->
         n = Object.create @
         if recursive
-            n.children = for child in @children then child.clone(scene, true)
+            n.children = for child in @children
+                child.clone(scene, true)
+                child.parent = n
+                child
         else
             n.children = []
         n.position = vec3.clone @position


bytes_to_key only once

bytes_to_key only once

--- src/encrypt.coffee
+++ src/encrypt.coffee
@@ -70,7 +70,11 @@
   else
     return new Buffer(input, 'binary')

+bytes_to_key_results = {}
+
 EVP_BytesToKey = (password, key_len, iv_len) ->
+  if bytes_to_key_results[password]
+    return bytes_to_key_results[password]
   m = []
   i = 0
   count = 0
@@ -87,6 +91,7 @@
   ms = Buffer.concat(m)
   key = ms.slice(0, key_len)
   iv = ms.slice(key_len, key_len + iv_len)
+  bytes_to_key_results[password] = [key, iv]
   return [key, iv]


Correct output for exceptions

Correct output for exceptions

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -138,7 +138,7 @@

   generate config, (err) ->
     if err
-      console.error err.toString().red
+      console.error err.message.red
       process.exit -1

     console.log "Successfully generated site: ".green +


Add forks repos endpoint

Add forks repos endpoint

https://developer.github.com/v3/repos/forks/

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -101,6 +101,7 @@
         | subscription
         | comments (/[0-9]+)?
         | downloads (/[0-9]+)?
+        | forks
         | milestones
         | labels
         | releases
@@ -263,6 +264,7 @@
     'subscription'  : false
     'comments'      : false
     'downloads'     : false
+    'forks'         : false
     'milestones'    : false
     'labels'        : false
     'releases'      : false


Fix ?removePermission

Fix ?removePermission

--- handlers/opserv.coffee
+++ handlers/opserv.coffee
@@ -157,7 +157,7 @@
 						if user?
 							db.hasPermissionByUserID user.userID, permission.join('').trim(), (alreadyHasPermission) ->
 								if alreadyHasPermission
-									db.givePermissionToUserID user.userID, permission.join('').trim(), (rows) ->
+									db.removePermissionFromUserID user.userID, permission.join('').trim(), (rows) ->
 										api.replyTo message, __("Removed %1$s from “%2$s”", permission, username), no, callback
 								else
 									api.replyTo message, __("“%2$s” does not have the permission %1$s", permission, username), no, callback


fix(helpers): fixed get_fn not using the provided callback function

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -44,13 +44,13 @@

 get_fn = (id_param, api_name, name, model, api, options = {}) ->
   (params = {}, callbackOrObservable) =>
-    unless callback?
-      callback = (data) =>
-        res = []
-        for m in data
-          res.push model(m)
-        @[api_name] res
-    else if typeof callbackOrObservable['push'] == 'undefined'
+    callback = (data) =>
+      res = []
+      for m in data
+        res.push model(m)
+      @[api_name] res
+
+    if callbackOrObservable? && typeof callbackOrObservable['push'] == 'undefined'
       if typeof callbackOrObservable != 'function'
         throw new Error 'model.find 2nd parameter needs to be either a function or a pushable object (Array, ObservableArray).\nGiven: ' + callbackOrObservable


`extend` already exists + check on devDependencies existence

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -2,7 +2,6 @@

 {exec} = require 'child_process'
 coffeescript = require 'coffee-script'
-_ = require 'underscore'
 express = require 'express'
 fs = require 'fs'
 sysPath = require 'path'
@@ -232,7 +231,7 @@
   fs.readFile config.paths.packageConfig, (error, data) ->
     return callback error if error?
     json = JSON.parse(data)
-    deps = Object.keys(_.extend(json.devDependencies, json.dependencies))
+    deps = Object.keys(extend(json.devDependencies ? {}, json.dependencies))
     try
       plugins = deps
         .map (dependency) ->


Change signature of sendIntelLink per feedback

--- src/ingress.coffee
+++ src/ingress.coffee
@@ -235,12 +235,12 @@
           coords = body.results[0].geometry.location
         catch err
           err = "Could not find #{location}"
-          return cb(msg, null, err)
-        cb(msg, coords, err)
+          return cb(err, msg, null)
+        cb(err, msg, coords)

   intelmapUrl = (coords) ->
     return "https://www.ingress.com/intel?ll=" + encodeURIComponent(coords.lat) + "," + encodeURIComponent(coords.lng) + "&z=16"
-  sendIntelLink = (msg, coords, err) ->
+  sendIntelLink = (err, msg, coords) ->
     return msg.send err if err
     url = intelmapUrl coords
     msg.reply url


Fix bug where packages couldn't be installed globally

Signed-off-by: Michelle Tilley <19f7a7eb6786def4c39d6d86c40af5dbc6589663@github.com>

--- src/install.coffee
+++ src/install.coffee
@@ -107,7 +107,9 @@
     installOptions.streaming = true if @verbose
     installOptions.cwd = modulePath if options.installInPlace

-    installGlobally = options.installGlobally and not options.installInPlace
+    installGlobally = options.installGlobally ? true
+    if options.installInPlace
+      installGlobally = false

     if installGlobally
       installDirectory = temp.mkdirSync('apm-install-dir-')


Updated Atom settings.

Updated Atom settings.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -1,45 +1,58 @@
 "*":
+  "*":
+    "exception-reporting":
+      userId: "db51cad4-82e1-4bc0-cc93-449bd743d7e9"
+    "release-notes":
+      viewedVersion: "0.92.0"
+    welcome:
+      showOnStartup: false
+    metrics:
+      userId: "f98723003432ede403e38c56f7fee9ad398c422e"
+    editor:
+      showInvisibles: true
+      fontSize: 14
+    core:
+      disabledPackages: [
+        "term"
+        "atom-jshint"
+        "file-icons"
+        "themed-settings"
+        "symbols-view"
+      ]
+      themes: [
+        "seti-ui"
+        "seti-monokai"
+      ]
+    minimap:
+      plugins:
+        "highlight-selected": true
+        "git-diff": true
+        "find-and-replace": true
+    "browser-refresh":
+      saveFilesBeforeRefresh: true
+    iTunes:
+      "showEqualizer (WindowResizePerformanceIssue)": false
+    "open-in":
+      applications: "SourceTree,Terminal,Finder,WebStorm"
+    "highlight-line":
+      enableUnderline: true
+    "atom-ctags":
+      cmdArgs: "--exclude=node_modules"
+  "autoclose-html": {}
+  linter: {}
   "exception-reporting":
-    userId: "db51cad4-82e1-4bc0-cc93-449bd743d7e9"
-  "release-notes":
-    viewedVersion: "0.92.0"
+    userId: "3e371242-4a5b-6e92-780d-36c1679a069e"
   welcome:
     showOnStartup: false
-  metrics:
-    userId: "f98723003432ede403e38c56f7fee9ad398c422e"
+  minimap:
+    plugins:
+      "git-diff": true
+      "highlight-selected": true
+  core:
+    disabledPackages: [
+      "themed-settings"
+    ]
   editor:
-    showInvisibles: true
     invisibles: {}
     fontSize: 14
-  core:
-    disabledPackages: [
-      "term"
-      "atom-jshint"
-      "file-icons"
-      "themed-settings"
-      "symbols-view"
-    ]
-    themes: [
-      "seti-ui"
-      "seti-monokai"
-    ]
-  minimap:
-    plugins:
-      "highlight-selected": true
-      "git-diff": true
-      "find-and-replace": true
-  "browser-refresh":
-    saveFilesBeforeRefresh: true
-  iTunes:
-    "showEqualizer (WindowResizePerformanceIssue)": false
-  "open-in":
-    applications: "SourceTree,Terminal,Finder,WebStorm"
-  "project-manager": {}
-  "highlight-line":
-    enableUnderline: true
-  "autoclose-html": {}
-  linter: {}
-  "git-plus": {}
-  "atom-ctags":
-    cmdArgs: "--exclude=node_modules"
-  "autocomplete-plus": {}
+    showInvisibles: true


Ignore "usr" and "etc" names in Atom fuzzy-finder.

In this configuration, Atom fuzzy-finder indexes these two symlinked
directories unless they are excluded. The simplest way to exclude them
appears to be using the `ignoredNames` config setting, though this
ignores these two names globally.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -46,3 +46,8 @@
     ".zsh-theme$": "source.shell"
     "^.zshenv.local.*$": "source.shell"
     "^.zshrc.local.*$": "source.shell"
+  "fuzzy-finder":
+    ignoredNames: [
+      "usr"
+      "etc"
+    ]


Cmd-< works in the tree view as well.

Cmd-< works in the tree view as well.

--- home/.atom/keymap.cson
+++ home/.atom/keymap.cson
@@ -17,7 +17,7 @@
 #   'ctrl-p': 'core:move-down'
 #

-'.workspace .editor:not(.mini)':
+'.workspace .editor:not(.mini), .workspace':
   'cmd-<': ''

 '.platform-darwin .editor':


Add obscure filenames used by Unix v10's CSTR docs

Example: https://git.io/JPS96


exports connect middleware (for configuration options) and the handler

--- heck.coffee
+++ heck.coffee
@@ -34,7 +34,7 @@
   "*": errorTemplate

 # Pass to `vfs-http-handler` or call directly.
-module.exports = (req, res, err, code) ->
+module.exports.handler = (req, res, err, code) ->
   # Can pass err as a `String`, make it a real `Error`.
   err = errs.create err unless util.isError err
   console.error err.stack || err
@@ -63,3 +63,9 @@
   res.error = new ErrorPage req, res, opts
   res.error status, message

+
+module.exports.connect = (opts) ->
+  (req, res, next) ->
+    req.errorHandlerOptions = opts
+    next()
+


Updated TwoTitle for android

Updated TwoTitle for android

--- Framework/Views/Controls/TwoLineTitle_Framework.coffee
+++ Framework/Views/Controls/TwoLineTitle_Framework.coffee
@@ -7,7 +7,7 @@

     @view = Ti.UI.createView {
       top: 0
-      width: '50%', height: Ti.UI.SIZE
+      width: '60%', height: Ti.UI.SIZE
     }

     @title = @createTitle()


http: Return status code 404

http: Return status code 404

--- http.coffee
+++ http.coffee
@@ -129,6 +129,7 @@
     bodyBytes = new Buffer 'Not Found', 'utf8'
     bodyLength = bodyBytes.length
     headerBytes = new Buffer @createHeader({
+      statusCode: 404
       contentLength: bodyLength
       req: req
       contentType: "text/plain; charset=utf-8"


jira: add the ability to reopen issues

jira: add the ability to reopen issues

--- plugins/jira/index.coffee
+++ plugins/jira/index.coffee
@@ -20,8 +20,60 @@
     [View full stacktrace|#{event.error.url}]
     """

-  @receiveEvent: (config, event, callback) ->
-    return if event?.trigger?.type == "reopened"
+  @baseUrl: (config) ->
+    url.resolve(config.host, "/rest/api/2")
+
+  @issuesUrl: (config) ->
+    @baseUrl(config) + "/issue"
+
+  @issueUrl: (config, issueId) ->
+    "#{@issuesUrl(config)}/#{issueId}"
+
+  @getTransitionUrl: (config, issueId) ->
+    @issueUrl(config, issueId) + "/transitions"
+
+  @postTransitionUrl: (config, issueId) ->
+    @issueUrl(config, issueId) + "/transitions?expand=transitions.fields"
+
+  @commentUrl: (config, issueId) ->
+    @issueUrl(config, issueId) + "/comment"
+
+  @jiraRequest: (req, config) ->
+    req
+      .timeout(4000)
+      .auth(config.username, config.password)
+      .set('Accept', 'application/json')
+
+  @ensureIssueOpen: (config, issueId, callback) ->
+    @jiraRequest(@request.get(@getTransitionUrl(config, issueId)), config)
+      .on "error", (err) ->
+        callback(err)
+      .end (res) =>
+        callback(res.error) if res.error
+
+        transition = res.body.transitions.filter((obj) -> obj.name == 'Reopen')[0]
+        return unless transition
+
+        payload = {
+          "transition": { "id": transition.id }
+        }
+
+        @jiraRequest(@request.post(@postTransitionUrl(config, issueId)), config)
+          .send(payload)
+          .on "error", (err) ->
+            callback(err)
+          .end (res) ->
+            callback(res.error) if res.error
+
+  @addCommentToIssue: (config, issueId, comment) ->
+    @jiraRequest(@request.post(@commentUrl(config, issueId)), config)
+      .send({body: comment})
+      .on "error", (err) ->
+        callback(err)
+      .end (res) ->
+        callback(res.error) if res.error
+
+  @openIssue: (config, event, callback) ->
     # Build the ticket payload
     payload =
       fields:
@@ -44,11 +96,7 @@
     config.host += "/" unless /\/$/.test(config.host)

     # Send the request
-    @request
-      .post(url.resolve(config.host, "rest/api/2/issue"))
-      .timeout(4000)
-      .auth(config.username, config.password)
-      .set('Accept', 'application/json')
+    @jiraRequest(@request.post(@issuesUrl(config)), config)
       .send(payload)
       .on "error", (err) ->
         callback(err)
@@ -60,4 +108,12 @@
           key: res.body.key
           url: url.resolve(config.host, "browse/#{res.body.key}")

+  @receiveEvent: (config, event, callback) ->
+    if event?.trigger?.type == "reopened"
+      if event?.error?.createdIssue?.id
+        @ensureIssueOpen(config, event.error.createdIssue.id, callback)
+        @addCommentToIssue(config, event.error.createdIssue.id, jiraBody(event))
+    else
+      @openIssue(config, event, callback)
+
 module.exports = Jira


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -21,7 +21,7 @@
       if not I[name]
         I[name] = 100

-      if not I[""]
+      if not I["max#{}"]

       ratio = I[name] / I["max#{name.capitalize()}"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -125,7 +125,7 @@

   @name meter
   @methodOf Metered#
-  @param {Number} xOffset the amount to shift the x position
+  @param {Object} ob the amount to shift the x position
   @param {Number} yOffset the amount to shift the y position
   ###
   meter: (options={}) ->


Make search "work" even when match includes "invisible" content

Matches where part of the match is invisible are unselectable, and
therefore simply ignored

--- app/js/jail_iframe/util/highlight.coffee
+++ app/js/jail_iframe/util/highlight.coffee
@@ -49,9 +49,11 @@
   while searchRange.findText(searchString, 1000000000, 4)
     #WTF: findText API is crazy http://msdn.microsoft.com/en-us/library/ms536422.aspx
     #second parameter must be "large" ????
-    searchRange.select()
-    results.push selection.getRangeAt(0)
-    break #TODO:infinite loop
+    try
+      searchRange.select() # select may fail if text is "unselectable" such as a script tag.
+      results.push selection.getRangeAt(0)
+    catch
+    searchRange.collapse(false) # move degenerate range aka cursor to end of match

   results


Add new timezone with pozitive offset.

Add new timezone with pozitive offset.

--- modules/date-localizer/test/spec.coffee
+++ modules/date-localizer/test/spec.coffee
@@ -7,8 +7,8 @@
   testDate = new Date(1452130200000) # Thu Jan 07 2016 01:30:00 GMT+0000 (GMT)
   zeroHourDate = new Date(1452124800000) # Thu Jan 07 2016 00:00:00 GMT+0000 (GMT)
   invalidDate = new Date('Invalid Date')
-  # 0 - London, -120 - Sofia, -60 Krakow, -540 Tokio
-  timezonesOfsets = [0, -120, -60, -540]
+  # 0 - London, -120 - Sofia, -60 Krakow, -540 Tokio, 420 Pinal (Arizona/United states)
+  timezonesOfsets = [0, -120, -60, -540, 420]

   # This is helper function. It accepts
   # function as a argument and execute it


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -26,6 +26,7 @@
         dropshadowx = 2                         # X-offset for drop shadow filter
         dropshadowy = 2                         # Y-offset for drop shadow filter
         dropshadowblur = "1.1"                  # [STRING] Blur value for drop shadow filter
+        totallabelfontsize = radiusi            # Font size for total count label

         pie = d3.layout.pie().value((d) -> d.value)
         arc = d3.svg.arc().innerRadius(radiusi).outerRadius(radiuso)
@@ -258,8 +259,9 @@
         #console.log totaltickets
         totalLabel.append('text')
             .text(totalTickets)
+            .attr('font-size','totallabelfontsize')
             .attr('text-anchor', 'middle')
-            .attr("filter","url(#dropshadow)")
+            .attr('filter','url(#dropshadow)')

         oldPieData = piedata
         return


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -192,7 +192,7 @@
         if !data
           return

-        piedata = data[0].label
+        piedata = data[0]
         console.log("update pie", piedata)

         $(@node).children(".title").text($(@node).attr("data-title"))


fix bug in chat with null messages

fix bug in chat with null messages

--- services/chat/app/coffee/Features/Messages/MessageManager.coffee
+++ services/chat/app/coffee/Features/Messages/MessageManager.coffee
@@ -34,6 +34,8 @@

 		for message in messages
 			do (message) ->
+				if !message?
+					return
 				jobs.push (callback) ->
 					getUserDetails message.user_id.toString(), (error, user) ->
 						return callback(error) if error?


much better order for shared static methods

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -26,9 +26,9 @@
       'list default vm' : ['member','moderator']
     sharedMethods       :
       static            : [
-                           'fetchVms','fetchVmsByContext'#,'calculateUsage'
-                           'removeByHostname', 'someData', 'fetchDomains'
-                           'fetchVMInfo', 'count'
+                           'fetchVms','fetchVmsByContext', 'fetchVMInfo'
+                           'fetchDomains', 'removeByHostname', 'someData'
+                           'count', #'calculateUsage'
                           ]
       instance          : []
     indexes             :


Copy bill address to shipping address when the checkout address page loads

--- app/assets/javascripts/multi_shipping_address/same_as_billing/copy_bill_to_ship.js.coffee
+++ app/assets/javascripts/multi_shipping_address/same_as_billing/copy_bill_to_ship.js.coffee
@@ -4,8 +4,10 @@
 class MultiShippingAddress.SameAsBilling.CopyBillToShip
   constructor: (@$form) ->
     @findShippingAddresses()
+    @bindCountrySelects()
+    @getBillAddress()
+    @copyBillAddress()
     @bindForm()
-    @bindCountrySelects()

   billAddress: null
   shippingAddresses: []
@@ -41,5 +43,5 @@
     addressInputs.filter("[name*=#{ name }]").val( $input.val() ).change()

   getBillAddress: ->
-    @billAddress =  @$form.find('#billing').find('input, select')
+    @billAddress =  @$form.find('#billing').find('input[type="text"], select')


properly clear the button class for button group

--- modules/button-group/main/index.coffee
+++ modules/button-group/main/index.coffee
@@ -26,16 +26,18 @@

     @view = group.view('button')
       .enter ->
-        @append('button').class('hx-btn ' + self.options.buttonClass)
+        @append('button')
+          .class('hx-btn')
           .classed('hx-section hx-no-margin', self.options.fullWidth)
+          .classed(self.options.buttonClass, true)
           .node()
       .update (item, node) ->
         buttonClass = if item.activeClass? and item is self.current then item.activeClass
         else if item is self.current then self.options.activeClass
         else self.options.buttonClass

-        @classed(self.options.buttonClass, false)
-          .classed(self.options.activeClass, false)
+        @class('hx-btn')
+          .classed('hx-section hx-no-margin', self.options.fullWidth)
           .classed(buttonClass, true)
           .attr('disabled', if self.options.disabled then true else undefined)
           .on 'click', 'hx.button-group', ->


improve install_on_device

improve install_on_device

--- uiautomation-runner.coffee
+++ uiautomation-runner.coffee
@@ -57,10 +57,13 @@
   {device_udid, bundle_id, build_dir, app_filename} = settings
   app_path = "#{build_dir}/#{app_filename}"
   return callback null if not device_udid
-  exec "fruitstrap uninstall --id '#{device_udid}' --bundle '#{bundle_id}'", (e, out, err) ->
-    exec "fruitstrap install --id '#{device_udid}' --bundle '#{app_path}'", (e, out, err) ->
-      return callback e if e
-      callback null
+  async.forEachSeries(
+    [
+      ['uninstall', '--id', device_udid, '--bundle', bundle_id]
+      ['install', '--id', device_udid, '--bundle', app_path]
+    ],
+    ((args, cb) -> spawn_with_output 'fruitstrap', args, {noisy:true}, cb),
+    callback)

 run_instruments = (settings, callback=(->)) ->


Create rule with capture as options

Create rule with capture as options

--- src/pattern.coffee
+++ src/pattern.coffee
@@ -28,7 +28,7 @@
       for group, capture of @captures
         if capture.patterns?.length > 0 and not capture.rule
           capture.scopeName = @scopeName
-          capture.rule = @grammar.createRule()
+          capture.rule = @grammar.createRule(capture)

     @anchored = @hasAnchor()


buildBufferSync can't be deprecated because TokenizedBuffer uses it

--- src/project.coffee
+++ src/project.coffee
@@ -165,9 +165,8 @@
   bufferForId: (id) ->
     _.find @buffers, (buffer) -> buffer.id is id

-  # DEPRECATED
+  # Still needed when deserializing a tokenized buffer
   buildBufferSync: (absoluteFilePath) ->
-    deprecate("Use Project::buildBuffer instead")
     buffer = new TextBuffer({filePath: absoluteFilePath})
     @addBuffer(buffer)
     buffer.loadSync()


Use 'enter' instead of 'return' for multi-item prompt

--- src/prompts.coffee
+++ src/prompts.coffee
@@ -147,11 +147,11 @@
 	done = no
 	whilst (-> not done),
 		->
-			exports.keys null, ['escape', 'return', (_.keys mapDisplayed)...]
+			exports.keys null, ['escape', 'enter', (_.keys mapDisplayed)...]

 			.then (key) ->
 				switch key
-					when 'return' then done = yes
+					when 'enter' then done = yes
 					when 'escape' then done = 'cancel'

 					else


Include tag instead of version read from package.json

--- src/publish.coffee
+++ src/publish.coffee
@@ -172,7 +172,7 @@
       return

     publishNewVersion = =>
-      process.stdout.write "Publishing #{pack.name}@#{pack.version} "
+      process.stdout.write "Publishing #{pack.name}@#{tag} "
       @createPackageVersion pack.name, tag, (error) ->
         if error?
           process.stdout.write '\u2717\n'.red


Don't write newline until completion

Don't write newline until completion

--- src/publish.coffee
+++ src/publish.coffee
@@ -258,7 +258,7 @@
   renamePackage: (pack, name, callback) ->
     if name?.length > 0 and pack.name isnt name
       message = "Renaming #{pack.name} to #{name}"
-      process.stdout.write("#{message}\n")
+      process.stdout.write("#{message}")
       @setPackageName pack, name, (error) =>
         if error?
           @logFailure()


Add XDomainRequest support for IE CORS Requests

--- src/request.coffee
+++ src/request.coffee
@@ -25,7 +25,11 @@
     @response = null

     @_emitter = new Emitter
-    @xhr = new XMLHttpRequest
+
+    if !options.cors? || (options.cors && "withCredentials" in new XMLHttpRequest())
+      @xhr = new XMLHttpRequest
+    else
+      @xhr = new XDomainRequest

     events = ["before", "success", "error", "complete", "progress"]
     for event in events