refactor newBase

refactor newBase

--- lib/appHelper.coffee
+++ lib/appHelper.coffee
@@ -1,16 +1,17 @@
 # -- DEPENDENCIES --------------------------------------------------------------

-Args      = require 'args-js'
-fs        = require 'fs-extra'
-wrench    = require 'wrench'
-path      = require 'path'
-chalk     = require 'chalk'
-sh        = require 'execSync'
-_         = require 'lodash'
-Sails     = require 'sails/lib/app'
-sailsBin  = path.join __dirname, '../node_modules/sails/bin/sails.js'
-sailsLift = require 'sails/bin/sails-lift'
-_ioClient = require('./sails.io')(require('socket.io-client'))
+Args             = require 'args-js'
+fs               = require 'fs-extra'
+wrench           = require 'wrench'
+path             = require 'path'
+chalk            = require 'chalk'
+sh               = require 'execSync'
+_                = require 'lodash'
+localDependecies = path.join __dirname, '../node_modules'
+sailsBin         = path.join localDependecies, 'sails/bin/sails.js'
+Sails            = require (path.join localDependecies, 'sails/lib/app')
+sailsLift        = require (path.join localDependecies, 'sails/bin/sails-lift')
+_ioClient        = require('./sails.io')(require('socket.io-client'))

 # -- GLOBALS ------------------------------------------------------------------

@@ -19,10 +20,10 @@
   SAILOR : null
   APP    : null

-DEFAULT = {}
-DEFAULT.NAME          = 'testApp'
-DEFAULT.ORGANIZATION  = 'sailorjs'
-DEFAULT.PATH = path.join(process.cwd(), "/#{DEFAULT.NAME}")
+OPTIONS =
+  NAME         : 'testApp'
+  ORGANIZATION : 'sailorjs'
+  REPOSITORY   : 'testApp'

 TEMPLATE =
   BASE  : path.join(__dirname, '../template/base')
@@ -68,6 +69,79 @@

   ###
+   * Generate a new base proyect
+   * @param  {String} dir     Optional path
+   * @param  {Object} options Optional options, like:
+   *   - name
+   *   - organization
+   *   - repository
+   * @param  {Function} cb      Optional Callback
+  ###
+  @newBase: (dir, options, cb) =>
+    args = Args([
+      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}" }
+      {options      : Args.OBJECT   | Args.Optional, _default: OPTIONS            }
+      {cb           : Args.FUNCTION | Args.Optional, _default: undefined          }
+    ], arguments)
+
+    SCOPE.APP    = "#{args.dir}/#{args.options.name}"
+    wrench.copyDirSyncRecursive TEMPLATE.BASE, SCOPE.APP, forceDelete: true
+
+    @_copyTemplate template, args.options for template in TEMPLATE.FILES
+
+    appJSON  = require("#{SCOPE.APP}/package.json")
+    delete appJSON.dependencies?.sailorjs
+
+    SCOPE.SAILS  = @_resolvePath 'sails'
+    SCOPE.SAILOR = @_resolvePath 'sailor'
+
+    console.log SCOPE
+
+    # search the depndencies in the SCOPE's or download
+    @_copyDependencies(appJSON, args.cb)
+
+
+
+  ###
+   * Generate a new module
+   * @param  {String} dir     Optional path
+   * @param  {Object} options Optional options, like:
+   *   - name
+   *   - organization
+   *   - repository
+   * @param  {Function} cb      Optional Callback
+  ###
+  @newModule: (dir, options, cb) =>
+    args = Args([
+      {name         : Args.STRING   | Args.Optional, _default: OPTIONS.NAME}
+      {organization : Args.STRING   | Args.Optional, _default: OPTIONS.ORGANIZATION}
+      {repository   : Args.STRING   | Args.Optional, _default: name}
+      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}"}
+      {done         : Args.FUNCTION | Args.Optional, _default: undefined}
+    ], arguments)
+
+    SCOPE.APP    = "#{args.dir}/#{args.name}"
+    wrench.copyDirSyncRecursive TEMPLATE.MODULE, SCOPE.APP, forceDelete: true
+
+    options =
+      name         : args.name
+      organization : args.organization
+      repository   : args.repository
+
+    @_copyTemplate template, options for template in TEMPLATE.FILES
+
+    appJSON  = require("#{SCOPE.APP}/package.json")
+    delete appJSON.dependencies?.sailorjs
+
+    SCOPE.SAILS  = @_resolvePath 'sails'
+    SCOPE.SAILOR = @_resolvePath 'sailor'
+
+    # search the dependency in sails or sailor and linkin in the folder of the project
+    @_copyDependencies(appJSON, args.done)
+
+
+
+  ###
    * Create a symbolic link
    * @param  {String}   orig Origin path
    * @param  {String}   dist Destination path
@@ -92,7 +166,7 @@
   @writePluginFile: (source, dir, done) ->
     args = Args([
       {src:  Args.STRING   | Args.Required}
-      {dir : Args.STRING   | Args.Optional, _default: "#{DEFAULT.PATH}/config/plugins.coffee"}
+      {dir : Args.STRING   | Args.Optional, _default: path.join(process.cwd(), "/#{OPTIONS.NAME}/config/plugins.coffee")}
       {done: Args.FUNCTION | Args.Optional, _default: undefined}
     ], arguments)

@@ -100,79 +174,6 @@
       fs.writeFileSync args.dir, "module.exports.plugins = [" + JSON.stringify(args.src) + "]"

     args.done?()
-
-
-
-  ###
-   * Generate a new base proyect
-   * @param  {String}   name         Optional name
-   * @param  {String}   organization Optional organization
-   * @param  {String}   repository   Optional repository
-   * @param  {String}   dir          Optional path
-   * @param  {Function} done         Optional Callback
-  ###
-  @newBase: (name, organization, repository, dir, options, done) =>
-    args = Args([
-      {name         : Args.STRING   | Args.Optional, _default: DEFAULT.NAME}
-      {organization : Args.STRING   | Args.Optional, _default: DEFAULT.ORGANIZATION}
-      {repository   : Args.STRING   | Args.Optional, _default: name}
-      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}"}
-      {done         : Args.FUNCTION | Args.Optional, _default: undefined}
-    ], arguments)
-
-    SCOPE.APP    = "#{args.dir}/#{args.name}"
-    wrench.copyDirSyncRecursive TEMPLATE.BASE, SCOPE.APP, forceDelete: true
-
-    options =
-      name         : args.name
-      organization : args.organization
-      repository   : args.repository
-
-    @_copyTemplate template, options for template in TEMPLATE.FILES
-
-    appJSON  = require("#{SCOPE.APP}/package.json")
-    delete appJSON.dependencies?.sailorjs
-
-    SCOPE.SAILS  = @_resolvePath 'sails'
-    SCOPE.SAILOR = @_resolvePath 'sailor'
-
-    # search the dependency in sails or sailor and linkin in the folder of the project
-    @_copyDependencies(appJSON, args.done)
-
-
-  ###
-   * Generate a new module for a proyect
-   * @param  {String}   name name
-   * @param  {String}   dir  Optional path
-   * @param  {Function} done Optional Callback
-  ###
-  @newModule: (name, organization, repository, dir, done) =>
-    args = Args([
-      {name         : Args.STRING   | Args.Optional, _default: DEFAULT.NAME}
-      {organization : Args.STRING   | Args.Optional, _default: DEFAULT.ORGANIZATION}
-      {repository   : Args.STRING   | Args.Optional, _default: name}
-      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}"}
-      {done         : Args.FUNCTION | Args.Optional, _default: undefined}
-    ], arguments)
-
-    SCOPE.APP    = "#{args.dir}/#{args.name}"
-    wrench.copyDirSyncRecursive TEMPLATE.MODULE, SCOPE.APP, forceDelete: true
-
-    options =
-      name         : args.name
-      organization : args.organization
-      repository   : args.repository
-
-    @_copyTemplate template, options for template in TEMPLATE.FILES
-
-    appJSON  = require("#{SCOPE.APP}/package.json")
-    delete appJSON.dependencies?.sailorjs
-
-    SCOPE.SAILS  = @_resolvePath 'sails'
-    SCOPE.SAILOR = @_resolvePath 'sailor'
-
-    # search the dependency in sails or sailor and linkin in the folder of the project
-    @_copyDependencies(appJSON, args.done)

@@ -183,7 +184,7 @@
   ###
   @clean: (dir, done) ->
     args = Args([
-      {dir : Args.STRING   | Args.Optional, _default: "#{process.cwd()}/#{DEFAULT.NAME}"}
+      {dir : Args.STRING   | Args.Optional, _default: "#{process.cwd()}/#{OPTIONS.NAME}"}
       {done: Args.FUNCTION | Args.Optional, _default: undefined}
     ], arguments)

@@ -206,7 +207,6 @@
     ], arguments)

     @_changePath args.dir
-

     Sails().lift args.options, (err, sails) ->
       if args.done?
@@ -226,20 +226,20 @@

   @_resolvePath: (name) ->
+    # first try to resolve the dependency locally
+    return "#{localDependecies}/#{name}" if fs.existsSync("#{localDependecies}/#{name}")
+
+    # second, try to resolve in global mode
+    result = @execute "which #{name}"
+    return null if result.code isnt 0
+
     result = @execute "which #{name}"

     if result.stdout[result.stdout.length-1] is '\n'
       result.stdout = result.stdout.substring(0, result.stdout.length - 1)

-    # get the relative path
-    relative_path = result.stdout.split("/")
-    relative_path.splice(relative_path.length-1, 1)
-    relative_path = relative_path.join("/")
-    # get the symlink value
-    link  = fs.readlinkSync result.stdout
-    route = path.resolve relative_path, link
-    route = path.join route, '../..'
-
+    fileType = fs.lstatSync(result.stdout)
+    path.join fs.realpathSync(result.stdout), '../..' if fileType.isSymbolicLink()

   ###
@@ -262,9 +262,10 @@
   a symlink in the folder of the project
   ###
   @_copyDependencies = (pkg, cb) =>
-    moduleNames = _.union(Object.keys(pkg.devDependencies), Object.keys(pkg.dependencies))
-    app_node    = path.resolve SCOPE.APP, 'node_modules'
-    fs.mkdirsSync app_node
+    # get the names of the dependencies
+    moduleNames     = _.union(Object.keys(pkg.devDependencies), Object.keys(pkg.dependencies))
+    appDependencies = path.resolve SCOPE.APP, 'node_modules'
+    fs.mkdirsSync appDependencies

     for moduleName in moduleNames
       try
@@ -274,7 +275,7 @@

       catch e
         console.log "#{chalk.blue('info')}   : Dependency '#{moduleName}' doesn't found. Installing..."
-        @run "cd #{app_node} && npm install #{moduleName}"
+        @run "cd #{appDependencies} && npm install #{moduleName}"

     # Finally link sailor
     sailorLocal = path.resolve(SCOPE.APP, 'node_modules', 'sailorjs')


refactor messages

refactor messages

--- lib/appHelper.coffee
+++ lib/appHelper.coffee
@@ -265,7 +265,7 @@

     # ensure that SCOPE.SAILOR exists
     unless SCOPE.SAILOR?
-      console.log "#{chalk.blue('info')}   : Dependency 'sailorjs' doesn't found. Installing..."
+      console.log "#{chalk.blue('debug :')} Dependency 'sailorjs' doesn't found. Installing..."
       @run "cd #{appDependencies} && npm install sailorjs"
       SCOPE.SAILOR = path.resolve appDependencies, 'sailorjs'

@@ -276,7 +276,7 @@
         fs.symlinkSync srcModulePath, destModulePath, "junction"

       catch e
-        console.log "#{chalk.blue('info')}   : Dependency '#{moduleName}' doesn't found. Installing..."
+        console.log "#{chalk.blue('debug :')} Dependency '#{moduleName}' doesn't found. Installing..."
         @run "cd #{appDependencies} && npm install #{moduleName}"

     # Finally link sailor if is necessary


Fix bug where all AJAX calls weren't being halted

Signed-off-by: Michael Diolosa <1fe9c163c3612846d8518a0042166a6cea333eed@gmail.com>

--- src/controllers.coffee
+++ src/controllers.coffee
@@ -60,11 +60,11 @@

   ### Initializes a suggestion ###
   suggest: ->
+    @halt();
     @callbacks.checkingLengthThreshold?(@meets_length_threshold())
     return unless @can_suggest()

     @callbacks.initiateSuggestion?()
