coco 19474b4: nodes: removed Base::collectChildren

--- nodes.coffee
+++ nodes.coffee
@@ -101,10 +101,11 @@

   # `toString` representation of the node, for inspecting the parse tree.
   # This is what `coffee --nodes` prints out.
-  toString: (idt = '', override) ->
-    children = (child.toString idt + TAB for child in @collectChildren()).join('')
-    klass = override or @constructor.name + if @soak then '?' else ''
-    '\n' + idt + klass + children
+  toString: (idt = '', name = @constructor.name) ->
+    tree = '\n' + idt + name
+    tree += '?' if @soak
+    @eachChild (node) -> tree += node.toString idt + TAB
+    tree

   # Passes each child to a function, breaking when the function returns `false`.
   eachChild: (func) ->
@@ -113,11 +114,6 @@
       for child in flatten [@[attr]]
         return this if func(child) is false
     this
-
-  collectChildren: ->
-    nodes = []
-    @eachChild (node) -> nodes.push node
-    nodes

   traverseChildren: (crossScope, func) ->
     @eachChild (child) ->


remove debug logging

remove debug logging

--- vendor/assets/javascripts/carousel/views/photo_view.js.coffee
+++ vendor/assets/javascripts/carousel/views/photo_view.js.coffee
@@ -22,7 +22,6 @@
     $(@el).attr 'src', @model.get('image').url

   renderDetails: =>
-    console.log "reeeeeender"
     v = new SC.PhotoDetailsView({model:@model})
     v.render()


Add parameter-unencoding tests for set.

Add parameter-unencoding tests for set.

--- spec/hashparamsSpec.coffee
+++ spec/hashparamsSpec.coffee
@@ -124,7 +124,7 @@
                 hashYieldsValues '#tags=A%2CB,C%2CD', {tags: setOf('A,B', 'C,D'), authors: setOf()}
             it 'will not set values.foo', ->
                 hashYieldsValues '#foo=bar', {tags: setOf(), authors: setOf()}
-        describe 'unencoding', ->
+        describe 'string parameter unencoding', ->
             decodes = (encoded, decoded) ->
                 params = new HashParams('name' + decoded)
                 params.setHash '#name' + encoded + '=value' + encoded
@@ -137,6 +137,23 @@
             it 'accepts !', -> accepts '!'
             it 'accepts $', -> accepts '$'
             it 'accepts &', -> accepts '&'
+            it 'accepts ,', -> accepts ','
+            it 'decodes ;', -> decodes '%3B', ';'
+        describe 'set parameter unencoding', ->
+            decodes = (encoded, decoded) ->
+                params = new HashParams('name' + decoded + ':set')
+                params.setHash '#name' + encoded + '=value1' + encoded + ',value2' + encoded
+                expected = {}
+                expected['name' + decoded] = setOf('value1' + decoded, 'value2' + decoded)
+                expect(params.values).toEqual expected
+            accepts = (char) -> decodes char, char
+            it 'decodes space', -> decodes '%20', ' '
+            it 'decodes =', -> decodes '%3D', '='
+            it 'accepts !', -> accepts '!'
+            it 'accepts $', -> accepts '$'
+            it 'accepts &', -> accepts '&'
+            it 'decodes ,', -> decodes '%2C', ','
+            it 'decodes ;', -> decodes '%3B', ';'
     describe '.with()', ->
         describe 'starting with foreground=blue and background=green', ->
             params = null


Remove spec for #63

Remove spec for #63

No longer necessary as the fuzzyProvider specs cover this case.

--- spec/issues/63-spec.coffee
+++ spec/issues/63-spec.coffee
@@ -1,44 +0,0 @@
-{waitForAutocomplete} = require '../spec-helper'
-
-describe 'Autocomplete', ->
-  [mainModule, autocompleteManager, editorView, editor, completionDelay] = []
-
-  describe 'Issue 63', ->
-    beforeEach ->
-      runs ->
-        # Set to live completion
-        atom.config.set('autocomplete-plus.enableAutoActivation', true)
-
-        # Set the completion delay
-        completionDelay = 100
-        atom.config.set('autocomplete-plus.autoActivationDelay', completionDelay)
-        completionDelay += 100 # Rendering delay
-
-        workspaceElement = atom.views.getView(atom.workspace)
-        jasmine.attachToDOM(workspaceElement)
-
-
-      waitsForPromise -> atom.workspace.open('sample.js').then (e) ->
-        editor = e
-
-      # Activate the package
-      waitsForPromise -> atom.packages.activatePackage('autocomplete-plus').then (a) ->
-        mainModule = a.mainModule
-
-      waitsFor ->
-        mainModule.autocompleteManager?.ready
-
-      runs ->
-        autocompleteManager = mainModule.autocompleteManager
-
-      runs ->
-        advanceClock(mainModule.autocompleteManager.providerManager.fuzzyProvider.deferBuildWordListInterval)
-        editorView = atom.views.getView(editor)
-
-    it 'it adds words to the wordlist when pressing a special character', ->
-      runs ->
-        editor.insertText('somethingNew')
-        editor.insertText(')')
-
-        provider = autocompleteManager.providerManager.fuzzyProvider
-        expect(provider.wordList.indexOf('somethingNew')).not.toEqual(-1)


reactivity: chat input: test fix

reactivity: chat input: test fix

--- client/activity/lib/flux/test/emojis/filteredemojilistquerystore.coffee
+++ client/activity/lib/flux/test/emojis/filteredemojilistquerystore.coffee
@@ -45,4 +45,4 @@
       @reactor.dispatch actionTypes.UNSET_FILTERED_EMOJI_LIST_QUERY
       query = @reactor.evaluate ['filteredEmojiListQuery']

-      expect(query).to.equal ''
+      expect(query).to.be.null


