removing old comment

removing old comment

--- lib/lt3.coffee
+++ lib/lt3.coffee
@@ -136,7 +136,7 @@
             log "------End Object------"
           rl.prompt();
     ###
-
+
 executeCommand = (l_token,l_command,l_args) =>
   if @shell_context
     executeContextCommand l_token,l_command,l_args
@@ -170,7 +170,6 @@
             log "_id: #{object.data._id}, collection:#{object.data.collection},
             type:#{object.data.type}"
           rl.prompt();
-
     when 'get:site_ids'
       getDB (db) ->
         object=[]
@@ -823,62 +822,7 @@
             throw err if err
             log "page added to #{site_slug}"
             exit()
-
-  when 'add' ##DOES NOT WORK YET IN PROGRESS
-    getDB (db) ->
-       db.get('objects').find({slug:"richardduran2"}) (err, site) ->
-        log site
-
-    ###
-    # parse arguments
-    site_slug = args[0]
-    collection_name = args[1]
-    object_type = args[2]
-    object_slug = args[3]
-
-    exit("must specify object type") unless object_type
-    exit("must specify collection name") unless collection_name
-    exit("must specify site slug") unless site_slug
-    exit("must specify object slug") unless object_slug
-    object_slug = object_slug.replace /^\/(.*)/, '$1'
-
-    getDB (db) ->
-
-      # make sure site exists
-      db.get('sites').findOne {
-        slug: site_slug
-      }, (err, site) ->
-        throw err if err
-        exit('site does not exist') unless site
-
-        # make sure page location isn't taken
-        db.get('objects').findOne {
-          'site_id': site.get('_id').val()
-          slug: object_slug
-        }, (err, object) ->
-          throw err if err
-          exit('object already exists at this location') if object
-
-          # insert new page
-          db.get('objects').insert {
-            data: {}
-            password: ''
-            seo:
-              title: ''
-              description: ''
-              keywords: ''
-              image: ''
-            site_id: site.get('_id').val()
-            slug: object_slug
-            tags: [object_type, object_slug]
-            type: object_type
-          }, (err, object) ->
-            throw err if err
-            log "object added to #{site_slug}"
-            exit()
-    ###
-
-
+s
   when 'add:object'

     # parse arguments


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -13,7 +13,7 @@
   # put the player outside the viewport
   player.I.x = -400

-  #
+  # updat t
   player.update()
 </pre></code>


adding default title name

adding default title name

--- src/coffee/bokeh.coffee
+++ src/coffee/bokeh.coffee
@@ -480,7 +480,8 @@
   'legends' : [],
   'tools' : [],
   'overlays' : [],
-  'usedialog' : false
+  'usedialog' : false,
+  'title' : 'Plot'
   #axes fit here
 })
 Plot::display_defaults = _.clone(Plot::display_defaults)


Add beta Utils.globalLayers

Add beta Utils.globalLayers

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -565,3 +565,24 @@

 _.extend exports, Utils

+
+Utils.globalLayers = (importedLayers) ->
+
+	# Beta. Not sure if we should push this but it's nice to have.
+	# Use this to make all layers in an imported set available on
+	# on the top level, so without the "importedLayers" prefix.
+
+	for layerName, layer of importedLayers
+
+		# Replace all whitespace in layer names
+		layerName = layerName.replace(/\s/g,"")
+
+		# Check if there are global variables with the same name
+		if window.hasOwnProperty(layerName) and not window.Framer._globalWarningGiven
+			print "Warning: Cannot make layer '#{layerName}' a global, an variable with that name already exists"
+		else
+			window[layerName] = layer
+
+	window.Framer._globalWarningGiven = true
+
+


change limit to rootcontext (antonym)

change limit to rootcontext (antonym)

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -751,10 +751,10 @@

 # Coordinate system

-# convert a point from a layer to the context level, with limit you can make it continue to the root context
-Utils.convertPointToContext = (point = {}, layer, limit=false, includeLayer = true) ->
+# convert a point from a layer to the context level, with rootContext you can make it continue to the root context
+Utils.convertPointToContext = (point = {}, layer, rootContext=false, includeLayer = true) ->
 	point = _.defaults(point, {x:0, y:0, z:0})
-	ancestors = layer.ancestors(limit)
+	ancestors = layer.ancestors(rootContext)
 	ancestors.unshift(layer) if includeLayer

 	for ancestor in ancestors
@@ -764,17 +764,17 @@

 	return point

-Utils.convertFrameToContext = (frame = {}, layer, limit=false, includeLayer = true) ->
+Utils.convertFrameToContext = (frame = {}, layer, rootContext=false, includeLayer = true) ->
 	frame = _.defaults(frame, {x:0, y:0, width:100, height:100})
 	corners = Utils.pointsFromFrame(frame)
 	convertedCorners = corners.map (point) =>
-		return Utils.convertPointToContext(point, layer, limit, includeLayer)
+		return Utils.convertPointToContext(point, layer, rootContext, includeLayer)
 	return Utils.frameFromPoints(convertedCorners)

-# convert a point from the context level to a layer, with limit you can make it start from the root context
-Utils.convertPointFromContext = (point = {}, layer, limit=false, includeLayer = true) ->
+# convert a point from the context level to a layer, with rootContext you can make it start from the root context
+Utils.convertPointFromContext = (point = {}, layer, rootContext=false, includeLayer = true) ->
 	point = _.defaults(point, {x:0, y:0, z:0})
-	ancestors = layer.ancestors(limit)
+	ancestors = layer.ancestors(rootContext)
 	point = ancestors.pop().matrix3d.inverse().point(point) if ancestors.length
 	ancestors.reverse()
 	ancestors.push(layer) if includeLayer
@@ -782,12 +782,12 @@
 		point = ancestor.matrix3d.inverse().point(point)
 	return point

-# convert a frame from the context level to a layer, with limit you can make it start from the root context
-Utils.convertFrameFromContext = (frame = {}, layer, limit=false, includeLayer = true) ->
+# convert a frame from the context level to a layer, with rootContext you can make it start from the root context
+Utils.convertFrameFromContext = (frame = {}, layer, rootContext=false, includeLayer = true) ->
 	frame = _.defaults(frame, {x:0, y:0, width:100, height:100})
 	corners = Utils.pointsFromFrame(frame)
 	convertedCorners = corners.map (point) =>
-		return Utils.convertPointFromContext(point, layer, limit, includeLayer)
+		return Utils.convertPointFromContext(point, layer, rootContext, includeLayer)
 	return Utils.frameFromPoints(convertedCorners)

 # convert a point from layerA to layerB via the context


social: remove unused JAccount instance methods

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -111,14 +111,6 @@
         unfollow: [
           (signature Function)
         ]
-        fetchFollowersWithRelationship:
-          (signature Object, Object, Function)
-        countFollowersWithRelationship:
-          (signature Object, Function)
-        countFollowingWithRelationship:
-          (signature Object, Function)
-        fetchFollowingWithRelationship:
-          (signature Object, Object, Function)
         fetchTopics:
           (signature Object, Object, Function)
         fetchAppStorage:


Log missing data when try to fetch

Log missing data when try to fetch

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -245,6 +245,8 @@
         teasers = []
         collectTeasers = bongo.race (i, root, fin)->
           root.fetchSource (err, teaser)->
+            if not teaser
+              console.warn "Source does not exists:", root.sourceName, root.sourceId
             if err then callback err
             else
               teasers.push(teaser) if teaser


Remove process.argv args before calling rc to not interpret program.json and command name as command line arguments

--- CLI.coffee
+++ CLI.coffee
@@ -16,25 +16,34 @@
     commandLine = Meteor?.settings?.commandLine

     if commandLine
+      # This is not a meteor bundle, commandLine was provided in Meteor.settings,
+      # so we need to add 'node main.js' so rc will function properly.
       process.argv = Meteor.settings.commandLine.split(" ")
-      # In a meteor bundle, the first arg is node, the 2nd main.js, and the 3rd program.json
-      process.argv.unshift("program.json")
       process.argv.unshift("main.js")
       process.argv.unshift("node")
-
-    console.log process.argv.join(' ')
+    else
+      # In a meteor bundle, the first arg is node, the 2nd main.js, and the 3rd program.json
+      # We need to remove program.json, so it will not be interpreted by rc as a command line argument.
+      expect(process.argv[2], "program.json was expected at process.argv[2]").to.equal 'program.json'
+      process.argv.splice(2, 1)

       # THe first arg after is always the name of the command to execute.

-    expect(process.argv, "No command specified").to.to.have.length.above(3)
+    # Meteor._debug process.argv.join(' ')

-    commandName = process.argv[3]
+    expect(process.argv, "No command specified").to.have.length.above(2)
+
+    commandName = process.argv[2]

     command = @registeredCommands[commandName]
     expect(command, "#{commandName} is not a registered cli command").to.to.be.an 'object'

+    # Remove the command, so rc doesn't interpret it as a command line argument.
+    process.argv.splice(2, 1)
+
     opts = rc(commandName.replace('-', '_').toUpperCase(), command.defaultOptions)

+    # Execute the registered command
     command.func opts


load hit asynchronously with user info

load hit asynchronously with user info

--- loadHIT.coffee
+++ loadHIT.coffee
@@ -16,8 +16,8 @@
 db.on('error', console.error.bind(console, 'connection error:'))
 db.once('open', () ->
   async.waterfall([
-    getUserInfo,
-    getHitInfo
+    getBasicInfo,
+    getCondition
   ], (err, results) ->
     if (err) then return console.error err
     console.log "Waterfall done"
@@ -26,24 +26,30 @@
 )

 # 1. Count how many taskSets this user has done in this condition
-getUserInfo = (callback) ->
+# Also fetch HIT info, since it can be done asynchronously
+getBasicInfo = (callback) ->
   async.parallel({
     # Count of all taskSets done by the user
     countUserAll: (callback) -> TaskSet.count({user:user}, callback),
     # Count of tasksets in the given condition done
     countUserHIT: (callback) -> TaskSet.count({user:user, hit_id:hit_id}, callback)
+    # Load HIT info
+    hit: (callback) ->
+      Hit.find({_id: ObjectId(hit_id)}, (err, result) ->
+        if result.length is 0 then return callback("No matching hit", result)
+        callback(err, result[0])
+      )
   },
   # Pass (err, results) directly to the callback, which
   # will send it down the waterfall
   callback
   )

-# 2. Get HIT info and determine the current condition
-getHitInfo = (obj, callback) ->
-  Hit.find({_id: ObjectId(hit_id)}, (err, result) ->
-    if err or result.length is 0 then return callback(err, null)
-    obj.hit = result
-    console.log result
-    callback(err, result)
-    return
-  )
+# 2. Determine the current condition
+getCondition = (obj, callback) ->
+  if obj.countUserHIT == 1
+    obj.condition = obj.hit.condition.firstTask
+  else
+    obj.condition = obj.hit.condition.laterTasks
+  callback(null, obj)
+  return


[BETA] Always redirect to jumpIn section in Home.

--- src/core/voicious.coffee
+++ src/core/voicious.coffee
@@ -48,7 +48,7 @@
         @app.get '/', Session.withCurrentUser, (req, res) =>
             options =
                 title           : (@app.get 'title'),
-                hash            : req.query.hash || ''
+                hash            : '#jumpIn'
                 login_email     : ''
                 signup_email    : ''
                 name            : ''


Provide a function to centre the brush

Provide a function to centre the brush

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -245,5 +245,14 @@
         d3.select(@element).select('g.brush').call(@brush.extent([start, end]))
         true

+    center: ->
+        extent = @brush.extent()
+        center = -1 * (@scales.x(new Date((extent[0].getTime() + extent[1].getTime()) / 2)) - @options.width / 2)
+        center = 0 if center > 0
+        d3.select(@element)[0][0].dragging = { position: [center, 0] }
+
+        @root.attr('transform', "translate(#{center}, 0)")
+        true
+
 # Export the TimeSlider object for use in the browser
 this.TimeSlider = TimeSlider


bongo: base: add methods .getSignature and .testSignature

--- node_modules_koding/bongo/lib/base/index.coffee
+++ node_modules_koding/bongo/lib/base/index.coffee
@@ -307,6 +307,17 @@
     {instance} = @constructor.getSharedMethods()
     method in instance

+  @getSignature = (context, method) -> switch context
+    when 'static'     then @staticMethods_[method]
+    when 'instance'   then @instanceMethods_[method]
+
+  @testSignature = (context, method, args) ->
+    signatures = @getSignature context, method
+    signatures = [signatures]  unless Array.isArray signatures
+    for signature in signatures
+      return yes  if signature.test args
+    return no
+
   @setSharedEvents =(events)->
     {keys} = Object
     groupedEvents = getGroupedUnique events


split over 120 string

split over 120 string

--- scripts/amazon-ec.coffee
+++ scripts/amazon-ec.coffee
@@ -120,8 +120,19 @@
   robot.brain.set user, stores
   robot.brain.save()

-  msg.send "登録内容は" + stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title
+  showRegisteredContents(
+    stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title,
+    (item) -> (msg.send "登録内容は" + item)
+  )

+showRegisteredContents = (str,callback) ->
+
+  while(str.length > 120)
+    index = str.substring(0,120).lastIndexOf(',')
+    callback(str.substring(0,index))
+    str = str.substring(index+1)
+
+  callback(str)

 module.exports = (robot) ->

@@ -166,4 +177,8 @@
     user = if msg.envelope.user.hasOwnProperty('user') then msg.envelope.user.user else msg.envelope.user.name
     # 呼び出し
     stores = robot.brain.get(user)
-    msg.send "登録内容は" + stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title
+
+    showRegisteredContents(
+      stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title,
+      (item) -> (msg.send "登録内容は" + item)
+    )


Update plztarget selectors

Update plztarget selectors

--- grammars/build.cson
+++ grammars/build.cson
@@ -62,7 +62,7 @@
     match: '(\/(?:\/[\-\\w\.\_]*)*){0,1}:([\-\\w\.\_]+)'
     captures:
       1: name: 'plztarget.path'
-      2: name: 'storage.plztarget.name'
+      2: name: 'storage.type.plztarget.name'
   }

   number:


