Fix bugs in optimization of cursor line highlighting in the gutter

--- src/app/gutter.coffee
+++ src/app/gutter.coffee
@@ -49,6 +49,7 @@
         lastScreenRow = row

     @calculateWidth()
+    @highlightedRow = null
     @highlightCursorLine()

   calculateWidth: ->
@@ -59,16 +60,16 @@
       @widthChanged?(@outerWidth())

   highlightCursorLine: ->
-    currentRow = @editor().getCursorScreenPosition().row
-    return if @highlightedRow == currentRow
+    if @editor().getSelection().isEmpty()
+      rowToHighlight = @editor().getCursorScreenPosition().row
+      return if rowToHighlight == @highlightedRow
+      return if rowToHighlight < @firstScreenRow or rowToHighlight > @lastScreenRow

-    screenRowIndex = currentRow - @firstScreenRow
-    @highlightedLineNumber?.classList.remove('cursor-line')
-
-    if screenRowIndex >= 0 and @editor().getSelection().isSingleScreenLine()
-      @highlightedLineNumber = @lineNumbers[0].children[screenRowIndex]
-      @highlightedLineNumber?.classList.add('cursor-line')
-      @highlightedRow = currentRow
+      @highlightedLineNumber?.classList.remove('cursor-line')
+      if @highlightedLineNumber = @lineNumbers[0].children[rowToHighlight - @firstScreenRow]
+        @highlightedLineNumber.classList.add('cursor-line')
+        @highlightedRow = rowToHighlight
     else
+      @highlightedLineNumber?.classList.remove('cursor-line')
       @highlightedLineNumber = null
       @highlightedRow = null


Remove unneeded empty array fallback

Remove unneeded empty array fallback

--- src/app/keymap.coffee
+++ src/app/keymap.coffee
@@ -38,7 +38,7 @@
     @loadDirectory(fs.join(config.configDirPath, 'keymaps'))

   loadDirectory: (directoryPath) ->
-    @load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json']) ? []
+    @load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json'])

   load: (path) ->
     @add(path, CSON.readObject(path))


validation fix

validation fix

--- src/base_model.coffee
+++ src/base_model.coffee
@@ -198,13 +198,13 @@
   # @note Method generate add_x, where `x` it's a attribute from `@has_many` or `@has_one`
   constructor: (obj = {}) ->
     @logger = Sirius.Application.get_logger()
-    @_is_valid = false
     @callbacks = []
     # object, which contain all errors, which registers after validation
     @errors = {}
     @attributes = @normalize_attrs()
     name = Sirius.Utils.fn_name(@constructor)
     @errors = {}
+    @_is_valid_attr = {} # save pair attribute and validation state

     for attr in @attrs()
       # @attrs: [{key: value}]
@@ -249,6 +249,7 @@
     @_model_validators = @validators()
     for key, value of @_model_validators
       @errors[key] = {}
+      @_is_valid_attr[key] = false
       for validator_name, validator of value
         if @_registered_validators_keys.indexOf(validator_name) == -1 && validator_name != 'validate_with'
           throw new Error("Unregistered validator: #{validator_name}")
@@ -355,7 +356,7 @@
   # Check, if model instance valid
   # @return [Boolean] true, when is valid, otherwise false
   is_valid: () ->
-    @_is_valid
+    Object.keys(@_is_valid_attr).filter((key) => !@_is_valid_attr[key]).length == 0

   # Call when you want validate model
   # @nodoc
@@ -385,14 +386,15 @@

         if !result # when `validate` return false
           @errors[key][validator_key] = klass.error_message()
+          @_is_valid_attr[key] = false
         else #when true, then need set null for error
           @errors[key][validator_key] = ""
+          @_is_valid_attr[key] = true

     )

-    for key, value of @errors
-      for k, v of value when v != ""
-        @_is_valid = true
+
+    return

   # @param [String] - attr name, if attr not given then return `errors` object,
   # otherwise return array with errors for give field
@@ -419,8 +421,8 @@
   save: (exception = false) ->
     @validate()
     name = @constructor.name
-    throw new Error("#{name} model not valid!") if exception && !@_is_valid
-    return false if @_is_valid
+    throw new Error("#{name} model not valid!") if exception && !@is_valid()
+    return false if @is_valid()
     true

   #


RC: Fix authworker username

RC: Fix authworker username

--- config/main.rc.coffee
+++ config/main.rc.coffee
@@ -59,7 +59,7 @@
     HomePrefix:   "/Users/"
     UseLVE:       true
   authWorker    :
-    login       : 'prod-authworker'
+    login       : 'prod-auth-worker'
     queueName   : socialQueueName+'auth'
     authResourceName: 'auth'
     numberOfWorkers: 2


chrheatmap: more detail in displayError calls

--- src/chrheatmap.coffee
+++ src/chrheatmap.coffee
@@ -33,14 +33,14 @@
             nchr = data.nmar.length
             totmar = sumArray(data.nmar)
             if totmar != ny
-                displayError("sum(data.nmar) != data.z.length")
+                displayError("sum(data.nmar) [#{sumArray(data.nmar)}] != data.z.length [#{data.z.length}]")
             if data.chrnames.length != nchr
-                displayError.log("data.nmar.length != data.chrnames.length")
+                displayError.log("data.nmar.length [#{data.nmar.length}] != data.chrnames.length [#{data.chrnames.length}]")
             if data.labels.length != totmar
-                displayError("data.labels.length != sum(data.nmar)")
-            if chrGap < 2
+                displayError("data.labels.length [#{data.labels.length}] != sum(data.nmar) [#{sum(data.nmar)}]")
+            if chrGap < 1
+                displayError("chrGap should be >= 2 (was #{chrGap})")
                 chrGap = 2
-                displayError("chrGap should be >= 1")

             # determine start of each cell (leave 1 pixel border around each chromosome)
             xChrBorder = [0]


Simplify cli-parser.

Simplify cli-parser.

--- src/cli-parser.coffee
+++ src/cli-parser.coffee
@@ -30,6 +30,6 @@
       return hasArg = true
   args.push defaultAction unless hasArg

-exports.parse = (args) ->
-  addDefaultArg args ? process.argv
-  if args? then parser.parseArgs args else parser.parseArgs()
+exports.parse = (args=process.argv.slice(2)) ->
+  addDefaultArg args
+  parser.parseArgs args


Add World class

Add World class

--- src/coffee/app.coffee
+++ src/coffee/app.coffee
@@ -1,3 +1,18 @@
+class World extends PIXI.DisplayObjectContainer
+  constructor: (w, h) ->
+    super
+
+    @mushroom = new PIXI.Sprite(PIXI.Texture.fromImage("/img/mushroom.png"))
+    @mushroom.anchor.x = 0.5
+    @mushroom.anchor.y = 0.5
+    @mushroom.position.x = w / 2
+    @mushroom.position.y = h / 2
+
+    @addChild(@mushroom)
+
+  update: ->
+    @mushroom.rotation -= 0.1
+
 $ ->
   width = 640
   height = 480
@@ -7,18 +22,13 @@

   $('#world-view').append(renderer.view)

-  mushroom = new PIXI.Sprite(PIXI.Texture.fromImage("/img/mushroom.png"))
-  mushroom.anchor.x = 0.5
-  mushroom.anchor.y = 0.5
-  mushroom.position.x = width / 2
-  mushroom.position.y = height / 2
+  world = new World(width, height)
+  stage.addChild(world)

-  stage.addChild(mushroom)
-
-  animate = ->
+  animate = =>
     requestAnimationFrame(animate)

-    mushroom.rotation += 0.1
+    world.update()

     renderer.render(stage)


Allow to define hours to fetch orders in.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -6,10 +6,12 @@
 argv = require('optimist')
   .usage('Usage: $0 --projectKey key --clientId id --clientSecret secret')
   .default('timeout', 60000)
+  .default('fetchHours', 0) # by default don't restrict on modifications
   .describe('projectKey', 'your SPHERE.IO project-key')
   .describe('clientId', 'your OAuth client id for the SPHERE.IO API')
   .describe('clientSecret', 'your OAuth client secret for the SPHERE.IO API')
   .describe('timeout', 'Set timeout for requests')
+  .describe('fetchHours', 'Number of hours to fetch modified orders')
   .demand(['projectKey', 'clientId', 'clientSecret'])
   .argv

@@ -25,7 +27,7 @@
 options.sphere_client = sphere
 mapping = new Mapping options

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


fixed typo in argument check

fixed typo in argument check

--- src/command/sign.iced
+++ src/command/sign.iced
@@ -40,7 +40,7 @@
   #----------

   set_argv : (a) ->
-    if a.clearsign and a.deatch_sign
+    if a.clearsign and a.detach_sign
       new E.ArgsError "Can only handle one of --clearsign and --detach-sign"
     else
       super a


add option for setting connection isolation level

--- src/connection.coffee
+++ src/connection.coffee
@@ -214,6 +214,7 @@
     @config.options.cryptoCredentialsDetails ||= {}
     @config.options.useUTC ?= true
     @config.options.useColumnNames ?= false
+    @config.options.connectionIsolationLevel ||= ISOLATION_LEVEL.READ_COMMITTED

     if !@config.options.port && !@config.options.instanceName
       @config.options.port = DEFAULT_PORT
@@ -539,7 +540,7 @@
 set language us_english
 set dateformat mdy
 set datefirst 7
-set transaction isolation level read committed'''
+set transaction isolation level #{@getIsolationLevelText @config.options.connectionIsolationLevel}'''

   processedInitialSql: ->
       @clearConnectTimer()
@@ -656,4 +657,12 @@
   currentTransactionDescriptor: ->
     @transactionDescriptors[@transactionDescriptors.length - 1]

+  getIsolationLevelText: (isolationLevel) ->
+    switch isolationLevel
+      when ISOLATION_LEVEL.READ_UNCOMMITTED then 'read uncommitted'
+      when ISOLATION_LEVEL.REPEATABLE_READ then 'repeatable read'
+      when ISOLATION_LEVEL.SERIALIZABLE then 'serializable'
+      else 'read committed'
+
+
 module.exports = Connection


Fixed: missing 'new' statement in doc.

Fixed: missing 'new' statement in doc.

--- src/controller.coffee
+++ src/controller.coffee
@@ -15,7 +15,7 @@
     #
     # Examples
     #
-    #  Note = cozydb.getModel 'Note', SCHEMA
+    #  Note = new cozydb.getModel 'Note', SCHEMA
     #  baseController = cozydb.SimpleController
     #       model: Note
     #       reqProp: 'note'


Fix scrolling for floating blocks

Fix scrolling for floating blocks

--- src/controller.coffee
+++ src/controller.coffee
@@ -306,11 +306,12 @@
         # Draw it on the main context
         @tree.view.draw @mainCtx

-        # Update the main scroller dimensions
-        # to fit the document dimensions
+        # We want to get the dimensinos of the entire document;
+        # this includes both the root tree and any floating blocks.
+        #
+        # We will initialize this rectangle as the dimensions of the root tree,
+        # and then unite it with all the floating block boundaries.
         bounds = @tree.view.getBounds()
-        mainScrollerStuffing.style.height = bounds.bottom()
-        mainScrollerStuffing.style.width = bounds.right()

         # Alert the lasso segment, if it exists, to recompute its bounds
         if @lassoSegment?
@@ -335,6 +336,15 @@

           # Draw it on the main context
           view.draw @mainCtx
+
+          # Unite this boundary
+          # with the document boundary
+          bounds.unite view.getBounds()
+
+        # Update the scroller stuffing dimensions
+        # to fit the document dimensions
+        mainScrollerStuffing.style.height = bounds.bottom()
+        mainScrollerStuffing.style.width = bounds.right()

       # ## attemptReparse ##
       # This will be triggered by most cursor operations. It finds all handwritten blocks that do not contain the cursor,


Jview: pistachio params added to jview

Jview: pistachio params added to jview

--- src/core/jview.coffee
+++ src/core/jview.coffee
@@ -4,10 +4,12 @@

   viewAppended:->
     template = @getOptions().pistachio or @pistachio
+    params   = @getOptions().pistachioParams or @pistachioParams
     template = template.call this  if 'function' is typeof template
+    params   = params.call this    if 'function' is typeof params

     if template?
-      @setTemplate template
+      @setTemplate template, params
       @template.update()


Add root prefix to HTTPromiseFormat.

Add root prefix to HTTPromiseFormat.

--- src/httpromise.coffee
+++ src/httpromise.coffee
@@ -45,7 +45,7 @@

 class Request
   constructor: (config,method,url,data) ->
-    format = HTTPromiseFormat[config.type]
+    format = root.HTTPromiseFormat[config.type]
     if config.headers? and typeof config.headers isnt "object"
       throw new TypeError("If given, headers must an object where each key value pair represents a request header")


Include XML error response message in exception.

