Extract the namespace to separated method

--- lib/assets/javascript/init.js.coffee
+++ lib/assets/javascript/init.js.coffee
@@ -14,25 +14,27 @@
     console.log "Initjs: #{appName} is not defined. Run `rails generate initjs` to generate the app file." unless @App?

   exec: (controllerClass, controllerName, action) ->
-    namespace = App
-    this.initModules(App)
+    @initModules(@App)
+    namespace = @namespace(controllerClass)
+
+    if namespace and controllerName
+      controller = namespace[controllerName]
+      @initModules(controller)
+      @App.currentView = @initView(View) if controller and View = controller[action]
+
+  namespace: (controllerClass)->
+    namespace = @App

     if controllerClass
-      railsNamespace = controllerClass.split("::").slice(0, -1)
+      railsNamespace = controllerClass.split('::').slice(0, -1)
     else
       railsNamespace = []

     for name in railsNamespace
       if namespace
         namespace = namespace[name]
-        console.log namespace
-        this.initModules(namespace)
-
-    if namespace and controllerName
-      controller = namespace[controllerName]
-      this.initModules(controller) if controller?
-      App.currentView = this.initView(View) if controller and View = controller[action]
-
+        @initModules(namespace)
+    return namespace

   initView: (view)->
     if typeof view is 'function'


Fix InitPartial with multiple partials

Fix InitPartial with multiple partials

--- lib/assets/javascript/init.js.coffee
+++ lib/assets/javascript/init.js.coffee
@@ -10,6 +10,7 @@

   initializePartial: ->
     $infos = @$partialInfos()
+    @$partialInfos().remove()
     if $infos.length > 0
       @partial = true
       @initialize($infos)


Check if related function is there

Check if related function is there

--- client/app/MainApp/maintabs/maintabview.coffee
+++ client/app/MainApp/maintabs/maintabview.coffee
@@ -80,7 +80,7 @@
     # kodingrouter

     index        = @getPaneIndex pane
-    visibles     = @getVisibleTabs()
+    visibles     = @getVisibleTabs?()
     visibleIndex = visibles.indexOf pane
     leftPane     = visibles[visibleIndex - 1]
     rightPane    = visibles[visibleIndex + 1]


Client: Sidebar: Added setActiveInvitationMachineId action

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -269,6 +269,13 @@
   { reactor, computeController } = kd.singletons

   reactor.dispatch actions.MACHINE_LIST_ITEM_DELETED, { id ,machineListItem }
+
+
+setActiveInvitationMachineId = (id) ->
+
+  { reactor, computeController } = kd.singletons
+
+  reactor.dispatch actions.SET_ACTIVE_INVITATION_MACHINE_ID, { id }

 module.exports = {
@@ -289,4 +296,5 @@
   createWorkspace
   setMachineListItem
   unsetMachineListItem
+  setActiveInvitationMachineId
 }


computehelpers: Fixed typo in promise chain

--- client/app/lib/providers/computehelpers.coffee
+++ client/app/lib/providers/computehelpers.coffee
@@ -46,8 +46,8 @@

     result = Promise.all destroyPromises
     result.timeout globals.COMPUTECONTROLLER_TIMEOUT  unless waitForCompleteDeletion
-      .then        -> callback? null
-      .catch (err) -> callback? err
+    result.then        -> callback? null
+    result.catch (err) -> callback? err
     return result

@@ -107,8 +107,8 @@

     # Callback if needed, and return the resulting promise.
     result.timeout globals.COMPUTECONTROLLER_TIMEOUT  unless waitForCompleteDeletion
-      .then        -> callback? null
-      .catch (err) -> callback? err
+    result.then        -> callback? null
+    result.catch (err) -> callback? err
     return result


removed href link from template

removed href link from template

--- client/app/MainApp/avatararea.coffee
+++ client/app/MainApp/avatararea.coffee
@@ -409,7 +409,7 @@
     """
     {{> @avatar}}

-      {{#(subject) or '(No title)'}}

+      {{#(subject) or '(No title)'}}

       {{@teaser #(body)}}

         <time>{{> @participants}} {{$.timeago #(meta.createdAt)}}</time>


Update label counts.

Update label counts.

--- web/coffeescripts/files.coffee
+++ web/coffeescripts/files.coffee
@@ -19,7 +19,7 @@
   labelNodeList: (label)->
     text = if label.size == 1 then 'file' else 'files'
     node = $("""
-

+

         #{label.size} #{text}

@@ -27,7 +27,6 @@
     $('.text', node).text label.name
     node.attr 'data-name', label.name
     node.click (event) => this.selectLabel(event)
-    node.fadeIn(100)

   selectLabel: (event) ->
     name = $(event.currentTarget).attr 'data-name'
@@ -127,8 +126,9 @@
     file = node.data 'file'
     file.labels.push label for label in labels
     @api.save FILES, file, (result) ->
-    this.labelNode node, label for label in labels
-    this.findLabels()
+    for label in labels
+      this.labelNode node, label
+      this.updateLabelCount label, 1
     false

   removeLabel: (node, item) ->
@@ -137,10 +137,21 @@
     file.labels = (label for label in file.labels when label != remove)
     @api.save FILES, file, (result) ->
     item.fadeOut 200, -> item.remove()
-    this.findLabels()
+    this.updateLabelCount remove, -1
+
+  updateLabelCount: (name, inc) ->
+    el = $ "#labels li[data-name='#{name}'] .count"
+    if el.length > 0
+      count = parseInt(el.text().split(' ')[0]) + inc
+      text = if count == 1 then 'file' else 'files'
+      el.text "#{count} #{text}"
+    else
+      this.labelNodeList(name: name, size: 1)

   deleteFile: (node) ->
     @api.remove FILES, node.attr('data-id'), (result) =>
+      for label in node.data('file').labels
+        this.updateLabelCount label, -1
       node.fadeOut 200, -> node.remove()
     false


Update user agent

Update user agent

--- bots/libs/HexBot.coffee
+++ bots/libs/HexBot.coffee
@@ -10,7 +10,7 @@
       pageSettings:
         loadImages: false
         loadPlugins: false
-        userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1530.2 Safari/537.36'
+        userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.15 Safari/537.36'
     )
   )


Setup analytics.js only if it is enabled

--- client/app/lib/setupanalytics.coffee
+++ client/app/lib/setupanalytics.coffee
@@ -55,6 +55,8 @@

 module.exports = ->

-  setupIdentify()
-  setupPageAnalyticsEvent()
+  if globals.config.sendEventsToSegment
+    setupIdentify()
+    setupPageAnalyticsEvent()
+
   setupRollbar()


IDE: Fix spacing.

IDE: Fix spacing.

--- client/ide/workspace/panes/terminalpane.coffee
+++ client/ide/workspace/panes/terminalpane.coffee
@@ -32,6 +32,7 @@
         @runCommand "cd #{path}" if path

     @webtermView.connectToTerminal()
+
     @webtermView.once "WebTerm.terminated", =>
       paneView = @parent
       tabView  = paneView.parent


decode HTML chars before setting the title

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -76,7 +76,7 @@

   getDefaultRoute:-> '/Activity'

-  setPageTitle:(title="Koding")-> document.title = title
+  setPageTitle:(title="Koding")-> document.title = Encoder.htmlDecode title

   getContentTitle:(model)->
     {JAccount, JStatusUpdate, JGroup} = KD.remote.api


Set display types for bigdec

Set display types for bigdec

--- client/lanes/components/grid/EditingMixin.cjsx
+++ client/lanes/components/grid/EditingMixin.cjsx
@@ -22,6 +22,12 @@
         date: (props) ->
             <LC.DateTime {...props} inputOnly step={15}
                 onChange={_.partial(@onDateFieldChange, _, props.field)} />
+
+    displayTypes:
+        bigdec: (props) ->
+            {props.value.toFixed(2)}
+        text: (props) ->
+            {props.value}

     listenNetworkEvents: true
     getDefaultProps: -> editors: {}
@@ -68,19 +74,21 @@
                 {@renderControls()}

+    renderEditValue: (props) ->
+        ( @props.editors[props.field.id] ||
+          @editorTypes[props.field.type] ||
+          @editorTypes['text']
+        ).call(this, props)
+
+    renderDisplayValue: (props) ->
+        ( @displayTypes[props.field.type] || @displayTypes['text'] ).call(this, props)
+
     renderField: (field, index) ->
-        control = if field.editable
-            props = _.extend( {
-                index, field,
-                props: @props,
-                name: field.id,
-                value: @getFieldValue(field)
-
-            }, _.pick(@props, 'model', 'query', 'rowIndex') )
-
-            (@props.editors[field.id] || @editorTypes[field.type] || @editorTypes['text']).call(this, props)
-        else
-            {@getFieldValue(field)}
+        props = _.extend( {
+            index, field, props: @props, name: field.id, value: @getFieldValue(field)
+        }, _.pick(@props, 'model', 'query', 'rowIndex') )
+        control = if field.editable then @renderEditValue(props) else
+            @renderDisplayValue(props)

         <div key={field.id}
             style = {@props.cellStyles.styles[index]}


"hgjhagsqwreference"

"hgjhagsqwreference"

--- web/scripts/referenceUI.coffee
+++ web/scripts/referenceUI.coffee
@@ -8,7 +8,7 @@
 window.dictionary = (text,cont) ->
     #appends a div for the paragraph content, to be further developed to contain code snippets and videos
     info = document.createElement("div")
-    $(info).css({"width":"100%","height":"35%","position":"absolute","top":"65%","border-top":"1px solid black"})
+    $(info).css({"overflow":"auto","white-space": "pre-wrap", "width":"100%","height":"35%","position":"absolute","top":"65%","border-top":"1px solid black"})
     $(cont).append(info)
     #the attache function accepts a key and data and creates the content for the info div
     attache = (k,d) ->
@@ -64,20 +64,24 @@
     $("body").prepend(refContainer)

     dictionary = document.createElement("div")
+    ref = document.createElement("div")
     input = document.createElement("div")
     output = document.createElement("div")

     $(dictionary).css({width:'35%',height:'90%',position:'absolute',left:'5%',top:'5%',bottom:'80%','border':'1px solid black'})
+    #$(ref).css({width:'35%',height:'30%',position:'absolute',left:'5%',top:'5',bottom:'80%','border':'1px solid black'})
     $(input).css({width:'50%',height:'40%',position:'absolute',right:'5%',top:'5%','border':'1px solid black'})
     $(output).css({width:'50%',height:'40%',position:'absolute',right:'5%',top:'50%','border':'1px solid black'})

     $(refContainer).prepend(dictionary)
+    #$(refContainer).prepend(ref)
     $(refContainer).prepend(input)
     $(refContainer).prepend(output)

     en1 = document.createElement("img")
     en2 = document.createElement("img")
     en3 = document.createElement("img")
+    en4 = document.createElement("img")

     $(en1).attr({'src':'img/enlarge1.png',class:'en'})
     $(en2).attr({'src':'img/enlarge1.png',class:'en'})
@@ -133,7 +137,7 @@
     $(".en").click(enClick)
     $("#bF").click(closeClick)

-    window.dictionary("config/dictionary.json",dictionary)
+    window.dictionary("dictionary.json",dictionary)

     setUpJavaSandbox input, output
     return


removed double call to clear unpublished changes

--- client/src/app/game/undo/undoController.coffee
+++ client/src/app/game/undo/undoController.coffee
@@ -4,7 +4,7 @@

 angular.module('gamEvolve.game.undo', ['gamEvolve.model.undo'])
-.controller "UndoCtrl", ($scope, $window, undo, currentGame, cache, gameConverter, WillChangeLocalVersionEvent, GameVersionPublishedEvent, NewGameLoadingEvent) ->
+.controller "UndoCtrl", ($scope, $window, undo, currentGame, cache, gameConverter, WillChangeLocalVersionEvent, GameVersionPublishedEvent) ->
   currentLocalVersion = null
   initialVersionHasBeenCached = false

@@ -32,7 +32,9 @@
   onGameVersionPublished = ->
     currentGame.setStatusMessage("Published at #{moment().format("HH:mm:ss")}")
     currentGame.clearHasUnpublishedChanges()
-  GameVersionPublishedEvent.listen(onGameVersionPublished)
+
+  unsubcribeOnGameVersionPublished = GameVersionPublishedEvent.listen(onGameVersionPublished)
+  $scope.$on("$destroy", unsubcribeOnGameVersionPublished)

   onUpdateCurrentGame = ->
     if not currentGame.version then return


payment: add provider constants to workflow

--- client/Main/payment/workflow.coffee
+++ client/Main/payment/workflow.coffee
@@ -21,6 +21,11 @@
     HOBBYIST     : 'hobbyist'
     DEVELOPER    : 'developer'
     PROFESSIONAL : 'professional'
+
+  @provider =
+    STRIPE : 'stripe'
+    PAYPAL : 'paypal'
+    KODING : 'koding'

   FAILED_ATTEMPT_LIMIT = 3


Tests: Added postMessageAndSeeIfItsPostedOnlyOnce test.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -264,3 +264,17 @@

     browser.end()