better jquery parity check

better jquery parity check

--- oridomi.coffee
+++ oridomi.coffee
@@ -126,7 +126,7 @@
 # Set a reference to jQuery (or another `$`-aliased DOM library).
 # If it doesn't exist, set to null so oriDomi knows we are working without jQuery.
 # oriDomi doesn't require it to work, but offers a useful plugin bridge.
-$ = if window.$?.data then window.$ else null
+$ = if (window.jQuery or window.$)?.data then window.$ else null

 # This variable is set to true and negated later if the browser does
 # not support oriDomi.


fixed typo in order risk

fixed typo in order risk

--- src/resources/order_risk.coffee
+++ src/resources/order_risk.coffee
@@ -10,4 +10,4 @@
 		super(site)

-module.exports = Risk
+module.exports = OrderRisk


Add singular function names to number module

--- src/galao/number.coffee
+++ src/galao/number.coffee
@@ -1,9 +1,18 @@
 # Number to time conversions
+Number::second ?= ->
+  this * 1000
+
 Number::seconds ?= ->
   this * 1000
+
+Number::minute ?= ->
+  @seconds() * 60

 Number::minutes ?= ->
   @seconds() * 60

+Number::hour ?= ->
+  @minutes() * 60
+
 Number::hours ?= ->
   @minutes() * 60


added multiple schemas to example

added multiple schemas to example

--- examples/draft3_advanced.coffee
+++ examples/draft3_advanced.coffee
@@ -3,8 +3,7 @@
 # a schema declaring a URI with "id"

 validator = new Validator
-  id: "urn:jsck.examples.basic#"
-  type: "object"
+  id: "urn:jsck.examples.advanced#"
   definitions:
     user:
       type: "object"
@@ -16,7 +15,10 @@
         email:
           type: "string"

-{valid} = validator.validate_schema "urn:jsck.examples.basic#", document
+{valid} = validator.validate_schema "urn:jsck.examples.advanced#",
+  login: "automatthew"
+  email: "automatthew@mailinator.com"
+
 console.log "Schema with id:", valid

@@ -26,5 +28,29 @@
   login: "automatthew"
   email: "automatthew@mailinator.com"

-{valid} = validator.validate_schema "urn:jsck.examples.basic#/definitions/user", document
+{valid} = validator.validate_schema "urn:jsck.examples.advanced#/definitions/user",
+  login: "automatthew"
+  email: "automatthew@mailinator.com"
+
 console.log "Schema identified by JSON Pointer:", valid
+
+
+# Adding multiple schemas
+#
+# You can instantiate JSCK with multiple schemas or add them later
+#
+# Instantiation:
+# validator = new Validator(schema1, schema2, schema3)
+
+validator.add
+  id: "urn:jsck.examples.user_list#"
+  type: "array"
+  items: {$ref: "urn:jsck.examples.advanced#/definitions/user"}
+
+{valid} = validator.validate_schema "urn:jsck.examples.user_list#", [
+  { login: "dyoder" }
+  { login: "automatthew" }
+]
+
+console.log "Multiple schemas:", valid
+


Better alignment within boundaries of text selection

--- js-library/app/js/jail_iframe/classes/buttons.coffee
+++ js-library/app/js/jail_iframe/classes/buttons.coffee
@@ -47,7 +47,14 @@

   placeNearSelection: (mouseX=null) ->
     selectionBox = window.document.getSelection().getRangeAt(0).getBoundingClientRect()
-    top = selectionBox.top + $(window).scrollTop() - 2
-    left = mouseX ? ($(window).scrollLeft() + selectionBox.left + selectionBox.width/2)
+    selectionLeft = selectionBox.left + $(window).scrollLeft()
+    selectionTop = selectionBox.top + $(window).scrollTop()
+    buttonWidth = @frame.$el.outerWidth(true)

-    @setCoordinates top, left
+    if mouseX
+      left = Math.min Math.max(mouseX, selectionLeft+buttonWidth/2),
+        selectionLeft+selectionBox.width-buttonWidth/2
+    else
+      left = selectionLeft + selectionBox.width/2
+
+    @setCoordinates selectionTop-2, left


Protocol messages are now logged to log file.

--- src/server/browser/index.coffee
+++ src/server/browser/index.coffee
@@ -38,17 +38,22 @@
         # If true, then DOM events should be emitted, otherwise, drop them.
         @emitting = false

+
+        # Browsers log to logs/#{browser.id}.log
+        # TODO: only use logfile if not running tests.
+        @logPath = Path.resolve(__dirname, '..', '..', '..', 'logs', "#{@id}.log")
+        @logStream = FS.createWriteStream(@logPath)
+        @logStream.write("Log opened: #{Date()}\n")
+        @logStream.write("BrowserID: #{@id}\n")
+
         # Performance tracking.
         @bytesSent = 0
         @bytesReceived = 0
         @on 'DOMEvent', (params) =>
-            @bytesSent += JSON.stringify(params).length
-
-        # Browsers log to logs/#{browser.id}.log
-        @logPath = Path.resolve(__dirname, '..', '..', '..', 'logs', "#{@id}.log")
-        @logStream = FS.createWriteStream(@logPath)
-        @logStream.write("Log opened: #{Date()}\n")
-        @logStream.write("BrowserID: #{@id}\n")
+            msg = JSON.stringify(params)
+            bytes = msg.length
+            @bytesSent += bytes
+            @logStream.write("#{Date()}  -- Sent #{bytes} bytes (#{msg})\n")

     pauseRendering : () =>
         @emit 'DOMEvent',
@@ -60,13 +65,19 @@

     processClientEvent : (params) ->
-        @bytesReceived += JSON.stringify(params).length
+        msg = JSON.stringify(params)
+        bytes = msg.length
+        @bytesReceived += bytes
+        @logStream.write("#{Date()}  -- Recv #{bytes} bytes (#{msg})\n")
         @pauseRendering()
         @events.processEvent(params)
         @resumeRendering()

     processClientDOMUpdate : (params) ->
-        @bytesReceived += JSON.stringify(params).length
+        msg = JSON.stringify(params)
+        bytes = msg.length
+        @bytesReceived += bytes
+        @logStream.write("#{Date()}  -- Recv #{bytes} bytes (#{msg})\n")
         @clientAPI.DOMUpdate(params)

     processComponentEvent : (params) ->


Use single function to build simulation page

--- js/simulation.coffee
+++ js/simulation.coffee
@@ -471,16 +471,26 @@
   add_card(addf, type, "#images", with_div=with_div, take_func=take_func)

-header(SIM_NAME)
-author(DATA, SIM_NAME)
-summary(DATA)
-github(DATA)
-code(DATA)
-benchmark(DATA)
-date(DATA)
-software(DATA)
-results_table(DATA)
-add_card(add_image, "image", "#logo_image")(DATA)
-add_card(add_youtube, "youtube", "#youtube")(DATA)
-add_card_col(add_image, "image", id)(DATA)
-add_card_col(add_chart, "line", take_data)(DATA)
+build = (data, sim_name) ->
+  ### Build the simulation landing page
+
+  Args:
+    data: the metadata for the simulation
+    sim_name: the name of the simulation
+  ###
+  header(sim_name)
+  author(data, sim_name)
+  summary(data)
+  github(data)
+  code(data)
+  benchmark(data)
+  date(data)
+  software(data)
+  results_table(data)
+  add_card(add_image, "image", "#logo_image")(data)
+  add_card(add_youtube, "youtube", "#youtube")(data)
+  add_card_col(add_image, "image", id)(data)
+  add_card_col(add_chart, "line", take_data)(data)
+
+
+build(DATA, SIM_NAME)


remove log statements

remove log statements

--- src/lib/run/task.coffee
+++ src/lib/run/task.coffee
@@ -52,11 +52,9 @@
         # pipeline.
         if load
           unless pipeline[0] instanceof @filters.loadFiles
-            @log.debug "PREPENDING loadFiles"
             pipeline.unshift @filters.loadFiles
         else
           unless pipeline[0] instanceof @filters.loadFilenames
-            @log.debug "PREPENDING loadFilenames"
             pipeline.unshift @filters.loadFilenames

         # sanity check


rm uneeded

rm uneeded

--- src/mixinfactory.coffee
+++ src/mixinfactory.coffee
@@ -27,12 +27,6 @@
     The augmentation logic is in the factory method .from_obj, which is
     the only meaningful way to create instances.
   ###
-
-
-  @_mixing_hooks: [
-    'premixing_hook'
-    'postmixing_hook'
-  ]

   @validate: (mixin) ->
     unless mixin instanceof @


Fixed unit conversion of nanoseconds

Fixed unit conversion of nanoseconds

--- src/natives/main.coffee
+++ src/natives/main.coffee
@@ -1283,7 +1283,7 @@
             else
               # time is in nanseconds, but we don't have that
               # type of precision
-              timeout = (new Date).getTime() + time / 1000 if time > 0
+              timeout = (new Date).getTime() + time / 1000000 if time > 0
             rs.park rs.curr_thread, timeout
         o 'unpark(Ljava/lang/Object;)V', (rs, _this, thread) ->
             rs.unpark thread


Example

Example

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -1,25 +1,25 @@
-# Framer.Extras.Hints.enable()
-#
-# sketch = Framer.Importer.load("imported/Mail@2x")
-#
-# nav = new NavComponent
-# nav.push(sketch.inbox)
-#
+Framer.Extras.Hints.enable()
+
+sketch = Framer.Importer.load("imported/Mail@2x")
+
+nav = new NavComponent
+nav.push(sketch.inbox)
+
 # On a hamburger tap, we show the menu
-# sketch.hamburger.onTap ->
-# 	nav.modalLeft(sketch.menu)
-#
+sketch.hamburger.onTap ->
+	nav.modalLeft(sketch.menu)
+
 # If we tap on a few rows, show the mail
-# for row in sketch.yesterday.children
-# 	row.onTap -> nav.push(sketch.mail)
-#
+for row in sketch.yesterday.children
+	row.onTap -> nav.push(sketch.mail)
+
 # If we tap on the mail, we go back again
-# sketch.mail.onTap -> nav.back()
+sketch.mail.onTap -> nav.back()

 # Set up menu bar (not so interesting)
-# sketch.status.parent = null
-# sketch.status.point = 0
-# sketch.status.bringToFront()
+sketch.status.parent = null
+sketch.status.point = 0
+sketch.status.bringToFront()

 # sketch.menu.bringToFront()
@@ -44,35 +44,35 @@
 #
 # nav = new NavComponent
 # nav.push(a)
+
+# a.onTap -> nav.modalBottom(c)
+
+# a.onTap -> nav.push(b)
+# b.onTap -> nav.back()
+
+# scaleTransition = (nav, layerA, layerB, background) ->
+# 	transition =
+# 		layerA:
+# 			show: {x: 0, y: 0, scale: 1.0, opacity:1}
+# 			hide: {x: 0, y: 0, scale: 0.5, opacity:0}
+# 			options: {curve: "spring(300, 35, 0)"}
+# 		layerB:
+# 			show: {x: 0, y: 0, scale: 1.0, opacity:1}
+# 			hide: {x: 0, y: 0, scale: 0.5, opacity:0}
+# 			options: {curve: "spring(300, 35, 0)"}
 #
-# # a.onTap -> nav.modalBottom(c)
 #