--- src/lib/amazon.coffee
+++ src/lib/amazon.coffee
@@ -26,7 +26,6 @@
     when 3, 4
       { endpoint, key, secret } = options
       invoke endpoint, key, secret, name, parameters, (error, response, body) ->
-        console.log body
         if error
           callback error
         else
@@ -35,6 +34,15 @@
             # We cannot reuse the ResponseParser, since node-xml does not reset
             # itself and offers tno reset function.
             (new ResponseParser).read(body, callback)
+          else if body
+            (new ResponseParser).read body, (error, object) ->
+              if (error)
+                callback new Error(http.STATUS_CODES[response.statusCode])
+              else
+                error = new Error(object.Errors[0].Message)
+                error.code = object.Errors[0].Code
+                error.status = statusCode
+                callback error
           else
             callback new Error(http.STATUS_CODES[response.statusCode])


Minor PlayState#shutdown refactoring.

Minor PlayState#shutdown refactoring.

--- src/state-play.coffee
+++ src/state-play.coffee
@@ -96,10 +96,7 @@

     shutdown: ->
       # Null references to disposable objects we don't own.
-      @background.destroy()
-      @platforms.destroy()
-      @player.destroy()
-      @inStateMenu.destroy()
+      gameObject.destroy() for gameObject in [@background, @inStateMenu, @music, @platforms, @player]

       @game.onBlur.remove @onBlur, @
       @game.onFocus.remove @onFocus, @


Revert "subprocess: Default timeout higher"

This reverts commit 14697b1e9b052e034339b34e47bd561ae4c5de38.

--- src/subprocess.coffee
+++ src/subprocess.coffee
@@ -9,7 +9,7 @@
 debug = require('debug')('fbp-spec:subprocess')

 exports.start = (command, options, callback) ->
-  options.timeout = 4000 if not options.timeout?
+  options.timeout = 2000 if not options.timeout?

   started = false
   stderr = ""


Environments: removed change state button on contextmenu

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -20,11 +20,11 @@
     colorSelection.on "ColorChanged", @bound 'setColorTag'

     vmName = @getData().vm.hostnameAlias
-    vmStateSwitch    = new NVMToggleButtonView        null, {vmName}
+    # vmStateSwitch    = new NVMToggleButtonView        null, {vmName}
     vmAlwaysOnSwitch = new VMAlwaysOnToggleButtonView null, {vmName}
     # vmMountSwitch = new NMountToggleButtonView {}, {vmName}
     items =
-      customView1        : vmStateSwitch
+      # customView1        : vmStateSwitch
       customView4        : vmAlwaysOnSwitch
       # customView2        : vmMountSwitch
       'Re-initialize VM' :


remove unused constants

remove unused constants

--- static/coffee/completions.coffee
+++ static/coffee/completions.coffee
@@ -2,8 +2,6 @@

   KEY_LEFT = 37
   KEY_RIGHT = 39
-  KEY_CTRL = 17
-  KEY_META = 91

   constructor: (@variables=[], @functions=[]) ->


KodingLogger.test use setImmediate instead

--- workers/social/lib/social/models/kodinglogger.coffee
+++ workers/social/lib/social/models/kodinglogger.coffee
@@ -123,7 +123,7 @@
       .searchEvents withQuery

       .then (data) ->
-        setTimeout -> callback null, processData data, limit
+        setImmediate -> callback null, processData data, limit

       .catch (err) ->
         callback


that lodash

that lodash

--- src/models/channel-encryption.coffee
+++ src/models/channel-encryption.coffee
@@ -1,4 +1,5 @@
-NodeRSA           = require 'node-rsa'
+NodeRSA = require 'node-rsa'
+_       = require 'lodash'

 class ChannelEncryption
   constructor: ({privateKey}) ->


Fix sender address when sending mails from user, refs #168

--- server/controllers/mails.coffee
+++ server/controllers/mails.coffee
@@ -138,7 +138,7 @@
                 if users?[0]?.value.public_name? and
                         users?[0]?.value.public_name isnt ''
                     publicName = users[0].value.public_name
-                    displayName = publicName.toLowerCase().replace ' ', '-'
+                    displayName = publicName.toLowerCase().replace /\W/g, '-'
                     displayName += "-"
                     userEmail = users[0].value.email
                 else


Remove cache directory before downloading

This ensures any previously failed downloads/extractions are cleaned
up before a new atom-shell distribution is downloaded and extracted.

--- tasks/update-atom-shell-task.coffee
+++ tasks/update-atom-shell-task.coffee
@@ -26,10 +26,12 @@
     inputStream.on 'response', (response) ->
       if response.statusCode is 200
         grunt.log.writeln("Downloading atom-shell version #{version.cyan}")
-        cachePath = "#{getCachePath(version)}/atom-shell.zip"
-        mkdir(path.dirname(cachePath))
-        outputStream = fs.createWriteStream(cachePath)
-        outputStream.on 'close', -> callback(null, cachePath)
+        cacheDirectory = getCachePath(version)
+        rm(cacheDirectory)
+        mkdir(cacheDirectory)
+        cacheFile = path.join(cacheDirectory, 'atom-shell.zip')
+        outputStream = fs.createWriteStream(cacheFile)
+        outputStream.on 'close', -> callback(null, cacheFile)
         inputStream.pipe(outputStream)
       else
         if response.statusCode is 404
@@ -45,7 +47,7 @@
     try
       zip = new AdmZip(zipPath);
       zip.extractAllTo(directoryPath, true)
-      rm(zipPath, force: true)
+      rm(zipPath)
       callback()
     catch error
       callback(error)


modify

modify

--- test/test-npm-module.spec.coffee
+++ test/test-npm-module.spec.coffee
@@ -10,5 +10,5 @@

 describe 'test-npm-module', ->
   it 'works', ->
-    actual = testNpmModule 'World'
-    expect(actual).to.eql 'Hello World'
+    actual = testNpmModule 'World1'
+    expect(actual).to.eql 'Hello World1'


Fixing unit tests after cellTemplate rename rebase

--- test/unit/table_view.spec.coffee
+++ test/unit/table_view.spec.coffee
@@ -353,17 +353,17 @@
               >

-                  Header Cell
+                  Header Cell

-                  {{cell.value()}}
+                  {{cell.value()}}

-                  Footer Cell
+                  Footer Cell

"""
@@ -372,9 +372,9 @@
           scope.$apply()

         it "Should add the resizable directives", ->
-          expect(element.find("[table-section=header] [cell-template] .cell-wrapper[mac-resizable]").length).toBe models.length
-          expect(element.find("[table-section=header] [cell-template] .cell-wrapper[mac-resizable-column]").length).toBe models.length
-          expect(element.find("[table-section=header] [cell-template] .cell-wrapper[mac-resizable-containment]").length).toBe models.length
+          expect(element.find("[table-section=header] [mac-cell-template] .cell-wrapper[mac-resizable]").length).toBe models.length
+          expect(element.find("[table-section=header] [mac-cell-template] .cell-wrapper[mac-resizable-column]").length).toBe models.length
+          expect(element.find("[table-section=header] [mac-cell-template] .cell-wrapper[mac-resizable-containment]").length).toBe models.length

         it "Should add the reorderable directives", ->
           expect(element.find("[table-section=header] [table-row][mac-reorderable]").length).toBe 1


Added test case for parsing XML lists when the list member name is not provided.

--- test/unit/xml_parser.spec.coffee
+++ test/unit/xml_parser.spec.coffee
@@ -40,6 +40,19 @@
       xml = """
       <xml>
         <items>
+          <member>abc</member>
+          <member>xyz</member>
+        </items>
+      </xml>
+      """
+      rules = {items:{t:'a',m:{}}}
+      parse xml, rules, (data) ->
+        expect(data).toEqual({items:['abc','xyz']})
+
+    it 'Observes list member names when present', ->
+      xml = """
+      <xml>
+        <items>
           <item>abc</item>
           <item>xyz</item>
         </items>


refactor

refactor

--- src/services/sentence-service.coffee
+++ src/services/sentence-service.coffee
@@ -14,8 +14,8 @@
   addSentence: (text, translatedText) ->
     sentence = new Sentence(text, translatedText)
     @sentences.push sentence
-    service = EventService.getInstance()
-    service.emit 'sentence-service:changed', @sentences
+    event = EventService.getInstance()
+    event.emit 'sentence-service:changed', @sentences

   getSentences: ->
     @sentences


Renamed properties

Renamed properties

--- client/admin/lib/views/invitations/invitesomeoneview.coffee
+++ client/admin/lib/views/invitations/invitesomeoneview.coffee
@@ -98,8 +98,8 @@
       admins.push invite.email  if invite.role is 'admin'

     Tracker.track Tracker.INVITED_TEAMMEMBERS, {
-      invites_count : invites.length
-      admins_count  : admins.length
+      invitesCount : invites.length
+      adminsCount  : admins.length
     }

     if admins.length


TeamMembersCommonView: Show/hide no item view.

--- client/admin/lib/views/members/teammemberscommonview.coffee
+++ client/admin/lib/views/members/teammemberscommonview.coffee
@@ -96,7 +96,8 @@
   listMembers: (members) ->

     unless members.length
-      return @listController.lazyLoader.hide()
+      @listController.lazyLoader.hide()
+      return @listController.noItemView.show()

     @skip += members.length

@@ -112,6 +113,7 @@
     @skip  = 0
     @query = @searchInput.getValue()

+    @listController.noItemView.hide()
     @listController.removeAllItems()
     @listController.lazyLoader.show()
     @fetchMembers()


Improved naming.

Improved naming.

--- app/assets/javascripts/app/controllers/ticket_overview.js.coffee
+++ app/assets/javascripts/app/controllers/ticket_overview.js.coffee
@@ -548,7 +548,7 @@
         },
         {
           value:  'escalation_time'
-          name:   'Escalation in'
+          name:   'Escalation'
         },
         {
           value:  'article_count'
@@ -579,7 +579,7 @@
         last_contact_customer:  'Last Contact Customer'
         first_response:         'First Response'
         close_time:             'Close Time'
-        escalation_time:        'Escalation in'
+        escalation_time:        'Escalation'
         article_count:          'Article Count'
       class:   'medium'
     },


fixed trackball controls

fixed trackball controls

--- app/assets/javascripts/oxalis/controller/plane_controller.coffee
+++ app/assets/javascripts/oxalis/controller/plane_controller.coffee
@@ -90,6 +90,7 @@
       event.preventDefault()
       return

+    @initTrackballControls()
     @bind()
     @start()

@@ -120,6 +121,9 @@
           @view.curWidth)
       leftClick : @onPreviewClick
     ) )
+
+
+  initTrackballControls : ->

     view = $("#skeletonview")[0]
     pos = @model.scaleInfo.voxelToNm(@flycam.getPosition())


fix group category selector for string ids

fixes CNVS-10699

Test plan:
  try changing the group on an assignment, make sure the proper group
  is still selected when you go back to edit

  if possible this needs to be done before and after
  Ifa654f7d853fd167d5bfbaee6184657209d58272

Change-Id: Ic79447d6559558a4de2d296e2fe3f0fc0a8c7095
Reviewed-on: https://gerrit.instructure.com/29288
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Amber Taniuchi <ef4bfd1e6d278f6bc7d4a478f02be2cb9581a0a5@instructure.com>
Product-Review: Bryan Madsen <0a3ba1af216659500dd8f41e29f3979f42452870@instructure.com>
Tested-by: Bryan Madsen <0a3ba1af216659500dd8f41e29f3979f42452870@instructure.com>

--- app/coffeescripts/views/assignments/GroupCategorySelector.coffee
+++ app/coffeescripts/views/assignments/GroupCategorySelector.coffee
@@ -34,6 +34,10 @@

     initialize: ->
       super
+
+      # delete this after Ifa654f7d853fd167d5bfbaee6184657209d58272 hits prod
+      gc.id = gc.id.toString() for gc in @groupCategories
+
       @startedOutAsGroupAssignment = @parentModel.get('group_category_id')?

     showGroupCategoryCreateDialog: =>


fixed disabled buttons causing error when clicked

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -109,18 +109,12 @@
         ed = @editor
         if $.inArray('switchUp', @editorConfig.buttons) != -1
             jQuery('.ace_uparrow').click ed.button ed.usesCurrentPosition ed.switchUp
-        else
-            jQuery('.ace_uparrow').click ed.editor.focus

         if $.inArray('switchDown', @editorConfig.buttons) != -1
             jQuery('.ace_downarrow').click ed.button ed.usesCurrentPosition ed.switchDown
-        else
-            jQuery('.ace_downarrow').click ed.editor.focus

         if $.inArray('deleteLine', @editorConfig.buttons) != -1
             jQuery('.ace_xbutton').click ed.button ed.usesTextDocument ed.usesCurrentRow ed.deleteLine
-        else
-            jQuery('.ace_xbutton').click ed.editor.focus

         ed.onChangeListener @onStudentCodeChange
         ed.onClickListener @onEditorClick


