improve zip perf

improve zip perf

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1025,6 +1025,7 @@
     if !sources.length
       return Bacon.never()

+    needsBarrier = _.any sources, (s) -> s.flatten
     sources = _.map Source.fromObservable, sources
     observables = _.map ((s) -> s.obs), sources

@@ -1049,7 +1050,8 @@
                  #console.log "match", p
                  val = -> p.f(sources[i.index].consume() for i in p.ixs ...)
                  reply = sink trigger.e.apply(val)
-                 triggers = _.filter ((trigger) -> !trigger.source.flatten), triggers
+                 if triggers.length
+                   triggers = _.filter ((trigger) -> !trigger.source.flatten), triggers
                  break
           if _.all(sources, cannotSync) or _.all(pats, cannotMatch)
             reply = Bacon.noMore
@@ -1068,8 +1070,7 @@
             if source.sync
               #console.log "queuing", e.toString(), _.toString(resultStream)
               triggers.push {source: source, e: e}
-              # TODO flush immediately if no flattened sources
-              flushLater()
+              if needsBarrier then flushLater() else flush()
           unsubAll() if reply == Bacon.noMore
           reply or Bacon.more


修正性能优化新带来的小问题

修正性能优化新带来的小问题

--- src/widget/edit/dropdown.coffee
+++ src/widget/edit/dropdown.coffee
@@ -853,10 +853,13 @@
 		attrBinding = @_elementAttrBindings?["items"]
 		list = @_list
 		list._textProperty = @_textProperty or @_valueProperty or "value"
+
 		if attrBinding
 			@_refreshAttrValue("items")
 			list.set("bind", attrBinding.expression.raw)
-		list.set("items", @_items)
+		else
+			list.set("items", @_items)
+
 		list.refresh()

 cola.registerWidget(cola.Dropdown)


added nonce to Db queries in order to bypass IE cache

--- src/Db/Db.coffee
+++ src/Db/Db.coffee
@@ -12,9 +12,16 @@
           buf.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
       return if buf.length then "?" + buf.join("&") else ""

+    _addNonce: (url) ->
+      if url.indexOf('?') >= 0
+        url += "&"
+      else
+        url += "?"
+      return url + '_nonce=' + @_randomString(15)
+
     get: (_id) ->
       t0 = performance.now()
-      $http.get(@dbUrl + _id)
+      $http.get @_addNonce(@dbUrl + _id)
       .then (result) =>
         t1 = performance.now()
         console.log "download time:", (t1 - t0)
@@ -33,7 +40,7 @@
         else
           return doc

-    _generateSeparator: (size)->
+    _randomString: (size)->
       if not size
         size = 32
       chars = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']
@@ -43,7 +50,7 @@
       return result

     _toAttachment: (doc) ->
-      boundary = @_generateSeparator()
+      boundary = @_randomString()
       if doc.data? and angular.isObject(doc.data) and doc.data.data?
         #data = new Uint8Array(doc.data.data.length)
         #for i in [0..doc.data.data.length-1]
@@ -70,7 +77,7 @@
     put: (doc) ->
       [strDoc, boundary] = @_toAttachment(doc)
       t0 = performance.now()