+
+  postMessageAndSeeIfItsPostedOnlyOnce: (browser) ->
+
+    post = helpers.getFakeText()
+
+    helpers.postActivity(browser)
+
+    secondPostSelector = '[testpath=activity-list] section:nth-of-type(1) [testpath=ActivityListItemView]:nth-of-type(2) article'
+    secondPost = browser.getText secondPostSelector
+
+    assert.notEqual(post, secondPost)
+
+    browser.end()
+


do not add the default item twice.

do not add the default item twice.

--- client/Framework/components/list/KDListViewController.coffee
+++ client/Framework/components/list/KDListViewController.coffee
@@ -63,7 +63,7 @@
     @defaultItem.destroy() if @defaultItem

   putDefaultItem:(list=[])->
-    if @getOptions().showDefaultItem
+    if @getOptions().showDefaultItem and not @defaultItem
       if list.length is 0 then @addDefaultItem()
       else @removeDefaultItem()


Set indentations

Set indentations

--- client/activity/lib/views/comments/commentinputwidget.coffee
+++ client/activity/lib/views/comments/commentinputwidget.coffee
@@ -1,15 +1,15 @@
-_  = require 'lodash'
-kd = require 'kd'
-KDButtonView = kd.ButtonView
-KDCustomHTMLView = kd.CustomHTMLView
-ActivityInputWidget = require '../activityinputwidget'
-CommentInputView = require './commentinputview'
-EmbedBoxWidget = require '../embedboxwidget'
-showError = require 'app/util/showError'
-trackEvent = require 'app/util/trackEvent'
-whoami = require 'app/util/whoami'
-generateFakeIdentifier = require 'app/util/generateFakeIdentifier'
-AvatarStaticView = require 'app/commonviews/avatarviews/avatarstaticview'
+_                       = require 'lodash'
+kd                      = require 'kd'
+KDButtonView            = kd.ButtonView
+KDCustomHTMLView        = kd.CustomHTMLView
+ActivityInputWidget     = require '../activityinputwidget'
+CommentInputView        = require './commentinputview'
+EmbedBoxWidget          = require '../embedboxwidget'
+showError               = require 'app/util/showError'
+trackEvent              = require 'app/util/trackEvent'
+whoami                  = require 'app/util/whoami'
+generateFakeIdentifier  = require 'app/util/generateFakeIdentifier'
+AvatarStaticView        = require 'app/commonviews/avatarviews/avatarstaticview'

 module.exports = class CommentInputWidget extends ActivityInputWidget


groups: markdown parser for headline snow returns text and line numbers

--- client/landing/app/MainApp/lazy/staticgroupcontroller.coffee
+++ client/landing/app/MainApp/lazy/staticgroupcontroller.coffee
@@ -50,9 +50,13 @@
             lines = content.split("\n");
             for line,index in lines
               if /^#[^#]{1,}/.test line
-                menuItems.push line.replace(/^#*\s*/g, '')
+                menuItems.push
+                  title : line.replace(/^#*\s*/g, '')
+                  line  : index
               else if /^\s*(=){1,}\s*$/.test line
-                menuItems.push lines[index-1]
+                menuItems.push
+                  title : lines[index-1]
+                  line  : index-1

             callback menuItems


fixed broken coffee script caused by errant pseudo code I checked in

--- cms/static/coffee/src/views/module_edit.coffee
+++ cms/static/coffee/src/views/module_edit.coffee
@@ -27,8 +27,6 @@
           el: metadataEditor,
           model: new CMS.Models.MetadataEditor(metadataEditor.data('metadata'))
           });
-
-   ----> find id of the li that wraps the is-set A thinger, that thing's name, get the first word set to VAR, and then add is-active to the div whose class equals  wrapper-comp-VAR

   changedMetadata: ->
     return @metadataEditor.getModifiedMetadataValues()
@@ -89,17 +87,11 @@
   clickModeButton: (event) ->
     event.preventDefault()
     if $(this).hasClass(".is-set")
-      alert("Hey Sucka")
+      alert("Hi There")
     else
       previousTab = @$el.find('li.active-mode .is-set')
-      previousTab.parent().siblings().find("a").addClass('is-set').attr({
-        aria-selected: 'true',
-        tab-index: '1'
-        })
-      previousTab.removeClass('is-set').attr({
-        aria-selected: 'false',
-        tab-index: '0'
-        })
+      previousTab.parent().siblings().find("a").addClass('is-set')
+      previousTab.removeClass('is-set')

       previousTabContent = @$el.find('div.is-active')
       previousTabContent.siblings().addClass('is-active')


take out some unused code

take out some unused code

--- src/flux/task.coffee
+++ src/flux/task.coffee
@@ -31,49 +31,6 @@
 getCompleteSteps = (steps) ->
   _.filter steps, (step) ->
     step? and step.is_completed
-
-getCoreSteps = (steps) ->
-  _.filter steps, (step) ->
-    step? and step.group is 'core'
-
-getFirstCoreStepIndex = (steps) ->
-  _.findIndex steps, (step) ->
-    step? and step.group is 'core'
-
-getFirstCoreStepIndex = (steps) ->
-  _.findIndex steps, (step) ->
-    step? and step.group is 'core'
-
-getLastCoreStepIndex = (steps) ->
-  _.findLastIndex steps, (step) ->
-    step? and step.group is 'core'
-
-getCoreStepsIndexes = (steps) ->
-  firstIndex = getFirstCoreStepIndex(steps)
-  lastIndex = getLastCoreStepIndex(steps)
-
-  coreSteps = [
-    firstIndex
-  ]
-  unless lastIndex is firstIndex
-    coreSteps.push lastIndex
-
-  coreSteps
-
-getIncompleteCoreStepsIndexes = (steps) ->
-  firstIndex =   _.findIndex steps, (step) ->
-    step? and not step.is_completed and step.group is 'core'
-
-  lastIndex = _.findLastIndex steps, (step) ->
-    step? and not step.is_completed and step.group is 'core'
-
-  coreSteps = [
-    firstIndex
-  ]
-  unless lastIndex is firstIndex
-    coreSteps.push lastIndex
-
-  coreSteps

 TaskConfig =
   _steps: {}
@@ -160,17 +117,21 @@
       allSteps = getSteps(@_steps[taskId])
       steps = getCompleteSteps(allSteps)

-    getCoreSteps: (taskId) ->
-      allSteps = getSteps(@_steps[taskId])
-      steps = getCoreSteps(allSteps)
-
-    getCoreStepsIndexes: (taskId) ->
-      allSteps = getSteps(@_steps[taskId])
-      steps = getCoreStepsIndexes(allSteps)
-
     getIncompleteCoreStepsIndexes: (taskId) ->
       allSteps = getSteps(@_steps[taskId])
-      steps = getIncompleteCoreStepsIndexes(allSteps)
+      firstIndex =   _.findIndex allSteps, (step) ->
+        step? and not step.is_completed and step.group is 'core'
+
+      lastIndex = _.findLastIndex allSteps, (step) ->
+        step? and not step.is_completed and step.group is 'core'
+
+      coreSteps = [
+        firstIndex
+      ]
+      unless lastIndex is firstIndex
+        coreSteps.push lastIndex
+
+      coreSteps

     hasIncompleteCoreStepsIndexes: (taskId) ->
       allSteps = getSteps(@_steps[taskId])


