Remove unused login/logout methods

Remove unused login/logout methods

--- src/user/model.coffee
+++ src/user/model.coffee
@@ -19,13 +19,6 @@
   update: (data) ->
     _.extend(this, data.user)
     @courses = _.compact _.map data.courses, (course) -> new Course(course) if course.is_concept_coach
-
-    @channel.emit('change')
-    delete @isLoggingOut
-
-  logout: ->
-    _.extend(this, BLANK_USER)
-    @isLoggingOut = true
     @channel.emit('change')

   get: ->
@@ -51,21 +44,14 @@
     @channel.emit('change')
     @ensureStatusLoaded(true) # re-fetch course list from server

-
-  urlForLogin:  -> @_urlWithReturn('login')
-  urlForLogout: -> @_urlWithReturn('logout')
-
-  _urlWithReturn: (type) ->
-    self = encodeURIComponent(window.location.href)
-    @endpoints[type] + '?parent=' + self
-
   removeCourse: (course) ->
     index = @courses.indexOf(course)
     @courses.splice(index, 1) unless index is -1
     @channel.emit('change')

   _signalLogoutCompleted: ->
-    @isLoggingOut = false
+    _.extend(this, BLANK_USER)
+    @isLoggingOut = true
     @channel.emit('logout.received')

 <<<<<<< fdd8b1358cb4fba314c089ee25e1a96cd60f4be8


Update misc.coffee to handle tooltip exception

to handle exception thrown when tooltip mode is chosen to be "NONE" and mouse is hovered over the svg elements.

--- src/utils/misc.coffee
+++ src/utils/misc.coffee
@@ -202,7 +202,7 @@
         return bbox

       getTextBBox: (svgTextElement) ->
-        if svgTextElement isnt null then svgTextElement.getBBox() else {}
+        return if svgTextElement isnt null then svgTextElement.getBBox() else {}

       getWidestTickWidth: (svg, axisKey) ->
         max = 0


Send PM, throw error for unmatched GitHub login

Throw error on failure of GitHub user login resolve
Always send PM to requesting user
Test on Heroku with hubot-hipchat
Test issues mentions and assignments

TODO: test PRs mentions and assignments

--- src/github-notify.coffee
+++ src/github-notify.coffee
@@ -55,7 +55,7 @@
 # Resolve user name to a potential GitHub username by using, in order:
 #   - GitHub credentials as handled by the github-credentials.coffee script
 #   - environment variables in the form of HUBOT_GITHUB_USER_.*
-#   - the exact chat name
+#   - throws an Error if the username can't be matched
 #
 # user - The chat username to be resolved
 #
@@ -64,7 +64,8 @@
   name = user.name.replace('@', '')
   resolve_cred = (u) -> u.githubLogin
   resolve_env = (n) -> process.env["HUBOT_GITHUB_USER_#{n.replace(/\s/g, '_').toUpperCase()}"]
-  resolve_cred(user) or resolve_env(name) or resolve_env(name.split(' ')[0]) or name
+  resolve_cred(user) or resolve_env(name) or resolve_env(name.split(' ')[0]) or
+  throw new Error("Sorry, I can't find any GitHub logins for #{name}. You can specify one by telling me 'i am githubusername'.")

 # Resolve all parameters for a given subscription/unsubscription request:
 # chat user, qualified GitHub user, kind of notification, repository, qualified GitHub repository.
@@ -206,48 +207,84 @@
   # return the array
   userInfos

+# Send a private message to multiple users.
+#
+# robot - The current robot instance
+# user - A User array, each one is as stored in a `respond` msg object
+# message - The private message String
+#
+# Returns nothing.
+private_messages = (robot, users, message) ->
+  users.forEach (user) ->
+    private_message robot, user, message
+
+# Send a private message to a given user.
+#
+# robot - The current robot instance
+# user - A User, as stored in a `respond` msg object
+# message - The private message String
+#
+# Returns nothing.
+private_message = (robot, user, message) ->
+  user = get_pm_user user
+  robot.send {user: user}, message
+
+# Delete the reply_to information from a given User,
+# so that it is possible to force a private message on send.
+#
+# user - A User instance
+#
+# Returns nothing.
+get_pm_user = (user) ->
+  delete user.reply_to
+  user
+
 module.exports = (robot) ->

   github = require('githubot')(robot)

   # handle notification subscriptions
   robot.respond NOTIFY_REGEX, (msg) ->
-    params = resolve_params github, msg
     try
+      params = resolve_params github, msg
       add_notification(robot, params)
       reply = "You will now receive notifications for #{params.kind} #{(if params.qualified_repo? then 'in ' + params.qualified_repo else 'everywhere')}."
     catch error
       reply = error
     finally
       robot.brain.save
-    robot.send params.user, reply
+      private_message robot, get_pm_user(msg.message.user), reply

   # handle notification unsubscriptions
   robot.respond UNNOTIFY_REGEX, (msg) ->
-    params = resolve_params github, msg
     try
+      params = resolve_params github, msg
       remove_notification(robot, params)
       reply = "You will no longer receive notifications for #{params.kind} #{(if params.qualified_repo? then 'in ' + params.repo else 'anywhere')}."
     catch error
       reply = error
     finally
       robot.brain.save
-    robot.send params.user, reply
+      private_message robot, get_pm_user(msg.message.user), reply

   # handle notification subscription listing
   robot.respond LIST_REGEX, (msg) ->
-    params = resolve_params github, msg, 2
-    # get the notifications
-    userSettings = _.find(robot.brain.get(GITHUB_NOTIFY_PRE + params.kind), {userName: params.qualified_user}) or {}
-    userNotifications = userSettings.userNotifications or []
-    # build an appropriate response by joining all checked repositories
-    if userNotifications?.length > 0
-      s = _.map(userNotifications, (n) ->
-        if n is true then 'every repository' else n
-      ).join(', ')
-      reply = "You will receive notifications for #{params.kind} in #{s}."
-    else reply = "You will receive no notifications for #{params.kind}."
-    robot.send params.user, reply
+    try
+      params = resolve_params github, msg, 2
+      # get the notifications
+      userSettings = _.find(robot.brain.get(GITHUB_NOTIFY_PRE + params.kind), {userName: params.qualified_user}) or {}
+      userNotifications = userSettings.userNotifications or []
+      # build an appropriate response by joining all checked repositories
+      if userNotifications?.length > 0
+        s = _.map(userNotifications, (n) ->
+          if n is true then 'every repository' else n
+        ).join(', ')
+        reply = "You will receive notifications for #{params.kind} in #{s}."
+      else reply = "You will receive no notifications for #{params.kind}."
+    catch error
+      reply = error
+    finally
+      private_message robot, get_pm_user(msg.message.user), reply

   # handle new comments and new issue assignments
   robot.router.post '/hubot/gh-notify', (req, res) ->
@@ -264,22 +301,18 @@
     if (event is 'issues' and payload.action is 'opened')
       # TODO: check when PRs are opened
       userInfos = on_commented_issue(robot, payload.issue, payload.repository, mentions_users)
-      userInfos.forEach (userInfo) ->
-        robot.send userInfo, "You have been mentioned in a new issue by #{payload.issue.user.login} in #{payload.repository.full_name}: #{payload.issue.html_url}."
+      private_messages robot, userInfos, "You have been mentioned in a new issue by #{payload.issue.user.login} in #{payload.repository.full_name}: #{payload.issue.html_url}."
     else if (event is 'issue_comment' and payload.action is 'created')
       # TODO: check when PRs are commented on
       userInfos = on_commented_issue(robot, payload.comment, payload.repository, mentions_users)
-      userInfos.forEach (userInfo) ->
-        robot.send userInfo, "You have been mentioned in a new comment by #{payload.comment.user.login} in #{payload.repository.full_name}: #{payload.comment.html_url}."
+      private_messages robot, userInfos, "You have been mentioned in a new comment by #{payload.comment.user.login} in #{payload.repository.full_name}: #{payload.comment.html_url}."
     else if (event is 'issues' and payload.action is 'assigned')
       userInfos = on_assigned_issue(robot, payload.assignee, payload.repository, assignments_users)
-      userInfos.forEach (userInfo) ->
-        robot.send userInfo, "You have been assigned to an issue in #{payload.repository.full_name}: #{payload.issue.html_url}."
+      private_messages robot, userInfos, "You have been assigned to an issue in #{payload.repository.full_name}: #{payload.issue.html_url}."
     else if (event is 'pull_request' and payload.action is 'assigned')
       # TODO: check this fella, if OK merge with previous condition check
       userInfos = on_assigned_issue(robot, payload.assignee, payload.repository, assignments_users)
-      userInfos.forEach (userInfo) ->
-        robot.send userInfo, "You have been assigned to a PR in #{payload.repository.full_name}: #{payload.issue.html_url}."
+      private_messages robot, userInfos, "You have been assigned to a PR in #{payload.repository.full_name}: #{payload.issue.html_url}."

     res.send 'HOLO YOLO'


Fix processing function tests

Fix processing function tests

--- spec/InPort.coffee
+++ spec/InPort.coffee
@@ -153,7 +153,7 @@
       s = new socket.InternalSocket
       p.attach s
       p.on 'data', (data) ->
-        chai.expect(data).to.equal 'noflo is awesome'
+        chai.expect(data).to.equal 'awesome'
         done()
       s.send 'awesome'

@@ -170,24 +170,36 @@
   describe 'with processing shorthand', ->
     it 'should create a port with a callback', ->
       s = new socket.InternalSocket
-      ps = new ports
-        outPorts:
+      ps =
+        outPorts: new ports.OutPorts
           out: new outport
-      ps.add 'in', (packet, component) ->
-        chai.expect(packet).to.equal 'some-data'
-        chai.assert component.outPorts.out instanceof outport
+        inPorts: new ports.InPorts
+      ps.inPorts.add 'in', (event, payload) ->
+        return unless event is 'data'
+        chai.expect(payload).to.equal 'some-data'
       chai.assert ps.inPorts.in instanceof inport
       ps.inPorts.in.attach s
       s.send 'some-data'

     it 'should also accept metadata (i.e. options) when provided', (done) ->
       s = new socket.InternalSocket
-      ps = new ports
-      ps.add 'in',
+      expectedEvents = [
+        'connect'
+        'data'
+        'disconnect'
+      ]
+      ps =
+        outPorts: new ports.OutPorts
+          out: new outport
+        inPorts: new ports.InPorts
+      ps.inPorts.add 'in',
         datatype: 'string'
         required: true
-      , (packet, component) ->
-        chai.expect(packet).to.equal 'some-data'
+      , (event, payload) ->
+        chai.expect(event).to.equal expectedEvents.shift()
+        return unless event is 'data'
+        chai.expect(payload).to.equal 'some-data'
         done()
       ps.inPorts.in.attach s
       s.send 'some-data'
+      s.disconnect()


regex tweaks

regex tweaks

--- lib/build.coffee
+++ lib/build.coffee
@@ -109,7 +109,7 @@
       if(atom.workspace.getActiveTextEditor().buffer?.file?)
         isWin = /^win/.test(process.platform)
         path = atom.workspace.getActiveTextEditor().buffer.file.path
-        projectPath = if isWin then @root+'\\src.' else @root+'/src/'
+        projectPath = if isWin then @root+'\\src' else @root+'/src'
         console.log('projectPath : '+projectPath)
         pathHeRegex = ///#{projectPath}///
         console.log('path : '+path)
@@ -119,6 +119,7 @@
           fileBaseName = atom.workspace.getActiveTextEditor().buffer.file.getBaseName()
           folderNamePath = path.replace ///#{fileBaseName}///, ''
           folderNamePath = folderNamePath.replace pathHeRegex, ''
+          folderNamePath = folderNamePath.substring(1)
           console.log('folderNamePath '+folderNamePaths)
           folderName =  if isWin then folderNamePath.split("\\") else folderNamePath.split "/"
           fileName = fileBaseName.split "."


Removed code not useful anymore

Removed code not useful anymore

--- lib/debug.coffee
+++ lib/debug.coffee
@@ -101,13 +101,6 @@
           value: @isExtraEnabled.bind(@, tag)
         _log:
           value: @_getLogFunc "[#{tag}]"
-        name:
-          value: tag
-          enumerable: true
-        enabled:
-          get: -> @isEnabled()
-          set: (b) -> if b is true then @enable() else @disable()
-          enumerable: true
         toString:
           value: -> "[#{tag}: #{if @isEnabled() then 'enabled' else 'disabled'}]"
     @ex[tag]


Ignore some more factoids.

Ignore some more factoids.

--- lib/logic.coffee
+++ lib/logic.coffee
@@ -47,6 +47,11 @@
   "these"
   "they"
   "that"
+  "who"
+  "what"
+  "where"
+  "when"
+  "why"
 ]

 userIdsToNames = {}


