some readability small changes

some readability small changes

--- bokehjs/src/coffee/models/plots/plot_canvas.coffee
+++ bokehjs/src/coffee/models/plots/plot_canvas.coffee
@@ -773,12 +773,9 @@

   get_layoutable_children: () ->
     children = [
-      @above_panel,
-      @below_panel,
-      @left_panel,
-      @right_panel,
-      @get('canvas'),
-      @get('frame')
+      @above_panel, @below_panel,
+      @left_panel, @right_panel,
+      @canvas, @frame
     ]
     # Add the layout panels for each of the axes
     for side in ['above', 'below', 'left', 'right']
@@ -805,41 +802,40 @@
     return constraints

   _get_constant_constraints: () ->
-    constraints = []
-
-    # Add the constraints that always apply for a plot
+
     min_border_top    = @get('min_border_top')
     min_border_bottom = @get('min_border_bottom')
     min_border_left   = @get('min_border_left')
     min_border_right  = @get('min_border_right')
-    frame             = @get('frame')
-    canvas            = @get('canvas')
+
+    # Create the constraints that always apply for a plot
+    constraints = []

     # Set the border constraints
-    constraints.push(GE(@above_panel._height, -min_border_top))
-    constraints.push(GE(@below_panel._height, -min_border_bottom))
-    constraints.push(GE(@left_panel._width, -min_border_left))
-    constraints.push(GE(@right_panel._width, -min_border_right))
+    constraints.push(GE( @above_panel._height, -min_border_top    ))
+    constraints.push(GE( @below_panel._height, -min_border_bottom ))
+    constraints.push(GE( @left_panel._width,   -min_border_left   ))
+    constraints.push(GE( @right_panel._width,  -min_border_right  ))

     # Set panel top and bottom related to canvas and frame
-    constraints.push(EQ(@above_panel._top, [-1, canvas._top]))
-    constraints.push(EQ(@above_panel._bottom, [-1, frame._top]))
-    constraints.push(EQ(@below_panel._bottom, [-1, canvas._bottom]))
-    constraints.push(EQ(@below_panel._top, [-1, frame._bottom]))
-    constraints.push(EQ(@left_panel._left, [-1, canvas._left]))
-    constraints.push(EQ(@left_panel._right, [-1, frame._left]))
-    constraints.push(EQ(@right_panel._right, [-1, canvas._right]))
-    constraints.push(EQ(@right_panel._left, [-1, frame._right]))
+    constraints.push(EQ( @above_panel._top,    [-1, @canvas._top]    ))
+    constraints.push(EQ( @above_panel._bottom, [-1, @frame._top]     ))
+    constraints.push(EQ( @below_panel._bottom, [-1, @canvas._bottom] ))
+    constraints.push(EQ( @below_panel._top,    [-1, @frame._bottom]  ))
+    constraints.push(EQ( @left_panel._left,    [-1, @canvas._left]   ))
+    constraints.push(EQ( @left_panel._right,   [-1, @frame._left]    ))
+    constraints.push(EQ( @right_panel._right,  [-1, @canvas._right]  ))
+    constraints.push(EQ( @right_panel._left,   [-1, @frame._right]   ))

     # Plot sides align
-    constraints.push(EQ(@above_panel._height, [-1, @_top]))
-    constraints.push(EQ(@above_panel._height, [-1, canvas._top], frame._top))
-    constraints.push(EQ(@below_panel._height, [-1, @_height], @_bottom))
-    constraints.push(EQ(@below_panel._height, [-1, frame._bottom]))
-    constraints.push(EQ(@left_panel._width, [-1, @_left]))
-    constraints.push(EQ(@left_panel._width, [-1, frame._left]))
-    constraints.push(EQ(@right_panel._width, [-1, @_width], @_right))
-    constraints.push(EQ(@right_panel._width, [-1, canvas._right], frame._right))
+    constraints.push(EQ( @above_panel._height, [-1, @_top]                         ))
+    constraints.push(EQ( @above_panel._height, [-1, @canvas._top], @frame._top     ))
+    constraints.push(EQ( @below_panel._height, [-1, @_height], @_bottom            ))
+    constraints.push(EQ( @below_panel._height, [-1, @frame._bottom]                ))
+    constraints.push(EQ( @left_panel._width,   [-1, @_left]                        ))
+    constraints.push(EQ( @left_panel._width,   [-1, @frame._left]                  ))
+    constraints.push(EQ( @right_panel._width,  [-1, @_width], @_right              ))
+    constraints.push(EQ( @right_panel._width,  [-1, @canvas._right], @frame._right ))

     return constraints


log file.post

log file.post

--- web.coffee
+++ web.coffee
@@ -52,10 +52,11 @@
       get.on "end",          -> res.end()

 app.post "/file/:hash", (req, res) ->
-  storage.verify_hash req.files.data.path, req.params.hash, (err) ->
-    return res.send(err, 403) if err
-    storage.create_stream "/hash/#{req.params.hash}", fs.createReadStream(req.files.data.path), (err) ->
-      res.send "ok"
+  log "file.post", hash:req.params.hash, ->
+    storage.verify_hash req.files.data.path, req.params.hash, (err) ->
+      return res.send(err, 403) if err
+      storage.create_stream "/hash/#{req.params.hash}", fs.createReadStream(req.files.data.path), (err) ->
+        res.send "ok"

 app.post "/manifest", (req, res) ->
   manifest.init(JSON.parse(req.body.manifest)).save (err, manifest_url) ->


advertise adapter

advertise adapter

--- src/dt-jquery.coffee
+++ src/dt-jquery.coffee
@@ -31,6 +31,7 @@
         for n,f of defaultfn
             @fn[n] ?= f.bind(this)
         super
+        @builder.adapters['jquery'] = this
         do @patch_fn

     # override builder.query


Fix typo

Fix typo

--- views/components/settings/parts/poi-config.cjsx
+++ views/components/settings/parts/poi-config.cjsx
@@ -6,7 +6,7 @@
 {Grid, Col, Button, ButtonGroup, Input, Alert} = ReactBootstrap
 {config, toggleModal} = window
 {APPDATA_PATH} = window
-{showItenInFolder, openItem} = require 'shell'
+{showItemInFolder, openItem} = require 'shell'
 Divider = require './divider'
 NavigatorBar = require './navigator-bar'
 themes = glob.sync(path.join(ROOT, 'assets', 'themes', '*')).map (filePath) ->


evaluator: string methods

evaluator: string methods

--- src/evaluator.coffee
+++ src/evaluator.coffee
@@ -174,6 +174,15 @@
   'mod': (p) -> p[0] % p[1]
   'round': (p) -> Math.round p[0]
   'sqrt': (p) -> Math.sqrt p[0]
+  'begins': (p) -> p[0].startsWith p[1]
+  'find': (p) -> p[1].indexOf p[0]
+  'left': (p) -> p[0].substr 0, p[1]
+  'lower': (p) -> p[0].toLowerCase()
+  'mid': (p) -> p[0].substr p[1], p[2]
+  'right': (p) -> p[0].substr -p[1]
+  'replace': (p) -> p[0].replace p[1], p[2]
+  'upper': (p) -> p[0].toUpperCase()
+  'urlencode': (p) -> encodeURI p[0]
 }

 evaluate = (formula, data) ->


* [bug] should lookup the parent whether has eventable ability.

--- src/eventable.coffee
+++ src/eventable.coffee
@@ -12,9 +12,9 @@

 eventable         = require 'events-ex/eventable'
 addionalOptions   = require './eventable-options'
+hasAbilityOnParent= require 'custom-ability/lib/has-ability-on-parent'

 module.exports = (aClass, aOptions)->
-  vAbilities = aClass::$abilities
-  aOptions = addionalOptions(aOptions) unless vAbilities and vAbilities.Eventable
+  aOptions = addionalOptions(aOptions) unless hasAbilityOnParent aClass, 'Eventable'
   eventable aClass, aOptions


make sure hand can't be toggled during animation

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -599,7 +599,7 @@
 		return @_device.hands != undefined

 	nextHand: ->
-		return if @orientation != 0
+		return if @hands.rotationZ != 0
 		if @handSwitchingSupported()
 			hands = _.keys(@_device.hands)
 			if hands.length > 0


Log errors from nak to console

Log errors from nak to console

--- src/app/project.coffee
+++ src/app/project.coffee
@@ -262,24 +262,28 @@
         iterator({path, range, match})

     deferred = $.Deferred()
-    exit = (code) ->
-      if code is -1
-        deferred.reject({command, code})
-      else
-        deferred.resolve()
+    errors = []
+    stderr = (data) ->
+      errors.push(data)
     stdout = (data) ->
       lines = data.split('\n')
       lines.pop() # the last segment is a spurious '' because data always ends in \n due to bufferLines: true
       for line in lines
         readPath(line) if state is 'readingPath'
         readLine(line) if state is 'readingLines'
+    exit = (code) ->
+      if code is 0
+        deferred.resolve()
+      else
+        console.error("Project scan failed: #{code}", errors.join('\n'))
+        deferred.reject({command, code})

     command = require.resolve('.bin/nak')
     args = ['--hidden', '--ackmate', regex.source, @getPath()]
     ignoredNames = config.get('core.ignoredNames') ? []
     args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0
     args.unshift('--addVCSIgnores') if config.get('core.excludeVcsIgnoredPaths')
-    new BufferedProcess({command, args, stdout, exit})
+    new BufferedProcess({command, args, stdout, stderr, exit})
     deferred

   ### Internal ###


Only deal with new Actions for now

Only deal with new Actions for now

--- js/inner.coffee
+++ js/inner.coffee
@@ -271,19 +271,24 @@
     @build_channel = VM.pusher.subscribe(@pusherChannel())
     @build_channel.bind('pusher:subscription_error', (status) -> notifyError status)

-    @build_channel.bind('updateAction', (json) => @updateAction json)
-    @build_channel.bind('appendAction', (json) => @appendAction json)
+    @build_channel.bind('newAction', (json) => @newAction json)
+    # @build_channel.bind('updateAction', (json) => @updateAction json)
+    # @build_channel.bind('appendAction', (json) => @appendAction json)
+
+  newAction: (json) =>
+    if not @steps()[json.step]
+      @steps().setIndex(json.step, new Step({}))
+
+    @steps()[json.step].actions.setIndex(json.index, new ActionLog(json.log))

   updateAction: (json) =>
-    fillArray(@steps(), json.steps, Step)
-    fillArray(@steps()[json.step].actions, json.index, ActionLog)
-    @steps()[json.step].actions()[json.index] = new ActionLog(json.log)
-
-  appendAction: (json) =>
-    fillArray(@steps(), json.steps, Step)
-    fillArray(@steps()[json.step].actions(), json.index, ActionLog)
-
-    @steps[json.step].actions()[json.action].push(json.out)
+    null
+
+  # appendAction: (json) =>
+  #   fillArray(@steps(), json.steps, Step)
+  #   fillArray(@steps()[json.step].actions(), json.index, ActionLog)
+
+  #   @steps[json.step].actions()[json.action].push(json.log)

   # TODO: CSRF protection
   retry_build: (data, event) =>


Fix logic, that function was called inferred(), its now the opposite

--- js/inner.coffee
+++ js/inner.coffee
@@ -271,7 +271,7 @@
       full_spec += @dependencies()
       full_spec += @test()
       full_spec += @extra()
-      "" == full_spec
+      "" != full_spec

     @uninferrable = @komp =>
       @status() == "uninferrable"


Only focus if the document can open the path

--- src/atom/window.coffee
+++ src/atom/window.coffee
@@ -13,7 +13,6 @@
     @setTitle (_.last @path.split '/') or 'Untitled Document'

     $atomController.window.makeKeyWindow
-    atom.trigger 'window:load'

   shutdown: ->

@@ -28,8 +27,8 @@
     OSX.NSApp.createController @path

   open: (path) ->
-    $atomController.window.makeKeyAndOrderFront $atomController
     if atom.document.open path
+      $atomController.window.makeKeyAndOrderFront $atomController
       atom.trigger 'window:open', path
     else
       atom.app.open path


bug fix

bug fix


(- re tests docs) Those aren't methods.

(- re tests docs) Those aren't methods.

--- Paws/Test/utility.tests.coffee
+++ Paws/Test/utility.tests.coffee
@@ -8,7 +8,7 @@

    run = utilities.runInNewContext
-   describe '#runInNewContext', ->
+   describe 'runInNewContext()', ->
       it 'should not error out', ->
          expect(-> run 'true')    .to.not.throwException()
          expect(-> run 'Function').to.not.throwException()