:white_check_mark: fix spec for replacing linebreak (#408)

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -997,10 +997,16 @@

     it "replaces a single character with a line break", ->
       keydown('r')
-      # TODO: Is this how you input a CR?
-      commandModeInputKeydown('\n')
+      commandModeInputKeydown('\r')
       expect(editor.getText()).toBe '\n2\n34\n\n'
-      expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+    xit "replaces a single character with a line break", ->
+      keydown('r')
+      commandModeInputKeydown('\r')
+      expect(editor.getText()).toBe '\n2\n34\n\n'
+      # For some reason, the buffer position remains
+      # at [0, 0], even if it changes in an actual editor.
+      expect(editor.getCursorBufferPosition()).toEqual [1, 0]

     it "composes properly with motions", ->
       keydown('2')


fix that terrible spec

fix that terrible spec

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -194,16 +194,19 @@
     beforeEach ->
       editor.getBuffer().setText("012 345\nabc\n")
       editor.setCursorScreenPosition([0, 4])
-
+
     describe "when selected lines in visual linewise mode", ->
       beforeEach ->
-        keydown('V')
-        keydown('k')
-        keydown('y')
-
-      it "saves the lines to the default register in linewise mode", ->
+        keydown('V', shift: true)
+        keydown('j')
+        keydown('y')
+
+      it "is in linewise motion", ->
+        console.log("CK", vimState.getRegister('"').type)
+        expect(vimState.getRegister('"').type).toEqual "linewise"
+
+      it "saves the lines to the default register", ->
         expect(vimState.getRegister('"').text).toBe "012 345\nabc\n"
-        expect(vimState.getRegister('"').type).toBe 'linewise'

     describe "when followed by a second y ", ->
       beforeEach ->


fix up spectrogram example to use new angle convention

--- examples/audio/static/coffee/spectrogram.coffee
+++ examples/audio/static/coffee/spectrogram.coffee
@@ -354,7 +354,6 @@
       outer_radius: 'outer_radius'
       start_angle: 'start_angle'
       end_angle: 'end_angle'
-      direction: 'clock'
     }
     glyph = Collections('GlyphRenderer').create({
       data_source: @hist_source.ref()


Fixed fs error

Fixed fs error

--- spec/spark-ide-spec.coffee
+++ spec/spark-ide-spec.coffee
@@ -415,14 +415,15 @@
     it 'tests passing firmware', ->
       SettingsHelper.setCredentials 'foo@bar.baz', '0123456789abcdef0123456789abcdef'
       SettingsHelper.setCurrentCore '0123456789abcdef0123456789abcdef', 'Foo'
+      atom.config.set 'spark-ide.deleteFirmwareAfterFlash', true
+      firmwarePath = atom.project.getPaths()[0] + '/firmware.bin'
       SparkStub.stubSuccess 'flashCore'
-      fs.openSync atom.project.getPaths()[0] + '/firmware.bin', 'w'
-
-      sparkIde.flashCloud 'firmware2.bin'
+      fs.openSync firmwarePath, 'w'
+
+      sparkIde.flashCloud 'firmware.bin'
       expect(sparkIde.spark.flashCore).toHaveBeenCalled()
-      expect(sparkIde.spark.flashCore).toHaveBeenCalledWith('0123456789abcdef0123456789abcdef', ['firmware2.bin'])
-
-      fs.unlinkSync atom.project.getPaths()[0] + '/firmware.bin'
+      expect(sparkIde.spark.flashCore).toHaveBeenCalledWith('0123456789abcdef0123456789abcdef', ['firmware.bin'])
+
       SettingsHelper.clearCurrentCore()
       SettingsHelper.clearCredentials()


Wait for status change before checking modified state

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2515,11 +2515,19 @@

       describe "when a file loses its modified status", ->
         it "updates its and its parent directories' styles", ->
-          fs.writeFileSync(modifiedFile, originalFileContent)
-          atom.project.getRepositories()[0].getPathStatus(modifiedFile)
-
-          expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified'
-          expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified'
+          runs ->
+            fs.writeFileSync(modifiedFile, originalFileContent)
+
+          waitsFor (done) ->
+            disposable = atom.project.getRepositories()[0].onDidChangeStatuses ->
+              disposable.dispose()
+              done()
+
+          runs ->
+            atom.project.getRepositories()[0].getPathStatus(modifiedFile)
+
+            expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified'
+            expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified'

   describe "when the resize handle is double clicked", ->
     beforeEach ->


Use Workspace::onDidOpen in specs

Use Workspace::onDidOpen in specs

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -6,14 +6,12 @@
 wrench = require 'wrench'
 os = require 'os'

-waitsForFileToOpen = (fn) ->
-  openHandler = jasmine.createSpy()
-  runs ->
-    atom.workspaceView.one "uri-opened", openHandler
-    fn()
-
-  waitsFor ->
-    openHandler.callCount == 1
+waitsForFileToOpen = (causeFileToOpen) ->
+  waitsFor (done) ->
+    disposable = atom.workspace.onDidOpen ->
+      disposable.dispose()
+      done()
+    causeFileToOpen()

 describe "TreeView", ->
   [treeView, root, sampleJs, sampleTxt] = []


:art: Clean up unpublish specs

:art: Clean up unpublish specs

--- spec/unpublish-spec.coffee
+++ spec/unpublish-spec.coffee
@@ -58,8 +58,8 @@
       describe 'when the user accepts the default answer', ->
         it 'does not unpublish the package', ->
           callback = jasmine.createSpy('callback')
-          spyOn(Unpublish.prototype, 'prompt').andCallFake (q, cb) -> cb('')
-          spyOn(Unpublish.prototype, 'unpublishPackage').andCallFake -> callback()
+          spyOn(Unpublish.prototype, 'prompt').andCallFake (args..., cb) -> cb('')
+          spyOn(Unpublish.prototype, 'unpublishPackage')
           apm.run(['unpublish', 'test-package'], callback)

           waitsFor 'waiting for unpublish command to complete', ->
@@ -107,8 +107,8 @@
       describe 'when the user accepts the default answer', ->
         it 'does not unpublish the package', ->
           callback = jasmine.createSpy('callback')
-          spyOn(Unpublish.prototype, 'prompt').andCallFake (q, cb) -> cb('')
-          spyOn(Unpublish.prototype, 'unpublishPackage').andCallFake -> callback()
+          spyOn(Unpublish.prototype, 'prompt').andCallFake (args..., cb) -> cb('')
+          spyOn(Unpublish.prototype, 'unpublishPackage')
           apm.run(['unpublish', 'test-package'], callback)

           waitsFor 'waiting for unpublish command to complete', ->


remove console

remove console

--- BackofficeBundle/Resources/public/coffee/page/blockView.coffee
+++ BackofficeBundle/Resources/public/coffee/page/blockView.coffee
@@ -1,7 +1,6 @@
 BlockView = OrchestraView.extend(
   initialize: (options) ->
     @events = {}
-    console.log('click span.block-param-' + @cid)
     @events['click span.block-param-' + @cid] = 'paramBlock'
     @block = options.block
     @areaCid = options.areaCid


setPath -> setPaths

setPath -> setPaths

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -12,7 +12,7 @@
   getEditorView = -> atom.views.getView(atom.workspace.getActiveTextEditor())

   beforeEach ->
-    atom.project.setPath(temp.mkdirSync('atom-symbols-view-'))
+    atom.project.setPaths([temp.mkdirSync('atom-symbols-view-')])
     fs.copySync(path.join(__dirname, 'fixtures'), atom.project.getPath())

     activationPromise = atom.packages.activatePackage("symbols-view")
@@ -248,7 +248,7 @@
         expect(atom.workspace.getActiveEditor().getCursorBufferPosition()).toEqual [0,4]

     it "includes ? and ! characters in ruby symbols", ->
-      atom.project.setPath(path.join(atom.project.getPath(), 'ruby'))
+      atom.project.setPaths([path.join(atom.project.getPath(), 'ruby')])

       waitsForPromise ->
         atom.workspace.open 'file1.rb'


make jax context accessible to window in dev env

--- app/assets/javascripts/jax/controller-list.js.coffee
+++ app/assets/javascripts/jax/controller-list.js.coffee
@@ -1,18 +1,17 @@
 #= require "./dom-helpers"

 {div, link} = Suite
-context = null

 controller = (name) ->
   div link name, ->
-    context or= new Jax.Context "webgl"
+    window.jax or= new Jax.Context "webgl"
     # jax doesn't unload redirects to the same controller.
     # Within a single app this is probably expected behavior.
     # But, in this particular context, user most likely wants
     # to reload the controller entirely. So we need to manually
     # kick off the cleanup procedure.
-    context.unloadScene()
-    context.redirectTo name
+    window.jax.unloadScene()
+    window.jax.redirectTo name
     false

 $(document).ready ->


return id of explorativ #65

return id of explorativ #65

--- app/assets/javascripts/oxalis/view/mergemodal.coffee
+++ app/assets/javascripts/oxalis/view/mergemodal.coffee
@@ -174,7 +174,7 @@
       @merge(url)

   mergeExplorativs : ->
-    explorativId = @ui.project.find("select :selected").val()
+    explorativId = @ui.project.find("select :selected").prop("id")
     url = "/annotations/Explorational/#{explorativId}/merge/#{@_model.tracingType}/#{@_model.tracingId}"
     @merge(url)


Show personal ranking when user starts dragging an answer

--- app/assets/javascripts/views/question_view.js.coffee
+++ app/assets/javascripts/views/question_view.js.coffee
@@ -70,7 +70,9 @@
         helper: -> $(this).clone().width($(this).width())
         appendTo: 'body'
         connectToSortable: '.personal.vote'
+        delay: 1
       )
+      item.mousedown => @showPersonalVote()

     if position = options.position
       item.data('position', position)


call formatter for major categorical ticks (#7025)

--- bokehjs/src/coffee/models/axes/categorical_axis.coffee
+++ bokehjs/src/coffee/models/axes/categorical_axis.coffee
@@ -83,15 +83,16 @@
     info = []

     if range.levels == 1
-      info.push([ ticks.major, coords.major, @model.major_label_orientation, @visuals.major_label_text ])
+      labels = @model.formatter.doFormat(ticks.major, @)
+      info.push([ labels, coords.major, @model.major_label_orientation, @visuals.major_label_text ])

     else if range.levels == 2
-      labels = (x[1] for x in ticks.major)
+      labels = @model.formatter.doFormat((x[1] for x in ticks.major), @)
       info.push([ labels,     coords.major, @model.major_label_orientation, @visuals.major_label_text ])
       info.push([ ticks.tops, coords.tops,  'parallel',                     @visuals.group_text       ])

     else if range.levels == 3
-      labels = (x[2] for x in ticks.major)
+      labels = @model.formatter.doFormat((x[2] for x in ticks.major), @)
       mid_labels = (x[1] for x in ticks.mids)
       info.push([ labels,     coords.major, @model.major_label_orientation, @visuals.major_label_text ])
       info.push([ mid_labels, coords.mids,  'parallel',                     @visuals.subgroup_text    ])


Fix capstone in progress check

Fix capstone in progress check

--- ozaria/site/views/play/level/tome/CastButtonView.coffee
+++ ozaria/site/views/play/level/tome/CastButtonView.coffee
@@ -80,12 +80,12 @@
   onNextButtonClick: (e) ->
     if @winnable and @options.level.get('ozariaType') == 'capstone'
       @options.session.recordScores @world?.scores, @options.level
-      capstoneStage = @options.capstoneStage # passed in from PlayLevelView->TomeView
+      capstoneStage = @options.capstoneStage # passed in from PlayLevelView->TomeView, it is the Capstone Stage that has been just completed
       finalStage = GoalManager.maxCapstoneStage(@options.level.get('additionalGoals'))
       args = {
         showModal: true
         manual: true
-        capstoneInProgress: capstoneStage <= finalStage
+        capstoneInProgress: capstoneStage < finalStage
         isCapstone: true
       }
       Backbone.Mediator.publish 'level:show-victory', args


Fixed race condition and improved performance: Refresh models after all new records of _all_ types were added instead of current model only.

--- app/assets/javascripts/app/lib/app_post/collection.coffee
+++ app/assets/javascripts/app/lib/app_post/collection.coffee
@@ -74,9 +74,9 @@
         if !_.isEmpty(later)
           loadAssetsLater[type] = later

-      # process existing assets
-      for type, collections of loadAssetsLater
-        App[type].refresh(collections)
+    # process existing assets
+    for type, collections of loadAssetsLater
+      App[type].refresh(collections)

     if params.targetModel
       for type, collections of assets


9226: fixed js bug

9226: fixed js bug

--- app/assets/javascripts/views/batch_actions_view.js.coffee
+++ app/assets/javascripts/views/batch_actions_view.js.coffee
@@ -126,7 +126,8 @@
       # (we do it this way in case the main form has other stuff in it that we don't want to submit)
       form.append(@form.find('input.batch_op:checked').clone())
       form.append(@form.find('input[name=select_all]').clone())
-      form.append(@search_form_view.$el.find('input[name=search]').clone())
+      if (@search_form_view)
+        form.append(@search_form_view.$el.find('input[name=search]').clone())

       token = $('meta[name="csrf-token"]').attr('content')
       $('<input>').attr({type: 'hidden', name: 'authenticity_token', value: token}).appendTo(form)


Better variable name.

Better variable name.

--- app/assets/javascripts/app/resources.js.coffee
+++ app/assets/javascripts/app/resources.js.coffee
@@ -1,22 +1,22 @@
 angular.module "mb.resources", ["ngResource", "mb.filters"], ($provide) ->

   $provide.factory "Database", [
-    "$resource", (resource) ->
+    "$resource", ($resource) ->

-      resource "/api/databases/:dbName.json", {},
+      $resource "/api/databases/:dbName.json", {},
         query: method: "GET", isArray: true
   ]

   $provide.factory "Collection", [
-    "$resource", "$filter", (resource, $filter) ->
+    "$resource", "$filter", ($resource, $filter) ->

-      resource "/api#{$filter("collectionsPath")()}/:collectionName.json", {},
+      $resource "/api#{$filter("collectionsPath")()}/:collectionName.json", {},
         query: method: "GET", isArray: true
   ]

   $provide.factory "Document", [
-    "$resource", "$filter", (resource, $filter) ->
+    "$resource", "$filter", ($resource, $filter) ->

-      resource "/api#{$filter("documentsPath")()}/:id.json", {},
+      $resource "/api#{$filter("documentsPath")()}/:id.json", {},
         query: method: "GET", isArray: false
   ]


Store built parser as class variable

Store built parser as class variable

--- src/app/binding-set.coffee
+++ src/app/binding-set.coffee
@@ -7,13 +7,16 @@

 module.exports =
 class BindingSet
+
+  @parser: null
+
   selector: null
   commandsByKeystrokes: null
   commandForEvent: null
   parser: null

   constructor: (@selector, commandsByKeystrokes, @index) ->
-    @parser = PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs'))
+    BindingSet.parser ?= PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs'))
     @specificity = Specificity(@selector)
     @commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes)

@@ -42,7 +45,7 @@
     normalizedKeystrokes.join(' ')

   normalizeKeystroke: (keystroke) ->
-    keys = @parser.parse(keystroke)
+    keys = BindingSet.parser.parse(keystroke)
     modifiers = keys[0...-1]
     modifiers.sort()
     [modifiers..., _.last(keys)].join('-')


Modes; update badge on focus change.

Modes; update badge on focus change.

--- content_scripts/mode.coffee
+++ content_scripts/mode.coffee
@@ -60,6 +60,10 @@
     func()
     mode.exit()

+# We need to detect when the focused frame/tab changes, and update the badge.
+handlerStack.push
+  "focus": -> handlerStack.alwaysContinueBubbling -> Mode.updateBadge()
+
 # A SingletonMode is a Mode of which there may be at most one instance (of @singleton) active at any one time.
 # New instances cancel previous instances on startup.
 class SingletonMode extends Mode
@@ -116,5 +120,4 @@
 root = exports ? window
 root.Mode = Mode
 root.SingletonMode = SingletonMode
-root.MultiMode = MultiMode
 root.ConstrainedMode = ConstrainedMode


send permissions to client before startup

--- packages/rocketchat-authorization/client/startup.coffee
+++ packages/rocketchat-authorization/client/startup.coffee
@@ -1,2 +1 @@
-Meteor.startup ->
-	RocketChat.authz.subscription = Meteor.subscribe 'permissions'+RocketChat.authz.subscription = Meteor.subscribe 'permissions'


stick notifications

stick notifications

--- app/assets/javascripts/notifications.js.coffee
+++ app/assets/javascripts/notifications.js.coffee
@@ -1,6 +1,6 @@
 $(document).ready ->
     $("#flash-notifications").hide()
     $("#flash-notifications > .flash-notice").each ->
-        $.gritter.add {title: "-", text:($(this).html()), class_name: "success"}
+        $.gritter.add {title: "-", text:($(this).html()), class_name: "success", sticky: true}
     $("#flash-notifications > .flash-alert").each ->
-        $.gritter.add {title: ":(", text:($(this).html()), class_name: "alert"}
+        $.gritter.add {title: ":(", text:($(this).html()), class_name: "alert", sticky: true}


limit the auto-updater-win api to very few methods

--- atom/browser/api/lib/auto-updater/auto-updater-win.coffee
+++ atom/browser/api/lib/auto-updater/auto-updater-win.coffee
@@ -18,11 +18,11 @@

     @emit 'checking-for-update'

-    unless SquirrelUpdate.existsSync()
+    unless SquirrelUpdate.supported()
       @emit 'update-not-available'
       return

-    @downloadUpdate (error, update) =>
+    SquirrelUpdate.download (error, update) =>
       if error?
         @emit 'update-not-available'
         return
@@ -33,7 +33,7 @@

       @emit 'update-available'

-      @installUpdate (error) =>
+      SquirrelUpdate.update @updateUrl, (error) =>
         if error?
           @emit 'update-not-available'
           return
@@ -41,13 +41,4 @@
         # info about the newly installed version and a function any of the event listeners can call to restart the application
         @emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), @updateUrl, => @quitAndInstall()

-  downloadUpdate: (callback) ->
-    SquirrelUpdate.download(callback)
-
-  installUpdate: (callback) ->
-    SquirrelUpdate.update(@updateUrl, callback)
-
-  supportsUpdates: ->
-    SquirrelUpdate.supported()
-
 module.exports = new AutoUpdater()


change version

change version

--- package.cson
+++ package.cson
@@ -1,5 +1,5 @@
 name: 'node-jquery-lite'
-version: '0.0.1'
+version: '0.2.0'
 description: 'Project of jquery lite for node.js.'
 main: './index.js'
 repository:


Comment about RFC 2045 + hygiene

Comment about RFC 2045 + hygiene

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -3401,6 +3401,8 @@

         binary_to_string: (bin) =>
             # We print the size of the binary, not the size of the base 64 string
+            # We suppose something stronger than the RFC 2045:
+            # We suppose that there is ONLY one CRLF every 76 characters
             blocks_of_76 = Math.floor(bin.data.length/78) # 78 to count \r\n
             leftover = bin.data.length-blocks_of_76*78

@@ -3441,11 +3443,12 @@
                     if next.length is 1
                         next = "0" + next
                     snippet += next
-                    if i > 4 and i isnt size-1
-                        snippet += "..."
-                        break
-                    else if i isnt str.length-1
+
+                    if i isnt str.length-1
                         snippet += " "
+                if size > str.length
+                    snippet += "..."
+
                 return "<binary, #{sizeStr}, \"#{snippet}\">"

     class @ResultView extends DataExplorerView.SharedResultView


mainview : set logo of group

mainview : set logo of group

--- client/Main/maincontroller/mainviews/mainview.coffee
+++ client/Main/maincontroller/mainviews/mainview.coffee
@@ -80,18 +80,14 @@
         KD.utils.stopDOMEvent event
         if KD.isLoggedIn()
         then KD.getSingleton('router').handleRoute "/Activity", {entryPoint}
-
         else location.replace '/'

     @innerContainer.addSubView @logo

-    {groupsController} = KD.singletons
-    groupsController.ready =>
-      group     = groupsController.getCurrentGroup()
-      groupLogo = group.customize?.logo or ""
-      @logo.setCss 'background-image', "url(#{groupLogo})"
-
-      @logo.setClass KD.config.environment
+    groupLogo = KD.currentGroup?.logo or ""
+    @logo.setCss 'background-image', "url(#{groupLogo})"
+
+    @logo.setClass KD.config.environment

     @innerContainer.addSubView @logotype = new KDCustomHTMLView


code refactoring

code refactoring

--- client/Social/Activity/views/activitylistitem.coffee
+++ client/Social/Activity/views/activitylistitem.coffee
@@ -283,13 +283,14 @@
           @destroy()

       article.classList.add 'tall'
-      if @$('.activity-content-wrapper .mark-for-show-more').length > 0
+      if @hasShowMoreMark
         @addSubView @showMore, '.mark-for-show-more'
       else
         @addSubView @showMore, '.activity-content-wrapper'

   pistachio: ->
+    @hasShowMoreMark = yes
     """
     <div class="activity-content-wrapper">
       {{> @settingsButton}}


Reverted changes

Reverted changes

--- client/activity/lib/views/activityactionsview.coffee
+++ client/activity/lib/views/activityactionsview.coffee
@@ -53,9 +53,9 @@
           lazyLoad    : yes
         , customView  : new ActivitySharePopup delegate: this, url: shareUrl

-        pane = @parent.getDelegate()
+        pane = kd.singletons.appManager.frontApp.getView().tabs.getActivePane()

-        pane.parent.parent.parent.parent.parent.scrollView.wrapper.on "scroll", @sharePopup.bound "destroy"
+        pane.scrollView.wrapper.on 'scroll', @sharePopup.bound 'destroy'

         trackEvent "Activity share, click"


Pass "stackTemplate" to ProvidersView

Pass "stackTemplate" to ProvidersView

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -54,6 +54,8 @@

     providers.addSubView @providersView = new ProvidersView
       selectedCredentials: @credentials
+      stackTemplate       : stackTemplate
+      selectedCredentials : @credentials

     @tabView.showPaneByIndex 0


Fix an error with the check 'is_blocked'

--- app/coffee/modules/taskboard/lightboxes.coffee
+++ app/coffee/modules/taskboard/lightboxes.coffee
@@ -114,7 +114,7 @@
             event.preventDefault()
             target = angular.element(event.currentTarget)
             target.toggleClass("selected")
-            $scope.us.is_blocked = not $scope.us.is_blocked
+            $scope.task.is_blocked = not $scope.task.is_blocked
             $el.find(".blocked-note").toggle(400)

         $el.on "click", "label.iocaine", (event) ->


groups: moved config view (back face) to admin-only part of the logic, made the customise-button toggle the config view

--- client/app/MainApp/lazy/staticgroupcontroller.coffee
+++ client/app/MainApp/lazy/staticgroupcontroller.coffee
@@ -85,14 +85,6 @@
     @groupTitleView.addSubView @buttonWrapper = new KDCustomHTMLView
       cssClass : "button-wrapper"
       lazyDomId : "group-button-wrapper"
-
-
-    groupConfigView = new KDView
-      lazyDomId : 'group-config'
-
-    groupConfigView.addSubView new StaticGroupCustomizeView
-      delegate : @
-    ,@getData()

     @groupContentView = new KDView
       lazyDomId : 'group-loading-content'
@@ -263,7 +255,16 @@
         cssClass : 'editor-button'
         title    : "Customize"
         callback : =>
-          @groupContentWrapperView.setClass 'edit'
+          if @groupContentWrapperView.$().hasClass 'edit'
+            @groupContentWrapperView.unsetClass 'edit'
+          else @groupContentWrapperView.setClass 'edit'
+
+      groupConfigView = new KDView
+        lazyDomId : 'group-config'
+
+      groupConfigView.addSubView new StaticGroupCustomizeView
+        delegate : @
+      ,@getData()


use new qery and selection methods

use new qery and selection methods

--- client/skr/screens/time-invoicing/TimeInvoicing.cjsx
+++ client/skr/screens/time-invoicing/TimeInvoicing.cjsx
@@ -37,7 +37,7 @@
         return unless rate # the first call is when the model isn't parsed yet
         selectedRows = 0
         @query.results.eachRow (row, xd) ->
-            unless xd && false == xd.selected
+            if LC.Grid.Selections.isSelected(row, xd)
                 selectedRows += 1
                 total = total.add( hoursForRow(row) * rate )
         selectionState = if selectedRows is @query.results.length then 'all'
@@ -86,7 +86,7 @@
         rs.eachRow (row) ->
             xd = rs.xtraData(row)
             xd.selected = false == xd.selected
-        @query.changeCount++
+        @query.markModified()
     renderToggleAllButton: ->
         return null if 0 is @query.results.length


Update rock_python.coffee

Update rock_python.coffee

--- app/lib/field-guide-content/rock_python.coffee
+++ app/lib/field-guide-content/rock_python.coffee
@@ -4,7 +4,7 @@
     The African rock python is Africa’s largest snake. Their long, stout body is covered in small, smooth scales that form patterned blotches that vary in color between brown, olive, chestnut, and yellow, and often join up in a broad, irregular stripe. Their triangular head contains many sharp, backwardly curved teeth. The top of their head has a distinctive dark brown “spearhead” outlined in a buff yellow, and they have triangular markings under their eyes.
   '''

-  scientificName: '(Python sebae)'
+  scientificName: 'Python sebae'
   mainImage: 'assets/fieldguide-content/reptiles/rock_python/python-feature.jpg'

   conservationStatus: 'Least Concern' # Options are Least Concern, Near Threatened, Vulnerable, and Endangered; all have their own little icons as well.


Fix gradient vars to allow multiple

Fix gradient vars to allow multiple

--- app/scripts/components/shaders/Gradient.coffee
+++ app/scripts/components/shaders/Gradient.coffee
@@ -41,20 +41,21 @@
 ]

 Crafty.c 'Gradient',
-  _topColor:
-    _red: 0
-    _green: 0
-    _blue: 0
-    _strength: 1.0
-  _bottomColor:
-    _red: 0
-    _green: 0
-    _blue: 0
-    _strength: 1.0
-  _color: ""
-  ready: yes
+  init: ->
+    # Declaring the vars here instead as class attributes
+    # make them unique for each instance
+    @_topColor =
+      _red: 0
+      _green: 0
+      _blue: 0
+      _strength: 1.0
+    @_bottomColor =
+      _red: 0
+      _green: 0
+      _blue: 0
+      _strength: 1.0
+    @ready = yes

-  init: ->
     @bind 'Draw', @_drawGradient
     if @has 'WebGL'
       @_establishShader "Gradient",


Fix deltas referring to last system saved If a system is added, it is added to treema at the end of the list of systems. Treema then raises it's changed event resulting in this order being set on the level. The next time a system is added, the first system is in it's correct position and so the delta shows it as moving. This changes ensures we save the correct order in the first instance.

--- app/views/editor/level/systems_tab_view.coffee
+++ app/views/editor/level/systems_tab_view.coffee
@@ -59,12 +59,7 @@
     unless systems.length
       systems = @buildDefaultSystems()
       insertedDefaults = true
-
-    systemModels = @supermodel.getModels LevelSystem
-    systemModelMap = {}
-    systemModelMap[sys.get('original')] = sys.get('name') for sys in systemModels
-    systems = _.sortBy systems, (sys) -> systemModelMap[sys.original]
-
+    systems = @getSortedByName systems
     treemaOptions =
       # TODO: somehow get rid of the + button, or repurpose it to open the LevelSystemAddView instead
       supermodel: @supermodel
@@ -84,7 +79,14 @@
     @onSystemsChanged() if insertedDefaults

   onSystemsChanged: (e) =>
-    @level.set 'systems', @systemsTreema.data
+    systems = @getSortedByName @systemsTreema.data
+    @level.set 'systems', systems
+
+  getSortedByName: (systems) =>
+    systemModels = @supermodel.getModels LevelSystem
+    systemModelMap = {}
+    systemModelMap[sys.get('original')] = sys.get('name') for sys in systemModels
+    _.sortBy systems, (sys) -> systemModelMap[sys.original]

   onSystemSelected: (e, selected) =>
     selected = if selected.length > 1 then selected[0].getLastSelectedTreema() else selected[0]


Avoid starting a watch-initiated compile if one is already in-progress.

--- development/watcher.coffee
+++ development/watcher.coffee
@@ -8,11 +8,15 @@
 module.exports = (project_directory, build_directory, options, project_config) ->
     SDKError.log("Watching for changes: #{ SDKError.formatProjectPath(project_directory) }")

+    is_compiling_assets = false
+    is_compiling_site = false
+
     # Selectively watch to avoid EMFILE errors.
     watch_targets = fs.readdirSync(project_directory).filter (f) ->
         f not in ['assets', 'node_modules'] and f[0] isnt '.'

     _doAssets = (file_name) ->
+        is_compiling_assets = true
         compileAssets
             project_directory   : project_directory
             build_directory     : build_directory
@@ -22,13 +26,16 @@
             callback: ->
                 file_counts = SDKError.colors.green("#{ compileAssets.files_emitted.length } assets")
                 SDKError.log("#{ file_counts } generated in #{ SDKError.formatProjectPath(project_directory, build_directory) }")
+                is_compiling_assets = false

     _doFiles = (file_name) ->
         ext = file_name.split('.').pop()
         if ext in ['cjsx', 'coffee', 'html']
+            is_compiling_site = true
             runCompilation project_directory, options, (files, assets) ->
                 file_counts = SDKError.colors.green("#{ files.length } files, #{ assets.length } assets")
                 SDKError.log("#{ file_counts } generated in #{ SDKError.formatProjectPath(project_directory, build_directory) }")
+                is_compiling_site = false
         else if ext in ['sass']
             _doAssets(file_name)

@@ -40,8 +47,10 @@
                 delete require.cache[key]

     watch path.join(project_directory, 'assets'), (file_name) ->
-        _handleChange(file_name)
-        _doAssets(file_name)
+        unless is_compiling_assets
+            _handleChange(file_name)
+            _doAssets(file_name)
     watch watch_targets, (file_name) ->
-        _handleChange(file_name)
-        _doFiles(file_name)
+        unless is_compiling_assets or is_compiling_site
+            _handleChange(file_name)
+            _doFiles(file_name)


basic testing

basic testing

--- jsapp/test/unit/xlform/integration.tests.coffee
+++ jsapp/test/unit/xlform/integration.tests.coffee
@@ -162,6 +162,13 @@
         ,text,q4,Question4
         """

+      it 'can view group settings', ->
+        $groupEl = @div.find('.group').eq(0)
+        $groupEl.find('.js-toggle-group-settings').click()
+        $groupSettings = $groupEl.find('.card__settings').eq(0)
+        expect($groupSettings.find('.xlf-dv-name').find('input').length).toBe(1)
+        ``
+
       it 'can group all rows and groups together', ->
         firstLevelRows = @div.find('.survey-editor__list > .survey__row')
         expect(firstLevelRows.length).toBe(5)


update settings to allow mongo host and remove unused health check userid

--- services/notifications/config/settings.defaults.coffee
+++ services/notifications/config/settings.defaults.coffee
@@ -5,10 +5,4 @@
 			host: process.env["LISTEN_ADDRESS"] or "localhost"

 	mongo:
-		url : "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex"
-
-	notifications:
-		healthCheck:
-			user_id: "5620bece05509b0a7a3cbc62"
-
-
+		url: process.env['MONGO_CONNECTION_STRING'] or "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex"


removed debugger statement from events.js.coffee

--- app/assets/javascripts/events.js.coffee
+++ app/assets/javascripts/events.js.coffee
@@ -27,7 +27,6 @@
     checked = []
     startTime = new Date(data[3])
     status = data[7]
-    debugger
     $('.event-filter').each ->
       $this = $(this)
       if $this.is(':checked')


WKTReader: constructor supports geometryFactories

--- test/wktreader-test.coffee
+++ test/wktreader-test.coffee
@@ -5,6 +5,8 @@

 WKTReader = (require "../geos").WKTReader
 Geometry = (require "../geos").Geometry
+GeometryFactory = (require "../geos").GeometryFactory
+PrecisionModel = (require "../geos").PrecisionModel

 tests = (vows.describe "WKTReader").addBatch

@@ -26,4 +28,20 @@
     "should throw an exception on malformed WKT": (reader) ->
       assert.throws reader.read, Error

+  "A WKTReader with non standard GeometryFactory":
+    topic: ->
+      new WKTReader new GeometryFactory new PrecisionModel "FIXED"
+
+    "a new instance should be an instance of WKTReader": (reader) ->
+      assert.instanceOf reader, WKTReader
+
+    "should have a read function": (reader) ->
+      assert.isFunction reader.read
+
+    "should read a WKT POINT(1 1)": (reader) ->
+      geom = reader.read "POINT(1 1)"
+      assert.instanceOf geom, Geometry
+      assert.equal geom.toString(), "POINT (1.0 1.0)"
+
+
 tests.export module


Add support for shallow collection to the index

--- index.coffee
+++ index.coffee
@@ -34,7 +34,7 @@
 # It scans the supplied directory, find all the modules there and
 # return an object, where keys are names of modules minus the ext.
 # This is used to build up entire module hierarchy of the framework.
-collectModules = (directory) ->
+collectModules = (directory, shallow) ->
     supported = [".coffee", ".js"]
     ext = (name) -> paths.extname name
     sym = (name) -> paths.basename name, ext name
@@ -43,6 +43,7 @@
     ingest = (x) -> require "#{directory}/#{x}"
     return {} unless fs.existsSync directory
     scanSync = wrench.readdirSyncRecursive
+    scanSync = fs.readdirSync if shallow
     scanned = scanSync directory.toString()
     supported = _.filter scanned, isSupported
     modules = _.map supported, ingest


do NOT list hidden/dotted files

do NOT list hidden/dotted files

--- index.coffee
+++ index.coffee
@@ -38,7 +38,7 @@
       res.status(200).set('Content-Type', 'text/plain').send()
       return
     maxListCount = config.maxListCount + 1
-    shCmd = "ls -tlA | head -#{maxListCount} | tail -n +2 | tr -s ' ' | cut -d' ' -f6,7,8"
+    shCmd = "ls -tl | head -#{maxListCount} | tail -n +2 | tr -s ' ' | cut -d' ' -f6,7,8"
     execOptions = {cwd: "#{__dirname}/tastes/"}
     execFile '/bin/sh', ['-c', shCmd], execOptions, (err, stdout, stderr) ->
       return next err  if err?


Added more advanced typing

Added more advanced typing

--- index.coffee
+++ index.coffee
@@ -6,18 +6,10 @@
 frame = new Frame process
 stdin.resume();

-frame
-  .save 'screen'
-  .format 'none'
-  .erase()
-  .pos stdout.rows, 0
-  .background 'black'
-  .color 'black', 'white'
-  .fillLine()
-  .pos stdout.rows+1, 0
-  .write '> '
+frame.build()

-typed = 0
+typed = ''
+previous = ''
 frame.on 'input', (data, rawdata) ->
   if rawdata[0] is 127
     if typed isnt 0
@@ -25,12 +17,22 @@
         .set '[1D'
         .write ' '
         .set '[1D'
-        typed--
+        typed = typed.slice(0, -1)
   else if rawdata[0] is 13
-
+    frame.prompt()
+    previous = typed
+    typed = ''
+  else if rawdata[0] is 27
+    if rawdata[1] is 91 and rawdata[2] is 65
+      frame.write previous
+      typed = previous
+    else if rawdata[2] is 66
+    else
+      frame.write data
+      typed++
   else
     frame.write data
-    typed++
+    typed += data

 frame.on 'exit', (data) ->
   frame


Fix image deletion.

Fix image deletion.

--- app/assets/javascripts/images.js.coffee
+++ app/assets/javascripts/images.js.coffee
@@ -43,8 +43,7 @@
 deleteSelected = ->
   $('.selected').each((i, item) ->
     $item = $(item).find('a')
-    destroyUrl = "#{albumUrl()}/images/#{$item.data('image-id')}"
-    $.post(destroyUrl, _method: 'delete')
+    $.post("/images/#{$item.data('image-id')}", _method: 'delete')
     $item.parent().fadeOut(500).remove()
   )
   cleanupDelete()


Parsing passed file

Parsing passed file

Signed-off-by: Michael Granados <ee1a82c99aeec5ba2ac924a7ea98b5fc2f1f3865@gmail.com>

--- index.coffee
+++ index.coffee
@@ -1 +1,27 @@
+#!/usr/bin/env coffee
+
+fs = require "fs"
 aglio = require "aglio"
+Remarkable = require "remarkable"
+
+file = "#{process.cwd()}/#{process.argv[2]}"
+blueprint = fs.readFileSync(file).toString()
+
+highlight = (content) ->
+    """<ac:structured-macro ac:name="code"><ac:parameter ac:name="title">Headers</ac:parameter><ac:plain-text-body><![CDATA[#{content}]]></ac:plain-text-body></ac:structured-macro>"""
+
+md = new Remarkable 'full', { html: true, linkify: true, typographer: true, highlight }
+
+options =
+    includePath: process.cwd()
+    template: "#{__dirname}/template.jade"
+    locals: {
+        highlight: highlight,
+        markdown: (content) ->
+            md.render content
+    }
+
+aglio.render blueprint, options, (err, html, warnings) ->
+    if err
+    	return console.log "ERRO: #{err.message}"
+    console.log html


better comments on `visiblity` API usage

--- index.coffee
+++ index.coffee
@@ -107,9 +107,10 @@
 			win.off 'scroll', @onInViewportScroll
 			win.off 'resize', @onInViewportScroll

-		# Public API for invoking visibility test
+		# Public API for invoking visibility tests
+
+		# Check if the element is visible at all in the viewport
 		isInViewport: (el, options) -> visibility.isInViewport(el, options)

-		# Public API for invoking visibility test
-		isInViewportEntirely: (el, options) ->
-			visibility.isFullyVisible el
+		# Check if the elemetn is entirely visible in the viewport
+		isInViewportEntirely: (el, options) -> visibility.isFullyVisible el


fix crash on save

fix crash on save

--- index.coffee
+++ index.coffee
@@ -49,7 +49,7 @@
       filename = config.generate()
       break  unless fs.existsSync(filename)
     relPath = "tastes/#{filename}"
-    saveFile relPath, req, res, (err) ->
+    saveFile relPath, config, req, res, (err) ->
       return next err  if err?
       res.status(201).location("#{filename}").send()

@@ -59,7 +59,7 @@
     relPath = "tastes/#{req.params.filename}"
     fs.exists relPath, (exists) ->
       return res.status(409).send()  if exists
-      saveFile relPath, req, res, (err) ->
+      saveFile relPath, config, req, res, (err) ->
         return next err  if err?
         res.status(204).send()

@@ -68,7 +68,7 @@
   app

-exports.saveFile = (relPath, req, res, next) ->
+exports.saveFile = (relPath, config, req, res, next) ->
   contentType = req.headers['content-type']
   encoding = 'utf-8'
   encoding = mediaTyper.parse(contentType).parameters.charset  if contentType?


paper review route (issue when changing from a link)

--- app/assets/javascripts/router.js.coffee
+++ app/assets/javascripts/router.js.coffee
@@ -4,7 +4,9 @@

   @route    'papers'      , {path:'/papers/:type'}
   @route    'submit_paper', {path:'/submit'}
-  @resource 'paper'       , {path:'/paper/:id'}
+  @resource 'paper'       , {path:'/paper/:paper_id'}
+
+  @resource 'paper_review', {path:'/paper/review/:paper_id'}

   @resource 'index', {path: '/'} , ->
     @route 'about'


updating hostname

updating hostname

--- index.coffee
+++ index.coffee
@@ -51,7 +51,7 @@

 reddish_port = 8000
 reddish_monitor_port = 8001
-reddish_hostname = 'dev.freeflow.io'
+reddish_hostname = 'reddi.sh'

 # setup handshaken and connected state


don't try to scroll unless on the root page (by checking for loader element)

--- app/assets/javascripts/scroll.js.coffee
+++ app/assets/javascripts/scroll.js.coffee
@@ -3,7 +3,7 @@
     height = Math.min($('.scroll-col').eq(0).height(),
                       $('.scroll-col').eq(1).height(),
                       $('.scroll-col').eq(2).height())
-    if not $('#loader').hasClass('loading') and $(window).scrollTop() > height - $(window).height() - 50
+    if $('#loader').length > 0 and not $('#loader').hasClass('loading') and $(window).scrollTop() > height - $(window).height() - 50
       $('#loader').addClass('loading')
       $.getScript('/items')
    $(window).scroll


adds better logout error hook messaging

adds better logout error hook messaging

--- index.coffee
+++ index.coffee
@@ -119,6 +119,11 @@
 	 		url: 'https://clef.io/api/v1/logout'
 	 		form: data
 	 		(err, resp, body) ->
+	 			if err
+	 				msg = "ERROR in clef logout request: #{err}"
+	 				console.log msg
+	 				return v0.response.internalError res, msg
+
 	 			console.log "Clef logout request recieved #{body}"
 	 			try
 			 		userInfo = JSON.parse body


simple patchlevel test

simple patchlevel test

Signed-off-by: Adam Stokes <0a364f4bf549cc82d725fa7fd7ed34404be64079@ubuntu.com>

--- index.coffee
+++ index.coffee
@@ -40,8 +40,7 @@
       if model.patchLevel?
         model.debVersion = "#{model.debVersion}.#{model.patchLevel}"
       model.semVer = false
-      if xre.exec("#{model.major}.#{model.minor}.#{model.patchLevel}",
-        /^(\d+\.\d+\.\d+)/)
+      if model.major? and model.minor? and model.patchLevel?
         model.semVer = true
       model.body = match.body
       model.firstname = match.firstname


travis ェ…

travis ェ…

--- src/coffee/kazitori.coffee
+++ src/coffee/kazitori.coffee
@@ -1,4 +1,4 @@
-# pushState をいい感じに捌けるルーターライブラリ
+# pushState をいい感じに捌けるルーターライブラリ
 # pushState を使ったコンテンツで必要なことはひと通り出来るはず。
 #
 #----------------------------------------------
@@ -35,8 +35,8 @@
 #--------------------------------------------

 ###URL 変数に対して指定できる型###
-# **Default:**
-# int : Number としてキャストされます
+# **Default:**
+# int : Number としてキャストされます
 # string : String としてキャストされます
 #

@@ -239,7 +239,7 @@
     if options.internal and options.internal is true
       @._dispatcher.dispatchEvent( new KazitoriEvent(KazitoriEvent.INTERNAL_CHANGE, next, prev))
     @loadURL(frag, options)
-    return
+    return

   #中断する
   #メソッド名 intercept のほうがいいかな
@@ -261,10 +261,10 @@

     target = if isBefore then @.beforeHandlers else @.handlers
     target.unshift new Rule(rule, (fragment)->
-        args = @_extractParams(fragment)
-        args = @_getCastedParams(args)
-        callback && callback.apply(@.router, args)
-      ,@)
+      args = @_extractParams(fragment)
+      args = @_getCastedParams(args)
+      callback && callback.apply(@.router, args)
+    ,@)
     return @

   #URL を読み込む
