report complexity per line for the class

--- chuck.coffee
+++ chuck.coffee
@@ -106,6 +106,7 @@
   metrics.linesPerMethod = metrics.lines / metrics.methods.length
   metrics.complexity = sum(m.complexity for m in metrics.methods)
   metrics.complexityPerMethod = metrics.complexity / metrics.methods.length
+  metrics.complexityPerLine = metrics.complexity / metrics.lines
   metrics

 module.exports =


Added 'file:///' to presentation script

Added 'file:///' to presentation script

Firefox won't crash when exporting (or viewing) presentation HTML.

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -525,7 +525,7 @@

       runBtn = codeChunk.getElementsByClassName('run-btn')[0]
       runBtn?.addEventListener 'click', ()=>
-        @runCodeChunk(codeChunk)
+        @runCodeChunk(codeChunk)

       runAllBtn = codeChunk.getElementsByClassName('run-all-btn')[0]
       runAllBtn?.addEventListener 'click', ()=>
@@ -913,7 +913,7 @@
     if slideConfigs.length
       htmlContent = @parseSlidesForExport(htmlContent, slideConfigs, isSavingToHTML)
       if offline
-        presentationScript = "<script src='#{path.resolve(__dirname, '../dependencies/reveal/js/reveal.js')}'></script>"
+        presentationScript = "<script src='file:///#{path.resolve(__dirname, '../dependencies/reveal/js/reveal.js')}'></script>"
       else
         presentationScript = "<script src='https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.3.0/js/reveal.min.js'></script>"


rollbar: notify shared hosting failures

rollbar: notify shared hosting failures

--- client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -101,6 +101,8 @@

       kiteFailureTimer = @utils.wait 5000, =>
         unless @treeLoaded
+          KD.logToExternal "SharedHosting fetching files failed"
+
           msg = "Couldn't fetch files! Click to retry"
           @treeController.notify msg, 'clickable', "Sorry, a problem occured while communicating with servers, please try again later.", yes
           log msg+" fired from initial kiteFailureTimer"


new: propagate caller user information to environment variables

--- src/shellcmd.coffee
+++ src/shellcmd.coffee
@@ -25,20 +25,27 @@
 process.env.HUBOT_SHELLCMD_KEYWORD = "shellcmd" if not process.env.HUBOT_SHELLCMD_KEYWORD

 module.exports = (robot) ->
-  run_cmd = (cmd, args, cb ) ->
+  run_cmd = (cmd, args, envs, cb ) ->
+    console.log "spawn!"
     spawn = require("child_process").spawn
-    child = spawn(cmd, args)
+    opts =
+        env: envs
+    child = spawn(cmd, args, opts)
     child.stdout.on "data", (buffer) -> cb buffer.toString()
     child.stderr.on "data", (buffer) -> cb buffer.toString()
     #child.stdout.on "end", -> cb resp

   robot.respond "/"+process.env.HUBOT_SHELLCMD_KEYWORD+"$/i", (msg) ->
     cmd = process.env.HUBOT_SHELLCMD;
-    run_cmd cmd, [], (text) -> msg.send text
+    envs = {}
+    envs["HUBOT_USER_" + key.toUpperCase()] = value for key, value of msg.envelope.user
+    run_cmd cmd, [], envs, (text) -> msg.send text

   robot.respond "/"+process.env.HUBOT_SHELLCMD_KEYWORD+" (.*)/i", (msg) ->
     msg.match[0] = msg.match[0].replace(/^[a-z0-9]+$/i);
     msg.match.shift();
     args = msg.match[0].split(" ");
     cmd = process.env.HUBOT_SHELLCMD;
-    run_cmd cmd, args, (text) -> msg.send text.replace("\n","")
+    envs = {}
+    envs["HUBOT_USER_" + key.toUpperCase()] = value for key, value of msg.envelope.user
+    run_cmd cmd, args, envs, (text) -> msg.send text.replace("\n","")


add react tabGuard element

add react tabGuard element

--- src/tabguard.coffee
+++ src/tabguard.coffee
@@ -1,6 +1,8 @@
 React = require 'react'
 ReactDOM = require 'react-dom'
 arrayFrom = require 'array-from'
+
+tabGuard = React.createFactory 'tabguard'

 focusableElementsList = [
   'a[href]'
@@ -18,8 +20,10 @@

 focusableElementsSelector = focusableElementsList.join()

+TabGuard = React.createClass
+  propTypes:
+    className: React.PropTypes.string

-TabGuard = React.createClass
   componentDidMount: ->
     @_registerTabGuard()

@@ -75,7 +79,9 @@
     @_lastElement.removeEventListener 'keydown', @_handleLastElement

   render: ->
-    @props.children
+    tabGuard
+      className: @props.className
+      children: @props.children

 module.exports = TabGuard


CLI making require('lovely') return the Lovely object itself

--- cli/lovely.coffee
+++ cli/lovely.coffee
@@ -5,19 +5,23 @@

 # the packages building tools
 sources_cache  = {}
-exports.source = compile_source = (name)->
+compile_source = (name)->
   sources_cache[name] or= source.compile(__dirname + "/../stl/#{name}/")

+#
+# building the core package
+#
+# NOTE: we're exporting the `Lovely` object itself
+#
+eval(core = compile_source('core'))
+module.exports = this.Lovely

-# building the core package
-eval(core = compile_source('core'))

-exports.Lovely = this.Lovely
-
-for key of this.Lovely
-  exports[key] = this.Lovely[key]
-
+#
 # Defining the development tools initialization
+# so that it didn't bother until the code actually
+# called the `Lovely.Test` property
+#
 Test = null
-exports.__defineGetter__ 'Test', ->
+module.exports.__defineGetter__ 'Test', ->
   Test or= require('./test/core')


allow slashes in URLs

allow slashes in URLs

--- code/serv.coffee
+++ code/serv.coffee
@@ -61,7 +61,7 @@
 app.delete /.*/, check_api_key

 # Documentation for SSHing to a box.
-app.get "/:box_name$", (req, res) ->
+app.get "/:box_name/?", (req, res) ->
   res.header('Content-Type', 'application/json')
   Box.findOne {name: req.params.box_name}, (err, box) ->
     return res.send { error: "Box not found" }, 404 unless box?
@@ -72,7 +72,7 @@

 # Create a box
-app.post "/:box_name$", (req, res) ->
+app.post "/:box_name/?", (req, res) ->
   res.header('Content-Type', 'application/json')
   re = /^[a-zA-Z0-9_+-]+$/
   if not re.test req.params.box_name
@@ -97,7 +97,7 @@
               res.send {status: "ok"}

 # Add an SSH key to a box
-app.post "/:box_name/sshkeys$", (req, res) ->
+app.post "/:box_name/sshkeys/?", (req, res) ->
   res.header('Content-Type', 'application/json')
   unless req.body.sshkey? then return res.send { error: "SSH Key not specified" }, 400


Added snippets from my own selection of modules.

--- completions.cson
+++ completions.cson
@@ -3217,7 +3217,7 @@

 ######
 #
-# Linkable
+# sheadawson/silverstripe-linkable
 #
 ######

@@ -3246,7 +3246,59 @@

 ######
 #
-# SortableUploadField
+# gorriecoe/silverstripe-link
+#
+######
+
+'Link':
+  'prefix': 'link'
+  'module': 'gorriecoe/silverstripe-link'
+  'scope': '.text.html.php'
+  'body': "Link::class"
+  'useNamespace': [
+    'gorriecoe\\Link\\Models\\Link'
+  ]
+
+######
+#
+# gorriecoe/silverstripe-linkfield
+#
+######
+
+'LinkField':
+  'prefix': 'linkfield'
+  'module': 'gorriecoe/silverstripe-linkfield'
+  'scope': '.text.html.php'
+  'body': """
+  LinkField::create(
+  \t\'${1:name}\',
+  \t\'${2:${1:title}}\'
+  \t${3:\$this},
+  )${0}
+  """
+  'useNamespace': [
+    'gorriecoe\\Link\\Models\\Link'
+    'gorriecoe\\LinkField\\LinkField'
+  ]
+
+######
+#
+# gorriecoe/silverstripe-embed
+#
+######
+
+'Embeddable':
+  'prefix': 'embed'
+  'module': 'gorriecoe/silverstripe-embed'
+  'scope': '.text.html.php'
+  'body': "Embeddable::class"
+  'useNamespace': [
+    'gorriecoe\\Embed\\Extensions\\Embeddable'
+  ]
+
+######
+#
+# bummzack/sortablefile
 #
 ######

@@ -3263,7 +3315,7 @@

 ######
 #
-# Focuspoint
+# jonom/focuspoint
 #
 ######

@@ -3305,55 +3357,55 @@

 ######
 #
-# Gridfield extensions
+# symbiote/silverstripe-gridfieldextensions
 #
 ######

 'GridFieldAddExistingSearchButton':
   'prefix': 'gridfieldaddexistingsearchbutton'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldAddExistingSearchButton()'

 'GridFieldTitleHeader':
   'prefix': 'gridfieldtitleheader'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldTitleHeader()'

 'GridFieldEditableColumns':
   'prefix': 'gridfieldeditablecolumns'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldEditableColumns()'

 'GridFieldDeleteAction':
   'prefix': 'gridfielddeleteaction'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldDeleteAction()'

 'GridFieldAddNewInlineButton':
   'prefix': 'gridfieldaddnewinlinebutton'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldAddNewInlineButton()'

 'GridFieldAddNewMultiClass':
   'prefix': 'gridfieldaddnewmulticlass'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldAddNewMultiClass())'

 'GridFieldOrderableRows':
   'prefix': 'gridfieldorderablerows'