-# # a.onTap -> nav.push(b)
-# # b.onTap -> nav.back()
-
-scaleTransition = (nav, layerA, layerB, background) ->
-	transition =
-		layerA:
-			show: {x: 0, y: 0, scale: 1.0, opacity:1}
-			hide: {x: 0, y: 0, scale: 0.5, opacity:0}
-			options: {curve: "spring(300, 35, 0)"}
-		layerB:
-			show: {x: 0, y: 0, scale: 1.0, opacity:1}
-			hide: {x: 0, y: 0, scale: 0.5, opacity:0}
-			options: {curve: "spring(300, 35, 0)"}
-
-
-
-cardA = new Layer
-	size: Screen.size
-	backgroundColor: "red"
-
-cardB = new Layer
-	size: Screen.size
-	backgroundColor: "blue"
-
-nav = new NavComponent()
-nav.push(cardA)
-
-cardA.onTap -> nav.push(cardB, true, true, scaleTransition)
-cardB.onTap -> nav.back()+#
+# cardA = new Layer
+# 	size: Screen.size
+# 	backgroundColor: "red"
+#
+# cardB = new Layer
+# 	size: Screen.size
+# 	backgroundColor: "blue"
+#
+# nav = new NavComponent()
+# nav.push(cardA)
+#
+# cardA.onTap -> nav.push(cardB, true, true, scaleTransition)
+# cardB.onTap -> nav.back()


Refactor zoom config

Refactor zoom config

--- orgviz/static/timeline.coffee
+++ orgviz/static/timeline.coffee
@@ -3,8 +3,26 @@
 # data_source: string
 #     URL to load data from
 setupTimeline = (data_source) ->
+  zoomSteps = new Array(
+    {pixelsPerInterval: 280,  unit: Timeline.DateTime.HOUR},
+    {pixelsPerInterval: 140,  unit: Timeline.DateTime.HOUR},
+    {pixelsPerInterval:  70,  unit: Timeline.DateTime.HOUR},
+    {pixelsPerInterval:  35,  unit: Timeline.DateTime.HOUR},
+    {pixelsPerInterval: 400,  unit: Timeline.DateTime.DAY},
+    {pixelsPerInterval: 200,  unit: Timeline.DateTime.DAY},
+    {pixelsPerInterval: 100,  unit: Timeline.DateTime.DAY},
+    {pixelsPerInterval:  50,  unit: Timeline.DateTime.DAY},
+    {pixelsPerInterval: 400,  unit: Timeline.DateTime.MONTH},
+    {pixelsPerInterval: 200,  unit: Timeline.DateTime.MONTH},
+    {pixelsPerInterval: 100,  unit: Timeline.DateTime.MONTH},
+    {pixelsPerInterval:  50,  unit: Timeline.DateTime.MONTH},
+    {pixelsPerInterval: 400,  unit: Timeline.DateTime.YEAR},
+    {pixelsPerInterval: 200,  unit: Timeline.DateTime.YEAR},
+    {pixelsPerInterval: 100,  unit: Timeline.DateTime.YEAR}
+  )
+  zoomIndex = 10
+
   Timeline.loadJSON data_source, (timeline_data) ->
-    tl_el = document.getElementById("tl")
     eventSource1 = new Timeline.DefaultEventSource()

     theme1 = Timeline.ClassicTheme.create()
@@ -14,34 +32,17 @@
     bandInfos = [
       Timeline.createBandInfo(
         width:          45,
-        intervalUnit:   Timeline.DateTime.MONTH,
-        intervalPixels: 100,
+        intervalUnit:   zoomSteps[zoomIndex].unit,
+        intervalPixels: zoomSteps[zoomIndex].pixelsPerInterval,
+        zoomIndex:      zoomIndex,
+        zoomSteps:      zoomSteps,
         eventSource:    eventSource1,
-
-        zoomIndex:      10,
-        zoomSteps:      new Array(
-          {pixelsPerInterval: 280,  unit: Timeline.DateTime.HOUR},
-          {pixelsPerInterval: 140,  unit: Timeline.DateTime.HOUR},
-          {pixelsPerInterval:  70,  unit: Timeline.DateTime.HOUR},
-          {pixelsPerInterval:  35,  unit: Timeline.DateTime.HOUR},
-          {pixelsPerInterval: 400,  unit: Timeline.DateTime.DAY},
-          {pixelsPerInterval: 200,  unit: Timeline.DateTime.DAY},
-          {pixelsPerInterval: 100,  unit: Timeline.DateTime.DAY},
-          {pixelsPerInterval:  50,  unit: Timeline.DateTime.DAY},
-          {pixelsPerInterval: 400,  unit: Timeline.DateTime.MONTH},
-          {pixelsPerInterval: 200,  unit: Timeline.DateTime.MONTH},
-          {pixelsPerInterval: 100,  unit: Timeline.DateTime.MONTH},
-          {pixelsPerInterval:  50,  unit: Timeline.DateTime.MONTH},
-          {pixelsPerInterval: 400,  unit: Timeline.DateTime.YEAR},
-          {pixelsPerInterval: 200,  unit: Timeline.DateTime.YEAR},
-          {pixelsPerInterval: 100,  unit: Timeline.DateTime.YEAR}
-        ),
-
         theme:          theme1,
         layout:         'original',
       )
     ]

+    tl_el = document.getElementById("tl")
     tl = Timeline.create(tl_el, bandInfos, Timeline.HORIZONTAL);

     url = '.'


social: return most followed tags for active tags

--- workers/social/lib/social/models/activeitems.coffee
+++ workers/social/lib/social/models/activeitems.coffee
@@ -65,7 +65,7 @@
     group = options.group or "koding"
     {select: selector} = nameMapping.topic
     selector.group = group
-    JTag.some selector, {limit:10, "counts.followers":-1}, callback
+    JTag.some selector, {limit:10, sort:"counts.followers":-1}, callback

   @_fetchUsers = (options={}, callback)->
     {client}   = options


minor: codestyling

minor: codestyling

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -596,7 +596,8 @@
         dash queue, callback

   # isInAllowedDomain checks if given email's domain is in allowed domains
-  isInAllowedDomain:(email, callback)->
+  isInAllowedDomain: (email, callback) ->
+
     return no  unless @allowedDomains?.length > 0

     # even if incoming email doesnt have a @ in it, whole string will be taken


Groups: removed group deletion with admin rights.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1254,8 +1254,7 @@
     @fetchOwner (err, owner)=>
       return callback err if err
       unless owner.getId().equals client.connection.delegate.getId()
-        unless client.connection.delegate.can "reset groups"
-          return callback new KodingError 'You must be the owner to perform this action!'
+        return callback new KodingError 'You must be the owner to perform this action!'

       removeHelper = (model, err, callback, queue)->
         return callback err if err


Valid message, options, and detail

Valid message, options, and detail

--- src/notification.coffee
+++ src/notification.coffee
@@ -9,6 +9,17 @@
     @dismissed = true
     @dismissed = false if @isDismissable()
     @displayed = false
+    @validate()
+
+  validate: ->
+    if typeof @message isnt 'string'
+      throw new Error("Notification must be created with string message: #{@message}")
+
+    if typeof @options isnt 'object'
+      throw new Error("Notification must be created with an options object: #{@options}")
+
+    if @options?.detail? and typeof @options.details isnt 'string'
+      throw new Error("Notification must be created with string detail: #{@options.detail}")

   onDidDismiss: (callback) ->
     @emitter.on 'did-dismiss', callback


Fix relative paths

Fix relative paths

--- generatePictures.coffee
+++ generatePictures.coffee
@@ -1,6 +1,7 @@
 requirejs = require('requirejs')

 requirejs.config({
+  baseUrl: './src'
   nodeRequire: require,
   shim: {
     'socketio': {
@@ -16,7 +17,7 @@
     #jquery: 'lib/jquery'

     socketio: '/socket.io/socket.io',
-    underscore: 'lib/underscore'
+    underscore: '../lib/underscore'
   }
 })


Attach active pane item view before showing/hiding

Fixes #4112

--- src/pane-element.coffee
+++ src/pane-element.coffee
@@ -94,15 +94,15 @@
     hasFocus = @hasFocus()
     itemView = @model.getView(item)

+    unless @itemViews.contains(itemView)
+      @itemViews.appendChild(itemView)
+      callAttachHooks(itemView)
+
     for child in @itemViews.children
       if child is itemView
         @showItemView(child) if @attached
       else
         @hideItemView(child)
-
-    unless @itemViews.contains(itemView)
-      @itemViews.appendChild(itemView)
-      callAttachHooks(itemView)

     itemView.focus() if hasFocus


changed disconnect from error to warning

--- src/pantry-redis.coffee
+++ src/pantry-redis.coffee
@@ -11,7 +11,7 @@
 		@client = redis.createClient(port, host)

 		@client.on 'end', =>
-			@log.error "Disconnected from Host: #{host}, Port: #{port}"
+			@log.warning "Disconnected from Host: #{host}, Port: #{port}"
 			@up = false

 		@client.on 'error', (err) =>


Add y-axis info for lower LOD curve plot; add point at probe position

--- cistrans/cistrans.coffee
+++ cistrans/cistrans.coffee
@@ -41,7 +41,7 @@
   # colors definitions
   lightGray = d3.rgb(230, 230, 230)
   darkGray = d3.rgb(200, 200, 200)
-  pink = "#E9CFEC"
+  pink = "hotpink" # "#E9CFEC"
   purple = "#8C4374"
   # bgcolor = "black"
   labelcolor = "black"   # "white"
@@ -232,6 +232,38 @@
     lodcurve_yScale = d3.scale.linear()
                         .domain([0, maxlod*1.05])
                         .range([bottom[1], top[1]])
+
+    # y-axis
+    yaxis = svg.append("g").attr("class", "probe_data").attr("id", "loweryaxis")
+    ticks = lodcurve_yScale.ticks(6)
+    yaxis.selectAll("empty")
+         .data(ticks)
+         .enter()
+         .append("line")
+         .attr("y1", (d) -> lodcurve_yScale(d))
+         .attr("y2", (d) -> lodcurve_yScale(d))
+         .attr("x1", left[1])
+         .attr("x2", right[1])
+         .attr("stroke", "white")
+         .attr("stroke-width", "1")
+    yaxis.selectAll("empty")
+         .data(ticks)
+         .enter()
+         .append("text")
+         .text((d) ->
+            return if maxlod > 10 then d3.format(".0f")(d) else d3.format(".1f")(d))
+         .attr("y", (d) -> lodcurve_yScale(d))
+         .attr("x", left[1] - pad.left*0.1)
+         .style("text-anchor", "end")
+    yaxis.append("line")
+         .attr("y1", lodcurve_yScale(5))
+         .attr("y2", lodcurve_yScale(5))
+         .attr("x1", left[1])
+         .attr("x2", right[1])
+         .attr("stroke", purple)
+         .attr("stroke-width", "1")
+         .attr("stroke-dasharray", "2,2")
+
     # lod curves by chr
     lodcurve = (c) ->
         d3.svg.line()
@@ -245,6 +277,18 @@
             .attr("class", "thickline")
             .attr("stroke", "darkslateblue")
             .style("pointer-events", "none")
+
+    # point at probe
+    svg.append("circle")
+       .attr("class", "probe_data")
+       .attr("id", "probe_circle")
+       .attr("cx", chrLowXScale[data.probes[probe_data.probe].chr](data.probes[probe_data.probe].pos_cM))
+       .attr("cy", top[1] + pad.top*0.2)
+       .attr("r", bigRad)
+       .attr("fill", pink)
+       .attr("stroke", "darkslateblue")
+       .attr("stroke-width", 1)
+       .attr("opacity", 1)

     # title
     titletext = probe_data.probe
@@ -269,6 +313,15 @@
          .attr("fill", maincolor)
          .style("font-size", "18px")

+    # black border
+    svg.append("rect").attr("class", "probe_data")
+       .attr("x", left[1])
+       .attr("y", top[1])
+       .attr("height", h[1])
+       .attr("width", w[1])
+       .attr("class", "outerBox")
+
+
   # circles at eQTL peaks
   peaks = svg.append("g").attr("id", "peaks")
              .selectAll("empty")
@@ -283,7 +336,7 @@
              .attr("opacity", (d) -> Zscale(d.lod))
              .on "mouseover", (d) ->
                  d3.select(this).attr("r", bigRad)
-                                .attr("fill", "hotpink")
+                                .attr("fill", pink)
                                 .attr("stroke", "darkslateblue")
                                 .attr("stroke-width", 1)
                                 .attr("opacity", 1)


Check focus on every notification removal

--- lib/notification-element.coffee
+++ lib/notification-element.coffee
@@ -184,7 +184,6 @@
   removeNotification: ->
     @classList.add('remove')
     @removeNotificationAfterTimeout()
-    atom.workspace.getActivePane().activate() if this is document.activeElement

   handleRemoveNotificationClick: ->
     @model.dismiss()
@@ -212,6 +211,8 @@
     , @visibilityDuration

   removeNotificationAfterTimeout: ->
+    atom.workspace.getActivePane().activate() if this is document.activeElement
+
     setTimeout =>
       @remove()
     , @animationDuration # keep in sync with CSS animation


Add datastore

Add datastore

--- job-registry.cson
+++ job-registry.cson
@@ -126,6 +126,7 @@
         204: 'enqueue-jobs-for-subscriptions-message-received'
     'enqueue-jobs-for-subscriptions-message-received':
       task: 'meshblu-core-task-enqueue-jobs-for-subscriptions-message-received'
+      datastoreCollection: 'subscriptions'

 GetAuthorizedSubscriptionTypes:
   start: 'authenticate'


Take full provider object for initializing the resources

--- src/steroids/Providers.coffee
+++ src/steroids/Providers.coffee
@@ -174,7 +174,11 @@
       )
     deferred.promise

