primary blur

primary blur

--- src/Arena.coffee
+++ src/Arena.coffee
@@ -18,7 +18,7 @@
     @square.height = 450
     @square.anchor.x = 0.5
     @square.anchor.y = 0.5
-    @square.alpha = 0.045
+    @square.alpha = 0.02

     @photons = @game.add.group()

@@ -30,7 +30,7 @@
     @buffer.renderXY(@game.stage, 0, 0, false)

   update:->
-    @_init() if @photons.countLiving() < 4
+    @_init() if @photons.countLiving() < 3

   destroy:->

@@ -38,10 +38,9 @@
     @game.state.start('splash')

   _init:->
-    @_addPhoton(64, 400, 225, 100, 0, 0xff0000)
-    @_addPhoton(64, 400, 225, -100, 0, 0x00ff00)
-    @_addPhoton(64, 400, 225, 0, 100, 0xff00ff)
-    @_addPhoton(64, 400, 225, 0, -100, 0x00ffff)
+    @_addPhoton(128, 400, 225, 100, 0, 0xff0000)
+    @_addPhoton(128, 400, 225, -100, 0, 0x00ff00)
+    @_addPhoton(128, 400, 225, 0, 100, 0x0000ff)

   _split:=>
     return if @photons.countLiving() > 512
@@ -60,7 +59,7 @@
       @_addPhoton(data.r * 0.5, data.x, data.y, -data.vy, -data.vx, data.colour)

   _addPhoton:(r, x, y, vx, vy, colour)->
-    r = 16 if r < 16
+    r = 32 if r < 32
     photon = @game.add.sprite(x, y, 'circle')
     @game.physics.enable(photon, Phaser.Physics.ARCADE)
     photon.width = r
@@ -77,6 +76,7 @@
       obj.destroy()
     photon.checkWorldBounds = true
     photon.events.onOutOfBounds.add (obj)=>
+      obj.kill()
       @photons.remove(obj)

     @photons.add(photon)


improving deps tracking

improving deps tracking

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -822,7 +822,7 @@
 Bacon.dependsOn = (a,b) ->
   if a == b
     return false
-  deps = a.deps()
+  deps = a.allDeps
   for dep in deps
     if dep == b
       return true
@@ -840,17 +840,11 @@
       waiters.push {obs, f}
   flush = ->
     if waiters.length
-      console.log "flushing, waiters", (_.map _.toString, (_.map ((x) -> x.obs), waiters))
+      #console.log "flushing, waiters", (_.map _.toString, (_.map ((x) -> x.obs), waiters))
       ok = _.filter independent, waiters
-      console.log "ok to send", ok.length
-
-      # TODO: problem is that what's registered here may be an Observable
-      # that's skipped in the descriptions as an impl.detail, and so the
-      # deps never point to this one.
-
-      if waiters.length == 2
-        console.log("**", Bacon.dependsOn(waiters[0].obs, waiters[1].obs))
-        console.log("**", Bacon.dependsOn(waiters[1].obs, waiters[0].obs))
+
+      # TODO: still something amiss. Doesn't block updates..
+      #
       firstIndex = _.indexWhere waiters, independent
       throw "no independent observable" if firstIndex < 0
       {f} = waiters.splice(firstIndex, 1)[0]
@@ -948,6 +942,7 @@
     invoke = if lazy then _.id else (f) -> f()
     @subscribe = obs.subscribe if not @subscribe?
     @markEnded = -> @ended = true
+    @toString = @obs.toString
     if consume
       @consume = () -> invoke(queue.shift())
       @push  = (x) -> queue.push(x)
@@ -980,10 +975,15 @@
         _.flatMap findDeps, x
       else if isObservable(x)
         [x]
+      else if x instanceof Source
+        [x.obs]
       else
         []
     @apply = (obs) ->
-      obs.deps = -> findDeps(args)
+      prevAllDeps = obs.allDeps || []
+      dps = findDeps([context].concat(args))
+      obs.allDeps = dps.concat(prevAllDeps)
+      obs.deps = -> dps
       obs.toString = -> _.toString(context) + "." + _.toString(method) + "(" + _.map(_.toString, args) + ")"
       obs.desc = -> { context, method, args }
       obs


save global object

save global object

--- src/Bu.coffee
+++ src/Bu.coffee
@@ -1,13 +1,20 @@
 # namespace, constants and utility functions

+# global object
+global = window or this
+
 # namespace `Bu` is also a shortcut to class `Bu.Renderer`
-@Bu = () -> new Bu.Renderer arguments...
+global.Bu = () -> new Bu.Renderer arguments...
+
+# save global
+Bu.global = global

 ###
 # constants
 ###

+# library version
 Bu.VERSION = '0.3.1'

 # shapes related


Change callback for pushstate

Change callback for pushstate

--- source/core/route.coffee
+++ source/core/route.coffee
@@ -1,5 +1,12 @@
+###
+HTML5 API History Wrapper
+
+@namespace Atoms.Core
+@class Route
+
+@author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+###
 "use strict"
-

 Atoms.Url = do (a = Atoms) ->

@@ -12,9 +19,6 @@
   history       = false
   routes        = {}
   current_path  = null
-
-  current       = null
-
   forward       = true

   ###
@@ -90,19 +94,15 @@
         route.callback?.call(@, obj)
         break

+  _pushState = (properties) ->
+    if forward
+      Atoms.System.Layout.show properties.article, properties.section
+    else
+      Atoms.System.Layout.back properties.article, properties.section

   _listen = do ->
-    urls =
-      ""                   : (properties) -> console.error "callback /", properties
-      "/:article/:section" : (properties) =>
-        if forward
-          Atoms.System.Layout.show properties.article, properties.section
-        else
-          Atoms.System.Layout.back properties.article, properties.section
-
-    _addRoute(path, callback) for path, callback of urls
+    _addRoute "/:article/:section", _pushState
     Atoms.$(window).on "popstate", _change
-

   return {
     path    : _path


Avoid seed from being hoisted

Avoid seed from being hoisted

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -552,41 +552,42 @@

   now = Date.now()
   for i in [0...100] by 1
-    seed = now + i
-    it "updates the displayed text correctly when the underlying buffer changes: #{seed}", ->
-      random = new Random(seed)
-      buffer = new TextBuffer(text: buildRandomLines(random, 10))
-      invisibles = {}
-      invisibles.space = '•' if random(2) > 0
-      invisibles.eol = '¬' if random(2) > 0
-      invisibles.cr = '¤' if random(2) > 0
-      showIndentGuides = Boolean(random(2))
-      displayLayer = buffer.addDisplayLayer({tabLength: 4, invisibles, showIndentGuides})
-      textDecorationLayer = new TestDecorationLayer([], buffer, random)
-      displayLayer.setTextDecorationLayer(textDecorationLayer)
-
-      foldIds = []
-      screenLinesById = new Map
-
-      for j in [0...5] by 1
-        k = random(10)
-        if k < 2
-          createRandomFold(random, displayLayer, foldIds)
-        else if k < 4 and foldIds.length > 0
-          destroyRandomFold(random, displayLayer, foldIds)
-        else
-          performRandomChange(random, buffer, displayLayer)
-
-        # incrementally-updated text matches freshly computed text
-        expectedDisplayLayer = buffer.addDisplayLayer({foldsMarkerLayer: displayLayer.foldsMarkerLayer.copy(), tabLength: 4, invisibles, showIndentGuides})
-        expect(JSON.stringify(displayLayer.getText())).toBe(JSON.stringify(expectedDisplayLayer.getText()))
-
-        verifyPositionTranslations(displayLayer, expectedDisplayLayer)
-        verifyTokens(displayLayer)
-        verifyRightmostScreenPosition(displayLayer)
-        verifyScreenLineIds(displayLayer, screenLinesById)
-
-        expectedDisplayLayer.destroy()
+    do ->
+      seed = now + i
+      it "updates the displayed text correctly when the underlying buffer changes: #{seed}", ->
+        random = new Random(seed)
+        buffer = new TextBuffer(text: buildRandomLines(random, 10))
+        invisibles = {}
+        invisibles.space = '•' if random(2) > 0
+        invisibles.eol = '¬' if random(2) > 0
+        invisibles.cr = '¤' if random(2) > 0
+        showIndentGuides = Boolean(random(2))
+        displayLayer = buffer.addDisplayLayer({tabLength: 4, invisibles, showIndentGuides})
+        textDecorationLayer = new TestDecorationLayer([], buffer, random)
+        displayLayer.setTextDecorationLayer(textDecorationLayer)
+
+        foldIds = []
+        screenLinesById = new Map
+
+        for j in [0...5] by 1
+          k = random(10)
+          if k < 2
+            createRandomFold(random, displayLayer, foldIds)
+          else if k < 4 and foldIds.length > 0
+            destroyRandomFold(random, displayLayer, foldIds)
+          else
+            performRandomChange(random, buffer, displayLayer)
+
+          # incrementally-updated text matches freshly computed text
+          expectedDisplayLayer = buffer.addDisplayLayer({foldsMarkerLayer: displayLayer.foldsMarkerLayer.copy(), tabLength: 4, invisibles, showIndentGuides})
+          expect(JSON.stringify(displayLayer.getText())).toBe(JSON.stringify(expectedDisplayLayer.getText()))
+
+          verifyPositionTranslations(displayLayer, expectedDisplayLayer)
+          verifyTokens(displayLayer)
+          verifyRightmostScreenPosition(displayLayer)
+          verifyScreenLineIds(displayLayer, screenLinesById)
+
+          expectedDisplayLayer.destroy()

 performRandomChange = (random, buffer, displayLayer) ->
   tries = 10
@@ -633,8 +634,7 @@
 verifyTokens = (displayLayer) ->
   containingTags = []

-  tokenLines = getTokenLines(displayLayer)
-  for tokens in tokenLines
+  for tokens in getTokenLines(displayLayer)
     for {closeTags, openTags, text} in tokens
       for tag in closeTags
         mostRecentOpenTag = containingTags.pop()


--- spec/notice/client_spec.coffee
+++ spec/notice/client_spec.coffee
@@ -104,7 +104,9 @@
                 @emitted   = {}
                 Connector.connect = (opts) =>
                     on: (event, handler) =>
-                        handler @whenEvent[event] if @whenEvent[event]?
+                        if @whenEvent[event]?
+                            handler @whenEvent[event]
+                            @whenEvent[event] = handler # so it can be called again
                     emit: (event, args...) =>
                         @emitted[event] = args

@@ -184,7 +186,7 @@
                     done()

-                it 'sets connection.state to connecting', (done) ->
+                it 'when connect.state is pending it sets connection.state to connecting', (done) ->

                     Date.now = -> 1
                     @whenEvent['connect'] = true
@@ -199,9 +201,28 @@
                         stateAt: 1
                     done()

+                it 'when connect.state is interrupted it sets the connection.state to resuming', (done) ->
+
+                    Date.now = -> 1
+                    @whenEvent['connect'] = true
+                    @whenEvent['accept'] = true
+                    @whenEvent['disconnect'] = true
+
+                    Client = client()
+                    Client.create 'client name', @opts
+
+                    @whenEvent['connect']()  # refire connect
+                    connection = _client().clients['client name'].connection
+                    connection.should.eql
+                        state: 'resuming'
+                        stateAt: 1
+                    done()
+
+
+
             context 'accept', ->

-                it 'sets connect.state to accepted', (done) ->
+                it 'when connect.state is connecting it sets connect.state to accepted', (done) ->

                     Date.now = -> 1
                     @whenEvent['connect'] = true
@@ -216,7 +237,7 @@
                     done()

-                it 'calls back with the accepted client', (done) ->
+                it 'when connect.state is connecting calls back with the accepted client', (done) ->

                     Date.now = -> 1
                     @whenEvent['connect'] = true
@@ -235,6 +256,11 @@
                             state: 'accepted'
                             stateAt: 1
                         done()
+
+                it 'when connect.state is resuming it sets the state to accepted'
+                it 'when connect.state is resuming it increments the interrupted count'
+                it 'when connect.state is resuming it does not callback with the accepted client'
+                it 'when connect.state is resuming it informs local (middleware) of resumption'


taking out debugger call

taking out debugger call

--- src/coffee/base.coffee
+++ src/coffee/base.coffee
@@ -33,7 +33,7 @@
   try
     binder['eventers'][target.id] = target
   catch error
-    debugger;
+
   target.on(event, callback, binder)
   # also need to bind destroy to remove obj from eventers.
   # no special logic needed to manage this life cycle, because


Color

Color

--- site/app.coffee
+++ site/app.coffee
@@ -191,23 +191,23 @@

 activity_level = (ili, mean, stdev) ->
   l = (ili-mean)/stdev
-  if l < 0
+  if l < 1.1
     return 1
-  if l < 1
+  if l < 2.5
     return 2
-  if l < 2
+  if l < 4.0
     return 3
-  if l < 3
+  if l < 4.9
     return 4
-  if l < 4
+  if l < 6.0
     return 5
-  if l < 5
+  if l < 7.6
     return 6
-  if l < 6
+  if l < 8.0
     return 7
-  if l < 7
+  if l < 9.5
     return 8
-  if l < 8
+  if l < 12
     return 9
   return 10


:fix: when I simplify vimState::destroy() it reveal bug.

If properly destroyed() in vimState::destroy() this spec guard should
has to be necessary from long before.

--- spec/vim-mode-plus-spec.coffee
+++ spec/vim-mode-plus-spec.coffee
@@ -17,7 +17,7 @@
       atom.packages.activatePackage('status-bar')

   afterEach ->
-    vimState.activate('reset')
+    vimState.activate('reset') unless vimState.destroyed

   describe ".activate", ->
     it "puts the editor in normal-mode initially by default", ->


Fixed UI typo.

Fixed UI typo.

--- src/UI.coffee
+++ src/UI.coffee
@@ -122,7 +122,7 @@
     downloadROMsList = ->
         setROMsDialogMessage "Downloading game list..."
         $.getJSON("roms/")
-            .fail(-> setROMsDialogMessage "Error: Unable to download games list!")
+            .fail(-> setROMsDialogMessage "Error: Unable to download game list!")
             .done((data) -> setROMsDialogList data)

     setROMsDialogMessage = (message) ->


:haircut:

:haircut:

--- spec/coffeescript.coffee
+++ spec/coffeescript.coffee
@@ -40,42 +40,3 @@
 OPERATOR = /// ^ (
 ?: [-=]>             # function
 ) ///