-  'module': 'silverstripe-australia/gridfieldextensions'
+  'module': 'symbiote/gridfieldextensions'
   'scope': '.text.html.php'
   'body': 'new GridFieldOrderableRows(${1:\'Sort\'})${0}'

 ######
 #
-# Sortable Gridfield
+# undefinedoffset/sortablegridfield
 #
 ######

@@ -3364,7 +3416,7 @@

 ######
 #
-# Better Buttons
+# unclecheese/betterbuttons
 #
 ######

@@ -3412,7 +3464,7 @@

 ######
 #
-# Display Logic
+# unclecheese/silverstripe-display-logic
 #
 ######

@@ -3519,7 +3571,7 @@

 ######
 #
-# Lumberjack
+# silverstripe/lumberjack
 #
 ######

@@ -3553,7 +3605,7 @@

 ######
 #
-# Fluent
+# tractorcow/silverstripe-fluent
 #
 ######


camelCase

camelCase

--- app/graph_drawing.coffee
+++ app/graph_drawing.coffee
@@ -11,20 +11,20 @@

 expandIsolatedValuesToLineSegments = (values) ->
   result = []
-  segment_length = 0
+  segmentLength = 0
   previous = null
   _.each values, (v, i) ->
     if v.value?
-      segment_length++
+      segmentLength++
       previous = v
     else
-      if segment_length is 1
+      if segmentLength is 1
         result.push previous
-      segment_length = 0
+      segmentLength = 0

     result.push v

-  if segment_length is 1
+  if segmentLength is 1
     result.push previous
   result

@@ -50,7 +50,7 @@

   result

-default_params =
+defaultParams =
   width: 800
   height: 400
   type: 'line'
@@ -84,15 +84,15 @@
 create = (container) ->
   x = d3.time.scale()
   y = d3.scale.linear()
-  x_axis = d3.svg.axis().scale(x).orient('bottom')
-  y_axis = d3.svg.axis().scale(y).orient('left')
+  xAxis = d3.svg.axis().scale(x).orient('bottom')
+  yAxis = d3.svg.axis().scale(y).orient('left')

   color = d3.scale.ordinal()

-  mouse_over = new Bacon.Bus
-  mouse_out = new Bacon.Bus
+  mouseOver = new Bacon.Bus
+  mouseOut = new Bacon.Bus
   clicks = new Bacon.Bus
-  mouse_moves = new Bacon.Bus
+  mouseMoves = new Bacon.Bus

   area = d3.svg.area()
     .x((d) -> d.x)
@@ -114,7 +114,7 @@
       d.value = y)

   svg = d3.select(container).append('svg')
-    .on('mousemove', (d, i) -> mouse_moves.push d3.mouse g.node())
+    .on('mousemove', (d, i) -> mouseMoves.push d3.mouse g.node())

   # TODO font
   g = svg
@@ -126,11 +126,11 @@
   yXaxisG = g.append('g')
     .attr('class', 'y axis')

-  vertical_crosshair = g.append('line')
+  verticalCrosshair = g.append('line')
     .attr('class', 'crosshair')
     .attr('y1', 0)

-  crosshair_time = g.append('text')
+  crosshairTime = g.append('text')
     .style('text-anchor', 'middle')
     .attr('y', -6)

@@ -175,7 +175,7 @@
       computedParams = _.object _.map fgColorParams, (k) -> [k, params.fgcolor]
     else
       computedParams = {}
-    params = _.extend {}, default_params, computedParams, params
+    params = _.extend {}, defaultParams, computedParams, params
     width = params.width
     height = params.height

@@ -210,15 +210,15 @@
     _.each allNames, (name, i) ->
       colorsByName[name] = color(i)

-    observe_mouse = (s) ->
-      s.on('mouseover', (d, i) -> mouse_over.push {index: i, event: d3.event, data: d})
-      .on('mouseout', (d, i) -> mouse_out.push {index: i, event: d3.event, data: d})
+    observeMouse = (s) ->
+      s.on('mouseover', (d, i) -> mouseOver.push {index: i, event: d3.event, data: d})
+      .on('mouseout', (d, i) -> mouseOut.push {index: i, event: d3.event, data: d})
       .on('click', (d, i) -> clicks.push i)

     selected = clicks.scan _.map(data, -> true), (state, i) ->
-      new_state = _.clone state
-      new_state[i] = !new_state[i]
-      new_state
+      newState = _.clone state
+      newState[i] = !newState[i]
+      newState
     selected.onValue (s) ->
       legend.selectAll('li')
       .data(s)
@@ -227,8 +227,8 @@
       .data(s)
       .classed 'deselected', (d) -> !d

-    hover_selections = mouse_over.map ({index}) -> {index, selection: d3.select(container).selectAll ".target#{index}"}
-    hover_selections.onValue ({selection, index}) ->
+    hoverSelections = mouseOver.map ({index}) -> {index, selection: d3.select(container).selectAll ".target#{index}"}
+    hoverSelections.onValue ({selection, index}) ->
       if type == 'line'
         path = selection.select('path')
         path.style('stroke-width', (params.lineWidth ? 0) + 3)
@@ -236,7 +236,7 @@
         circles = selection.select('.circles').selectAll('circle')
         circles.attr('r', 4)
       selection.classed 'hovered', true
-    unhovers = hover_selections.merge(mouse_out)
+    unhovers = hoverSelections.merge(mouseOut)
       .withStateMachine([], (previous, event) -> [[event], previous])
       .filter (e) -> e.selection?
     unhovers.onValue ({selection, index}) ->
@@ -248,33 +248,33 @@
         circles = selection.select('.circles').selectAll('circle')
         circles.attr('r', 2)