-  initResourceProvider: (provider_name) =>
+  initResourceProvider: (provider) =>
+
+    # TODO: Refactor this method
+    provider_name = provider.name
+
     deferred = q.defer()

     console.log "Provisioning a SandboxDB database for your app..."


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_camera.coffee
+++ src/engine_camera.coffee
@@ -13,7 +13,7 @@

       options = Object.extend(defaults, currentOptions)

-      Matrix.translation(App.width / 2 - options.x + options.width, App.height / 2 - options.y - options.height)
+      Matrix.translation(App.width / 2 - options.x, App.height / 2 - options.y)

   self.bind "afterUpdate", ->
     if currentObject


Add damage step to bridge

Add damage step to bridge

--- src/scenery/City.coffee
+++ src/scenery/City.coffee
@@ -686,6 +686,18 @@
     @addBackground(-20,  -60, p1, 1.2)
     @addBackground(834, -60, p2, 1.2)

+    @bind 'BridgeDamage', once: yes, (level) =>
+      d0 = Crafty('FrontDeck').get(0).addComponent('TweenPromise', 'Delta2D').sprite(16, 32)
+      d1 = Crafty('MainDeck').get(0).addComponent('TweenPromise', 'Delta2D').sprite(16, 32)
+      d2 = Crafty('BackDeck').get(0).addComponent('TweenPromise', 'Delta2D').sprite(16, 32)
+      d0.half.sprite(16, 32)
+      d1.half.sprite(16, 32)
+      d2.half.sprite(16, 32)
+
+      p1 = Crafty('TiltPillarLeft').get(0).addComponent('TweenPromise', 'Delta2D').sprite(42, 29)
+      p2 = Crafty('TiltPillarRight').get(0).addComponent('TweenPromise', 'Delta2D').sprite(42, 29)
+      dh = Crafty('BridgeCeiling').get(0).addComponent('TweenPromise', 'Delta2D')
+
     @bind 'BridgeCollapse', once: yes, (level) =>
       d0 = Crafty('FrontDeck').get(0).addComponent('TweenPromise', 'Delta2D').sprite(16, 32)
       d1 = Crafty('MainDeck').get(0).addComponent('TweenPromise', 'Delta2D').sprite(16, 32)


Finish design for alias/many/one

Finish design for alias/many/one

--- src/schema/index.coffee
+++ src/schema/index.coffee
@@ -33,6 +33,22 @@
 # alias will be there. If you're not using aliases and/or virtual objects, then no harm done, all
 # aliases will equal names, and you're good to go.
 # MUST: remove the ability to change alias / name once schema object is instantiated
+# New rules will be:
+# 0. No name changing ever!
+# 1. Name will remain the database name, as we have now
+# 2. API into schema will be based on DB NAMES, so that people who don't want to worry about
+# aliasing don't even have to think about it. All operations using the schema classes will be keyed
+# off NAME.
+# 3. Tables will not have an 'alias' anymore. It is so confusing anyway, since it sounds like a SQL
+# alias, as in a SELECT statement. Tables will have two properties, 'many', and 'one', to name the
+# collections and the active record object. Many will give name to the table gateway and the FROM
+# name in SQL queries. One will give name to the active record. Both 'many' and 'one' will default
+# to name. Many and one will also be used by ActiveRecord when naming properties for relations.
+# 4. For columns, 'alias' will be replaced by 'property'
+# 5. No collections by many or one at first. Only by name, which is immutable (see 0), so no need
+# for property acrobatics
+# 6. Once the DB loads the schema, there'll be collections by many/one names. After you load a
+# schema into a DB, however, you can no longer mess with it, so we'll be ok.
 class AliasedObject extends DbObject
     constructor: (schema) ->
         unless schema.name? || schema.alias?


Remove stray console.log

Remove stray console.log

--- src/adapters/generic.coffee
+++ src/adapters/generic.coffee
@@ -37,7 +37,6 @@
       if _(@disabledUsers).contains user.id
         @robot.logger.info "JIRA Notification surpressed for #{user.name}"
       else
-        console.log "sending message", user.name, message
         @send message: room: user.name, message

 module.exports = GenericAdapter


Use "closed" event instead of "destroyed".

The meaning of "destroyed" event has changed since atom-shell v0.12.0,
fixes #1910.

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -325,7 +325,7 @@
     if pidToKillWhenClosed?
       @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow

-    openedWindow.browserWindow.on 'destroyed', =>
+    openedWindow.browserWindow.on 'closed', =>
       @killProcessForWindow(openedWindow)

   # Kill all processes associated with opened windows.


ListView: fix reverse order bug when list contains two items

--- src/components/list/listview.coffee
+++ src/components/list/listview.coffee
@@ -99,8 +99,8 @@
     isFirstIndex = index is 0
     isInBetween  = 0 < index < @items.length - 1

-    if isLastIndex       then @items.push item
-    else if isFirstIndex then @items.unshift item
+    if isFirstIndex      then @items.unshift item
+    else if isLastIndex  then @items.push item
     else @items.splice index, 0, item

     @emit 'ItemWasAdded', item, index


Ace: route redirects to IDE

Ace: route redirects to IDE

--- client/Ace/routes.coffee
+++ client/Ace/routes.coffee
@@ -1,5 +1,4 @@
 do ->
   KD.registerRoutes 'Ace',
-    '/:name?/Ace' : ({params:{name}, query})->
-      router = KD.getSingleton 'router'
-      router.openSection 'Ace', name, query+    '/:name?/Ace' : (routeInfo, state, path) ->
+      KD.singletons.router.handleRoute path.replace(/\/Ace/, '/IDE')


Fixed far planet mesh render order

Fixed far planet mesh render order

--- source/Planetfield.coffee
+++ source/Planetfield.coffee
@@ -138,10 +138,11 @@
 				vec3.scale(lightPos, lightPos, 0.25)
 				vec3.add(lightCenter, lightCenter, lightPos)

-		# build a list of planets to render in reverse order, since we need to render farthest to nearest
+		# sort the list of planets to render in reverse order, since we need to render farthest to nearest
 		# because the depth buffer is not enabled yet (we're still rendering on massive scales, potentially)
+		@meshPlanets.sort( ([ax,ay,az,aw,ak], [cx,cy,cz,cw,ck]) -> (cx*cx + cy*cy + cz*cz) - (ax*ax + ay*ay + az*az) )
 		[localPos, globalPos, lightVec] = [vec3.create(), vec3.create(), vec3.create()]
-		for i in [@meshPlanets.length-1 .. 0]
+		for i in [0 .. @meshPlanets.length-1]
 			[x, y, z, w, alpha] = @meshPlanets[i]
 			localPos = vec3.fromValues(x, y, z)
 			vec3.add(globalPos, localPos, camera.position)


Fix posts feed post type check on post create

--- lib/assets/javascripts/views/posts_feed.js.coffee
+++ lib/assets/javascripts/views/posts_feed.js.coffee
@@ -29,7 +29,7 @@
     TentStatus.Models.StatusPost.on 'create:success', (post, xhr) =>
       return unless @shouldAddPostToFeed(post)
       collection = @postsCollection()
-      return unless @shouldAddPostTypeToFeed(post.get('type'), collection)
+      return unless @shouldAddPostTypeToFeed(post.get('type'), collection.options.params.types)
       collection.unshift(post)
       @prependRender([post])


added normalizeArgs method, put method defaults into single object

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -326,6 +326,21 @@
       -90
     else
       angle
+
+
+  _normalizeArgs: (method, args) ->
+    angle = @lastAngle = @_normalizeAngle args[0]
+    anchor = @_getLonghandAnchor args[1]
+    options = extendObj args[2], @_methodDefaults[method]
+    if anchor isnt @lastAnchor
+      @reset =>
+        @_showStage anchor
+        setTimeout =>
+          @[method].apply @, args
+        , 0
+      false
+    else
+      [angle, anchor, options]

   _getXy: (i, anchor) ->
@@ -439,6 +454,17 @@
     @panelHeight * (@hPanels - 1) - @hPanels + 1

+  _methodDefaults:
+    accordion:
+      sticky: false
+      stairs: false
+      fracture: false
+      twist: false
+    curl:
+      twist: false
+    ramp: {}
+
+
   reset: (callback) ->
     for panel, i in @panels[@lastAnchor]
       panel.style[css.transform] = @_transform @_getXy i, @lastAnchor
@@ -448,26 +474,10 @@
     @_callback callback: callback

-  _accordionDefaults:
-    anchor: 'left'
-    sticky: false
-    stairs: false
-    fracture: false
-    twist: false
-
-
-  accordion: (angle, options) ->
-    options = extendObj options, @_accordionDefaults
-    {anchor} = options
-
-    if anchor isnt @lastAnchor
-      return @reset =>
-        @_showStage anchor
-        setTimeout =>
-          @accordion angle, options
-        , 0
-
-    @lastAngle = angle = @_normalizeAngle angle
+  accordion: (angle, anchor, options) ->
+    normalized = @_normalizeArgs 'accordion', arguments
+    return if !normalized
+    [angle, anchor, options] = normalized

     for panel, i in @panels[anchor]

@@ -531,25 +541,13 @@
     @accordion angle / 10, options

-  _curlDefaults:
-    anchor: 'left'
-    twist: false
-
-
-  curl: (angle, options = {}) ->
-    options = extendObj options, @_curlDefaults
-    {anchor} = options
-
-    if anchor isnt @lastAnchor
-      return @reset =>
-        @_showStage anchor
-        setTimeout =>
-          @curl angle, options
-        , 0
-
-    @lastAngle = angle = @_normalizeAngle angle
+
+  curl: (angle, anchor, options = {}) ->
+    normalized = @_normalizeArgs 'curl', arguments
+    return if !normalized
+    [angle, anchor, options] = normalized
+
     angle /=  @_getPanelType anchor
-
     rotation = @_getRotation anchor, angle

     for panel, i in @panels[anchor]
@@ -561,13 +559,17 @@
     @_callback options

-  _rampDefaults:
-    anchor: 'left'
-
-
-  ramp: (angle, options) ->
-    options = extendObj options, @_rampDefaults
-    {anchor} = options
+  ramp: (angle, anchor, options) ->
+    normalized = @_normalizeArgs 'ramp', arguments
+    return if !normalized
+    [angle, anchor, options] = normalized
+
+    rotation = @_getRotation anchor, angle
+    @panels[anchor][1].style[css.transform] = @_transform @_getXy(1, anchor), rotation
+
+    @_callback options
+
+

     if anchor isnt @lastAnchor
       return @reset =>


stash axis name in touchStart

stash axis name in touchStart

--- oridomi.coffee
+++ oridomi.coffee
@@ -760,15 +760,15 @@
     @_touchAxis = if @lastOp.anchor in anchorListV then 'x' else 'y'
     # Set a reference to the last folded angle to accurately derive deltas.
     @["_#{ @_touchAxis }Last"] = @lastOp.angle
-
+    axis1 = "_#{ @_touchAxis }1"
     # Determine the starting tap's coordinate for touch and mouse events.
     if e.type is 'mousedown'
-      @["_#{ @_touchAxis }1"] = e["page#{ @_touchAxis.toUpperCase() }"]
-    else
-      @["_#{ @_touchAxis }1"] = e.targetTouches[0]["page#{ @_touchAxis.toUpperCase() }"]
+      @[axis1] = e["page#{ @_touchAxis.toUpperCase() }"]
+    else
+      @[axis1] = e.targetTouches[0]["page#{ @_touchAxis.toUpperCase() }"]

     # Return that value to an external listener.
-    @settings.touchStartCallback @["_#{ @_touchAxis }1"]
+    @settings.touchStartCallback @[axis1]

   # Called on touch/mouse movement.


separated transition callback handler from _callback