@@ -310,7 +310,7 @@
   executeHandlers:()=>
     #毎回 match チェックしてるので使いまわしたいのでリファクタ
     matched = @._matchCheck(@.fragment, @.handlers)
-    if matched.length < 1
+    if matched.length < 1
       if @.notFound isnt null
         #a- 2回呼ばれるので loadURL じゃなくて @.notFound.callback のほうがいいな
         @loadURL(@.notFound)
@@ -371,7 +371,7 @@
   # befores から指定された事前に処理したいメソッドをバインド
   _bindBefores:()->
     if not @.befores?
-      return
+      return
     befores = @_keys(@.befores)
     for key in befores
       @registerHandler(key, @.befores[key], true)
@@ -379,11 +379,11 @@
     if @.beforeAnytime
       callback = @_bindFunctions(@.beforeAnytime)
       @.beforeAnytimeHandler = {
-          callback:@_binder (fragment)->
-            args = [fragment]
-            callback && callback.apply(@, args)
-          ,@
-        }
+        callback:@_binder (fragment)->
+          args = [fragment]
+          callback && callback.apply(@, args)
+        ,@
+      }
     return

@@ -743,7 +743,7 @@
       if @queue.length < 1
         @queue = []
         @.dispatchEvent(new KazitoriEvent(KazitoriEvent.TASK_QUEUE_COMPLETE))