-    mouse_position = mouse_moves.map (pos) ->
-      x_constrained = Math.max 0, Math.min(pos[0], width)
-      time: x.invert x_constrained
+    mousePosition = mouseMoves.map (pos) ->
+      xConstrained = Math.max 0, Math.min(pos[0], width)
+      time: x.invert xConstrained
       value: y.invert pos[1]
-      x: x_constrained
+      x: xConstrained
       y: pos[1]

-    time_min = null
-    time_max = null
+    timeMin = null
+    timeMax = null
     _.each data, ({datapoints}, j) ->
       if datapoints.length > 0
         _.each [0, datapoints.length - 1], (i) ->
           datapoint = datapoints[i]
           value = get_value datapoint, i, j
           timestamp = get_timestamp datapoint, i, j
-          time_min = Math.min timestamp, time_min ? timestamp
-          time_max = Math.max timestamp, time_max
-
-    time_min = new Date(time_min * 1000)
-    time_max = new Date(time_max * 1000)
-    x.domain [params.xMin ? time_min, params.xMax ? time_max]
+          timeMin = Math.min timestamp, timeMin ? timestamp
+          timeMax = Math.max timestamp, timeMax
+
+    timeMin = new Date(timeMin * 1000)
+    timeMax = new Date(timeMax * 1000)
+    x.domain [params.xMin ? timeMin, params.xMax ? timeMax]

     stack.offset(params.areaOffset)

     if type is 'line'
-      area_opacity = params.areaAlpha
-      line_opacity = 1.0
+      areaOpacity = params.areaAlpha
+      lineOpacity = 1.0

       if params.interpolate?
         line.interpolate params.interpolate
@@ -286,26 +286,26 @@
       simplify = _.identity

     if params.drawNullAsZero
-      transform_value = (v) -> v ? 0
-      filter_scatter_values = simplify
-      expand_line_values = simplify
+      transformValue = (v) -> v ? 0
+      filterScatterValues = simplify
+      expandLineValues = simplify
     else
       # TODO this won't work well with stack
-      transform_value = (v) -> v
-      filter_scatter_values = (values) ->
+      transformValue = (v) -> v
+      filterScatterValues = (values) ->
         simplify _.filter values, (d) -> d.value?
-      expand_line_values = _.compose expandIsolatedValuesToLineSegments, simplify
-
-    value_min = null
-    value_max = null
+      expandLineValues = _.compose expandIsolatedValuesToLineSegments, simplify
+
+    valueMin = null
+    valueMax = null
     targets = for s, targetIndex in data
       values = for datapoint, i in s.datapoints
         value = get_value datapoint, i, targetIndex
         timestamp = get_timestamp datapoint, i, targetIndex
         time = new Date(timestamp * 1000)
-        value = transform_value value
-        value_min = Math.min value, value_min ? value if value?
-        value_max = Math.max value, value_max
+        value = transformValue value
+        valueMin = Math.min value, valueMin ? value if value?
+        valueMax = Math.max value, valueMax
         {value, time: time, x: x(time), original: datapoint}
       bisector = d3.bisector (d) -> d.time
       lineMode =
@@ -325,26 +325,26 @@

     if params.areaMode is 'stacked'
       stack targets
-      value_min = null
-      value_max = null
+      valueMin = null
+      valueMax = null
       for {values} in targets
         for {value, y0} in values
           value += y0
-          value_min = Math.min value, value_min
-          value_max = Math.max value, value_max
-
-    if value_min == value_max
-      value_min = Math.round(value_min) - 1
-      value_max = Math.round(value_max) + 1
-    y.domain [params.yMin ? value_min, params.yMax ? value_max]
+          valueMin = Math.min value, valueMin
+          valueMax = Math.max value, valueMax
+
+    if valueMin == valueMax
+      valueMin = Math.round(valueMin) - 1
+      valueMax = Math.round(valueMax) + 1
+    y.domain [params.yMin ? valueMin, params.yMax ? valueMax]

     _.each targets, (target) ->
       {values} = target
       _.each values, (v) ->
         if v.value?
           v.y = y v.value
-      target.lineValues = expand_line_values values
-      target.scatterValues = filter_scatter_values values
+      target.lineValues = expandLineValues values
+      target.scatterValues = filterScatterValues values

     svg
       .attr('width', width + margin.left + margin.right)
@@ -357,15 +357,15 @@
     xAxisG
       .attr('class', 'x axis')
       .attr('transform', "translate(0, #{height})")
-      .call(x_axis)
-
-    yXaxisG.call(y_axis)
+      .call(xAxis)
+
+    yXaxisG.call(yAxis)

     axes = g.selectAll('.axis')
     axes.selectAll('path, line').attr('stroke', params.axisLineColor).attr({'fill': 'none', 'shape-rendering': 'crispEdges'})
     axes.selectAll('text').attr('fill', params.axisTextColor).style('font-size', params.axisTextSize)

-    vertical_crosshair
+    verticalCrosshair
       .attr('y2', height)
       .attr('stroke', params.crosshairLineColor)

@@ -397,17 +397,17 @@

     positionCrosshair = (x, time) ->
       currentCrosshairTime = time
-      vertical_crosshair
+      verticalCrosshair
         .attr('x1', x)
         .attr('x2', x)

-      crosshair_time
+      crosshairTime
         .text(moment(time).format('lll'))
         .attr('x', x)
         .attr('fill', params.crosshairTextColor)
         .style('font-size': params.crosshairTextSize)

-      target_values = _.map targets, (t) ->
+      targetValues = _.map targets, (t) ->
         i = t.bisector.left t.values, time, 1
         d0 = t.values[i - 1]
         d1 = t.values[i]
@@ -417,11 +417,11 @@
           d0

       legend.selectAll('.crosshair-value')
-        .data(target_values)
+        .data(targetValues)
         .text((d) -> params.valueFormat(d?.value))
         .style(color: params.crosshairValueTextColor)

-    mouse_position.onValue (p) ->
+    mousePosition.onValue (p) ->
       positionCrosshair p.x, p.time
       cursorBus.push p.time

@@ -444,12 +444,12 @@
       .data(targets)
     target.enter().append("g")
       .attr('class', (d, i) -> "target target#{i}")
-      .call(observe_mouse)
+      .call(observeMouse)
     target.exit().remove()
     target.selectAll('g > *').remove()

-    add_path = (target, hover) ->
+    addPath = (target, hover) ->
       if hover
         lineWidth = (params.lineWidth ? 0) + 10
       else
@@ -468,10 +468,10 @@
           .style('fill-opacity', 0)
       else
         path
-          .style('stroke-opacity', line_opacity)
-          .style('fill-opacity', area_opacity)
-
-    add_circles = (target, hover) ->
+          .style('stroke-opacity', lineOpacity)
+          .style('fill-opacity', areaOpacity)
+
+    addCircles = (target, hover) ->
       if hover
         radius = 5
         opacity = 0
@@ -491,19 +491,19 @@
         .style('fill-opacity', opacity)

     if type is 'line'
-      add_path target, false
-      add_path target, true
+      addPath target, false
+      addPath target, true
     else if type is 'scatter'
-      add_circles target, false
-      add_circles target, true
-
-    legend_target = legend.selectAll('li')
+      addCircles target, false
+      addCircles target, true
+
+    legendTarget = legend.selectAll('li')
       .data(targets)

-    legendTargetEnter = legend_target.enter().append('li')
+    legendTargetEnter = legendTarget.enter().append('li')
       .attr('class', (d, i) -> "target#{i}")
       .attr('data-target', (d) -> d.name)
-      .call(observe_mouse)
+      .call(observeMouse)

     legendTargetEnter.append('span')
       .attr('class', 'color')
@@ -512,13 +512,13 @@
     legendTargetEnter.append('span')
       .attr('class', 'crosshair-value')