@@ -87,5 +87,5 @@
          expect(-> fan.method 'bar').to.not.throwError()
          expect(fan.foo).to.be 'bar'

-   describe '#subclass (via __proto__)', subclassTests true if utilities.hasPrototypeAccessors()
-   describe '#subclass (via a foreign context)', subclassTests false
+   describe 'subclass() (via __proto__)', subclassTests true if utilities.hasPrototypeAccessors()
+   describe 'subclass() (via a foreign context)', subclassTests false


bin: Fixing require paths

bin: Fixing require paths

--- src/bin/gitwalk.coffee
+++ src/bin/gitwalk.coffee
@@ -6,10 +6,10 @@
 #   gitwalk 'github:pazdera/.*@master?'

 program = require 'commander'
-pkginfo = require '../package.json'
-gitwalk = require '../src/gitwalk'
-processors = require '../src/processors'
-logger = require '../src/logger'
+pkginfo = require '../../package.json'
+gitwalk = require '../lib/gitwalk'
+processors = require '../lib/processors'
+logger = require '../lib/logger'

 program
   .version pkginfo.version
@@ -25,6 +25,10 @@
 proc = null
 procName = null
 procArgs = []
+
+if program.args.length == 0
+  program.outputHelp()
+  process.exit 1

 while program.args.length > 0
   arg = program.args.shift()


fix ble.state for blendmicro npm update  #6

--- src/ble-firmata.coffee
+++ src/ble-firmata.coffee
@@ -121,7 +121,7 @@

   force_write: (bytes, callback) ->
     try
-      unless @ble.state is 'connected'
+      unless @ble.state is 'open'
         return
       @ble.write bytes, callback
     catch err


Cache the object descriptors

Cache the object descriptors

_propertyList used to be called a ton on new layer creation, but now we chache it until a new property descriptor is added.


setSize method

setSize method


very small revision

very small revision

--- src/greenhorn.coffee
+++ src/greenhorn.coffee
@@ -92,7 +92,7 @@
             @_elmnts[elmnt]
     @set = (elmnt, attr, what) ->
         if Object::toString.call(what) is '[object Object]'
-            @_elmnts[elmnt][attr][key] = value for key, value of what
+            @_elmnts[elmnt][attr][key] = value for own key, value of what
         else
             @_elmnts[elmnt][attr] = what


StacksV2: context switching between InitialView and DefineStackView based on the flow requirements

--- client/admin/lib/views/groupstacksettingsv2.coffee
+++ client/admin/lib/views/groupstacksettingsv2.coffee
@@ -1,17 +1,34 @@
-kd             = require 'kd'
-InitialView    = require './stacksv2/initialview'
+kd              = require 'kd'
+curryIn         = require 'app/util/curryIn'
+
+InitialView     = require './stacksv2/initialview'
+DefineStackView = require './stacksv2/definestackview'

 module.exports = class GroupStackSettingsV2 extends kd.View

   constructor: (options = {}, data) ->

-    options.cssClass = 'stacks'
+    curryIn options, cssClass: 'stacks'
+
     super options, data

   viewAppended: ->
-    @addSubView new InitialView
+
+    @addSubView initialView = new InitialView
+
+    initialView.on ['CreateNewStack', 'EditStack'], (stackTemplate) =>
+
+      initialView.hide()
+
+      requiresReload  = !stackTemplate?
+      defineStackView = @addSubView new DefineStackView {}, { stackTemplate }
+
+      defineStackView.on ['Cancel', 'Completed'], (stackTemplate) ->
+        initialView.reload()  if requiresReload
+        initialView.show()
+        @destroy()

   setGroupTemplate: (stackTemplate) ->


Much better

Much better

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -5,14 +5,15 @@
     @kc = KD.singletons.kiteController

   _cbWrapper:(callback, emitStateChanged)->
+    return callback  unless emitStateChanged
+
     kallback = (rest...)=>
       @info (err, info)=>
         warn "[VM]", err  if err
         @emit 'StateChanged', err, info
       callback? rest...

-    return kallback  if emitStateChanged
-    return callback
+    return kallback

   run:(command, callback, emitStateChanged=yes)->
     @kc.run


StackTemplateListItem: show Last updated with the correct modifiedAt [completes #106420772]

--- client/app/lib/stacks/stacktemplatelistitem.coffee
+++ client/app/lib/stacks/stacktemplatelistitem.coffee
@@ -58,7 +58,7 @@
           This stack template is currently used by your team. If you continue
           to edit, all of your changes will be applied to all team members directly.
           We highly recommend you to clone this stack template
-          first and work on the cloned version. Once you finish your work,
+          first and work on the cloned version. Once you finish your work,
           you can easily apply your changes for all team members.
         "
         buttons      :
@@ -110,7 +110,7 @@
     """

       {div.title{#(title)}} {{> @inuseView}} {{> @notReadyView}} {{> @accessLevelView}}
-      #{timeago meta.createdAt}
+      Last updated #{timeago meta.modifiedAt}

{{> @settings}}

     """


Adding license.

Adding license.

--- src/coffee-lint.coffee
+++ src/coffee-lint.coffee
@@ -1,9 +1,9 @@
 ###
 CoffeeLint

-Copyright (c) 2011 Matthew Perpick
+Copyright (c) 2011 Matthew Perpick.
+CoffeeLint is freely distributable under the MIT license.
 ###
-

 fs = require('fs')


Refactor and fix createuser function name


add title to the re-number button

add title to the re-number button

--- src/coffee/client/puzzle.coffee
+++ src/coffee/client/puzzle.coffee
@@ -686,6 +686,7 @@

                   <input type="button" value="Re-assign numbers" onClick={this.renumber}
+                        title="Sets the numbers in the grid based off of the locations of the black cells, according to standard crossword rules."
                         className="lt-button" disabled={@needToRenumber()} />


support room rename notifications

support room rename notifications

--- src/coffee/control/index.coffee
+++ src/coffee/control/index.coffee
@@ -25,11 +25,13 @@
         newRoom.$promise.then ->
           rooms[idx] = newRoom

-    handleRoomNotification = (event, payload) ->
+    handleRoomNotification = (event, payload, room) ->
       $scope.$apply ->
         message = JSON.parse payload.message
         if message.type == 'left-room'
           leftRoom(message.userId, message.roomId)
+        else if message.type == 'room-renamed'
+          room.name = message.name
         else
           if $state.params._id != room._id
             $scope.notifications[room._id]++
@@ -37,7 +39,7 @@
     strapRoom = (room) ->
       $scope.notifications[room._id] = 0
       $subscriber.subscribe room._id
-      $scope.$on (PubNub.ngMsgEv room._id), handleRoomNotification
+      $scope.$on (PubNub.ngMsgEv room._id), (event, payload) -> handleRoomNotification event, payload, room

     for room in rooms
       do (room) ->


session: add returnUrl to session

session: add returnUrl to session

--- workers/social/lib/social/models/session.coffee
+++ workers/social/lib/social/models/session.coffee
@@ -31,6 +31,7 @@
         odesk       : Object
         facebook    : Object
         linkedin    : Object
+      returnUrl     : String
       foreignAuthType : String
       impersonating : Boolean
     sharedEvents    :


Finished backbone model tests for textbooks

--- cms/static/coffee/spec/models/textbook_spec.coffee
+++ cms/static/coffee/spec/models/textbook_spec.coffee
@@ -67,3 +67,69 @@
         model = new CMS.Models.Textbook(serverModelSpec, {parse: true})
         expect(deepAttributes(model)).toEqual(clientModelSpec)
         expect(model.toJSON()).toEqual(serverModelSpec)
+
+describe "CMS.Collections.TextbookSet", ->
+    beforeEach ->
+        window.TEXTBOOK_URL = "/textbooks"
+        @collection = new CMS.Collections.TextbookSet()
+
+    afterEach ->
+        delete window.TEXTBOOK_URL
+
+    it "should have a url set", ->
+        expect(_.result(@collection, "url"), window.TEXTBOOK_URL)
+
+    it "can call save", ->
+        spyOn(@collection, "sync")
+        @collection.save()
+        expect(@collection.sync).toHaveBeenCalledWith("update", @collection, undefined)
+
+    it "should respond to editOne events", ->
+        model = new CMS.Models.Textbook()
+        @collection.trigger('editOne', model)
+        expect(@collection.editing).toEqual(model)
+
+
+describe "CMS.Models.Chapter", ->
+    beforeEach ->
+        @model = new CMS.Models.Chapter()
+
+    it "should have a name by default", ->
+        expect(@model.get("name")).toEqual("")
+
+    it "should have an asset_path by default", ->
+        expect(@model.get("asset_path")).toEqual("")
+
+    it "should have an order by default", ->
+        expect(@model.get("order")).toEqual(1)
+
+    it "should be empty by default", ->
+        expect(@model.isEmpty()).toBeTruthy()
+
+
+describe "CMS.Collections.ChapterSet", ->
+    beforeEach ->
+        @collection = new CMS.Collections.ChapterSet()
+
+    it "is empty by default", ->
+        expect(@collection.isEmpty()).toBeTruthy()
+
+    it "is empty if all chapters are empty", ->
+        @collection.add([{}, {}, {}])
+        expect(@collection.isEmpty()).toBeTruthy()
+
+    it "is not empty if a chapter is not empty", ->
+        @collection.add([{}, {name: "full"}, {}])
+        expect(@collection.isEmpty()).toBeFalsy()
+
+    it "should have a nextOrder function", ->
+        expect(@collection.nextOrder()).toEqual(1)
+        @collection.add([{}])
+        expect(@collection.nextOrder()).toEqual(2)
+        @collection.add([{}])
+        expect(@collection.nextOrder()).toEqual(3)
+        # verify that it doesn't just return an incrementing value each time
+        expect(@collection.nextOrder()).toEqual(3)
+        # try going back one
+        @collection.remove(@collection.last())
+        expect(@collection.nextOrder()).toEqual(2)


fix(form): fix line which exceeds 80 chars

--- src/components/EntryForm.coffee
+++ src/components/EntryForm.coffee
@@ -143,6 +143,14 @@

 module.exports = connectReduxForm(
   form      : 'edit'
-  fields    : ['title', 'description', 'homepage', 'phone', 'lat', 'lng', 'category']
+  fields    : [
+    'title',
+    'description',
+    'homepage',
+    'telephone',
+    'lat',
+    'lng',
+    'category'
+  ]
   validate  : validation.entryForm
 )(Form)


textarea shows textarea values right on open for builder, as opposed to waiting till a change

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -197,10 +197,18 @@
     id: React.PropTypes.string
     className: React.PropTypes.string
     onChange: React.PropTypes.func
+    heightBuffer: React.PropTypes.number
+
+  getDefaultProps: ->
+    heightBuffer: 28

   resize: (event) ->
-    event.target.style.height = ''
-    event.target.style.height = "#{event.target.scrollHeight}px"
+    textarea = @refs.textarea.getDOMNode()
+    textarea.style.height = ''
+    textarea.style.height = "#{textarea.scrollHeight + @props.heightBuffer}px"
+
+  componentDidMount: ->
+    @resize() if @props.default.length > 0

   onChange: (event) ->
     @props.onChange(event.target?.value, event.target)


STATE.STOP时才触发_fetch

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -127,10 +127,13 @@
                 @trigger('player:play', startTime)
                 def.resolve()

-            # XXX: 应该在_fetch中决定是否发起选链。
-            # 即是否从cache中取, 是否setUrl都是依据_fetch的实现去决定。
-            # 如果继承时覆盖重写_fetch, 这些都要自己权衡。
-            @_fetch().done () ->
+            if @getState() is STATES.STOP
+                # XXX: 应该在_fetch中决定是否发起选链。
+                # 即是否从cache中取, 是否setUrl都是依据_fetch的实现去决定。
+                # 如果继承时覆盖重写_fetch, 这些都要自己权衡。
+                @_fetch().done () =>
+                    play()
+            else
                 play()

             return def.promise()


autoconnect

autoconnect

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -34,6 +34,8 @@
       @ready => @ws.send JSON.stringify req
       @emit 'info', "proto request", req
       return
+
+    @connect()

   # connection state:
   connect: ->


delay wording converted to followups

delay wording converted to followups

--- components/notifications/KDNotificationView.coffee
+++ components/notifications/KDNotificationView.coffee
@@ -3,12 +3,12 @@
     super options
     options = @notificationSetDefaults options

-    @notificationSetType    options.type
-    @notificationSetTitle   options.title     if options.title?
-    @notificationSetContent options.content   if options.content?
-    @notificationSetTimer   options.duration  if options.duration?
-    @notificationSetOverlay options.overlay   if options.overlay?
-    @notificationSetDelay   options.delay     if options.delay?
+    @notificationSetType        options.type
+    @notificationSetTitle       options.title         if options.title?
+    @notificationSetContent     options.content       if options.content?
+    @notificationSetTimer       options.duration      if options.duration?
+    @notificationSetOverlay     options.overlay       if options.overlay?
+    @notificationSetFollowUps   options.followUps     if options.followUps?

     @notificationShowTimer() if options.showTimer? and options.showTimer
     @notificationSetCloseHandle options.closeManually
@@ -133,13 +133,13 @@
       @notificationTimerDiv.text next
     ,1000

-  notificationSetDelay: (delayList)->
-    unless Array.isArray delayList then delayList = [delayList]
-    delayList.forEach (delay)=>
-      delay.duration  ?= 10000
-      @utils.wait delay.duration, =>
-        @notificationSetTitle   delay.title    if delay.title
-        @notificationSetContent delay.content  if delay.content
+  notificationSetFollowUps: (followUps)->
+    unless Array.isArray followUps then followUps = [followUps]
+    followUps.forEach (followUp)=>
+      followUp.duration  ?= 10000
+      @utils.wait followUp.duration, =>
+        @notificationSetTitle   followUp.title    if followUp.title
+        @notificationSetContent followUp.content  if followUp.content
         @notificationSetPositions()

   notificationShowTimer:()->


Use do (field) -> instead of explicity closure

--- VersionOne.FeatureRequestor/Scripts/v1assetEditor.coffee
+++ VersionOne.FeatureRequestor/Scripts/v1assetEditor.coffee
@@ -310,7 +310,7 @@
         selectLists.push value if value.options.length < 1 if value.type is "Select"
       that = @
       for field in selectLists
-        ( (field) ->
+        do (field) ->
           assetName = field.editorAttrs["data-assetName"]
           fields = field.formFields
           fields = ["Name"] if not fields? or fields.length is 0
@@ -333,7 +333,6 @@
               that.debug "No results for query: " + url
           ).fail(that._ajaxFail)
           ajaxRequests.push ajaxRequest
-        )(field)

       return promise if @resolveIfEmpty(promise, ajaxRequests)