-    @halt();

     if @el.val()
       @callbacks.suggesting?()


Persist IIPs in graph, fixes #126

Persist IIPs in graph, fixes #126

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -486,6 +486,17 @@
           port: parts[3]
       graph.connections.push edge

+    iipIds = Object.keys(@iips).sort()
+    for id in iipIds
+      iip = @iips[id]
+      parts = fromIipId id
+      edge =
+        data: iip
+        tgt:
+          process: parts[0]
+          port: parts[1]
+      graph.connections.push edge
+
     return graph

   loadGraphFile: (path, opts, callback) ->


Testing compilation

Testing compilation

--- lib/friebyrd.coffee
+++ lib/friebyrd.coffee
@@ -4,9 +4,8 @@

   F = {}

-  fail = () -> []
-  succeed = (result) -> [result]
-
+  F.succeed = (result) -> [result]
+  F.fail = () -> []

   # exports and sundries


Fix PathWatcher typo # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Your branch is ahead of 'origin/master' by 2 commits. #   (use "git push" to publish your local commits) # # Changes to be committed: #   (use "git reset HEAD <file>..." to unstage) # # 	modified:   lib/git-plus.coffee # #

--- lib/git-plus.coffee
+++ lib/git-plus.coffee
@@ -56,7 +56,7 @@
       options:
         cwd: @dir
       stdout: (data) =>
-        Pathwatcher.close()
+        PathWatcher.close()
         atom.workspace.destroyActivePane()
       stderror: (data) =>
         alert data.toString()


:lipstick: Use unless instead of if not

:lipstick: Use unless instead of if not

--- lib/directory.coffee
+++ lib/directory.coffee
@@ -80,7 +80,7 @@
     index = 0
     for entry in @directory.getEntries() when not @isPathIgnored(entry.path)
       name = entry.getBaseName()
-      newEntries.push([entry, index]) if not @entries.hasOwnProperty(name)
+      newEntries.push([entry, index]) unless @entries.hasOwnProperty(name)
       delete removedEntries[name]
       index++


Improved grammar accuracy in punch description

--- src/models/punch.coffee
+++ src/models/punch.coffee
@@ -336,7 +336,12 @@
   description: (user) ->
     if @times.block?
       blockTimeQualifier = "#{@times.block} hour"
-      article = if @times.block in [8, 18] then 'an' else 'a'
+      if blockTimeQualifier.charAt(0) is '8' or
+         @times.block is 11 or
+         @times.block is 18
+        article = 'an'
+      else
+        article = 'a'
       elapsedQualifier = ''
     else
       time = @times.slice(-1)[0]


Also fixed horizontal offset when using extension button

--- js-library/app/js/jail_iframe/classes/buttons.coffee
+++ js-library/app/js/jail_iframe/classes/buttons.coffee
@@ -48,6 +48,6 @@
   placeNearSelection: (mouseX=null) ->
     selectionBox = window.document.getSelection().getRangeAt(0).getBoundingClientRect()
     top = selectionBox.top + $(window).scrollTop() - 2
-    left = mouseX ? (selectionBox.left + selectionBox.width/2)
+    left = mouseX ? ($(window).scrollLeft() + selectionBox.left + selectionBox.width/2)

     @setCoordinates top, left


Annotation -> Discussion

Annotation -> Discussion

--- js-library/app/js/jail_iframe/classes/buttons.coffee
+++ js-library/app/js/jail_iframe/classes/buttons.coffee
@@ -31,7 +31,7 @@
 class FactlinkJailRoot.ShowButton extends Button
   content: """

-
Show Annotation

+
Show Discussion

Loading...

   """
@@ -40,7 +40,7 @@
 class FactlinkJailRoot.CreateButton extends Button
   content: """

-
Add Annotation

+
Create Discussion

Loading...

   """


Add name class to span

Add name class to span

--- lib/file-view.coffee
+++ lib/file-view.coffee
@@ -10,6 +10,7 @@
     @classList.add('file', 'entry', 'list-item')

     @fileName = document.createElement('span')
+    @fileName.classList.add('name')
     @appendChild(@fileName)
     @fileName.textContent = @file.name
     @fileName.setAttribute('data-name', @file.name)


Update atom/config.cson

Update atom/config.cson

--- osx/atom/config.cson
+++ osx/atom/config.cson
@@ -1,34 +1,34 @@
 "*":
-  "exception-reporting":
-    userId: "9a0fc135-6b10-32a3-829f-6113bc15a030"
-  "release-notes":
-    viewedVersion: "0.95.0"
-  welcome:
-    showOnStartup: false
-  metrics:
-    userId: "b52998dda154e5f0664dc6f4bd03c798e49e2ea5"
-  editor:
-    fontFamily: "Inconsolata"
-    preferredLineLength: 150
-    invisibles: {}
-    showInvisibles: true
-    showIndentGuide: true
+  "atom-color-highlight": {}
+  "autocomplete-plus": {}
   core:
+    disabledPackages: [
+      "ruby-slim"
+    ]
+    projectHome: "/Users/jmblog/src"
     themes: [
       "atom-light-ui"
       "atom-light-syntax"
     ]
-    projectHome: "/Users/jmblog/src"
-    disabledPackages: [
-      "ruby-slim"
-    ]
-  "autocomplete-plus": {}
+  editor:
+    fontFamily: "Inconsolata"
+    invisibles: {}
+    preferredLineLength: 150
+    showIndentGuide: true
+    showInvisibles: true
   emmet: {}
+  "exception-reporting":
+    userId: "9a0fc135-6b10-32a3-829f-6113bc15a030"
+  "highlight-selected": {}
+  linter: {}
+  metrics:
+    userId: "b52998dda154e5f0664dc6f4bd03c798e49e2ea5"
+  pigments: {}
+  "release-notes":
+    viewedVersion: "0.95.0"
   "tree-view": {}
-  "highlight-selected": {}
-  "atom-color-highlight": {}
-  pigments: {}
-  linter: {}
+  welcome:
+    showOnStartup: false
 ".slim.text":
   editor:
     preferredLineLength: 150


Fix typo

Fix typo

--- node_modules/bongo-client/src/bongo.coffee
+++ node_modules/bongo-client/src/bongo.coffee
@@ -210,7 +210,7 @@
             if method.useMQ
               channelId = createId()
               channel = bongo.mq.subscribe channelId
-              channe.on 'broker:subscription_succeeded', ->
+              channel.on 'broker:subscription_succeeded', ->
                 method.apply constructor, [channelId].concat args
             else
               method.apply constructor, [null].concat args
@@ -230,7 +230,7 @@
             if method.useMQ
               channelId = 'private-'+createId()
               channel = bongo.mq.subscribe channelId
-              channe.on 'broker:subscription_succeeded', ->
+              channel.on 'broker:subscription_succeeded', ->
                 method channelId, id, (@ unless id), args...
             else
               method null, id, (@ unless id), args...
@@ -269,8 +269,8 @@
       if res?.__channelId
         channel = bongo.mq.channel res.__channelId
         if channel?
-          channe.on 'message', fn.bind null, null
-          channe.on 'error', fn.bind null
+          channel.on 'message', fn.bind null, null
+          channel.on 'error', fn.bind null
       else
         args = slice.call(arguments)
         wrapArgs args, (wrappedArgs)-> fn.apply @, wrappedArgs


console can be reset

console can be reset

--- platform/src/app.coffee
+++ platform/src/app.coffee
@@ -130,6 +130,8 @@
   $("#resetButton").on "click", ->
     currentFrame = 0
     currentModel = currentModel.atVersion(0)
+    console.log("reset button: on click;")
+    resetConsoleContent()

     # TODO: move these handlers to MVC events
     $("#timeSlider").slider "option",
@@ -295,6 +297,14 @@

   localStorage.removeItem(programId)

+# Reset console content
+resetConsoleContent = ->
+  console.log("resetConsoleContent")
+
+  editors.console.setValue("");
+  editors.console.selection.clearSelection();
+  editors.console.setReadOnly(true);
+
 ### Main ###

 $(document).ready ->
@@ -327,8 +337,9 @@
                       ["layoutEditor", "optics/layout.json"]]
       loadIntoEditor(editors[id], url)

-  currentConsole = "test";
-  editors.console.setValue(currentConsole);
+  # TODO replace by resetConsoleContent
+  console.log("Main: doc ready")
+  editors.console.setValue("test");
   editors.console.selection.clearSelection();
   editors.console.setReadOnly(true);


Tests: Added checkWorkspaceFromVmList test.

--- tests/src/ide/ide.coffee
+++ tests/src/ide/ide.coffee
@@ -188,3 +188,30 @@
       .pause                   2000 # required
       .end()

+
+  createWorkspaceFromVmList: (browser) ->
+
+    user          = helpers.beginTest(browser)
+    paragraph     = helpers.getFakeText()
+    workspaceName = paragraph.split(' ')[0]
+
+    browser
+      .waitForElementVisible   '.kdscrollview li a.more-link', 20000
+      .click                   '.kdscrollview li a.more-link'
+      .waitForElementVisible   '.kdmodal-inner', 20000
+      .click                   '.kdmodal-inner button'
+      .pause 2000 # required
+      .waitForElementVisible   '.add-workspace-view', 20000
+      .setValue                '.add-workspace-view input.kdinput.text', workspaceName + '\n'
+      .waitForElementVisible   '.vm-info', 20000
+      .pause 2000, =>
+
+        browser.url (data) =>
+          url    = data.value
+          vmName = url.split('/IDE/')[1].split('/')[0]
+
+          browser
+            .assert.urlContains      workspaceName # Assertion
+            .assert.containsText     '.vm-info', '~/Workspaces/' + workspaceName # Assertion
+            .waitForElementPresent   'a[href="/IDE/' + vmName + '/' + workspaceName + '"]', 20000 # Assertion
+            .end()


icon -> iconHTML

icon -> iconHTML

--- lib/suggestion-list-element.coffee
+++ lib/suggestion-list-element.coffee
@@ -127,7 +127,7 @@
     li.remove() while li = @ol.childNodes[items.length]
     @selectedLi?.scrollIntoView(false)

-  renderItem: ({icon, type, snippet, text, className, replacementPrefix, leftLabel, leftLabelHTML, rightLabel, rightLabelHTML}, index) ->
+  renderItem: ({iconHTML, type, snippet, text, className, replacementPrefix, leftLabel, leftLabelHTML, rightLabel, rightLabelHTML}, index) ->
     li = @ol.childNodes[index]
     unless li
       li = document.createElement('li')
@@ -142,7 +142,7 @@

     typeIconContainer = li.querySelector('.icon-container')
     typeIconContainer.innerHTML = ''
-    if iconHTML = (icon ? (type ? '')[0])
+    if iconHTML = (iconHTML ? (type ? '')[0])
       typeIconContainer.innerHTML = IconTemplate
       typeIcon = typeIconContainer.childNodes[0]
       typeIcon.innerHTML = iconHTML


Deprecate tell.coffee -- it is a package of its own now

Upgrade instructions at
https://github.com/lorenzhs/hubot-tell/blob/master/UPGRADING.md

--- src/scripts/tell.coffee
+++ src/scripts/tell.coffee
@@ -1,3 +1,6 @@
+# THIS SCRIPT HAS MOVED TO ITS OWN PACKAGE. PLEASE USE
+# https://github.com/lorenzhs/hubot-tell INSTEAD!
+#
 # Description:
 #   Tell Hubot to send a user a message when present in the room
 #
@@ -14,6 +17,7 @@
 #   christianchristensen, lorenzhs, xhochy

 module.exports = (robot) ->
+   robot.logger.warning "tell.coffee has moved from hubot-scripts to its own package. See https://github.com/lorenzhs/hubot-tell/blob/master/UPGRADING.md for upgrade instructions"
    localstorage = {}
    robot.respond /tell ([\w.-]*):? (.*)/i, (msg) ->
      datetime = new Date()


Get gutter view instance on each display update

So that markers can be restored when editor.showLineNumbers is toggled.

--- lib/lint-view.coffee
+++ lib/lint-view.coffee
@@ -16,7 +16,6 @@
     @editorView.overlayer.append(this)

     @editor = @editorView.getEditor()
-    @gutterView = @editorView.gutter

     @violationViews = []

@@ -80,18 +79,19 @@
       violationView.isValid

   updateGutterMarkers: ->