-    catch error
+    catch error
       @reject(error)

   reject:(error)->


Improve picture loading logic

Improve picture loading logic

--- app/assets/javascripts/components/PictureList.js.cjsx
+++ app/assets/javascripts/components/PictureList.js.cjsx
@@ -2,23 +2,33 @@
   request = require('superagent')
   NOW_LOADING_KEY = 'NOW_LOADING_KEY'

-  # Picture
-  # ===========================================================================
-
+  pictureList = document.querySelector("#js-picture-list")
+  loadingImagePath = pictureList.dataset.loadingImagePath
   pictureAttr = (link, src, text, key) ->
     { link: link, src: src, text: text, key: key}

-  pictureList = document.querySelector("#js-picture-list")
+  # Picture
+  # ===========================================================================

   Picture = React.createClass
     propTypes:
       link: React.PropTypes.string
       src: React.PropTypes.string.isRequired
       text: React.PropTypes.string
+    getInitialState: ->
+      src: loadingImagePath
+    componentDidMount: ->
+      img = new Image()
+      img.addEventListener("load", @handleImgLoad)
+      img.src = @props.src
+    handleImgLoad: (e) ->
+      e.target.removeEventListener(e.type, @handleImgLoad)
+      @setState
+        src: @props.src
     render: ->

-
+