code given to PlayerCodeEditor constructor is now wrapped in class 'StudentCode' and public static void main (String[] args) method

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -19,6 +19,13 @@
             Sets internal variables, the default text and buttons
             and their event handlers.
         ###
+        codePrefix = "class StudentCode {\n    public static void main(String[] args) {\n"
+        codeSuffix = "\n    }\n}"
+        unless @codeText.startsWith codePrefix
+            @codeText = codePrefix + @codeText
+        unless @codeText.endsWith codeSuffix
+            @codeText = @codeText + codeSuffix
+
         @editor = ace.edit @editorDivId
         @editSession = @editor.getSession()
         @editSession.setMode "ace/mode/java"


fixed linter

fixed linter

--- test/play_stream_test.coffee
+++ test/play_stream_test.coffee
@@ -47,13 +47,13 @@
         console.log('checking type', playerDiv.type)
         playerExists = playerDiv.type == 'application/x-shockwave-flash'
         setTimeout callback
-      async.until testFunction, checkFunction, (err)=>
+      async.until testFunction, checkFunction, (err)->
         console.log("GOT ERROR", err)
         done(err)

     afterEach ->
       afterJwPlayer = document.getElementById("#{@playerDivID}_wrapper")
-      afterJwPlayer.parentNode.removeChild(afterJwPlayer);
+      afterJwPlayer.parentNode.removeChild(afterJwPlayer)

     it 'plays a live file', (done)->
       domId = @playerDivID
@@ -83,13 +83,13 @@
       checkFunction = (callback)->
         playerExists = window.document.getElementById('player-id_media')?
         setTimeout callback
-      async.until testFunction, checkFunction, (err)=>
+      async.until testFunction, checkFunction, (err)->
         console.log("GOT ERROR", err)
         done(err)

     afterEach ->
       afterJwPlayer = document.getElementsByClassName('jwplayer')[0]
-      afterJwPlayer.parentNode.removeChild(afterJwPlayer);
+      afterJwPlayer.parentNode.removeChild(afterJwPlayer)

     it 'plays a recorded file', (done)->
       domId = @playerDivID


add collapse id and tag to apn and gcm for replacing previous message

--- config/notify.coffee
+++ config/notify.coffee
@@ -12,14 +12,17 @@
 module.exports =
   notify:
     gcm: (device, data) ->
-      msg = new gcm.Message()
-      msg.addNotification
-        title: data.title
-        body: data.message || ' '
-        sound: 'default'
+      msg = new gcm.Message
+        collapseKey: data.title
+        notification:
+          tag: data.title
+          title: data.title
+          body: data.message || ' '
+          sound: 'default'
       gcmProvider.sendAsync msg, registrationTokens: [device]
     apn: (device, data) ->
       msg = new apn.Notification
+        collapseId: data.title
         title: data.title
         body: data.message
         sound: 'default'


remove all.js from karma

remove all.js from karma

--- karma.conf.coffee
+++ karma.conf.coffee
@@ -19,7 +19,6 @@
     # list of files / patterns to load in the browser
     files: [
       'bower_components/jquery/dist/jquery.js',
-      '.tmp/all.js',
       '.tmp/specs.js'
     ]


Pass actual / expected assertion arguments in the correct order

--- test/src/system/pasting_test.coffee
+++ test/src/system/pasting_test.coffee
@@ -23,14 +23,14 @@
     clickToolbarButton attribute: "quote", ->
       pasteContent "text/html", "
Hello world

This is a test
", ->
         document = getDocument()
-        equal 2, document.getBlockCount()
+        equal document.getBlockCount(), 2

         block = document.getBlockAtIndex(0)
-        deepEqual ["quote"], block.getAttributes()
+        deepEqual block.getAttributes(), ["quote"],
         equal block.toString(), "abcHello world\n"

         block = document.getBlockAtIndex(1)
-        deepEqual ["quote", "code"], block.getAttributes()
+        deepEqual block.getAttributes(), ["quote", "code"]
         equal block.toString(), "This is a test\n"

         done()
@@ -40,18 +40,18 @@
     typeCharacters "abc\n", ->
       pasteContent "text/html", "

one

two

", ->
         document = getDocument()
-        equal 3, document.getBlockCount()
+        equal document.getBlockCount(), 3

         block = document.getBlockAtIndex(0)
-        deepEqual ["bulletList", "bullet"], block.getAttributes()
+        deepEqual block.getAttributes(), ["bulletList", "bullet"]
         equal block.toString(), "abc\n"

         block = document.getBlockAtIndex(1)
-        deepEqual ["bulletList", "bullet"], block.getAttributes()
+        deepEqual block.getAttributes(), ["bulletList", "bullet"]
         equal block.toString(), "one\n"

         block = document.getBlockAtIndex(2)
-        deepEqual ["bulletList", "bullet"], block.getAttributes()
+        deepEqual block.getAttributes(), ["bulletList", "bullet"]
         equal block.toString(), "two\n"

         done()
@@ -83,18 +83,18 @@
       typeCharacters "abc\n", ->
         pasteContent "text/html", "

one

two

", ->
           document = getDocument()
-          equal 3, document.getBlockCount()
+          equal document.getBlockCount(), 3

           block = document.getBlockAtIndex(0)
