model_postgres: fix config order/limig

model_postgres: fix config order/limig

--- src/local/model_postgres.coffee
+++ src/local/model_postgres.coffee
@@ -626,7 +626,7 @@
                 cb2()

         async.parallel [ (cb2) ->
-            db.query "SELECT DISTINCT node FROM node_config WHERE node in (SELECT node FROM subscriptions WHERE listener=$1) #{conds}", params
+            db.query "SELECT DISTINCT node FROM node_config WHERE node in (SELECT node FROM subscriptions WHERE listener=$1) #{conds} ORDER BY updated DESC #{limit}", params
             , (err, res) =>
                 if err
                     return cb2 err


Social: fix logic of channel fetcher function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -28,12 +28,11 @@

   fetchChannels:(options, callback)->
     @getCurrentGroup (group)=>
-      return callback { message: "Group doesnt have socialApiChannelId" } unless group.socialApiChannelId
-      options.id = group.socialApiChannelId
+      options.groupName = group.slug
       {SocialChannel} = KD.remote.api
-      SocialChannel.fetchActivity options, (err, result)=>
+      SocialChannel.fetchChannels options, (err, result)=>
         return callback err if err
-        return callback null, @mapActivity result
+        return callback null, @mapChannel result

   mapActivity:(result)->


privatemessage: add leave method wrapper to socialapicontroller

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -500,6 +500,10 @@
       fnName              : 'removeParticipants'
       validateOptionsWith : ['channelId', "accountIds"]

+    leave                 : channelRequesterFn
+      fnName              : 'leave'
+      validateOptionsWith : ['channelId']
+
     fetchFollowedChannels: channelRequesterFn
       fnName             : 'fetchFollowedChannels'
       mapperFn           : mapChannels


Activity: add participant modal added to includes file

--- client/Social/Activity/includes.coffee
+++ client/Social/Activity/includes.coffee
@@ -31,6 +31,7 @@
   "views/activitymenuitem.coffee"
   "views/chatheadautocompleteitem.coffee"
   "views/privatemessagemodal.coffee"
+  "views/privatemessagerecipientmodal.coffee"
   "views/alltopicsmodal.coffee"
   "views/yourtopicsmodal.coffee"
   "views/conversationsmodal.coffee"


Support multiple localstorages with different versions in LocalStorageController

--- client/app/MainApp/LocalStorage.coffee
+++ client/app/MainApp/LocalStorage.coffee
@@ -38,12 +38,12 @@
     super
     @localStorages = {}

-  storage:(appName, version)->
-
-    if @localStorages[appName]?
-      storage = @localStorages[appName]
+  storage:(appName, version = "1.0")->
+    key = "#{appName}-{version}"
+    if @localStorages[key]?
+      storage = @localStorages[key]
     else
-      storage = @localStorages[appName] = new LocalStorage appName, version
+      storage = @localStorages[key] = new LocalStorage appName, version

     return storage


Add earthquake move anim

Add earthquake move anim

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -305,7 +305,15 @@
     [a, d] = [$attacker.position(), $defender.position()]
     front = (a.top < d.top)
     scale = (if front then 1.3 else 1/1.3)
-    if 'contact' in moveData.flags
+    if moveName == 'Earthquake'
+      $attacker.add($defender).each (index) ->
+        callback = (if index == 0 then done else undefined)
+        $(this).transition(x: -75, 62.5, 'easeInOutCubic')
+          .transition(x: 75, 125, 'easeInOutCubic')
+          .transition(x: -30, 125, 'easeInOutCubic')
+          .transition(x: 30, 125, 'easeInOutCubic')
+          .transition(x: 0, 62.5, 'easeInOutCubic', callback)
+    else if 'contact' in moveData.flags
       # Simple attack animation
       # Tackling the opponent
       $attacker


Forms submit when the final step is reached.

--- vendor/assets/javascripts/modal.js.coffee
+++ vendor/assets/javascripts/modal.js.coffee
@@ -57,9 +57,12 @@
         return

       form = $("form[data-path='#{path}']")
+      steps = $(form).find('*[data-modal-step]')
       modal = $("script[type='text/template'][data-path='#{path}']")

-      steps = $(form).find('*[data-modal-step]')
+      # beforesubmit handler
+      beforeSubmit = =>
+        form[0].submit()

       modalOptions = if steps.length > 0

@@ -86,7 +89,7 @@
           if i >= (steps.length - 1)
             submit = $(_modal).find('input[type=submit]')
             label = $(submit).val()
-            $(bottomBar).find('.next').html(label).show().addClass('submit')
+            $(bottomBar).find('.next').html(label).show().addClass('submit').removeClass('next')
             $(submit).hide()

           views["step#{i}"] = view: _.template($(_modal).html())
@@ -94,7 +97,7 @@
         # split steps into views
         {
           cancelEl: '.cancel'
-          submitEl: '.done'
+          submitEl: '.submit'
           views: views

           events:
@@ -127,11 +130,14 @@
             $(step).replaceWith(section.children('*[data-modal-step]')[0])

             @next()
+
+          beforeSubmit: beforeSubmit
         }
       else
         {
           cancelEl: '.close'
           template: _.template($(modal).html())
+          beforeSubmit: beforeSubmit
         }

       Modal = Backbone.Modal.extend(modalOptions)


common.asyncSeries: Fix result array inverted

Looks like we did not use the resulting array anywhere
where order was critical, but when introducing that bugs appeared.

--- src/common.coffee
+++ src/common.coffee
@@ -21,7 +21,7 @@
     item = items.shift()
     func item, (err, result) ->
       return callback err if err
-      results.unshift result
+      results.push result
       return next()
   next()


remove old code accidentally in frmo categorical experiments

--- bokehjs/src/coffee/renderer/guide/axis.coffee
+++ bokehjs/src/coffee/renderer/guide/axis.coffee
@@ -221,11 +221,6 @@
       [nx, ny] = @mget('normals')
       side = @mget('side')
       orient = 'parallel'
-
-      if @normal_offset != 0
-        for i in [0...sx.length]
-          sx[i] += nx * @normal_offset
-          sy[i] += ny * @normal_offset

       angle = _angle_lookup[side][orient]
       standoff = @_tick_extent() + @_tick_label_extent() + @mget('axis_label_standoff')


how to use nodejs_client with non-local node.

--- salvus/nodejs_client.coffee
+++ salvus/nodejs_client.coffee
@@ -1,9 +1,11 @@
 message = require("salvus_message")
-
-sjsc = require("sockjs-client-ws")  #  https://github.com/steerapi/sockjs-client-node
+sjsc = require("sockjs-client-ws")

 #client = sjsc.create("https://localhost/node")  # doesn't work -- don't know why
+
 client = sjsc.create("http://localhost:5000/node")
+
+#client = sjsc.create("http://10.1.1.3:5000/node")

 walltime = -> (new Date()).getTime()
 tm = 0


iconのdropに時間差をつけた

iconのdropに時間差をつけた

--- app/assets/javascripts/map.coffee
+++ app/assets/javascripts/map.coffee
@@ -17,13 +17,15 @@
       map = that.map = new google.maps.Map($(options.canvas)[0], mapOptions);
       draw_routes_polyline(map, $(options.polyline).text())
       for s, i in data
-        MK
-          position: LL(s.lat, s.lon)
-          map: map
-          title: s.nickname
-          icon: s.image
-          zIndex: 10000-i
-          animation: google.maps.Animation.DROP
+        setTimeout ->
+          MK
+            position: LL(s.lat, s.lon)
+            map: map
+            title: s.nickname
+            icon: s.image
+            zIndex: 10000-i
+            animation: google.maps.Animation.DROP
+        , i * 100

   that.overlay_kml = (kml_url) ->
     console.log("load KML: " + kml_url)


[Client][Activity] Remove unused functions

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -196,11 +196,6 @@
     then fetch()
     else groupsController.once 'GroupChanged', fetch

-  listActivities:(activities, callback)->
-    @sanitizeCache activities, (err, sanitizedCache)=>
-      @extractCacheTimeStamps sanitizedCache
-      @listController.listActivitiesFromCache sanitizedCache
-      callback sanitizedCache

   fetchPublicActivities:(options = {})->
     {CStatusActivity} = KD.remote.api


Show blue indicators again

Show blue indicators again

--- src/plugins/image/current.coffee
+++ src/plugins/image/current.coffee
@@ -192,9 +192,9 @@

         jQuery('.trashcan', ui.helper).remove()

-        editable.append widget.overlay.big
-        editable.append widget.overlay.left
-        editable.append widget.overlay.right
+        editable[0].element.append widget.overlay.big
+        editable[0].element.append widget.overlay.left
+        editable[0].element.append widget.overlay.right

         widget._removeFeedbackElements()
         target = jQuery event.target


Wait for runtime to confirm ports before starting test run

--- src/protocol.coffee
+++ src/protocol.coffee
@@ -8,6 +8,10 @@

   graphId = graph.name or graph.properties.id
   graphId = "fixture.#{common.randomString(10)}" if not graphId
+
+  pendingPorts =
+    in: []
+    out: []

   runtime.sendGraph 'clear',
     id: graphId
@@ -53,6 +57,7 @@
         node: priv.process
         port: priv.port
         graph: graphId
+      pendingPorts.in.push pub
   if graph.outports
     for pub, priv of graph.outports
       runtime.sendGraph 'addoutport',
@@ -60,9 +65,24 @@
         node: priv.process
         port: priv.port
         graph: graphId
+      pendingPorts.out.push pub

-  # FIXME: wait for responses. Maybe until "ports" message matches our setup?
-  return callback null, graphId
+  waitForPorts = ({command, payload}) ->
+    return unless command in ['addinport', 'addoutport']
+    debug 'received port', payload.public
+    if command is 'addinport'
+      collection = pendingPorts.in
+    else
+      collection = pendingPorts.out
+    if collection.indexOf(payload.public) is -1
+      debug 'received unknown port', payload.public
+      return
+    collection.splice collection.indexOf(payload.public), 1
+    return if pendingPorts.in.length or pendingPorts.out.length
+    runtime.removeListener 'graph', waitForPorts
+    return callback null, graphId
+
+  runtime.on 'graph', waitForPorts

 exports.startNetwork = (runtime, graphId, callback) ->
   debug 'startnetwork', graphId


Pause for chat input focus before edit/delete action

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -609,12 +609,13 @@
       browser
         .waitForElementVisible  editButton, 20000
         .click                  editButton
-        .pause                  3000# wait for textarea
+        .pause                  3000 # wait for textarea
         .waitForElementVisible  chatInput, 20000
         .clearValue             chatInput
         .setValue               chatInput, editedmessage
         .setValue               chatInput, browser.Keys.ENTER
         .waitForElementVisible  editedText, 20000
+        .pause                  2000 # wait for new text
         .assert.containsText    editedText, editedmessage

     if deletePost


Don't call special attr if not func

Don't call special attr if not func

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -929,7 +929,7 @@
             updateSVGContents(elt, contents)

         for key of attrs when key of specialAttrs
-            specialAttrs[key](elt, attrs[key], attrs, contents)
+            specialAttrs[key](elt, attrs[key], attrs, contents) if _.isFunction attrs[key]
         elt

     rxt.tags = _.object([tag, rxt.mktag(tag)] for tag in tags)


css renderer doesnt require proxy.. bonus

--- web/src/cs/dreamAssetLoader.coffee
+++ web/src/cs/dreamAssetLoader.coffee
@@ -10,7 +10,7 @@
 	maxPerDream = 100
 	imagesRequested = 0
 	imagesErrored = 0
-	useImageProxy = true
+	useImageProxy = false
 	checkLoadStatus: (allowErrors = true) =>
 		loaded = false
 		if allowErrors
@@ -87,7 +87,7 @@
 			if imagesLoaded < maxPerDream
 				@loadMore()
 		textureLoader.load(termImage.url, termImageLoaded, imageProgressHandler, @loadMore)
-		console.log termImage
+

 	loadingProgress = (e) ->
@@ -103,7 +103,6 @@

 		textureLoader = new THREE.TextureLoader()
-		textureLoader.setCrossOrigin(true)
 		allTermImages = []
 		_.each(assetTypes, (assetType) =>
 			assets[assetType] = @data[assetType]


use isActionNode everywhere, tidy up a bit

--- app/coffee/game.coffee
+++ app/coffee/game.coffee
@@ -303,47 +303,52 @@

 NavigationDom = React.createClass
   render: ->
-    {sgfObject, viewingMove} = @props
-
-    options = [n: 0, text: 'Start']
-    maxMoves = null
-    do ->
-      moveNo = 0
-      for node in sgfObject.gameTrees[0].nodes
-        if node.B?
-          moveNo += 1
-          options.push n: moveNo, text: 'B ' + (a1FromSgfTag(node.B) or 'pass')
-        else if node.W?
-          moveNo += 1
-          options.push n: moveNo, text: 'W ' + (a1FromSgfTag(node.W) or 'pass')
-        else if node.TB? or node.TW?
-          moveNo += 1
-          options.push n: moveNo, text: 'Mark dead'
-      maxMoves = moveNo
-
-    reactOption = (option) ->
-      {n, text} = option
-      React.DOM.option {key: n, value: n},
-                       ["Move #{n}: #{text}"]
-
-    onChange = (event) =>
-      @props.changeCallback parseInt(event.target.value, 10)
-
-    # hack for Firefox, which won't fire change/input event on
-    # keyboard updates to select boxes until the focus is removed
-    onKeyUp = (event) ->
-      event.target.blur()
-      event.target.focus()
-
     {div, select} = React.DOM
     div {className: 'board_nav_block'},
         (select {
           className: 'move_select',
-          onChange: onChange
-          onInput: onChange
-          onKeyUp: onKeyUp
-          value: viewingMove ? maxMoves},
-                (reactOption o for o in options))
+          onChange: @onSelectChange
+          onInput: @onSelectChange
+          onKeyUp: @onSelectKeyUp
+          value: @getViewingMove()}, @getOptions())
+
+  onSelectChange: (event) ->
+    @props.changeCallback parseInt(event.target.value, 10)
+
+  # hack for Firefox, which won't fire change/input event on
+  # keyboard updates to select boxes until the focus is removed
+  onSelectKeyUp: (event) ->
+    event.target.blur()
+    event.target.focus()
+
+  getViewingMove: -> @props.viewingMove ? actionCount @props.sgfObject
+
+  getOptions: ->
+    makeOption = (optionSpec) ->
+      {n, text} = optionSpec
+      React.DOM.option {key: n, value: n},
+                      ["Move #{n}: #{text}"]
+
+    actionNodes =
+      node for node in @props.sgfObject.gameTrees[0].nodes \
+                    when isActionNode node
+
+    getActionText = (node) ->
+      if node.B?
+        return 'B ' + (a1FromSgfTag(node.B) or 'pass')
+      else if node.W?
+        return 'W ' + (a1FromSgfTag(node.W) or 'pass')
+      else if node.TB? or node.TW?
+        return 'Mark dead'
+      else
+        throw new Error "unrecognised action node"
+
+    optionSpecs = [n: 0, text: 'Start'].concat(
+      for node, i in actionNodes
+        n: i+1, text: getActionText node
+    )
+
+    (makeOption o for o in optionSpecs)

 ScoreDom = React.createClass
   render: ->