-    legend_target.select('span.color')
+    legendTarget.select('span.color')
       .style('color', (d, i) -> d.color)

-    legend_target.select('span.name')
+    legendTarget.select('span.name')
       .text((d) -> d.name)

-    legend_target.exit().remove()
+    legendTarget.exit().remove()

   {draw, exportImage, destroy}


Typo fix in sign in prompt

Typo fix in sign in prompt

- Closes #627

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -203,7 +203,7 @@
                 header={null} />

           else
-
Please sign in to contribute to the disucssion

+
Please sign in to contribute to the discussion

         }</PromiseRenderer>
       }</ChangeListener>


First draft of car class

First draft of car class

--- coffee/car.coffee
+++ coffee/car.coffee
@@ -1,12 +1,14 @@
 class Car
   @MARGIN = 3
+  @SPEED = 2

   class StreetDirection
     @CROSS = 0
     @HORIZONTAL = 1
     @VERTICAL = 2

-  constructor: (grid) ->
+  constructor: (source, grid) ->
+    @source = $(source)
     @grid = grid
     @currentPosition = @grid.randomCrossStreets()
     @currentStreetDirection = StreetDirection.CROSS
@@ -17,7 +19,23 @@
   _comparePoints: (p1, p2) ->
     return @_compareDouble(p1.getX(), p2.getX()) and @_compareDouble(p1.getY(), p2.getY())

+  _setCarPosition: (position) ->
+    @source.css
+      top: position.getY()
+      left: position.getX()
+
   _animateTo: (point, callback) ->
+    if @_comparePoints(@currentPosition, point)
+      callback()
+    else
+      setTimeout(
+                  () =>
+                    @currentPosition.setX(@currentPosition.getX() + 1)
+                    @currentPosition.setY(@currentPosition.getY() + 1)
+                    @_animateTo(point, callback)
+                ,
+                  1 / Car.SPEED
+                )

   moveTo: (target) ->
     return if not @grid.isWithinAStreet(target)


Change to dark atom theme

Change to dark atom theme

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -16,8 +16,8 @@
       "vendor"
     ]
     themes: [
-      "unity-ui"
-      "glacier-syntax"
+      "atom-dark-ui"
+      "atom-dark-syntax"
     ]
   editor:
     fontFamily: "Menlo"


Updated Atom Config

Updated Atom Config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,35 +1,18 @@
 'editor':
+  'fontSize': 16
   'showIndentGuide': true
-  'fontFamily': 'Menlo'
-  'fontSize': 15
+  'softWrap': true
 'core':
-  'excludeVcsIgnoredPaths': true
+  'themes': [
+    'atom-dark-ui'
+    'atom-dark-syntax'
+  ]
   'ignoredNames': [
-    '.bundle'
     '.git'
-    'log'
-    'repositories'
-    'tmp'
-    'vendor'
+    '.svn'
+    '.DS_Store'
   ]
-  'themes': [
-    'unity-ui'
-    'glacier-syntax'
-  ]
-  'hideGitIgnoredFiles': true
-  'disabledPackages': [
-    'background-tips'
-    'exception-reporting'
-    'metrics'
-  ]
-'spell-check':
-  'grammars': [
-    'text.plain'
-    'source.gfm'
-    'text.git-commit'
-  ]
+'release-notes':
+  'viewedVersion': '0.89.0'
 'welcome':
   'showOnStartup': false
-'release-notes':
-  'viewedVersion': '0.89.0'
-'tree-view': {}


Removed unused file

Removed unused file

--- core/app/backbone/views/interacting_users/interacting_users_view.coffee
+++ core/app/backbone/views/interacting_users/interacting_users_view.coffee
@@ -1,17 +0,0 @@
-class window.InteractingUsersView extends Backbone.Marionette.Layout
-
-  className: 'discussion-interacting-users'
-  template: 'interacting_users/box'
-
-  regions:
-    interactingUsersRegion: '.js-region-interacting-users-names'
-    interactingUsersAvatarRegion: '.js-region-interacting-users-avatars'
-
-  onRender: ->
-    opinionaters = @model.opinionaters()
-
-    @interactingUsersRegion.show new InteractingUsersNamesView
-      collection: opinionaters
-
-    @interactingUsersAvatarRegion.show new InteractingUsersAvatarsView
-      collection: opinionaters


Removing colons

Removing colons

--- framer/Events.coffee
+++ framer/Events.coffee
@@ -40,9 +40,9 @@
 Events.Scroll = "scroll"

 # Image events
-Events.ImageLoaded = "image:load"
-Events.ImageLoadError = "image:error"
-Events.ImageLoadCancelled = "image:cancelled"
+Events.ImageLoaded = "imageload"
+Events.ImageLoadError = "imageerror"
+Events.ImageLoadCancelled = "imagecancelled"

 # Add all gesture events
 _.extend(Events, Gestures)


Force returning a Session instance; Added additional namespaces

--- src/wrathnet.coffee
+++ src/wrathnet.coffee
@@ -22,7 +22,10 @@
   @crypto = {}
   @entities = {}
   @expansions = {
-    wotlk: {}
+    wotlk: {
+      handlers: {}
+      net: {}
+    }
   }
   @net = {}
   @sessions = {}
@@ -30,4 +33,4 @@

   # Convenience constructor for starting a new WrathNet session for given expansion
   constructor: (expansion, userAgent) ->
-    new WrathNet.sessions.Session expansion, userAgent
+    return new WrathNet.sessions.Session expansion, userAgent


fixed events mixin

fixed events mixin

--- manager/lib/assets/javascripts/stark/state.js.coffee
+++ manager/lib/assets/javascripts/stark/state.js.coffee
@@ -10,7 +10,7 @@
   logger: "state"

   # mixin events
-  _.extend @.prototype, Backbone.Events.prototype
+  _.extend @.prototype, Backbone.Events

   # [internal] Reference to the Stark::App instance
   app: null


save allocation to in memory data

save allocation to in memory data

--- src/node_modules/round-contributors/round-contributors-controller.coffee
+++ src/node_modules/round-contributors/round-contributors-controller.coffee
@@ -29,6 +29,7 @@
         amount: data
         roundId: round.id
       )
+      allocations[contributor.user.id] = allocation
       round.allocations.add(allocation)

     AllocationsStore.save(allocation).then ->


Admin: Integrations: Removed dummy data.

--- client/admin/lib/views/integrations/adminconfiguredintegrationslistview.coffee
+++ client/admin/lib/views/integrations/adminconfiguredintegrationslistview.coffee
@@ -2,35 +2,6 @@
 AdminIntegrationsListView          = require './adminintegrationslistview'
 AdminConfiguredIntegrationItemView = require './adminconfiguredintegrationitemview'

-DUMMY_DATA =
-  title       : 'Airbrake'
-  iconPath    : 'https://koding-cdn.s3.amazonaws.com/temp-images/airbrake.png'
-  summary     : 'Error monitoring and handling.'
-  description : """
-
Airbrake captures errors generated by your team's web applications, and aggregates the results for easy review.

-
This integration will post notifications in Slack when an error is reported in Airbrake.

-  """
-  channels : [
-    { name: '#general',   id: '123' }
-    { name: '#random',    id: '124' }
-    { name: '#off-topic', id: '125' }
-  ]
-  selectedChannel : '123'
-  webhookUrl      : 'https://hooks.koding.com/services/123/abc/def'
-  instructions    : """
-    #### Step 1
-
-    In your Airbrake dashboard, click on the menu button in the top navigation, and then select **Integrations** from the sub-menu.
-
-    ![airbrake_step1.png](https://koding-cdn.s3.amazonaws.com/temp-images/airbrake_step1.png)
-
-
-    #### Step 2
-
-    Click on Slack in the list of integrations, add `https://hooks.slack.com/services/123/abc/def` as the Slack webhook, and choose your notification settings. Press the Save button when you're done.
-
-    ![airbrake_step2.png](https://koding-cdn.s3.amazonaws.com/temp-images/airbrake_step2.png)
-  """

 module.exports = class AdminConfiguredIntegrationsListView extends AdminIntegrationsListView