Update URL when selecting on /works-for-you with React grid, plus fix for empty counts when searching for artist

--- src/desktop/apps/notifications/client/sidebar.coffee
+++ src/desktop/apps/notifications/client/sidebar.coffee
@@ -23,39 +23,38 @@

   toggleForSale: (e) ->
     forSale = $(e.currentTarget).prop('checked')
+    artist = @$selectedArtist.attr 'data-artist' if @$selectedArtist
     if @useReactionGrid
-      require('./react_grid.js').default.setupReactGrid({forSale: forSale, artistID: @$selectedArtist?.attr('data-artist')})
-    else
-      @filterState.set
-        forSale: forSale
-        loading: true
-        empty: false
-        initialLoad: false
+      require('./react_grid.js').default.setupReactGrid({forSale: forSale, artistID: artist})
+    @filterState.set
+      forSale: forSale
+      loading: true
+      empty: false
+      initialLoad: false

   toggleArtist: (e) ->
     if @$selectedArtist then @$selectedArtist.attr 'data-state', null
     @$selectedArtist = @$(e.currentTarget).parent()
     @$selectedArtist.attr 'data-state', 'selected'
+    artist = @$selectedArtist.attr 'data-artist' if @$selectedArtist
     if @useReactionGrid
-      require('./react_grid.js').default.setupReactGrid({forSale: @filterState.get('forSale'), artistID: @$selectedArtist.attr('data-artist')})
-    else
-      @filterState.set
-        artist: @$selectedArtist.attr('data-artist')
-        loading: true
-        empty: false
-        initialLoad: false
+      require('./react_grid.js').default.setupReactGrid({forSale: @filterState.get('forSale'), artistID: artist})
+    @filterState.set
+      artist: artist
+      loading: true
+      empty: false
+      initialLoad: false

   clearArtistWorks: (e) ->
     @$selectedArtist.attr 'data-state', null
     @$selectedArtist = ''
     if @useReactionGrid
       require('./react_grid.js').default.setupReactGrid({artistID: "", forSale: @filterState.get('forSale')})
-    else
-      @filterState.set
-        artist: null
-        loading: true
-        empty: false
-        initialLoad: false
+    @filterState.set
+      artist: null
+      loading: true
+      empty: false
+      initialLoad: false

   setupSearch: (options = {}) ->
     @typeahead = new TypeaheadView
@@ -73,4 +72,5 @@
         id: model.get('id')
         name: model.get('name')
         published_artworks_count: model.get('published_artworks_count')
+        forsale_artworks_count: model.get('forsale_artworks_count')
     @$(".filter-artist[data-artist=#{model.get('id')}]").children('.filter-artist-name').click()


Fix media type for unofficial mp4 mime type.

--- src/parser.coffee
+++ src/parser.coffee
@@ -248,9 +248,9 @@
                 mediaFile.deliveryType = mediaFileElement.getAttribute("delivery")
                 mediaFile.codec = mediaFileElement.getAttribute("codec")
                 mediaFile.mimeType = mediaFileElement.getAttribute("type")
-                # Compatiable fix: fix media type for outdated mp4 type.
+                # Compatiable fix: fix media type for unofficial mp4 mime type.
                 if mediaFile.mimeType is "video/x-mp4"
-                	mediaFile.mimeType = "video/x-mp4"
+                	mediaFile.mimeType = "video/mp4"
                 mediaFile.apiFramework = mediaFileElement.getAttribute("apiFramework")
                 mediaFile.bitrate = parseInt mediaFileElement.getAttribute("bitrate") or 0
                 mediaFile.minBitrate = parseInt mediaFileElement.getAttribute("minBitrate") or 0


fix window title messed up

fix window title messed up

Former-commit-id: a52aed5ff185be427d5d21cbadc10cb659491f54 [formerly 1ccb7478197b9e40a204413d65f9e90f063092e4] [formerly d642481d305007789de4c85f06a361ce7176192d [formerly d642481d305007789de4c85f06a361ce7176192d [formerly 4bdaaf039e7368a9886b504bf2f571868b778158]]]
Former-commit-id: 5e934c4e165b7a0c61897275c4c965ea66fc5906 [formerly 74c660dac43d1f34ec5aa5e7b9a9b4e8f83c36cf]
Former-commit-id: e2f138589e066b5c4534f8d2f9fdab5394ff9b90
Former-commit-id: b2bebc14f252f2a06cf8fda1b7df6e96c60d6ddf

--- app/assets/javascripts/backbone/entities/proposal.coffee
+++ app/assets/javascripts/backbone/entities/proposal.coffee
@@ -118,16 +118,11 @@
     # TODO: refactor this method out...handles what happens when
     # current user saves a new position
     newPositionSaved : (position) ->
-      console.log 'NEW POS'
       user_id = position.get('user_id')
       if position.get('published')
-        console.log 'PUBLISHED!'
         if !@user_participated(user_id)
           @positions = null
           @participant_list.push user_id
-          console.log 'PUSHING!', user_id, @participant_list
-
-        console.log @getParticipants()

         if !@get('top_pro')
           _.each position.written_points, (pnt) =>
@@ -158,10 +153,15 @@
       @get('publicity') > 0 && @get('published')

     getMeta : ->
+      defaults =
+        title : "#{@get('category')||''} #{@get('designator')||''} #{@get('name')}"
+        description : @get('name')
+        keywords : "#{@get('category')||''} #{@get('designator')||''}"
+
       meta =
-        title : if @get('seo_title') then @get('seo_title') else "Discuss #{@get('category')} #{@get('designator')} #{@get('name')}: #{@get('name')}"
-        description : if @get('seo_description') then @get('seo_title') else "Think through and discuss #{@get('category')} #{@get('designator')} - #{@get('name')}."
-        keywords : if @get('seo_keywords') then @get('seo_keywords') else "#{@get('category')} #{@get('designator')}"
+        title : if @get('seo_title') then @get('seo_title') else defaults.title
+        description : if @get('seo_description') then @get('seo_title') else defaults.description
+        keywords : if @get('seo_keywords') then @get('seo_keywords') else defaults.keywords

       meta


OX-341 changed ugly list comprehension

OX-341 changed ugly list comprehension

--- app/assets/javascripts/oxalis/geometries/skeleton.coffee
+++ app/assets/javascripts/oxalis/geometries/skeleton.coffee
@@ -162,7 +162,7 @@
       if nodeList.length
         @nodesBuffer[index].pushMany(node.pos for node in nodeList)
         # Assign the ID to the vertex, so we can access it later
-        @nodes[index].geometry.nodeIDs.pushMany([node.id] for node in nodeList)
+        @nodes[index].geometry.nodeIDs.pushSubarray(node.id for node in nodeList)

       for node in nodeList
         # currently disabled due to performance issues


sort outcome gradebook by student name

sort outcome gradebook by student name

fixes CNVS-10634

test plan:
  * as a teacher, navigate to a course with the outcome
    gradebook enabled;
  * verify that rows are student by student sortable name.

Change-Id: Ie79872c6a0a7f04118fcbc95da2f17eb8ba3d703
Reviewed-on: https://gerrit.instructure.com/29003
Reviewed-by: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>
Product-Review: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>
QA-Review: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>

--- app/coffeescripts/gradebook2/OutcomeGradebookGrid.coffee
+++ app/coffeescripts/gradebook2/OutcomeGradebookGrid.coffee
@@ -167,7 +167,8 @@
       #
       # Returns an array of rows.
       toRows: (rollups, options = {}) ->
-        _.reject(_.map(rollups, Grid.Util._toRowFn(options.section)), (v) -> v == null)
+        rows = _.reject(_.map(rollups, Grid.Util._toRowFn(options.section)), (v) -> v == null)
+        rows.sort((a, b) -> Grid.Events._sortStudents(a, b, true))

       # Internal: Generate a toRow function that filters by the given section.
       #


Don't show warning on ungraded discussions.

If we have a graded discussion assigned to only some sections
of a course, and mark it ungraded, no longer show a warning
that it's not assigned to everyone.

Apologies for not knowing how to write a spec around this.

Fixes COMMS-1014

Test Plan:
* Have a course with multiple sections.
* Have a discussion assigned using the "overrides" to only
  some of those sections.
* Now, from the discussions edit page, mark that discussion
  as ungraded.
* There should no longer be a warning shown.

Change-Id: I9c8fb4812ffb0c7f18eb89d9d8c55bdfc8100886
Reviewed-on: https://gerrit.instructure.com/146073
Tested-by: Jenkins
Reviewed-by: Steven Burnett <a0b450117c5d20a22d5c4d52bb8ae13adee003d8@instructure.com>
QA-Review: Aaron Kc Hsu <089edc94045de3d9f5f372fb17706d21afcc259f@instructure.com>
Product-Review: Venk Natarajan <f9eab59b292314637a3c917fea4742741e36a19b@instructure.com>

--- app/coffeescripts/views/DiscussionTopics/EditView.coffee
+++ app/coffeescripts/views/DiscussionTopics/EditView.coffee
@@ -367,7 +367,7 @@
     submit: (event) =>
       event.preventDefault()
       event.stopPropagation()
-      if @dueDateOverrideView.containsSectionsWithoutOverrides()
+      if @gradedChecked() && @dueDateOverrideView.containsSectionsWithoutOverrides()
         sections = @dueDateOverrideView.sectionsWithoutOverrides()
         missingDateDialog = new MissingDateDialog
           validationFn: -> sections


small fix

small fix

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -150,6 +150,8 @@
                 act
               else
                 throw new Error("Action must be string or function");
+
+    throw new Error("action #{act} not found in controller #{controller}") if !action

     @action = wrapper(action)


Load the correct network interface for subscribeGraph option

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -131,7 +131,7 @@

   # Choose legacy or modern network based on whether graph
   # subscription is needed
-  NetworkType = if options.subscribeGraph then Network else exports.Network
+  NetworkType = if options.subscribeGraph then exports.Network else Network
   network = new NetworkType graph, options

   networkReady = (network) ->


Don't add the same steam user twice

Don't add the same steam user twice

--- lib/js/systems/steam.coffee
+++ lib/js/systems/steam.coffee
@@ -77,7 +77,9 @@

   saveUser: (user)->
     console.log 'saving:', user
-    @usersToCheck.push(user)
+    for u in @usersToCheck
+      return if u.name is user.name
+    @usersToCheck.push(user) if user not in @usersToCheck
     console.log 'added a user:', @usersToCheck, user

   module.exports = Steam