@@ -407,7 +412,8 @@
   prisoners = { black: 0, white: 0 }
   moveNo = 0
   for node in sgfObject.gameTrees[0].nodes
-    break if moves? and moveNo >= moves
+    if isActionNode node then moveNo += 1
+    break if moves? and moveNo > moves
     if node.AB
       coords = if Array.isArray(node.AB) then node.AB else [node.AB]
       for coordStr in coords
@@ -419,21 +425,17 @@
         [x, y] = decodeSgfCoord coordStr
         boardState[y][x] = 'white'
     if node.B
-      moveNo += 1
       [x, y] = decodeSgfCoord node.B
       result = go_rules.getNewStateAndCaptures('black', x, y, boardState)
       boardState = result.state
       prisoners.black += result.captures.black
       prisoners.white += result.captures.white
     else if node.W
-      moveNo += 1
       [x, y] = decodeSgfCoord node.W
       result = go_rules.getNewStateAndCaptures('white', x, y, boardState)
       boardState = result.state
       prisoners.black += result.captures.black
       prisoners.white += result.captures.white
-    else if 'B' of node or 'W' of node or 'TB' of node or 'TW' of node
-      moveNo += 1
   if scoring
     [x, y] = [null, null]
     deadStones = deadFromUi ?


#976 - Questions are missing from skip logic dropdown

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -163,7 +163,7 @@
     selectRow: (evt)->
       $et = $(evt.target)
       $ect = $(evt.currentTarget)
-      if $et.closest('.card__settings').length > 0
+      if $et.closest('.card__settings, .card__buttons').length > 0
         return
       # a way to ensure the event is not run twice when in nested .js-select-row elements
       _isIntendedTarget = $ect.closest('.survey__row').get(0) is $et.closest('.survey__row').get(0)


updating new code to use models

updating new code to use models

--- src/scripts/airline_tycoon.cjsx
+++ src/scripts/airline_tycoon.cjsx
@@ -13,9 +13,9 @@
 blue = '#9fc5e8'
 grey = '#eeeeee'

-player1 = name: 'Blue',   color: blue, money: 0, hq: airports[2]
-player2 = name: 'Pink',   color: pink, money: 0, hq: airports[0]
-nobody =  name: 'Nobody', color: grey, money: 0, hq: airports[1]
+player1 = name: 'Blue',   color: blue, money: 0, hq: Airport.list[2]
+player2 = name: 'Pink',   color: pink, money: 0, hq: Airport.list[0]
+nobody  =  name: 'Nobody', color: grey, money: 0, hq: Airport.list[1]

 player1.hq.owner = player1
 player2.hq.owner = player2


refactor hideKeyboard

refactor hideKeyboard

--- app/controllers/base_panel.coffee
+++ app/controllers/base_panel.coffee
@@ -8,10 +8,12 @@
   addButton: (text, callback) ->
     super.addClass('button')

-  activate: ->
-    # Hide keyboard if was open
+  hideKeyboard: ->
     document.activeElement.blur();
     $("input").blur();
+
+  activate: ->
+    @hideKeyboard()
     super


prevent default on key press

prevent default on key press

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -286,10 +286,14 @@
     # Update Annotation attribute
     Annotation.zoom = zoom

+    deltaX = halfWidth + Annotation.xOffset
+    deltaY = halfHeight + Annotation.yOffset
+
     # Move element within zoom reference frame
     for key, a of @annotations
-      x = (a.x - halfWidth) * zoom + halfWidth
-      y = (a.y - halfHeight) * zoom + halfHeight
+      # TODO: Marker jumps when zooming after pan
+      x = (a.x - halfWidth) * zoom + halfWidth - deltaX
+      y = (a.y - halfHeight) * zoom + halfHeight + deltaY
       a.gRoot.setAttribute("transform", "translate(#{x}, #{y})")

   # Called when viewer is ready
@@ -307,7 +311,10 @@

     # Set up pan key and mouse events
     $(document).keyup((e) => @panKey = false if e.keyCode is 32)
-    $(document).keydown((e) => @panKey = true if e.keyCode is 32)
+    $(document).keydown((e) =>
+      e.preventDefault()
+      @panKey = true if e.keyCode is 32
+    )

     # Pass events to viewer if pan key is true
     svg.onmousedown = (e) =>


Updated the logs #gploy #default #stage #live

--- src/deploy.coffee
+++ src/deploy.coffee
@@ -57,7 +57,7 @@
 		@connection = new ftp()
 		@connection.on "error", =>
 			console.log "Connection failed.".bold.red
-			@completed.dispatch()
+			@completed.dispatch() if @completed
 			return
 		@connection.on "ready", =>
 			@connections.push @connection
@@ -74,7 +74,6 @@

 	setupMultipleFTP: ->
 		con = new ftp()
-		con.on "error", => return console.log "Connection failed.".bold.red
 		con.on "ready", =>
 			# Once is connected, check the revision files
 			@connections.push con


Add fullName getter to user model

Add fullName getter to user model

--- app/models/user.coffee
+++ app/models/user.coffee
@@ -37,6 +37,8 @@
     role:
       model: 'role'
     comparePasswords: comparePasswords
+    fullName: ->
+      "#{@firstName} #{@lastName}"
 #    projects:
 #      collection: 'project'
 #      via: 'users'


Add node webkit detection

Add node webkit detection

--- src/device.coffee
+++ src/device.coffee
@@ -70,9 +70,12 @@

 device.fxosTablet = ->
   device.fxos() and _find 'tablet'
-
+
 device.meego = ->
   _find 'meego'
+
+device.nodeWebkit = ->
+  typeof window.process == 'object'

 device.mobile = ->
   device.androidPhone() or device.iphone() or device.ipod() or device.windowsPhone() or device.blackberryPhone() or device.fxosPhone() or device.meego()
@@ -155,7 +158,10 @@

 else if device.meego()
   _addClass "meego mobile"
-
+
+else if device.nodeWebkit()
+  _addClass "node-webkit"
+
 else
   _addClass "desktop"


Change ga statitistics type to universal

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -31,7 +31,7 @@
       common.analyser = new AudioAnalyser

       # Init google analitics
-      common.ga = new GAConstructor preprocess.GA, Backbone
+      common.ga = new GAConstructor preprocess.GA, Backbone, true

       #common.sapi = new social.SocialApi


Demand six subjects from fetch

Demand six subjects from fetch

--- app/models/cyclone_subject.coffee
+++ app/models/cyclone_subject.coffee
@@ -35,6 +35,12 @@
     fetcher = $.Deferred()

     Api.get "/projects/cyclone_center/groups/subjects?limit=#{count}", (rawSubjects) =>
+      # Try again if we didn't get enough subjects.
+      if rawSubjects.length isnt count
+        console.warn "CycloneSubject got #{rawSubjects.length}, but expected #{count}. Trying again..."
+        return @fetch count
+
+      console.info "Got #{rawSubjects.length} new subjects!", rawSubjects
       newSubjects = @fromJSON rawSubject for rawSubject in rawSubjects
       fetcher.resolve newSubjects


Update editor_chat documentation

Update editor_chat documentation

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -122,8 +122,6 @@
                 date: time_stamp
             is_equal: (a, b) => (a - 0) == (b - 0)

-        #console.log("-- History:", [{author_id: sender_id, content:mesg, date:time_stamp}] )
-
         @syncdb.save()
         @setState(last_sent: mesg)

@@ -149,6 +147,8 @@
             is_equal: (a, b) => (a - 0) == (b - 0)
         @syncdb.save()

+    # Used to edit sent messages.
+    # Inefficient. Assumes number of edits is small.
     send_edit: (message, mesg) =>
         if not @syncdb?
             # TODO: give an error or try again later?


Add hideTooltip function to hide the tip right now.

--- src/space-pen-extensions.coffee
+++ src/space-pen-extensions.coffee
@@ -76,6 +76,12 @@

   this.tooltip(jQuery.extend({}, tooltipDefaults, tooltipOptions))

+jQuery.fn.hideTooltip = ->
+  tip = @data('bs.tooltip')
+  if tip
+    tip.leave(currentTarget: this)
+    tip.hide()
+
 jQuery.fn.setTooltip.getKeystroke = getKeystroke
 jQuery.fn.setTooltip.replaceModifiers = replaceModifiers


now global search map does function

now global search map does function

--- conf/couchdb/views/global_search_map.coffee
+++ conf/couchdb/views/global_search_map.coffee
@@ -1,6 +1,13 @@
 (doc) ->
+
+  Array::unique = ->
+    output = {}
+    output[@[key]] = @[key] for key in [0...@length]
+    value for key, value of output
+
+
   words = [doc.title] if(doc.title)
-  words = words.concat doc.ingredients  if(doc.ingredients)
-  words = words.concat doc.keywords  if(doc.keywords)
+  words.push(ing.name.toLowerCase()) for ing in  doc.ingredients if(doc.ingredients)
+  words.push(word.toLowerCase()) for word in doc.keywords  if(doc.keywords)

-  emit(word.toLowerCase()) for word in words.unique()+  emit word.toLowerCase(), doc.title for word in words.unique()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -69,4 +69,4 @@
   @param {GameObject} obj The object you want to follow
   ###
   follow: (obj) ->
-    I.velocity = obj.position().subtract(self.position()).norm()
+    I.velocity = obj.position().subtract(self.position()).norm(I.sp)


misc stream filter spinner tweaking

misc stream filter spinner tweaking

--- app/coffee/stream.coffee
+++ app/coffee/stream.coffee
@@ -7,7 +7,7 @@
   constructor: ->
     @stream = new StreamModel()
     @streamView = new StreamView(@stream)
-
+
     $.subscribe 'filter:change', =>
       @stream.load().then (data) =>
         @streamView.update(data.html)
@@ -24,9 +24,9 @@
     # adds the loading wheel
     #entry-filter-wrap
     $('#entry-filter, #follow-filter').change( (event) =>
-      alert(event.currentTarget.id)
-      $('#entry-filter-wrap .spinner').show()
-      $('#entry-filter-wrap').find('.spinner').show()
+      # alert(event.currentTarget.id)
+      # $('#entry-filter-wrap .spinner').show()
+      # $('#entry-filter-wrap').find('.spinner').show()
       $('#entry-filter-wrap .spinner').show() if event.currentTarget.id == '#entry_filter'
       $(event.currentTarget.id).prev().show()
     )


