Fix multiple boosters

Fix multiple boosters

--- js/script.coffee
+++ js/script.coffee
@@ -95,7 +95,11 @@
 			body += " to #{p.rocket.second_stage.payloads[0].orbit}	"
 			# :ROCKET_NAME :ROCKET_TYPE [:CORE_SERIAL] to :LANDING_VEHICLE
 			# CORE_SERIAL
-			body += "#{p.rocket.rocket_name} #{p.rocket.rocket_type} [#{p.rocket.first_stage.cores[0].core_serial}]#{if p.rocket.first_stage.cores[0].landing_vehicle then " to #{p.rocket.first_stage.cores[0].landing_vehicle}" else ' EXP'}	"
+			body += "#{p.rocket.rocket_name} #{p.rocket.rocket_type}#{if p.rocket.first_stage.cores[0].landing_vehicle then " to #{p.rocket.first_stage.cores[0].landing_vehicle}" else ' EXP'}"
+			for c in p.rocket.first_stage.cores
+			  # body += "[#{c.core_serial}] "
+				body += ""
+			body += "	"
 			# :LAUNCH_SITE_NAME
 			# #:FLIGHT_NUMBER :LOCAL_DATE
 			body += "#{p.launch_site.site_name.replace /(?=\D)\s(?=\d)/g, '-'}##{p.flight_number} #{p.launch_date_local.substr(0,10)}	"
@@ -122,8 +126,8 @@
 	if /cap_serial/.test u then id = launch.rocket.second_stage.payloads[0].cap_serial
 	ele = document.getElementById id
 	gap = ((ele.parentNode.parentNode.getAttribute('data-launch') - launch.launch_date_unix) / unix_month).toPrecision(2) + " Months"
-	if /core_serial/.test u then ele.innerHTML = "#{gap} #{launch.rocket.second_stage.payloads[0].payload_id} #{launch.rocket.second_stage.payloads[0].orbit} #{launch.rocket.first_stage.cores[0].landing_vehicle}"
-	if /cap_serial/.test u then ele.innerHTML = "#{gap} #{launch.rocket.second_stage.payloads[0].payload_id} #{launch.rocket.second_stage.payloads[0].orbit} #{(launch.rocket.second_stage.payloads[0].flight_time_sec / unix_day).toPrecision(2)} Days"
+	if /core_serial/.test u then ele.innerHTML = "##{launch.flight_number} #{gap} #{launch.rocket.second_stage.payloads[0].payload_id} #{launch.rocket.second_stage.payloads[0].orbit} #{launch.rocket.first_stage.cores[0].landing_vehicle}"
+	if /cap_serial/.test u then ele.innerHTML = "##{launch.flight_number} #{gap} #{launch.rocket.second_stage.payloads[0].payload_id} #{launch.rocket.second_stage.payloads[0].orbit} #{(launch.rocket.second_stage.payloads[0].flight_time_sec / unix_day).toPrecision(2)} Days"
 	return

 # XMLHttpRequest.coffee


:art: Consume H::genValue in LinterPlus::eachLinter

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -41,11 +41,7 @@
     return @editorLinters.get editor

   eachLinter: (callback) ->
-    values = @editorLinters.values()
-    value = values.next()
-    while not value.done
-      callback(value.value)
-      value = values.next()
+    @h.genValue(@editorLinters, callback)

   observeLinters: (callback) ->
     @eachLinter callback


optimize hiding and showing on marker views

--- lib/marker-view.coffee
+++ lib/marker-view.coffee
@@ -12,10 +12,14 @@
   initialize: ({@editor, @marker} = {}) ->
     @regions = []

-    @updateDisplayPosition = @marker.isValid()
+    @isMarkerValid = @marker.isValid()
+    @updateDisplayPosition = @isMarkerValid
     @marker.on 'changed', ({newHeadScreenPosition, newTailScreenPosition, valid}) =>
       @updateDisplayPosition = valid
-      if valid then @show() else @hide()
+      if valid != @isMarkerValid
+        # @isMarkerValid is an optimization so we dont call into show or hide unless necessary
+        if valid then @show() else @hide()
+        @isMarkerValid = valid

     @marker.on 'destroyed', => @remove()


:new: Add LinterRegistry::getLinters

:new: Add LinterRegistry::getLinters

--- lib/linter-registry.coffee
+++ lib/linter-registry.coffee
@@ -9,6 +9,9 @@
       Regular: new WeakSet
       Fly: new WeakSet
     @emitter = new Emitter
+
+  getLinters: ->
+    return @linters.slice() # Clone the array

   hasLinter: (linter) ->
     @linters.indexOf(linter) isnt -1


Remove some comments.

Remove some comments.

--- lib/mocha-phantomjs.coffee
+++ lib/mocha-phantomjs.coffee
@@ -1,11 +1,9 @@
 system  = require 'system'
 webpage = require 'webpage'
-fs      = require 'fs'

 USAGE = """
         Usage: phantomjs run-mocha.coffee URL [timeout]
         """
-

 class Reporter

@@ -76,24 +74,17 @@
       mocha.phantomjs.failures = @failures
       mocha.phantomjs.ended = true

-
 class Spec extends Reporter

   constructor: ->
     super 'bdd', 'spec'

+class Dot extends Reporter

-# fs.write "/dev/stdout", "Hello!", "w"
-# fs.write "/dev/stdout", '\u001b[2K', "w"
-# fs.write "/dev/stdout", '\u001b[0G', "w"
-# fs.write "/dev/stdout", "Hello, world!", "w"
+  constructor: ->
+    super 'bdd', 'dot'

-reporter = new Spec
+reporter = new Dot
 reporter.run()

-# @page.onInitialized = =>
-#   @page.injectJs 'mocha-phantomjs/core_extensions.js'
-# @page.onResourceRequested = (resource) ->
-#   console.log(JSON.stringify(resource))

-


no need to print whole brain on debug

no need to print whole brain on debug

--- lib/phabricator.coffee
+++ lib/phabricator.coffee
@@ -56,7 +56,7 @@
         users: { },
         bot_phid: env.PHABRICATOR_BOT_PHID
       }
-      @robot.logger.debug 'Phabricator Data Loaded: ' + JSON.stringify(@data, null, 2)
+      @robot.logger.debug '---- Phabricator Data Loaded.'
     @robot.brain.on 'loaded', storageLoaded
     storageLoaded() # just in case storage was loaded before we got here
     @data.templates ?= { }


No more CoffeeScript

No more CoffeeScript

--- loggerconsole.coffee
+++ loggerconsole.coffee
@@ -1,166 +0,0 @@
-###
-@class LoggerConsole
-@summary Colorful console adapter for ostrio:logger (Logger)
-###
-class LoggerConsole
-  constructor: (@logger, @settings = {}) ->
-    check @logger, Match.OneOf Logger, Object
-
-    self = @
-    if Meteor.isServer
-      ###
-      @url https://github.com/euforic/coloring/blob/master/lib/index.js
-      ###
-      styles =
-        'bold': [
-          '1'
-          '22'
-        ]
-        'white': [
-          '37'
-          '39'
-        ]
-        'blue': [
-          '34'
-          '39'
-        ]
-        'cyan': [
-          '36'
-          '39'
-        ]
-        'magenta': [
-          '35'
-          '39'
-        ]
-        'red': [
-          '31'
-          '39'
-        ]
-        'yellow': [
-          '33'
-          '39'
-        ]
-
-      colorize = (style, text) ->
-        open  = '\x1B[' + styles[style][0] + 'm '
-        close = '\x1B[' + styles[style][1] + 'm '
-        return open + text + close
-
-      self.cons =
-        error: (obj, message) ->
-          if obj.level is 'FATAL'
-            process.stdout.write colorize('red', colorize('bold', message)) + '\r\n'
-          else
-            process.stdout.write colorize('red', message) + '\r\n'
-          return
-        info: (obj, message) ->
-          process.stdout.write colorize('cyan', message) + '\r\n'
-          return
-        warn: (obj, message) ->
-          process.stdout.write colorize('magenta', message) + '\r\n'
-          return
-        debug: (obj, message) ->
-          process.stdout.write colorize('white', colorize('bold', message)) + '\r\n'
-          return
-        trace: (obj, message) ->
-          process.stdout.write colorize('blue', message) + '\r\n'
-          return
-        log: (obj, message) ->
-          process.stdout.write colorize('bold', message) + '\r\n'
-          return
-    else
-      self.cons =
-        error: (obj, message) ->
-          if obj.level is 'FATAL'
-            style = 'color:#fb3120;font-weight:bold'
-          else
-            style = 'color:#fb3120'
-          if _.isFunction console.error
-            console.error message, style, obj.data
-          else
-            console.log message, style, obj.data
-          return
-        info: (obj, message) ->
-          if _.isFunction console.info
-            console.info message, 'color:#34b1bf', obj.data
-          else
-            console.log message, 'color:#34b1bf', obj.data
-          return
-        warn: (obj, message) ->
-          if _.isFunction console.warn
-            console.warn message, 'color:#d025d1', obj.data
-          else
-            console.log message, 'color:#d025d1', obj.data
-          return
-        debug: (obj, message) ->
-          if _.isFunction console.debug
-            console.debug message, 'color:white;font-weight:bold;background-color:#000', obj.data
-          else
-            console.log message, 'color:white;font-weight:bold;background-color:#000', obj.data
-          return
-        trace: (obj, message) ->
-          if _.isFunction console.trace
-            console.trace message, 'color:#501de9', obj.data
-          else
-            console.log message, 'color:#501de9', obj.data
-          return
-        log: (obj, message) ->
-          console.log message, 'font-weight:bold', obj.data
-          return
-
-    self.logger.add 'Console', (level, message, data = {}, userId) ->
-      time = new Date()
-
-      obj =
-        time: time
-        level: level
-        message: message
-
-      if data and Meteor.isServer
-        data = self.logger.antiCircular data
-      if _.isString data.stackTrace
-        data.stackTrace = data.stackTrace.split /\n|\\n|\r|\r\n/g
-
-      obj.data   = data   if data
-      obj.userId = userId if userId
-
-      if self.settings.format
-        _message = self.settings.format obj
-        check _message, String
-
-        if Meteor.isServer
-          message = _message
-        else
-          message = '%c' + _message + ''
-      else
-        if Meteor.isServer
-          message = '[' + obj.level + ': ' + obj.message + ' @ ' + obj.time + '] ' + '\r\n'
-          if obj.data and not _.isEmpty obj.data
-            message += JSON.stringify(obj.data, false, 2) + '\r\n'
-        else
-          message = '%c[' + obj.level + ': ' + obj.message + ']'
-
-      switch level
-        when 'FATAL' then self.cons.error obj, message
-        when 'ERROR' then self.cons.error obj, message
-        when 'INFO'  then self.cons.info  obj, message
-        when 'WARN'  then self.cons.warn  obj, message
-        when 'DEBUG' then self.cons.debug obj, message
-        when 'TRACE' then self.cons.trace obj, message
-        else self.cons.log obj, message
-      return
-
-  enable: (rule = {}) ->
-    check rule, {
-      enable: Match.Optional Boolean
-      client: Match.Optional Boolean
-      server: Match.Optional Boolean
-      filter: Match.Optional [String]
-    }
-
-    rule.enable ?= true
-    rule.client ?= true
-    rule.server ?= true
-
-    @logger.rule 'Console', rule
-    return @


Append core panels in order

Append core panels in order

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -21,7 +21,7 @@
       @div class: 'config-menu', =>
         @div class: 'atom-banner'
         @ul class: 'panels-menu nav nav-pills nav-stacked', outlet: 'panelMenu', =>
-          @div class: 'panel-menu-separator'
+          @div class: 'panel-menu-separator', outlet: 'menuSeparator'
           @div class: 'editor-container settings-filter', =>
             @subview 'filterEditor', new EditorView(mini: true, placeholderText: 'Filter packages')
         @div class: 'button-area', =>
@@ -119,17 +119,17 @@
     panelMenuItem = $$ ->
       @li name: name, =>
         @a class: "icon icon-#{iconName}", name
-    @addPanel name, panelMenuItem, true, panel
+    @menuSeparator.before(panelMenuItem)
+    @addPanel(name, panelMenuItem, panel)

   addPackagePanel: (pack) ->
     title = @packageManager.getPackageTitle(pack)
     panelMenuItem = new PackageMenuView(pack, @packageManager)
-    @addPanel pack.name, panelMenuItem, false, =>
+    @panelMenu.append(panelMenuItem)
+    @addPanel pack.name, panelMenuItem, =>
       new InstalledPackageView(pack, @packageManager)

-  addPanel: (name, panelMenuItem, isCorePanel, panelCreateCallback) ->
-    @panelMenu.prepend(panelMenuItem) if isCorePanel
-    @panelMenu.append(panelMenuItem) unless isCorePanel
+  addPanel: (name, panelMenuItem, panelCreateCallback) ->
     @panelCreateCallbacks ?= {}
     @panelCreateCallbacks[name] = panelCreateCallback
     @showPanel(name) if @panelToShow is name