Handle new scope names for C++, Objective-C++

--- lib/clang-format.coffee
+++ lib/clang-format.coffee
@@ -18,7 +18,7 @@
     buffer = editor.getBuffer()
     atom.subscribe buffer, 'saved', =>
       scope = editor.getCursorScopes()[0]
-      if atom.config.get('clang-format.formatOnSave') and scope is 'source.c++'
+      if atom.config.get('clang-format.formatOnSave') and scope in ['source.c++', 'source.cpp']
         @format(editor)

     atom.subscribe buffer, 'destroyed', ->


:art: just cosmetic change

:art: just cosmetic change

--- lib/base.coffee
+++ lib/base.coffee
@@ -12,7 +12,7 @@
     # Reason: https://github.com/t9md/atom-vim-mode-plus/issues/85
     vimState.operationStack.run(klass, properties)

-delegatingMethods = [
+vimStateMethods = [
   "onDidChangeInput"
   "onDidConfirmInput"
   "onDidCancelInput"
@@ -40,9 +40,10 @@
   requireTarget: false
   operator: null
   asTarget: false
+  context: {}
   @commandPrefix: 'vim-mode-plus'

-  @delegatesMethods delegatingMethods..., toProperty: 'vimState'
+  @delegatesMethods vimStateMethods..., toProperty: 'vimState'

   constructor: (@vimState, properties) ->
     {@editor, @editorElement} = @vimState
@@ -164,9 +165,12 @@

   registries = {Base}
   @extend: (@command=true) ->
-    if @name of registries and (not @suppressWarning)
+    if (@name of registries) and (not @suppressWarning)
       console.warn "Duplicate constructor #{@name}"
     registries[@name] = this
+
+  @getClass: (klassName) ->
+    registries[klassName]

   @getRegistries: ->
     registries
@@ -180,9 +184,6 @@
   @registerCommand: ->
     atom.commands.add('atom-text-editor', @getCommandName(), => run(this))

-  @getClass: (klassName) ->
-    registries[klassName]
-
 class OperationAbortedError extends Base
   @extend(false)
   constructor: (@message) ->


Set-up for vertical slider detection by checking the width/height

--- framer/Components/SliderComponent.coffee
+++ framer/Components/SliderComponent.coffee
@@ -45,7 +45,6 @@
 		@fill.borderRadius = @borderRadius

 		@knob.draggable.enabled = true
-		@knob.draggable.speedY = 0
 		@knob.draggable.overdrag = false
 		@knob.draggable.momentum = true
 		@knob.draggable.momentumOptions = {friction: 5, tolerance: 0.25}
@@ -59,8 +58,16 @@

 		@on("change:size", @_updateFrame)
 		@on("change:borderRadius", @_setRadius)
+
+		# Check for vertical sliders
+		if @width > @height
+			@knob.draggable.speedY = 0
+			@knob.on("change:x", @_updateFill)
+		else
+			@knob.draggable.speedX = 0
+			@knob.on("change:y", @_updateFill)

-		@knob.on("change:x", @_updateFill)
+
 		@knob.on("change:size", @_updateKnob)

 		@knob.on Events.Move, =>
@@ -80,11 +87,18 @@
 		@_updateValue()

 	_updateFill: =>
-		@fill.width = @knob.midX
+		if @width > @height
+			@fill.width = @knob.midX
+		else
+			@fill.height = @knob.midY

 	_updateKnob: =>
-		@knob.midX = @fill.width
-		@knob.centerY()
+		if @width > @height
+			@knob.midX = @fill.width
+			@knob.centerY()
+		else
+			@knob.midY = @fill.height
+			@knob.centerX()

 	_updateFrame: =>
 		@knob.draggable.constraints =
@@ -93,8 +107,12 @@
 			width: @width + @knob.width
 			height: @height + @knob.height

-		@fill.height = @height
-		@knob.centerY()
+		if @width > @height
+			@fill.height = @height
+			@knob.centerY()
+		else
+			@fill.width = @width
+			@knob.centerX()

 	_setRadius: =>
 		radius = @borderRadius
@@ -106,7 +124,6 @@
 			@_knobSize = value
 			@knob.width = @_knobSize
 			@knob.height = @_knobSize
-			@knob.centerY()
 			@_updateFrame()

 	@define "min",
@@ -118,20 +135,33 @@
 		set: (value) -> @_max = value

 	@define "value",
-		get: -> @valueForPoint(@knob.midX)
+		get: ->
+			if @width > @height
+				@valueForPoint(@knob.midX)
+			else
+				@valueForPoint(@knob.midY)

 		set: (value) ->
-			@knob.midX = @pointForValue(value)
+			if @width > @height
+				@knob.midX = @pointForValue(value)
+			else
+				@knob.midY = @pointForValue(value)
 			@_updateFill()

 	_updateValue: =>
 		@emit("change:value", @value)

 	pointForValue: (value) ->
-		return Utils.modulate(value, [@min, @max], [0, @width], true)
+		if @width > @height
+			return Utils.modulate(value, [@min, @max], [0, @width], true)
+		else
+			return Utils.modulate(value, [@min, @max], [0, @height], true)

 	valueForPoint: (value) ->
-		return Utils.modulate(value, [0, @width], [@min, @max], true)
+		if @width > @height
+			return Utils.modulate(value, [0, @width], [@min, @max], true)
+		else
+			return Utils.modulate(value, [0, @height], [@min, @max], true)

 	animateToValue: (value, animationOptions={curve:"spring(300,25,0)"}) ->
 		animationOptions.properties = {x:@pointForValue(value)}


adding missing regex flag

adding missing regex flag


remove "Reset Julia Server" command

remove "Reset Julia Server" command

--- lib/package/commands.coffee
+++ lib/package/commands.coffee
@@ -80,7 +80,7 @@
       'julia-client:start-julia': -> disrequireClient 'boot Julia', -> boot()
       'julia-client:kill-julia': -> juno.connection.client.kill()
       'julia-client:interrupt-julia': => requireClient 'interrupt Julia', -> juno.connection.client.interrupt()
-      'julia-client:reset-julia-server': -> juno.connection.local.server.reset()
+      # 'julia-client:reset-julia-server': -> juno.connection.local.server.reset() # server mode not functional
       'julia-client:connnect-external-process': -> disrequireClient -> juno.connection.messages.connectExternal()
       'julia-client:connect-platformio-terminal': -> disrequireClient -> juno.connection.terminal.runPlatformIOTerm()
       'julia-client:connect-terminal': -> disrequireClient -> juno.connection.terminal.connectedRepl()


チャンネル制限処理の誤りを修正

チャンネル制限処理の誤りを修正

--- scripts/1_limit.coffee
+++ scripts/1_limit.coffee
@@ -11,6 +11,6 @@
       return

   robot.hear /.*/, (msg) ->
-    unless _.contains [config.getSlackChannel()], msg.envelope.room
+    unless _.contains [process.env.HUBOT_SLACK_CHANNEL, process.env.HUBOT_SLACK_CHANNEL_DEV], msg.envelope.room
       msg.finish()
       return


fix indentation

fix indentation

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -158,7 +158,7 @@
     open = spawn cmd, args

     open.stderr.on "data", (error) ->
-       atom.notifications.addError("**#{name}**: #{error}", dismissable: true)
+      atom.notifications.addError("**#{name}**: #{error}", dismissable: true)

     open.on "close", ( errorCode ) ->
       if errorCode is 0 and atom.config.get("#{name}.notify")


reference @url_base instead of @base_url

--- lib/clever.coffee
+++ lib/clever.coffee
@@ -330,7 +330,7 @@
       auth: "#{client_id}:#{client_secret}"
       ca: certs
       method: 'post'
-      uri: "#{@base_url}#{@tokens_path}"
+      uri: "#{@url_base}#{@tokens_path}"
       json:
         grant_type: 'authorization_code'
         code: code


ensure that options is always an object

ensure that options is always an object

--- lib/clever.coffee
+++ lib/clever.coffee
@@ -111,7 +111,7 @@
   class Resource
     @path: null

-    @_process_args: (conditions, fields, options, cb) ->
+    @_process_args: (conditions, fields, options={}, cb) ->
       if _(conditions).isFunction()
         cb = conditions
         conditions = {}


Performance fix for Chrome 16 on Windows.

--- game.coffee
+++ game.coffee
@@ -1,5 +1,8 @@

 canvas = atom.canvas
+
+if /Windows.*Chrome\/16/.test navigator.userAgent
+	canvas.style.borderRadius = 0
 ctx = atom.ctx
 audioCtx = atom.audioContext


Fix a couple of edge cases in parsePath

Fix a couple of edge cases in parsePath

--- lib/common.coffee
+++ lib/common.coffee
@@ -235,28 +235,32 @@

 parsePath = (pattern, start, res) ->
   path = []
-  i = j = start ? 0
+  i = start ? 0

   # Colon separator is needed for parseAlias
-  while j < pattern.length and pattern[j] != ':'
-    if pattern[j] == '.'
-      n = pattern.slice(i, j)
-      path.push(if n == '*' then null else n)
-      i = j + 1
-    else if pattern[j] == '[' and i == j
-      j = parseAlias(pattern, j + 1, path) + 1
-      i = j + 1
-    ++ j
-
-  if j > 0
-    n = pattern.slice(i, j)
-    path.push(if n == '*' then null else n)
+  if i < pattern.length and pattern[i] != ':'
+    while true
+      if pattern[i] == '['
+        i = parseAlias(pattern, i + 1, path) + 1
+      else
+        j = i
+        while i < pattern.length and pattern[i] != ':' and pattern[i] != '.'
+          ++ i
+        n = pattern.slice(j, i)
+        path.push(if n == '*' then null else n)
+
+      if i >= pattern.length or pattern[i] == ':'
+        break
+      else if pattern[i] != '.'
+        throw new Error('Invalid alias expression')
+
+      ++ i

   if not res?
     return path

   res.push(path)
-  return j
+  return i

 exports.UNDEFINED_TYPE = UNDEFINED_TYPE


Don't watch editors more than once if they're re-added to the workspace

--- lib/main.coffee
+++ lib/main.coffee
@@ -4,8 +4,14 @@

 module.exports =
   activate: ->
+    watchedEditors = new WeakSet()
+
     atom.workspace.observeTextEditors (editor) ->
+      return if watchedEditors.has(editor)
+
       editorElement = atom.views.getView(editor)
       matchManager = new MatchManager(editor, editorElement)
       new BracketMatcherView(editor, editorElement, matchManager)
       new BracketMatcher(editor, editorElement, matchManager)
+      watchedEditors.add(editor)
+      editor.onDidDestroy -> watchedEditors.delete(editor)


make obvious it's object!

make obvious it's object!

--- lib/main.coffee
+++ lib/main.coffee
@@ -29,15 +29,17 @@
   #
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onWillAddItem: (fn) -> @emitter.on('will-add-item', fn)
-
   onDidStart: (fn) -> @emitter.on('did-start', fn)
   onDidStop: (fn) -> @emitter.on('did-stop', fn)
   onDidRemoveHover: (fn) -> @emitter.on('did-remove-hover', fn)

   provideDemoMode: ->
-    onWillAddItem: @onWillAddItem.bind(this)
-    onDidStart: @onDidStart.bind(this)
-    onDidStop: @onDidStop.bind(this)
+    {
+      onWillAddItem: @onWillAddItem.bind(this)
+      onDidStart: @onDidStart.bind(this)
+      onDidStop: @onDidStop.bind(this)
+      onDidRemoveHover: @onDidRemoveHover.bind(this)
+    }

   deactivate: ->
     @stop() if @demo?


trying in Settings

trying in Settings

--- lib/main.coffee
+++ lib/main.coffee
@@ -14,6 +14,10 @@

     # ContextMenu
     that.updateContextMenu(defC.Context)
+
+    # Settings
+    atom.commands.add 'atom-workspace', 'settings-view:open', ->
+      that.updateSettings()

   updateMenu: (menuList, def) ->
     return if not def
@@ -34,3 +38,19 @@
         continue if item.type is "separator"
         label = set[item.command]
         item.label = label if label?
+
+  updateSettings: () ->
+    setTimeout(@delaySettings, 0, this)
+
+  delaySettings: (that) ->
+    panel = document.querySelector('.settings-view .panels-menu')
+    data = [
+      {label: "Settings", value: "設定"}
+      {label: "Keybindings", value: "キーバインド"}
+      {label: "Packages", value: "パッケージ"}
+      {label: "Themes", value: "テーマ"}
+      {label: "Updates", value: "パッケージの更新"}
+      {label: "Install", value: "パッケージのインストール"}
+    ]
+    for d in data
+      panel.querySelector("[name='#{d.label}']>a").text = d.value


Fix missing requirement

Fix missing requirement

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,6 +1,7 @@
 whenjs = require 'when'
 fs = null
 glob = null
+path = null
 CompositeDisposable = null
 DockerManager = null

@@ -21,6 +22,7 @@
 		DockerManager ?= require './docker-manager'
 		fs ?= require 'fs-plus'
 		glob ?= require 'glob'
+		path ?= require 'path'

 		# Install packages we depend on
 		require('atom-package-deps').install('particle-dev-local-compiler', true)


Set up initial detection of new-line and previous-line

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,97 +1,126 @@
-# NOTE
-# When in dev-mode, the command {language-markdown:compile-grammar} is added, which triggers a re-compile of the grammar. This piece of awesomeness needs to be triggered manually however, everytime you've changed a piece of the grammar. Yay for automation. But since a change in grammar needs a reload of Atom anyway, I think I can get away with this solution.
-if atom.inDevMode()
+{CompositeDisposable, Directory} = require 'atom'
+CSON = require 'season'
+path = require 'path'
+fs = require 'fs'

-  {CompositeDisposable, Directory} = require 'atom'
-  CSON = require 'season'
-  path = require 'path'
-  fs = require 'fs'
+module.exports =

-  module.exports =
+  subscriptions: null

-    subscriptions: null
+  activate: (state) ->
+    @subscriptions = new CompositeDisposable()

-    # Create the {language-markdown:compile-grammar} command,
+    # Create a new list-item after pressing [enter]
+    @subscriptions.add atom.workspace.observeTextEditors (editor) ->
+      editor.onDidInsertText (event) ->
+        if editor.getGrammar().name is 'Markdown'
+          if event.text is "\n"
+            previousRowNumber = event.range.start.row
+            previousRowRange = editor.buffer.rangeForRow(previousRowNumber)
+            previousLine = editor.getTextInRange(previousRowRange)
+            # console.log previousLine
+
+            # TODO
+            # - Use tokens to determine if {previousLine} was indeed Markdown, and not a bit of embedded Javascript for example
+            # - Determine if {previousLine} was a list-item, and if so, what kind of list-item
+            # - If {previousLine} was not an empty list-item, insert the correct new list-item on the current line, with the correct amount of leading white-space
+
+            # NOTE
+            # Taken from gfm-lists by @jonmagic; not sure if it is usable.
+            #
+            # match = previous_line.match(/^\s*([\-|\*]\s\[[x|\s]\]|\*|\-|\d\.)\s.+/)
+            # console.log(match)
+            # if match
+            #   switch match[1]
+            #     when "*"     then editor.insertText "* "
+            #     when "-"     then editor.insertText "- "
+            #     when "- [ ]" then editor.insertText "- [ ] "
+            #     when "* [ ]" then editor.insertText "* [ ] "
+            #     when "- [x]" then editor.insertText "- [ ] "
+            #     when "* [x]" then editor.insertText "* [ ] "
+            #     else editor.insertText "#{parseInt(match[1]) + 1}. "
+
+    # Only when in dev-mode,
+    # create the {language-markdown:compile-grammar} command,
     # via which the compiler can be executed
-    activate: ->
-      @subscriptions = new CompositeDisposable()
+    if atom.inDevMode()
       @subscriptions.add atom.commands.add 'atom-workspace', 'markdown:compile-grammar-and-reload': => @compileGrammar()

-    # Reads fixtures from {input},
-    # parses {data} to expand shortened syntax,
-    # creates and returns patterns from valid items in {data}.
-    compileFencedCodeGrammar: ->
-      input = '../grammars/fixtures/fenced-code.cson'
-      filepath = path.join(__dirname, input)
-      data = CSON.readFileSync(filepath)
-      @_createPatternsFromData(data)
+  # Loads the basic grammar structure,
+  # which includes the grouped parts in the repository,
+  # and then loads all grammar subrepositories,
+  # and appends them to the main repository,
+  # and finally writes {grammar} to {output}
+  compileGrammar: ->
+    input = '../grammars/repositories/markdown.cson'
+    output = '../grammars/language-markdown.json'
+    repositoryDirectories = ['blocks', 'flavors', 'inlines']
+    filepath = path.join(__dirname, input)
+    grammar = CSON.readFileSync(filepath)

-    # Loads the basic grammar structure,
-    # which includes the grouped parts in the repository,
-    # and then loads all grammar subrepositories,
-    # and appends them to the main repository,
-    # and finally writes {grammar} to {output}
-    compileGrammar: ->
-      input = '../grammars/repositories/markdown.cson'
-      output = '../grammars/language-markdown.json'
-      repositoryDirectories = ['blocks', 'flavors', 'inlines']
-      filepath = path.join(__dirname, input)
-      grammar = CSON.readFileSync(filepath)
+    for directoryName in repositoryDirectories
+      directory = new Directory(path.join(__dirname, '../grammars/repositories/'+directoryName))
+      entries = directory.getEntriesSync()
+      for entry in entries
+        {key, patterns} = CSON.readFileSync(entry.path)
+        if key and patterns
+          grammar.repository[key] =
+            patterns: patterns

-      for directoryName in repositoryDirectories
-        directory = new Directory(path.join(__dirname, '../grammars/repositories/'+directoryName))
-        entries = directory.getEntriesSync()
-        for entry in entries
-          {key, patterns} = CSON.readFileSync(entry.path)
-          if key and patterns
-            grammar.repository[key] =
-              patterns: patterns
+    # Compile and add fenced-code-blocks to repository
+    grammar.repository['fenced-code-blocks'] =
+      patterns: @compileFencedCodeGrammar()

-      # Compile and add fenced-code-blocks to repository
-      grammar.repository['fenced-code-blocks'] =
-        patterns: @compileFencedCodeGrammar()
+    # Write {grammar} to {filepath},
+    # and reload window when complete
+    filepath = path.join(__dirname, output)
+    CSON.writeFileSync filepath, grammar, do ->
+      atom.commands.dispatch 'body', 'window:reload'

-      # Write {grammar} to {filepath},
-      # and reload window when complete
-      filepath = path.join(__dirname, output)
-      CSON.writeFileSync filepath, grammar, do ->
-        atom.commands.dispatch 'body', 'window:reload'
+  # Reads fixtures from {input},
+  # parses {data} to expand shortened syntax,
+  # creates and returns patterns from valid items in {data}.
+  compileFencedCodeGrammar: ->
+    input = '../grammars/fixtures/fenced-code.cson'
+    filepath = path.join(__dirname, input)
+    data = CSON.readFileSync(filepath)
+    @createPatternsFromData(data)

-    # Transform an {item} into a {pattern} object,
-    # and adds it to the {patterns} array.
-    # Returns {patterns}.
-    _createPatternsFromData: (data) ->
-      patterns = []
-      for item in data.list
-        if item = @_parseItem(item)
+  # Transform an {item} into a {pattern} object,
+  # and adds it to the {patterns} array.
+  # Returns {patterns}.
+  createPatternsFromData: (data) ->
+    patterns = []
+    for item in data.list
+      if item = @parseItem(item)

-          pattern =
-            # begin: '^\\s*([`~]{3,})\\s*(?:\\{)((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`]*)$'
-            # begin: '^\\s*([`~]{3,})\\s*((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`]*)$'
-            begin: '^\\s*([`~]{3,})\\s*(\\{?)((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`\\}]*)(\\}?)$'
-            beginCaptures:
-              1: name: 'punctuation.md'
-              2: name: 'punctuation.md'
-              3: name: 'language.constant.md'
-              5: patterns: [{ include:'#special-attribute-elements' }]
-              6: name: 'punctuation.md'
-            end: '^\\s*(\\1)$'
-            endCaptures:
-              1: name: 'punctuation.md'
-            name: 'fenced.code.md'
-            contentName: item.contentName
-            patterns: [{ include: item.include }]
+        pattern =
+          # begin: '^\\s*([`~]{3,})\\s*(?:\\{)((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`]*)$'
+          # begin: '^\\s*([`~]{3,})\\s*((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`]*)$'
+          begin: '^\\s*([`~]{3,})\\s*(\\{?)((?:\\.?)(?:'+item.pattern+'))(?=( |$))\\s*([^`\\}]*)(\\}?)$'
+          beginCaptures:
+            1: name: 'punctuation.md'
+            2: name: 'punctuation.md'
+            3: name: 'language.constant.md'
+            5: patterns: [{ include:'#special-attribute-elements' }]
+            6: name: 'punctuation.md'
+          end: '^\\s*(\\1)$'
+          endCaptures:
+            1: name: 'punctuation.md'
+          name: 'fenced.code.md'
+          contentName: item.contentName
+          patterns: [{ include: item.include }]

-          patterns.push pattern
+        patterns.push pattern

-      return patterns
+    return patterns

-    # When provided with a valid {item} ({item.pattern} is required),
-    # missing {include} and/or {contentName} are generated.
-    _parseItem: (item) ->
-      if (typeof item is 'object') and item.pattern?
-        unless item.include then item.include = 'source.'+item.pattern
-        unless item.contentName then item.contentName = 'embedded.'+item.include
-        return item
-      else
-        return false
+  # When provided with a valid {item} ({item.pattern} is required),
+  # missing {include} and/or {contentName} are generated.
+  parseItem: (item) ->
+    if (typeof item is 'object') and item.pattern?
+      unless item.include then item.include = 'source.'+item.pattern
+      unless item.contentName then item.contentName = 'embedded.'+item.include
+      return item
+    else
+      return false


Lang ext with common and variations languages

--- lib/main.coffee
+++ lib/main.coffee
@@ -8,8 +8,7 @@

 langList = []

-Lang = ->
-  return
+Lang = {}

 Lang.setList = (list) ->
   langList = list
@@ -40,7 +39,7 @@
   if lang && _.has(langList, lang)
     Lang.init(lang)

-common =
+Lang.common =
   en: "English"
   de: "Deutsch"
   fr: "Français"
@@ -83,7 +82,7 @@
   ja: "日本語"
   ko: "한국어"

-variations =
+Lang.variations =
   "en-us": "US"
   "en": "UK"
   "es": "España"


add brunch

add brunch

--- lib/paas.coffee
+++ lib/paas.coffee
@@ -10,11 +10,15 @@
     # Setup haibu client and proxyClient.
     constructor: ->
         @proxyClient = new HttpClient "http://localhost:9104/"
+        @controllerClient = new HttpClient "http://localhost:9002/"
         @memoryManager = new MemoryManager()
         @client = haibu.createClient(
             host: '127.0.0.1'
             port: 9002
         ).drone
+        @client.brunch = (manifest, callback) =>
+            data = brunch: manifest
+            @controllerClient.post "drones/#{manifest.name}/brunch", data, callback

     # Ask to proxy to rebuild his routes.
     # Because route commands are public, we can't allow that someone add or
@@ -52,9 +56,16 @@
                         console.log err.stack
                         callback(err)
                     else
-                        console.info "Successfully spawned app: #{app.name}"
-                        console.info "Update proxy..."
-                        callback null, result
+                        @client.brunch app.getHaibuDescriptor(), (err, res) =>
+                            if err
+                                console.log "Error spawning app: #{app.name}"
+                                console.log err.message
+                                console.log err.stack
+                                callback(err)
+                            else
+                                console.info "Successfully spawned app: #{app.name}"
+                                console.info "Update proxy..."
+                                callback null, result

     # Remove and reinstall app inside Haibu.
     updateApp: (app, callback) ->
@@ -83,8 +94,15 @@
                                 console.log err.stack
                                 callback(err)
                             else
-                                console.info "Successfully update app: #{app.name}"
-                                callback null, result
+                                @client.brunch app.getHaibuDescriptor(), (err, res) =>
+                                    if err
+                                        console.log "Error spawning app: #{app.name}"
+                                        console.log err.message
+                                        console.log err.stack
+                                        callback(err)
+                                    else
+                                        console.info "Successfully update app: #{app.name}"
+                                        callback null, result

     # Send a uninstall request to haibu server ("clean" request).


engine data module

engine data module

--- src/engine_data.coffee
+++ src/engine_data.coffee
@@ -1,5 +1,5 @@
 ###*
-The Data module provides methods to store global and persistent .
+The Data module provides methods to store global and persistent data in the engine.

 @name Delay
 @fieldOf Engine


intercept newline as confirm

intercept newline as confirm

--- lib/input-method.coffee
+++ lib/input-method.coffee
@@ -1,4 +1,4 @@
-{Range} = require 'atom'
+{CompositeDisposable, Range} = require 'atom'
 {log} = require './logger'
 Keymap = require './input-method/keymap'

@@ -8,12 +8,27 @@
     activated: false
     mute: false

+    subscriptions: null
     # raw characters
     rawInput: ''
     # visual marker
     textBufferMarker: null

     constructor: (@core) ->
+
+        # intercept newline `\n` as confirm
+        commands =
+            'editor:newline': (ev) =>
+                if @activated
+                    @deactivate()
+                    ev.stopImmediatePropagation()
+
+        @subscriptions = new CompositeDisposable
+        @subscriptions.add atom.commands.add 'atom-text-editor.agda-mode-input-method-activated', commands
+
+    destroy: ->
+        @subscriptions.destroy()
+
     activate: ->
         if not @activated

@@ -21,6 +36,7 @@
             log 'IM', 'activated'
             @rawInput = ''
             @activated = true
+

             # add class 'agda-mode-input-method-activated'
             editorElement = atom.views.getView(atom.workspace.getActiveTextEditor())
@@ -34,10 +50,6 @@
             startPosition = @editor.getCursorBufferPosition()
             @textBufferMarker = @editor.markBufferRange(new Range startPosition, startPosition)
             @textBufferMarker.onDidChange @dispatchEvent
-
-            # atom.commands.add 'atom-text-editor[data-grammar="source agda"]', 'core:move-up', (ev) ->
-                # console.log 'up'
-                # console.log ev.stopImmediatePropagation?()

             # decoration
             @decoration = @editor.decorateMarker @textBufferMarker,


ink refactor

ink refactor

--- lib/julia-client.coffee
+++ lib/julia-client.coffee
@@ -53,6 +53,7 @@
     @subscriptions.dispose()
     modules.deactivate()
     cons.deactivate()
+    @spinner.dispose()

   commands: (subs) ->
     subs.add atom.commands.add 'atom-text-editor',
@@ -84,7 +85,8 @@
     @ink = ink
     evaluation.ink = ink
     cons.ink = ink
-    comm.loading = ink.loading
+    comm.loading = new ink.Loading
+    @spinner = new ink.Spinner comm.loading
     comm.handle 'show-block', ({start, end}) =>
       ink.highlight atom.workspace.getActiveTextEditor(), start-1, end-1


namespaced Movable

namespaced Movable

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -180,7 +180,6 @@

   self

-
 ###*
 Construct an object instance from the given entity data.
 @name construct


Extract Default Modules

Extract Default Modules

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -166,7 +166,10 @@
   "Expirable"
   "Follow"
   "Metered"
-  "Movable",Rotatable, TimedEvents, Tween]
+  "Movable"
+  "Rotatable"
+  "TimedEvents"
+  Tween]

 ###*
 Construct an object instance from the given entity data.