@@ -47,8 +18,4 @@
     super

     item.on 'IntegrationCustomizeRequested', (integrationData) =>
-
-      # temp code...
-      integrationData = DUMMY_DATA
-
       @showIntegrationDetails integrationData


Add FAILED action, remove unused helpers

--- src/flux/course-listing.coffee
+++ src/flux/course-listing.coffee
@@ -3,7 +3,6 @@
 flux = require 'flux-react'

 {CourseActions, CourseStore} = require './course'
-{CrudConfig, makeSimpleStore, extendConfig} = require './helpers'

 LOADING = 'loading'
 LOADED  = 'loaded'
@@ -27,6 +26,10 @@
     @_course_ids = []
     CourseActions.reset()
     @_asyncStatus = null
+    @emitChange()
+
+  FAILED: (status, msg) ->
+    @_asyncStatus = FAILED
     @emitChange()

   loaded: (courses) ->


Bugfix issue 66 and Bugfix accessing childnodes by ".children"

--- muc/strophe.muc.coffee
+++ muc/strophe.muc.coffee
@@ -48,7 +48,7 @@
       msg.cnode Strophe.xmlElement("password", [], password)

     # One handler for all rooms that dispatches to room callbacks
-    @_muc_handler ?=  conn.addHandler (stanza) =>
+    @_muc_handler ?=  @_connection.addHandler (stanza) =>
       from = stanza.getAttribute 'from'
       roomname = from.split("/")[0]

@@ -668,7 +668,7 @@
     data.nick = Strophe.getResourceFromJid a.from.textContent
     data.type = a.type?.textContent or null
     data.states = []
-    for c in pres.children
+    for c in pres.childNodes
       switch c.nodeName
         when "status"
           data.status = c.textContent or null
@@ -677,7 +677,7 @@
         when "x"
           a = c.attributes
           if a.xmlns?.textContent is Strophe.NS.MUC_USER
-            for c2 in c.children
+            for c2 in c.childNodes
               switch c2.nodeName
                 when "item"
                   a = c2.attributes
@@ -696,7 +696,7 @@
     @parse info if info?

   parse: (result) =>
-    query = result.getElementsByTagName("query")[0].children
+    query = result.getElementsByTagName("query")[0].childNodes
     @identities =  []
     @features =  []
     @x = []
@@ -710,11 +710,11 @@
         when "feature"
           @features.push attrs.var.textContent
         when "x"
-          attrs = child.children[0].attributes
+          attrs = child.childNodes[0].attributes
           break if (
             (not attrs.var.textContent is 'FORM_TYPE') or
             (not attrs.type.textContent is 'hidden') )
-          for field in child.children when not field.attributes.type
+          for field in child.childNodes when not field.attributes.type
             attrs = field.attributes
             @x.push (
               var: attrs.var.textContent


Fix error when selecting all boards on the discussion pane

--- lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
+++ lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
@@ -243,10 +243,11 @@
         @setTopic(event)  # just sets the title for the dropdown
         item = $(event.target).closest('li')
         if item.find("span.board-name").data("discussion_id") == "#all"
-          item = item.parent()
           @discussionIds = ""
-        discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id
-        @retrieveDiscussions(discussionIds)
+          @clearSearch()
+        else
+          discussionIds = _.map item.find(".board-name[data-discussion_id]"), (board) -> $(board).data("discussion_id").id
+          @retrieveDiscussions(discussionIds)

     retrieveDiscussion: (discussion_id, callback=null) ->
       url = DiscussionUtil.urlFor("retrieve_discussion", discussion_id)


remove debugging console.log

remove debugging console.log

--- src/lib/loopback-server.coffee
+++ src/lib/loopback-server.coffee
@@ -23,9 +23,6 @@
         @app.lwaTokenManager = new AdminTokenManager(adminTokenOptions)

         return @app.start (err) =>
-
-            console.log "err"
-            console.log err

             return reject(err) if err


server.listen is async

server.listen is async


Parse bodyParams.payload as json if it exists

--- packages/rocketchat-api/server/api.coffee
+++ packages/rocketchat-api/server/api.coffee
@@ -45,6 +45,9 @@
 	auth:
 		token: 'services.resume.loginTokens.hashedToken'
 		user: ->
+			if @bodyParams?.payload?
+				@bodyParams = JSON.parse @bodyParams.payload
+
 			for method in RocketChat.API.v1.authMethods
 				result = method.apply @, arguments
 				if result not in [undefined, null, false]


Fix select/deselect all link

Fix select/deselect all link

--- app/assets/javascripts/utils.js.coffee
+++ app/assets/javascripts/utils.js.coffee
@@ -20,7 +20,7 @@
 root.toggleCheckboxesBySelector = (selector) ->
   all_checked = true
   $(selector).each -> all_checked = false if !($(this).is(':checked'))
-  $(selector).each -> this.checked = !all_checked
+  $(selector).each -> $(this).prop('checked', !all_checked)

 root.highlight = (element) ->


Add method to run the GM detection

Add method to run the GM detection

--- packages/rocketchat-file/file.server.coffee
+++ packages/rocketchat-file/file.server.coffee
@@ -20,41 +20,49 @@
 		RocketChat.settings.updateOptionsById 'Accounts_AvatarResize', {alert: 'The_image_resize_will_not_work_because_we_can_not_detect_ImageMagick_or_GraphicsMagick_installed_in_your_server'}

-console.log 'GM: Getting GraphicsMagick version'
-exec 'gm version', Meteor.bindEnvironment (error, stdout, stderr) ->
-	console.log 'GM: GraphicsMagick', arguments
-	if not error? and stdout.indexOf('GraphicsMagick') > -1
-		console.log 'GM: GraphicsMagick installed'
-		RocketChatFile.enable()
-
-		RocketChat.Info.GraphicsMagick =
-			enabled: true
-			version: stdout
-	else
-		console.log 'GM: GraphicsMagick not installed'
-		RocketChat.Info.GraphicsMagick =
-			enabled: false
-
-	console.log 'GM: Getting ImageMagick version'
-	exec 'convert -version', Meteor.bindEnvironment (error, stdout, stderr) ->
-		console.log 'GM: ImageMagick', arguments
-		if not error? and stdout.indexOf('ImageMagick') > -1
-			console.log 'GM: ImageMagick installed'
-			if RocketChatFile.enabled isnt true
-				# Enable GM to work with ImageMagick if no GraphicsMagick
-				RocketChatFile.gm = RocketChatFile.gm.subClass({imageMagick: true})
-				RocketChatFile.enable()
-
-			RocketChat.Info.ImageMagick =
+detectGM = ->
+	console.log 'GM: Getting GraphicsMagick version'
+	exec 'gm version', Meteor.bindEnvironment (error, stdout, stderr) ->
+		console.log 'GM: GraphicsMagick', arguments
+		if not error? and stdout.indexOf('GraphicsMagick') > -1
+			console.log 'GM: GraphicsMagick installed'
+			RocketChatFile.enable()
+
+			RocketChat.Info.GraphicsMagick =
 				enabled: true
 				version: stdout
 		else
-			console.log 'GM: ImageMagick not installed'
-			if RocketChatFile.enabled isnt true
-				RocketChatFile.disable()
-
-			RocketChat.Info.ImageMagick =
+			console.log 'GM: GraphicsMagick not installed'
+			RocketChat.Info.GraphicsMagick =
 				enabled: false
+
+		console.log 'GM: Getting ImageMagick version'
+		exec 'convert -version', Meteor.bindEnvironment (error, stdout, stderr) ->
+			console.log 'GM: ImageMagick', arguments
+			if not error? and stdout.indexOf('ImageMagick') > -1
+				console.log 'GM: ImageMagick installed'
+				if RocketChatFile.enabled isnt true
+					# Enable GM to work with ImageMagick if no GraphicsMagick
+					RocketChatFile.gm = RocketChatFile.gm.subClass({imageMagick: true})
+					RocketChatFile.enable()
+
+				RocketChat.Info.ImageMagick =
+					enabled: true
+					version: stdout
+			else
+				console.log 'GM: ImageMagick not installed'
+				if RocketChatFile.enabled isnt true
+					RocketChatFile.disable()
+
+				RocketChat.Info.ImageMagick =
+					enabled: false
+
+detectGM()
+
+Meteor.methods
+	'detectGM': ->
+		detectGM()
+		return

 RocketChatFile.bufferToStream = (buffer) ->


fix a missing dependency on jquery on login page

test plan: login page should work

Change-Id: Ia475bceb2f293d52bce7ca20522d481cc06cc0af
Reviewed-on: https://gerrit.instructure.com/100422
Tested-by: Jenkins
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
QA-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/bundles/login.coffee
+++ app/coffeescripts/bundles/login.coffee
@@ -1,7 +1,8 @@
 require [
+  'jquery'
   'jsx/login/LoginFormSwitcher'
   'login'
-], (LoginFormSwitcher) ->
+], ($, LoginFormSwitcher) ->

   switcher = new LoginFormSwitcher(
     $("#login_form")


Don't need the 13 check as 10 will always end newlines

--- src/chunked-line-reader.coffee
+++ src/chunked-line-reader.coffee
@@ -39,7 +39,7 @@

       while bytesRead
         # Scary looking. Uses very few new objects
-        char = if process.platform is 'win32' then 13 else 10
+        char = 10
         index = lastIndexOf(chunkedBuffer, bytesRead, char)

         if index < 0


need scroll to mixin after all, there's changes on master that need it

--- src/components/task/index.cjsx
+++ src/components/task/index.cjsx
@@ -25,7 +25,7 @@

 {UnsavedStateMixin} = require '../unsaved-state'

-{PinnedHeaderFooterCard, PinnedHeader} = require 'openstax-react-components'
+{PinnedHeaderFooterCard, PinnedHeader, ScrollToMixin} = require 'openstax-react-components'

 module.exports = React.createClass
   propTypes:
@@ -33,7 +33,7 @@

   displayName: 'Task'

-  mixins: [StepFooterMixin, CrumbMixin, UnsavedStateMixin]
+  mixins: [StepFooterMixin, CrumbMixin, UnsavedStateMixin, ScrollToMixin]

   contextTypes:
     router: React.PropTypes.func


fix test

fix test

--- exercises/luhn/luhn.spec.coffee
+++ exercises/luhn/luhn.spec.coffee
@@ -19,7 +19,7 @@
     expect(luhn.valid()).toBe true

   xit 'a valid Canadian SIN', ->
-    luhn = new Luhn('055 444 286')
+    luhn = new Luhn('055 444 285')
     expect(luhn.valid()).toBe true

   xit 'invalid credit card', ->


copy manifest file after bumpUp version.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -255,18 +255,19 @@
     'coffee:production',
     'jade:production',
     'stylus:production',
-    'chromeManifest:dist',
     'minify'
   ]

   grunt.registerTask 'release-minor', [
     'production',
     'release:minor',
+    'chromeManifest:dist',
     'compress'
   ]

   grunt.registerTask 'release-patch', [
     'production',
     'release:patch',
+    'chromeManifest:dist',
     'compress'
   ]