-          deepEqual ["quote", "bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["quote", "bulletList", "bullet"]
           equal block.toString(), "abc\n"

           block = document.getBlockAtIndex(1)
-          deepEqual ["quote", "bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["quote", "bulletList", "bullet"]
           equal block.toString(), "one\n"

           block = document.getBlockAtIndex(2)
-          deepEqual ["quote", "bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["quote", "bulletList", "bullet"]
           equal block.toString(), "two\n"

           done()
@@ -107,18 +107,18 @@
         pressKey "backspace", ->
           pasteContent "text/html", "

a

b

", ->
           document = getDocument()
-          equal 3, document.getBlockCount()
+          equal document.getBlockCount(), 3

           block = document.getBlockAtIndex(0)
-          deepEqual ["bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet"]
           equal block.toString(), "a\n"

           block = document.getBlockAtIndex(1)
-          deepEqual ["bulletList", "bullet", "bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet", "bulletList", "bullet"]
           equal block.toString(), "b\n"

           block = document.getBlockAtIndex(2)
-          deepEqual ["bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet"]
           equal block.toString(), "zz\n"
           done()

@@ -130,18 +130,18 @@
         expandSelection "left", ->
           pasteContent "text/html", "

a

b

", ->
           document = getDocument()
-          equal 3, document.getBlockCount()
+          equal document.getBlockCount(), 3

           block = document.getBlockAtIndex(0)
-          deepEqual ["bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet"]
           equal block.toString(), "a\n"

           block = document.getBlockAtIndex(1)
-          deepEqual ["bulletList", "bullet", "bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet", "bulletList", "bullet"]
           equal block.toString(), "b\n"

           block = document.getBlockAtIndex(2)
-          deepEqual ["bulletList", "bullet"], block.getAttributes()
+          deepEqual block.getAttributes(), ["bulletList", "bullet"]
           equal block.toString(), "zz\n"
           done()

@@ -157,15 +157,15 @@
               equal document.getBlockCount(), 3

               block = document.getBlockAtIndex(0)
-              deepEqual ["bulletList", "bullet"], block.getAttributes()
+              deepEqual block.getAttributes(), ["bulletList", "bullet"]
               equal block.toString(), "a\n"

               block = document.getBlockAtIndex(1)
-              deepEqual ["bulletList", "bullet"], block.getAttributes()
+              deepEqual block.getAttributes(), ["bulletList", "bullet"]
               equal block.toString(), "b\n"

               block = document.getBlockAtIndex(2)
-              deepEqual ["bulletList", "bullet"], block.getAttributes()
+              deepEqual block.getAttributes(), ["bulletList", "bullet"]
               equal block.toString(), "c\n"
               done()


Fixing double callback issue on catalog save.

The catalog save callback was called twice, once after File.rename finished
and a line after that. This causes issues when under 'record' mode.

--- src/replay/catalog.coffee
+++ src/replay/catalog.coffee
@@ -99,7 +99,6 @@
           file.write part
         file.end ->
           File.rename tmpfile, filename, callback
-          callback null
       catch error
         callback error


ensure l20n loads after DOM is loaded (#1548)

* ensure l20n loads after DOM is loaded

--- src/scripts/loader.coffee
+++ src/scripts/loader.coffee
@@ -8,6 +8,7 @@
   session = require('cs!session')
   analytics = require('cs!helpers/handlers/analytics')
   router = require('cs!router')
+  l20n = require('l20n')
   require('cs!helpers/backbone/history') # Extend Backbone.history to support query strings

   RegExp.escape = (str) -> str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
@@ -99,4 +100,10 @@

       return

+    # Sometimes l10n loads before the DOM is loaded.
+    # This ensures that l10n processing happens once the DOM is loaded
+    $(document).ready ->
+      document.l10n.connectRoot(document.documentElement)
+      document.l10n.translateRoots()
+
   return {init: init}


Generate worker supervisor program section based on environment configuration

--- deployment/supervisord.coffee
+++ deployment/supervisord.coffee
@@ -56,16 +56,20 @@
   """

 # becareful while editing this function, any change will affect every worker
-generateSupervisorSectionForWorker = (app, options={})->
+generateWorkerSection = (app, options={}, KONFIG) ->
+
+  {projectRoot} = KONFIG
+  {supervisord: {logdir}} = KONFIG
+
   section =
     command                 : "command"
     stdout_logfile_maxbytes : "10MB"
     stdout_logfile_backups  : 50
-    stderr_logfile          : "/var/log/koding/#{app}.log"
-    stdout_logfile          : "/var/log/koding/#{app}.log"
+    stderr_logfile          : "#{logdir}/#{app}.log"
+    stdout_logfile          : "#{logdir}/#{app}.log"
     numprocs                : options.instances or 1
     numprocs_start          : 0
-    directory               : "/opt/koding"
+    directory               : projectRoot
     autostart               : yes
     autorestart             : yes
     startsecs               : 10
@@ -73,7 +77,7 @@
     stopsignal              : "TERM"
     stopwaitsecs            : 10
     redirect_stderr         : yes
-    stdout_logfile          : "/var/log/koding/#{app}.log"
+    stdout_logfile          : "#{logdir}/#{app}.log"
     stdout_logfile_maxbytes : "1MB"
     stdout_logfile_backups  : 10
     stdout_capture_maxbytes : "1MB"
@@ -125,7 +129,7 @@
     groupConfigs[options.group]       or= {}
     groupConfigs[options.group][name] or= {}

-    conf += generateSupervisorSectionForWorker name, options
+    conf += generateWorkerSection name, options, KONFIG

   # add group sections


Updated the "namedUrl" method to work with IE. IE seems to have troubles with using "RegExp.$1" variables when in a string.replace context.

--- src/tent-client.js.coffee
+++ src/tent-client.js.coffee
@@ -16,9 +16,9 @@
     servers[0]

   @namedUrl: (server, name, params = {}) ->
-    server?.urls[name]?.replace URI_TEMPLATE_REGEX, =>
-      param = params[RegExp.$1] || ''
-      delete params[RegExp.$1]
+    server?.urls[name]?.replace URI_TEMPLATE_REGEX, (wholeMatch, firstGroup) =>
+      param = params[firstGroup] || ''
+      delete params[firstGroup]

       encodeURIComponent(param)


Only show invite code for production

Only show invite code for production

--- app/assets/javascripts/admin/views/connections/new.js.coffee
+++ app/assets/javascripts/admin/views/connections/new.js.coffee
@@ -28,17 +28,27 @@
       @$el.find("#connect_buttons").hide()

   toggle_env: ->
+    # update label with env name
     env = @$el.find("[name='env']").val()
     @$el.find('#env_label').text env

-    if env=='custom'
-      @$el.find("p.url").hide()
-      @$el.find("input.url").show()
-    else
-      url = @$el.find("p.url")
-      url.text Augury.SignUp.urls[env]
-      url.show()
-      @$el.find("input.url").hide()
+    # hide invite code (only needed in production)
+    @$el.find("#invite").hide()
+
+    switch env
+      when 'custom'
+        # custom envs can input a url for the integrator
+        @$el.find("p.url").hide()
+        @$el.find("input.url").show()
+      when 'production'
+        # production needs an invite code
+        @$el.find("#invite").show()
+      else
+        # other not input for url, just show hardcoded url
+        url = @$el.find("p.url")
+        url.text Augury.SignUp.urls[env]
+        url.show()
+        @$el.find("input.url").hide()

   cancel: ->
     window.location.href = "/admin/"


[ci skip] refactoring top layout

[ci skip] refactoring top layout

--- app/assets/javascripts/components/welcomes/top_panel.js.cjsx
+++ app/assets/javascripts/components/welcomes/top_panel.js.cjsx
@@ -21,14 +21,11 @@
   render: ->

-

-          ☆12参考表
-          <image src={image_path('letsencrypt-logo.png')} style={height: '40px'} />
-

+
☆12参考表

-
2chスレッドで議論された地力表を反映

-
IRTによる地力値を用いた参考表コンテンツ

-
クリアランプ管理をグラフで可視化

+
掲示板で議論された地力表を反映

+
地力値を用いた参考表コンテンツ

+
ランプ管理をグラフで可視化

           {@renderRegister()}


add additional comment to RosterUserView.coffee

--- app/coffeescripts/views/courses/roster/RosterUserView.coffee
+++ app/coffeescripts/views/courses/roster/RosterUserView.coffee
@@ -62,6 +62,7 @@
           ENV.permissions.manage_students
       # SFU MOD CANVAS-224 Only show unenroll link if allowed
       # User can only be unenrolled here if all of their enrollments are not defined by SIS
+      # canRemoveStudents was added by Instructure in c856b5 but we are keeping our mod for now (gnb 2015-09-01)
       json.canRemove = _.every(json.enrollments, (en) -> en.sis_source_id == null)
       # END SFU MOD


Non-hacky way of detecting ping event

Non-hacky way of detecting ping event

--- routes/postreceive.coffee
+++ routes/postreceive.coffee
@@ -55,7 +55,7 @@

 postreceive = (req, res, next) ->
   # ping event
-  if 'hook' in req.body and 'zen' in req.body
+  if req.headers['X-GitHub-Event'] == 'ping'
     res.status(204).end()
     return


finish move

finish move

--- lib/marked.coffee
+++ lib/marked.coffee
@@ -1,2 +1,5 @@
 marked.setOptions
   smartypants: true
+
+marked.InlineLexer.rules.gfm.url =
+  ///^((coauthor:/?/?|https?://)[^\s<]+[^<.,:;"')\]\s])///


cakefile: install sikuli to /tmp

cakefile: install sikuli to /tmp

--- cake_tasks/sikuli.coffee
+++ cake_tasks/sikuli.coffee
@@ -5,7 +5,7 @@
   exec "ls #{path}", (error, stdout, stderr)->
     if error
       sikuliUrl  = "https://launchpad.net/sikuli/sikulix/x1.0-rc3/+download/Sikuli-X-1.0rc3%20%28r905%29-osx-10.6.dmg"
-      sikuliFile = "sikuli.dmg"
+      sikuliFile = "/tmp/sikuli.dmg"

       wget = spawn "wget", [sikuliUrl, "-O#{sikuliFile}"]


speedup webpack watch (for dev) by a factor of 10x.

--- src/webpack.config.coffee
+++ src/webpack.config.coffee
@@ -31,7 +31,7 @@
 This webpack config file might look scary, but it only consists of a few moving parts.

 1. There is the "main" SMC application, which is split into "css", "lib" and "smc":
-   1. css: a collection of all static styles from various locations. it might be possible
+   1. css: a collection of all static styles from various locations. It might be possible
       to use the text extraction plugin to make this a .css file, but that didn't work out.
       Some css is inserted, but it doesn't work and no styles are applied. In the end,
       it doesn't matter to load it one way or the other. Furthermore, as .js is even better,
@@ -202,7 +202,7 @@
 while base_url_html and base_url_html[base_url_html.length-1] == '/'
     base_url_html = base_url_html.slice(0, base_url_html.length-1)

-# this is the main indes.html file, which should be served without any caching
+# this is the main index.html file, which should be served without any caching
 jade2html = new HtmlWebpackPlugin
                         date             : BUILD_DATE
                         title            : TITLE
@@ -401,8 +401,9 @@
     cache: true

     # https://webpack.github.io/docs/configuration.html#devtool
-    # don't use cheap-module-eval-source-map produces too large files
-    devtool: if SOURCE_MAP then 'source-map' else false
+    # **do** use cheap-module-eval-source-map; it produces too large files, but who cares since we are not
+    # using this in production.  DO NOT use 'source-map', which is VERY slow.
+    devtool: if SOURCE_MAP then '#cheap-module-eval-source-map'

     entry: # ATTN don't alter or add names here, without changing the sorting function above!
         css  : 'webapp-css.coffee'


forgot START_COMMENT

forgot START_COMMENT

--- lib/parser.coffee
+++ lib/parser.coffee
@@ -26,6 +26,7 @@
     * @package Regex
     * @supports {js}
     * @supports {coffee}
+    * @supports {escapedCoffee}
   ###
   @START_COMMENT =
     js: /^\s*\/\*\*/


Add test for missing paths

Add test for missing paths

--- spec/projects-list-view-spec.coffee
+++ spec/projects-list-view-spec.coffee
@@ -13,7 +13,7 @@
     testproject2:
       _id: 'testproject2'
       title: "Test project 2"
-      paths: ["/Users/project-2"]
+      paths: [atom.getConfigDirPath()]
       template: "test-template"
       icon: "icon-bug"
       group: "Test"
@@ -33,6 +33,11 @@
   it "will list all projects", ->
     listView.show(projects())
     expect(list.find('li').length).toBe 2
+
+  it "will let you know if a path is not available", ->
+    listView.show(projects())
+    expect(list.find('li').eq(0).data('pathMissing')).toBe true
+    expect(list.find('li').eq(1).data('pathMissing')).toBe false

   it "will add the correct icon to each project", ->
     listView.show(projects())
@@ -89,4 +94,4 @@
     it "sort after group", ->
       atom.config.set('project-manager.sortBy', 'group')
       listView.show(projects())
-      expect(list.find('li:eq(0)').data('projectId')).toBe "testproject2"+      expect(list.find('li:eq(0)').data('projectId')).toBe "testproject2"


Bump serialization version on WorkspaceView

Since we've replaced views with models, the 'PaneContainer' deserializer
now loads a model instead of a view, which isn't what we want at this
layer.

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -41,7 +41,7 @@
   Serializable.includeInto(this)
   atom.deserializers.add(this, PaneView, PaneRowView, PaneColumnView, EditorView)

-  @version: 2
+  @version: 3

   @configDefaults:
     ignoredNames: [".git", ".svn", ".DS_Store"]


more changes to find

more changes to find

--- noddy/service/v2/find.coffee
+++ noddy/service/v2/find.coffee
@@ -180,14 +180,14 @@
           delete metadata.doi
           delete options.doi # don't allow the user-provided data to later override if we can't validate it on crossref
         else
-          metadata = crs
+          _get metadata, crs
       if metadata.licence? and metadata.licence.indexOf('creativecommons') isnt -1
         res.url = 'https://doi.org/' + metadata.doi
         res.found.crossref = res.url
   _get_formatted_europepmc = () ->
     if (not _got() or (res.find and not res.url)) and 'epmc' in res.sources and (metadata.doi or metadata.pmid or metadata.pmcid or metadata.title)
       res.checked.push('epmc') if 'epmc' not in res.checked
-      metadata = API.use.europepmc.format undefined, metadata
+      _get metadata, API.use.europepmc.format undefined, metadata
       if metadata.url
         res.url = metadata.url
         res.found.epmc = res.url


add php web-safe

add php web-safe

--- data/tags/php.coffee
+++ data/tags/php.coffee
@@ -4,3 +4,4 @@
 desc: "生产环境中最常用的服务端编程语言。"
 reference:
   "PHP 中文手册": "http://www.php.net/manual/zh/"
+  "PHP 安全编码": "http://fe.baidu.com/doc/websafe/websafeCriterion/php.html"


Blaze fix for batch admin page.

Blaze fix for batch admin page.

--- admin/experimentAdmin.coffee
+++ admin/experimentAdmin.coffee
@@ -100,7 +100,7 @@
       $pull: { treatmentIds: @_id }
   "click .-ts-add-batch-treatment": (e, tmpl) ->
     e.preventDefault()
-    treatment = Spark.getDataContext(tmpl.find(":selected"))
+    treatment = UI.getElementData(tmpl.find(":selected"))
     return unless treatment._id
     Batches.update @_id,
       $addToSet: { treatmentIds: treatment._id }


Render text layer on hovered pages (and adjacent ones).

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -405,6 +405,16 @@
 						scope.adjustingScroll = true
 						element.scrollTop(currentScrollTop + delta)

+				element.on 'mouseover', '.pdf-page-container', (e) ->
+					pdfPageEl = $(e.currentTarget)
+
+					pdfPageEl.find('.plv-text-layer').addClass('plv-text-layer-visible')
+					pdfPageEl.prev().find('.plv-text-layer').addClass('plv-text-layer-visible')
+					pdfPageEl.next().find('.plv-text-layer').addClass('plv-text-layer-visible')
+
+				element.on 'mouseout', '.pdf-page-container', (e) ->
+					$('.plv-text-layer-visible').removeClass('plv-text-layer-visible')
+
 				element.on 'scroll', () ->
 					#console.log 'scroll event', element.scrollTop(), 'adjusting?', scope.adjustingScroll
 					#scope.scrollPosition = element.scrollTop()


repl task

repl task

--- natal.coffee
+++ natal.coffee
@@ -376,6 +376,12 @@
     launch readConfig()

+cli.command 'repl'
+  .description 'Launch a ClojureScript REPL with background compilation'
+  .action ->
+    startRepl readConfig().name
+
+
 cli.command 'listdevices'
   .description 'List available simulator devices by index'
   .action ->


Support for intermediate certificates in SSL

--- lib/server.coffee
+++ lib/server.coffee
@@ -68,10 +68,17 @@
   fs = require 'fs'
   httpsKey = path.resolve(config.get('CONFIG_DIR'), "#{service}.key")
   httpsCert = path.resolve(config.get('CONFIG_DIR'), "#{service}.crt")
+  httpsCa = path.resolve(config.get('CONFIG_DIR'), "#{service}.ca-bundle")
   options = {
     key: fs.readFileSync(httpsKey),
     cert: fs.readFileSync(httpsCert)
   }
+
+  try
+    # Use intermediate certificates if available
+    options.ca = fs.readFileSync(httpsCa).toString()
+      .match(/\-+BEGIN CERTIFICATE\-+[0-9a-zA-Z\+\-\/\=\s]+?\-+END CERTIFICATE\-+/g)
+
   server = require('https').createServer(options, listener)
 else
   server = require('http').createServer(listener)


pretty print

pretty print

--- lib/server.coffee
+++ lib/server.coffee
@@ -13,7 +13,7 @@
 kublai.use express.logger('dev')
 #kublai.use(require('less-middleware')({ src: './public/style' }))
 kublai.use express.static('./public')
-kublai.set 'json spaces',0
+#kublai.set 'json spaces',0
 kublai.get '/data/:year/:set/counties/:tables', (req,res)->
 	if docs[req.params.year].counties[req.params.tables]
 		res.jsonp docs[req.params.year].counties[req.params.tables]
@@ -48,4 +48,4 @@
 kublai.get '*', (req, res)->
 	res.status(404).sendfile('./lib/404.jpg')
 int = require './internals.coffee'
-int.run(kublai)+int.run(kublai)


attempt fix

attempt fix

--- lib/sorter.coffee
+++ lib/sorter.coffee
@@ -52,13 +52,14 @@
 					text = text.slice(0, -1)
 				else
 					end = ''
+					style="top: 1px; bottom: 2px;" a="test"

 				# map
 				separations = [
 					{
 						find: '="'
-						join: ' '
-						split: ' '
+						join: '" '
+						split: '" '
 					}
 					{
 						find: ';'


If you aren't admin, go straight to login page.

--- client/router.coffee
+++ client/router.coffee
@@ -82,9 +82,9 @@
 # ########
 # filters
 # ########
-mustBeAdmin = ->
+mustBeAdmin = (pause) ->
   if @ready() and not Houston._user_is_admin Meteor.userId()
-    @stop()
+    pause()
     Houston._go 'login'

 # If the host app doesn't have a router, their html may show up


Save user settings for Ace editor

Save user settings for Ace editor

This is also a very detailed example of new AppStorage Class usage.

--- client/landing/app/Applications/Ace.kdapplication/ace.coffee
+++ client/landing/app/Applications/Ace.kdapplication/ace.coffee
@@ -1,6 +1,6 @@
 ###
   todo:
-
+
     - put search replace
     - fix setSoftWrap it goes back to off when you reopen the settings

@@ -8,38 +8,49 @@

 class Ace extends KDView
-
+
   constructor:(options, file)->
-
+
     super options, file
     @lastSavedContents = ""
+    @appStorage = new AppStorage 'Ace', '1.0'

   setDomElement:(cssClass)->

     @domElement = $ "

"

   viewAppended:->
-
-    require ['ace/ace'], (ace)=>
-      @editor = ace.edit "editor#{@getId()}"
-      @prepareEditor()
-      @utils.wait => @emit "ace.ready"
-      @fetchContents (err, contents)=>
-        @setContents contents if contents
-        @editor.gotoLine 0
-
+
+    @appStorage.fetchStorage (storage)=>
+      log storage
+      require ['ace/ace'], (ace)=>
+        @editor = ace.edit "editor#{@getId()}"
+        @prepareEditor()
+        @utils.wait => @emit "ace.ready"
+        @fetchContents (err, contents)=>
+          @setContents contents if contents
+          @editor.gotoLine 0

   prepareEditor:->
-
+
     @setTheme()
     @setSyntax()
-    @setShowPrintMargin no
+    @setUseSoftTabs         @appStorage.getValue 'useSoftTabs',         yes
+    @setShowGutter          @appStorage.getValue 'showGutter',          yes
+    @setShowPrintMargin     @appStorage.getValue 'showPrintMargin',     no
+    @setHighlightActiveLine @appStorage.getValue 'highlightActiveLine', yes
+    @setShowInvisibles      @appStorage.getValue 'showInvisibles',      yes
+    @setFontSize            @appStorage.getValue 'fontSize',            10
+    @setTabSize             @appStorage.getValue 'tabSize',             4
+    @setUseWordWrap         @appStorage.getValue 'useWordWrap',         yes
+    @setSoftWrap            @appStorage.getValue 'softWrap',            'free'
     @setEditorListeners()
-
+
   setEditorListeners:->
+
     @editor.getSession().selection.on 'changeCursor', (cursor)=>
       @emit "ace.change.cursor", @editor.getSession().getSelection().getCursor()
-
+
     @editor.commands.addCommand
         name    : 'save'
         bindKey :
@@ -63,11 +74,11 @@

     file.on "fs.save.started", =>
       @lastContentsSentForSave = @getContents()
-
+
   ###
   FS REQUESTS
   ###
-
+
   requestSave:->

     contents = @getContents()
@@ -79,48 +90,56 @@
     contents = @getContents()
     @emit "ace.requests.saveAs", contents

-
+
   fetchContents:(callback)->
-
+
     file = @getData()
     unless /localfile:/.test file.path
       file.fetchContents callback
     else
       callback null, file.contents or ""
-
+
   ###
   GETTERS
   ###
-
+
   getContents:-> @editor.getSession().getValue()
-
+
   getTheme:-> @editor.getTheme().replace "ace/theme/", ""

   getSyntax:-> @syntaxMode

-  getUseSoftTabs:-> @editor.getSession().getUseSoftTabs()
-
-  getShowGutter:-> @editor.renderer.getShowGutter()
-
-  getShowPrintMargin:-> @editor.getShowPrintMargin()
-
-  getHighlightActiveLine:-> @editor.getHighlightActiveLine()
-
-  getShowInvisibles:-> @editor.getShowInvisibles()
-
-  getFontSize:-> parseInt @$("#editor#{@getId()}").css("font-size"), 10
-
-  getTabSize:-> @editor.getSession().getTabSize()
-
-  getUseWordWrap:-> @editor.getSession().getUseWrapMode()
-
-  getSoftWrap:->
-
-    limit = @editor.getSession().getWrapLimitRange().max
+  getUseSoftTabs:->
+    @appStorage.getValue 'useSoftTabs', @editor.getSession().getUseSoftTabs()
+
+  getShowGutter:->
+    @appStorage.getValue 'showGutter', @editor.renderer.getShowGutter()
+
+  getShowPrintMargin:->
+    @appStorage.getValue 'showPrintMargin', @editor.getShowPrintMargin()
+
+  getHighlightActiveLine:->
+    @appStorage.getValue 'highlightActiveLine', @editor.getHighlightActiveLine()
+
+  getShowInvisibles:->
+    @appStorage.getValue 'showInvisibles', @editor.getShowInvisibles()
+
+  getFontSize:->
+    @appStorage.getValue 'fontSize', parseInt(@$("#editor#{@getId()}").css("font-size"), 10)
+
+  getTabSize:->
+    @appStorage.getValue 'tabSize', @editor.getSession().getTabSize()
+
+  getUseWordWrap:->
+    @appStorage.getValue 'useWordWrap', @editor.getSession().getUseWrapMode()
+
+  getSoftWrap:->
+
+    limit = @appStorage.getValue 'softWrap', @editor.getSession().getWrapLimitRange().max
     if limit then limit
     else
       if @getUseWordWrap() then "free" else "off"
-
+
   getSettings:->
     theme               : @getTheme()
     syntax              : @getSyntax()
@@ -133,67 +152,93 @@
     fontSize            : @getFontSize()
     tabSize             : @getTabSize()
     softWrap            : @getSoftWrap()
-
+
   ###
   SETTERS
   ###

   setContents:(contents)-> @editor.getSession().setValue contents

-  setTheme:(themeName = "merbivore_soft")->
-
+  setTheme:(themeName)->
+    themeName or= @appStorage.getValue 'theme', 'merbivore_soft'
     require ["ace/theme/#{themeName}"], (callback) =>
       @editor.setTheme "ace/theme/#{themeName}"
-
+      @appStorage.setValue 'theme', themeName, =>
+        callback
+
   setSyntax:(mode)->
-
+
     file = @getData()
     mode or= file.syntax
-
+
     unless mode
       ext  = @utils.getFileExtension file.path
       for name, [language, extensions] of __aceSettings.syntaxAssociations
         if ///^(?:#{extensions})$///i.test ext
           mode = name
       mode or= "text"
-
+
     require ["ace/mode/#{mode}"], ({Mode})=>
       @editor.getSession().setMode new Mode
       @syntaxMode = mode

-  setUseSoftTabs:(value)-> @editor.getSession().setUseSoftTabs value
-
-  setShowGutter:(value)-> @editor.renderer.setShowGutter value
-
-  setShowPrintMargin:(value)-> @editor.setShowPrintMargin value
-
-  setHighlightActiveLine:(value)-> @editor.setHighlightActiveLine value
+  setUseSoftTabs:(value)->
+
+    @editor.getSession().setUseSoftTabs value
+    @appStorage.setValue 'useSoftTabs', value, =>
+
+  setShowGutter:(value)->
+
+    @editor.renderer.setShowGutter value
+    @appStorage.setValue 'showGutter', value, =>
+
+  setShowPrintMargin:(value)->
+
+    @editor.setShowPrintMargin value
+    @appStorage.setValue 'showPrintMargin', value, =>
+
+  setHighlightActiveLine:(value)->
+
+    @editor.setHighlightActiveLine value
+    @appStorage.setValue 'highlightActiveLine', value, =>

   # setHighlightSelectedWord:(value)-> @editor.setHighlightActiveLine value

-  setShowInvisibles:(value)-> @editor.setShowInvisibles value
-
-  setFontSize:(value)-> @$("editor#{@getId()}").css fontSize : "#{value}px"
-
-  setTabSize:(value)-> @editor.getSession().setTabSize value
-
-  setUseWordWrap:(value)-> @editor.getSession().setUseWrapMode value
-
-  softWrapValueMap = ->
-
-    'off'  : [ null, 80 ]
-    '40'   : [ 40,   40 ]
-    '80'   : [ 80,   80 ]
-    'free' : [ null, 80 ]
-
+  setShowInvisibles:(value)->
+
+    @editor.setShowInvisibles value
+    @appStorage.setValue 'showInvisibles', value, =>
+
+  setFontSize:(value)->
+
+    @$("editor#{@getId()}").css fontSize : "#{value}px"
+    @appStorage.setValue 'fontSize', value, =>
+
+  setTabSize:(value)->
+
+    @editor.getSession().setTabSize value
+    @appStorage.setValue 'tabSize', value, =>
+
+  setUseWordWrap:(value)->
+
+    @editor.getSession().setUseWrapMode value
+    @appStorage.setValue 'useWordWrap', value, =>
+
   setSoftWrap:(value)->
-
-    [limit, margin] = softWrapValueMap()[value]
+    softWrapValueMap =
+      'off'  : [ null, 80 ]
+      '40'   : [ 40,   40 ]
+      '80'   : [ 80,   80 ]
+      'free' : [ null, 80 ]
+
+    [limit, margin] = softWrapValueMap[value]

     @editor.getSession().setWrapLimitRange limit, limit
     @editor.renderer.setPrintMarginColumn margin
     @setUseWordWrap no if value is "off"

+    @appStorage.setValue 'softWrap', value, =>
+
   ###
   HELPERS
   ###
@@ -202,9 +247,9 @@
   notify:(msg, style, details)->

     notification.destroy() if notification
-
+
     style or= 'error' if details
-
+
     notification = new KDNotificationView
       title     : msg or "Something went wrong"
       type      : "mini"


filter for tasks view

filter for tasks view

--- SingularityUI/app/views/tasks.coffee
+++ SingularityUI/app/views/tasks.coffee
@@ -50,13 +50,9 @@

         # Only show tasks that match the search query
         if @searchFilter
-            searchFilter = @searchFilter.toLowerCase()
-            fuse = new Fuse(
-                tasks
-                keys: ["id", "host"]
-                threshold: 0.4
-                maxPatternLength: 128)
-            tasks = fuse.search(searchFilter).reverse()
+            tasks = _.filter tasks, (task) =>
+                searchField = "#{ task.id }#{ task.host }".toLowerCase().replace(/-/g, '_')
+                searchField.toLowerCase().indexOf(@searchFilter.toLowerCase().replace(/-/g, '_')) isnt -1
         # Sort the table if the user clicked on the table heading things
         if @sortAttribute?
             tasks = _.sortBy tasks, (task) =>


Adapted warning log message when an error occurs


Update docupdaterhandler tests.

Update docupdaterhandler tests.

--- services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee
+++ services/web/test/UnitTests/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee
@@ -252,7 +252,7 @@
 					.calledWith(new Error("doc updater returned failure status code: 500"))
 					.should.equal true

-	describe "acceptChange", ->
+	describe "acceptChanges", ->
 		beforeEach ->
 			@change_id = "mock-change-id-1"
 			@callback = sinon.stub()
@@ -260,11 +260,14 @@
 		describe "successfully", ->
 			beforeEach ->
 				@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body)
-				@handler.acceptChange @project_id, @doc_id, @change_id, @callback
+				@handler.acceptChanges @project_id, @doc_id, [ @change_id ], @callback

 			it 'should accept the change in the document updater', ->
-				url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}/change/#{@change_id}/accept"
-				@request.post.calledWith(url).should.equal true
+				req =
+					url: "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}/change/accept"
+					json:
+						change_ids: [ @change_id ]
+				@request.post.calledWith(req).should.equal true

 			it "should call the callback", ->
 				@callback.calledWith(null).should.equal true
@@ -272,7 +275,7 @@
 		describe "when the document updater API returns an error", ->
 			beforeEach ->
 				@request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null)
-				@handler.acceptChange @project_id, @doc_id, @change_id, @callback
+				@handler.acceptChanges @project_id, @doc_id, [ @change_id ], @callback

 			it "should return an error to the callback", ->
 				@callback.calledWith(@error).should.equal true
@@ -280,7 +283,7 @@
 		describe "when the document updater returns a failure error code", ->
 			beforeEach ->
 				@request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "")
-				@handler.acceptChange @project_id, @doc_id, @change_id, @callback
+				@handler.acceptChanges @project_id, @doc_id, [ @change_id ], @callback

 			it "should return the callback with an error", ->
 				@callback


update deploy permissions

update deploy permissions

--- cookbooks/rubygems-hubot/files/default/scripts/deploy.coffee
+++ cookbooks/rubygems-hubot/files/default/scripts/deploy.coffee
@@ -39,11 +39,7 @@
       callback "Exited with #{exit_code}"

 allowedToDeploy = (username, env) ->
-  if env == 'production'
-    allowedUsers = ['dwradcliffe', 'shk']
-  else
-    allowedUsers = ['dwradcliffe', 'qrush', 'evanphx', 'shk', 'arthurnn', 'sferik']
-  return username in allowedUsers
+  return username in ['dwradcliffe', 'qrush', 'evanphx', 'shk', 'arthurnn', 'sferik']

 deploy = (msg, env, branch) ->
   unless allowedToDeploy(msg.message.user.name, env)


xhr2: fix httpAgent & httpsAgent members declaration

- this is one of _how did that ever worked?_

--- src/001-xml_http_request.coffee
+++ src/001-xml_http_request.coffee
@@ -292,15 +292,17 @@

   # @property {http.Agent} the agent option passed to HTTP requests
   #
-  # NOTE: this is not in the XMLHttpRequest API, and will not work in browsers.
-  # It is a stable node-xhr2 API that is useful for testing & going through web-proxies.
-  httpAgent: http.globalAgent
+  # NOTE: this is not in the XMLHttpRequest API, and will not work in
+  # browsers.  It is a stable node-xhr2 API that is useful for testing
+  # & going through web-proxies.
+  nodejsHttpAgent: http.globalAgent

   # @property {https.Agent} the agent option passed to HTTPS requests
   #
-  # NOTE: this is not in the XMLHttpRequest API, and will not work in browsers.
-  # It is a stable node-xhr2 API that is especially useful for testing.
-  httpsAgent: https.globalAgent
+  # NOTE: this is not in the XMLHttpRequest API, and will not work in
+  # browsers.  It is a stable node-xhr2 API that is useful for testing
+  # & going through web-proxies.
+  nodejsHttpsAgent: https.globalAgent

   # HTTP methods that are disallowed in the XHR spec.
   #


Correct comment

Correct comment

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-display.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-display.coffee
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.

-# ## t5/core/exception-report
+# ## t5/core/exception-display
 #
 # Provides dynamic behavior for the t5/core/ExceptionDisplay component; specifically,
 # filtering the stack trace.


showing tmpdir

showing tmpdir


test: split: remove unused fn

test: split: remove unused fn


Recently discussed problem has been understood. Adding relevant values
to the global scope upon `eval` in REPL.

See https://github.com/jashkenas/coffee-script/commit/bcf1e178a10377607aaa807adf3aeef688657e7a#commitcomment-362493

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -78,9 +78,10 @@
 # Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
 # The CoffeeScript REPL uses this to run the input.
 exports.eval = (code, options) ->
-  __filename = module.filename = process.argv[1] = options.filename
-  __dirname  = path.dirname __filename
-  global.require = require
+  global.__filename = module.filename = process.argv[1] = options.filename
+  global.__dirname  = path.dirname __filename
+  global.require    = require
+  global.module     = module
   (0;eval) compile code, options

 # Instantiate a Lexer for our use here.


CoffeeScript 0.6.1, for Node.js v0.1.90

CoffeeScript 0.6.1, for Node.js v0.1.90

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -22,7 +22,7 @@
   helpers:      this.helpers

 # The current CoffeeScript version number.
-exports.VERSION: '0.6.0'
+exports.VERSION: '0.6.1'

 # Instantiate a Lexer for our use here.
 lexer: new Lexer()


Adding an event handler for the orientation changing on a mobile device so that it performs the same action as when the window is resized. This will allow you to resize the canvas and image nicely when the mobile device rotates

--- coffee/jquery.coffee
+++ coffee/jquery.coffee
@@ -44,7 +44,7 @@
     lc.updateSize()

   $el.resize(resize)
-  $(window).resize(resize)
+  $(window).bind('orientationchange resize', resize)
   resize()

   if 'onInit' of opts


Push GA events

Push GA events

--- coffee/timer2.coffee
+++ coffee/timer2.coffee
@@ -155,6 +155,9 @@
 		disableGroup($('input:radio[name=time]'))
 		$("#slider").slider("disable")
 		$('#btn-reset').removeAttr("disabled")
+		_gaq.push(['_trackEvent', 'start-time', time.toString()])
+		_gaq.push(['_trackEvent', 'start-tea', localStorage[CHOSEN_TEA]])
+

 	#reset button function
 	$('#btn-reset').click ->


Hard code "React.podspec" for react-native itself

--- src/PodfileFragmentGenerator.coffee
+++ src/PodfileFragmentGenerator.coffee
@@ -67,6 +67,8 @@
   for np in nativePackages
     if np.nativePackage.podspec?
       podspecs.push [np.name, path.join np.path, np.nativePackage.podspec]
+    else if np.name is 'react-native'
+      podspecs.push [np.name, path.join np.path, 'React.podspec']
     else
       defaultPodspec = path.join np.path, "#{ np.name }.podspec"
       if fs.existsSync defaultPodspec


Remove unnecessary type conversions to Number

--- nodes.coffee
+++ nodes.coffee
@@ -1276,7 +1276,7 @@
     if to
       if from instanceof Value and from.isSimpleNumber() and
          to instanceof Value and to.isSimpleNumber()
-        to = +to.compile(o) - +fromRef
+        to = to.compile(o) - fromRef
         to += 1 unless exclusive
       else
         to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef


Prevented parameter contents from causing unexpected returns in the body.

--- nodes.coffee
+++ nodes.coffee
@@ -853,6 +853,7 @@
     o.scope     = sharedScope or new Scope(o.scope, @body, this)
     o.top       = true
     o.indent    = @idt(1)
+    empty       = @body.expressions.length is 0
     del o, 'noWrap'
     del o, 'globals'
     splat = undefined
@@ -877,7 +878,7 @@
         else
           params.push param
     params = (param.compile(o) for param in params)
-    @body.makeReturn()
+    @body.makeReturn() unless empty
     (o.scope.parameter(param)) for param in params
     code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
     func = "function(#{ params.join(', ') }) {#code#{ code and @tab }}"
@@ -898,9 +899,9 @@

 #### ParamNode

-# A parameter in a function definition. Special parameters have a particular
-# type - either 'this', meaning it assigns straight to the current context, or
-# 'splat', where it gathers up a block of the parameters into an array.
+# A parameter in a function definition. Beyond a typical Javascript parameter,
+# these parameters can also attach themselves to the context of the function,
+# as well as be a splat, gathering up a group of parameters into an array.
 exports.ParamNode = class ParamNode extends BaseNode

   class:    'ParamNode'


waypoint

waypoint

--- nodes.coffee
+++ nodes.coffee
@@ -1463,7 +1463,6 @@
   # some cannot.
   compileNode: (o) ->
     body          = Expressions.wrap [@body]
-    hasPure       = body.containsPureStatement()
     hasPureLast   = last(body.expressions)?.containsPureStatement()
     source        = if @range then @source.base else @source
     scope         = o.scope
@@ -1478,7 +1477,7 @@
     guardPart     = ''
     defPart       = ''
     idt1          = @tab + TAB
-    if @scoped and hasPure
+    if @scoped and @jumps()
       throw SyntaxError 'cannot use a pure statement in a scoped loop.'
     if @range
       forPart = source.compile merge(o, {index: ivar, @step})


fix missing imports

fix missing imports

--- server/collections.coffee
+++ server/collections.coffee
@@ -1,3 +1,6 @@
+import { Files } from '/imports/api/files/files.coffee'
+import { Pictures } from '/imports/api/pictures/pictures.coffee'
+
 validUserOrTeamOrMeeting = (userId, doc) ->
 	if userId
 		if doc.userId


use parseOptions from invitetoteambycsv

use parseOptions from invitetoteambycsv

--- servers/lib/server/handlers/invitetoteambycsvAnalyze.coffee
+++ servers/lib/server/handlers/invitetoteambycsvAnalyze.coffee
@@ -5,15 +5,8 @@
 Busboy = require 'busboy'
 parser = require 'csv-parse'
 { generateFakeClient } = require './../client'
+{ parserOpts: csvParseOpts } = require './invitetoteambycsv'

-module.exports.parserOpts = csvParseOpts =
-  delimiter: ','
-  columns: ['email', 'firstName', 'lastName', 'role']
-  relax: no # Preserve quotes inside unquoted field.
-  relax_column_count: yes
-  skip_empty_lines: true
-  trim: true # remove whitespaces
-  auto_parse: no # convert integers to numbers etc

 module.exports.handler = (req, rres) ->
   generateFakeClient req, rres, (err, client, session) ->


added log line

added log line

--- services/document-updater/app/coffee/DispatchManager.coffee
+++ services/document-updater/app/coffee/DispatchManager.coffee
@@ -16,6 +16,7 @@
 			_waitForUpdateThenDispatchWorker: (callback = (error) ->) ->
 				timer = new Metrics.Timer "worker.waiting"
 				worker.client.blpop "pending-updates-list", 0, (error, result) ->
+					logger.log("getting pending-updates-list", error, result)
 					timer.done()
 					return callback(error) if error?
 					return callback() if !result?


Refactor client app storage.

Refactor client app storage.

--- app/client/js/storage.coffee
+++ app/client/js/storage.coffee
@@ -27,8 +27,8 @@
       @songsStore
     ]

-    if @tryCreateLocalStorage()
-      @updateStoreOnLocalStorageChange()
+    @tryCreateLocalStorage()
+    @listenLocalStorage()
     @fetchStoresFromLocalStorage()
     @listenStores()

@@ -36,7 +36,7 @@
     @const
     @type {string}
   ###
-  @LOCALSTORAGE_KEY: 'songary'
+  localStorageKey: 'songary'

   ###*
     @type {goog.storage.Storage}
@@ -63,17 +63,14 @@
     - http://git.yathit.com/ydn-db/wiki/Home
     - https://github.com/swannodette/mori
     - https://github.com/benjamine/jsondiffpatch
-    @return {boolean}
     @protected
   ###
   tryCreateLocalStorage: ->
     mechanism = goog.storage.mechanism.mechanismfactory
-      .createHTML5LocalStorage Storage.LOCALSTORAGE_KEY
+      .createHTML5LocalStorage @localStorageKey
     # For instance, Safari in private mode does not allow localStorage.
-    return false if !mechanism
+    return if !mechanism
     @localStorage = new goog.storage.Storage mechanism
-    @preloadDefaultSongs()
-    true

   preloadDefaultSongs: ->
     return if @localStorage.get 'songs'
@@ -90,11 +87,12 @@
     NOTE(steida): This sync app state across tabs/windows.
     @protected
   ###
-  updateStoreOnLocalStorageChange: ->
+  listenLocalStorage: ->
     # NOTE(steida): IE 9/10/11 implementation of window storage event is
     # broken. http://stackoverflow.com/a/4679754
     # Naive fix via document.hasFocus() does not work. Investigate it later.
-    return if goog.labs.userAgent.browser.isIE()
+    return if !@localStorage || goog.labs.userAgent.browser.isIE()
+
     goog.events.listen window, 'storage', (e) =>
       browserEvent = e.getBrowserEvent()
       storeName = browserEvent.key.split('::')[1]


Set up a new GA tracking code for 2015

Set up a new GA tracking code for 2015

--- client/view/setup.coffee
+++ client/view/setup.coffee
@@ -14,7 +14,7 @@
         a.src = g
         m.parentNode.insertBefore(a, m)
     )(window, document, 'script', '//www.google-analytics.com/analytics.js','ga')
-    ga('create', 'UA-36904731-2', 'reversim.com')
+    ga('create', 'UA-36904731-3', 'reversim.com')

   ga('send', 'pageview',
     'page': document.location.pathname,


[minor] Use string interpolation

[minor] Use string interpolation

--- src/backbone.websql.deferred.coffee
+++ src/backbone.websql.deferred.coffee
@@ -77,7 +77,7 @@
       @_executeSql "INSERT #{orReplace} INTO `#{@tableName}`(#{colNames.join(",")}) VALUES (#{placeholders.join(",")});", params

     find: (model, doneCallback, failCallback, options) ->
-      @_executeSql "SELECT `id`, `value` FROM `" + @tableName + "` WHERE (`id` = ?);",
+      @_executeSql "SELECT `id`, `value` FROM `#{@tableName}` WHERE (`id` = ?);",
         [model.id.toString()], doneCallback, failCallback, options

     findAll: (model, doneCallback, failCallback, options) ->


findAll is only for Backbone.Collection

findAll is only for Backbone.Collection

--- src/backbone.websql.deferred.coffee
+++ src/backbone.websql.deferred.coffee
@@ -172,7 +172,7 @@

     switch method
       when "read"
-        if model.id is undefined
+        if model instanceof Backbone.Collection
           store.findAll(model, doneCallback, failCallback, options)
         else
           isSingleResult = true


Removed redundant brackets

Removed redundant brackets

--- skeletons/skeleton.coffee
+++ skeletons/skeleton.coffee
@@ -1,7 +1,7 @@
 Template.BASENAME.helpers
 #  helper: ->

-Template.BASENAME.rendered = () ->
+Template.BASENAME.rendered = ->

 Template.BASENAME.events
 #  "click .selector": (e, template) ->


elektron: persist app sessions

elektron: persist app sessions

--- elektron/src/start.coffee
+++ elektron/src/start.coffee
@@ -12,6 +12,7 @@
     width  : 1280
     height : 800
     webPreferences    :
+      partition       : 'persist:koding'
       preload         : path.resolve path.join __dirname, 'noderequire.js'
       nodeIntegration : no


Remove debugging line

Remove debugging line

--- mobile/app/settings/settings.coffee
+++ mobile/app/settings/settings.coffee
@@ -41,7 +41,6 @@
           type: 'button-positive',
           onTap: ->
             $scope.value = $scope.input.value
-            console.log $scope.value
             settings.set $scope.key, $scope.value
         ]


test ResponseHandler

test ResponseHandler


[FIX] Use author link in footer

[FIX] Use author link in footer

--- app/views/footer-view.coffee
+++ app/views/footer-view.coffee
@@ -16,4 +16,4 @@
   if site.sourceData
     links.push(href: site.sourceData, text: 'Data')

-  m Footer, Object.assign {links}, author, site
+  m Footer, Object.assign {links}, site, author


Fix localstorage if clause

Fix localstorage if clause

--- client/app.coffee
+++ client/app.coffee
@@ -32,7 +32,7 @@
     )

   zoom = 15
-  if localStorage
+  if localStorage and localStorage.getItem('rectangle')
     rectangle = JSON.parse(localStorage.getItem('rectangle'))
     rectangle_editor = new L.RectangleEditor([[rectangle._southWest.lat, rectangle._southWest.lng],[rectangle._northEast.lat, rectangle._northEast.lng]])
     zoom = localStorage.getItem('zoom') || zoom


Use lazy loading for requires to ensure we only include the minimum necessary code.

--- coffee/patchboard.coffee
+++ coffee/patchboard.coffee
@@ -1,5 +1,7 @@
-module.exports =
-  Server: require("patchboard-server")
-  Client: require("patchboard-client")
-  Tools: require("./tools")
+lazyRequire = (path) -> get: (-> require( path )), enumerable: true

+Object.defineProperties module.exports,
+  Server: lazyRequire "patchboard-server"
+  Client: lazyRequire "patchboard-client"
+  Tools: lazyRequire "./tools"
+


Event missing from callback

Event missing from callback

--- coffee/vex.dialog.coffee
+++ coffee/vex.dialog.coffee
@@ -38,7 +38,7 @@
         dialog.buttons.NO
     ]
     showCloseButton: false
-    onSubmit: ->
+    onSubmit: (event) ->
         $form = $ @
         $vexContent = $form.parent()
         event.preventDefault()


allow a configuration file to override settings, and fixed paths for javascript files

--- plugin/gulp-wordpress.coffee
+++ plugin/gulp-wordpress.coffee
@@ -20,16 +20,15 @@

 regex_all     = "/**/*"

-theme_name        = "THEME"
-theme_url         = "../theme"
-wordpress_url     = "../../wordpress"
-wordpress_plugins = []
+config = require "../theme/config.coffee"

-
-require "../theme/config.coffee"
-
+theme_name        = config.theme_name        or "THEME"
+theme_url         = config.theme_url         or "../theme"
+wordpress_url     = config.wordpress_url     or "../../wordpress"
+wordpress_plugins = config.wordpress_plugins or []

 output_url = "#{wordpress_url}/wp-content/themes/#{theme_name}"
+
 paths =
   "fonts":
     "src": ["#{theme_url}/assets/fonts/#{regex_all}"]
@@ -63,7 +62,8 @@

 process.stdout.write("Theme Name: #{theme_name}\n")
-gulpif(production, process.stdout.write("Compiling for Production\n\n"))
+process.stdout.write("Compiling for Production\n\n") if production
+

 gulp.task "fonts", ->
   gulp.src paths.fonts.src
@@ -83,7 +83,9 @@
     .pipe coffee(bare: true)
     .pipe gulp.dest(paths.js.base)

-  gulp.src require(paths.js.app)
+  js_files = ("#{paths.js.base}/#{js}" for js in require(paths.js.app))
+
+  gulp.src js_files
     .pipe plumber()
     .pipe concat("index.js")
     .pipe gulpif(production, uglify())


cstyle functional

cstyle functional

--- config/syntax.coffee
+++ config/syntax.coffee
@@ -13,21 +13,60 @@

 class Syntax
   constructor: (@alphabet) ->
-  pre: (args,cb) ->
-    @proc args,cb
-  proc: (args,cb) ->
-    @post args,cb
-  post: (args,cb) ->
-    cb args
+  fromText: (text,cb) ->
+    cb text

 class CSyntax extends Syntax
-  pre: (args,cb) ->
-    regexp = new RegExp escape(@alphabet.comment.start) + '(.|[\r\n])*?' + escape(@alphabet.comment.end), 'g'
+  sanitize: (str) ->
+    console.log 'unsanitized:\n' + str
+    noNoise = str.replace (new RegExp '^\\s*' + (escape @alphabet.comment.noise), 'gm'), ''
+    console.log 'no noise:\n' + noNoise
+    normWhite = noNoise.replace /\s+/, ' '
+    console.log 'norm whitespace:\n' + normWhite
+    normWhite
+
+  fromText: (args,cb) ->
+    regexp = new RegExp escape(@alphabet.comment.start) + '(.|[\\r\\n])*?' + escape(@alphabet.comment.end), 'g'
     args.blocks = args.text.match regexp
-    super args,cb
-  proc: (args,cb) ->
+    @fromBlocks args,cb
+
+  fromBlocks: (args,cb) ->
+    regexp = new RegExp (
+      escape(@alphabet.comment.start) +
+      '[' + @alphabet.comment.noise + '\\s\\r\\n]*' +
+      '(\\w*)' +
+      '[' + @alphabet.comment.noise + '\\s\\r\\n]*' +
+      '([^' + @alphabet.attr.sign + ']*)' +
+      '([^]*)' +
+      escape(@alphabet.comment.end))
+
+    if not args.parent?
+      [junk,name,desc,attrBlob] = args.blocks[0].match regexp
+      attrs = @attrsFromBlob attrBlob
+      attrs.name = @sanitize name
+      if desc
+        attrs.description = @sanitize desc
+      args.parent = attrs
+      args.blocks.shift()
+
     for block in args.blocks
-      regexp = new RegExp ''
+      [junk,name,desc,attrBlob] = block.match regexp
+      attrs = @attrsFromBlob attrBlob
+      if desc
+        attrs.description = @sanitize desc
+      args.parent[@sanitize name] = attrs
+    cb args.parent
+
+
+  attrsFromBlob: (attrBlob) ->
+    alphabet = @alphabet
+    attrPairs = attrBlob.match new RegExp @alphabet.attr.sign + '[^' + @alphabet.attr.sign + ']*', 'g'
+    attrArray = attrPairs?.map (attr) ->
+      attr.match new RegExp alphabet.attr.sign + '(\\w*)' + '([^' + alphabet.attr.sign + '*])'
+    attrObj = {}
+    for attr in attrArray?
+      attrObj[@sanitize attr[0]] = @sanitize attr[1]
+    attrObj

 module.exports =
   Syntax: Syntax


Remove comments

Remove comments

--- app/src/controllers/common/dialogs/common_dialogs_ticket_dialog_view_controller.coffee
+++ app/src/controllers/common/dialogs/common_dialogs_ticket_dialog_view_controller.coffee
@@ -1,12 +1,6 @@
 class @CommonDialogsTicketDialogViewController extends ledger.common.DialogViewController

   view:
-#    body: '#ticket_body'
-#    email: '#ticket_email'
-#    name: '#ticket_name'
-#    subject: '#ticket_subject'
-#    tags: '#ticket_tags'
-#    logs: '#ticket_isOkToSendLog'
     logsSwitchContainer: "#logs_switch_container"
     logsContainer: "#logs_container"
     tagsSegmentedControlContainer: "#tags_segmented_control_container"
@@ -110,31 +104,6 @@
     else
       sendBlock(data)

-#  sendTicket: ->
-#    # Get metadata
-#    metadata = @_getMetadata()
-#    @_getLogs()
-#    .then (logs) =>
-#      # Set a boolean to pass isOkToSend()
-#      @view.logs.val(@logsSwitch.isOn())
-#      # form checking UI
-#      _.map @view, (value) =>
-#        if (value.val?() is '' or value.val?() is null) and not value.next().hasClass('verif')
-#          value.after('
The field must be filled!
')
-#        #if (value.val?() isnt '' or value.val?() isnt null) and value.next().hasClass('verif')
-#          #value.next().remove()
-#
-#      isOktoSend = _.every @view, (value) =>
-#        value.val?() isnt '' and value.val?()?
-#      l 'isOktoSend', isOktoSend
-#
-#      if isOktoSend and @_validateEmail()
-#        ledger.api.GrooveRestClient.singleton().sendTicket @view.body.val(), @view.email.val(), @view.name.val(), @view.subject.val(), @view.tags.val(), metadata, logs
-#      else
-#        l 'all fields are required!'
-#
-#
-
   _getMetadata: ->
     # Get OS, version Chrome, version firmware, version chromeApp, etc..
     parser = new UAParser()


alert when teams have no ssh keys

alert when teams have no ssh keys

--- scripts/setup-ssh-keys.coffee
+++ scripts/setup-ssh-keys.coffee
@@ -1,10 +1,15 @@
+unless process.env.LOGIN
+  console.log "Usage: LOGIN=[github username]:[password] coffee #{__filename}"
+  process.exit(1)
+github = (path) -> "https://#{encodeURIComponent(process.env.LOGIN)}@api.github.com/#{path}"
+
 mongoose = require('../models')(require('../config/env').mongo_url)
 #require('../lib/mongo-log')(mongoose.mongo)

 spawn = require('child_process').spawn
-
 async = require 'async'
 request = require 'request'
+isArray = require('util').isArray

 Team = mongoose.model 'Team'

@@ -16,36 +21,60 @@
     console.log team.slug, 'getting github logins'
     team.people (err, people) ->
       return next(err) if err?
-      logins = (github.login for { github } in people when github?.login)
+      logins = (p.github.login for p in people when p?.github?.login)

       if logins.length is 0
         return next("Error: #{team} team has no github logins")

       next(null, logins)

-  addSSHKeys = (logins, next) ->
-    console.log team.slug, 'adding ssh keys'
-    cmd = """
-      for u in #{logins.join(' ')}; do
-        (
-          echo;
-          echo "### BEGIN ${u} KEYS (see https://github.com/${u}.keys)";
-          wget -qO- "https://github.com/${u}.keys";
-          echo;
-          echo "### END ${u} KEYS"
-        ) >> ~/.ssh/authorized_keys;
-      done
-    """.replace(/\n\s*/g, ' ')
+  getGithubSSHKeys = (logins, next) ->
+    console.log team.slug, 'getting ssh keys'
+    keys = {}
+    async.each logins, (login, next) ->
+      request
+          url: github "users/#{login}/keys"
+          json: {}
+        , (err, res, body) ->
+          return next(err) if err?
+          return next(Error(JSON.stringify(body))) unless isArray(body)
+          return next(Error(JSON.stringify(body))) if body.length && !body[0].key
+
+          if body.length is 0
+            console.warn team, login, 'has no keys'
+          else
+            keys[login] = (key for {key} in body)
+          next()
+    , (err) -> next(err, keys)
+
+  createAuthorizedKeys = (loginKeys, next) ->
+    console.log team.slug, 'creating authorized keys file'
+    keyString = ''
+    for login, keys of loginKeys
+      keyString += """\n
+        ### BEGIN #{login} SSH KEYS (see https://github.com/#{login}.keys)
+        #{keys.join('\n')}
+        ### END #{login} SSH KEYS\n
+      """
+    return next("#{team} has no ssh keys") unless keyString
+    next(null, keyString)
+
+  addAuthorizedKeys = (authorizedKeys, next) ->
+    console.log team.slug, 'appending to authorized keys file'
+
     login = "root@#{team.slug}.2013.nodeknockout.com"
+    cmd = "cat /dev/stdin >> ~/.ssh/authorized_keys"

     ssh = spawn "ssh", [login, cmd]
-
     ssh.stdout.on 'data', (s) -> console.log s.toString()
     ssh.stderr.on 'data', (s) -> console.log s.toString()
     ssh.on 'error', next
     ssh.on 'exit', next

-  async.waterfall [getGithubLogins, addSSHKeys], next
+    ssh.stdin.write(authorizedKeys)
+    ssh.stdin.end()
+
+  async.waterfall [getGithubLogins, getGithubSSHKeys, createAuthorizedKeys, addAuthorizedKeys], next

 Team.find { slug: 'organizers' }, (err, teams) ->
   return last(err) if err


KDRouter: @ to this

KDRouter: @ to this

--- core/KDRouter.coffee
+++ core/KDRouter.coffee
@@ -152,7 +152,7 @@
     unless suppressListeners
       listeners = node[listenerKey]
       if listeners?.length
-        listener.call @, routeInfo, state, path  for listener in listeners
+        listener.call this, routeInfo, state, path  for listener in listeners

     return this


Access deactivatePackage from atom.packages

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -184,7 +184,7 @@
           rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
           rootView.openSync()

-          atom.deactivatePackage('fuzzy-finder')
+          atom.packages.deactivatePackage('fuzzy-finder')
           states = _.map atom.packages.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ]
           expect(states.length).toBe 3
           states = _.sortBy states, (path, time) -> -time


Put "after" on same level as "before".

Put "after" on same level as "before".

--- test/createProject.coffee
+++ test/createProject.coffee
@@ -62,6 +62,6 @@
         # console.log err
         console.log err.stdout
         throw err
-  after ->
-    fs.renameSync expectedDir + "/package.json", expectedDir + "/package.jumpstart.json"
-    rimraf targetDir+    after ->
+      fs.renameSync expectedDir + "/package.json", expectedDir + "/package.jumpstart.json"
+      rimraf targetDir


Remove ajax request to solve blueprint.

Remove ajax request to solve blueprint.

--- admin/static/coffee/resolve_issues.coffee
+++ admin/static/coffee/resolve_issues.coffee
@@ -330,7 +330,7 @@
                 _.each @contestants, (contestant) =>
                     @.$('#resolve_' + contestant.contestant_id).off 'click'

-            if @model.get('object_type') is 'blueprint'
+            if @model.get('field') is 'blueprint'
                 @contestants = []
             else
                 # grab possible conflicting values


update who's on call test to match current code.

--- test/pager-me-test.coffee
+++ test/pager-me-test.coffee
@@ -67,7 +67,7 @@
     expect(@robot.respond).to.have.been.calledWith(/pager( me)? (.+) (\d+)$/i)

   it 'registers a pager on call listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/who('s|s| is|se)? (on call|oncall|on-call)( (?:for )?(.+))?/i)
+    expect(@robot.respond).to.have.been.calledWith(/who(’s|'s|s| is|se)? (on call|oncall|on-call)( (?:for )?(.+))?/i)

   it 'registers a pager services listener', ->
     expect(@robot.respond).to.have.been.calledWith(/(pager|major)( me)? services$/i)


Fix typo in test name

Fix typo in test name

--- test/pathview_test.coffee
+++ test/pathview_test.coffee
@@ -15,5 +15,5 @@
   it 'registers a pathview listener', ->
     expect(@robot.respond).to.have.been.calledWith(/pathview me foo to bar/)

-  it 'registers a hear listener', ->
+  it 'registers a pvc listener', ->
     expect(@robot.respond).to.have.been.calledWith(/pvc me foo to bar/)


Fix test

Fix test

--- api/apps/articles/test/model/index.coffee
+++ api/apps/articles/test/model/index.coffee
@@ -976,7 +976,7 @@
           name: 'Kana Abe'
         sections: []
       , ->
-        Article.publishScheduledArticles (err, results) ->
+        Article.unqueue (err, results) ->
           results[0].weekly_email.should.be.false()
           results[0].daily_email.should.be.false()
           done()


Remove 'count' column from smugmug utils

Unsure why this was added but was misaligning the header and thfr
breaking the sort provided by plugin.

Fixes #652

--- _coffee/smugmug_api.coffee
+++ _coffee/smugmug_api.coffee
@@ -13,13 +13,13 @@
 fill_album_list = (albums) ->
   ret = ""
   $(albums).each (i, album) ->
-    ret += "
#{ i+1 }	#{ album["Name"] }	#{ album["AlbumKey"] }	#{ album["ImageCount"] }	?
\n"
+    ret += "
#{ album["Name"] }	#{ album["AlbumKey"] }	#{ album["ImageCount"] }	?
\n"
   $("#smug-albums tbody").html(ret)

 fill_image_list = (images) ->
   ret = ""
   $(images).each (i, image) ->
-    ret += "
#{ i+1 }	#{ image["Title"] }	#{ image["FileName"] }	#{ image["ImageKey"] }	?
\n"
+    ret += "
#{ image["Title"] }	#{ image["FileName"] }	#{ image["ImageKey"] }	?
\n"
   $("#smug-images tbody").html(ret)

 fetch_usage_list = (url, callback) ->


Adding another existential operator

Adding another existential operator

--- app/assets/coffee/views/_base/view.coffee
+++ app/assets/coffee/views/_base/view.coffee
@@ -6,7 +6,7 @@

     remove: (args...) ->
       console.log "removing", @ if App.environment is "local"
-      if @model?.isDestroyed()
+      if @model?.isDestroyed?()
         @$el.fadeOut 400, =>
           _remove.apply @, args
       else


Improve Table of Contents nested list font size

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -63,5 +63,6 @@

   tocElement.find( 'ul' ).addClass( 'list list--s' )
   tocElement.children( 'ul' ).addClass( 'list--divided list--divided--s mbm' )
+  tocElement.children( 'ul' ).children( 'li' ).addClass( 'tsm' )
   tocElement.find( 'ul' ).find( 'ul' ).addClass( 'mlm mts' )
-  tocElement.find( 'li' ).addClass( 'list-item tsm' )
+  tocElement.find( 'li' ).addClass( 'list-item' )


Update chronograph dashboard

Update chronograph dashboard

--- app/assets/javascripts/chronograph.coffee
+++ app/assets/javascripts/chronograph.coffee
@@ -22,11 +22,13 @@

     ctx.clearRect(0, 0, @settings.size, @settings.size)

+
     # hour
     ctx.beginPath()
     ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 12, 0, Math.PI*2, true)
     ctx.lineWidth = 22
-    ctx.strokeStyle = "#1C1C1C"
+    ctx.strokeStyle = "#000000"
+    ctx.globalAlpha = 0.325
     ctx.shadowBlur  = 0
     ctx.stroke()
     ctx.closePath()
@@ -36,17 +38,22 @@
     ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 38.5, 0, Math.PI*2, true)
     ctx.lineWidth = 22
     ctx.strokeStyle = "#000000"
+    ctx.globalAlpha = 0.225
     ctx.shadowBlur  = 0
     ctx.stroke()
     ctx.closePath()
+

     # second
     ctx.beginPath()
     ctx.arc(@settings.size/2, @settings.size/2, @settings.size/2 - 55, 0, Math.PI*2, true)
     ctx.lineWidth = 2
-    ctx.strokeStyle = "#444444"
+    ctx.strokeStyle = "#000000"
+    ctx.globalAlpha = 0.225
     ctx.shadowBlur  = 0
     ctx.stroke()
     ctx.closePath()

+    ctx.globalAlpha = 125
+
 window.Chronograph = Chronograph


moving requestAnimationFrame to gamelib

moving requestAnimationFrame to gamelib

--- src/browser_extensions.coffee
+++ src/browser_extensions.coffee
@@ -1,10 +0,0 @@
-window.requestAnimationFrame ||=
-  window.webkitRequestAnimationFrame ||
-  window.mozRequestAnimationFrame    ||
-  window.oRequestAnimationFrame      ||
-  window.msRequestAnimationFrame     ||
-  (callback, element) ->
-    window.setTimeout( ->
-      callback(+new Date())
-    , 1000 / 60)
-


remove alert code line

remove alert code line

--- app/assets/javascripts/products.js.coffee
+++ app/assets/javascripts/products.js.coffee
@@ -11,7 +11,6 @@
 conektaSuccessResponseHandler = (token) ->
   $form = $("#card-form")
   $form.append $("<input type=\"hidden\" name=\"charge[token]\" />").val(token.id)
-  alert token.id
   $form.get(0).submit()
   return


cleanup constants.coffee

cleanup constants.coffee

--- server/utils/constants.coffee
+++ server/utils/constants.coffee
@@ -1,18 +1,16 @@

 exports.MSGBYPAGE = 30
 # safeDestroy parameters (to be tweaked)
-# loads 200 ids in memory at once
+# number of ids loaded in memory at once
 exports.LIMIT_DESTROY = 200
-# loads 30 messages in memory at once
+# number of messages loaded in memory at once
 exports.LIMIT_UPDATE = 50
-# send 5 request to the DS in parallel
+# number of request sent to the DS in parallel
 exports.CONCURRENT_DESTROY = 1
 # number of IMAP & cozy UID&flags in memory when comparing boxes
 exports.FETCH_AT_ONCE = 10000
 # maximum number of messages to fetch at once for each box
 exports.LIMIT_BY_BOX  = 1000
-# accounts refreshs in ms, 5 minutes
-exports.REFRESH_INTERVAL = 300000

 exports.RFC6154 =
     draftMailbox:   '\\Drafts'


fixes #1157: when compiling multiple scripts, compile them if at least one of them isn't empty

--- command.coffee
+++ command.coffee
@@ -86,7 +86,7 @@
             fs.readFile source, (err, code) ->
               if opts.join
                 contents[sources.indexOf source] = code.toString()
-                compileJoin() if helpers.compact(contents).length is sources.length
+                compileJoin() if helpers.compact(contents).length > 0
               else
                 compileScript(source, code.toString(), base)
             watch source, base if opts.watch and not opts.join


dirty forms for f-c-with-aside forms

dirty forms for f-c-with-aside forms

--- app/assets/javascripts/folio/console/modules/_dirty-forms.coffee
+++ app/assets/javascripts/folio/console/modules/_dirty-forms.coffee
@@ -9,7 +9,7 @@
   $form.on 'submit', -> $(window).off('beforeunload')

 $('.simple_form')
-  .filter(-> @className.match(/(new_|edit_)/))
+  .filter(-> @className.match(/(new_|edit_)/) or @className.indexOf('f-c-with-aside') isnt -1)
   .not('.f-c-simple-form-with-atoms')
   .one 'change', ->
     handler($(this))


if/else for update/create product in product_form_template

--- app/assets/javascripts/templates/product_form_template.js.coffee
+++ app/assets/javascripts/templates/product_form_template.js.coffee
@@ -82,7 +82,12 @@
                 </div>
             </fieldset>
             <div class="form-actions widget">
-              <input type="submit" value="Update Product" name="commit" class="btn btn-primary">
+                {{#put}}
+                    <input type="submit" value="Update Product" name="commit" class="btn btn-primary">
+                {{/put}}
+                {{^put}}
+                    <input type="submit" value="Create Product" name="commit" class="btn btn-primary">
+                {{/put}}
             </div>
         </fieldset>
     </form>


Actually require initializers

Actually require initializers

--- assets/scripts/travis.coffee
+++ assets/scripts/travis.coffee
@@ -18,6 +18,14 @@
     url: "https://charmscout.herokuapp.com/feedback"

   $('head').append $('<script src="https://charmscout.herokuapp.com/charmeur.js?v=2" async defer></script>')
+
+require 'initializers/auth'
+require 'initializers/config'
+require 'initializers/google-analytics'
+require 'initializers/pusher'
+require 'initializers/services'
+require 'initializers/storage'
+require 'initializers/stylesheets-manager'

 require 'utils/ajax'


make ctrl+G take walls into account

make ctrl+G take walls into account

--- src/coffee/client/puzzle.coffee
+++ src/coffee/client/puzzle.coffee
@@ -609,24 +609,22 @@
             return

         # get the contiguous run of open cells containing the selection
-        # (we can assume for this that it's an 'across' word, since we did
-        # the transpose, above)
         cells = []
         if @state.grid_focus.is_across
             c1 = col
             c2 = col
-            while c1 > 0 and g[row][c1 - 1].open
+            while c1 > 0 and g[row][c1 - 1].open and not g[row][c1 - 1].rightbar
                 c1--
-            while c2 < @width() - 1 and g[row][c2 + 1].open
+            while c2 < @width() - 1 and g[row][c2 + 1].open and not g[row][c2].rightbar
                 c2++
             for i in [c1 .. c2]
                 cells.push([row, i])
         else
             r1 = row
             r2 = row
-            while r1 > 0 and g[r1 - 1][col].open
+            while r1 > 0 and g[r1 - 1][col].open and not g[r1 - 1][col].bottombar
                 r1--
-            while r2 < @height() - 1 and g[r2 + 1][col].open
+            while r2 < @height() - 1 and g[r2 + 1][col].open and not g[r2][col].bottombar
                 r2++
             for i in [r1 .. r2]
                 cells.push([i, col])
@@ -688,14 +686,19 @@
             return

         fail = () ->
+            console.trace()
             alert('Unable to enter "' + word + '" into the grid; maybe the grid has been modified?')

         info = @state.findMatchesInfo
         g = @state.puzzle.grid

         # Check that all the cells are still open
-        for [r, c] in info.cells
+        for i in [0 .. info.cells.length - 1]
+            [r, c] = info.cells[i]
             if not (0 <= r and r < @height() and 0 <= c and c < @width() and g[r][c].open)
+                fail()
+                return
+            if i < info.cells.length - 1 and g[r][c][if info.is_across then 'rightbar' else 'bottombar']
                 fail()
                 return

@@ -710,8 +713,8 @@
         else
             prevR--
             nextR++
-        if (prevR >= 0 and prevR < @height() and prevC >= 0 and prevC < @width() and g[prevR][prevC].open) or \
-           (nextR >= 0 and nextR < @height() and nextC >= 0 and nextC < @width() and g[nextR][nextC].open)
+        if (prevR >= 0 and prevR < @height() and prevC >= 0 and prevC < @width() and g[prevR][prevC].open and not g[prevR][prevC][if info.is_across then 'rightbar' else 'bottombar']) or \
+           (nextR >= 0 and nextR < @height() and nextC >= 0 and nextC < @width() and g[nextR][nextC].open and not g[info.cells[info.cells.length - 1][0]][info.cells[info.cells.length - 1][1]][if info.is_across then 'rightbar' else 'bottombar'])
             fail()
             return