-    return if @gutterView.length == 0
-    return unless @gutterView.isVisible()
+    gutterView = @editorView.gutter
+    return if gutterView.length == 0
+    return unless gutterView.isVisible()

     for severity in Violation.SEVERITIES
-      @gutterView.removeClassFromAllLines("lint-#{severity}")
+      gutterView.removeClassFromAllLines("lint-#{severity}")

     return if @violationViews.length == 0

     for violationView in @getValidViolationViews()
       line = violationView.getCurrentBufferStartPosition().row
       klass = "lint-#{violationView.violation.severity}"
-      @gutterView.addClassToLine(line, klass)
+      gutterView.addClassToLine(line, klass)

   moveToNextViolation: ->
     @moveToNeighborViolation('next')


Dump stacktrace for better debugging

Dump stacktrace for better debugging

--- lib/lint-view.coffee
+++ lib/lint-view.coffee
@@ -54,7 +54,7 @@
     @removeViolationViews()

     if error?
-      console.log(error)
+      console.log(error.stack)
     else if @editorView.active
       @addViolationViews(violations)
     else


fixed a minor bug with the normalizing behavior.

--- app/assets/javascripts/teabag/base/reporters.coffee
+++ app/assets/javascripts/teabag/base/reporters.coffee
@@ -28,7 +28,7 @@
       skipped = results.skipped
     else
       status = "passed" if @spec.state == "passed" || @spec.state == "skipped"
-      skipped = @spec.state == "skipped"
+      skipped = typeof(@spec.state) == "undefined"
     status = "pending" if @spec.pending
     status: status
     skipped: skipped


:art: Rename MessageRegistry::messages to MessageRegistry::linterResponses

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -7,7 +7,7 @@
     @updated = false
     @publicMessages = []
     @emitter = new Emitter
-    @messages = new Map()
+    @linterResponses = new Map()
     @editorMessages = new Map()

     @shouldUpdatePublic = true
@@ -16,7 +16,7 @@
   set: ({linter, messages, editor}) ->
     try validate.messages(messages) catch e then return helpers.error(e)
     if linter.scope is 'project'
-      @messages.set(linter, messages)
+      @linterResponses.set(linter, messages)
     else
       throw new Error("Given editor isn't really an editor") unless editor instanceof TextEditor
       if not @editorMessages.has(editor) then @editorMessages.set(editor, new Map())
@@ -28,7 +28,7 @@
     if @updated
       @updated = false
       publicMessages = []
-      @messages.forEach (messages) -> publicMessages = publicMessages.concat(messages)
+      @linterResponses.forEach (messages) -> publicMessages = publicMessages.concat(messages)
       @editorMessages.forEach (linters) -> linters.forEach (messages) ->
         publicMessages = publicMessages.concat(messages)
       @publicMessages = publicMessages.sort (a, b) ->
@@ -42,9 +42,9 @@
     return @emitter.on('did-update-messages', callback)

   deleteMessages: (linter) ->
-    if @messages.has(linter)
+    if @linterResponses.has(linter)
       @updated = true
-      @messages.delete(linter)
+      @linterResponses.delete(linter)

   deleteEditorMessages: (editor) ->
     if @editorMessages.has(editor)
@@ -54,7 +54,7 @@
   deactivate: ->
     @shouldUpdatePublic = false
     @emitter.dispose()
-    @messages.clear()
+    @linterResponses.clear()
     @editorMessages.clear()

 module.exports = MessageRegistry


Handling all kinds of errors when setting error message.

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -43,12 +43,17 @@
       @_set @messageType.ERROR, field, ''

   setError: (error) =>
-    # We get field value from error details
-    if _.startsWith error.details, 'Field: '
-      field = error.details.substring 'Field: '.length
+    if error instanceof Meteor.Error
+      # We get field value from error details
+      if _.startsWith error.details, 'Field: '
+        field = error.details.substring 'Field: '.length
+      else
+        field = ''
+      @setErrorMessage error.reason, field
+    else if error instanceof Error
+      @setErrorMessage error.message
     else
-      field = ''
-    @setErrorMessage error.reason, field
+      @setErrorMessage error.toString()

   # Sets error message for given field or global error message if field is not set
   setErrorMessage: (message, field) =>


lets try this out

lets try this out

--- lib/assets/javascripts/code_sync/editor/index.coffee
+++ lib/assets/javascripts/code_sync/editor/index.coffee
@@ -24,10 +24,9 @@

     @assetsCollection = new CodeSync.ProjectAssets()

-    client = CodeSync.Client.get()
-
-    client.onNotification = (notification)=>
-      console.log "Editor trapping notification", @currentPath, notification
+    #if client = CodeSync.Client.get()
+    #  client.onNotification = (notification)=>
+    #    console.log "Editor trapping notification", @currentPath, notification

     @render() unless @autoRender is false

@@ -292,4 +291,5 @@
     window.codeSyncEditor = new CodeSync.AssetEditor()
     window.codeSyncEditor.slideIn()

-CodeSync.AssetEditor.setHotKey = (hotKey)-> key(hotKey, CodeSync.AssetEditor.toggleEditor)+CodeSync.AssetEditor.setHotKey = (hotKey)->
+  key(hotKey, CodeSync.AssetEditor.toggleEditor)


rm nextTick

rm nextTick

--- lib/nextTick.coffee
+++ lib/nextTick.coffee
@@ -1 +0,0 @@
-module.exports = require('timers')?.setImmediate or process.nextTick


fetchLikedContents for JAccount to fetch liked contents and their teasers for given selector

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -50,7 +50,7 @@
         'fetchFollowingWithRelationship', 'fetchTopics'
         'fetchMounts','fetchActivityTeasers','fetchRepos','fetchDatabases'
         'fetchMail','fetchNotificationsTimeline','fetchActivities'
-        'fetchStorage','count','addTags','fetchLimit'
+        'fetchStorage','count','addTags','fetchLimit', 'fetchLikedContents'
         'fetchFollowedTopics', 'fetchKiteChannelId', 'setEmailPreferences'
         'fetchNonces', 'glanceMessages', 'glanceActivities', 'fetchRole'
         'fetchAllKites','flagAccount','unflagAccount'
@@ -228,6 +228,30 @@
     else
       callback null, "private-#{kiteName}-#{delegate.profile.nickname}"

+  fetchLikedContents: secure ({connection}, options, selector, callback)->
+
+    {delegate} = connection
+    [callback, selector] = [selector, callback] unless callback
+
+    selector            or= {}
+    selector.as           = 'like'
+    selector.targetId     = @getId()
+    selector.sourceName or= $in: ['JCodeSnip', 'JStatusUpdate']
+
+    Relationship.some selector, options, (err, contents)=>
+      if err then callback err, []
+      else if contents.length is 0 then callback null, []
+      else
+        teasers = []
+        collectTeasers = bongo.race (i, root, fin)->
+          root.fetchSource (err, teaser)->
+            if err then callback err
+            else
+              teasers.push(teaser) if teaser
+              fin()
+        , -> callback null, teasers
+        collectTeasers node for node in contents
+
   dummyAdmins = ["sinan", "devrim", "aleksey-m", "gokmen", "chris"]

   flagAccount: secure (client, flag, callback)->


turn off notes js debugging

turn off notes js debugging

--- lms/static/coffee/src/notes.coffee
+++ lms/static/coffee/src/notes.coffee
@@ -1,5 +1,5 @@
 class StudentNotes
-    _debug: true
+    _debug: false

     targets: [] # holds elements with annotator() instances

@@ -70,4 +70,4 @@
 #
 # Comment this line to disable notes.

-$(document).ready ($) -> new StudentNotes $, @+$(document).ready ($) -> new StudentNotes $, @


social: fetch socialApiId via createSocialApi method in sendNotification

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -1070,9 +1070,11 @@
     isTainted

   sendNotification: (event, contents) ->
-    @emit 'notification',
-      eventName: event, body: contents, account: {id: @socialApiId, nick: @profile.nickname}
-
+    @createSocialApiId (err, socialApiId) =>
+      return console.error "Could not send notification to account #{err}"  if err
+
+      @emit 'notification',
+        eventName: event, body: contents, account: {id: socialApiId, nick: @profile.nickname}
   fetchGroupsWithPending:(method, status, options, callback)->
     [callback, options] = [options, callback]  unless callback
     options ?= {}


datadog: add variable tag support

datadog: add variable tag support

--- workers/social/lib/social/models/datadog.coffee
+++ workers/social/lib/social/models/datadog.coffee
@@ -38,12 +38,26 @@
       tags   : ["user:%nickname%", "context:terminal"]

-  tagReplace = (sourceTag, nickname)->
+
+  tagReplace = (sourceTag, userTags)->
+
+    parseTag = (tag) ->
+
+      # check if tag contains any variable
+      tagMatch = tag.match /%(.*)%$/m
+
+      return tag  unless tagMatch?.length
+
+      # check if tag variable value is set in userTags
+      tagValue = userTags[tagMatch[1]]
+      return tag  unless tagValue
+
+      return tag.replace /%(.*)%$/g, tagValue

     tags = []

     for tag in sourceTag
-      tags.push tag.replace '%nickname%', nickname
+      tags.push parseTag tag

     return tags

@@ -74,8 +88,8 @@
     tags['nickname'] = nickname

     title = ev.title
-    tags  = tagReplace ev.tags, nickname
     text = parseText ev.text, tags
+    tags  = tagReplace ev.tags, tags

     if logs?
       if logs.length < 400


Update the command descriptions to match the recent changes

--- src/scripts/cash.coffee
+++ src/scripts/cash.coffee
@@ -9,8 +9,8 @@
 #   HUBOT_CASH_THOUSANDS_SEPARATOR - the symbol used for splitting thousands. Default: ,
 #
 # Commands:
-#   hubot cash <left|on hand> is <amount> - set the cash on hand
-#   hubot cash burn rate is <amount> - set the burn rate
+#   hubot cash <left|on hand>: <amount> - set the cash on hand
+#   hubot cash <burn rate|burn>: <amount> - set the burn rate
 #   hubot cash <update|state|stats> - show the cash situation
 #
 # Notes:


Error checking for Pipl setup, we're good!

--- src/scripts/pipl.coffee
+++ src/scripts/pipl.coffee
@@ -22,35 +22,43 @@

     request_url = "http://api.pipl.com/search/v3/json/?email=#{encodeURIComponent target_email}&exact_name=0&query_params_mode=and&key=#{PIPL_API_KEY}"

-    #msg.send request_url + "&pretty=true" #foar great debugging!
+    msg.send request_url + "&pretty=true" #foar great debugging!

     request_response = robot.http(request_url)

     .get() (err, res, body) ->
       pipl_json = JSON.parse(body)

-      person_sources = ""
-      records_source = ""
+      person_sources = "Person:\n"
+      records_source = "Records:\n"

-      ## person
-      person_sources += """#{person_source.name}:   #{person_source.url}\n""" for person_source in pipl_json.person.sources
+      if pipl_json.error?
+        msg.send "Pipl Error: #{pipl_json.error}"
+      else
+        ## person
+        if pipl_json.person.sources?
+          person_sources += """ - #{person_source.name}:   #{person_source.url}\n""" for person_source in pipl_json.person.sources
+        else
+          person_sources += """ - No information found.\n"""

-      ## related_urls
-      # for related_url in pipl_json.related_urls
-      #   msg.send related_url
+        ## related_urls (really noisy)
+        # for related_url in pipl_json.related_urls
+        #   msg.send related_url

-      ## records
-      #records_source """#{record.source.name}: #{record.source.url}\n""" for record in pipl_json.records
+        ## records
+        if pipl_json.records?
+          records_source += """ - #{record.source.name}: #{record.source.url}\n""" for record in pipl_json.records
+        else
+          records_source += """ - No information found.\n"""

-      pipl_summary = """
-      Pipl Profile for Email: #{target_email}
-      ------------------------------------------------
-      Total Records: #{pipl_json["@records_count"]}
+        pipl_summary = """
+        Pipl Profile for Email: #{target_email}
+        ------------------------------------------------
+        Total Records: #{pipl_json["@records_count"]}

-      Person Records:
-      #{person_sources}
+        #{person_sources}
+        #{records_source}
+        """

-      """
-
-      msg.send pipl_summary
+        msg.send pipl_summary


Added week?

Added week?

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -177,8 +177,7 @@
   robot.respond REGEX.modes, (res) ->
     if not Organization.ready()
       Logger.log "Don\'t punch #{res.match[1]}, Organization isn\'t ready yet"