--- oridomi.coffee
+++ oridomi.coffee
@@ -528,19 +528,25 @@

   # `_callback` normalizes callback handling for all public methods.
-  _callback: (options) ->
-    if typeof options.callback is 'function'
-      # Create a local callback for the animation's end.
-      onTransitionEnd = (e) =>
-        # Remove the event listener immediately to prevent bubbling.
-        e.currentTarget.removeEventListener css.transitionEnd, onTransitionEnd, false
-        # Invoke the callback.
-        options.callback()
-
-      # If there was no transformation (0 degrees) invoke the callback immediately.
-      if @lastAngle is 0
-        options.callback()
-      # Otherwise, attach an event listener to be called on the transition's end.
+  _callback: (operation) ->
+    # If there was no transformation, invoke the callback immediately.
+    if @_touchStarted or @_isIdenticalOperation operation
+      @_conclude operation.options.callback
+    # Otherwise, attach an event listener to be called on the transition's end.
+    else
+      @panels[@lastAnchor][0].addEventListener css.transitionEnd, @_onTransitionEnd, false
+
+    @lastOp = operation
+
+
+  # Handler called when a CSS transition ends.
+  _onTransitionEnd: (e) =>
+    # Remove the event listener immediately to prevent bubbling.
+    e.currentTarget.removeEventListener css.transitionEnd, @_onTransitionEnd, false
+    # Initialize transition teardown process.
+    @_conclude @lastOp.options.callback
+
+
       else
         @panels[@lastAnchor][0].addEventListener css.transitionEnd, onTransitionEnd, false


A slight modification to EditSession

A slight modification to EditSession

--- src/app/edit-session.coffee
+++ src/app/edit-session.coffee
@@ -1122,7 +1122,7 @@
   # Returns an {Array} of {Cursor}s.
   getCursors: -> new Array(@cursors...)

-  # Public: Retrieves a single cursor
+  # Public: Retrieves the most recently added cursor.
   #
   # Returns a {Cursor}.
   getCursor: ->


Change split-view bindings to avoid confusion w/ tab-switching

Now you can use the vim-style `ctrl-w *` bindings, or use `ctrl-|` and
`ctrl--`, which resemble horizontal and vertical splits as characters.

--- src/app/keymaps/editor.cson
+++ src/app/keymaps/editor.cson
@@ -14,10 +14,10 @@
   'ctrl-{': 'editor:fold-all'
   'ctrl-}': 'editor:unfold-all'
   'alt-meta-ctrl-f': 'editor:fold-selection'
-  'alt-meta-left': 'editor:split-left'
-  'alt-meta-right': 'editor:split-right'
-  'alt-meta-up': 'editor:split-up'
-  'alt-meta-down': 'editor:split-down'
+  'ctrl-|': 'editor:split-right'
+  'ctrl-w v': 'editor:split-right'
+  'ctrl--': 'editor:split-down'
+  'ctrl-w s': 'editor:split-down'
   'shift-tab': 'editor:outdent-selected-rows'
   'meta-[': 'editor:outdent-selected-rows'
   'meta-]': 'editor:indent-selected-rows'


Fix copy/paste error.

Fix copy/paste error.

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -836,7 +836,7 @@
     name: req.body.name
     tag_or_untag: "tag"

-  body = buildIntercomRequestBody 'users', messageObject
+  body = buildIntercomRequestBody 'tags', messageObject
   request.post body, intercomResponseHandler(resp, 'sendIntercomTag')

 app.all '*', ensureAuthenticated


add nodetime request logging, see if we can organise them nicely into routes

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -4,6 +4,7 @@
 existsSync = fs.existsSync || path.existsSync
 crypto = require 'crypto'
 child_process = require 'child_process'
+util = require 'util'

 _ = require 'underscore'
 express = require 'express'
@@ -17,6 +18,7 @@
 eco = require 'eco'
 checkIdent = require 'ident-express'
 request = require 'request'
+nodetime = require 'nodetime'

 {User} = require 'model/user'
 {Dataset} = require 'model/dataset'
@@ -37,6 +39,23 @@
 # Doesn't seem to do much.
 mongoose.connection.on 'error', (err) ->
   console.warn "MONGOOSE CONNECTION ERROR #{err}"
+
+if process.env.NODETIME_KEY
+  nodetime.profile
+    accountKey: process.env.NODETIME_KEY
+    appName: process.env.CU_NODETIME_APP
+
+# TODO: move into npm module
+nodetimeLog = (req, res, next) ->
+  matched = _.find app.routes[req.method.toLowerCase()], (route) ->
+    route.regexp.test req.url
+  if matched.path isnt '*'
+    res.nodetimePromise = nodetime.time 'Custard request ', matched.path, req.url
+    oldSend = res.send
+    res.send = (args... ) ->
+      res.nodetimePromise.end()
+      oldSend.apply res, args
+  return next()

 assets.jsCompilers.eco =
   match: /\.eco$/
@@ -141,6 +160,8 @@
   if not process.env.NODE_ENV
     # Set the public folder as static assets
     app.use express.static(process.cwd() + '/shared')
+  if process.env.NODETIME_KEY
+    app.use nodetimeLog

 passport.use 'local', new LocalStrategy(verify)

@@ -587,8 +608,3 @@
     console.error "Could not close connections in time, forcefully shutting down"
     process.exit 1
   , 30*1000
-
-if process.env.NODETIME_KEY
-  require('nodetime').profile
-    accountKey: process.env.NODETIME_KEY
-    appName: process.env.CU_NODETIME_APP


Bugfix: allow focusing on input.

Bugfix: allow focusing on input.

--- src/views/search-input.coffee
+++ src/views/search-input.coffee
@@ -45,7 +45,7 @@
             # will only cause the intended typeahead selection.
             'click .tt-suggestion': (e) ->
                 e.stopPropagation()
-            #'click input': (e) -> e.stopPropagation()
+            'click input': (e) -> e.stopPropagation()
             'click .typeahead-suggestion.fulltext': 'executeQuery'
             'click .action-button.search-button': 'search'
             'submit .input-container': 'search'


add comments for the memory manager

add comments for the memory manager

--- server/lib/memory.coffee
+++ server/lib/memory.coffee
@@ -5,14 +5,16 @@

 freeMemCmd = "free | grep cache: | cut -d':' -f2 | sed -e 's/^ *[0-9]* *//'"

+
+# Utilities to get memory consumption and disk usage.
 class exports.MemoryManager

     constructor: ->
         @controllerClient = new ControllerClient
             token: @_getAuthController()

-
     # Get token from token file if in production mode.
+    # TODO: this method should be integrated to the controller client.
     _getAuthController: ->
         if process.env.NODE_ENV is 'production'
             try
@@ -27,6 +29,8 @@
             return ""

+    # Crappy method to extract result from a df command, should be rewritter
+    # with the one located in the controller.
     _extractDataFromDfResult: (resp) ->
         data = {}
         lines = resp.split('\n')
@@ -44,6 +48,8 @@
                 data.usedDiskSpace = usedSpace
         data

+    # Return memory information from a complex free command (remove useless
+    # information)
     getMemoryInfos: (callback) ->
         data = totalMem: os.totalmem() / (1024)
         exec freeMemCmd, (err, resp) ->
@@ -55,6 +61,10 @@
                 data.freeMem = line
                 callback null, data

+    # Try to get disk infos from the Cozy controller (it can access to the
+    # couch configuration file and guess on which fs it is located). If it
+    # fails it tries to make a 'df -h' itself and guess that couch is located
+    # in the / fs
     getDiskInfos: (callback) ->
         @controllerClient.client.get 'diskinfo', (err, res, body) ->
             if err or res.statusCode isnt 200
@@ -64,6 +74,7 @@
             else
                 callback null, body

+    # Return true if there is at least 60MB of memory free.
     isEnoughMemory: (callback) ->
         @getMemoryInfos (err, data) =>
             if err then callback err


Specify cwd instead of cd'ing

Specify cwd instead of cd'ing

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -326,7 +326,7 @@
   describe "API documentation", ->
     it "meets a minimum threshold for /app (with no errors)", ->
       docRunner = jasmine.createSpy("docRunner")
-      Exec "cd #{project.resolve('../..')} && rake docs:app:stats", docRunner
+      Exec "rake docs:app:stats", cwd: project.resolve('../..'), docRunner
       waitsFor ->
         docRunner.callCount > 0


Test that dualsync syncs both to localsync and onlineSync

--- spec/dualsync_spec.coffee
+++ spec/dualsync_spec.coffee
@@ -17,7 +17,7 @@
 spyOnLocalsync = (spec) ->
   coffee = require('coffee-script')
   runs ->
-    coffee.eval "window.oldLocalsync = window.localsync; window.localsync = jasmine.createSpy('localsync').andCallFake((method, model, options) -> options.success())", sandbox: window
+    coffee.eval "window.oldLocalsync = window.localsync; window.localsync = jasmine.createSpy('localsync').andCallFake((method, model, options) -> (options.success?()) unless options.ignoreCallbacks)", sandbox: window
   runs ->
     spec(window.localsync)
   runs ->
@@ -27,9 +27,56 @@
 describe 'delegating to localsync and onlineSync, and calling the model callbacks', ->
   describe 'dual tier storage', ->
     describe 'create', ->
+      it 'delegates to both localsync and onlinesync', ->
+        spyOnLocalsync ->
+          ready = false
+          runs ->
+            window.dualsync('create', model, success: (-> ready = true))
+          waitsFor (-> ready), "The success callback should have been called", 100
+          runs ->
+            expect(window.onlineSync).toHaveBeenCalled()
+            expect(window.onlineSync.calls[0].args[0]).toEqual 'create'
+            expect(window.localsync).toHaveBeenCalled()
+            expect(window.localsync.calls[0].args[0]).toEqual 'create'
+
     describe 'read', ->
+      it 'delegates to both localsync and onlinesync', ->
+        spyOnLocalsync ->
+          ready = false
+          runs ->
+            window.dualsync('read', model, success: (-> ready = true))
+          waitsFor (-> ready), "The success callback should have been called", 100
+          runs ->
+            expect(window.onlineSync).toHaveBeenCalled()
+            expect(_(window.onlineSync.calls).any((call) -> call.args[0] == 'read'))
+            expect(window.localsync).toHaveBeenCalled()
+            expect(_(window.localsync.calls).any((call) -> call.args[0] == 'read'))
+
     describe 'update', ->
+      it 'delegates to both localsync and onlinesync', ->
+        spyOnLocalsync ->
+          ready = false
+          runs ->
+            window.dualsync('update', model, success: (-> ready = true))
+          waitsFor (-> ready), "The success callback should have been called", 100
+          runs ->
+            expect(window.onlineSync).toHaveBeenCalled()
+            expect(_(window.onlineSync.calls).any((call) -> call.args[0] == 'update'))
+            expect(window.localsync).toHaveBeenCalled()
+            expect(_(window.localsync.calls).any((call) -> call.args[0] == 'update'))
+
     describe 'delete', ->
+      it 'delegates to both localsync and onlinesync', ->
+        spyOnLocalsync ->
+          ready = false
+          runs ->
+            window.dualsync('delete', model, success: (-> ready = true))
+          waitsFor (-> ready), "The success callback should have been called", 100
+          runs ->
+            expect(window.onlineSync).toHaveBeenCalled()
+            expect(_(window.onlineSync.calls).any((call) -> call.args[0] == 'delete'))
+            expect(window.localsync).toHaveBeenCalled()
+            expect(_(window.localsync.calls).any((call) -> call.args[0] == 'delete'))

   describe 'respects the remote only attribute on models', ->
     it 'delegates for remote models', ->


Provide the message as a .description

Provide the message as a .description

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -1009,7 +1009,7 @@
           if err.missingProjectPaths?
             missingProjectPaths.push(err.missingProjectPaths...)
           else
-            @notifications.addError "Unable to deserialize project", stack: err.stack
+            @notifications.addError "Unable to deserialize project", description: err.message, stack: err.stack
     else
       projectPromise = Promise.resolve()


Update spec

Update spec

--- spec/grammars-spec.coffee
+++ spec/grammars-spec.coffee
@@ -142,9 +142,13 @@
         expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe 'source.coffee'
         expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe 'source.ruby'

-      it "favors grammars with matching first-line-regexps even if custom file types match the file", ->
+      it "favors user-defined file types over grammars with matching first-line-regexps", ->
         atom.config.set('core.customFileTypes', 'source.ruby': ['bootstrap'])
-        expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe 'source.js'
+        expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe 'source.ruby'
+
+  describe "when there is a grammar with a first line pattern, the file type of the file is known, but from a different grammar", ->
+    it "favors file type over the matching pattern", ->
+      expect(atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName).toBe 'source.ruby'

   describe ".removeGrammar(grammar)", ->
     it "removes the grammar, so it won't be returned by selectGrammar", ->