update mongo adapter to not nest json0 data; clean up throws in mongo adapter

type, version, and docName are always stored on _type, _v, and _id.
When the type isn't json0, the data is stored under the data property

--- lib/mongo.coffee
+++ lib/mongo.coffee
@@ -46,9 +46,40 @@
   # Deleted documents are kept around so that we can start their version from
   # the last version if they get recreated. When they are deleted, their type
   # is set to null, so don't return any documents with a null type.
-  query.$query.type = {$ne: null} unless query.$query.type
+  query.$query._type = {$ne: null} unless query.$query._type

   return query
+
+castToDoc = (docName, data) ->
+  doc = if data.type is 'http://sharejs.org/types/JSONv0'
+    data.data || {}
+  else
+    data: if data.data? then data.data else null
+  doc._type = data.type || null
+  doc._v = data.v
+  doc._id = docName
+  return doc
+
+castToSnapshot = (doc) ->
+  return unless doc
+  type = doc._type
+  v = doc._v
+  docName = doc._id
+  delete doc._type
+  delete doc._v
+  delete doc._id
+  if type is 'http://sharejs.org/types/JSONv0'
+    data =
+      data: doc
+      type: type
+      v: v
+      docName: docName
+  else
+    data = doc
+    data.type = type
+    data.v = v
+    data.docName = docName
+  return data

 # mongo is a mongoskin client. Create with:
 #  mongo.db('localhost:27017/tx?auto_reconnect', safe:true)
@@ -58,25 +89,24 @@
   name: 'mongo'

   create: (cName, docName, data, callback) ->
-    data._id = docName
-    mongo.collection(cName).insert data, callback
+    return callback 'db already closed' if @closed
+    doc = castToDoc docName, data
+    mongo.collection(cName).insert doc, callback

   close: ->
-    throw new Error 'db already closed' if @closed
+    return callback 'db already closed' if @closed
     mongo.close()
     @closed = true

   getSnapshot: (cName, docName, callback) ->
-    throw new Error 'db already closed' if @closed
-    mongo.collection(cName).findOne {_id:docName}, (err, data) ->
-      delete data._id if data
-      callback err, data
+    return callback 'db already closed' if @closed
+    mongo.collection(cName).findOne {_id:docName}, (err, doc) ->
+      callback err, castToSnapshot(doc)

   setSnapshot: (cName, docName, data, callback) ->
-    throw new Error 'db already closed' if @closed
-    data.data = null if data.data is undefined
-    data.type = null if data.type is undefined
-    mongo.collection(cName).update {_id:docName}, {$set:data}, {upsert:true}, callback
+    return callback 'db already closed' if @closed
+    doc = castToDoc docName, data
+    mongo.collection(cName).update {_id:docName}, doc, {upsert:true}, callback

   query: (cName, inputQuery, callback) ->
     return callback 'db already closed' if @closed
@@ -91,18 +121,11 @@
         cursor[item[0]] item[1]

       cursor.toArray (err, results) ->
-        if results then for r in results
-          r.docName = r._id
-          delete r._id
-
-        try
-          callback err, results
-        catch e
-          console.log e.stack
-          throw e
-
+        results = results && results.map(castToSnapshot)
+        callback err, results

   queryDoc: (cName, docName, inputQuery, callback) ->
+    return callback 'db already closed' if @closed
     query = normalizeQuery inputQuery

     if query.$query._id
@@ -110,11 +133,8 @@
     else
       query.$query._id = docName

-    mongo.collection(cName).findOne query, (err, result) ->
-      if result
-        result.docName = docName
-        delete result._id
-      callback err, result
+    mongo.collection(cName).findOne query, (err, doc) ->
+      callback err, castToSnapshot(doc)

   # Test whether an operation will make the document its applied to match the specified query.
   # This function doesn't really have enough information to know in all cases, but if we can determine
@@ -130,4 +150,3 @@

   # Test if a document matches a particular query. Should be synchronous and return true or false.
   #matchesQuery: null # (query, doc) ->
-


updated to new version of atom

updated to new version of atom

--- lib/mylyn.coffee
+++ lib/mylyn.coffee
@@ -64,12 +64,20 @@
       task

   reloadTreeView:()=>
-      view = @view.createView()
-      selectedPath = view.selectedEntry()?.getPath()
-      view.updateRoots()
-      if selectedPath
-        view.selectEntryForPath selectedPath
-      @rebuild()
+      console.log @view
+
+
+      @view.treeViewOpenPromise
+                .then (view)=>
+                      selectedPath = view.selectedEntry()?.getPath()
+                      view.updateRoots()
+                      if selectedPath
+                        view.selectEntryForPath selectedPath
+                      @rebuild()
+
+
+
+

   selectTask:(callback)=>


fix typo in Join.conditions

fix typo in Join.conditions

--- lib/nodes.coffee
+++ lib/nodes.coffee
@@ -27,12 +27,12 @@
     ret.join("\n")

 exports.Join = class Join
-  constructor: (@right, @condtions=null, @side=null, @mode=null) -> null
+  constructor: (@right, @conditions=null, @side=null, @mode=null) -> null
   toString: ->
     ret = ''
     ret += "#{@side} " if @side?
     ret += "#{@mode} " if @mode?
-    ret + "JOIN #{@right}\n" + indent("ON #{@condtions}")
+    ret + "JOIN #{@right}\n" + indent("ON #{@conditions}")

 exports.LiteralValue = class LiteralValue
   constructor: (@value, @value2=null) ->


little fix

little fix

--- lib/peasy.coffee
+++ lib/peasy.coffee
@@ -2,11 +2,9 @@
 # ## Peasy means parsing is easy
 # ### an easy but powerful parser
 # With Peasy, you write the parser by hand, just like to write any other kind of program.
-# You need not play many balls like that any more:
-#
+# You need not play many balls like that any more:

 # ![ballacrobatics.jpg](https://raw.github.com/chaosim/peasy/master/ballacrobatics.jpg)
-# You just play one ball like so:
-#
+# You just play one ball like so:

 # ![dolphinball.jpg](https://raw.github.com/chaosim/peasy/master/dolphinball.jpg),

 # Peasy provided two method to tell which symbol is left recursive.


refactor(lib/apply-configuration.coffee): Rework coerceToDict() method

Make more canonical. Renamed `@dict` to `dict` since non-member. Also made `coerceToArray()` return
explicit.

--- lib/apply-configuration.coffee
+++ lib/apply-configuration.coffee
@@ -12,17 +12,18 @@
     else if value instanceof Array
       value
     else value
+    return value

   coerceToDict = (value) ->
     array = coerceToArray value
-    @dict = {}
+    dict = {}

     if array.length > 0
       for item in array
-        splitItem = item.split(':')
-        @dict[splitItem[0]] = splitItem[1]
+        [key, value] = item.split(':')
+        dict[key] = value

-    return @dict
+    return dict

   configuration =
     ramlPath: null


Fix image preload settings

Fix image preload settings

--- app/templates/gulp/tasks/imagepreload.coffee
+++ app/templates/gulp/tasks/imagepreload.coffee
@@ -7,4 +7,6 @@
       script: "_imagepreload.js"
       scriptPath: "_imagepreload.js"
       md5: false
+      reduceRev: (filename)-> filename.replace(/([^\.]+)-([^\.]+)\.([^\.]+)/, "$1.$3");
+      rev: not PROP.isDev
     .pipe gulp.dest PROP.path.scripts("dest")


Remove console.log

Remove console.log

--- utils.coffee
+++ utils.coffee
@@ -26,8 +26,6 @@
   # In development, using pwd is fine. Remove the .meteor/foo/bar stuff though.
   realPath = process.cwd().replace(/(\.meteor.*)/g, '')
   ROOT = path.join(realPath, 'private')
-
-console.log ROOT

 Utils =


:shirt: Fix linter error

:shirt: Fix linter error

--- lib/update-package-dependencies.coffee
+++ lib/update-package-dependencies.coffee
@@ -21,7 +21,7 @@
       atom.commands.add view, 'core:cancel', ->
         panel.destroy()

-      if code == 0
+      if code is 0
         view.displaySuccess()
       else
         view.displayFailure()


Add hrules and Nicola/Karl text

Add hrules and Nicola/Karl text

--- linked_heatmaps/linked_heatmaps.coffee
+++ linked_heatmaps/linked_heatmaps.coffee
@@ -64,7 +64,7 @@

   # dimensions
   w = 300 # width of heat maps
-  pad = {left:60, top:25, right:25, bottom: 35, inner: 0}
+  pad = {left:60, top:40, right:25, bottom: 35, inner: 0}
   h = w # height of heat maps
   h2 = (h - pad.top - pad.bottom)/2 # height of line plot

@@ -84,6 +84,13 @@
   yScaleImg = []
   zScaleImg = []
   for i in [0...data.nll.length]
+    d3.select("div#heatmap_fig")
+      .append("hr")
+      .style("border", 0)
+      .style("width", "100%")
+      .style("color", "black")
+      .style("background-color", "black")
+      .style("height", "2px")
     d3.select("div#heatmap_fig")
       .append("h3")
       .text("Chromosome #{i+1}")
@@ -127,14 +134,24 @@
                   .range([darkBlue, "white"])
                   .clamp(true)

+    # title text
     for j in [0..1]
       panels[i][j].append("text").attr("id", "loglik#{i}#{j}")
                .text("")
                .attr("x", w/2)
-               .attr("y", -pad.top*0.5)
+               .attr("y", -pad.top*0.25)
                .attr("text-anchor", "middle")
                .attr("dominant-baseline", "middle")
-
+               .attr("fill", labelcolor)
+      panels[i][j].append("text")
+               .text(["Nicola", "Karl"][j])
+               .attr("x", w/2)
+               .attr("y", -pad.top*0.75)
+               .attr("text-anchor", "middle")
+               .attr("dominant-baseline", "middle")
+               .attr("fill", maincolor)
+
+    # left image panel
     panels[i][0].append("g").attr("id", "n_imgrect_#{i}")
              .selectAll("empty")
              .data(llList[i])
@@ -175,6 +192,7 @@
                        d3.select("rect#mouseover1").remove())

+    # right image panel
     panels[i][1].append("g").attr("id", "n_imgrect_#{i}")
              .selectAll("empty")
              .data(llList[i])


Fixed typo.

Fixed typo.

--- lib/provider_virtualbox.coffee
+++ lib/provider_virtualbox.coffee
@@ -32,7 +32,7 @@

 	extract_namespace: (node_name) ->
 		###
-		Extracts a namespace by looking it up in the node it itself and upper layers of the manifest
+		Extracts a namespace by looking it up in the node itself and upper layers of the manifest
 		###
 		try
 			node = @get_node node_name


Fix name style

Fix name style

--- src/layout/header.coffee
+++ src/layout/header.coffee
@@ -10,8 +10,8 @@
       "#{level_dict.goal[key]}"
     ]
   make.rows 'header', [
-    {_LABEL: 'game', name: level_dict.game, name_style: "24pt"}
-    {_LABEL: 'level', name: level_dict.name, name_style: "18pt"}
+    {_LABEL: 'game', name: level_dict.game, name_style: {font_size: 36}}
+    {_LABEL: 'level', name: level_dict.name, name_style: {font_size: 24}}
     make.columns 'progress', [
       "#{level_dict.level_index}"
       "of"


lodpanelframe: missing ) in comment

lodpanelframe: missing ) in comment

--- src/lodpanelframe.coffee
+++ src/lodpanelframe.coffee
@@ -23,7 +23,7 @@
     boxwidth = chartOpts?.boxwidth ? 1           # width of outer box in pixels
     ylineOpts = chartOpts?.ylineOpts ? {color:"white", width:2} # color and width of horizontal lines
     gap = chartOpts?.gap ? 5 # gap between chromosomes in pixels
-    horizontal = chartOpts.horizontal ? false # if true, chromosomes on vertical axis (xlab, ylab, etc stay the same
+    horizontal = chartOpts.horizontal ? false # if true, chromosomes on vertical axis (xlab, ylab, etc stay the same)
     # chartOpts end
     xscale = null
     yscale = null


implemented application defined compilers

--- src/main/compiler.coffee
+++ src/main/compiler.coffee
@@ -14,6 +14,7 @@

 module.exports = class Compiler
   constructor: (@opts = {}) ->
+    @opts.compilers ?= {}

   # pushes a new function score. Function score stores
   # information about what variables are defined in
@@ -77,7 +78,7 @@

   compileApply: (arr) ->
     if @isIdentifier arr[0]
-      compiler = compilers[arr[0].name]
+      compiler = compilers[arr[0].name] or @opts.compilers[arr[0].name]
       return compiler arr[1..], @ if compiler?
     compilers['apply'] arr, @

@@ -126,3 +127,4 @@
     # the caller needs to know them
     f.argNames = refs
     f
+


Hydrator: fix specs.