-      Logger.logToChannel strings.orgnotready,
-                          res.message.user.name
+      Logger.logToChannel strings.orgnotready, res.message.user.name
       return
     parse res, res.match.input, res.match[1]

@@ -186,8 +185,7 @@
   robot.respond REGEX.rel_time, (res) ->
     if not Organization.ready()
       Logger.log 'Don\'t punch a block, Organization isn\'t ready yet'
-      Logger.logToChannel strings.orgnotready,
-                          res.message.user.name
+      Logger.logToChannel strings.orgnotready, res.message.user.name
       return
     parse res, res.match.input, 'none'

@@ -240,8 +238,7 @@
   robot.respond /(append|add)/i, (res) ->
     if not Organization.ready()
       Logger.log 'Don\'t append to punch, Organization isn\'t ready yet'
-      Logger.logToChannel strings.orgnotready,
-                          res.message.user.name
+      Logger.logToChannel strings.orgnotready, res.message.user.name
       return
     user = Organization.getUserBySlackName res.message.user.name
     if not user
@@ -295,8 +292,7 @@
   robot.respond /undo/i, (res) ->
     if not Organization.ready()
       Logger.warn 'Don\'t undo, Organization isn\'t ready yet'
-      Logger.logToChannel strings.orgnotready,
-                          res.message.user.name
+      Logger.logToChannel strings.orgnotready, res.message.user.name
       return
     user = Organization.getUserBySlackName res.message.user.name
     if not user
@@ -336,11 +332,10 @@
                          Logger

   # User feedback
-  robot.respond /(hours|today)+[\?\!\.¿¡]/i, (res) ->
-    if not Organization.ready()
-      Logger.log "Don\'t output diagnostics, Organization isn\'t ready yet"
-      Logger.logToChannel strings.orgnotready,
-                          res.message.user.name
+  robot.respond /(hours|today|week)+[\?\!\.¿¡]/i, (res) ->
+    if not Organization.ready()
+      Logger.log "Don\'t output hours, Organization isn\'t ready yet"
+      Logger.logToChannel strings.orgnotready, res.message.user.name
       return
     user = Organization.getUserBySlackName res.message.user.name
     if not user
@@ -351,21 +346,31 @@
                            at #{Organization.name}.",
                           res.message.user.name
       return
-
-    earlyToday = moment({hour: 0, minute: 0, second: 0})
-    now = moment({hour: 0, minute: 0, second: 0}).add(1, 'days')
-    report = user.toRawPayroll(earlyToday, now)
+
+    mode = res.match[1]
+    Logger.debug mode
+    report = dateArticle = null
     headers = HEADERS.payrollreports
+    if mode is 'week'
+      sunday = moment({hour: 0, minute: 0, second: 0}).day("Sunday")
+      now = moment({hour: 0, minute: 0, second: 0}).add(1, 'days')
+      report = user.toRawPayroll(sunday, now)
+      dateArticle = "this week"
+    else
+      earlyToday = moment({hour: 0, minute: 0, second: 0})
+      now = moment({hour: 0, minute: 0, second: 0}).add(1, 'days')
+      report = user.toRawPayroll(earlyToday, now)
+      dateArticle = "today"

     loggedAny = false
     if not report[headers.logged] and
        not report[headers.vacation] and
        not report[headers.sick] and
        not report[headers.unpaid]
-      msg = 'You haven\'t recorded any hours today.'
+      msg = "You haven\'t recorded any hours #{dateArticle}."
     else
       if not report[headers.logged]
-        msg = 'You haven\'t recorded any paid work time'
+        msg = "You haven\'t recorded any paid work time"
       else
         msg = "You have *#{toTimeStr(report[headers.logged])} of paid work time*"
         loggedAny = true
@@ -379,7 +384,7 @@
             loggedAny = true
           else
             msg += " and *#{toTimeStr(report[header])} of #{kind} time*"
-      msg += ' recorded for today.'
+      msg += " recorded for #{dateArticle}."
     if report.extra?.projects and report.extra?.projects?.length > 0
       msg += ' ('
       for project in report.extra.projects


remove console.log

remove console.log

--- src/lib/config-json-generator.coffee
+++ src/lib/config-json-generator.coffee
@@ -125,8 +125,6 @@
     ###
     loadDefaultConfig: (configName) ->

-        console.log @defaultConfigsPath
-
         try
             require "#{@defaultConfigsPath}/#{configName}.json"
         catch e


testing doc locations in PCanvas

testing doc locations in PCanvas