Fix kontrol postgres username and password environment variables

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -402,8 +402,8 @@
       # Include this to dockerfile before we continute with building
       mkdir -p kontrol
       cp $KONFIG_PROJECTROOT/go/src/github.com/koding/kite/kontrol/*.sql kontrol/
-      sed -i -e "s/somerandompassword/$KONFIG_POSTGRES_PASSWORD/" kontrol/001-schema.sql
-      sed -i -e "s/kontrolapplication/$KONFIG_POSTGRES_USERNAME/" kontrol/001-schema.sql
+      sed -i -e "s/somerandompassword/$KONFIG_KONTROL_POSTGRES_PASSWORD/" kontrol/001-schema.sql
+      sed -i -e "s/kontrolapplication/$KONFIG_KONTROL_POSTGRES_USERNAME/" kontrol/001-schema.sql

       docker build -t koding/postgres .


run: exit with 1 on ctrlc

run: exit with 1 on ctrlc

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -68,6 +68,7 @@

     function ctrl_c () {
       supervisorctl shutdown
+      exit 1;
     }

     function nginxstop () {


changed order

changed order

--- core/app/backbone/views/facts/fact_relation_tabs_view.coffee
+++ core/app/backbone/views/facts/fact_relation_tabs_view.coffee
@@ -33,14 +33,14 @@

   onClose: -> delete @discussion

+  factRelationView: (type) ->
+    new FactRelationsView model: @cachedDiscussion(type)
+
   cachedDiscussion: (type) ->
     @discussion ?= {}
     @discussion[type] ?= new Discussion
       fact: @model
       type: type
-
-  factRelationView: (type) ->
-    new FactRelationsView model: @cachedDiscussion(type)

   tabClick: (e) ->
     e.preventDefault()


added max sockets to s3 wrapper, getting some ETIMEDOUT under laod, may be cause

--- app/coffee/s3Wrapper.coffee
+++ app/coffee/s3Wrapper.coffee
@@ -1,3 +1,5 @@
+http = require('http')
+http.globalAgent.maxSockets = 300
 settings = require("settings-sharelatex")
 request = require("request")
 logger = require("logger-sharelatex")


Temporarily disable example rendering

Temporarily disable example rendering

--- app/controllers/home.coffee
+++ app/controllers/home.coffee
@@ -30,13 +30,13 @@
       @primaryChart.render()
       @zoomChart.render()

-    i = 0
-    for type, list of examples
-      $('.examples').append $("

#{ type }
")
-
-      for file in list
-        new Chart file: "examples/#{ file }.csv", container: ".examples [name='#{ type }']", name: "example-#{ i += 1}", title: file, width: 320, height: 200
-
-
+    # i = 0
+    # for type, list of examples
+    #   $('.examples').append $("

#{ type }
")
+    #
+    #   for file in list
+    #     new Chart file: "examples/#{ file }.csv", container: ".examples [name='#{ type }']", name: "example-#{ i += 1}", title: file, width: 320, height: 200
+    #
+    #

 module.exports = Home


Clean up code

Clean up code

--- src/views/route.coffee
+++ src/views/route.coffee
@@ -76,7 +76,6 @@
             @routingParameters.ensureUnitDestination()
             @routingParameters.setDestination @model
             previousOrigin = @routingParameters.getOrigin()
-            locationLoadingCancelToken = null
             if lastPos
                 if not previousOrigin
                     @routingParameters.setOrigin lastPos,
@@ -106,7 +105,6 @@
             @routeSettingsRegion.show new RouteSettingsView
                 model: @routingParameters
                 unit: @model
-                locationLoadingCancelToken: locationLoadingCancelToken
             @showRouteSummary null

         showRouteSummary: (route) ->


kinetic_thingy_delegate: Add visibility to methods added by extend_thingy()

--- Oghma/kinetic_thingy_delegate.coffee
+++ Oghma/kinetic_thingy_delegate.coffee
@@ -318,7 +318,8 @@
     'redraw',
     'is_locked', 'is_public', 'is_visible', 'is_hidden', 'is_owned',
     'raise', 'lower',
-    'redraw'
+    'redraw',
+    'visibility'
   ]

   # Add common methods to thingy.
@@ -328,7 +329,8 @@
   #
   # By default, the following methods are added to the thingy, each behaves
   # as the same method of this class: {#redraw}, {#is_locked}, {#is_public},
-  # {#is_visible}, {#is_hidden}, {#is_owned}, {#raise}, {#lower}, {#redraw}.
+  # {#is_visible}, {#is_hidden}, {#is_owned}, {#raise}, {#lower}, {#redraw},
+  # {#visibility}.
   #
   # @param [array<string>] what Which methods to add.
   # @return [Oghma.KineticThingyDelegate] this


remove feed mocks

remove feed mocks

--- app/js/classes/feeds.coffee
+++ app/js/classes/feeds.coffee
@@ -13,41 +13,8 @@
 	items      : DS.hasMany 'App.Item'

-@App.FeedsRoute = Ember.Route.extend
-	selectedFeed: {},
-
-	# mock
-	model: ->
-		[
-			{
-				title: 'The Wreck Channel'
-				url: 'http://www.google.com/rss'
-				created_at: new Date()
-				items: [
-					{
-						title: 'How to sleep on the street?'
-						link: 'http://www.google.com'
-						content: '
How to sleep on the street?
'
-					}
-					{
-						title: 'The 10 best insects you should try'
-						link: 'http://www.yahoo.com'
-						content: '
The 10 best insects you should try
'
-					}
-				]
-			}
-			{
-				title: 'UFO Hotline'
-				url: 'http://www.blizzard.com/rss'
-				created_at: new Date()
-				items: [{
-					title: 'Who kidnapped my grandpa?'
-					link: 'http://www.google.com'
-					content: '
Who kidnapped my grandpa?
'
-				}]
-			},
-		]
-
+# @App.FeedsRoute = Ember.Route.extend
+
 @App.FeedsController = Ember.ArrayController.extend
 	newFeedUrl: null
 	urlPrompt: false


missing part

missing part

--- app/js/services/info.coffee
+++ app/js/services/info.coffee
@@ -19,7 +19,7 @@
                     @info.wallet_unlocked = data.wallet_unlocked
                     @info.last_block_time = data.blockchain_head_block_timestamp
                     @info.last_block_num = data.blockchain_head_block_num
-                    @info.last_block_time_rel = data.blockchain_head_block_time_rel
+                    @info.last_block_time_rel = data.blockchain_head_block_time_age
                 else
                     @info.wallet_unlocked = data.wallet_unlocked


Add User activity convenience method

Add User activity convenience method


Re-ordered promises to ensure unrender is queued synchronously.

--- src/EntityUtils.coffee
+++ src/EntityUtils.coffee
@@ -120,8 +120,8 @@

   render: (id, args) ->
     df = Q.defer()
-    @renderingEnabledDf.promise.then Meteor.bindEnvironment =>
-      @renderQueue.add id, => df.resolve @_render(id, args)
+    @renderQueue.add id, =>
+      @renderingEnabledDf.promise.then Meteor.bindEnvironment => df.resolve @_render(id, args)
     df.promise

   _render: (id, args) ->
@@ -396,8 +396,8 @@

   unrender: (id) ->
     df = Q.defer()
-    @renderingEnabledDf.promise.then Meteor.bindEnvironment =>
-      @renderQueue.add id, ->
+    @renderQueue.add id, =>
+      @renderingEnabledDf.promise.then Meteor.bindEnvironment =>
         AtlasManager.unrenderEntity(id)
         df.resolve(id)
     df.promise


Long lines are not bad.

Long lines are not bad.

Sometimes Python requires longer lines.

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -18,6 +18,8 @@
   "git-plus": {}
   "go-plus": {}
   latex: {}
+  "linter-flake8":
+    maxLineLength: 1000
   "merge-conflicts": {}
   "one-dark-ui": {}
   "package-sync":


Bug fix

Bug fix

--- lib/cache_sync.coffee
+++ lib/cache_sync.coffee
@@ -26,9 +26,8 @@

   create: (model, options) ->
     @wrapped_sync_fn 'create', model, Utils.bbCallback (err, json) =>
+      return options.error(err) if err
       Cache.findOrCreate(@model_type.model_name, @model_type, json) # add to the cache
-
-      return options.error(err) if err
       options.success(json)

   update: (model, options) ->


Define a custom file setter

Define a custom file setter

--- lib/components/uploader/DropzoneComponent.coffee
+++ lib/components/uploader/DropzoneComponent.coffee
@@ -309,6 +309,19 @@
     elem = file.previewElement
     elem.className = elem.className + ' clickable'

+    # add a set method to make it easier manipulating the file
+    # and triggering the appropiate events
+    instance = @
+    file.set = (key, val) ->
+      if key is 'name'
+        @name = val
+        node.textContent = val for node in @previewElement.querySelectorAll '[data-dz-name]'
+      else if key is 'upload'
+        if _.isObject(val) then @uploadModel.set val
+      else
+        @[key] = val
+      instance.updateFieldValue()
+
     elem.addEventListener 'click', =>
       @dz.options.clickedfile file, elem


Add security checks

Add security checks

--- lib/components/uploader/DropzoneComponent.coffee
+++ lib/components/uploader/DropzoneComponent.coffee
@@ -246,6 +246,8 @@
   Used for example when editing an existing record
   ###
   populatePreviousUploads: (data = []) ->
+    unless _.isArray data then return
+
     data.forEach (dataObj) =>
       mockFile = @deserialize dataObj


Make indentation consistent

Make indentation consistent

--- lib/helpers/TaggableModelControllerHelper.coffee
+++ lib/helpers/TaggableModelControllerHelper.coffee
@@ -5,7 +5,7 @@

 module.exports = class TaggableModelControllerHelper extends BaseObject

-	defaults:
+  defaults:
     tagsModelAttribute: 'tags'
     tagFactory:         null


Make coffeelint happier

Make coffeelint happier

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -208,6 +208,7 @@
 #     }, function (err, results) {
 #       // Do something with results
 #     });
+#
 exports.asCallback = require('./AsCallback').asCallback

 # ## Generating components from JavaScript functions
@@ -221,4 +222,5 @@
 #         description: 'Generate a random number',
 #       });
 #     };
+#
 exports.asComponent = require('./AsComponent').asComponent


PhysicalQuantity value should always be a number

--- app/assets/javascripts/core.js.coffee
+++ app/assets/javascripts/core.js.coffee
@@ -29,7 +29,7 @@
 class hQuery.PhysicalQuantity
   constructor: (@json) ->
   units: -> @json['units']
-  scalar: -> @json['scalar']
+  scalar: -> parseFloat(@json['scalar'])

 ###*
 @class A code with its corresponding code system


drawing a grid in the canvas and sizing it to the window size

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -1,17 +1,48 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/

-class Rect
-  constructor: (@ctx, @x1, @y1, @x2, @y2) ->
+
+class Grid
+  constructor: (@width, @height, @ctx)->
+    @increment = 10
+    @setLineColor()
+
+  setLineColor: ->
+    @ctx.strokeStyle = '#B4D6D7'

   draw: ->
-    @ctx.fillStyle = "rgb(200,0,0)"
-    @ctx.fillRect(@x1, @y1, @x2, @y2)
+    @ctx.beginPath()
+    @drawHorizontalLines()
+    @drawVerticalLines()
+    @ctx.stroke()
+
+  drawHorizontalLines: ->
+    vPosition = 0
+    loop
+      @drawHorizontalLine(vPosition)
+      vPosition += 40
+      break if vPosition > @height
+
+  drawHorizontalLine: (vPosition) ->
+    @ctx.moveTo(0,vPosition+0.5)
+    @ctx.lineTo(@width, vPosition+0.5)
+
+  drawVerticalLines: ->
+    hPosition = 0
+    loop
+      @drawVerticalLine(hPosition)
+      hPosition += 40
+      break if hPosition > @width
+
+  drawVerticalLine: (hPosition) ->
+    @ctx.moveTo(hPosition+0.5, 0)
+    @ctx.lineTo(hPosition+0.5, @width)
+

 class View
-  constructor: ->
-    @ctx = $('#view').get(0).getContext('2d')
+  constructor: (@width, @height) ->
+    @canvas = $('#view').get(0)
+    @canvas.width = @width
+    @canvas.height = @height
+    @ctx = @canvas.getContext('2d')
     @elements = []

   draw: ->
@@ -20,8 +51,10 @@

 class ViewController
   constructor: ->
-    @view = new View
+    @view = new View($(window).width(), $(window).height())
     @ctx = @view.ctx
+    @grid = new Grid(@view.width, @view.height, @ctx)
+    @addElement(@grid)

   addElement: (element) ->
     @view.elements.push element
@@ -31,8 +64,6 @@

 $(document).ready ->
   viewController = new ViewController
-  rect = new Rect(viewController.ctx, 10, 10, 100, 200)
-  viewController.addElement(rect)
   viewController.draw()


update wording

update wording

--- app/assets/javascripts/i18n.js.coffee
+++ app/assets/javascripts/i18n.js.coffee
@@ -14,7 +14,7 @@
       lost_server_conn: 'Sorry, we lost our connection to the server —
please check your network and try to re-login.'
       no_messages:      'Sorry, we stopped receiving messages —
please open just one app window at a time.'
       ok:               'Ok'
-      outgoing_call:    'Do you want to call
NAME?'
+      outgoing_call:    'You are about to call:
NAME'
       no_hungup_call:   'This is only available after you finished a call.'
       reload_necessary: 'We need to reload the browser window to activate the new settings.'
       reload:           'Reload'


Check if haystack is object before search

--- src/lib/index.coffee
+++ src/lib/index.coffee
@@ -2,10 +2,9 @@
 isObject = require 'is-object'

 find = (haystack, needle, memo = []) ->
-  if needle of haystack
-    memo.push haystack[needle]
-
-  if isObject haystack
+  if needle and isObject haystack
+    if needle of haystack
+      memo.push haystack[needle]
     for val in (i for i of haystack)
       find(haystack[val], needle, memo) if isObject haystack[val]


fixedish a bug

fixedish a bug

--- src/lib/index.coffee
+++ src/lib/index.coffee
@@ -8,6 +8,9 @@

 session = null
 pairingCode = null
+
+g_current_app_msg = null
+g_current_app_time = null

 # cb(err)
 getLoginInfo = (cb) ->
@@ -50,7 +53,8 @@
       if current_app not of all_apps
         debug "Could not find current app (#{current_app}) in all apps"
         return
-
+      g_current_app_msg = current_app
+      g_current_app_time = new Date()
       if all_apps[current_app] != 1
         open_chrome_tab(all_apps[current_app].window_index, all_apps[current_app].tab_index)
       else
@@ -145,8 +149,12 @@
           @context_ref.child('current_app').set('UNKNOWN')
           return
         else
+          if g_current_app_msg != null and g_current_app_msg != current_app and \
+              new Date() - g_current_app_time < 4000
+            return
+          g_current_app_msg = null
+          g_current_app_time = null
           @context_ref.child('current_app').set(Object.keys(current_app)[0])
-          return
     else
       current_app = opened_apps.get_apps [stdout], []
       if not Object.keys(current_app).length


Fix JS toolbar wording

Fix JS toolbar wording

--- app/assets/javascripts/toolbar.coffee
+++ app/assets/javascripts/toolbar.coffee
@@ -129,7 +129,7 @@
         if score < @threshold
           item.addClass "fold"
           where = item.children("h2").children(".anchor")
-          link = $('[+]').insertBefore(where)
+          link = $('[+]').insertBefore(where)
           link.after(' ').click ->
             item.removeClass "fold"
             link.remove()


Remove useless checks.

Remove useless checks.

--- src/MemoryState.coffee
+++ src/MemoryState.coffee
@@ -428,8 +428,7 @@

   # @private
   removeRoom : (name) ->
-    if @rooms.get name
-      @rooms.delete name
+    @rooms.delete name
     Promise.resolve()

   # @private
@@ -439,8 +438,7 @@

   # @private
   removeSocket : (id) ->
-    if @sockets.get id
-      @sockets.delete id
+    @sockets.delete id
     Promise.resolve()

   # @private
@@ -488,8 +486,7 @@

   # @private
   removeUser : (name) ->
-    if @users.get name
-      @users.delete name
+    @users.delete name
     Promise.resolve()


EmptyShell: properties => object literal notation

--- coffee/src/shells/empty.shell.coffee
+++ coffee/src/shells/empty.shell.coffee
@@ -3,14 +3,15 @@
 goog.require 'acorn.shells.Shell'
 goog.require 'acorn.config'

+Shell = acorn.shells.Shell

-Shell = acorn.shells.Shell
-EmptyShell = acorn.shells.EmptyShell
+EmptyShell = acorn.shells.EmptyShell =

-EmptyShell.id = 'acorn.EmptyShell'
-EmptyShell.title = 'EmptyShell'
-EmptyShell.description = 'an empty, nutless shell'
-EmptyShell.icon = 'icon-check-empty'
+  id: 'acorn.EmptyShell'
+  title: 'EmptyShell'
+  description: 'an empty, nutless shell'
+  icon: 'icon-check-empty'
+

 class EmptyShell.Model extends Shell.Model


Fixing js error if no tags

Fixing js error if no tags

--- app/coffee/modules/common/tags.coffee
+++ app/coffee/modules/common/tags.coffee
@@ -398,7 +398,7 @@
         $scope.$watch $attrs.ngModel, (model) ->
             return if not model

-            if model.tags.length
+            if model.tags?.length
                 hideAddTagButtonText()
             else
                 showAddTagButtonText()


Hotfix null items.

Hotfix null items.

--- tunemachine/spotify.coffee
+++ tunemachine/spotify.coffee
@@ -39,11 +39,13 @@
         if err?
           return next(err, null)

-        items = body.items.map (p) ->
-          result = {}
-          result.id = p.id
-          result.name = p.name
-          return result
+        items = []
+        if body.items?
+          items = body.items.map (p) ->
+            result = {}
+            result.id = p.id
+            result.name = p.name
+            return result

         values.playlists = values.playlists.concat items

@@ -70,13 +72,15 @@
         if err?
           return next(err, null)

-        items = body.items.map (p) ->
-          result = {}
-          result.id = p.track.id
-          result.name = p.track.name
-          result.artist = p.track.artists.map (a) -> a.name
-            .join ", "
-          return result
+        items = []
+        if body.items?
+          items = body.items.map (p) ->
+            result = {}
+            result.id = p.track.id
+            result.name = p.track.name
+            result.artist = p.track.artists.map (a) -> a.name
+              .join ", "
+            return result

         values.songs = values.songs.concat items


Only run Stagehand if Dart SDK is present.

--- lib/dart-tools.coffee
+++ lib/dart-tools.coffee
@@ -111,15 +111,16 @@
   registerGlobalCommands: =>
     Stagehand = require './stagehand/stagehand'
     atom.commands.add 'atom-workspace', 'dart-tools:stagehand', =>
-      if atom.project.getPaths().length is 0
-        atom.notifications.addInfo(
-          "[dart-tools] There is no open project to run Stagehand against."
-        )
-        return
-      atom.notifications.addInfo '[dart-tools] Activating Stagehand...'
-      Stagehand.activate().then =>
-        Stagehand.showProjectTemplates().then (projectTemplate) =>
-          Stagehand.generate(projectTemplate)
+      Utils.whenDartSdkFound =>
+        if atom.project.getPaths().length is 0
+          atom.notifications.addInfo(
+            "[dart-tools] There is no open project to run Stagehand against."
+          )
+          return
+        atom.notifications.addInfo '[dart-tools] Activating Stagehand...'
+        Stagehand.activate().then =>
+          Stagehand.showProjectTemplates().then (projectTemplate) =>
+            Stagehand.generate(projectTemplate)

   dispose: =>
     @subscriptions?.dispose()


Fix add dev ip/remove dev ip

Fix add dev ip/remove dev ip

AWS’s checks here recently became more restrictive

--- lib/deployment.coffee
+++ lib/deployment.coffee
@@ -411,9 +411,7 @@

     _deauthorizeIpAddress: (ipAddress, securityGroupId, cb) ->
         params = {
-            CidrIp: "#{ipAddress}/32",
             DryRun: false,
-            FromPort: 443,
             GroupId: securityGroupId,
             IpPermissions: [
                 {
@@ -426,9 +424,7 @@
                     ],
                     ToPort: 443
                 }
-            ],
-            IpProtocol: 'tcp',
-            ToPort: 443
+            ]
         }
         @ec2.revokeSecurityGroupIngress params, (err, data) ->
             if err
@@ -437,9 +433,7 @@

     _authorizeIpAddress: (ipAddress, securityGroupId, cb) ->
         params = {
-            CidrIp: "#{ipAddress}/32",
             DryRun: false,
-            FromPort: 443,
             GroupId: securityGroupId,
             IpPermissions: [
                 {
@@ -452,9 +446,7 @@
                     ],
                     ToPort: 443
                 }
-            ],
-            IpProtocol: 'tcp',
-            ToPort: 443
+            ]
         }
         @ec2.authorizeSecurityGroupIngress params, (err, data) ->
             if err


Minor: remove unnecessary `require`.

Minor: remove unnecessary `require`.

--- lib/dziparser._coffee
+++ lib/dziparser._coffee
@@ -1,6 +1,5 @@
 fs = require 'fs'
 xml2js = require 'xml2js'
-processor = require './processor'

 #Public API
 module.exports = class DZIParser


Add usage. Fix command. encode uri

Add usage. Fix command. encode uri

--- scripts/educast.coffee
+++ scripts/educast.coffee
@@ -1,4 +1,16 @@
+# Description:
+#   Search from to educast
+#
+# Dependencies:
+#   None
+#
+# Configuration:
+#   None
+#
+# Commands:
+#   hubot educast <keyword> - Search from educast

 module.exports = (robot) ->
-        robot.respond /educast (.*)/i, (res) ->
-                res.send "https://beta.educast.pro/search/?q=#{res.match[1]}"
+        robot.respond /educast\s+(.*)/i, (res) ->
+                keyword = encodeURIComponent(res.match[1])
+                res.send "https://beta.educast.pro/search/?q=#{keyword}"


don't use the wrong theme color value

don't use the wrong theme color value

--- app/javascript/legacy/settings.coffee
+++ app/javascript/legacy/settings.coffee
@@ -113,7 +113,7 @@

     $('#update_theme .color').each ->
       $this = $ this
-      this.value = '#' + getHexColorFromThemeValue(this.dataset.default)
+      this.value = '#' + getHexColorFromThemeValue(this.value)

       $this.minicolors
         control:      'hue'


making it rain

making it rain

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -11,6 +11,9 @@
 module.exports = (robot) ->

    robot.hear /make it rain/i, (msg) ->
+     msg.send "https://s3.amazonaws.com/uploads.hipchat.com/12562/184609/xdhwG85hEGYKVeR/rainingkev-2.gif"
+
+   robot.hear /making it rain/i, (msg) ->
      msg.send "https://s3.amazonaws.com/uploads.hipchat.com/12562/184609/xdhwG85hEGYKVeR/rainingkev-2.gif"

    robot.hear /rain bomb( (\d+))?/i, (msg) ->


fix typo

fix typo

--- coffeescript/love.js/graphics.coffee
+++ coffeescript/love.js/graphics.coffee
@@ -158,7 +158,7 @@
   getWidth: () =>
     @default_canvas.width

-  getWidth: () =>
+  getHeight: () =>
     @default_canvas.height

   # PRIVATE


Add missing node_modules segment

Add missing node_modules segment

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -64,7 +64,7 @@
       path.join('npm', 'node_modules', '.bin', 'starwars')
       path.join('pegjs', 'examples')
       path.join('get-parameter-names', 'node_modules', 'testla')
-      path.join('get-parameter-names', '.bin', 'testla')
+      path.join('get-parameter-names', 'node_modules', '.bin', 'testla')
       path.join('jasmine-reporters', 'ext')
       path.join('jasmine-node', 'node_modules', 'gaze')
       path.join('jasmine-node', 'spec')


Make #include string delimiters match preprocessor

--- grammars/csound.cson
+++ grammars/csound.cson
@@ -374,11 +374,11 @@
           }
           {
             name: 'string.quoted.include.csound'
-            begin: '"'
+            begin: '([^ \\t])'
             beginCaptures:
               0:
                 name: 'punctuation.definition.string.begin.csound'
-            end: '"'
+            end: '\\1'
             endCaptures:
               0:
                 name: 'punctuation.definition.string.end.csound'


Removed guided

Removed guided

--- app/js/jail_iframe/util/create.coffee
+++ app/js/jail_iframe/util/create.coffee
@@ -17,10 +17,9 @@
   text = selInfo.text
   siteUrl = FactlinkJailRoot.siteUrl()
   siteTitle = selInfo.title
-  guided = !!FactlinkConfig.guided

   FactlinkJailRoot.on 'modalOpened', success
-  FactlinkJailRoot.factlinkCoreEnvoy 'prepareNewFactlink', text, siteUrl, siteTitle, guided
+  FactlinkJailRoot.factlinkCoreEnvoy 'prepareNewFactlink', text, siteUrl, siteTitle

 # We make this a global function so it can be used for direct adding of facts
 # (Right click with chrome-extension)


Port ConvertStreetLight to Process API

Port ConvertStreetLight to Process API

--- components/ConvertStreetLight.coffee
+++ components/ConvertStreetLight.coffee
@@ -1,39 +1,35 @@
 noflo = require 'noflo'

-class ConvertStreetLight extends noflo.Component
-  icon: 'road'
-  description: 'Convert street light RGB values so we can send them to PWM'
-
-  constructor: ->
-    @inPorts = new noflo.InPorts
-      colors:
-        datatype: 'array'
-        description: 'Street light values'
-    @outPorts = new noflo.OutPorts
-      street1:
-        datatype: 'int'
-        addressable: true
-      street2:
-        datatype: 'int'
-        addressable: true
-      street3:
-        datatype: 'int'
-        addressable: true
-      street4:
-        datatype: 'int'
-        addressable: true
-
-    @inPorts.colors.on 'data', (data) =>
-      @convert data
-
-  convertLight: (light, colors) ->
-    for color, idx in colors
-      continue unless @outPorts.ports["street#{light}"].isAttached idx
-      @outPorts.ports["street#{light}"].send color, idx
-
-  convert: (data) ->
-    return unless data.length is 4
-    for light, idx in data
-      @convertLight idx+1, light
-
-exports.getComponent = -> new ConvertStreetLight
+exports.getComponent = ->
+  c = new noflo.Component
+  c.icon = 'road'
+  c.description = 'Convert street light RGB values so we can send them to PWM'
+  c.inPorts.add 'colors',
+    datatype: 'array'
+    description: 'Street light values'
+  c.outPorts.add 'street1',
+    datatype: 'int'
+    addressable: true
+  c.outPorts.add 'street2',
+    datatype: 'int'
+    addressable: true
+  c.outPorts.add 'street3',
+    datatype: 'int'
+    addressable: true
+  c.outPorts.add 'street4',
+    datatype: 'int'
+    addressable: true
+  c.process (input, output) ->
+    return unless input.hasData 'colors'
+    data = input.getData 'colors'
+    unless data.length is 4
+      output.done()
+      return
+    for light, target in data
+      for color, idx in light
+        light = target + 1
+        result = {}
+        result["street#{light}"] = new noflo.IP 'data', color,
+          index: idx
+        output.send result
+    output.done()


components: GioFileLoadContent: use custom object to carry data

--- components/GioFileLoadContent.coffee
+++ components/GioFileLoadContent.coffee
@@ -23,7 +23,9 @@
     try
       file = Gio.File.new_for_uri data
       [status, content, etag] = file.load_contents null
-      content.toString = -> "[C Buffer - length=#{@length}]" # prevent warnings
-      out.send content
+      out.send
+        data: content
+        length: content.length
+        toString = -> "[C Buffer - length=#{@length}]" # prevent warnings
     catch e
       c.error e, groups


Added authenticity_token

Added authenticity_token

--- mixirc/mixirc.coffee
+++ mixirc/mixirc.coffee
@@ -14,7 +14,8 @@
 userAgent = "mixirc <3"

 userList = {}
-ignoreList = []
+#ignoreList = []
+ignoreList = ["Jennibel", "Jennibelle"]
 ircBot = {}

 useFirebase = true
@@ -69,6 +70,8 @@
 		messageSplit: 512

 	ircBot.addListener 'message', ircMessage
+	console.log "Listening to IRC"
+

 postMix = (user, url, data) ->
 	jar = request.jar()
@@ -115,6 +118,7 @@
 	data = querystring.encode
 		"comment[content]": comment,
 		"comment[broadcaster_id]": channelId,
+		"authenticity_token": user.mixlrAuthToken
 	# An object of options to indicate where to post to
 	httpHeader =
 		hostname: 'mixlr.com'
@@ -245,7 +249,7 @@
 	channelId = data.config.channelId
 	userAgent = data.config.userAgent
 	userList = data.users
-	ignoreList = data.ignoreList
+	#ignoreList = data.ignoreList
 	startBot()

 # vim: set noet ts=4:


Don't register the default runtime

Don't register the default runtime

--- components/StoreRemoteRuntime.coffee
+++ components/StoreRemoteRuntime.coffee
@@ -24,6 +24,11 @@
       out.send data
       do callback
       return
+    if data.protocol is 'iframe' and data.address is 'https://noflojs.org/noflo-browser/everything.html?fbp_noload=true&fbp_protocol=iframe'
+      # No need to persist the default NoFlo runtime in registry.
+      out.send data
+      do callback
+      return

     unless c.params?.user?['flowhub-token']
       # User not logged in, persist runtime only locally


Fan speed control shouldn't blow up when invalid

--- lib/controls/FanSpeedControl.coffee
+++ lib/controls/FanSpeedControl.coffee
@@ -7,7 +7,7 @@
       target.getAspect('discreteSpeed').executeCommand 'set', params.value

   isActive: ->
-    speed = @getConnectionTarget('discreteSpeed').getAspect('discreteSpeed').
+    speed = @getConnectionTarget('discreteSpeed')?.getAspect('discreteSpeed').
       getDatum('state')
     speed? and speed != 'off'


convert gh-pages logs to @debug

convert gh-pages logs to @debug

--- lib/deployers/gh-pages/index.coffee
+++ lib/deployers/gh-pages/index.coffee
@@ -41,11 +41,11 @@
     @original_branch = execute('git rev-parse --abbrev-ref HEAD')
     if not @original_branch then return deferred.reject(@errors.make_commit)

-    console.log "starting on branch #{original_branch}"
+    @debug.log "starting on branch #{original_branch}"
     deferred.resolve()

   move_to_gh_pages_branch = ->
-    console.log 'switching to gh-pages branch'.grey
+    @debug.log 'switching to gh-pages branch'

     if not execute('git branch | grep gh-pages')
       execute('git branch -D gh-pages')
@@ -56,7 +56,7 @@

   remove_source_files = ->
     deferred = W.defer()
-    console.log 'removing source files'.grey
+    @debug.log 'removing source files'

     opts = { root: '', directoryFilter: ["!#{@public}", '!.git'] };

@@ -75,13 +75,13 @@
     fn.call()

   push_code = ->
-    console.log 'pushing to origin/gh-pages'.grey
+    @debug.log 'pushing to origin/gh-pages'
     execute "git push origin gh-pages --force"

-    console.log "switching back to #{@original_branch} branch".grey
+    @debug.log "switching back to #{@original_branch} branch"
     execute "git checkout #{@original_branch}"

-    console.log 'deployed to github pages'.grey
+    @debug.log 'deployed to github pages'
     fn.call()

   #


fix race condition returning from catch

fix race condition returning from catch

--- lib/deployers/gh-pages/index.coffee
+++ lib/deployers/gh-pages/index.coffee
@@ -72,13 +72,11 @@
     repo: @repo
     sha: @branch
   .then (res) -> res[0].sha
-  # there is a race condition here which causes occasional failures when
-  # deploying to empty repositories, because catch does not resolve returned
-  # promises. hopefully this will be fixed soon.
   .catch (err) =>
     msg = JSON.parse(err.message).message
-    if msg == 'Git Repository is empty.' then create_initial_commit.call(@)
-    if msg == 'Not Found' then create_branch.call(@)
+    if msg == 'Git Repository is empty.' then return create_initial_commit.call(@)
+    if msg == 'Not Found' then return create_branch.call(@)
+    throw err

 ###*
  * If a repo is empty, a commit needs to be created before trees can be pushed.


Add the todo on the flatten function.

Add the todo on the flatten function.

--- lib/helpers/metadata_builder.coffee
+++ lib/helpers/metadata_builder.coffee
@@ -102,6 +102,7 @@
         _.extend(metadata, overridenProperties) if not _.isEmpty(overridenProperties)
       return metadata
     constructEntityMetaDatas: (model)->
+      # TODO: pbn => Use a flatten function in order to flatten @metadatas and be ablt to access it without any problem.
       if model.modelName?
         mdName = model.modelName.split('.')
         if mdName.length is 1


Add onMessage functions

Add onMessage functions

--- tentacle-edison.coffee
+++ tentacle-edison.coffee
@@ -20,10 +20,13 @@
       @emit 'error', error

   onMessage: (message) =>
-    return unless message?.payload?
+    pins = _.map @config.pins, (pin) =>
+      try
+        @_processPin pin
+      catch error
+        @emit 'error', error

-    @messageTentacle _.extend({}, message.payload, topic: 'action')
-
+    @_emitPins pins

   onConfig: (config) =>
     @_clearBroadcast()
@@ -33,7 +36,7 @@

     @config.pins, (pin) =>
       try
-        @_setPinMode pin.number, pin.action
+        @_setPinAction pin
       catch error
         @emit 'error', error

@@ -47,11 +50,7 @@

   _broadcastPins: =>
     pins = _.compact _.map(@config.pins, @_readPin)
-    @emit 'message',
-      version: 1
-      topic: 'action'
-      pins: pins
-      response: true
+    @_emitPins pins

   _clearPins: =>
     _.times 19, _setDigitalWrite
@@ -60,23 +59,42 @@
     gpio = new mraa.Gpio pinNumber
     gpio.read()

+  _digitalWrite: (pinNumber, value) =>
+    gpio = new mraa.Gpio pinNumber
+    gpio.write value
+
+  _emitPins: (pins) =>
+    @emit 'message',
+      version: 1
+      topic: 'action'
+      pins: pins
+      response: true
+
+  _setPwmWrite: (pinNumber, value) =>
+    pwm = mraa.Pwm(pinNumber)
+    pwm.write value / 255.0
+
+  _processPin: (pin) =>
+    @_setPinAction pin
+    return @_readPin(pin) ? @_writePin(pin)
+
   _readPin: (pin) =>
     return unless _.contains ['digitalRead', 'analogRead'], pin.action

-    value = @_digitalRead pin.pinNumber if pin.action == 'digitalRead'
-    value = @_analogRead pin.pinNumber if pin.action == 'analogRead'
+    value = @_digitalRead pin.number if pin.action == 'digitalRead'
+    value = @_analogRead pin.number if pin.action == 'analogRead'

     {
       action: pin.action
-      number: pin.pinNumber
+      number: pin.number
       value:  value
     }

-  _setPinMode: (pinNumber, mode) =>
-    return @_setDigitalRead pinNumber  if mode == 'digitalRead'
-    return @_setDigitalWrite pinNumber if mode == 'digitalWrite'
-    return @_setAnalogRead pinNumber   if mode == 'analogRead'
-    return @_setPwmWrite pinNumber     if mode == 'pwmWrite'
+  _setPinAction: (pin) =>
+    return @_setDigitalRead pin.number  if pin.action == 'digitalRead'
+    return @_setDigitalWrite pin.number if pin.action == 'digitalWrite'
+    return @_setAnalogRead pin.number   if pin.action == 'analogRead'
+    return @_setPwmWrite pin.number     if pin.action == 'pwmWrite'

   _setAnalogRead: (pinNumber) =>
     aio = new mraa.Aio pinNumber
@@ -93,4 +111,16 @@
   _setPwmWrite: (pinNumber) =>
     pwm = new mraa.Pwm pinNumber

+  _writePin: (pin) =>
+    return unless _.contains ['digitalWrite', 'pwmWrite'], pin.action
+
+    @_digitalWrite pin.number, pin.value if pin.action == 'digitalRead'
+    @_pwmWrite pin.number, pin.value if pin.action == 'pwmWrite'
+
+    {
+      action: pin.action
+      number: pin.number
+      value:  pin.value
+    }
+
 module.exports = Tentacle


Fix spec failure

Fix spec failure

--- lib/keybinding-resolver-view.coffee
+++ lib/keybinding-resolver-view.coffee
@@ -54,7 +54,7 @@
       binding != keyBinding

     unmatchedKeyBindings = atom.keymap.findKeyBindings({keystrokes}).filter (binding) ->
-      binding != keyBinding and keyBinding not in unusedKeyBindings
+      binding != keyBinding and binding not in unusedKeyBindings

     @commands.html $$ ->
       @table class: 'table-condensed', =>


added more 'flavour'

added more 'flavour'

--- scripts/testing.coffee
+++ scripts/testing.coffee
@@ -14,21 +14,24 @@
     'yes ma\'am!'
     'man, oh man.'
     'you da main maing, maing!'
+    'yes, yes, very well.'
+    'work, work.'
   ]
   negative = [
     'aww'
     'hmm'
-    ''
+    'sigh'
   ]
   mistake = [
     'silly-billy'
     'oops'
-    'i\'m pregnant'
+    'i\'m pregnant, also'
     'well, this is awkward'
     'oh dear'
     'you too slow, maing'
     'uh-oh'
     'um, this is embarrassing'
+    'me not that kind of orc, also'
   ]

   servers =
@@ -172,7 +175,7 @@
     new Rcon(server, (ctx) ->
       ctx.exec("sm_say #{msg.match[2]}", (res) ->
         ctx.close()
-        return msg.reply "yeah, so, i delivered your damned message. now what?"
+        return msg.reply "#{msg.random(affirmative)} your message was delivered..."
       )
     )

@@ -185,7 +188,7 @@
     new Rcon(server, (ctx) ->
       ctx.exec("sm_say [ #tfbot ] #{Object.keys(previous.participants).join(' ')}", (res) ->
         ctx.close()
-        return msg.reply "kind of ironic that you're doing this from irc, isn't it? But, very well..."
+        return msg.reply "#{msg.random(affirmative)} player roster was delivered..."
       )
     )

@@ -198,7 +201,7 @@
       new Rcon(server, (ctx) ->
         ctx.exec("changelevel #{map}", (res) ->
           ctx.close()
-          return msg.reply "okay, okay, i'm changing the map already. calm yourself."
+          return msg.reply "#{msg.random(affirmative)} changing map as we speak..."
         )
       )
     else
@@ -208,9 +211,9 @@

     lobby = robot.brain.get('lobby')

-    return msg.reply 'um, this is embarrassing. it seems a pickup is already filling...' if lobby?
-
-    msg.send 'man, oh man, starting a new pickup...'
+    return msg.reply "#{msg.random(mistake)}, it seems a pickup is already filling..." if lobby?
+
+    msg.send "#{msg.random(affirmative)} starting a new pickup..."
     user = msg.message.user.id

     validMap = msg.match[2] in maps
@@ -230,14 +233,14 @@
   robot.respond /(cg|kill)/i, (msg) ->

     lobby = msg.message.user.id
-    return msg.reply 'hmm, there\'s no pickup filling...' unless lobby?
+    return msg.reply "#{msg.random(mistake)}, there\'s no pickup filling..." unless lobby?

     user = msg.message.user.id # check their auth if target isn't them

     # cancel the game
     robot.brain.set 'lobby', null

-    return msg.send 'aww, pickup cancelled...'
+    return msg.send "#{msg.random(negative)}, pickup cancelled..."

   robot.respond /add (me|.*)/i, (msg) ->
     lobby = robot.brain.get('lobby')
@@ -265,12 +268,12 @@

       return msg.reply "#{if msg.match[1] is 'me' then 'you are' else target + ' is'} already added..."

-    return msg.reply 'you too slow, maing. the pickup is already full...'
+    return msg.reply "#{msg.random(mistake)}, the pickup is already full..."

   robot.respond /rem (me|.*)/i, (msg) ->
     lobby = robot.brain.get('lobby')

-    return msg.reply 'silly-billy, there\'s no pickup filling...' if not lobby?
+    return msg.reply "#{msg.random(mistake)}, there\'s no pickup filling..." if not lobby?

     user = msg.message.user.id # check their auth if target isn't them
     target = if msg.match[1] is 'me' then user else msg.match[1]
@@ -287,7 +290,7 @@
   robot.respond /map (.*)/i, (msg) ->
     lobby = robot.brain.get('lobby')

-    return msg.reply 'silly-billy, there\'s no pickup filling...' unless lobby?
+    return msg.reply "#{msg.random(mistake)}, there\'s no pickup filling..." unless lobby?

     validMap = msg.match[2] in maps
     filtered = filterMaps(msg.match[2], maps)
@@ -301,26 +304,26 @@

     lobby.map = map
     robot.brain.set('lobby', lobby)
-    return msg.reply "yes ma\'am! changing map to #{map}..."
+    return msg.reply "#{msg.random(affirmative)} changing map to #{map}..."
     # very well, friend.

   robot.respond /server (.*)/i, (msg) ->
     lobby = robot.brain.get('lobby')

-    return msg.reply 'silly-billy, there\'s no pickup filling...' unless lobby?
+    return msg.reply "#{msg.random(mistake)}, there\'s no pickup filling..." unless lobby?

     user = msg.message.user.id # check their auth
     if msg.match[1] in serverList
       lobby.server = msg.match[1]
       robot.brain.set 'lobby', lobby
-      return msg.reply "as you wish, master. changing the server to #{msg.match[1]}..."
-
-    return msg.reply "oops, #{msg.match[1]} isn't a valid server..."
+      return msg.reply "#{msg.random(affirmative)} changing the server to #{msg.match[1]}..."
+
+    return msg.reply "#{msg.random(mistake)}, #{msg.match[1]} isn't a valid server..."

   robot.hear /^status$/i, (msg) ->
     lobby = robot.brain.get('lobby')

-    return msg.reply 'silly-billy, there\'s no pickup filling...' unless lobby?
+    return msg.reply "#{msg.random(mistake)}, there\'s no pickup filling..." unless lobby?

     players = Object.keys(lobby.participants)
     return msg.send "|| #{lobby.server} | #{lobby.map} | #{players.length}/12 | [ #{players.join(', ')} ] ||"


gruntfile

gruntfile

--- lib/json_utils.coffee
+++ lib/json_utils.coffee
@@ -51,7 +51,7 @@
 #            result[key] = value
 #            callback()

-      else if _.isArray(template)
+      if _.isArray(template)
         queue = new Queue()

         result = {}


Fixed serialization

Fixed serialization

--- lib/json_utils.coffee
+++ lib/json_utils.coffee
@@ -16,7 +16,7 @@
     return _.map(values, JSONUtils.parse) if _.isArray(values)
     if _.isObject(values)
       result = {}
-      result[key] = JSONUtils.parse(value) for key, value of values
+      result[key] = decodeURIComponent(JSONUtils.parse(value)) for key, value of values
       return result
     else if _.isString(values)
       # Date
@@ -25,28 +25,29 @@
         return if date and date.isValid() then date.toDate() else values
       # "quoted string"
       else if match = /^\"(.*)\"$/.exec(values)
-        return match[0]
+        return decodeURIComponent(match[0])
       # Boolean
       else
         return true if values is 'true'
         return false if values is 'false'
         # stringified JSON
         try
-          return JSONUtils.parse(values) if values = JSON.parse(values)
+          return decodeURIComponent(JSONUtils.parse(values)) if values = JSON.parse(values)
         catch err
-    return values
-
-  # Serialze json to a toQuery format.
+    return decodeURIComponent(values)
+
+  # Serialze json to a toQuery format. Note: the caller should use encodeURIComponent on all keys and values when added to URL
   #
   # @example
   #   query = JSONUtils.toQuery(json)
   #
-  @toQuery: (values) ->
+  @toQuery: (values, depth=0) ->
     if _.isArray(values)
       return JSON.stringify(values)
     else if _.isObject(values)
+      return JSON.stringify(values) if depth > 0
       result = {}
-      result[key] = JSON.stringify(value) for key, value of values
+      result[key] = JSONUtils.toQuery(value, 1) for key, value of values
       return result
     else if values.toJSON
       return values.toJSON()


add mime type to upload success response

--- cgFileUploadCtrl.coffee
+++ cgFileUploadCtrl.coffee
@@ -30,6 +30,7 @@
             try
                 file = JSON.parse(file) unless typeof file is 'object'
                 file.size = @_size
+                file.type = @_mimetype

             @onLoad?(file)
             @_disabled = false
@@ -107,6 +108,7 @@
         upload: (file) ->
             return unless file
             @_size = (file.size / Math.pow(1024, 2)).toFixed(2)
+            @_mimetype = file.type
             @onUploadStart?(
                 size: @_size
                 filename: file.name


Remove debug console.log calls

Remove debug console.log calls

--- lib/log-filter.coffee
+++ lib/log-filter.coffee
@@ -62,16 +62,12 @@
           indexesForLines.push(lineNumber)
         linesIndexes.push(indexesForLines)
       lineToDisplayIndexes = []
-      console.log (logEntriesArray)
-      console.log (linesIndexes)
       for logLine, i in logEntriesArray
-        console.log(regex.test(logLine)+" : ["+i+"]"+logLine)
         if regex.test(logLine) then else lineToDisplayIndexes = lineToDisplayIndexes.concat(linesIndexes[i])
       @results.text = lineToDisplayIndexes
     else
       @results.text = for line, i in buffer.getLines()
         if regex.test(line) then else i
-    console.log (@results)
     @filterLines()

   performLevelFilter: (scopes) ->


Better regex for definition, namespace and use search

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -81,9 +81,9 @@
         if className and className[0] == "\\"
             return className.substr(1) # FQCN, not subject to any further context.

-        usePattern = /(?:use)(?:[^\w\\\\])([\w\\\\]+)(?![\w\\\\])(?:(?:[ ]+as[ ]+)(\w+))?(?:;)/
-        namespacePattern = /(?:namespace)(?:[^\w\\\\])([\w\\\\]+)(?![\w\\\\])(?:;)/
-        definitionPattern = /(?:abstract class|class|trait|interface)\s+(\w+)/
+        usePattern = /^[ \t]*(?:use)(?:[^\w\\\\])([\w\\\\]+)(?![\w\\\\])(?:(?:[ ]+as[ ]+)(\w+))?(?:;)/
+        namespacePattern = /^[ \t]*(?:namespace)(?:[^\w\\\\])([\w\\\\]+)(?![\w\\\\])(?:;)/
+        definitionPattern = /^[ \t]*(?:abstract class|class|trait|interface)\s+(\w+)/

         text = editor.getText()


Update key.create due to API change

Update key.create due to API change

--- lib/models/key.coffee
+++ lib/models/key.coffee
@@ -22,9 +22,11 @@
 THE SOFTWARE.
 ###

+Promise = require('bluebird')
 _ = require('lodash')
 errors = require('resin-errors')
 pine = require('resin-pine')
+auth = require('../auth')

 ###*
 # @summary Get all ssh keys
@@ -126,8 +128,14 @@
 	# Avoid ugly whitespaces
 	key = key.trim()

-	return pine.post
-		resource: 'user__has__public_key'
-		body: { title, key }
-	.get('id')
+	Promise.props
+		userId: auth.getUserId()
+	.then (results) ->
+		return pine.post
+			resource: 'user__has__public_key'
+			body:
+				title: title
+				public_key: key
+				user: results.userId
+		.get('id')
 	.nodeify(callback)


Coverage testing now captures command coverage

--- test/basic.test.coffee
+++ test/basic.test.coffee
@@ -5,6 +5,7 @@
 copy = require('../src/util/copy')
 path = require('path')
 assert = require('chai').assert
+fs = require('fs')

 suite('basic', () ->
   @timeout(0)
@@ -57,18 +58,18 @@
       .then((out) ->
         assert.equal(
           out,
-                    '''
-          On branch master
-          nothing to commit, working directory clean
-
-          '''
-        )
-      )
-      .then -> exec('git ls-files')
-      .then((out) ->
-        assert.equal(
-          out,
-                    '''
+          '''
+          On branch master
+          nothing to commit, working directory clean
+
+          '''
+        )
+      )
+      .then -> exec('git ls-files')
+      .then((out) ->
+        assert.equal(
+          out,
+          '''
           .gitignore
           .npmrc
           package.json
@@ -107,6 +108,7 @@

   suite('coverage', () ->
     test('scafolding', () ->
+
       mkdirp(path.join(__dirname, 'pkgs/coverage'))
       .then -> chdir(path.join(__dirname, 'pkgs/coverage'))
       .then -> exec('git init')
@@ -144,7 +146,28 @@
     )

     test('version bump', () ->
-      exec('npm version major')
+
+      closeStdin = require('../src/steps/close-stdin')
+      readPackage = require('../src/steps/read-package')
+      argv = require('../src/argv')
+      actions =
+        preversion : require('../src/preversion')
+        version : require('../src/version')
+
+      Promise.resolve()
+      .then(() ->
+        actions.preversion(argv.parse('preversion'))
+      )
+      .then(() ->
+        readPackage()
+      )
+      .then((json) ->
+        json.version = '2.0.0'
+        fs.writeFileSync('package.json', JSON.stringify(json, null, 2))
+      )
+      .then(() ->
+        actions.version(argv.parse('version dist'))
+      )
       .then -> exec('git status')
       .then((out) ->
         assert.equal(


Mask page when main menu is active. PP-8

--- client/src/scripts/main-view.coffee
+++ client/src/scripts/main-view.coffee
@@ -3,8 +3,18 @@
   _pageViews = nodes$ []
   _modalViews = nodes$ []
   _isModal = lift$ _modalViews, (modalViews) -> modalViews.length > 0
+  _topic = node$ null
+  _isDisplayingTopics = node$ no
   _isListMasked = node$ no
-  _isPageMasked = node$ no
+  _isPageMasked = lift$ _isDisplayingTopics, identity
+  _topicTitle = lift$ _topic, _isDisplayingTopics, (topic, isDisplayingTopics) ->
+    if isDisplayingTopics then 'Menu' else if topic then topic.title else ''
+  toggleTopics = -> _isDisplayingTopics not _isDisplayingTopics()
+  apply$ _isDisplayingTopics, (isDisplayingTopics) ->
+    if isDisplayingTopics
+      _listViews.push topicListView
+    else
+      _listViews.remove topicListView

   createTopic = (title, handle, isEnabled) ->
     self =
@@ -51,17 +61,6 @@
     _clusterTopic = createTopic 'Cluster', null, no
     _administrationTopic = createTopic 'Administration', null, no
   ]
-
-  _topic = node$ null
-  _isDisplayingTopics = node$ no
-  _topicTitle = lift$ _topic, _isDisplayingTopics, (topic, isDisplayingTopics) ->
-    if isDisplayingTopics then 'Menu' else if topic then topic.title else ''
-  toggleTopics = -> _isDisplayingTopics not _isDisplayingTopics()
-  apply$ _isDisplayingTopics, (isDisplayingTopics) ->
-    if isDisplayingTopics
-      _listViews.push topicListView
-    else
-      _listViews.remove topicListView

   topicListView = Steam.TopicListView _, _topics
   frameListView = Steam.FrameListView _


Update patterns for gulp-filter

Update patterns for gulp-filter

--- tasks/resources.coffee
+++ tasks/resources.coffee
@@ -6,7 +6,7 @@

 # Move and process the resources for darwin64
 gulp.task 'resources:darwin', ->
-  templateFilter = filter ['*.plist', '*.json'], { restore: true }
+  templateFilter = filter ['**/*.plist', '**/*.json'], { restore: true }

   gulp.src './resources/darwin/**/*'
     .pipe templateFilter