fix(action-overflow): unbind events on destroy

--- modules/scripts/directives/action-overflow.coffee
+++ modules/scripts/directives/action-overflow.coffee
@@ -67,3 +67,8 @@
               open = false
               # prevent tap from happening on the icon again
               element.trigger 'touchcancel'
+
+          scope.$on '$destroy', ->
+            element.unbind 'tap'
+            $actions.unbind 'touchstart'
+            $$window.unbind 'touchstart'


Clear all warnings

Clear all warnings


:gift: dev mode command reloadPackages to reload vmp and vmp plugins

list of package must be stored as devReloadPackages setting variable.
Must directly set in init.coffee since no default config schema is
provided

--- lib/developer.coffee
+++ lib/developer.coffee
@@ -20,11 +20,33 @@
       'generate-introspection-report': => @generateIntrospectionReport()
       'report-commands-have-no-default-keymap': => @reportCommandsHaveNoDefaultKeymap()
       'toggle-dev-environment': => @toggleDevEnvironment()
+      'reload-packages': => @reloadPackages()

     subscriptions = new CompositeDisposable
     for name, fn of commands
       subscriptions.add @addCommand(name, fn)
     subscriptions
+
+  reloadPackages: ->
+    packages = settings.get('devReloadPackages') ? []
+    packages.push('vim-mode-plus')
+    for packName in packages
+      pack = atom.packages.getLoadedPackage(packName)
+
+      if pack?
+        console.log "deactivating #{packName}"
+        atom.packages.deactivatePackage(packName)
+        atom.packages.unloadPackage(packName)
+
+        packPath = pack.path
+        Object.keys(require.cache)
+          .filter (p) ->
+            p.indexOf(packPath + path.sep) is 0
+          .forEach (p) ->
+            delete require.cache[p]
+
+        atom.packages.loadPackage(packName)
+        atom.packages.activatePackage(packName)

   toggleDevEnvironment: ->
     return unless editor = atom.workspace.getActiveTextEditor()


Inspector: accept 'text/html' results

Inspector: accept 'text/html' results

--- lib/inspector.coffee
+++ lib/inspector.coffee
@@ -17,37 +17,14 @@
             @view?.close()
             return

+        @view ?= new MessagePanelView
+            title: 'Hydrogen Inspector'
+            closeMethod: 'destroy'
+
         [code, cursor_pos] = @getCodeToInspect()

         kernel.inspect code, cursor_pos, (result) =>
-            console.log 'Inspector: Result:', result
-            found = result.found
-            if found is true
-                onInspectResult = ({mimetype, el}) =>
-                    lines = el.innerHTML.split('\n')
-                    firstline = lines[0]
-                    lines.splice(0, 1)
-                    message = lines.join('\n')
-
-                    @view ?= new MessagePanelView
-                        title: 'Hydrogen Inspector'
-                        closeMethod: 'destroy'
-
-                    if @_lastInspectionResult is message and @view.panel?
-                        @view?.close()
-                    else
-                        @addInspectResult(firstline, message)
-                        @_lastInspectionResult = message
-
-
-                onError = (error) ->
-                    console.error 'Inspector: Rendering error:', error
-
-                transform(result.data).then onInspectResult, onError
-
-            else
-                atom.notifications.addInfo 'No introspection available!'
-                @view?.close()
+            @showInspectionResult result

     getCodeToInspect: ->
         if @editor.getSelectedText()
@@ -60,16 +37,67 @@
             cursor_pos = cursor.getBufferColumn()
         return [code, cursor_pos]

-    addInspectResult: (firstline, message) ->
-        @view.clear()
-        @view.attach()
-        @view.add new PlainMessageView
-            message: firstline
-            className: 'inspect-message'
-            raw: true
-        @view.add new PlainMessageView
-            message: message
-            className: 'inspect-message'
-            raw: true
+    showInspectionResult: (result) ->
+        console.log 'Inspector: Result:', result
+
+        unless result.found
+            atom.notifications.addInfo 'No introspection available!'
+            @view?.close()
+            return
+
+        onInspectResult = ({mimetype, el}) =>
+            if mimetype is 'text/plain'
+                lines = el.innerHTML.split('\n')
+                firstline = lines[0]
+                lines.splice(0, 1)
+                message = lines.join('\n')
+
+                if @_lastInspectionResult is message and @view.panel?
+                    @view?.close()
+                    return
+
+                @view.clear()
+                @view.attach()
+                @view.add new PlainMessageView
+                    message: firstline
+                    className: 'inspect-message'
+                    raw: true
+                @view.add new PlainMessageView
+                    message: message
+                    className: 'inspect-message'
+                    raw: true
+
+                @_lastInspectionResult = message
+                return
+
+            else if mimetype is 'text/html'
+                container = document.createElement('div')
+                container.appendChild(el)
+                message = container.innerHTML
+                if @_lastInspectionResult is message and @view.panel?
+                    @view?.close()
+                    return
+
+                @view.clear()
+                @view.attach()
+                @view.add new PlainMessageView
+                    message: message
+                    className: 'inspect-message'
+                    raw: true
+
+                @_lastInspectionResult = message
+                return
+
+            console.error 'Inspector: Rendering error:', mimetype, el
+            atom.notifications.addInfo 'Cannot render introspection result!'
+            @view?.close()
+            return
+
+        onError = (error) =>
+            console.error 'Inspector: Rendering error:', error
+            atom.notifications.addInfo 'Cannot render introspection result!'
+            @view?.close()
+
+        transform(result.data).then onInspectResult, onError

 transform = transformime.createTransform()


Fix PHP regex, for real this time.

Fix PHP regex, for real this time.

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -56,7 +56,7 @@
     singleLineComment: ['#']

   PHP:
-    nameMatchers:      ['.php\d?$', '.fbp']
+    nameMatchers:      [/\.php\d?$/, '.fbp']
     pygmentsLexer:     'php'
     singleLineComment: ['//']


add Sweden in fixtures ;)

add Sweden in fixtures ;)

--- tests/fixtures/countries.coffee
+++ tests/fixtures/countries.coffee
@@ -10,8 +10,8 @@

 country2 =
     _id: new ObjectID(),
-    countryCode: "CA",
-    countryName: "Canada",
+    countryCode: "SE",
+    countryName: "Sweden",
 countries.push country2

 module.exports.Country = countries


Assign process ivar from within spawn

Assign process ivar from within spawn

--- src/buffered-process.coffee
+++ src/buffered-process.coffee
@@ -69,9 +69,9 @@
       cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
       cmdOptions = _.clone(options)
       cmdOptions.windowsVerbatimArguments = true
-      @process = @spawn(@getCmdPath(), cmdArgs, cmdOptions)
-    else
-      @process = @spawn(command, args, options)
+      @spawn(@getCmdPath(), cmdArgs, cmdOptions)
+    else
+      @spawn(command, args, options)

     @killed = false
     @handleEvents(stdout, stderr, exit) if @process?
@@ -187,10 +187,9 @@

   spawn: (command, args, options) ->
     try
-      spawned = ChildProcess.spawn(command, args, options)
+      @process = ChildProcess.spawn(command, args, options)
     catch spawnError
       process.nextTick => @handleError(spawnError)
-    spawned

   handleEvents: (stdout, stderr, exit) ->
     stdoutClosed = true


Highlight correct line on break

Highlight correct line on break

Fixes @25

--- lib/php-debug.coffee
+++ lib/php-debug.coffee
@@ -113,10 +113,10 @@
       if @currentBreakDecoration
         @currentBreakDecoration.destroy()
       line = breakpoint.getLine()
-      range = [[line, 0], [line, 0]]
+      range = [[line-1, 0], [line-1, 0]]
       marker = editor.markBufferRange(range, {invalidate: 'surround'})
       @currentBreakDecoration = editor.decorateMarker(marker, {type: 'line', class: 'debug-break'})
-      editor.scrollToBufferPosition([line,0])
+      editor.scrollToBufferPosition([line-1,0])
     @GlobalContext.getCurrentDebugContext().syncCurrentContext()

   toggle: ->


fixed a offset of popup window

fixed a offset of popup window

--- src/coffee/ScoreView.coffee
+++ src/coffee/ScoreView.coffee
@@ -95,7 +95,10 @@
   ###
   showComment:( el )->
     height = el.clientHeight
-    offset = domUtils.offset el
+    offset =
+      left:el.offsetLeft + 5
+      top:el.offsetTop + 10
+
     domUtils.css @commentWindowView.el, {display:'block', left:"#{offset.left}px" , top:"#{offset.top + height}px"}

   ###


Bold Some Custom Consequences

Bold Some Custom Consequences

--- src/coffee/game/rest.coffee
+++ src/coffee/game/rest.coffee
@@ -23,19 +23,19 @@
           hunches++ if qualities.lookup('origin').value == origins.illusionist
           hunches++ for [0...qualities.lookup('inscribedRibbon').value]
           quality.value = hunches
-          "You now have #{hunches} Illusion Hunches."
+          "You now have **#{hunches}** Illusion Hunches."
         hallucinationHunch: (quality) ->
           hunches = 1
           hunches++ if qualities.lookup('origin').value == origins.hallucinist
           hunches++ for [0...qualities.lookup('sealboneTrinket').value]
           quality.value = hunches
-          "You now have #{hunches} Hallucination Hunches."
+          "You now have **#{hunches}** Hallucination Hunches."
         hypnotismHunch: (quality) ->
           hunches = 1
           hunches++ if qualities.lookup('origin').value == origins.hypnotist
           hunches++ for [0...qualities.lookup('veinyCarnelian').value]
           quality.value = hunches
-          "You now have #{hunches} Hypnotism Hunches."
+          "You now have **#{hunches}** Hypnotism Hunches."
         bother: (quality) ->
           if quality.value
             quality.value = false


If graph has exported ports, only export those

--- src/components/Graph.coffee
+++ src/components/Graph.coffee
@@ -98,6 +98,10 @@
       @network.graph.checkTransactionEnd()
       return exported.public

+    # Component has exported ports and this isn't one of them
+    if Object.keys(@network.graph.inports).length > 0
+      return false
+
     return false if port.isAttached()
     return (nodeName+'.'+portName).toLowerCase()

@@ -115,6 +119,10 @@
       @network.graph.addOutport exported.public, exported.process, exported.port, exported.metadata
       @network.graph.checkTransactionEnd()
       return exported.public
+
+    # Component has exported ports and this isn't one of them
+    if Object.keys(@network.graph.outports).length > 0
+      return false

     return false if port.isAttached()
     return (nodeName+'.'+portName).toLowerCase()


Fix command performance

Fix command performance

--- src/config/inspector.coffee
+++ src/config/inspector.coffee
@@ -94,6 +94,7 @@
   time: 0
   step: (world, args) -> world.update('time', 1)

+  # TODO: Make this faster on the iPad 1
   inspect: (world, args) ->
     current = set_current(world, args)
     programs = current.get('programs')
@@ -106,7 +107,7 @@
     my.assert program, "No program for #{name}"
     program.add_child command
     console.log "command #{name} -> ", program._child_array()
-
+
   i: (world) -> world.get('split')
   width: (world) -> world.get('device').width - world.get('size')
   height: (world) -> world.get('size') + my.control.spacing


:lipstick:

:lipstick:

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -266,7 +266,7 @@
       if entry.getPath() is entryPath
         bestMatchEntry = entry
         break
-      else if entry.getPath().length > bestMatchEntry.getPath().length and entry.directory?.contains?(entryPath)
+      if entry.getPath().length > bestMatchEntry.getPath().length and entry.directory?.contains(entryPath)
         bestMatchEntry = entry

     bestMatchEntry


Tidy up whitespace

Tidy up whitespace

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -252,7 +252,6 @@

     if @submode == 'linewise'
       @editor.selectLine()
-

   # Private: Resets the command mode back to it's initial state.
   #


:lipstick: modify format, layout.

:lipstick: modify format, layout.

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -146,11 +146,12 @@
   # Initialize all of vim-mode' commands.
   init: ->
     @registerCommands
-      'activate-normal-mode': => @activate('normal')
-      'activate-linewise-visual-mode': => @activate('visual', 'linewise')
+      'activate-normal-mode':               => @activate('normal')
+      'activate-linewise-visual-mode':      => @activate('visual', 'linewise')
       'activate-characterwise-visual-mode': => @activate('visual', 'characterwise')
-      'activate-blockwise-visual-mode': => @activate('visual', 'blockwise')
-      'reset-normal-mode': => @activate('reset')
+      'activate-blockwise-visual-mode':     => @activate('visual', 'blockwise')
+      'reset-normal-mode':                  => @activate('reset')
+
       'set-count': (e) => @count.set(e) # 0-9
       'set-register-name': => @register.setName() # "
       'reverse-selections': => @reverseSelections() # o