Be defensive with pane.activeView

Be defensive with pane.activeView

--- lib/status-bar-view.coffee
+++ lib/status-bar-view.coffee
@@ -68,7 +68,7 @@

   subscribeToBuffer: ->
     @unsubscribeFromBuffer()
-    if @buffer = @pane.activeItem.getBuffer?()
+    if @buffer = @pane.activeItem?.getBuffer?()
       @buffer.on 'modified-status-changed', @updateBufferHasModifiedText
       @buffer.on 'saved', @updateStatusBar

@@ -80,7 +80,7 @@
     @updateCursorPositionText()

   updateGrammarText: ->
-    grammar = @pane.activeView.getGrammar?()
+    grammar = @pane.activeView?.getGrammar?()
     if grammar?
       if grammar is syntax.nullGrammar
         grammarName = 'Plain Text'
@@ -155,13 +155,13 @@
   updatePathText: ->
     if path = @getActiveItemPath()
       @currentPath.text(project.relativize(path)).show()
-    else if title = @pane.activeItem.getTitle?()
+    else if title = @pane.activeItem?.getTitle?()
       @currentPath.text(title).show()
     else
       @currentPath.hide()

   updateCursorPositionText: ->
-    if position = @pane.activeView.getCursorBufferPosition?()
+    if position = @pane.activeView?.getCursorBufferPosition?()
       @cursorPosition.text("#{position.row + 1},#{position.column + 1}").show()
     else
       @cursorPosition.hide()


Check attribute setting

Check attribute setting

--- test/spec/test_reactive.coffee
+++ test/spec/test_reactive.coffee
@@ -363,10 +363,7 @@
       expect(elt).not.toBeUndefined()
       expect(elt instanceof SVGRectElement).toBe(true)
     it 'should have the set attributes', ->
-      #expect(elt.prop('class')).toBe('my-class')
-      #expect(elt.prop('id')).toBe('my-elt')
-      #expect(elt.hasClass('my-class')).toBe(true)
-      #expect(elt.data('foo')).toBe('bar')
+      expect(elt.getAttribute('x')).toBe('10')

 describe 'nested mutations', ->
   it 'should not complain about directly nested mutations in dependent binds of dependent binds', ->


Test for SVG Element

Test for SVG Element

--- test/spec/test_reactive.coffee
+++ test/spec/test_reactive.coffee
@@ -51,6 +51,7 @@
       ]
     it 'should have the right tag', ->
       expect(elt.is('header')).toBe(true)
+      expect(elt[0] instanceof Element).toBe(true)
     it 'should have the set attributes', ->
       expect(elt.prop('class')).toBe('my-class')
       expect(elt.attr('style')).toBe('font-size: 10px')
@@ -95,6 +96,7 @@
     it 'should have the right tag', ->
       expect(elt).toBeDefined()
       expect(elt.is('rect')).toBe(true)
+      expect(elt[0] instanceof SVGElement).toBe(true)
     it 'should have the set attributes', ->
       expect(elt.attr('x')).toBe('10')
     it 'should have the given child contents', ->


fix for random unit tests failing

fix for random unit tests failing

--- test/test_sailthru_util.coffee
+++ test/test_sailthru_util.coffee
@@ -91,24 +91,23 @@
     test.done()

 exports.testMd5 = (test) ->
-    test.expect 1
+    test.expect 2
     data1 = 'simple_text'
     hash1 = SailthruUtil.md5 data1
     cmd1 = "python -c 'import hashlib; print hashlib.md5(\"" + data1 + "\").hexdigest()'"

     exec1 = exec cmd1, (error, stdout, stderr) ->
         test.equal stdout.trim(), hash1
-        #test.done()

-    data2 = "नमस्ते विश्व"
-    hash2 = SailthruUtil.md5 data2
+        data2 = "नमस्ते विश्व"
+        hash2 = SailthruUtil.md5 data2

-    cmd2 = "python -c 'import hashlib; print hashlib.md5(\"" + data2 + "\").hexdigest()'"
+        cmd2 = "python -c 'import hashlib; print hashlib.md5(\"" + data2 + "\").hexdigest()'"

-    exec2 = exec cmd2, (error, stdout, stderr) ->
-        test.equal stdout.trim(), hash2
+        exec2 = exec cmd2, (error, stdout, stderr) ->
+            test.equal stdout.trim(), hash2

-    exec2.on 'exit', (code) ->
-        test.done()
+        exec2.on 'exit', (code) ->
+            test.done()


corrected indention to 4 spaces

corrected indention to 4 spaces

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -60,9 +60,9 @@
         return @_hasLevel(path, 'error')

     hasError : () ->
-      for path of @paths
-        return true if @pathHasError(path)
-      return false
+        for path of @paths
+            return true if @pathHasError(path)
+        return false

     _hasLevel : (path, level) ->
         for error in @paths[path]


require valid email address

require valid email address

--- models/models.coffee
+++ models/models.coffee
@@ -318,7 +318,8 @@
         return fn ['Unauthorized'] unless voter.isNew()
         @person = voter
         @person.type = 'Voter'
-        @person.save =>
+        @person.save (error, person) =>
+          return fn error if error?
           @person.welcomeVoter fn
     else
       if @isNew()
@@ -335,7 +336,7 @@
       fn()

   beforeInstantiate: (fn) ->
-    Person.first @person.id(), (error, voter) =>
+    Person.first { _id: @person._id }, (error, voter) =>
       @person = voter
       fn()

@@ -343,6 +344,7 @@
     errors = []
     for dimension in [ 'Utility', 'Design', 'Innovation', 'Completeness' ]
       errors.push "#{dimension} should be between 1 and 5 stars" unless 1 <= this[dimension.toLowerCase()] <= 5
+    errors.push 'Invalid email address' unless validEmail @email
     errors

 _.extend Vote, {


Useful helpers

Useful helpers

--- models/article.coffee
+++ models/article.coffee
@@ -1,8 +1,10 @@
 _ = require 'underscore'
 Q = require 'q'
 Backbone = require 'backbone'
+moment = require 'moment'
 sd = require('sharify').data
 Artworks = require '../collections/artworks.coffee'
+{ crop, resize } = require '../components/resizer/index.coffee'

 module.exports = class Article extends Backbone.Model
   urlRoot: "#{sd.POSITRON_URL}/api/articles"
@@ -49,3 +51,9 @@

   href: ->
     "/article/#{@get('slug')}"
+
+  cropUrlFor: (attr, args...) ->
+    crop @get(attr), args...
+
+  date: (attr) ->
+    moment @get(attr)


Check for dependencies before iterating

Check for dependencies before iterating

--- lib/asset.coffee
+++ lib/asset.coffee
@@ -128,15 +128,16 @@
             callback null

     @dependencies = (visited = [], required = []) ->
-      ext = @ext()
-      visited.push @
-      for directive in @directives
-        for dependency in directive.dependencies
-          if dependency in visited
-            unless dependency in required or ext isnt dependency.ext()
-              required.push dependency
-          else
-            required = dependency.dependencies visited, required
+      if @directives
+        ext = @ext()
+        visited.push @
+        for directive in @directives
+          for dependency in directive.dependencies
+            if dependency in visited
+              unless dependency in required or ext isnt dependency.ext()
+                required.push dependency
+            else
+              required = dependency.dependencies visited, required
       required

     # Constructor Code


Added user field to bounce & run

Added user field to bounce & run

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -33,7 +33,7 @@

     run: (confirmedOrPromptData) ->
         options =
-            url: "#{ @url() }/run"
+            url: "#{ @url() }/run?user=#{app.getUsername()}"
             type: 'POST'
             contentType: 'application/json'

@@ -46,7 +46,7 @@

     bounce: =>
         $.ajax
-            url: "#{ @url() }/bounce"
+            url: "#{ @url() }/bounce?user=#{app.getUsername()}"
             type: "POST"

     destroy: =>


whoops

whoops

--- modules/image.coffee
+++ modules/image.coffee
@@ -79,10 +79,10 @@
                     (d.data.allowNSFW or msg.channel.name.indexOf('nsfw') >= 0)
       # Query the tumblr tag
       tumblr.taggedPosts args, (err, data)=>
-        return msg.reply 'Something went wrong.' if err or data.meta.status isnt 200
+        return msg.reply 'Something went wrong.' if err
         # Filter only image results
         try
-          results = data.response.filter((r)=> r.type is 'photo')
+          results = data.filter((r)=> r.type is 'photo')
           return msg.reply 'No results.' unless results.length
           # Pick a random image
           image = @chance.pickone results


Fix regression where resolved filenames where no longer emitted with the 'change' event during preview.

--- src/core/server.coffee
+++ src/core/server.coffee
@@ -129,7 +129,17 @@
     ignoreInitial: true

   # reload content tree on changes
-  contentWatcher.on 'all', (path) -> loadContents(changeHandler, path) if not block.contentsLoad
+  contentWatcher.on 'all', (type, filename) ->
+    return if block.contentsLoad
+    loadContents (error) ->
+      contentFilename = null
+      if not error? and filename?
+        # resolve filename for changed content
+        for content in ContentTree.flatten contents
+          if content.__filename is filename
+            contentFilename = content.filename
+            break
+      changeHandler error, contentFilename

   templateWatcher = chokidar.watch env.templatesPath,
     ignoreInitial: true


Add move and set uppper left methods to shapes

--- src/core/shapes.coffee
+++ src/core/shapes.coffee
@@ -132,6 +132,12 @@
       img = new Image()
       img.src = data.imageSrc
     createShape('Image', {x: data.x, y: data.y, image: img, scale: data.scale})
+  move: ( moveInfo={} ) ->
+    @x = @x - moveInfo.xDiff
+    @y = @y - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    @x = upperLeft.x
+    @y = upperLeft.y

 defineShape 'Rectangle',
@@ -152,6 +158,12 @@
   }
   toJSON: -> {@x, @y, @width, @height, @strokeWidth, @strokeColor, @fillColor}
   fromJSON: (data) -> createShape('Rectangle', data)
+  move: ( moveInfo={} ) ->
+    @x = @x - moveInfo.xDiff
+    @y = @y - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    @x = upperLeft.x
+    @y = upperLeft.y

 # this is pretty similar to the Rectangle shape. maybe consolidate somehow.
@@ -173,6 +185,12 @@
   }
   toJSON: -> {@x, @y, @width, @height, @strokeWidth, @strokeColor, @fillColor}
   fromJSON: (data) -> createShape('Ellipse', data)
+  move: ( moveInfo={} ) ->
+    @x = @x - moveInfo.xDiff
+    @y = @y - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    @x = upperLeft.x
+    @y = upperLeft.y

 defineShape 'Line',
@@ -196,6 +214,16 @@
   toJSON: ->
     {@x1, @y1, @x2, @y2, @strokeWidth, @color, @capStyle, @dash, @endCapShapes}
   fromJSON: (data) -> createShape('Line', data)
+  move: ( moveInfo={} ) ->
+    @x1 = @x1 - moveInfo.xDiff
+    @y1 = @y1 - moveInfo.yDiff
+    @x2 = @x2 - moveInfo.xDiff
+    @y2 = @y2 - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    br = @getBoundingRect()
+    xDiff = br.x - upperLeft.x
+    yDiff = br.y - upperLeft.y
+    @move({ xDiff, yDiff })

 # returns false if no points because there are no points to share style
@@ -306,6 +334,22 @@
         0, @smoothedPoints.length - @segmentSize * (@tailSize - 1)
       ).concat(@tail)

+  move: ( moveInfo={} ) ->
+    if !@smooth
+      pts = @points
+    else
+      pts = @smoothedPoints
+
+    for pt in pts
+      pt.move(moveInfo)
+
+    @points = @smoothedPoints
+
+  setUpperLeft: (upperLeft={}) ->
+    br = @getBoundingRect()
+    xDiff = br.x - upperLeft.x
+    yDiff = br.y - upperLeft.y
+    @move({ xDiff, yDiff })

 LinePath = defineShape 'LinePath', linePathFuncs

@@ -330,6 +374,12 @@
     {x: @x - @size / 2, y: @y - @size / 2, width: @size, height: @size}
   toJSON: -> {@x, @y, @size, @color}
   fromJSON: (data) -> createShape('Point', data)
+  move: ( moveInfo={} ) ->
+    @x = @x - moveInfo.xDiff
+    @y = @y - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    @x = upperLeft.x
+    @y = upperLeft.y

 defineShape 'Polygon',
@@ -365,6 +415,16 @@
         x, y, size: data.strokeWidth, color: data.strokeColor
       })
     createShape('Polygon', data)