-
-
-OPERATOR = /// ^ (
-  ?: [-=]>             # function
-   | [-+*/%<>&|^!?=]=  # compound assign / compare
-   | >>>=?             # zero-fill right shift
-   | ([-+:])\1         # doubles
-   | ([&|<>])\2=?      # logic / shift
-   | \?\.              # soak access
-   | \.{2,3}           # range or splat
-) ///
-
-
-word = ///(?<=^|[^\\w\\d\\*])\\*\\*\\*(?!$|\\*|\\s)///
-
-square = (x) -> x * x
-cube   = (x) -> square(x) * x
-
-# Functions:
-square = (x) -> x * x
-
-# Arrays:
-list = [1, 2, 3, 4, 5]
-
-# Objects:
-math =
-  root:   Math.sqrt
-  square: square
-  cube:   (x) -> x * square x
-
-# Splats:
-race = (winner, runners...) ->
-  print winner, runners
-
-# Existence:
-alert "I knew it!" if elvis?
-
-# Array comprehensions:
-cubes = (math.cube num for num in list)


:new: Add specs for new async behavior

:new: Add specs for new async behavior

--- spec/emitter-spec.coffee
+++ spec/emitter-spec.coffee
@@ -30,3 +30,20 @@
     emitter = new Emitter
     expect(-> emitter.on('foo', null)).toThrow()
     expect(-> emitter.on('foo', 'a')).toThrow()
+
+  it "works well with async callbacks", ->
+    called = 0
+    emitter = new Emitter()
+    emitter.on('foo', ->
+      new Promise (resolve) ->
+        called++
+        resolve()
+    )
+    emitter.on('foo', ->
+      new Promise (resolve) ->
+        called++
+        resolve()
+    )
+    waitsForPromise ->
+      emitter.emit('foo').then ->
+        expect(called).toBe(2)


Activate package again to verify deactivate event reporting

--- spec/metrics-spec.coffee
+++ spec/metrics-spec.coffee
@@ -20,7 +20,8 @@
     atom.packages.activatePackage('metrics')
     spyOn(Reporter, 'request')
     atom.packages.deactivatePackage('metrics')
-    expect(Reporter.request).toHaveBeenCalled()
+    atom.packages.activatePackage('metrics')
+    expect(Reporter.request.callCount).toBe 2

     requestArgs = Reporter.request.calls[0].args[0]
     body = JSON.parse(requestArgs.body)


Added: comments on all the code

Added: comments on all the code

--- src/aria-tips.coffee
+++ src/aria-tips.coffee
@@ -1,18 +1,69 @@
+# Wrappers for AMD and CommonJS. Fallback to global scope.
 ((root, factory) ->

+    # If AMD is used.
     if typeof define is "function" and define.amd
         define [], factory
+
+    # If CommonJS is used.
     else if typeof module is "object" and module.exports
         module.exports = factory()
+
+    # Otherwise bind to `window` object.
     else
         root.AriaTips = factory()

 )(this, ->

-    arrowDelta = 6
-
+    ###
+        * Tooltips are the elements that are displayed when the mouse hovers a
+        trigger.
+
+        * Triggers are the elements that listen to mouseover events in order to
+        show the tooltips.
+    ###
+
+
+    # Number of pixels the tooltip's arrow takes.
+    ARROW_DELTA = 6
+
+
+    # Unbinders are functions that unbind events on triggers. They are defined
+    # when the events are bound.
     unbinders = []

+
+    # Do all the magic needed to show the tooltips!
+    bindAll = ->
+        # Unbind all existing events in order to prevent listening to the same
+        # event multiple times.
+        unbindAll()
+
+        # Get all tooltips in the DOM.
+        tips = document.querySelectorAll '[role=tooltip]'
+
+        for tip in tips
+            # Tooltips need to be appended to body for global positioning.
+            document.body.appendChild tip
+
+            # Get all triggers in the DOM.
+            triggers = document.querySelectorAll "[aria-describedby=#{tip.id}]"
+            for trigger in triggers
+
+                # Bind the mouseover/mouseout events on the trigger.
+                unbinder = bindEvents.call trigger
+
+                # Store the returned function to unbind the events if needed.
+                unbinders.push unbinder
+
+
+    # Remove all the magic that show the tooltips.
+    unbindAll = ->
+        unbinder() for unbinder in unbinders
+        unbinders = []
+
+
+    # Return the opposite position, given a position.
     oppositePosition = (position) ->
         switch position
             when 'top'    then 'bottom'
@@ -21,93 +72,129 @@
             when 'right'  then 'left'

-    attachTooltips = ->
-        unbindAll()
-        tips = document.querySelectorAll '[role=tooltip]'
-        for tip in tips
-            # Tips need to be appended to body for global positioning.
-            document.body.appendChild tip
-            controls = document.querySelectorAll "[aria-describedby=#{tip.id}]"
-            for control in controls
-                unbinder = bindHoverEvents.call control
-                unbinders.push unbinder
-
-
-    unbindAll = ->
-        unbinder() for unbinder in unbinders
-        unbinders = []
-
-
-    positionTooltip = (control)->
+    # Position the tooltip to its trigger.
+    # `this` must reference the tooltip.
+    positionTooltip = (trigger) ->
+
+        # Reset tooltip's direction if it has been previously overriden.
         resetTooltip.call @

-        direction = control.dataset.tooltipDirection or @dataset.tooltipDirection
-        boundingBox = control.getBoundingClientRect()
+        # Direction can be defined in the trigger or in the tooltip. The trigger
+        # has priority.
+        direction = trigger.dataset.tooltipDirection or \
+                    @dataset.tooltipDirection
+
+        # Get the trigger's center coordinates.
+        boundingBox = trigger.getBoundingClientRect()
         center =
             x: 0 | boundingBox.left + (boundingBox.right - boundingBox.left) / 2
             y: 0 | boundingBox.top + (boundingBox.bottom - boundingBox.top) / 2
+
+        # Initialize the tooltip positionning.
         result =
             top:    'auto'
             bottom: 'auto'
             left:   'auto'
             right:  'auto'

+        # Get the direction's position value.
         position = switch direction
             when 'top'    then window.innerHeight - boundingBox.top
             when 'bottom' then boundingBox.bottom
             when 'left'   then window.innerWidth - boundingBox.left
             when 'right'  then boundingBox.right

-        result[oppositePosition(direction)] = "#{0 | position + arrowDelta}px"
+        # Get the opposite direction's position value.
+        oppositeDirection = oppositePosition direction
+        result[oppositeDirection] = "#{0 | position + ARROW_DELTA}px"
+
+        # Set the position for the axis perpendicular to the one defined
+        # by (position, oppositeDirection).
         switch direction
             when 'top', 'bottom' then result.left = "#{center.x}px"
             when 'left', 'right' then result.top = "#{center.y}px"

+        # Perform the positioning by setting the tooltip's attributes.
         @style[prop] = value for prop, value of result
+
+        # If the default tooltip's direction has been overriden by the trigger,
+        # we must keep the original direction, and set the overriden direction
+        # to the tooltip.
         unless direction is @dataset.tooltipDirection
             @origDirection = @dataset.tooltipDirection
             @dataset.tooltipDirection = direction

+    # Reset tooltip's direction.
+    # `this` must reference the tooltip.
     resetTooltip = ->
         if @origDirection
             @dataset.tooltipDirection = @origDirection
             @origDirection = null

-    fillTooltipLabel = (control) ->
+    # If the trigger has a defined `aria-label`, the lib automatically appends
+    # it in a `pre` tag.
+    # `this` must reference the tooltip.
+    fillTooltipLabel = (trigger) ->
+
+        # Remove existing labels in the tooltip.
         labels = @querySelectorAll 'pre.label'
         label.parentNode.removeChild label for label in labels

-        labelText = control.getAttribute 'aria-label'
-        return unless labelText
-
-        label = document.createElement 'pre'
-        label.classList.add 'label'
-        label.innerHTML = "(#{labelText})"
-        @appendChild label
-
-
-    bindHoverEvents = ->
-        tip = document.querySelector "##{@getAttribute 'aria-describedby'}"
+        # Get label from the trigger.
+        labelText = trigger.getAttribute 'aria-label'
+
+        # If there is one, append it to the tooltip.
+        if labelText
+            label = document.createElement 'pre'
+            label.classList.add 'label'
+            label.innerHTML = "(#{labelText})"
+            @appendChild label
+
+
+    # Bind events on for a given trigger element.
+    # `this` must reference the trigger element.
+    bindEvents = ->
+
+        # Get the related tooltip
+        tooltipId = @getAttribute 'aria-describedby'
+        tip = document.querySelector "##{tooltipId}"
+
+        # Initialize timeout in the scope.
         timeout = null

+        # `mouseover` event handler.
         onMouseOver = =>
+
+            # Since the tooltip's appearance animation make the trigger lose
+            # the focus, the tooltip needs to be its own trigger to prevent it
+            # from "flashing". It's basically a debounce mechanism.
             tip.addEventListener 'mouseover', onMouseOver
             tip.addEventListener 'mouseout', onMouseOut

+            # If the tooltip is appearing or disappearing, clear the timeout.
             if timeout
                 clearTimeout timeout
                 timeout = null
                 tip.setAttribute 'aria-hidden', false
             else
+                # Initialize the tooltip.
                 fillTooltipLabel.call tip, @
+
+                # Position the tooltip.
                 positionTooltip.call tip, @
+
+                # Mark as shown after a short duration.
                 timeout = setTimeout ->
                     tip.setAttribute 'aria-hidden', false
                 , 300

+        # `mouseover` event handler.
         onMouseOut = ->
+
+            # For the same reason as in `onMouseOver`, we debounce the
+            # `mouseout` event handler.
             clearTimeout timeout
             timeout = setTimeout ->
                 timeout = null
@@ -116,9 +203,14 @@
                 tip.setAttribute 'aria-hidden', true
             , 30

+
+        # Bind the actual events.
         @addEventListener 'mouseover', onMouseOver
         @addEventListener 'mouseout', onMouseOut

+        # Since a tooltip management is scoped (there is one pair of handlers
+        # for onMouseOver/onMouseOut events for each trigger), we return an
+        # unbind function so it can be called later on.
         unbindEvents = =>
             tip.removeEventListener 'mouseover', onMouseOver
             tip.removeEventListener 'mouseout', onMouseOut
@@ -127,9 +219,10 @@

         return unbindEvents

+
+    # The module's public interface.
     return {
-        bind: -> attachTooltips()
+        bind: -> bindAll()
         unbind: -> unbindAll()
     }
-
 )