--- src/power_canvas.coffee
+++ src/power_canvas.coffee
@@ -1,3 +1,7 @@
+###*
+* @name PowerCanvas
+* @constructor
+###
 ( ($) ->
   $.fn.powerCanvas = (options) ->
     options ||= {}
@@ -5,10 +9,6 @@
     canvas = this.get(0)
     context = undefined

-    ###*
-     * @name PowerCanvas
-     * @constructor
-    ###
     $canvas = $(canvas).extend
       ###*
        * Passes this canvas to the block with the given matrix transformation


Use data attribute instead of CSS classes

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -560,7 +560,7 @@
       @trigger 'editor:path-changed'

     @subscribe @editor, "grammar-changed", =>
-      @addGrammarScopeClasses()
+      @addGrammarScopeAttribute()
       @trigger 'editor:grammar-changed'

     @subscribe @editor, 'selection-added', (selection) =>
@@ -586,10 +586,9 @@
     if @attached and @editor.buffer.isInConflict()
       _.defer => @showBufferConflictAlert(@editor) # Display after editor has a chance to display

-  addGrammarScopeClasses: ->
-    scopeParts = @editor.getGrammar()?.scopeName?.split('.')
-    classes = scopeParts.join(' ') if scopeParts?.length
-    @addClass(classes) if classes?
+  addGrammarScopeAttribute: ->
+    grammarScope = @editor.getGrammar()?.scopeName?.replace(/\./g, ' ')
+    @attr('data-scope', grammarScope)

   getModel: ->
     @editor


Tweaked the wording on index help

Tweaked the wording on index help

--- help/index.coffee
+++ help/index.coffee
@@ -26,11 +26,13 @@
 The following is a list of the current available help topics:

   help this             What the heck is this?
+  help programs         What comes pre installed on Koding?
+  help sudo             What is my sudo pass?
+  help specs            What are my VMs specifications?
+  help mysql            Connect to your MySQL Server on Koding
+  help postgresql       Connect to your PostgreSQL on Koding
+  help mongodb          Connect to your MongoDB on Koding
   help ftp              Connect to your VM through FTP
-  help programs         What comes pre installed on Koding?
-  help mysql            Connect to MySQL on Koding
-  help postgresql       Connect to PostgreSQL on Koding
-  help mongodb          Connect to MongoDB on Koding

 As always, additional help can be found on Koding University at:
   http://learn.koding.com


update test for new date formatting

update test for new date formatting

--- apps/show/components/related_shows/test/view.coffee
+++ apps/show/components/related_shows/test/view.coffee
@@ -51,7 +51,7 @@
       Backbone.sync.args[0][2].success [relatedShow]
       @view.$('.show-related-show-title').html().should.containEql 'Test Show'
       @view.$('.show-related-show-partner').html().should.containEql 'Test Gallery'
-      @view.$('.show-related-show-running-dates').html().should.containEql 'July 12 – August 23'
+      @view.$('.show-related-show-running-dates').html().should.containEql 'Jul. 12th – Aug. 23rd 2013'

     xit 'displays the correct number of install shots', ->
       # this relies on a query to the width of an element on the dom


fix sorite fleet without equipment

fix sorite fleet without equipment

--- lib/simulate.coffee
+++ lib/simulate.coffee
@@ -22,7 +22,7 @@

 checkRepairItem = (sortieShip) ->
-  if sortieShip.nowHP <= 0
+  if sortieShip.nowHP <= 0 && sortieShip.equipment?
     for id, i in sortieShip.equipment
       if id == 42
         sortieShip.nowHP = Math.floor(sortieShip.maxHP / 5)


Working on paths. #This still doesn't work, as far as I know, but it's easier to read, and is better than nothing on linux

--- lib/d-struct.coffee
+++ lib/d-struct.coffee
@@ -1,17 +1,18 @@
 # Grab a few objects that we'll need
 popen = require('child_process').exec
 platform = require('os').platform
+paths = atom.project.getPaths().shift()

-# --TODO:-linux-support-still-needs-to-be-added-- Still Working!
+# TODO: linux support still needs to be added
 commands =
     build:
-        win32: "start cmd /k \"cd #{atom.project.getPaths().shift()} && dub run\""
-        darwin: "osascript -e \'tell application \"Terminal\" to do script \"cd #{atom.project.getPaths().shift()}; dub run\"\'"
-        #linux: "bash -c \"cd #{atom.project.getPaths().shift()} && dub run\""
+        win32: "start cmd /k \"cd #{paths} && dub run\""
+        darwin: "osascript -e \'tell application \"Terminal\" to do script \"cd #{paths}; dub run\"\'"
+        linux: "gnome-terminal -e \"bash -ic \'cd #{paths} && dub run && read -s -n 1 line\'\""
     run:
-        win32: "start cmd /k \"cd \"#{atom.project.getPaths().shift()}\" && dub build\""
-        darwin: "osascript -e \'tell application \"Terminal\" to do script \"cd #{atom.project.getPaths().shift()}; dub build\"\'"
-        #linux: "bash -c \"cd #{atom.project.getPaths().shift()} && dub build\""
+        win32: "start cmd /k \"cd \"#{paths}\" && dub build\""
+        darwin: "osascript -e \'tell application \"Terminal\" to do script \"cd #{paths}; dub build\"\'"
+        linux: "gnome-terminal -e \"bash -ic \'cd #{paths} && dub build && read -s -n 1 line\'\""

 # Open a terminal and run dub
 dub_build = ->
@@ -32,3 +33,4 @@
     dub_build()
   dubrun: ->
     dub_run()
+


Rename isDirectory() to isDirectorySync()

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -37,7 +37,7 @@

   loadAtomSnippets: (packagePath, done) ->
     snippetsDirPath = path.join(packagePath, 'snippets')
-    return done() unless fsUtils.isDirectory(snippetsDirPath)
+    return done() unless fsUtils.isDirectorySync(snippetsDirPath)

     loadSnippetFile = (filename, done) =>
       return done() if filename.indexOf('.') is 0
@@ -54,7 +54,7 @@

   loadTextMateSnippets: (bundlePath, done) ->
     snippetsDirPath = path.join(bundlePath, 'Snippets')
-    return done() unless fsUtils.isDirectory(snippetsDirPath)
+    return done() unless fsUtils.isDirectorySync(snippetsDirPath)

     loadSnippetFile = (filename, done) =>
       return done() if filename.indexOf('.') is 0


Process macro

Process macro

--- lisp.coffee
+++ lisp.coffee
@@ -217,7 +217,10 @@
             if funs = funcs[funname.name]
               funs()
             else
-              if lambda = currentEnv().get(funname.name)
+              if macro = currentEnv().getMacro(funname.name)
+                expr = @exec_lambda(macro, args)
+                @eval_expr(expr)
+              else if lambda = currentEnv().get(funname.name)
                 @exec_lambda(lambda, args)
               else
                 error NameError, "undefined function \"#{funname.name}\"", funname.pos


Fixed a few bugs in Exo.List after implementing the new multi model/controller pattern in a project.

--- list.coffee
+++ list.coffee
@@ -31,14 +31,14 @@

 	renderTemplates: (collection) ->
 		templates = @templates || {default: @template}
-		@html collection.map (item) => templates[item.className] or templates.default
+		@html collection.map (item) => templates[item.getClassName()] or templates.default
 			.call(@, [item])[0]

 	renderControllers: (collection) ->
 		controllers = @controllers || {default: @controller}
 		# Dynamically create child controllers
 		for item, i in collection
-			child = @getOrCreateChild item, controllers[item.className] or controllers.default
+			child = @getOrCreateChild item, controllers[item.getClassName()] or controllers.default
 			child.listIndex = i
 			child.activate()

@@ -47,11 +47,11 @@
 		@deactivateAndKillOrphans(@getChildren(), collection)
 		@orderChildren(@getChildren(), collection)

-	getOrCreateChild: (item) ->
-		child = @getChildById(item.id)
+	getOrCreateChild: (item, controller) ->
+		child = @getChildById(item.getClassName() + item.id)
 		unless child
-			child = new @controller
-			child.id = item.id
+			child = new controller
+			child.id = item.getClassName() + item.id
 			@addChild child
 			child.prepareWithModel item
 			@append child


bugfix

bugfix

--- lixian.iced
+++ lixian.iced
@@ -180,6 +180,11 @@
   _reload: (cb)->
     await @_cookie 'userid', defer e, @_userid
     return cb if e
+
+    unless @_userid
+      @logon = false
+      return cb null
+
     timespan = Date.now() / 1000
     await @_get "http://dynamic.cloud.vip.xunlei.com/user_task",
       userid: @_userid


SocialWorker: remove duplicate appStorage documents as well

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -1,3 +1,4 @@
+_           = require 'underscore'
 jraphical   = require 'jraphical'
 KodingError = require '../error'
 ApiError    = require './socialapi/error'
@@ -964,34 +965,59 @@
     unless appId and version
       return callback new KodingError 'version and appId is not set!'

-    oldStorage         = null
-    newStorage         = null
-    accountId          = @getId()
+    accountId    = @getId()
+    newStorage   = null
+    oldStorages  = null
+    luckyStorage = null

     queue = [

       =>
-        # trying to fetch an old app storage document by relationship
+        # trying to fetch old app storages, there were more than one
+        # appStorages for the same appId and version, due to some bug
         query = { 'data.appId':appId, 'data.version':version }
-        @fetchAppStorage query, (err, storage) ->
+        @fetchAppStorages query, (err, storages) ->
+
+          storages.forEach (storage) ->
+            console.log storage
+            data = storage?.bucket
+            console.log data
+            # if storage bucket is not empty pick it as lucky one
+            luckyStorage = storage  unless _.isEmpty data
+
+          # if there was no lucky storage, pick the first one
+          luckyStorage ?= storages[0]
+
           return calback err  if err
-          return callback null, null  unless storage
-          oldStorage = storage
+          # returning storage null bcs there was no old storage to be migrated
+          return callback null, null  unless storages.length > 0
+          oldStorages = storages
           queue.next()

       =>
         # creating new app storage document
-        options.data = oldStorage.bucket
+        options.data = luckyStorage?.bucket or {}
         @createAppStorage options, (err, storage) ->
           return callback err  if err
           newStorage = storage
           queue.next()

       ->
-        # removing old storage
-        oldStorage.remove (err) ->
-          return callback err  if err
-          return callback null, newStorage
+        # removing old storages
+        oldStorages.forEach (oldStorage) ->
+          _queue = []
+          _queue.push ->
+            oldStorage.remove (err) ->
+              return _queue.fin err  if err
+              _queue.fin()
+
+          dash _queue, (err) ->
+            return callback err  if err
+            queue.next()
+
+      ->
+        callback null, newStorage
+

     ]


impersonate: just callback with the error

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -600,7 +600,8 @@
       callback new KodingError 'Access denied'
     else
       JSession = require './session'
-      JSession.update {clientId: sessionToken}, $set:{username: nickname}, callback
+      JSession.update {clientId: sessionToken}, $set:{username: nickname}, (err) ->
+        callback err

   @verifyEmailByUsername = secure (client, username, callback)->
     {connection:{delegate}, sessionToken} = client


Improve error logging

Improve error logging

--- workers/social/lib/social/models/sidebar.coffee
+++ workers/social/lib/social/models/sidebar.coffee
@@ -180,7 +180,14 @@
     SocialChannel.byId client, id: workspace.channelId, (err, channel) ->

       if err
-        console.error 'Fetch workspace channel:', err
-        return failureFn err
+        data =
+          channelId   : workspace.channelId
+          workspaceId : workspace.getId()
+
+        console.error 'error: fetch workspace channel'
+        console.error JSON.stringify {data}
+        console.error JSON.stringify {err}
+
+        return failureFn()

       successFn()


KiteSubscription: subscribe user to kite plan

--- client/Social/Apps/Views/createkitemodal.coffee
+++ client/Social/Apps/Views/createkitemodal.coffee
@@ -82,7 +82,18 @@
     details = @modalTabs.forms.Details.getFormData()
     plans   = (form.getFormData() for form in @pricingForms)

-    log { details, plans }
+    KD.remote.api.JKite.create details, (err, kite)=>
+      return  KD.showError err if err
+      {dash} = Bongo
+      queue = plans.map (plan) -> ->
+        kite.createPlan plan, (err, kiteplan)->
+          return queue.fin err  if err
+          queue.fin()
+
+      dash queue, (err) =>
+        return KD.showError err if err
+        @destroy()
+

 class KitePricingFormView extends KDFormViewWithFields


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -7,8 +7,8 @@
     player.include 'Flickerable'

     # called with no arguments, flicker will toggle the player's alpha
-    # value between 0.9 (value provided above)
-    # and 0.5 (flickerable default) every 0.1 second, lasting 2 seconds total
+    # value between 0.9 (value provided above) and 0.5 (flickerable default)
+  every 0.1 second, lasting 2 seconds total
     player.flicker()

 @name Flickerable


small fix regexp

small fix regexp

--- markdown_to_json.coffee
+++ markdown_to_json.coffee
@@ -53,7 +53,7 @@
     item.links.push link
     findItem rows, index, item

-  else if /^#\s\w/.test(row)
+  else if /^#\s[A-Za-zА-Яа-я0-9]/.test(row)
     JSONSTRING.push item
     index -= 1
     findHead rows, index
@@ -70,7 +70,7 @@
   index += 1
   row = rows[index]

-  if /^#\s\w/.test(row)
+  if /^#\s[A-Za-zА-Яа-я0-9]/.test(row)
     item = {}
     item.name = row.replace(/^#\s/, '')
     item.links = []


Added: default values can be set for all future requests (useful for testing)

--- main.coffee
+++ main.coffee
@@ -12,22 +12,49 @@

 class FixtureManager

+
+    ###
+    # The value of those properties are set through @_resetDefaults
+    # or @setDefaultValues()
+    ###
+
     # Where the fixture files are
-    dirPath: './tests/fixtures/'
+    dirPath: null
     # If the script must be restrain to one doctype, which one
     doctypeTarget: null
     # If true, the script won't output anything
-    silent: false
+    silent: null
     # If set, will be executed at the end of the process
     callback: null
     # If true, will removed documents of concerned doctypes before loading docs
-    removeBeforeLoad: true
-
-    dataSystemUrl: "http://localhost:9101/"
+    removeBeforeLoad: null
+    # Data System URL
+    dataSystemUrl: null
+
+    defaultValues:
+        dirPath: './tests/fixtures/'
+        doctypeTarget: null
+        silent: false
+        callback: null
+        removeBeforeLoad: true
+        dataSystemUrl: "http://localhost:9101/"

     constructor: ->
+        @_resetDefaults()
         @client = new Client @dataSystemUrl

+    _resetDefaults:  ->
+        @dirPath = @defaultValues['dirPath']
+        @doctypeTarget = @defaultValues['doctypeTarget']
+        @silent = @defaultValues['silent']
+        @callback = @defaultValues['callback']
+        @removeBeforeLoad = @defaultValues['removeBeforeLoad']
+        @dataSystemUrl = @defaultValues['dataSystemUrl']
+
+    setDefaultValues: (opts) ->
+        for opt, value of opts
+            @defaultValues[opt] = value if @defaultValues[opt]?
+        @_resetDefaults()

     load: (opts) ->

@@ -62,12 +89,6 @@
         catch e
             @log "[ERROR] Cannot load fixtures -- #{e}".red

-    _resetDefaults:  ->
-        @dirPath = './tests/fixtures/'
-        @doctypeTarget = null
-        @silent = false
-        @callback = null
-        @removeBeforeLoad = true

     onRawFixturesLoad: (err, docs) =>


:ring::kissing_cat: Updated in browser at strd6.github.io/editor

--- main.coffee
+++ main.coffee
@@ -8,4 +8,7 @@
 app.get '/', (req, res) ->
   res.send req.query

+app.get '/yolo', (req, res) ->
+  res.send "heyy"
+
 app.listen(PORT)


Better SQL profiler.

Better SQL profiler.

Conflicts:
	main.coffee

--- main.coffee
+++ main.coffee
@@ -17,8 +17,15 @@
 'use strict'

 config = require "#{__dirname}/config.js"
-
+lib = require "#{__dirname}/lib.coffee"
+
+chalk = require 'chalk'
 anyDB = require 'any-db'
+transaction = require 'any-db-transaction'
+async = require 'async'
+express = require 'express'
+sync = require 'sync'
+

 dbConnection = anyDB.createPool config.DATABASE_URL, min: 2, max: 20
 dbConnection.query 'SELECT version()', [], (error, result) ->
@@ -35,40 +42,17 @@
 		for value, index in data
 			index++
 			while logged.indexOf("$#{index}") isnt -1
-				logged = logged.replace "$#{index}", "<#{value}>"
+				logged = logged.replace "$#{index}", chalk.blue(JSON.stringify(value))
 		start = Date.now()
 		@_query q, data, (error, result) ->
 			time = Date.now() - start
-			console.log "\n#{time} ms: #{logged}\n"
+			time = switch
+				when time < 10 then chalk.green("#{time} ms")
+				when time < 20 then chalk.yellow("#{time} ms")
+				else chalk.red("#{time} ms")
+			console.log "\n#{time}: #{logged}\n"
+			# console.log "\n#{time}: t: #{logged}\n"
 			cb(error, result)
-	dbConnection._begin = dbConnection.begin
-	dbConnection.begin = ->
-		console.log "\nBEGIN TRANSACTION\n"
-		tx = @_begin()
-		tx.____start = Date.now()
-		tx.__query = tx.query
-		tx.query = (q, data, cb) ->
-			logged = q
-			for value, index in data
-				index++
-				while logged.indexOf("$#{index}") isnt -1
-					logged = logged.replace "$#{index}", "<#{value}>"
-			start = Date.now()
-			@__query q, data, (error, result) ->
-				time = Date.now() - start
-				console.log "\n#{time} ms: t: #{logged}\n"
-				cb(error, result)
-		# doesn't work
-		#tx.__commit = tx.commit
-		#tx.commit = (a) ->
-			#console.log "\nCOMMIT, the whole thing took #{Date.now() - tx.____start} ms\n"
-			#tx.__commit a
-		return tx
-
-lib = require "#{__dirname}/lib.coffee"
-async = require 'async'
-express = require 'express'
-sync = require 'sync'

 app = express()
 app.enable 'trust proxy'


Usage of eval() is forbidden by FirefoxOS CSP

In privileged and certified FirefoxOS apps, the Content Security Policy forbids the usage of potentially hazardous functions such as eval(). This is a quick fix for the only occurrence of eval() in QuoJS.

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -139,8 +139,8 @@
         diff = parseInt(GESTURE.initial_angle - angle, 10)
         if Math.abs(diff) > 20 or GESTURE.angle_difference isnt 0
             i = 0
-            symbol = if GESTURE.angle_difference < 0 then "-" else "+"
-            eval "diff " + symbol + "= 180;" while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
+            symbol = if GESTURE.angle_difference < 0 then -1 else 1
+            diff += 180 * symbol while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
             GESTURE.angle_difference = parseInt(diff, 10)
             _trigger "rotating", angle: GESTURE.angle_difference


code cleanup

code cleanup

--- src/undo-manager.coffee
+++ src/undo-manager.coffee
@@ -22,6 +22,16 @@
   if delta.endLength < delta.startLength + offset
     lastChangeIndex = delta.endLength
   return lastChangeIndex
+
+_change = (source, dest) ->
+  if @stack[source].length > 0
+    change = @stack[source].pop()
+    _ignoreChanges.call(this, =>
+      @editor.applyDelta(change[source], { source: 'user' })
+      index = getLastChangeIndex(change[source])
+      @editor.setSelection(new Scribe.Range(@editor, index, index))
+    )
+    @stack[dest].push(change)

 _ignoreChanges = (fn) ->
   oldIgnoringChanges = @ignoringChanges
@@ -59,8 +69,9 @@
     )

   clear: ->
-    @undoStack = []
-    @redoStack = []
+    @stack =
+      undo: []
+      redo: []
     @oldDelta = @editor.getDelta()

   record: (changeDelta, oldDelta) ->
@@ -68,31 +79,24 @@
     @redoStack = []
     undoDelta = oldDelta.invert(changeDelta)
     timestamp = new Date().getTime()
-    if @lastRecorded + @options.delay > timestamp and @undoStack.length > 0
-      change = @undoStack.pop()
+    if @lastRecorded + @options.delay > timestamp and @stack.undo.length > 0
+      change = @stack.undo.pop()
       undoDelta = undoDelta.compose(change.undo)
       changeDelta = change.redo.compose(changeDelta)
     else
       @lastRecorded = timestamp
-    @undoStack.push({
+    @stack.undo.push({
       redo: changeDelta
       undo: undoDelta
     })
-    @undoStack.unshift() if @undoStack.length > @options.maxStack
+    @stack.undo.unshift() if @stack.undo.length > @options.maxStack

   redo: ->
-    if @redoStack.length > 0
-      change = @redoStack.pop()
-      _ignoreChanges.call(this, =>
-        @editor.applyDelta(change.redo, { source: 'user' })
-        index = getLastChangeIndex(change.redo)
-        @editor.setSelection(new Scribe.Range(@editor, index, index))
-      )
-      @undoStack.push(change)
+    _change.call(this, 'redo', 'undo')

   transformExternal: (delta) ->
     return if delta.isIdentity()
-    @undoStack = _.map(@undoStack, (change) ->
+    @stack['undo'] = _.map(@stack['undo'], (change) ->
       return {
         redo: delta.follows(change.redo, true)
         undo: change.undo.follows(delta, true)
@@ -100,14 +104,7 @@
     )

   undo: ->
-    if @undoStack.length > 0
-      change = @undoStack.pop()
-      _ignoreChanges.call(this, =>
-        @editor.applyDelta(change.undo, { source: 'user' })
-        index = getLastChangeIndex(change.undo)
-        @editor.setSelection(new Scribe.Range(@editor, index, index))
-      )
-      @redoStack.push(change)
+    _change.call(this, 'undo', 'redo')

 module.exports = Scribe


Disable autosave

Disable autosave

--- config.cson
+++ config.cson
@@ -14,7 +14,7 @@
     lintOnEdit: false
   react: {}
   autosave:
-    enabled: true
+    enabled: false
   "advanced-new-file":
     showFilesInAutoComplete: true
     suggestCurrentFilePath: true


Instanssi 2013 config

Instanssi 2013 config

--- config.cson
+++ config.cson
@@ -6,210 +6,189 @@
     enttec1:
       # path: "/dev/serial/by-id/usb-FTDI_FT245R_USB_FIFO_ENP9D7H7-if00-port0"
       # path: "/dev/serial/by-id/usb-FTDI_USB__-__Serial-if00-port0"
-      path: "/dev/serial/by-id/usb-FTDI_USB__-__Serial-if00-port0"
-      # path: "/dev/serial/by-id/usb-ENTTEC_DMX_USB_PRO_ENR35XBU-if00-port0"
+      # path: "/dev/serial/by-id/usb-FTDI_USB__-__Serial-if00-port0"
+      path: "/dev/serial/by-id/usb-ENTTEC_DMX_USB_PRO_ENR35XBU-if00-port0"
       type: "enttec"
   mapping:
     light:
-      # 10:
-      #   name: "sali rgb"
-      #   host: "enttec1"
-      #   type: "rgb"
-      #   address: 8
-
       0:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 1
+        address: 60
       1:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 6
+        address: 65
       2:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 11
+        address: 70
       3:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 16
+        address: 75
       4:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 21
+        address: 80
       5:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 26
+        address: 85
       6:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 31
+        address: 90
       7:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 36
+        address: 95
       8:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 41
+        address: 100
       9:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 46
+        address: 105
       10:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 51
+        address: 110
       11:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 56
+        address: 115
       12:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 61
+        address: 120
       13:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 66
+        address: 125
       14:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 71
+        address: 130
       15:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 76
+        address: 135
       16:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 81
+        address: 140
       17:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 86
+        address: 145
       18:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 91
+        address: 150
       19:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 96
+        address: 155
       20:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 101
+        address: 160
       21:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 106
+        address: 165
       22:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 111
+        address: 170
       23:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 116
+        address: 175
       24:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 121
+        address: 180
       25:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 126
+        address: 185
       26:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 131
+        address: 190
       27:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 136
+        address: 195
       28:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 141
+        address: 200
       29:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 146
+        address: 205
       30:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 151
+        address: 210
       31:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 156
+        address: 215
       32:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 161
+        address: 220
       33:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 166
+        address: 225
       34:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 171
+        address: 230
       35:
         name: "sali rgb"
         host: "enttec1"
         type: "rgb"
-        address: 176
-      36:
-        name: "sali rgb"
-        host: "enttec1"
-        type: "rgb"
-        address: 181
-      37:
-        name: "sali rgb"
-        host: "enttec1"
-        type: "rgb"
-        address: 186
-      38:
-        name: "sali rgb"
-        host: "enttec1"
-        type: "rgb"
-        address: 191
+        address: 235
 }


Use compileApp method on applications kite instead of compiling on client

--- client/app/MainApp/kodingappscontroller.coffee
+++ client/app/MainApp/kodingappscontroller.coffee
@@ -387,87 +387,37 @@
         log app, "app approved"
         callback?()

-  # We will move that to Server-Side GG
-
   compileApp:(name, callback)->

-    kallback = (app)=>
-
-      return warn "#{name}: No such app!" unless app
-
-      {source}      = app
-      {blocks}      = source
-      {nickname}    = KD.whoami().profile
-      orderedBlocks = []
-      blockStrings  = []
-      asyncStack    = []
-
-      for blockName, blockOptions of blocks
-        blockOptions.name = blockName
-        if blockOptions.order? and not isNaN(order = parseInt(blockOptions.order, 10))
-          orderedBlocks[order] = blockOptions
-        else
-          orderedBlocks.push blockOptions
-
-      if app.devMode
-        appResourceRoot = "/Users/#{nickname}/Sites/#{nickname}.koding.com/website/.applications"
-        appDevModePath  = "#{appResourceRoot}/#{__utils.slugify name}"
-
-        asyncStack.push (cb)=>
-          @kiteController.run "rm -rf #{escapeFilePath appDevModePath}", =>
-            @kiteController.run "test -d #{appResourceRoot} || mkdir #{appResourceRoot}", =>
-              @kiteController.run "ln -s #{escapeFilePath getAppPath app} #{escapeFilePath appDevModePath}", -> cb()
-
-      orderedBlocks.forEach (block)=>
-
-        if block.pre
-          asyncStack.push (cb)=> @addScript app, block.pre, cb
-
-        if block.files
-          {files} = block
-          files.forEach (file, index)=>
-            if "object" is typeof file
-              for fileName, fileExtras of file
-                do =>
-                  # log fileExtras.pre  if fileExtras.pre
-                  if fileExtras.pre
-                    asyncStack.push (cb)=> @addScript app, fileExtras.pre, cb
-                  # log fileName
-                  asyncStack.push (cb)=> @addScript app, fileName, cb
-                  # log fileExtras.post if fileExtras.post
-                  if fileExtras.post
-                    asyncStack.push (cb)=> @addScript app, fileExtras.post, cb
-            else
-              # log file
-              asyncStack.push (cb)=> @addScript app, file, cb
-        # log block.post if block.post
-        if block.post
-          asyncStack.push (cb)=> @addScript app, block.post, cb
-
-      async.parallel asyncStack, (error, result)=>
-
-        _final = "(function() {\n\n/* KDAPP STARTS */"
-        result.forEach (output)=>
-          _final += "\n\n/* BLOCK STARTS */\n\n"
-          _final += "#{if output then output else '//couldn\'t compile the hunk!'}"
-          _final += "\n\n/* BLOCK ENDS */\n\n"
-        _final += "/* KDAPP ENDS */\n\n}).call();"
-
-        _final = @defineApp app.name, _final
-        @saveCompiledApp app, _final, =>
-          callback?()
+    compileOnServer = (app)=>
+      return warn "#{name}: No such application!" unless app
+      appPath = getAppPath app
+      @kiteController.run
+        kiteName  : "applications"
+        method    : "compileApp"
+        withArgs  :
+          appPath : appPath
+      , (err)=>
+        if not err
+          @fetchCompiledApp app, (err, res)=>
+            if not err then @defineApp name, res
+            callback?()
+        else
+          warn "An error occured while compiling:", err
+          callback? yes

     unless @constructor.manifests[name]
-      @fetchApps (err, apps)=> kallback apps[name]
+      @fetchApps (err, apps)->
+        compileOnServer apps[name]
     else
-      @kiteController.run "stat #{escapeFilePath getAppPath @constructor.manifests[name]}", (err)=>
+      @kiteController.run "test -d #{escapeFilePath getAppPath @constructor.manifests[name]}", (err)=>
         if err
           new KDNotificationView
             title    : "App list is out-dated, refreshing apps..."
             duration : 2000
           @refreshApps noop
         else
-          kallback @constructor.manifests[name]
+          compileOnServer @constructor.manifests[name]

   installApp:(app, version='latest', callback)->


[replays] Empty message if no replays saved.

--- client/app/js/views/replays/replay_view.coffee
+++ client/app/js/views/replays/replay_view.coffee
@@ -15,6 +15,10 @@
       $row = $('<div/>').addClass('row-fluid')
       $row.append(groupHTML)
       $row.appendTo(@$el)
+
+    if @collection.length == 0
+      @$el.append($("<p/>").text("You have not saved any replays."))
+
     this

   deleteReplay: (e) =>


Client: Sidebar: Added new state data with getDataBinding

--- client/app/lib/components/sidebar/index.coffee
+++ client/app/lib/components/sidebar/index.coffee
@@ -15,6 +15,18 @@

   { getters, actions } = ActivityFlux

+  getDataBindings: ->
+    return {
+      publicChannels               : getters.followedPublicChannelThreadsWithSelectedChannel
+      privateChannels              : getters.followedPrivateChannelThreads
+      selectedThreadId             : getters.selectedChannelThreadId
+      stacks                       : EnvironmentFlux.getters.stacks
+      sharedMachines               : EnvironmentFlux.getters.sharedMachines
+      collaborationMachines        : EnvironmentFlux.getters.collaborationMachines
+      sharedMachineListItems       : EnvironmentFlux.getters.sharedMachineListItems
+      activeInvitationMachineId    : EnvironmentFlux.getters.activeInvitationMachineId
+      activeLeavingSharedMachineId : EnvironmentFlux.getters.activeLeavingSharedMachineId
+    }

   constructor: ->

@@ -24,15 +36,6 @@
      renderedStacksCount : 0

-  getDataBindings: ->
-    return {
-      publicChannels          : getters.followedPublicChannelThreadsWithSelectedChannel
-      privateChannels         : getters.followedPrivateChannelThreads
-      selectedThreadId        : getters.selectedChannelThreadId
-      stacks                  : EnvironmentFlux.getters.stacks
-      sharedMachines          : EnvironmentFlux.getters.sharedMachines
-      collaborationMachines   : EnvironmentFlux.getters.collaborationMachines
-    }

   componentDidMount: ->


uniques now correctly works with variables that aren't strings or numbers

--- src/util/uniques.coffee
+++ src/util/uniques.coffee
@@ -4,18 +4,21 @@
 uniques = (data, value, fetch, vars, depth) ->

   return [] if data is undefined
-  depth = vars.id.value if vars and depth is undefined
-  data  = [data] unless data instanceof Array
+
+  depth   = vars.id.value if vars and depth is undefined
+  data    = [data] unless data instanceof Array
+  vals    = []
+  lookups = []

   if value is undefined
     return data.reduce (p, c) ->
-      p.push c if p.indexOf(c) < 0
+      lookup = JSON.stringify(c)
+      if lookups.indexOf(lookup) < 0
+        p.push c if p.indexOf(c) < 0
+        lookups.push lookup
       p
     , []

-  data = [ data ] unless data instanceof Array
-  vals = []
-  lookups = []

   for d in data

@@ -31,10 +34,7 @@

       if val isnt undefined and val isnt null

-        if ["number", "string"].indexOf(typeof val) >= 0
-          lookup = val
-        else
-          lookup = JSON.stringify(val)
+        lookup = JSON.stringify(val)

         if lookup isnt undefined and lookups.indexOf(lookup) < 0
           vals.push val
@@ -42,4 +42,5 @@

   vals.sort (a, b) -> a - b

+
 module.exports = uniques


cleanup

cleanup

--- client/apps/edit/components/content2/section_controls/index.coffee
+++ client/apps/edit/components/content2/section_controls/index.coffee
@@ -26,8 +26,7 @@
   getControlsWidth: ->
     # used for classic layout only
     section = @props.section
-    if @props.isHero or
-     (section.get('type') is 'embed' and section.get('layout') is 'overflow')
+    if @props.isHero
         width = 1100
     else if section.get('layout')?.includes('overflow') or
      section.get('type') is 'image_set'


Fix bug for language check with one question

--- frontend/coffeescript/app/webform/views.coffee
+++ frontend/coffeescript/app/webform/views.coffee
@@ -29,8 +29,6 @@
             'click #prev_btn':          'prev_question'
             'click #language-select-btn':'language_select'

-        # languages:  []
-
         language_select: ( event ) ->
           @modalView = new LanguageSelectModal( { current: @current_language, view: this } )
           $('.modal').html( @modalView.render().el )
@@ -99,6 +97,8 @@
         # Defaults to English if an English label is present, otherwise returns
         # the first language it finds.
         get_translations: (field) ->
+          return if not field?
+
           if field.type is 'group'
             if typeof field.children[0].label is 'object'
               # Default to English, otherwise return first label found


Make route dynamic

Make route dynamic

--- src/routes/index.coffee
+++ src/routes/index.coffee
@@ -30,7 +30,7 @@
   for k,v of req.body
     form[wageTheftReportInfo.mapping[k]] = v

-  request.post {url: wageTheftReportInfo, form: form}, (err, httpResponse, body) ->
+  request.post {url: wageTheftReportInfo.post_url, form: form}, (err, httpResponse, body) ->
     return res.status(400).json({err, httpResponse, body}).end() if err?

     res.status(200).end()


Make route dynamic

Make route dynamic

--- src/routes/index.coffee
+++ src/routes/index.coffee
@@ -15,9 +15,24 @@
   res.header("Access-Control-Allow-Headers", "Content-Type, X-Requested-With")
   next()

+wageTheftReportInfo =
+  post_url: "https://docs.google.com/forms/d/12rKqdkVtQTcckc3ZyNp6AY7sZIKlj11k-QVFaAwKjCk/formResponse"
+  mapping:
+    "employer[name]": "entry.1223479717"
+    "employee[name]": "entry.1255459603"
+    "employee[email]": "entry.570498028"
+    "employee[phone]": "entry.229881285"
+
 router.post '/wage_theft/report', (req, res) ->
-  debug "Req: #{JSON.stringify req.body}"
-  res.end()
+  form = {}
+
+  for k,v of req.body
+    form[wageTheftReportInfo.mapping[k]] = v
+
+  request.post {url: wageTheftReportInfo, form: form}, (err, httpResponse, body) ->
+    return res.status(400).end() if err?
+
+    res.status(200).end()

 router.use '/employer_size', employer_size_router
 router.use '/survey', survey_router


StaticPage: Fix hardcoded commenter full name

--- servers/lib/crawler/staticpages/activity.coffee
+++ servers/lib/crawler/staticpages/activity.coffee
@@ -38,7 +38,7 @@
           #{avatarImage}

-          Sinan Yasar
+          #{fullName}

#{message.body}


Manually manage config subscription

Manually manage config subscription

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -20,7 +20,8 @@
     @modifiedSubscription = @item.onDidChangeModified? =>
       @updateModifiedStatus()

-    @subscribe atom.config.observe 'tabs.showIcons', => @updateIconVisibility()
+    @configSubscription = atom.config.observe 'tabs.showIcons', =>
+      @updateIconVisibility()

     @updateDataAttributes()
     @updateTitle()
@@ -51,6 +52,7 @@
     @titleSubscription?.dispose()
     @modifiedSubscription?.dispose()
     @iconSubscription?.dispose()
+    @configSubscription?.off() # Not a Disposable yet

     @destroyTooltip() if @hasBeenMousedOver


Add tooltips to the tabs showing the path

Closes #2

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -13,6 +13,16 @@
     @item.on? 'modified-status-changed', => @updateModifiedStatus()
     @updateTitle()
     @updateModifiedStatus()
+    @initializeTooltip()
+
+  initializeTooltip: ->
+    return unless @item.getPath
+    @setTooltip
+      title: @item.getPath()
+      delay:
+        show: 2000
+        hide: 100
+      placement: 'bottom'

   updateTitle: ->
     return if @updatingTitle


listen to path change instead of title change event and set path to new path

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -33,22 +33,22 @@
       @addEventListener 'dblclick', => @clearPreview()

   handleEvents: ->
-    titleChangedHandler = =>
+    pathChangedHandler = (@path) =>
       @updateDataAttributes()
       @updateTitle()
       @updateTooltip()

-    if typeof @item.onDidChangeTitle is 'function'
-      onDidChangeTitleDisposable = @item.onDidChangeTitle(titleChangedHandler)
-      if Disposable.isDisposable(onDidChangeTitleDisposable)
-        @subscriptions.add(onDidChangeTitleDisposable)
-      else
-        console.warn "::onDidChangeTitle does not return a valid Disposable!", @item
+    if typeof @item.onDidChangePath is 'function'
+      onDidChangePathDisposable = @item.onDidChangePath(pathChangedHandler)
+      if Disposable.isDisposable(onDidChangePathDisposable)
+        @subscriptions.add(onDidChangePathDisposable)
+      else
+        console.warn "::onDidChangePath does not return a valid Disposable!", @item
     else if typeof @item.on is 'function'
       #TODO Remove once old events are no longer supported
-      @item.on('title-changed', titleChangedHandler)
+      @item.on('path-changed', pathChangedHandler)
       @subscriptions.add dispose: =>
-        @item.off?('title-changed', titleChangedHandler)
+        @item.off?('path-changed', pathChangedHandler)

     iconChangedHandler = =>
       @updateIcon()


Fix log

Fix log

--- server/api/setOutcome.coffee
+++ server/api/setOutcome.coffee
@@ -49,7 +49,7 @@
         runId = contest + "r" + runId
     submit = await Submit.findById(req.params.submitId)
     problem = await Problem.findById(problemId)
-    logger.info("Store to database #{runId} #{problemId} #{problem._id}")
+    logger.info("Store to database #{runId} #{problemId} #{problem?._id}")
     if req.body.result in ["AC", "IG", "DQ"]
         logger.info("Force-storing to database result #{req.params.submitId}")
         submit.outcome = req.body.result


re-word password reset email

re-word password reset email

--- server/code/lib/email.coffee
+++ server/code/lib/email.coffee
@@ -45,7 +45,10 @@
     text: """
     Hi #{user.displayName},

-    To reset your ScraperWiki password, please go to:
+    Someone has requested a password reset for
+    your ScraperWiki account.
+
+    If this was you, please reset your password here:
       https://scraperwiki.com/set-password/#{token}

     Thanks,


Automatically show results at certain points (when need to reset, when finished with a problem, etc)

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -54,6 +54,16 @@
   # locally scoped jquery.
   $: (selector) ->
     $(selector, @el)
+
+  show_results_current: () =>
+    data = {'task_number' : @task_number-1}
+    $.postWithPrefix "#{@ajax_url}/get_results", data, (response) =>
+      if response.success
+        @results_container.after(response.html).remove()
+        @results_container = $('div.result-container')
+        @submit_evaluation_button = $('.submit-evaluation-button')
+        @submit_evaluation_button.click @message_post
+        Collapsible.setCollapsibles(@results_container)

   show_results: (event) =>
     status_item = $(event.target).parent().parent()
@@ -114,6 +124,7 @@
     if @child_type=="openended"
       @skip_button.hide()
     if @allow_reset=="True"
+      @show_results_current
       @reset_button.show()
       @submit_button.hide()
       @answer_area.attr("disabled", true)
@@ -149,6 +160,7 @@
       if @task_number<@task_count
         @next_problem()
       else
+        @show_results_current()
         @reset_button.show()

@@ -260,6 +272,7 @@
             @gentle_alert "Moved to next step."
           else
             @gentle_alert "Your score did not meet the criteria to move to the next step."
+            @show_results_current()
         else
           @errors_area.html(response.error)
     else


Bugfix: double close.

Bugfix: double close.

--- src/views/radius.coffee
+++ src/views/radius.coffee
@@ -6,7 +6,7 @@
         className: 'radius-controls'
         events:
           change: 'onChange'
-          'click #close-radius': 'onClose'
+          'click #close-radius': 'onUserClose'
         serializeData: ->
             selected: @selected or 750
             values: [
@@ -17,5 +17,5 @@
             @selected = $(ev.target).val()
             @render()
             app.request 'setRadiusFilter', @selected
-        onClose: (ev) ->
+        onUserClose: (ev) ->
             app.request 'clearRadiusFilter'


Update ru/menu_linux.cson (#247)

Update ru/menu_linux.cson (#247)

--- def/ru/menu_linux.cson
+++ def/ru/menu_linux.cson
@@ -176,17 +176,17 @@
           "Split Right":
             value: "Разделить справа"
           "Focus Next Pane":
-            value: "Сфокусироваться на следующей панеле"
+            value: "Сфокусироваться на следующей панели"
           "Focus Previous Pane":
-            value: "Сфокусироваться на предыдущей панеле"
+            value: "Сфокусироваться на предыдущей панели"
           "Focus Pane Above":
-            value: "Сфокусироваться на панеле выше"
+            value: "Сфокусироваться на панели выше"
           "Focus Pane Below":
-            value: "Сфокусироваться на панеле ниже"
+            value: "Сфокусироваться на панели ниже"
           "Focus Pane On Left":
-            value: "Сфокусироваться на панеле слева"
+            value: "Сфокусироваться на панели слева"
           "Focus Pane On Right":
-            value: "Сфокусироваться на панеле справа"
+            value: "Сфокусироваться на панели справа"
           "Close Pane":
             value: "Закрыть панель"
       Developer:
@@ -300,6 +300,6 @@
       "Search Issues":
         value: "Найти проблему"
       "About Atom":
-        value: "О Atom"
+        value: "Об Atom"
       "Welcome Guide":
         value: "Добро пожаловать!"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -67,6 +67,6 @@
   equals player.I.velocity.y, 0

 test "should correctly track facing direction", ->
-  player.I.velocity
+  player.I.velocity.equal(Point(1, ))

 module()


Remove console.log calls

Remove console.log calls

--- js/app.coffee
+++ js/app.coffee
@@ -178,24 +178,20 @@
       @builds.removeAll()
       @builds_have_been_loaded(false)

-    console.log("Fetching builds from " + path)
     position = {offset: page * @builds_per_page, limit: @builds_per_page}

     $.getJSON path, position, (data) =>
       @builds_list_path = path
-      console.log(data)
       @builds(new CI.inner.Build d for d in data)
       @builds_have_been_loaded(true)
     .fail (request) => VM.error.display()

   loadRecentBuilds: (page, refresh) =>
-    console.log("loadRecentBuilds " + page + " " + refresh)
     @loadBuilds('/api/v1/recent-builds', page, refresh)

   loadOrg: (username, page, refresh) =>
     if !@projects_have_been_loaded() then @loadProjects()

-    console.log("loadOrg: loading builds")
     @loadBuilds("/api/v1/organization/#{username}", page, refresh)

     if not refresh
@@ -415,8 +411,6 @@
     (cx) ->
       # github allows '/' is branch names, so match more broadly and combine them
       branch = cx.params.splat.join('/')
-      console.log("params: " + cx.params)
-      console.log("splat: " + cx.params.splat)

       VM.current_page new CI.inner.ProjectBranchPage
         username: cx.params.username


bugfix on contact

bugfix on contact

--- server/models/contact.coffee
+++ server/models/contact.coffee
@@ -14,12 +14,13 @@

 Contact::includePicture = (callback) ->
     if @_attachments?.picture
-        stream = @getFile 'picture', (err) ->
-            log.error "Contact #{contact.id} getting picture", err if err?
+        stream = @getFile 'picture', (err) =>
+            log.error "Contact #{@id} getting picture", err if err?
         stream_to_buffer_array stream, (err, parts) =>
             return callback err if err
             base64 = Buffer.concat(parts).toString('base64')
             avatar = "data:image/jpeg;base64," + base64
+            @datapoints ?= []
             @datapoints.push
                 name: 'avatar'
                 value: avatar


avoid 1 request to the DS by message in a conversation

--- server/models/message.coffee
+++ server/models/message.coffee
@@ -577,6 +577,7 @@

     # we update all messages to the new conversationID
     async.eachSeries rows, (row, cb) ->
+        return cb null if row.value is pickedConversationID
         Message.find row.id, (err, message) ->
             log.warn "Cant get message #{row.id}, ignoring" if err
             if err or message.conversationID is pickedConversationID


Remove wwls quotes

Remove wwls quotes

--- src/scripts/wwls.coffee
+++ src/scripts/wwls.coffee
@@ -32,4 +32,4 @@

 module.exports = (robot) ->
   robot.hear /what would linus say/i, (msg) ->
-    msg.send "\"#{msg.random quotes}\""
+    msg.send msg.random quotes


make hover and active state work correctly in image plugin

--- plugins/image.coffee
+++ plugins/image.coffee
@@ -111,6 +111,7 @@

             id = "#{@options.uuid}-image"
             buttonset.append jQuery("<input id=\"#{id}\" type=\"checkbox\" /><label for=\"#{id}\" class=\"image_button\" >Image</label>").button()
+            buttonset.children("label").unbind('mouseout')
             button = jQuery "##{id}", buttonset
             button.bind "change", (event) ->
                 if widget.options.dialog.dialog "isOpen"
@@ -148,6 +149,8 @@
         _init: ->

         _openDialog: ->
+            jQuery('.image_button').addClass('ui-state-active')
+
             # Update active Image
             jQuery(".#{@widgetName}-activeImage").attr "src", jQuery(".#{@widgetName}-imageThumbnailActive").first().attr "src"

@@ -161,7 +164,7 @@
             @options.dialog.dialog("open")

         _closeDialog: ->
+            jQuery('.image_button').removeClass('ui-state-active')
             @options.dialog.dialog("close")

-
-)(jQuery)+)(jQuery)


rtmp: Do not sort messages by timestamp

rtmp: Do not sort messages by timestamp

--- rtmp.coffee
+++ rtmp.coffee
@@ -213,8 +213,6 @@
   params.messageStreamID = 1
   params.originalTimestamp = params.timestamp
   queuedRTMPMessages.push params
-  queuedRTMPMessages.sort (a, b) ->
-    a.timestamp - b.timestamp

   setImmediate flushRTMPMessages

@@ -225,8 +223,6 @@
   params.messageStreamID = 1
   params.originalTimestamp = params.timestamp
   queuedRTMPMessages.push params
-  queuedRTMPMessages.sort (a, b) ->
-    a.timestamp - b.timestamp

   setImmediate flushRTMPMessages


rtsp: Add debug log

rtsp: Add debug log

--- rtsp.coffee
+++ rtsp.coffee
@@ -347,6 +347,7 @@

   sendEOS: (stream) ->
     for clientID, client of stream.rtspClients
+      logger.debug "[rtsp:#{stream.id}] sending goodbye to client #{clientID}"
       buf = new Buffer rtp.createGoodbye
         ssrcs: [ client.videoSSRC ]
       if client.useTCPForVideo


:lipstick: Remove unneeded state.width truthy test

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -60,7 +60,7 @@
     @selectEntryForPath(state.selectedPath) if state.selectedPath
     @focusAfterAttach = state.hasFocus
     @scrollTopAfterAttach = state.scrollTop if state.scrollTop
-    @width(state.width) if state.width and state.width > 0
+    @width(state.width) if state.width > 0
     @attach() if state.attached

   afterAttach: (onDom) ->


Tooltips have help style by default for now.

--- core/app/backbone/factlink/tooltip_mixin.coffee
+++ core/app/backbone/factlink/tooltip_mixin.coffee
@@ -18,6 +18,7 @@
     focus_on: null
     margin: 0
     orthogonalOffset: 0
+    helpStyle: true

   tooltipAdd: (selector, options) ->
     @tooltip_options = _.extend {}, @default_options, options


remove wakatime cli directory when package is uninstalled

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -16,10 +16,15 @@

 latestCLIVersion = '4.1.0'

+packageVersion = null
+unloadHandler = null
+lastHeartbeat = 0
+lastFile = ''
+
 module.exports =

   activate: (state) ->
-    window.VERSION = atom.packages.getLoadedPackage('wakatime').metadata.version
+    packageVersion = atom.packages.getLoadedPackage('wakatime').metadata.version

     if not isCLIInstalled()
       installCLI(->
@@ -41,12 +46,10 @@
             OK: -> installPython()
             Cancel: -> window.alert('Please install Python (https://www.python.org/downloads/) and restart Atom to enable the WakaTime plugin.')
     )
+    cleanupOnUninstall()
     setupConfig()
     setupEventHandlers()
-    console.log 'WakaTime v'+VERSION+' loaded.'
-
-lastHeartbeat = 0
-lastFile = ''
+    console.log 'WakaTime v'+packageVersion+' loaded.'

 enoughTimePassed = (time) ->
   return lastHeartbeat + 120000 < time
@@ -57,6 +60,18 @@
       apikey: ""
       ignore: ["^/var/", "^/tmp/", "^/private/", "COMMIT_EDITMSG$", "PULLREQ_EDITMSG$", "MERGE_MSG$"]
     atom.config.set("wakatime", defaults)
+
+cleanupOnUninstall = () ->
+  if unloadHandler?
+    unloadHandler.dispose()
+    unloadHandler = null
+  unloadHandler = atom.packages.onDidUnloadPackage((p) ->
+    if p? and p.name == 'wakatime'
+      removeCLI()
+      if unloadHandler?
+        unloadHandler.dispose()
+        unloadHandler = null
+  )

 setupEventHandlers = () ->
   atom.workspace.observeTextEditors (editor) =>
@@ -208,16 +223,24 @@

 extractCLI = (zipFile, callback) ->
   console.log 'Extracting wakatime-master.zip file...'
+  removeCLI(->
+    unzip(zipFile, __dirname, callback)
+  )
+
+removeCLI = (callback) ->
   if fs.existsSync(__dirname + path.sep + 'wakatime-master')
     try
       rimraf(__dirname + path.sep + 'wakatime-master', ->
-        unzip(zipFile, __dirname, callback)
+        if callback?
+          callback()
       )
     catch e
       console.warn e
-      unzip(zipFile, __dirname, callback)
+      if callback?
+        callback()
   else
-    unzip(zipFile, __dirname, callback)
+    if callback?
+      callback()

 downloadFile = (url, outputFile, callback) ->
   r = request(url)
@@ -253,7 +276,7 @@
         unless apikey
           return

-        args = [cliLocation(), '--file', file.path, '--key', apikey, '--plugin', 'atom-wakatime/' + VERSION]
+        args = [cliLocation(), '--file', file.path, '--key', apikey, '--plugin', 'atom-wakatime/' + packageVersion]
         if isWrite
           args.push('--write')
         if lineno?


test(cli-test.coffee): Enhance "--header" testing

Broke test into two, testing for acceptability based on header value. The latter now triggers HTTP
406 failure. Additional minor description changes elsewhere.

--- test/e2e/cli-test.coffee
+++ test/e2e/cli-test.coffee
@@ -33,8 +33,6 @@
 stdout = ''
 report = ''
 exitStatus = null
-
-receivedRequest = {}

 #
 # To dump individual raw test results:
@@ -209,7 +207,7 @@

       describe 'when invoked with "--generate-hooks" option', () ->

-        describe 'by itself', () ->
+        describe 'by itself (use package-provided template)', () ->

           runGenHooksTestAsync = (done) ->
             ramlFile = "#{RAML_DIR}/single-get.raml"
@@ -415,7 +413,7 @@
       before (done) ->
         runRamlIncludesTestAsync done

-      it 'should print count of tests run', () ->
+      it 'should print count of passing tests run', () ->
         expect(stdout).to.have.string('1 passing')

       it 'should exit normally', () ->
@@ -457,20 +455,42 @@
       describe 'when invoked with "--header" option', () ->

         receivedRequest = {}
+        producedMediaType = 'application/vnd.api+json'
+        reqMediaType = undefined

         runHeaderTestAsync = (done) ->
-          extraHeader = 'Accept:application/json'
+          extraHeader = "Accept:#{reqMediaType}"
           ramlFile = "#{RAML_DIR}/single-get.raml"
           cmd = "#{ABAO_BIN} #{ramlFile} --server #{SERVER} --header #{extraHeader}"

           app = express()

+          app.use (req, res, next) ->
+            receivedRequest = req
+            next()
+
+          app.use (req, res, next) ->
+            err = null
+            produces = ["#{producedMediaType}"]
+            if !req.accepts produces
+              err = new Error('Not Acceptable')
+              err.status = 406
+            next(err)
+
           app.get '/machines', (req, res) ->
-            receivedRequest = req
             machine =
               type: 'bulldozer'
               name: 'willy'
-            res.status(200).json([machine])
+            res.setHeader 'Content-Type', "#{producedMediaType}"
+            res.status(200).send([machine])
+
+          app.use (err, req, res, next) ->
+            res.status(err.status || 500)
+              .json({
+                message: err.message,
+                stack: err.stack
+              })
+            return

           server = app.listen PORT, () ->
             execCommand cmd, () ->
@@ -478,17 +498,41 @@

           server.on 'close', done

-        before (done) ->
-          runHeaderTestAsync done
-
-        it 'should have an additional header in the request', () ->
-          expect(receivedRequest.headers.accept).to.equal('application/json')
-
-        it 'should print count of tests run', () ->
-          expect(stdout).to.have.string('1 passing')
-
-        it 'should exit normally', () ->
-          expect(exitStatus).to.equal(0)
+        context 'with "accept"-able request', () ->
+
+          before (done) ->
+            reqMediaType = "#{producedMediaType}"
+            runHeaderTestAsync done
+
+          it 'should have the additional header in the request', () ->
+            expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
+
+          it 'should print count of passing tests run', () ->
+            expect(stdout).to.have.string('1 passing')
+
+          it 'should exit normally', () ->
+            expect(exitStatus).to.equal(0)
+
+
+        context 'with un-"accept"-able request', () ->
+
+          before (done) ->
+            reqMediaType = 'application/json'
+            runHeaderTestAsync done
+
+          it 'should have the additional header in the request', () ->
+            expect(receivedRequest.headers.accept).to.equal("#{reqMediaType}")
+
+          # Errors thrown by Mocha show up in stdout; those by Abao in stderr.
+          it 'Mocha should throw an error', () ->
+            detail = "Error: expected 406 to equal '200'"
+            expect(stdout).to.have.string(detail)
+
+          it 'should run test but not complete', () ->
+            expect(stdout).to.have.string('1 failing')
+
+          it 'should exit due to error', () ->
+            expect(exitStatus).to.equal(1)

       describe 'when invoked with "--hookfiles" option', () ->
@@ -502,8 +546,11 @@

           app = express()

+          app.use (req, res, next) ->
+            receivedRequest = req
+            next()
+
           app.get '/machines', (req, res) ->
-            receivedRequest = req
             machine =
               type: 'bulldozer'
               name: 'willy'
@@ -655,7 +702,7 @@
           expect(exitStatus).to.equal(0)

-        describe 'when expecting error', () ->
+        describe 'when expecting validation to fail', () ->

           runSchemaFailTestAsync = (done) ->
             pattern = "#{SCHEMA_DIR}/*.json"
@@ -666,7 +713,7 @@

             app.get '/machines', (req, res) ->
               machine =
-                typO: 'bulldozer'       # 'type' != 'typ0'
+                typO: 'bulldozer'       # 'type' != 'typO'
                 name: 'willy'
               res.status(200).json([machine])


test(test/e2e/cli-test.coffee): Fix last commit

Fixed Allow header join. Changed verbage of test description.

--- test/e2e/cli-test.coffee
+++ test/e2e/cli-test.coffee
@@ -384,7 +384,7 @@

         app.options '/machines', (req, res, next) ->
           allow = ['OPTIONS', 'HEAD', 'GET']
-          res.setHeader 'Allow', allow.join ', '
+          res.setHeader 'Allow', allow.join ','
           res.setHeader 'Cache-Control', 'no-cache, no-store, must-revalidate'
           res.status(204).end()
           next()
@@ -456,7 +456,7 @@
         expect(optionsResponse.body).to.be.empty
         expect(headResponse.body).to.be.empty

-      it 'GET and HEAD responses should have identical headers', () ->
+      it 'GET and HEAD responses should have equivalent headers', () ->
         expect(getResponse.headers).to.deep.equal(headResponse.headers)

       it 'should exit normally', () ->