Remove z-index even when in style attribute, for valid SVG

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -252,6 +252,16 @@
     if style
       match = ///(?:^|;)\s*#{styleKey}\s*:\s*([^;\s][^;]*)///i.exec style
       match?[1]
+removeAttributeOrStyle = (node, attr, styleKey = attr) ->
+  node.removeAttribute attr
+  style = node.getAttribute 'style'
+  return unless style?
+  newStyle = style.replace ///(?:^|;)\s*#{styleKey}\s*:\s*([^;\s][^;]*)///i, ''
+  if style != newStyle
+    if newStyle.trim()
+      node.setAttribute 'style', newStyle
+    else
+      node.removeAttribute 'style'

 getHref = (node) ->
   for key in ['xlink:href', 'href']
@@ -270,7 +280,7 @@
   ## a z-index="..." attribute.  Check for this first.
   ## 2. Look for style="z-index:..." as in HTML.
   z = parseInt attributeOrStyle node, 'z-index'
-  node.removeAttribute 'z-index'
+  removeAttributeOrStyle node, 'z-index'
   if isNaN z
     0
   else


fixed issue with endpont

fixed issue with endpont

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -86,7 +86,7 @@
         processing = !results.Processing.length ? results.Processing.data[0][0] + results.Processing.data[0][1] : 0,
         completed = !results.Completed.length ? results.Completed.data[0][0] + results.Completed.data[0][1] : 0,
         successful = !results.Successful.length ? results.Successful.data[0][0] + results.Successful.data[0][1] : 0,