{@props.text}

@@ -28,8 +38,7 @@

   PictureList = React.createClass
     getInitialState: ->
-      _loadingImagePath = pictureList.dataset.loadingImagePath
-      _initialAttrList = [pictureAttr(null, _loadingImagePath, 'Now Loading...', NOW_LOADING_KEY)]
+      _initialAttrList = [pictureAttr(null, loadingImagePath, 'Now Loading...', NOW_LOADING_KEY)]
       attrList: _initialAttrList
     appendAttrList: (list) ->
       i = _.findIndex @state.attrList, (item) -> item?.key is NOW_LOADING_KEY


EDSC-596 fixed broken specs

EDSC-596 fixed broken specs

--- app/assets/javascripts/modules/global_hooks.js.coffee
+++ app/assets/javascripts/modules/global_hooks.js.coffee
@@ -43,46 +43,48 @@
     this.setAttribute('href',  "/login?next_point=#{encodeURIComponent(window.location.href)}")

   $(document).ready ->
+    map = $('#map').data('map')?.map
+    if map?
+      map.on 'projectionchange', (e) ->
+        metrics.createMapEvent("Set Projection: #{e.projection}")

-    $('#map').data('map').map.on 'projectionchange', (e) ->
-      metrics.createMapEvent("Set Projection: #{e.projection}")
+      map.on 'basemapchange', (e) ->
+        metrics.createMapEvent("Set Base Map: #{e.name}")

-    $('#map').data('map').map.on 'basemapchange', (e) ->
-      metrics.createMapEvent("Set Base Map: #{e.name}")
+      map.on 'overlayschange', (e) ->
+        overlays = e.overlays
+        names = []
+        for name in overlays
+          names.push(name.split('*')[0])
+        metrics.createMapEvent("Set Overlays: #{names.join(', ')}")

-    $('#map').data('map').map.on 'overlayschange', (e) ->
-      overlays = e.overlays
-      names = []
-      for name in overlays
-        names.push(name.split('*')[0])
-      metrics.createMapEvent("Set Overlays: #{names.join(', ')}")
+      map.on 'spatialtoolchange', (e) ->
+        metrics.createMapEvent("Spatial: #{e.name}")

-    $('#map').data('map').map.on 'spatialtoolchange', (e) ->
-      metrics.createMapEvent("Spatial: #{e.name}")
+      map.on 'edsc.stickygranule', (e) ->
+        metrics.createMapEvent("Selected Granule")

-    $('#map').data('map').map.on 'edsc.stickygranule', (e) ->
-      metrics.createMapEvent("Selected Granule")
+      map.on 'shapefile:start', (e) ->
+        metrics.createMapEvent("Added Shapefile")

-    $('#map').data('map').map.on 'shapefile:start', (e) ->
-      metrics.createMapEvent("Added Shapefile")
-
-    $('#timeline').on 'buttonzoom', (e) ->
+    timeline = $('#timeline')
+    timeline.on 'buttonzoom', (e) ->
       metrics.createTimelineEvent("Button Zoom")

-    $('#timeline').on 'arrowpan', (e) ->
+    timeline.on 'arrowpan', (e) ->
       metrics.createTimelineEvent("Left/Right Arrow Pan")

-    $('#timeline').on 'clicklabel', (e) ->
+    timeline.on 'clicklabel', (e) ->
       metrics.createTimelineEvent("Click Label")

-    $('#timeline').on 'createdtemporal', (e) ->
+    timeline.on 'createdtemporal', (e) ->
       metrics.createTimelineEvent("Created Temporal")

-    $('#timeline').on 'scrollzoom', (e) ->
+    timeline.on 'scrollzoom', (e) ->
       metrics.createTimelineEvent("Scroll Zoom")

-    $('#timeline').on 'scrollpan', (e) ->
+    timeline.on 'scrollpan', (e) ->
       metrics.createTimelineEvent("Scroll Pan")

-    $('#timeline').on 'draggingpan', (e) ->
+    timeline.on 'draggingpan', (e) ->
       metrics.createTimelineEvent("Dragging Pan")


static pages: fix for prerender page url

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -320,7 +320,7 @@

           #setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
           set $prerender "service.prerender.io";
-          rewrite .* /$scheme://$host#{if KONFIG.publicPort is "80" then "" else ":"+KONFIG.publicPort}$request_uri? break;
+          rewrite .* /#{if environment is "dev" then "http" else "https"}://$host#{if KONFIG.publicPort is "80" then "" else ":"+KONFIG.publicPort}?$args? break;
           proxy_pass http://$prerender;
         }


scroll change

scroll change

--- app/assets/javascripts/pinkman_base/controller.coffee
+++ app/assets/javascripts/pinkman_base/controller.coffee
@@ -190,7 +190,7 @@

   scrolling: (callback) ->
     if $("##{@id}").length
-      $(window).scroll ->
+      $("window,.pink-yield").scroll ->
         unless Pinkman._stopScroll
           Pinkman._stopScroll = yes
           callback(window.scrollY)


nginx: remove class A network allowance from internal ips blocks, and fix channel/history location

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -28,7 +28,6 @@
   allow                 127.0.0.1;
         allow                 192.168.0.0/16;
         allow                 172.16.0.0/12;
