arvid branch merged

arvid branch merged

--- core/utils.coffee
+++ core/utils.coffee
@@ -118,7 +118,7 @@
     return null unless text
     text.replace /\n/g, "<br />"

-  applyTextExpansions: (text)->
+  applyTextExpansions: (text, shorten)->
     return null unless text
     # @expandWwwDotDomains @expandUrls @expandUsernames @expandTags text
     text = text.replace /
/g, ' '


regexp catches more precisely and has better explanation

--- core/utils.coffee
+++ core/utils.coffee
@@ -165,7 +165,22 @@

   expandUrls: (text) ->
     return null unless text
-    text.replace /([a-zA-Z]+\:\/\/)?(\w+:\w+@)?[a-zA-Z\d\.-]+\.([a-zA-Z]{2,4}(:\d+)?)([\/\?][\S\/]*)*\b\/?/g, (url) ->
+
+    urlGrabber = ///
+      (\s|^)                              # Start after a whitespace or string[0]
+      ([a-zA-Z]+\://)?                    # Captures any protocol (just not //)
+      (\w+:\w+@)?                         # Username:Password
+      ([a-zA-Z\d-]|[a-zA-Z\d-]\.)*        # Subdomains
+      [a-zA-Z\d-]{2,}                     # Domain name
+      \.                                  # THE DOT
+      ([a-zA-Z]{2,4}(:\d+)?)              # Domain Extension with Port
+      ([/\?\#][\S/]*)*                    # Some Request, greedy capture
+      \b                                  # Last word boundary
+      /?                                  # Optional trailing Slash
+    ///g
+
+    # used to be /(\s|^)([a-zA-Z]+\:\/\/)?(\w+:\w+@)?([a-zA-Z\d-]|[a-zA-Z\d-]\.)*[a-zA-Z\d-]{2,}\.([a-zA-Z]{2,4}(:\d+)?)([\/\?#][\S\/]*)*\b\/?/g
+    text.replace urlGrabber, (url) ->
       originalUrl = url

       # remove protocol and trailing path


next out of loadTeam even if there's no id

--- controllers/teams.coffee
+++ controllers/teams.coffee
@@ -10,6 +10,7 @@
       return res.send 404 unless team
       req.team = team
       next()
+  next()

 loadPeople = (req, res, next) ->
   req.team.people (err, people) ->


[grunt] do not use custom directory for thumbnails and instead store resized images right next to originals

--- tasks/grunt-responsive-image.coffee
+++ tasks/grunt-responsive-image.coffee
@@ -8,7 +8,7 @@
     thumbnails:
       options:
         sizes: [
-            width: 240
+          width: 240
         ]
       files: [
         expand: true
@@ -17,5 +17,5 @@
         # {,models,blog/posts}
         # @note Remove `!` character to enable resizing of images
         src: '!{,**/}*.{jpg,jpeg,gif,png}'
-        custom_dest: '<%= path.build.thumbnails %>/{%= path %}/{%= width %}'
+        dest: '<%= path.build.images %>'
       ]


mochacompat: Move Mocha import to module level again

Caused ReferenceError

--- src/mochacompat.coffee
+++ src/mochacompat.coffee
@@ -12,13 +12,13 @@
 path = require 'path'
 http = require 'http'
 websocket = require './websocket' # FIXME: split out transport interface of noflo-runtime-*, use that directly
+Mocha = require 'mocha'

 debug = require('debug')('fbp-spec:mochacompat')
 testsuite = require './testsuite'

 loadTests = (files) ->
   options = {}
-  Mocha = require 'mocha'
   mocha = new Mocha options

   for f in files


Pass the event to $ajax, so data-loading-text works

Conflicts:
	resources/assets/js/inner/billing.coffee

--- js/inner/billing.coffee
+++ js/inner/billing.coffee
@@ -266,11 +266,12 @@

     StripeCheckout.open(_.extend @stripeDefaults(), vals)

-  ajaxCancelPlan: () =>
+  ajaxCancelPlan: (_, event) =>
     console.log("ajaxCancelPlan")
     $.ajax
       url: @apiURL('plan')
       type: 'DELETE'
+      event: event
       data: JSON.stringify
         'cancel-reasons': @cancel_reasons()
         'cancel-notes': @cancel_notes()


Fix user menu to allow cmd+click links

Fix user menu to allow cmd+click links

--- assets/javascripts/views/user_menu.js.coffee
+++ assets/javascripts/views/user_menu.js.coffee
@@ -13,8 +13,9 @@
       [@scrollX, @scrollY] = [window.scrollX, window.scrollY]
       @block_click = true
     @$el.on 'touchend', @toggle
-    @$el.on 'click', =>
+    @$el.on 'click', (e) =>
       return false if @block_click
+      return true if e.target.tagName == 'A'
       return unless url = @$el.attr('href')
       window.location.href = url


Fixes #3259 -- Use placeholders when adding params to scope

Don't register nested variables of complex parameters as parameters of the compiled function. Use the computed placeholder name instead.

--- nodes.coffee
+++ nodes.coffee
@@ -1336,8 +1336,8 @@
     delete o.isExistentialEquals
     params = []
     exprs  = []
-    @eachParamName (name) -> # this step must be performed before the others
-      unless o.scope.check name then o.scope.parameter name
+    for param in @params
+      o.scope.parameter param.asReference o
     for param in @params when param.splat
       for {name: p} in @params
         if p.this then p = p.properties[0].name


nodes: added THIS

nodes: added THIS

--- nodes.coffee
+++ nodes.coffee
@@ -9,8 +9,9 @@
 {compact, flatten, merge, del, include, indexOf, starts, ends, last} = require './helpers'

 # Constant functions for nodes that don't need customization.
-YES = -> yes
-NO  = -> no
+YES  = -> yes
+NO   = -> no
+THIS = -> this

 #### BaseNode

@@ -139,7 +140,7 @@
   class:    'BaseNode'
   children: []

-  unwrap          : -> this
+  unwrap          : THIS
   isStatement     : NO
   isPureStatement : NO
   isComplex       : YES
@@ -273,8 +274,7 @@
   constructor: (@expression) ->
     super()

-  makeReturn: ->
-    this
+  makeReturn: THIS

   compile: (o) ->
     expr = @expression.makeReturn()
@@ -408,8 +408,7 @@
   constructor: (@comment) ->
     super()

-  makeReturn: ->
-    this
+  makeReturn: THIS

   compileNode: (o) ->
     @tab + '/*' + @comment.replace(/\n/g, '\n' + @tab) + '*/'
@@ -1283,8 +1282,7 @@
     super()

   # A **ThrowNode** is already a return, of sorts...
-  makeReturn: ->
-    return this
+  makeReturn: THIS

   compileNode: (o) ->
     "#{@tab}throw #{@expression.compile(o)};"


fixes #1630: `in` should check indices of the right operand when it can pass a `hasOwnProperty` check for them

--- nodes.coffee
+++ nodes.coffee
@@ -590,7 +590,6 @@

   # Hooks one constructor into another's prototype chain.
   compile: (o) ->
-    utility 'hasProp'
     new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o

 #### Access
@@ -1593,7 +1592,7 @@
     varPart     = "\n#{idt1}#{namePart};" if namePart
     if @object
       forPart   = "#{ivar} in #{svar}"
-      guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" if @own
+      guardPart = "\n#{idt1}if (!#{utility 'hasProp'}.call(#{svar}, #{ivar})) continue;" if @own
     body        = body.compile merge(o, indent: idt1), LEVEL_TOP
     body        = '\n' + body + '\n' if body
     """
@@ -1801,35 +1800,35 @@

   # Correctly set up a prototype chain for inheritance, including a reference
   # to the superclass for `super()` calls, and copies of any static properties.
-  extends: '''
+  extends: -> """
     function(child, parent) {
-      for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
+      for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; }
       function ctor() { this.constructor = child; }
       ctor.prototype = parent.prototype;
       child.prototype = new ctor;
       child.__super__ = parent.prototype;
       return child;
     }
-  '''
+  """

   # Create a function bound to the current value of "this".
-  bind: '''
+  bind: -> '''
     function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
   '''

   # Discover if an item is in an array.
-  indexOf: '''
+  indexOf: -> """
     Array.prototype.indexOf || function(item) {
       for (var i = 0, l = this.length; i < l; i++) {
-        if (this[i] === item) return i;
+        if (#{utility 'hasProp'}.call(this, i) && this[i] === item) return i;
       }
       return -1;
     }
-  '''
+  """

   # Shortcuts to speed up the lookup time for native functions.
-  hasProp: 'Object.prototype.hasOwnProperty'
-  slice  : 'Array.prototype.slice'
+  hasProp: -> 'Object.prototype.hasOwnProperty'
+  slice  : -> 'Array.prototype.slice'

 # Levels indicate a node's position in the AST. Useful for knowing if
 # parens are necessary or superfluous.
@@ -1871,7 +1870,7 @@
 # Helper for ensuring that utility functions are assigned at the top level.
 utility = (name) ->
   ref = "__#{name}"
-  Scope.root.assign ref, UTILITIES[name]
+  Scope.root.assign ref, UTILITIES[name]()
   ref

 multident = (code, tab) ->


Use _.extend instead of the hand-rolled merge

--- nodes.coffee
+++ nodes.coffee
@@ -20,11 +20,10 @@
   memo

 # Merge objects.
-merge: (src, dest) ->
-  fresh: {}
-  (fresh[key]: val) for key, val of src
-  (fresh[key]: val) for key, val of dest
-  fresh
+merge: (options, overrides) ->
+  _.tap {}, (fresh) ->
+    _.extend fresh, options
+    _.extend fresh, overrides

 # Delete a key from an object, returning the value.
 del: (obj, key) ->


replace 'git add .' to 'git add --all'

replace 'git add .' to 'git add --all'

--- lib/git.coffee
+++ lib/git.coffee
@@ -30,7 +30,7 @@
 git =
   sync: (callback) ->
     mergeConflictsDetect().then () =>
-      @gitCmd args: ['add', '.']
+      @gitCmd args: ['add', '--all']
     .then () =>
       @gitCmd args: ['status', '-s']
     .then (resolve) =>


Closes #20; Removed jQuery.load for QueryCSS

QueryCSS now grabs an instance of the containing element when getStyles is called as opposed to creating the container on load.

--- coffee/epoch.coffee
+++ coffee/epoch.coffee
@@ -322,19 +322,21 @@
   @purge: ->
     QueryCSS.cache = {}

-  # Called on load to insert the css reference element container.
-  @load: ->
+  # Gets the reference element container.
+  @getContainer: ->
+    return QueryCSS.container if QueryCSS.container?
     $('body').append('

')
     QueryCSS.container = $('#_canvas_css_reference', 'body')

-  # @return [String] A unique identifier for the given container.
+  # @return [String] A unique identifier for the given container and selector.
+  # @param [String] selector Selector from which to derive the styles
   # @param container The containing element for a chart.
-  @containerId: (container) ->
+  @hash: (selector, container) ->
     id = container.attr('id')
     if (klass = container.attr('class'))?
       klass = klass.split(/\s+/).join('.')

-    if id? and klass?
+    pre = if id? and klass?
       "\##{id}.#{klass}"
     else if id?
       "\##{id}"
@@ -343,19 +345,23 @@
     else
       ""

+    return "#{pre}__#{selector}"
+
   # @return The computed styles for the given selector in the given container element.
   # @param [String] selector Selector from which to derive the styles.
   # @param container HTML containing element in which to place the reference SVG.
   @getStyles: (selector, container) ->
     # 0) Check for cached styles
-    cacheKey = "#{QueryCSS.containerId(container)}__#{selector}"
+    cacheKey = QueryCSS.hash(selector, container)
     cache = QueryCSS.cache[cacheKey]
     return cache if cache?

     # 1) Create Reference SVG
     clone = $(container).clone().html('<svg></svg>')
     clone.removeAttr('style')
-    QueryCSS.container.append(clone)
+
+
+    QueryCSS.getContainer().append(clone)
     svg = $('svg', clone)

     # 2) Create Reference Element
@@ -375,11 +381,9 @@
     QueryCSS.cache[cacheKey] = styles

     # 4) Cleanup and return the styles
-    $(clone, QueryCSS.container).remove()
+    $(clone, QueryCSS.getContainer()).remove()
     return styles

-$ QueryCSS.load
-
 Epoch.QueryCSS = QueryCSS

 # "I think, baby, I was born just a little late!" -- Middle Class Rut


Stray log

Stray log

--- source/coffee/index.coffee
+++ source/coffee/index.coffee
@@ -102,7 +102,6 @@

       ret = []
       ret.push (ntrapy.toArray n?.args)... for n in @wsPlans()?.plan
-      console.log "Ret: ", ret
       ret

     @getActions = (node) =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -14,6 +14,7 @@
     flashTargetAlpha: 0

   fadeInDefaults =
+    dur
     color: 'black'

   fadeOutDefaults =


fixed coffeelint errors on workers/social/lib/social/models/github.coffee

--- workers/social/lib/social/models/github.coffee
+++ workers/social/lib/social/models/github.coffee
@@ -1,4 +1,5 @@
-{ Base, signature, JsPath:{getAt, setAt} } = require 'bongo'
+  # coffeelint: disable=no_implicit_braces
+{ Base, signature, JsPath:{ getAt, setAt } } = require 'bongo'

 { argv }  = require 'optimist'
 GithubAPI = require 'github'
@@ -19,7 +20,7 @@

   @set
     permissions           :
-      'api access'        : ['member','moderator']
+      'api access'        : ['member', 'moderator']

     sharedMethods         :
       static              :
@@ -42,7 +43,7 @@

     gh = new GithubAPI {
       version : '3.0.0' # API version, not configurable
-      headers : 'user-agent': userAgent
+      headers : { 'user-agent': userAgent }
       debug, timeout
     }

@@ -171,3 +172,5 @@
       gh[base][method] options, cb
     catch err
       cb err
+
+


fix error instantiating close wrapper

fix error instantiating close wrapper

--- bokehjs/src/coffee/widget/crossfilter.coffee
+++ bokehjs/src/coffee/widget/crossfilter.coffee
@@ -238,7 +238,7 @@
         @listenTo(view, 'remove', @child_remove)
       )
       _.map(filter_widgets, (model) =>
-        wrapper = new CloseWrapper(view : @views[model.id])
+        wrapper = new CloseWrapper.View(view : @views[model.id])
         @$el.find('.bk-filters-selections').append(wrapper.$el)
       )


Renabled node tests

Renabled node tests

--- config/run_tests.coffee
+++ config/run_tests.coffee
@@ -10,14 +10,14 @@
   queue = new Queue(1)

   # run node tests
-  # queue.defer (callback) ->
-  #   gutil.log 'Running Node.js tests'
-  #   global.test_parameters = require '../test/parameters' # ensure that globals for the target backend are loaded
-  #   gulp.src('test/spec/**/*.coffee')
-  #     .pipe(mocha({}))
-  #     .pipe es.writeArray (err, array) ->
-  #       delete global.test_parameters # cleanup globals
-  #       callback(err)
+  queue.defer (callback) ->
+    gutil.log 'Running Node.js tests'
+    global.test_parameters = require '../test/parameters' # ensure that globals for the target backend are loaded
+    gulp.src('test/spec/**/*.coffee')
+      .pipe(mocha({}))
+      .pipe es.writeArray (err, array) ->
+        delete global.test_parameters # cleanup globals
+        callback(err)

   # run browser tests
   queue.defer (callback) ->


bouboup holds

bouboup holds

--- src/coffee/Main.coffee
+++ src/coffee/Main.coffee
@@ -211,6 +211,8 @@
 			# 	# 		for mesh in food.children
 			# 	# 			mesh.material.emissiveIntensity = 1
 			# )
+			VJ.add(VJ,'BEAT_HOLD_TIME',13,Midi.XL1,true).minMax(0.1,2)
+			VJ.add(VJ,'BEAT_DECAY_RATE',14,Midi.XL1,true).minMax(0.1,2)

 			# CAMERA
 			# VJ.addGroup([


remove li start test for now

remove li start test for now

--- test/li.coffee
+++ test/li.coffee
@@ -17,8 +17,3 @@
     it 'offers help', ->
       li.main ['li', 'help']
       mock.verify()
-  describe 'start', ->
-    it 'has a start command', ->
-      li.main ['li', 'start', 'placebo', 'vanilla']
-      mock.verify()
-


Save model type property to __type attribute in ShareJS

--- src/sharedcollection.coffee
+++ src/sharedcollection.coffee
@@ -57,7 +57,7 @@

     if not (model instanceof Backbone.Model)
       attrs = model
-      Model = @classMap[attrs.type] or @model
+      Model = @classMap[attrs.__type] or @model
       model = new Model attrs,
         collection: this
       if model.validate and not model._performValidation(attrs, options)
@@ -155,7 +155,7 @@
       @_syncDoc.submitOp operations, @captureError(model, "change")

-  _sendModelAdd: (model) ->
+  _sendModelAdd: (model, options) ->

     # Models in shared collection must have unique id. Create one if user did
     # not give one.
@@ -167,6 +167,15 @@
       return

     log "SEND ADD #{ model.id }: #{ JSON.stringify model.toJSON() }"
+
+    attrs = model.toJSON()
+
+    # If model has type property we must put it with serialized attributes so
+    # that it can be properly deserialized in the other end.
+    if model.type
+      if not @classMap[model.type]
+        throw new Error "Cannot serialize model. Unkown type: '#{ model.type }'. You must add this model class to `modelClasses` options when creating this collection"
+      attrs.__type = model.type

     @_syncDoc.submitOp [
       p: [@collectionId , model.id]


corrected use of replaceState

corrected use of replaceState

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -29,7 +29,7 @@

 checkHeaders = (xhr) ->
   if(location = xhr.getResponseHeader('X-Push-State-Location'))
-    window.history.replaceState currentState, '', location
+    historyReplaceState currentState, '', location

 fetchHistory = (state) ->
   cacheCurrentPage()


postgres -- set/unset/get project host

postgres -- set/unset/get project host

--- src/smc-hub/postgres.coffee
+++ src/smc-hub/postgres.coffee
@@ -1957,16 +1957,40 @@
             cb : opts.cb

     ###
-    The project host, storage location, and state.
+    Project host, storage location, and state.
     ###
     set_project_host: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            project_id : required
+            host       : required
+            cb         : required
+        assigned = new Date()
+        @_query
+            query : "UPDATE projects"
+            jsonb_set :
+                host : {host:opts.host, assigned:assigned}
+            where : 'project_id :: UUID = $' : opts.project_id
+            cb    : (err) => opts.cb(err, assigned)

     unset_project_host: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            project_id : required
+            cb         : required
+        @_query
+            query : "UPDATE projects"
+            set   :
+                host : null
+            where : 'project_id :: UUID = $' : opts.project_id
+            cb    : opts.cb

     get_project_host: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            project_id : required
+            cb         : required
+        @_query
+            query : "SELECT host#>>'{host}' AS host FROM projects"
+            where : 'project_id :: UUID = $' : opts.project_id
+            cb    : one_result('host', opts.cb)

     set_project_storage: (opts) =>
         throw Error("NotImplementedError")


delete a misplaced confusing wrong sentence (thanks dimap)

--- src/smc-webapp/billing.cjsx
+++ src/smc-webapp/billing.cjsx
@@ -1807,7 +1807,6 @@
         <ExplainPlan type='course'/>
         <SubscriptionGrid period='month4 year1' is_static={true}/>

-        Or you can have students pay $9/month
         <ExplainResources type='shared' is_static={true}/>

         <FAQ/>


webapp: fixing ShowSupportLink.show -- #1109

--- src/smc-webapp/support.cjsx
+++ src/smc-webapp/support.cjsx
@@ -583,7 +583,7 @@

     show: (evt) ->
         evt.preventDefault()
-        exports.show()
+        redux.getActions('support').show(true)

     render : ->
         <a onClick={@show} href='#' style={cursor: 'pointer'}>


Client: Test: Stores: Minor code refactoring

--- client/app/lib/flux/test/stores/environment/activeinvitationmachineidstore.coffee
+++ client/app/lib/flux/test/stores/environment/activeinvitationmachineidstore.coffee
@@ -33,7 +33,7 @@
         id : _id
       }

-      store = @reactor.evaluateToJS [ActiveInvitationMachineIdStore.getterPath]
+      store = @reactor.evaluate(['activeInvitationMachineId'])

       expect(store).toBe _id

@@ -44,7 +44,7 @@
         id : null
       }

-      store = @reactor.evaluateToJS [ActiveInvitationMachineIdStore.getterPath]
+      store = @reactor.evaluate(['activeInvitationMachineId'])

       expect(store).toBe null

@@ -57,7 +57,7 @@
         id : _id
       }

-      store = @reactor.evaluateToJS [ActiveInvitationMachineIdStore.getterPath]
+      store = @reactor.evaluate(['activeInvitationMachineId'])

       expect(store).toBe _id

@@ -66,6 +66,6 @@
         forceUpdate : yes
       }

-      store = @reactor.evaluateToJS [ActiveInvitationMachineIdStore.getterPath]
+      store = @reactor.evaluate(['activeInvitationMachineId'])

       expect(store).toBe newMachineId


Teamwork: Fixed a missing variable name.

--- client/landing/app/Applications/Teamwork.kdapplication/Views/teamworktools.coffee
+++ client/landing/app/Applications/Teamwork.kdapplication/Views/teamworktools.coffee
@@ -107,7 +107,7 @@

     modal.addSubView urlInput = new KDInputView
       cssClass       : "teamwork-url-share-input"
-      defaultValue   : "#{location.origin}/Develop/Teamwork?import=#{shorten}"
+      defaultValue   : "#{location.origin}/Develop/Teamwork?import=#{shortenUrl}"
       attributes     :
         readonly     : "readonly"
       click          : => urlInput.getDomElement().select()


revert change for files

revert change for files

--- src/smc-hub/hub.coffee
+++ src/smc-hub/hub.coffee
@@ -1385,7 +1385,7 @@
                         cb         : opts.cb
                 else
                     # no
-                    opts.cb("not_public")
+                    opts.cb("path '#{opts.path}' of project with id '#{opts.project_id}' is not public")

     mesg_public_get_directory_listing: (mesg) =>
         for k in ['path', 'project_id']


coordinator: Make queue/role finding free functions

These don't modify state and are not public API,
so should not be methods

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -25,9 +25,23 @@
 fromIipId = (id) ->
   return id.split ' '

+participantsByRole = (participants, role) ->
+  matchRole = (id) =>
+    part = participants[id]
+    return part.role == role
+
+  m = Object.keys(participants).filter matchRole
+  return m
+
+# XXX: there is now a mixture of participant id and role used here
+findQueue = (participants, partId, dir, portName) =>
+  part = participants[partId]
+  part = participants[participantsByRole(participants, partId)] if not part?
+  for port in part[dir]
+    return port.queue if port.id == portName

 waitForParticipant = (coordinator, role, callback) ->
-  existing = coordinator.participantsByRole role
+  existing = participantsByRole coordinator.participants, role
   return callback null, coordinator.participants[existing[0]] if existing.length

   onTimeout = () =>
@@ -167,7 +181,7 @@
     callback = defaultCallback if not callback

     part = @participants[participantId]
-    part = @participants[@participantsByRole(participantId)] if not part?
+    part = @participants[participantsByRole(@participants, participantId)] if not part?
     port = findPort part, 'inport', inport
     return @broker.sendTo 'inqueue', port.queue, message, callback

@@ -177,7 +191,7 @@
     callback = defaultCallback if not callback

     part = @participants[participantId]
-    part = @participants[@participantsByRole(participantId)] if not part?
+    part = @participants[participantsByRole(@participants, participantId)] if not part?

     debug 'subscribeTo', participantId, outport
     port = findPort part, 'outport', outport
@@ -202,14 +216,6 @@
   connect: (fromId, fromPort, toId, toName, callback) ->
     callback = ((err) ->) if not callback

-    # XXX: there is now a mixture of participant id and role used here
-
-    findQueue = (partId, dir, portName) =>
-      part = @participants[partId]
-      part = @participants[@participantsByRole(partId)] if not part?
-      for port in part[dir]
-        return port.queue if port.id == portName
-
     # NOTE: adding partial connection info to make checkParticipantConnections logic work
     edgeId = connId fromId, fromPort, toId, toName
     edge =
@@ -228,8 +234,8 @@
       waitForParticipant @, toId, (err) =>
         return callback err if err
         # TODO: support roundtrip
-        @connections[edgeId].srcQueue = findQueue fromId, 'outports', fromPort
-        @connections[edgeId].tgtQueue = findQueue toId, 'inports', toName
+        @connections[edgeId].srcQueue = findQueue @participants, fromId, 'outports', fromPort
+        @connections[edgeId].tgtQueue = findQueue @participants, toId, 'inports', toName
         @emit 'graph-changed'
         @broker.addBinding {type: 'pubsub', src:edge.srcQueue, tgt:edge.tgtQueue}, (err) =>
           return callback err
@@ -397,13 +403,5 @@
         @processes = proc
         setup.bindings options, callback

-  participantsByRole: (role) ->
-    matchRole = (id) =>
-      part = @participants[id]
-      return part.role == role
-
-    m = Object.keys(@participants).filter matchRole
-    return m
-

 exports.Coordinator = Coordinator


Fix yScale in donut partition chart. Fix #72

The linear scale is actually implied by the comment.
However a strange scale of exp(.4) has been applied.

--- src/taxonomyViz.coffee
+++ src/taxonomyViz.coffee
@@ -1224,7 +1224,7 @@
 	drawBasicDonut: ( donutID, donutName, donutData, donutContainedSamp, posID ) ->

 		radius = 100
-		yScale = d3.scale.pow().exponent(.4).domain([0, d3.max(donutData)]).range([0, 100]) # linear
+		yScale = d3.scale.linear().domain([0, d3.max(donutData)]).range([0, 100]) # linear
 		arc = d3.svg.arc().innerRadius(50).outerRadius(radius)
 		pie = d3.layout.pie().sort(null).value( (d) -> return yScale(d) )


initiate travel automatically

initiate travel automatically

--- ng-modules/travelScreen/travelScreen.coffee
+++ ng-modules/travelScreen/travelScreen.coffee
@@ -39,6 +39,7 @@

     vm.onEntry = function(){
         $scope.$emit('changeMusicTo', vm.music);
+        vm.startTravel();
     }

     vm.onExit = function(){


Use File.prototype.safe{Write,Remove}Sync

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -1351,15 +1351,15 @@
     @setPath(filePath)

     if options?.backup
-      backupFilePath = @backUpFileContentsBeforeWriting()
+      backupFile = @backUpFileContentsBeforeWriting()

     try
       @file.writeSync(@getText())
-      if backupFilePath?
-        @removeBackupFileAfterWriting(backupFilePath)
+      if backupFile?
+        backupFile.safeRemoveSync()
     catch error
-      if backupFilePath?
-        fs.writeFileSync(filePath, fs.readFileSync(backupFilePath))
+      if backupFile?
+        @file.writeSync(backupFile.readSync())
       throw error

     @cachedDiskContents = @getText()
@@ -1411,34 +1411,9 @@
         throw new Error("Can't create a backup file for #{@getPath()} because files already exist at every candidate path.")
       backupFilePath += '~'

-    backupFD = fs.openSync(backupFilePath, 'w')
-    fs.writeSync(backupFD, @file.readSync())
-
-    # Ensure backup file contents are really on disk before proceeding
-    fs.fdatasyncSync(backupFD)
-    fs.closeSync(backupFD)
-
-    # Ensure backup file directory entry is really on disk before proceeding
-    #
-    # Windows doesn't support syncing on directories so we'll just have to live
-    # with less safety on that platform.
-    unless process.platform is 'win32'
-      try
-        backupDirectoryFD = fs.openSync(path.dirname(backupFilePath), 'r')
-        fs.fdatasyncSync(backupDirectoryFD)
-        fs.closeSync(backupDirectoryFD)
-      catch error
-        console.warn("Non-fatal error syncing parent directory of backup file #{backupFilePath}")
-
-    backupFilePath
-
-  removeBackupFileAfterWriting: (backupFilePath) ->
-    # Ensure new file contents are really on disk before proceeding
-    fd = fs.openSync(@getPath(), 'a')
-    fs.fdatasyncSync(fd)
-    fs.closeSync(fd)
-
-    fs.removeSync(backupFilePath)
+    file = new File(backupFilePath)
+    file.safeWriteSync(@file.readSync())
+    file

   ###
   Section: Display Layers


Dispose of ::scopedConfigSubscriptions when Editor is destroyed

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -175,6 +175,7 @@

   destroyed: ->
     @unsubscribe()
+    @scopedConfigSubscriptions.dispose()
     selection.destroy() for selection in @getSelections()
     @buffer.release()
     @displayBuffer.destroy()


Oops, don’t call the method twice

Oops, don’t call the method twice

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -2422,7 +2422,7 @@
     @getLastCursor().getScopeDescriptor().getScopesArray()
   getCursorScopes: ->
     deprecate 'Use editor.getLastCursor().getScopeDescriptor() instead'
-    @scopesAtCursor().getScopesArray()
+    @scopesAtCursor()

   ###
   Section: Clipboard Operations


ComputeProvider: no need to try create stacks for guests [completes #77817654]

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -293,6 +293,9 @@
     JGroup = require '../group'
     JGroup.on 'MemberAdded', ({group, member})->

+      # No need to try creating group stacks for guests group members
+      return  if group.slug is 'guests'
+
       client =
         connection :
           delegate : member


Don't use config in TextEditor::getSoftWrapColumn

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -2832,8 +2832,8 @@
   getSoftWrapColumn: ->
     scopeDescriptor = @getRootScopeDescriptor()
     if @isSoftWrapped()
-      if @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
-        Math.min(@getEditorWidthInChars(), @config.get('editor.preferredLineLength', scope: scopeDescriptor))
+      if @softWrapAtPreferredLineLength
+        Math.min(@getEditorWidthInChars(), @preferredLineLength)
       else
         @getEditorWidthInChars()
     else


always pin if sticky attachment is shown

--- resources/assets/coffee/_classes/sticky-header.coffee
+++ resources/assets/coffee/_classes/sticky-header.coffee
@@ -45,10 +45,6 @@
     @headerHeight() + offset

-  @shouldPin: (offset = window.pageYOffset) ->
-    offset > window._styles.header.height
-
-
   constructor: ->
     @stickMarker = document.getElementsByClassName('js-sticky-header')

@@ -57,10 +53,10 @@
     $(document).on 'turbolinks:load osu:page:change', @stickOrUnstick

-  applyCss: ->
+  applyCss: =>
     return unless header[0]?

-    if StickyHeader.shouldPin()
+    if @shouldPin()
       document.body.classList.add 'js-header-is-pinned'
     else
       document.body.classList.remove 'js-header-is-pinned'
@@ -73,9 +69,20 @@
       Fade.out sticky[0]

-  stickOrUnstick: =>
+  shouldPin: (offset = window.pageYOffset) =>
+    offset > window._styles.header.height || @shouldStick()
+
+
+  shouldStick: =>
     return unless @stickMarker.length > 0 && sticky[0]?
     markerTop = @stickMarker[0].getBoundingClientRect().top
     headerBottom = StickyHeader.offsetForScrollTo(sticky[0].getBoundingClientRect().height)

-    @setVisible markerTop < headerBottom
+    markerTop < headerBottom
+
+
+  stickOrUnstick: =>
+    visible = @shouldStick() # undefined when elements don't exist
+    return unless visible?
+
+    @setVisible visible


Remove newline hint if can't comment

Remove newline hint if can't comment

--- resources/assets/lib/components/comment-editor.coffee
+++ resources/assets/lib/components/comment-editor.coffee
@@ -70,8 +70,9 @@
       div
         className: "#{bn}__footer"
         div className: "#{bn}__footer-item #{bn}__footer-item--notice hidden-xs",
-          osu.trans 'comments.editor.textarea_hint._',
-            action: osu.trans("comments.editor.textarea_hint.#{mode}")
+          if canComment
+            osu.trans 'comments.editor.textarea_hint._',
+              action: osu.trans("comments.editor.textarea_hint.#{mode}")

         if @props.close?
           div className: "#{bn}__footer-item",


teamwork: focus to terminal on tab switch if nothing has focus

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -102,7 +102,7 @@
             if pane.terminalView
               {terminal} = pane.terminalView.webterm
               terminal.scrollToBottom()
-              terminal.container.trigger 'click'
+              terminal.setFocused yes  if document.activeElement is document.body
             else if pane.editor
               pane.editor.codeMirrorEditor.refresh()


don't bomb when metric doesn't exist in graphite

--- dashing/javascripts/app/models/metric.coffee
+++ dashing/javascripts/app/models/metric.coffee
@@ -127,20 +127,22 @@
         until: gUntil,
         success: (data) =>
           values = []
-          $(data[0].datapoints).each (i, d) ->
-            date = new Date(0)
-            offset = date.getTimezoneOffset() * 60
-            date.setTime((d[1] + offset )*1000)
+
+          if data.length > 0 and data[0].datapoints
+            $(data[0].datapoints).each (i, d) ->
+              date = new Date(0)
+              offset = date.getTimezoneOffset() * 60
+              date.setTime((d[1] + offset )*1000)

-            if d[0] == null
-              val = 0
-            else
-              val = d[0]
+              if d[0] == null
+                val = 0
+              else
+                val = d[0]

-            tmpVal = val + ""
-            if tmpVal.indexOf('.') > 0
-              val = parseFloat(val.toFixed(2))
+              tmpVal = val + ""
+              if tmpVal.indexOf('.') > 0
+                val = parseFloat(val.toFixed(2))

-            values.push({date: date, seconds: d[1], value: val})
+              values.push({date: date, seconds: d[1], value: val})

           @set 'values', values


Add a description for each package

Add a description for each package

--- package.cson
+++ package.cson
@@ -1 +1,2 @@
-'main': 'lib/bracket-matcher'
+'main': './lib/bracket-matcher'
+'description': 'Highlight the matching bracket for the `(){}[]` character under the cursor.\n\nMove the cursor to the matching bracket with `ctrl-j`.'


select all md on click, fixes #6

select all md on click, fixes #6

--- public/assets/javascripts/app.coffee
+++ public/assets/javascripts/app.coffee
@@ -1,3 +1,24 @@
+# Select text
+# Source: http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse
+jQuery.fn.selectText = ->
+  doc = document
+  element = this[0]
+  range = undefined
+  selection = undefined
+  if doc.body.createTextRange
+    range = document.body.createTextRange()
+    range.moveToElementText element
+    range.select()
+  else if window.getSelection
+    selection = window.getSelection()
+    range = document.createRange()
+    range.selectNodeContents element
+    selection.removeAllRanges()
+    selection.addRange range
+
 $ ->
   $("#doc").change ->
     $("#submit").click()
+
+  $(".md-preview").click ->
+    $(".md-preview").selectText()


engine data module

engine data module

--- src/engine_data.coffee
+++ src/engine_data.coffee
@@ -11,6 +11,6 @@
   Object.reverseMerge I,
     data: {}

-  get: ->
+  get: (k) ->

   set: ->


Restore trailing space

Restore trailing space

--- src/yayson/presenter.coffee
+++ src/yayson/presenter.coffee
@@ -51,7 +51,7 @@
         data = @constructor.adapter.get instance, key
         presenter = rels[key]
         buildData = (d) =>
-          data =
+          data =
             id: @constructor.adapter.id d
             type: presenter::type
         build = (d) =>


HUB-358 Tracker sometimes raises a warning

--- lib/actor/hactor.coffee
+++ lib/actor/hactor.coffee
@@ -672,8 +672,8 @@
       outboundsTabCopy.push (outbound)
     _.forEach @inboundAdapters, (inbound) =>
       inboundsTabCopy.push (inbound)
-    # Stop adapters
-    _.invoke inboundsTabCopy, "stop"
+    # Stop adapters with "true" option so channel_in adapters don't try to re-subscribe
+    _.invoke inboundsTabCopy, "stop", true
     _.invoke outboundsTabCopy, "stop"
     done()


Moved the draggable spec options up a scope-level.

--- spec/draggable_spec.coffee
+++ spec/draggable_spec.coffee
@@ -1,38 +1,39 @@
+options =
+  dragDistance: 50
+  alternateDraggableClass: 'alternateDraggableClass'
+  alternateDraggingClass: 'alternateDraggingClass'
+  ineffectualButtons:
+    'middle': jQuery.simulate.buttonCode.MIDDLE
+    'right': jQuery.simulate.buttonCode.RIGHT
+  scrollOffsetVariants:
+    'no': 0
+    'a non-zero': 50
+  handleConfigVariants:
+    'selector': -> '#handle'
+    'DOM element': -> $('#handle').get(0)
+    'jQuery object': -> $('#handle')
+  helperConfigVariants:
+    'clone': 'clone'
+    'a factory method that produces something DOM-element-like': jasmine.createSpy('helperFactory').andReturn($(sandbox()).text('I’m a helper'))
+  elementPositionVariants: ['static', 'absolute', 'fixed']
+  getTransformCSS: (scale, skew, rotate, translate, originXPercent = 1, originYPercent = 1) ->
+    originString = "#{originXPercent * 100}% #{originYPercent * 100}%"
+    transformString = "rotate(#{rotate}deg) scale(#{scale}) skew(#{skew}deg) translate(#{translate}px)"
+    {
+      'transform-origin': originString
+      '-webkit-transform-origin': originString
+      '-moz-transform-origin': originString
+      '-o-transform-origin': originString
+      '-ms-transform-origin': originString
+
+      'transform': transformString
+      '-webkit-transform': transformString
+      '-moz-transform': transformString
+      '-o-transform': transformString
+      '-ms-transform': transformString
+    }
+
 describe 'A draggable', ->
-  options =
-    dragDistance: 50
-    alternateDraggableClass: 'alternateDraggableClass'
-    alternateDraggingClass: 'alternateDraggingClass'
-    ineffectualButtons:
-      'middle': jQuery.simulate.buttonCode.MIDDLE
-      'right': jQuery.simulate.buttonCode.RIGHT
-    scrollOffsetVariants:
-      'no': 0
-      'a non-zero': 50
-    handleConfigVariants:
-      'selector': -> '#handle'
-      'DOM element': -> $('#handle').get(0)
-      'jQuery object': -> $('#handle')
-    helperConfigVariants:
-      'clone': 'clone'
-      'a factory method that produces something DOM-element-like': jasmine.createSpy('helperFactory').andReturn($('<div>').text('I’m a helper'))
-    elementPositionVariants: ['static', 'absolute', 'fixed']
-    getTransformCSS: (scale, skew, rotate, translate, originXPercent = 1, originYPercent = 1) ->
-      originString = "#{originXPercent * 100}% #{originYPercent * 100}%"
-      transformString = "rotate(#{rotate}deg) scale(#{scale}) skew(#{skew}deg) translate(#{translate}px)"
-      {
-        'transform-origin': originString
-        '-webkit-transform-origin': originString
-        '-moz-transform-origin': originString
-        '-o-transform-origin': originString
-        '-ms-transform-origin': originString
-
-        'transform': transformString
-        '-webkit-transform': transformString
-        '-moz-transform': transformString
-        '-o-transform': transformString
-        '-ms-transform': transformString
-      }

   describe 'configured using the default options', ->


Fix 15-spec

Fix 15-spec

--- spec/issues/15-spec.coffee
+++ spec/issues/15-spec.coffee
@@ -10,32 +10,31 @@

   describe "Issue 15", ->
     beforeEach ->
-      directory = temp.mkdirSync()
-      # Create a fake workspace and open a sample file
-      atom.workspaceView = new WorkspaceView
-      atom.workspaceView.openSync "issues/11.js"
-      atom.workspaceView.simulateDomAttachment()
+      runs ->
+        directory = temp.mkdirSync()

-      # Set to live completion
-      atom.config.set "autocomplete-plus.enableAutoActivation", true
+        # Set to live completion
+        atom.config.set "autocomplete-plus.enableAutoActivation", true

-      # Set the completion delay
-      completionDelay = 100
-      atom.config.set "autocomplete-plus.autoActivationDelay", completionDelay
-      completionDelay += 100 # Rendering delay
+        # Set the completion delay
+        completionDelay = 100
+        atom.config.set "autocomplete-plus.autoActivationDelay", completionDelay
+        completionDelay += 100 # Rendering delay
+        atom.workspaceView = new WorkspaceView()
+        atom.workspace = atom.workspaceView.model
+
+      waitsForPromise -> atom.workspace.open("issues/11.js").then (e) ->
+        editor = e
+        atom.workspaceView.simulateDomAttachment()

       # Activate the package
-      activationPromise = atom.packages.activatePackage "autocomplete-plus"
+      waitsForPromise -> atom.packages.activatePackage("autocomplete-plus").then (a) -> autocomplete = a

-      editorView = atom.workspaceView.getActiveView()
-      {editor} = editorView
-      autocomplete = new AutocompleteView editorView
+      runs ->
+        editorView = atom.workspaceView.getActiveView()
+        autocomplete = new AutocompleteView editorView

     it "does dismiss autocompletion when saving", ->
-
-      waitsForPromise ->
-        activationPromise
-
       runs ->
         editorView.attachToDom()
         expect(editorView.find(".autocomplete-plus")).not.toExist()


Remove some tests for outdated pane behaviors

--- spec/pane-view-spec.coffee
+++ spec/pane-view-spec.coffee
@@ -46,104 +46,12 @@
     deserializerDisposable.dispose()
     jasmine.restoreDeprecationsSnapshot()

-  describe "when an item is moved within the same pane", ->
-    it "emits a 'pane:item-moved' event with the item and the new index", ->
-      pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
-      paneModel.moveItem(view1, 2)
-      expect(itemMovedHandler).toHaveBeenCalled()
-      expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
-
-  describe "when an item is moved to another pane", ->
-    it "detaches the item's view rather than removing it", ->
-      container.attachToDom()
-      expect(view1.is(':visible')).toBe true
-      paneModel2 = paneModel.splitRight()
-      view1.data('preservative', 1234)
-      paneModel.moveItemToPane(view1, paneModel2, 1)
-      expect(view1.data('preservative')).toBe 1234
-      paneModel2.activateItemAtIndex(1)
-      expect(view1.data('preservative')).toBe 1234
-      expect(view1.is(':visible')).toBe true
-
-  describe "when the title of the active item changes", ->
-    describe 'when there is no onDidChangeTitle method (deprecated)', ->
-      beforeEach ->
-        jasmine.snapshotDeprecations()
-
-        view1.onDidChangeTitle = null
-        view2.onDidChangeTitle = null
-
-        pane.activateItem(view2)
-        pane.activateItem(view1)
-
-      afterEach ->
-        jasmine.restoreDeprecationsSnapshot()
-
-      it "emits pane:active-item-title-changed", ->
-        activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
-        pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
-
-        expect(pane.getActiveItem()).toBe view1
-
-        view2.trigger 'title-changed'
-        expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
-
-        view1.trigger 'title-changed'
-        expect(activeItemTitleChangedHandler).toHaveBeenCalled()
-        activeItemTitleChangedHandler.reset()
-
-        pane.activateItem(view2)
-        view2.trigger 'title-changed'
-        expect(activeItemTitleChangedHandler).toHaveBeenCalled()
-
-    describe 'when there is a onDidChangeTitle method', ->
-      it "emits pane:active-item-title-changed", ->
-        activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
-        pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
-
-        expect(pane.getActiveItem()).toBe view1
-        view2.changeTitle()
-        expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
-
-        view1.changeTitle()
-        expect(activeItemTitleChangedHandler).toHaveBeenCalled()
-        activeItemTitleChangedHandler.reset()
-
-        pane.activateItem(view2)
-        view2.changeTitle()
-        expect(activeItemTitleChangedHandler).toHaveBeenCalled()
-
-  describe "when an unmodifed buffer's path is deleted", ->
-    it "removes the pane item", ->
-      editor = null
-      jasmine.unspy(window, 'setTimeout')
-      filePath = path.join(temp.mkdirSync(), 'file.txt')
-      fs.writeFileSync(filePath, '')
-
-      waitsForPromise ->
-        atom.workspace.open(filePath).then (o) -> editor = o
-
-      runs ->
-        pane.activateItem(editor)
-        expect(pane.items).toHaveLength(5)
-        fs.removeSync(filePath)
-
-      waitsFor ->
-        pane.items.length is 4
-
   describe "when a pane is destroyed", ->
     [pane2, pane2Model] = []

     beforeEach ->
       pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another
       pane2 = atom.views.getView(pane2Model).__spacePenView
-
-    it "triggers a 'pane:removed' event with the pane", ->
-      removedHandler = jasmine.createSpy("removedHandler")
-      container.on 'pane:removed', removedHandler
-      paneModel.destroy()
-      expect(removedHandler).toHaveBeenCalled()
-      expect(removedHandler.argsForCall[0][1]).toBe pane

     describe "if the destroyed pane has focus", ->
       [paneToLeft, paneToRight] = []
@@ -155,14 +63,6 @@
         expect(pane2.hasFocus()).toBe true
         pane2Model.destroy()
         expect(pane.hasFocus()).toBe true
-
-  describe "::getNextPane()", ->
-    it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
-      pane.activateItem(editor1)
-      expect(pane.getNextPane()).toBeUndefined
-      pane2 = pane.splitRight(pane.copyActiveItem())
-      expect(pane.getNextPane()).toBe pane2
-      expect(pane2.getNextPane()).toBe pane

   describe "when the pane's active status changes", ->
     [pane2, pane2Model] = []
@@ -178,18 +78,6 @@
       expect(pane).toHaveClass('active')
       pane2Model.activate()
       expect(pane).not.toHaveClass('active')
-
-    it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", ->
-      pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler")
-      pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
-      paneModel.activate()
-
-      expect(becameActiveHandler.callCount).toBe 1
-      expect(becameInactiveHandler.callCount).toBe 0
-
-      pane2Model.activate()
-      expect(becameActiveHandler.callCount).toBe 1
-      expect(becameInactiveHandler.callCount).toBe 1

   describe "when the pane is focused", ->
     beforeEach ->


Fixed specs

Fixed specs

--- spec/spark-dev-spec.coffee
+++ spec/spark-dev-spec.coffee
@@ -521,21 +521,21 @@
     describe 'when there already is open panel', ->
       it 'switches to it', ->
         activateItemForUriSpy = jasmine.createSpy 'activateItemForUri'
-        spyOn(atom.workspace, 'paneForUri').andReturn
-          activateItemForUri: activateItemForUriSpy
+        spyOn(atom.workspace, 'paneForURI').andReturn
+          activateItemForURI: activateItemForUriSpy

         sparkIde.openPane 'foo'

-        expect(atom.workspace.paneForUri).toHaveBeenCalled()
-        expect(atom.workspace.paneForUri).toHaveBeenCalledWith(url)
+        expect(atom.workspace.paneForURI).toHaveBeenCalled()
+        expect(atom.workspace.paneForURI).toHaveBeenCalledWith(url)
         expect(activateItemForUriSpy).toHaveBeenCalled()
         expect(activateItemForUriSpy).toHaveBeenCalledWith(url)

-        jasmine.unspy atom.workspace, 'paneForUri'
+        jasmine.unspy atom.workspace, 'paneForURI'

     describe 'when there is no panel', ->
       it 'opens new one', ->
-        spyOn(atom.workspace, 'paneForUri').andReturn null
+        spyOn(atom.workspace, 'paneForURI').andReturn null
         spyOn atom.workspace, 'open'

         # Without splitted panels, split
@@ -575,5 +575,5 @@

         jasmine.unspy atom.workspace, 'getPanes'
         jasmine.unspy atom.workspace, 'getActivePane'
-        jasmine.unspy atom.workspace, 'paneForUri'
+        jasmine.unspy atom.workspace, 'paneForURI'
         jasmine.unspy atom.workspace, 'open'


Test onDidChangeActivePaneItem observation in title-bar-spec

--- spec/title-bar-spec.coffee
+++ spec/title-bar-spec.coffee
@@ -1,12 +1,19 @@
 TitleBar = require '../src/title-bar'

 describe "TitleBar", ->
-  it 'updates the title based on document.title', ->
-    titleBar = new TitleBar(atom)
+  it 'updates the title based on document.title when the active pane item changes', ->
+    titleBar = new TitleBar({
+      workspace: atom.workspace,
+      themes: atom.themes,
+      applicationDelegate: atom.applicationDelegate,
+    })

     expect(titleBar.element.querySelector('.title').textContent).toBe document.title
+    initialTitle = document.title

-    document.title = 'new-title'
-    titleBar.updateTitle()
+    atom.workspace.getActivePane().activateItem({
+      getTitle: -> 'Test Title'
+    })

-    expect(titleBar.element.querySelector('.title').textContent).toBe 'new-title'
+    expect(document.title).not.toBe(initialTitle)
+    expect(titleBar.element.querySelector('.title').textContent).toBe document.title


Add spec

Add spec

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -336,7 +336,6 @@
         it "does nothing", ->
           treeView.trigger 'tree-view:copy-project-path'
           expect(atom.clipboard.write).not.toHaveBeenCalled()
-

   describe "when a directory's disclosure arrow is clicked", ->
     it "expands / collapses the associated directory", ->
@@ -1246,3 +1245,10 @@
     describe "when a file is ignored", ->
       it "adds a custom style", ->
         expect(treeView.find('.file:contains(ignored.txt)')).toHaveClass 'status-ignored'
+
+  describe "when the resize handle is double clicked", ->
+    beforeEach ->
+      $(treeView).width(10).find('.list-tree').width 100
+    it "changes its width", ->
+      $(treeView).find('.tree-view-resize-handle').trigger 'dblclick'
+      expect($(treeView).width()).not.toBe 10


Remove linter errors

Remove linter errors

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -300,7 +300,7 @@

       describe "when the user does not have access to the file", ->
         beforeEach ->
-          spyOn(fs, 'openSync').andCallFake (path)->
+          spyOn(fs, 'openSync').andCallFake (path) ->
             error = new Error("EACCES, permission denied '#{path}'")
             error.path = path
             error.code = 'EACCES'
@@ -319,7 +319,7 @@

       describe "when there is an unhandled error", ->
         beforeEach ->
-          spyOn(fs, 'openSync').andCallFake (path)->
+          spyOn(fs, 'openSync').andCallFake (path) ->
             throw new Error("I dont even know what is happening right now!!")

         it "creates a notification", ->


Joose support (cc.class), cc.namespaceFor + fix when using alert in "defines".

--- lib/ccloader/web.coffee
+++ lib/ccloader/web.coffee
@@ -35,7 +35,7 @@

   _define: ->
     # console.log "define #{@name}"
-    @status = 'defined'
+    @status = 'loaded'
     self = {}

     try
@@ -55,6 +55,7 @@

     do onload for onload in @onloads
     delete @onloads
+    @status = 'defined'
     return

 class CC
@@ -83,10 +84,11 @@
       # externally referenced via "cc.requires".
       return cc.modules[name] = new Module name

-  # set a value at a particular namespace under @global
-  # e.g. ns = "hey.baby", val = "1"
-  #   -> hey.baby = 1
-  set: (ns, val) ->
+  # set path to
+  # given "grandparent.parent.element"
+  #    creates: cc.grandparent = { parent = {} }
+  #    returns: [ cc.grandparent.parent, element ]
+  namespaceFor: (ns) ->
     obj = @global
     components = ns.split '.'
     for space in components[0...(components.length - 1)]
@@ -98,9 +100,17 @@
       else
         alert "namespace conflict, #{ns} = #{current} of #{typeof current}"

-    lastComp = components[components.length - 1]
+    [ obj, components[components.length - 1] ]
+
+  # set a value at a particular namespace under @global
+  # e.g. ns = "hey.baby", val = "1"
+  #   -> hey.baby = 1
+  set: (ns, val) ->
+    # console.log "cc.set #{ns} = #{val}"
+    [ obj, lastComp ] = @namespaceFor ns
     current = obj[lastComp]
     if current
+      # if existing and current are both objects then merge them
       if typeof current == 'object' and typeof val == 'object'
         for own key, subval of val
           current[key] = subval
@@ -108,6 +118,12 @@
         alert "namespace conflict, #{ns} = #{current} of #{typeof current}"
     else
       obj[lastComp] = val
+
+  class: (ns, clss) ->
+    if not Class?
+      throw 'please install Joose to use cc.class'
+    @namespaceFor ns
+    Class ns, clss

   scriptOnload: (script, onload) ->
     if script.readyState
@@ -159,7 +175,7 @@
         if 'failed' == mod.status
           # console.log "require #{name} failed"
           callback name
-        else if 'loading' == mod.status
+        else if 'defined' != mod.status
           # console.log "require #{name} is loading"
           mod.pushOnload callback
         else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -1,5 +1,5 @@
 ###*
-A Game State that loads up
+A Game State that loads the map for a given

 @see GameState
 @name LevelState


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -3,7 +3,7 @@

 @name LevelState
 @constructor
-@param {Number} duration
+@param {Number} duration T
 @param {Number} period How fast the osciallator function repeats
 @param {Number} offset How much to offset the created osciallator function. Useful for translating between sin and cosine functions.
 ###


Fix changeports

Fix changeports

--- src/lib/Journal.coffee
+++ src/lib/Journal.coffee
@@ -216,11 +216,11 @@
       when 'addInport' then @graph.addInport a.name, a.port.process, a.port.port, a.port.metadata
       when 'removeInport' then @graph.removeInport a.name
       when 'renameInport' then @graph.renameInport a.oldId, a.newId
-      when 'changeInport' then @graph.setInportMetadata a.port, calculateMeta(a.old, a.new)
+      when 'changeInport' then @graph.setInportMetadata a.name, calculateMeta(a.old, a.new)
       when 'addOutport' then @graph.addOutport a.name, a.port.process, a.port.port, a.port.metadata a.name
       when 'removeOutport' then @graph.removeOutport
       when 'renameOutport' then @graph.renameOutport a.oldId, a.newId
-      when 'changeOutport' then @graph.setOutportMetadata a.port, calculateMeta(a.old, a.new)
+      when 'changeOutport' then @graph.setOutportMetadata a.name, calculateMeta(a.old, a.new)
       else throw new Error("Unknown journal entry: #{entry.cmd}")

   executeEntryInversed: (entry) ->
@@ -245,11 +245,11 @@
       when 'addInport' then @graph.removeInport a.name
       when 'removeInport' then @graph.addInport a.name, a.port.process, a.port.port, a.port.metadata
       when 'renameInport' then @graph.renameInport a.newId, a.oldId
-      when 'changeInport' then @graph.setInportMetadata a.port, calculateMeta(a.new, a.old)
+      when 'changeInport' then @graph.setInportMetadata a.name, calculateMeta(a.new, a.old)
       when 'addOutport' then @graph.removeOutport a.name
       when 'removeOutport' then @graph.addOutport a.name, a.port.process, a.port.port, a.port.metadata
       when 'renameOutport' then @graph.renameOutport a.newId, a.oldId
-      when 'changeOutport' then @graph.setOutportMetadata a.port, calculateMeta(a.new, a.old)
+      when 'changeOutport' then @graph.setOutportMetadata a.name, calculateMeta(a.new, a.old)
       else throw new Error("Unknown journal entry: #{entry.cmd}")

   moveToRevision: (revId) ->


Move code out of loadJSON callback

Move code out of loadJSON callback

--- orgviz/static/timeline.coffee
+++ orgviz/static/timeline.coffee
@@ -45,43 +45,42 @@
     {pixelsPerInterval: 100,  unit: Timeline.DateTime.YEAR}
   )
   zoomIndex = 10
+  eventSource = new Timeline.DefaultEventSource()
+
+  theme = Timeline.ClassicTheme.create()
+  theme.autoWidth = true
+  theme.mouseWheel = 'zoom'
+
+  bandInfos = [
+    Timeline.createBandInfo(
+      width:          45,
+      intervalUnit:   zoomSteps[zoomIndex].unit,
+      intervalPixels: zoomSteps[zoomIndex].pixelsPerInterval,
+      zoomIndex:      zoomIndex,
+      zoomSteps:      zoomSteps,
+      eventSource:    eventSource,
+      theme:          theme,
+      layout:         'original',
+    )
+  ]
+
+  tl_el = document.getElementById("tl")
+  tl = Timeline.create(tl_el, bandInfos, Timeline.HORIZONTAL);

   Timeline.loadJSON data_source, (timeline_data) ->
-    eventSource = new Timeline.DefaultEventSource()
-
-    theme = Timeline.ClassicTheme.create()
-    theme.autoWidth = true
-    theme.mouseWheel = 'zoom'
-
-    bandInfos = [
-      Timeline.createBandInfo(
-        width:          45,
-        intervalUnit:   zoomSteps[zoomIndex].unit,
-        intervalPixels: zoomSteps[zoomIndex].pixelsPerInterval,
-        zoomIndex:      zoomIndex,
-        zoomSteps:      zoomSteps,
-        eventSource:    eventSource,
-        theme:          theme,
-        layout:         'original',
-      )
-    ]
-
-    tl_el = document.getElementById("tl")
-    tl = Timeline.create(tl_el, bandInfos, Timeline.HORIZONTAL);
-
     url = '.'
     eventSource.loadJSON(timeline_data, url)
     tl.layout()

     setupKeybinds tl

-    resizeTimerID = null
-    $(document).resize ->
-      if resizeTimerID == null
-        resizeTimerID = window.setTimeout(->
-          resizeTimerID = null
-          tl.layout()
-        , 500)
+  resizeTimerID = null
+  $(document).resize ->
+    if resizeTimerID == null
+      resizeTimerID = window.setTimeout(->
+        resizeTimerID = null
+        tl.layout()
+      , 500)

 root = exports ? this


`xmlMode` now is used during parsing views

e.g. now xml attributes names are case-sensitive

--- Element/element/parser.coffee
+++ Element/element/parser.coffee
@@ -92,7 +92,8 @@

 			r = node

-		parser = new htmlparser.Parser handler
+		parser = new htmlparser.Parser handler,
+			xmlMode: true
 		parser.write html
 		parser.end()


Fix indent.

Fix indent.

--- grammars/riot.cson
+++ grammars/riot.cson
@@ -150,7 +150,7 @@
                     '1':
                         'name': 'source.smarty.embedded.html'
                     '2':
-                    'name': 'support.function.built-in.smarty'
+                        'name': 'support.function.built-in.smarty'
                 'end': '(\\{/(literal)\\})'
             }
             {
@@ -213,7 +213,7 @@
         'captures':
             '1':
                 'name': 'entity.other.attribute-name.id.html'
-                '2':
+            '2':
                 'name': 'punctuation.separator.key-value.html'
         'end': '(?<=\'|")|(?=\\s|>)'
         'name': 'meta.attribute-with-value.id.html'


Fixes #997

Fixes #997

--- app/assets/javascripts/oxalis/view/action-bar/dataset_actions_view.coffee
+++ app/assets/javascripts/oxalis/view/action-bar/dataset_actions_view.coffee
@@ -64,7 +64,7 @@

For a full list of all keyboard shortcuts see the help section.

We encourage you to read the tutorials to completely understand how webKnossos works.

-
Introductory  videos are available.

+

             Close


log csv file contents; #281

log csv file contents; #281

--- server/methods/createAccount.coffee
+++ server/methods/createAccount.coffee
@@ -23,6 +23,8 @@
 				Meteor.users.update userId, $set: state: 'created'

 	createAccounts: (newUsers, projectId) ->
+		console.log(newUsers)
+
 		check newUsers, Array
 		check { userId: Meteor.userId(), projectId: projectId}, isAdmin


.

Former-commit-id: 70dd5c1296c069761fbb4993140b8b70e46994f2 [formerly 31b49f3e18d177d82b52015bdb09bca3f19d60e4] [formerly e5090dc4ff087a45c0aa6b2d2be567d63a4cfe05 [formerly ae14573c3d3075a17d062a90cbbf70b480a51036]] [formerly e0a0378ac434919b89afa6f57967f90d12b0907c [formerly 191072a8f974d8a4c253508832acd25a9ed1870b] [formerly 2894e7344444c942a91b9d1c79ed500b09344da7 [formerly 2894e7344444c942a91b9d1c79ed500b09344da7 [formerly 726c9aa9399af09b51a47f0eb914caf558568289]]]]
Former-commit-id: 2fb405f0420fac1fc60b4c43bff24e289cd4bd38 [formerly 6704f27c9d185dbc5549f3f8dab7228909a6713d] [formerly aa09b848bbb8f4d012fd3c65b2e1cff9018df4a2 [formerly 71173899bbbafedbe06fd24928b16e0fddef3969]]
Former-commit-id: c981d9220a2b5f8d28b55a22d40a3d9be029b963 [formerly 6cdf4a9800089f21f2ce5875121e97bb1cb8598e]
Former-commit-id: a110e265603b2fada0495a53e6d4499fe4ee2085
Former-commit-id: a1cd9bcc5a499488a39f0efb47cd0a3276905f6e

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -1691,6 +1691,10 @@
 ####################
 # Bitcoin

+customizations.bitcoin =
+  show_proposer_icon: true
+  collapse_descriptions_at: 600
+
 customizations.bitcoinfoundation =
   show_new_proposal_button: false

@@ -1707,6 +1711,8 @@

   # default proposal options
   show_proposer_icon: true
+  collapse_descriptions_at: 600
+
   homie_histo_title: "Members' Opinions"
   collapse_descriptions_at: 600


don't show add your opinion button to proposal embed if proposal is closed

--- @client/proposal_embed.coffee
+++ @client/proposal_embed.coffee
@@ -210,39 +210,40 @@
           marginBottom: 20
           position: 'relative'

-        A
-          href: proposal_link(@proposal)
-          target: '_blank'
-          style:
-            color: focus_color()
-            padding: '6px 12px'
-            backgroundColor: focus_color()
-            borderRadius: 16
-            color: 'white'
-            position: 'relative'
-
-          SPAN
-            key: 'slider_bubblemouth'
-            style: css.crossbrowserify
-              left: 35 - w / 2
-              top: 8
-              position: 'absolute'
-              width: w
-              height: h
-              zIndex: 10
-              transform: "translate(0, -25px) scale(.5) "
-
-            Bubblemouth
-              apex_xfrac: .2
-              width: w
-              height: h
-              fill: focus_color()
-              stroke: focus_color()
-              stroke_width: 0
-              dash_array: "none"
-
-
-          "Add your opinion"
+        if @proposal.active
+          A
+            href: proposal_link(@proposal)
+            target: '_blank'
+            style:
+              color: focus_color()
+              padding: '6px 12px'
+              backgroundColor: focus_color()
+              borderRadius: 16
+              color: 'white'
+              position: 'relative'
+
+            SPAN
+              key: 'slider_bubblemouth'
+              style: css.crossbrowserify
+                left: 35 - w / 2
+                top: 8
+                position: 'absolute'
+                width: w
+                height: h
+                zIndex: 10
+                transform: "translate(0, -25px) scale(.5) "
+
+              Bubblemouth
+                apex_xfrac: .2
+                width: w
+                height: h
+                fill: focus_color()
+                stroke: focus_color()
+                stroke_width: 0
+                dash_array: "none"
+
+
+            "Add your opinion"

       DIV


We dont need to fetch the workflow for nextProps location queries (#3626)

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -59,9 +59,6 @@
       @setupSplits nextProps

   componentWillUpdate: (nextProps, nextState) ->
-    if nextState.project? and nextState.preferences? and nextProps.location.query?.workflow? and @canFetchWorkflowByQuery(nextState.project, nextProps.user)
-      @getSelectedWorkflow(nextState.project, nextState.preferences) unless nextState.loadingSelectedWorkflow
-
     if nextState.preferences?.preferences?.selected_workflow? and @state.workflow?
       if nextState.preferences?.preferences.selected_workflow isnt @state.workflow.id
         @getSelectedWorkflow(nextState.project, nextState.preferences) unless nextState.loadingSelectedWorkflow


Remaining client docs

Remaining client docs

--- src/nack/client.coffee
+++ src/nack/client.coffee
@@ -6,9 +6,12 @@
 {EventEmitter}        = require 'events'
 {BufferedWriteStream} = require './buffered'

-# This is a Writable Stream.
-#
-# This is an EventEmitter with the following events:
+# A **ClientRequest** is returned when `Client.request()` is called.
+#
+# It is a Writable Stream and responds to the conventional
+# `write` and `end` methods.
+#
+# Its also an EventEmitter with the following events:
 #
 #   Event 'response'
 #   function (response) { }
@@ -71,16 +74,19 @@
     for key, value of metaVariables
       @env[key] = value

+  # Write chunk to client
   write: (chunk) ->
     # Netstring encode the chunk and write it to the socket
     @bufferedSocket.write ns.nsWrite(chunk.toString())

+  # Closes writting socket.
   end: ->
     @bufferedSocket.end()

-# This is a Readable Stream.
-#
-# This is an EventEmitter with the following events:
+# A **ClientResponse** is emitted from the client request's
+# `response` event.
+#
+# It is a Readable Stream and emits the conventional events:
 #
 #   Event: `data`
 #   function (chunk) { }
@@ -149,16 +155,37 @@
       # Catch and emit as a socket error
       @socket.emit 'error', error

+# A **Client** establishes a connection to a worker process.
+#
+# It takes a `port` and `host` or a UNIX socket path.
+#
+# Its API is similar to `http.Client`.
+#
+#     var conn = client.createConnection('/tmp/nack.sock');
+#     var request = conn.request('GET', '/', {'host', 'localhost'});
+#     request.end();
+#     request.on('response', function (response) {
+#       console.log('STATUS: ' + response.statusCode);
+#       console.log('HEADERS: ' + JSON.stringify(response.headers));
+#       response.on('data', function (chunk) {
+#         console.log('BODY: ' + chunk);
+#       });
+#     });
+#
 exports.Client = class Client extends Stream
+  # Reconnect if the connection is closed.
   reconnect: ->
     if @readyState is 'closed'
       @connect @port, @host

+  # Start the connection and create a ClientRequest.
   request: (args...) ->
     @reconnect()
     request = new ClientRequest @, args...
     request

+  # Proxy a `http.ServerRequest` and `http.serverResponse` between
+  # the `Client`.
   proxyRequest: (serverRequest, serverResponse) ->
     metaVariables =
       "REMOTE_ADDR": serverRequest.connection.remoteAddress
@@ -171,6 +198,7 @@
       serverResponse.writeHead clientResponse.statusCode, clientResponse.headers
       sys.pump clientResponse, serverResponse

+# Public API for creating a **Client**
 exports.createConnection = (port, host) ->
   client = new Client
   client.port = port


add browser & os kind check method.

add browser & os kind check method.

--- syslibs/library.coffee
+++ syslibs/library.coffee
@@ -1,151 +1,187 @@
 # debug write
 JSLog = (a, b...) ->
-  #console.log(a)
-  for data in b
-    a = a.replace('%@', data)
-  console.log(a)
-  return a
+    #console.log(a)
+    for data in b
+        a = a.replace('%@', data)
+    console.log(a)
+    return a

 # format strings
-sprintf = (a, b...)->
-  for data in b
-    match = a.match(/%0\d*@/)
-    if (match?)
-      repstr = match[0]
-      num = parseInt(repstr.match(/\d+/))
-      zero =""
-      zero += "0" while (zero.length < num)
-      data2 = (zero+data).substr(-num)
-      a = a.replace(repstr, data2)
-    else
-      a = a.replace('%@', data)
-  return a
-
+sprintf = (a, b...)->
+    for data in b
+        match = a.match(/%0\d*@/)
+        if (match?)
+            repstr = match[0]
+            num = parseInt(repstr.match(/\d+/))
+            zero =""
+            zero += "0" while (zero.length < num)
+            data2 = (zero+data).substr(-num)
+            a = a.replace(repstr, data2)
+        else
+            a = a.replace('%@', data)
+    return a
+
 # YES/NO dialog
 isConfirm = (str) ->
-  if (window.confirm(str))
-    return 1
+    if (window.confirm(str))
+        return 1

 # get cookie value
 getCookieValue = (arg) ->
-  if (arg)
-    cookieData = document.cookie + ";"
-    startPoint1 = cookieData.indexOf(arg)
-    startPoint2 = cookieData.indexOf("=",startPoint1)+1
-    endPoint = cookieData.indexOf(";",startPoint1)
-    if(startPoint2 < endPoint && startPoint1 > -1)
-      cookieData = cookieData.substring(startPoint2,endPoint)
-      cookieData = cookieData
-      return cookieData
-  return false
+    if (arg)
+        cookieData = document.cookie + ";"
+        startPoint1 = cookieData.indexOf(arg)
+        startPoint2 = cookieData.indexOf("=",startPoint1)+1
+        endPoint = cookieData.indexOf(";",startPoint1)
+        if(startPoint2 < endPoint && startPoint1 > -1)
+            cookieData = cookieData.substring(startPoint2,endPoint)
+            cookieData = cookieData
+            return cookieData
+    return false

 # get unique id
 UniqueID = ->
-  S4 = ->
-    return (((1+Math.random())*0x10000)|0).toString(16).substring(1)
-  return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4())
+    S4 = ->
+        return (((1+Math.random())*0x10000)|0).toString(16).substring(1)
+    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4())

 #//////////////////////////////////////////////////////////////////////////////////////////

 # create JSRect
 JSRectMake = (x, y, w, h) ->
-  frame = new JSRect()
-  frame.origin.x = x
-  frame.origin.y = y
-  frame.size.width = w
-  frame.size.height = h
-  return frame
+    frame = new JSRect()
+    frame.origin.x = x
+    frame.origin.y = y
+    frame.size.width = w
+    frame.size.height = h
+    return frame

 # create JSPoint
 JSPointMake = (x, y) ->
-  point = new JSPoint()
-  point.x = x
-  point.y = y
-  return point
-
+    point = new JSPoint()
+    point.x = x
+    point.y = y
+    return point
+
 # create JSSize
 JSSizeMake = (w, h) ->
-  size = new JSSize()
-  size.width = w
-  size.height = h
-  return size
-
+    size = new JSSize()
+    size.width = w
+    size.height = h
+    return size
+
 # create JSRange
 JSMakeRange = (loc, len) ->
-  range = new JSRange()
-  range.location = loc
-  range.length = len
-  return range
-
+    range = new JSRange()
+    range.location = loc
+    range.length = len
+    return range
+
 #//////////////////////////////////////////////////////////////////////////////////////////

 # get browser size(not include scrolling bar)
 getApplicationFrame=->
-  frame = JSRectMake(0, 0, window.innerWidth - 1, window.innerHeight - 1)
-  return frame
-
+    frame = JSRectMake(0, 0, window.innerWidth - 1, window.innerHeight - 1)
+    return frame
+
 # get browser size(include scrolling bar)
 getBounds=->
-  frame = JSRectMake(0, 0, window.innerWidth, window.innerHeight)
-  return frame
+    frame = JSRectMake(0, 0, window.innerWidth, window.innerHeight)
+    return frame

 # Color management
 JSColor = (color) ->
-  ret = color
-  switch color
-    when "clearColor"
-      ret = "transparent"
+    ret = color
+    switch color
+        when "clearColor"
+            ret = "transparent"

-  return ret
-
+    return ret
+
 #//////////////////////////////////////////////////////////////////////////////////////////

 # Get Standard Path
 JSSearchPathForDirectoriesInDomains = (kind) ->
-  ret = ""
-  switch kind
-    when "JSLibraryDirectory"
-      ret = "Library"
-    when "JSDocumentDirectory"
-      ret = "Documents"
-    when "JSPictureDirectory"
-      ret = "Media/Picture"
-    when "JSSystemDirectory"
-      ret = "syslibs"
-    when "JSBackendDirectory"
-      ret = "backend"
-
-  return ret
+    ret = ""
+    switch kind
+        when "JSLibraryDirectory"
+            ret = "Library"
+        when "JSDocumentDirectory"
+            ret = "Documents"
+        when "JSPictureDirectory"
+            ret = "Media/Picture"
+        when "JSSystemDirectory"
+            ret = "syslibs"
+        when "JSBackendDirectory"
+            ret = "backend"
+
+    return ret

 #//////////////////////////////////////////////////////////////////////////////////////////

 random = (max) ->
-  return Math.floor(Math.random() * (max + 1))
+    return Math.floor(Math.random() * (max + 1))

 #//////////////////////////////////////////////////////////////////////////////////////////

 JSEscape = (str) ->
-  str = str.replace(/&/g, "&")
-  str = str.replace(/\'/g, """)
-  str = str.replace(/\"/g, """)
-  str = str.replace(/</g, "<")
-  str = str.replace(/>/g, ">")
-  return str
+    str = str.replace(/&/g, "&")
+    str = str.replace(/\'/g, """)
+    str = str.replace(/\"/g, """)
+    str = str.replace(/</g, "<")
+    str = str.replace(/>/g, ">")
+    return str

 #//////////////////////////////////////////////////////////////////////////////////////////

 objectNum = (obj)->
-  if (obj?)
-    return Object.keys(obj).length
-  else
-    return 0
+    if (obj?)
+        return Object.keys(obj).length
+    else
+        return 0

 #//////////////////////////////////////////////////////////////////////////////////////////

 isTouch =->
-  return ('ontouchstart' of window)
+    return ('ontouchstart' of window)

 #//////////////////////////////////////////////////////////////////////////////////////////

 isAndroid =->
-  return navigator.userAgent.indexOf('Android') != -1
+    return navigator.userAgent.indexOf('Android') != -1
+
+#//////////////////////////////////////////////////////////////////////////////////////////
+
+getBrowser =->
+    ua = navigator.userAgent
+    if (ua.match(".*iPhone.*"))
+        kind = 'iOS'
+    else if (ua.match(".*Android"))
+        kind = 'Android'
+    else if (ua.match(".*Windows.*"))
+        kind = 'Windows'
+    else if (ua.match(".*BlackBerry.*"))
+        kind = 'BlackBerry'
+    else if (ua.match(".*Symbian.*"))
+        kind = 'Symbian'
+    else if (ua.match(".*Macintosh.*"))
+        kind = 'Mac'
+    else if (ua.match(".*Linux.*"))
+        kind = 'Linux'
+    else
+        kind = 'Unknown'
+
+    if (ua.match(".*Safari.*") && !ua.match(".*Android.*") && !ua.match(".*Chrome.*"))
+        browser = 'Safari'
+    else if (ua.match(".*Gecko.*Firefox.*"))
+        browser = "Firefox"
+    else if (ua.match(".*Opera*"))
+        browser = "Opera"
+    else if (ua.match(".*MSIE*"))
+        browser = "MSIE"
+    else if (ua.match(".*Gecko.*Chrome.*"))
+        browser = "Chrome"
+    else
+        browser = 'Unknown'
+
+    return {'kind':kind, 'browser':browser}


refactor to use split due to lack of support for new URL

--- src/desktop/components/main_layout/header/view.coffee
+++ src/desktop/components/main_layout/header/view.coffee
@@ -131,8 +131,7 @@
     # Sometime '/personalize/' can exist as a redirect parameter in the URL.
     # This causes the flash message to display at unexpected times.
     # This ensures we check for personalize in the pathname.
-    referrerUrl = new URL(document.referrer)
-    if referrerUrl.pathname.match '^/personalize.*'
+    if document.referrer.split('?')[0].match '^/personalize.*'
       new FlashMessage message: 'Thank you for personalizing your profile'
     else if document.referrer.match '/artsy-primer-personalize/'
       new FlashMessage message: 'Thank you. Please expect your personalized portfolio in the next 2 business days.'


changed authorization token

changed authorization token

--- public/init.coffee
+++ public/init.coffee
@@ -2,7 +2,7 @@

 # TODO remove this before going live
 $.ajaxSettings.headers =
-  # "Authorization": "Bearer M0mPgTiGPtw5LkdCGwhel3gcGc5PqIPF"
+  "Authorization": "Bearer 1"
   "Accept": "application/vnd.epages.v1+json"

 App.start()


Fix stylesheets link.

Fix stylesheets link.

--- template/base/config.coffee
+++ template/base/config.coffee
@@ -30,7 +30,7 @@
           'vendor/scripts/backbone-0.9.0.js'
         ]

-    'styles/app.css':
+    'stylesheets/app.css':
       languages:
         '\\.css$': languages.CSSLanguage
         '\\.styl$': languages.StylusLanguage


Revert accidental dev changes to UndoManager

--- services/web/public/coffee/ide/editor/directives/aceEditor/undo/UndoManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/undo/UndoManager.coffee
@@ -14,13 +14,8 @@

 			@editor.on "changeSession", (e) =>
 				@reset()
-				# @session = e.session
+				@session = e.session
 				e.session.setUndoManager(@)
-
-		addSession: (session) ->
-			@session = session
-
-		addSelection: () ->

 		showUndoConflictWarning: () ->
 			@$scope.$apply () =>
@@ -36,8 +31,7 @@
 			@undoStack = []
 			@redoStack = []

-		add: (delta, allowMerge, session) ->
-			return
+		execute: (options) ->
 			if @firstUpdate
 				# The first update we receive is Ace setting the document, which we should
 				# ignore


We need to check for "smaller equals".

We need to check for "smaller equals".

--- app/assets/javascripts/bsat/sidebar.js.coffee
+++ app/assets/javascripts/bsat/sidebar.js.coffee
@@ -7,7 +7,7 @@
   #
   $('#sidebar-toggle-button').click (e) ->
     e.preventDefault()
-    if $(window).width() < 768
+    if $(window).width() <= 768
       $('body').toggleClass('sidebar-open')
       $('body').removeClass('sidebar-close')
     else


save marker images on init and use them later

--- app/assets/javascripts/gmaps/presenter.coffee
+++ app/assets/javascripts/gmaps/presenter.coffee
@@ -5,6 +5,9 @@
     @canvas = document.getElementById('map-canvas')
     @markers = $('#map-data').data('markers') unless @markers
     @infowindow = new (google.maps.InfoWindow)({ maxWidth: 200 })
+
+    @marker_image_default = { url: Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_1') }
+    @marker_image_highlighted = { url: Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_highlighted') }

     return unless @markers

@@ -72,15 +75,15 @@
     for id in markerData.ids
       $("#result-offer-#{id} .Listing-results__offer").removeClass('Listing-results__offer--highlighted');

-  handleResultMouseOver: (event) ->
+  handleResultMouseOver: (event) =>
     marker = $(event.currentTarget).data('marker')
     if marker
-      marker.setIcon Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_highlighted')
+      marker.setIcon @marker_image_highlighted

-  handleResultMouseOut: (event) ->
+  handleResultMouseOut: (event) =>
     marker = $(event.currentTarget).data('marker')
     if marker
-      marker.setIcon Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_1')
+      marker.setIcon @marker_image_default

   # called by library internally; listener set in Operations.ConstructMap
   handleNativeMapClick: (e) =>


Fix javascript error occuring when e is null/undefined

--- app/views/editor/level/thangs_tab_view.coffee
+++ app/views/editor/level/thangs_tab_view.coffee
@@ -255,7 +255,7 @@
 #      @thangsTreema.deselectAll()

   selectAddThang: (e) =>
-    return unless $(e.target).closest('.editor-level-thangs-tab-view').length
+    return unless e? and $(e.target).closest('.editor-level-thangs-tab-view').length
     if e then target = $(e.target) else target = @$el.find('.add-thangs-palette')  # pretend to click on background if no event
     return true if target.attr('id') is 'surface'
     target = target.closest('.add-thang-palette-icon')


Remove console.logs

Remove console.logs

--- assets/javascripts/simulator/simulator.coffee
+++ assets/javascripts/simulator/simulator.coffee
@@ -1,5 +1,4 @@
 runObservables = (observableFactories) ->
-  console.log "running observables", observableFactories
   R.foldl((map, key) ->
     scheduler = new Rx.TestScheduler
     results = scheduler.startWithTiming ->


tiny test advance

tiny test advance

--- test/cron_events_test.coffee
+++ test/cron_events_test.coffee
@@ -48,7 +48,8 @@
           cronTime: '0 0 1 1 *',
           eventName: 'event2',
           eventData: { },
-          started: true
+          started: true,
+          tz: 'UTC'
         }
       }
       room.robot.brain.emit 'loaded'
@@ -63,6 +64,8 @@
         expect(room.robot.cron.jobs.somejob).not.to.be.defined
       it 'jobs stored as started are started', ->
         expect(room.robot.cron.jobs.other).to.be.defined
+      it 'job in brain should have a tz recorded', ->
+        expect(room.robot.brain.data.cron.other.tz).to.eql 'UTC'

   # ---------------------------------------------------------------------------------
@@ -521,7 +524,7 @@
       context 'and user is admin', ->
         hubot 'cron stop somejob', 'admin_user'
         it 'should comply and stop the job', ->
-          expect(hubotResponse()).to.eql "The job somejob is now paused."
+          expect(hubotResponse()).to.eql 'The job somejob is now paused.'
         it 'should change brain to record it\'s not started', ->
           expect(room.robot.brain.data.cron.somejob.started).to.be.false
         it 'should not have added a job in the jobs queue', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -10,6 +10,7 @@
 ###

 ###*
+
 Observe or configure a gameObject that has been added
 to the engine.
 @name update


Fix margins

Fix margins

--- src/ubret/Graph.coffee
+++ src/ubret/Graph.coffee
@@ -6,7 +6,7 @@
   constructor: (opts) ->
     super opts

-    @margin = opts.margin or { left: 80, top: 20, bottom: 40, right: 20 }
+    @margin = opts.margin or { left: 80, top: 20, bottom: 90, right: 20 }
     @format = if opts.format then d3.format(opts.format) else d3.format(',.02f')

     @color = opts.color or '#0172E6'


Disable activity graph y axis for now

Disable activity graph y axis for now

--- static/js/plots.coffee
+++ static/js/plots.coffee
@@ -202,7 +202,7 @@
             # TODO: Limit the extent somehow!
             #plot.selectAll(".group .area").attr("d", (d) -> area d_and_i d)
             plot.select(".axis.x").call(xAxis)
-            plot.select(".axis.y").call(yAxis)
+            #plot.select(".axis.y").call(yAxis)

         zoom.on("zoom", draw)


Use an IPN callback from Moolah to confirm a transaction requiring no user input


:bug: Fix invalid status icon priority

:bug: Fix invalid status icon priority

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -121,7 +121,7 @@
     statusIconPosition = atom.config.get('linter.statusIconPosition')
     @statusTiles.push statusBar["add#{statusIconPosition}Tile"]
       item: @bottomStatus,
-      priority: 999
+      priority: -999

   # this method is called on package deactivate
   destroy: ->


Improvments to fuzzy.

Improvments to fuzzy.

--- lib/models/fuzzy.coffee
+++ lib/models/fuzzy.coffee
@@ -1,17 +1,11 @@
+# Fuzzy
+# https://github.com/myork/fuzzy
 #
-# * Fuzzy
-# * https://github.com/myork/fuzzy
-# *
-# * Copyright (c) 2012 Matt York
-# * Licensed under the MIT license.
+# Copyright (c) 2012 Matt York
+# Licensed under the MIT license.

 fuzzy = {}
-
-# Use in node or in browser
-if typeof exports isnt "undefined"
-  module.exports = fuzzy
-else
-  @fuzzy = fuzzy
+module.exports = fuzzy

 # Return all elements of `array` that have a fuzzy
 # match against `pattern`.
@@ -49,7 +43,7 @@
   # or wrap in template if its the next string in the pattern
   idx = 0
   while idx < len
-    # Ignore Whitespaces:
+    # Ignore Whitespaces
     patternIdx++ if pattern[patternIdx] is ' '

     ch = string[idx]
@@ -57,54 +51,16 @@
       ch = pre + ch + post
       patternIdx += 1

-      # consecutive characters should increase the score more than linearly
       currScore += 1 + currScore
     else
       currScore = 0
     totalScore += currScore
     result[result.length] = ch
     idx++
-
-  # return rendered string if we have a match for every char
-  if patternIdx is pattern.length
-    return (
-      rendered: result.join("")
-      score: totalScore
-    )
-  null
-
-
-# The normal entry point. Filters `arr` for matches against `pattern`.
-# It returns an array with matching values of the type:
-#
-#     [{
-#         string:   '<b>lah' // The rendered string
-#       , index:    2        // The index of the element in `arr`
-#       , original: 'blah'   // The original element in `arr`
-#     }]
-#
-# `opts` is an optional argument bag. Details:
-#
-#    opts = {
-#        // string to put before a matching character
-#        pre:     '<b>'
-#
-#        // string to put after matching character
-#      , post:    '</b>'
-#
-#        // Optional function. Input is an element from the passed in
-#        // `arr`, output should be the string to test `pattern` against.
-#        // In this example, if `arr = [{crying: 'koala'}]` we would return
-#        // 'koala'.
-#      , extract: function(arg) { return arg.crying; }
-#    }
-fuzzy.filter = (pattern, arr, opts) ->
-  opts = opts or {}
-
-  # Sort by score. Browsers are inconsistent wrt stable/unstable
-  # sorting, so force stable by using the index in the case of tie.
-  # See http://ofb.net/~sethml/is-sort-stable.html
-  arr.reduce(
+  return {rendered: result.join(""), score: totalScore} if patternIdx is pattern.length
+
+fuzzy.filter = (pattern, arr, opts={}) ->
+  highlighted = arr.reduce(
     (prev, element, idx, arr) ->
       str = element
       str = opts.extract(element) if opts.extract
@@ -121,3 +77,83 @@
     compare = b.score - a.score
     return compare if compare
     a.index - b.index
+
+  # No matches? Sort the original array using Damerau-Levenshtein.
+  if highlighted.length < 1
+    highlighted = arr.reduce(
+      (prev, element, idx, arr) ->
+        str = element
+        str = opts.extract(element) if opts.extract
+        prev[prev.length] =
+          string: str
+          score: DamerauLevenshtein(pattern, str)
+          index: idx
+          original: element
+        prev
+      ,[]
+    ).sort (a, b) ->
+      compare = a.score - b.score
+      return compare if compare
+      b.index - a.index
+  highlighted
+
+DamerauLevenshtein = (down, across, prices={}, damerau=true) ->
+  # https://github.com/cbaatz/damerau-levenshtein
+  switch typeof prices.insert
+    when "function"
+      insert = prices.insert
+    when "number"
+      insert = (c) ->
+        prices.insert
+    else
+      insert = (c) ->
+        1
+  switch typeof prices.remove
+    when "function"
+      remove = prices.remove
+    when "number"
+      remove = (c) ->
+        prices.remove
+    else
+      remove = (c) ->
+        1
+  switch typeof prices.substitute
+    when "function"
+      substitute = prices.substitute
+    when "number"
+      substitute = (from, to) ->
+        prices.substitute
+    else
+      substitute = (from, to) ->
+        1
+  switch typeof prices.transpose
+    when "function"
+      transpose = prices.transpose
+    when "number"
+      transpose = (backward, forward) ->
+        prices.transpose
+    else
+      transpose = (backward, forward) ->
+        1
+# ---------------------------------------------------------------------------- #
+  ds = []
+  if down is across
+    return 0
+  else
+    down = down.split("")
+    down.unshift null
+    across = across.split("")
+    across.unshift null
+    down.forEach (d, i) ->
+      ds[i] = []  unless ds[i]
+      across.forEach (a, j) ->
+        if i is 0 and j is 0
+          ds[i][j] = 0
+        else if i is 0
+          ds[i][j] = ds[i][j - 1] + insert(a)
+        else if j is 0
+          ds[i][j] = ds[i - 1][j] + remove(d)
+        else
+          ds[i][j] = Math.min(ds[i - 1][j] + remove(d), ds[i][j - 1] + insert(a), ds[i - 1][j - 1] + ((if d is a then 0 else substitute(d, a))))
+          ds[i][j] = Math.min(ds[i][j], ds[i - 2][j - 2] + ((if d is a then 0 else transpose(d, down[i - 1]))))  if damerau and i > 1 and j > 1 and down[i - 1] is a and d is across[j - 1]
+    ds[down.length - 1][across.length - 1]


Client: Activity: Feed: Added tab links and classNames

--- client/activity/lib/components/publicfeedpane/feedpanetabcontainer.coffee
+++ client/activity/lib/components/publicfeedpane/feedpanetabcontainer.coffee
@@ -37,13 +37,27 @@
     kd.singletons.router.handleRoute route

+  getMostRecentTabClassNames: -> classnames
+    'FeedPane-tab' : yes
+    'active'       : not @props.showPopularMessages
+
+
+  getMostLikedTabClassNames: -> classnames
+    'FeedPane-tab' : yes
+    'active'       : @props.showPopularMessages
+
+
   render: ->

     return null  unless @props.thread

-      <Link className='FeedPane-tab'>Most Liked</Link>
-      <Link className='FeedPane-tab active'>Most Recent</Link>
+      <Link
+        onClick={@bound 'showPopularMessages'}
+        className={@getMostLikedTabClassNames()}>Most Liked</Link>
+      <Link
+        onClick={@bound 'showMostRecentMessages'}
+        className={@getMostRecentTabClassNames()}>Most Recent</Link>

         <input className='FeedPane-searchInput' placeholder='Search...'/>


bugfix: array comprehensions require round-braces

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -162,7 +162,7 @@
                 callback err, items, rest...
                 # to trigger dataEnd
                 unless err
-                  ids = item.getId?() for item in items
+                  ids = (item.getId?() for item in items)
                   callback null, null, ids
             else
               JGroup.streamModels selector, options, callback
@@ -231,8 +231,6 @@
       @emit 'ready'

   markGroupRelationship:(controller, ids)->
-    # return unless KD.isLoggedIn()
-
     fetchRoles =
       member: (view)-> view.markMemberGroup()
       admin : (view)-> view.markGroupAdmin()


Hide loader just before listing the items.

--- client/landing/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
+++ client/landing/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
@@ -95,7 +95,6 @@
     KD.whoami().fetchGroups (err, groups)=>
       if err then warn err
       else if groups?
-        @listController.hideLazyLoader()

         stack = []
         groups.forEach (group)->
@@ -113,7 +112,9 @@
                 return a.group.slug > b.group.slug
               else
                 return not a.admin and b.admin
+            @listController.hideLazyLoader()
             @listController.instantiateListItems results
+

   decreasePendingCount:->
     @pending--


Actually trigger input event

Actually trigger input event

--- src/trix/controllers/editor_element_controller.coffee
+++ src/trix/controllers/editor_element_controller.coffee
@@ -67,7 +67,7 @@

   didChangeDocument: (document) ->
     @save()
-    triggerEvent(@inputElement, "input")
+    triggerEvent("input", onElement: @element)

   shouldAcceptFile: (document) ->
     true


Make sure to show/hide sex and characteristics after a dragon change

[#87262294]

--- app/views/dragon-view.coffee
+++ app/views/dragon-view.coffee
@@ -17,6 +17,7 @@
     @_updateClasses()
     @_updateCharacteristics()
     @_updateSexLabel()
+    @_updateVisibility()

   setSexVisible: (@showSex) ->
     @_updateVisibility()
@@ -87,4 +88,5 @@

     @_updateSexLabel()
     @_updateCharacteristics()
+    @_updateVisibility()


预先增加用户相关的 action

预先增加用户相关的 action

--- ShareTalk/src/routes/index.coffee
+++ ShareTalk/src/routes/index.coffee
@@ -1,7 +1,16 @@
 express = require "express"
 router = express.Router()

+# 首页
 router.get "/", (req, res) ->
   res.render "index"

+# 登入页面
+router.get "/login", (req, res) ->
+  res.render "index"
+
+# 登入请求
+router.post "/dpLogin", (req, res) ->
+  res.render "index"
+
 module.exports = router


:skull: pathlen stuff I'm no longer using

--- source/javascripts/county-map.coffee
+++ source/javascripts/county-map.coffee
@@ -75,7 +75,6 @@

     connections.enter().append('path')
       .attr('d', env.path)
-      .each(-> pathlen = @getTotalLength())
       .attr('class', (d) -> "connection stars#{d.properties.stars}")
       .style('stroke', (d) -> env.colors[d.properties.stars - 1])
       .attr('stroke-dasharray', -> len = @getTotalLength(); "#{len},#{len}")


add an info notification to tell users that the site build has begun

--- lib/server/build.coffee
+++ lib/server/build.coffee
@@ -3,6 +3,8 @@
 module.exports =
   build: ->
     buildCommand = atom.config.get('jekyll.buildCommand')
+
+    atom.notifications.addInfo('Starting Jekyll Site Build')

     @buildProcess = childProcess.spawn buildCommand[0], buildCommand[1...], {cwd: atom.project.getPaths()[0]}


Headers null if not not allowed access

Headers null if not not allowed access

--- lib/zen.firewall.coffee
+++ lib/zen.firewall.coffee
@@ -51,7 +51,7 @@
     headers = {}
     for key, value of ZEN.headers
       headers[key] = if Array.isArray(value) then value.join(", ") else value
-    headers["Access-Control-Allow-Origin"] = request.headers.origin or "*"
+    headers = null if headers["Access-Control-Allow-Origin"] not in [request.headers.origin, "*"]
     response.writeHead "204", "No Content", headers
     response.end()
     valid = false


cover admin_test

cover admin_test

--- test/phabs_admin_test.coffee
+++ test/phabs_admin_test.coffee
@@ -383,6 +383,43 @@
   # ---------------------------------------------------------------------------------
   context 'user wants to create an alias for a project', ->

+    context 'and is project is unknown to phabricator', ->
+      beforeEach ->
+        room.robot.brain.data.phabricator.projects = {
+          'Bug Report': { },
+          'project with phid': { phid: 'PHID-PROJ-1234567' },
+        }
+        room.robot.brain.data.phabricator.aliases = {
+          bugs: 'project with phid',
+          bug: 'project with phid'
+        }
+        do nock.disableNetConnect
+        nock(process.env.PHABRICATOR_URL)
+          .get('/api/project.query')
+          .query({
+            'names[0]': 'project1',
+            'api.token': 'xxx'
+          })
+          .reply(200, { result: {
+            'data': [ ],
+            'slugMap': [ ],
+            'cursor': {
+              'limit': 100,
+              'after': null,
+              'before': null
+            }
+          } })
+
+      afterEach ->
+        room.robot.brain.data.phabricator = { }
+        nock.cleanAll()
+
+      context 'phad project1 alias bug', ->
+        hubot 'phad project1 alias bug'
+        it 'should reply with proper info', ->
+          expect(hubotResponse())
+            .to.eql 'Sorry, project1 not found.'
+
     context 'when the alias already exists', ->
       beforeEach ->
         room.robot.brain.data.phabricator.projects = {
@@ -480,6 +517,43 @@

   # ---------------------------------------------------------------------------------
   context 'user wants to add a feed to a project', ->
+
+    context 'and is project is unknown to phabricator', ->
+      beforeEach ->
+        room.robot.brain.data.phabricator.projects = {
+          'Bug Report': { },
+          'project with phid': { phid: 'PHID-PROJ-1234567' },
+        }
+        room.robot.brain.data.phabricator.aliases = {
+          bugs: 'project with phid',
+          bug: 'project with phid'
+        }
+        do nock.disableNetConnect
+        nock(process.env.PHABRICATOR_URL)
+          .get('/api/project.query')
+          .query({
+            'names[0]': 'project1',
+            'api.token': 'xxx'
+          })
+          .reply(200, { result: {
+            'data': [ ],
+            'slugMap': [ ],
+            'cursor': {
+              'limit': 100,
+              'after': null,
+              'before': null
+            }
+          } })
+
+      afterEach ->
+        room.robot.brain.data.phabricator = { }
+        nock.cleanAll()
+
+      context 'phad project1 feed to #dev', ->
+        hubot 'phad project1 feed to #dev'
+        it 'should reply with proper info', ->
+          expect(hubotResponse())
+            .to.eql 'Sorry, project1 not found.'

     context 'but the feed already exists', ->
       beforeEach ->
@@ -537,6 +611,43 @@

   # ---------------------------------------------------------------------------------
   context 'user wants to remove a feed from a project', ->
+
+    context 'and is project is unknown to phabricator', ->
+      beforeEach ->
+        room.robot.brain.data.phabricator.projects = {
+          'Bug Report': { },
+          'project with phid': { phid: 'PHID-PROJ-1234567' },
+        }
+        room.robot.brain.data.phabricator.aliases = {
+          bugs: 'project with phid',
+          bug: 'project with phid'
+        }
+        do nock.disableNetConnect
+        nock(process.env.PHABRICATOR_URL)
+          .get('/api/project.query')
+          .query({
+            'names[0]': 'project1',
+            'api.token': 'xxx'
+          })
+          .reply(200, { result: {
+            'data': [ ],
+            'slugMap': [ ],
+            'cursor': {
+              'limit': 100,
+              'after': null,
+              'before': null
+            }
+          } })
+
+      afterEach ->
+        room.robot.brain.data.phabricator = { }
+        nock.cleanAll()
+
+      context 'phad project1 remove from #dev', ->
+        hubot 'phad project1 remove from #dev'
+        it 'should reply with proper info', ->
+          expect(hubotResponse())
+            .to.eql 'Sorry, project1 not found.'

     context 'and the feed exists', ->
       beforeEach ->


set initial lastOp via method call in constructor

--- oridomi.coffee
+++ oridomi.coffee
@@ -437,11 +437,10 @@
     # These properties record starting angles for touch/drag events.
     # Initialize both to zero.
     @_xLast = @_yLast = 0
-    # This property determines the effect used during touch/drag events.
-    @lastOp = @_defaultOp = method: 'accordion', angle: 0, options: {}

     # Cache a jQuery object of the element if applicable.
     @$el = $ @el if $
+    @accordion 0

   # Internal Methods


kitecontroller: return the kite explicitly for readability

--- client/app/MainApp/kite/kitecontroller.coffee
+++ client/app/MainApp/kite/kitecontroller.coffee
@@ -31,6 +31,7 @@
   createKite:(kiteName, correlationName, kiteKey)->
     kite = new Kite { kiteName, correlationName, kiteKey }
     kite.on 'destroy', => @destroyKite kite
+    return kite

   # notification = null
   notify = (options = {})->


added register link to sidebar

added register link to sidebar

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -39,9 +39,10 @@

     navAdditions = [
       { type  : 'separator',      order : 65 }
-      { title : 'Invite Friends', order : 66,  type : 'account', role : 'member' }
-      { title : 'Logout',         order : 100, path : '/Logout', type : 'account', loggedIn : yes }
-      { title : 'Login',          order : 101, path : '/Login',  type : 'account', loggedIn : no  }
+      { title : 'Invite Friends', order : 66,  type : 'account',   role : 'member' }
+      { title : 'Logout',         order : 100, path : '/Logout',   type : 'account', loggedIn : yes }
+      { title : 'Login',          order : 101, path : '/Login',    type : 'account', loggedIn : no  }
+      { title : 'Register',       order : 102, path : '/Register', type : 'account', loggedIn : no  }
     ]

     KD.registerNavItem navItem for navItem in navAdditions