@@ -16,7 +16,7 @@

 # Move and process the resources for linux32 and linux64
 gulp.task 'resources:linux', ->
-  templateFilter = filter ['*.desktop', '*.sh'], { restore: true }
+  templateFilter = filter ['**/*.desktop', '**/*.sh'], { restore: true }

   manifest.linux.name = manifest.name
   manifest.linux.productName = manifest.productName


updated event handlers to API1.0

updated event handlers to API1.0

--- lib/timekeeper.coffee
+++ lib/timekeeper.coffee
@@ -3,7 +3,7 @@
 url = require "url"

 # atom
-{$} = require 'atom'
+$ = require 'jquery'

 # timerkeeper
 Timer = require "./timer.coffee"
@@ -60,19 +60,20 @@
         # Setup event handlers - only if we are not in spec mode
         if atom.mode isnt "spec"
             # Render views
-            $( window ).on "ready", =>
+            $( window ).ready =>
                 # Attach the timer views
                 @timer.renderStatusBarViews()

                 # Call autostart to check if we want to autostart time tracking
                 @timer.autostart()

+            ## Below is severely deprecated, must fix
             # Track focus to set auto-pauses
             # Start/End autopause based on window focus
-            $( window ).on "blur", =>
+            $( window ).blur =>
                 # Just call the autopause method of the timer object
                 @timer.autopause()