+
+  move: ( moveInfo={} ) ->
+    for pt in @points
+      pt.move(moveInfo)
+
+  setUpperLeft: (upperLeft={}) ->
+    br = @getBoundingRect()
+    xDiff = br.x - upperLeft.x
+    yDiff = br.y - upperLeft.y
+    @move({ xDiff, yDiff })

 defineShape 'Text',
@@ -434,6 +494,12 @@
     }
   toJSON: -> {@x, @y, @text, @color, @font, @forcedWidth, @forcedHeight, @v}
   fromJSON: (data) -> createShape('Text', data)
+  move: ( moveInfo={} ) ->
+    @x = @x - moveInfo.xDiff
+    @y = @y - moveInfo.yDiff
+  setUpperLeft: (upperLeft={}) ->
+    @x = upperLeft.x
+    @y = upperLeft.y

 defineShape 'SelectionBox',


fix #6 so select().remove() works for multiple matches

--- lib/chain.coffee
+++ lib/chain.coffee
@@ -308,16 +308,20 @@
     # add action as part of select() stuff.

     # length at the start of the first operation, and, index starts at zero.
-    length = chain.array.length
-    index = 0
+    array  = chain.array
+    length = array.length
+    index  = 0

     # let's remember the results of each action
     results = []

-    for fn in chain.array
+    while index < length
+
+      fn = array[index]

       # apply the action to the function only if the selector say so
       if selector fn, index
+
         # put both of them in there as the first two args in the slots we created above
         args[0] = fn
         args[1] = index
@@ -326,14 +330,14 @@
         result = action.apply chain, args

         # compare length before the action and after the action
-        diff = chain.array.length - length
+        diff = array.length - length

         # apply the difference to the index.
         # when we remove one it's -1 so we reduce the index.
         index += diff

         # and then update the length
-        length = chain.array.length
+        length = array.length

         # gather each action's results
         results.push result


Remove scopes from event names in subscriptions

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -538,31 +538,31 @@
     @subscribe @editor, "destroyed", =>
       @remove()

-    @subscribe @editor, "contents-conflicted.editor", =>
+    @subscribe @editor, "contents-conflicted", =>
       @showBufferConflictAlert(@editor)

-    @subscribe @editor, "path-changed.editor", =>
+    @subscribe @editor, "path-changed", =>
       @editor.reloadGrammar()
       @trigger 'editor:path-changed'

-    @subscribe @editor, "grammar-changed.editor", =>
+    @subscribe @editor, "grammar-changed", =>
       @trigger 'editor:grammar-changed'

-    @subscribe @editor, 'selection-added.editor', (selection) =>
+    @subscribe @editor, 'selection-added', (selection) =>
       @newCursors.push(selection.cursor)
       @newSelections.push(selection)
       @requestDisplayUpdate()

-    @subscribe @editor, 'screen-lines-changed.editor', (e) =>
+    @subscribe @editor, 'screen-lines-changed', (e) =>
       @handleScreenLinesChange(e)

-    @subscribe @editor, 'scroll-top-changed.editor', (scrollTop) =>
+    @subscribe @editor, 'scroll-top-changed', (scrollTop) =>
       @scrollTop(scrollTop)

-    @subscribe @editor, 'scroll-left-changed.editor', (scrollLeft) =>
+    @subscribe @editor, 'scroll-left-changed', (scrollLeft) =>
       @scrollLeft(scrollLeft)

-    @subscribe @editor, 'soft-wrap-changed.editor', (softWrap) =>
+    @subscribe @editor, 'soft-wrap-changed', (softWrap) =>
       @setSoftWrap(softWrap)

     @trigger 'editor:path-changed'


Fix circular dependency between subscriber and event

--- lib/event.coffee
+++ lib/event.coffee
@@ -1,5 +1,4 @@
 async = require 'async'
-Subscriber = require('./subscriber').Subscriber
 logger = require 'winston'

 class Event
@@ -31,6 +30,7 @@
                     cb(null)

     unicastSubscriber: ->
+        Subscriber = require('./subscriber').Subscriber
         if (matches = Event::unicast_format.exec @name)?
             subscriberId = matches[1]
             new Subscriber(@redis, subscriberId)


Split up elapse into two methods.

Split up elapse into two methods.

--- src/environment.coffee
+++ src/environment.coffee
@@ -26,28 +26,27 @@
     # @param element {Array} the element to update
     # @param milliseconds {Number} the number of ms that have elapsed
     elapse: (elements, milliseconds) ->
+        (@elapseElement(e, milliseconds) for e in elements)
+        return this # HACK: Don't compile the loop into an expression.

-        for element in elements
+    elapseElement : (element, milliseconds) ->
+       # Find the net force on the element.
+       forces = [
+           @drag(element),
+           @gravity(element)
+       ]
+       resultant = forces.reduce (t, s) -> t.add(s)    #FIXME: not portable

-            # Find the net force on the element.
-            forces = [
-                @drag(element),
-                @gravity(element)
-            ]
-            resultant = forces.reduce (t, s) -> t.add(s)    #FIXME: not portable
+       # Calculate the effect of the forces on the element.
+       acceleration = resultant.scale(element.getInverseMass())
+       velocity = element.getVelocity().add(acceleration.scale(milliseconds))
+       displacement = velocity.scale(milliseconds)
+       position = element.getPosition().add(displacement)

-            # Calculate the effect of the forces on the element.
-            acceleration = resultant.scale(element.getInverseMass())
-            velocity = element.getVelocity().add(acceleration.scale(milliseconds))
-            displacement = velocity.scale(milliseconds)
-            position = element.getPosition().add(displacement)
-
-            # Update the element.
-            element.setPosition(position)
-            element.speed = velocity.getLength()
-            element.direction = velocity.normalize()
-
-        return this # HACK: Don't compile the loop into an expression.
+       # Update the element.
+       element.setPosition(position)
+       element.speed = velocity.getLength()
+       element.direction = velocity.normalize()

     # Return the force of drag on the element.
     #


minor import change

minor import change

--- library/nucleus/api.coffee
+++ library/nucleus/api.coffee
@@ -34,7 +34,7 @@
 tools = require "./tools"
 extendz = require "./extends"
 routing = require "./routing"
-service = require "./service"
+{Service} = require "./service"
 {STATUS_CODES} = require "http"

 # This is an abstract base class for every service in the system
@@ -42,7 +42,7 @@
 # to some arbitrary resource, determined by HTTP path and guarded
 # by the domain matching. This is the crucial piece of framework.
 # It supports strictly methods defined in the HTTP specification.
-module.exports.Api = class Api extends service.Service
+module.exports.Api = class Api extends Service

     # This is a marker that indicates to some internal subsystems
     # that this class has to be considered abstract and therefore


minor additions to example API

minor additions to example API

--- src/example_api.coffee
+++ src/example_api.coffee
@@ -85,9 +85,15 @@
       extends: {$ref: "#resource"}
       mediaType: media_type("reference")
       properties:
-        name: {type: "string"}
-        commit: {type: "string"}
-        message: {type: "string"}
+        name:
+          required: true
+          type: "string"
+        commit:
+          required: true
+          type: "string"
+        message:
+          required: true
+          type: "string"

     branch:
       extends: {$ref: "#reference"}
@@ -121,6 +127,9 @@
             type: "integer"
           offset:
             type: "integer"
+          sort:
+            type: "string"
+            enum: ["asc", "desc"]
         status: 200

   organization:


Wait for proper channel, log success/failure message.

--- src/fetch-token.coffee
+++ src/fetch-token.coffee
@@ -7,8 +7,9 @@
 subscribeToDonationChannel = (channelToken) ->
   channel = pusher.subscribe(channelToken)

-  channel.bind "my_event", (data) ->
-    console.log('success!')
+  channel.bind "charge_completed", (data) ->
+    console.log(data.status)
+    console.log(data.message)

 stripeResponseHandler = (status, response) ->
@@ -34,11 +35,11 @@

     req.done (response, textStatus, jqXHR) ->
       $form.find(".payment-errors").text "Success! Waiting to charge card..."
-      console.log(response)
+      subscribeToDonationChannel(response.pusher_channel_token)
     req.fail (response, textStatus, errorThrown) ->
       $form.find(".payment-errors").text errorThrown

-    subscribeToDonationChannel('test_channel')
+
     false

 jQuery ($) ->


update

update

--- app/assets/javascripts/common.js.coffee
+++ app/assets/javascripts/common.js.coffee
@@ -61,10 +61,10 @@
 frameTransparent = () ->
   $('iframe').each ->
     url = $(this).attr("src")
-    if $(this).attr("data-transparent") != 1
+    if $(this).attr("data-transparent") != "1"
       $(this).attr {
         "src" : url+"?wmode=transparent"
-        "data-transparent" : 1
+        "data-transparent" : "1"
       }


EXTREMELY important bug fix

EXTREMELY important bug fix

--- app/assets/javascripts/common.js.coffee
+++ app/assets/javascripts/common.js.coffee
@@ -60,5 +60,7 @@
   new Konami( () ->
     audio = new Audio('/jingle.mp3')
     audio.play()
+    styles = document.styleSheets[document.styleSheets.length-1]
+    styles.insertRule('* { font-family: "Comic Sans MS" !important; }',styles.cssRules.length)
   )
 )


decaffeinating

decaffeinating

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


whitespace

whitespace

--- app/assets/javascripts/router.js.coffee
+++ app/assets/javascripts/router.js.coffee
@@ -6,11 +6,11 @@
     @view = new Thorax.LayoutView el: '#container'

   routes:
-    '':                                 'dashboard'
+    '':                                                                 'dashboard'
     'measures/:id(/:sub_id)(/providers/:provider_id)/patient_results':  'patientResultsForMeasure'
-    'measures/:id(/:sub_id)(/providers/:provider_id)':           'measure'
-    'patients/:id':                     'patient'
-    'providers(/:id)':                  'provider'
+    'measures/:id(/:sub_id)(/providers/:provider_id)':                  'measure'
+    'patients/:id':                                                     'patient'
+    'providers(/:id)':                                                  'provider'

   dashboard: ->
     @view.setView new Thorax.Views.ProviderView model: PopHealth.rootProvider


GCW-1695-Chinese wordings for entering the pin

--- addon/locales/zh-tw/translations.coffee
+++ addon/locales/zh-tw/translations.coffee
@@ -26,7 +26,7 @@
   "remove" : "移除"
   "yes" : "是"
   "no" : "否"
-  "ok" : "可以"
+  "ok" : "確定"
   "index.title" : "國際十字路會呈獻"
   "index.donate_goods" : "捐贈剩餘物資，援助需要人士"
   "index.how" : "要怎樣做？"


Foundation fix

Foundation fix

--- app/assets/javascripts/utilities/listener.coffee
+++ app/assets/javascripts/utilities/listener.coffee
@@ -8,27 +8,29 @@

     # Define overriding method.
     jQuery.fn.on = ->
-      listener_function = arguments[2]
-      element = arguments[1]
-      listener_namespace = arguments[0].split(".")
-      event_trigger = listener_namespace[0]
-      listener_name = listener_namespace[listener_namespace.length - 1]
-      namespaces = listener_namespace.slice(1, -1)
+      if (jQuery.type( arguments[0] ) == "string")
+        listener_function = arguments[2]
+        element = arguments[1]

-      listener_info = {
-        trigger: event_trigger,
-        element: element,
-        name: listener_name,
-        funct: listener_function
-      }
+        listener_namespace = arguments[0].split(".")
+        event_trigger = listener_namespace[0]
+        listener_name = listener_namespace[listener_namespace.length - 1]
+        namespaces = listener_namespace.slice(1, -1)

-      App.Listener.createNestedObject(App, namespaces, listener_info)
+        listener_info = {
+          trigger: event_trigger,
+          element: element,
+          name: listener_name,
+          funct: listener_function
+        }

-      # Execute the original method.
-      originalOnMethod.apply this, arguments
-      return
+        App.Listener.createNestedObject(App, namespaces, listener_info)

-    return
+        # Execute the original method.
+        originalOnMethod.apply this, arguments
+
+      else
+        originalOnMethod.apply this, arguments
   )()

   @createNestedObject = (base, names, value) ->


View logger

View logger

--- lib/index.coffee
+++ lib/index.coffee
@@ -6,7 +6,7 @@
 setupEvent = (eventName, rootSelector, selector, action) ->

 bone.view = (selector, options) ->
-    view = {}
+    view = {}
     events = options.events
     for eventSelector, functionName of events
         continue if functionName is 'events'
@@ -17,6 +17,8 @@
             fullSelector = selector
             fullSelector += " #{subSelector}" if subSelector?
             action = options[functionName]
+            #View: [Selector:Action]
+            console.log('View: ['+fullSelector+" : "+eventName+"]");
             $('body').on eventName, fullSelector, (event) ->
                 root = $(fullSelector).parents(selector)[0]
                 action root, event


removed async where unnecessary

removed async where unnecessary

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,5 +1,6 @@
 fs        = require 'fs'
 request   = require 'request'