-        completedWErrors= !results.['Completed with error(s)'].length ? results.['Completed with error(s)'].data[0][0] + results.['Completed with error(s)'].data[0][1] : 0;
+        completedWErrors= !results['Completed with error(s)'].length ? results['Completed with error(s)'].data[0][0] + results['Completed with error(s)'].data[0][1] : 0;

     data.push({


event: use POST instead of GET for queries

--- src/models/event.coffee
+++ src/models/event.coffee
@@ -34,8 +34,8 @@
     q = _.pluck(queue, 'q').join '\n'

     request config.HYPERPLANE_API_URL + '/events',
-      method: 'get'
-      qs:
+      method: 'post'
+      body:
         q: q
       headers:
         Authorization: "Token #{@accessTokenStream.getValue()}"


The Player is always a circle

The Player is always a circle

--- app/models/Player.coffee
+++ app/models/Player.coffee
@@ -11,6 +11,8 @@
   animationState: [0, 1, 2, 1]

   constructor: (x, y, width, height, owningLevel, settings) ->
+    settings.ellipse = true
+
     super x, y, width/2, height/2, owningLevel, settings

     @size.x = width


Fix problems with initial tooltip code

Fix problems with initial tooltip code

--- src/tooltips.coffee
+++ src/tooltips.coffee
@@ -1,24 +1,27 @@
 # tool tips

 d3panels.tooltip_create = (selection, options) ->
-    selection.append("div")
-             .attr("class", "d3panels-tooltip")
-             .style("opacity", 0)
+    d3.select("body").append("div")
+             .attr("class", "d3panels-tooltip #{options.tipclass}")
+             .style("opacity", 1)

-d3panels.tooltip_activate = (objects, tipdiv, options) ->
-    objects.on("mouseover", (d) ->
-          tipdiv.html(d.tooltip)
+d3panels.tooltip_activate = (objects, tipdiv, options, tooltip_func) ->
+    objects.on("mouseover.d3panels-tooltip", (d,i) ->
+          mouseX = d3.event.pageX
+          mouseY = d3.event.pageY

-          h = tipdiv.node().getBoundingClientRect().height
+          tipdiv.html(tooltip_func(d,i))

-          tipdiv.style("left", (d3.event.pageX + d.r*3)+"px")
-                .style("top", (d3.event.pageY - h/2)+"px")
+          tipbox_height = tipdiv.node().getBoundingClientRect().height
+
+          tipdiv.style("left", "#{(mouseX + 10)}px")
+                .style("top", "#{(mouseY - tipbox_height/2)}px")
                 .transition()
                 .duration(0)
                 .style("opacity", 0.9))

-    objects.on("mouseout", (d) ->
+    objects.on("mouseout.d3panels-tooltip", (d) ->
               tipdiv.transition()
                   .duration(1000)
                   .style("opacity", 0))


Update server.coffee

Update server.coffee

--- app/server/server.coffee
+++ app/server/server.coffee
@@ -48,7 +48,7 @@
   # Session setup
   app.log.info 'init: setting up sessions'
   app.express.use session(
-    name: 'slaterscreens'
+    name: app.config.locals.shortname
     secret: app.config.server.session.secret
     store: new RedisStore(client: app.redis)
     resave: true


re-enable grid display

re-enable grid display

--- src/layout.coffee
+++ src/layout.coffee
@@ -38,7 +38,7 @@
   level_dict._CHILDREN = [
     header(level_dict)
     controls(level_dict)
-    # grid(level_dict)
+    grid(level_dict)
     inspector
   ]
   game_dict


Fix discussion move

Fix discussion move

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -194,7 +194,7 @@

     @discussionsRequest().update({title, sticky, locked, board_id}).save()
       .then (discussion) =>
-        if discussion[0].board_id isnt board_id
+        if discussion[0].board_id isnt @props.params.board
           {owner, name} = @props.params
           discussionRoute = if (owner and name) then 'project-talk-discussion' else 'talk-discussion'
           @transitionTo discussionRoute, merge(@props.params, board: board_id), @props.query
@@ -264,7 +264,7 @@
                   <PromiseRenderer promise={talkClient.type('boards').get({section: discussion.section, page_size: 100})}>{(boards) =>

Board:

-                      <select value={discussion.board_id}>
+                      <select defaultValue={discussion.board_id}>
                         {boards.map (board, i) =>
                           <option key={board.id} value={board.id}>{board.title}</option>
                           }


Capitalize the name

Capitalize the name

--- src/lieber.coffee
+++ src/lieber.coffee
@@ -23,4 +23,7 @@
       when 'me'
         msg.reply "You are #{msg.random phrases}"
       else
-        msg.send "#{name} is #{msg.random phrases}"
+        msg.send "#{name.capitalize()} is #{msg.random phrases}"
+
+String::capitalize = ->
+  @substr(0, 1).toUpperCase() + @substr(1)


Add key code map

Add key code map

--- src/listnr.coffee
+++ src/listnr.coffee
@@ -4,6 +4,31 @@

 removeEvent = (el, type, handler) ->
   el.removeEventListener(type, handler)
+
+
+keyCodeMap =
+  8: 'backspace'
+  9: 'tab'
+  13: 'enter'
+  16: 'shift'
+  17: 'ctrl'
+  18: 'alt'
+  20: 'capslock'
+  27: 'esc'
+  32: 'space'
+  33: 'pageup'
+  34: 'pagedown'
+  35: 'end'
+  36: 'home'
+  37: 'left'
+  38: 'up'
+  39: 'right'
+  40: 'down'
+  45: 'ins'
+  46: 'del'
+  91: 'meta'
+  93: 'meta'
+  224: 'meta'

 class Context


cleanup renderer setters

cleanup renderer setters

--- src/math3d.coffee
+++ src/math3d.coffee
@@ -247,6 +247,7 @@
             @opts.width ?= document.documentElement["clientWidth"]/2
             @opts.height ?= @opts.width*2/3

+            @opts.renderer ?= _defaultRendererType
             @setDynamicRenderer()
             @init_on_mouseover()

@@ -281,13 +282,12 @@
         parentElement.appendChild @element

     setDynamicRenderer: ->
-        if @renderer_type is 'dynamic'
+        if @rendererType is 'dynamic'
             # already have it
             return

-        @opts.renderer ?= _defaultRendererType
         @renderer = getRenderer @, @opts.renderer
-        @renderer_type = 'dynamic'
+        @rendererType = 'dynamic'

         # remove the current renderer (if it exists)
         removeElement @element.lastChild
@@ -297,39 +297,35 @@

         @renderer.setClearColor @background, 1
         @renderer.setSize @opts.width, @opts.height
-        if @controls?
-            @controls.enabled = true
-            if @last_canvas_pos?
-                @controls.object.position.copy @last_canvas_pos
-            if @last_canvas_target?
-                @controls.target.copy @last_canvas_target
+
+        @controls?.enable = true
+
         if @opts.spin
             @animate render: false
+
         @render_scene true

     setStaticRenderer: ->
-        if @renderer_type is 'static'
+        if @rendererType is 'static'
             # already have it
             return

-        if not @static_image?
-            @static_image = document.createElement 'img'
-            @static_image.className = 'math-3d-static-renderer'
-            @static_image.style.width = @opts.width
-            @static_image.style.height = @opts.height
-        @static_image.src = @data_url()
-
-        @renderer_type = 'static'
-        if @controls?
-            @controls.enabled = false
-            @last_canvas_pos = @controls.object.position
-            @last_canvas_target = @controls.target
+        @controls?.enable = false
+
+        if not @staticImage?
+            @staticImage = document.createElement 'img'
+            @staticImage.className = 'math-3d-static-renderer'
+            @staticImage.style.width = @opts.width
+            @staticImage.style.height = @opts.height
+        @staticImage.src = @dataUrl()
+
+        @rendererType = 'static'

         # remove the current renderer (if it exists)
         removeElement @element.lastChild

         # place renderer in correct place in the DOM
-        @element.appendChild @static_image
+        @element.appendChild @staticImage

     # On mouseover, we switch the renderer out to use webgl, if available, and also enable spin animation.
     init_on_mouseover: ->
@@ -343,7 +339,7 @@
         @element.onclick = =>
             @setDynamicRenderer()

-    data_url: (opts) ->
+    dataUrl: (opts) ->
         opts = defaults opts,
             type    : 'png'      # 'png' or 'jpeg' or 'webp' (the best)
             quality : undefined   # 1 is best quality; 0 is worst; only applies for jpeg or webp
@@ -789,7 +785,7 @@
         @scene.add @camera

     runChangeHooks: ->
-        if @renderer_type is 'dynamic'
+        if @rendererType is 'dynamic'
             for hook in @changeHooks
                 hook()

@@ -826,7 +822,7 @@
         @_animate opts

     _animate: (opts) ->
-        if @renderer_type is 'static'
+        if @rendererType is 'static'
             # will try again when we switch to dynamic renderer
             @_animate_started = false
             return
@@ -858,7 +854,7 @@
             f()

     render_scene: (force = false) ->
-        if @renderer_type isnt 'dynamic'
+        if @rendererType isnt 'dynamic'
             # if we don't have the renderer, swap it in, make a static image,
             # then give it back to whoever had it.
             owner = _sceneUsingRenderer


Prototypling Matrix

Prototypling Matrix

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -26,74 +26,79 @@
 ###
 ( ->
   ###*
-   *

-   *  _        _
-   * | a  c tx  |
-   * | b  d ty  |
-   * |_0  0  1 _|
-   *

-   * Creates a matrix for 2d affine transformations.
-   *
-   * concat, inverse, rotate, scale and translate return new matrices with the
-   * transformations applied. The matrix is not modified in place.
-   *
-   * Returns the identity matrix when called with no arguments.
-   * @name Matrix
-   * @param {Number} [a]
-   * @param {Number} [b]
-   * @param {Number} [c]
-   * @param {Number} [d]
-   * @param {Number} [tx]
-   * @param {Number} [ty]
-   * @constructor
+

+     _        _
+    | a  c tx  |
+    | b  d ty  |
+    |_0  0  1 _|
+

+  Creates a matrix for 2d affine transformations.
+
+  concat, inverse, rotate, scale and translate return new matrices with the
+  transformations applied. The matrix is not modified in place.
+
+  Returns the identity matrix when called with no arguments.
+
+  @name Matrix
+  @param {Number} [a]
+  @param {Number} [b]
+  @param {Number} [c]
+  @param {Number} [d]
+  @param {Number} [tx]
+  @param {Number} [ty]
+  @constructor
   ###
   Matrix = (a, b, c, d, tx, ty) ->
-    a = if a? then a else 1
-    d = if d? then d else 1
-
-    ###*
-     * @name a
-     * @fieldOf Matrix#
-    ###
-    a: a
-    ###*
-     * @name b
-     * @fieldOf Matrix#
+    __proto__: Matrix::
+    ###*
+    @name a
+    @fieldOf Matrix#
+    ###
+    a: if a? then a else 1
+
+    ###*
+    @name b
+    @fieldOf Matrix#
     ###
     b: b || 0
-    ###*
-     * @name c
-     * @fieldOf Matrix#
+
+    ###*
+    @name c
+    @fieldOf Matrix#
     ###
     c: c || 0,
-    ###*
-     * @name d
-     * @fieldOf Matrix#
-    ###
-    d: d
-    ###*
-     * @name tx
-     * @fieldOf Matrix#
+
+    ###*
+    @name d
+    @fieldOf Matrix#
+    ###
+    d: if d? then d else 1
+
+    ###*
+    @name tx
+    @fieldOf Matrix#
     ###
     tx: tx || 0
-    ###*
-     * @name ty
-     * @fieldOf Matrix#
+
+    ###*
+    @name ty
+    @fieldOf Matrix#
     ###
     ty: ty || 0

-    ###*
-     * Returns the result of this matrix multiplied by another matrix
-     * combining the geometric effects of the two. In mathematical terms,
-     * concatenating two matrixes is the same as combining them using matrix multiplication.
-     * If this matrix is A and the matrix passed in is B, the resulting matrix is A x B
-     * http://mathworld.wolfram.com/MatrixMultiplication.html
-     * @name concat
-     * @methodOf Matrix#
-     *
-     * @param {Matrix} matrix The matrix to multiply this matrix by.
-     * @returns The result of the matrix multiplication, a new matrix.
-     * @type Matrix
+  Matrix:: =
+    ###*
+    Returns the result of this matrix multiplied by another matrix
+    combining the geometric effects of the two. In mathematical terms,
+    concatenating two matrixes is the same as combining them using matrix multiplication.
+    If this matrix is A and the matrix passed in is B, the resulting matrix is A x B
+    http://mathworld.wolfram.com/MatrixMultiplication.html
+    @name concat
+    @methodOf Matrix#
+
+    @param {Matrix} matrix The matrix to multiply this matrix by.
+    @returns The result of the matrix multiplication, a new matrix.
+    @type Matrix
     ###
     concat: (matrix) ->
       Matrix(
@@ -106,16 +111,16 @@
       )

     ###*
-     * Given a point in the pretransform coordinate space, returns the coordinates of
-     * that point after the transformation occurs. Unlike the standard transformation
-     * applied using the transformPoint() method, the deltaTransformPoint() method
-     * does not consider the translation parameters tx and ty.
-     * @name deltaTransformPoint
-     * @methodOf Matrix#
-     * @see #transformPoint
-     *
-     * @return A new point transformed by this matrix ignoring tx and ty.
-     * @type Point
+    Given a point in the pretransform coordinate space, returns the coordinates of
+    that point after the transformation occurs. Unlike the standard transformation
+    applied using the transformPoint() method, the deltaTransformPoint() method
+    does not consider the translation parameters tx and ty.
+    @name deltaTransformPoint
+    @methodOf Matrix#
+    @see #transformPoint
+
+    @return A new point transformed by this matrix ignoring tx and ty.
+    @type Point
     ###
     deltaTransformPoint: (point) ->
       Point(
@@ -124,13 +129,13 @@
       )

     ###*
-     * Returns the inverse of the matrix.
-     * http://mathworld.wolfram.com/MatrixInverse.html
-     * @name inverse
-     * @methodOf Matrix#
-     *
-     * @returns A new matrix that is the inverse of this matrix.
-     * @type Matrix
+    Returns the inverse of the matrix.
+    http://mathworld.wolfram.com/MatrixInverse.html
+    @name inverse
+    @methodOf Matrix#
+
+    @returns A new matrix that is the inverse of this matrix.
+    @type Matrix
     ###
     inverse: ->
       determinant = this.a * this.d - this.b * this.c
@@ -145,44 +150,44 @@
       )

     ###*
-     * Returns a new matrix that corresponds this matrix multiplied by a
-     * a rotation matrix.
-     * @name rotate
-     * @methodOf Matrix#
-     * @see Matrix.rotation
-     *
-     * @param {Number} theta Amount to rotate in radians.
-     * @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
-     * @returns A new matrix, rotated by the specified amount.
-     * @type Matrix
+    Returns a new matrix that corresponds this matrix multiplied by a
+    a rotation matrix.
+    @name rotate
+    @methodOf Matrix#
+    @see Matrix.rotation
+
+    @param {Number} theta Amount to rotate in radians.
+    @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
+    @returns A new matrix, rotated by the specified amount.
+    @type Matrix
     ###
     rotate: (theta, aboutPoint) ->
       this.concat(Matrix.rotation(theta, aboutPoint))

     ###*
-     * Returns a new matrix that corresponds this matrix multiplied by a
-     * a scaling matrix.
-     * @name scale
-     * @methodOf Matrix#
-     * @see Matrix.scale
-     *
-     * @param {Number} sx
-     * @param {Number} [sy]
-     * @param {Point} [aboutPoint] The point that remains fixed during the scaling
-     * @type Matrix
+    Returns a new matrix that corresponds this matrix multiplied by a
+    a scaling matrix.
+    @name scale
+    @methodOf Matrix#
+    @see Matrix.scale
+
+    @param {Number} sx
+    @param {Number} [sy]
+    @param {Point} [aboutPoint] The point that remains fixed during the scaling
+    @type Matrix
     ###
     scale: (sx, sy, aboutPoint) ->
       this.concat(Matrix.scale(sx, sy, aboutPoint))

     ###*
-     * Returns the result of applying the geometric transformation represented by the
-     * Matrix object to the specified point.
-     * @name transformPoint
-     * @methodOf Matrix#
-     * @see #deltaTransformPoint
-     *
-     * @returns A new point with the transformation applied.
-     * @type Point
+    Returns the result of applying the geometric transformation represented by the
+    Matrix object to the specified point.
+    @name transformPoint
+    @methodOf Matrix#
+    @see #deltaTransformPoint
+
+    @returns A new point with the transformation applied.
+    @type Point
     ###
     transformPoint: (point) ->
       Point(
@@ -191,31 +196,31 @@
       )

     ###*
-     * Translates the matrix along the x and y axes, as specified by the tx and ty parameters.
-     * @name translate
-     * @methodOf Matrix#
-     * @see Matrix.translation
-     *
-     * @param {Number} tx The translation along the x axis.
-     * @param {Number} ty The translation along the y axis.
-     * @returns A new matrix with the translation applied.
-     * @type Matrix
+    Translates the matrix along the x and y axes, as specified by the tx and ty parameters.
+    @name translate
+    @methodOf Matrix#
+    @see Matrix.translation
+
+    @param {Number} tx The translation along the x axis.
+    @param {Number} ty The translation along the y axis.
+    @returns A new matrix with the translation applied.
+    @type Matrix
     ###
     translate: (tx, ty) ->
       this.concat(Matrix.translation(tx, ty))

   ###*
-   * Creates a matrix transformation that corresponds to the given rotation,
-   * around (0,0) or the specified point.
-   * @see Matrix#rotate
-   *
-   * @param {Number} theta Rotation in radians.
-   * @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
-   * @returns
-   * @type Matrix
-  ###
-  Matrix.rotation = (theta, aboutPoint) ->
+  Creates a matrix transformation that corresponds to the given rotation,
+  around (0,0) or the specified point.
+  @see Matrix#rotate
+
+  @param {Number} theta Rotation in radians.
+  @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
+  @returns
+  @type Matrix
+  ###
+  Matrix.rotate = Matrix.rotation = (theta, aboutPoint) ->
     rotationMatrix = Matrix(
       Math.cos(theta),
       Math.sin(theta),
@@ -234,18 +239,18 @@
     return rotationMatrix

   ###*
-   * Returns a matrix that corresponds to scaling by factors of sx, sy along
-   * the x and y axis respectively.
-   * If only one parameter is given the matrix is scaled uniformly along both axis.
-   * If the optional aboutPoint parameter is given the scaling takes place
-   * about the given point.
-   * @see Matrix#scale
-   *
-   * @param {Number} sx The amount to scale by along the x axis or uniformly if no sy is given.
-   * @param {Number} [sy] The amount to scale by along the y axis.
-   * @param {Point} [aboutPoint] The point about which the scaling occurs. Defaults to (0,0).
-   * @returns A matrix transformation representing scaling by sx and sy.
-   * @type Matrix
+  Returns a matrix that corresponds to scaling by factors of sx, sy along
+  the x and y axis respectively.
+  If only one parameter is given the matrix is scaled uniformly along both axis.
+  If the optional aboutPoint parameter is given the scaling takes place
+  about the given point.
+  @see Matrix#scale
+
+  @param {Number} sx The amount to scale by along the x axis or uniformly if no sy is given.
+  @param {Number} [sy] The amount to scale by along the y axis.
+  @param {Point} [aboutPoint] The point about which the scaling occurs. Defaults to (0,0).
+  @returns A matrix transformation representing scaling by sx and sy.
+  @type Matrix
   ###
   Matrix.scale = (sx, sy, aboutPoint) ->
     sy = sy || sx
@@ -263,35 +268,35 @@
     return scaleMatrix

   ###*
-   * Returns a matrix that corresponds to a translation of tx, ty.
-   * @see Matrix#translate
-   *
-   * @param {Number} tx The amount to translate in the x direction.
-   * @param {Number} ty The amount to translate in the y direction.
-   * @return A matrix transformation representing a translation by tx and ty.
-   * @type Matrix
-  ###
-  Matrix.translation = (tx, ty) ->
+  Returns a matrix that corresponds to a translation of tx, ty.
+  @see Matrix#translate
+
+  @param {Number} tx The amount to translate in the x direction.
+  @param {Number} ty The amount to translate in the y direction.
+  @return A matrix transformation representing a translation by tx and ty.
+  @type Matrix
+  ###
+  Matrix.translate = Matrix.translation = (tx, ty) ->
     Matrix(1, 0, 0, 1, tx, ty)

   ###*
-   * A constant representing the identity matrix.
-   * @name IDENTITY
-   * @fieldOf Matrix
+  A constant representing the identity matrix.
+  @name IDENTITY
+  @fieldOf Matrix
   ###
   Matrix.IDENTITY = Matrix()

   ###*
-   * A constant representing the horizontal flip transformation matrix.
-   * @name HORIZONTAL_FLIP
-   * @fieldOf Matrix
+  A constant representing the horizontal flip transformation matrix.
+  @name HORIZONTAL_FLIP
+  @fieldOf Matrix
   ###
   Matrix.HORIZONTAL_FLIP = Matrix(-1, 0, 0, 1)

   ###*
-   * A constant representing the vertical flip transformation matrix.
-   * @name VERTICAL_FLIP
-   * @fieldOf Matrix
+  A constant representing the vertical flip transformation matrix.
+  @name VERTICAL_FLIP
+  @fieldOf Matrix
   ###
   Matrix.VERTICAL_FLIP = Matrix(1, 0, 0, -1)


norm with optional value

norm with optional value

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -158,8 +158,8 @@
      * @returns The unit vector pointing in the same direction as this vector.
      * @type Point
     ###
-    norm: ->
-      this.scale(1.0/this.length())
+    norm: (length=1.0) ->
+      this.scale(length/this.length())

     ###*
      * Computed the length of this point as though it were a vector from (0,0) to (x,y)


Mangle all the things.

Mangle all the things.

--- src/minify.coffee
+++ src/minify.coffee
@@ -3,7 +3,8 @@
     esmangle  = require 'esmangle'
     escodegen = require 'escodegen'

-    compressed = esmangle.mangle (esmangle.optimize ast),
+    optimized = esmangle.optimize ast, null, destructive: yes
+    mangled   = esmangle.mangle optimized,
       destructive: yes

     escodegen.generate compressed,
@@ -12,12 +13,12 @@
         indent:
           style: ''
           base: 0
+        compact: yes
+        escapeless: yes
+        hexadecimal: yes
+        parentheses: no
+        quotes: 'auto'
         renumber: yes
-        hexadecimal: yes
-        quotes: 'auto'
-        escapeless: yes
-        compact: yes
-        parentheses: no
         semicolons: no

   uglify: (ast) ->


fixes artwork column bug where last column falls underneath

--- components/artwork_columns/view.coffee
+++ components/artwork_columns/view.coffee
@@ -51,7 +51,9 @@

   _columnWidth: ->
     width = @totalWidth or @$el.width()
-    Math.floor((width - ((@numberOfColumns - 1) * @gutterWidth)) / @numberOfColumns)
+    colWidth = Math.floor((width - ((@numberOfColumns - 1) * @gutterWidth)) / @numberOfColumns)
+    totalWidth = (colWidth * @numberOfColumns) + ((@numberOfColumns - 1) * @gutterWidth)
+    return (if totalWidth <= @$el.innerWidth() then colWidth - 1 else colWidth)

   sizeColumns: =>
     margin = Math.floor(@gutterWidth / 2)


save against empty result

save against empty result

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -84,7 +84,7 @@
     data.updated_at = moment().format()
     data.author_id = ObjectId data.author_id
     db.articles.update { _id: id }, data, { upsert: true }, (err, res) ->
-      callback err, _.extend data, _id: res.upserted?[0]._id or id
+      callback err, _.extend data, _id: res.upserted?[0]?._id or id

 @destroy = (id, callback) ->
   db.articles.remove { _id: ObjectId(id) }, (err, res) ->


Add a comment for Frame expando fix

Add a comment for Frame expando fix

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -221,7 +221,7 @@
     @port = chrome.runtime.connect name: "frames"

     @port.onMessage.addListener (request) =>
-      root.extend window, root unless extend?
+      root.extend window, root unless extend? # See #2800 and #2831.
       (@listeners[request.handler] ? this[request.handler]) request

     # We disable the content scripts when we lose contact with the background page, or on unload.


Fix bug

Fix bug

--- index.coffee
+++ index.coffee
@@ -178,7 +178,7 @@
   context.message.send('to start a new game just type "start"')

-resolve = (message)->
+resolve = (bot_message)->
   strategies = [
     tutorial_strategy
     start_game_strategy


fixes:

fixes:

1. don't always fork a pty on startup
2. 2 NPEs
3. fix argument passing

--- index.coffee
+++ index.coffee
@@ -132,7 +132,7 @@
     atom.packages.activatePackage('tree-view').then (treeViewPkg) =>
       node = new ListView()
       treeViewPkg.mainModule.treeView.find(".tree-view-scroller").prepend node
-      @newTerm()
+      # @newTerm()

   getTerminals: =>
     Terminals.map (t) ->
@@ -200,7 +200,7 @@
       splitter()

   newTerm: (forkPTY=true, rows=30, cols=80, title='tty') ->
-    termView = @createTermView {forkPTY, rows, cols}
+    termView = @createTermView forkPTY, rows, cols
     pane = atom.workspace.getActivePane()
     model = Terminals.add {
       local: !!forkPTY,
@@ -212,7 +212,9 @@
     disposable = pane.onDidChangeActiveItem ->
       activeItem = pane.getActiveItem()
       if activeItem != termView
-        return termView.term.constructor._textarea = null
+        if termView.term
+          termView.term.constructor._textarea = null
+        return

       process.nextTick ->
         termView.focus()
@@ -221,7 +223,8 @@
         # but, it is the constructor instead of the instance!!!1 - probably to avoid having to bind this as a premature
         # optimization.
         atomPane = activeItem.parentsUntil("atom-pane").parent()[0]
-        termView.term.constructor._textarea = atomPane
+        if termView.term
+          termView.term.constructor._textarea = atomPane

     id = model.id
     termView.id = id


Change to use kb-bindings events instead of listening for keydown ourselves

Keybindings are now centrally handled using: https://github.com/deathcap/kb-bindings
This allows us to listen for the events using their (semantic) binding name,
and trigger only when the key is first pressed. Much cleaner since now only one
DOM keydown event listener is needed (in kb-bindings) and all the keybinding
definitions can be managed there (if managed by the end user, for example).

Requires https://github.com/deathcap/voxel-engine/commit/791c74ad818db9ed96475dd1e812debd709a8a1b

--- index.coffee
+++ index.coffee
@@ -47,11 +47,34 @@
     chunkDistance: 2
     materials: []  # added dynamically later
     texturePath: 'AssetPacks/ProgrammerArt/textures/blocks/' # subproject with textures
-    worldOrigin: [0, 0, 0],
+    worldOrigin: [0, 0, 0]
     controls:
       discreteFire: false
       fireRate: 100 # ms between firing
       jumpTimer: 25
+    keybindings:
+      # voxel-engine defaults
+      'W': 'forward'
+      'A': 'left'
+      'S': 'backward'
+      'D': 'right'
+      '<up>': 'forward'
+      '<left>': 'left'
+      '<down>': 'backward'
+      '<right>': 'right'
+      '<mouse 1>': 'fire'
+      '<mouse 3>': 'firealt'
+      '<space>': 'jump'
+      '<shift>': 'crouch'
+      '<control>': 'alt'
+      '<tab>': 'sprint'
+
+      # our extras
+      'R': 'pov'
+      'T': 'vr'
+      'O': 'home'
+      'E': 'inventory'
+      'C': 'gamemode'
     }

   # add lighting - based on voxel-engine addLights()
@@ -203,11 +226,6 @@
     adjacentActive: () -> false   # don't hold <Ctrl> for block placement (right-click instead, 'reach' plugin)
   }

-
-  haveMouseInteract = false
-  game.interact.on 'attain', () => haveMouseInteract = true
-  game.interact.on 'release', () => haveMouseInteract = false
-
   # one of everything, please..
   creativeInventoryArray = []
   for props in registry.blockProps
@@ -215,42 +233,28 @@

   survivalInventoryArray = []

-  ever(document.body).on 'keydown', (ev) =>
-    return if !haveMouseInteract    # don't care if typing into a GUI, etc. TODO: use kb-controls here too? (like voxel-engine)
-
-    if ev.keyCode == 'R'.charCodeAt(0)
-      # toggle between first and third person
-      avatar.toggle()
-      # TODO: disable/re-enable voxel-walk in 1st/3rd person?
-    else if ev.keyCode == 'T'.charCodeAt(0)
-      game.plugins.toggle 'oculus'
-    else if ev.keyCode == 'O'.charCodeAt(0)
-      home(avatar)
-    else if ev.keyCode == 'E'.charCodeAt(0)
-      inventoryDialog.toggle()
-    else if ev.keyCode == 'C'.charCodeAt(0)
-      # TODO: add gamemode event? for plugins to handle instead of us
-      if game.mode == 'survival'
-        game.mode = 'creative'
-        game.plugins.enable 'fly'
-        mine.instaMine = true
-        survivalInventoryArray = inventoryHotbar.inventory.array
-        inventoryHotbar.inventory.array = creativeInventoryArray
-        inventoryHotbar.refresh()
-        console.log 'creative mode'
-      else
-        game.mode = 'survival'
-        game.plugins.disable 'fly'
-        mine.instaMine = false
-        inventoryHotbar.inventory.array = survivalInventoryArray
-        inventoryHotbar.refresh()
-        console.log 'survival mode'
-
-  # cancel context-menu on right-click
-  ever(document.body).on 'contextmenu', (event) ->
-    event.preventDefault()
-    return false
-
+  game.buttons.down.on 'pov', () -> avatar.toggle() # TODO: disable/re-enable voxel-walk in 1st/3rd person?
+  game.buttons.down.on 'vr', () -> game.plugins.toggle 'oculus'
+  game.buttons.down.on 'home', () -> home(avatar)
+  game.buttons.down.on 'inventory', () -> inventoryDialog.toggle()
+  game.buttons.down.on 'gamemode', () ->
+    # TODO: add gamemode event? for plugins to handle instead of us
+    if game.mode == 'survival'
+      game.mode = 'creative'
+      game.plugins.enable 'fly'
+      mine.instaMine = true
+      survivalInventoryArray = inventoryHotbar.inventory.array
+      inventoryHotbar.inventory.array = creativeInventoryArray
+      inventoryHotbar.refresh()
+      console.log 'creative mode'
+    else
+      game.mode = 'survival'
+      game.plugins.disable 'fly'
+      mine.instaMine = false
+      inventoryHotbar.inventory.array = survivalInventoryArray
+      inventoryHotbar.refresh()
+      console.log 'survival mode'
+
   # right-click to place block
   reach.on 'interact', (target) =>
     if not target


Input parsed improved

Input parsed improved

--- input.coffee
+++ input.coffee
@@ -11,7 +11,7 @@
 	@__path__ = 'File.Input'

 	RE = @RE = new RegExp '([^#]*)#{([^}]*)}([^#]*)', 'gm'
-	VAR_RE = @VAR_RE = ///(^[a-z][a-z0-9]*)|(?:\[)([a-z][a-z0-9]*)(?:\])///gi
+	VAR_RE = @VAR_RE = ///(^|\s|\[|:)([a-z]\w*)+///gi

 	@Text = require('./input/text.coffee') File, @
 	@Attr = require('./input/attr.coffee') File, @
@@ -27,10 +27,9 @@
 		while (match = RE.exec text) isnt null

 			# parse prop
-			prop = match[2].replace VAR_RE, (_, elem1, elem2, index) ->
-				str = "get(storages, '#{escape(elem1 or elem2)}')"
-				if index then str = "[#{str}]"
-				str
+			prop = match[2].replace VAR_RE, (_, prefix, elem) ->
+				str = "get(storages, '#{escape(elem)}')"
+				"#{prefix}#{str}"

 			# add into func string
 			if match[1] then func += "'#{utils.addSlashes match[1]}' + "


Add webhook for github

Add webhook for github


heartbeatTimeout = 30s

heartbeatTimeout = 30s

--- client/Main/kite/kodingkontrol.coffee
+++ client/Main/kite/kodingkontrol.coffee
@@ -13,6 +13,8 @@
       type          : 'sessionID'
       key           : Cookies.get 'clientId'
     transportClass  : SockJS
+    transportOptions:
+      heartbeatTimeout: 30 * 1000 # 30 seconds

   reauthenticate: ->
     # disconnect the old kontrol kite


shortcuts/modal: display first tab

shortcuts/modal: display first tab

--- client/app/lib/shortcuts/modal.coffee
+++ client/app/lib/shortcuts/modal.coffee
@@ -42,7 +42,7 @@
             collection  : collection
         }

-    #tabView.showPaneByIndex 0
+    tabView.showPaneByIndex 0

     buttonBar = new kd.View
       cssClass: 'buttons'


Add documentation and use string interpolation

--- client/views/events/event_show.coffee
+++ client/views/events/event_show.coffee
@@ -42,6 +42,8 @@

   'url': ->
     text = encodeURIComponent(@name)
+    # Convert ISOStrings to Google Calendar date format
+    # https://support.google.com/calendar/answer/3033039
     from = moment(@from)
       .toISOString()
       .replace(/[\-\.\:]/g, '')
@@ -52,18 +54,7 @@
       .replace('000Z', 'Z')
     details = encodeURIComponent(@description)
     location = encodeURIComponent(@location)
-    url =
-      "http://www.google.com/calendar/event?action=TEMPLATE&text=" +
-      text +
-      "&dates=" +
-      from +
-      "/" +
-      to +
-      "&details=" +
-      details +
-      "&location=" +
-      location +
-      "&trp=true&sprop=events%40penn&sprop=name:eventsatpenn.com"
+    url = "http://www.google.com/calendar/event?action=TEMPLATE&text=#{ text }&dates=#{ from }/#{ to }&details=#{ details }&location=#{ location }&trp=true&sprop=events%40penn&sprop=name:eventsatpenn.com"
     url


Add custom notification for ELOOP save errors

Closes #9242

--- src/pane.coffee
+++ src/pane.coffee
@@ -722,7 +722,7 @@
       @notificationManager.addWarning("Unable to save file: #{error.message}")
     else if error.code is 'EACCES'
       addWarningWithPath('Unable to save file: Permission denied')
-    else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST']
+    else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST', 'ELOOP']
       addWarningWithPath('Unable to save file', detail: error.message)
     else if error.code is 'EROFS'
       addWarningWithPath('Unable to save file: Read-only file system')


Add comments to source.

Add comments to source.

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -1,8 +1,13 @@
-# rivets.js
-# version : 0.1.2
-# author : Michael Richards
-# license : MIT
+#     rivets.js
+#     version : 0.1.2
+#     author : Michael Richards
+#     license : MIT

+# Registers a specific binding routine between a model object and a DOM element.
+# All information for that routine is passed in when calling this function; the
+# specific element to bind to, an adapter interface for the model object, the
+# type of binding, the context object and the keypath at which to subscribe to
+# on the model object.
 registerBinding = (el, adapter, type, context, keypath) ->
   bind = bindings[type] || attributeBinding type
   bind el, adapter.read context, keypath
@@ -14,17 +19,24 @@
     el.addEventListener 'change', ->
       adapter.publish context, keypath, getInputValue this

+# Returns the current input value for the specified element.
 getInputValue = (el) ->
   switch el.type
     when 'text', 'textarea', 'password', 'select-one' then el.value
     when 'checkbox', 'radio' then el.checked

+# Returns an attribute binding routine for the specified attribute. This is what
+# `registerBinding` falls back to when there is no routine for the binding type.
 attributeBinding = (attr) -> (el, value) ->
   if value then el.setAttribute attr, value else el.removeAttribute attr

+# Returns a state binding routine for the specified attribute. Can optionally be
+# negatively evaluated. This is used to build a lot of the core state binding
+# routines.
 stateBinding = (attr, inverse = false) -> (el, value) ->
   attributeBinding(attr) el, if inverse is !value then attr else false

+# Core binding routines.
 bindings =
   checked:
     stateBinding 'checked'
@@ -49,8 +61,11 @@
   hide: (el, value) ->
     el.style.display = if value then 'none' else ''

+# Bindings that should also be observed for changes on the DOM element in order
+# to propogate those changes back to the model object.
 bidirectionals = ['value', 'checked', 'unchecked', 'selected', 'unselected']

+# The rivets module. Exposes a single `bind` function.
 rivets =
   bind: (el, adapter, contexts = {}) ->
     for node in el.getElementsByTagName '*'
@@ -62,6 +77,7 @@
           keypath = path.join '.'
           registerBinding node, adapter, type, contexts[context], keypath

+# Exports rivets for both CommonJS and the browser.
 if module?
   module.exports = rivets
 else


runner: Fix indent, add debug

runner: Fix indent, add debug

--- src/runner.coffee
+++ src/runner.coffee
@@ -84,11 +84,12 @@

     received = {}
     onReceived = (port, data) =>
-        received[port] = data
-        nExpected = Object.keys(testcase.expect).length
-        if Object.keys(received).length == nExpected
-          @client.removeListener 'runtime', checkPacket
-          return callback null, received
+      debug 'runtest got output on', port
+      received[port] = data
+      nExpected = Object.keys(testcase.expect).length
+      if Object.keys(received).length == nExpected
+        @client.removeListener 'runtime', checkPacket
+        return callback null, received

     checkPacket = (msg) =>
       d = msg.payload


DYALOG_NO_SM_SH=1 in a spawned interpreter's env

--- proxy.coffee
+++ proxy.coffee
@@ -221,7 +221,8 @@
           log "listening for connections from spawned interpreter on #{hp}"
           exe = process.env.DYALOG_IDE_INTERPRETER_EXE || 'dyalog'
           log "spawning interpreter #{JSON.stringify exe}"
-          child = spawn exe, ['+s', '-q'], stdio: ['pipe', 'ignore', 'ignore'], env: extend process.env, RIDE_CONNECT: hp, RIDE_INIT: "CONNECT:#{hp}"
+          child = spawn exe, ['+s', '-q'], stdio: ['pipe', 'ignore', 'ignore'], env: extend process.env,
+            RIDE_CONNECT: hp, RIDE_INIT: "CONNECT:#{hp}", DYALOG_NO_SM_SH: '1'
           toBrowser '*spawned', pid: child.pid
           child.on 'error', (err) ->
             server?.close(); server = client = null


minor syntax

minor syntax

--- public/js/main.coffee
+++ public/js/main.coffee
@@ -1,3 +1,3 @@
-Game = require('./Game.coffee')
+Game = require './Game'

 g = new Game()


Added event document type.

Added event document type.

--- docpad.coffee
+++ docpad.coffee
@@ -80,6 +80,10 @@
 		posts: (database) ->
 			database.findAllLive({relativeOutDirPath:'posts'},[date:-1])

+		# This one, will fetch in all documents that have the tag "event" specified in their meta data
+		events: (database) ->
+			database.findAllLive({relativeOutDirPath:'events'},[date:-1])
+

 	# =================================
 	# DocPad Events


:bug:Fix level loader tests

:bug:Fix level loader tests

Busted by
https://github.com/codecombat/codecombat/commit/5d7687dc94c22586c9af78ab
82a4a9b283ccdd25

--- test/app/lib/LevelLoader.spec.coffee
+++ test/app/lib/LevelLoader.spec.coffee
@@ -143,7 +143,7 @@
     jasmine.Ajax.requests.sendResponses(responses)
     requests = jasmine.Ajax.requests.all()
     urls = (r.url for r in requests)
-    expect('/db/thang.type/mace/version?project=name,components,original,rasterIcon,kind,prerenderedSpriteSheetData' in urls).toBeTruthy()
+    expect('/db/thang.type/mace/version?project=name,components,original,rasterIcon,raster,kind,prerenderedSpriteSheetData' in urls).toBeTruthy()

   it 'loads components which are inherited by level thangs from thang type default components', ->
     new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'})
@@ -167,7 +167,7 @@
     jasmine.Ajax.requests.sendResponses(responses)
     requests = jasmine.Ajax.requests.all()
     urls = (r.url for r in requests)
-    expect('/db/thang.type/wand/version?project=name,components,original,rasterIcon,kind,prerenderedSpriteSheetData' in urls).toBeTruthy()
+    expect('/db/thang.type/wand/version?project=name,components,original,rasterIcon,raster,kind,prerenderedSpriteSheetData' in urls).toBeTruthy()

   it 'loads components for item thang types which are inherited by level thangs from thang type default equips component configs', ->
     new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'})
@@ -175,7 +175,7 @@
     responses =
       '/db/level/id': levelWithShaman
       '/db/thang.type/names': [thangTypeShamanWithWandEquipped]
-      '/db/thang.type/wand/version?project=name,components,original,rasterIcon,kind': thangTypeWand
+      '/db/thang.type/wand/version?project=name,components,original,rasterIcon,raster,kind': thangTypeWand

     jasmine.Ajax.requests.sendResponses(responses)
     requests = jasmine.Ajax.requests.all()


Remove comment.

Remove comment.

--- app/client/js/react/app.coffee
+++ app/client/js/react/app.coffee
@@ -29,7 +29,6 @@

       componentDidUpdate: ->
         @ensureAllAnchorsHaveTouchAction_()
-        # touch-action="none"

       # http://www.polymer-project.org/platform/pointer-events.html#basic-usage
       ensureAllAnchorsHaveTouchAction_: ->


add userpilot identify in sign-up process

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -206,6 +206,7 @@
             user = @model.make_model("users", response.data)
             @.setToken(user.auth_token)
             @.setUser(user)
+            @userpilot.identify(true)
             @rootscope.$broadcast("auth:register", user)
             return user


Disable fitsviewer button

Disable fitsviewer button

--- app/controllers/examine.coffee
+++ app/controllers/examine.coffee
@@ -31,8 +31,8 @@

   render: =>
     @html require('views/examine/examine')(@)
-    if @checkBrowserFeatures()
-      $('#controls').html require('views/examine/fitsviewer')()
+    #if @checkBrowserFeatures()
+    #  $('#controls').html require('views/examine/fitsviewer')()

   info: (key, values...) =>
     value = values.shift()


Add assertion.

Add assertion.

--- test/unit/login7-payload-test.coffee
+++ test/unit/login7-payload-test.coffee
@@ -10,9 +10,9 @@
     database: 'db'
   })

+  test.ok(payload.data)
+  #assertPayload(test, payload)
   console.log(payload.toString(''))
-
-  #assertPayload(test, payload)

   test.done()


Increase regional map marker opacity

Increase regional map marker opacity

--- app/scripts/dcplotsctrl.coffee
+++ app/scripts/dcplotsctrl.coffee
@@ -631,7 +631,7 @@
           [lng,lat] = x.location.coordinates
           m = L.circleMarker L.latLng(lat,lng),
             stroke: false
-            opacity: 0.8
+            opacity: 0.9
             fillColor: statusColor(x.status_group)

           html = makePopup(x)


Forgot to set checkpoint back :sleeping:

--- app/scripts/scenes/Game.coffee
+++ app/scripts/scenes/Game.coffee
@@ -10,7 +10,7 @@
   level.start()

   options =
-    startAtCheckpoint: data.checkpoint ? 5
+    startAtCheckpoint: data.checkpoint ? 0

   if data.checkpoint
     label = "Checkpoint #{data.checkpoint}"


commented Tile class

commented Tile class

--- src/Tile.coffee
+++ src/Tile.coffee
@@ -1,14 +1,21 @@
 ###
 	Tile can move the player to different map locations
+
+	Custom properties:
+	==================
+	times   "activeTime,inactiveTime" comma strings in millis
+	delay   "delay" in millis
 ###
 _ = require 'lodash'

 Entity = require './Entity.coffee'

 class Tile extends Entity
+
 	constructor: (@game, @layer, @data) ->
 		super @game, @layer, @data

+		# Grab timer properties from Tiled object data
 		if @data.properties.times?
 			times = @data.properties.times.split ','
 			@activeTime = times[0]
@@ -17,16 +24,24 @@
 			@delay = @data.properties.delay || 0
 			@startTimer()

+			# To handle window blurs messing up timeOuts, stop all
+			# timers on window exit and readd them on focus
 			window.addEventListener 'focus', @startTimer
 			window.addEventListener 'blur', @stopTimer
 		return @

+	###
+	Player should die if the tile is deactivated when they touch it
+	###
 	onPlayerTouch: (player) =>
 		@player = player
 		if @deactivated
 			@killPlayer()
 		return true

+	###
+	Kill the sprite and if the player is on this tile, them too X(
+	###
 	deactivate: () =>
 		super

@@ -34,14 +49,23 @@
 			@killPlayer()
 			return

-		setTimeout @reactivate, @inactiveTime
+		# Ignite the timer to reactivate the tile
+		@timerID = setTimeout @reactivate, @inactiveTime
 		return

+	###
+	Re add the sprite to the scene and make the tile walkable
+	###
 	reactivate: () =>
 		super
-		setTimeout @deactivate, @activeTime
+
+		# Ignite the timer to deactivate the tile
+		@timerID = setTimeout @deactivate, @activeTime
 		return

+	###
+	Player dies
+	###
 	killPlayer: () =>
 		console.log 'you fell into blissful eternity'

@@ -49,10 +73,16 @@
 		app.death @
 		return

+	###
+	Builds a timer for deactivating the tile
+	###
 	startTimer: () =>
 		@timerID = setTimeout @deactivate, (parseInt @activeTime) + (parseInt @delay)
 		return

+	###
+	Kills the timer, if any is set
+	###
 	stopTimer: () =>
 		if @timerID?
 			clearTimeout @timerID


amqp: Implement queue introspection

amqp: Implement queue introspection

--- src/amqp.coffee
+++ src/amqp.coffee
@@ -1,13 +1,14 @@

 debug = require('debug')('msgflo:amqp')
 async = require 'async'
+uuid = require 'uuid'
+
 interfaces = require './interfaces'

 try
   amqp = require 'amqplib/callback_api'
 catch e
   amqp = e
-

 class Client extends interfaces.MessagingClient
   constructor: (@address, @options={}) ->
@@ -149,9 +150,51 @@
     @channel.sendToQueue 'fbp', data
     return callback null

+
+dataSubscriptionQueueName = (id) ->
+  throw new Error("Missing id") if not id
+  return ".msgflo-broker-subscriptions-#{id}"
+
+bindingId = (b) ->
+  return "[#{b.src}]->[#{b.tgt}]"
+
 class MessageBroker extends Client
   constructor: (address, options) ->
     super address, options
+    @options.id = uuid.v4() if not @options.id
+    @subscriptions = {}
+
+  connect: (callback) ->
+    super (err) =>
+      return callback err if err
+      # create queue for data subscriptions
+      name = dataSubscriptionQueueName @options.id
+      options =
+        exclusive: true
+        durable: false
+        autoDelete: true
+      @channel.assertQueue name, options, (err) =>
+        return callback err if err
+        onSubscribedQueueData = (message) =>
+          exchange = message.fields.exchange
+          debug 'broker subscriber got message on exchange', exchange
+          matches = Object.keys(@subscriptions).filter (id) =>
+            sub = @subscriptions[id]
+            # XXX: how to account for which queue the message is for
+            # can we create some identifier when we subscribe?
+            return sub?.binding.src == exchange
+          for id in matches
+            sub = @subscriptions[id]
+            data = message.content
+            try
+              data = JSON.parse message.content.toString()
+            catch e
+              null
+            sub.handler sub.binding, data
+
+        @channel.consume name, onSubscribedQueueData, {}, (err) ->
+          debug 'broker created subscription queue', err
+          return callback err

   addBinding: (binding, callback) ->
     # TODO: support roundrobin type
@@ -197,12 +240,32 @@
     return callback null, []

   # Data subscriptions
-  subscribeData: (binding, datahandler, callback) -> # TODO: implement
-    return callback null
-  unsubscribeData: (binding, datahandler, callback) -> # TODO: implement
-    return callback null
-  listSubscriptions: (callback) -> # TODO: implement
-    return callback null, []
+  subscribeData: (binding, datahandler, callback) ->
+    exchange = binding.src
+    queue = dataSubscriptionQueueName @options.id
+    options =
+      autoDelete: true
+    @channel.bindQueue queue, exchange, '', options, (err) =>
+      return callback err if err
+      id = bindingId binding
+      @subscriptions[id] =
+        binding: binding
+        handler: datahandler
+      return callback null
+
+  unsubscribeData: (binding, datahandler, callback) ->
+    # TODO: also remove the subscription with broker
+    id = bindingId binding
+    delete @subscriptions[id]
+    return callback null
+
+  listSubscriptions: (callback) ->
+    # Is there a way to get this information through AMQP?
+    # Or do need to use RabbitMQ HTTP API?
+    subs = []
+    for id, sub of @subscriptions
+      subs.push sub.binding
+    return callback null, subs

   # Participant registration
   subscribeParticipantChange: (handler) ->


Add documentation to source code.

Add documentation to source code.

--- src/bang.coffee
+++ src/bang.coffee
@@ -4,10 +4,15 @@
 {exec}  = require "child_process"
 os      = require "os"

+# Bang is program for storing and retrieving text snippets
+# on the command line.
 module.exports = class Bang
+  # Initializes Bang's data store.
   constructor: ->
     @data = @getData()

+  # Entry point for the CLI. Processes arguments and delegates
+  # to the appropriate methods.
   start: (args) ->
     program = new Command

@@ -30,8 +35,10 @@
       else
         @list()

+  # Data is persisted to disk at ~/.bang.
   dataPath: process.env.HOME + "/.bang"

+  # Loads an existing data store or creates a new one.
   getData: ->
     return @data if @data

@@ -40,12 +47,15 @@
     else
       {}

+  # Wraps console.log for testing purposes.
   log: (args...) ->
     console.log args...

+  # Writes the data store to disk as JSON.
   save: ->
     fs.writeFileSync @dataPath, JSON.stringify(@data)

+  # Retrieve a key's value.
   get: (key) ->
     value = @data[key]
     return unless value
@@ -53,16 +63,19 @@
     @log value
     value

+  # Set the value of a key.
   set: (key, value) ->
     @data[key] = value
     @save()
     this

+  # Delete a key.
   delete: (key) ->
     delete @data[key]
     @save()
     this

+  # List all keys and their values.
   list: ->
     amount = 0

@@ -74,6 +87,7 @@

     this

+  # Copy a value to the clipboard on Mac OS X and Linux.
   copy: (value) ->
     copyCommand = if os.type().match /darwin/i then "pbcopy" else "xclip -selection clipboard"
     exec "printf '#{value.replace(/\'/g, "\\'")}' | #{copyCommand}", (error, stdout, stderr) ->
@@ -81,6 +95,7 @@

     this

+  # Pad a string by the given amount to line up items nicely.
   pad: (item, amount) ->
     out = ""
     i = amount - item.length


db -- emit 'disconnect' on disconnect

db -- emit 'disconnect' on disconnect

--- src/smc-hub/postgres-base.coffee
+++ src/smc-hub/postgres-base.coffee
@@ -70,7 +70,7 @@
         # no password file
         return

-class exports.PostgreSQL extends EventEmitter    # emits a 'connect' event whenever we successfully connect to the database.
+class exports.PostgreSQL extends EventEmitter    # emits a 'connect' event whenever we successfully connect to the database and 'disconnect' when connection to postgres fails
     constructor: (opts) ->
         opts = defaults opts,
             host            : process.env['PGHOST'] ? 'localhost'    # or 'hostname:port'
@@ -227,6 +227,7 @@
                 if @_notification?
                     client.on('notification', @_notification)
                 client.on 'error', (err) =>
+                    @emit('disconnect')
                     dbg("error -- #{err}")
                     client?.end()
                     client?.removeAllListeners()


I think this opts.table delete is not needed

--- src/smc-hub/postgres-base.coffee
+++ src/smc-hub/postgres-base.coffee
@@ -305,7 +305,6 @@
             if misc.is_array(opts.select)
                 opts.select = (quote_field(field) for field in opts.select).join(',')
             opts.query = "SELECT #{opts.select} FROM \"#{opts.table}\""
-            #delete opts.table    # TODO/Q: why is it deleted?
             delete opts.select

         push_param = (param, type) ->


Fix chat open bug

Fix chat open bug

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -276,11 +276,11 @@
     restore_scroll_position: ->
         saved_scroll = @props.opened_file?.get('component')?.scroll_position
         if saved_scroll?
-            @refs.editor_container.scrollTop = saved_scroll
+            @refs.editor_inner_container.scrollTop = saved_scroll

     save_scroll_position: ->
-        if @refs.editor_container? and @props.save_scroll?
-            val = @refs.editor_container.scrollTop
+        if @refs.editor_inner_container? and @props.save_scroll?
+            val = @refs.editor_inner_container.scrollTop
             @props.save_scroll(val)

     render_editor: (path) ->
@@ -290,7 +290,7 @@
         if not Editor?
             <Loading />
         else
-

+

                 <Editor
                     name         = {redux_name}
                     path         = {path}


[CHORE] fix whitespace in filters.coffee

--- test/filters.coffee
+++ test/filters.coffee
@@ -83,7 +83,7 @@
         { name: "sirlantis" }
       ])

-  describe "map", ->
+  describe "map", ->
     it "maps/collects an array on a given property", ->
       expect(@filters.map([1, 2, 3])).to.deep.equal([1, 2, 3])
       expect(@filters.map([
@@ -94,7 +94,7 @@

   describe "escape", ->
     it "escapes strings", ->
-      expect(@filters.escape("")).to.equal "<strong>"
+      expect(@filters.escape("")).to.equal "<strong>"

   describe "escape_once", ->
     it "returns an escaped version of html without affecting existing escaped entities", ->
@@ -105,11 +105,11 @@
     it "strip html from string", ->
       expect(@filters.strip_html("
test
")).to.equal "test"
       expect(@filters.strip_html("
test
")).to.equal "test"
-
+
       expect(@filters.strip_html(
         ""
       )).to.equal ""
-
+
       expect(@filters.strip_html(
         ""
       )).to.equal ""


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -12,7 +12,9 @@
     health: 0
     maxHealth: 110

-  obj.
+  obj.include Metered
+
+  obj.me

 test "should set max<Attribute> if it isn't present in the including object", ->
   obj = GameObject


movable velocity and acceleration getters

--- test/movable.coffee
+++ test/movable.coffee
@@ -27,7 +27,7 @@

   ok object.velocity()

-test "should be able to get velocity", ->
+test "should be able to get acceleration", ->
   object = GameObject()

   ok object.velocity()


ISSUE-47 Add tests for JSON, JSONP

ISSUE-47 Add tests for JSON, JSONP

--- tests/routes.coffee
+++ tests/routes.coffee
@@ -113,3 +113,43 @@
     # c.head '/', (err,res) -> t.equal 5, res.body, undefined
     c.head '/', (err,res) -> t.equal 5, res.body, ''
     c.patch '/', (err,res) -> t.equal 6, res.body, 'patched'
+
+  json: (t) ->
+    t.expect 2
+    t.wait 3000
+
+    zapp = zappa port++, ->
+
+      @get '/', -> @json {attr1: 'attr1', attr2: 'attr2'}
+
+    c = t.client zapp.server
+    c.get '/', (err, res) ->
+        t.equal 1, res.headers['content-type'], 'application/json; charset=utf-8'
+        t.equal 2, res.body, '{\n  "attr1": "attr1",\n  "attr2": "attr2"\n}'
+
+  jsonp: (t) ->
+    t.expect 2
+    t.wait 3000
+
+    zapp = zappa port++, ->
+
+      @get '/', -> @jsonp {attr1: 'attr1', attr2: 'attr2'}
+
+    c = t.client zapp.server
+    c.get '/?callback=foo', (err, res) ->
+        t.equal 1, res.headers['content-type'], 'text/javascript; charset=utf-8'
+        t.equal 2, res.body, 'foo && foo({\n  "attr1": "attr1",\n  "attr2": "attr2"\n});'
+
+  'jsonp + custom callback': (t) ->
+    t.expect 2
+    t.wait 3000
+
+    zapp = zappa port++, ->
+
+      @set 'jsonp callback name': 'cb'
+      @get '/', -> @jsonp {attr1: 'attr1', attr2: 'attr2'}
+
+    c = t.client zapp.server
+    c.get '/?cb=foo', (err, res) ->
+        t.equal 1, res.headers['content-type'], 'text/javascript; charset=utf-8'
+        t.equal 2, res.body, 'foo && foo({\n  "attr1": "attr1",\n  "attr2": "attr2"\n});'


US632 - fix bug where error label was not changing appropriately

--- c3/app/assets/javascripts/circs/s2_report_changes/g10_startedEmploymentAndOngoing.coffee
+++ c3/app/assets/javascripts/circs/s2_report_changes/g10_startedEmploymentAndOngoing.coffee
@@ -18,6 +18,25 @@
     currentText = $("div[class='validation-summary'] a[href='#" + howOften + "']").text().trim()
     existingError = currentText.substring(howOftenYText.length, currentText.length)
     $("div[class='validation-summary'] a[href='#" + howOften + "']").text(howOftenNText + existingError)
+    currentText = $("div[class='validation-summary'] a[href='#" + usuallyPaidSameAmount + "']").text().trim()
+    existingError = currentText.substring(usuallyPaidSameAmountText.length, currentText.length)
+    textToUse = switch ($("#" + howOftenFrequency).val())
+      when "weekly" then usuallyPaidSameAmountNWeekText
+      when "fortnightly" then usuallyPaidSameAmountNFortnightText
+      when "fourweekly" then usuallyPaidSameAmountNFourWeekText
+      when "monthly" then usuallyPaidSameAmountNMonthText
+      else usuallyPaidSameAmountNOtherText
+    $("div[class='validation-summary'] a[href='#" + usuallyPaidSameAmount + "']").text(textToUse + existingError)
+  if ($("#" + beenPaidYetY).prop('checked'))
+#    currentText = $("div[class='validation-summary'] a[href='#" + howMuchPaid + "']").text().trim()
+#    existingError = currentText.substring(howMuchPaidYText.length, currentText.length)
+#    $("div[class='validation-summary'] a[href='#" + howMuchPaid + "']").text(howMuchPaidNText + existingError)
+#    currentText = $("div[class='validation-summary'] a[href='#" + whatDatePaid + "']").text().trim()
+#    existingError = currentText.substring(whatDatePaidYText.length, currentText.length)
+#    $("div[class='validation-summary'] a[href='#" + whatDatePaid + "']").text(whatDatePaidNText + existingError)
+#    currentText = $("div[class='validation-summary'] a[href='#" + howOften + "']").text().trim()
+#    existingError = currentText.substring(howOftenYText.length, currentText.length)
+#    $("div[class='validation-summary'] a[href='#" + howOften + "']").text(howOftenNText + existingError)
     currentText = $("div[class='validation-summary'] a[href='#" + usuallyPaidSameAmount + "']").text().trim()
     existingError = currentText.substring(usuallyPaidSameAmountText.length, currentText.length)
     textToUse = switch ($("#" + howOftenFrequency).val())


fixing log msg

fixing log msg

--- src/toaster/grains/script.coffee
+++ src/toaster/grains/script.coffee
@@ -414,7 +414,7 @@

 					# ..and finally prints a warning msg
 					console.log "WARNING! ".bold.yellow,
-								"Dependency #{Dependencybold}".yellow,
+								"Dependency #{dependency.bold}".yellow,
 								"not found for class".yellow,
 								"#{file.classpath.yellow.bold}."

@@ -428,8 +428,7 @@
 								file.classname.bold.white,
 								"in file".yellow,
 								file.filepath.yellow
-
-
+
 		# after all, return everything in the proper order
 		files


Remove functionality to un-flag a message

--- static/coffee/controllers.coffee
+++ static/coffee/controllers.coffee
@@ -407,16 +407,9 @@
   #----------------------------------------------------------------------------

   $scope.onToggleMessageFlag = (message) ->
-    if $scope.folder == 'flagged'
-      UtilsService.confirmModal('Are you sure you want to un-flag this message?').then(() ->
-         MessageService.bulkFlag([message], !message.flagged).then(() ->
-          $scope.updateItems()
-        )
-      )
-    else
-      MessageService.bulkFlag([message], !message.flagged).then(() ->
-        $scope.updateItems()
-      )
+    MessageService.bulkFlag([message], !message.flagged).then(() ->
+      $scope.updateItems()
+    )

   $scope.onReplyToMessage = (message) ->
     MessageService.checkLock([message]).then((results) ->


atom is light now :)

atom is light now :)

--- atom/config.cson
+++ atom/config.cson
@@ -2,8 +2,9 @@
   editor:
     invisibles:
       {}
+    lineHeight: 1.4
     fontSize: 15
-    lineHeight: 1.4
+    fontFamily: "Fira Mono"
   core:
     disabledPackages: [
       "welcome"
@@ -18,8 +19,8 @@
     ]
     audioBeep: false
     themes: [
-      "seti-ui"
-      "one-dark-syntax"
+      "one-light-ui"
+      "one-light-syntax"
     ]
   "exception-reporting":
     userId: "4b873c95-5c84-bae8-43b3-7ff01cd0be7c"
@@ -52,10 +53,17 @@
   "linter-jsxhint":
     {}
   "linter-rubocop":
-    rubocopExecutablePath: "/usr/bin/rubocop"
+    rubocopExecutablePath: "/Users/jorin/.rbenv/shims/rubocop"
+    executablePath: "/Users/jorin/.rbenv/shims/rubocop"
   "scope-inspector":
     userId: "86b0158bfbb54093f95ca277e6d432b546ac0ffb"
     showHoistingIndicators: false
     showSidebar: false
   goto:
     moreIgnoredNames: "node_modules"
+  "atom-ternjs":
+    {}
+  "atom-beautify":
+    {}
+  "one-light-ui":
+    {}


Run Atom linter in TypeScript files

Run Atom linter in TypeScript files

--- atom/config.cson
+++ atom/config.cson
@@ -143,6 +143,7 @@
       "source.babel"
       "source.js-semantic"
       "source.js.sca.ss"
+      "source.ts"
     ]
   "linter-ui-default":
     hidePanelWhenEmpty: true


Changed soft wrap setting.

Changed soft wrap setting.

I decided I didn't like soft wrap to the preferred line limit.

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -17,7 +17,6 @@
     showIndentGuide: true
     scrollPastEnd: true
     softWrap: true
-    softWrapAtPreferredLineLength: true
     fontFamily: "inconsolata"
   "merge-conflicts": {}
   "travis-ci-status": {}


Added Time and Timespan classes for time calculations.

--- src/time.coffee
+++ src/time.coffee
@@ -1,6 +1,10 @@
-
-
-# keeps track of time, measures intervals etc.
+util = require './util'
+
+
+###
+  Timer: keeps track of time, measures intervals etc.
+  ------------------------------------------------------------------------------
+###
 class Timer
   now: null
   prev: null
@@ -19,7 +23,10 @@
   reset: -> @now = @prev = Date.now()

-# keeps track of rhythm, converts between beats, bars, time, tempo etc
+###
+  Tempo: keeps track of rhythm, converts between beats, bars, time, tempo etc
+  ------------------------------------------------------------------------------
+###
 class Tempo
   bpm: 120
   sigNum: 4
@@ -117,6 +124,104 @@
     @beat

+###
+  Time: Represents a single moment in time
+  ------------------------------------------------------------------------------
+###
+class Time
+  value: 0 # time in milliseconds
+  constructor: (arg) ->
+    if typeof(arg) == 'number'
+      @value = arg
+    else
+      @value = arg.value
+
+  # adds the given time object or millisecond value
+  add: (time) -> @value += if typeof(time) == 'number' then time else time.value
+
+  # adds the given time object or millisecond value and returns the result as new object
+  add_: (time) -> new Time(@value + if typeof(time) == 'number' then time else time.value)
+
+  # subtrct the given time object or millisecond value
+  sub: (time) -> @time -= if typeof(time) == 'number' then time else time.value
+
+  # subtracts the given time object or millisecond value and returns the result as new object
+  sub_: (time) -> new Time(@value - if typeof(time) == 'number' then time else time.value)
+
+  scale: (factor) -> @value *= factor
+  scale_: (factor) -> new Time(@value * factor)
+
+  clone: -> new Time(@value)
+  equals: (time) -> @value == if typeof(time) == 'number' then time else time.value
+  toString: -> "Time(#{@value}ms)"
+
+  # factory methods
+  @s: (seconds) -> new Time seconds * 1000
+  @ms: (milliseconds) -> new Time milliseconds
+
+
+
+###
+  Timespan: Represents a duration of time between two moments
+  ------------------------------------------------------------------------------
+###
+class Timespan extends util.EXObject
+  constructor: ->
+    switch arguments.length
+      when 0
+        @from = new Time(0)
+        @to = new Time(0)
+
+      when 1
+        @from = new Time(0)
+        @to = new Time(argument[0])
+
+      when 2
+        @from = new Time( arguments[0] )
+        @to = new Time( arguments[1] )
+
+  # returns a list of Time events by stepping through this timespan along a given interval
+  # interval can be another time object or millisecond value
+  segmentByInterval: (interval, snapEnd=false) ->
+    interval = if typeof interval == 'number' then new Time(interval) else interval
+
+    segments = []
+    current = new Timespan(@from, @from.add_(interval))
+    segments.push current.clone()
+
+    while current.to.value < @to.value
+      current.from.add interval
+      current.to.add interval
+      segments.push current.clone()
+
+    # creates another segment at the
+    if snapEnd and segments.length > 0
+      last = segments[ segments.length - 1 ]
+      halfInterval = interval.scale_ 0.5
+      if @to.value - last.to.value > halfInterval
+        segments.push new Timespan(last.clone(), @to.clone())
+      else
+        last.to.value = @to.value
+
+    segments
+
+  clone: -> new Timespan(@from, @to)
+  toString: -> "Timespan(#{@from} - #{@to})"
+
+  # sets the duration
+  @set 'length', (length) -> @to.value = @from.value + length
+  @get 'length', -> new Time(@to.value - @from.value)
+
+  # return this duration in seconds
+  @get 's', -> @length.s
+
+  # factory methods
+  @s: (seconds) -> new Timespan(seconds * 1000)
+
+
 module.exports =
   Timer: Timer
-  Tempo: Tempo+  Tempo: Tempo
+
+  Time: Time
+  Timespan: Timespan


change variance to delta

change variance to delta

--- dashing/javascripts/app/views/detail.coffee
+++ dashing/javascripts/app/views/detail.coffee
@@ -116,7 +116,7 @@

       container = $('#rate-chart')[0]
       graphOpts =
-        title: "Variance"
+        title: "Target Delta"
         colors: ["green", "red", "orange"]
         bars:
           show: true


Another nasty getRootDir

Another nasty getRootDir

--- src/util.coffee
+++ src/util.coffee
@@ -41,7 +41,7 @@
     file =
       switch
         when bufferOrFileOrString.file?
-          bufferOrFileOrString.file?
+          bufferOrFileOrString.file
         when bufferOrFileOrString instanceof File
           bufferOrFileOrString
         when typeof bufferOrFileOrString is 'string'


chore(web): don't put html files to dist

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -75,9 +75,6 @@
     .pipe connect.reload()

 gulp.task 'html', ->
-  gulp.src ['./app/**/*.html']
-    .pipe gulp.dest './dist/app/'
-    .pipe connect.reload()
   gulp.src ['./index2.html']
     .pipe gulp.dest './dist'
     .pipe connect.reload()


pend artist test

pend artist test

--- apps/artist/test/routes.coffee
+++ apps/artist/test/routes.coffee
@@ -74,7 +74,7 @@
       afterEach ->
         @req = @reqRestore

-      it 'parse the medium and passes it to Sharify', (done) ->
+      xit 'parse the medium and passes it to Sharify', (done) ->
         routes.index @req, @res
         Backbone.sync.args[0][2].success fabricate 'artist', id: 'andy-foobar'
         Backbone.sync.args[1][2].success()


Improved Processes class documentation

Improved Processes class documentation

--- www/src/theoricus/core/processes.coffee
+++ www/src/theoricus/core/processes.coffee
@@ -15,36 +15,71 @@

   # variables
   ###*
-  Block the url state to be changed. Useful if there is a current {{#crossLink "Process"}}{{/crossLink}} being executed.
+  Block the url state to be changed. Useful if there is a current {{#crossLink "Process"}}__Process__{{/crossLink}} being executed.

   @property {Boolean} locked
   ###
   locked: false
   disable_transitions: null

+  ###*
+    Stores the current processes that are active.
+
+    @property active_processes {Array}
+  ###
   active_processes: []
+
+  ###*
+    Stores the current processes that doesn't need to be active.
+
+    @property dead_processes {Array}
+  ###
   dead_processes: []
+
+  ###*
+    Stores the new Process dependencies.
+
+    @property pending_processes {Array}
+  ###
   pending_processes: []

   ###*
-  Responsible for handling the page/url change. It removes last Process, runs new Process dependencies, and then add the required Process.
+  Responsible for handling the url change.
+
+  When the URL changes, it initializes the Process responsible for the current Route (which is responsible for the current URL).
+
+  Stores the new Process dependency processes at `@pending_processes`
+
+  Destroy the current processes that are active, but are not dependency of the new Process.
+
+  Runs the processes that are not active yet.

   __Execution order__

-  1. `_on_router_change`
+  1. `_on_router_change` :
+
+      The URL changed, it will create a new Process to handle the current Route.

   2. `_filter_pending_processes`

+      Will search for all the new Process dependencies recursively, and store them at `pending_processes`
+
   3. `_filter_dead_processes`

+      Will search for all the processes that doesn't need to be active.
+
   4. `_destroy_dead_processes` - one by one, waiting or not for callback (timing can be sync/async)

+      Will destroy the processes that doesn't need to be active.
+
   6. `_run_pending_process` - one by one, waiting or not for callback (timing can be sync/async)
+
+      Will run the process that are required, but not active yet.

   @class Processes
   @constructor
   @param @the {Theoricus} Shortcut for app's instance.
-  @param @Routes {Object} App Routes
+  @param @Routes {Object} App routes defined in the `routes.coffee`
   ###
   constructor:( @the, @Routes )->
     Factory = @the.factory
@@ -57,7 +92,7 @@
       @router = new Router @the, @Routes, @_on_router_change

   ###*
-  Executed when the route changes, it creates a {{#crossLink "Process"}}{{/crossLink}} to manipulate the route, removes the current process, and run the new process alongside its dependencies.
+  Executed when the url changes, it creates a {{#crossLink "Process"}}__Process__{{/crossLink}} to manipulate the route, removes the current process, and run the new process alongside its dependencies.

   @method _on_router_change
   @param route {Route} Route containing the controller and url state information.
@@ -79,18 +114,12 @@
         do @_filter_dead_processes
         do @_destroy_dead_processes

-  ###
-  2nd
-
-  Check if target scope ( for rendering ) exists
-  If yes adds it to pending_process list
-  If no  throws an error
-
-  @param [theoricus.core.Process] process
-  ###
-  ###*
-
-
+  ###*
+    Searchs and stores the {{#crossLink "Process"}}__Process__{{/crossLink}} dependencies recursively.
+
+    @method _filter_pending_processes
+    @param process {Process} Process to search the dependencies.
+    @param after_filter {Function} Callback to be called when all the dependencies are stored.
   ###
   _filter_pending_processes:( process, after_filter )->

@@ -120,10 +149,10 @@
       do after_filter

   ###*
-  Finds the dependency of the given {{#crossLink "Process"}}{{/crossLink}}
+  Finds the dependency of the given {{#crossLink "Process"}}__Process__{{/crossLink}}

   @method _find_dependency
-  @param process {Process} Processto find the dependency.
+  @param process {Process} Process to find the dependency.
   @param after_find {Function} Callback to be called after the dependency has been found.
   ###
   _find_dependency:( process, after_find )->
@@ -152,7 +181,7 @@

   ###*
   Check which of the processes needs to stay active in order to render current process.
-  The ones that doesn't, are pushed to dead_processes.
+  The ones that doesn't, are pushed to `@dead_processes`.

   @method _filter_dead_processes
   ###


grab the armory 2015 fair

grab the armory 2015 fair

--- apps/fair_organizer/routes.coffee
+++ apps/fair_organizer/routes.coffee
@@ -38,7 +38,7 @@
     complete: ->
       profile = res.locals.profile
       return next() unless profile?.isFairOrOrganizer() and profile?.ownerHasId()
-      fair = new Fair id: profile.ownerId()
+      fair = new Fair id: 'the-armory-show-2015'
       fair.fetch
         error: res.backboneError
         success: =>


retry call to overpass

retry call to overpass

--- src/data.coffee
+++ src/data.coffee
@@ -35,6 +35,7 @@

 MAXBUFFER = 1024 * 1024 * 64  # 64MB
 SAVERAW = process.env.SAVERAW
+RETRY = +(process.env.RETRY or 0)

 osmid = (o) -> "#{o.type}/#{o.id}"
@@ -65,7 +66,7 @@
   deferred = Q.defer()
   request url, (err, res, body) ->
     if err?
-      deferred.fail(err)
+      deferred.reject(err)
     else
       deferred.resolve(body)
   return deferred.promise
@@ -73,6 +74,14 @@

 ensureDir = (path) ->
   fs.mkdirSync(path) unless fs.existsSync(path)
+
+
+retry = (n, fn, args...) ->
+  fn(args...)
+  .catch (e) ->
+    if n > 0
+      console.log "call failed, trying #{n} more times"
+      retry(n-1, fn, args...)

 closeRings = (feature) ->
@@ -171,7 +180,7 @@
     q = query(bbox)
     url = "http://overpass-api.de/api/interpreter?data=#{encodeURIComponent(q)}"
     console.log("overpass:", q)
-    httpGet(url)
+    retry(RETRY, httpGet, url)

   .then (resp) ->
     if SAVERAW?


Actually use environment for key/cert file

--- src/echo.coffee
+++ src/echo.coffee
@@ -22,8 +22,8 @@

 STUN_ADDRESS = process.env.STUN_ADDRESS ? "176.28.9.212"

-CERT_FILE = "cert.pem"
-KEY_FILE = "key.pem"
+CERT_FILE = process.env.CERT_FILE ? "cert.pem"
+KEY_FILE = process.env.KEY_FILE ? "key.pem"

 # init


Refactor some deeply nested code

Refactor some deeply nested code