Fix tasks

Fix tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -69,7 +69,7 @@
     grunt.registerTask "default", [
         "coffeelint"
         "coffee"
-        "test"
+        "nodeunit"
     ]

     grunt.registerTask "test", [


Make sure main file is built first.

Make sure main file is built first.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,6 +1,7 @@
 module.exports = (grunt) ->

   files = [
+    'src/tweenmachine.coffee'
     'src/*.coffee'
   ]


Fix the CSS deploy

Fix the CSS deploy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,7 +31,7 @@
             jshint:
                 files: ['js/*.js']
                 tasks: ['jshint']
-
+
         connect:

             livereload:
@@ -68,6 +68,7 @@
                     expand: true
                     src: [
                         'slides/**'
+                        'css/**'
                         'bower_components/**'
                         'js/**'
                         'images/**'
@@ -80,7 +81,7 @@
                     filter: 'isFile'
                 }]

-
+
         manifest:

             generate:
@@ -145,7 +146,7 @@
                 options:
                     remote: 'git@github.com:ukmadlz/ibm-intro.git'
                     branch: 'gh-pages'
-
+

     # Load all grunt tasks.
@@ -187,13 +188,13 @@
             'copy'
         ]

-
+
     grunt.registerTask 'deploy',
         'Deploy to Github Pages', [
             'dist'
             'buildcontrol'
         ]
-
+

     # Define default task.
     grunt.registerTask 'default', [


Compile keymaps into atom application

Compile keymaps into atom application

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -43,6 +43,7 @@
     glob_to_multiple:
       expand: true
       src: [
+        'keymaps/*.cson'
         'src/**/*.cson'
         'static/**/*.cson'
         'themes/**/*.cson'


added livereload-possibility

added livereload-possibility

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -20,6 +20,8 @@
   }

   uglifyDist = true
+
+  livereload = 35729 # false to disable

   ###
     DO NOT CHANGE ANYTHING BELOW THIS LINE!
@@ -56,6 +58,8 @@
       all:
         files: "#{paths.custom.base}**/*"
         tasks: devAllWatch
+      options:
+        livereload: livereload
     dev:
       options:
         all: moduleNames


Fix little kink in the Gruntfile

Fix little kink in the Gruntfile

Prevents from running js -> coffee compilation

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -41,4 +41,4 @@
   grunt.registerTask('test', 'mochacov:test')

   grunt.registerTask('default', 'watch')
-  grunt.registerTask('travis', 'check', 'test')
+  grunt.registerTask('travis', ['check', 'test'])


fix #304

fix #304

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -114,6 +114,7 @@
         files: [
           expand: true # Enable dynamic expansion
           cwd: "web/bundles/" # Src matches are relative to this path
+          dest: "web/bundles/" # Destination path prefix
           src: ["**/*.{png,jpg,gif}"] # Actual patterns to match
         ]


Structure tasks somewhat more clearly.

Structure tasks somewhat more clearly.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -53,6 +53,7 @@
                         '!<%= yeoman.dist %>/.git*'
                     ]
                 ]
+            compress: ['zip/TabAhead.zip']

         coffeelint:
             # `grunt-coffeelint` does not support this natively
@@ -150,7 +151,6 @@
                 'karma:e2e'
             ]
             dist: [
-                'coffee'
                 'imagemin'
                 'htmlmin'
             ]
@@ -188,6 +188,7 @@

     grunt.registerTask 'build', [
         'test'
+        'coffee'
         'clean:dist'
         'useminPrepare'
         'concurrent:dist'
@@ -196,6 +197,7 @@
         'uglify'
         'copy'
         'usemin'
+        'clean:compress'
         'compress'
     ]


Remove commentend-out spec browserification

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -830,20 +830,7 @@
     chromeExtensionCoreConnector: Rule.browserify 'chrome/extension/scripts/chrome_core_connector'
     chromeExtensionCoreConnectorSpec: Rule.browserifySpec 'chrome/extension/scripts/chrome_core_connector'
     genericCoreFirewall: Rule.browserify 'generic_core/firewall'