+path      = require 'path'
 W         = require 'when'
 nodefn    = require 'when/node'

@@ -68,9 +69,8 @@
      ###

     file = (key, obj) ->
-      W ->
-        f = fs.readFileSync obj.file, 'utf8'
-        respond.call(@, key, obj, JSON.parse(f))
+      f = fs.readFileSync path.resolve(obj.file), 'utf8'
+      respond.call(@, key, obj, JSON.parse(f))

     ###*
      * If an object is passed, adds object
@@ -80,8 +80,7 @@
      ###

     data = (key, obj) ->
-      W ->
-        respond.call(@, key, obj, obj.data)
+      respond.call(@, key, obj, obj.data)

     ###*
      * Takes json and adds to records object


standardize logs

standardize logs

--- lib/index.coffee
+++ lib/index.coffee
@@ -16,16 +16,16 @@

     async.waterfall [
       (cb_wf) => @_redis.get reserve_key, (err, val) =>  # log existing value for runtime debugging
-        @log "Existing resource lock value for", reserve_key, val unless err?  # ignore errors
+        @log "RESERVE: Existing resource lock value for", reserve_key, val unless err?  # ignore errors
         cb_wf()
       (cb_wf) => @_redis.setnx reserve_key, val, (err, state) =>
-        debug "RESERVE (err, state, reserve_key):", err, state, reserve_key
+        debug "RESERVE: (err, state, reserve_key):", err, state, reserve_key
         if err?
-          @log "RESERVE_ERROR: Could not get lock for #{reserve_key} on #{val}. Assuming no lock."
+          @log "RESERVE: Failed to get lock for #{reserve_key} on #{val}"
           return cb_wf err

         lock_status = if state? then (state is 1) else false
-        @log(val, "attempted to reserve", reserve_key, 'RESERVE_STATUS:', lock_status)
+        @log("RESERVE:", val, "attempted to reserve", reserve_key, 'STATUS:', lock_status)
         return cb_wf err, false unless lock_status

         @_reserve_key = reserve_key
@@ -42,10 +42,10 @@
     async.until(
       => @_reserve_key?
       (cb_u) => @lock resource, (err, lock_status) =>
-        @log "Attempted to reserve #{resource}:", lock_status, err
+        @log "RESERVE: Attempted to reserve #{resource}:", lock_status, err
         setTimeout cb_u, 1000, err  # try every second
       (err) =>
-        @log "failed to reserve resource", err if err?
+        @log "RESERVE: Failed to reserve resource", err if err?
         @log "RESERVE: Done waiting for resource. reserve_state:", @_reserve_key?
         return cb err, @_reserve_key?
     )
@@ -56,8 +56,8 @@
     clearInterval @_heartbeat if @_heartbeat? # we're done here, no more heartbeats
     @_init_redis()
     @_redis.del @_reserve_key, (err, state) =>
-      @log "RESERVE_ERROR: Failed to RELEASE LOCK for #{@_reserve_key} on #{@_reserve_val}" if err?
-      @log 'RESERVE_RELEASED', @_reserve_key unless err?
+      @log "RESERVE: Failed to RELEASE LOCK for #{@_reserve_key} on #{@_reserve_val}" if err?
+      @log 'RESERVE: Released', @_reserve_key unless err?
       delete @_reserve_key  # loose the lock
       delete @_reserve_val
       cb err, state or !err?
@@ -76,7 +76,7 @@
         @_set_expiration cb_wf
     ], (err, expire_state) =>
       if err? or expire_state is 0
-        @log "RESERVE_ERROR: Failed to ensure reservation, will try again in 10 minutes. expire_status:", expire_state, err
+        @log "RESERVE: Failed to ensure reservation, will try again in 10 minutes. expire_status:", expire_state, err

   _set_expiration: (cb) ->
     return setImmediate cb unless @_reserve_key?  # nothing reserved here
@@ -84,7 +84,7 @@

     # set expiration to 30 min
     @_redis.expire @_reserve_key, @lock_ttl, (err, state) =>
-      @log "RESERVE_ERROR: Reserved but failed to set expiration for #{@_reserve_key}", err if err?
+      @log "RESERVE: Failed to set expiration for #{@_reserve_key} after reservation", err if err?
       return cb null, (state is 1)

   _init_redis: ->
@@ -93,5 +93,5 @@
     unless @_redis?
       @_redis = redis.createClient @port, @host, { retry_max_delay:100, connect_timeout: 500, max_attempts: 10 }
       @_redis.once 'error', (err) =>
-        @log "RESERVE_ERROR: connecting to REDIS: #{@host}:#{@port}.", err
+        @log "RESERVE: Error connecting to REDIS: #{@host}:#{@port}.", err
         throw err


ensureAssetConfigHasType: Attempt not to die when the file command cannot be executed to guess the content type of an asset with an unrecognized extension (eg. on Windows).

--- lib/index.coffee
+++ lib/index.coffee
@@ -410,8 +410,11 @@
         ])
         fileOutput = ''

-        # The 'file' utility might close its stdin as soon as it has figured
-        # out the content type:
+        fileProcess.on 'error', (err) =>
+          @emit 'warn', "Could not determine asset type from contents using the file command: #{err.stack}"
+
+        # The 'file' utility might close its stdin as soon as it has figured out
+        # the content type:
         fileProcess.stdin.on 'error', ->

         fileProcess.stdout.on('data', (chunk) ->


Enhance & standardize cucumber steps.

Enhance & standardize cucumber steps.

--- features/step_definitions/steps.coffee
+++ features/step_definitions/steps.coffee
@@ -9,12 +9,12 @@

 module.exports = ->

-  this.Before (callback)->
+  @Before (callback)->
     TestSystem ?= require '../support/test_system'
     system = TestSystem
     callback()

-  this.Given /^the System is$/, (source, callback) ->
+  @Given /^the System is$/, (source, callback) ->
     try
       system = TestSystem.parse(source)
     catch e
@@ -36,7 +36,16 @@

     callback()

-  this.Given /^I dress the following JSON document:$/, (doc, callback) ->
+  @Given /^I dress JSON's '(.*?)' with (.*?)$/, (jsonValue, typename, callback) ->
+    try
+      json = JSON.parse(jsonValue)
+      result = system.fetch(typename).dress(json)
+    catch e
+      result = e
+
+    callback()
+
+  @Given /^I dress the following JSON document:$/, (doc, callback) ->
     try
       json = JSON.parse(doc)
       result = system.dress(json)
@@ -45,7 +54,7 @@

     callback()

-  this.Given /^I dress the following JSON document with (.*?):$/, (type, doc, callback) ->
+  @Given /^I dress the following JSON document with (.*?):$/, (type, doc, callback) ->
     try
       json = JSON.parse(doc)
       result = system.fetch(type).dress(json)
@@ -54,7 +63,7 @@

     callback()

-  this.Given /^I validate the following JSON data against (.*?)$/, (type, json, callback) ->
+  @Given /^I validate the following JSON data against (.*?)$/, (type, json, callback) ->
     type = system.fetch(type)

     try
@@ -94,7 +103,7 @@

   this.Then /^it should be a TypeError as:$/, (table, callback) ->
     unless result instanceof Qjs.TypeError
-      callback.fail new Error("#{result} is not a TypeError")
+      callback.fail result

     for k, v of table.hashes()[0]
       unless result[k] == v


Make sure :sorting parameter is nested inside the :q parameter to fix sorting of admin products

Fixes #6105.

--- app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -104,7 +104,7 @@
   $scope.$watch 'sortOptions', (sort) ->
     return unless sort && sort.predicate != ""

-    $scope.sorting = sort.getSortingExpr()
+    $scope.q.sorting = sort.getSortingExpr()
     $scope.fetchProducts()
   , true


Changed upper case filters to lower case per request

--- fits/app/controllers/fitsviewer.coffee
+++ fits/app/controllers/fitsviewer.coffee
@@ -155,8 +155,8 @@

   createBandButtons: =>
     for band in @bands
-      bandUpper = band.toUpperCase()
-      @controls.append("<button id='band-#{band}' class='band' value='#{band}' disabled='disabled'>#{bandUpper}</button>")
+      bandLower = band.toLowerCase()
+      @controls.append("<button id='band-#{band}' class='band' value='#{band}' disabled='disabled'>#{bandLower}</button>")
     @controls.append("<button id='band-color' class='band' value='color'>#{ I18n.t('fits.color') }</button>")

   destroyBandButtons: =>


More supported things.

More supported things.

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -35,7 +35,7 @@
     content = content + "\\n"

     # Greentext
-    content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\.\,\-\_\/\?\:\&\=\#\%\+\*\~\\√\◊\(\)]*\\n)/ig,"$1")
+    content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\.\,\-\_\/\?\:\&\^\<\>\=\#\%\+\*\~\\√\◊\(\)]*\\n)/ig,"$1")

     # Zee text.
     if @model.get('user').get('role') == 'creator'


Iterate horizontally

Iterate horizontally

--- framer/Components/GridComponent.coffee
+++ framer/Components/GridComponent.coffee
@@ -73,8 +73,8 @@

 		@_reset()

-		for column in [@columns-1..0]
-			for row in [@rows-1..0]
+		for row in [@rows-1..0]
+			for column in [@columns-1..0]

 				frame =
 					x: @cellX(column)
@@ -98,4 +98,8 @@
 	_reset: ->
 		_.invoke(@cells, "destroy")
 		@_cells = {}
+
+	# copy: ->
+	# 	result = super
+	# 	result.render()


fix(leaderboard): account for ties in rank correctly

Previously, if two people were tied, say for 1st place, the
next person would be treated as rank 2. Because there needs
to be a gap to account for movement in the tie rank, the
next person should actually be rank 3.

fixes #36

--- frontend/directives/leaderboard.coffee
+++ frontend/directives/leaderboard.coffee
@@ -40,6 +40,13 @@
         ).reverse()
         currentRank = 1
         numRanks = ranks.length
+        rankGap = 0
+        # for every row sorted by matchWins, then gameWins descending,
+        # assign them the currentRank. if the next row isn't equivalent
+        # in standing, increment the rank by one, accounting for any
+        # gaps (ties) (eg: if two people are ranked 1st, the next
+        # person would be ranked 3rd).  if the next row is the same
+        # rank, increase the rankGap.
         for rank, i in ranks
           increment = false
           nextRank = ranks[i + 1]
@@ -49,7 +56,12 @@
               increment = true
           rank.rank = currentRank
           rank.opacity = opacityFor currentRank, numRanks
-          currentRank++ if increment
+          if increment
+            currentRank++
+            currentRank += rankGap
+            rankGap = 0
+          else
+            rankGap++
         $scope.rankings = ranks
       $scope.$watch "season()", ->
         updateRankings()


Tweak code-of-conduct message (trying to link to #presentaciones channel)

--- scripts/welcome-to-zihub.coffee
+++ scripts/welcome-to-zihub.coffee
@@ -62,4 +62,5 @@

   robot.respond /la bienvenida/i, (msg) ->
 #    msg.reply welcomeMsg('')
+    msg.reply 'voy'
     robot.adapter.client.chat.postMessage(msg.room, welcomeMsg(''))


friendly debugging representations

friendly debugging representations

--- src/java_object.coffee
+++ src/java_object.coffee
@@ -18,6 +18,12 @@

   get_field_from_offset: (rs, offset) -> @array[offset.toInt()]
   set_field_from_offset: (rs, offset, value) -> @array[offset.toInt()] = value
+
+  toString: ->
+    if @array.length <= 10
+      "<#{@type} [#{@array}]>"
+    else
+      "<#{@type} (length-#{@array.length})>"

 class root.JavaObject
@@ -57,8 +63,14 @@
     else
       @fields[f.name] = value

+  toString: ->
+    if @type.toClassString() is 'java/lang/String'
+      "<#{@type} '#{@jvm2js_str()}'>"
+    else
+      "<#{@type} (*#{@ref})>"
+
   # Convert a Java String object into an equivalent JS one.
-  jvm2js_str: () ->
+  jvm2js_str: ->
     util.chars2js_str(@fields.value, @fields.offset, @fields.count)

 root.thread_name = (thread) ->


Fixed problem where state information would display while zoomed out.

--- app/scripts/controllers/district.coffee
+++ app/scripts/controllers/district.coffee
@@ -73,9 +73,15 @@
         ).on("mouseout", () ->
           return tooltip.style("visibility", "hidden")
         ).on("click", () ->
-          district_id = d3.select(this).text()
-          $scope.state_district = {state: district_id.slice(0, 2), district: district_id.slice(3, 6)}
-          $scope.$apply()
+          if !$scope.district_reps.length and $scope.zoomed
+            $scope.usMap.transition().duration(750).attr("transform", "translate(0,0)scale(1)").style "stroke-width", 1 + "px"
+            d3.select('#map_dialog').transition().duration(750).style("opacity", 1e-6)
+            $scope.state_district = {state: null, district: null}
+            $scope.district_reps = []
+          else
+            district_id = d3.select(this).text()
+            $scope.state_district = {state: district_id.slice(0, 2), district: district_id.slice(3, 6)}
+            $scope.$apply()
         )
         $scope.usMap.append("path").attr("class", "district-boundaries").attr("clip-path", "url(#clip-land)").datum(topojson.mesh(congress, congress.objects.districts, (a, b) ->
           (a.id / 1000 | 0) is (b.id / 1000 | 0)
@@ -173,7 +179,7 @@
     , true)

     $scope.$watch('district_reps', (newVals, oldVals) ->
-      if $scope.district_reps.length
+      if $scope.district_reps.length and $scope.state_district.state
         $scope.showDistrictDialog()
     , true)


sankey: better tooltip

sankey: better tooltip

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -475,9 +475,15 @@
       # update the node label
       @nodes.data(@node_list.models, (d) -> d.get('id'))
         .attr("data-tooltip", (d) =>
-          html = d.label()
-          html += '
'
-          html += Metric.autoscale_value d.value(), 'PJ', 2)
+          h = "
+            #{d.label()}: #{Metric.autoscale_value d.value(), 'PJ', 2}
+
"
+          for l in d.right_links
+            h += "-> #{l.right.label()}: #{Metric.autoscale_value l.value(), 'PJ', 2}
"
+          for l in d.left_links
+            h += "#{l.left.label()} <-: #{Metric.autoscale_value l.value(), 'PJ', 2}
"
+          h
+          )

       # move the rectangles
       @nodes.selectAll("rect")