Hydrator: fix specs.

--- src/OVirtResponseHydrator.coffee
+++ src/OVirtResponseHydrator.coffee
@@ -109,7 +109,7 @@
 # + Resource link export
 #     + Detect that current node has a resource links.
 #     + Retrive resource links related to `xpath`.
-#     - Remove resource link child elements from target node.
+#     + Remove resource link child elements from target node.
 #     + If current node is a root one then export corresponding
 #       `_resourceLinks` namespace to target.
 #     + Otherwise populate the namespace over the node.


remove the debugging artifacts

remove the debugging artifacts

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -194,14 +194,8 @@
         assert _.isString qualified = "#{symbol}.#{key}"
         assert not _.isEmpty src = refCache[src] or src
         spoofing = not (/function/.test src.toString())
-
-        console.log "SPOOF", qualified, spoofing
-        console.log src if qualified is "$root.__hash_nav_mnt_2"
-        console.log src if qualified is "$root.testIdRemote"
-
         set = "#{qualified} = (#{src}).call(#{symbol})\r\n"
         set = "#{qualified} = (#{src})\r\n" if spoofing
-
         try set += "#{qualified}.#{metadata} = #{blob}"
         assert refCache[src] = qualified # sym to cache
         return context.sources.push "\r\n#{set}\r\n"


Fix indentation

Fix indentation

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -41,14 +41,14 @@
 # a valid __super__ descriptor, among some other prototypal things.
 Object.defineProperty Object::, "rebased",
     enumerable: no, value: (baseclass) ->
-    isClass = _.isObject baseclass.__super__
-    noClass = "The argument is not a class"
-    throw new Error noClass unless isClass
-    _.extend r = receiver = this, baseclass
-    `function ctor() {this.constructor = r}`
-    ctor.prototype = baseclass.prototype
-    this.__super__ = baseclass.prototype
-    this.prototype = new ctor(); this
+        isClass = _.isObject baseclass.__super__
+        noClass = "The argument is not a class"
+        throw new Error noClass unless isClass
+        _.extend r = receiver = this, baseclass
+        `function ctor() {this.constructor = r}`
+        ctor.prototype = baseclass.prototype
+        this.__super__ = baseclass.prototype
+        this.prototype = new ctor(); this

 # Scan the supplied class and return an entire inheritance hierarchy
 # of classes. The hierarchy is represented as an array of prototypes


Fix the composition cloner mistake

Fix the composition cloner mistake

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -50,7 +50,7 @@
     # mapped over each node of the linear hierarchy of the class that
     # invokes the composition functionality to obtain a shadow of the
     # original class that can be later modified to modify hierarchy.
-    @cloner = module?.exports?.cloner = (subject) ->
+    cloner = module?.exports?.cloner = (subject) ->
         isClass = _.isObject subject?.__super__
         noClass = "The #{subject} is not a class"
         throw new Error noClass unless isClass
@@ -119,7 +119,7 @@
     # between the foreign and common peers in the inheritance chain. Do
     # refer to the implementation for the understanding of what happens.
     Object.defineProperty Object::, "compose",
-        enumerable: no, value: (compound, shader=@cloner, silent=yes) ->
+        enumerable: no, value: (compound, shader=cloner, silent=yes) ->
             current = this.hierarchy()
             foreign = compound.hierarchy()
             identify = compound.identify()


add a couple of comments

add a couple of comments

--- library/nucleus/service.coffee
+++ library/nucleus/service.coffee
@@ -182,10 +182,10 @@
         $server = try urlOfServer onlySsl, $location
         $master = try urlOfMaster onlySsl, $location
         return new Object # make and return summary
-            reference: $reference or null
-            location: $location or null
-            server: $server or null
-            master: $master or null
+            reference: $reference or null # MD5-ed
+            location: $location or null # relative
+            server: $server or null # server URL
+            master: $master or null # master URL

     # This method should process the already matched HTTP request.
     # But since this is an abstract base class, this implementation


fix parsing symfile name with space

fix parsing symfile name with space

--- src/model/symfile.coffee
+++ src/model/symfile.coffee
@@ -107,9 +107,9 @@
         throw new Error 'Form must include a "symfile" field'

       contents = props.symfile
-      header = contents.split('\n')[0].split(/\s+/)
+      header = contents.split('\n')[0].match(/^(MODULE) ([^ ]+) ([^ ]+) ([0-9A-F]+) (.*)/)

-      [dec, os, arch, code, name] = header
+      [line, dec, os, arch, code, name] = header

       if dec != 'MODULE'
         msg = 'Could not parse header (expecting MODULE as first line)'


Revert "This caused the calculator to not render, so I reverted it for"

This reverts commit f2cff3e9b58a3f17b30355fcf0bf0c8865eb0376.

--- math_spec.coffee
+++ math_spec.coffee
@@ -1,4 +1,3 @@
-#= require almond
 #= require lib/math


added smart profiling methods: profile and profileAsync

--- src/Toolbox.coffee
+++ src/Toolbox.coffee
@@ -15,42 +15,85 @@
 class Observatory.Toolbox extends Observatory.GenericEmitter
   #constructor: (name, maxSeverity, formatter)-> super name, maxSeverity, formatter

-  exec: (f, options = errors: true, profile: true, profileLoglevel: 'INFO', message: "exec() call", module: 'profiler' )=>
-    if typeof f isnt 'function'
-      @error "Tried to call exec() without a function as an argument"
-      return
-
-    obj =
-      function: f.toString()
-      type: 'profile'
-
-    @_emitWithSeverity Observatory.LOGLEVEL[options.profileLoglevel], options.message + " starting for " + obj.function, options.module if options.profile
-    if options.errors
-      try
-        t = Date.now()
-        ret = f.call this
-        t2 = Date.now() - t
-      catch e
-        t2 = Date.now() - t
-        @trace e
-    else
-      t = Date.now()
-      ret = f.call this
-      t2 = Date.now() - t
-
-    @profile options.message + " done in #{t2} ms", t2, obj, module, options.profileLoglevel if options.profile
-    ret
-
-
-  profile: (message, time, object, module = 'profiler', severity = 'VERBOSE', buffer = false)->
+  # Simply records log with type = 'profile'
+  dumbProfile: (message, time, object, module = 'profiler', severity = 'VERBOSE', buffer = false)->
     object = object ? {}
     object.timeElapsed = time
     @_emitWithSeverity Observatory.LOGLEVEL[severity], message, object, module, 'profile', buffer

-  _profile: (message, time, object, module = 'profiler', severity = 'VERBOSE', buffer = false)->
+  # Simply records log with type = 'profile' and disregards current logging level (severity)
+  forceDumbProfile: (message, time, object, module = 'profiler', severity = 'VERBOSE', buffer = false)->
     object = object ? {}
     object.timeElapsed = time
     @_forceEmitWithSeverity Observatory.LOGLEVEL[severity], message, object, module, 'profile', buffer
+
+  # profile sync function execution
+  # * options: additional options to put into profiling log message
+  # * - message: message to log with
+  # * - method: method name that we are profiling
+  # * - buffer: whether to buffer log output (needed in Meteor mostly)
+  # * func: function to profile followed by its' arguments, however many
+  profile: (options, thisArg, func)=>
+    args = _.rest (_.rest (_.rest arguments))
+
+    t1 = Date.now()
+    ret = func.apply thisArg, args
+    t2 = Date.now() - t1
+
+    msg = "#{options.method} call finished in #{t2} ms | #{options.message}"
+    object =
+      timeElapsed: t2
+      method: options.method
+      arguments: EJSON.stringify args
+      stack: (new Error()).stack
+    @_verbose msg, object, 'profiler', 'profile', (options.buffer? is true)
+    #console.log object
+    ret
+
+  # profile async function execution
+  # * options: additional options to put into profiling log message
+  # * - message: message to log with
+  # * - method: method name that we are profiling
+  # * - buffer: whether to buffer log output (needed in Meteor mostly)
+  # * func: function to profile followed by its' arguments, however many
+  profileAsync: (options, thisArg, func)=>
+    args = _.rest (_.rest (_.rest arguments))
+
+    sargs = EJSON.stringify args
+    orig_callback = args.pop()
+
+    callback = (err,res)=>
+      t2 = Date.now() - @__startTime
+      msg = "#{options.method} call finished in #{t2} ms | #{options.message}"
+      object =
+        timeElapsed: t2
+        method: options.method
+        arguments: sargs
+        stack: (new Error()).stack
+        type: "profile.end"
+      @_verbose msg, object, 'profiler', 'profile', (options.buffer? is true)
+      #console.log object
+
+      orig_callback err, res if typeof orig_callback is 'function'
+
+    if typeof orig_callback is 'function'
+      args.push callback
+    else
+      args.push orig_callback
+      args.push callback
+
+
+    msg = "#{options.method} call started"
+    object =
+      method: options.method
+      arguments: sargs
+      stack: (new Error()).stack
+      type: "profile.start"
+    @_verbose msg, object, 'profiler', 'profile', (options.buffer? is true)
+
+    @__startTime = Date.now()
+    func.apply thisArg, args
+

   inspect: (obj, long = false, print = false)->