-        allow                 10.0.0.0/8;
         deny                  all;
 """

@@ -281,7 +280,7 @@
         proxy_connect_timeout 1;
       }

-      location /api/social/channel/(.*)/history {
+      location ~ /api/social/channel/(.*)/history {
         proxy_pass            http://socialapi/channel/$1/history$is_args$args;
         proxy_set_header      X-Real-IP       $remote_addr;
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;


nginx: add x-forwarded-host header for proxied requests

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -43,6 +43,7 @@
         proxy_pass            #{proxyPass};
         proxy_set_header      X-Real-IP       $remote_addr;
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header      Host            $host;
         proxy_next_upstream   error timeout   invalid_header http_500;
         proxy_connect_timeout 1;
         #{if internalOnly then allowInternal else ''}
@@ -64,13 +65,13 @@
         proxy_set_header      Upgrade         $http_upgrade;
         proxy_set_header      Connection      $connection_upgrade;

-        proxy_set_header      Host $host;
-        proxy_set_header      X-Real-IP $remote_addr;
+        proxy_set_header      Host            $host;
+        proxy_set_header      X-Real-IP       $remote_addr;
         proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_redirect        off;

         # Don't buffer WebSocket connections
-        proxy_buffering off;
+        proxy_buffering       off;

         # try again with another upstream if there is an error
         proxy_next_upstream   error timeout   invalid_header http_500;


Add button to reset media

Add button to reset media

--- app/classifier/tasks/survey-editor.cjsx
+++ app/classifier/tasks/survey-editor.cjsx
@@ -19,6 +19,7 @@

   getInitialState: ->
     importErrors: []
+    resettingMedia: false

   getDefaultProps: ->
     workflow: null
@@ -92,10 +93,20 @@

         Survey images{' '}
-        <MediaArea resource={@props.workflow} metadata={prefix: @props.taskPrefix} style={
-          maxHeight: '90vh'
-        } onAdd={@handleImageAdd} onDelete={@handleImageDelete} />
-

+        <AutoSave resource={@props.workflow}>
+          <button type="button" disabled={@state.resettingMedia} onClick={@resetMedia}>Delete all</button>
+          {if @state.resettingMedia
+            }
+        </AutoSave>
+

+
+      <MediaArea
+        resource={@props.workflow}
+        metadata={prefix: @props.taskPrefix}
+        style={maxHeight: '90vh'}
+        onAdd={@handleImageAdd}
+        onDelete={@handleImageDelete}
+      />

@@ -115,6 +126,7 @@
           <button type="button" onClick={@resetTask}>Delete all</button>
         </AutoSave>

+
       {for choiceID in @props.task.choicesOrder
         choice = @props.task.choices[choiceID]

-    # TODO: Removes the links.
-    # TODO: Delete the orphaned resources.
-    # Object.assign @props.task,
-    #   images: {}
-    # @props.workflow.update 'tasks'
-    alert 'TODO.'
+  resetMedia: (e) ->
+    if e.shiftKey or confirm 'Really delete all the images from this task? This might take a while...'
+      @setState resettingMedia: true
+      errors = 0
+      massDelete = @props.workflow.get('attached_images', page_size: 200).then (workflowImages) =>
+        taskImages = workflowImages.filter (image) =>
+          image.metadata.prefix is @props.taskPrefix
+        Promise.all taskImages.map (image) =>
+          image.delete().catch =>
+            errors += 1
+      massDelete.then =>
+        @setState resettingMedia: false
+        if errors isnt 0
+          alert "There were #{errors} errors deleting all those resources. Try again to get the rest."

   handleImportError: (error, file, row) ->
     @state.importErrors.push {error, file, row}


updated clearAlert

updated clearAlert

--- app/coffee/drm-alerts/drm-alerts.coffee
+++ app/coffee/drm-alerts/drm-alerts.coffee
@@ -8,20 +8,22 @@
     constructor: (@alertClass = "drm-dismissable-alert", @speed = 300) ->
         self = @

-        $('html').on 'click', "div.#{@alertClass} button.close", -> self.clearAlert.call @, self.speed
+        $('body').on 'click', "div.#{@alertClass} button.close", (e) ->
+            e.preventDefault()
+            self.clearAlert.call @, self.speed

     showAlert: (type, message, holder) ->
-        className = "drm-#{type}-alert #{@alertClass}"
-        newAlert = $ '

',
+        _className = "drm-#{type}-alert #{@alertClass}"
+        _newAlert = $ '

',
             text: message,
-            class: className
+            class: _className

-        close = $ '<button></button>',
+        _close = $ '<button></button>',
             text: 'x'
             class: 'close'

-        newAlert.prependTo holder
-        close.prependTo newAlert
+        _newAlert.prependTo holder
+        _close.prependTo _newAlert

     clearAlert: (speed) ->
         $(@).parent().fadeOut speed, ->


improve replace emoji regex

improve replace emoji regex

--- app/modules/components/wysiwyg/wysiwyg.service.coffee
+++ app/modules/components/wysiwyg/wysiwyg.service.coffee
@@ -54,7 +54,7 @@
         return html

     replaceEmojiNameByImgs: (text) ->
-        emojiIds = taiga.getMatches(text, /:([^: ]*):/g)
+        emojiIds = taiga.getMatches(text, /:([\w ]*):/g)

         for emojiId in emojiIds
             regexImgs = new RegExp(':' + emojiId + ':', 'g')


make groups always use the combine strategy of their parent layer

fixes an issue that caused AI2ASS to always create at least 2 lines for drawings with more than 2 paths - regardless of the combine strategy and path properties

--- AI2ASS.coffee
+++ AI2ASS.coffee
@@ -95,10 +95,11 @@
         currGroup: null
         emptyPrefix: null

-        makeMergeGroup: ->
+        makeMergeGroup: () ->
           group = {
             dirtyRects: []
             lines: {}
+            layer: @

             addPath: (path, prefix) ->
               unless @isZeroArea path.visibleBounds
@@ -116,7 +117,7 @@
               if path.parent.typename == "CompoundPathItem"
                 return true

-              switch @combineStrategy
+              switch @layer.combineStrategy
                 when "off"
                   return false
                 when "any"
@@ -134,7 +135,6 @@
                   return true
           }

-          group.combineStrategy = @combineStrategy
           return group

         addGroup: ->
@@ -148,9 +148,9 @@
           @currGroup.addPath path, prefix
       }

-      layer.addGroup()
       layer.emptyPrefix = emptyPrefix
       layer.combineStrategy = @combineStrategy
+      layer.addGroup()
       return layer

     process: ( obj, clip, opacity = 100 ) ->


Replace promise with callbacks for UnspentOutputsRestClient

--- app/src/restclients/unspent_outputs_restclient.coffee
+++ app/src/restclients/unspent_outputs_restclient.coffee
@@ -4,15 +4,15 @@
   getUnspentOutputsFromAddresses: (addresses, callback) ->
     addresses = (address for address in addresses when Bitcoin.Address.validate(address) is true)
     query = _(addresses).join(',')
-    @http().get(url: "blockchain/addresses/#{query}/unspents").done( (response) =>
-      l response
-      callback?(response)
-    ).fail (xhr, status, message) =>
-      callback(null, {xhr, status, message})
+    @http().get
+      url: "blockchain/addresses/#{query}/unspents"
+      onSuccess: (response) =>
+        callback?(response)
+      onError: (xhr, status, message) =>
+        callback(null, {xhr, status, message})

   getUnspentOutputsFromPaths: (addressesPaths, callback) ->
     ledger.wallet.pathsToAddresses addressesPaths, (addresses, notFound) =>
-      l addressesPaths, addresses
       if notFound.length == addressesPaths.length
         callback? null, {title: 'Missing addresses', missings: notFound}
       else


add brcypt check for rock translate

add brcypt check for rock translate

--- _source/lib/lib/regex.coffee
+++ _source/lib/lib/regex.coffee
@@ -37,6 +37,9 @@
     return @.isVisa(str) or @.isMastercard(str) or
       @.isAmericanExpress(str) or @.isDiscover(str)

+  isBcryptHash: (str) ->
+    return @.isBcrypt(str)
+

 for regex of Apollos.regex
   _addRegexTestFunction regex


Fix the merge

Fix the merge

--- framer/Components/PageComponent.coffee
+++ framer/Components/PageComponent.coffee
@@ -79,14 +79,14 @@
 		return unless @previousPage
 		@snapToPage(@previousPage)

-		# Modify the previous page stack so we don't end up in a loop
+		# Modify the previous page stack so we don"t end up in a loop
 		@_previousPages = @_previousPages[0..@_previousPages.length-3]

 	addPage: (page, direction="right") ->

-		# We only allow adding pages to the right and bottom for now, because it shouldn't
+		# We only allow adding pages to the right and bottom for now, because it shouldn"t
 		# be hard to insert them in the right order, and if we need to manage that for you
-		# we'd have to change the position of every content layer so the new page fits.
+		# we"d have to change the position of every content layer so the new page fits.
 		# Ergo: too much magic.
 		directions = ["down", "bottom", "south"] + ["right", "east"]

@@ -152,9 +152,11 @@

 		# See if we meet the minimum velocity to scroll to the next page. If not we snap
 		# to the layer closest to the scroll point.
-		if Math.max(Math.abs(velocity.x), Math.abs(velocity.y)) < @velocityThreshold or
-			(@content.draggable._directionLockEnabledX and (@direction == 'right' or @direction == 'left')) or
-			(@content.draggable._directionLockEnabledY and (@direction == 'down' or @direction == 'up'))
+
+		xLock = @content.draggable._directionLockEnabledX and (@direction == "right" or @direction == "left")
+		yLock = @content.draggable._directionLockEnabledY and (@direction == "down" or @direction == "up")
+
+		if Math.max(Math.abs(velocity.x), Math.abs(velocity.y)) < @velocityThreshold or xLock or yLock
 			# print "velocity"
 			@snapToPage(@closestPage, true, @animationOptions)
 			return


Simplified AnsiInterpreter#feed

Simplified AnsiInterpreter#feed

--- app/assets/javascripts/ansi-interpreter.js.coffee
+++ app/assets/javascripts/ansi-interpreter.js.coffee
@@ -136,26 +136,19 @@
       @COMPILED_PATTERNS.push [ regexp, @PATTERNS[re] ]

   feed: (data) ->
-    match = undefined
-    handler = undefined
+    while data.length > 0
+      match = null

-    while data.length > 0
-      match = handler = null
-      i = 0
+      for pattern in @COMPILED_PATTERNS
+        match = pattern[0].exec(data)

-      while i < @COMPILED_PATTERNS.length
-        pattern = @COMPILED_PATTERNS[i]
-        match = pattern[0].exec(data)
         if match
           handler = pattern[1]
+          handler.call(this, data, match)
+          data = data.slice(match[0].length)
           break
-        i++

-      if handler
-        handler.call this, data, match
-        data = data.slice(match[0].length)
-      else
-        break
+      break unless match

     @terminal.render()
     data


Simplified regexp for OSC

Simplified regexp for OSC

--- app/assets/javascripts/ansi-interpreter.js.coffee
+++ app/assets/javascripts/ansi-interpreter.js.coffee
@@ -83,7 +83,7 @@

     "\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)

-    "\x1b\\\x5d[012]\x3b(?:.)*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
+    "\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)

     "\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)


keep styles in comments

keep styles in comments

--- app/assets/javascripts/api/crud_comment.js.coffee
+++ app/assets/javascripts/api/crud_comment.js.coffee
@@ -121,7 +121,7 @@
   else
     abaa.className = "juvia-comment-pure-content"

-  comment_content = $(comment_text).clone().find("script,noscript,style").remove().end().html();
+  comment_content = $(comment_text).clone().find("script,noscript").remove().end().html();
   $(abaa).html comment_content

   aba.appendChild abaa


Reactivated google analytics event tracking.

--- app/assets/javascripts/controllers/node.js.coffee
+++ app/assets/javascripts/controllers/node.js.coffee
@@ -47,7 +47,7 @@

       unless self.get('wheelchairSubmit')
         return
-
+
       data =
         _method: 'PUT'
         wheelchair: self.get('wheelchair')
@@ -61,11 +61,14 @@
         dataType: 'json'

       promise.done (response)->
+        # Track this event, when google analytics is active (Production)
+        _gaq.push ["_trackEvent", "Data", "Tag", self.get("wheelchair")]  if _gaq?
         self.set('oldWheelchair', null)
         self.get('controllers.flash-messages').pushMessage('notice', response.message)
-        # TODO: Sent event to Google Analytics

       promise.fail (response)->
+        # Track this event as failed, when google analytics is active (Production)
+        _gaq.push ["_trackEvent", "Data", "Tag", "failed"]  if _gaq?
         @resetStatus()
         self.get('controllers.flash-messages').pushMessage('error', response.message)


Use standard aac codec. Libfaac not compiled in.

--- app/assets/javascripts/preset_templates.js.coffee
+++ app/assets/javascripts/preset_templates.js.coffee
@@ -4,7 +4,7 @@
 presets = {
   encoder_preset: {
     video: '-f mp4 -vcodec libx264 -profile:v baseline -level:v 3.1 -s @Video size in pixels (e.g. 320x180)@ -b:v @Target bitrate@k -an -maxrate @Max. bitrate@k -minrate @Min. bitrate@k -keyint_min @Min. GOP length in frames@ -g @Max. GOP length in frames@ -threads 4',
-    audio: '-f mp4 -vn -acodec libfaac -ar @Audio samplerate@ -ab @Target bitrate@k -ac @Number of audio channels@ -metadata:s:0 language=@Language of audio track@'
+    audio: '-f mp4 -vn -acodec aac -strict -2 -ar @Audio samplerate@ -ab @Target bitrate@k -ac @Number of audio channels@ -metadata:s:0 language=@Language of audio track@'
   },
   post_processing_preset: {
     generic: '-dash @Segment duration in ms.@ -profile onDemand -rap -frag-rap -url-template -segment-name %s_ -segment-ext mp4'


fix it

fix it

--- graphics/001-terrain-gen/viewer.coffee
+++ graphics/001-terrain-gen/viewer.coffee
@@ -44,9 +44,12 @@
                 context.fillStyle = color(height)
                 context.fillRect(Math.floor(2 * width * col), Math.floor(width * row), 2 * width, width)

+    counter = 1
     viewer.convertImage = ->
         img = new Image()
+        img.id = 'img_' + counter
         img.src = canvas.toDataURL("image/png;base64")
         document.getElementById("image").appendChild(img)
+        counter++

     viewer


adding module tag for docs

adding module tag for docs

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -4,6 +4,9 @@
 @name Selector
 @fieldOf Engine
 @module
+
+@param {Object} I Instance variables
+@param {Object} self Reference to the engine
 ###
 Engine.Selector = (I, self) ->


Adding request defaults to index utils module

--- app/assets/javascripts/admin/index_utils/index_utils.js.coffee
+++ app/assets/javascripts/admin/index_utils/index_utils.js.coffee
@@ -1 +1,3 @@
-angular.module("admin.indexUtils", ['ngResource'])
+angular.module("admin.indexUtils", ['ngResource']).config ($httpProvider) ->
+  $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content");
+  $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*";


publish both or nothing (#2826)

publish both or nothing (#2826)

* publish both or nothing

* pub always

--- .gulp/publishing.iced
+++ .gulp/publishing.iced
@@ -1,33 +1,23 @@
 task 'publish-preview' , 'Publishes the the packages to NPM.',['version-number','build'],  (done)->
-  publish_core = false
-  publish_bootstrapper = false

-  execute 'git show --pretty="" --name-only',{silent:true}, (c,o,e)->
-    publish_core = true if( o.indexOf("src/") >-1 )
-    publish_bootstrapper = true if( o.indexOf("src/autorest/") >-1 )
+  # update core package.json
+  package_path = "#{basefolder}/src/autorest-core/package.json"
+  package_folder = "#{basefolder}/src/autorest-core"
+  package_json = require package_path
+  package_json.version = version
+  JSON.stringify(package_json,null,'  ').to package_path

-    return done() if !publish_core # neither need publishing
+  # publish core package
+  await execute "npm publish --tag preview",{cwd:package_folder, silent:false }, defer c,o,e
+  echo  "\n\nPublished Core:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"

-    # update core package.json
-    package_path = "#{basefolder}/src/autorest-core/package.json"
-    package_folder = "#{basefolder}/src/autorest-core"
-    package_json = require package_path
-    package_json.version = version
-    JSON.stringify(package_json,null,'  ').to package_path
+  # update bootstrapepr package.json
+  package_path = "#{basefolder}/src/autorest/package.json"
+  package_folder = "#{basefolder}/src/autorest"
+  package_json = require package_path
+  package_json.version = version
+  JSON.stringify(package_json,null,'  ').to package_path

-    # publish core package
-    execute "npm publish --tag preview",{cwd:package_folder, silent:false }, (c,o,e) ->
-      echo  "\n\nPublished Core:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"
-
-      return done() if !publish_bootstrapper # doesn't need new bootstrapper
-
-      # update bootstrapepr package.json
-      package_path = "#{basefolder}/src/autorest/package.json"
-      package_folder = "#{basefolder}/src/autorest"
-      package_json = require package_path
-      package_json.version = version
-      JSON.stringify(package_json,null,'  ').to package_path
-
-      execute "npm publish --tag preview",{cwd:package_folder, silent:false }, (c,o,e) ->
-        echo  "\n\nPublished Bootstrapper:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"
-        done()+  await execute "npm publish --tag preview",{cwd:package_folder, silent:false }, defer c,o,e
+  echo  "\n\nPublished Bootstrapper:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"
+  done()


Don't remeasure the first character of a line

Previously we were ignoring the measurement cache for characters located
at `left: 0px` because `0` is evaluated as falsy in Javascript, causing
those character to be constantly re-measured.

This commit fixes it so that we explicitly check for null values when
consulting the cache.

--- src/lines-yardstick.coffee
+++ src/lines-yardstick.coffee
@@ -91,34 +91,34 @@
     lineNode = @lineNodesProvider.lineNodeForScreenRow(row)
     lineId = @lineNodesProvider.lineIdForScreenRow(row)

-    return 0 unless lineNode?
+    if lineNode?
+      if @leftPixelPositionCache[lineId]?[column]?
+        @leftPixelPositionCache[lineId][column]
+      else
+        textNodes = @lineNodesProvider.textNodesForScreenRow(row)
+        textNodeStartColumn = 0
+        for textNode in textNodes
+          textNodeEndColumn = textNodeStartColumn + textNode.textContent.length
+          if textNodeEndColumn > column
+            indexInTextNode = column - textNodeStartColumn
+            break
+          else
+            textNodeStartColumn = textNodeEndColumn

-    if cachedPosition = @leftPixelPositionCache[lineId]?[column]
-      return cachedPosition
+        if textNode?
+          indexInTextNode ?= textNode.textContent.length
+          lineOffset = lineNode.getBoundingClientRect().left
+          if indexInTextNode is 0
+            leftPixelPosition = @clientRectForRange(textNode, 0, 1).left
+          else
+            leftPixelPosition = @clientRectForRange(textNode, 0, indexInTextNode).right
+          leftPixelPosition -= lineOffset

-    textNodes = @lineNodesProvider.textNodesForScreenRow(row)
-    textNodeStartColumn = 0
-
-    for textNode in textNodes
-      textNodeEndColumn = textNodeStartColumn + textNode.textContent.length
-      if textNodeEndColumn > column
-        indexInTextNode = column - textNodeStartColumn
-        break
-      else
-        textNodeStartColumn = textNodeEndColumn
-
-    if textNode?
-      indexInTextNode ?= textNode.textContent.length
-      lineOffset = lineNode.getBoundingClientRect().left
-      if indexInTextNode is 0
-        leftPixelPosition = @clientRectForRange(textNode, 0, 1).left
-      else
-        leftPixelPosition = @clientRectForRange(textNode, 0, indexInTextNode).right
-      leftPixelPosition -= lineOffset
-
-      @leftPixelPositionCache[lineId] ?= {}
-      @leftPixelPositionCache[lineId][column] = leftPixelPosition
-      leftPixelPosition
+          @leftPixelPositionCache[lineId] ?= {}
+          @leftPixelPositionCache[lineId][column] = leftPixelPosition
+          leftPixelPosition
+        else
+          0
     else
       0


promise status is only as wide as the graph

--- app/compat.coffee
+++ app/compat.coffee
@@ -13,7 +13,7 @@

 requireables = q: Q, _: _, moment: moment, colors: colors

-compat = modules.export exports, 'compat', ({fn, doc} ) ->
+compat = modules.export exports, 'compat', ({doc, component_fn} ) ->
   doc 'graph',
     'Loads and graphs time-series data'
     """