Support sending a string as the response

--- src/Layer.coffee
+++ src/Layer.coffee
@@ -59,6 +59,9 @@
       if result.constructor is Object
         return res.send result

+      if typeof result is "string"
+        return res.send result
+
       if result instanceof Error
         res.status 400 if res.statusCode is 200
         return res.send {error: result.message}


Stop supporting `path.exists`

Stop supporting `path.exists`

--- src/biscotto.coffee
+++ src/biscotto.coffee
@@ -54,7 +54,7 @@

     # Read .biscottoopts project defaults
     try
-      if (fs.existsSync || path.existsSync)('.biscottoopts')
+      if fs.existsSync('.biscottoopts')
         configs = fs.readFileSync '.biscottoopts', 'utf8'

         for config in configs.split('\n')
@@ -161,9 +161,7 @@
             describe  : 'Show internal methods'
           )
           .options('metadata',
-            boolean   : true
-            default   : biscottoopts.metadata || true
-            describe  : 'Whether to generate metadata slug'
+            describe  : 'The path to the top-level npm module directory'
           )
           .options('stability',
             describe  : 'Set stability level'
@@ -310,7 +308,7 @@
       'src'
       'lib'
       'app'
-    ], (fs.exists || path.exists), (results) ->
+    ], fs.exists, (results) ->
       results.push '.' if results.length is 0
       done null, results

@@ -323,7 +321,7 @@
       'readme.markdown'
       'readme.md'
       'readme'
-    ], (fs.exists || path.exists), (results) -> done null, _.first(results) || ''
+    ], fs.exists, (results) -> done null, _.first(results) || ''

   # Public: Find extra project files in the repository.
   @detectExtras: (done) ->
@@ -338,14 +336,14 @@
       'LICENSE.markdown'
       'LICENSE.MIT'
       'LICENSE.GPL'
-    ], (fs.exists || path.exists), (results) -> done null, results
+    ], fs.exists, (results) -> done null, results

   # Public: Find the project name by either parsing `package.json`,
   # or getting the current working directory name.
   #
   # done - The {Function} callback to call once this is done
   @detectName: (done) ->
-    if (fs.exists || path.exists)('package.json')
+    if fs.existsSync('package.json')
       name = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'))['name']
     else
       name = path.basename(process.cwd())


fix help for string

fix help for string

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -37,7 +37,9 @@
       if arguments.length > 0
         if _.isString cmd
           op = @imported_context_fns[cmd]
-          fns = cmd: op if op?
+          if op?
+            fns = {}
+            fns[cmd] = op
         else if cmd?._lead_context_name
           name = cmd._lead_context_name
           if cmd._lead_context_fn?


added require for fs

added require for fs

--- src/apihero-module-jade.coffee
+++ src/apihero-module-jade.coffee
@@ -1,3 +1,4 @@
+fs   = require 'fs'
 path = require 'path'
 jade = require 'jade'


QUALITY: Remove unnecessary download method

--- src/app/api/models/task.coffee
+++ src/app/api/models/task.coffee
@@ -22,12 +22,6 @@
   Task.generateCommentsAttachmentUrl = (project, task, comment) ->
     "#{api}/projects/#{project.project_id}/task_def_id/#{task.task_definition_id}/comments/#{comment.id}?as_attachment=false&auth_token=#{currentUser.authenticationToken}"

-  Task.downloadCommentAttachment = (project, task, comment, onSuccess) ->
-    $http.get("#{api}/projects/#{project.project_id}/task_def_id/#{task.task_definition_id}/comments/#{comment.id}?as_attachment=false&auth_token=#{currentUser.authenticationToken}").then(
-      (successResponse) -> onSuccess(successResponse)
-      (errorResponse) -> onError(errorResponse)
-    )
-
   Task.generateSubmissionUrl = (project, task) ->
     "#{api}/projects/#{project.project_id}/task_def_id/#{task.definition.id}/submission?auth_token=#{currentUser.authenticationToken}"


Improve performance of shape creation

Improve performance of shape creation

--- src/core/shapes.coffee
+++ src/core/shapes.coffee
@@ -8,8 +8,9 @@

 defineShape = (name, props) ->
-  Shape = (args...) ->
-    props.constructor.call(this, args...)
+  # improve Chrome JIT perf by not using arguments object
+  Shape = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) ->
+    props.constructor.call(this, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
     this
   Shape.prototype.className = name
   Shape.fromJSON = props.fromJSON
@@ -51,8 +52,9 @@
   Shape

-createShape = (name, args...) ->
-  s = new shapes[name](args...)
+# improve Chrome JIT perf by not using arguments object
+createShape = (name, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) ->
+  s = new shapes[name](a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
   s.id = util.getGUID()
   s


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -147,7 +147,7 @@
           moduleName = Module
           Module = Module.constantize()
         else
-
+          # Attempt to discover module na
         self.extend Module(I, self) unless I.included_modules.include moduleName

       return self


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -151,5 +151,5 @@
     @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
     include: (modules...) ->
-      modules.each (Module
+      i
       self.extend Module(I, self)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -146,6 +146,7 @@
         if Module.isString?()
           moduleName = Module
           Module = Module.constantize()
+        else if moduleName =
         else
           # Attempt to look up module in global namespace
           for key, value of root


allow non-flat stats (#4)

allow non-flat stats (#4)

Merged request.

--- src/ScreepsStatsd.coffee
+++ src/ScreepsStatsd.coffee
@@ -77,9 +77,12 @@
       succes = true
       @report(finalData)

-  report: (data) =>
-    console.log "Pushing to gauges - " + new Date()
-    for k,v of data
-      @client.gauge k, v
+  report: (data, prefix="") =>
+    if prefix is ''
+      console.log "Pushing to gauges - " + new Date()
+      if typeof v is 'object'
+        @report(v, prefix+k+'.')
+      else
+        @client.gauge prefix+k, v

 module.exports = ScreepsStatsd


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/collision.coffee
+++ src/collision.coffee
@@ -216,7 +216,8 @@
     ###
     rayRectangle: (source, direction, target) ->
       unless target.xw? and target.yw?
-        return Collision.rayRectangl
+        return Collision.rayRectangle()
+
       xw = target.xw
       yw = target.yw


formatting

formatting

--- src/color/hsv.coffee
+++ src/color/hsv.coffee
@@ -35,15 +35,13 @@

 rgbM.extendRgb (rgb) ->
+
   rgb::hsv = (T = new Hsv()) ->
-
     max = Math.max @r, @g, @b
     min = Math.min @r, @g, @b
     T.v = max
     d = max - min
-    T.s = if max != 0 then d / max
-    else
-      0
+    T.s = if max != 0 then d / max else 0
     if T.s == 0
       T.h = 0
     else
@@ -53,7 +51,7 @@
           T.h += (@g - @b) / d
         when @g
           T.h = 2 + (@b - @r) / d
-        else
-          T.h = 4 + (@r - @g) / d # blue
+        else # blue
+          T.h = 4 + (@r - @g) / d
       T.h /= 6
     T


performance update for the component

performance update for the component

--- src/component.coffee
+++ src/component.coffee
@@ -29,7 +29,9 @@
 	# log 'component with fields %j has been defined before', fields

 	# Create properties based on the fields
-	props = Object.create null
+	props = Object.create(null)
+	props[sData] = writable: yes
+
 	fast.reduce fields, createProps, props if fields isnt emptyFields

 	proto = {}
@@ -66,7 +68,6 @@
 	return fields

 createProps = (props, field, i) ->
-	props[sData] = writable: yes
 	props[field] =
 		get: ->
 			return this[sData]?[i]


Restored !configFile?

Restored !configFile?

--- src/beautifiers/rubocop.coffee
+++ src/beautifiers/rubocop.coffee
@@ -37,7 +37,8 @@
     )

     # Find or generate a config file if non exists
-    if !@findFile(path.dirname(fullPath), ".rubocop.yml")?
+    configFile = @findFile(path.dirname(fullPath), ".rubocop.yml")
+    if !configFile?
       yaml = require("yaml-front-matter")
       config = {
         "Style/IndentationWidth":


Protect from exceptions when attaching editor views w/ dead editors

Addresses #1306

We still need to know why this is happening, so I left an exception in
non-release builds. Since the pane system is about to change a lot I
think this is good enough for now.

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -780,6 +780,14 @@

   afterAttach: (onDom) ->
     return unless onDom
+
+    # TODO: Remove this guard when we understand why this is happening
+    unless @editor.isAlive()
+      if atom.isReleasedVersion()
+        return
+      else
+        throw new Error("Assertion failure: EditorView is getting attached to a dead editor. Why?")
+
     @redraw() if @redrawOnReattach
     return if @attached
     @attached = true


report new embed format

report new embed format

--- src/core/line.coffee
+++ src/core/line.coffee
@@ -160,7 +160,11 @@
     @delta = new Delta()
     @leaves.toArray().forEach((leaf) =>
       @length += leaf.length
-      @delta.insert(leaf.text, leaf.formats)
+      # TODO use constant for embed type
+      if dom.EMBED_TAGS[leaf.node.tagName]?
+        @delta.insert(1, leaf.formats)
+      else
+        @delta.insert(leaf.text, leaf.formats)
     )
     @delta.insert('\n', @formats)


Save author displayName when new contact is received

--- src/fleepClient.coffee
+++ src/fleepClient.coffee
@@ -24,7 +24,7 @@
       @poll()

-  post: (path, body, callback) ->
+  post: (path, body = {}, callback) ->
     request = new WebRequest(@robot.logger)
     request.post path, body, callback, @ticket, @token_id

@@ -70,17 +70,25 @@
       resp.event_horizon
     @robot.logger.debug 'Finished handling long poll response'

+
   # Processes a single Event object in a list of Fleep events
   handleStreamEvent: (event) =>
+
+    @robot.logger.debug event

     # Event does not have a rec_type, API error?
     if not event.mk_rec_type?
       @robot.logger.error 'Invalid response from the server'
       return

-    # New contact information, currently we don't do anything with it
+    # New contact information
     if event.mk_rec_type is 'contact'
-      @robot.logger.debug event
+      user = @robot.brain.userForId event.account_id
+
+      # Only save the contact name if it's currently unknown
+      if not user.name? or user.name is user.id
+        user.name = event.display_name
+        @robot.logger.debug "New contact: id #{user.id}, name #{user.name}"
       return

     # Skip everything but text message events
@@ -110,7 +118,6 @@
     # Ignore messages without the 'message' key - some invalid state
     if not event.message?
       @robot.logger.error 'Invalid API response from the server!'
-      @robot.logger.debug event
       return

     message = event.message.replace(/(<([^>]+)>)/ig,"")
@@ -123,9 +130,8 @@
     author = @robot.brain.userForId author_id
     author.room = conversation_id
     author.reply_to = author_id
-
+
     @emit 'gotMessage', author, message
-

   poll: =>
     @robot.logger.debug 'Starting long poll request'
@@ -166,4 +172,9 @@

     @robot.logger.debug "Changing bot nick"
     @post 'account/configure', {display_name: @name}, (err, resp) ->
-      @robot.logger.debug resp+      @robot.logger.debug resp
+
+    # Fetch contact info
+    @robot.logger.debug "Syncing contacts"
+    @post 'contact/sync/all', {ignore: []}, (err, resp) =>
+      @handleStreamEvent contact for contact in resp.contacts


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -19,7 +19,7 @@
     I.flickerDuration = I.flickerDuration.approach(0, 1)

     if I.flickerDuration and I.age.mod(I.flickerFrequency) is 0
-      if I.
+      if I.alpha is I.flickerAP
       I.alpha = I.flickerAlpha
     else
       I.alpha = originalAlpha


Atualiza opções do Price

Atualiza opções do Price

--- src/coffee/portal-price.coffee
+++ src/coffee/portal-price.coffee
@@ -22,8 +22,8 @@
 		@bindEvents()

 	render: =>
-		if @sku
-			dust.render 'price', @sku, (err, out) =>
+		if @sku or @options.fallback is 'sku'
+			dust.render 'price', @sku or @originalSku, (err, out) =>
 				throw new Error "Price Dust error: #{err}" if err
 				@element.html out
 				@update()
@@ -100,6 +100,5 @@

 # PLUGIN DEFAULTS
 $.fn.price.defaults =
-	useOriginalHTML: true
-	useOriginalSku: false
+	fallback: 'html'
 	originalSku: null


hack to avoid non-printing chars breaking compiled JS

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -259,6 +259,16 @@
   ge: '>='
   gt: '>'
   le: '<='
+
+escapeStr = (str) ->
+  str.replace(/[\\]/g, '\\\\')
+     .replace(/[\"]/g, '\\\"')
+     .replace(/[\/]/g, '\\/')
+     .replace(/[\b]/g, '\\b')
+     .replace(/[\f]/g, '\\f')
+     .replace(/[\n]/g, '\\n')
+     .replace(/[\r]/g, '\\r')
+     .replace(/[\t]/g, '\\t')

 compile_class_handlers =
   PushOpcode: (b) -> b.push @value
@@ -275,7 +285,7 @@
   LoadConstantOpcode: (b) ->
     val = @constant.value
     if @constant.type is 'String'
-      b.push "rs.init_string('#{@str_constant.value}', true)"
+      b.push "rs.init_string('#{escapeStr @str_constant.value}', true)"
     else if @constant.type is 'class'
       # this may not be side-effect independent if we can change classloaders at
       # runtime, but for now we can assume it is


Rename Meter module

Rename Meter module

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -159,7 +159,7 @@
   "Drawable"
   "Expirable"
   "Follow"
-  "GameObject.Metered"
+  "GameObject.Meter"
   "Movable"
   "Rotatable"
   "TimedEvents"


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -4,7 +4,7 @@

 player = GameObject
-  health: 100
+  health:

 player.cooldown "health",
   target: 100


Fixes #8

Fixes #8

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -258,6 +258,7 @@
     if cards?
       for i in [0...cards.length] by 1
         responseString += "\n#{i}: #{cards[i]}"
+    responseString += "\nCurrent black card: *#{db.blackCard}*"
     robot.messageRoom sender(msg), responseString

   robot.hear /cah (submit|play)(?: ([0-4]+))+$/i, (msg) ->


Fix \e overlap

Fix \e overlap

--- src/glyphs/lowercases/e.coffee
+++ src/glyphs/lowercases/e.coffee
@@ -1,7 +1,7 @@
 exports.glyphs['e'] =
 	unicode: 'e'
 	ot:
-		advanceWidth: width * 540 + thickness * 2 - ( 86 * 2 )
+		advanceWidth: contours[0].nodes[2].expandedTo[1].x + 30 * spacing
 	anchors:
 		0:
 			x: 0
@@ -17,6 +17,24 @@
 			closed: false
 			nodes:
 				0:
+					x: contours[0].nodes[4].expandedTo[0].x
+					y: contours[0].nodes[2].y
+					dirOut: 0 + 'deg'
+					typeOut: 'line'
+					expand: Object({
+						width: thickness * ( 20 / 85 )
+						distr: 1
+					})
+				1:
+					x: contours[0].nodes[2].x
+					y: contours[0].nodes[2].y
+					dirOut: 0 + 'deg'
+					expand: Object({
+						angle: - 90 + 'deg'
+						width: thickness * ( 25 / 85 )
+						distr: 1
+					})
+				2:
 					x: ( 404 + (42) ) * width
 					y: xHeight * ( 340 / 500 ) * crossbar
 					dirOut: Math.max( 90, 150 - ( 50 / 500 ) * xHeight ) + 'deg'
@@ -26,8 +44,8 @@
 						angle: 0 + 'deg'
 						distr: 0.75
 					})
-				1:
-					x: contours[0].nodes[2].expandedTo[1].x + ( contours[0].nodes[0].expandedTo[1].x - contours[0].nodes[2].expandedTo[1].x ) * (295-50)/(495-50)
+				3:
+					x: contours[0].nodes[4].expandedTo[1].x + ( contours[0].nodes[2].expandedTo[1].x - contours[0].nodes[4].expandedTo[1].x ) * (295-50)/(495-50)
 					y: xHeight + overshoot
 					dirOut: 180 + 'deg'
 					type: 'smooth'
@@ -36,7 +54,7 @@
 						angle: - 104 + 180 + 'deg'
 						distr: 1
 					})
-				2:
+				4:
 					x: 45
 					y: xHeight * ( 240 / 500 )
 					dirOut: 90 + 'deg'
@@ -46,9 +64,9 @@
 						angle: 21 + 180 + 'deg'
 						distr: 0.75
 					})
-				3:
+				5:
 					x: 270
-					x: contours[0].nodes[2].expandedTo[1].x + ( contours[0].nodes[0].expandedTo[1].x - contours[0].nodes[2].expandedTo[1].x ) * 0.5
+					x: contours[0].nodes[4].expandedTo[1].x + ( contours[0].nodes[2].expandedTo[1].x - contours[0].nodes[4].expandedTo[1].x ) * 0.5
 					y: - overshoot
 					dirOut: 0 + 'deg'
 					type: 'smooth'
@@ -58,38 +76,16 @@
 						width: thickness * ( 60 / 85 )
 						distr: 1
 					})