:new: Notify UI providers when messages change

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -36,6 +36,7 @@
     @linters.onDidUpdateMessages ({linter, messages, editor}) =>
       @messages.set({linter, messages, editorLinter: @editors.ofTextEditor(editor)})
     @messages.onDidUpdateMessages (messages) =>
+      @ui.notify(messages)
       @views.render(messages)
     @views.onDidUpdateScope (scope) =>
       @state.scope = scope


Both raw and final meta names now start with a slash.

--- lib/loader.coffee
+++ lib/loader.coffee
@@ -105,12 +105,12 @@
               if metatype
                 if metatype[1]      # _default.meta           => /
                   myrawmetas['/']= m
-                else if metatype[2] # something/_default.meta => something/
-                  myrawmetas[metatype[2]]= m
-                else                # anything[/]else.meta    => anything[/]else
-                  myrawmetas[metatype[3]]= m
+                else if metatype[2] # something/_default.meta => /something/
+                  myrawmetas['/'+metatype[2]]= m
+                else                # anything[/]else.meta    => /anything[/]else
+                  myrawmetas['/'+metatype[3]]= m
               else if m?.meta       # non-meta module that includes a meta attribute
-                myattribmetas[modname]= m.meta
+                myattribmetas['/'+modname]= m.meta
               else if sitesModuleSignature.test(modname)
                 siteroot= path.dirname(fullname)
                 siteLookupRoot ?= siteroot