Set focus to search field on index pages

--- app/assets/javascripts/global.js.coffee
+++ app/assets/javascripts/global.js.coffee
@@ -33,6 +33,10 @@
       $(element).hide()
   )

+@setFocusToField = (element_id) ->
+  val = $(element_id).val()
+  $(element_id).focus().val('').val(val)
+
 @globalReady = () ->
   initializeTypeahead()
   $("span[rel~=tooltip], button[rel~=tooltip]").tooltip( trigger: 'hover' )
@@ -42,6 +46,7 @@
   $("#global-search").typeahead(
     remote: root_url + 'search?q=%QUERY'
   )
+  setFocusToField("#search")

 @ready = () ->
   contourReady()


Fix typo

Fix typo

--- app/assets/javascripts/istart.js.coffee
+++ app/assets/javascripts/istart.js.coffee
@@ -33,8 +33,8 @@
       "fnStateLoad": (oSettings) ->
         JSON.parse localStorage.getItem('DataTables_'+window.location.pathname)
       "aoColumnDefs": [
-        { "bSortable": false, "aTargets": [ "sort-no" ] },,
-        { "sType": "string",  "aTargets": [ "sort-as-string" ] }
+        { "bSortable": false, "aTargets": [ "sort-no" ] },
+        { "sType": "string",  "aTargets": [ "sort-as-string" ] },
         { "sType": "numeric", "aTargets": [ "sort-as-numeric" ] },
         { "sType": "date",    "aTargets": [ "sort-as-date" ] },
         { "sType": "dom",     "aTargets": [ "sort-as-dom" ] }


Convenience method for grabbing the default source

--- models/customer.coffee
+++ models/customer.coffee
@@ -18,6 +18,9 @@
   requiresPaymentMethod: ->
     @requiresPayment() and @related().sources.length is 0

+  defaultSource: ->
+    @related().sources.get(@get 'default_source')
+
   related: ->
     return @__related__ if @__related__?


added comments

added comments

--- npm/src/brunch.coffee
+++ npm/src/brunch.coffee
@@ -128,6 +128,7 @@
   if file.match(/styl$/)
     exports.spawnStylus()

+# generate a list of paths containing all coffee files
 exports.generateSourcePaths = ->
   appSources = ['brunch/src/app/helpers/*.coffee',
     'brunch/src/app/models/*.coffee',
@@ -141,6 +142,7 @@
   sourcePaths.unshift('brunch/src/app/main.coffee')
   sourcePaths

+# spawns a new coffee process which merges all *.coffee files into on js file
 exports.spawnCoffee = (sourcePaths) ->
   coffeeParams = ['--output',
     'brunch/build/web/js',
@@ -159,16 +161,19 @@
     else
       util.log('there was a problem during .coffee to .js compilation. see above')

+# spawns a new docco process which generates documentation
 exports.spawnDocco = (sourcePaths) ->
   executeDocco = spawn('docco', sourcePaths)
   executeDocco.stderr.on 'data', (data) ->
     util.log data

+# spawns a new fusion compiling which merges all the templates into one namespace
 exports.spawnFusion = ->
   executeFusion = spawn 'fusion', ['--config', 'brunch/config/fusion/options.yaml','brunch/src/app/templates']
   executeFusion.stdout.on 'data', (data) ->
     util.log data

+# spawn a new stylus process which compiles main.styl
 exports.spawnStylus = ->
   executeStylus = spawn('stylus', ['--compress', '--out', 'brunch/build/web/css', 'brunch/src/app/styles/main.styl'])
   executeStylus.stdout.on 'data', (data) ->


minor: codestyling

minor: codestyling

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -74,7 +74,7 @@
 app.get '/Verify/:token'                        , require './handlers/verifytoken'
 app.get '/Hackathon/:section?'                  , require './handlers/hackathon'
 app.get '/humans.txt'                           , generateHumanstxt
-app.all '/:name/:section?/:slug?'                , require './handlers/main.coffee'
+app.all '/:name/:section?/:slug?'               , require './handlers/main.coffee'
 app.get '/'                                     , require './handlers/root.coffee'
 app.get '*'                                     , require './handlers/rest.coffee'


newkoding: endpoint for opting out of newkoding

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -238,6 +238,10 @@
       res.cookie 'clientId', response.replacementToken
       res.send 200, 'ok'

+app.post '/:name?/Optout', (req, res) ->
+  res.cookie 'useOldKoding', 'true'
+  res.redirect 301, '/'
+
 app.all "/:name?/Logout", (req, res)->
   if req.method is 'POST'
     res.clearCookie 'clientId'


Timeout for gravatar request

Timeout for gravatar request

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -599,8 +599,11 @@
   _request  =
     url     : _url
     headers : 'User-Agent': 'request'
+    timeout : 10000

   request _request, (err, response, body) ->
+
+    return res.status(400).send err.code  if err

     if body isnt 'User not found'
       try
@@ -609,8 +612,6 @@
         return res.status(400).send 'User not found'

       return res.status(200).send gravatar
-
-    res.status(400).send body

 app.get '/Hackathon/:section?', (req, res, next)->


Remove duplicate controller

Remove duplicate controller

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -76,6 +76,7 @@

         return originalWhen.call($routeProvider, path, route)

+    # Home
     $routeProvider.when("/",
         {
             templateUrl: "home/home.html",
@@ -89,6 +90,7 @@
         }
     )

+    # Discover
     $routeProvider.when("/discover",
         {
             templateUrl: "discover/discover-home/discover-home.html",
@@ -110,6 +112,7 @@
         }
     )

+    # My Projects
     $routeProvider.when("/projects/",
         {
             templateUrl: "projects/listing/projects-listing.html",
@@ -123,16 +126,7 @@
             controllerAs: "vm"
         }
     )
-
-    $routeProvider.when("/blocked-project/:pslug/",
-        {
-            templateUrl: "projects/project/blocked-project.html",
-            loader: true,
-            controller: "Project",
-            controllerAs: "vm"
-        }
-    )
-
+    # Project
     $routeProvider.when("/project/:pslug/",
         {
             templateUrl: "projects/project/project.html",
@@ -152,6 +146,7 @@
         }
     )

+    # Epics
     $routeProvider.when("/project/:pslug/epics",
     {
             section: "epics",
@@ -162,7 +157,6 @@
         }
     )

-    # Epics
     $routeProvider.when("/project/:pslug/epic/:epicref",
         {
             templateUrl: "epic/epic-detail.html",
@@ -485,6 +479,12 @@
     )

     # Errors/Exceptions
+    $routeProvider.when("/blocked-project/:pslug/",
+        {
+            templateUrl: "projects/project/blocked-project.html",
+            loader: true,
+        }
+    )
     $routeProvider.when("/error",
         {templateUrl: "error/error.html"})
     $routeProvider.when("/not-found",


Fix grammar in template name_spec.coffee

--- app/templates/src/test/name_spec.coffee
+++ app/templates/src/test/name_spec.coffee
@@ -71,7 +71,7 @@

       voice = null

-      Given "I am a awesome singing", ->
+      Given "I am an awesome singer", ->
       When "I sing", ->
         voice = <%= slugname %>.awesome()
       Then "it should sound awesome", ->


handling "Newline message" (issue #1)

handling "Newline message" (issue #1)

--- lib/parse.coffee
+++ lib/parse.coffee
@@ -54,14 +54,14 @@
 		file.lines.push {type:'add', add:true, ln:ln_add++, content:line}
 		file.additions++

-	# todo end of line
+	noeol = '\\ No newline at end of file'
 	normal = (line) ->
 		return if not file
 		file.lines.push {
 			type: 'normal'
 			normal: true
-			ln1: ln_del++
-			ln2: ln_add++
+			ln1: ln_del++ unless noeol
+			ln2: ln_add++ unless noeol
 			content: line
 		}

@@ -86,7 +86,7 @@
 		return false

 	for line in lines
-		normal(line) if not parse line
+		normal(line) unless parse line

 	return files


Fix alias unpacking bug in queries with $ne operator

--- lib/query.coffee
+++ lib/query.coffee
@@ -95,10 +95,12 @@
     else
       conditions = permute(k, v, aliases)
       if conditions.length > 1
-        if new_query['$and']?
+        new_query['$and'] ?= []
+        if v?['$ne']?
+          for c in conditions
+            new_query['$and'].push(c)
+        else
           new_query['$and'].push({'$or' : conditions})
-        else
-          new_query['$and'] = [{'$or' : conditions}]
       else
         common.extend(new_query, conditions[0])


video changed

video changed

--- app/controllers/video_controller.coffee
+++ app/controllers/video_controller.coffee
@@ -12,7 +12,3 @@
       $scope.resUrl = videoUrl.replace('watch?v=','embed/')
       $scope.resUrl.replace('&index=18&', '?')
   return
-
-  #https://www.youtube.com/watch?v=OnuuYcqhzCE&index=18&list=PLBiPNxqFKPZI948_8dohmZxxjvJxYIXSx
-
-  #https://www.youtube.com/embed/OnuuYcqhzCE?list=PLBiPNxqFKPZI948_8dohmZxxjvJxYIXSx


Update the order of placeholders on initialize

--- app/initialize.coffee
+++ app/initialize.coffee
@@ -2,27 +2,30 @@
 App = require 'app'

-# ===== Templates =====
-require 'templates/application'
-require 'templates/index'
-
-
-# ===== Models =====
-
-
-# ===== Controllers =====
-
-
-# ===== Views =====
-
-
-# ===== Handlebars Helpers =====
+# ===== Router =====
+App.Router.map ->
+    # put your routes here

 # ===== Routes =====
 require 'routes/IndexRoute'

-# ===== Router =====
-App.Router.map ->
-    # put your routes here
+# ===== Store =====
+
+
+# ===== Models =====
+
+
+# ===== Views =====
+
+
+# ===== Controllers =====
+
+
+# ===== Template Helpers =====
+
+
+# ===== Templates =====
+require 'templates/application'
+require 'templates/index'


tap action double bind bug fix

tap action double bind bug fix

--- JSKit/02_JSView.coffee
+++ JSKit/02_JSView.coffee
@@ -91,18 +91,6 @@
                 $(@_viewSelector).css("overflow", "normal")

     setUserInteractionEnabled:(@_userInteractionEnabled)->
-        if (@_userInteractionEnabled == true)
-            $(@_viewSelector).unbind("click").bind "click", (event) =>
-                if (@_tapAction? && @_alpha > 0.0)
-                    @_tapAction(@_self, event)
-                    event.stopPropagation()
-            $(@_viewSelector).unbind("dblclick").bind "dblclick", (event) =>
-                if (@_tapAction2? && @_alpha > 0.0)
-                    @_tapAction2(@_self, event)
-                    event.stopPropagation()
-        else
-            $(@_viewSelector).unbind("click")
-            $(@_viewSelector).unbind("dblclick")

     disableDragObject:(object)->
         if ($(@_viewSelector).length)
@@ -244,12 +232,14 @@

         $(@_viewSelector).css("cursor", "pointer")
         if (tapnum == 1)
+            $(@_viewSelector).unbind("tap")
             $(@_viewSelector).on 'tap', (e)=>
                 if (@_tapAction? && @_userInteractionEnabled == true && @_alpha > 0.0 && @_hidden == false)
                     @_tapAction(@_self, e)
                     e.stopPropagation()

         if (tapnum == 2)
+            $(@_viewSelector).unbind("doubletap")
             $(@_viewSelector).on 'doubletap', (e)=>
                 if (@_tapAction2? && @_userInteractionEnabled == true && @_alpha > 0.0 && @_hidden == false)
                     @_tapAction2(@_self, e)


change color

change color

--- graphics/001-terrain-gen/viewer.coffee
+++ graphics/001-terrain-gen/viewer.coffee
@@ -16,7 +16,7 @@
     color = (height) ->
         r = 128
         b = 192
-        r = Math.floor(height / 48) if height > 8192
+        r = Math.floor(height / 32) if height > 8192
         r = 255 if r > 255
         b = Math.floor(320 - height / 64) if height < 8192
         b = 255 if b > 255


Import ITUNES_LINK

Import ITUNES_LINK

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -16,6 +16,7 @@
   BLOG_URL
   ADMIN_SLUGS
   IOS_APP_ID
+  ITUNES_LINK
 } = require '../config'

 express = require 'express'


Only change sidebar selection if there is a matching link

--- app/scripts/routes/notifications.coffee
+++ app/scripts/routes/notifications.coffee
@@ -36,5 +36,6 @@

   # FIXME: total hack, but can't think of a better way to do it
   selectItem: ->
-    $('#lists').find('.selected').removeClass('selected')
-    $('#lists').find("a[href='#{window.location.hash}']").addClass('selected')
+    if (item = $('#lists').find("a[href='#{window.location.hash}']")).length
+      $('#lists').find('.selected').removeClass('selected')
+      item.addClass('selected')


[fix] JamendoSync: returns musicinfo including tags

--- app/scripts/syncs/jamendo_tracks.coffee
+++ app/scripts/syncs/jamendo_tracks.coffee
@@ -15,6 +15,7 @@
       client_id: settings.keys.jamendo.client_id
       format: 'json'
       limit: 10
+      include: 'musicinfo'
       # lang: 'en'
       # order: undefined
       # speed: undefined


Simplify code.

Simplify code.

Also: Rename to mapView for clarity.

--- src/map-printer.coffee
+++ src/map-printer.coffee
@@ -22,25 +22,19 @@
         leafletImage = leafletImageIe

     class SMPrinter
-        constructor: (@map) ->
-            @makingPrint
-            @printed
-            # Add print event listeners
-            ((printMap, afterPrint) ->
-                # webkit
-                if window.matchMedia
-                    mediaQueryList = window.matchMedia('print')
-                    mediaQueryList.addListener (mql) ->
-                        if mql.matches
-                            printMap()
-                        else
-                            afterPrint()
+        constructor: (@mapView) ->
+            # Webkit
+            if window.matchMedia
+                mediaQueryList = window.matchMedia 'print'
+                mediaQueryList.addListener (mql) =>
+                    if mql.matches
+                        @printMap()
+                    else
+                        @afterPrint()

-                # IE + FF
-                window.onbeforeprint = () -> printMap();
-                window.onafterprint = () -> afterPrint()
-
-            )(@printMap, @afterPrint)
+            # IE + FF
+            window.onbeforeprint = => @printMap()
+            window.onafterprint = => @afterPrint()

         printMap: (notOnBeforePrint) =>
             if !notOnBeforePrint and !document.getElementById('map-as-png')
@@ -52,8 +46,8 @@
             @makingPrint = true
             @printed = false

-            map = @map.map
-            markers = @map.allMarkers._featureGroup._layers
+            map = @mapView.map
+            markers = @mapView.allMarkers._featureGroup._layers

             listOfUnits = document.createElement('div')
             listOfUnits.id = PRINT_LEGEND_ELEMENT_ID;


mochacompat: Pass HTTP server

mochacompat: Pass HTTP server

--- src/mochacompat.coffee
+++ src/mochacompat.coffee
@@ -322,14 +322,13 @@
     currentTest: null
     graph: null
     specs: specs
-
   httpServer = new http.Server
   runtime = websocket httpServer, {}
   runtime.receive = (protocol, command, payload, context) ->
     handleFbpCommand state, runtime, mocha, specs, protocol, command, payload, context

   httpServer.listen options.port, (err) ->
-    return callback err, state
+    return callback err, state, httpServer

 exports.main = main = () ->


trying to work on reset changing direction oddly

--- web/scripts/gameManager.coffee
+++ web/scripts/gameManager.coffee
@@ -1,6 +1,7 @@
 debugging = true
 log = (mesg) ->  console.log mesg if debugging
-
+if not deepcopy?
+    deepcopy = (src) -> $.extend(true, {},src)

 class window.GameManager
     constructor: (@environment) ->
@@ -78,6 +79,8 @@

     reset: =>
         @codeEditor.resetEditor()
+        @gameState = new MapGameState this, @visual, @config.visual.characters
+        @commandMap = new MapGameCommands @gameState
         @visual.startGame @config.visual
         return

@@ -155,7 +158,7 @@
     #       v
     constructor: (@gameManager, @gameVisual, characterLoadconfig) ->
         # @config ?= { x: 4, y: 4, direction: 0, maxX:9, maxY:9, traps: [[2,4],[9,9]], targets: [[5,5]], targetCount : 0}
-        @config = @gameManager.config.game
+        @config = deepcopy @gameManager.config.game
         @score = 0
         @stars = 0
         @protagonist = {
@@ -188,14 +191,16 @@
         return

     turnRight: ->
-        @turn (@protagonist.dir + 1) % 4
+        @turn ((@protagonist.dir + 1) % 4)
         return

     turnLeft: ->
-        @turn (@protagonist.dir + 3) % 4
+        @turn ((@protagonist.dir + 3) % 4)
         return

     turn: (dir) ->
+        if dir == 4
+            alert "Danger, Mr Robinson Dir is 4!"
         @protagonist.dir = dir
         @gameVisual.charFace @protagonist.index, @protagonist.dir
         return


Update spec to match its description

Update spec to match its description

--- spec/workspace-view-spec.coffee
+++ spec/workspace-view-spec.coffee
@@ -237,10 +237,22 @@
   describe "core:close", ->
     it "closes the active pane item until all that remains is a single empty pane", ->
       atom.config.set('core.destroyEmptyPanes', true)
-      atom.project.openSync('../sample.txt')
+
+      paneView1 = atom.workspaceView.getActivePaneView()
+      editorView = atom.workspaceView.getActiveView()
+      editorView.splitRight()
+      paneView2 = atom.workspaceView.getActivePaneView()
+
+      expect(paneView1).not.toBe paneView2
+      expect(atom.workspaceView.getPanes()).toHaveLength 2
+      atom.workspaceView.trigger('core:close')
+
       expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1
+      expect(atom.workspaceView.getPanes()).toHaveLength 1
       atom.workspaceView.trigger('core:close')
+
       expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 0
+      expect(atom.workspaceView.getPanes()).toHaveLength 1

   describe "the scrollbar visibility class", ->
     it "has a class based on the style of the scrollbar", ->


fix typo

fix typo

--- src/openpgp/armor.iced
+++ src/openpgp/armor.iced
@@ -29,7 +29,7 @@
         if @ret.clearsign then mt.clearsign
         else throw new Error "Signed message, but not clear-signed"
       when "MESSAGE" then mt.generic
-      else throw new Error "Unknown messasge type: #{@type}"
+      else throw new Error "Unknown message type: #{@type}"
     @ret.fields.type = @type

 #=========================================================================


#1700 - fixes failing unit tests

#1700 - fixes failing unit tests

--- jsapp/test/unit/SkipLogic.Tests.coffee
+++ jsapp/test/unit/SkipLogic.Tests.coffee
@@ -705,6 +705,7 @@
       _builder.survey =
         on: sinon.spy()
         off: sinon.spy()
+      _builder.current_question = questions: 'test question'
       _view = sinon.stubObject $vRdsl.SkipLogicCriterionBuilderView
       _delimiter_spy =
         show: sinon.spy()


doesnt work

doesnt work

--- nodeko.coffee
+++ nodeko.coffee
@@ -2,7 +2,7 @@

 get '*', (path) ->
   host: @headers.host
-  if host == 'nodeknockout.com' or host == 'nodeknockout.heroku.com'
+  if false # host == 'nodeknockout.com' or host == 'nodeknockout.heroku.com'
     @redirect 'www.nodeknockout.com' + path
   else
     @pass path


improve column breakpoints

improve column breakpoints

--- imports/ui/pages/vessels/vessels.coffee
+++ imports/ui/pages/vessels/vessels.coffee
@@ -20,16 +20,16 @@
 				{ name: '_id', title: '', breakpoints: '', visible: false, filterable: false }
 				{ name: 'id', title: '#', breakpoints: '', filterable: false }
 				{ name: 'name', title: TAPi18n.__('vessels.name'), breakpoints: '' }
-				{ name: 'flag', title: TAPi18n.__('vessels.flag'), breakpoints: 'xs', filterable: false }
-				{ name: 'type', title: TAPi18n.__('vessels.type'), breakpoints: 'xs', filterable: false }
+				{ name: 'flag', title: TAPi18n.__('vessels.flag'), breakpoints: 'xs sm', filterable: false }
+				{ name: 'type', title: TAPi18n.__('vessels.type'), breakpoints: 'xs sm', filterable: false }
 				{ name: 'callsign', title: TAPi18n.__('vessels.callsign'), breakpoints: '' }
 				{ name: 'eni', title: TAPi18n.__('vessels.eni'), breakpoints: 'xs' }
 				{ name: 'imo', title: TAPi18n.__('vessels.imo'), breakpoints: 'xs' }
 				{ name: 'mmsi', title: TAPi18n.__('vessels.mmsi'), breakpoints: 'xs' }
-				{ name: 'lastVisit', title: TAPi18n.__('vessels.lastVisit'), breakpoints: 'xs', filterable: false }
-				{ name: 'harborGroup', title: TAPi18n.__('vessels.harborGroup'), breakpoints: 'xs', filterable: false }
-				{ name: 'nextVisit', title: TAPi18n.__('vessels.nextVisit'), breakpoints: 'xs', filterable: false }
-				{ name: 'languages', title: TAPi18n.__('vessels.languages'), breakpoints: 'xs', filterable: false }
+				{ name: 'lastVisit', title: TAPi18n.__('vessels.lastVisit'), breakpoints: 'all', filterable: false }
+				{ name: 'harborGroup', title: TAPi18n.__('vessels.harborGroup'), breakpoints: 'all', filterable: false }
+				{ name: 'nextVisit', title: TAPi18n.__('vessels.nextVisit'), breakpoints: 'all', filterable: false }
+				{ name: 'languages', title: TAPi18n.__('vessels.languages'), breakpoints: 'all', filterable: false }
 				{ name: 'comments', title: TAPi18n.__('vessels.comments'), breakpoints: 'all', filterable: false }
 			]


MachineSettingsModal: add Run/Edit Init Script, Open Terminal buttons

--- client/Main/machinesettingsmodal.coffee
+++ client/Main/machinesettingsmodal.coffee
@@ -113,7 +113,6 @@
       Terminated, NotInitialized, Building, Terminating
     ]

-
     @addSubView new KDCustomHTMLView
       cssClass : 'modal-arrow'
       position :
@@ -126,14 +125,23 @@
     label.setClass 'advanced'

     advanced.addSubView new KDButtonView
+      style    : 'solid compact green'
+      title    : 'Run Init Script'
+      callback : -> ComputeController.runInitScript machine
+
+    advanced.addSubView new KDButtonView
+      style    : 'solid compact'
+      title    : 'Edit Init Script'
+      callback : -> ComputeController.UI.showBuildScriptEditorModal machine
+
+    advanced.addSubView new KDButtonView
       style    : 'solid compact red'
       title    : 'Terminate VM'
-      callback : => KD.singletons.computeController.destroy @machine
-
-    advanced.addSubView @deleteButton = new KDButtonView
-      style    : 'solid compact red'
-      title    : 'Delete VM'
-      callback : @bound 'deleteVM'
+      callback : -> KD.singletons.computeController.destroy machine
+
+    advanced.addSubView new CustomLinkView
+      title    : "Open a Terminal for this machine"
+      click    : -> new TerminalModal { machine }

     label.on 'click', @bound 'toggleAdvancedSettings'


Marker keeps a copy of the problem.

Marker keeps a copy of the problem.

We'll use that for displaying information on hover, or when the caret is
present.

--- lib/analysis/analysis_decorator.coffee
+++ lib/analysis/analysis_decorator.coffee
@@ -31,6 +31,7 @@
       marker.setProperties
         isDartMarker: true
         isProblem: true
+        problem: problem

       editor.decorateMarker marker,
         type: 'highlight',


tweak pagination style

tweak pagination style

--- lib/carte/client/views/pagination.cjsx
+++ lib/carte/client/views/pagination.cjsx
@@ -28,7 +28,7 @@

         }

-

+

         {
           if @props.cards.page


Show slider without handle on dependent nodes

--- src/code/views/node-view.coffee
+++ src/code/views/node-view.coffee
@@ -164,13 +164,13 @@
     )

   renderSliderView: ->
-    if not @props.data.canEditInitialValue() then return null
     enabled = not @props.running or @props.data.canEditValueWhileRunning()
     (SliderView
       horizontal: false
       filled: true
       height: 44
       width: 15
+      showHandle: @props.data.canEditInitialValue()
       showLabels: false
       onValueChange: @changeValue
       value: @props.data.initialValue


removing octal literals, where found. (they don't work in strict more or CoffeeScript 1.3.x)

--- kites/sharedHosting/createtmpdir.coffee
+++ kites/sharedHosting/createtmpdir.coffee
@@ -9,7 +9,7 @@
   tmpDir = "#{config.usersPath}#{username}/.tmp"
   fs.stat tmpDir, (err, stat)->
     if err
-      fs.mkdir tmpDir, 0755, (err)->
+      fs.mkdir tmpDir, 0o0755, (err)->
         if err
           callback err
           log.error err


fix that clipped layers are flattened

fix that clipped layers are flattened

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -838,7 +838,7 @@
 	ancestors.unshift(layer) if includeLayer

 	for ancestor in ancestors
-		point.z = 0 if ancestor.flat
+		point.z = 0 if ancestor.flat or ancestor.clip
 		point = ancestor.matrix3d.point(point)
 		point.z = 0 unless ancestor.parent


Remove extraneous syntax

Remove extraneous syntax

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -48,12 +48,12 @@
   forwardLabelClick: -> @focus()

   render: ->
-    classes = classnames('form-control', @props.class,
-      {empty: not @props.default}
-    )
-    wrapperClasses = classnames('form-control-wrapper', 'tutor-input', @props.className, {
-      'is-required': @props.required, 'has-error': @state.errors?.length
-    })
+    classes = classnames 'form-control', @props.class,
+      empty: not @props.default
+
+    wrapperClasses = classnames 'form-control-wrapper', 'tutor-input', @props.className,
+      'is-required': @props.required
+      'has-error': @state.errors?.length

     errors = _.map(@state.errors, (error) ->
       return unless TutorErrors[error]?
@@ -145,14 +145,12 @@
     @setState({hasFocus: false})

   render: ->
-    classes = classnames('form-control', {
-      empty: not @props.value and not @state.hasFocus
-    })
-
-    wrapperClasses = classnames(
-      'form-control-wrapper', 'tutor-input', '-tutor-date-input', @props.className, {
-        'is-required': @props.required, 'has-error': @state.errors?.length
-    })
+    classes = classnames 'form-control',
+      empty: (not @props.value and not @state.hasFocus)
+
+    wrapperClasses = classnames 'form-control-wrapper', 'tutor-input', '-tutor-date-input', @props.className,
+      'is-required': @props.required
+      'has-error': @state.errors?.length

     now = TimeStore.getNow()
     value = @props.value
@@ -222,12 +220,11 @@
   forwardLabelClick: -> @focus()

   render: ->
-    classes = classnames('form-control', @props.inputClass, {
+    classes = classnames 'form-control', @props.inputClass,
       empty: not @props.default
-    })
-    wrapperClasses = classnames("form-control-wrapper", "tutor-input", @props.className, {
+
+    wrapperClasses = classnames "form-control-wrapper", "tutor-input", @props.className,
       'is-required': @props.required
-    })

     <div className={wrapperClasses}>
       <textarea


Added redirect for Mobile job post.

Added redirect for Mobile job post.

--- lib/routers/hardcoded_redirects.coffee
+++ lib/routers/hardcoded_redirects.coffee
@@ -27,6 +27,7 @@
   '/home/featured%20works': '/tag/apple/artworks'
   '/dev': '/inquiry/development'
   '/artist': '/artists'
+  '/job/mobile-engineer': '/article/artsy-jobs-mobile-engineer'

 for from, path of redirects
   router.get from, to(path)


fixed issue with nav..hopefully

fixed issue with nav..hopefully

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -41,7 +41,7 @@
 	)

-$('li').on('click', '.new-issue' , ( event ) ->
+$('li').on('click', '.new-issue' ,  ->
 	cur_location = navigator.geolocation.getCurrentPosition(
 		console.log('hi1')
 		(pos) ->


OX-177 Removed brainflight.coffee

OX-177 Removed brainflight.coffee

--- app/assets/javascripts/brainflight.coffee
+++ app/assets/javascripts/brainflight.coffee
@@ -1,14 +0,0 @@
-MVC = null
-
-require.config
-  baseUrl : "/assets/javascripts"
-  locale  : "de-de"
-  shim :
-    routes :
-      exports : "jsRoutes"
-
-require [ "core_ext" ], ->
-  require ["controller", "view", "model"], (Controller, View, Model) ->
-
-    controller = new Controller()
-    MVC = { controller, View, Model }


Register event for main nav explicitly

Register event for main nav explicitly

--- app/assets/javascripts/masthead.js.coffee
+++ app/assets/javascripts/masthead.js.coffee
@@ -9,6 +9,9 @@
     setNavHeight();
   $("#main-nav button").click ->
     hideSearch()
+
+  # Register main nav as a Bootstrap dropdown
+  $('#main-nav [data-toggle=dropdown]').dropdown()

   $mastheadSearch = $("#masthead-search")


Update save all

Update save all

--- app/assets/javascripts/models/base.coffee
+++ app/assets/javascripts/models/base.coffee
@@ -8,14 +8,17 @@
     # TODO fix the naive inflection
     collection = @collection_from_page(@snake_name)
     added_attrs = []
+
     $.each collection, (i, attrs) ->
-      added_attrs.push($.extend attrs, opts.add_data_to_each())
+      added_attrs.push($.extend attrs, opts.add_data_to_each)

     data[@snake_name + "s"] = added_attrs

+    path = @route + "/save_all.json"
+
     $.ajax
       type: "PATCH"
-      url: @route + "/save_all"
+      url: App.BaseUrl + "/" + path
       data: data

       success: (data, textStatus, xhr) =>
@@ -25,7 +28,8 @@
           model.assign_attributes(attrs)
           model._clear_errors()
           model._update_data_vals_on_page()
-          model.mark_dirty_or_clean()
+          # TODO in demo app
+          # model.mark_dirty_or_clean()
       error: (xhr) =>
         data = JSON.parse(xhr.responseText)
         $(data).each (i, response_object) =>


delete unused file

delete unused file

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


port front end JS to use datatables 1.10

--- app/assets/javascripts/variants.js.coffee
+++ app/assets/javascripts/variants.js.coffee
@@ -1,32 +1,35 @@
 jQuery ->
-  variantsTable = $('#variants').dataTable
-    sPaginationType: "bootstrap"
-    bJQueryUI: true
-    bProcessing: true
-    bServerSide: true
-    sAjaxSource: $('#variants').data('source')
-    iDisplayLength: 50
-    fnServerParams: (aoData) ->
-      params = {}
-      $(".filter-group").each (index) ->
-        name = $(this).attr("id")
-        val = $(this).val()
-        aoData.push { name: name , value: val  }
-        params[name] = val
-
-      $(".range-group").each (index) ->
-        $(this).children("input").each (index) ->
+  variantsTable = $('#variants').DataTable
+    pagingType: "bootstrap"
+    jQueryUI: true
+    processing: true
+    serverSide: true
+    ajax:
+      url: $('#variants').data('source')
+      type: 'POST'
+      data: (data) ->
+        params = {}
+        $(".filter-group").each (index) ->
           name = $(this).attr("id")
           val = $(this).val()
-          aoData.push { name: name , value: val  }
+          data[name] = val
           params[name] = val

-      queryString = $.param(params)
-      $("#vcf-export-button").attr("href", "/api/v1/variants.vcf?" + queryString)
-      $("#tsv-export-button").attr("href", "/api/v1/variants.tsv?" + queryString)
+        $(".range-group").each (index) ->
+          $(this).children("input").each (index) ->
+            name = $(this).attr("id")
+            val = $(this).val()
+            data[name] = val
+            params[name] = val

+        queryString = $.param(params)
+        $("#vcf-export-button").attr("href", "/api/v1/variants.vcf?" + queryString)
+        $("#tsv-export-button").attr("href", "/api/v1/variants.tsv?" + queryString)
+
+        data
+    pageLength: 50

   $(".chosen-select").chosen
     no_results_text: 'Sorry, nothing matches your query.'
   $("#update-grid-button").click (e) ->
-    variantsTable.fnReloadAjax()
+    variantsTable.ajax.reload()


Add Small Point and Image Invert to Dev Classifier

--- app/pages/dev-classifier/mock-data.coffee
+++ app/pages/dev-classifier/mock-data.coffee
@@ -31,6 +31,7 @@
     enable_subject_flags: true
     multi_image_mode: 'flipbook_and_separate'
     multi_image_layout: 'grid3'
+    invert_subject: true

   first_task: 'init'

@@ -112,6 +113,7 @@
       '''
       tools: [
         {type: 'point', label: 'Point', color: 'red', min: 1, max: 2}
+        {type: 'point', label: 'Small Point', color: 'coral', size: 'small'}
         {type: 'line', label: 'Line', color: 'yellow', min: 0}
         {type: 'rectangle', label: 'Rectangle', color: 'lime', max: 2}
         {type: 'polygon', label: 'Polygon', color: 'cyan', details: MISC_DRAWING_DETAILS}


Game Dev 2 and 3 tournament exception given to students and teachers. (#4983)

--- app/views/play/level/PlayLevelView.coffee
+++ app/views/play/level/PlayLevelView.coffee
@@ -172,7 +172,9 @@
       not e.level.isType('course-ladder')

       # TODO: Add a general way for standalone levels to be accessed by students, teachers
-      e.level.get('slug') isnt 'peasants-and-munchkins'
+      e.level.get('slug') not in ['peasants-and-munchkins',
+                                  'game-dev-2-tournament-project',
+                                  'game-dev-3-tournament-project']
     ])
       return _.defer -> application.router.redirectHome()


Relate the artwork and fair to the layer

--- apps/artwork/client/layered-search.coffee
+++ apps/artwork/client/layered-search.coffee
@@ -34,14 +34,14 @@
     'click .layered-search-layer-button' : 'selectLayer'

   initialize: (options = {}) ->
-    { @artwork } = options
+    { @artwork, @fair } = options
     @setupLayers()

   setupLayers: ->
-    @layers = new Layers artworkId: @artwork.id
+    @layers = new Layers artwork: @artwork, fair: @fair
     @layers.fetch
       success:  => @render()
-      errro:    => @remove()
+      error:    => @remove()

   selectLayer: (e) ->
     id = ($target = $(e.currentTarget)).data 'id'


On/Off for audio seems to work. Hard to test on one computer !

--- src/frontend/connections.coffee
+++ src/frontend/connections.coffee
@@ -166,6 +166,18 @@
             do @enableCamera

     toggleMicro : () =>
+        @userMedia['audio'] = !@userMedia['audio']
+        do ($ '#feeds > li:first > video:first').remove
+        if @localStream isnt undefined
+            do @localStream.stop
+            console.log @peers
+            for id, peer of @peers
+                peer.rmLocalStream @localStream
+                do peer.offerHandshake
+        @localStream = undefined
+        if @userMedia['video'] is yes or @userMedia['audio'] is yes
+            console.log "On Enable la camera"
+            do @enableCamera

     dance : () =>
         @ws.dance @wsPortal


* benchmark add/update

* benchmark add/update

--- benchmark.coffee
+++ benchmark.coffee
@@ -10,18 +10,25 @@
   run()
   diff = process.hrtime(start)
   em = process.memoryUsage()
-  usedMem = em.rss - sm.rss
-  heapTotal = em.heapTotal - sm.heapTotal
-  heapUsed = em.heapUsed - sm.heapUsed
-  console.log("Memory Used: " + (usedMem/1024) + "KB")
-  console.log("Heap Total: " + (heapTotal/1024) + "KB")
-  console.log("Heap Used: " + (heapUsed/1024) + "KB")
-  console.log("Time Cost: " + (diff[0]*1e3+diff[1]/1e6) + " ms\n")
+  usedMem = (em.rss - sm.rss) / 1024
+  heapTotal = (em.heapTotal - sm.heapTotal) / 1024
+  heapUsed = (em.heapUsed - sm.heapUsed) / 1024
+  time = diff[0]*1e3 + diff[1]/1e6
+  console.log("Memory Used: " + (usedMem) + "KB")
+  console.log("Heap Total: " + (heapTotal) + "KB")
+  console.log("Heap Used: " + (heapUsed) + "KB")
+  console.log("Time Cost: " + (time) + " ms\n")

 console.log("fixed Cache:")

 benchmark ->
-  console.log("------set benchmark---------")
+  console.log("------add benchmark---------")
+  for i in [0...maxCap]
+    cache.setFixed('test' + i, i)
+  return
+
+benchmark ->
+  console.log("------update benchmark---------")
   for i in [0...maxCap]
     cache.setFixed('test' + i, i)
   return
@@ -46,7 +53,13 @@
 console.log("LRU Cache:")

 benchmark ->
-  console.log("------set benchmark---------")
+  console.log("------add benchmark---------")
+  for i in [0...maxCap]
+    lruCache.set('test' + i, i)
+  return
+
+benchmark ->
+  console.log("------update benchmark---------")
   for i in [0...maxCap]
     lruCache.set('test' + i, i)
   return


Changed output based on sh

Changed output based on sh

--- lib/build.coffee
+++ lib/build.coffee
@@ -95,7 +95,7 @@
     @child.stdout.on 'data', @buildView.append
     @child.stderr.on 'data', @buildView.append
     @child.on 'error', (err) =>
-      @buildView.append 'Unable to execute: ' + cmd.exec
+      @buildView.append (if cmd.sh then 'Unable to execute with sh: ' else 'Unable to execute: ')  + cmd.exec
       @buildView.append '`cmd` cannot contain space. Use `args` for arguments.' if /\s/.test(cmd.exec)

     @child.on 'close', (exitCode) =>
@@ -104,7 +104,7 @@
       @child = null

     @buildView.buildStarted()
-    @buildView.append 'Executing: ' + cmd.exec + [' '].concat(args).join(' ')
+    @buildView.append (if cmd.sh then 'Executing with sh: ' else 'Executing: ') + cmd.exec + [' '].concat(args).join(' ')

   abort: (cb) ->
     @child.removeAllListeners 'close'


Remove AltKey combination.

Remove AltKey combination.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -763,7 +763,7 @@

   dragEnter: (event) ->

-    return  if @splitRegions or not event.altKey
+    return  if @splitRegions

     @tabView.addSubView @splitRegions = new SplitRegionView


Update locals variable in order to fix a bug with single view template (#14)

--- lib/index.coffee
+++ lib/index.coffee
@@ -38,7 +38,7 @@

   W.map posts, (p) =>
     tpl = path.join(@roots.root, config.template)
-    locals   = _.merge(@roots.config.locals, post: p)
+    locals   = _.merge({}, @roots.config.locals, post: p)
     output = "#{type}/#{p.slug}.html"
     compiler = _.find @roots.config.compilers, (c) ->
       _.contains(c.extensions, path.extname(tpl).substring(1))


fix refefences clobbering

fix refefences clobbering

only update models from references in a patch-doc message when it is
requried, otherwise existing models can get erroneously overwritten

--- bokehjs/src/coffee/common/document.coffee
+++ bokehjs/src/coffee/common/document.coffee
@@ -307,7 +307,8 @@

     # this first pass removes all 'refs' replacing them with real instances
     foreach_depth_first to_update, (instance, attrs, was_new) ->
-      instance.set(attrs)
+      if was_new
+        instance.set(attrs)

     # after removing all the refs, we can run the initialize code safely
     foreach_depth_first to_update, (instance, attrs, was_new) ->


Update Bing bot to generate word instead of getting from website

--- bots/Bing.coffee
+++ bots/Bing.coffee
@@ -13,6 +13,7 @@
     userAgent:'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.58 Safari/537.22'
   verbose:false; logLevel:'debug' # output logs at specified level
 )
+CONSONANTS='bcdfghjklmnpqrstvwxyz'; VOWELS='aeiou'
 ARGS=[]; DASHBOARD='http://www.bing.com/rewards/dashboard'
 cliCount=0; offers=[]; executed=0; facebookLogin=false
 CLI=[{c:0,n:'email',     d:''},{c:1,n:'password',d:''},
@@ -67,12 +68,14 @@

 casper.then execQueries = ->
   @echo "Executing #{ARGS[2]} quer#{if ARGS[2] is 1 then 'y' else 'ies'}." # tell user how many queries are being executed
-  @repeat ARGS[2], execQuery = -> # repeat `queryCount` times
+  @repeat ARGS[2], execQuery = -> # repeat `queryCount` tiems
     @wait Math.floor(Math.random() * ((ARGS[4] - ARGS[3]) * 1000 + 1)) + (ARGS[3] * 1000), -> # wait between queries
-      @thenOpen 'http://randomword.setgetgo.com/get.php', execQuery = -> # visit site to retrieve random word
-        if not @exists 'body > *' # if body does not have any children (check to make sure the site is correct)
-          word = @fetchText('body').trim() # get the random word (without excess whitespace)
-          @thenOpen ("http://www.bing.com/search?q=#{word}"), -> @echo "#{++executed}) #{word}" # query bing with the word
+      word = ''; len = Math.floor(Math.random()*5)+5
+      for i in [1..len] by 2 # generate a random word (length of 5..9 letters)
+        word += CONSONANTS[Math.floor(Math.random()*CONSONANTS.length)] # add a consonant
+        if i<len then word += VOWELS[Math.floor(Math.random()*VOWELS.length)] # add a vowel if there is room
+      word = word[0].toUpperCase() + word[1..-1] # capitalize the first letter of the word
+      @thenOpen ("http://www.bing.com/search?q=#{word}"), -> @echo "#{++executed}) #{word}" # query bing with the word

 casper.thenOpen DASHBOARD, getTotalPoints = -> # get the number of unused points on the account
   totalPoints = @evaluate -> document.querySelector('#user-status .user-balance .data-value-text').innerText


Warns you if hipchat keys aren't set

Warns you if hipchat keys aren't set

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -78,15 +78,21 @@
   getKeysFromConfig: ->
     @app_key = atom.config.get 'pusher_app_key'
     @app_secret = atom.config.get 'pusher_app_secret'
+    @hc_key = atom.config.get 'hipchat_token'
+    @room_id = atom.config.get 'hipchat_room_id'

-  missingKeys: ->
+  missingPusherKeys: ->
     _.any([@app_key, @app_secret], (key) ->
+      typeof(key) is "undefined")
+
+  missingHipChatKeys: ->
+    _.any([@hc_key, @room_id], (key) ->
       typeof(key) is "undefined")

   startSession: ->
     @getKeysFromConfig()

-    if @missingKeys()
+    if @missingPusherKeys()
       alertView = new AlertView "Please set your Pusher keys."
       atom.workspace.addModalPanel(item: alertView, visible: true)
     else
@@ -99,8 +105,11 @@
   inviteOverHipChat: ->
     @getKeysFromConfig()

-    if @missingKeys()
-      alertView = new AlertView
+    if @missingPusherKeys()
+      alertView = new AlertView "Please set your Pusher keys."
+      atom.workspace.addModalPanel(item: alertView, visible: true)
+    else if @missingHipChatKeys()
+      alertView = new AlertView "Please set your HipChat keys."
       atom.workspace.addModalPanel(item: alertView, visible: true)
     else
       inviteView = new InputView("Please enter the HipChat mention name of your pair partner:")
@@ -112,15 +121,13 @@

   sendHipChatMessageTo: (mentionName) ->

-    hc_key = atom.config.get 'hipchat_token'
-    room_id = atom.config.get 'hipchat_room_id'

-    hc_client = new HipChat(hc_key)
+    hc_client = new HipChat(@hc_key)

     @sessionId = "#{@app_key}-#{@app_secret}-#{randomstring.generate(11)}"

     params = {
-      room: room_id,
+      room: @room_id,
       from: 'PusherPair',
       message: "Hello there #{mentionName}. Somebody really really wants to pair with you. Go onto Atom, and if you've installed the PusherPair plugin, hit 'Join a pairing session', and enter this string: #{@sessionId}",
       message_format: 'text'


Alerts when your pair buddy has joined the session

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -166,6 +166,8 @@
       @pairingChannel.trigger 'client-joined', {joined: true}

     @pairingChannel.bind 'client-joined', (data) =>
+      noticeView = new AlertView "Your pair buddy has joined the session."
+      atom.workspace.addModalPanel(item: noticeView, visible: true)
       @sendGrammar()
       @syncGrammars()
       @shareCurrentFile(buffer)


OPCE28: selections updated with deleteSel

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -68,8 +68,8 @@
     else
       throw new Error "Invalid argument #{side}, expected 'left' or 'right'"
     index = 0
-    for index in [0..array.length-1]
-      if array[index].equals selection
+    for sel, index in array
+      if sel.equals selection
         array.splice index, 1
         break

@@ -351,12 +351,19 @@
     @rte.removeSel selToRemove

   # Unbind selection from word
+  #
+  # @returns [Array<Word>] an array containing the old value of left and right
   unbind: ->
     @left.removeSel @, "left"
     @right.removeSel @, "right"

+    left = @left
+    right = @right
+
     @left = null
     @right = null
+
+    [left, right]

   # Bind selection to word
   # @param [Word] left a word instance to bind at left
@@ -394,6 +401,56 @@

     newSel

+  # Delete all selections within this selection and update all selections
+  # that overlap with this one to point to the word passed as argument, position
+  # 0
+  # @param [Word] wordToBoundTo the word to bound overlapping selections to
+  deleteHelper: (wordToBound) ->
+    thisSel = @
+    selections = @rte.getSelections((s) -> s != thisSel)
+
+    tmpSelections = []
+    # delete selection contained in deleted selection
+    for sel, index in selections
+      if not (sel.in thisSel)
+        tmpSelections.push sel
+        continue
+      console.log sel.print()+" in "+thisSel.print()
+      sel.unbind()
+      @rte.removeSel sel
+
+    selections = tmpSelections
+    tmpSelections = []
+    # update selections at left of deleted selection
+    for sel, index in selections
+      if not (sel.atLeftOf thisSel)
+        tmpSelections.push sel
+        continue
+      console.log sel.print()+" at left of "+thisSel.print()
+      [left, right] = sel.unbind()
+      sel.rightPos = 0
+      sel.bind left, wordToBound
+
+      if sel.isEmpty()
+        sel.unbind()
+        @rte.removeSel sel
+
+    selections = tmpSelections
+    tmpSelections = []
+    # update selections at right of deleted selection
+    for sel, index in selections
+      if not (thisSel.atLeftOf sel)
+        continue
+      console.log thisSel.print()+" at left of "+sel.print()
+      [left, right] = sel.unbind()
+      sel.leftPos = 0
+      sel.bind wordToBound, right
+
+      if sel.isEmpty()
+        sel.unbind()
+        @rte.removeSel sel
+
+
 # Class describing the Rich Text Editor type
 #
 class Rte
@@ -469,7 +526,18 @@
     if not (0 <= index < @_rte.words.length)
       throw new Error "Index out of bounds"

-    @_rte.words[index].word = content
+    word = @getWord(index)
+    word.word = content
+    # the word has been replaced, all selections at boundaries of
+    # the selection
+    for selection in word.getSelections()
+      selection.leftPos = word.word.length
+      selection.rightPos = 0
+      # if by this, the selection becomes empty (== length 0), remove it
+      if selection.isEmpty()
+        selection.unbind()
+        @removeSel selection
+

   # Append new words at the end of the word list
   # @param [String] content the string to append
@@ -514,6 +582,9 @@
   #   Delete the word at position
   #
   #   @param [Integer] position position the word to delete
+  #
+  # @note selections that where bound to any of the deleted word
+  # are moved to the beginning of the first word not deleted
   deleteWords: (start, end) ->
     if _.isUndefined(end)
       end = start+1
@@ -524,16 +595,20 @@
       # delete all the selections within
       indexStart = absoluteFromRelative start, 0, @
       length = (@getWords 0).length
-      while end >= length
-        end -= 1
-      endPos = (@getWord end).word.length-1
-      indexEnd = absoluteFromRelative end, endPos, @
+
+      endPos = (@getWord (end-1)).word.length-1
+      indexEnd = absoluteFromRelative (end-1), endPos, @
+
+      # remove all the selections within deleted area
+      # and change the extremities of those overlapping
       opts = {bind: false}
       selection = new Selection indexStart, indexEnd, @, opts

-      for sel in @getSelections((s) -> s.in(selection))
-        sel.unbind()
-        @removeSel sel
+      selection.deleteHelper (@getWord (end))
+
+      # remove words
+      @_rte.words.splice start, (end-start)
+

   # Merge two words at position
   #
@@ -721,7 +796,7 @@
   removeSel: (selection) ->
     index = 0
     array = @_rte.selections
-    for index in [0..array.length-1]
+    for el, index in array
       if (array[index] == selection)
         array.splice index, 1
         break
@@ -729,11 +804,11 @@
   # Only for manual use, so no efficiency research
   garbageCollect: ->
     sels = @_rte.selections
-    for index in [0..sels.length-1]
-      for key, value of sels[index]
+    for sel, index in sels
+      for key, value of sel
         if value == null or value == ""
-          sels[index].unbind()
-          @removeSel sels[index]
+          sel.unbind()
+          @removeSel sel

 if window?