-      $http.put(@dbUrl + doc._id, strDoc, {
+      $http.put(@_addNonce(@dbUrl + doc._id), strDoc, {
         transformRequest: angular.identity
         headers:
           'Content-Type': "multipart/related;boundary=\"#{boundary}\""
@@ -80,7 +87,7 @@
         return result.data

     post: (doc) ->
-      doc._id = @_generateSeparator()
+      doc._id = @_randomString()
       return @put(doc)

     query: (fun, options) ->
@@ -88,7 +95,7 @@
       ddoc = s[0]
       view = s[1]
       $http.get(
-        @dbUrl + "_design/#{ddoc}/_view/#{view}" + @_encodeOptions(options)
+        @_addNonce(@dbUrl + "_design/#{ddoc}/_view/#{view}" + @_encodeOptions(options))
       )
       .then (result) =>
         return result.data


Added settings object to form.

Added settings object to form.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -26,7 +26,7 @@
         console.debug 'onSubmit', args, @
         result = formArgs.onSubmit?.apply(@, args)
         formTemplate = getTemplate(template)
-        callback = -> formTemplate.data?.settings?.onSubmit?.apply(@, args)
+        callback = -> formTemplate.settings.onSubmit?.apply(@, args)
         deferCallback(result, callback)
         return result if result?

@@ -36,7 +36,7 @@
         console.debug 'onSuccess', args, @
         AutoForm.resetForm(name)
         result = formArgs.onSuccess?.apply(@, args)
-        callback = -> formTemplate.data?.settings?.onSuccess?.apply(@, args)
+        callback = -> formTemplate.settings.onSuccess?.apply(@, args)
         deferCallback(result, callback)

       onError: (operation, error, template) ->
@@ -76,9 +76,10 @@
         console.debug 'onCancel', arguments, @
         formArgs.onCancel?(template)
         formTemplate = getTemplate(template)
-        formTemplate.data?.settings?.onCancel?()
+        formTemplate.settings.onCancel?()

     Form.created = ->
+      @settings = @data.settings ? {}
       formArgs.onCreate?.apply(@, arguments)

     Form.rendered = ->


fixed code style on servers.lib.server.setsession.coffee

--- servers/lib/server/setsession.coffee
+++ servers/lib/server/setsession.coffee
@@ -21,7 +21,7 @@
     # update clientId cookie
     updateCookie req, res, result.session

-    remoteIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress
+    remoteIp = req.headers['x-forwarded-for'] or req.connection.remoteAddress
     return next()  unless remoteIp

     res.cookie 'clientIPAddress', remoteIp, { maxAge: 900000, httpOnly: no }
@@ -29,6 +29,6 @@
     if result?.session?.username
       usertracker.track result.session.username

-    JSession.updateClientIP result.session.clientId, remoteIp, (err)->
+    JSession.updateClientIP result.session.clientId, remoteIp, (err) ->
       console.log err  if err?
       next()


spec: fix for fragility by better scoping assertion

DOM pollution causes this spec to be a common fragile offender

Change-Id: I8bef2cd1f61d4acce28b8ca28e25567b812ba097
Reviewed-on: https://gerrit.instructure.com/71232
Tested-by: Jenkins
Reviewed-by: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>
Product-Review: Ethan Vizitei <73eeb0b2f65d05a906adf3b21ee1f9f5a2aa3c1c@instructure.com>
QA-Review: Ethan Vizitei <73eeb0b2f65d05a906adf3b21ee1f9f5a2aa3c1c@instructure.com>

--- spec/coffeescripts/views/external_tools/AddAppViewSpec.coffee
+++ spec/coffeescripts/views/external_tools/AddAppViewSpec.coffee
@@ -129,7 +129,7 @@
     equal $('#canvas_app_name').val(), app.get('name'),
       'Name is pre-populated from app'

-    equal $('fieldset .control-group').size(), 8,
+    equal $('#add_app_form fieldset .control-group').size(), 8,
       'All fields are present (6 plus key/secret)'

 #  Causing an intermitent spec failure


Update info card spec to work with state

--- spec/javascripts/lib/components/availability_info_spec.coffee
+++ spec/javascripts/lib/components/availability_info_spec.coffee
@@ -129,7 +129,7 @@
       describe 'when the user has not entered dates', ->
         beforeEach ->
           spyOn(avInfo, "hasSearched").andReturn(false)
-          $(avInfo.config.LISTENER).trigger(':page/received', params)
+          $(avInfo.config.LISTENER).trigger(':page/received', ["", params])

         it 'does not show the info card', ->
           expect(avInfo._show).not.toHaveBeenCalled()
@@ -140,7 +140,7 @@
       describe 'when the user has entered dates', ->
         beforeEach ->
           spyOn(avInfo, "hasSearched").andReturn(true)
-          $(avInfo.config.LISTENER).trigger(':page/received', params)
+          $(avInfo.config.LISTENER).trigger(':page/received', ["", params])

         it 'shows the info card', ->
           expect(avInfo._show).toHaveBeenCalled()


social-api: simplify `mapActivity` function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -4,22 +4,27 @@
     @openedChannels = {}
     super options, data

-  mapActivity = (data)->
-    message = data?.message or data
-    # if no result, no need to do something
-    return message unless message
+  mapActivity = (data) ->

-    {SocialMessage} = KD.remote.api
-    message._id = message.id
-    m = new SocialMessage message
+    return  unless plain = data.message or data
+
+    {accountOldId, replies, interactions} = data
+
+    plain._id = plain.id
+
+    m = new KD.remote.api.SocialMessage plain
     m.account = {}
     m.account.constructorName = "JAccount"
-    m.account._id = data.accountOldId
-    m.meta = {}
-    m.meta.createdAt = message.createdAt
-    m.replies = mapActivities data.replies
-    m.repliesCount = data.replies?.length or 0
-    m.interactions = data.interactions
+    m.account._id = accountOldId
+
+    m.replies      = mapActivities data.replies or []
+    m.repliesCount = data.repliesCount
+
+    m.interactions    = interactions or
+      like            :
+        actorsCount   : 0
+        actorsPreview : []
+        isInteracted  : no

     return m


[rooms] setTopic is now a room method.

[rooms] setTopic is now a room method.

--- client/app/js/models/chats/room.coffee
+++ client/app/js/models/chats/room.coffee
@@ -6,7 +6,7 @@
     collectionType: 'UserList'
   ]

-  EVENTS: "userMessage rawMessage announce clear".split(/\s+/)
+  EVENTS: "userMessage rawMessage announce clear setTopic".split(/\s+/)

   for eventName in this::EVENTS
     do (eventName) =>


Ensure query is an instance of Query

Ensure query is an instance of Query

--- client/lanes/components/grid/Grid.cjsx
+++ client/lanes/components/grid/Grid.cjsx
@@ -16,7 +16,7 @@
         query: 'load'

     propTypes:
-        query:  Lanes.PropTypes.State.isRequired
+        query:  React.PropTypes.instanceOf(Lanes.Models.Query).isRequired
         width:  React.PropTypes.number
         height: React.PropTypes.number
         editor: React.PropTypes.oneOfType([


Avoid over 140

Avoid over 140

--- scripts/tweet.coffee
+++ scripts/tweet.coffee
@@ -16,9 +16,11 @@

   robot.respond /修論/i, (msg) ->
     text = "修論ダメです"
+    temp = text
     for i in [1..10]
+      temp = text if temp.length >= 140
       try
-        msg.reply attach(text, space, random_int(70-text.length/2))
+        msg.reply attach(temp, space, random_int(70-temp.length/2))
         break
       catch err
         continue


Fix ShareJsUpdateManager tests

Fix ShareJsUpdateManager tests

--- test/unit/coffee/ShareJsUpdateManager/ShareJsUpdateManagerTests.coffee
+++ test/unit/coffee/ShareJsUpdateManager/ShareJsUpdateManagerTests.coffee
@@ -17,12 +17,16 @@
 				"./ShareJsDB" : @ShareJsDB = { mockDB: true }
 				"redis-sharelatex" : createClient: () => @rclient = auth:->
 				"logger-sharelatex": @logger = { log: sinon.stub() }
+				"./WebRedisManager": @WebRedisManager = {}
 			globals:
 				clearTimeout: @clearTimeout = sinon.stub()

 	describe "applyUpdate", ->
 		beforeEach ->
+			@lines = ["one", "two"]
 			@version = 34
+			@update = {p: 4, t: "foo"}
+			@updatedDocLines = ["onefoo", "two"]
 			@model =
 				applyOp: sinon.stub().callsArg(2)
 				getSnapshot: sinon.stub()
@@ -31,20 +35,19 @@
 			@ShareJsUpdateManager.getNewShareJsModel = sinon.stub().returns(@model)
 			@ShareJsUpdateManager._listenForOps = sinon.stub()
 			@ShareJsUpdateManager.removeDocFromCache = sinon.stub().callsArg(1)
-			@update = {p: 4, t: "foo"}
-			@updatedDocLines = ["one", "two"]

 		describe "successfully", ->
 			beforeEach (done) ->
 				@model.getSnapshot.callsArgWith(1, null, {snapshot: @updatedDocLines.join("\n"), v: @version})
 				@model.db.appliedOps["#{@project_id}:#{@doc_id}"] = @appliedOps = ["mock-ops"]
-				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, (err, docLines, version, appliedOps) =>
+				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, @lines, @version, (err, docLines, version, appliedOps) =>
 					@callback(err, docLines, version, appliedOps)
 					done()

 			it "should create a new ShareJs model", ->
 				@ShareJsUpdateManager.getNewShareJsModel
-					.called.should.equal true
+					.calledWith(@project_id, @doc_id, @lines, @version)
+					.should.equal true

 			it "should listen for ops on the model", ->
 				@ShareJsUpdateManager._listenForOps
@@ -69,7 +72,7 @@
 				@error = new Error("Something went wrong")
 				@ShareJsUpdateManager._sendError = sinon.stub()
 				@model.applyOp = sinon.stub().callsArgWith(2, @error)
-				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, (err, docLines, version) =>
+				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, @lines, @version, (err, docLines, version) =>
 					@callback(err, docLines, version)
 					done()

@@ -86,7 +89,7 @@
 				@error = new Error("Something went wrong")
 				@ShareJsUpdateManager._sendError = sinon.stub()
 				@model.getSnapshot.callsArgWith(1, @error)
-				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, (err, docLines, version) =>
+				@ShareJsUpdateManager.applyUpdate @project_id, @doc_id, @update, @lines, @version, (err, docLines, version) =>
 					@callback(err, docLines, version)
 					done()

@@ -114,22 +117,22 @@
 				@opData =
 					op: {t: "foo", p: 1}
 					meta: source: "bar"
-				@rclient.publish = sinon.stub()
+				@WebRedisManager.sendData = sinon.stub()
 				@callback("#{@project_id}:#{@doc_id}", @opData)

 			it "should publish the op to redis", ->
-				@rclient.publish
-					.calledWith("applied-ops", JSON.stringify(project_id: @project_id, doc_id: @doc_id, op: @opData))
+				@WebRedisManager.sendData
+					.calledWith({project_id: @project_id, doc_id: @doc_id, op: @opData})
 					.should.equal true

 	describe "_sendError", ->
 		beforeEach ->
 			@error_text = "Something went wrong"
-			@rclient.publish = sinon.stub()
+			@WebRedisManager.sendData = sinon.stub()
 			@ShareJsUpdateManager._sendError(@project_id, @doc_id, new Error(@error_text))

 		it "should publish the error to the redis stream", ->
-			@rclient.publish
-				.calledWith("applied-ops", JSON.stringify(project_id: @project_id, doc_id: @doc_id, error: @error_text))
+			@WebRedisManager.sendData
+				.calledWith({project_id: @project_id, doc_id: @doc_id, error: @error_text})
 				.should.equal true


Implemented SHX, SHY and SHA instructions.

--- emulator/core/units/cpu.coffee
+++ emulator/core/units/cpu.coffee
@@ -373,6 +373,15 @@
     STY: (address) =>
         @$writeByte address, @registerY

+    SHA: (address) => # Also known as AHX
+        @$storeHighAddressIntoMemory address, @accumulator & @registerX
+
+    SHX: (address) => # Also known as SXA
+        @$storeHighAddressIntoMemory address, @registerX
+
+    SHY: (address) => # Also known as SYA
+        @$storeHighAddressIntoMemory address, @registerY
+
     ###########################################################
     # Memory read instructions
     ###########################################################
@@ -632,6 +641,12 @@
     storeValueIntoMemory: (address, value) ->
         @$updateZeroAndNegativeFlag value
         @$writeByte address, value
+
+    storeHighAddressIntoMemory: (address, register) ->
+        if @pageCrossed
+            @$writeByte address, @read address # Just copy the same value
+        else
+            @$writeByte address, register & ((address >>> 8) + 1)

     addValueToAccumulator: (operand) ->
         result = @accumulator + operand + @carryFlag
@@ -762,6 +777,11 @@
         @registerOperation 0x84, @STY, @zeroPageMode,  0, 0, 0 # 3 cycles
         @registerOperation 0x94, @STY, @zeroPageXMode, 0, 1, 0 # 4 cycles
         @registerOperation 0x8C, @STY, @absoluteMode,  0, 0, 0 # 4 cycles
+
+        @registerOperation 0x93, @SHA, @indirectYMode, 0, 1, 0 # 6 cycles (undocumented operation)
+        @registerOperation 0x9F, @SHA, @absoluteYMode, 0, 1, 0 # 5 cycles (undocumented operation)
+        @registerOperation 0x9E, @SHX, @absoluteYMode, 0, 1, 0 # 5 cycles (undocumented operation)
+        @registerOperation 0x9C, @SHY, @absoluteXMode, 0, 1, 0 # 5 cycles (undocumented operation)

         ###########################################################
         # Memory read instructions


check if model exists before creating

check if model exists before creating

--- cli/src/generators/model.coffee
+++ cli/src/generators/model.coffee
@@ -13,5 +13,9 @@
 		contents = contents.replace /~NAME_CAMEL/g, name_camel
 		contents = contents.replace /~CONTROLLER_LC/g, controller_name_lc

-		fs.writeFileSync filepath, contents
-		console.log "#{'Created'.bold} #{filepath}".green+		# write jade
+		unless fs.existsSync filepath
+			fs.writeFileSync filepath, contents
+			console.log "#{'Created'.bold} #{filepath}".green
+		else
+			console.log "#{'Already exists'.red.bold} #{filepath}".green


Add back screen row to block decorations nodes

--- src/block-decorations-component.coffee
+++ src/block-decorations-component.coffee
@@ -69,9 +69,11 @@
     @updateBlockDecorationNode(id)

   updateBlockDecorationNode: (id) ->
+    newBlockDecorationState = @newState.blockDecorations[id]
+    oldBlockDecorationState = @oldState.blockDecorations[id]
     blockDecorationNode = @blockDecorationNodesById[id]

-    if @newState.blockDecorations[id].isVisible
+    if newBlockDecorationState.isVisible
       blockDecorationNode.previousSibling.classList.remove("atom--invisible-block-decoration")
       blockDecorationNode.classList.remove("atom--invisible-block-decoration")
       blockDecorationNode.nextSibling.classList.remove("atom--invisible-block-decoration")
@@ -79,3 +81,7 @@
       blockDecorationNode.previousSibling.classList.add("atom--invisible-block-decoration")
       blockDecorationNode.classList.add("atom--invisible-block-decoration")
       blockDecorationNode.nextSibling.classList.add("atom--invisible-block-decoration")
+
+    if oldBlockDecorationState.screenRow isnt newBlockDecorationState.screenRow
+      blockDecorationNode.dataset.screenRow = newBlockDecorationState.screenRow
+      oldBlockDecorationState.screenRow = newBlockDecorationState.screenRow


dispara eventos para alterar dados ao selecionar sku

--- src/coffee/portal-sku-selector-product.coffee
+++ src/coffee/portal-sku-selector-product.coffee
@@ -27,8 +27,8 @@
 	$(".sku-selector-container").on 'skuSelected', (e, sku, selectedDimension) ->
 		console.log 'Selected:', sku, selectedDimension
 		window.FireSkuChangeImage?(sku.sku)
-		#window.FireSkuDataReceived?(sku.sku)
-		#window.FireSkuSelectionChanged?(sku.sku)
+		window.FireSkuDataReceived?(sku.sku)
+		window.FireSkuSelectionChanged?(sku.sku)

 mainTemplate = """{{dimensionLists}}"""


Adding more snippets

Adding more snippets

--- snippets/verify.cson
+++ snippets/verify.cson
@@ -175,7 +175,13 @@
     'body': 'verify that the alias "$1" is equal to "$2"'
   'verify67':
     'prefix': 'verify that the alias "alias" is not equal to "value"'
-    'body': 'verify that the alias "$1" is not equal to "$2"'
+    'body': 'verify that the alias "$1" is not equal to "$2"'
+  'verify68':
+    'prefix': 'verify that the alias "alias" is equal to alias "value"'
+    'body': 'verify that the alias "$1" is equal to alias "$2"'
+  'verify69':
+    'prefix': 'verify that the alias "alias" is not equal to alias "value"'
+    'body': 'verify that the alias "$1" is not equal to alias "$2"'

   'verify70':
     'prefix': 'verify that the page contains the text "text"'


Adding more snippets

Adding more snippets

--- snippets/verify.cson
+++ snippets/verify.cson
@@ -545,21 +545,21 @@
         'prefix': 'verify the element the name of "identifier" is not clickable within "time" seconds ignoring timeouts'
         'body': 'verify the element the name of "$1" is not clickable within "$2" seconds ignoring timeouts'

-     'verify200':
+     'verify400':
          'prefix': 'verify a link with the text content of "identifier" is present'
          'body': 'verify a link with the text content of "$1" is present'
-     'verify201':
+     'verify401':
          'prefix': 'verify a link with the text content of "identifier" is present within "time" seconds'
          'body': 'verify a link with the text content of "$1" is present within "$2" seconds'
-     'verify202':
+     'verify402':
          'prefix': 'verify a link with the text content of "identifier" is present within "time" seconds ignoring timeouts'
          'body': 'verify a link with the text content of "$1" is present within "$2" seconds ignoring timeouts'
-     'verify203':
+     'verify403':
          'prefix': 'verify a link with the text content of "identifier" is not present'
          'body': 'verify a link with the text content of "$1" is present'
-     'verify204':
+     'verify404':
          'prefix': 'verify a link with the text content of "identifier" is not present within "time" seconds'
          'body': 'verify a link with the text content of "$1" is present within "$2" seconds'
-     'verify205':
+     'verify405':
          'prefix': 'verify a link with the text content of "identifier" is not present within "time" seconds ignoring timeouts'
          'body': 'verify a link with the text content of "$1" is present within "$2" seconds ignoring timeouts'


Style.

Style.

--- client/publication.coffee
+++ client/publication.coffee
@@ -660,7 +660,7 @@
   Publication.documents.findOne Session.get 'currentPublicationId'

 Template.publicationMetaMenu.rendered = ->
-    $(@findAll '.balance-text').balanceText()
+  $(@findAll '.balance-text').balanceText()

 addAccessEvents =
   'mousedown .add-access, mouseup .add-access': (event, template) ->


IDE: Added `toggleFullscreenIDEView` method to AppController.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -610,6 +610,9 @@
       @setActiveTabView @ideViews.first.tabView
       @fakeViewsDestroyed = yes

+  toggleFullscreenIDEView: ->
+    @activeTabView.parent.toggleFullscreen()
+
   doResize: ->
     @forEachSubViewInIDEViews_ (pane) ->
       {paneType} = pane.options


feat(snippets): update AIXA2PJet snippets (#153)

--- snippets/language-coursescript.cson
+++ snippets/language-coursescript.cson
@@ -1649,7 +1649,7 @@
       QuestionPart:
         Pic: $9
         Text: $10
-        Scorer: $11
+        Audio: $11
       TR_Pic: $12
       coordinate: x=$13,y=$14
       \"""
@@ -1671,7 +1671,7 @@
       QuestionPart:
         Pic: $9
         Text: $10
-        Scorer: $11
+        Audio: $11
       TR_Pic: $12
       coordinate: x=$13,y=$14
       \"""


landing: concatenate error message lines instead of joining array elements

--- client/landing/spaceship.coffee
+++ client/landing/spaceship.coffee
@@ -31,13 +31,10 @@

   decorateEmailAddressError: (emails) ->
     @emailAddressInput.setClass "error"
-    lines = ("
" + email + "
" for email in emails)
-
-    @errorMessage.updatePartial """
-    Errors occurred with following email addresses:
-
#{lines.join ""}

-    """
-
+    lines = '
Errors occurred with following email addresses:'
+    lines += "
" + email + "
" for email in emails
+    lines += "
"
+    @errorMessage.updatePartial lines
     @errorMessage.show()

   pistachio: ->


Actually fetch should return the promise from sync

Otherwise the caller has no way of knowing when the request completes

--- client/lanes/models/Base.coffee
+++ client/lanes/models/Base.coffee
@@ -169,7 +169,6 @@
     @fetch: (options = {}) ->
         record = new this()
         record.fetch(options)
-        record

     # Removes the model's record from the server (if it is persistent)
     # and then fires the "destroy" event


halstead metrics

halstead metrics

--- src/chuck.coffee
+++ src/chuck.coffee
@@ -269,8 +269,29 @@
   cc += 1 if methodBody[methodBody.length-1].statement isnt 'return'
   cc

+unique = (strs) ->
+  uniq = []
+  for str in strs when -1 is uniq.indexOf str
+    uniq.push str
+  uniq
+
 countHalstead = (methodBody) ->
   countBlockHalstead methodBody
+
+calculateHalstead = (all) ->
+  m =
+    totalOperators: all.operators.length
+    totalOperands: all.operands.length
+    uniqueOperators: unique(all.operators).length
+    uniqueOperands: unique(all.operands).length
+  m.programLength = m.totalOperators + m.totalOperands
+  m.vocabularySize = m.uniqueOperators + m.uniqueOperands
+  m.programVolume = m.programLength * Math.log(m.vocabularySize)/Math.log(2)
+  m.difficultyLevel = (m.uniqueOperators / 2) * (m.totalOperands / m.uniqueOperands)
+  m.implementationEffort = m.programVolume * m.difficultyLevel
+  m.implementationTime = m.implementationEffort / 18
+  m.deliveredBugs = Math.pow( m.implementationEffort, 2/3 )/3000
+  m

 lineCount = (pos) ->
   pos.last_line - pos.first_line + 1
@@ -281,7 +302,12 @@

 analyzeClass = (cls) ->
   metrics = {}
-  metrics.methods = for m in cls.body when m.member is 'method'
+  metrics.methods = []
+  hals = []
+  for m in cls.body when m.member is 'method'
+    hal = countHalstead m.body
+    hals.push hal
+    r =
       name: m.name
       lines: lineCount m.position
       parameters: m.parameters.length
@@ -291,7 +317,8 @@
         conditionalNestFactor: NEST_FACTOR
         loopNestFactor: NEST_FACTOR
       )
-      halstead: countHalstead m.body
+      halstead: calculateHalstead hal
+    metrics.methods.push r
   metrics.methodCount = metrics.methods.length
   metrics.propertyCount = (m for m in cls.body when m.member is 'property').length
   metrics.innerClassCount = (m for m in cls.body when m.member is 'inner_class').length
@@ -301,6 +328,7 @@
   metrics.complexity = sum(m.complexity for n, m of metrics.methods)
   metrics.complexityPerMethod = metrics.complexity / metrics.methodCount
   metrics.complexityPerLine = metrics.complexity / metrics.lines
+  metrics.halstead = calculateHalstead combineHalsteads hals
   metrics

 module.exports =


Make reports use the correct URL for put

--- client/src/models/report.coffee
+++ client/src/models/report.coffee
@@ -13,4 +13,8 @@
       key: 'report'
   ]

-  url: "/api/report"
+  url: =>
+    if @get('id')
+      "/api/report/#{@get('id')}"
+    else
+      "/api/report"


add the published name of space modules as property

--- source/module.coffee
+++ source/module.coffee
@@ -78,7 +78,7 @@
   # Publishes a module into the space environment to make it
   # visible and requireable for other modules and the application
   @publish: (module, identifier) ->
-
+    module.publishedAs = identifier
     if Space.Module.published[identifier]?
       throw new Error "Two modules tried to be published as <#{identifier}>"
     else


removes unused error message

removes unused error message

--- source/module.coffee
+++ source/module.coffee
@@ -3,7 +3,6 @@

   @ERRORS: {
     injectorMissing: 'Instance of Space.Injector needed to initialize module.'
-    alreadyInitialized: 'Module was already initialized.'
   }

   Configuration: {}


sort commits after fetched all

sort commits after fetched all


Toggle visibility

Toggle visibility

--- source/javascripts/games.js.coffee
+++ source/javascripts/games.js.coffee
@@ -5,11 +5,27 @@
 createTogglePlayerHandler = (player) ->
   cls = playerToClass(player)
   ->
-    $("#games-chart .#{cls}").toggleClass('inactive')
+    $(".opponent.toggleable.#{cls}").toggleClass('inactive')
     $("#games-chart .#{cls}").toggle()
+    hideTournaments()

 playerToClass = (player) ->
   player.replace(/\s/g, '_')
+
+hideTournaments = ->
+  $('.tournament, .tournaments-by-year').show()
+
+  $('.tournament').each ->
+    if $('.game', this).filter(':visible').get(0)
+      $(this).show()
+    else
+      $(this).hide()
+
+  $('.tournaments-by-year').each ->
+    if $('.tournament', this).filter(':visible').get(0)
+      $(this).show()
+    else
+      $(this).hide()

 T.def 'tournament-type', (type) ->
   [ 'div.toggleable'
@@ -23,7 +39,16 @@
     'Opponents: '
     [ 'div.all.toggleable'
       click: ->
-        $('#games-chart .game').hide()
+        $('.all.toggleable').toggleClass('inactive')
+        if $('.all.toggleable').hasClass('inactive')
+          $('.opponent.toggleable').addClass('inactive')
+          $('#games-chart .game').hide()
+        else
+          $('.opponent.toggleable').removeClass('inactive')
+          $('#games-chart .game').show()
+
+        hideTournaments()
+
       'All'
     ]
     [ 'div.individuals'
@@ -114,15 +139,19 @@
     " (#{game.rank})"
   ]

-T.def 'tournaments', (year, tournaments) ->
-  [
+T.def 'tournaments-by-year', (year, tournaments) ->
+  if not tournaments then return
+  [ 'div.tournaments-by-year'
     ['div.year', year]
     T.each('tournament', tournaments)
   ]

+T.def 'tournaments', (tournaments) ->
+  for year in ['2013'..'1998']
+    T('tournaments-by-year', year, tournaments[year])
+
 showChart = (data) ->
-  for year, tournaments of data
-    T('tournaments', year, tournaments).render inside: '#games-chart'
+  T('tournaments', data).render inside: '#games-chart'

 loadDataAndShowChart = (player) ->
   loadData "#{player}_games", (result) ->


removed ref

removed ref

--- src/components/course-periods-nav.cjsx
+++ src/components/course-periods-nav.cjsx
@@ -82,7 +82,7 @@
     {afterTabsItem} = @props
     periodsItems = _.map(sortedPeriods, @renderPeriod)

-    <BS.Nav ref='tabBar' bsStyle='tabs' activeKey={active} onSelect={@onSelect}>
+    <BS.Nav bsStyle='tabs' activeKey={active} onSelect={@onSelect}>
       {periodsItems}
       {afterTabsItem?()}
     </BS.Nav>


ListViewBox: removed not ready event

ListViewBox: removed not ready event

--- src/components/list/listviewbox.coffee
+++ src/components/list/listviewbox.coffee
@@ -18,5 +18,5 @@

   updateProps: ->

-    @emit 'BoxIsEmptied', @getId()  if @subViews.length is 0
-    @emit 'HeightChanged', @getElement().clientHeight
+    @emit 'BoxIsEmptied', @getId()  if @getItems().length is 0
+    # @emit 'HeightChanged', @getElement().clientHeight


sort data in a way where the most prominent feature is on top

--- source/javascripts/master-map.coffee
+++ source/javascripts/master-map.coffee
@@ -50,7 +50,10 @@
   # Returns nothing
   drawRecruitPathsToSchool = (school) ->
     schoolRecruits = recruits.filter((r) -> r.institution in [school.team, school.alt])
+    schoolRecruits.sort (a, b) -> d3.ascending(parseFloat(a.stars), parseFloat(b.stars))
+
     recruitFeatures = schoolRecruits.map((player) -> lineStringFromPlayerToSchool(player, school))
+    recruitFeatures.sort (a, b) -> d3.ascending(parseFloat(a.properties.stars), parseFloat(b.properties.stars))

     connections = zoomGroup
       .selectAll('.connection')
@@ -84,6 +87,8 @@
     usa      = r1[0]
     schools = d3.csv.parse r2[0]
     recruits = d3.csv.parse r3[0]
+
+    schools.sort (a, b) -> d3.ascending parseFloat(a.capacity), parseFloat(b.capacity)

     # Convert to GeoJSON
     states   = topojson.mesh usa, usa.objects.states, (a, b) -> a.id != b.id


Fix GBM RoC Curve

Fix GBM RoC Curve

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -163,7 +163,7 @@
               g.from table
             )

-      if table = _.inspect 'output - training_metrics - Thresholds x Metric Scores', _model
+      if table = _.inspect 'output - training_metrics - Metrics for Thresholds', _model
        renderPlot 'ROC Curve', _.plot (g) ->
          g(
            g.path g.position 'FPR', 'TPR'


Updated GameSpec to use new SynchronizedTime api

--- spec/GameSpec.coffee
+++ spec/GameSpec.coffee
@@ -14,7 +14,8 @@

   it "moves players foward after timer tick", ->
-    game = new Game()
-    SynchronizedTime.setTime(0)
+    SynchronizedTime.setTimeForTesting(0)
     game.start()
-    SynchronizedTime.setTime(0.1)
+    SynchronizedTime.setTimeForTesting(0.1)
+    game.timer_tick()
+


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -1,3 +1,9 @@
+###
+  check for named colors in a hash table
+  check for /\#?[0-9a-f]+/i give to hex parser (hex parser chex for length 3, 4, 6, 8. 4 and 8 are rgba in hex form)
+  give to rgba parser
+###
+
 (->
   rgbParser = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),?\s*(\d\.?\d*)?\)$/


pixie

pixie

--- src/color.coffee
+++ src/color.coffee
@@ -1,4 +1,3 @@
-test = true
 Color = (I) ->
   I ||= {}


Fixed spawn tests to not leak tasks

Fixed spawn tests to not leak tasks

--- specsrc/spawn.coffee
+++ specsrc/spawn.coffee
@@ -1,8 +1,12 @@
 {describeTaskPromise} = require('task')

-{Task, spawn} = require('backburner')
+backburner = require('backburner')

 describe 'backburner.spawn', ->
+    backburner.killAll()
+    afterEach = ->
+        backburner.killAll()
+
     describeTaskPromise ->
             resolve = false
             reject = false
@@ -13,6 +17,6 @@
                 else if reject
                     @thisTask.reject()
                     @thisTask.stop()
-            p = spawn fn
+            p = backburner.spawn fn
             return [p, (-> resolve = true), (-> reject = true)]
         , 'The result of spawn()'


do not ignore components folder from watcher by default

fixes #293

--- lib/config_parser.coffee
+++ lib/config_parser.coffee
@@ -115,7 +115,7 @@
     # set up default watcher ignores
     @config.watcher_ignore_folders = _.union(
       @config.watcher_ignore_folders,
-      ['components', 'plugins', '.git', @proj.dirs['public']]
+      ['plugins', '.git', @proj.dirs['public']]
     )

     @config.watcher_ignore_files = _.union(


IMPROVED: parseFailureInfoObject also reports in the rootParser space

--- source/Caffeine.Eight/Parser.coffee
+++ source/Caffeine.Eight/Parser.coffee
@@ -325,12 +325,17 @@

     parseFailureInfoObject: (options) ->
       failureIndex = options?.failureIndex ? @_failureIndex
-      merge {
-        sourceFile: @options.sourceFile
-        failureIndex
-        location: @getFailureUrl failureIndex
-        expectingInfo: if failureIndex == @_failureIndex then @expectingInfo
-      }, @getLineColumn failureIndex
+
+      if @parentParser
+        @rootParser.getParseFailureInfoObject
+          failureIndex: @offsetInRootParserSource failureIndex
+      else
+        merge {
+          sourceFile: @options.sourceFile
+          failureIndex
+          location: @getFailureUrl failureIndex
+          expectingInfo: if failureIndex == @_failureIndex then @expectingInfo
+        }, @getLineColumn failureIndex

     parseFailureInfo: (options = {})->
       return unless @_source
@@ -347,7 +352,6 @@

       else
         compactFlatten([
-          ""
           @colorString "gray", "#{errorType} error at #{@colorString "red", @getFailureUrl failureIndex}"
           ""
           @colorString "gray", "Source:"


:art: Use prop name

:art: Use prop name

--- spec/project-find-view-spec.coffee
+++ spec/project-find-view-spec.coffee
@@ -645,7 +645,7 @@
             searchPromise

           runs ->
-            expect(atom.workspace.scan.argsForCall[0][1]['paths']).toEqual ['*.js']
+            expect(atom.workspace.scan.argsForCall[0][1].paths).toEqual ['*.js']

         it "updates the results list when a buffer changes", ->
           atom.commands.dispatch(projectFindView[0], 'core:confirm')


:new: Add triggerBufferModifyingLinters

:new: Add triggerBufferModifyingLinters

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -53,9 +53,27 @@

     scopes = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition()).scopes
     scopes.push '*' # To allow global linters
+    @triggerBufferModifyingLinters.then( =>
+      return Promise.all(@triggerLinters(wasTriggeredOnChange, scopes))
+    ).then =>
+      @lock(wasTriggeredOnChange, false)

-    Promise.all(@triggerLinters(wasTriggeredOnChange, scopes)).then =>
-      @lock(wasTriggeredOnChange, false)
+  triggerBufferModifyingLinters: (wasTriggeredOnChange, scopes)->
+    Sequence = Promise.resolve()
+    @linter.getLinters().forEach (linter) =>
+      return unless linter.modifiesBuffer
+      return unless Helpers.shouldTriggerLinter(linter, wasTriggeredOnChange, scopes)
+      Sequence = Sequence.then( =>
+        linter.lint(@editor)
+      ).then((results) =>
+        if linter.scope is 'project'
+          @linter.setMessages(linter, results)
+        else
+          # Trigger event instead of updating on purpose, because
+          # we want to make MessageRegistry the central message repo
+          @emitter.emit('should-update', {linter, results})
+      )
+    return Sequence

   # This method returns an array of promises to be used in lint
   triggerLinters: (wasTriggeredOnChange, scopes) ->


Fix typo in beautify-languages-spec, calling text.replace when not string

--- spec/beautify-languages-spec.coffee
+++ spec/beautify-languages-spec.coffee
@@ -156,6 +156,18 @@

                         beautifyCompleted = false
                         completionFun = (text) ->
+                          expect(text instanceof Error).not.toEqual(true, text)
+                          return beautifyCompleted = true if text instanceof Error
+                        #   logger.verbose(expectedTestPath, text) if ext is ".less"
+                        #   if text instanceof Error
+                        #     return beautifyCompleted = text # text == Error
+
+                          expect(text).not.toEqual(null, "Language or Beautifier not found")
+                          return beautifyCompleted = true if text is null
+
+                          expect(typeof text).toEqual("string", "Text: #{text}")
+                          return beautifyCompleted = true if typeof text isnt "string"
+
                           # Replace Newlines
                           text = text.replace(/(?:\r\n|\r|\n)/g, '⏎\n')
                           expectedContents = expectedContents\
@@ -168,17 +180,7 @@
                           text = text.replace(/(?:\ )/g, '␣')
                           expectedContents = expectedContents\
                             .replace(/(?:\ )/g, '␣')
-                        #   logger.verbose(expectedTestPath, text) if ext is ".less"
-                          expect(text instanceof Error).not.toEqual(true, text)
-                          return beautifyCompleted = true if text instanceof Error
-                        #   if text instanceof Error
-                        #     return beautifyCompleted = text # text == Error
-
-                          expect(text).not.toEqual(null, "Language or Beautifier not found")
-                          return beautifyCompleted = true if text is null
-
-                          expect(typeof text).toEqual("string", "Text: #{text}")
-                          return beautifyCompleted = true if typeof text isnt "string"
+
                           # Check for beautification errors
                           if text isnt expectedContents
                             # console.warn(allOptions, text, expectedContents)


fix bugs

fix bugs

--- vendor/assets/javascripts/angular_objectify_resource/base_model.coffee
+++ vendor/assets/javascripts/angular_objectify_resource/base_model.coffee
@@ -34,7 +34,7 @@
     toParams: ->
       result = {}
       _.forIn @, (key, value)->
-        result[key] = value unless utils.string_starts_with('_') || angular.isFunction(that)
+        result[key] = value unless utils.string_starts_with(key, '_') || angular.isFunction(value)
       result

     _convert_dates: ->


Attempt at getting specs to load minimap

--- spec/highlight-selected-spec.coffee
+++ spec/highlight-selected-spec.coffee
@@ -20,9 +20,16 @@
       editor = atom.workspace.getActiveTextEditor()
       editorElement = atom.views.getView(editor)

-      activationPromise = atom.packages
-        .activatePackage('highlight-selected').then ({mainModule}) ->
-          {highlightSelected} = mainModule
+      if atom.packages.getAvailablePackageNames().indexOf('minimap') isnt -1
+        atom.packages.activatePackage('minimap').then ->
+          atom.packages.activatePackage('minimap-highlight-selected').then ->
+            activationPromise = atom.packages
+              .activatePackage('highlight-selected').then ({mainModule}) ->
+                {highlightSelected} = mainModule
+      else
+        activationPromise = atom.packages
+          .activatePackage('highlight-selected').then ({mainModule}) ->
+            {highlightSelected} = mainModule

     waitsForPromise ->
       activationPromise


JStackTemplate::build: allow only owners to request build for a template

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -757,37 +757,44 @@
         JStackTemplate.create client, cloneData, callback

-  build: secure (client, payload, callback) ->
-
-    unless @getAt 'config.verified'
-      return callback new KodingError 'Stack needs to be verified first'
-
-    # TODO: GitHub Payload specific updates, this can be moved some other
-    # place as a middleware ~GG
-    options = {}
-    if payload?.after
-      options.title = "#{@getAt 'title'} - #{payload.after[..10]}"
-      if payload.repository?.url?
-        options.description = \
-          "Auto build for #{payload.repository.url}@#{payload.after}"
-
-    @clone client, options, (err, clonedTemplate) ->
-      return callback err  if err
-      unless clonedTemplate
-        return callback new KodingError 'Failed to clone template'
-
-      clonedTemplate.generateStack client, {}, (err, res) ->
+  build: permit
+
+    advanced: [
+      { permission: 'update own stack template', validateWith: Validators.own }
+      { permission: 'update stack template' }
+    ]
+
+    success: (client, payload, callback) ->
+
+      unless @getAt 'config.verified'
+        return callback new KodingError 'Stack needs to be verified first'
+
+      # TODO: GitHub Payload specific updates, this can be moved some other
+      # place as a middleware ~GG
+      options = {}
+      if payload?.after
+        options.title = "#{@getAt 'title'} - #{payload.after[..10]}"
+        if payload.repository?.url?
+          options.description = \
+            "Auto build for #{payload.repository.url}@#{payload.after}"
+
+      @clone client, options, (err, clonedTemplate) ->
         return callback err  if err
-        unless res.stack
-          return callback new KodingError 'Failed to generate stack'
-
-        { stack: generatedStack } = res
-        stackId = generatedStack.getId()
-        provider = (generatedStack.getAt 'config.requiredProviders')[0]
-        variables = flattenPayload payload
-
-        Kloud = require './kloud'
-        Kloud.buildStack client, { stackId, provider, variables }, callback
+        unless clonedTemplate
+          return callback new KodingError 'Failed to clone template'
+
+        clonedTemplate.generateStack client, {}, (err, res) ->
+          return callback err  if err
+          unless res.stack
+            return callback new KodingError 'Failed to generate stack'
+
+          { stack: generatedStack } = res
+          stackId = generatedStack.getId()
+          provider = (generatedStack.getAt 'config.requiredProviders')[0]
+          variables = flattenPayload payload
+
+          Kloud = require './kloud'
+          Kloud.buildStack client, { stackId, provider, variables }, callback

   verify$: permit


Match fontkit output for standard fonts

Match fontkit output for standard fonts

--- lib/font/standard.coffee
+++ lib/font/standard.coffee
@@ -5,7 +5,7 @@
 class StandardFont extends PDFFont
   constructor: (@document, @name, @id) ->
     @font = new AFMFont STANDARD_FONTS[@name]()
-    {@ascender,@decender,@bbox,@lineGap} = @font
+    {@ascender,@descender,@bbox,@lineGap} = @font

   embed: ->
     @dictionary.data =
@@ -20,10 +20,16 @@
     encoded = @font.encodeText text
     glyphs = @font.glyphsForString '' + text
     advances = @font.advancesForGlyphs glyphs
+    positions = []
     for glyph, i in glyphs
-      advances[i] -= @font.widthOfGlyph glyph
+      positions.push
+        xAdvance: advances[i]
+        yAdvance: 0
+        xOffset: 0
+        yOffset: 0
+        advanceWidth: @font.widthOfGlyph glyph

-    return [encoded, advances]
+    return [encoded, positions]

   widthOfString: (string, size) ->
     glyphs = @font.glyphsForString '' + string


Heroku style bug fix

Heroku style bug fix

--- config.coffee
+++ config.coffee
@@ -32,7 +32,7 @@

     stylesheets:
       joinTo:
-        'styles/app.css': /^(app|vendor|bower_components)/
+        'styles/app.css': /^(app|bower_components|vendor\/(?!node))/
       order:
         before: ['bower_components/normalize-css/normalize.css']


Improved error handling.

Improved error handling.

--- app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_form.js.coffee
@@ -10,9 +10,10 @@
       @el.prepend( @form )

     # trigger change to rebuild shown/hidden item and update sub selections
-    @form.find('input').trigger('change')
-    @form.find('textarea').trigger('change')
-    @form.find('select').trigger('change')
+    if typeof @form is 'object'
+      @form.find('input').trigger('change')
+      @form.find('textarea').trigger('change')
+      @form.find('select').trigger('change')

   html: =>
     @form.html()


in confirmBulkAllocations, create allocation, even if negative, but not if zero

--- app/components/review-bulk-allocation-page/review-bulk-allocation-page.coffee
+++ app/components/review-bulk-allocation-page/review-bulk-allocation-page.coffee
@@ -79,7 +79,7 @@
         membershipParams = {group_id: groupId, email: newMember.email.toLowerCase()}
         Records.memberships.remote.create(membershipParams).then (data) ->
           newMembership = data.memberships[0]
-          if newMember.allocation_amount > 0
+          if newMember.allocation_amount != 0
             allocationParams = {groupId: groupId, userId: data.users[0].id, amount: newMember.allocation_amount}
             allocation = Records.allocations.build(allocationParams)
             allocation.save().then ->
@@ -93,7 +93,7 @@

       _.each $scope.existingMembers, (existingMember) ->
         existingMember.status = 'pending'
-        if existingMember.allocation_amount > 0
+        if existingMember.allocation_amount != 0
           params = {groupId: groupId, userId: existingMember.id, amount: existingMember.allocation_amount}
           allocation = Records.allocations.build(params)
           allocation.save().then ->


Remove old API artifacts

Remove old API artifacts

--- lib/jedi-provider.coffee
+++ lib/jedi-provider.coffee
@@ -4,9 +4,7 @@
 readline = require('readline')

 class JediProvider
-	id: 'autocomplete-plus-python-jedi'
 	selector: '.source.python'
-	providerblacklist: null

 	inclusionPriority: 10
 	excludeLowerPriority: true
@@ -28,11 +26,6 @@
 			@rl.close()

 	constructor: ->
-		@providerblacklist =
-			'autocomplete-plus-fuzzyprovider': '.source.python'
-			'autocomplete-plus-symbolprovider': '.source.python'
-			'autocomplete-plus-jedi': '.source.python'
-
 		projectPath = atom.project.getPath()
 		command = "python"
 		@proc = spawn(command, [ __dirname + '/jedi-cmd.py', projectPath ])


Removed unnecessary brackets.

Removed unnecessary brackets.

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -33,27 +33,27 @@
         @ignoreErrors = ''
       do @initCmd
     atom.config.observe 'linter-pylama.usePylint', =>
-      usePyLint = atom.config.get('linter-pylama.usePylint')
+      usePyLint = atom.config.get 'linter-pylama.usePylint'
       @usePyLint = if usePyLint then 'pylint' else ''
       do @initCmd
     atom.config.observe 'linter-pylama.useMccabe', =>
-      useMcCabe = atom.config.get('linter-pylama.useMccabe')
+      useMcCabe = atom.config.get 'linter-pylama.useMccabe'
       @useMcCabe = if useMcCabe then 'mccabe' else ''
       do @initCmd
     atom.config.observe 'linter-pylama.usePep8', =>
-      usePEP8 = atom.config.get('linter-pylama.usePep8')
+      usePEP8 = atom.config.get 'linter-pylama.usePep8'
       @usePEP8 = if usePEP8 then 'pep8' else ''
       do @initCmd
     atom.config.observe 'linter-pylama.usePep257', =>
-      usePEP257 = atom.config.get('linter-pylama.usePep257')
+      usePEP257 = atom.config.get 'linter-pylama.usePep257'
       @usePEP257 = if usePEP257 then 'pep257' else ''
       do @initCmd
     atom.config.observe 'linter-pylama.usePyflakes', =>
-      usePyFlakes = atom.config.get('linter-pylama.usePyflakes')
+      usePyFlakes = atom.config.get 'linter-pylama.usePyflakes'
       @usePyFlakes = if usePyFlakes then 'pyflakes' else ''
       do @initCmd
     atom.config.observe 'linter-pylama.skipFiles', =>
-      skipFiles = atom.config.get('linter-pylama.skipFiles')
+      skipFiles = atom.config.get 'linter-pylama.skipFiles'
       if skipFiles
         @skipFiles = "--skip #{skipFiles}"
       else
@@ -61,16 +61,16 @@
       do @initCmd

     atom.config.observe 'linter-pylama.executablePath', =>
-      pylamaVersion = atom.config.get('linter-pylama.pylamaVersion')
+      pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
       if pylamaVersion is 'external'
         @enable = false
         @pylamaPath = atom.config.get 'linter-pylama.executablePath'
         exec "#{@pylamaPath} --version", @executionCheckHandler

     atom.config.observe 'linter-pylama.pylamaVersion', =>
-      pylamaVersion = atom.config.get('linter-pylama.pylamaVersion')
+      pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
       if pylamaVersion is 'internal'
-        @pylamaPath = path.join(path.dirname(__dirname), 'bin', 'pylama.py')
+        @pylamaPath = path.join path.dirname(__dirname), 'bin', 'pylama.py'
         @enabled = true
         @pylamaVersion = pylamaVersion
         do @initCmd


Teamwork: Added loading for playgrounds load.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkdashboard.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkdashboard.coffee
@@ -41,8 +41,11 @@
       title       : "Start your session now!"

     manifests     = @getDelegate().playgroundsManifest
+
+  createPlaygrounds: (manifests) ->
     manifests.forEach (manifest) =>
-      @playgrounds.addSubView new KDCustomHTMLView
+      @setClass "ready"
+      @playgrounds.addSubView view = new KDCustomHTMLView
         cssClass  : "tw-playground-item"
         partial   : """

@@ -101,8 +104,7 @@

+
Loading Playgrounds...

         {{> @playgrounds}}

     """
-
-


Have parseLine and parseLines functions

Have parseLine and parseLines functions

Have not tested at all, no idea if it will even "compile".

--- lib/python-indent.coffee
+++ lib/python-indent.coffee
@@ -42,16 +42,123 @@

     # Get base variables
     row = editor.getCursorBufferPosition().row
-    previousLine = editor.buffer.lineForRow(row - 1)
+    col = editor.getCursorBufferPosition().column
+
+    # What does this line do?
     return unless row

-    # Based on configuration, branch to corresponding logic
-    if (matchOpeningDelimiter = PythonIndent.openingDelimiterIndentRegex.exec previousLine) isnt null
-      PythonIndent.indentOnOpeningDelimiter(editor, row, previousLine, matchOpeningDelimiter)
-    else if PythonIndent.hangingIndentRegex.test previousLine
-      PythonIndent.indentHanging(editor, row, previousLine)
-    else if PythonIndent.openingDelimiterUnindentRegex.test previousLine
-      PythonIndent.unindentOnOpeningDelimiter(editor, row, previousLine)
+    PythonIndent.indentCorrectly(editor, row, col)
+
+
+  indentCorrectly: (editor, row, col) ->
+    # Parse the entire file up to the current point, keeping track of brackets
+    lines = editor.getTextInBufferRange([[0,0], [row, col]]).split('\n')
+
+    stacks = PythonIndent.parseLines(lines)
+
+    bracketStack = stacks[0]
+    parenthesesStack = stacks[1]
+
+    if not bracketStack.length and not parenthesesStack.length
+        return
+
+    # Get the indent column, need to check both bracket and parentheses locations
+    if not bracketStack.length
+        # Only have open parentheses to worry about
+        [..., indentColumn] = parenthesesStack
+        indentColumn = indentColumn[1]
+    else if not parenthesesStack.length
+        # Only have open brackets to worry about
+        [..., indentColumn] = bracketStack
+        indentColumn = indentColumn[1]
+    else
+        # Have both open brackets and open parentheses to worry about
+        [..., bracketIndent] = bracketStack
+        [..., parenthesesIndent] = parenthesesStack
+
+        # Find out which came last, checking rows and then columns
+        useBracket = true
+        if bracketIndent[0] < parenthesesIndent[0]
+            useBracket = false
+        else if bracketIndent[0] == parenthesesIndent[0]
+            if bracketIndent[1] < parenthesesIndent[1]
+                useBracket = false
+
+        if useBracket
+            indentColumn = bracketIndent[1]
+        else
+            indentColumn = parenthesesIndent[1]
+
+    # Get tab length for context
+    tabLength = editor.getTabLength()
+
+    # Calculate soft-tabs from spaces (can have remainder)
+    tabs = (indentColumn / tabLength) - editor.indentationForBufferRow row
+    rem = indentColumn % tabLength
+
+    # If there's a remainder, `editor.buildIndentString` requires the tab to
+    # be set past the desired indentation level, thus the ceiling.
+    tabs = if rem > 0 then Math.ceil tabs else tabs
+
+    # Offset is the number of spaces to subtract from the soft-tabs if they
+    # are past the desired indentation (not divisible by tab length).
+    offset = if rem > 0 then tabLength - rem else 0
+
+    # I'm glad Atom has an optional `column` param to subtract spaces from
+    # soft-tabs, though I don't see it used anywhere in the core.
+    indent = editor.buildIndentString(tabs, column=offset)
+
+    # Set the indent
+    editor.getBuffer().setTextInRange([[row, 0], [row, 0]], indent)
+
+
+  parseLines: (lines) ->
+    # initialize some empty stacks
+    bracketStack = []
+    parenthesesStack = []
+
+    # loop over each line, adding to each stack
+    for i in lines.length
+        line = lines[i]
+        stacks = PythonIndent.parseLine([], [], [], i, 0, line)
+        bracketStack.concat(stacks[0])
+        parenthesesStack.concat(stacks[1])
+
+    # return the stacks as an array
+    return [bracketStack, parenthesesStack]
+
+
+  parseLine: (bracketStack, parenthesesStack, stringDelimiter, row, col, line) ->
+    # base case
+    if not line.length or (not stringDelimiter.length and line[0] == '#')
+        # stacks will be turned as array
+        return [bracketStack, parenthesesStack]
+
+    c = line[0]
+
+    # if stringDelimiter is set, then we are in a string
+    if stringDelimiter.length
+        # if current character is same as string delimiter, then we have exited string
+        if c == stringDelimiter
+            stringDelimiter = []
+    else
+        switch c
+            when '['
+                bracketStack.push([row, col])
+            when ']'
+                bracketStack.pop()
+            when '('
+                parenthesesStack.push([row, col])
+            when ')'
+                parenthesesStack.pop()
+            when '\''
+                stringDelimiter = '\''
+            when '"'
+                stringDelimiter = '"'
+
+    return PythonIndent.parseLine(bracketStack, parenthesesStack,
+                                  stringDelimiter, row, col+1, line[1..])
+

   indentOnOpeningDelimiter: (editor, row, previousLine, matchOpeningDelimiter) ->
     # Get index of opening delimiter.


break out warnings into individual ones

break out warnings into individual ones

--- src/flubr.coffee
+++ src/flubr.coffee
@@ -28,8 +28,14 @@

 module.exports = (robot) ->

-  if not process.env.HUBOT_FLUBR_URL or not process.env.HUBOT_FLUBR_PASS or not process.env.HUBOT_FLUBR_FAIL
-    robot.logger.warning 'Please set the HUBOT_FLUBR_URL, HUBOT_FLUBR_PASS and HUBOT_FLUBR_FAIL environment variables'
+  if not process.env.HUBOT_FLUBR_URL
+    robot.logger.warning 'The HUBOT_FLUBR_URL environment variable not set'
+
+  if not process.env.HUBOT_FLUBR_PASS
+    robot.logger.warning 'The HUBOT_FLUBR_PASS environment variable not set'
+
+  if not process.env.HUBOT_FLUBR_FAIL
+    robot.logger.warning 'The HUBOT_FLUBR_FAIL environment variable not set'

   robot.hear PASS, (msg) ->
     getImage msg, 'pass'


Remove unneeded call to parent

Remove unneeded call to parent

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -166,7 +166,7 @@
   filterPackages: ->
     filterText = @filterEditor.getEditor().getText()
     all = _.map @panelMenu.children('[type=package]'), (item) ->
-      element: $(item).parent()
+      element: $(item)
       text: $(item).text()
     active = fuzzaldrin.filter(all, filterText, key: 'text')
     _.each all, ({element}) -> element.hide()


Remove unused function

Remove unused function

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -23,7 +23,7 @@
           @div class: 'panel-menu-separator', outlet: 'menuSeparator'
           @div class: 'editor-container settings-filter', =>
             @subview 'filterEditor', new EditorView(mini: true, placeholderText: 'Filter packages')
-        @ul class: 'panels-packages nav nav-pills nav-stacked', outlet: 'panelPackages', =>
+        @ul class: 'panels-packages nav nav-pills nav-stacked', outlet: 'panelPackages'
         @div class: 'button-area', =>
           @button class: 'btn btn-default icon icon-link-external', outlet: 'openDotAtom', 'Open ~/.atom'
       @div class: 'panels padded', outlet: 'panels'


are the domains eligible for creation?

are the domains eligible for creation?

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -1,12 +1,13 @@
 jraphical = require 'jraphical'
 module.exports = class JDomain extends jraphical.Module

-  DomainManager = require 'domainer'
-
-  {secure, ObjectId}  = require 'bongo'
-  {Relationship} = jraphical
-  {permit} = require './group/permissionset'
-  Validators = require './group/validators'
+  DomainManager      = require 'domainer'
+  Validators         = require './group/validators'
+  KodingError        = require '../error'
+  {secure, ObjectId} = require 'bongo'
+  {Relationship}     = jraphical
+  {permit}           = require './group/permissionset'
+  JGroup             = require './group'

   @trait __dirname, '../traits/protected'

@@ -94,24 +95,63 @@
         type        : Date
         default     : -> new Date

-  @createDomain: permit 'create domains',
-    success:(client, options={}, callback)->
-      model = new JDomain options
-      model.save (err) ->
-        return callback err if err
-
-        account = client.connection.delegate
-        rel = new Relationship
-          targetId: model.getId()
-          targetName: 'JDomain'
-          sourceId: account.getId()
-          sourceName: 'JAccount'
-          as: 'owner'
-
-        rel.save (err)->
-          return callback err if err
-
-        callback err, model
+  @isDomainEligible: (params, callback)->
+    {delegate, domain} = params
+    return callback new KodingError("Invalid domain.")  unless /\.kd\.io$/.test domain
+
+    match = domain.match /(.*)\.(\w+)\.kd\.io$/
+    return callback new KodingError("Invalid domain.") unless match
+
+    [rest..., prefix, slug] = match
+
+    if slug is delegate.profile.nickname
+      callback null, /^vm[\-]([0-9]+)$/.test prefix
+
+    else
+      JGroup.one {slug}, (err, group)->
+        return callback err  if err
+
+        unless group
+          return callback new KodingError("No group found.")
+
+        delegate.checkPermission group, 'create domains', (err, hasPermission)->
+          return callback err  if err
+          return callback null, no  unless hasPermission
+          callback null, /shared[\-]?([0-9]+)?$/.test prefix
+
+  @createDomain: secure (client, options={}, callback)->
+      {delegate} = client.connection
+
+      JGroup.one {slug:'koding'}, (err, group)->
+        return callback err  if err
+
+        delegate.checkPermission group, 'create domains', (err, hasPermission)->
+          return callback err  if err
+          return callback new KodingError "Access denied"  unless hasPermission
+
+          JDomain.isDomainEligible
+            delegate : delegate
+            domain   : options.domain
+          , (err, isEligible)->
+            return callback err  if err
+            return callback new KodingError "You can't create this domain."  unless isEligible
+
+            model = new JDomain options
+            model.save (err) ->
+              return callback err if err
+
+              account = client.connection.delegate
+              rel = new Relationship
+                targetId: model.getId()
+                targetName: 'JDomain'
+                sourceId: account.getId()
+                sourceName: 'JAccount'
+                as: 'owner'
+
+              rel.save (err)->
+                return callback err if err
+
+              callback err, model

   @isDomainAvailable = (domainName, tld, callback)->
@@ -162,7 +202,7 @@
     JRecurlyCharge.charge client,
       code   : 'domain_abc'
       amount : amount
-      desc   : "Domain registration fee - #{data.domain} (#{data.years} year(s)})"
+      desc   : "Domain registration fee - #{data.domainName} (#{data.years} year(s)})"
     , callback

   bound: require 'koding-bound'


fixing bug where image object was being returned instead of the uri

--- src/giphy.coffee
+++ src/giphy.coffee
@@ -155,13 +155,17 @@
       # for whatever reason istanbul is complaining about this missing else block
       ### istanbul ignore else ###
       if imagesBySize.length > 0
+        image = null
         allowedImages = imagesBySize
           .filter (x) -> x.size <= size

         if allowedImages and allowedImages.length > 0
-          allowedImages[allowedImages.length - 1]
+          image = allowedImages[allowedImages.length - 1]
         else if allowLargerThanMaxSize
-          imagesBySize[0]
+          image = imagesBySize[0]
+
+        if image and image.url
+          image.url

   getUriFromResultData: (data) ->
     if data and data.images


Integrate Geordi v2

Integrate Geordi v2

--- app/lib/analytics.coffee
+++ app/lib/analytics.coffee
@@ -28,8 +28,8 @@
     url: 'http://127.0.0.1:3000/api/events/',
     type: 'POST',
     contentType: 'application/json; charset=utf-8',
-    contentLength: length+2,
-    data: "{"+JSON.stringify(eventData)+"}",
+    contentLength: length,
+    data: JSON.stringify(eventData),
     dataType: 'json'
   }


:lipstick: Refactor element initialization

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -29,13 +29,9 @@
     @activeCellPosition = new Point
     @subscriptions = new CompositeDisposable

-    @setModel(@getDefaultModel())
     @initializeContent()
     @subscribeToContent()
     @subscribeToConfig()
-
-    @updateScreenRows()
-    @setSelectionFromActiveCell()

   initializeContent: ->
     @shadowRoot = @createShadowRoot()
@@ -150,23 +146,27 @@
   subscribeToConfig: ->
     @observeConfig
       'table-edit.undefinedDisplay': (@configUndefinedDisplay) =>
-        @requestUpdate()
-      'table-edit.pageMovesAmount': (@configPageMovesAmount) => @requestUpdate()
+        @requestUpdate() if @attached
+      'table-edit.pageMovesAmount': (@configPageMovesAmount) =>
+        @requestUpdate() if @attached
       'table-edit.minimumRowHeight': (@configMinimumRowHeight) =>
       'table-edit.columnWidth': (@configColumnWidth) =>
       'table-edit.rowHeight': (@configRowHeight) =>
-        @computeRowOffsets()
-        @requestUpdate()
-      'table-edit.rowOverdraw': (@configRowOverdraw) => @requestUpdate()
+        if @table?
+          @computeRowOffsets()
+          @requestUpdate() if @attached
+      'table-edit.rowOverdraw': (@configRowOverdraw) =>
+        @requestUpdate() if @attached

   attach: (target) ->
     target.appendChild(this)

   attachedCallback: ->
+    @buildModel() unless @getModel()?
+    @computeRowOffsets()
+    @mountComponent() unless @bodyComponent?.isMounted()
+    @requestUpdate()
     @attached = true
-    @mountComponent() unless @mounted?
-    @computeRowOffsets()
-    @requestUpdate()

   detachedCallback: ->
     @attached = false
@@ -179,14 +179,9 @@
     @parentNode?.removeChild(this)

   mountComponent: ->
-    @updateScreenRows()
     props = {parentView: this}
     @bodyComponent = React.renderComponent(TableComponent(props), @body)
     @headComponent = React.renderComponent(TableHeaderComponent(props), @head)
-    @mounted = true
-
-  unmountComponent: ->
-    @mounted = false

   showGutter: ->
     @gutter = true
@@ -212,10 +207,10 @@

   getModel: -> @table

-  getDefaultModel: ->
+  buildModel: ->
     model = new Table
     model.addColumn('untitled 0')
-    model
+    @setModel(model)

   setModel: (table) ->
     return unless table?
@@ -235,10 +230,9 @@

     @subscribeToColumn(column) for column in @table.getColumns()

-    if @mounted
-      @updateScreenRows()
-      @setSelectionFromActiveCell()
-      @requestUpdate()
+    @updateScreenRows()
+    @setSelectionFromActiveCell()
+    @requestUpdate()

   unsetModel: ->
     @modelSubscriptions.dispose()


Added rotation properties to animation

Added rotation properties to animation

--- src/animation.coffee
+++ src/animation.coffee
@@ -32,7 +32,7 @@

 	start: (callback) =>

-		@beginProperties = @view.properties
+		@beginProperties = @originalProperties
 		@view._animationTransformOrigin = @origin
 		setTimeout =>
 			@_start callback
@@ -67,10 +67,10 @@
 		# spring(tension, friction, velocity)

 		if curve[..5] == "spring"
-
+
 			if @time
 				console.log "view.animate: ignoring time for spring"
-
+
 			values = parseCurve curve
 			options =
 				tension: values[0]
@@ -78,26 +78,26 @@
 				velocity: values[2]
 				speed: 1/60
 				tolerance: @tolerance or 0.01
-
+
 			@_startSpring options, callback

 			return
-
+
 		@_animate @endProperties, curve, time, =>
 			@_end callback

 	_startSpring: (options, callback) =>
-
+
 		@spring = new Spring options

 		beginState = {}
 		deltas = {}
-
+
 		for k, v of @endProperties
 			deltas[k] = (@endProperties[k] - @beginProperties[k]) / 100.0
 			beginState[k] = @beginProperties[k]
-
+
 		run = =>

 			# If the spring stopped moving we can stop
@@ -141,7 +141,7 @@
 		, time

 		for k, v of properties
-			if k in ["rotation", "opacity", "scale", "x", "y", "z", "width", "height"]
+			if k in ["rotateX", "rotateY", "rotateZ", "opacity", "scale", "x", "y", "z", "width", "height"]
 				@view[k] = properties[k]

 	reverse: ->
@@ -152,4 +152,4 @@
 		for p in PROPERTIES
 			options[p] = @[p]

-		return new Animation options+		return new Animation options


undo location dist

undo location dist

--- app/assets/javascripts/controllers/home/home_controller.coffee
+++ app/assets/javascripts/controllers/home/home_controller.coffee
@@ -14,7 +14,7 @@
         jQuery.ajax
           url: '/api/home/top_menu_items?latitude=' + data.loc[0] +
             '&longitude=' + data.loc[1] +
-            '&range=10000000000000&limit=25&sortOrder=score-desc',
+            '&range=5&limit=25&sortOrder=score-desc',
           success: (response) ->
             $scope.menuItems = response['data']
             $scope.$apply()


fix projects sort

fix projects sort

--- app/modules/projects/components/sort-projects.directive.coffee
+++ app/modules/projects/components/sort-projects.directive.coffee
@@ -16,7 +16,7 @@
             index = itemEl.index()

             sorted_project_ids = _.map(scope.projects.toJS(), (p) -> p.id)
-            sorted_project_ids = _.without(sorted_project_ids, project.id)
+            sorted_project_ids = _.without(sorted_project_ids, project.get("id"))
             sorted_project_ids.splice(index, 0, project.get('id'))

             sortData = []
@@ -35,4 +35,4 @@

     return directive

-angular.module("taigaProjects").directive("tgSortProjects", ["tgProjectsService", SortProjectsDirective])
+angular.module("taigaProjects").directive("tgSortProjects", ["tgCurrentUserService", SortProjectsDirective])


cleanup

cleanup

--- lib/watch-sidebar.coffee
+++ lib/watch-sidebar.coffee
@@ -18,7 +18,7 @@
         @toolbar.classList.add('toolbar', 'block')

         languageDisplay = document.createElement('button')
-        languageDisplay.classList.add('btn', 'icon', 'icon-sync', 'language')
+        languageDisplay.classList.add('btn', 'icon', 'icon-sync')
         languageDisplay.innerText = "Watch: #{@language}"
         languageDisplay.onclick = =>
             editor = atom.workspace.getActiveTextEditor()


Remove contest method from choice

Remove contest method from choice

--- lib/Choice.coffee
+++ lib/Choice.coffee
@@ -50,8 +50,6 @@
     @onBranch @, branch, callback

     branch
-
-  contest: ->

   get: (name) ->
     return @attributes[name] if @attributes[name] isnt 'undefined'


cover index creation

cover index creation

--- src/index.coffee
+++ src/index.coffee
@@ -1,7 +1,8 @@
 # create global index if in browser
+index =
+  matrix: require "./matrix"
+  lu:     require "./luDecomposition"
+  ud:     require "./udDecomposition"

 if window?
-  window.m4th =
-    matrix: require "./matrix"
-    lu:     require "./luDecomposition"
-    ud:     require "./udDecomposition"
+  window.m4th = index


Support for controller's components

Support for controller's components

--- src/index.coffee
+++ src/index.coffee
@@ -146,6 +146,10 @@
                         moduleInfo.url + p

             app.controller ctrl
+
+            for comp in ctrl::components or []
+                comp::name = "#{ctrl::name}:#{comp::name}"
+                app.component comp

         for name in moduleInfo.model
             words =


fix(index): call `.runBeforeDom()` on modules after local settings are ready

--- src/index.coffee
+++ src/index.coffee
@@ -14,18 +14,17 @@
 ]

-for m in modules
-  if m.defineSettings
-    m._definedKeys = settings.add m.defineSettings()
-
-  m.runBeforeDom? settings.get m._definedKeys
-
-
 domReady = new Promise ( resolve ) ->
   window.document.addEventListener "DOMContentLoaded", resolve

-# Load local settings initially. This is enough to get rolling.
+
 settingsReady = settings.fetchLocal()
+.then ->
+  for m in modules
+    if m.defineSettings
+      m._definedKeys = settings.add m.defineSettings()
+
+    m.runBeforeDom? settings.get m._definedKeys

 Promise.all [ settingsReady, domReady ]


Add 'options.secure' and default to false

--- src/index.coffee
+++ src/index.coffee
@@ -6,7 +6,6 @@
 Promise = require "Promise"
 express = require "express"
 isType = require "isType"
-https = require "https"
 path = require "path"
 now = require "performance-now"
 log = require "log"
@@ -26,14 +25,27 @@
     req.parts = req.path.split "/"
     req.next()

-  port = options.port or process.env.PORT or 4443
-  server = https.createServer ssl(), app
+  server =
+    if options.secure
+    then require("https").createServer ssl(), app
+    else require("http").createServer app

-  deferred = Promise.defer()
-  server.listen port, deferred.resolve
-  app.onceReady = (callback) ->
-    deferred.promise.then ->
-      callback "https://" + ip.address() + ":" + port
+  port =
+    options.port or
+    process.env.PORT or
+    if options.secure then 4443 else 8000
+
+  app.onceReady = do ->
+
+    {promise, resolve} = Promise.defer()
+    server.listen port, resolve
+
+    protocol = if options.secure then "https" else "http"
+    url = protocol + "://" + ip.address() + ":" + port
+
+    return (callback) ->
+      promise.then ->
+        callback url

   if options.compress
     app.use compression()


Send PING if there's no activity on the socket after 60 seconds.

--- irc.coffee
+++ irc.coffee
@@ -117,6 +117,11 @@
 		@send 'PASS', @opts.password if @opts.password
 		@send 'NICK', @opts.nick
 		@send 'USER', @opts.nick, '0', '*', 'An irc5 user'
+		@socket.setTimeout 60000, @onTimeout
+
+	onTimeout: =>
+    @send 'PING', +new Date
+    @socket.setTimeout 60000, @onTimeout

 	onData: (pdata) ->
 		@data = @data.concat pdata
@@ -142,6 +147,7 @@

 	onError: (err) ->
 	  console.log "error", err
+	  @socket.setTimeout 0, @onTimeout
 	  @socket.end()
 	  @emit 'disconnect'


Animation: workaround for specific issue, remove after fixing issue or next week.

--- engine/animation.coffee
+++ engine/animation.coffee
@@ -278,7 +278,9 @@
                     target[prop] = v[0]
                 else
                     p = target[prop]
-                    for j in [0...v.length]
+                    # TODO: workaround for specific issue
+                    # remove after fixing issue or next week
+                    for j in [0...(p.anim_length or v.length)]
                         p[j] = v[j]
                     if is_quat
                         quat.normalize p, p


now create infowindows on the fly

now create infowindows on the fly

--- vendor/assets/javascripts/gmaps/google/builders/marker.coffee
+++ vendor/assets/javascripts/gmaps/google/builders/marker.coffee
@@ -26,19 +26,18 @@
   constructor: (@args, @provider_options = {}, @internal_options = {})->
     @before_init()
     @create_marker()
-    @create_infowindow()
+    @create_infowindow_on_click()
     @after_init()

   build: ->
-    new(@model_class())(@serviceObject, @infowindow)
+    @object = new(@model_class())(@serviceObject, @infowindow)

   create_marker: ->
     @serviceObject = new(@primitives().marker)(@marker_options())

   create_infowindow: ->
     return null unless _.isString @args.infowindow
-    @infowindow = new(@primitives().infowindow)({content: @args.infowindow })
-    @bind_infowindow()
+    new(@primitives().infowindow)({content: @args.infowindow })

   marker_options: ->
     coords = @_randomized_coordinates()
@@ -49,13 +48,18 @@
       shadow:   @_get_picture('shadow')
     _.extend @provider_options, base_options

-  bind_infowindow: ->
+  create_infowindow_on_click: ->
     @addListener 'click', @infowindow_binding

   infowindow_binding: =>
+    @constructor.CURRENT_INFOWINDOW.close() if @_should_close_infowindow()
     @panTo()
-    @constructor.CURRENT_INFOWINDOW.close() if @_should_close_infowindow()
+    @infowindow ?= @create_infowindow()
+
+    return unless @infowindow?
+
     @infowindow.open( @getServiceObject().getMap(), @getServiceObject())
+    @object.infowindow ?= @infowindow
     @constructor.CURRENT_INFOWINDOW = @infowindow

   panTo: ->


improves items creation.

improves items creation.

--- app/assets/javascripts/backend/budget.js.coffee
+++ app/assets/javascripts/backend/budget.js.coffee
@@ -85,8 +85,8 @@
     $("a[data-association='item']").each ->
       $(this).click()
       new_item = $(this).closest('td').prev()
-      if new_item.prev().hasClass('homogeneous')
-        new_item.addClass('homogeneous')
+      if new_item.parent().find("input:checked[id$='homogeneous_values']").is(':input')
+        new_item.find("input[id$='quantity']").addClass('homogeneous')
         updateItems()
       new_item.attr('data-support-destroy', support_destroy_id)
     # adds total calculation cells


add console log to the server

add console log to the server

--- frontend/config/server.coffee
+++ frontend/config/server.coffee
@@ -68,7 +68,9 @@

   # POST
   # Create a transoporter
-  app.post '/api/v1/transporters', (req, res) ->
+  app.post '/api/v1/transporter', (req, res) ->
+    console.log('trasporter created... yay!!!')
+    console.log('parameters: >> ' + req.params)
     res.json {
       name: 'Pepito',
       document_number: '123456789',


remove boilerplate code

remove boilerplate code

--- lib/clever.coffee
+++ lib/clever.coffee
@@ -285,29 +285,20 @@
     Event    : Event
     Query    : Query

-module.exports.handle_errors = handle_errors
 Clever.handle_errors = handle_errors

 Clever.me = (token, url_base..., cb) ->
-  promise = Q.defer()
   url_base = url_base[0]
   url_base ?= API_BASE
   auth = {token: token} if _.isString(token)
-  auth ?= {token: token.access_token}
+  auth ?= {token: token.access_token or token.token}
   opts =
     method: 'get'
     ca: certs
     json: true
     uri: "#{url_base}/me"
   apply_auth auth, opts
-  quest opts, (err, resp, body) ->
-    handle_errors resp, body, (err, resp, body) ->
-      promise.reject err if err
-      promise.resolve body?.data
-      return cb?(err) if err
-      cb?(err, body?.data)
-  return promise.promise
-
+  make_request opts, cb

 Clever.OAuth = class OAuth
   @url_base: CLEVER_BASE


Calculate buffer size from distance in km

--- app/assets/javascripts/widgets/direction.coffee
+++ app/assets/javascripts/widgets/direction.coffee
@@ -191,8 +191,17 @@
       coordinates = route.path.map (p) ->
         new jsts.geom.Coordinate p.latitude, p.longitude

+      centerLat = route.bounds.getCenter().lat()
+      centerLng = route.bounds.getCenter().lng()
+      # in km
+      degreeInKm = haversineDistance \
+        Math.round(centerLat) - 0.5,
+        centerLng,
+        Math.round(centerLat) + 0.5,
+        centerLng
+
       line_string = factory.createLineString coordinates
-      buffer = line_string.buffer 0.1 # TODO: Calculate buffer from distance
+      buffer = line_string.buffer distance / degreeInKm

       paths = buffer.shell.points.map (p) ->
         new google.maps.LatLng p.x, p.y
@@ -239,6 +248,18 @@
   false
 , 3000

+
+haversineDistance = (lat1, lon1, lat2, lon2) ->
+  R = 6371 # km
+  dlat = (lat2 - lat1) * Math.PI / 180.0
+  dlon = (lon2 - lon1) * Math.PI / 180.0
+  lat1 = lat1 * Math.PI / 180.0
+  lat2 = lat2 * Math.PI / 180.0
+  a = Math.sin(dlat / 2) * Math.sin(dlat/2) +
+      Math.sin(dlon / 2) * Math.sin(dlon/2) * Math.cos(lat1) * Math.cos(lat2)
+  c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
+  R * c
+
 searchRelics = ->
   debouncedSearchRelics()
   false


added timestamp methods

added timestamp methods

--- app/coffee/drm-timestamps/drm-timestamps.coffee
+++ app/coffee/drm-timestamps/drm-timestamps.coffee
@@ -125,6 +125,13 @@

         date = _parseDate item
         time = _parseTime item
+
         return new Date date.year, date.month, date.date, time.hour, time.minute, time.second

+    prettifyDate: (date) ->
+        console.log date
+
+    createTimestamp: (date) ->
+        console.log date
+
 new DrmTimeStamps()


base directive can render block content as template

--- src/compiler/directives/base.coffee
+++ src/compiler/directives/base.coffee
@@ -1,7 +1,11 @@
+marked = require 'marked'
+
 class Base
   # @param options {Hash} The options object given to a mustache helper.
   # @param options.root {Path} The project root.
+  # @param options.hbs {Path} The Handlebars instance used to render this template.
   constructor: (@options) ->
+    @hbs = @options.hbs
     @root = @options.root
     # Is this directive being invoked as a block helper?
     @isBlock = !!@options.fn
@@ -10,9 +14,17 @@
   # object as the context for the block content.
   #
   # @return {String}
-  contentString: (context=null) ->
+  contentString: (context={}) ->
     if fn = @options.fn
       fn(context || this)
+
+  # Recursively render the block content as template.
+  # @return {HTMLString}
+  content: (context={}) ->
+    content = @contentString()
+    template = @hbs.compile(content)
+    hbsOutput = template(context)
+    marked(hbsOutput,{})

   process: ->
     throw "abstract"


add new jsbeautify opts #55 comma first

add new jsbeautify opts #55 comma first

--- lib/config.coffee
+++ lib/config.coffee
@@ -55,6 +55,10 @@
     title: 'e4x style'
     type: 'boolean'
     default: false
+  comma_first:
+    title: 'comma first'
+    type: 'boolean'
+    default: false
   ignore_files:
     type: 'array'
     default: ['.jshintrc']


Remove survey message

Remove survey message

--- components/layout/client.coffee
+++ components/layout/client.coffee
@@ -32,7 +32,6 @@
   setupAnalytics()
   syncAuth()
   initShortCuts()
-  showSurveyMessage() unless isMobile()
   initLoggedOutCta() unless sd.CURRENT_USER?.id

 isMobile = ->
@@ -164,15 +163,6 @@

   initNightMode()

-showSurveyMessage = ->
-  if sd.CURRENT_USER and (sd.CURRENT_USER.following_count > 1 or sd.CURRENT_USER.channel_count > 1 )
-    model = new Backbone.Model
-      id: 'survey_message'
-      title: "We have questions, you have answers."
-      body: "Want to help us make Are.na better? Fill out this quick survey. As thanks, we will give away premium accounts to a few random responders. Thanks!"
-      type: 'announcement'
-    new MessageView container: $('#message-container'), model: model
-
 showAnnouncements = (announcements) ->
   # stub


Propagate the close event

Propagate the close event

--- components/modalize/view.coffee
+++ components/modalize/view.coffee
@@ -62,12 +62,12 @@
     this

   escape: (e) =>
-    @close() if e.which is 27
+    @close(null, e) if e.which is 27

   maybeClose: (e) ->
-    @close() if $(e.target).hasClass('js-modalize-backdrop')
+    @close(null, e) if $(e.target).hasClass('js-modalize-backdrop')

-  close: (callback) ->
+  close: (callback, e) ->
     $(window).off 'keyup.modalize'
     @scrollbar.reenable()
     @trigger 'closing'
@@ -75,4 +75,4 @@
       @subView?.remove?()
       @remove()
       callback?()
-      @trigger 'closed'
+      @trigger 'closed', e


Removed some comments that didn't make sense in context.

--- src/lists.coffee
+++ src/lists.coffee
@@ -26,8 +26,7 @@
   if p >= v.length then return nil
   # Annoying, but since lists are represented as nested arrays, they
   # have to be intercepted first.  The use of duck-typing here is
-  # frustrating, but I suppose the eventual runtime will be doing
-  # something like this anyway for base types.
+  # frustrating.
   item = if pairp(v[p]) then v[p] else if vectorp(v[p]) then vectorToList(v[p]) else v[p]
   cons(item, vectorToList(v, p + 1))

@@ -40,10 +39,6 @@
   return v if nilp l
   v.push if pairp (car l) then listToVector(car l) else (car l)
   listToVector (cdr l), v
-
-# This is the simplified version. It can't be used stock with reader,
-# because read() returns a rich version decorated with extra
-# information.

 metacadr = (m) ->
   seq = m.match(/c([ad]+)r/)[1].split('')


test fix cozydb.save

test fix cozydb.save

--- test/autostop.coffee
+++ test/autostop.coffee
@@ -461,7 +461,7 @@
                     Event: description: 'access the events'
                 isStoppable: true

-            app.save done
+            Application.create app, done

         it "Then I wait for 4 minutes", ->
             @sandbox.clock.tick 4 * minute


Teamwork: Terminal pane size is 50% again.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -37,7 +37,7 @@
               type        : "split"
               options     :
                 direction : "horizontal"
-                sizes     : [ "100%", null ]
+                sizes     : [ "50%", null ]
                 splitName : "InnerSplit"
               views       : [
                 {


chat: almost working chat

chat: almost working chat

- awaits properly-implemented wildcard event emitter

--- client/landing/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Chat.kdapplication/AppController.coffee
@@ -10,24 +10,30 @@

     notificationController = KD.getSingleton 'notificationController'
     notificationController.on 'chatRequest', @bound 'handleChatRequest'
-    notificationController.on 'chatRoomOpen', @bound 'handleChatRoomOpen'
+    notificationController.on 'chatOpen', @bound 'handleChatOpen'

-  handleChatRoomOpen:({publishingToken, subscriptionToken})->
+    @channels       = {}
+    @conversations  = {}

+  handleChatOpen:({routingKey, bindingKey, publicName})->
+    channel = KD.remote.mq.setP2PKeys publicName, { routingKey, bindingKey }, 'secret'

   create:(invitees, callback)->
     {JChatConversation} = KD.remote.api
     JChatConversation.create invitees, (err, chatConversation)=>
       return callback err  if err
-      chatChannel = @subscribe chatConversation.publicName
-      callback null, chatChannel
+
+      @handleChatRequest {
+        invitee     : KD.whoami().profile.nickname
+        publicName  : chatConversation.publicName
+      }, callback

   subscribe:(publicName)->
     options = { serviceType: 'chat', isP2P: yes, exchange: 'chat' }
     channel = KD.remote.subscribe publicName, options
     channel.on 'message', @bound 'handleChatMessage'

-  handleChatRequest:(request)->
+  handleChatRequest:(request, callback)->
     {invitee, publicName} = request

     if invitee isnt KD.whoami().profile.nickname
@@ -35,6 +41,8 @@

     chatChannel = @subscribe publicName

-    console.log {chatChannel}
+    @channels[publicName] = chatChannel
+
+    callback? null, chatChannel

   handleChatMessage:-> console.log arguments


Add pending padding tests

Add pending padding tests

--- test/tests/TextLayerTest.coffee
+++ test/tests/TextLayerTest.coffee
@@ -92,3 +92,12 @@
 				text.size.should.eql width: 400, height: 2750
 				done()
 			text.text = longText
+
+	describe "Padding", ->
+		it "should have no padding initially"
+		it "should all padding when given a numeric value"
+		it "should set horizontal padding"
+		it "should set vertical padding"
+		it "should set top, left, right and left padding"
+		it "should keep padding 0 when not explicity set"
+		it "should have a preference for more specific padding definitions"


Automatically submit to ladder the first time for ladder levels

--- app/views/play/level/tome/CastButtonView.coffee
+++ app/views/play/level/tome/CastButtonView.coffee
@@ -113,7 +113,10 @@
       @playSound 'cast-end', 0.5 unless @options.level.isType('game-dev')
       # Worked great for live beginner tournaments, but probably annoying for asynchronous tournament mode.
       myHeroID = if me.team is 'ogres' then 'Hero Placeholder 1' else 'Hero Placeholder'
-      if @autoSubmitsToLadder and not e.world.thangMap[myHeroID]?.errorsOut and not me.get('anonymous')
+      shouldAutoSubmit = @autoSubmitsToLadder or (@options.level.isType('ladder') and not @options.session.get('submitDate') and not @autosubmittedOnce)
+      shouldAutoSubmit &&= not e.world.thangMap[myHeroID]?.errorsOut and not me.get('anonymous')
+      if shouldAutoSubmit
+        @autosubmittedOnce = true
         _.delay (=> @ladderSubmissionView?.rankSession()), 1000 if @ladderSubmissionView
     @hasCastOnce = true
     @updateCastButton()


Return Promise of Data on Save/Update

Return Promise of Data on Save/Update

--- src/model.coffee
+++ src/model.coffee
@@ -176,7 +176,7 @@
         Data.replace = (data) ->
           _data.data = _.extend(_data.data, data)
           store.setItem("#{name}.data", _data.data)
-          return _data
+          return _data.data

       else # is Model[]
         _data =
@@ -199,7 +199,7 @@
         #
         # @param {Object} data New data
         # @param {Object} criteria Used to find entry
-        # @return {Object} The complete data representation
+        # @return {Object} The updated entry
         ###
         Data.updateEntry = (data, criteria) ->
           hit = _.findWhere(_data.all, criteria)
@@ -207,11 +207,12 @@
             # update existing entry
             hit = _.extend(hit, data)
           else
-            # add existing entry (on top of list)
+            # add new entry (on top of list)
             _data.all.unshift(data)

           store.setItem("#{name}.all", _data.all)
-          return _data
+
+          return if hit? then hit else data

         ###
         # @method Remove Single Entry from Collection
@@ -288,7 +289,7 @@
           data = config.transformResponse(data, 'one')

           Data.updateEntry data, config.matchingCriteria(data)
-          return data
+          return $q.when(data)

       # #### Destroy some Entry
       ###
@@ -321,9 +322,7 @@
         _url = "#{config.baseUrl}#{config.url}"
         _url += "/#{entry.id}" unless IS_SINGLETON

-        console.log 'saving', config.baseUrl, config.url, entry.id
-
-        $http.post(_url, JSON.stringify(entry))
+        return $http.post(_url, JSON.stringify(entry))
         .then ({status, data}) ->
           data = config.transformResponse(data, 'save')


fix conversation sorting by timestamp

fix conversation sorting by timestamp

put most recent at top.

--- src/model.coffee
+++ src/model.coffee
@@ -15,15 +15,24 @@
     object = id: id, name: name
     @identitiesById[id] = object
   conversationsSort: () ->
-    timestampDesc = (a, b) -> a.ts < a.ts
-    @conversations = @conversations.sort timestampDesc
+    timestampDesc = (a, b) -> (parseInt a.ts) > (parseInt b.ts)
+    @conversations = (@conversations.sort timestampDesc).reverse()
+    @conversations.forEach (c) ->
+      console.log new Date(parseInt c.ts).toISOString(), c.name
   conversationAdd: (id, name, participants, ts) ->
-    object = id: id, name: name, participants: participants, timestamp: ts
+    object = id: id, name: name, participants: participants, ts: ts
     @conversations.push object
     @conversationsSort()
     @conversationsById[id] = object
+  conversationUpdateTs: (id, ts) =>
+    @conversations.forEach (c) ->
+      if c.id == id
+        c.ts = ts
+    @conversationsSort()
   messageAdd: (event) =>
     id = event.conversation_id.id
+    ts = event.timestamp
+    @conversationUpdateTs id, ts
     if not @conversationCurrent then @conversationCurrent = id
     @messagesByConversationId[id] = @messagesByConversationId[id] || []
     @messagesByConversationId[id].push event


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -10,4 +10,4 @@
     Object.extend(I.clampData, data)

   clampToBounds: (bounds) ->
-    bou+    bounds ||=


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -1,5 +1,5 @@
 ###*
-The Clampable module provides h to tween object properties.
+The Clampable module provides helper methods to tween object properties.

 @name Clampable
 @module


remove unused things in nodes

remove unused things in nodes

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -475,14 +475,6 @@
       else if node instanceof Code
         node.klass    = name
         node.context  = name if node.bound
-
-  # Ensure that all functions bound to the instance are proxied in the
-  # constructor.
-  addBoundFunctions: (o) ->
-    if @boundFuncs.length
-      for bvar in @boundFuncs
-        lhs = (new Value (new Literal "this"), [new Access bvar]).compile o
-        @ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"

   # Merge the properties from a top-level object as prototypal properties
   # on the class.
@@ -1004,28 +996,6 @@
 # Constants
 # ---------

-UTILITIES =
-
-  # Correctly set up a prototype chain for inheritance, including a reference
-  # to the superclass for `super()` calls, and copies of any static properties.
-  extends: -> """
-    function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }
-  """
-
-  # Create a function bound to the current value of "this".
-  bind: -> '''
-    function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
-  '''
-
-  # Discover if an item is in an array.
-  indexOf: -> """
-    [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
-  """
-
-  # Shortcuts to speed up the lookup time for native functions.
-  hasProp: -> '{}.hasOwnProperty'
-  slice  : -> '[].slice'
-
 # Levels indicate a node's position in the AST. Useful for knowing if
 # parens are necessary or superfluous.
 LEVEL_TOP    = 1  # ...;
@@ -1041,34 +1011,7 @@
 IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
 IDENTIFIER = /// ^ #{IDENTIFIER_STR} $ ///
 SIMPLENUM  = /^[+-]?\d+$/
-METHOD_DEF = ///
-  ^
-    (?:
-      (#{IDENTIFIER_STR})
-      \.prototype
-      (?:
-        \.(#{IDENTIFIER_STR})
-      | \[("(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*')\]
-      | \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
-      )
-    )
-  |
-    (#{IDENTIFIER_STR})
-  $
-///

 # Is a literal value a string?
 IS_STRING = /^['"]/

-# Utility Functions
-# -----------------
-
-# Helper for ensuring that utility functions are assigned at the top level.
-utility = (name) ->
-  ref = "__#{name}"
-  Scope.root.assign ref, UTILITIES[name]()
-  ref
-
-multident = (code, tab) ->
-  code = code.replace /\n/g, '$&' + tab
-  code.replace /\s+$/, ''


trying to display only available slots areas

--- app/assets/javascripts/bookings/appointments.js.coffee
+++ app/assets/javascripts/bookings/appointments.js.coffee
@@ -27,7 +27,35 @@
         dataType: 'json'
         success: (doc)->
           events = []
-          for slot in doc.appointments
+          allAppointments = doc.appointments
+          slots = allAppointments.filter (item)->
+                    item if item.recurring
+          appointments = allAppointments.filter (item)->
+                    item if ! item.recurring
+
+          #for slot in slots
+          i=0
+          while i < slots.length # length changes while going through the loop
+            console.log slots.length
+            slot = slots[i]
+            i++
+            for appointment in appointments
+              if appointment.start >= slot.start && appointment.end <= slot.end
+                first = jQuery.extend({}, slot)
+                second = jQuery.extend({}, slot)
+                first.start = slot.start
+                first.end = appointment.start
+                second.start = appointment.end
+                second.end = slot.end
+                slots.push first if appointment.start > slot.start
+                slots.push second if appointment.end < slot.end
+                slots.splice(slots.indexOf(slot), 1)
+                appointments.splice(appointments.indexOf(appointment), 1)
+                break
+
+          # allAppointments = appointments.concat slots
+
+          for slot in allAppointments
             events.push
               title: slot.title.toString()
               start: moment(slot.start).format("MMM DD, YYYY HH:mm Z")


#90 get mapped data value

#90 get mapped data value

--- app/assets/javascripts/oxalis/model/binary/cube.coffee
+++ app/assets/javascripts/oxalis/model/binary/cube.coffee
@@ -160,6 +160,12 @@
     return null

+  getMappingByPosition : ( position ) ->
+
+    return @getMappingByZoomedAddress(
+      @positionToZoomedAddress( position ))
+
+
   isBucketRequestedByZoomedAddress : (address) ->

     if address[3] >= @ZOOM_STEP_COUNT
@@ -357,10 +363,16 @@
     { bucket, voxelIndex} = @getBucketAndVoxelIndex( voxel, 0 )

     if bucket?
+
       result = 0
       # Assuming little endian byte order
       for i in [0...@BYTE_OFFSET]
         result += (1 << (8 * i)) * bucket[ voxelIndex + i]
+
+      mapping = @getMappingByPosition( voxel )
+      if mapping?
+        return mapping[result]
+
       return result

     return null
@@ -385,7 +397,7 @@
     ]

-  positionToZoomedAddress : ([x, y, z], zoomStep) ->
+  positionToZoomedAddress : ([x, y, z], zoomStep = 0) ->
     # return the bucket a given voxel lies in

     [


Renamed autosave and added interval to store seconds

--- app/assets/javascripts/stores/questionnaire_store.cjsx
+++ app/assets/javascripts/stores/questionnaire_store.cjsx
@@ -10,7 +10,8 @@
   questionnaireMode = null
   currentPage   = 0
   questionnaire = {}
-  autoSaveInterval = null
+  autoSaveTimer = null
+  autoSaveInterval = 60000

   constructor: ->
@@ -18,10 +19,10 @@
     @startAutoSave()

   startAutoSave: ->
-    autoSaveInterval = setInterval(@saveOrUpdateReport, 60000)
+    autoSaveTimer = setInterval(@saveOrUpdateReport, autoSaveInterval)

   stopAutoSave: ->
-    clearInterval(autoSaveInterval) if autoSaveInterval
+    clearInterval(autoSaveTimer) if autoSaveTimer

   currentPage: ->
     currentPage


FIX - keeping up with deprications: compactFlatten

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -54,6 +54,7 @@
   formattedInspect
   object
   toInspectedObjects
+  customCompactFlatten
 } = require './StandardImport'

 stats = clone zeroedStats =
@@ -476,7 +477,7 @@
       default: noChildren = []
       setter: (newChildren, oldChildren) ->
         @__drawPropertiesChanged = true # TODO - this is a hack-fix; is this the right way to do this?
-        newChildren = compactFlatten newChildren, keepIfRubyTrue
+        newChildren = customCompactFlatten newChildren, keepIfRubyTrue
         firstTimeSettingChildren = oldChildren == noChildren

         @_filterChildren = null


NEW: scrollOnscreen now supports "center" option

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -721,6 +721,7 @@

     # OUT: rectangle
+    # TODO: shouldn't this take into account transofmrations???
     logicalAreaInParentSpace: (pending, into) ->
       {_axis, _currentSize} = @getState pending

@@ -904,12 +905,12 @@
         resolve results = @toBitmapSync options
         callback? results.bitmap, results.elementToBitmapMatrix

-  scrollOnscreen: -> @scrollOnScreen()
-
-  scrollOnScreen: ->
+  scrollOnscreen: (center) -> @scrollOnScreen center
+
+  scrollOnScreen: (center) ->
     {parent} = @
     while parent
-      parent.scrollToChild? @
+      parent.scrollToChild? @, center
       {parent} = parent

   toBitmap: (options) ->