@@ -158,8 +159,9 @@
       'replace-mode-backspace': => @replaceModeBackspace()

     @registerOperationCommands InsertMode, [
-      'insert-register'
-      'copy-from-line-above', 'copy-from-line-below'
+      'insert-register',
+      'copy-from-line-above',
+      'copy-from-line-below'
     ]

     @registerOperationCommands TextObject, [
@@ -187,24 +189,24 @@
       'move-to-beginning-of-line',
       'repeat-find', 'repeat-find-reverse',
       'move-down', 'move-up', 'move-left', 'move-right',
-      'move-to-next-word'     , 'move-to-next-whole-word',
-      'move-to-end-of-word'   , 'move-to-end-of-whole-word',
+      'move-to-next-word'     , 'move-to-next-whole-word'    ,
+      'move-to-end-of-word'   , 'move-to-end-of-whole-word'  ,
       'move-to-previous-word' , 'move-to-previous-whole-word',
-      'move-to-next-paragraph', 'move-to-previous-paragraph',
-      'move-to-first-character-of-line', 'move-to-last-character-of-line',
-      'move-to-first-character-of-line-up', 'move-to-first-character-of-line-down',
+      'move-to-next-paragraph', 'move-to-previous-paragraph' ,
+      'move-to-first-character-of-line'         , 'move-to-last-character-of-line'      ,
+      'move-to-first-character-of-line-up'      , 'move-to-first-character-of-line-down',
       'move-to-first-character-of-line-and-down',
       'move-to-last-nonblank-character-of-line-and-down',
       'move-to-first-line', 'move-to-last-line',
       'move-to-top-of-screen', 'move-to-bottom-of-screen', 'move-to-middle-of-screen',
-      'scroll-half-screen-up', 'scroll-half-screen-down',
-      'scroll-full-screen-up', 'scroll-full-screen-down',
-      'move-to-mark'           , 'move-to-mark-line',
-      'find'                   , 'find-backwards',
-      'till'                   , 'till-backwards',
-      'search'                 , 'search-backwards',
+      'scroll-half-screen-up'  , 'scroll-half-screen-down'      ,
+      'scroll-full-screen-up'  , 'scroll-full-screen-down'      ,
+      'move-to-mark'           , 'move-to-mark-line'            ,
+      'find'                   , 'find-backwards'               ,
+      'till'                   , 'till-backwards'               ,
+      'search'                 , 'search-backwards'             ,
       'search-current-word'    , 'search-current-word-backwards',
-      'repeat-search'          , 'repeat-search-reverse',
+      'repeat-search'          , 'repeat-search-reverse'        ,
       'bracket-matching-motion',
     ]

@@ -221,7 +223,7 @@
       'put-after', 'put-before',
       'upper-case', 'lower-case', 'toggle-case', 'toggle-case-and-move-right',
       'camel-case', 'snake-case', 'dash-case',
-      'surround', 'surround-word',
+      'surround'       , 'surround-word'           ,
       'delete-surround', 'delete-surround-any-pair'
       'change-surround', 'change-surround-any-pair'
       'join',


Longer period for PullNotify update

Longer period for PullNotify update

--- static/script/settlers.coffee
+++ static/script/settlers.coffee
@@ -20,7 +20,7 @@
     __call_update = () ->
       pull_notify.update()

-    $('.win-commandlayout').everyTime '60s', __call_update
+    $('.win-commandlayout').everyTime '300s', __call_update

   update:		() ->
     new window.hlib.Ajax


change

change

--- services/clsi/app.coffee
+++ services/clsi/app.coffee
@@ -1,3 +1,4 @@
+
 CompileController = require "./app/js/CompileController"
 Settings = require "settings-sharelatex"
 logger = require "logger-sharelatex"


Fix the undefined msg used in random phrase

--- src/talk-rubbish.coffee
+++ src/talk-rubbish.coffee
@@ -19,6 +19,9 @@
 Array.prototype.diff = (arr) ->
   this.filter (item) ->
     arr.indexOf(item) < 0
+
+Array.prototype.random = ->
+  this[Math.floor(Math.random() * this.length)]

 module.exports = (robot) ->
   robot.hear /afk/i, (msg) ->
@@ -44,7 +47,7 @@
       else if said.length >= list.length
         said = [] # reset said items

-      phrase = msg.random(list.diff(said))
+      phrase = list.diff(said).random()
       if phrase?
         said.push phrase
         robot.brain.set "hubot_phrases_said", said


Fix failing test

Fix failing test

--- test/server/routes/api/templates.coffee
+++ test/server/routes/api/templates.coffee
@@ -137,7 +137,6 @@
       .end (err, res) ->
         expect(err).not.to.exist;
         expect(res.status).to.equal(201)
-        expect(res.body.contents).to.equal('baz')
         done();

   it 'does not allow filename mismatches', (done) ->


Tidy spaces.

Tidy spaces.

--- src/projects/projects.e2e-spec.coffee
+++ src/projects/projects.e2e-spec.coffee
@@ -46,7 +46,7 @@
       finder.then (resolved) ->
         [property, resolved]

-    Promise.all(resolvers).then (resolved) =>
+    Promise.all(resolvers).then (resolved) ->
       resolved.reduce(accumulate, {})

 ###*
@@ -64,7 +64,7 @@
   it 'should load the page', ->
     expect(page.content, 'The page was not loaded')
       .to.eventually.exist
-
+
   # Note - it would be nice to test whether a trailing slash
   # resolves to the same page, but the many attempts to build
   # such a test case were unsuccessful. Test manually instead.
@@ -81,7 +81,7 @@

     describe 'Help', ->
       help = null
-
+
       before ->
         help = page.help

@@ -95,7 +95,7 @@

   describe 'Projects', ->
     rows = null
-
+
     before ->
       page.projects().then (colls) ->
         rows = colls
@@ -117,4 +117,3 @@
         expect(actual, "The visited #{ row.name } Collection List" +
                        " page URL is incorrect")
           .to.eventually.match(matcher)
-


placeholder support for built-in bits type