-            $( window ).on "focus", =>
+            $( window ).focus =>
                 # Just call the autopause method of the timer object
                 @timer.autopause()


Add Project

Add Project

--- src/app/project.coffee
+++ src/app/project.coffee
@@ -97,26 +97,57 @@
   ignoreRepositoryPath: (path) ->
     config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path))

+  # Public: Given a path, this resolves it relative to the project directory.
+  #
+  # filePath - The {String} name of the path to convert
+  #
+  # Returns a {String}.
   resolve: (filePath) ->
     filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/'
     fsUtils.absolute filePath

+  # Public: Given a path, this makes it relative to the project directory.
+  #
+  # filePath - The {String} name of the path to convert
+  #
+  # Returns a {String}.
   relativize: (fullPath) ->
     return fullPath unless fullPath.lastIndexOf(@getPath()) is 0
     fullPath.replace(@getPath(), "").replace(/^\//, '')

+  # Public: Identifies if the project is using soft tabs.
+  #
+  # Returns a {Boolean}.
   getSoftTabs: -> @softTabs
+
+  # Public: Sets the project to use soft tabs.
+  #
+  # softTabs - A {Boolean} which, if `true`, sets soft tabs
   setSoftTabs: (@softTabs) ->

+  # Public: Identifies if the project is using soft wrapping.
+  #
+  # Returns a {Boolean}.
   getSoftWrap: -> @softWrap
+
+  # Public: Sets the project to use soft wrapping.
+  #
+  # softTabs - A {Boolean} which, if `true`, sets soft wrapping
   setSoftWrap: (@softWrap) ->

+  # Public: Given a path to a file, this constructs and associates a new {EditSession}.
+  #
+  # filePath - The {String} path of the file to associate with
+  # editSessionOptions - Options that you can pass to the `EditSession` constructor
+  #
+  # Returns an {EditSession}.
   buildEditSession: (filePath, editSessionOptions={}) ->
     if ImageEditSession.canOpen(filePath)
       new ImageEditSession(filePath)
     else
       @buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)

