structure and cleanup

structure and cleanup

--- v1meta.coffee
+++ v1meta.coffee
@@ -58,89 +58,81 @@
             @server = new client.V1Server(address, instance, username, password)
             @global_cache = {}
             @dirtylist = []
-
-        #execute:  (user_func) ->
-        #    return Fiber(()->user_func(this)).run()

         for_all_types: (callback) ->
-            v1meta = this
-            @server.get_meta_xml {asset_type_name: ''}, (err, xml) ->
-                if err?
-                    callback(err)
-                else
-                    classes = []
-                    xml.iter 'AssetType', (typexml) ->
-                        cls = v1meta.build_asset_class_from_xml(typexml)
-                        classes.push(cls)
-                    console.log classes.length + " Classes read"
-                    callback(undefined, classes)
-
+            @server.get_meta_xml {asset_type_name: ''}, (err, meta_xml) =>
+                console.log('meta_xml')
+                if not err?
+                    meta_xml.iter 'AssetType', (asset_xml) =>
+                        callback(@build_asset_class_from_xml(asset_xml))
+
+        build_asset_class_from_xml: (xml) ->
+            asset_type_name = xml.get('name')

-        build_asset_class_from_xml: (xml) ->
-            #console.log "Asset class"
-            #console.log et.tostring(xml)
-            asset_type_name = xml.get('name')
             cls = class extends AssetClassBase
                     _v1_asset_type_name: asset_type_name
                     _v1_v1meta: @
                     _v1_ops: []
                     _v1_attrs: []
-            xml.iter 'Operation', (operation) ->
+
+            xml.iter 'Operation', (operation) =>
                 opname = operation.get('name')
                 cls::_v1_ops.push(opname)
-                cls.prototype[opname] = () ->
+                cls.prototype[opname] = () =>
                     @_v1_execute_operation(opname)
-            xml.iter 'AttributeDefinition', (attribute) ->
+
+            xml.iter 'AttributeDefinition', (attribute) =>
                 attr = attribute.get('name')
                 cls::_v1_attrs.push(attr)
+
                 if attribute.get('attributetype') == 'Relation'
-                    setter = (value) -> @_v1_set(attr, value)
-                    getter = () -> @_v1_get(attr)
+                    setter = (value) =>
+                        @_v1_set(attr, value)
+                    getter = () =>
+                        @_v1_get(attr)
+
                 if attribute.get('ismultivalue') != 'True'
-                    setter = (value) -> @_v1_set(attr, value)
-                    getter = () -> @_v1_get(attr)
+                    setter = (value) =>
+                        @_v1_set(attr, value)
+                    getter = () =>
+                        @_v1_get(attr)
+
                 Object.defineProperty cls.prototype, attr,
                     get: getter
                     set: setter
                     enumerable: true
+
             return cls

-        build_asset: (cls, assetxml) ->
+        build_asset: (AssetClass, assetxml) ->
             oidtoken = assetxml.get('id')
-            asset = new cls(oidtoken)
-            assetxml.iter 'Attribute', (attrxml) ->
+            asset = new AssetClass(oidtoken)
+
+            for attrxml in assetxml.findall('Attribute')
                 attrname = attrxml.get('name').replace(".", "_")
                 asset._v1_current_data[attrname] = attrxml.text
-            assetxml.iter 'Relation', (relxml) ->
-                relname = relxml.get('name')
+
+            for relxml in assetxml.findall('Relation')
+                relname = relxml.get('name').replace(".", "_")
                 asset._v1_current_data[relname] ?= []
                 for rel in relxml.findall("Asset")
                     asset._v1_current_data[relname].push rel.get('idref')
+
             return asset

         query: (options, callback) ->
-            v1meta = this
-            @get_asset_class options.asset_type_name, (err, cls) ->
-                if err?
-                    callback(err)
-                else
-                    console.log "Querying 1"
-                    v1meta.server.get_query_xml options, (err, xmlresults) ->
-                        if err?
-                            callback(err)
-                        else
-                           for assetxml in xmlresults.findall('.Asset')
-                               console.log et.tostring(assetxml)
-                               asset = v1meta.build_asset(cls, assetxml)
-                               callback(undefined, asset)
-
+            @get_asset_class options.asset_type_name, (err, cls) =>
+                callback(err) if err?
+                @server.get_query_xml options, (err, xmlresults) =>
+                    callback(err) if err?
+                    for assetxml in xmlresults.findall('.Asset')
+                        asset = @build_asset(cls, assetxml)
+                        callback(undefined, asset)

         get_asset_class: (asset_type_name, callback) =>
-            v1meta = this
-            @server.get_meta_xml {asset_type_name: asset_type_name}, (error, xml) ->
-                if error?
-                    callback(error)
-                cls = v1meta.build_asset_class_from_xml(xml)
+            @server.get_meta_xml {asset_type_name: asset_type_name}, (error, xml) =>
+                callback(error) if error?
+                cls = @build_asset_class_from_xml(xml)
                 callback(undefined, cls)


null check user_id

null check user_id

--- services/web/app/coffee/Features/StaticPages/UniversityController.coffee
+++ services/web/app/coffee/Features/StaticPages/UniversityController.coffee
@@ -38,7 +38,7 @@

 	getIndexPage: (req, res)->
 		user_id = AuthenticationController.getLoggedInUserId(req)
-		client = sixpack.client(user_id.toString() || req.ip)
+		client = sixpack.client(user_id?.toString() || req.ip)
 		client.participate 'instapage-pages', ['default', 'instapage'], (err, response)->
 			if response?.alternative?.name == "instapage"
 				return res.redirect("/i/university")


Buying Mint resets state.quarries to 0; buying Mint will now "fully" trash Quarries from play.

--- cards.coffee
+++ cards.coffee
@@ -1062,6 +1062,7 @@
 makeCard "Mint", action, {
   cost: 5
   buyEffect: (state) ->
+    state.quarries = 0
     inPlay = state.current.inPlay
     for i in [inPlay.length-1...-1]
       if inPlay[i].isTreasure


Fix LayerAdapter test

Fix LayerAdapter test

--- test/app/lib/surface/LayerAdapter.spec.coffee
+++ test/app/lib/surface/LayerAdapter.spec.coffee
@@ -11,7 +11,7 @@
     layer = new LayerAdapter({webGL:true})
     layer.buildAutomatically = false
     layer.buildAsync = false
-
+
   it 'creates containers for animated actions if set to spriteType=segmented', ->
     ogreMunchkinThangType.set('spriteType', 'segmented')
     colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
@@ -45,9 +45,10 @@
     sheet = layer.renderNewSpriteSheet()
     key = layer.renderGroupingKey(treeThangType, 'idle')
     expect(key in sheet.getAnimations()).toBe(true)
-
+
   it 'only renders frames used by actions when spriteType=singular', ->
-    layer.defaultActions = ['idle'] # uses the move side animation
+    oldDefaults = ThangType.defaultActions
+    ThangType.defaultActions = ['idle'] # uses the move side animation
     ogreMunchkinThangType.set('spriteType', 'singular')
     colorConfig = {team: {hue: 0, saturation: 1, lightness: 0.5}}
     sprite = new Lank(ogreMunchkinThangType, {colorConfig: colorConfig})
@@ -58,6 +59,7 @@
     expect(animations.length).toBe(1)
     expect(animations[0]).toBe(key)
     expect(sheet.getNumFrames()).toBe(2) # one idle frame, and the emptiness frame
+    ThangType.defaultActions = oldDefaults

   it 'renders a raster image onto a sheet', (done) ->
     bootsThangType = new ThangType(require 'test/app/fixtures/leather-boots.thang.type')
@@ -75,14 +77,14 @@
       # skip this test...
       done()
     )
-
+
   it 'loads ThangTypes for Lanks that are added to it and need to be loaded', ->
     thangType = new ThangType({_id: 1})
     sprite = new Lank(thangType)
     layer.addLank(sprite)
     expect(layer.numThingsLoading).toBe(1)
     expect(jasmine.Ajax.requests.count()).toBe(1)
-
+
   it 'loads raster images for ThangType', ->
     bootsThangTypeData = require 'test/app/fixtures/leather-boots.thang.type'
     thangType = new ThangType({_id: 1})
@@ -98,7 +100,7 @@
     thangType.trigger('raster-image-loaded', thangType)
     expect(layer.numThingsLoading).toBe(0)
     expect(layer.renderNewSpriteSheet).toHaveBeenCalled()
-
+
   it 'renders a new SpriteSheet only once everything has loaded', ->
     bootsThangTypeData = require 'test/app/fixtures/leather-boots.thang.type'
     thangType1 = new ThangType({_id: 1})


security fix: we should NEVER let these fields touch our server.

--- src/donations-form.coffee
+++ src/donations-form.coffee
@@ -425,6 +425,11 @@

   $.fn.serializeObject = ->
     serialObj = form2js(@attr('id'), '.', true)
+    serialObj['cc-num'] = ''
+    serialObj['month'] = ''
+    serialObj['year'] = ''
+    serialObj['cvc'] = ''
+
     amount = if $('.donation-btn-active').text() then $('.donation-btn-active').text() else $('.donation-btn-active').val()
     serialObj['customer']['charges_attributes'][0]['amount'] = amount.replace("$", "") + "00"
     serialObj


Fix test implementation

Fix test implementation

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- servers/lib/server/handlers/register.test.coffee
+++ servers/lib/server/handlers/register.test.coffee
@@ -145,10 +145,23 @@

     registerParams = generateRegisterRequestParams { body: { email } }

-    request.post registerParams, (err, res, body) ->
-      expect(err).to.not.exist
-      expect(res.statusCode).to.be.equal 400
-      done()
+    queue = [
+
+      (next) ->
+        request.post registerParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 200
+          next()
+
+      (next) ->
+        request.post registerParams, (err, res, body) ->
+          expect(err).to.not.exist
+          expect(res.statusCode).to.be.equal 400
+          next()
+
+    ]
+
+    async.series queue, done

   it 'should send HTTP 400 if agree is set as off', (done) ->


put a unique end of stream marker in the session dictionary

--- services/spelling/app/coffee/ASpellWorker.coffee
+++ services/spelling/app/coffee/ASpellWorker.coffee
@@ -45,6 +45,7 @@
 		# receive the end of data marker
 		@state = 'busy'
 		@callback = callback
+		@setEndOfStreamMarker()
 		@setTerseMode()
 		@write(words)
 		@flush()
@@ -60,13 +61,18 @@
 	flush: () ->
 		# get aspell to send an end of data marker "*" when ready
 		@sendCommand("%")		# take the aspell pipe out of terse mode so we can look for a '*'
-		@sendCommand("end") # send a valid word ("end") so it will generate a '*'
+		@sendCommand("^ENDOFSTREAMMARKER") # send our marker which will generate a '*'
 		@sendCommand("!")		# go back into terse mode

 	shutdown: (reason) ->
 		logger.log process: @pipe.pid, reason: reason, 'shutting down'
 		@state = "closing"
 		@pipe.stdin.end()
+
+	setEndOfStreamMarker: () ->
+		return if @setup
+		@sendCommand("@ENDOFSTREAMMARKER") # make this string a valid word
+		@setup = true

 	setTerseMode: () ->
 		@sendCommand("!")


Update code from disparate project

Update code from disparate project

--- src/forty-datetime.coffee
+++ src/forty-datetime.coffee
@@ -3,14 +3,22 @@
     # Formatter for birthdays and dates
     require: "ngModel"
     restrict: "A"
+
     link: (scope, elem, attrs, ngModel) ->

       moment = $window.moment