@@ -137,15 +137,13 @@
       metanames= []
       for sitename in siteStack
         metanames= _.union(metanames,_.keys(rawmetas[sitename]),_.keys(attribmetas[sitename]))
-      for rawname in _.uniq(metanames).sort(fileNameCompare)
-        metaname= '/'+rawname
-        metaname= '/' if '//' == metaname
+      for metaname in _.uniq(metanames).sort(fileNameCompare)
         meta= meta_lookup(metaname,siteKey)
         for sitename in siteStack
-          if rawmetas[sitename] && rawmetas[sitename][rawname]
-            meta= extend(meta,rawmetas[sitename][rawname],path.join(sitename,rawname))
-          if attribmetas[sitename] && attribmetas[sitename][rawname]
-            meta= extend(meta,attribmetas[sitename][rawname],path.join(sitename,rawname+':'))
+          if rawmetas[sitename] && rawmetas[sitename][metaname]
+            meta= extend(meta,rawmetas[sitename][metaname],path.join(sitename,metaname))
+          if attribmetas[sitename] && attribmetas[sitename][metaname]
+            meta= extend(meta,attribmetas[sitename][metaname],path.join(sitename,metaname+':'))
         metacache[siteKey][metaname]= meta

   meta_lookup: meta_lookup


memory: add pivotal link

memory: add pivotal link

--- lib/memory.coffee
+++ lib/memory.coffee
@@ -2,6 +2,7 @@
 {merge} = require 'mecano/lib/misc'

 # https://github.com/hortonworks/hdp-configuration-utils/blob/master/2.1/hdp-configuration-utils.py
+# https://support.pivotal.io/hc/en-us/articles/201462036-Mapreduce-YARN-Memory-Parameters

 memory = (ctx) ->
   {yarn_site, hadoop_conf_dir} = ctx.config.ryba


:fire: leftover

:fire: leftover

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -80,7 +80,6 @@

     @editor.mergeCursors()
     @editor.mergeIntersectingSelections()
-    # @emitDidSelect()

   # Modify selection inclusively
   # -------------------------


added todo

added todo

--- _src/test/main.coffee
+++ _src/test/main.coffee
@@ -3,6 +3,8 @@
 Module = require( "../." )

 _moduleInst = null
+
+# TODO spin up a express server and test the module

 describe "----- hyperrequest TESTS -----", ->


Catch errors during greyhound dump

Catch errors during greyhound dump

--- lib/site.coffee
+++ lib/site.coffee
@@ -53,7 +53,10 @@

   dumpGreyhound: (greyhound, info, bio, callback) ->
     file = "#{self.sitePath}/_greyhounds/#{greyhound}.md"
-    data = matter.stringify bio, info, engines: engines
+    try
+      data = matter.stringify bio, info, engines: engines
+    catch err
+      return callback err
     console.log "Dump #{greyhound}"
     fs.writeFile file, data, (err) ->
       callback err


Correctly delete multibyte characters

Correctly delete multibyte characters

--- src/trix/controllers/level_2_input_controller.coffee
+++ src/trix/controllers/level_2_input_controller.coffee
@@ -33,7 +33,9 @@

   deleteContentBackward: (event) ->
     textDeleted = getTargetText(event)
+    {length} = textDeleted
     @delegate?.inputControllerWillPerformTyping()
+    @responder?.offsetSelectedRange(-length) if length > 1
     @responder?.deleteInDirection("backward")
     {textDeleted}


Fix error in embedded videos

Fix error in embedded videos

--- scripts/players.coffee
+++ scripts/players.coffee
@@ -28,7 +28,7 @@
             for s in seek.querySelectorAll('.hidden')
                 s.classList.remove('hidden')
             seek.querySelector('.progress').classList.add('hidden')
-            window.updateSize()
+            window.updateSize() if window.updateSize?
         if media.buffered.length == 0
             loaded = 100
         else


Treat movement over controls as activity

--- scripts/players.coffee
+++ scripts/players.coffee
@@ -70,12 +70,13 @@
         controls.classList.add('idle')
         video.classList.add('idle')
     timeout = null
-    video.addEventListener('mousemove', (e) ->
+    idleEvent = (e) ->
         clearTimeout(timeout)
         controls.classList.remove('idle')
         video.classList.remove('idle')
         timeout = setTimeout(idleUI, 3000)
-    , false)
+    video.addEventListener('mousemove', idleEvent, false)
+    controls.addEventListener('mousemove', idleEvent, false)

     seeking = false
     wasPaused = true


PUBDEV-1509 Switch from =bang to gput for split-frame routines.

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -940,7 +940,7 @@
       frameExpr = JSON.stringify frameKey
       randomVecKey = createTempKey()

-      _.requestExec "(= !#{randomVecKey} (h2o.runif #{frameExpr} #-1))", (error, result) ->
+      _.requestExec "(gput #{randomVecKey} (h2o.runif #{frameExpr} #-1))", (error, result) ->
         if error
           go error
         else
@@ -956,7 +956,7 @@
             else
               g

-            "(= !#{JSON.stringify part.key} ([ %#{frameExpr} #{sliceExpr} \"null\"))"
+            "(gput #{JSON.stringify part.key} ([ %#{frameExpr} #{sliceExpr} \"null\"))"

           futures = map exprs, (expr) ->
             _fork _.requestExec, expr


Staticize properties that don't need to be instance-specific

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -46,13 +46,14 @@
 # ```

 class KissmetricsClient
+  @HOST = 'trk.kissmetrics.com'
+  @QUERY_TYPES =
+    record : 'e'
+    set    : 's'
+    alias  : 'a'
+
   constructor: (@apiKey, @person) ->
-    @queries    = []
-    @host       = 'trk.kissmetrics.com'
-    @queryTypes =
-      record : 'e'
-      set    : 's'
-      alias  : 'a'
+    @queries = []

   # ### Record
@@ -187,10 +188,11 @@
       "#{key}=#{val}"

     queryString = queryParts.join '&'
+    queryType   = KissmetricsClient.QUERY_TYPES[type]

     @queries.push @_httpsRequest
-      host: @host
-      path: "#{@queryTypes[type]}?#{queryString}"
+      host: KissmetricsClient.HOST
+      path: "#{queryType}?#{queryString}"

     return @


:fire: Remove unnecessary checks

:fire: Remove unnecessary checks

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -48,7 +48,7 @@

     @subscriptions.add @pane.onDidChangeActiveItem (item) =>
       if atom.config.get('tabs.usePreviewTabs') and item instanceof TextEditor
-        if @tab instanceof TabView and @getTabs().length > 1 and @tab.item isnt item and @tab.isPreviewTab and @tab.item instanceof TextEditor
+        if @getTabs().length > 1 and @tab.item isnt item and @tab.isPreviewTab
           @pane.destroyItem @tab.item
         @tab = @tabForItem(item)
       @updateActiveTab()


:fire: Remove jQuery from mouse event handlers

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -77,29 +77,9 @@

     @updateActiveTab()

-    @on 'mousedown', '.tab', ({target, which, ctrlKey}) =>
-      tab = $(target).closest('.tab')[0]
-      if which is 3 or (which is 1 and ctrlKey is true)
-        @find('.right-clicked').removeClass('right-clicked')
-        tab.classList.add('right-clicked')
-        false
-      else if which is 1 and not target.classList.contains('close-icon')
-        @pane.activateItem(tab.item)
-        setImmediate => @pane.activate()
-        true
-      else if which is 2
-        @pane.destroyItem(tab.item)
-        false
-
-    @on 'dblclick', ({target}) =>
-      if target is @element
-        atom.commands.dispatch(@element, 'application:new-file')
-        false
-
-    @on 'click', '.tab .close-icon', ({target}) =>
-      tab = $(target).closest('.tab')[0]
-      @pane.destroyItem(tab.item)
-      false
+    @on 'mousedown', '.tab', @onMouseDown
+    @on 'dblclick', @onDoubleClick
+    @on 'click', '.tab .close-icon', @onClick

     RendererIpc.on('tab:dropped', @onDropOnOtherWindow)

@@ -374,6 +354,30 @@
     else if @wheelDelta >= @tabScrollingThreshold
       @wheelDelta = 0
       @pane.activatePreviousItem()