+  # Internal:
   buildEditSessionForBuffer: (buffer, editSessionOptions) ->
     options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
     options.project = this
@@ -126,27 +157,39 @@
     @trigger 'edit-session-created', editSession
     editSession

+  # Internal:
   defaultEditSessionOptions: ->
     tabLength: @tabLength
     softTabs: @getSoftTabs()
     softWrap: @getSoftWrap()

+  # Public: Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
+  #
+  # Returns an {Array} of {EditSession}s.
   getEditSessions: ->
     new Array(@editSessions...)

+  # Internal:
   eachEditSession: (callback) ->
     callback(editSession) for editSession in @getEditSessions()
     @on 'edit-session-created', (editSession) -> callback(editSession)
-
+
+  # Public: Removes an {EditSession} association from the project.
+  #
+  # Returns the removed {EditSession}.
   removeEditSession: (editSession) ->
     _.remove(@editSessions, editSession)
-
+
+  # Public: Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
+  #
+  # Returns an {Array} of {Buffer}s.
   getBuffers: ->
     buffers = []
     for editSession in @editSessions when not _.include(buffers, editSession.buffer)
       buffers.push editSession.buffer
     buffers

+  # Internal:
   eachBuffer: (args...) ->
     subscriber = args.shift() if args.length > 1
     callback = args.shift()
@@ -157,6 +200,15 @@
     else
       @on 'buffer-created', (buffer) -> callback(buffer)

+  # Public: Given a file path, this retrieves or creates a new {Buffer}.
+  #
+  # If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
+  # `text` is used as the contents of the new buffer.
+  #
+  # filePath - A {String} representing a path. If `null`, an "Untitled" buffer is created.
+  # text - The {String} text to use as a buffer, if the file doesn't have any contents
+  #
+  # Returns the {Buffer}.
   bufferForPath: (filePath, text) ->
     if filePath?
       filePath = @resolve(filePath)
@@ -166,6 +218,12 @@
     else
       @buildBuffer(null, text)

+  # Public: Given a file path, this sets its {Buffer}.
+  #
+  # filePath - A {String} representing a path
+  # text - The {String} text to use as a buffer
+  #
+  # Returns the {Buffer}.
   buildBuffer: (filePath, text) ->
     buffer = new Buffer(filePath, text)
     @buffers.push buffer
@@ -175,6 +233,10 @@
   removeBuffer: (buffer) ->
     _.remove(@buffers, buffer)

+  # Public: Performs a search across all the files in the project.
+  #
+  # regex - A {RegExp} to search with
+  # iterator - A {Function} callback on each file found
   scan: (regex, iterator) ->
     bufferedData = ""
     state = 'readingPath'


Don't start apps that don't have an imageId

--- src/application.coffee
+++ src/application.coffee
@@ -135,6 +135,7 @@
 			throw err

 application.start = start = (app) ->
+	return if !app.imageId?
 	volumes =
 		'/data': {}
 		'/lib/modules': {}