-#    genericCoreFirewallSpec: Rule.browserifySpec 'generic_core/firewall'
     genericCoreFreedomModule: Rule.browserify 'generic_core/freedom-module'
-#    genericCoreUproxyCoreSpec: Rule.browserifySpec 'generic_core/uproxy_core'
-#    genericCoreLocalInstanceSpec: Rule.browserifySpec 'generic_core/local-instance'
-#    genericCoreMetricsSpec: Rule.browserifySpec 'generic_core/metrics'
-#    genericCoreRemoteConnectionSpec: Rule.browserifySpec 'generic_core/remote-connection'
-#    genericCoreRemoteInstanceSpec: Rule.browserifySpec 'generic_core/remote-instance'
-#    genericCoreRemoteUserSpec: Rule.browserifySpec 'generic_core/remote-user'
-#    genericCoreSocialSpec: Rule.browserifySpec 'generic_core/social'
-#    genericCoreStorageSpec: Rule.browserifySpec 'generic_core/storage'
-
-#    genericUiUiSpec: Rule.browserifySpec 'generic_ui/scripts/ui'
-#    genericUiUserSpec: Rule.browserifySpec 'generic_ui/scripts/user'
-    genericUiCopypasteStateSpec: Rule.browserifySpec 'generic_ui/scripts/copypaste-state'
     integrationSpec: Rule.browserifySpec 'integration/core'
     integrationFreedomModule: Rule.browserify 'integration/test_connection'

@@ -980,7 +967,6 @@
   'browserify:ccaMain'
 ]

-#
 taskManager.add 'version_file', [
   'gitinfo'
   'string-replace:version'


test(Gruntfile): grunt test with no args runs Chrome

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -733,6 +733,8 @@
                         grunt.option('browsers', tests)
                     else
                         grunt.option('browsers', [test_type])
+                else
+                    grunt.option('browsers', ['Chrome'])
                     taskList.push('tests:local')

         grunt.task.run(taskList)


Helping future me

Helping future me

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -44,4 +44,6 @@

   # Default task(s).
   grunt.registerTask 'default', ['sass','coffee','bower_concat','uglify']
+  #grunt.registerTask 'default', ['sass']                             # CSS only
+  #grunt.registerTask 'default', ['coffee','bower_concat','uglify']   # Coffeescript only
   return


tweak coding style

tweak coding style

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,7 +1,7 @@
 module.exports = (grunt) ->
   'use strict'

-  require('load-grunt-tasks')(grunt)
+  require('load-grunt-tasks') grunt
   semver = require 'semver'

   pkg = grunt.file.readJSON 'package.json'


Abbreviating commands in generateApiDocs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -50,9 +50,9 @@
         command: "node_modules/grunt-protractor-runner/node_modules/.bin/webdriver-manager update"
       generateApiDocs:
         command: [
-          "node_modules/docha/bin/docha -p '.tmp/elementHelper.spec.js' -o 'docs/elementHelper.md' -e _"
-          "node_modules/docha/bin/docha -p '.tmp/generalStepDefs.spec.js' -o 'docs/generalStepDefs.md' -e _"
-          "node_modules/docha/bin/docha -p '.tmp/transform.spec.js' -o 'docs/transform.md' -e _"
+          "node_modules/.bin/docha -p '.tmp/elementHelper.spec.js' -o 'docs/elementHelper.md' -e _"
+          "node_modules/.bin/docha -p '.tmp/generalStepDefs.spec.js' -o 'docs/generalStepDefs.md' -e _"
+          "node_modules/.bin/docha -p '.tmp/transform.spec.js' -o 'docs/transform.md' -e _"
         ].join '&'

     connect:


- [grunt] added processhtml to `grunt build` task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,6 +62,7 @@
     'autoprefixer:build'
     'uncss:build'
     'csso:build'
+    'processhtml:build'
   ]

   ###


max-duration needs to be lower