+
+  onMouseDown: ({target, which, ctrlKey}) =>
+    tab = closest(target, '.tab')
+    if which is 3 or (which is 1 and ctrlKey is true)
+      @element.querySelector('.right-clicked')?.classList.remove('right-clicked')
+      tab.classList.add('right-clicked')
+      false
+    else if which is 1 and not target.classList.contains('close-icon')
+      @pane.activateItem(tab.item)
+      setImmediate => @pane.activate()
+      true
+    else if which is 2
+      @pane.destroyItem(tab.item)
+      false
+
+  onDoubleClick: ({target}) =>
+    if target is @element
+      atom.commands.dispatch(@element, 'application:new-file')
+      false
+
+  onClick: ({target}) =>
+    tab = closest(target, '.tab')
+    @pane.destroyItem(tab.item)
+    false

   updateTabScrollingThreshold: ->
     @tabScrollingThreshold = atom.config.get('tabs.tabScrollingThreshold')


make tooltips more robust

make tooltips more robust

--- lib/util/tooltip.coffee
+++ lib/util/tooltip.coffee
@@ -36,6 +36,8 @@
     # remember old mouse listeners
     @oldOnMouseOver = @parent.onmouseover
     @oldOnMouseOut = @parent.onmouseout
+
+    @positionOverlay()

     @showOnHover()
     this


Updated mustache render method

Updated mustache render method

--- lib/zen.response.coffee
+++ lib/zen.response.coffee
@@ -46,7 +46,7 @@
     files = {}
     files[partial] = __mustache partial for partial in partials or []
     bindings.zen = global.ZEN
-    @html mustache.to_html(__mustache(file), bindings, files), code
+    @html mustache.render(__mustache(file), bindings, files), code

   # -- JSON responses ----------------------------------------------------------
   json: (data = {}, code, headers = {}) ->


Removed unwanted dependency

Removed unwanted dependency

--- gulp.coffee
+++ gulp.coffee
@@ -11,7 +11,6 @@
 gRename = require('gulp-rename')
 gUglify = require('gulp-uglify')
 gCoffeeLint = require('gulp-coffeelint')
-gRunSequence = require('run-sequence')
 gCoverageEnforcer = require("gulp-istanbul-enforcer");

 pipeCoffee = gLazy()


try a fix to get old oab site to login properly

--- server/accounts.coffee
+++ server/accounts.coffee
@@ -132,9 +132,10 @@

 API.accounts.token = (tok, send=true) ->
-  if tok.location? and tok.location.indexOf('openaccessbutton.org') isnt -1
-    delete tok.location
-    tok.service = 'openaccessbutton'
+  try
+    if tok and typeof tok is 'object' and tok.location? and typeof tok.location is 'string' and tok.location.indexOf('openaccessbutton.org') isnt -1
+      delete tok.location
+      tok.service = 'openaccessbutton'
   settings = API.settings.service?[tok.service]?.accounts ? API.settings.accounts
   settings.cookie ?= API.settings.accounts?.cookie
   settings.timeout ?= 30
@@ -222,6 +223,10 @@
 # login requires params.hash OR params.email and params.token OR params.timestamp and params.resume OR user
 # should provide url and service name too, and device fingerprint will be saved if provided
 API.accounts.login = (params, user, request) ->
+  try
+    if params and typeof params is 'object' and params.location? and typeof params.location is 'string' and params.location.indexOf('openaccessbutton.org') isnt -1
+      delete params.location
+      params.service = 'openaccessbutton'
   Tokens.remove 'timeout:<' + Date.now() # get rid of old tokens
   token
   user = API.accounts.retrieve(user) if typeof user is 'string'


further improvements in challenge mode

further improvements in challenge mode

--- server/src/Game.coffee
+++ server/src/Game.coffee
@@ -50,9 +50,10 @@
     forbidden: []
     currentPlayer: 0 #this tells us the index of the player who's turn it is, and is automatically incremented after each resource move

-  constructor: (players, gameNumber) ->
+  constructor: (players, gameNumber, gameSize) ->
     @players = players
     @gameNumber = gameNumber
+    if gameSize? then @playerLimit = gameSize
     @nowJsGroupName = "game#{gameNumber}"
     @allocate()

@@ -177,6 +178,8 @@
       index

   moveToRequired: (index, clientId) ->
+    if @challengeMode
+      throw "Can't move during challenge mode"
     if !@goalHasBeenSet()
       throw "Can't move yet, goal has not been set"
     if !@authenticateMove(clientId)
@@ -189,6 +192,8 @@
       @nextTurn()

   moveToOptional: (index, clientId) ->
+    if @challengeMode
+      throw "Can't move during challenge mode"
     if !@goalHasBeenSet()
       throw "Can't move yet, goal has not been set"
     if !@authenticateMove(clientId)
@@ -201,6 +206,8 @@
       @nextTurn()

   moveToForbidden: (index, clientId) ->
+    if @challengeMode
+      throw "Can't move during challenge mode"
     if !@goalHasBeenSet()
       throw "Can't move yet, goal has not been set"
     if !@authenticateMove(clientId)
@@ -213,6 +220,8 @@
       @nextTurn()

   nowChallenge: (clientId) ->
+    if @challengeMode
+      throw "Can't challenge during challenge mode"
     if !validateChallenge(clientId)
       throw "Can't make challenge, you've just moved"
     @challengeMode = true
@@ -226,6 +235,8 @@
     @possiblePlayers.push(clientId)

   submitImpossible: (clientId) ->
+    if !@challengeMode
+      throw "Can't submit opinion, not currently challenge mode"
     if (clientId in @possiblePlayers) || (clientId in @impossiblePlayers) || (clientId == @challenger)
       throw "Already stated your opinion"
     @impossiblePlayers.push(clientId)


dot dot

dot dot

--- client/app/MainApp/monitor_status.coffee
+++ client/app/MainApp/monitor_status.coffee
@@ -1,5 +1,5 @@
 class Ping extends KDObject
-  [NOTSTARTED, WAITING, SUCCESS, FAILED] = [1...4]
+  [NOTSTARTED, WAITING, SUCCESS, FAILED] = [1..4]

   constructor: (item, name, options={}) ->
     super options


Delete a member

Delete a member

--- app/coffee/modules/admin/memberships.coffee
+++ app/coffee/modules/admin/memberships.coffee
@@ -43,8 +43,9 @@
     ]

     constructor: (@scope, @rootScope, @repo, @confirm, @rs, @params, @q, @location) ->
+        @scope.sectionName = "Memberships" #i18n
+        @scope.project = {}
         @scope.filters = {}
-        @scope.sectionName = "Memberships"

         promise = @.loadInitialData()
         promise.then null, ->
@@ -251,7 +252,7 @@
 ## Member Actions Directive
 #############################################################################

-MembershipsMemberActionsDirective = ($log) ->
+MembershipsMemberActionsDirective = ($log, $repo, $confirm) ->
     activedTemplate = _.template("""

         Active
@@ -265,7 +266,7 @@
         Pending

-
+

     """) # i18n
@@ -276,6 +277,8 @@
         return pendingTemplate()

     link = ($scope, $el, $attrs) ->
+        $ctrl = $el.controller()
+
         if not $attrs.tgMembershipsMemberActions?
             return $log.error "MembershipsMemberActionsDirective: the directive need a member"

@@ -283,13 +286,30 @@
         html = render(member)
         $el.html(html)

+        $el.on "click", ".pending", (event) ->
+            event.preventDefault()
+            #TODO: Re-send the invitation
+            console.log "re-sending the invitation to #{member.email}"
+
+        $el.on "click", ".delete", (event) ->
+            event.preventDefault()
+
+            title = "Delete member" # i18n
+            subtitle = if member.user then member.full_name else "the invitation to #{member.email}" # i18n
+
+            $confirm.ask(title, subtitle).then ->
+                $repo.remove(member).then ->
+                    $ctrl.loadMembers()
+                    $confirm.notify("success", null, "We've deleted #{subtitle}.") # i18n
+
         $scope.$on "$destroy", ->
             $el.off()

     return {link: link}

-module.directive("tgMembershipsMemberActions", ["$log", MembershipsMemberActionsDirective])
+module.directive("tgMembershipsMemberActions", ["$log", "$tgRepo", "$tgConfirm",
+                                                MembershipsMemberActionsDirective])

 #############################################################################


Show a message if there is not a description

--- app/coffee/modules/common/components.coffee
+++ app/coffee/modules/common/components.coffee
@@ -504,7 +504,8 @@
 EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading) ->
     template = """

-

+

@@ -513,11 +514,24 @@
                       tg-markitup="tg-markitup"></textarea>

-    """
+    """ # TODO: i18n
+    noDescriptionMegEditMode = """
+

+        -- no description yet, why don't you add a good one clicking here? --
+

+    """ # TODO: i18n
+    noDescriptionMegReadMode = """
+

+        -- no description yet --
+