Adding 'No such option' output to parallel 'No such task' (fixes #1862)

--- cake.coffee
+++ cake.coffee
@@ -41,8 +41,8 @@

 # Run `cake`. Executes all of the tasks you pass, in order. Note that Node's
 # asynchrony may cause tasks to execute in a different order than you'd expect.
-# If no tasks are passed, print the help screen. Keep a reference to the
-# original directory name, when running Cake tasks from subdirectories.
+# If no tasks are passed, print the help screen. Keep a reference to the
+# original directory name, when running Cake tasks from subdirectories.
 exports.run = ->
   global.__originalDirname = fs.realpathSync '.'
   process.chdir cakefileDirectory __originalDirname
@@ -50,7 +50,11 @@
   CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
   oparse = new optparse.OptionParser switches
   return printTasks() unless args.length
-  options = oparse.parse(args)
+  console.log args
+  try
+    options = oparse.parse(args)
+  catch e
+    return missingOption "#{e}".match(/option: (.+)/)[1]
   invoke arg for arg in options.arguments

 # Display the list of Cake tasks in a format similar to `rake -T`
@@ -63,12 +67,17 @@
     console.log "cake #{name}#{spaces} #{desc}"
   console.log oparse.help() if switches.length

+# Print an error and exit when attempting to use an invalid option.
+missingOption = (option) ->
+  console.log "No such option: \"#{option}\""
+  process.exit 1
+
 # Print an error and exit when attempting to call an undefined task.
 missingTask = (task) ->
   console.log "No such task: \"#{task}\""
   process.exit 1

-# When `cake` is invoked, search in the current and all parent directories
+# When `cake` is invoked, search in the current and all parent directories
 # to find the relevant Cakefile.
 cakefileDirectory = (dir) ->
   return dir if path.existsSync path.join dir, 'Cakefile'


adding string missing from translations

adding string missing from translations

--- jsapp/xlform/src/mv.skipLogicHelpers.coffee
+++ jsapp/xlform/src/mv.skipLogicHelpers.coffee
@@ -406,7 +406,7 @@
       return ''
     constructor: (view_factory, @context) ->
       @criterion_builder_button = view_factory.create_button ' ' + _t("Add a condition"), 'skiplogic__button skiplogic__select-builder'
-      @handcode_button = view_factory.create_button '${} Manually enter your skip logic in XLSForm code', 'skiplogic__button skiplogic__select-handcode'
+      @handcode_button = view_factory.create_button '${} ' + _t("Manually enter your skip logic in XLSForm code"), 'skiplogic__button skiplogic__select-handcode'
       ###@view = @view_factory.create_skip_logic_picker_view(context)###
     switch_editing_mode: () -> return


Minor visual tweak

Minor visual tweak

--- src/chromeExtension/components/020-story.cjsx
+++ src/chromeExtension/components/020-story.cjsx
@@ -552,6 +552,7 @@
     textDecoration: 'underline'
     cursor: 'pointer'
     fontWeight: 'normal'
+    fontFamily: 'Menlo, Consolas, monospace'
   icon:
     marginRight: 4


one more step

one more step

--- nodes.coffee
+++ nodes.coffee
@@ -909,13 +909,8 @@

   # The map of conversions from CoffeeScript to JavaScript symbols.
   CONVERSIONS: {
-    '==':   '==='
-    '!=':   '!=='
-    'and':  '&&'
-    'or':   '||'
-    'is':   '==='
-    'isnt': '!=='
-    'not':  '!'
+    '==': '==='
+    '!=': '!=='
   }

   # The list of operators for which we perform


normalizing loop declarations towards common JS practice.

--- nodes.coffee
+++ nodes.coffee
@@ -78,8 +78,7 @@
   # Compile to a source/variable pair suitable for looping.
   compileLoopReference: (o, name) ->
     src = tmp = @compile o
-    unless NUMBER.test(src) or
-           IDENTIFIER.test(src) and o.scope.check(src, immediate: on)
+    unless NUMBER.test(src) or (IDENTIFIER.test(src) and o.scope.check(src, immediate: on))
       src = "#{ tmp = o.scope.freeVariable name } = #{src}"
     [src, tmp]

@@ -1352,9 +1351,8 @@
       else
         "#{pvar} < 0 ? #{ivar} >= #{tvar} : #{ivar} <= #{tvar}"
     else
-      if name or not @raw
+      if name
         [sourcePart, svar] = @source.compileLoopReference o, 'ref'
-        sourcePart = "(#{sourcePart})" unless sourcePart is svar or @object
       else
         sourcePart = svar = @source.compile o
       namePart = if @pattern
@@ -1363,31 +1361,33 @@
         "#{name} = #{svar}[#{ivar}]"
       unless @object
         if 0 > pvar and (pvar | 0) is +pvar  # negative int
-          vars = "#{ivar} = #{sourcePart}.length - 1"
+          vars = "#{ivar} = #{svar}.length - 1"
           cond = "#{ivar} >= 0"
         else
           lvar = scope.freeVariable 'len'
-          vars = "#{ivar} = 0, #{lvar} = #{sourcePart}.length"
+          vars = "#{ivar} = 0, #{lvar} = #{svar}.length"
           cond = "#{ivar} < #{lvar}"
+    defPart   = ''
     if @object
       forPart   = "#{ivar} in #{sourcePart}"
       guardPart = not @raw and
         "#{idt}if (!#{ utility 'hasProp' }.call(#{svar}, #{ivar})) continue;\n"
     else
       vars += ", #{step}" if step isnt pvar
+      defPart = "#{@tab}#{sourcePart};\n" if svar isnt sourcePart
       forPart = "#{vars}; #{cond}; " + switch +pvar
-        when  1 then '++' + ivar
-        when -1 then '--' + ivar
+        when  1 then ivar + '++'
+        when -1 then ivar + '--'
         else ivar + if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar
     unless top
       rvar      = scope.freeVariable 'result'
-      resultDef = "#{@tab}#{rvar} = [];\n"
+      defPart  += "#{@tab}#{rvar} = [];\n"
       resultRet = @compileReturnValue rvar, o
       body      = Push.wrap rvar, body
     body = Expressions.wrap [new If @guard, body] if @guard
     varPart = "#{idt}#{namePart};\n" if namePart
     """
-    #{ resultDef or '' }#{@tab}for (#{forPart}) {
+    #{ defPart or '' }#{@tab}for (#{forPart}) {
     #{ guardPart or '' }#{varPart}#{ body.compile merge o, indent: idt, top: on }
     #{@tab}}#{ resultRet or '' }
     """


no join for comment either

no join for comment either

--- models/comment.coffee
+++ models/comment.coffee
@@ -26,7 +26,7 @@
     if @belongsToCurrentUser(user)
       permissions.push 'can-edit'

-    (_.uniq permissions).join ' '
+    (_.uniq permissions)

   belongsToCurrentUser: (user)->
     @get('user').id is user.id


Disable CSRF

Disable CSRF

--- config/express.coffee
+++ config/express.coffee
@@ -65,13 +65,15 @@
     #app.use helpers(pkg.name)

     # adds CSRF support
-    app.use express.csrf()
-    app.use require('connect-csrf-cookie')()
+    #app.use express.csrf()
+    #app.use require('connect-csrf-cookie')()

     # This could be moved to view-helpers :-)
+    ###
     app.use (req, res, next) ->
       res.locals.csrf_token = req.csrfToken()
       next()
+    ###

     # routes should be at the last
     app.use app.router


fix binding cli in async contexts

fix binding cli in async contexts

--- src/context.coffee
+++ src/context.coffee
@@ -131,7 +131,7 @@
         nested_context = _.extend {}, run_context,
           output: output $item

-        nested_context.cli = bind_cli nested_context
+        nested_context.cli = bind_cli nested_context, ops
         promise = fn.call(nested_context)
         promise.done ->
           $item.attr 'data-async-status', "loaded in #{duration()}"


Export address helpers

Export address helpers

--- src/filings.coffee
+++ src/filings.coffee
@@ -5,3 +5,4 @@
   Transactions: require './transactions'
   Companies: require './companies'
   Names: require './names'
+  Address: require './address'


bump version number

bump version number

--- src/futures.coffee
+++ src/futures.coffee
@@ -1,5 +1,6 @@
 ###
-jquery-futures v.0.1.0
+jquery-futures v.0.2.0
+Karan Sagar
 ###
 partial = (f, args...) -> (more...) -> f.apply(null, Array::concat.call(args, more))
 methodize = (obj, funcName) -> (fn) -> Future[funcName](obj, fn)


add grafana-images capability

add grafana-images capability

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -17,6 +17,8 @@
 #   HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY - Optional; Secret access key for S3
 #   HUBOT_GRAFANA_S3_PREFIX - Optional; Bucket prefix (useful for shared buckets)
 #   HUBOT_GRAFANA_S3_REGION - Optional; Bucket region (defaults to us-standard)
+#   HUBOT_USE_GRAFANA_IMAGES - true|false, whether to use grafana-images
+#   HUBOT_GRAFANA_IMAGES_HOST - The host where grafana images resides, e.g. 'http://grafana.example.com'
 #
 # Dependencies:
 #   "knox": "^0.9.2"
@@ -41,6 +43,8 @@
   s3_prefix = process.env.HUBOT_GRAFANA_S3_PREFIX
   s3_region = 'us-standard'
   s3_region = process.env.HUBOT_GRAFANA_S3_REGION if process.env.HUBOT_GRAFANA_S3_REGION
+  use_grafana_images = process.env.HUBOT_USE_GRAFANA_IMAGES
+  grafana_images_host = if process.env.HUBOT_GRAFANA_IMAGES_HOST then process.env.HUBOT_GRAFANA_IMAGES_HOST else grafana_host

   # Get a specific dashboard with options
   robot.respond /(?:grafana|graph|graf) (?:dash|dashboard|db) ([A-Za-z0-9\-\:_]+)(.*)?/i, (msg) ->
@@ -135,7 +139,9 @@

           # Fork here for S3-based upload and non-S3
           if (s3_bucket && s3_access_key && s3_secret_key)
-            fetchAndUpload msg, title, imageUrl, link
+            s3FetchAndUpload msg, title, imageUrl, link
+          else if (use_grafana_images == "true")
+            customFetchAndUpload msg, title, imageUrl, link
           else
             sendRobotResponse msg, title, imageUrl, link

@@ -165,6 +171,19 @@
       response.trim()

       msg.send response
+
+  # Show help text
+  robot.respond /(?:grafana|graph|graf) help$/i, (msg) ->
+    response = "grafana usage:\n"
+    response += "  hubot graf list\n"
+    response += "  hubot graf db cm-monitoring-default\n"
+    response += "  hubot graf db cm-monitoring-default:8\n"
+    response += "  hubot graf db cm-monitoring-default:swap\n"
+    response += "  hubot graf db cm-monitoring-default now-12hr\n"
+    response += "  hubot graf db cm-monitoring-default now-24hr now-12hr\n"
+    response += "  hubot graf db cm-monitoring-default:8 now-8d now-1d\n"
+    response += "  hubot graf db cm-monitoring-default:swap server=ncv-dashing now-1w\n"
+    msg.send response

   # Handle generic errors
   sendError = (message, msg) ->
@@ -201,13 +220,40 @@
       data = JSON.parse(body)
       return callback(data)

+  customFetchAndUpload = (msg, title, url, link) ->
+    requestHeaders = {
+      encoding: "utf8",
+      Authorization: "Bearer #{grafana_api_key}",
+      Accept: "application/json"
+    }
+
+    req_opts = {
+      method: "POST",
+      url: "#{grafana_images_host}/grafana-images",
+      headers: requestHeaders,
+      json: {
+        imageUrl: url
+      }
+    }
+
+    # post to grafana-images
+    request req_opts, (err, res, json) ->
+      robot.logger.debug "grafana-images POST: #{req_opts.url}, content-type[#{res.headers['content-type']}]"
+
+      if res.statusCode == 200
+        sendRobotResponse msg, title, json.pubImg, link
+      else
+        robot.logger.debug res
+        robot.logger.error "Upload Error Code: #{res.statusCode}"
+        msg.send "#{title} - [Access Error] - #{link}"
+
   # Pick a random filename
-  uploadPath = () ->
+  s3UploadPath = () ->
     prefix = s3_prefix || 'grafana'
     "#{prefix}/#{crypto.randomBytes(20).toString('hex')}.png"

   # Fetch an image from provided URL, upload it to S3, returning the resulting URL
-  fetchAndUpload = (msg, title, url, link) ->
+  s3FetchAndUpload = (msg, title, url, link) ->
     if grafana_api_key
         requestHeaders =
           encoding: null,
@@ -237,7 +283,7 @@
       'encoding'       : null
     }

-    filename = uploadPath()
+    filename = s3UploadPath()

     req = client.put(filename, headers)
     req.on 'response', (res) ->


uggh, forgot this..

uggh, forgot this..

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -604,7 +604,7 @@
   ['nonassoc',  'INDENT', 'OUTDENT']
   ['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
   ['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
-  ['right',     'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
+  ['right',     'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'AWAIT']
   ['right',     'POST_IF']
 ]


Only support VS2010 for now

Only support VS2010 for now

--- src/install.coffee
+++ src/install.coffee
@@ -102,10 +102,11 @@

     if config.isVs2010Installed()
       "--msvs_version=2010"
-    else if config.isVs2012Installed()
-      "--msvs_version=2012"
+    #else if config.isVs2012Installed()
+      #"--msvs_version=2012"
     else
-      throw new Error("You must have either VS2010 or VS2012 installed")
+      throw new Error("You must have VS2010 installed")
+      #throw new Error("You must have either VS2010 or VS2012 installed")

   installModules: (options, callback) =>
     process.stdout.write 'Installing modules '


remove unnecessary component

remove unnecessary component

--- client/apps/edit/components/section_artworks/index.coffee
+++ client/apps/edit/components/section_artworks/index.coffee
@@ -6,7 +6,6 @@
 _ = require 'underscore'
 Artworks = require '../../../../collections/artworks.coffee'
 Artwork = require '../../../../models/artwork.coffee'
-{ openErrorModal } = require '../../../../components/error_modal/index.coffee'
 React = require 'react'
 ByUrls = require './by_urls.coffee'
 imagesLoaded = require 'imagesloaded'


Revert "Don't use <p> tags in content."

Revert "Don't use <p> tags in content."

This reverts commit 2f56cfc695387b0dd5ab314f820987aef9fae315.

--- client/ide/lib/views/tabview/ideapplicationtabview.coffee
+++ client/ide/lib/views/tabview/ideapplicationtabview.coffee
@@ -46,7 +46,7 @@
     { ace } = aceView
     file    = ace.getData()

-    content = "Your changes will be lost if you don't save them."
+    content = "
Your changes will be lost if you don't save them.
"

     { frontApp } = kd.singletons.appManager

@@ -54,7 +54,7 @@
       participants = frontApp.getWatchingMeParticipantsList()

       if participants.length
-        content += " Also #{participants.join(', ')} maybe made changes here?"
+        content += "
Also #{participants.join(', ')} maybe made changes here?
"

     modal = new KDModalView
       width         : 620


Add endpoint for resending verification email.

--- js/src/http/UserDirectoryApi.coffee
+++ js/src/http/UserDirectoryApi.coffee
@@ -22,6 +22,7 @@
   getUsersUrl  = -> Configuration.get('heraclesUrl') + '/directory/users'
   usersInRealmUrl = -> Configuration.get('heraclesUrl') + '/directory'
   setFirstLoginUrl = -> Configuration.get('heraclesUrl') + '/directory/setlogin'
+  getConfirmation = -> Configuration.get('heraclesUrl') + '/registration/confirmation/resend'
   getUserIdFromEmail = (email) -> Configuration.get('heraclesUrl') + '/directory/validate/sharing/email/' + email
   getUserSettingUrl = (userSetting) -> getUserUrl() + '/setting/' + userSetting

@@ -195,4 +196,13 @@
         })
       ))

+    resendConfirmationEmail: () ->
+      Promise.resolve(axios(
+        Requests.wrapCredentials({
+          url: getConfirmation()
+          method: 'GET'
+        })
+      ))
+
+
   return UserDirectoryApi


Add message when user created

Add message when user created

--- www/client/user/controllers/login.coffee
+++ www/client/user/controllers/login.coffee
@@ -1,5 +1,5 @@
 angular.module 'keranbeandco.user'
-.controller 'login', ($scope, $state, ImprovedUser) ->
+.controller 'login', ($scope, $state, ImprovedUser, $mdToast) ->

   $scope.login = ->
     ImprovedUser.login