max-duration needs to be lower

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -37,7 +37,7 @@
   ]

   sauceSerializedTestConfig = JSON.stringify({
-    public: "public"
+    public: "public",
     tags:  [
       "TRAVIS_JOB_ID: " + process.env.TRAVIS_JOB_ID,
       "TRAVIS_JOB_NUMBER: " + process.env.TRAVIS_JOB_NUMBER,
@@ -135,11 +135,12 @@
     'saucelabs-mocha':
       all:
         options:
-          urls: ["http://127.0.0.1:9999/pub/test/browser/index.html"],
-          tunnelTimeout: 5,
-          build: process.env.TRAVIS_BUILD_NUMBER,
-          browsers: browsers,
-          testname: "chai-counter unit tests",
+          urls: ["http://127.0.0.1:9999/pub/test/browser/index.html"]
+          tunnelTimeout: 5
+          build: process.env.TRAVIS_BUILD_NUMBER
+          browsers: browsers
+          testname: "chai-counter unit tests"
+          "max-duration": 60
           sauceConfig: sauceSerializedTestConfig
     'watch': {}


Fixed Zuckrig for esteWatch #78

Fixed Zuckrig for esteWatch #78

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -126,16 +126,16 @@
         ]

       coffee: (filepath) ->
-        config = getCoffeeConfig(filepath)
+        config = getCoffeeConfig filepath
         grunt.config ['coffee', 'all', 'files'], config
+        grunt.config ['zuckrig', 'all', 'files'], config
         grunt.config ['coffee2closure', 'all', 'files'], config
-        ['coffee', 'coffee2closure']
+        ['coffee', 'zuckrig', 'coffee2closure']

       js: (filepath) ->
-        grunt.config ['zuckrig', 'all', 'src'], filepath
         grunt.config ['esteDeps', 'all', 'src'], filepath
         grunt.config ['esteUnitTests', 'all', 'src'], filepath
-        ['zuckrig', 'esteDeps', 'esteUnitTests']
+        ['esteDeps', 'esteUnitTests']

     coffeelint:
       options:


sauce on; update browsers

sauce on; update browsers

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -344,7 +344,7 @@
           browsers: [
             browserName: 'googlechrome'
             platform: 'OS X 10.8'
-            version: '37'
+            version: '39'
           ,
             browserName: 'safari'
             platform: 'OS X 10.9'
@@ -360,7 +360,7 @@
           ,
             browserName: 'firefox'
             platform: 'Windows 7',
-            version: '33'
+            version: '34'
           ]
           build: process.env.TRAVIS_JOB_ID
           testname: 'NoFlo UI browser tests'
@@ -409,8 +409,8 @@
     'inlinelint'
     'build'
     'coffee'
-    # 'connect'
-    # 'saucelabs-mocha'
+    'connect'
+    'saucelabs-mocha'
   ]
   @registerTask 'app', ['build', 'phonegap-build']
   @registerTask 'default', ['test']


Put bower grunt task first

Put bower grunt task first

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -39,10 +39,10 @@
             forceLatest: true
             production: true

+  grunt.loadNpmTasks 'grunt-bower-task'
   grunt.loadNpmTasks 'grunt-contrib-watch'
   grunt.loadNpmTasks 'grunt-contrib-uglify'
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-compass'
-  grunt.loadNpmTasks 'grunt-bower-task'

-  grunt.registerTask 'default', ['coffee', 'uglify', 'compass', 'bower']
+  grunt.registerTask 'default', ['bower', 'coffee', 'uglify', 'compass']


Don't copy dev dependencies during build task

Closes #601

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -134,10 +134,21 @@
     directories = [
       'benchmark'
       'dot-atom'
-      'node_modules'
       'spec'
       'vendor'
     ]
+
+    {devDependencies} = grunt.file.readJSON('package.json')
+    {dependencies} = grunt.file.readJSON('package.json')
+    for child in fs.readdirSync('node_modules')
+      directory = path.join('node_modules', child)
+      try
+        {name} = grunt.file.readJSON(path.join(directory, 'package.json'))
+        if not devDependencies[name]? or dependencies[name]?
+          directories.push(directory)
+      catch e
+        directories.push(directory)
+
     cp directory, path.join(APP_DIR, directory) for directory in directories

     cp 'src', path.join(APP_DIR, 'src'), filter: /.+\.(cson|coffee|less)$/


Remove no-quiet flag

Remove no-quiet flag

--- build/tasks/bootstrap-atom.coffee
+++ build/tasks/bootstrap-atom.coffee
@@ -7,5 +7,5 @@

     process.chdir(grunt.config.get('workDir'))

-    cp.safeSpawn 'node', ['script/bootstrap', '--no-quiet'], ->
+    cp.safeSpawn 'node', ['script/bootstrap'], ->
       done()


Ensure we load components of each module only once

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -11,15 +11,18 @@
 log.pause()

 class ComponentLoader
-  @components = null
+  components = null
+  checked = []

   constructor: (@baseDir) ->

   getModuleComponents: (moduleDef) ->
     components = {}
+    checked.push moduleDef.name

     # Handle sub-modules
     _.each moduleDef.dependencies, (def) =>
+      return unless checked.indexOf(def.name) is -1
       depComponents = @getModuleComponents def
       return if _.isEmpty depComponents
       _.each depComponents, (cPath, name) ->


Copy fonts to correct directory

Copy fonts to correct directory

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -58,6 +58,9 @@
           {
             src: 'src/fonts/*'
             dest: 'dist/fonts/'
+            filter: 'isFile'
+            expand: true
+            flatten: true
           },
           {
             src: '*.md'


Re-add more test platforms

Re-add more test platforms

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,10 +8,10 @@
 module.exports = (grunt) ->

   browsers = []
-  #browsers.push browser for browser in ({browserName: "iphone", version: "#{vers}"} for vers in ["5.1", "6.0", "6.1", 7])
-  #browsers.push browser for browser in ({browserName: "android", version: "#{vers}"} for vers in ["4.0"])
-  #browsers.push browser for browser in ({browserName: "safari", version: "#{vers}"} for vers in [5..7])
-  #browsers.push browser for browser in ({platform: "XP", browserName: "opera", version: "#{vers}"} for vers in [11..12])
+  browsers.push browser for browser in ({browserName: "iphone", version: "#{vers}"} for vers in ["5.1", "6.0", "6.1", 7])
+  browsers.push browser for browser in ({browserName: "android", version: "#{vers}"} for vers in ["4.0"])
+  browsers.push browser for browser in ({browserName: "safari", version: "#{vers}"} for vers in [5..7])
+  browsers.push browser for browser in ({platform: "XP", browserName: "opera", version: "#{vers}"} for vers in [11..12])
   browsers.push browser for browser in ({platform: "XP", browserName: "googlechrome", version: "#{vers}"} for vers in [26..31])
   #browsers.push browser for browser in ({browserName: "firefox", version: "#{vers}"} for vers in [4..26] by 3)
   browsers.push browser for browser in ({browserName: "internet explorer", version: "#{vers}"} for vers in [6..11])


Removed slash in dir variables

Removed slash in dir variables

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,9 +3,9 @@
   require('load-grunt-tasks') grunt

   paths =
-    src: 'src/'
-    dist: 'dist/'
-    tmp: '.tmp/'
+    src: 'src'
+    dist: 'dist'
+    tmp: '.tmp'

   grunt.initConfig

@@ -13,16 +13,16 @@

     watch:
       jade:
-        files: ['<%= paths.src %>views/*.jade']
+        files: ['<%= paths.src %>/views/*.jade']
         tasks: ['jade']
       stylus:
-        files: ['<%= paths.src %>styles/*.styl']
+        files: ['<%= paths.src %>/styles/*.styl']
         tasks: ['stylus']
       coffee:
-        files: ['<%= paths.src %>scripts/*.coffee']
+        files: ['<%= paths.src %>/scripts/*.coffee']
         tasks: ['coffee', 'uglify:compile']
       copyFont:
-        files: ['<%= paths.src %>styles/fonts/*']
+        files: ['<%= paths.src %>/styles/fonts/*']
         tasks: ['copy:font']
       copyStatic:
         files: [
@@ -36,7 +36,7 @@
     jade:
       compile:
         files:
-          '<%= paths.dist %>index.html': '<%= paths.src %>views/index.jade'
+          '<%= paths.dist %>/index.html': '<%= paths.src %>/views/index.jade'

     stylus:
       compile:
@@ -49,32 +49,32 @@
             'normalize.css/normalize.css'
           ]
         files:
-          '<%= paths.dist %>styles/index.css': '<%= paths.src %>styles/index.styl'
+          '<%= paths.dist %>/styles/index.css': '<%= paths.src %>/styles/index.styl'

     coffee:
       compile:
         files:
-          '<%= paths.tmp %>scripts/index.js': '<%= paths.src %>scripts/index.coffee'
+          '<%= paths.tmp %>/scripts/index.js': '<%= paths.src %>/scripts/index.coffee'

     uglify:
       vendor:
         src: [
           'bower_components/jquery/dist/jquery.js'
         ]
-        dest: '<%= paths.dist %>scripts/vendor.js'
+        dest: '<%= paths.dist %>/scripts/vendor.js'
       compile:
         options:
           mangle: true
           compress: true
         src: [
-          '<%= paths.tmp %>scripts/index.js'
+          '<%= paths.tmp %>/scripts/index.js'
         ]
-        dest: '<%= paths.dist %>scripts/index.js'
+        dest: '<%= paths.dist %>/scripts/index.js'

     coffeelint:
       all: [
         'Gruntfile.coffee'
-        '<%= paths.src %>scripts/*.coffee'
+        '<%= paths.src %>/scripts/*.coffee'
       ]
       options:
         max_line_length: level: 'ignore'
@@ -84,14 +84,14 @@
       font:
         files: [
           expand: true
-          cwd: '<%= paths.src %>styles/fonts'
+          cwd: '<%= paths.src %>/styles/fonts'
           src: ['*']
-          dest: '<%= paths.dist %>styles/fonts'
+          dest: '<%= paths.dist %>/styles/fonts'
         ]
       static:
         files: [
           expand: true
-          cwd: '<%= paths.src %>'
+          cwd: '<%= paths.src %>/'
           src: [
             '**/*'
             '!views{,/**/*}'


activate live reload

activate live reload

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -90,6 +90,7 @@
         options:
           port: 4000
           base: '_site'
+          livereload: true

   grunt.registerTask "build", [
     "sass"


Temporarily remove firefox test platform

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -13,7 +13,7 @@
   #browsers.push browser for browser in ({browserName: "safari", version: "#{vers}"} for vers in [5..7])
   #browsers.push browser for browser in ({platform: "XP", browserName: "opera", version: "#{vers}"} for vers in [11..12])
   browsers.push browser for browser in ({platform: "XP", browserName: "googlechrome", version: "#{vers}"} for vers in [26..31])
-  browsers.push browser for browser in ({browserName: "firefox", version: "#{vers}"} for vers in [4..26] by 3)
+  #browsers.push browser for browser in ({browserName: "firefox", version: "#{vers}"} for vers in [4..26] by 3)
   browsers.push browser for browser in ({browserName: "internet explorer", version: "#{vers}"} for vers in [6..11])

   grunt.initConfig


Rename the uglification task

Rename the uglification task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -19,7 +19,7 @@
     uglify:
       options:
         report: 'min'
-      noflo:
+      engine:
         files:
           './browser/gss-engine.min.js': ['./browser/gss-engine.js']