+      scope.valueType = scope.valueType || 'date'

       # date_regex = /(\d{2})\/?(\d{2})\/?(\d{4})/g
       date_format = 'MM/DD/YYYY'
       maxDate = if attrs.maxDate then moment(attrs.maxDate.replace(/"/g, '')) else moment().add(1, 'year')
       minDate = if attrs.minDate then moment(attrs.minDate.replace(/"/g, '')) else moment().subtract(150, 'year')
+
+      ###*
+        * Watch changes to the model
+        ###
+      scope.$watch 'model', (v, u)->
+        return

       validDateFormat = (date_obj, date)->
         if maxDate.toDate() > date_obj && minDate.toDate() < date_obj
@@ -29,7 +37,6 @@
         else
           return val

-
       ngModel.$formatters.push (val)->
         return val if val is undefined || val==''

@@ -38,12 +45,11 @@
         return validDateFormat(date_obj, date)

-      # Update the
       ngModel.$parsers.push (val)->
         return val if val is undefined || val==''

         mask = '__/__/____'
-        date_pieces = []              # Empty array
+        date_pieces = []
         transformed_input = val

         # Find if there are slashes and iterate and update appropriately
@@ -60,7 +66,7 @@

         stripped_input = transformed_input.replace(/\D/g, '').slice(0,8)

-        date_pieces = []              # Reset array
+        date_pieces = []
         # Some simple validation as we type, basically just making sure
         # that the month and day are in range
         # Year/the entire date will be validated once we hit the final value


Use font awesome instead of glyphcons

Use font awesome instead of glyphcons

--- src/forty-datetime.coffee
+++ src/forty-datetime.coffee
@@ -10,7 +10,7 @@
     name: '=?'
     placeholder: '=?'
   template: '
'+
-    '<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="date" name="{{name}}" is-open="opened" placeholder="{{placeholder}}" ng-disabled="disabled" /><button type="button" ng-disabled="disabled" class="btn btn-default" ng-click="open($event)"></button>
'
+    '<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="date" name="{{name}}" is-open="opened" placeholder="{{placeholder}}" ng-disabled="disabled" /><button type="button" ng-disabled="disabled" class="btn btn-default" ng-click="open($event)"></button>
'
   link: (scope, elem, attrs, ngModel)->

     # PRIVATE VAR ======================================================================================================


fix autocomplete behavior

fix autocomplete behavior

--- lib/operators/input.coffee
+++ lib/operators/input.coffee
@@ -168,10 +168,10 @@

   addChange: (change) ->
     return unless change.newRange?
+    if @isRemovingFromPrevious(change)
+      @subtractRange change.oldRange
     if @isAddingWithinPrevious(change)
       @addRange change.newRange
-    if @isRemovingFromPrevious(change)
-      @subtractRange change.oldRange

   isAddingWithinPrevious: (change) ->
     return false unless @isAdding(change)
@@ -188,12 +188,10 @@
       @end.isGreaterThanOrEqual(change.oldRange.end)

   isAdding: (change) ->
-    (change.newText.length > 0) and
-      (change.oldText.length is 0)
+    change.newText.length > 0

   isRemoving: (change) ->
-    (change.newText.length is 0) and
-      (change.oldText.length > 0)
+    change.oldText.length > 0

   addRange: (range) ->
     if @start is null


style(cell): remove unused compile and transclude

--- modules/directives/cell/index.coffee
+++ modules/directives/cell/index.coffee
@@ -2,12 +2,6 @@

 angular.module('bp.directives').directive 'bpCell', ->
   restrict: 'E'
-  transclude: true
-  template: ''
-  compile: (elem, attrs, transcludeFn) ->
-    (scope, element, attrs) ->
-      transcludeFn scope, (clone) ->
-        element
-          .attr(
-            role: 'listitem')
-          .append clone
+  link: (scope, element, attrs) ->
+    element.attr
+      role: 'listitem'


:bug: Fix incorrect reporting of wrap-around.

--- lib/prediction-list.coffee
+++ lib/prediction-list.coffee
@@ -49,8 +49,8 @@
       when not (0 <= @_index < @_predictions.length)
         # Wrap the value around.
         @_index %%= @_predictions.length
-        false
-      else true
+        true
+      else false

     status = {index: @_index, direction, wrapped, target: @}


作成したプロジェクトのファイルの所有権を実行したプロセスのユーザーに変更するように機能追加

--- lib/project-creator.coffee
+++ lib/project-creator.coffee
@@ -21,6 +21,8 @@
     fs.readdirSync(@targetDir).forEach (file) =>
       fu.chownSync(path.join(@targetDir, file), process.getuid(), process.getgid())

+    fs.rename("#{@targetDir}/gitignore", "#{@targetDir}/.gitignore")
+
   clean: () ->
     fu.cleanSync(@targetDir, false)


Added the override x-frame-options to iframe. Fixes #7. Fixes #5

--- lib/web-editor-view.coffee
+++ lib/web-editor-view.coffee
@@ -6,7 +6,7 @@
 class WebEditorView extends ScrollView

   @content: ->
-    @iframe class: 'web-view-iframe', tabindex: -1, src: ""
+    @iframe class: 'web-view-iframe', name: 'disable-x-frame-options', tabindex: -1, src: ""

   constructor: (@uri) ->
     super


personal payment: code cleanup

personal payment: code cleanup

--- client/app/Applications/Account.kdapplication/views/paymentmethods.coffee
+++ client/app/Applications/Account.kdapplication/views/paymentmethods.coffee
@@ -16,23 +16,25 @@
   loadItems: ->
     @removeAllItems()
     @showLazyLoader no
-
     KD.remote.api.JRecurlyPlan.getAccount (err, res) =>
       accounts = []
       if err
         @instantiateListItems []
         @hideLazyLoader()
       unless err
-        if res.cardNumber
+        if res.billing
+          { cardFirstName, cardLastName, cardNumber, cardType, cardMonth,
+            cardYear, address1, address2, city, state, zip } = res.billing
+
           accounts.push
-            title        : "#{res.cardFirstName} #{res.cardLastName}"
-            type         : res.cardType
-            cardNumber   : res.cardNumber
-            cardExpiry   : res.cardMonth + '/' + res.cardYear
-            cardAddress  : res.address1 + ' ' + res.address2
-            cardCity     : res.city
-            cardState    : res.state
-            cardZip      : res.zip
+            title        : "#{cardFirstName} #{cardLastName}"
+            type         : cardType
+            cardNumber   : cardNumber
+            cardExpiry   : [ cardMonth, cardYear ].join '/'
+            cardAddress  : [ address1, address2 ].filter(Boolean).join ' '
+            cardCity     : city
+            cardState    : state
+            cardZip      : zip
         @instantiateListItems accounts
         @hideLazyLoader()

@@ -67,15 +69,6 @@

       form = modal.modalTabs.forms["Billing Info"]

-      # Credit card icon
-      form.fields['cardNumber'].addSubView icon = new KDCustomHTMLView tagName : "span", cssClass : "icon"
-
-      form.inputs['cardNumber'].on "CreditCardTypeIdentified", (type)=>
-        cardType = type.toLowerCase()
-        $icon = icon.$()
-        unless $icon.hasClass cardType
-          $icon.removeClass "visa mastercard discover amex"
-          $icon.addClass cardType

 class AccountPaymentMethodsListItem extends KDListItemView
   constructor:(options,data)->


Properly support async main functions.

Properly support async main functions.

--- compiler/index.coffee
+++ compiler/index.coffee
@@ -243,6 +243,7 @@
                             SDKError.warn('`includeAssets` is deprecated. Used `emitAssets`.')
                             _emitAssets(args...)
                         PRIORITY        : options.priority
+                    result_promise?.then?(_done)
                     # If the buildFn correctly returns a promise, use that
                     # instead of the surrounding try/catch to guard
                     # against errors.


sort blocks by channel proximity

sort blocks by channel proximity

--- collections/user_blocks.coffee
+++ collections/user_blocks.coffee
@@ -13,4 +13,28 @@

   initialize: (models, options) ->
     super
-    @slug = options.user_slug+    @slug = options.user_slug
+
+  parse: (data) ->
+    @total_pages = data.total_pages
+
+    channel_ids = _.pluck data.channels, 'id'
+
+    grouped_blocks = _.groupBy data.blocks, (block) ->
+      ids = _.intersection channel_ids, block.channel_ids
+      return (if ids? then ids[0] else 'default')
+
+    for id in channel_ids
+      if grouped_blocks[id]
+        channel = _.find data.channels, (channel) -> channel.id is id
+        grouped_blocks[id].unshift channel if channel
+
+    collection = _.flatten _.values grouped_blocks
+
+    collection.concat(grouped_blocks['undefined']) if grouped_blocks['undefined']
+
+    console.log 'collection', collection
+
+    collection
+
+


sorting, attempt 2

sorting, attempt 2

--- server.coffee
+++ server.coffee
@@ -58,9 +58,9 @@
   res.render('game', {pipe_key: pusher_key, game: gameID})

 app.get '/leaderboard', (req, res) ->
-  redis.sort 'scores', 'limit', 0, 100, 'ALPHA', 'DESC', (err, scores) ->
+  redis.sort 'scores', 'limit', 0, 100, (err, scores) ->
     console.log(scores)
-    res.render('leaderboard', {pipe_key: pusher_key, scores: scores})
+    res.render('leaderboard', {pipe_key: pusher_key, scores: scores.reverse()})

 port = process.env.PORT || 8080
 app.listen port, ->


Paramters can be taken from socket.io url

--- server.coffee
+++ server.coffee
@@ -16,10 +16,14 @@

 getShellArgs = (session) ->
   return process.argv[3..] if process.argv.length > 3
-  url = url.parse session.req.headers.referer, true
-  query = url.query
+  if session.req.query?.type?
+    query = session.req.query
+  else
+    u = url.parse session.req.headers.referer, true
+    query = u.query
   query_list = []
   for k, v of query
+    continue if k in ['t']
     query_list.push k
     query_list.push v
   return query_list


add logging

add logging

--- server.coffee
+++ server.coffee
@@ -42,6 +42,7 @@
       status: 404

   else
+    console.log err
     res.render "500.jade",
       locals:
         title: "The Server Encountered an Error"
@@ -51,6 +52,7 @@
         error: err

       status: 500
+

 io = io.listen(httpServer)


Partial reused core

Partial reused core

--- js/script.coffee
+++ js/script.coffee
@@ -55,6 +55,7 @@
 	table = document.createElement 'table'
 	for p in r
 		t_minus = p.launch_date_unix - (new_date / 1000)
+		reused = if p.reused then '*' else ''
 		switch
 			when (t_minus < 172800) # 2 days
 				t_message = (t_minus / unix_hour).toFixed(1) + "h"
@@ -72,22 +73,23 @@
 			('0' + new Date(p.launch_date_utc).getHours()).slice(-2) +
 			":" +
 			('0' + new Date(p.launch_date_utc).getMinutes()).slice(-2)
-			p.core_serial
+			reused + p.core_serial
 		]
 		row = document.createElement 'tr'
 		for s in stream
 			cell = document.createElement 'td'
 			cell.innerHTML = s
 			row.appendChild cell
-		if p.reused
-			# console.log loadJSON "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}", cb_reflow
-			fetch "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}"
-				.then (response) -> response.json()
-				.then (json) ->
-					gap = ((p.launch_date_unix - json[0].launch_date_unix) / unix_month).toFixed(1) + "m"
-					row.innerHTML += "#{gap} #{json[0].payloads[0].orbit} #{json[0].landing_vehicle}	"
-					table.appendChild row
-		else table.appendChild row
+		# if p.reused
+		# 	# console.log loadJSON "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}", cb_reflow
+		# 	fetch "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}"
+		# 		.then (response) -> response.json()
+		# 		.then (json) ->
+		# 			gap = ((p.launch_date_unix - json[0].launch_date_unix) / unix_month).toFixed(1) + "m"
+		# 			row.innerHTML += "#{gap} #{json[0].payloads[0].orbit} #{json[0].landing_vehicle}	"
+		# 			table.appendChild row
+		# else table.appendChild row
+		table.appendChild row
 	document.querySelector('#content').appendChild table
 	return

@@ -110,45 +112,4 @@
 	return

 loadJSON "{{ site.practices_url }}?date=#{new_date}", cb_practice
-fetch "{{ site.spacex_url }}?date=#{new_date}"
-	.then (response) -> response.json()
-	.then (json) ->
-		table = document.createElement 'table'
-		for p in json
-			t_minus = p.launch_date_unix - (new_date / 1000)
-			switch
-				when (t_minus < 172800) # 2 days
-					t_message = (t_minus / unix_hour).toFixed(1) + "h"
-				when (t_minus > unix_month)
-					t_message = (t_minus / unix_month).toFixed(1) + "m"
-				else
-					t_message = (t_minus / unix_day).toFixed(1) + "d"
-			stream = [
-				p.payloads[0].payload_id
-				p.payloads[0].payload_type
-				p.payloads[0].orbit
-				p.launch_site.site_name.replace /(?=\D)\s(?=\d)/g, '-'
-				p.landing_vehicle || 'expandible'
-				"-" + t_message
-				('0' + new Date(p.launch_date_utc).getHours()).slice(-2) +
-				":" +
-				('0' + new Date(p.launch_date_utc).getMinutes()).slice(-2)
-				p.core_serial
-			]
-			row = document.createElement 'tr'
-			for s in stream
-				cell = document.createElement 'td'
-				cell.innerHTML = s
-				row.appendChild cell
-			if p.reused
-				# console.log loadJSON "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}", cb_reflow
-				fetch "{{ site.spacex_cores_url }}#{p.core_serial}?date=#{new_date}"
-					.then (response) -> response.json()
-					.then (json) ->
-						gap = ((p.launch_date_unix - json[0].launch_date_unix) / unix_month).toFixed(1) + "m"
-						row.innerHTML += "#{gap} #{json[0].payloads[0].orbit} #{json[0].landing_vehicle}	"
-						table.appendChild row
-			else table.appendChild row
-			# table.appendChild row
-		document.querySelector('#content').appendChild table
-	return+loadJSON "{{ site.spacex_url }}?date=#{new_date}", cb_spacex


fixed line number bug in run single test function

--- lib/atom-python-test.coffee
+++ lib/atom-python-test.coffee
@@ -84,8 +84,10 @@
     # guarantee that the class is a Test class, not an utility one.
     reversedLines = buffer.lines[0...testLineNumber].reverse()

-    for line, classLineNumber in reversedLines
+    for line, i in reversedLines
       startIndex = line.search(class_re)
+
+      classLineNumber = testLineNumber - i - 1

       # We think that we have found a Test class, but this is guaranteed only if
       # the test indentation is greater than the class indentation.


Refactoring get script

Refactoring get script

--- lib/build/get-script.coffee
+++ lib/build/get-script.coffee
@@ -10,26 +10,25 @@
   getFiles : ({configJson, appPath}, callback)->
     async.parallel(
       javascript_files: (jsCallback)=>
-        @getScripts {appPath, scripts: configJson.javascript, compress: true }, jsCallback
+        @getJavaScripts {appPath, scripts: configJson.javascript}, jsCallback
       css_files: (cssCallback)=>
         @getScripts {appPath, scripts: configJson.css }, cssCallback
       callback
+    )
+  getJavaScripts:({appPath, scripts}, callback)->
+    @getScripts({appPath, scripts, compress:true}, (err, results) =>
+      if err then callback(err)
+      else
+        for key,code of results
+          fileName = scripts[key]
+          results[key] = @processJavaScript(code,fileName)
+        callback(null, results)
     )
   getScripts : ({appPath, scripts, compress}, callback)->
     fullPathScripts = []
     for script in scripts || []
       fullPathScripts.push(path.resolve(appPath, script))
-
-    async.map(fullPathScripts, @readFile, (err, results) =>
-
-      if err then callback(err)
-      else
-        if compress
-          for key,code of results
-            fileName = scripts[key]
-            results[key] = @processJavaScript(code,fileName)
-        callback(null, results)
-    )
+    async.map(fullPathScripts, @readFile, callback)

   compressJavaScript:(code)->
     ast = uglify.parse(code)


Add comment specifying a main entry point on backlog.

--- app/coffee/controllers/backlog.coffee
+++ app/coffee/controllers/backlog.coffee
@@ -75,11 +75,12 @@
         @scope.filtersOpened = false
         @scope.showTags = false

+        @scope.$on "milestones:loaded", (ctx, data) =>
+            @scope.milestones = data
+
+        # Main: Entry point
         @scope.$on("points:loaded", _.bind(@.refreshBacklog, @))
         @scope.$on("userstory-form:create", _.bind(@.refreshBacklog, @))
-
-        @scope.$on "milestones:loaded", (ctx, data) =>
-            @scope.milestones = data

     calculateStats: ->
         @scope.$emit("stats:update")


Fixed greenmine issue 70: Removing async behavior on selected points calculation

--- app/coffee/controllers/backlog.coffee
+++ app/coffee/controllers/backlog.coffee
@@ -102,21 +102,19 @@
             calculateStats()

     calculateStoryPoints = (selectedUserStories) ->
-        defered = $q.defer()
-
-        gm.utils.defer ->
-            total = 0
-
-            for us in selectedUserStories
-                for roleId, pointId of us.points
-                    pointsValue = $scope.constants.points[pointId].value
-                    if pointsValue is null
-                        pointsValue = 0
-                    total += pointsValue
-
-            defered.resolve(total)
-
-        return defered.promise
+        total = 0
+
+        if not selectedUserStories?
+            return 0
+
+        for us in selectedUserStories
+            for roleId, pointId of us.points
+                pointsValue = $scope.constants.points[pointId].value
+                if pointsValue is null
+                    pointsValue = 0
+                total += pointsValue
+
+        return total

     getSelectedUserStories = ->
         selected = _.filter($scope.unassingedUs, "selected")
@@ -155,10 +153,7 @@

     $scope.changeUserStoriesSelection = ->
         selected = $scope.selectedUserStories = getSelectedUserStories()
-
-        promise = calculateStoryPoints(selected)
-        promise.then (points) ->
-            $scope.selectedStoryPoints = points
+        $scope.selectedStoryPoints = calculateStoryPoints(selected)

     $scope.$on("points:loaded", loadUserStories)
     $scope.$on("userstory-form:create", loadUserStories)
@@ -215,6 +210,10 @@

     # Signal Handlign
     $scope.$on("sortable:changed", resortUserStories)
+    $scope.$on("sortable:changed", ->
+        selected = $scope.selectedUserStories = getSelectedUserStories()
+        $scope.selectedStoryPoints = calculateStoryPoints(selected)
+    )

 BacklogUserStoryModalController = ($scope, $rootScope, $gmOverlay, rs, $gmFlash) ->


Always return a Promise for favourites

Always return a Promise for favourites

--- app/collections/favorites-button.cjsx
+++ app/collections/favorites-button.cjsx
@@ -44,7 +44,7 @@
       favorites.get('subjects', id: @props.subject.id)
         .then ([subject]) -> subject?
     else
-      false
+      Promise.resolve false

   componentWillMount: ->
     # see if the subject is in the project's favorites collection


Add snippet for Magento getChildHtml method

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -23,4 +23,7 @@
     'body': '<?php echo $this->__(\'\') ?>'
   'Get URL':
     'prefix': 'geturl'
-    'body': '<?php echo $this->getUrl(\'\') ?>'+    'body': '<?php echo $this->getUrl(\'\') ?>'
+  'Get Child HTML':
+    'prefix': 'chtml'
+    'body': '<?php echo $this->getChildHtml(\'\') ?>'


Fix dialog_login event pushing

Fix dialog_login event pushing

--- app/scripts/views/dialog_login.coffee
+++ app/scripts/views/dialog_login.coffee
@@ -55,8 +55,5 @@
     d.done =>
       @hide()
       @publishEvent "!view:header:render"
-      if utils.isLogged()
-        @publishEvent "!login:login"
-      else
-        @publishEvent "!login:logout"
+      @publishEvent "!login:login"
       utils.gotoUrl "/"


Teamwork: Implemented Playground features.

--- client/app/Applications/Teamwork.kdapplication/Views/playgroundteamwork.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/playgroundteamwork.coffee
@@ -6,6 +6,20 @@

     @container.setClass options.playground

+    @on "ContentIsReady", =>
+      return unless @amIHost()
+      manifest = @getOptions().playgroundManifest
+      {prerequisite, initialState} = manifest
+      if prerequisite
+        if prerequisite.type is "sh"
+          if initialState
+          then @doPrerequisite prerequisite.command, => @setUpInitialState initialState
+          else @doPrerequisite prerequisite.command
+        else
+          warn "Unhandled prerequisite type."
+      else if initialState
+        @setUpInitialState initialState
+
   handleRun: (panel) ->
     options      = @getOptions()
     {playground} = options
@@ -13,23 +27,51 @@

     return warn "Missing run config for #{playground}."  unless runConfig

-    unless runConfig.handler
-      return warn "Missing handler for #{playground} run config."
+    {handler, command} = runConfig
+    {paneLauncher}     = panel

-    {handler, command} = runConfig
+    if not handler or not command
+      return warn "Missing parameter for #{playground} run config. You must pass a handler and a command"

     if handler is "terminal"
-      unless command
-        return warn "Missing command for #{playground} run config"
-
-      {path}         = panel.getPaneByName("editor").getActivePaneFileData()
-      plainPath      = FSHelper.plainPath path
-      command        = command.replace "$ACTIVE_FILE_PATH", """ "#{plainPath}" """
-      {paneLauncher} = panel
-
+      {path}    = panel.getPaneByName("editor").getActivePaneFileData()
+      plainPath = FSHelper.plainPath path
+      command   = command.replace "$ACTIVE_FILE_PATH", """ "#{plainPath}" """
       if paneLauncher.paneVisibilityState.terminal is no
         paneLauncher.handleLaunch "terminal"
-
       paneLauncher.terminalPane.runCommand command
+    else if handler is "preview"
+      @handlePreview command
     else
       warn "Unimplemented run hanldler for #{playground}"
+
+  doPrerequisite: (command, callback = noop) ->
+    return warn "no command passed for prerequisite"  unless command
+    KD.getSingleton("vmController").run command, (err, res) =>
+      return warn err  if err
+      callback()
+
+  setUpInitialState: (initialState) ->
+    if initialState.preview
+      @handlePreview initialState.preview.url
+
+    if initialState.editor
+      @openFiles initialState.editor.files
+
+  handlePreview: (url) ->
+    {paneLauncher} = @getActivePanel()
+    url            = url.replace "$USERNAME", @getHost()
+
+    if paneLauncher.paneVisibilityState.preview is no
+      paneLauncher.handleLaunch "preview"
+    paneLauncher.previewPane.openUrl url
+
+  openFiles: (files) ->
+    editor = @getActivePanel().getPaneByName "editor"
+
+    for path in files
+      filePath = "Web/Teamwork/#{@getOptions().playground}/#{path.replace /^.\//, ''}"
+      file     = FSHelper.createFileFromPath filePath
+
+      file.fetchContents (err, contents) =>
+        editor.openFile file, contents


strip carriage returns

strip carriage returns

--- SingularityUI/app/reducers/taskGroups.coffee
+++ SingularityUI/app/reducers/taskGroups.coffee
@@ -137,6 +137,8 @@
     currentOffset = offset
     lines = _.initial(data.match /[^\n]*(\n|$)/g).map (data) ->
       currentOffset += data.length
+
+      data = data.replace('\r', '')  # carriage return screws stuff up

       timestamp = parseLineTimestamp(data)


ATOM_NODE_GYP_PATH is gone

ATOM_NODE_GYP_PATH is gone

--- spec/install-spec.coffee
+++ spec/install-spec.coffee
@@ -534,16 +534,16 @@
       npmNodeModules = fs.realpathSync(path.join(__dirname, '..', 'node_modules', 'npm', 'node_modules'))

       beforeEach ->
+        # Normally npm_config_node_gyp would be ignored, but it works here because we're calling apm
+        # directly and not through the scripts in bin/
         fs.copySync path.join(npmNodeModules, 'node-gyp'), path.join(npmNodeModules, 'with a space')
         process.env.npm_config_node_gyp = path.join(npmNodeModules, 'with a space', 'bin', 'node-gyp.js')
-        process.env.ATOM_NODE_GYP_PATH = path.join(npmNodeModules, 'with a space', 'bin', 'node-gyp.js')

         # Read + execute permission
         fs.chmodSync(process.env.npm_config_node_gyp, fs.constants.S_IRUSR | fs.constants.S_IXUSR)

       afterEach ->
         delete process.env.npm_config_node_gyp
-        delete process.env.ATOM_NODE_GYP_PATH

       it 'builds native code successfully', ->
         callback = jasmine.createSpy('callback')


moved test where it belongs

moved test where it belongs

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -102,18 +102,18 @@
     describe "the l keybinding", ->
       beforeEach -> editor.setCursorScreenPosition([1, 2])

+      it "moves the cursor right, but not to the next line", ->
+        keydown('l')
+        expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+        keydown('l')
+        expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
       it "moves the cursor to the next line if wrapLeftRightMotion is true", ->
         atom.config.set('vim-mode.wrapLeftRightMotion', true)
         keydown('l')
         keydown('l')
         expect(editor.getCursorScreenPosition()).toEqual [2, 0]
-
-      it "moves the cursor right, but not to the next line", ->
-        keydown('l')
-        expect(editor.getCursorScreenPosition()).toEqual [1, 3]
-
-        keydown('l')
-        expect(editor.getCursorScreenPosition()).toEqual [1, 3]

       describe "on a blank line", ->
         it "doesn't move the cursor", ->


Let's make our A-W page an A-Z page again

--- desktop/apps/galleries_institutions/routes.coffee
+++ desktop/apps/galleries_institutions/routes.coffee
@@ -85,7 +85,7 @@
   partners.fetchUntilEndInParallel
     cache: true
     data:
-      size: 20
+      size: 40
       type: mapTypeClasses[req.params.type]
       sort: 'sortable_id'
       has_full_profile: true


namespaced unbind

namespaced unbind

--- test/bindable.coffee
+++ test/bindable.coffee
@@ -48,7 +48,10 @@

   o.bind "test.TestNamespace", ->
     ok true
+
   o.trigger "test"
+
+  o.

 module()


Add cabal sandbox to search path

Add cabal sandbox to search path

--- lib/ghc-modi-process.coffee
+++ lib/ghc-modi-process.coffee
@@ -19,7 +19,14 @@

   spawnProcess: =>
     @modiPath = atom.config.get('haskell-ghc-mod.ghcModiPath')
-    @process = CP.spawn(@modiPath,['-b\r'])
+    rootPath = atom.project.getRootDirectory().path
+    sep = if process.platform=='win32' then ';' else ':'
+    env = process.env
+    env.PATH = rootPath+"/.cabal-sandbox/bin"+sep+env.PATH
+    options =
+      cwd: rootPath
+      env: env
+    @process = CP.spawn(@modiPath,['-b\r'],options)
     @process.once 'exit', =>
       @spawnProcess()


Added coverage for unknown types

Added coverage for unknown types

--- test/coverage.coffee
+++ test/coverage.coffee
@@ -128,7 +128,9 @@
           table[0].push("`isAn.#{check.typeName}(x)`")
           for inputFunc,iInput in inputs
             isType = check(inputFunc())
-            assert(isAn.Boolean.Literal(isType))
+            assert(isAn.Boolean.Literal(isType), 'correct result type')
+            assert.equal(check, isAn(check.typeName, { returnChecker: true }), 'same function')
+            assert.equal(isAn(inputFunc(), check.typeName), isType, 'identical results')

             table[1 + iInput].push(
               if isType then 'TRUE' else ''
@@ -136,6 +138,10 @@

           return
         )
+  test('invalid type name', () ->
+    assert.isUndefined(isAn(1, 'unknown'))
+    assert.isUndefined(isAn('unknown', { returnChecker: true }))
+  )

   suite('results tables', () ->
     # string padd/truncate
@@ -213,4 +219,21 @@
     return
   )

+  test('unknown type', () ->
+    # monkey patch the types
+    n = isAn.Number
+    o = isAn.Object
+
+    try
+      isAn.Number = () -> false
+      assert.isUndefined(isAn(1))
+
+      isAn.Object = () -> true
+      assert.equal(isAn(1), 'Object')
+
+    finally
+      isAn.Number = n
+      isAn.Object = o
+
+  )
 )


Save editor before executing commands

Save editor before executing commands

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -131,6 +131,7 @@

   getDocsForWord: ({ target }) =>
     editor = @getEditor()
+    @saveFile editor
     uri = editor.getURI()
     word = Symbol.serializeWord @getWordUnderCursor(editor)

@@ -370,6 +371,7 @@

   printDefinition: ({ target }) =>
     editor = @getEditor()
+    @saveFile editor
     uri = editor.getURI()
     word = Symbol.serializeWord @getWordUnderCursor(editor)


:lipstick:

:lipstick:

--- lib/markdown-preview.coffee
+++ lib/markdown-preview.coffee
@@ -4,19 +4,19 @@

 module.exports =
   activate: ->
-    atom.workspaceView.command 'markdown-preview:show', => @show()
+    atom.workspaceView.command 'markdown-preview:show', =>
+      @show()

     atom.project.registerOpener (urlToOpen) ->
       {protocol, pathname} = url.parse(urlToOpen)
-      if protocol is 'markdown-preview:' and fs.isFileSync(pathname)
-        new MarkdownPreviewView(pathname)
+      return unless protocol is 'markdown-preview:' and fs.isFileSync(pathname)
+      new MarkdownPreviewView(pathname)

   show: ->
     activePane = atom.workspaceView.getActivePane()
     editor = activePane.activeItem

-    isMarkdownEditor = editor.getGrammar?()?.scopeName is "source.gfm"
-    unless isMarkdownEditor
+    unless editor.getGrammar?().scopeName is "source.gfm"
       console.warn("Can not render markdown for '#{editor.getUri() ? 'untitled'}'")
       return


adds saving options

adds saving options

--- lib/Model.coffee
+++ lib/Model.coffee
@@ -209,8 +209,12 @@

     beforeSave: (cb) -> cb()

-    save: (cb) ->
+    save: (saveOpts = {}, cb) ->
       beforeSave = Promise.promisify @beforeSave, @
+
+      if _.isFunction saveOpts
+        cb = saveOpts
+        saveOpts = {}

       promise = beforeSave().then =>
         # TODO: defaults, required
@@ -249,7 +253,7 @@

           if @isNewRecord
             newObj.id = @getId() if @getId()?
-            db.run(c.r().insert(newObj)).then (reply) =>
+            db.run(c.r().insert(newObj, saveOpts)).then (reply) =>
               unless @id?
                 @id = reply?.generated_keys?[0]

@@ -260,7 +264,7 @@
           else
             @saveRelated().then =>
               if @getId()?
-                db.run(c.r().get(@getId()).update(newObj)).then => @
+                db.run(c.r().get(@getId()).update(newObj, saveOpts)).then => @
               else
                 reject new ModelError "undefined id"


Implement a queuing system to avoid quadratic pipe reading time in node IPC.

--- lib/spell-check-task.coffee
+++ lib/spell-check-task.coffee
@@ -1,9 +1,12 @@
 idCounter = 0
+log = console.log

 module.exports =
 class SpellCheckTask
   @handler: null
   @callbacksById: {}
+  @isBusy: false
+  @queue: []

   constructor: (@task) ->
     @id = idCounter++
@@ -19,16 +22,41 @@
       [projectPath, relativePath] = atom.project.relativizePath(buffer.file.path)

     # Submit the spell check request to the background task.
-    args = {
-      id: @id,
-      projectPath,
-      relativePath,
-      text: buffer.getText()
+    entry = {
+      task: @task,
+      args: {
+        id: @id,
+        projectPath,
+        relativePath,
+        text: buffer.getText()
+      }
     }
-    @task?.start args, @constructor.dispatchMisspellings
+
+    log('Pushing ' + @id, entry)
+    queue = @constructor.queue
+
+    if (queue.length > 0)
+      for i in [0..queue.length-1]
+        if (queue[i].id is @id)
+          log('Ejecting previous entry at ' + i)
+          queue.splice(i, 1)
+          break
+
+    queue.push(entry)
+    @constructor.sendNextMaybe()
+
+  @sendNextMaybe: ->
+    if not @isBusy and @queue.length > 0
+      @isBusy = true
+      entry = @queue.shift()
+      log('Dispatching ' + entry.args.id + ' from queue of ' + (@queue.length + 1))
+      entry.task?.start entry.args, @dispatchMisspellings

   onDidSpellCheck: (callback) ->
     @constructor.callbacksById[@id] = callback

   @dispatchMisspellings: (data) =>
+    log('completed ' + data.id, data)
     @callbacksById[data.id]?(data.misspellings)
+    @isBusy = false
+    @sendNextMaybe()


Removed debug log, and added description

--- lib/streams/commonjs.coffee
+++ lib/streams/commonjs.coffee
@@ -47,15 +47,19 @@
   # provide the file extension. Basically, i want the same interface fir
   # both Streams.
   transform: (transformProvider) -> @_browserify.transform (file) ->
-    console.log "Common transform", file
     ext = path.extname file
-
     transform = transformProvider file, ext
     if not transform? then return new PassTransform()
     transform

 # ## PassTransform
+#
+# Our PassTransform is a workaround to the fact that i can't figure out how
+# to make Browserify let me choose *not* to transform that specific stream.
+# Browserify seemingly requires all transforms return streams, so this
+# is here to make a passthrough stream for when callers opt out of returning
+# a stream for that specific transform-file.
 class PassTransform extends Transform
   constructor: -> super
   _transform: (chunk) -> @push chunk


Available kernels func

Available kernels func

--- lib/paths.coffee
+++ lib/paths.coffee
@@ -8,9 +8,7 @@

 jupyterResolve = _.memoize (category) ->
     match = child_process.spawnSync('jupyter', ['--paths']).stdout.toString()
-    _.filter (_.map (
-        ///#{category}:\n(\s+.*\n)+///gim.exec(match)[0].split('\n')
-    ), (e) -> e.replace(///#{category}:///,'').trim()), _.negate(_.isEmpty)
+    _.map ///#{category}:\n(\s+.*\n)+///gim.exec(match)[0].split('\n')[1...-1], (x) -> x.trim()

 jupyterPath = _.memoize (subdirs...) ->
@@ -22,6 +20,12 @@
     paths = (path.join(p, subdirs...) for p in paths)
     _.filter paths, (x) -> try fs.statSync(x).isDirectory() catch e

+kernels = _.memoize ->
+    stdout =  child_process.spawnSync('ipython',['kernelspec','list']).stdout.toString()
+    _.map /Available kernels:(\s+.+\s+.+\n)+/.exec(stdout)[0].split('\n')[1...-1], (x) ->
+        /\/.*/.exec(x)[0]
+
 module.exports =
   jupyterResolve: jupyterResolve
   jupyterPath: jupyterPath
+  availableKernels: kernels


steward: refactoring

steward: refactoring

--- lib/web/views/router.coffee
+++ lib/web/views/router.coffee
@@ -19,18 +19,19 @@
 "use strict"

 # Missing features:
-# - form support, catch form submits (how would this work?) get / post?
 # - should route only internal addresses and skip external urls
 # - relative url support
 # - implement support for older browsers: setInterval and check urls
-# - chrome fails when converting plain url to hashbang url: %23
-# - clarify when fallbackRoute is used
 # - querystring parameters as part of params
 # - copy build configs from bacon.js
 # - split to own repository
 # - more complete sinatra path parsing, JavascriptRouteParser
 # - test suite
 # - documentation
+# Planned features
+# - form support, catch form submits (how would this work?) get / post?
+# - chrome fails when converting plain url to hashbang url: %23
+# - clarify when fallbackRoute is used
 # - navigate
 # - go
 # Known issues:
@@ -169,6 +170,22 @@
       element.attachEvent("on"+event, fn);
     else
       raise "addListener can not attach listeners!"
+
+append = (h, key, value) ->
+  if old = h[key]
+    if !typeIsArray(old)
+      h[key] = [old]
+    h[key].push(value)
+  else
+    h[key]=value
+
+typeIsArray = ( value ) ->
+  value and
+  typeof value is 'object' and
+  value instanceof Array and
+  typeof value.length is 'number' and
+  typeof value.splice is 'function' and
+  not ( value.propertyIsEnumerable 'length' )

 class SinatraRouteParser
   constructor: (route) ->
@@ -197,20 +214,8 @@
       ret = {}
       matches.slice(1).map (match) =>
         key = @keys[i]
+        i+=1
         #        console.log("Found item", match, key)
-        if ret[key]
-          if !typeIsArray(ret[key])
-            ret[key] = [ret[key]]
-          ret[key].push(match)
-        else
-          ret[key]=match
-        i+=1
+        append(ret, key, match)
       ret

-typeIsArray = ( value ) ->
-  value and
-  typeof value is 'object' and
-  value instanceof Array and
-  typeof value.length is 'number' and
-  typeof value.splice is 'function' and
-  not ( value.propertyIsEnumerable 'length' )


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/tweening.coffee
+++ test/tweening.coffee
@@ -13,12 +13,6 @@
     equals o.I.x, i

-test "testing boolean values", ->
-  someFunction = ->
-    return true
-
-  # Test if someFunction returns true
-  ok someFunction()

 # Clear out the module
 module()


Close #2394; Add tab-i18n to list of allowed URLs in CORS

--- packages/rocketchat-cors/cors.coffee
+++ packages/rocketchat-cors/cors.coffee
@@ -28,7 +28,7 @@

 WebApp.rawConnectHandlers.use (req, res, next) ->
-	if /^\/(api|_timesync|sockjs)(\/|$)/.test req.url
+	if /^\/(api|_timesync|sockjs|tap-i18n)(\/|$)/.test req.url
 		res.setHeader("Access-Control-Allow-Origin", "*")

 	# Block next handlers to override CORS with value http://meteor.local


reduce duplicate

reduce duplicate

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -171,9 +171,7 @@
   getEolForBufferRow(editor, point.row).isEqual(point)

 getCharacterAtCursor = (cursor) ->
-  {editor} = cursor
-  bufferRange = editor.bufferRangeForScreenRange(cursor.getScreenRange())
-  editor.getTextInBufferRange(bufferRange)
+  getTextInScreenRange(cursor.editor, cursor.getScreenRange())

 getTextInScreenRange = (editor, screenRange) ->
   bufferRange = editor.bufferRangeForScreenRange(screenRange)


might be useful in some day

might be useful in some day

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -222,6 +222,17 @@
         []
       else
         [(startRow + 1)..vimLastBufferRow]
+
+getParagraphBoundaryRow = (editor, startRow, direction, fn) ->
+  wasAtNonBlankRow = not editor.isBufferRowBlank(startRow)
+  for row in getBufferRows(editor, {startRow, direction})
+    isAtNonBlankRow = not editor.isBufferRowBlank(row)
+    if wasAtNonBlankRow isnt isAtNonBlankRow
+      if fn?
+        return row if fn?(isAtNonBlankRow)
+      else
+        return row
+    wasAtNonBlankRow = isAtNonBlankRow

 # Return Vim's EOF position rather than Atom's EOF position.
 # This function change meaning of EOF from native TextEditor::getEofBufferPosition()
@@ -868,6 +879,7 @@
   scanForScopeStart
   detectScopeStartPositionForScope
   getBufferRows
+  getParagraphBoundaryRow
   registerElement
   getBufferRangeForPatternFromPoint
   sortComparable


:lipstick: Add space after comma

:lipstick: Add space after comma

--- src/coffee-cache.coffee
+++ src/coffee-cache.coffee
@@ -19,7 +19,7 @@
       fs.readFileSync(cachePath, 'utf8')

 compileCoffeeScript = (coffee, filePath, cachePath) ->
-  {js,v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true)
+  {js, v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true)
   # Include source map in the web page environment.
   if btoa? and JSON? and unescape? and encodeURIComponent?
     js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{filePath}"


Typo in utils

Typo in utils

--- src/coffee/utils.coffee
+++ src/coffee/utils.coffee
@@ -16,7 +16,7 @@
     limitParam = perPage if _.isNumber(perPage)

     # offset param
-    throw new Error 'Page must be a number >= 1' if _.isNumber(perPage) and page < 1
+    throw new Error 'Page must be a number >= 1' if _.isNumber(page) and page < 1
     offsetParam = (perPage or 100) * (page - 1)

     queryString = []


Fix tests

Fix tests

--- tests/CLITest.coffee
+++ tests/CLITest.coffee
@@ -49,19 +49,27 @@

   it 'executeCommand - should execute the hello-world command', ->
-    process.argv = ['node', 'main.js', 'program-json', 'hello-world']
+    process.argv = ['node', 'main.js', 'program.json', 'hello-world']
     cli.executeCommand()
     chai.assert logSpy.calledWith "Hello world from spacejamio:cli"

+  it 'executeCommand - should remove program.json and the command name from process.argv', ->
+    process.argv = ['node', 'main.js', 'program.json', 'hello-world']
+    cli.executeCommand()
+    expect(process.argv).to.have.length 2
+    expect(process.argv[0]).to.equal 'node'
+    expect(process.argv[1]).to.equal 'main.js'
+
+
   it 'executeCommand - should execute the echo command with the default string', ->
-    process.argv = ['node', 'main.js', 'program-json', 'echo']
+    process.argv = ['node', 'main.js', 'program.json', 'echo']
     cli.executeCommand()
     chai.assert logSpy.calledWith "I am echoing the --string default"

   it 'executeCommand - should execute the echo command with a provided string', ->
-    process.argv = ['node', 'main.js', 'program-json', 'echo', '--string', 'I am echoing this string']
+    process.argv = ['node', 'main.js', 'program.json', 'echo', '--string', 'I am echoing this string']
     cli.executeCommand()
     chai.assert logSpy.calledWith "I am echoing this string"

@@ -73,8 +81,7 @@
     chai.assert logSpy.calledWith "Hello world from spacejamio:cli"
     expect(process.argv[0]).to.equal 'node'
     expect(process.argv[1]).to.equal 'main.js'
-    expect(process.argv[2]).to.equal 'program.json'
-    expect(process.argv[3]).to.equal 'hello-world'
+    expect(process.argv).to.have.length 2

   it 'executeCommand - should fail if no command was provided', ->


fix tagname for graphite

fix tagname for graphite

--- services/clsi/app/coffee/CompileManager.coffee
+++ services/clsi/app/coffee/CompileManager.coffee
@@ -32,8 +32,8 @@
 			injectDraftModeIfRequired (error) ->
 				return callback(error) if error?
 				timer = new Metrics.Timer("run-compile")
-				# find the image tag to log it as a metric
-				tag = request.imageName?.match(/:(.*)/)?[1] or "default"
+				# find the image tag to log it as a metric, e.g. 2015.1 (convert . to - for graphite)
+				tag = request.imageName?.match(/:(.*)/)?[1]?.replace(/\./g,'-') or "default"
 				tag = "other" if project_id?.match(/^[0-9a-f]{24}$/) # exclude smoke test
 				Metrics.inc("compiles")
 				Metrics.inc("compiles-with-image.#{tag}")


Add null checks for null folders

Add null checks for null folders

--- services/docstore/app/coffee/DocManager.coffee
+++ services/docstore/app/coffee/DocManager.coffee
@@ -61,7 +61,7 @@
 		else
 			callback null, null, null

-	_findDocInFolder: (folder, doc_id, currentPath) ->
+	_findDocInFolder: (folder = {}, doc_id, currentPath) ->
 		for doc, i in folder.docs or []
 			if doc._id.toString() == doc_id.toString()
 				return {
@@ -75,7 +75,7 @@

 		return null

-	_findAllDocsInFolder: (folder) ->
+	_findAllDocsInFolder: (folder = {}) ->
 		docs = folder.docs or []
 		for childFolder in folder.folders or []
 			docs = docs.concat @_findAllDocsInFolder childFolder


Fixed issue with collection require of model

--- generators/collection/templates/collection.coffee
+++ generators/collection/templates/collection.coffee
@@ -3,13 +3,13 @@
         module.exports = factory(
             require "q"
             require "backbone"
-            require "./../models/" + <%= modelName %> + ".coffee"
+            require "./../models/<%= modelName %>.coffee"
         )
     else if typeof define is "function" and define.amd
         define( [
             "q"
             "backbone"
-            "./../models/" + <%= modelName %> + ".coffee"
+            "./../models/<%= modelName %>.coffee"
         ], factory )

 )( ( Q, Backbone, <%= modelName %>Model ) ->


FIX: shadow draw-area calc

FIX: shadow draw-area calc

--- source/Art/Engine/Elements/FillableBase.coffee
+++ source/Art/Engine/Elements/FillableBase.coffee
@@ -52,19 +52,21 @@
         color: color

   @getter
-    normalizedShadow: ->
-      return null if @_shadow == noShadow
-      {offset} = @_shadow
+    normalizedShadow: (pending)->
+      shadow = @getShadow pending
+      return null if shadow == noShadow
+      {offset} = shadow
       x = offset.layoutX @_currentSize
       y = offset.layoutY @_currentSize
-      merge @_shadow,
+      merge shadow,
         offsetX: x
         offsetY: y

-  _expandRectangleByShadow: (r, shadow) ->
+  _expandRectangleByShadow: (r, pending) ->
+    shadow = @getShadow pending
     return r if shadow == noShadow
     {x, y, w, h} = r
-    {blur, offsetX, offsetY} = @normalizedShadow
+    {blur, offsetX, offsetY} = @getNormalizedShadow pending
     offsetX ||= 0
     offsetY ||= 0
     blur ||= 0
@@ -82,7 +84,7 @@
   @virtualProperty
     drawAreaPadding: (pending) -> 0
     baseDrawArea: (pending) ->
-      @_expandRectangleByShadow @getPreFilteredBaseDrawArea(pending), max @getState(pending)._shadow, @getDrawAreaPadding pending
+      @_expandRectangleByShadow @getPreFilteredBaseDrawArea(pending), pending

   _prepareDrawOptions: (drawOptions, compositeMode, opacity)->
     super


fix tests

fix tests


Import link.

Import link.

--- spark.coffee
+++ spark.coffee
@@ -1,4 +1,4 @@
-combnk = $blab.combnk
+combnk = $blab.combnk # Imported function.

 $blab.spark = (A) ->
     At = A.T # easier to consider rows of $A^T$ than cols of $A$
@@ -13,6 +13,3 @@
             rank = (sv for sv in (nm.svd R.T).S when sv>0).length
             return k if rank < k
     N+1 # max spark, if loop doesn't return
-
-
-


Use `backwardsScanInRange` instead of passing `true`

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -116,14 +116,13 @@
     regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g')
     startPairPosition = null
     unpairedCount = 0
-    scanner = (match, range, {stop}) =>
+    buffer.backwardsScanInRange regex, scanRange, (match, range, {stop}) =>
       if match[0] is endPair
         unpairedCount++
       else if match[0] is startPair
         unpairedCount--
         startPairPosition = range.start
         stop() if unpairedCount < 0
-    buffer.scanInRange(regex, scanRange, scanner, true)
     startPairPosition

   updateMatch: (editor) ->


Comment in private_functions_spec.coffee

--- spec/javascripts/private_functions_spec.coffee
+++ spec/javascripts/private_functions_spec.coffee
@@ -1,4 +1,4 @@
-#
+# Test Foundation Player private functions
 describe 'Private functions suite', ->
   obj = {}
   beforeEach ->


skip suppressed tests

skip suppressed tests

--- karma.conf.coffee
+++ karma.conf.coffee
@@ -27,6 +27,8 @@
 			'spec'
 			'coverage'
 		]
+		specReporter:
+			suppressSkipped: true
 		singleRun: true
 		proxies:
 			'/img': '/src/img'


removed karma config

removed karma config

--- karma.conf.coffee
+++ karma.conf.coffee
@@ -1,31 +0,0 @@
-# Karma configuration
-
-module.exports = (config) ->
-  config.set
-
-    # base path, that will be used to resolve files and exclude
-    basePath: ''
-
-    # frameworks to use
-    frameworks: ['mocha', 'browserify']
-
-    # list of files / patterns to load in the browser
-    files: [
-      'test/browser/*.coffee'
-    ]
-
-    preprocessors:
-      '**/*.coffee': ['coffee']
-      'test/browser/*': ['browserify']
-
-    # Configure browserify
-    browserify:
-      extension: ['.coffee']  # This is for future compatibility.
-      transform: ['coffeeify']
-      watch: true # Watches dependencies only (Karma watches the tests)
-
-    # enable / disable watching file and executing tests whenever any file changes
-    autoWatch: false
-
-    # Browsers
-    browsers: if process.env.TRAVIS then ['Firefox'] else ['Chrome']


Do not keep connected to redis

Do not keep connected to redis

--- packages/rocketchat-tutum/startup.coffee
+++ packages/rocketchat-tutum/startup.coffee
@@ -9,14 +9,16 @@
 	redis = Npm.require 'redis'

 	client = redis.createClient(process.env.DOCKERCLOUD_REDIS_HOST)
-
 	client.del("frontend:#{process.env.DOCKERCLOUD_CLIENT_HOST}")
 	client.rpush("frontend:#{process.env.DOCKERCLOUD_CLIENT_HOST}", process.env.DOCKERCLOUD_CLIENT_NAME)
 	client.rpush("frontend:#{process.env.DOCKERCLOUD_CLIENT_HOST}", "http://#{process.env.DOCKERCLOUD_IP_ADDRESS.split('/')[0]}:3000")
+	client.quit()

 	# removes the redis entry in 90 seconds on a SIGTERM
 	process.on 'SIGTERM', ->
+		client = redis.createClient(process.env.DOCKERCLOUD_REDIS_HOST)
 		client.expire("frontend:#{process.env.DOCKERCLOUD_CLIENT_HOST}", 90)
+		client.quit()

 	day = 86400000

@@ -29,7 +31,9 @@
 		subscription = RocketChat.models.Subscriptions.findOne({ls: {$exists: true}}, {sort: {ls: -1}, fields: {ls: 1}})

 		if not subscription? or Date.now() - subscription.ls > inactiveDays * day
+			client = redis.createClient(process.env.DOCKERCLOUD_REDIS_HOST)
 			client.del("frontend:#{process.env.DOCKERCLOUD_CLIENT_HOST}")
+			client.quit()
 			process.exit 0

 	Meteor.setInterval ->


Return empty array from getPanes when PaneContainer is destroyed

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@github.com>

--- src/pane-container.coffee
+++ src/pane-container.coffee
@@ -102,7 +102,10 @@
     @setRoot(newChild)

   getPanes: ->
-    @getRoot().getPanes()
+    if @alive
+      @getRoot().getPanes()
+    else
+      []

   getPaneItems: ->
     @getRoot().getItems()
@@ -195,7 +198,7 @@
   # Called by Model superclass when destroyed
   destroyed: ->
     @cancelStoppedChangingActivePaneItemTimeout()
-    pane.destroy() for pane in @getPanes()
+    pane.destroy() for pane in @getRoot().getPanes()
     @subscriptions.dispose()
     @emitter.dispose()


fix hugo generator binary names

fix hugo generator binary names

--- lib/generators/hugo.coffee
+++ lib/generators/hugo.coffee
@@ -13,6 +13,10 @@
   @run: (callback) ->
     projectPath = atom.config.get 'docapp.projectPath'
     command = path.resolve(projectPath, "./bin/hugo-#{process.platform}")
+    if process.arch is 'x64' or process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
+      command = command + "-x64"
+    else
+      command = command + "-x32"
     args = [
       '-s', projectPath ,
       "--theme=#{atom.config.get 'docapp.theme'}" ,


Fix url for getting feature repositories

--- fmc/fmc-webui/src/main/webapp/app/controllers/profile_details_page.coffee
+++ fmc/fmc-webui/src/main/webapp/app/controllers/profile_details_page.coffee
@@ -264,7 +264,7 @@
     initialize: ->
       super
       @repos = new FON.Collection
-      @repos.url = "/versions/#{@model.get("version")}/profiles/#{@model.id}/available_repos"
+      @repos.url = "versions/#{@model.get("version")}/profiles/#{@model.id}/available_repos"
       @repos.bind "add", @update_select, @
       @repos.bind "remove", @update_select, @
       @repos.fetch


OX-402 Test case for createNode

OX-402 Test case for createNode

--- app/assets/javascripts/oxalis/model/statelogger.coffee
+++ app/assets/javascripts/oxalis/model/statelogger.coffee
@@ -85,7 +85,7 @@

   edgeObject : (node, treeId) ->
     $.assert(node.neighbors.length == 1,
-      "Node has to have exactly one neighbor", 0)
+      "Node has to have exactly one neighbor", node.neighbors.length)

     return {
       treeId : treeId
@@ -95,7 +95,10 @@

   createNode : (node, treeId) ->
     $.assert(node.neighbors.length <= 1,
-      "New node can't have more than one neighbor", 0)
+      "New node can't have more than one neighbor", node.neighbors.length)
+    $.assert(node.treeId == node.neighbors[0].treeId,
+      "Neighbot has different treeId",
+      {treeId1 : node.treeId, treeId2 : node.neighbors[0].treeId})

     @pushDiff("createNode", @nodeObject(node, treeId))
     if node.neighbors.length == 1


Use the opts

Use the opts

--- app/assets/javascripts/tenon/features/medium.js.coffee
+++ app/assets/javascripts/tenon/features/medium.js.coffee
@@ -1,6 +1,6 @@
 class Tenon.features.Medium
   constructor: ($el = $('.editable-text')) ->
-    Tenon.mediumEditor = new MediumEditor $('.editable-text'),
+    Tenon.mediumEditor = new MediumEditor $('.editable-text'), @opts

   opts:
     placeholder: ''


Stop synchronization when the device has been removed

--- src/remote/watcher.coffee
+++ src/remote/watcher.coffee
@@ -91,6 +91,9 @@
                         @onChange change.doc, @changed(change)
                     .on 'error', (err) =>
                         @changes = null
+                        if err?.status is 401
+                            msg = 'The device is no longer registered'
+                            return callback new Error msg
                         retry = =>
                             @listenToChanges options, callback
                         @couch.ping (available) =>


Update checkout/address.js to more preferred style

* Don't store functions on globals unless they should be exported
* Remove redundant iffe
* Use $(".foo") instead of ($ ".foo")

--- frontend/app/assets/javascripts/spree/frontend/checkout/address.js.coffee
+++ frontend/app/assets/javascripts/spree/frontend/checkout/address.js.coffee
@@ -1,87 +1,86 @@
-Spree.ready ($) ->
-  Spree.onAddress = () ->
-    if ($ '#checkout_form_address').is('*')
-      ($ '#checkout_form_address').validate()
+$ ->
+  if $('#checkout_form_address').is('*')
+    $('#checkout_form_address').validate()

-      getCountryId = (region) ->
-        $('#' + region + 'country select').val()
+    getCountryId = (region) ->
+      $('#' + region + 'country select').val()

-      Spree.updateState = (region) ->
-        countryId = getCountryId(region)
-        if countryId?
-          unless Spree.Checkout[countryId]?
-            $.get Spree.routes.states_search, {country_id: countryId}, (data) ->
-              Spree.Checkout[countryId] =
-                states: data.states
-                states_required: data.states_required
-              Spree.fillStates(region)
-          else
-            Spree.fillStates(region)
+    statesByCountry = {}

-      Spree.fillStates = (region) ->
-        countryId = getCountryId(region)
-        data = Spree.Checkout[countryId]
-        return unless data?
+    updateState = (region) ->
+      countryId = getCountryId(region)
+      if countryId?
+        unless statesByCountry[countryId]?
+          $.get Spree.routes.states_search, {country_id: countryId}, (data) ->
+            statesByCountry[countryId] =
+              states: data.states
+              states_required: data.states_required
+            fillStates(region)
+        else
+          fillStates(region)

-        statesRequired = data.states_required
-        states = data.states
+    fillStates = (region) ->
+      countryId = getCountryId(region)
+      data = statesByCountry[countryId]
+      return unless data?

-        statePara = ($ '#' + region + 'state')
-        stateSelect = statePara.find('select')
-        stateInput = statePara.find('input')
-        stateSpanRequired = statePara.find('[id$="state-required"]')
-        if states.length > 0
-          selected = parseInt stateSelect.val()
-          stateSelect.html ''
-          statesWithBlank = [{ name: '', id: ''}].concat(states)
-          $.each statesWithBlank, (idx, state) ->
-            opt = ($ document.createElement('option')).attr('value', state.id).html(state.name)
-            opt.prop 'selected', true if selected is state.id
-            stateSelect.append opt
+      statesRequired = data.states_required
+      states = data.states

-          stateSelect.prop('disabled', false).show()
-          stateInput.hide().prop 'disabled', true
-          statePara.show()
+      statePara = $('#' + region + 'state')
+      stateSelect = statePara.find('select')
+      stateInput = statePara.find('input')
+      stateSpanRequired = statePara.find('[id$="state-required"]')
+      if states.length > 0
+        selected = parseInt stateSelect.val()
+        stateSelect.html ''
+        statesWithBlank = [{ name: '', id: ''}].concat(states)
+        $.each statesWithBlank, (idx, state) ->
+          opt = $(document.createElement('option')).attr('value', state.id).html(state.name)
+          opt.prop 'selected', true if selected is state.id
+          stateSelect.append opt
+
+        stateSelect.prop('disabled', false).show()
+        stateInput.hide().prop 'disabled', true
+        statePara.show()
+        stateSpanRequired.show()
+        stateSelect.addClass('required') if statesRequired
+        stateSelect.removeClass('hidden')
+        stateInput.removeClass('required')
+      else
+        stateSelect.hide().prop 'disabled', true
+        stateInput.show()
+        if statesRequired
           stateSpanRequired.show()
-          stateSelect.addClass('required') if statesRequired
-          stateSelect.removeClass('hidden')
+          stateInput.addClass('required')
+        else
+          stateInput.val ''
+          stateSpanRequired.hide()
           stateInput.removeClass('required')
-        else
-          stateSelect.hide().prop 'disabled', true
-          stateInput.show()
-          if statesRequired
-            stateSpanRequired.show()
-            stateInput.addClass('required')
-          else
-            stateInput.val ''
-            stateSpanRequired.hide()
-            stateInput.removeClass('required')
-          statePara.toggle(!!statesRequired)
-          stateInput.prop('disabled', !statesRequired)
-          stateInput.removeClass('hidden')
-          stateSelect.removeClass('required')
+        statePara.toggle(!!statesRequired)
+        stateInput.prop('disabled', !statesRequired)
+        stateInput.removeClass('hidden')
+        stateSelect.removeClass('required')

-      ($ '#bcountry select').change ->
-        Spree.updateState 'b'
+    $('#bcountry select').change ->
+      updateState 'b'

-      ($ '#scountry select').change ->
-        Spree.updateState 's'
+    $('#scountry select').change ->
+      updateState 's'

-      Spree.updateState 'b'
+    updateState 'b'

-      order_use_billing = ($ 'input#order_use_billing')
-      order_use_billing.change ->
-        update_shipping_form_state order_use_billing
-
-      update_shipping_form_state = (order_use_billing) ->
-        if order_use_billing.is(':checked')
-          ($ '#shipping .inner').hide()
-          ($ '#shipping .inner input, #shipping .inner select').prop 'disabled', true
-        else
-          ($ '#shipping .inner').show()
-          ($ '#shipping .inner input, #shipping .inner select').prop 'disabled', false
-          Spree.updateState('s')
-
+    order_use_billing = $('input#order_use_billing')
+    order_use_billing.change ->
       update_shipping_form_state order_use_billing

-  Spree.onAddress()
+    update_shipping_form_state = (order_use_billing) ->
+      if order_use_billing.is(':checked')
+        $('#shipping .inner').hide()
+        $('#shipping .inner input, #shipping .inner select').prop 'disabled', true
+      else
+        $('#shipping .inner').show()
+        $('#shipping .inner input, #shipping .inner select').prop 'disabled', false
+        updateState('s')
+
+    update_shipping_form_state order_use_billing


Start polling only after sync request was done

--- assets/scripts/app/models/user.coffee
+++ assets/scripts/app/models/user.coffee
@@ -51,9 +51,11 @@
   ).property()

   sync: ->
-    Travis.ajax.post('/users/sync')
-    @setWithSession('isSyncing', true)
-    @poll()
+    self = this
+    Travis.ajax.post('/users/sync', {}, ->
+      self.setWithSession('isSyncing', true)
+      self.poll()
+    )

   poll: ->
     Travis.ajax.get '/users', (data) =>


Fix 'undefined is not a function' error

Fix 'undefined is not a function' error

--- controllers/auth.coffee
+++ controllers/auth.coffee
@@ -38,8 +38,8 @@
     next()
   else
     err = new Error 'Forbidden: Admin'
-    err.status 403
-    err.type 'forbidden'
+    err.status = 403
+    err.type = 'forbidden'
     next err

 exports.logout = (req, res) ->


indicate if course is concept coach or tutor

--- tutor/src/components/course-listing/course.cjsx
+++ tutor/src/components/course-listing/course.cjsx
@@ -79,6 +79,7 @@
         {...courseDataProps}
         data-is-teacher={courseIsTeacher}
         data-course-id={course.id}
+        data-course-course-type={if course.is_concept_coach then 'cc' else 'tutor'}
         className={itemClasses}
       >
         <div


update waiting message

update waiting message

--- tutor/src/components/task-step/placeholder.cjsx
+++ tutor/src/components/task-step/placeholder.cjsx
@@ -27,7 +27,7 @@

     message = if exists
       if isLoading
-        "Your personalized questions are loading.  Please wait."
+        "Your personalized question is loading. Please wait."
       else
         "Unlock this personalized question by answering more #{type} problems for this assignment."
     else


remove async false

remove async false

--- BackofficeBundle/Resources/public/coffee/backboneRouter.coffee
+++ BackofficeBundle/Resources/public/coffee/backboneRouter.coffee
@@ -19,26 +19,25 @@
       return
     return
   initDisplayRouteChanges: (selector) ->
+    $.ajaxSetup().abortXhr()
     selector = if selector == undefined then '[href="#' + Backbone.history.fragment + '"]' else selector
     $('nav li.active').removeClass 'active'
-    lis = $('nav li:has(a' + selector + ')')
-    lis.addClass 'active'
-    lis.each ->
-      ul = $('ul', this)
-      link = $(this).children('a').first()
-      if link.data('subtree')
-        $.ajax
-          url: link.data('subtree')
-          type: "GET"
-          async: false
-          success: (response) ->
-            ul.html(response)
-            ul.jarvismenu $("nav").data('opts')
-            link.data('subtree', null)
-        return
-    openMenu $("nav").data('opts').speed, $("nav").data('opts').openedSign
+    $('nav li:has(a' + selector + ')').addClass 'active'
+    link = $('nav li a' + selector)
+    if link.data('subtree')
+      $.ajax
+        url: link.data('subtree')
+        type: "GET"
+        success: (response) ->
+          ul = link.next()
+          ul.html(response)
+          ul.jarvismenu $("nav").data('opts')
+          link.data('subtree', null)
+          openMenu $("nav").data('opts').speed, $("nav").data('opts').openedSign
+      return
+    else
+      openMenu $("nav").data('opts').speed, $("nav").data('opts').openedSign
     document.title = $('nav a' + selector).attr('title') or document.title
-    $.ajaxSetup().abortXhr()
     drawBreadCrumb()
     displayLoader()
     return


Check phantom.version to help prevent issues like #93

--- lib/mocha-phantomjs.coffee
+++ lib/mocha-phantomjs.coffee
@@ -124,7 +124,9 @@
     catch error
       false

-
+if phantom.version.major isnt 1 or phantom.version.minor < 9
+  console.log 'mocha-phantomjs requires PhantomJS > 1.9.1'
+  phantom.exit -1

 reporter = system.args[2] || 'spec'
 config   = JSON.parse system.args[3] || '{}'


Update beerme.coffee

Update beerme.coffee

Missing a comma within the list of URLs

--- src/scripts/beerme.coffee
+++ src/scripts/beerme.coffee
@@ -19,7 +19,7 @@
   "http://www.joyandfood.com/wp-content/uploads/2012/05/Beer-beer.jpg",
   "http://www.bristolvantage.com/wp-content/uploads/2012/02/beer-calories1.jpg",
   "http://cdn.biruwananbai.com/wp-content/uploads/2012/04/more_beer-01.jpg",
-  "http://blog.collegebars.net/uploads/10-beers-you-must-drink-this-summer/10-beers-you-must-drink-this-summer-sam-adams-summer-ale.jpg"
+  "http://blog.collegebars.net/uploads/10-beers-you-must-drink-this-summer/10-beers-you-must-drink-this-summer-sam-adams-summer-ale.jpg",
   "http://media.treehugger.com/assets/images/2011/10/save-the-beers.jpg",
   "http://poemsforkush.files.wordpress.com/2012/04/beer.jpg",
   "http://www.wirtzbeveragegroup.com/wirtzbeveragenevada/wp-content/uploads/2010/06/Beer.jpg",


add examples / tests to study correct pipeline behavior on abort


Change iterator name

Change iterator name

--- lib/symbol-provider.coffee
+++ lib/symbol-provider.coffee
@@ -145,7 +145,7 @@
       score *= @getLocalityScore(symbol, position) if symbol.path is @editor.getPath()
       candidates.push({symbol, score, locality, rowDifference}) if score > 0

-    candidates.sort(@sortSymbolsReverseIterator)
+    candidates.sort(@symbolSortReverseIterator)

     # Just get the first unique 20
     wordsSeen = {}
@@ -158,7 +158,7 @@
       wordsSeen[key] = true
     results

-  sortSymbolsReverseIterator: (a, b) -> b.score - a.score
+  symbolSortReverseIterator: (a, b) -> b.score - a.score

   getLocalityScore: (symbol, position) ->
     if symbol.bufferRows?


Remove unnecessary comments

Remove unnecessary comments

--- lib/symbol-provider.coffee
+++ lib/symbol-provider.coffee
@@ -100,11 +100,6 @@
   Section: Suggesting Completions
   ###

-  # Public:  Gets called when the document has been changed. Returns an array
-  # with suggestions. If `exclusive` is set to true and this method returns
-  # suggestions, the suggestions will be the only ones that are displayed.
-  #
-  # Returns an {Array} of Suggestion instances
   requestHandler: (options) =>
     return unless options?
     return unless options.editor?
@@ -118,11 +113,6 @@
       suggestions = @findSuggestionsForWord(options)
       resolve(suggestions)

-  # Private: Finds possible matches for the given string / prefix
-  #
-  # prefix - {String} The prefix
-  #
-  # Returns an {Array} of Suggestion instances
   findSuggestionsForWord: (options) =>
     return unless @symbolList?
     # Merge the scope specific words into the default word list
@@ -181,9 +171,6 @@
   settingsForScopeDescriptor: (scopeDescriptor, keyPath) =>
     atom.config.getAll(keyPath, scope: scopeDescriptor)

-  # Private: Finds autocompletions in the current syntax scope (e.g. css values)
-  #
-  # Returns an {Array} of strings
   builtinCompletionsForCursorScope: =>
     cursorScope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
     completions = @settingsForScopeDescriptor(cursorScope, "editor.completions")


:lipstick: Reformat component rendering

:lipstick: Reformat component rendering

--- lib/table-component.coffee
+++ lib/table-component.coffee
@@ -16,11 +16,27 @@
       rowData = @props.table.getRow(row)
       columns = []
       rowData.eachCell (cell,i) ->
-        columns.push div key: "cell-#{row}-#{i}", className: 'table-edit-column', style: { width: columnsWidth[i] }, cell.getValue()
+        columns.push div {
+          key: "cell-#{row}-#{i}"
+          className: 'table-edit-column'
+          style:
+            width: columnsWidth[i]
+        }, cell.getValue()

-      div key: "row-#{row}", className: 'table-edit-row', style: { height: "#{rowHeight}px", top: "#{row * rowHeight}px" }, 'data-row-id': row + 1, columns
+      div {
+        key: "row-#{row}"
+        className: 'table-edit-row'
+        'data-row-id': row + 1
+        style:
+          height: "#{rowHeight}px"
+          top: "#{row * rowHeight}px"
+      }, columns

-    div className: 'table-edit-content', style: { height: @getTableHeight() }, rows
+    div {
+      className: 'table-edit-content'
+      style:
+        height: @getTableHeight()
+    }, rows

   getTableHeight: ->
     @state.totalRows * @state.rowHeight


Change websocket trigger to turbolinks:load; debugging

--- app/assets/javascripts/channels/posts.coffee
+++ app/assets/javascripts/channels/posts.coffee
@@ -1,7 +1,8 @@
 is_page_visible = true
 num_unseen_posts = 0

-$(document).on 'page:change', ->
+$(document).on 'turbolinks:load', ->
+  console.log "hi"
   if location.pathname == '/posts'
     App.posts = App.cable.subscriptions.create "PostsChannel",
       received: (data) ->
@@ -11,6 +12,7 @@
           num_unseen_posts++
           document.title = "(#{num_unseen_posts}*) Recent posts - metasmoke"
   else if /^\/post\/(\d*)(\/)?$/.test(location.pathname)
+    console.log "oy"
     App.posts = App.cable.subscriptions.create { channel: "PostsChannel", post_id: location.pathname.match(/^\/post\/(\d*)(\/)?$/)[1] },
       received: (data) ->
         $('strong.post-feedbacks').prepend(data['feedback'])


Remove uneeded taskType prop from model.

--- app/assets/javascripts/models/task.js.coffee
+++ app/assets/javascripts/models/task.js.coffee
@@ -3,7 +3,6 @@
   assignee: DS.belongsTo('user')
   assignees: DS.hasMany('user')
   phase: DS.belongsTo('phase')
-  typeKey: 'Task'

   body: a('string')
   completed: a('boolean')


Remove empty space

Remove empty space

--- app/assets/javascripts/models/user.js.coffee
+++ app/assets/javascripts/models/user.js.coffee
@@ -3,7 +3,7 @@
   name: DS.attr('string')
   nickname: DS.attr('string')
   admin: DS.attr('boolean')
-  permissions: DS.hasMany('permission' )
+  permissions: DS.hasMany('permission')
   person: DS.belongsTo('author')
   token: DS.attr('string')


coffeescript-way, including PersonImagen and removed #imagen_url_small

--- app/assets/javascripts/models/user.js.coffee
+++ app/assets/javascripts/models/user.js.coffee
@@ -1,4 +1,4 @@
-Bandango.User = Ember.Object.extend( Ember.Validations, {
+Bandango.User = Ember.Object.extend Ember.Validations, Bandango.PersonImagen,
   validations: {
     nombres: {
       presence: true
@@ -14,9 +14,3 @@
     else
       nombres
     ).property("nombres")
-
-
-  imagen_url_small: (->
-    @get("imagen_url") + "?s=200"
-  ).property("imagen_url")
-});


prevent default as first thing

prevent default as first thing

--- app/assets/javascripts/mtl/clickables.coffee
+++ app/assets/javascripts/mtl/clickables.coffee
@@ -10,6 +10,6 @@
   ev.stopPropagation()

 $(document).on 'click', '[data-mtl-submit]', (ev) ->
+  ev.preventDefault()
   selector = $(this).data('mtl-submit')
   $(selector).submit()
-  ev.preventDefault()


Avoid double-require of a preloadable module.

--- lib/loader.coffee
+++ lib/loader.coffee
@@ -105,9 +105,9 @@
 sitesModuleSignature= /_sites$/
 preload= (modules,superMeta)->
   for modname in modules
-    require(modname) if meta_lookup(modname)?._preload_modules
+    m= meta_lookup(modname)?._preload_modules && require(modname)
     if sitesModuleSignature.test(modname)
-      m = require(modname)  # assure load even if preload_modules is off
+      m ?= require(modname)  # assure load even if preload_modules is off
       sites[path.dirname(modname)]= m
       siteLookupRoot ?= path.dirname(modname)
       for sitepath in m.paths


Be linkable each articles

Be linkable each articles

--- app/scripts/directives/wintersarticle.coffee
+++ app/scripts/directives/wintersarticle.coffee
@@ -12,15 +12,19 @@
       restrict: 'E'
       template: """

-

-

-            {{wintersArticleElement.description}}
-

-
{{wintersArticleElement.createdAt | date:'MMM d, yyyy'}}

+
+

+

+              {{wintersArticleElement.description}}
+

+
{{wintersArticleElement.createdAt | date:'MMM d, yyyy'}}

+

-

-
+
+
+
+

       """
       link: (scope, element, attrs) ->
@@ -48,6 +52,7 @@
           description = "#{description[0..24]}..."

         scope.wintersArticleElement =
+          id: gist.id
           image: image
           description: description
           createdAt: gist.created_at


:fire: passing unUsed parameter, just a mistake

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -88,10 +88,10 @@
   selectInclusive: (selection) ->
     {cursor} = selection
     moveLeft = ({allowWrap}={}) ->
-      moveCursorLeft(cursor, {allowWrap, preserveFolds: true, preserveGoalColumn: true})
+      moveCursorLeft(cursor, {allowWrap, preserveGoalColumn: true})

     moveRight = ({allowWrap}={}) ->
-      moveCursorRight(cursor, {allowWrap, preserveFolds: true, preserveGoalColumn: true})
+      moveCursorRight(cursor, {allowWrap, preserveGoalColumn: true})

     mergeTailRange = (tailRange) ->
       newRange = selection.getBufferRange().union(tailRange)


pool.poolRef for uniformity.

pool.poolRef for uniformity.

--- src/xo.coffee
+++ src/xo.coffee
@@ -188,7 +188,7 @@

       # This helper normalizes a record by inserting its type.
       normalizeObject = (object, ref, type) ->
-        object.$poolRef = poolRef unless type is 'pool'
+        object.$poolRef = poolRef
         object.$ref = ref
         object.$type = type


fix interface, return pairs allover

fix interface, return pairs allover

--- lib/parser.coffee
+++ lib/parser.coffee
@@ -4,18 +4,23 @@
 version = require './domain/version'

-Parser = (@template_dir, @ext='.html', @key='versions', @domain='template') ->
+Parser = (@template_dir, @o={}) ->
+  throw new Error("need a valid template directory #{@template_dir}") if !path.existsSync(@template_dir)
+  @o.ext    or= '.html'
+  @o.domain or= 'template'
+  @o.key    or= 'versions'
+  return

 Parser::data = ->
   vobj = {}
-  for file in fsx.readDirSync(@template_dir).files when path.extname(file) is @ext
+  for file in fsx.readDirSync(@template_dir).files when path.extname(file) is @o.ext
     v = version.read(fs.readFileSync(file))
     throw new Error("path: #{file} does not start with a valid version number") if !v
-    vobj[path.basename(file).split(@ext)[0]] = v
-  JSON.stringify(vobj)
+    vobj[path.basename(file).split(@o.ext)[0]] = v
+  [@o.key, JSON.stringify(vobj)]

 Parser::domains = ->
-  [@domain, __dirname+'/domain/'] # make tinysemver requireable as template::version on the client
+  [@o.domain, __dirname+'/domain/'] # make tinysemver requireable as template::version on the client


typo fix (err coffee :)

typo fix (err coffee :)

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -59,7 +59,7 @@
   finish: ->
     @_context
       .then => do @_browser.quit
-      .done -> do process.exit @_errorCount
+      .done -> process.exit @_errorCount

   errorHandler: (error) =>
     @_errorCount++


schema takes related models as a param

schema takes related models as a param

--- lib/schema.coffee
+++ lib/schema.coffee
@@ -12,10 +12,15 @@
   Integer: 'Integer'
   Float: 'Float'

+RELATIONS =
+  One: One
+  Many: Many
+
 module.exports = class Schema
-  constructor: (@model_type, type_overrides={}) ->
+  constructor: (@model_type, type_overrides={}, relation_types={}) ->

     @types = _.defaults(type_overrides, TYPES)
+    @relation_types = _.defaults(relation_types, RELATIONS)
     @fields ={}; @relations ={}; @ids_accessor = {}
     @_parse()

@@ -26,9 +31,9 @@
       options = @_parseFieldOptions(options()) if _.isFunction(options)
       switch options.type
         when 'hasOne', 'belongsTo'
-          relation = @relations[key] = new One(@model_type, key, options)
+          relation = @relations[key] = new @relation_types.One(@model_type, key, options)
         when 'hasMany'
-          relation = @relations[key] = new Many(@model_type, key, options)
+          relation = @relations[key] = new @relation_types.Many(@model_type, key, options)
         else
           throw new Error "Unrecognized relationship: #{util.inspect(options)}"


completely removed NODE_ENV

completely removed NODE_ENV

--- lib/sentry.coffee
+++ lib/sentry.coffee
@@ -58,9 +58,6 @@
     unless @enabled
       return console.log @disable_message

-    unless process.env.NODE_ENV is 'production'
-      return console.log "If #{process.env.NODE_ENV} was enabled, would have sent to Sentry:", data
-
     # data.logger must be a string else sentry fails quietly
     if data.logger? and not _.isString data.logger
       data.logger = "WARNING: logger not passed as string! #{JSON.stringify(data.logger)}"


fix say without callback

fix say without callback

--- system.coffee
+++ system.coffee
@@ -15,7 +15,7 @@
         request.get chatURL, (err, response, body) ->
             if not err and response.statusCode is 200
                 console.log "answer sent" if program.verbose
-                cb()
+                cb?()
             else
                 console.log "erro #{[err]}"


Get rid of XXX

Get rid of XXX

There's something weird going on with `node_modules` loaders at build.

--- webpack.coffee
+++ webpack.coffee
@@ -11,8 +11,7 @@
 				'posts': path.join process.cwd(), 'posts'
 				'drafts': path.join process.cwd(), 'drafts'
 				'elements': path.join __dirname, 'elements'
-				'theme': config.theme # XXX: not sure if this is the right way to inject theme
-				# hitting Cannot find module "./Nav" with this
+				'theme': config.theme
 			extensions: [
 				''
 				'.webpack.js'


Fixes for ThangListEntryView popover hover states.

--- app/views/play/level/tome/thang_list_entry_view.coffee
+++ app/views/play/level/tome/thang_list_entry_view.coffee
@@ -21,6 +21,7 @@
     'surface:frame-changed': "onFrameChanged"
     'level-set-letterbox': 'onSetLetterbox'
     'tome:thang-list-entry-popover-shown': 'onThangListEntryPopoverShown'
+    'surface:coordinates-shown': 'onSurfaceCoordinatesShown'

   events:
     'click': 'onClick'
@@ -86,12 +87,18 @@

   onMouseEnter: (e) ->
     return unless @controlsEnabled and @spells.length
-    @showSpells()
+    @clearTimeouts()
+    @showSpellsTimeout = _.delay @showSpells, 100

   onMouseLeave: (e) ->
     return unless @controlsEnabled and @spells.length
+    @clearTimeouts()
+    @hideSpellsTimeout = _.delay @hideSpells, 100
+
+  clearTimeouts: ->
+    clearTimeout @showSpellsTimeout if @showSpellsTimeout
     clearTimeout @hideSpellsTimeout if @hideSpellsTimeout
-    @hideSpellsTimeout = _.delay @hideSpells, 100
+    @showSpellsTimeout = @hideSpellsTimeout = null

   onThangListEntryPopoverShown: (e) ->
     # I couldn't figure out how to get the mouseenter / mouseleave to always work, so this is a fallback
@@ -99,25 +106,28 @@
     return if e.entry is @
     @hideSpells()

+  onSurfaceCoordinatesShown: (e) ->
+    # Definitely aren't hovering over this.
+    @hideSpells()
+
   showSpells: =>
+    @clearTimeouts()
     @sortSpells()
     @$el.data('bs.popover').options.content = @getSpellListHTML()
     @$el.popover('setContent').popover('show')
     @$el.parent().parent().parent().i18n()
-    clearTimeout @hideSpellsTimeout if @hideSpellsTimeout
-    @hideSpellsTimeout = null
     @popover = @$el.parent().parent().parent().find('.popover')
     @popover.off 'mouseenter mouseleave'
-    @popover.mouseenter (e) => @onMouseEnter()
-    @popover.mouseleave (e) => @onMouseLeave()
+    @popover.mouseenter (e) => @showSpells() if @controlsEnabled
+    @popover.mouseleave (e) => @hideSpells()
     thangID = @thang.id
     @popover.find('code').click (e) ->
       Backbone.Mediator.publish "level-select-sprite", thangID: thangID, spellName: $(@).data 'spell-name'
     Backbone.Mediator.publish 'tome:thang-list-entry-popover-shown', entry: @

   hideSpells: =>
+    @clearTimeouts()
     @$el.popover('hide')
-    @hideSpellsTimeout = null

   getSpellListHTML: ->
     spellsPopoverTemplate {spells: @spells}
@@ -129,13 +139,16 @@
   onSetLetterbox: (e) ->
     if e.on then @reasonsToBeDisabled.letterbox = true else delete @reasonsToBeDisabled.letterbox
     @updateControls()
+
   onDisableControls: (e) ->
     return if e.controls and not ('surface' in e.controls)  # disable selection?
     @reasonsToBeDisabled.controls = true
     @updateControls()
+
   onEnableControls: (e) ->
     delete @reasonsToBeDisabled.controls
     @updateControls()
+
   updateControls: ->
     enabled = _.keys(@reasonsToBeDisabled).length is 0
     return if enabled is @controlsEnabled


Remove unnecessary else in commitStore.add since we're throwing in the if

--- source/server/infrastructure/commit_store.coffee
+++ source/server/infrastructure/commit_store.coffee
@@ -31,45 +31,45 @@
         expectedVersion,
         currentVersion
       )
-    else
-      newVersion = currentVersion + 1
-      @_setEventVersion(event, newVersion) for event in changes.events

-      # serialize events and commands
-      serializedChanges = events: [], commands: []
-      for event in changes.events
-        serializedChanges.events.push type: event.typeName(), data: event.toData()
-      for command in changes.commands
-        serializedChanges.commands.push type: command.typeName(), data: command.toData()
+    newVersion = currentVersion + 1
+    @_setEventVersion(event, newVersion) for event in changes.events

-      commit = {
-        sourceId: sourceId.toString()
-        version: newVersion
-        changes: serializedChanges
-        insertedAt: new Date()
-        eventTypes: @_getEventTypes(changes.events)
-        sentBy: @configuration.appId
-        receivers: [{ appId: @configuration.appId, receivedAt: new Date }]
-      }
+    # serialize events and commands
+    serializedChanges = events: [], commands: []
+    for event in changes.events
+      serializedChanges.events.push type: event.typeName(), data: event.toData()
+    for command in changes.commands
+      serializedChanges.commands.push type: command.typeName(), data: command.toData()

-      # insert commit with next version
-      @log.debug(@_logMsg("Inserting commit"), commit)
-      try
-        commitId = @commits.insert commit
-      catch error
-        if (error.code == 11000)
-          # A commit for this aggregate version already exists
-          # Re-query for the changed state
-          lastCommit = @_getLastCommit(sourceId)
-          throw new Space.eventSourcing.CommitConcurrencyException(
-            sourceId,
-            expectedVersion,
-            lastCommit.version
-          )
-        else
-          throw error
+    commit = {
+      sourceId: sourceId.toString()
+      version: newVersion
+      changes: serializedChanges
+      insertedAt: new Date()
+      eventTypes: @_getEventTypes(changes.events)
+      sentBy: @configuration.appId
+      receivers: [{ appId: @configuration.appId, receivedAt: new Date }]
+    }

-      @commitPublisher.publishChanges(changes, commitId)
+    # insert commit with next version
+    @log.debug(@_logMsg("Inserting commit"), commit)
+    try
+      commitId = @commits.insert commit
+    catch error
+      if (error.code == 11000)
+        # A commit for this aggregate version already exists
+        # Re-query for the changed state
+        lastCommit = @_getLastCommit(sourceId)
+        throw new Space.eventSourcing.CommitConcurrencyException(
+          sourceId,
+          expectedVersion,
+          lastCommit.version
+        )
+      else
+        throw error
+
+    @commitPublisher.publishChanges(changes, commitId)

   getEvents: (sourceId, versionOffset=1) ->
     events = []


hub/invite collab: missing callback

hub/invite collab: missing callback

--- src/smc-hub/client.coffee
+++ src/smc-hub/client.coffee
@@ -1222,6 +1222,8 @@
                 (cb) =>
                     if locals.done or (not locals.email_address)
                         cb(); return
+
+                    cb()  # we return early, because there is no need to let someone wait for sending the email

                     # available message fields
                     # mesg.title            - title of project


hub/client: make mesg_invite_noncloud_collaborators' local state clean

--- src/smc-hub/client.coffee
+++ src/smc-hub/client.coffee
@@ -1364,10 +1364,11 @@
                     # at least we can limit its size.
                     cb("email address must be at most 128 characters: '#{email_address}'")
                     return
-                done  = false
-                account_id = undefined
+
                 locals =
-                    settings = undefined
+                    done       : false
+                    account_id : undefined
+                    settings   : undefined

                 async.series([
                     # already have an account?
@@ -1376,16 +1377,16 @@
                             email_address : email_address
                             cb            : (err, _account_id) =>
                                 dbg("account_exists: #{err}, #{_account_id}")
-                                account_id = _account_id
+                                locals.account_id = _account_id
                                 cb(err)
                     (cb) =>
-                        if account_id
+                        if locals.account_id
                             dbg("user #{email_address} already has an account -- add directly")
                             # user has an account already
-                            done = true
+                            locals.done = true
                             @database.add_user_to_project
                                 project_id : mesg.project_id
-                                account_id : account_id
+                                account_id : locals.account_id
                                 group      : 'collaborator'
                                 cb         : cb
                         else
@@ -1398,7 +1399,7 @@
                                 ttl           : 60*60*24*14  # valid for 14 days
                                 cb            : cb
                     (cb) =>
-                        if done
+                        if locals.done
                             cb()
                         else
                             @database.when_sent_project_invite
@@ -1408,7 +1409,7 @@
                                     if err
                                         cb(err)
                                     else if when_sent >= misc.days_ago(7)   # successfully sent < one week ago -- don't again
-                                        done = true
+                                        locals.done = true
                                         cb()
                                     else
                                         cb()
@@ -1423,7 +1424,7 @@
                                     cb()

                     (cb) =>
-                        if done
+                        if locals.done
                             dbg("NOT send_email invite to #{email_address}")
                             cb()
                             return


minor fix

minor fix

--- src/builder/index.coffee
+++ src/builder/index.coffee
@@ -355,7 +355,6 @@
         namespace["version"] = packagejson.version
         namespace["hash_name"] = @hash_func
         namespace["decoder_name"] = @coding_func
-        namespace["ask_password"] = @password?
         if @manifest?
             filepath = path.resolve(@root, @manifest)
             relative = @_relativate(path.relative(@root, filepath))