@@ -590,9 +591,10 @@
 	knex('app').select()
 	.then (apps) ->
 		Promise.map apps, (app) ->
-			executeSpecialActionsAndBootConfig(JSON.parse(app.env))
-			.then ->
-				unlockAndStart(app)
+			if app.imageId?
+				executeSpecialActionsAndBootConfig(JSON.parse(app.env))
+				.then ->
+					unlockAndStart(app)
 	.catch (error) ->
 		console.error('Error starting apps:', error)
 	.then ->


Cleanup

Cleanup

--- SingularityUI/app/views/tasks.coffee
+++ SingularityUI/app/views/tasks.coffee
@@ -6,7 +6,7 @@
     templateTasksScheduled: require './templates/tasksScheduled'
     templateTasksCleaning: require './templates/tasksCleaning'

-    killTaskTemplate = require './templates/vex/killTask'
+    killTaskTemplate: require './templates/vex/killTask'

     render: (tasksFilter) =>
         if tasksFilter is 'active'
@@ -46,7 +46,7 @@
             taskModel = @collection.get($(e.target).data('task-id'))

             vex.dialog.confirm
-                message: killTaskTemplate(taskId: taskModel.get('id'))
+                message: @killTaskTemplate(taskId: taskModel.get('id'))
                 callback: (confirmed) =>
                     return unless confirmed
                     taskModel.destroy()


Update zen.config.coffee

Update zen.config.coffee

--- lib/zen.config.coffee
+++ lib/zen.config.coffee
@@ -20,7 +20,7 @@

   # -- ZEN port ----------------------------------------------------------------
   ZEN.port = process.argv[4] if process.argv[4]?
-  ZEN.port = process.env.port if process.env.port?
+  ZEN.port = process.env.PORT if process.env.PORT?

   # -- ZEN timezone ------------------------------------------------------------
   process.env.TZ = ZEN.timezone if ZEN.timezone


Perform tests within callbacks to wait gobgp process appropriately

--- test/gobgp-node.coffee
+++ test/gobgp-node.coffee
@@ -39,20 +39,18 @@
     it 'originates a route', ->
       expect(global_rib()).to.be.empty

-      gobgp.addPath family: 'ipv4-unicast', PREFIX
-
-      prefixes = global_rib(PREFIX)
-      expect(prefixes.length).to.equal 1
+      gobgp.addPath family: 'ipv4-unicast', PREFIX, ->
+        prefixes = global_rib(PREFIX)
+        expect(prefixes.length).to.equal 1

     it 'originates a route with BGP community string', ->
       expect(global_rib()).to.be.empty

-      gobgp.addPath family: 'ipv4-unicast', "#{PREFIX} community no-advertise"
+      gobgp.addPath family: 'ipv4-unicast', "#{PREFIX} community no-advertise", ->
+        prefixes = global_rib(PREFIX)
+        expect(prefixes.length).to.equal 1

-      prefixes = global_rib(PREFIX)
-      expect(prefixes.length).to.equal 1
-
-      expect(communities(PREFIX)).to.eql [4294967042]
+        expect(communities(PREFIX)).to.eql [4294967042]

     it 'originates a route with BGP community byte array', ->
       expect(global_rib()).to.be.empty
@@ -64,12 +62,11 @@
         0x04,                      # Length
         0xff, 0xff, 0xff, 0x02])  # NO_ADVERTISE

-      gobgp.addPath family: 'ipv4-unicast', path
+      gobgp.addPath family: 'ipv4-unicast', path, ->
+        prefixes = global_rib(PREFIX)
+        expect(prefixes.length).to.equal 1

-      prefixes = global_rib(PREFIX)
-      expect(prefixes.length).to.equal 1
-
-      expect(communities(PREFIX)).to.eql [4294967042]
+        expect(communities(PREFIX)).to.eql [4294967042]

     it 'shows the RIB', ->  # TODO: This does actually nothing. Use chai-as-promised
       exec "gobgp global rib add #{PREFIX}"
@@ -86,19 +83,17 @@
       exec "gobgp global rib add #{PREFIX}"
       expect(global_rib()).not.to.be.empty

-      gobgp.deletePath family: 'ipv4-unicast', PREFIX
-
-      expect(global_rib()).to.be.empty
+      gobgp.deletePath family: 'ipv4-unicast', PREFIX, ->
+        expect(global_rib()).to.be.empty

   describe 'family ipv4-flowspec', ->
     it 'originates a route', ->
       expect(flowspec_rib()).to.be.empty

-      gobgp.addPath family: 'ipv4-flowspec', FLOWSPEC_PREFIX
-
-      prefixes = flowspec_rib(FLOWSPEC_JSON_PREFIX)
-      expect(prefixes.length).to.equal 1
+      gobgp.addPath family: 'ipv4-flowspec', FLOWSPEC_PREFIX, ->
+        prefixes = flowspec_rib(FLOWSPEC_JSON_PREFIX)
+        expect(prefixes.length).to.equal 1

     it 'shows the RIB', ->  # TODO: This does actually nothing. Use chai-as-promised
       exec "gobgp global rib -a ipv4-flowspec add #{FLOWSPEC_PREFIX}"
@@ -116,24 +111,21 @@
       exec "gobgp global rib -a ipv4-flowspec add #{FLOWSPEC_PREFIX}"
       expect(flowspec_rib()).not.to.be.empty

-      gobgp.deletePath family: 'ipv4-flowspec', FLOWSPEC_PREFIX
-
-      expect(flowspec_rib()).to.be.empty
+      gobgp.deletePath family: 'ipv4-flowspec', FLOWSPEC_PREFIX, ->
+        expect(flowspec_rib()).to.be.empty

   describe 'backward compatibility for modPath', ->
     it 'originates a route', ->
       expect(global_rib()).to.be.empty

-      gobgp.modPath family: 'ipv4-unicast', PREFIX
-
-      prefixes = global_rib(PREFIX)
-      expect(prefixes.length).to.equal 1
+      gobgp.modPath family: 'ipv4-unicast', PREFIX, ->
+        prefixes = global_rib(PREFIX)
+        expect(prefixes.length).to.equal 1

     it 'withdraws a route', ->
       exec "gobgp global rib add #{PREFIX}"
       expect(global_rib()).not.to.be.empty

-      gobgp.modPath family: 'ipv4-unicast', withdraw: true, PREFIX
-
-      expect(global_rib()).to.be.empty
+      gobgp.modPath family: 'ipv4-unicast', withdraw: true, PREFIX, ->
+        expect(global_rib()).to.be.empty


Makes div the same size as the countdown clock

This fixes layout problems that I've had with this since the div has a default height and width of 0px x 0px

Changed code relative to #31 since it didn't work

--- coffee/react-countdown-clock.coffee
+++ coffee/react-countdown-clock.coffee
@@ -174,7 +174,7 @@
     @_timer.fill()

   render: ->
-

+

       <canvas ref='background' style={ position: 'absolute' } width={@props.size} height={@props.size}></canvas>
       <canvas ref='timer' style={ position: 'absolute' } width={@props.size} height={@props.size}></canvas>


Compositor: actually fix compositing several passes.