-				4:
-					x: contours[0].nodes[0].expandedTo[1].x - ( 20 / 85 ) * thickness + ( 10 / 85 ) * thickness * aperture - 10
+				6:
+					x: contours[0].nodes[2].expandedTo[1].x - ( 20 / 85 ) * thickness + ( 10 / 85 ) * thickness * aperture - 10
 					y: xHeight * ( 135 / 500 ) + ( 20 / 85 ) * thickness - 20
-					y: Math.max( contours[0].nodes[3].expandedTo[0].y + thickness / 10, 40 + xHeight * ( 95 / 500 ) * aperture )
+					y: Math.max( contours[0].nodes[5].expandedTo[0].y + thickness / 10, 40 + xHeight * ( 95 / 500 ) * aperture )
 					# dirIn: Math.min( - 116, Math.max( - 100 , - ( 100 / 500 ) * xHeight * aperture ) ) + 'deg'
 					# dirIn: - 110 + 'deg'
 					dirIn: Math.min( - 140 + ( 30 / 500 ) * xHeight * aperture , - 90 ) + 'deg'
 					expand: Object({
 						angle: 163 + 180 + 'deg'
-						angle: contours[0].nodes[4].dirIn + Math.PI / 2
+						angle: contours[0].nodes[6].dirIn + Math.PI / 2
 						distr: 0.25
 						width: thickness * ( 11 / 85 )
 					})
-		1:
-			skeleton: true
-			closed: false
-			nodes:
-				0:
-					x: contours[0].nodes[2].expandedTo[0].x
-					y: contours[0].nodes[0].y
-					dirOut: 0 + 'deg'
-					typeOut: 'line'
-					expand: Object({
-						width: thickness * ( 20 / 85 )
-						distr: 1
-					})
-				1:
-					x: contours[0].nodes[0].x
-					y: contours[0].nodes[0].y
-					dirOut: 0 + 'deg'
-					expand: Object({
-						angle: - 90 + 'deg'
-						width: thickness * ( 25 / 85 )
-						distr: 1
-					})


adjust comment style

adjust comment style

--- src/lu.coffee
+++ src/lu.coffee
@@ -6,12 +6,11 @@
 T = require "ut1l/create/throwable"
 fail = T "MatrixException"

-###
-  A very basic LU decomposition implementation without pivoting. Decomposition is done in place. Given buffer must
-  be square and regular. The values of L below the diagonal are stored. The ones on the diagonal and the zeros
-  above the diagonal are not stored. The values of U on and above the diagonal are stored. The zero values below
-  the diagonal are not stored.
-###
+
+# A very basic LU decomposition implementation without pivoting. Decomposition is done in place. Given buffer must
+# be square and regular. The values of L below the diagonal are stored. The ones on the diagonal and the zeros
+# above the diagonal are not stored. The values of U on and above the diagonal are stored. The zero values below
+# the diagonal are not stored.

 luDecompConstructor = (A, T = do A.clone) ->


using a cross platform child reference

using a cross platform child reference

--- src/mixins/list.coffee
+++ src/mixins/list.coffee
@@ -132,7 +132,7 @@
       @views[model.cid] = view

       if container
-        index = container.children.length
+        index = container.childNodes.length
         model.index = index
       else
         # we must find what position this view should be in


don't fold on 3-4 of a kind

don't fold on 3-4 of a kind

--- src/valueHand.coffee
+++ src/valueHand.coffee
@@ -18,6 +18,11 @@
   .value()

   myPairs = pairs.length - cpairs.length
+
+  threeOrMore = _.find pairs, (group) ->
+    group.length > 2
+  if !!threeOrMore
+    return 1

   value = 0


Properly test the proxiedObject the whole way up the lookup path.

--- src/view/view.coffee
+++ src/view/view.coffee
@@ -258,21 +258,18 @@
     if index != -1 then keypath.substr(0, index) else keypath

   targetForKeypath: (keypath, forceTarget) ->
-    proxiedObject = @get('proxiedObject')
-    lookupNode = proxiedObject || this
+    lookupNode = this

     while lookupNode
-      if typeof Batman.get(lookupNode, keypath) isnt 'undefined'
-        return lookupNode
+      if target = @_testTargetForKeypath(lookupNode, keypath)
+        return target

       if forceTarget and not nearestNonBackingView and not lookupNode.isBackingView
         nearestNonBackingView = lookupNode

       controller = lookupNode.controller if not controller and lookupNode.isView and lookupNode.controller

-      if proxiedObject and lookupNode == proxiedObject
-        lookupNode = this
-      else if lookupNode.isView and lookupNode.superview
+      if lookupNode.isView and lookupNode.superview
         lookupNode = lookupNode.superview
       else if controller
         lookupNode = controller
@@ -287,6 +284,12 @@

     return nearestNonBackingView

+  _testTargetForKeypath: (object, keypath) ->
+    if proxiedObject = Batman.get(object, 'proxiedObject')
+      return proxiedObject if typeof Batman.get(proxiedObject, keypath) isnt 'undefined'
+
+    return object if typeof Batman.get(object, keypath) isnt 'undefined'
+
   lookupKeypath: (keypath) ->
     base = @baseForKeypath(keypath)
     target = @targetForKeypath(base)


basic loading individual objects with caching

--- snapbill.coffee
+++ snapbill.coffee
@@ -10,12 +10,14 @@
         console.log "EPIC FAIL #{error}"

     request: (type, url, params, callback) ->
+        console.log "Requesting at #{url} with params #{params}"
         params = Snapbill.encode_params params
         if typeof XMLHttpRequest != 'undefined'
             request = new XMLHttpRequest()
             request.onload = =>
                 if request.readyState is 4
                     if 2 is Math.floor request.status / 100
+                        console.log "we got data home boy #{request.responseText}"
                         callback JSON.parse request.responseText
                     else
                         error = "Request to #{url} failed with request code #{request.status}"
@@ -98,6 +100,18 @@

         return cache[typeName][objectData.id]

+    @load: (snapbill, filters, callback) ->
+        console.log snapbill
+        cached = snapbill.object_cache[@type][filters.id] if filters.id?
+        callback cached, null if cached?
+
+        snapbill.request "POST", "/v1/#{@type}/get", filters, (requestData, error) =>
+            if error?
+                callback null, error
+            else
+                callback @create_object snapbill, @, requestData
+
+
     @list: (snapbill, params, callback) ->
         snapbill.request "POST", "/v1/#{@type}/list", params, (requestData, error) =>
             if error?
@@ -127,10 +141,18 @@
 User.prototype.constructor = User
 User.constructor = User

+class Client extends SnapbillObject
+    @type: 'client'
+
+Client.prototype.constructor = Client
+Client.constructor = Client
+
+
 snapbill =
-    User: User
-    Snapbill: Snapbill
-    SnapbillObject: SnapbillObject
+    User           :User
+    Client         :Client
+    Snapbill       :Snapbill
+    SnapbillObject :SnapbillObject

 if exports?
     exports = snapbill


fix typo in playlist extension

fix typo in playlist extension