--- src/lang/typedefs.coffee
+++ src/lang/typedefs.coffee
@@ -28,6 +28,17 @@
         value

 module.exports = [
+
+  new Typedef 'bits',
+    construct: (value) ->
+      unless @bit?.length > 0
+        throw new Error "[#{@tag}] must have one or more 'bit' definitions"
+      return unless value? and typeof value is 'string'
+      # TODO: handle value a number in the future
+      value = value.split ' '
+      unless (value.every (v) -> @bit.some (b) -> b.tag is v)
+        throw new Error "[#{@tag}] invalid bit name(s) for '#{value}' on #{@bit.map (x) -> x.tag}"
+      value

   new Typedef 'boolean',
     construct: (value) ->


fix return for prompt

fix return for prompt

--- SingularityUI/app/views/taskOverviewSubview.coffee
+++ SingularityUI/app/views/taskOverviewSubview.coffee
@@ -77,10 +77,9 @@
         # Check if the status is no longer in Cleanup since clicking the kill button
         if @killType is 'killOverride'
             @getCleaningStatus =>
-                if not @model.get 'isInCleanup'
-                    @killType = 'kill9Warning'
-
-                return @killPrompt()
+                @killType = 'kill9Warning' if not @model.get 'isInCleanup'
+                @killPrompt()
+            return

         @killPrompt()


A new take on fixing bracket forwarding with Process API, fixes #465

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -37,6 +37,7 @@
     @ordered = options.ordered ? false
     @autoOrdering = options.autoOrdering ? null
     @outputQ = []
+    @bracketContext = {}
     @activateOnInput = options.activateOnInput ? true
     @forwardBrackets = in: ['out', 'error']

@@ -104,52 +105,121 @@
           @handleIP ip, port
     @

-  # Handles an incoming IP object
+  isForwardingInport: (port) ->
+    if typeof port is 'string'
+      portName = port
+    else
+      portName = port.name
+    if portName of @forwardBrackets
+      return true
+    false
+
+  isForwardingOutport: (outport) ->
+    if typeof outport is 'string'
+      outportName = outport
+    else
+      outportName = outport.name
+    for inport, outports of @forwardBrackets
+      return true if outports.indexOf(outportName) isnt -1
+    false
+
+  isOrdered: ->
+    return true if @ordered
+    return true if @autoOrdering
+    false
+
+  # The component has received an Information Packet. Call the processing function
+  # so that firing pattern preconditions can be checked and component can do
+  # processing as needed.
   handleIP: (ip, port) ->
+    unless port.options.triggering
+      # If port is non-triggering, we can skip the process function call
+      return
+
     if ip.type is 'openBracket'
-      # Switch component to ordered mode when receiving a stream unless
-      # auto-ordering is disabled
-      @autoOrdering = true if @autoOrdering is null
-    # If port is non-triggering, we can skip the process function call
-    return unless port.options.triggering
-    if port.name of @forwardBrackets and
-    (ip.type is 'openBracket' or ip.type is 'closeBracket')
-      # Bracket forwarding
-      outputEntry =
+      # Prepare bracket context for packet scope as needed
+      @bracketContext[ip.scope] = [] unless @bracketContext[ip.scope]
+
+      # Initialize the context for this bracket
+      context =
+        ip: ip
+        ports: []
+      @bracketContext[ip.scope].push context
+
+      if @bracketContext[ip.scope].length is 1 and @autoOrdering is null
+        # Switch component to ordered mode when receiving a stream unless
+        # auto-ordering is disabled
+        debug "#{@nodeId} port #{port.name} entered auto-ordering mode"
+        @autoOrdering = true
+
+    if ip.type is 'closeBracket' and @isOrdered()
+      # Clear this scope
+      context = @bracketContext[ip.scope].pop()
+      context.closeIp = ip
+      result =
         __resolved: true
-        __forwarded: true
-        __type: ip.type
-        __scope: ip.scope
-      for outPort in @forwardBrackets[port.name]
-        outputEntry[outPort] = [] unless outPort of outputEntry
-        outputEntry[outPort].push ip
-      if ip.scope?
-        port.scopedBuffer[ip.scope].pop()
-      else
-        port.buffer.pop()
-      @outputQ.push outputEntry
-      @processOutputQueue()
-      return
-    result = {}
+        __closeBracketContext: context
+      @outputQ.push result
+      do @processOutputQueue
+
+    if @isForwardingInport(port) and ip.type in ['openBracket', 'closeBracket']
+      # Bracket forwarding enabled on port. In this case we don't call processing
+      # functions on brackets, only on data.
+      return
+
+    bracketContext = if @bracketContext[ip.scope] then @bracketContext[ip.scope].slice(0) else []
+    result =
+      __bracketContext: bracketContext
     # Prepare the input/output pair
     input = new ProcessInput @inPorts, ip, @, port, result
     output = new ProcessOutput @outPorts, ip, @, result
     # Call the processing function
     @handle input, output, -> output.done()
-    debug "#{@nodeId} didn't match preconditions on #{port.name}" unless input.activated
+
+    unless input.activated
+      debug "#{@nodeId} #{ip.type} packet on #{port.name} didn't match preconditions"
+      return
+
+    # Component fired
+    if @isOrdered()
+      # Ordered mode. Instead of sending directly, we're queueing
+      @outputQ.push result if @isOrdered()
+    return
+
+  addBracketForwards: (result) ->
+    # First see if there are any brackets to forward
+    return unless result.__bracketContext
+    for outport, ips of result
+      continue if outport.indexOf('__') is 0
+      continue unless @isForwardingOutport outport
+      unforwarded = result.__bracketContext.filter (ctx) ->
+        ctx.ports.indexOf(outport) is -1
+      continue unless unforwarded.length
+      unforwarded.reverse()
+      for ctx in unforwarded
+        ips.unshift ctx.ip.clone()
+        debug "#{@nodeId} forwarding #{ctx.ip.type} to #{outport}"
+        ctx.ports.push outport

   processOutputQueue: ->
     while @outputQ.length > 0
       result = @outputQ[0]
       break unless result.__resolved
+
+      if result.__closeBracketContext
+        # Handle closing bracket forwarding
+        for outport in result.__closeBracketContext.ports
+          debug "#{@nodeId} forwarding closeBracket to #{outport}"
+          result[outport] = [result.__closeBracketContext.closeIp.clone()]
+        delete result.__closeBracketContext
+
       for port, ips of result
         continue if port.indexOf('__') is 0
         continue unless @outPorts.ports[port].isAttached()
+        @addBracketForwards result
         for ip in ips
           @outPorts[port].sendIP ip
       @outputQ.shift()
-    bracketsClosed = true
-    @autoOrdering = null if bracketsClosed and @autoOrdering is true

 exports.Component = Component

@@ -162,12 +232,14 @@
   # Preconditions met, set component state to `activated`
   activate: ->
     return if @activated
-    @result.__resolved = false
-    debug "#{@nodeInstance.nodeId} activated on #{@port.name}"
-    if @nodeInstance.ordered or @nodeInstance.autoOrdering
-      @nodeInstance.outputQ.push @result
     @nodeInstance.load++
+    debug "#{@nodeInstance.nodeId} #{@ip.type} packet on #{@port.name} caused activation #{@nodeInstance.load}"
     @activated = true
+
+    if @nodeInstance.isOrdered()
+      # We're handling packets in order. Set the result as non-resolved so that it can
+      # be send when the order comes up
+      @result.__resolved = false

   # ## Input preconditions
   # When the processing function is called, it can check if input buffers
@@ -192,7 +264,7 @@
   hasStream: (port) ->
     buffer = @buffer.get port
     return false if buffer.length is 0
-    # check if we have everything until "disconnect"
+    # check if we have everything until end of a complete stream
     received = 0
     for packet in buffer
       if packet.type is 'openBracket'
@@ -208,12 +280,13 @@
   #
   # Fetches IP object(s) for port(s)
   get: (args...) ->
+    @activate()
     args = ['in'] unless args.length
-    if (@nodeInstance.ordered or @nodeInstance.autoOrdering) and
-    @nodeInstance.activateOnInput and
-    not ('__resolved' of @result)
-      @activate()
-    res = (@ports[port].get @scope for port in args)
+    res = []
+    for port in args
+      ip = @ports[port].get @scope
+      res.push ip
+
     if args.length is 1 then res[0] else res

   # Fetches `data` property of IP object(s) for given port(s)
@@ -307,12 +380,7 @@
 class ProcessOutput
   constructor: (@ports, @ip, @nodeInstance, @result) ->
     @scope = @ip.scope
-
-  # Sets component state to `activated`
-  activate: ->
-    @result.__resolved = false
-    if @nodeInstance.ordered or @nodeInstance.autoOrdering
-      @nodeInstance.outputQ.push @result
+    @sent = []

   # Checks if a value is an Error
   isError: (err) ->
@@ -333,23 +401,23 @@

   # Sends a single IP object to a port
   sendIP: (port, packet) ->
-    if typeof packet isnt 'object' or IP.types.indexOf(packet.type) is -1
+    unless IP.isIP packet
       ip = new IP 'data', packet
     else
       ip = packet
     ip.scope = @scope if @scope isnt null and ip.scope is null
-    if @nodeInstance.ordered or @nodeInstance.autoOrdering
+
+    if @nodeInstance.isOrdered()
+      debug "#{@nodeInstance.nodeId} is in ordered mode, deferred send of #{ip.type} to #{port}"
       @result[port] = [] unless port of @result
       @result[port].push ip
-    else
-      @nodeInstance.outPorts[port].sendIP ip
+      return
+    debug "#{@nodeInstance.nodeId} direct send of #{ip.type} to #{port}"
+    @nodeInstance.outPorts[port].sendIP ip

   # Sends packets for each port as a key in the map
   # or sends Error or a list of Errors if passed such
   send: (outputMap) ->
-    if (@nodeInstance.ordered or @nodeInstance.autoOrdering) and
-    not ('__resolved' of @result)
-      @activate()
     return @error outputMap if @isError outputMap

     componentPorts = []
@@ -386,7 +454,10 @@
   # Finishes process activation gracefully
   done: (error) ->
     @error error if error
-    if @nodeInstance.ordered or @nodeInstance.autoOrdering
+
+    debug "#{@nodeInstance.nodeId} finished processing #{@nodeInstance.load}"
+    @nodeInstance.load--
+
+    if @nodeInstance.isOrdered()
       @result.__resolved = true
       @nodeInstance.processOutputQueue()
-    @nodeInstance.load--


Add 'watch:babel' task

Add 'watch:babel' task

--- gulp/tasks/watch.coffee
+++ gulp/tasks/watch.coffee
@@ -4,12 +4,16 @@

 gulp.task "watch", ->
   [
+    "babel"
     "coffee"
     "scss"
     "haml"
   ].forEach((name) ->
     gulp.start("watch:#{name}")
   )
+
+gulp.task "watch:babel", ->
+  $.watch(config.src.babel, -> gulp.start("compile:babel"))

 gulp.task "watch:coffee", ->
   $.watch(config.src.coffee, -> gulp.start("compile:coffee"))
@@ -18,4 +22,4 @@
   $.watch(config.src.scss, -> gulp.start("compile:scss"))

 gulp.task "watch:haml", ->
-  $.watch(config.src.haml, -> gulp.start("compile:haml"))+  $.watch(config.src.haml, -> gulp.start("compile:haml"))


sending all events to particles

sending all events to particles

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -39,4 +39,4 @@
     I.everyEvents.push
       fn: fn
       every: period
-      lastFired: I+      lastFired: I.age


fixed value fetching issue when fetching a nested id list

--- src/core/fetch/value.coffee
+++ src/core/fetch/value.coffee
@@ -101,6 +101,13 @@
     return d3.sum(arr)
   uniqueValues arr

+fetchArray = (vars, arr, variable, depth) ->
+  val = []
+  for item in arr
+    v = find vars, item, variable, depth
+    val.push valueParse item, depth, variable, v
+  aggregate vars, val, variable
+
 fetch = (vars, node, variable, depth) ->

   return null unless variable
@@ -110,26 +117,20 @@
   depth = vars.id.value unless depth

   if nodeObject and node.values instanceof Array
-    val = []
-    for item in node.values
-      v = find(vars, item, variable, depth)
-      val.push valueParse item, depth, variable, v
-    val = aggregate vars, val, variable
+    val = fetchArray vars, node.values, variable, depth
+  else if nodeObject and node[variable] instanceof Array
+    val = fetchArray vars, node[variable], variable, depth
   else if node instanceof Array
-    val = []
-    for item in node
-      v = find(vars, item, variable, depth)
-      val.push valueParse item, depth, variable, v
-    val = aggregate vars, val, variable
+    val = fetchArray vars, node, variable, depth
   else
     val = find vars, node, variable, depth
     val = valueParse node, depth, variable, val
-    if val is null and nodeObject and node[vars.id.value] instanceof Array
-      val = []
-      for item in node[vars.id.value]
-        v = find(vars, item, variable, depth)
-        val.push valueParse item, depth, variable, v
-      val = aggregate vars, val, variable
+    # if val is null and nodeObject and node[vars.id.value] instanceof Array
+    #   val = []
+    #   for item in node[vars.id.value]
+    #     v = find(vars, item, variable, depth)
+    #     val.push valueParse item, depth, variable, v
+    #   val = aggregate vars, val, variable

   val


Add reference to MDN regarding Date.toISOString() shim

--- src/transparency.coffee
+++ src/transparency.coffee
@@ -198,6 +198,7 @@
 isDate = (obj) ->
   Object.prototype.toString.call(obj) == '[object Date]'

+# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString
 pad = (n) ->
   if n < 10 then "0#{n}" else n.toString()


Improved snippets

Improved snippets

--- snippets/language-d.cson
+++ snippets/language-d.cson
@@ -1,22 +1,22 @@
 ".source.d":
-	"if":
+	"if+condition":
 		"prefix": "if"
 		"body": "if($1) {$2}"
-	"elseif":
+	"else if":
 		"prefix": "elif"
 		"body": "else if($1) {$2}"
-	"else":
+	"else+braces":
 		"prefix": "el"
 		"body": "else {$1}"
-	"switch":
+	"switch+case":
 		"prefix": "sw"
 		"body": """
 switch($1) {$2
 	default:
 		break;
 }"""
-	"case":
-		"prefix": "case"
+	"case+value":
+		"prefix": "cs"
 		"body": """case $1:
 	${2:break;}"""
 	"Exception template":


fix header stuck

fix header stuck

--- client/index.coffee
+++ client/index.coffee
@@ -36,4 +36,4 @@
         $main_header.addClass 'has-background'
       else
         $main_header.removeClass 'has-background'
-    offset: $main_header.height()
+    offset: -> $main_header.height()


rearranged a bit

rearranged a bit

--- src/scripts/opendns-umbrella.coffee
+++ src/scripts/opendns-umbrella.coffee
@@ -8,7 +8,7 @@
 #   OPENDNS_KEY - Sign up at https://investigate.opendns.com
 #
 # Commands:
-#   hubot opendns <url> - Gets OpenDNS Domain Reputation
+#   hubot opendns <domain> - Gets OpenDNS Domain Reputation
 #   hubot opendns rr <ip> - Gets OpenDNS Resource Record history for a given IP
 #   hubot opendns secinfo <domain> - Gets OpenDNS Security Information for a given domain
 #
@@ -16,8 +16,24 @@
 #   Scott J Roberts - @sroberts

 OPENDNS_KEY = process.env.OPENDNS_KEY
-token = OPENDNS_KEY
-opendns_base = "https://investigate.api.opendns.com/"
+
+# module.exports = (robot) ->
+#   robot.respond /opendns (.*)/i, (msg) ->
+#
+#     if OPENDNS_KEY?
+#       artifact = msg.match[1].toLowerCase()
+#
+#       msg.http("https://investigate.api.opendns.com/domains/categorization/#{artifact}")
+#         .headers('Authorization': 'Bearer ' + OPENDNS_KEY)
+#         .get() (err, res, body) ->
+#           if res.statusCode is 200
+#
+#             opendns_json = JSON.parse body
+#
+#           else
+#             msg.send "Doh! #{res.statusCode}: Which means that didn't work."
+#     else
+#       msg.send "OpenDNS API key not configured."

 module.exports = (robot) ->
   robot.respond /opendns (.*)/i, (msg) ->
@@ -26,7 +42,7 @@
       artifact = msg.match[1].toLowerCase()

       msg.http("https://investigate.api.opendns.com/domains/score/#{artifact}")
-        .headers('Authorization': 'Bearer ' + token)
+        .headers('Authorization': 'Bearer ' + OPENDNS_KEY)
         .get() (err, res, body) ->

           if res.statusCode is 200


webapp/latex: fls is yet another latex related extension to mask

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -45,7 +45,7 @@
     'py'   : ['pyc']
     'java' : ['class']
     'cs'   : ['exe']
-    'tex'  : 'aux bbl blg fdb_latexmk glo idx ilg ind lof log nav out snm synctex.gz toc xyc'.split(' ')
+    'tex'  : 'aux bbl blg fdb_latexmk fls glo idx ilg ind lof log nav out snm synctex.gz toc xyc'.split(' ')

 BAD_FILENAME_CHARACTERS       = '\\'
 BAD_LATEX_FILENAME_CHARACTERS = '\'"()"~%'


added export of Decryptor

added export of Decryptor

--- src/dec.iced
+++ src/dec.iced
@@ -18,7 +18,7 @@
 #  @param {Buffer} key  A buffer with the keystream data in it
 #  @param {Buffer} salt Salt for key derivation, should be the user's email address
 #
-class Decryptor extends Base
+exports.Decryptor = class Decryptor extends Base

   #----------------------


Fix 4

Fix 4

yet again another fix-

--- Pen.coffee
+++ Pen.coffee
@@ -1,24 +1,4 @@
 pen = do ->
-  Object::forEach = (cb) ->
-    it = @
-    for key of it
-      cb(key, it[key], it)
-    return
-  Object::filter = (cb) ->
-    it = @
-    obj = {}
-    for key of it
-      res = cb(key, it[key], it)
-      if res is true
-        obj[key] = it[key]
-    return obj
-  Object::map = (cb) ->
-    it = @
-    obj = {}
-    for key of it
-      res = cb(key, it[key], it)
-      obj[key] = res
-    return obj
   vrs = {}
   elCount = 0
   vrs.class2Type = {}
@@ -91,7 +71,6 @@
     else
       app = it.ops.global.html.app or false
       parse = it.ops.global.html.parse or false
-    vrs.log str
     it.text = str
     if str?
       if app is true


Register both outports with WirePattern

Register both outports with WirePattern

--- components/Router.coffee
+++ components/Router.coffee
@@ -62,17 +62,18 @@

   noflo.helpers.WirePattern c,
     in: 'url'
-    out: 'route'
+    out: ['route', 'missed']
     forwardGroups: false
-  , (url, groups, out) ->
+    async: true
+  , (url, groups, out, callback) ->
     ctx = buildContext url
     unless ctx
-      c.outPorts.missed.send ctx
-      c.outPorts.missed.disconnect()
-      return
+      out.missed.send ctx
+      return callback()

-    out.beginGroup ctx.route
-    out.beginGroup 'open'
-    out.send ctx
-    out.endGroup()
-    out.endGroup()
+    out.route.beginGroup ctx.route
+    out.route.beginGroup 'open'
+    out.route.send ctx
+    out.route.endGroup()
+    out.route.endGroup()
+    callback()