@@ -15,3 +15,6 @@
       lastName: $scope.createLastName
       email: $scope.createEmail
       password: $scope.createPassword
+    .$promise
+    .then () ->
+      $mdToast.showSimple 'Le compte a été créé, vous pouvez maintenant vous connecter !'


Implement links.

Implement links.

--- src/postprocessor.coffee
+++ src/postprocessor.coffee
@@ -175,8 +175,16 @@
     # @return [Object] link context JSON Schema definition
     #
     processLink: (link, ctx={}) ->
+        # Process attribute
+        @processAttribute(attribute: link.attribute, ctx)
+
+        link = _.merge _.omit(link, ['rule', 'attribute']),
+            property: link.attribute.name
+
         # Add link to context and return context
-        utils.addAsObjectMember(ctx, 'links', link.attribute.name, link)
+        utils.addAsObjectMember(ctx, 'links', link.property, link)
+
+        # Return context
         ctx


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Removed unused variable

Removed unused variable

--- core/app/backbone/views/activities/activities_view.coffee
+++ core/app/backbone/views/activities/activities_view.coffee
@@ -57,7 +57,6 @@
     @$(".js-activities-list").append childView.render().el

   newChildView: (model) ->
-    ch = @collection.channel
     ActivitiesGroupView.new
       model: model


Navigate back when a factlink get's deleted.

--- core/app/backbone/views/facts/discussion_page_view.coffee
+++ core/app/backbone/views/facts/discussion_page_view.coffee
@@ -1,4 +1,7 @@
 class window.DiscussionPageView extends TabbedMainRegionLayout
+  initialize: ->
+    @bindTo @model, "destroy", @go_back, @
+
   onRender: ->
     @model.fetch
       success: (model, response) =>
@@ -6,3 +9,10 @@
       error: => FactlinkApp.NotificationCenter.error("This Factlink could not be found. Click here to go back.")

     @titleRegion.show new ExtendedFactTitleView model: @model, back_button: @options.back_button
+
+  go_back: ->
+    url = @options.back_button.get('url')
+    if url
+      FactlinkApp.NotificationCenter.info 'Factlink deleted.'
+      Backbone.history.navigate(url, true)
+


MyWot running, just needs categories

MyWot running, just needs categories

--- src/scripts/mywot.coffee
+++ src/scripts/mywot.coffee
@@ -5,7 +5,7 @@
 #   None
 #
 # Configuration:
-#   None
+#   MYWOT_API_KEY -
 #
 # Commands:
 #   hubot mywot <url> - Gets MyWot Report for URL
@@ -13,8 +13,64 @@
 # Author:
 #   Scott J Roberts - @sroberts

+# mywot_categories = {
+#   "101": "Malware or viruses",
+#   "102": "Poor customer experience",
+#   "103": "Phishing",
+#   "104": "Scam",
+#   "105": "Potentially illegal",
+#   "201": "Misleading claims or unethical",
+#   "202": "Privacy risks",
+#   "203": "Suspicious",
+#   "204": "Hate, discrimination",
+#   "205": "Spam",
+#   "206": "Potentially unwanted programs",
+#   "207": "Ads / pop-ups",
+#   "301": "Online tracking",
+#   "302": "Alternative or controversial medicine",
+#   "303": "Opinions, religion, politics",
+#   "304": "Other",
+#   "401": "Adult content",
+# 	"402": "Incidental nudity",
+#   "403": "Gruesome or shocking",
+#   "404": "Site for kids",
+#   "501": "Good site"
+# }
+
+MYWOT_API_KEY = process.env.MYWOT_API_KEY
+
+mywot_url = "http://api.mywot.com/0.4/public_link_json2?key=#{MYWOT_API_KEY}&"
+
+reputation = (number) ->
+  rep = switch
+    when number >= 80 then "Excellent"
+    when number >= 60 then "Good"
+    when number >= 40 then "Unsatisfactory"
+    when number >= 20 then "Poor"
+    when number >= 0 then "Very Poor"
+
+  return rep
+
+
 module.exports = (robot) ->
-  robot.respond /mywot/i, (msg) ->
-    # If there's strings generate rule with strings
+  robot.respond /mywot (.*)/i, (msg) ->
+    mywot_term = msg.match[1].toLowerCase()

-    # If there aren't then just generate a template
+    msg.send "API URL: " + mywot_url + "hosts=#{mywot_term}/"
+
+    robot.http(mywot_url + "hosts=#{mywot_term}/")
+      .get() (err, res, body) ->
+
+        mywot_json = JSON.parse body
+
+        msg.send mywot_json[mywot_term][0]
+
+        mywot_trustworthiness = mywot_json[mywot_term][0]
+        mywot_childfriendliness = mywot_json[mywot_term][4]
+
+        mywot_profile = "MyWot Result for #{mywot_term}\n---------------------------\n"
+
+        mywot_profile += "- Trustworthiness: #{reputation(mywot_trustworthiness[0])} (Confidence: #{mywot_trustworthiness[1]}%)\n"
+        mywot_profile += "- Child Safety:    #{reputation(mywot_childfriendliness[0])} (Confidence: #{mywot_childfriendliness[1]}%)\n"
+
+        msg.send mywot_profile


Allow fragment to track to be specified

Allow fragment to track to be specified

--- src/scripts/helpers/handlers/analytics.coffee
+++ src/scripts/helpers/handlers/analytics.coffee
@@ -27,12 +27,10 @@
     # Wrapper functions to add analytics events
     # ga: () -> window.ga?.apply(@, arguments) # analytics.js
     gaq: () ->
-      console.log arguments[0] + ', ' + arguments[1]
       window._gaq?.push(arguments[0], arguments[1]) # ga.js

     # Send the current page to every analytics service
-    send: (account) ->
-      fragment = Backbone.history.fragment
+    send: (account, fragment = Backbone.history.fragment) ->
       if not /^\//.test(fragment) then fragment = '/' + fragment

       # Use the default analytics ID in settings if no account is specified


Response

Response

* responds to /fake (\w+) (\w+)/i

--- src/faker.coffee
+++ src/faker.coffee
@@ -14,9 +14,15 @@
 # Author:
 #   Dustin Schie <me@dustinschie.com>

+URL = 'https://fakerapi.herokuapp.com'
+
 module.exports = (robot) ->
-  robot.respond /hello/, (res) ->
-    res.reply "hello!"
-
-  robot.hear /orly/, ->
-    res.send "yarly"
+  robot.respond /fake (\w+) (\w+)/i, (res) ->
+    path = res.match[1].split(' ').join('/')
+    robot.http("#{URL}/#{path}")
+      .get() (err, resp, body) ->
+        if  resp.statusCode isnt 200
+          res.send "Encountered an error :( #{err}"
+          return
+        else
+          resp.send body


Refactored main code into Game class

Refactored main code into Game class


modified lambda to support multi-item bodies

--- ya.coffee
+++ ya.coffee
@@ -75,7 +75,7 @@
     if cdr instanceof Array and cdr.length > 0
       return cdr[1..]

-  LAMBDA: (parameterNames, body)->
+  LAMBDA: (parameterNames, body...)->
     # log "LAMBDA paramNames:", parameterNames
     # log "       body:", body
     # replace body's references to strings defined in parameterNames array
@@ -85,7 +85,9 @@
       for idx in [0...args.length]
         this.bind(parameterNames[idx], args[idx])

-      _eval.call(this, body)
+      for part in body
+        last_part = _eval.call(this, part)
+      last_part

   ATOM:(symbol)->
     if (_eval.call(this,symbol) instanceof Array)


Added rudimentary tweening, wut!

Added rudimentary tweening, wut!

--- src/tween.coffee
+++ src/tween.coffee
@@ -31,7 +31,7 @@
   player.tween 30,
     x: 50
     y: 50
-
+    easing:

   player.flicker(90, 5, 0.3)


And updated version in zappa.coffee.

And updated version in zappa.coffee.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -2,7 +2,7 @@
 # [node.js](http://nodejs.org) runtime, integrating [express](http://expressjs.com), [socket.io](http://socket.io)
 # and other best-of-breed libraries.

-zappa = version: '0.4.2'
+zappa = version: '0.4.3'

 codename = 'Wowie Zowie'


Rename expectWithOracle

Rename expectWithOracle

--- test/test.coffee
+++ test/test.coffee
@@ -1,7 +1,7 @@
 rmutt = require '..'
 expect = require('chai').expect

-expectc = (source, expected) ->
+expectWithOracle = (source, expected) ->
   compiled = rmutt.compile source
   for result, index in expected.reverse()
     expect(compiled oracle: index)
@@ -9,7 +9,6 @@

 # TODO: empty grammar test
 # TODO: undefined config test
-# TODO: rename expectc

 describe 'rmutt', ->

@@ -89,7 +88,7 @@
       top: a{2};
       a: "x", "y";
     """
-    expectc source, [
+    expectWithOracle source, [
       'yy'
       'xy'
       'yx'
@@ -147,7 +146,7 @@
       lastName: "McPhee", "Eaton-Hogg", "Worthingham";
       position: "the butler", "the chauffeur";
     """