--- src/javascripts/extensions/playlist.coffee
+++ src/javascripts/extensions/playlist.coffee
@@ -94,7 +94,7 @@
     file.join('.')

   click: (event) =>
-    if @currentEpisod && event.data == @currentEpisode.feedItem
+    if @currentEpisode && event.data == @currentEpisode.feedItem
       @app.player.playPause()
     else
       @playItem(event.data)


Fixed promises.

Fixed promises.

--- src/app/user/user.coffee
+++ src/app/user/user.coffee
@@ -48,39 +48,36 @@
     return getUserTask
   getList: (ids, embed) ->
     that = @
-    embedString = null
-    if embed == null
-      embedString = ""
-    else
-      embedString = embed
     if (angular.isDefined(ids) && ids.length > 0)
-      if (ids.length > 0)
-        listString = "["
-        listString = listString + ids[0]
-        if ids.length > 1
-          angular.forEach(ids, (value, key) ->
-            listString = listString + ', ' + value
-            return
-          )
-        listString = listString + "]"
-        return @$http.get(
-          @basePath + '/user' + '?ids=' + listString,
-          headers: {
-            'Authorization':
-              that.SecurityService.currentUser.authorizationString
-          }
-        ).success (status) ->
-          that.$log.debug("Received user list modal response")
+      listString = "["
+      listString = listString + ids[0]
+      if ids.length > 1
+        angular.forEach(ids, (value, key) ->
+          listString = listString + ', ' + value
           return
+        )
+      listString = listString + "]"
+      return @$http.get(
+        @basePath + '/user' + '?ids=' + listString,
+        headers: {
+          'Authorization':
+            that.SecurityService.currentUser.authorizationString
+        }
+      ).success (status) ->
+        that.$log.debug("Received user list modal response")
+        return
     else
       return{
         success: (callback) ->
           callback({users:[]})
           return
         error: (callback) ->
+          callback()
+          return
+        then: (callback) ->
+          callback()
           return
       }