+    """ # TODO: i18n

     link = ($scope, $el, $attrs, $model) ->
-        $scope.$on "$destroy", ->
-            $el.off()
+        $el.find('div.edit-description').hide()
+        $el.find('div.view-description span.edit').hide()
+
+        isEditable = ->
+            return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1

         $el.on "click", ".view-description", (event) ->
             # We want to dettect the a inside the div so we use the target and
@@ -552,11 +566,15 @@
         $scope.$watch $attrs.ngModel, (value) ->
             return if not value
             $scope.item = value
-            if $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
+
+            if isEditable
                 $el.find('div.view-description span.edit').show()
-
-        $el.find('div.edit-description').hide()
-        $el.find('div.view-description span.edit').hide()
+                $scope.noDescriptionMsg = noDescriptionMegEditMode
+            else
+                $scope.noDescriptionMsg = noDescriptionMegReadMode
+
+        $scope.$on "$destroy", ->
+            $el.off()

     return {
         link: link


Fix typo

Fix typo

--- client/lib/cordova/facebook-login.coffee
+++ client/lib/cordova/facebook-login.coffee
@@ -16,12 +16,12 @@
 		facebookConnectPlugin.getLoginStatus (response) ->
 			if response.status isnt "connected"
 				facebookConnectPlugin.login ["public_profile", "email"], fbLoginSuccess, (error) ->
-					console.log(JSON.stringidy(error), error)
+					console.log(JSON.stringify(error), error)
 			else
 				fbLoginSuccess(response)

 		, (error) ->
-			console.log(JSON.stringidy(error), error)
+			console.log(JSON.stringify(error), error)

 	else
 		Facebook.requestCredential(options, credentialRequestCompleteCallback)


fixed bug on changing recorded memory

fixed bug on changing recorded memory

--- client/src/app/game/player/player.coffee
+++ client/src/app/game/player/player.coffee
@@ -182,7 +182,7 @@
           for index, results of message.value
             # Copy over old inputIoData
             frameIndex = gameTime.currentFrameNumber + parseInt(index)
-            gameHistory.data.frames[frameIndex] = extendFrameResults(results, lastMemory, gameHistory.data.frames[index].inputIoData)
+            gameHistory.data.frames[frameIndex] = extendFrameResults(results, lastMemory, gameHistory.data.frames[frameIndex].inputIoData)

             if results.errors
               console.error("Update frames errors", results.errors)


Fix water splashes

Fix water splashes

--- app/scripts/components/ShipSpawnable.coffee
+++ app/scripts/components/ShipSpawnable.coffee
@@ -36,7 +36,7 @@
     @ship.playerColor = @color()
     @ship.colorOverride?(@color(), 'partial') #if @has('ColorEffects')
     @ship.color?(@color()) if @has('Color')
-    @ship.setSealevel((Crafty.viewport.height - 20) + @level.sealevelOffset)
+    @ship.setSealevel((Crafty.viewport.height - 20) + (@level.sealevelOffset ? 0))

     @assignControls(@ship) if @has('ControlScheme')


Allow respawning if already having ship

Allow respawning if already having ship

--- app/scripts/components/ShipSpawnable.coffee
+++ app/scripts/components/ShipSpawnable.coffee
@@ -19,6 +19,14 @@

     pos = @spawnPosition()
     pos.x = 10 if pos.x < 10
+    if @ship?
+      pos = {
+        x: @ship.x + Crafty.viewport.x
+        y: @ship.y + Crafty.viewport.y
+      }
+      armed = @ship.items
+      @ship.destroy()
+      @ship = null

     @ship = Crafty.e(@level.getShipType())
       .attr


Implement adding types

Implement adding types

--- app/scripts/controllers/addtypemodal.coffee
+++ app/scripts/controllers/addtypemodal.coffee
@@ -6,7 +6,7 @@
     SERVER_URL = "https://#{location.hostname}:8080"

     $scope.type = {}
-    $scope.task = args.task
+    $scope.isRelation = args.relation
     $scope.baseTypes = []
     $scope.groups = []
     $scope.selectedBase = undefined
@@ -18,16 +18,16 @@
             $scope.baseTypes.push baseType

     $scope.save = () ->
-
+        return if not $scope.selectedBase or not $scope.type.label
+        $scope.type.type_id = $scope.selectedBase.id
         $http.put(
-            SERVER_URL + "/task_types/0"
-            {"type": $scope.type}
+            SERVER_URL + "/task_types/-1"
+            {"type": $scope.type, "relation": args.relation, "task": args.task}
         ).then(
             (success) ->
                 $scope.$close()
             (error) ->
                 $scope.$close()
-                $scope.$parent.alert("The new type could not be saved", "danger")
         )

     return


renaming findIndex to findQuadrant

renaming findIndex to findQuadrant

--- src/quad_tree.coffee
+++ src/quad_tree.coffee
@@ -54,7 +54,7 @@
     BOTTOM_LEFT = 2
     BOTTOM_RIGHT = 3

-    findIndex = (item) ->
+    findQuadrant = (item) ->
       bounds = I.bounds

       x = bounds.x
@@ -111,7 +111,7 @@

       insert: (item) ->
         if I.nodes.length
-          index = findIndex(item)
+          index = findQuadrant(item)

           I.nodes[index].insert(item)

@@ -128,7 +128,7 @@
           I.children.clear()

       retrieve: (item) ->
-        index = findIndex(item)
+        index = findQuadrant(item)

         return I.nodes[index]?.retrieve(item) || I.children


Fix for when the array is empty

Fix for when the array is empty

`$$.fn.hasClass` and `$$.fn.style` will both throw exceptions if the current selection of elements is empty.

--- src/quo.style.coffee
+++ src/quo.style.coffee
@@ -24,12 +24,12 @@
                 @className = @className.trim()

     $$.fn.hasClass = (name) ->
-        _existsClass name, this[0].className
+        this.length > 0 and _existsClass name, this[0].className

     $$.fn.style = (property, value) ->
         if value
             @each -> @style[property] = value
-        else
+        else if this.length > 0
             this[0].style[property] or _computedStyle(this[0], property)

     $$.fn.css = (property, value) -> @style property, value


client: prompt user on /Teamwork if they want to move away

--- client/Main/ApplicationManager.coffee
+++ client/Main/ApplicationManager.coffee
@@ -26,7 +26,7 @@
     # set unload listener
     wc = KD.singleton 'windowController'
     wc.addUnloadListener 'window', =>
-      for own app of @appControllers when app in ['Ace', 'Terminal']
+      for own app of @appControllers when app in ['Ace', 'Terminal', 'Teamwork']
         safeToUnload = no
         break
       return safeToUnload ? yes
@@ -390,4 +390,4 @@
   #   KD.whoami().fetchAppStorage {appId, version}, (error, storage) =>
   #     unless storage
   #       storage = {appId,version,bucket:{}} # creating a fake storage
-  #     callback error, storage+  #     callback error, storage


Clean up logic of the option matcher in Brainstem.Expectation

--- vendor/assets/javascripts/brainstem/brainstem-expectation.coffee
+++ vendor/assets/javascripts/brainstem/brainstem-expectation.coffee
@@ -62,6 +62,8 @@
     @manager._checkPageSettings options
     if !@disabled && @collectionName == name
       _(['include', 'only', 'order', 'filters', 'perPage', 'page', 'limit', 'offset', 'search']).all (optionType) =>
+        return true if @options[optionType] == "*"
+
         option = _.compact(_.flatten([options[optionType]]))
         expectedOption = _.compact(_.flatten([@options[optionType]]))

@@ -69,7 +71,7 @@
           option = Brainstem.Utils.wrapObjects(option)
           expectedOption = Brainstem.Utils.wrapObjects(expectedOption)

-        @options[optionType] == "*" || Brainstem.Utils.matches(option, expectedOption)
+        Brainstem.Utils.matches(option, expectedOption)
     else
       false


realtime: set ssl value depending on protocol

--- client/Main/realtimecontroller.coffee
+++ client/Main/realtimecontroller.coffee
@@ -27,7 +27,7 @@
       @pubnub = PUBNUB.init
         subscribe_key : subscribekey
         uuid          : KD.whoami()._id
-        ssl           : ssl
+        ssl           : if KD.config.environment is 'dev' then window.location.protocol is 'https:' else ssl

       @getTimeDiffWithServer()


realtime: fix events not received when computer time of client is ahead of pubnub server bug

--- client/Main/realtimecontroller.coffee
+++ client/Main/realtimecontroller.coffee
@@ -19,12 +19,19 @@
     super options, data

     {subscribekey, ssl} = KD.config.pubnub
+    @timeDiff = 0

     if KD.isPubnubEnabled()
       @pubnub = PUBNUB.init
         subscribe_key : subscribekey
         uuid          : KD.whoami()._id
         ssl           : ssl
+
+      @pubnub.time (serverTime) =>
+        diff = new Date() * 10000 - serverTime
+        # when time difference between pubnub server and client is less than 500ms
+        # ignore the difference
+        @timeDiff = if Math.abs(diff) < 5000000 then 0 else diff

       realtimeToken = Cookies.get("realtimeToken")

@@ -197,7 +204,9 @@
         # and some messages are dropped in this resubscription time interval
         # for this reason for every subscribe request, we are fetching all messages sent
         # in last 3 seconds
-        timetoken: ((new Date()).getTime() - 3000) * 10000
+        # another issue is if user's computer time is ahead of server, it is not receiving the events.
+        # this timeDiff is added because of this problem. https://www.pivotaltracker.com/story/show/87093608
+        timetoken: (((new Date()).getTime() - 3000) * 10000) - @timeDiff
         restore : yes


KodingKite: set 60sec. timeout for max connection wait time

--- client/app/lib/kite/kodingkite.coffee
+++ client/app/lib/kite/kodingkite.coffee
@@ -12,7 +12,7 @@

   [DISCONNECTED, CONNECTED] = [0, 1]
   MAX_QUEUE_SIZE = 50
-
+  MAX_WAITING_TIME = 60000 # 1 min.

   init: ->

@@ -78,6 +78,8 @@
         KiteLogger.queued name, rpcMethod

         @waitForConnection _args
+
+          .timeout MAX_WAITING_TIME

           .then (args)=>
             KiteLogger.started name, rpcMethod


ensure options is set

ensure options is set

--- client/lanes/models/Collection.coffee
+++ client/lanes/models/Collection.coffee
@@ -103,6 +103,7 @@

     # Sets the attribute data from a server respose
     setFromServer: (data, options, method) ->
+        options ||= {}
         @_isLoaded = true
         if 'delete' == method
             models = _.map(_.map(options.originalData, 'id'), (id) =>


removed setting of meta tags for facebook

--- client/views/facebook/facebook.coffee
+++ client/views/facebook/facebook.coffee
@@ -5,19 +5,10 @@
         template = Template.instance()
         data = Template.currentData()

-        $('meta[property^="og:"]').remove()
-        #
-        # OpenGraph tags
-        #
         description = data.facebook?.description || data.excerpt || data.description || data.summary
         url = data.url || location.origin + location.pathname
         title = data.title
-        $('<meta>', { property: 'og:type', content: 'article' }).appendTo 'head'
-        $('<meta>', { property: 'og:site_name', content: location.hostname }).appendTo 'head'
-        $('<meta>', { property: 'og:url', content: url }).appendTo 'head'
-        $('<meta>', { property: 'og:title', content: title }).appendTo 'head'
-        $('<meta>', { property: 'og:description', content: description }).appendTo 'head'
-
+
         if data.thumbnail
             if typeof data.thumbnail == "function"
                 img = data.thumbnail()
@@ -26,8 +17,17 @@
         if img
             if not /^http(s?):\/\/+/.test(img)
                 img = location.origin + img
-
-        $('<meta>', { property: 'og:image', content: img }).appendTo 'head'
+
+        #
+        # OpenGraph tags
+        #
+#        $('meta[property^="og:"]').remove()
+#        $('<meta>', { property: 'og:type', content: 'article' }).appendTo 'head'
+#        $('<meta>', { property: 'og:site_name', content: location.hostname }).appendTo 'head'
+#        $('<meta>', { property: 'og:url', content: url }).appendTo 'head'
+#        $('<meta>', { property: 'og:title', content: title }).appendTo 'head'
+#        $('<meta>', { property: 'og:description', content: description }).appendTo 'head'
+#        $('<meta>', { property: 'og:image', content: img }).appendTo 'head'

         if ShareIt.settings.sites.facebook.appId?
             template.$('.fb-share').click (e) ->


JMachine: make inform optional, and disable it for deny requests

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -302,7 +302,7 @@

   removeUsers: (options, callback)->

-    {targets, permanent} = options
+    {targets, permanent, inform} = options

     users = @users.splice 0

@@ -310,16 +310,17 @@
       users = excludeUser { users, user, permanent }

     @update $set: { users }, (err)->
-      informAccounts targets, @getAt 'uid'
+      informAccounts targets, @getAt 'uid'  if inform
       callback err

   shareWith: (options, callback)->

-    { target, asUser, asOwner, permanent } = options
-
-    asUser    ?= yes
-    asOwner   ?= no
+    { target, asUser, asOwner, permanent, inform } = options
+
+    asUser  ?= yes
+    asOwner ?= no
+    inform  ?= yes

     unless target?
       return callback new KodingError "Target required."
@@ -340,7 +341,7 @@

         if asUser
         then @addUsers {targets, asOwner, permanent}, callback
-        else @removeUsers {targets, permanent}, callback
+        else @removeUsers {targets, permanent, inform}, callback

       else
         callback new KodingError "Target does not support machines."
@@ -630,7 +631,11 @@
     if isOwner user, this
       return callback null

-    options = target: [user.username], asUser: no, permanent: yes
+    options =
+      target    : [user.username]
+      asUser    : no
+      inform    : no
+      permanent : yes

     @shareWith options, (err)->
       callback err


JMachine::shareWith: to support multiple share operation at once by passing array of usernames as target

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -375,26 +375,41 @@
         @update $set: { label }, (err)-> kallback err, slug

-  addUser: (user, owner, callback)->
-
-    userId = user.getId()
-    users  = []
-
-    for u in @users
-      users.push u  unless userId.equals u.id
-
-    users.push { id: userId, owner }
+  removeUser = (users, user)->
+
+    userId   = user.getId()
+    newUsers = []
+
+    for u in users
+      newUsers.push u  unless userId.equals u.id
+
+    return newUsers
+
+
+  addUser = (users, user, owner)->
+
+    newUsers = removeUser users, user
+    newUsers.push { id: user.getId(), owner }
+
+    return newUsers
+
+
+  addUsers: (usersToAdd, owner, callback)->
+
+    users = @users.splice 0
+
+    for user in usersToAdd
+      users = addUser users, user, owner

     @update $set: { users }, callback

-  removeUser: (user, callback)->
-
-    userId = user.getId()
-    users  = []
-
-    for u in @users
-      users.push u  unless userId.equals u.id
+  removeUsers: (usersToRemove, callback)->
+
+    users = @users.splice 0
+
+    for user in usersToRemove
+      users = removeUser users, user

     @update $set: { users }, callback

@@ -416,13 +431,15 @@
       if err or not result?
         return callback new KodingError "Target not found."

-      [ target ] = result.models
+      targets = (target.models[0]  for target in result)
+
+      [ target ] = targets

       if target instanceof JUser

         if user
-        then @addUser target, owner, callback
-        else @removeUser target, callback
+        then @addUsers targets, owner, callback
+        else @removeUsers targets, callback

       else
         callback new KodingError "Target does not support machines."


:bug: fix

:bug: fix

--- lib/developer.coffee
+++ lib/developer.coffee
@@ -9,6 +9,7 @@
 Scroll      = require './scroll'
 settings    = require './settings'
 {debug}     = require './utils'
+introspection = require './introspection'

 module.exports =
 class Developer


Travi build

Travi build

--- lib/model/apn.coffee
+++ lib/model/apn.coffee
@@ -28,7 +28,7 @@
     @connection = new apn.Connection(opts)

   send: (opts)->
-
+    device = new apn.Device opts.token

 module.exports = new APN()


Doesn't try to set the label off element when not drawn yet

--- js/controls/onoffbutton/onoffbutton.coffee
+++ js/controls/onoffbutton/onoffbutton.coffee
@@ -25,7 +25,7 @@
     @offLabel = _label
     @refreshLabelOff()
   refreshLabelOff: ->
-    @setMarkupOfPart( 'offlabel', @labelOff )
+    @setMarkupOfPart( 'offlabel', @labelOff ) if @drawn
   refreshValue: ->
     if @value != false
       @setStyleOfPart( 'offvalue', 'display', 'none' )