@@ -210,17 +210,17 @@
     ```
     """

-  fn 'graph', (args...) ->
+  component_fn 'graph', (ctx, args...) ->
     if Q.isPromise args[0]
       data_promise = args[0]
-      params = Bacon.combineTemplate _.extend {}, @options(), args[1]
+      params = Bacon.combineTemplate _.extend {}, ctx.options(), args[1]
     else
-      graphite_params = graphite.args_to_params {args, default_options: @options()}
+      graphite_params = graphite.args_to_params {args, default_options: ctx.options()}
       params = Bacon.constant graphite_params
       data_promise = graphite.get_data graphite_params

     data = Bacon.fromPromise(data_promise)
-    @add_component React.DOM.div null,
+    React.DOM.div {style: {width: '-webkit-min-content'}},
       graph.create_component(data, params),
       builtins.PromiseStatusComponent {promise: data_promise, start_time: new Date}


Remove nested subtasks Closes #35

Remove nested subtasks
Closes #35

--- app/config.coffee
+++ app/config.coffee
@@ -45,16 +45,15 @@
         subtasks: [{
           label: 'Related health issue or hazard'
           value: 'issue'
-          subtasks: [{
-            label: 'People affected'
-            value: 'who'
-          },{
-            label: 'The cause of the problem'
-            value: 'cause'
-          },{
-            label: 'Action taken'
-            value: 'action'
-          }]
+        },{
+          label: 'People affected'
+          value: 'who'
+        },{
+          label: 'The cause of the problem'
+          value: 'cause'
+        },{
+          label: 'Action taken'
+          value: 'action'
         },{
           label: 'Place or location'
           value: 'place'
@@ -94,16 +93,15 @@
         subtasks: [{
           label: 'Related health issue or hazard'
           value: 'issue'
-          subtasks: [{
-            label: 'People affected'
-            value: 'who'
-          },{
-            label: 'The cause of the problem'
-            value: 'cause'
-          },{
-            label: 'Action taken'
-            value: 'action'
-          }]
+        },{
+          label: 'People affected'
+          value: 'who'
+        },{
+          label: 'The cause of the problem'
+          value: 'cause'
+        },{
+          label: 'Action taken'
+          value: 'action'
         },{
           label: 'Place or location'
           value: 'place'
@@ -119,16 +117,15 @@
         subtasks: [{
           label: 'Related health issue or hazard'
           value: 'issue'
-          subtasks: [{
-            label: 'People affected'
-            value: 'who'
-          },{
-            label: 'The cause of the problem'
-            value: 'cause'
-          },{
-            label: 'Action taken'
-            value: 'action'
-          }]
+        },{
+          label: 'People affected'
+          value: 'who'
+        },{
+          label: 'The cause of the problem'
+          value: 'cause'
+        },{
+          label: 'Action taken'
+          value: 'action'
         },{
           label: 'Place or location'
           value: 'place'


Fix spec.

Fix spec.

--- spec/element_spec.coffee
+++ spec/element_spec.coffee
@@ -43,7 +43,7 @@

   it 'throw', ->
     e = new Element "box"
-    expect(-> e.position = 'one two three').toThrow "Invalid argument"
+    expect(-> e.position = 'one two three').toThrow "Invalid position argument"

   it 'should get xml', ->
     e = new Element 'box'


:green_heart: Fix main spec boilerplate.

--- spec/gamboge-spec.coffee
+++ spec/gamboge-spec.coffee
@@ -16,18 +16,19 @@
 {WorkspaceView} = require 'atom'

 describe "Gamboge", ->
-  activationPromise = null
+  [workspaceView] = []

   beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    activationPromise = atom.packages.activatePackage('gamboge')
+    atom.workspaceView = workspaceView = new WorkspaceView
+    atom.workspaceView.attachToDom()

-  describe "when the gamboge:suggest event is triggered", ->
-    it "shows the completion panel"
+    waitsForPromise ->
+      atom.packages.activatePackage('gamboge')

-  describe "when the gamboge:show-ghost-text is triggered", ->
-    it 'should add ghost text to the editor'
+    runs ->
+      atom.workspaceView.simulateDomAttachment()
+
   describe "when the gamboge:complete is triggered", ->
-    it 'should add the first ghost-text token to the buffer'
+    it 'should insert the first suggested token to the buffer'
   describe "when the gamboge:complete-all is triggered", ->
-    it 'should add *all* of the ghost-text tokens to the buffer'
+    it 'should add *all* of suggested tokens to the buffer'


Add tests for latexmk::constructArgs

Add tests for latexmk::constructArgs

--- spec/latexmk-spec.coffee
+++ spec/latexmk-spec.coffee
@@ -6,8 +6,29 @@
       return

   describe "constructArgs", ->
-    xit "...", ->
-      return
+    it "produces default arguments when package has default config values", ->
+      filePath = "/foo/bar.tex"
+      expectedArgs = [
+        "-interaction=nonstopmode"
+        "-f"
+        "-cd"
+        "-pdf"
+        "-pdflatex=\"pdflatex -synctex=1 -file-line-error %O %S\""
+        "\"#{filePath}\""
+      ]
+      args = latexmk.constructArgs(filePath)
+      expect(args).toEqual(expectedArgs)
+
+    it "adds -shell-escape flag when package config value is set", ->
+      spyOn(atom.config, "get").andCallFake (key) ->
+        return true if key == "latex.enableShellEscape"
+        return null
+
+      filePath = "/foo/bar.tex"
+      expectedArg = "-pdflatex=\"pdflatex -synctex=1 -file-line-error
+        -shell-escape %O %S\""
+      arg = latexmk.constructArgs(filePath)?.splice(-2, 1)[0]
+      expect(arg).toEqual(expectedArg)

   describe "constructPath", ->
     beforeEach ->


don't fetch all proposals individually on live refresh...just /proposals

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -10,18 +10,24 @@

 window.TRANSITION_SPEED = 700   # Speed of transition from results to crafting (and vice versa)

-window.LIVE_UPDATE_INTERVAL = 3000 * 60
+window.LIVE_UPDATE_INTERVAL = 3 * 60 * 1000

 # live updating
 setInterval ->
   dependent_keys = []
+  proposals = false
   for key of arest.components_4_key.hash
     if key[0] == '/' && arest.components_4_key.get(key).length > 0 && \
-       !key.match(/\/(user|opinion|point)\//)
-
-      if key != '/current_user' || fetch('/current_user').logged_in
+       !key.match(/\/(user|opinion|point|proposals)\//)
+
+      if key.match(/\/proposal\//)
+        proposals = true
+
+      else if key != '/current_user' || fetch('/current_user').logged_in
         arest.serverFetch(key)
         #console.log "FETCHING #{key}"
+  if proposals
+    arest.serverFetch('/proposals')

 , LIVE_UPDATE_INTERVAL


Remove Firebase externs, add Facebook externs.

--- app/client/js/externs.coffee
+++ app/client/js/externs.coffee
@@ -14,22 +14,26 @@

 # Note these classes are for externs and does not have to match real classes.
 class appUserStore
-  appUserStore::songs
   appUserStore::newSong
   appUserStore::publishedSongs
-  # User.
+  appUserStore::songs
   appUserStore::user
-  # User props and nested props.
-  appUserStore::displayName
-  appUserStore::id
-  appUserStore::provider
-  appUserStore::thirdPartyUserData
-  appUserStore::picture
-  appUserStore::data
-  appUserStore::url
-  appUserStore::uid
-  appSongsSong::createdAt
-  appSongsSong::updatedAt
+
+class appUser
+  appUser::email
+  appUser::id
+  appUser::name
+  appUser::providers
+  # Facebook
+  appUser::facebook
+  appUser::first_name
+  appUser::gender
+  appUser::last_name
+  appUser::link
+  appUser::locale
+  appUser::timezone
+  appUser::updated_time
+  appUser::verified

 class appContactFormMessage
   appContactFormMessage::message
@@ -50,6 +54,14 @@
   appSongsSong::language
   appSongsSong::lyricsForSearch
   appSongsSong::updatedAt
+
+class facebookApi
+  facebookApi::FB
+  facebookApi::api
+  facebookApi::fbAsyncInit
+  facebookApi::getLoginStatus
+  facebookApi::init
+  facebookApi::status

 class serverClientData
   serverClientData::app
@@ -105,263 +117,3 @@
 class nodeCld
   nodeCld::detect
   nodeCld::languages
-
-# TODO: Move to separate externs repo. Use typedef.
-
-class Firebase
-
-  ###*
-    @param {string} firebaseURL
-    @constructor
-  ###
-  constructor: (firebaseURL) ->
-
-  @ServerValue:
-    TIMESTAMP: null
-
-  ###*
-    @param {string} authToken
-    @param {Function=} onComplete
-    @param {Function=} onCancel
-  ###
-  auth: (authToken, onComplete, onCancel) ->
-
-  unauth: ->
-
-  ###*
-    @param {string} childPath
-    @return {Firebase}
-  ###
-  child: (childPath) ->
-
-  ###*
-    @return {Firebase}
-  ###
-  parent: ->
-
-  ###*
-    @return {string}
-  ###
-  name: ->
-
-  ###*
-    @param {(Object|string|number|boolean)} value
-    @param {Function=} onComplete
-  ###
-  set: (value, onComplete) ->
-
-  ###*
-    @param {Object} value
-    @param {Function=} onComplete
-  ###
-  update: (value, onComplete) ->
-
-  ###*
-    @param {Function=} onComplete
-  ###
-  remove: (onComplete) ->
-
-  ###*
-    @param {(Object|string|number|boolean)=} value
-    @param {Function=} onComplete
-    @return {Firebase}
-  ###
-  push: (value, onComplete) ->
-
-  ###*
-    @param {(Object|string|number|boolean)} value
-    @param {(string|number|Object)} priority
-    @param {Function=} onComplete
-  ###
-  setWithPriority: (value, priority, onComplete) ->
-
-  ###*
-    @param {(string|number|Object)} priority
-    @param {Function=} onComplete
-  ###
-  setPriority: (priority, onComplete) ->
-
-  ###*
-    @param {Function} updateFunction
-    @param {Function=} onComplete
-    @param {Function=} applyLocally
-  ###
-  transaction: (updateFunction, onComplete, applyLocally) ->
-
-  goOffline: ->
-
-  goOnline: ->
-
-  # TODO: This should be Firebase Query interface or base class.
-  # Now everything is externed on Firebase class. Use more accurate annotations.
-
-  ###*
-    @param {string} eventType
-    @param {function(FirebaseDataSnapshot)} callback
-    @param {Function=} cancelCallback
-    @param {Object=} context
-    @return {Function}
-  ###
-  on: (eventType, callback, cancelCallback, context) ->
-
-  ###*
-    @param {string=} eventType
-    @param {Function=} callback
-    @param {Object=} context
-    @return {Function}
-  ###
-  off: (eventType, callback, context) ->
-
-  ###*
-    @param {string} eventType
-    @param {function(FirebaseDataSnapshot)} successCallback
-    @param {Function=} failureCallback
-    @param {Object=} context
-    @return {Function}
-  ###
-  once: (eventType, successCallback, failureCallback, context) ->
-
-  ###*
-    @param {number} limit
-    @return {Firebase} Actually it returns Query
-  ###
-  limit: (limit) ->
-
-  ###*
-    @param {(string|number)=} priority
-    @param {(string|number)=} name
-    @return {Firebase} Actually it returns Query
-  ###
-  startAt: (priority, name) ->
-
-  ###*
-    @param {(string|number)=} priority
-    @param {(string|number)=} name
-    @return {Firebase} Actually it returns Query
-  ###
-  endAt: (priority, name) ->
-
-  ###*
-    @param {(string|number)=} priority
-    @param {(string|number)=} name
-    @return {Firebase} Actually it returns Query
-  ###
-  equalTo: (priority, name) ->
-
-  ###*
-    Get a Firebase reference to the Query's location.
-    @return {Firebase}
-  ###
-  ref: ->
-
-  ###*
-    Get a Firebase reference to the Query's location.
-    @return {Firebase}
-  ###
-  onDisconnect: ->
-
-class FirebaseSimpleLogin
-
-  ###*
-    @param {Firebase} ref
-    @param {Function} callback
-    @param {Object=} context
-    @constructor
-  ###
-  constructor: (ref, callback, context) ->
-
-  ###*
-    @param {string} provider
-    @param {Object=} options
-  ###
-  login: (provider, options) ->
-
-  logout: ->
-
-  ###*
-    @param {string} email
-    @param {string} password
-    @param {Function=} callback
-  ###
-  createUser: (email, password, callback) ->
-
-  ###*
-    @param {string} email
-    @param {string} oldPassword
-    @param {string} newPassword
-    @param {Function} callback
-  ###
-  changePassword: (email, oldPassword, newPassword, callback) ->
-
-  ###*
-    @param {string} email
-    @param {Function} callback
-  ###
-  sendPasswordResetEmail: (email, callback) ->
-
-  ###*
-    @param {string} email
-    @param {string} password
-    @param {Function} callback
-  ###
-  removeUser: (email, password, callback) ->
-
-class FirebaseDataSnapshot
-
-  ###*
-    @constructor
-  ###
-  constructor: ->
-
-  ###*
-    @return {(Object|String|Number|Boolean|Null)}
-  ###
-  val: ->
-
-  ###*
-    @param {string} childPath
-    @return {FirebaseDataSnapshot}
-  ###
-  child: (childPath) ->
-
-  ###*
-    @param {function(FirebaseDataSnapshot): boolean} childAction
-    @return {boolean}
-  ###
-  forEach: (childAction) ->
-
-  ###*
-    @param {string} childPath
-    @return {boolean}
-  ###
-  hasChild: (childPath) ->
-
-  ###*
-    @return {boolean}
-  ###
-  hasChildren: ->
-
-  ###*
-    @return {string}
-  ###
-  name: ->
-
-  ###*
-    @return {number}
-  ###
-  numChildren: ->
-
-  ###*
-    @return {Firebase}
-  ###
-  ref: ->
-
-  ###*
-    @return {(string|number|null)}
-  ###
-  getPriority: ->
-
-  ###*
-    @return {Object}
-  ###
-  exportVal: ->


Switch to async.waterfall

Switch to async.waterfall

--- app/controllers/repos.coffee
+++ app/controllers/repos.coffee
@@ -5,48 +5,81 @@
   return ({app, initPlugins}) ->
     router.use auth.ensureAuthenticated

-    sendErr = (res, msg) ->
-      res.send {error: msg}
+    checkRepoNotActive = ({repoId, sourceProviderName, userId}) -> (callback) ->
+      ActiveRepo.findOne {repoId, sourceProviderName, userId}, (err, activeRepo) ->
+        return callback(err) if err?
+        return callback(new Error "Repo #{activeRepo.repoId} is already active.") if activeRepo?
+      callback null
+
+    checkRepoActive = ({repoId, sourceProviderName, userId}) -> (callback) ->
+      ActiveRepo.findOne {repoId, sourceProviderName, userId}, (err, activeRepo) ->
+        return callback(err) if err?
+        return callback(new Error "Repo #{activeRepo.repoId} repo is not active.") if not activeRepo?
+      callback null
+
+    getSourceProviderSync = (sourceProviderName) -> _.findWhere initPlugins.sourceProviders, {NAME: sourceProviderName}
+
+    checkSourceProviderExists = ({sourceProviderName}) -> (callback) ->
+      sourceProvider = getSourceProviderSync(sourceProviderName)
+      return callback(new Error "Source provider plugin named #{sourceProviderName} does not exist.") if not sourceProvider?
+      callback null
+
+    activateRepo = ({userObject, sourceProviderName, repoId}) -> (callback) ->
+      getSourceProviderSync(sourceProviderName).activateRepo userObject, repoId, (err) -> callback(err)
+
+    deactivateRepo = ({userObject, sourceProviderName, repoId}) -> (callback) ->
+      getSourceProviderSync(sourceProviderName).deactivateRepo userObject, repoId, (err) -> callback(err)
+
+    addRepoToDatabase = ({repoId, sourceProviderName, userId}) -> (callback) ->
+      newActiveRepo = new ActiveRepo {repoId, sourceProviderName, userId}
+      newActiveRepo.save callback
+
+    removeRepoFromDatabase = ({userObject, sourceProviderName, repoId}) -> (callback) ->
+      ActiveRepo.findOneAndRemove {repoId, sourceProviderName, userId}, callback
+
+    cloneRepo = ({userObject, sourceProviderName, repoId}) -> (activeRepoWithId, callback) ->
+      sourceProvider = getSourceProviderSync(sourceProviderName)
+      repoPath = repoPathFor activeRepoWithId
+      cloneUrl = sourceProvider.cloneUrl(req.user, activeRepoWithId)
+      sshKeypath = config[sourceProviderName]?.SSH_KEYPATH
+      gitCommand = if sshKeypath? then "sh #{path.join config.server.ROOT, 'scripts/git.sh'} -i #{sshKeypath}" else 'git'
+      console.log ("preparing to clone #{repoId} with command: " + gitCommand)
+      cloneInto {repoPath, cloneUrl, gitCommand}, (err) -> callback(err)
+
+    deleteRepoFiles = (removedRepo, callback) ->
+      repoPath = repoPathFor removedRepo
+      del [repoPath], callback
+
+    finishRequest = (res, success) -> (err) ->
+      return res.send({error: err.message}) if err?
+      res.send {success}

     # TODO change to PUT and do some AJAX
     router.get '/activate/:sourceProviderName/:repoId', (req, res) ->
       req.setTimeout(5 * 60 * 1000)
       {sourceProviderName, repoId} = req.params
       userId = req.user._id
+      contextObject = {sourceProviderName, repoId, userId, userObject: req.user}

-      # TODO: async.waterfall()
-      ActiveRepo.findOne {repoId, sourceProviderName, userId}, (repoFindError, activeRepo) ->
-        return sendErr(res, repoFindError.message) if repoFindError?
-        return sendErr(res, 'That repo is already active.') if activeRepo?
-        sourceProvider = _.findWhere initPlugins.sourceProviders, {NAME: sourceProviderName}
-        sourceProvider.activateRepo req.user, repoId, (activateRepoError) ->
-          return sendErr(res, activateRepoError.message) if activateRepoError
-          newActiveRepo = new ActiveRepo {repoId, sourceProviderName, userId}
-          newActiveRepo.save (saveRepoError, activeRepoWithId) ->
-            return sendErr(res, saveRepoError.message) if saveRepoError?
-            repoPath = repoPathFor activeRepoWithId
-            cloneUrl = sourceProvider.cloneUrl(req.user, activeRepoWithId)
-            sshKeypath = config[sourceProviderName]?.SSH_KEYPATH
-            gitCommand = if sshKeypath? then "sh #{path.join config.server.ROOT, 'scripts/git.sh'} -i #{sshKeypath}" else 'git'
-            console.log ('preparing t clone with ' + gitCommand)
-            cloneInto {repoPath, cloneUrl, gitCommand}, (cloneIntoError) ->
-              return sendErr(res, cloneIntoError.message) if cloneIntoError?
-              res.send {success: 'Successfully activated repository.'}
+      async.waterfall [
+        checkSourceProviderExists(contextObject),
+        checkRepoNotActive(contextObject),
+        addRepoToDatabase(contextObject),
+        cloneRepo(contextObject),
+        activateRepo(contextObject)
+      ], finishRequest(res, 'Successfully activated repository.')

     router.get '/deactivate/:sourceProviderName/:repoId', (req, res, next) ->
       {sourceProviderName, repoId} = req.params
       userId = req.user._id
+      contextObject = {sourceProviderName, repoId, userId, userObject: req.user}

-      ActiveRepo.findOne {repoId, sourceProviderName, userId}, (repoFindError, activeRepo) ->
-        return sendErr(res, repoFindError.message) if repoFindError?
-        return sendErr(res, 'That repo is not active.') if not activeRepo?
-        sourceProvider = _.findWhere initPlugins.sourceProviders, {NAME: sourceProviderName}
-        sourceProvider.deactivateRepo req.user, req.params.repoId, (deactivateRepoError) ->
-          return sendErr(res, deactivateRepoError.message) if deactivateRepoError
-          ActiveRepo.findOneAndRemove {repoId, sourceProviderName, userId}, (findOneAndRemoveError, removedRepo) ->
-            repoPath = repoPathFor removedRepo
-            del.sync [repoPath]
-            return sendErr(res, findOneAndRemoveError.message) if findOneAndRemoveError
-            res.send {success: 'Deactivated repository.'}
+      async.waterfall [
+        checkSourceProviderExists(contextObject),
+        checkRepoActive(contextObject),
+        deactivateRepo(contextObject),
+        removeRepoFromDatabase(contextObject),
+        deleteRepoFiles
+      ], finishRequest(res, 'Deactivated repository.')

     app.use '/repos', router


Strip the .gz extension to work around XTK hidden assumption.

--- app/javascripts/image.coffee
+++ app/javascripts/image.coffee
@@ -26,12 +26,12 @@
     #
     # @param parent the image parent container
     # @param filename the image file path, relative to the web app root
-    # @param time_point the series time point
+    # @param timePoint the series time point
     # @returns a new image object
-    create = (parent, filename, time_point) ->
+    create = (parent, filename, timePoint) ->
       parent: parent
       filename: filename
-      time_point: time_point
+      timePoint: timePoint

       # The image state loading flag is true if the file is being
       # loaded, false otherwise.
@@ -45,32 +45,44 @@
       # The image state loading flag is set to true while the
       # file is read.
       #
-      # @returns a promise which resolves to this image when
-      #   the image file file content is loaded into the data
-      #   property
+      # @returns a promise which resolves when the image file
+      #   read is completed
       load: () ->
         # Set the loading flag.
         @state.loading = true
-        # Read the file. The Coffeescript fat arrow (=>) binds the
-        # this variable to the image object rather than the $http
-        # request.
+        # Read the file into an ArrayBuffer. The Coffeescript fat
+        # arrow (=>) binds the this variable to the image object
+        # rather than the $http request.
         File.read(filename).then (data) =>
           # Unset the loading flag.
           @state.loading = false
           # Set the data property to the file content.
           @data = data
-          # Return the image.
-          this

       # Builds an XTK renderer for this image.
-      createRenderer: =>
+      createRenderer: ->
         # The XTK renderer.
         renderer = new X.renderer3D()
         renderer.init()

         # The volume to render.
         volume = new X.volume()
-        volume.file = @filename
+        # XTK seems to expect a .gz filename to have compressed
+        # data. However, Express and most servers uncompress
+        # content on the fly. Therefore, fool XTK into accepting
+        # the file content by stripping the .gz extension.
+        # TODO - is there a better approach?
+        if @filename[-3..] == '.gz'
+          volume.file = @filename[0..-3]
+        else
+          volume.file = @filename
+        # FIXME - XTK throws the following:
+        #   Uncaught RangeError: byte length of Uint16Array should be a multiple of 2
+        # This occurs whether or not the filedata property is set
+        # below.
+        # TODO - Why does it fail even when filedata is not set?
+        # TODO - Walk through the XTK io code again to emulate their
+        # approach.
         volume.filedata = @data
         renderer.add volume


pend for now

pend for now

--- apps/auth/test/client.coffee
+++ apps/auth/test/client.coffee
@@ -87,7 +87,7 @@
       beforeEach ->
         Backbone.sync.yieldsTo 'success'

-      it 'creates the user then logs in', ->
+      xit 'creates the user then logs in', ->

         @view.signup foo: 'bar'
         # Registers the user