--- engine/compositor.coffee
+++ engine/compositor.coffee
@@ -104,11 +104,11 @@
             coord = vertex.xy;
             gl_Position = vec4((vertex.xy*2.0)-vec2(1.0,1.0), 0.0, 1.0);
         }"""
-        uniforms = []
         @buffers = @options.buffers
         # TODO: resolve and concatenate filters with 1:1 intermediate result
         # and measure in different platforms if that's better than flipping buffers
         @filters = for filter_name,filter_options of @options.filters
+            uniforms = []
             fs_code = """precision highp float;
             varying vec2 coord;"""
             for name, {buffer} of @buffers
@@ -180,13 +180,7 @@

     compose: ->
         {gl, uniform_functions} = @context.render_manager
-        # Bind textures
-        # TODO: Should we unbind the texture of the output buffer?
-        i = 0
-        for name, {buffer, texture} of @buffers
-            texture = texture or buffer.texture
-            gl.activeTexture gl.TEXTURE0 + i++
-            gl.bindTexture gl.TEXTURE_2D, texture
+        gl.disable gl.DEPTH_TEST
         # Bind quad mesh
         gl.bindBuffer gl.ARRAY_BUFFER, @context.render_manager.quad

@@ -194,6 +188,16 @@
         # but eventually we'll order it in the constructor evaluating dependencies
         for {material, output} in @filters
             prog = material.use()
+            # Bind textures
+            i = 0
+            # Note: relying on having the same order in "for .. of"
+            for name, {buffer, texture} of @buffers
+                # It may be a Texture instance, a WebGL texture or a buffer
+                if output.buffer != buffer
+                    texture = texture?.gl_tex or texture or buffer.texture
+                    gl.activeTexture gl.TEXTURE0 + i
+                    gl.bindTexture gl.TEXTURE_2D, texture
+                i++
             # Configure attributes
             @context.render_manager.change_enabled_attributes 1<<material.a_vertex
             gl.vertexAttribPointer material.a_vertex, 3.0, gl.FLOAT, false, 0, 0
@@ -206,6 +210,7 @@
             size = output.size or [output.buffer.size_x, output.buffer.size_y]
             output.buffer.enable([0, 0, size[0], size[1]])
             gl.drawArrays gl.TRIANGLE_STRIP, 0, 4
+        gl.enable gl.DEPTH_TEST
         return


Make links in CKEditor text target _blank by default. Fixes #16

As seen in http://ckeditor.com/forums/CKEditor-3.x/Default-Target-blank-for-link

--- app/assets/javascripts/ckeditor/config.js.coffee
+++ app/assets/javascripts/ckeditor/config.js.coffee
@@ -2,7 +2,7 @@
 CKEDITOR.editorConfig = (config) ->
   config.height = '500'
   config.allowedContent = true
-
+
   config.language = 'es'
   config.scayt_sLang = 'es_ES'
   config.scayt_autoStartup = true
@@ -23,4 +23,7 @@
   config.toolbar = 'Pure'
   config.toolbarCanCollapse = true
   config.toolbarStartupExpanded = false
-  true+  true
+
+CKEDITOR.on "dialogDefinition", (ev) ->
+  ev.data.definition.getContents("target").get("linkTargetType")["default"] = "_blank"  if ev.data.name is "link"


Fix parent getter

Fix parent getter

--- lib/Choice.coffee
+++ lib/Choice.coffee
@@ -59,7 +59,7 @@
     branch

   get: (name) ->
-    return @attributes[name] if @attributes[name] isnt 'undefined'
+    return @attributes[name] if typeof @attributes[name] isnt 'undefined'
     return null unless @source
     @source.get name


bugfix: a small one

bugfix: a small one

--- app/assets/javascripts/ajax.js.coffee
+++ app/assets/javascripts/ajax.js.coffee
@@ -26,17 +26,20 @@

 project_is_updated = (p_id, tested_at) ->
-  $("#project-tile-#{p_id}").data('tested-at') < tested_at
+  server_time = tested_at
+  client_time = $("#project-tile-#{p_id}").data('tested-at')
+  console.debug("Project #{p_id}: #{client_time} < #{server_time} ? #{client_time < server_time}")
+  client_time < server_time

 update_project_tile = (p_id) ->
+  console.debug("Updating project #{p_id}...")
   prefix   = $('#path-prefix').data('path-prefix')
   tile_url = prefix + '/ajax/' + "project_tile?project_id=#{p_id}"
   $.get(tile_url)
     .done( (html) ->
       $("#project-tile-#{p_id}").replaceWith(html)
       $("#project-tile-#{p_id} abbr.timeago").timeago();
-      console.debug(html)
       )

@@ -49,7 +52,7 @@
   if query
     $.post(prefix + '/ajax/' + query)
       .done( (data) ->
-        console.debug(JSON.stringify(data, undefined, 2))
+        # console.debug(JSON.stringify(data, undefined, 2))

         # Update activity indicators
         unless $("#server-status").hasClass('fa-signal')
@@ -61,6 +64,10 @@
         for p_id, proj of data.projects
           if proj.queued == 'false' and project_is_updated(p_id, proj.tested_at)
             update_project_tile(p_id)
+
+        $('.panel-body, .panel-heading, .panel-footer').click ->
+          Turbolinks.visit( $(@).parent().data('url') )
+
         )


Indent

Indent

--- app/assets/javascripts/events_calendar.js.coffee
+++ app/assets/javascripts/events_calendar.js.coffee
@@ -34,7 +34,7 @@
     maxTime: '20:00'

     events:
-        url: '/events/calendar.json'
+      url: '/events/calendar.json'

     eventDrop: eventUpdate
     eventResize: eventUpdate


removed incomplete test

removed incomplete test

--- apps/fair/test/client/overview.coffee
+++ apps/fair/test/client/overview.coffee
@@ -51,14 +51,3 @@

       view.$('.container-left .large-section-subheading').length.should.equal 1
       view.$('.container-left .large-section-subheading').text().should.containEql 'Pablo Picasso'
-
-
-    it 'syncs the following collection with the fair id', ->
-      view = new @OverviewView
-        el: @$el
-        fair: @fair
-        model: @model
-
-      syncFollowsSpy = sinon.spy @view.following, 'syncFollows'
-      @view.setupFollowButtons()
-      syncFollowsSpy.args[0][0].should.containEql @fair.id


update dashboard measure count

update dashboard measure count

This exposes one of the issues with Thorax... it doesn't have baked-in support for updating small aspects of the UI when properties change. We got around this by using jQuery to update the UI, but this is brittle as it forces us to keep the views up to date when the markup changes, and there aren't any explicit connections between the two files.

--- app/assets/javascripts/views/dashboard.js.coffee
+++ app/assets/javascripts/views/dashboard.js.coffee
@@ -133,7 +133,7 @@
       $("#filters .panel-body .checkbox:containsi(#{query})").show() # show matching measures
       $("#filters .panel:containsi(#{query})").next('.panel-collapse').find('.checkbox').show() # show matching categories

-      $('#filters .panel-body').not(':has(.checkbox:visible)').parent().prev('.panel').hide() # hide empty category titles
+      $('#filters .panel-body').not(':has(.checkbox:visible)').parent().prev('.panel-heading').hide() # hide empty category titles
       $('#filters .panel-body').not(':has(.checkbox:visible)').hide() # hide empty category measure containers
     else
       $('#filters .panel').show() # show all category titles
@@ -156,13 +156,13 @@
       @selectedCategories.selectMeasure measure
     else
       @selectedCategories.removeMeasure measure
-    $cb.closest('.panel-collapse').prev('.panel').find('.measure-count').text $cbs.filter(':checked').length
+    $cb.closest('.panel-collapse').prev('.panel-heading').find('.measure-count').text $cbs.filter(':checked').length

   toggleCategory: (e) ->
     # change DOM
     $cb = $(e.target)
     $cb.closest('.panel-body').find(':checkbox.individual').prop 'checked', $cb.is(':checked')
-    $measureCount = $cb.closest('.panel-collapse').prev('.panel').find('.measure-count')
+    $measureCount = $cb.closest('.panel-collapse').prev('.panel-heading').find('.measure-count')
     # change models
     category = $cb.model()
     if $cb.is(':checked')


improved Logger.print function

improved Logger.print function

--- lib/Logger.coffee
+++ lib/Logger.coffee
@@ -26,10 +26,18 @@
     @add 'info', "#{chalk.gray("info")}: #{msg}"

   @print: (messages)->
-    haveWarningOrErrorMessages =
+
+    isFinalMessage = false
+
+    isWarningOrErrorMessage =
       (messages['warning'].length isnt 0) or (messages['error'].length isnt 0)

-    console.log()
+    isSuccessOrInfoMessage = (type) ->
+      return false if isFinalMessage
+      isFinalMessage = true if (type is 'success') or (type is 'info')
+
+    console.log() if isWarningOrErrorMessage
+
     for type in Logger.types
-      console.log() if haveWarningOrErrorMessages
+      console.log() if isSuccessOrInfoMessage(type)
       console.log message for message in messages[type]


clear reference_genome when selected vicuna #40

--- app/assets/javascripts/jobs.js.coffee
+++ app/assets/javascripts/jobs.js.coffee
@@ -9,4 +9,5 @@
   if /bowtie/.test($('#job_tool option:selected').text())
     $('#reference_genome').show()
   else
+    $('#job_reference_genome').val('')
     $('#reference_genome').hide()


Deactivates zoom controls on "picture" map

--- app/assets/javascripts/maps.js.coffee
+++ app/assets/javascripts/maps.js.coffee
@@ -36,12 +36,12 @@
             layer.addTo map
           return

-        # Zoom
-        map.addControl L.control.zoom(
-          position: "topleft"
-          zoomInText: ""
-          zoomOutText: ""
-        )
+        # # Zoom
+        # map.addControl L.control.zoom(
+        #   position: "topleft"
+        #   zoomInText: ""
+        #   zoomOutText: ""
+        # )
         # Scale
         map.addControl new L.Control.Scale(
           imperial: false


Add Ajax

Add Ajax

--- app/assets/javascripts/tickets.coffee
+++ app/assets/javascripts/tickets.coffee
@@ -1,3 +1,20 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
+
+$(document).on 'ajax:success', '#createTicket', (xhr, data, status) ->
+  location.reload()
+
+$(document).on 'ajax:error', '#createTicket', (xhr, data, status) ->
+  form = $('#new_ticket .modal-body')
+  div = $('

')
+  ul = $('

')
+  data.responseJSON.messages.forEach (message, i) ->
+    li = $('

').text(message)
+    ul.append(li)
+
+  if $('#createTicketErrors')[0]
+    $('#createTicketErrors').html(ul)
+  else
+    div.append(ul)
+    form.prepend(div)


Ensure xray bar works after Turbolinks navigation

--- app/assets/javascripts/xray.js.coffee
+++ app/assets/javascripts/xray.js.coffee
@@ -155,7 +155,7 @@
     @reset()
     Xray.isShowing = true
     util.bm 'show', =>
-      @bar.$el.find('#xray-bar-togglers .xray-bar-btn').removeClass('active')
+      @bar.$el().find('#xray-bar-togglers .xray-bar-btn').removeClass('active')
       unless @$overlay.is(':visible')
         $('body').append @$overlay
         @bar.show()
@@ -163,14 +163,14 @@
         when 'templates'
           Xray.findTemplates()
           specimens = Xray.TemplateSpecimen.all
-          @bar.$el.find('.xray-bar-templates-toggler').addClass('active')
+          @bar.$el().find('.xray-bar-templates-toggler').addClass('active')
         when 'views'
           specimens = Xray.ViewSpecimen.all
-          @bar.$el.find('.xray-bar-views-toggler').addClass('active')
+          @bar.$el().find('.xray-bar-views-toggler').addClass('active')
         else
           Xray.findTemplates()
           specimens = Xray.specimens()
-          @bar.$el.find('.xray-bar-all-toggler').addClass('active')
+          @bar.$el().find('.xray-bar-all-toggler').addClass('active')
       for element in specimens
         continue unless element.isVisible()
         element.makeBox()
@@ -196,37 +196,51 @@
 # toggle buttons for showing the different types of specimens in the overlay.
 class Xray.Bar
   constructor: (el) ->
-    @$el = $(el)
-    @$el.css(zIndex: MAX_ZINDEX)
-    @$el.find('#xray-bar-controller-path .xray-bar-btn').click ->
+    @el = el
+
+  # Defer wiring up jQuery event handlers until needed and then memoize the
+  # result. If the Bar element no longer exists in the DOM, re-wire it.
+  # This allows the Bar to keep working even if e.g. Turbolinks replaces the
+  # DOM out from under us.
+  $el: ->
+    return @$el_memo if @$el_memo? && $.contains(window.document, @$el_memo[0])
+    @$el_memo = $(@el)
+    @$el_memo.css(zIndex: MAX_ZINDEX)
+    @$el_memo.find('#xray-bar-controller-path .xray-bar-btn').click ->
       Xray.open($(this).attr('data-path'))
-    @$el.find('.xray-bar-all-toggler').click       -> Xray.show()
-    @$el.find('.xray-bar-templates-toggler').click -> Xray.show('templates')
-    @$el.find('.xray-bar-views-toggler').click     -> Xray.show('views')
-    @$el.find('.xray-bar-settings-btn').click      -> Xray.toggleSettings()
+    @$el_memo.find('.xray-bar-all-toggler').click       -> Xray.show()
+    @$el_memo.find('.xray-bar-templates-toggler').click -> Xray.show('templates')
+    @$el_memo.find('.xray-bar-views-toggler').click     -> Xray.show('views')
+    @$el_memo.find('.xray-bar-settings-btn').click      -> Xray.toggleSettings()
+    @$el_memo

   show: ->
-    @$el.show()
+    @$el().show()
     @originalPadding = parseInt $('html').css('padding-bottom')
     if @originalPadding < 40
       $('html').css paddingBottom: 40

   hide: ->
-    @$el.hide()
+    @$el().hide()
     $('html').css paddingBottom: @originalPadding

 class Xray.Settings
   constructor: (el) ->
-    @$el = $(el)
-    @$el.find('form').submit @save
+    @el = el
+
+  $el: ->
+    return @$el_memo if @$el_memo? && $.contains(window.document, @$el_memo[0])
+    @$el_memo = $(@el)
+    @$el_memo.find('form').submit @save
+    @$el_memo

   toggle: =>
-    @$el.toggle()
+    @$el().toggle()

   save: (e) =>
     e.preventDefault()
-    editor = @$el.find('#xray-editor-input').val()
+    editor = @$el().find('#xray-editor-input').val()
     $.ajax
       url: '/_xray/config'
       type: 'POST'
@@ -239,7 +253,7 @@
       $msg = $("Success!")
     else
       $msg = $("Uh oh, something went wrong!")
-    @$el.append($msg)
+    @$el().append($msg)
     $msg.delay(2000).fadeOut(500, => $msg.remove(); @toggle())


Update link

Update link

--- examples/Styleguide.cjsx
+++ examples/Styleguide.cjsx
@@ -19,7 +19,7 @@

An opinionated toolkit for building websites with beautiful typography.

-        Code on Github
+        Code on Github


Don’t use Sequence in PaneAxis

Don’t use Sequence in PaneAxis

--- src/pane-axis.coffee
+++ src/pane-axis.coffee
@@ -1,4 +1,4 @@
-{Model, Sequence} = require 'theorist'
+{Model} = require 'theorist'
 {flatten} = require 'underscore-plus'
 Serializable = require 'serializable'

@@ -11,14 +11,9 @@
   Serializable.includeInto(this)

   constructor: ({@container, @orientation, children}) ->
-    @children = Sequence.fromArray(children ? [])
-
-    @subscribe @children.onEach (child) =>
-      child.parent = this
-      child.container = @container
-      @subscribe child, 'destroyed', => @removeChild(child)
-
-    @subscribe @children.onRemoval (child) => @unsubscribe(child)
+    @children = []
+    if children?
+      @addChild(child) for child in children

   deserializeParams: (params) ->
     {container} = params
@@ -40,27 +35,40 @@

   addChild: (child, index=@children.length) ->
     @children.splice(index, 0, child)
+    @childAdded(child)

   removeChild: (child) ->
     index = @children.indexOf(child)
     throw new Error("Removing non-existent child") if index is -1
     @children.splice(index, 1)
+    @childRemoved(child)
     @reparentLastChild() if @children.length < 2
-

   replaceChild: (oldChild, newChild) ->
     index = @children.indexOf(oldChild)
     throw new Error("Replacing non-existent child") if index is -1
     @children.splice(index, 1, newChild)
+    @childRemoved(oldChild)
+    @childAdded(newChild)

   insertChildBefore: (currentChild, newChild) ->
     index = @children.indexOf(currentChild)
     @children.splice(index, 0, newChild)
+    @childAdded(newChild)

   insertChildAfter: (currentChild, newChild) ->
     index = @children.indexOf(currentChild)
     @children.splice(index + 1, 0, newChild)
+    @childAdded(newChild)

   reparentLastChild: ->
     @parent.replaceChild(this, @children[0])
     @destroy()
+
+  childAdded: (child) ->
+    child.parent = this
+    child.container = @container
+    @subscribe child, 'destroyed', => @removeChild(child)
+
+  childRemoved: (child) ->
+    @unsubscribe child


Fix chart and legend data not switching correctly.

- The chart now unloads previous data when updated.
- The legend elements are now bound by the item name instead of the
  index and are removed as appropriate when the data is changed.

--- app/assets/javascripts/areas.coffee
+++ app/assets/javascripts/areas.coffee
@@ -44,30 +44,39 @@
   # Custom chart legend
   renderLegend = (data) ->
     # Append li
-    d3.select('.legend').selectAll('li').data(data["legend"])
-      .enter().append 'li'
-      .attr('class', 'key', 'data-id', (id) -> id)
-      .html((id) -> id
-    ).each((id) ->
-      li = d3.select(this)
-      # Add toggle bubble
-      span = li.insert "span"
-      # Style toggle bubble
-      span.attr("class", "toggle").style('background-color', chart.color(id))
-    ).on('mouseover', (id) ->
-      # Mouseover focus data
-      chart.focus id
-    ).on('mouseout', (id) ->
-      chart.revert
-    ).on 'click', (id) ->
-      # On click toggle class
-      $(this).toggleClass 'active'
-      chart.toggle id
+    p = d3
+      .select(".legend").selectAll("li")
+      .data(data["legend"], (d) -> d)
+
+    p
+      .enter()
+        .append "li"
+        .attr "class", "key"
+        .html (id) -> id
+        .each (id) ->
+          li = d3.select(this)
+          # Add toggle bubble
+          span = li.insert "span"
+          # Style toggle bubble
+          span.attr("class", "toggle").style("background-color", chart.color(id))
+
+      .on "mouseover", (id) ->
+        chart.focus id
+
+      .on "mouseout", (id) ->
+        chart.revert
+
+      .on "click", (id) ->
+        $(this).toggleClass "active"
+        chart.toggle id
+
+    p.exit().remove()

   updateChart = (data) =>
     updateChartHeader(data)
     chart.load
       columns: data["columns"]
+      unload: true
     renderLegend(data)

   # Update chart header on load