-    expectc source, [
+    expectWithOracle source, [
       "the butler said, 'I am the butler, so nice to meet you.'"
       "Dr. Nancy McPhee said, 'I am Dr. Nancy McPhee, so nice to meet you.'"
     ]
@@ -156,7 +155,7 @@
     source = """
       t: "0"|"1"|"2";
     """
-    expectc source, [
+    expectWithOracle source, [
       '2'
       '1'
       '0'
@@ -166,7 +165,7 @@
     source = """
       soldOut: | |  |    "SOLD OUT" | |  |;
     """
-    expectc source, [
+    expectWithOracle source, [
       ''
       'SOLD OUT'
       ''
@@ -179,7 +178,7 @@
       t: "0"|a;
       a: "1"|t;
     """
-    expectc source, [
+    expectWithOracle source, [
       '1'
       '0', '1', '0', '0', '0', '1', '0'
     ]
@@ -204,7 +203,7 @@
     source = """
       t: "a" ("b"|"c") "d";
     """
-    expectc source, [
+    expectWithOracle source, [
       'acd'
       'abd'
     ]
@@ -213,7 +212,7 @@
     source = """
       t: "a" ("b"|("c"|"d"));
     """
-    expectc source, [
+    expectWithOracle source, [
       'ad'
       'ab'
       'ac'
@@ -224,7 +223,7 @@
     source = """
       t: "a"{2} "b"{2,3} "c"? "d"* "e"+;
     """
-    expectc source, [
+    expectWithOracle source, [
       'aabbbcddddde'
       'aabbcddddde'
       'aabbbddddde'
@@ -264,7 +263,7 @@
     source = """
       t: (a: "0" | "1") a;
     """
-    expectc source, [
+    expectWithOracle source, [
       '1'
       '0'
     ]
@@ -273,7 +272,7 @@
     source = """
       t: (a = "0" | "1") a a;
     """
-    expectc source, [
+    expectWithOracle source, [
       '11'
       '00'
     ]
@@ -283,7 +282,7 @@
       a: b > ("0" % "a" "1" % "b");
       b: "0" | "1";
     """
-    expectc source, [
+    expectWithOracle source, [
       'b'
       'a'
     ]
@@ -292,7 +291,7 @@
     source = """
       a: "i like to eat apples and bananas" > "i" % "u";
     """
-    expectc source, [
+    expectWithOracle source, [
       'u luke to eat apples and bananas'
     ]

@@ -301,7 +300,7 @@
       a: "i like to eat apples and bananas" > ("i" % u);
       u: "u";
     """
-    expectc source, [
+    expectWithOracle source, [
       'u luke to eat apples and bananas'
     ]

@@ -318,7 +317,7 @@
     source = """
       a: "i like to eat apples and bananas" > /[aeiou]+/oo/;
     """
-    expectc source, [
+    expectWithOracle source, [
       'oo lookoo too oot oopploos oond boonoonoos'
     ]

@@ -326,7 +325,7 @@
     source = """
       a: "i like to eat apples and bananas" > (/[aeiou]+/oo/ /loo/x/);
     """
-    expectc source, [
+    expectWithOracle source, [
       'oo xkoo too oot ooppxs oond boonoonoos'
     ]

@@ -334,7 +333,7 @@
     source = """
       a: "a bad apple" > /a (.+) (.+)/i want the \\2s \\1ly/;
     """
-    expectc source, [
+    expectWithOracle source, [
       'i want the apples badly'
     ]

@@ -352,7 +351,7 @@
       slangify: "chck" % "chiggidy" "snp" % "snippidy";
       name: "check" | "chuck" | "snap" | "snipe";
     """
-    expectc source, [
+    expectWithOracle source, [
       'snppdy'
       'snppdy'
       'chggdy'
@@ -409,7 +408,7 @@
       s: "hello there " o;
       o: "beautiful" | "Mr. Smarty Pants";
     """
-    expectc source, [
+    expectWithOracle source, [
       "ogre starts with the letter 'O', Mr. Smarty Pants"
       "oatmeal starts with the letter 'O', Mr. Smarty Pants"
       "ogre starts with the letter 'O', beautiful"
@@ -421,7 +420,7 @@
       package test;
       a: "0" 3| "1";
     """
-    expectc source, [
+    expectWithOracle source, [
       '1'
       '0'
       '0'
@@ -440,7 +439,7 @@
     source = """
       a: ("a"|"b")>("a"%("0"|"zero") "b"%("1"|"one"));
     """
-    expectc source, [
+    expectWithOracle source, [
       'one'
       'zero'
       '1'
@@ -460,7 +459,7 @@
       r: ((a="foo") a ($b="bar")) b " "
       ((c:"foo"|"bar") c ($d:"baz"|"quux") d) d "\\n";
     """
-    expectc source, [
+    expectWithOracle source, [
       'foobar barquuxquux\n'
       'foobar fooquuxquux\n'
       'foobar barbazquux\n'
@@ -532,7 +531,7 @@
       quux: "snooby " bar;

     """
-    expectc source, [
+    expectWithOracle source, [
       'snooby fnord'
       'snooby quux'
     ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -3,7 +3,7 @@
 test "text moves up vertically", ->
   text = Text()

-  #text.update()
+  text.update()

   equals text.I.x, 0
   equals text.I.y, 0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -2,6 +2,7 @@

 test "text moves up vertically", ->
   text = Text()
+  te

   equals text.I.x, 0
   equals text.I.y,


Use --numeric-version for ghc version

Use --numeric-version for ghc version

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -82,16 +82,14 @@
   checkComp: (opts, {comp}) ->
     timeout = atom.config.get('haskell-ghc-mod.initTimeout') * 1000
     stackghc =
-      Util.execPromise 'stack', ['ghc', '--', '--version'], _.extend({timeout}, opts)
-      .then (stdout) ->
-        /version (.+)$/.exec(stdout.trim())[1]
+      Util.execPromise 'stack', ['ghc', '--', '--numeric-version'], _.extend({timeout}, opts)
+      .then (stdout) -> stdout.trim()
       .catch (error) ->
         Util.warn error
         return null
     pathghc =
-      Util.execPromise 'ghc', ['--version'], _.extend({timeout}, opts)
-      .then (stdout) ->
-        /version (.+)$/.exec(stdout.trim())[1]
+      Util.execPromise 'ghc', ['--numeric-version'], _.extend({timeout}, opts)
+      .then (stdout) -> stdout.trim()
       .catch (error) ->
         Util.warn error
         return null


production

production

--- routes/api.coffee
+++ routes/api.coffee
@@ -6,7 +6,7 @@
 constants = require '../constants'
 utils     = require '../utils'

-fulcrum = new Fulcrum({api_key: constants.api_key, url: 'https://edge.fulcrumapp.com/api/v2/'})
+fulcrum = new Fulcrum({api_key: constants.api_key, url: 'https://api.fulcrumapp.com/api/v2/'})
 router  = express.Router()

 router.get '/form', (req, res) ->
@@ -39,7 +39,7 @@
   fulcrum.records.create req.body, callback

 router.post '/photos', (req, res) ->
-  fulcrum_req = request('https://edge.fulcrumapp.com/api/v2/photos')
+  fulcrum_req = request('https://api.fulcrumapp.com/api/v2/photos')
   fulcrum_req_headers =
     'X-ApiToken': constants.api_key
     'Accept': 'application/json'


add test for ioredis monkey patching

add test for ioredis monkey patching

--- libraries/redis-wrapper/test.coffee
+++ libraries/redis-wrapper/test.coffee
@@ -75,6 +75,27 @@
 			assert(client instanceof @ioredis.Cluster)
 			client.config.should.deep.equal @cluster

+	describe "monkey patch ioredis exec", ->
+		beforeEach ->
+			@callback = sinon.stub()
+			@results = []
+			@multiOrig = { exec: sinon.stub().yields(null, @results)}
+			@client = { multi: sinon.stub().returns(@multiOrig) }
+			@redis._monkeyPatchIoredisExec(@client)
+			@multi = @client.multi()
+
+		it "should return the old redis format for an array", ->
+			@results[0] = [null, 42]
+			@results[1] = [null, "foo"]
+			@multi.exec @callback
+			@callback.calledWith(null, [42, "foo"]).should.equal true
+
+		it "should return the old redis format when there is an error", ->
+			@results[0] = [null, 42]
+			@results[1] = ["error", "foo"]
+			@multi.exec @callback
+			@callback.calledWith("error").should.equal true
+
 	describe "setting the password", ->
 		beforeEach ->
 			@standardOpts =


:art: simplify atom notational items

:art: simplify atom notational items

--- src/atom/notational-items.coffee
+++ src/atom/notational-items.coffee
@@ -1,7 +1,7 @@
 Bacon = require 'baconjs'
 {Task} = require 'atom'
 Path = require 'path'
-atoms = require './streams.coffee'
+{projectsPaths} = require './streams.coffee'
 columns = require './columns.coffee'

 # @param {Stream} watchedProjectsStream objects containing a path {String} and a task {Task}
@@ -18,13 +18,10 @@
       projects.filter ({path}) ->
         path isnt removedPath

-module.exports = ->
-  {addedStream, removedStream} = atoms.projectsPaths()
-
-  watchedProjectsStream = addedStream.map (path) ->
+watchedProjects = (addedStream) ->
+  addedStream.map (path) ->
     task = new Task(require.resolve('./watch-project-task.coffee'))
-    task.start(
-      path,
+    task.start(path,
       atom.config.get('core.ignoredNames'),
       atom.config.get('core.excludeVcsIgnoredPaths')
     )
@@ -33,12 +30,17 @@
       task: task
     }

-  # Only used to manage
-  projectsProp = projects(watchedProjectsStream, removedStream)
+
+module.exports = ->
+  {addedStream, removedStream} = projectsPaths()
+  watchedProjectsStream = watchedProjects(addedStream)
+
   deactivateBus = new Bacon.Bus()
-  projectsProp.sampledBy(deactivateBus).onValue (projects) ->
-    task.send('dispose') for {task} in projects
-    return Bacon.noMore
+  projects(watchedProjectsStream, removedStream)
+    .sampledBy(deactivateBus)
+    .onValue (projects) ->
+      task.send('dispose') for {task} in projects
+      return Bacon.noMore

   addItemsStream = watchedProjectsStream.flatMap ({task}) ->
     Bacon.fromEvent(task, 'add')


Semantic version unit test

Semantic version unit test

--- test/impromptu.coffee
+++ test/impromptu.coffee
@@ -5,6 +5,9 @@
 describe 'Impromptu', ->
   it 'should exist', ->
     should.exist Impromptu
+
+  it 'should use semantic versions', ->
+    /^\d+\.\d+\.\d+(\D|$)/.test(Impromptu.VERSION).should.be.true

 describe 'Impromptu.Error', ->
   it 'should have a message', ->


rotatable tests

rotatable tests

--- test/rotatable.coffee
+++ test/rotatable.coffee
@@ -1,10 +1,11 @@
 test "Rotatable objects update their rotation", ->
   obj = GameObject
     rotationalVelocity: Math.PI / 4
+    rotation: Math.PI / 6

   obj.include(Rotatable)

-  equals obj.I.rotation, 0, "Default rotation value is 0"
+  equals obj.I.rotation, Math.PI / 6, "Default rotation value is Math.PI / 6"

   2.times ->
     obj.update()


Failing pool tests

Failing pool tests

--- test/test_pool.coffee
+++ test/test_pool.coffee
@@ -135,3 +135,39 @@
     http.cat "http://127.0.0.1:#{PORT}/", "utf8", (err, data) ->
       test.ok err
       test.done()
+
+# exports.testErrorCreatingPool = (test) ->
+#   test.expect 2
+
+#   pool = createPool "#{__dirname}/fixtures/crash.ru", size: 1
+
+#   pool.on 'error', (error) ->
+#     test.same "b00m", error.message
+
+#   pool.on 'exit', ->
+#     test.ok true
+#     test.done()
+
+#   pool.spawn()
+
+# exports.testErrorCreatingProcessOnProxy = (test) ->
+#   test.expect 3
+
+#   pool = createPool "#{__dirname}/fixtures/crash.ru", size: 1
+
+#   pool.on 'exit', ->
+#     test.ok true
+
+#   server = http.createServer (req, res) ->
+#     server.close()
+
+#     pool.proxyRequest req, res, (err) ->
+#       test.ok err
+#       res.end()
+#       pool.quit()
+
+#   server.listen PORT
+#   server.on 'listening', ->
+#     http.cat "http://127.0.0.1:#{PORT}/", "utf8", (err, data) ->
+#       test.ok err
+#       test.done()


fix(autocomplete): Fix issue where `minLength` text was not localisable

Also fix issue where min length was not inserted into the string correctly
due to a typo

--- modules/autocomplete/main/index.coffee
+++ modules/autocomplete/main/index.coffee
@@ -3,7 +3,8 @@
     loading: 'Loading...',
     noResultsFound: 'No results found',
     otherResults: 'Other Results',
-    pleaseEnterMinCharacters: 'Please enter $minLength or more characters'
+    pleaseEnterMinCharacters: 'Please enter $minLength or more characters',
+    minCharacters: 'Min length $minLength characters',
   }
 })

@@ -132,7 +133,7 @@
     if not filteredData?
       message.text = @options.loadingMessage
     else if searchTerm.length < @options.minLength
-      message.text = hx.userFacingText.format(@options.pleaseEnterMinCharactersMessage, { minLength: options.minLength })
+      message.text = hx.userFacingText.format(@options.pleaseEnterMinCharactersMessage, { minLength: @options.minLength })
     else if (searchTerm.length > 0 or @options.showAll) and filteredData.length is 0
       if @options.trimTrailingSpaces and _.input.value().lastIndexOf(' ') is _.input.value().length - 1
         trimAndReload = true
@@ -197,6 +198,7 @@
         noResultsMessage: hx.userFacingText('autoComplete', 'noResultsFound')
         otherResultsMessage: hx.userFacingText('autoComplete', 'otherResults')
         pleaseEnterMinCharactersMessage: hx.userFacingText('autoComplete', 'pleaseEnterMinCharacters', true)
+        minCharactersMessage: hx.userFacingText('autoComplete', 'minCharacters', true)
       }, opts

       @_ = _ = {}
@@ -233,7 +235,7 @@
         (elem, item) -> hx.select(elem).text(item)

       @options.placeholder ?= if @options.minLength > 0
-        "Min length #{@options.minLength} characters"
+        hx.userFacingText.format(@options.minCharactersMessage, { minLength: @options.minLength })

       # create input and menu objects
       input = hx.select @selector


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -10,8 +10,7 @@
 enemy = GameObject
   health: 500

-
-
+player.
 # => Uncaught TypeError: Object has no method 'position'

 player.include(Bounded)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -128,8 +128,8 @@
   @methodOf Metered#
   @param {Object} options The meter configuration options
     borderColor - Color of the meter's border
-    borderWidth - The width of the meter's border
-    color - The col
+    borderWidth - Width of the meter's border
+    color - Color of
   ###
   meter: (options={}) ->
     Object.reverseMerge options,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -173,6 +173,7 @@
   player.meter 'health'
     show: false

+  # enabl
   player.showMeter 'health'
   </pre></code>


Fixed the name of Uint32 data type.

Fixed the name of Uint32 data type.

--- src/ndarray.coffee
+++ src/ndarray.coffee
@@ -34,7 +34,7 @@
 ###
 uint32 =
     size : 4
-    name : 'uint16'
+    name : 'uint32'

 ###
 Integer 32 bits
@@ -242,7 +242,7 @@

     TODO: testing with more than 1 dimension
     ###
-    flatPosition: (p) ->
+    flat: (p) ->

         if p.length != @ndim
             throw new error.NumjisException('incorrect position dimension, got: ' + p.length + ' expecting: ' + @ndim)
@@ -252,6 +252,8 @@
         for d in [0...@ndim]
             f += @stride[d]*p[d]

+        return f
+

     ###
     Fill the array with a given constant value
@@ -263,6 +265,19 @@
         for n in [0...@length]
             @data[n] = value

+
+###
+Creates a ND-array filled with zeros
+
+@param [int list] shape array shape
+@param [dtype, optional] array type
+
+@return NDArray object
+###
+zeros = (shape, dtype=float32) ->
+
+    arr = new NDArray(shape, dtype=dtype)
+    return arr

 ###
@@ -327,6 +342,7 @@
     # functions
     registerType : registerType
     typeFromName : typeFromName
+    zeros : zeros
     copy : copy
     arange : arange
     reshape : reshape


Accept options

Accept options

--- scripts/rc.coffee
+++ scripts/rc.coffee
@@ -8,8 +8,9 @@

 # Robot hooks
 module.exports = (robot) ->
-  robot.respond /(?:rc)\s+for\s([^\s]+)\s*$/i, (msg) ->
+  robot.respond /(?:rc)\s+for\s([^\s]+)\s*([^\s]+.*)?\s*$/i, (msg) ->
     who = msg.match[1].toLowerCase()
+    options = (msg.match[2] ? "").toLowerCase()

     new RCScore(who).fetch (err, self) =>
       if self.score == null


De-duplicate peerDep cycles by (name,version) tuples, not (name, spec) tuples

--- src/npm2nix.coffee
+++ src/npm2nix.coffee
@@ -52,7 +52,7 @@
         if el.name is node.name
           cycleDetected = true
           if el is path[0]
-            pkg.cycles.push path.slice 1
+            pkg.cycles.push path.slice 0
       unless cycleDetected
         peerDeps = packageSet[node.name][node.spec].peerDependencies or {}
         if Object.keys(peerDeps).length > index
@@ -71,9 +71,9 @@
   stream = fs.createWriteStream args.output
   stream.write "{ self, fetchurl, lib }:\n\n{"
   writePkg = (name, spec, pkg) ->
-    if name of cycleMembers and spec of cycleMembers[name]
+    if name of cycleMembers and pkg.version of cycleMembers[name]
       stream.write """
-      \n  full."#{escapeNixString name}"."#{escapeNixString spec}" = self.full."#{escapeNixString cycleMembers[name][spec].name}"."#{escapeNixString cycleMembers[name][spec].spec}";
+      \n  full."#{escapeNixString name}"."#{escapeNixString spec}" = self.full."#{escapeNixString cycleMembers[name][pkg.version].name}"."#{escapeNixString cycleMembers[name][pkg.version].spec}";
       """
     else
       findCycles name, spec, pkg
@@ -86,7 +86,7 @@
       for cycle in pkg.cycles
         for node in cycle
           cycleMembers[node.name] ?= {}
-          cycleMembers[node.name][node.spec] = { name: name, spec: spec }
+          cycleMembers[node.name][packageSet[node.name][node.spec].version] = { name: name, spec: spec }
           unless seen[node.name]
             pkgs.push packageSet[node.name][node.spec]
             names.push node.name


remove unused Theme.find and make code more readable(?)

--- server/app.coffee
+++ server/app.coffee
@@ -121,24 +121,19 @@
   Indicator = require("./models/indicator").model
   IndicatorData = require("./models/indicator_data").model

+  themeSeedsPath = "#{process.cwd()}/config/seeds/themes.json"
+  indicatorSeedsPath = "#{process.cwd()}/config/seeds/indicators.json"

-  Promise.promisify(Theme.count, Theme)(
-    null
-  ).then((themeCount) ->
+  Promise.join(
+    Promise.promisify(Theme.count, Theme)(null),
+    Promise.promisify(Indicator.count, Indicator)(null)
+  ).spread( (themesCount, indicatorsCount) ->
+    seedingPromise = Promise.resolve()
     if themeCount == 0
-      Theme.seedData(
-        "#{process.cwd()}/config/seeds/themes.json"
-      )
-    else
-      Promise.promisify(Theme.find, Theme)()
-  ).then((themes) ->
-    Promise.promisify(Indicator.count, Indicator)(null)
-  ).then((indicatorsCount) ->
+      seedingPromise = seedingPromise.then(Theme.seedData(themeSeedsPath))
     if indicatorsCount == 0
-      Indicator.seedData(
-        "#{process.cwd()}/config/seeds/indicators.json"
-      )
-  ).catch((err) ->
+      seedingPromise = seedingPromise.then(Indicator.seedData(indicatorSeedsPath))
+  ).catch( (err) ->
     console.log "error seeding indicator data:"
     console.error err
     console.error err.stack


atom-tree-view: nicer settings

atom-tree-view: nicer settings

--- .atom/config.cson
+++ .atom/config.cson
@@ -33,6 +33,11 @@
     lintOnFly: false
   "linter-pep257": {}
   "autocomplete-plus": {}
+  "tree-view":
+    autoReveal: true
+    focusOnReveal: false
+    hideIgnoredNames: true
+    hideVcsIgnoredFiles: true
   whitespace:
     ignoreWhitespaceOnCurrentLine: false
   "keyboard-localization":


atom: change eslint-linter settings

atom: change eslint-linter settings

--- .atom/config.cson
+++ .atom/config.cson
@@ -96,9 +96,7 @@
     validateOnlyOnSave: true
   linter:
     lintOnChange: false
-  "linter-eslint":
-    disableWhenNoEslintConfig: false
-    eslintrcPath: "/home/max/conf/.eslintrc.js"
+  "linter-eslint": {}
   "linter-flake8":
     ignoreErrorCodes: [
       "E128"


Use atom.styles to activate stylesheets in packages

--- src/package.coffee
+++ src/package.coffee
@@ -62,6 +62,7 @@

   constructor: (@path, @metadata) ->
     @emitter = new Emitter
+    @stylesheetDisposables = new CompositeDisposable
     @metadata ?= Package.loadMetadata(@path)
     @bundledPackage = Package.isBundledPackagePath(@path)
     @name = @metadata?.name ? path.basename(@path)
@@ -175,9 +176,9 @@
   activateStylesheets: ->
     return if @stylesheetsActivated

-    type = @getStylesheetType()
-    for [stylesheetPath, content] in @stylesheets
-      atom.themes.applyStylesheet(stylesheetPath, content, type)
+    group = @getStylesheetType()
+    for [sourcePath, source] in @stylesheets
+      @stylesheetDisposables.add(atom.styles.addStyleSheet(source, {sourcePath, sourcePath}))
     @stylesheetsActivated = true

   activateResources: ->
@@ -329,11 +330,10 @@
   reloadStylesheets: ->
     oldSheets = _.clone(@stylesheets)
     @loadStylesheets()
-    atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in oldSheets
-    @reloadStylesheet(stylesheetPath, content) for [stylesheetPath, content] in @stylesheets
-
-  reloadStylesheet: (stylesheetPath, content) ->
-    atom.themes.applyStylesheet(stylesheetPath, content, @getStylesheetType())
+    @stylesheetDisposables.dispose()
+    @stylesheetDisposables = new CompositeDisposable
+    @stylesheetsActivated = false
+    @activateStylesheets()

   requireMainModule: ->
     return @mainModule if @mainModule?


update mastercard range to 51-55

update mastercard range to 51-55

https://github.com/stripe/jquery.payment/pull/173

--- src/payform.coffee
+++ src/payform.coffee
@@ -94,7 +94,7 @@
     }
     {
       type: 'mastercard'
-      pattern: /^(5[0-5]|2[2-7])/
+      pattern: /^(5[1-5]|2[2-7])/
       format: defaultFormat
       length: [16]
       cvcLength: [3]


closes #453

closes #453

--- src/polling.coffee
+++ src/polling.coffee
@@ -36,7 +36,7 @@
     callback null

 exports.setupAgenda = (agenda, callback) ->
-  logger.info "Starting polling server"
+  logger.info "Starting polling server..."
   registerPollingChannelPromise = Q.denodeify exports.registerPollingChannel
   exports.agendaGlobal = agenda
   Channel.find { type: 'polling' }, (err, channels) ->


Make requires better

Make requires better

--- src/scandal.coffee
+++ src/scandal.coffee
@@ -5,10 +5,11 @@
 PathReplacer = require './path-replacer'
 path = require "path"

-search = require('./single-process-search').search
-singleProcessScanMain = require('./single-process-search').scanMain
-singleProcessSearchMain = require('./single-process-search').searchMain
-singleProcessReplaceMain = require('./single-process-search').replaceMain
+SingleProcess = require('./single-process-search')
+{search} = SingleProcess
+singleProcessScanMain = SingleProcess.scanMain
+singleProcessSearchMain = SingleProcess.searchMain
+singleProcessReplaceMain = SingleProcess.replaceMain

 ###
 This CLI is mainly for benchmarking. While there may be useful data output to


Finish writing achievement test cases

Finish writing achievement test cases

--- test/achievements.coffee
+++ test/achievements.coffee
@@ -6,8 +6,110 @@
   game.init()
   state = game.state
   achievements = game.achievements
-  observer = new Game.Observer()
-  events = []
-  callback = (e) ->
-    events.push e
-  observer.listen(achievements, callback)+  describe 'Structure', ->
+    it 'Should have an addAchievement method', ->
+      expect(achievements).to.respondTo('addAchievement')
+    it 'Should have an hasAchieved method', ->
+      expect(achievements).to.respondTo('hasAchieved')
+  describe 'Achievements', ->
+    describe 'Basic achievement handling', ->
+      observer = new Game.Observer()
+      events = []
+      callback = (e) ->
+        events.push e
+      observer.listen(achievements, callback)
+      it 'Should add achievements properly', ->
+        data = [
+          ['test']
+        ]
+        achievements.addAchievement('test', data)
+        expect(achievements.achievements).to.have.property('test', data)
+      it 'Should fire achievement event when an achievement is achieved', ->
+        state.fire('test')
+        lastEvent = events.pop()
+        expect(lastEvent).to.have.property('type', 'achievement')
+        expect(lastEvent.data).to.have.property('name', 'test')
+      observer.remove(achievements)
+    describe 'Different kinds of achievements', ->
+      data = {
+        basicCount: [
+          ['asdf', null, 5]
+        ]
+        countData: [
+          ['asdf', { type: 1 }, 5]
+        ]
+        multiple: [
+          ['asdf3']
+          ['asdf4']
+        ]
+        countWithinTime: [
+          ['asdf2', null, 5, 1000]
+        ]
+        withinTimeOf: [
+          ['asdf', null, 1, 1000, 'asdf2']
+        ]
+        requireAchievement: [
+          ['a:basicCount']
+          ['asdf5']
+        ]
+      }
+      for name, reqs of data
+        try
+          achievements.addAchievement(name, reqs)
+        catch
+          break
+      it 'Should work with achievements that require a count', ->
+        expect(achievements.hasAchieved('basicCount')).to.be.false
+        for i in [1..5]
+          state.fire('asdf', { type: (i / 3) | 0 })
+        expect(achievements.hasAchieved('basicCount')).to.be.true
+      it 'Should work with achievements that require a count with data', ->
+        expect(achievements.hasAchieved('countData')).to.be.false
+        for i in [1..2]
+          state.fire('asdf', { type: 1 })
+        expect(achievements.hasAchieved('countData')).to.be.true
+      it 'Should work with multiple required events', ->
+        expect(achievements.hasAchieved('multiple')).to.be.false
+        state.fire('asdf3')
+        expect(achievements.hasAchieved('multiple')).to.be.false
+        state.fire('asdf4')
+        expect(achievements.hasAchieved('multiple')).to.be.true
+      it 'Should work with achievements that require a count within a time period', (done) ->
+        this.timeout(2500)
+        expect(achievements.hasAchieved('countWithinTime')).to.be.false
+        times = []
+        # Run the event 5 times in a 1500ms span
+        for i in [1..4]
+          state.fire('asdf2')
+        setTimeout(() ->
+          state.fire('asdf2')
+          expect(achievements.hasAchieved('countWithinTime')).to.be.false
+          times = []
+          for i in [1..4]
+            state.fire('asdf2')
+        , 1250)
+        # Run the event 5 times in a 750ms span
+        setTimeout(() ->
+          state.fire('asdf2')
+          expect(achievements.hasAchieved('countWithinTime')).to.be.true
+          done()
+        , 1250 + 750)
+      it 'Should work with achievements that require an event to be fired within a time period after another event', (done) ->
+        this.timeout(2500)
+        state.fire('asdf')
+        expect(achievements.hasAchieved('withinTimeOf')).to.be.false
+        setTimeout(() ->
+          state.fire('asdf2')
+          expect(achievements.hasAchieved('withinTimeOf')).to.be.false
+          state.fire('asdf')
+        , 1250)
+        setTimeout(() ->
+          state.fire('asdf2')
+          expect(achievements.hasAchieved('withinTimeOf')).to.be.true
+          done()
+        , 1250 + 750)
+      it 'Should work with achievements that require a prior achievement', ->
+        expect(achievements.hasAchieved('basicCount')).to.be.true
+        expect(achievements.hasAchieved('requireAchievement')).to.be.false
+        state.fire('asdf5')
+        expect(achievements.hasAchieved('requireAchievement')).to.be.true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/approachable.coffee
+++ test/approachable.coffee
@@ -58,6 +58,7 @@

   obj.include(Approachable)

+  4.times ->
     obj.update()

   equals obj.I.cool, 0


Enable browser integration tests on phantomjs

--- test/browser.spec.coffee
+++ test/browser.spec.coffee
@@ -303,13 +303,13 @@
       svc = new AWS.S3(AWS.util.merge({signatureVersion: 'v4'}, config.s3))
       testWrite done, 'ƒoo', null, svc

-    it 'writes typed array data (no phantomjs)', (done) ->
+    it 'writes typed array data', (done) ->
       testWrite done, new Uint8Array([2, 4, 8]), (data) ->
         expect(data.Body[0]).to.equal(2)
         expect(data.Body[1]).to.equal(4)
         expect(data.Body[2]).to.equal(8)

-    it 'writes blobs (no phantomjs)', (done) ->
+    it 'writes blobs', (done) ->
       testWrite done, new Blob(['a', 'b', 'c']), (data) ->
         expect(data.Body[0]).to.equal(97)
         expect(data.Body[1]).to.equal(98)
@@ -326,7 +326,7 @@
             s3.deleteObject(Key: key).send(done)

     describe 'upload()', ->
-      it 'supports blobs using upload() (no phantomjs)', (done) ->
+      it 'supports blobs using upload()', (done) ->
         key = uniqueName('test')
         size = 100
         u = s3.upload(Key: key, Body: new Blob([new Uint8Array(size)]))


Fix tests due to precedence change.

Fix tests due to precedence change.

--- test/coffee/tests.coffee
+++ test/coffee/tests.coffee
@@ -5,7 +5,7 @@
       strictEqual coffee.parse(str, wrapAtRoot: true).serialize(), expected, m

     testString 'Function call',
-      'fd 10', '<block color="command" precedence="0">fd <socket precedence="0">10</socket></block>'
+      'fd 10', '<block color="command" precedence="0">fd <socket precedence="-1">10</socket></block>'

     testString 'Variable assignment',
       'a = b', '<block color="command" precedence="0"><socket precedence="0">a</socket> = <socket precedence="0">b</socket></block>'
@@ -16,7 +16,7 @@
         fd 10
       ''', '''
       <block color="control" precedence="0">if <socket precedence="0">true</socket><indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent></block>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent></block>
       '''

     testString 'Unless statement, normal form',
@@ -25,7 +25,7 @@
         fd 10
       ''', '''
       <block color="control" precedence="0">unless <socket precedence="0">true</socket><indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent></block>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent></block>
       '''

     testString 'One-line if statement',
@@ -40,9 +40,9 @@
         fd 10
       ''', '''
       <block color="control" precedence="0">if <socket precedence="0">true</socket><indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent>
       else<indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent></block>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent></block>
       '''

     testString 'One-line if-else statement',
@@ -55,12 +55,12 @@
         fd 10
       ''', '''
       <block color="control" precedence="0">while <socket precedence="0">a</socket><indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent></block>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent></block>
       '''

     testString 'One-line while statement',
       'while a then fd 10',
-      '<block color="control" precedence="0">while <socket precedence="0">a</socket> then <socket precedence="0"><block color="command" precedence="0">fd <socket precedence="0">10</socket></block></socket></block>'
+      '<block color="control" precedence="0">while <socket precedence="0">a</socket> then <socket precedence="0"><block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></socket></block>'

     testString 'For-in, normal form',
       '''
@@ -68,21 +68,21 @@
         fd 10
       ''', '''
       <block color="control" precedence="0">for <socket precedence="0">i</socket> in <socket precedence="0">list</socket><indent depth="2">
-      <block color="command" precedence="0">fd <socket precedence="0">10</socket></block></indent></block>
+      <block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></indent></block>
       '''

     testString 'One-line for-in',
       'for i in list then fd 10'
-      '<block color="control" precedence="0">for <socket precedence="0">i</socket> in <socket precedence="0">list</socket> then <socket precedence="0"><block color="command" precedence="0">fd <socket precedence="0">10</socket></block></socket></block>'
+      '<block color="control" precedence="0">for <socket precedence="0">i</socket> in <socket precedence="0">list</socket> then <socket precedence="0"><block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></socket></block>'

     testString 'Inverted one-line for-in',
       'fd 10 for i in list',
-      '<block color="control" precedence="0"><socket precedence="0"><block color="command" precedence="0">fd <socket precedence="0">10</socket></block></socket> for <socket precedence="0">i</socket> in <socket precedence="0">list</socket></block>'
+      '<block color="control" precedence="0"><socket precedence="0"><block color="command" precedence="0">fd <socket precedence="-1">10</socket></block></socket> for <socket precedence="0">i</socket> in <socket precedence="0">list</socket></block>'

     testString 'Semicolons at the root',
       'fd 10; bk 10',
       '<block color="command" precedence="0"><socket precedence="0">'+
-      '<block color="command" precedence="0">fd <socket precedence="0">10</socket></block>'+
+      '<block color="command" precedence="0">fd <socket precedence="-1">10</socket></block>'+
       '</socket>; <socket precedence="0">'+
       '<block color="command" precedence="0">bk <socket precedence="0">10</socket></block></socket></block>'

@@ -102,7 +102,7 @@
       '<block color="control" precedence="0">while <socket precedence="0">a</socket><indent depth="2">\n' +
       '<block color="command" precedence="0">see <socket precedence="0">hi</socket></block>\n'+
       '<block color="command" precedence="0"><socket precedence="0">'+
-      '<block color="command" precedence="0">fd <socket precedence="0">10</socket></block>'+
+      '<block color="command" precedence="0">fd <socket precedence="-1">10</socket></block>'+
       '</socket>; <socket precedence="0">'+
       '<block color="command" precedence="0">bk <socket precedence="0">10</socket></block></socket></block>\n'+
       '<block color="command" precedence="0">see <socket precedence="0">bye</socket></block></indent></block>'
@@ -210,7 +210,7 @@
     testString 'Parentheses around semicolon block',
       '(fd 10; bk 10)',
       '<block color="command" precedence="0">(<socket precedence="0"><block color="command" precedence="0">' +
-      'fd <socket precedence="0">10</socket></block></socket>; <socket precedence="0"><block color="command" precedence="0">' +
+      'fd <socket precedence="-1">10</socket></block></socket>; <socket precedence="0"><block color="command" precedence="0">' +
       'bk <socket precedence="0">10</socket></block></socket>)</block>'

@@ -377,8 +377,8 @@
     document.getBlockOnLine(1).moveTo document.getBlockOnLine(0).end.prev.container.start

     strictEqual document.stringify(), '''
-    console.log (for i in [1..10]
-      console.log hello)
+    console.log for i in [1..10]
+      console.log hello
     ''', 'Move for into socket (req. paren wrap)'

   test 'Paren wrap', ->


Adding timeout

Adding timeout

--- test/fixtures/app.coffee
+++ test/fixtures/app.coffee
@@ -50,6 +50,9 @@
   server.listen PORT, -> seedData.create done

 afterEach (done) ->
-  server.close -> db.wipe done
+  server.close ->
+    setTimeout ->
+      db.wipe(done)
+    , 200


fix localized message not defined on default exceptions

--- src/mailButler.appsscript.coffee
+++ src/mailButler.appsscript.coffee
@@ -103,7 +103,7 @@
       @addButlerMail params
       true
     catch e
-      e.getLocalisedMessage()
+      e.toString()

   @uninstall: (automatic) ->
     if not automatic and (base = ScriptApp.getService().getUrl())


test: fix quote indentation

test: fix quote indentation

--- test/option.quote.coffee
+++ test/option.quote.coffee
@@ -4,18 +4,18 @@

 describe 'Option `quote`', ->

-    it 'is compatible with buffer size', (next) ->
-      parser = parse escape: '$', quote: '::::::', (err, data) ->
-        data.should.eql [
-          [ '1', '2::::::2', '3' ]
-          [ 'b', 'c', 'd' ]
-        ]
-        next err
-      parser.write c for c in """
-      1,::::::2$::::::2::::::,3
-      b,c,d
-      """
-      parser.end()
+  it 'is compatible with buffer size', (next) ->
+    parser = parse escape: '$', quote: '::::::', (err, data) ->
+      data.should.eql [
+        [ '1', '2::::::2', '3' ]
+        [ 'b', 'c', 'd' ]
+      ]
+      next err
+    parser.write c for c in """
+    1,::::::2$::::::2::::::,3
+    b,c,d
+    """
+    parser.end()

   it 'default', ->
     parser = parse()


removed console logs from the tests

removed console logs from the tests

--- test/orpheus.spec.coffee
+++ test/orpheus.spec.coffee
@@ -192,9 +192,7 @@
 				.book_pages.get(key: ['Infinite Jest'])
 				.book_pages.get(key: ['A Game of Thrones'])
 				.leaderboard.get(key: ['Almog'])
-				.when( -> console.log @_commands)
 				.exec (err, res) ->
-					console.log res
 					expect(res.book_pages['1984 pages']).toEqual(500)
 					expect(res.book_pages['Infinite Jest pages']).toEqual(200)
 					expect(res.book_pages['A Game of Thrones pages']).toEqual(9999)
@@ -443,7 +441,6 @@
 						.points.zrange(0, -1, 'withscores')
 						.points.card()
 						.exec (err, user) ->
-							console.log(user)
 							if err then cb(err)
 							else
 								expect(user.name).toEqual 'feist'


WIP; small fixes & improvements

WIP; small fixes & improvements

--- src/services/locations.coffee
+++ src/services/locations.coffee
@@ -14,11 +14,11 @@
 	if req.query.results?
 		options.results = parseInt req.query.results
 	if req.query.stations?
-		options.products.stations = parse req.query.stations
+		options.stations = parse req.query.stations
 	if req.query.addresses?
-		options.products.addresses = parse req.query.addresses
+		options.addresses = parse req.query.addresses
 	if req.query.pois?
-		options.products.pois = parse req.query.pois
+		options.pois = parse req.query.pois
 	options.products = {}
 	for product in [
 		'suburban',


project: also expose OOM info to prom

project: also expose OOM info to prom

--- src/smc-project/kucalc.coffee
+++ src/smc-project/kucalc.coffee
@@ -229,6 +229,9 @@
     # HELP cocalc_project_running_processes_total
     # TYPE cocalc_project_running_processes_total gauge
     cocalc_project_running_processes_total{#{labels}} #{current_status.processes?.count ? 0}
+    # HELP cocalc_project_oom_kills_total
+    # TYPE cocalc_project_oom_kills_total counter
+    cocalc_project_oom_kills_total{#{labels}} #{current_status.oom_kills ? 0}
     """ + '\n'  # makes sure the response ends with a newline!

 # called inside raw_server


Tweak some synthesis/occupants defaults

Tweak some synthesis/occupants defaults

--- lib/adapters/synthesis/OccupantsNode.coffee
+++ lib/adapters/synthesis/OccupantsNode.coffee
@@ -44,9 +44,9 @@
     people: {} # name: { networkPresence: [path to a node with a
                #         networkPresenceSensor aspect] }
     doors:  [] # A list of paths with openCloseSensor aspects
-    networkGraceTime:  120 # (sec) time a device can be on/off the network
+    networkGraceTime:    0 # (sec) time a device can be on/off the network
                            # before we consider it to be here/gone
-    maxExitLagTime:    900 # (sec) max time after an door open that a following
+    maxExitLagTime:   1200 # (sec) max time after an door open that a following
                            # network presence loss may be considered related
     maxExitLeadTime:   120 # (sec) max time before an door open that a preceding
                            # network presence loss may be considered related


fixed same page link

fixed same page link

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -144,10 +144,8 @@
   event.which > 1 or event.metaKey or event.ctrlKey

 ignoreClick = (event, link) ->
-  samePageLink(link) or crossOriginLink(link) or anchoredLink(link) or
-  nonHtmlLink(link)  or remoteLink(link)      or noTurbolink(link)  or
-  newTabClick(event)
-
+  crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or
+  remoteLink(link)      or noTurbolink(link)  or newTabClick(event)

 browserSupportsPushState =
   window.history and window.history.pushState and window.history.replaceState


Another Regexer

Another Regexer

--- test/streams/load.coffee
+++ test/streams/load.coffee
@@ -38,22 +38,9 @@
       s.transform -> new CoffeeTransform()
       s.on 'data', (chunk) -> d += chunk
       s.on 'end', ->
-        #Ignore the annoying as hell "compiled by" message
-        d = d.split('\n')[1...].join('\n')
-        d.should.equal expected
-        return done()
-
-      expected = """
-      (function() {
-        (function() {
-          return new KDNotificationView({
-            title: 'Stub'
-          });
-        })();
-
-      }).call(this);
-
-      """
+        d.should.match /generated by coffeescript/i
+        d.should.match /required to pass/
+        done()

     it 'should pass filename and extension to the callback', (done) ->
       d = []