-    return
   getUserAvatar: (userId) ->
     getUserAvatarTask = @$http.get(
       @basePath + '/user/' + userId + '/profile/image',


Corrected when deferred syntax error

Corrected when deferred syntax error

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -70,7 +70,7 @@

         itemCategoriesDefers.push deferred

-      when itemCategoriesDefers .done (bountyItems) ->
+      $.when itemCategoriesDefers .done (bountyItems) ->
         console.log 'bountyItems = '
         console.log bountyItems


Fix Scala type of parameters examples

Fix Scala type of parameters examples

--- src/parser/scala-parser.coffee
+++ src/parser/scala-parser.coffee
@@ -184,13 +184,13 @@
         {
           key: "tracespace", display: "Followed by space",
           code: """
-                def add(a:Int, b:Int) = a + b
+                def add(a: Int, b: Int) = a + b
                 """
         }
         {
           key: "bothspace", display: "Using space in before/after",
           code: """
-                def add(a:Int, b:Int) = a + b
+                def add(a : Int, b : Int) = a + b
                 """
         }
         {
@@ -216,4 +216,4 @@

     convention.parametertype.commits.push commitUrl if tracespace.test(line) or bothspace.test(line) or nospace.test(line)
     convention.parametertype.commits = _.uniq convention.parametertype.commits
-    convention+    convention


Dont bound by accident. Now make it bounce on purpose, trystan.

--- src/entities.coffee
+++ src/entities.coffee
@@ -305,6 +305,7 @@
         settings.spriteheight = settings.height = 32
         # call the parent constructor
         @_super(me.Entity, 'init', [x, y, settings])
+        @body.collisionType = me.collision.types.WORLD_SHAPE
         # set start/end position based on the initial area size
         x = @pos.x
         @startX = x


Options are explicit

Options are explicit

--- src/eventbus.coffee
+++ src/eventbus.coffee
@@ -1,7 +1,5 @@
 class EventBus
-	constructor : (options) ->
-    @debug = options?.debug
-    @log = options?.log ? console.log
+	constructor : ({@debug = false, @log = console.log}) ->
     @bus = []
     @registrationId = 0
     @id = 0


Set the variable properly.

Set the variable properly.

--- web/volume/slot.coffee
+++ web/volume/slot.coffee
@@ -300,7 +300,8 @@
           $scope.assets[index].choose()
           $timeout ->
             $scope.play()
-          , 400
+            $scope.playing = 1
+          , 300
         # look for something else to play?
         return


add gtm.drag for the slot timeline

add gtm.drag for the slot timeline

--- web/volume/slot.coffee
+++ web/volume/slot.coffee
@@ -503,6 +503,8 @@
         return false if typeof $scope.editing == 'string'
         $scope.editing = true
         $scope.current.updateExcerpt() if $scope.current?.excerpts
+        window.dataLayer.push
+          'event': 'gtm.drag'
       playrange = $scope.asset?.segment.intersect(ruler.selection)
       for t in $scope.tags
         t.update()


Fix check if we already have the image.

Fix check if we already have the image.

--- src/application.coffee
+++ src/application.coffee
@@ -34,7 +34,7 @@
 		)

 exports.start = start = (app) ->
-	docker.getImage(app).inspectAsync()
+	docker.getImage(app.imageId).inspectAsync()
 	.catch (error) ->
 		console.log("Pulling image:", app.imageId)
 		deferred = Promise.defer()


Tweak unresponsive dialog text

Tweak unresponsive dialog text

--- src/atom-window.coffee
+++ src/atom-window.coffee
@@ -37,8 +37,8 @@
       chosen = dialog.showMessageBox @browserWindow,
         type: 'warning'
         buttons: ['Close', 'Keep Waiting']
-        message: 'Editor window is frozen'
-        detail: 'The editor window becomes frozen because of JavaScript dead loop, you can force closing it or just keep waiting.'
+        message: 'Editor is not responsing'
+        detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
       if chosen is 0
         setImmediate => @browserWindow.destroy()


Fix bug: Don't scroll to top on click after scrolling down.

The editor has a tabindex of -1 so it can receive focus. This makes it
behave like a focusable element such as a textarea. But in reality,
when the editor receives focus, it just focuses the hidden input that
it watches for textInput events. So when you click, focus is stolen
away from the hidden input and goes to the editor. But then the editor
sends focus back to the hidden input. Because the input was at the top
of the screen, WebKit would scroll up to bring it on screen when
focused. So now I just bring the hidden input on screen (by positioning
it even with scrollTop) before focusing it. That prevents the WebKit
from scrolling.

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -64,6 +64,7 @@

     handleEvents: ->
       @on 'focus', =>
+        @hiddenInput.css(top: @scrollTop())
         @hiddenInput.focus()
         false


Add Editor.getCursors()

Add Editor.getCursors()

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -350,6 +350,7 @@
   moveCursorUp: -> @compositeCursor.moveUp()
   moveCursorDown: -> @compositeCursor.moveDown()
   moveCursorRight: -> @compositeCursor.moveRight()
+  getCursors: -> @compositeCursor.getCursors()
   moveCursorLeft: -> @compositeCursor.moveLeft()
   setCursorScreenPosition: (position) -> @compositeCursor.setScreenPosition(position)
   getCursorScreenPosition: -> @compositeCursor.getCursor().getScreenPosition()


fix(search): Missed a .default

fix(search): Missed a .default

--- internal_packages/thread-search/spec/search-bar-spec.cjsx
+++ internal_packages/thread-search/spec/search-bar-spec.cjsx
@@ -2,7 +2,7 @@
 ReactDOM = require 'react-dom'
 ReactTestUtils = require('react-addons-test-utils')

-SearchBar = require '../lib/search-bar'
+SearchBar = require('../lib/search-bar').default
 SearchActions = require('../lib/search-actions').default

 describe 'SearchBar', ->


Update the method to read a binary file

Update the method to read a binary file

--- scripts/gh-book/app.coffee
+++ scripts/gh-book/app.coffee
@@ -31,7 +31,6 @@
     mediaTypes.add XhtmlFile
     mediaTypes.add OpfFile
     mediaTypes.add BinaryFile, {mediaType:'image/png'}
-    mediaTypes.add BinaryFile, {mediaType:'image/jpg'}
     mediaTypes.add BinaryFile, {mediaType:'image/jpeg'}

@@ -74,7 +73,7 @@
       session.getBranch().write path, text, commitText

     readFile =       (path) -> session.getBranch().read       path
-    readBinaryFile = (path) -> session.getBranch().readBinary path
+    readBinaryFile = (path) -> session.getBranch().read       path, true # isBinary == true
     readDir =        (path) -> session.getBranch().contents   path


Syntax testing.

Syntax testing.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -130,7 +130,7 @@
             score = ["#{user} has #{attrs.points}" for user, attrs of leaders]

             # Slice the top X results from array once sorted.
-            topscore = score.slice(0, pointcount)
+            topscore = score.slice(0, [pointcount])

             # Numeric descending sort: points.sort(function(a, b){return b-a});
             # score.sort(a, b) ->


Remove an unnecessary debug.

Remove an unnecessary debug.

--- src/bootstrap-elm.coffee
+++ src/bootstrap-elm.coffee
@@ -173,7 +173,6 @@
   # TODO just have the signal send this along
   query = document.getElementById("notes-search-text").value

-  console.debug "TODO: search notes database for", query
   notes = [
     {id: "1", title: "Awesome Note", snapshotId: "1234", creationTime: 0, lastModifiedTime: 0}
     {id: "2", title: "Great Scott!", snapshotId: "1234", creationTime: 0, lastModifiedTime: 0}


standard stepsize to 1

standard stepsize to 1

Former-commit-id: 9378df8164e6044b11ffd4bbea2fcf1e193318a7 [formerly b4c3a2f31f5d0181e019ca7ca3f2730a134119f9]
Former-commit-id: 8008944517e0a8b331e957e509b435a5aa335813

--- src/g/zoomer.coffee
+++ src/g/zoomer.coffee
@@ -10,4 +10,4 @@
     textVisible: true
     labelLength: 20
     labelFontsize: "10px"
-    stepSize: 2
+    stepSize: 1


appcache manifest url in hosting map

appcache manifest url in hosting map

--- src/builder/index.coffee
+++ src/builder/index.coffee
@@ -507,6 +507,15 @@
                     path: relative
                     url: url

+        if @appcache?
+            filepath = path.resolve(@root, @appcache)
+            relative = @_relativate(filepath)
+            url = @_host_path(relative)
+            if url?
+                map.appcache =
+                    path: relative
+                    url: url
+
         return map

     _create_index: ->


Convert Definition Schema to JSON and inject in body done

--- SwaggerImporter.coffee
+++ SwaggerImporter.coffee
@@ -14,7 +14,7 @@
         headers = {}
         queries = {}
         formData = {}
-        body = {}
+        body

         # Extract contentType from Consumes and add the first one to Headers
         if swaggerRequestValue.consumes
@@ -39,7 +39,7 @@

           # Add Body
           if swaggerRequestParamValue.in == 'body' #Only string
-            body[swaggerRequestParamValue.name] = swaggerRequestParamValue.name
+            body = @json_from_definition_schema swaggerCollection, swaggerRequestParamValue.schema

         swaggerRequestUrl = @createSwaggerRequestUrl swaggerCollection, swaggerRequestPath, queries
         swaggerRequestMethod = swaggerRequestMethod.toUpperCase()
@@ -53,29 +53,12 @@
         for key, value of headers
           pawRequest.setHeader key, value

-        # # Set raw body
-        # if postmanRequest["dataMode"] == "raw"
-        #     contentType = pawRequest.getHeaderByName "Content-Type"
-        #     rawRequestBody = postmanRequest["rawModeData"]
-        #     foundBody = false;
-        #
-        #     # If the Content-Type contains "json" make it a JSON body
-        #     if contentType and contentType.indexOf("json") >= 0 and rawRequestBody and rawRequestBody.length > 0
-        #         # try to parse JSON body input
-        #         try
-        #             jsonObject = JSON.parse rawRequestBody
-        #         catch error
-        #             console.log "Cannot parse Request JSON: #{ postmanRequest["name"] } (ID: #{ postmanRequestId })"
-        #         # set the JSON body
-        #         if jsonObject
-        #             pawRequest.jsonBody = jsonObject
-        #             foundBody = true
-        #
-        #     if not foundBody
-        #         pawRequest.body = rawRequestBody
-        #
+        # Set raw body
+        pawRequest.body = body if body
+
+
         # Set Form URL-Encoded body
-        if Object.keys(formData).length > 0
+        if Object.keys(formData).length > 0
             # Set Form URL-Encoded body
             if headers['Content-Type'] == "application/x-www-form-urlencoded"
               pawRequest.urlEncodedBody = formData
@@ -85,6 +68,33 @@

         return pawRequest

+    @json_from_definition_schema = (swaggerCollection, property, indent = 0) ->
+
+        if property.type == 'string'
+            s = "\"string\""
+        else if property.type == 'integer'
+            s = "0"
+        else if property.type == 'boolean'
+            s = "true"
+        else if typeof(property) == 'object'
+            indent_str = Array(indent + 1).join('    ')
+            indent_str_children = Array(indent + 2).join('    ')
+
+            if property.items
+              property = property.items
+              s = "[\n" +
+                  "#{indent_str_children}#{@json_from_definition_schema(swaggerCollection, property, indent+1)}" +
+                  "\n#{indent_str}]"
+            else
+              property = swaggerCollection.definitions[property["$ref"].split('/').pop()] if property["$ref"]
+              property = property.properties if property.properties # Skip properties
+
+              s = "{\n" +
+                  ("#{indent_str_children}\"#{key}\" : #{@json_from_definition_schema(swaggerCollection, value, indent+1)}" for key, value of property).join(',\n') +
+                  "\n#{indent_str}}"
+
+        return s
+
     @createSwaggerRequestUrl = (swaggerCollection, swaggerRequestPath, queries) ->

         # Build swaggerRequestQueries
@@ -120,7 +130,7 @@
         return pawGroup

     @importString = (context, string) ->
-
+
         try
           # Try JSON parse
           swaggerCollection = JSON.parse string
@@ -133,7 +143,7 @@

         if not valid
           throw new Error "Invalid Swagger file (invalid schema or schema version < 2.0)"
-
+
         if swaggerCollection

           # Create a PawGroup


Made old bounding boxes red again

Made old bounding boxes red again

--- js-library/app/js/jail_iframe/classes/bounding_box.coffee
+++ js-library/app/js/jail_iframe/classes/bounding_box.coffee
@@ -1,4 +1,4 @@
-FactlinkJailRoot.drawBoundingBox = (box, color='green') ->
+FactlinkJailRoot.drawBoundingBox = (box, color='red') ->
   coreContainerOffset = FactlinkJailRoot.$factlinkCoreContainer.offset()

   $boundingBox = $ """


Remove console log

Remove console log

--- js-library/app/js/jail_iframe/classes/icon_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/icon_buttons.coffee
@@ -6,7 +6,6 @@
 FactlinkJailRoot.on 'reposition_icons', ->
   if there_are_icons
     Tether.position()
-    console.log 'repositioned!'

 class IconButton
   constructor: (options) ->


fix github tests

fix github tests

--- spec/github.spec.coffee
+++ spec/github.spec.coffee
@@ -4,40 +4,40 @@
   describe 'github', ->
     it 'converts numbers to gist api urls', ->
       gist_url = github.to_gist_url 42
-      expect(gist_url).toBe 'https://api.github.com/gists/42'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/42'

     it 'converts simple string to gist api urls', ->
       gist_url = github.to_gist_url '42'
-      expect(gist_url).toBe 'https://api.github.com/gists/42'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/42'

     it 'converts gist urls to gist api urls', ->
       # https
       gist_url = github.to_gist_url 'https://gist.github.com/422'
-      expect(gist_url).toBe 'https://api.github.com/gists/422'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/422'

       # http
       gist_url = github.to_gist_url 'http://gist.github.com/422'
-      expect(gist_url).toBe 'https://api.github.com/gists/422'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/422'

     it 'converts gist repository urls to gist api urls', ->
       gist_url = github.to_gist_url 'https://gist.github.com/5794205.git'
-      expect(gist_url).toBe 'https://api.github.com/gists/5794205'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/5794205'

     it 'handles gist api urls', ->
       gist_url = github.to_gist_url 'http://api.github.com/gists/5794205'
-      expect(gist_url).toBe 'https://api.github.com/gists/5794205'
+      expect(gist_url.toString()).toBe 'https://api.github.com/gists/5794205'

     it 'handles repo contents api urls', ->
       url = 'https://api.github.com/repos/also/lead.js/contents/examples/colors.coffee?ref=1f2c7e04666cd2fc5460805ca884faf25c1074b2'
       repo_url = github.to_repo_url url
-      expect(repo_url).toBe url
+      expect(repo_url.toString()).toBe url

     it 'converts html urls to api urls', ->
       url = 'https://github.com/also/lead.js/blob/master/examples/browser.coffee'
       repo_url = github.to_repo_url url
-      expect(repo_url).toBe 'https://api.github.com/repos/also/lead.js/contents/examples/browser.coffee?ref=master'
+      expect(repo_url.toString()).toBe 'https://api.github.com/repos/also/lead.js/contents/examples/browser.coffee?ref=master'

     it 'converts paths to api urls', ->
       path = 'also/lead.js/examples/browser.coffee'
       repo_url = github.to_repo_url path
-      expect(repo_url).toBe 'https://api.github.com/repos/also/lead.js/contents/examples/browser.coffee'
+      expect(repo_url.toString()).toBe 'https://api.github.com/repos/also/lead.js/contents/examples/browser.coffee'


:new: Add specs for execNode

:new: Add specs for execNode

--- spec/helper-spec.coffee
+++ spec/helper-spec.coffee
@@ -5,11 +5,9 @@
 describe 'linter helpers', ->
   describe '::exec', ->
     it 'cries when no argument is passed', ->
-      gotError = false
-      try
+      expect ->
         helpers.exec()
-      catch erro then gotError = true
-      expect(gotError).toBe(true)
+      .toThrow()
     it 'works', ->
       waitsForPromise ->
         helpers.exec('cat', [testFile]).then (text) ->
@@ -22,13 +20,28 @@
       waitsForPromise ->
         helpers.exec('cat', [], stream: 'stdout', stdin: testContents).then (text) ->
           expect(text).toBe(testContents)
+  describe '::execNode', ->
+    it 'cries when no argument is passed', ->
+      expect ->
+        helpers.execNode()
+      .toThrow()
+    it 'works', ->
+      waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/something.js').then (data) ->
+          expect(data).toBe('STDOUT')
+    it 'lets you choose streams', ->
+      waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/something.js', [], {stream: 'stderr'}).then (data) ->
+          expect(data).toBe('STDERR')
+    it 'accepts stdin', ->
+      waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/something.js', ['input'], {stream: 'stdout', stdin: 'Wow'}).then (data) ->
+          expect(data).toBe('STDOUTWow')
   describe '::execFilePath', ->
     it 'cries when no argument is passed', ->
-      gotError = false
-      try
+      expect ->
         helpers.execFilePath()
-      catch erro then gotError = true
-      expect(gotError).toBe(true)
+      .toThrow()
     it 'cries when no filepath is passed', ->
       gotError = false
       try
@@ -41,11 +54,9 @@
           expect(text).toBe(testContents)
   describe '::parse', ->
     it 'cries when no argument is passed', ->
-      gotError = false
-      try
+      expect ->
         helpers.parse()
-      catch erro then gotError = true
-      expect(gotError).toBe(true)
+      .toThrow()
     it "cries when data isn't string", ->
       expect ->
         helpers.parse([], '')
@@ -63,10 +74,8 @@
       expect(results).toEqual(output)
   describe '::findFile', ->
     it 'cries wen no argument is passed', ->
-      gotError = false
-      try
-        helpers.parse()
-      catch erro then gotError = true
-      expect(gotError).toBe(true)
+      expect ->
+        helpers.findFile()
+      .toThrow()
     it 'works', ->
       expect(helpers.findFile(__dirname, 'package.json')).toBe(fs.realpathSync(__dirname + '/../package.json'))


fix the specs creating a silly number of ruby instances and causeing extreme processor usage and test the toggle command

--- spec/server-spec.coffee
+++ spec/server-spec.coffee
@@ -8,10 +8,11 @@
     atom.project.setPaths([path.join(__dirname, 'sample')])

     JekyllServer = require '../lib/server'
-    emitter = new Emitter

-    jekyllServer = new JekyllServer
-    jekyllServer.activate(emitter)
+    unless jekyllServer
+      emitter = new Emitter
+      jekyllServer = new JekyllServer
+      jekyllServer.activate(emitter)

     waitsForPromise ->
       atom.workspace.open('index.html')
@@ -37,20 +38,59 @@
         expect(spy.mostRecentCall.args[0]).toBe 'Off'

     it 'should start the server', ->
-      expect(jekyllServer.rawStatus()).toBe 'Off'
-      emitter.emit 'jekyll:start-server'
-      expect(jekyllServer.rawStatus()).toBe 'On'
-      emitter.emit 'jekyll:stop-server'
-      expect(jekyllServer.rawStatus()).toBe 'Off'
+      waitsFor ->
+        jekyllServer.rawStatus() == 'Off'
+
+      runs ->
+        emitter.emit 'jekyll:start-server'
+        waitsFor ->
+          jekyllServer.rawStatus() == 'On'
+        runs ->
+          expect(jekyllServer.rawStatus()).toBe 'On'
+          emitter.emit 'jekyll:stop-server'

   describe 'directly', ->
     it 'should start the server', ->
-      expect(jekyllServer.rawStatus()).toBe 'Off'
-      jekyllServer.start()
-      expect(jekyllServer.rawStatus()).toBe 'On'
-      jekyllServer.stop()
-      expect(jekyllServer.rawStatus()).toBe 'Off'
+      waitsFor ->
+        jekyllServer.rawStatus() == 'Off'
+
+      runs ->
+        jekyllServer.start()
+        expect(jekyllServer.rawStatus()).toBe 'On'
+        jekyllServer.stop()
+        expect(jekyllServer.rawStatus()).toBe 'Off'

     it 'should have loaded the PWD', ->
       expect(jekyllServer.pwd).toBe path.join(__dirname, 'sample')
+
+  describe 'the toggle command', ->
+    it 'should start the server if not running', ->
+      waitsFor ->
+        jekyllServer.rawStatus() == 'Off'
+
+      runs ->
+        jekyllServer.toggle()
+
+        waitsFor ->
+          jekyllServer.rawStatus() == 'On'
+
+        runs ->
+          expect(jekyllServer.rawStatus()).toBe 'On'
+          jekyllServer.stop()
+          expect(jekyllServer.rawStatus()).toBe 'Off'
+
+    it 'should stop the server if it is running', ->
+      waitsFor ->
+        jekyllServer.rawStatus() == 'Off'
+
+      runs ->
+        jekyllServer.start()
+
+        waitsFor ->
+          jekyllServer.rawStatus() == 'On'
+
+        runs ->
+          expect(jekyllServer.rawStatus()).toBe 'On'
+          jekyllServer.toggle()
+          expect(jekyllServer.rawStatus()).toBe 'Off'


whoops empty post if

whoops empty post if

--- providers/couch.coffee
+++ providers/couch.coffee
@@ -64,6 +64,8 @@
 						doc._rev = b2.rev
 						request @url + doc._id, {method : "PUT", json : doc}, (e3,r3,b3)=>
 							unless b3.error == "conflict"
+								true
 								#console.log "#{key}  in on try two"
 			else
-				#console.log "#{key} put in on try one"+				#console.log "#{key} put in on try one"
+				true


add theodo UK survey

add theodo UK survey

--- migrations/2016-21-01-survey.coffee
+++ migrations/2016-21-01-survey.coffee
@@ -164,6 +164,88 @@
     ,
       value: 'no'
       label: 'Non, ce n’est pas nécessaire pour l’instant'
+    ]
+  ,
+  ]
+,
+  company: 'Theodo UK'
+  questions: [
+    label: 'How do you feel about the speed of the team?'
+    type: 'radio'
+    items: [
+      value: 5
+      label: '5 - Excellent'
+    ,
+      value: 4
+      label: '4 - Very good'
+    ,
+      value: 3
+      label: '3 - Good'
+    ,
+      value: 2
+      label: '2 - Average'
+    ,
+      value: 1
+      label: '1 - Insufficient'
+    ,
+      value: 0
+      label: '0 - Very insufficient'
+    ,
+    ]
+  ,
+    label: 'How do you feel about the quality of Theodo’s support?'
+    type: 'radio'
+    items: [
+      value: 5
+      label: '5 - Excellent'
+    ,
+      value: 4
+      label: '4 - Very good'
+    ,
+      value: 3
+      label: '3 - Good'
+    ,
+      value: 2
+      label: '2 - Average'
+    ,
+      value: 1
+      label: '1 - Insufficient'
+    ,
+      value: 0
+      label: '0 - Very insufficient'
+    ,
+    ]
+  ,
+    label: 'What is the main priority the team should concentrate on to improve the score?'
+    type: 'textarea'
+  ,
+    label: 'Should you have a magic wand, what is « the » thing you would change at Theodo?'
+    type: 'textarea'
+  ,
+    label: 'Would you recommend Theodo?'
+    type: 'radio'
+    items: [
+      value: 'yes-of-course'
+      label: 'Oui bien sûr'
+    ,
+      value: 'yes'
+      label: 'Oui'
+    ,
+      value: 'not-really'
+      label: 'Pas vraiment'
+    ,
+      value: 'not-at-all'
+      label: 'Pas du tout'
+    ]
+  ,
+    label: 'Would you like to have an appointment with your sales person this week?'
+    type: 'radio'
+    items: [
+      value: 'yes'
+      label: 'Yes'
+    ,
+      value: 'no'
+      label: 'No, it is not necessary for now'
     ]
   ,
   ]


Update vansteki.gy.coffee

Update vansteki.gy.coffee

--- scripts/vansteki.gy.coffee
+++ scripts/vansteki.gy.coffee
@@ -6,4 +6,5 @@
   		youtube.get msg.match[1], ((data, title) ->
   			msg.send ">" + title
   			msg.send "<" + data[0].title + "|" + data[0].link + ">"
+  			msg.send "<https://github.com/|yoooo> fix some broken"
   		).bind(msg)


Handle paused state

Handle paused state

--- scripts/views/tasks.coffee
+++ scripts/views/tasks.coffee
@@ -422,17 +422,20 @@
                     taskIsSolved = no
                     taskProgress = null
                     identity = identityProvider.getIdentity()
+                    contest = contestProvider.getContest()
                     if identity.role is 'team'
                         taskProgress = _.findWhere contestProvider.getTeamTaskProgressEntries(), teamId: identity.id, taskId: taskId
                         if taskProgress?
                             taskIsSolved = yes

-                    if taskPreview.isOpened() and not taskIsSolved
+                    if taskPreview.isOpened() and not taskIsSolved and contest.isStarted()
                         $submitTaskAnswerGroup.show()
                         $submitTaskSubmitButton.show()
                     else
                         $submitTaskAnswerGroup.hide()
                         $submitTaskSubmitButton.hide()
+                        if contest.isPaused() and not taskIsSolved
+                            $submitTaskSubmitError.text 'Contest has been paused.'
                         if taskPreview.isClosed()
                             $submitTaskSubmitError.text 'Task has been closed by the event organizers.'
                         if taskIsSolved


Add dual pane support

Add dual pane support

--- src/htmldiff.coffee
+++ src/htmldiff.coffee
@@ -272,7 +272,23 @@

   return rendering

-diff = (before, after)->
+render_operations_dual_pane = (before_tokens, after_tokens, operations)->
+  before_render = ''
+  after_render = ''
+  for op in operations
+    next_block = op_map[op.action] op, before_tokens, after_tokens
+    switch op.action
+      when "equal"
+        before_render += next_block
+        after_render += next_block
+      when "insert" then after_render += next_block
+      when "delete" then before_render += next_block
+      when "replace"
+        before_render += next_block.substring 0, next_block.indexOf '<ins>'
+        after_render += next_block.substring next_block.indexOf '<ins>'
+  { before: before_render, after: after_render }
+
+diff = (before, after, dual_pane)->
   return before if before is after

   before = html_to_tokens before
@@ -280,8 +296,10 @@

   ops = calculate_operations before, after

-  render_operations before, after, ops
-
+  if dual_pane
+    render_operations_dual_pane before, after, ops
+  else
+    render_operations before, after, ops

 diff.html_to_tokens = html_to_tokens
 diff.find_matching_blocks = find_matching_blocks
@@ -289,6 +307,7 @@
 find_matching_blocks.create_index = create_index
 diff.calculate_operations = calculate_operations
 diff.render_operations = render_operations
+diff.render_operations_dual_pane = render_operations_dual_pane;

 if typeof define is 'function'
   define [], ()-> diff


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -152,7 +152,7 @@
   </pre></code>

   @name Color
-  @param {Array|Number|String|Color} args Either an Array or r, g, b values,
+  @param {Array|Number|String|Color} args... Either an Array or r, g, b values,
   a sequence of numbers defining r, g, b values, a hex or hsl string, or another Color object
   @constructor
   ###


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -11,7 +11,14 @@
     s = parseInt(bits).toString(16)

     s = '0' + s if s.length == 1
-
+
+  parseHex = (hexString) ->
+    if hexString[0] = '#'
+      case hexString.length
+        when 3 then #something
+        when 4 then #something
+        when 6 then #something
+
   parseRGB = (colorString) ->
     bits = rgbParser.exec(colorString)
     return [
@@ -20,7 +27,7 @@
       bits[3]
       if bits[4]? then bits[4] else 1
     ]
-
+
   window.Color = (color) ->
     color ||= "rgba(0, 0, 0, 0)"


Fix broken connections

Fix broken connections

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -255,6 +255,8 @@

             @driver_handler = new DataExplorerView.DriverHandler
                 container: @
+                on_success: @success_on_connect
+                on_fail: @error_on_connect

             @render()

@@ -1577,8 +1579,7 @@
         # Called if the driver could connect
         success_on_connect: =>
             @results_view.cursor_timed_out()
-            # If the we were disconnected because of an error, we say that we did reconnect
-            if window.driver_connected_previous_state is false
+            if @reconnecting is true
                 @.$('#user-alert-space').hide()
                 @.$('#user-alert-space').html @alert_reconnection_success_template()
                 @.$('#user-alert-space').slideDown 'fast'
@@ -1590,10 +1591,9 @@
             @results_view.cursor_timed_out()
             # We fail to connect, so we display a message except if we were already disconnected and we are not trying to manually reconnect
             # So if the user fails to reconnect after a failure, the alert will still flash
-            if window.driver_connected_previous_state isnt false or @reconnecting is true
-                @.$('#user-alert-space').hide()
-                @.$('#user-alert-space').html @alert_connection_fail_template({})
-                @.$('#user-alert-space').slideDown 'fast'
+            @.$('#user-alert-space').hide()
+            @.$('#user-alert-space').html @alert_connection_fail_template({})
+            @.$('#user-alert-space').slideDown 'fast'
             @reconnecting = false
             @driver_connected = 'error'

@@ -1601,11 +1601,7 @@
         reconnect: (event) =>
             @reconnecting = true
             event.preventDefault()
-            clearTimeout window.timeout_driver_connect
-            window.driver_connect()
-
-
-
+            @driver_handler.connect()

         handle_gutter_click: (editor, line) =>
             start =
@@ -1639,9 +1635,13 @@
             @.$('.wrapper_scrollbar').css 'width', ($(window).width()-92)+'px'

         destroy: =>
+            @results_view.destroy()
+            @history_view.destroy()
+            @driver_handler.destroy()
+
             @display_normal()
             $(window).off 'resize', @display_full
-            @results_view.destroy()
+
             clearTimeout @timeout_driver_connect
             # We do not destroy the cursor, because the user might come back and use it.

@@ -2443,6 +2443,8 @@
         connect: =>
             # Whether we are going to reconnect or not, the cursor might have timed out.
             @container.saved_data.cursor_timed_out = true
+            if @timeout?
+                clearTimeout @timeout

             if @connection?
                 if @driver_status is 'connected'


Moakes operations more atomic

Moakes operations more atomic

--- src/client/widget.coffee
+++ src/client/widget.coffee
@@ -113,13 +113,21 @@

         dfd = @sandbox.data.deferred()
         datasourceErrors = {}
+        @data = {}
         try
           keys = _.keys(@datasources)
           promises  = _.map keys, (k)=>
-            promiseDfd = @sandbox.data.deferred()
             ds = @datasources[k]
             ds.parse(_.extend({}, @, @options || {}))
-            ds.fetch().then (res)->
+            promiseDfd = @sandbox.data.deferred()
+            ds.fetch().then (res)=>
+              @data[k] = res
+              if _.isFunction res?.toJSON
+                ret[k] = res.toJSON()
+              else if _.isArray(res) && res[1] == 'success' && res[2].status == 200
+                ret[k] = res[0]
+              else
+                ret[k] = res
               promiseDfd.resolve(res)
             , (err)=>
               handler = @["on#{_.string.capitalize(_.string.camelize(k))}Error"]
@@ -127,18 +135,7 @@
               datasourceErrors[k] = err
               promiseDfd.resolve(handler err)
             promiseDfd
-          @data = {}
           widgetDeferred = @sandbox.data.when.apply(undefined, promises)
-          widgetDeferred.done ()=>
-            args = slice.call arguments
-            _.map keys, (k,i)=>
-              @data[k] = args[i]
-              if _.isFunction args[i]?.toJSON
-                ret[k] = args[i].toJSON()
-              else if _.isArray(args[i]) && args[i][1] == 'success' && args[i][2].status == 200
-                ret[k] = args[i][0]
-              else
-                ret[k] = args[i]
           templateDeferred = @sandbox.template.load(@templates, @ref)
           templateDeferred.done (tpls)=>
             @_templates     = tpls
@@ -147,7 +144,7 @@
             console.error("Error in Building Render Context", err.message, err)
             @renderError.call(@, err.message, err)
             dfd.reject(err)
-          readyDfd.done ()=>
+          readyDfd.done ()->
             dfd.resolve(ret)

         catch e


Be more verbose when something's not right in candyland (I.E. we have errors while rendering a widget)

--- src/client/widget.coffee
+++ src/client/widget.coffee
@@ -104,7 +104,9 @@
         templateDeferred = @sandbox.template.load(@templates, @ref)
         @data = {}
         readyDfd = $.when(widgetDeferred, templateDeferred)
-        readyDfd.fail _.bind(@renderError, @)
+        readyDfd.fail (err)=>
+          console.error("Error in Building Render Context", err.message, err)
+          @renderError.call(@, err.message, err)
         readyDfd.done (data, tpls)=>
           args = data
           _.map keys, (k,i)=>
@@ -151,15 +153,26 @@
     # Start nested widgets...
     render: (tpl, data)=>
       ctx = @buildContext.call(@)
-      ctx.fail (err)-> console.error("Error building context: ", err.message, err)
+      ctx.fail (err)->
+        console.error("Error fetching Datasources ", err.message, err)
+
       ctx.then (ctx)=>
-        beforeCtx = @beforeRender.call(@, ctx)
-        $.when(beforeCtx).done (dataAfterBefore)=>
-          data = _.extend(dataAfterBefore || ctx, data)
-          @doRender(tpl, data)
-          _.defer(@afterRender.bind(@, data))
-          _.defer((-> @sandbox.start(@$el)).bind(@))
-          @isInitialized = true;
+        try
+          beforeCtx = @beforeRender.call(@, ctx)
+          beforeRendering = $.when(beforeCtx)
+          beforeRendering.done (dataAfterBefore)=>
+            data = _.extend(dataAfterBefore || ctx, data)
+            @doRender(tpl, data)
+            _.defer(@afterRender.bind(@, data))
+            _.defer((-> @sandbox.start(@$el)).bind(@))
+            @isInitialized = true;
+
+          beforeRendering.fail (err)=>
+            console.error("Error in beforeRender", err.message, err)
+            @renderError.call(@, err)
+        catch err
+          console.error("Error in beforeRender", err.message, err)
+          @renderError.call(@, err)

     trackingData: {}


fix(export): Fix queryString handling when doing full product export

--- src/coffee/export.coffee
+++ src/coffee/export.coffee
@@ -15,6 +15,7 @@
 Header = require './header'
 Taxes = require './taxes'
 ExportMapping = require './exportmapping'
+queryStringParser = require 'querystring'
 GLOBALS = require './globals'

 # will clean temporary files even when an uncaught exception occurs
@@ -112,14 +113,20 @@
   # return the correct product service in case query string is used or not
   _getProductService: (staged = true, customWherePredicate = false) ->
     productsService = @client.productProjections
-    if customWherePredicate
-      productsService.where(customWherePredicate)
+    perPage = 100

     if @queryOptions.queryString
-      productsService.byQueryString(@queryOptions.queryString, @queryOptions.isQueryEncoded)
+      query = @queryOptions.queryString
+      if @queryOptions.isQueryEncoded
+        query = decodeURIComponent(query)
+
+      if customWherePredicate
+        query = @_appendPredicateToQueryString(query, customWherePredicate)
+      productsService.perPage(perPage).byQueryString(query, false)
       productsService
     else
-      productsService.all().perPage(500).staged(staged)
+      productsService.where(customWherePredicate || '')
+      productsService.all().perPage(perPage).staged(staged)

   _fetchResources: =>
     data = [
@@ -187,6 +194,12 @@
         console.log "Folder was archived and saved to %s", output
         tempDir.removeCallback()
         Promise.resolve "Export done."
+
+  _appendPredicateToQueryString: (queryString, predicate) =>
+    query = queryStringParser.parse(queryString)
+    query.where =
+      (if query.where then query.where + " AND #{predicate}" else predicate)
+    decodeURIComponent(queryStringParser.stringify(query))

   _processChunk: (products, productTypes, createFileWhenEmpty, header, exportMapper, outputFile) =>
     console.warn "Fetched #{products.body.count} product(s)."


added check make back in and list of make grunt tasks

--- server-ce/Gruntfile.coffee
+++ server-ce/Gruntfile.coffee
@@ -73,7 +73,7 @@
 						]
 						"Install tasks": ("install:#{service.name}" for service in SERVICES).concat(["install:all", "install"])
 						"Update tasks": ("update:#{service.name}" for service in SERVICES).concat(["update:all", "update"])
-						"Checks": ["check", "check:redis", "check:latexmk", "check:s3", "check:make"]
+						"Checks": ["check", "check:redis", "check:latexmk", "check:s3", "check:make", "check:mongo"]

 	for service in SERVICES
 		do (service) ->
@@ -84,7 +84,7 @@

 	grunt.registerTask 'install:all', "Download and set up all ShareLaTeX services",
-		["check:make"].concat(
+		[].concat(
 			("install:#{service.name}" for service in SERVICES)
 		)

@@ -104,12 +104,13 @@
 	grunt.registerTask "check:redis", "Check that redis is installed and running", () ->
 		Helpers.checkRedisConnect @async()

-	grunt.registerTask "check:mongo", "Check that make is installed", () ->
+	grunt.registerTask "check:mongo", "Check that mongo is installed", () ->
 		Helpers.checkMongoConnect @async()

-	grunt.registerTask "check", "Check that you have the required dependencies installed", ["check:redis", "check:latexmk", "check:s3", "check:fs", "check:aspell"]
-
-
+	grunt.registerTask "check", "Check that you have the required dependencies installed", ["check:redis", "check:mongo", "check:make"]
+
+	grunt.registerTask "check:make", "Check that make is installed", () ->
+		Helpers.checkMake @async()

 	grunt.registerTask 'migrate', "compile migrations and run them", ['coffee:migrate', 'shell:migrate']

@@ -171,7 +172,25 @@
 			proc.on "close", () ->
 				callback()

-
+		checkMake: (callback = (error) ->) ->
+			grunt.log.write "Checking make is installed... "
+			exec "make --version", (error, stdout, stderr) ->
+				if error? and error.message.match("not found")
+					grunt.log.error "FAIL."
+					grunt.log.errorlns """
+					Either make is not installed or is not in your path.
+
+					On Ubuntu you can install make with:
+
+					    sudo apt-get install build-essential
+
+					"""
+					return callback(error)
+				else if error?
+					return callback(error)
+				else
+					grunt.log.write "OK."
+					return callback()
 		checkMongoConnect: (callback = (error) ->) ->
 			grunt.log.write "Checking can connect to mongo"
 			mongojs = require("mongojs")


fixed m2m declaration

fixed m2m declaration

--- server/api/orm.coffee
+++ server/api/orm.coffee
@@ -41,6 +41,7 @@

 mixins = ->
   _.each tables, (opts, name) ->
+    debug "mixing model #{name}"
     if opts.paranoid
       (opts.routes ?= []).push 'get  /:id/restore one:show': (req, res) -> res.render "#{res.locals.route}/delete", item: req.item
       opts.routes.push         'post /:id/restore one:show': (req, res) -> req.item.restore().then (item) -> res.redirect "/#{res.locals.route}/#{item.id}"
@@ -72,11 +73,13 @@

 define = ->
   _.each tables, (options, tableName) ->
+    debug "defining model #{tableName}"
     attributes = configureAttrs(options)
     orm.define tableName, attributes, options

 hooks = ->
   _.each orm.models, (model, name) ->
+    debug "hooking model #{name}"
     # defined hooks
     if model.options.hooks_def?.length
       _.each model.options.hooks_def, (hook) ->
@@ -119,6 +122,7 @@
 relate = ->
   _.each orm.models, (model, name) ->
     return if !(relations = model.options.related)
+    debug "relating model #{name}"
     # console.log 'model', name, relations, model.options.include
     _.each relations, (def) -> _.each def, (relation, table) ->
       return console.log "error relating ro table #{table}" if !(foreign = orm.models[table])
@@ -150,8 +154,10 @@
           return console.log "can't pluralize destination table #{foreign.tableName} alias #{relation.as}" if relation.as == inflection.singularize relation.as
           return console.log "can't pluralize source table #{model.tableName} alias #{relation.of}" if relation.of == inflection.singularize relation.of
           relation.through ?= "#{model.tableName}2#{foreign.tableName}"
-          foreign.belongsToMany model, through: relation.through, as: relation.of, foreignKey: "#{relation.as}_id"
-          model.belongsToMany foreign, through: relation.through, as: relation.as, foreignKey: "#{relation.of}_id"
+          through = orm.define relation.through if !(through = orm.models[relation.through]) && _.isString relation.through
+          foreign.belongsToMany model, through: through, as: relation.of, foreignKey: "#{relation.as}_id"
+          model.belongsToMany foreign, through: through, as: relation.as, foreignKey: "#{relation.of}_id"
+          debug "made relation #{relation.through} with: ", _.omit orm.models[relation.through].options, 'sequelize'
         else console.log "undefined relation type #{relation.type}"

 sync = ->


Fix server crash

Fix server crash

--- server/models/album.coffee
+++ server/models/album.coffee
@@ -34,7 +34,7 @@
         log.debug "exist ? #{exist}"

         if exist
-            log.debug "#{photo.title} already imported"
+            log.debug "#{album.title} already imported"
             callback null, exist
         else
             Album.create album, callback


Fix streams for subscriptions

Fix streams for subscriptions

--- server/publications/subscription.coffee
+++ server/publications/subscription.coffee
@@ -31,6 +31,9 @@

 RocketChat.models.Subscriptions.on 'change', (type, args...) ->
+	if type is 'update' and args[0]?.alert?.$ne?
+		delete args[0].alert
+
 	records = RocketChat.models.Subscriptions.getChangedRecords type, args[0], fields

 	for record in records


Chart actual bug counts

Chart actual bug counts

--- src/client/game.coffee
+++ src/client/game.coffee
@@ -1,16 +1,5 @@
-make_chart = (svg_id) ->
-  cities = [
-    [
-      {iteration: 1, count: 10, name: 'Test1'},
-      {iteration: 2, count: 20, name: 'Test1'},
-      {iteration: 3, count: 30, name: 'Test1'}
-    ],
-    [
-      {iteration: 1, count: 30, name: 'Test2'},
-      {iteration: 2, count: 20, name: 'Test2'},
-      {iteration: 3, count: 10, name: 'Test2'}
-    ]
-  ]
+make_chart = (svg_id, bug_stats) ->
+  $(svg_id).empty()

   xScale     = new Plottable.Scale.Linear()
   yScale     = new Plottable.Scale.Linear()
@@ -18,15 +7,15 @@

   xAxis  = new Plottable.Axis.Numeric(xScale, "bottom")
   yAxis  = new Plottable.Axis.Numeric(yScale, "left")
-  yLabel = new Plottable.Component.Label("Temperature (ºF)", "left")
+  yLabel = new Plottable.Component.Label("Num of Jitterbugs", "left")
   legend = new Plottable.Component.Legend(colorScale)

-  plots = cities.map (city) ->
+  plots = bug_stats.map (stat) ->
     return new Plottable.Plot.Line(xScale, yScale)
-                      .addDataset(city)
+                      .addDataset(stat)
                       .project("x", "iteration", xScale)
                       .project("y", "count", yScale)
-                      .project("stroke", colorScale.scale(city[0].name))
+                      .project("stroke", colorScale.scale(stat[0].name))
                       .project("stroke-width", 1)

   gridlines = new Plottable.Component.Gridlines(xScale, yScale)
@@ -35,8 +24,6 @@
     [yLabel, yAxis, center],
     [null,   null,  xAxis ]
   ]).renderTo(svg_id)
-  panZoom   = new Plottable.Interaction.PanZoom(xScale, null)
-  center.registerInteraction(panZoom)

 jitterbug_game = (canvas_id) ->
   canvas = new fabric.Canvas 'jitterbug_game_canvas',
@@ -48,19 +35,22 @@
   canvas.setHeight  400
   canvas.setWidth  600

-  turns = 0
   bugs  = starting_bugs()
   render_game canvas, bugs

-  next_turn bugs, canvas, turns
-  make_chart '#jitterbug_progress_chart_svg'
-
-next_turn = (bugs, canvas, turns) ->
+  next_turn bugs, canvas
+
+next_turn = (bugs, canvas) ->
   next_iteration bugs
   render_game canvas, bugs

-  if turns < 1000
-    setTimeout((-> next_turn bugs, canvas, turns + 1), 50)
+  if bugs.turns % 10 == 0
+    stats = bugs.names.map((name) -> bugs.stats[name])
+    make_chart '#jitterbug_progress_chart_svg', stats
+
+  if bugs.turns < 1000
+    bugs.turns += 1
+    setTimeout((-> next_turn bugs, canvas), 50)

 empty_grid = ->
   grid = []
@@ -77,7 +67,10 @@
     grid:        empty_grid()
     colors:      {}
     next_serial: 0
+    stats:       {}
+    names:       []
     next_color:  ['black', 'yellow', 'blue', 'grey', 'pink', 'green', 'red']
+    turns:       0

   for i in [0..10]
     add_bug bugs, 'fly_trap', create_fly_trap
@@ -108,6 +101,12 @@
     color:     color
     serial:    (bugs.next_serial += 1)

+  unless bugs.stats[name]?
+    bugs.stats[name] = [{iteration: 0, count: 0, name: name}]
+    bugs.names.push name
+
+  bugs.stats[name][0].count += 1
+
 random_location = (bugs) ->
   x = random_num 59
   y = random_num 39
@@ -129,6 +128,14 @@

   for bug in bug_list
     move_bug bugs, bug
+
+  bugs.names.map (name) ->
+    count = bug_list.reduce(
+      ((sum, bug) -> if bug.name == name then sum + 1 else sum), 0)
+    bugs.stats[name].push
+      iteration: bugs.turns
+      count:     count
+      name:      name

 info_at_location = (grid, bug, x, y) ->
   info = if x < 0 or y < 0 or x == 59 or y == 39


Every hour do a .reload

Every hour do a .reload

--- app/assets/javascripts/events.js.coffee
+++ app/assets/javascripts/events.js.coffee
@@ -24,7 +24,7 @@
   updateEventTable()
   setInterval () ->
     update_counter = update_counter + 1
-    if update_counter > 60
+    if update_counter > 60 and modal_shown isnt true
       location.reload()
     updateEventTable()
   , 60000


Use naturalOrder for legend label ordering

--- app/assets/javascripts/legend.js.coffee
+++ app/assets/javascripts/legend.js.coffee
@@ -12,6 +12,7 @@
     @legend = new Rickshaw.Graph.Legend
       graph: @graphs[0]
       element: @element.get(0)
+      naturalOrder: yes

     instance = @
     @graphSeries = {}