Fixed bug where pjax would refresh stylesheets too fast if double pjax requests are made.

Signed-off-by: Philip Vieira <3ba3974e96044dcde818e6310ed799abe10671d4@vallin.se>

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -50,8 +50,11 @@
     $('[rel=twipsy]').twipsy()

   $(document).bind 'end.pjax', (a,b,c) ->
-    load_javascript(b.getResponseHeader('controller'),b.getResponseHeader('action'))
-    $("body").attr('class', "#{b.getResponseHeader('controller')} #{b.getResponseHeader('action')}")
+    controller = b.getResponseHeader('controller')
+    action     = b.getResponseHeader('action')
+    if controller != null and action != null
+      load_javascript(b.getResponseHeader('controller'),b.getResponseHeader('action'))
+      $("body").attr('class', "#{b.getResponseHeader('controller')} #{b.getResponseHeader('action')}")

   $(document).ready ->
     load_javascript($("body").data('controller'),$("body").data('action'))


Added cuz of recompile.

Added cuz of recompile.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -35,3 +35,5 @@
 #= require_tree ./views
 #= require_tree ./routers
 #= require_tree ./modules
+#
+#


t puhMerge branch 'staging' into production

* staging:
  Don’t save before clicking on links with [target] specified.

--- app/assets/javascripts/save-on-change.coffee
+++ app/assets/javascripts/save-on-change.coffee
@@ -60,7 +60,7 @@
     @dirty_forms = {}

   intercept_navigation: ->
-    $("a").not('.colorbox').on 'click', (e) =>
+    $("a").not('.colorbox').not('[target]').on 'click', (e) =>
       @stored_location = e.currentTarget.href
       @force_save_dirty()
       e.preventDefault()


Use parens to callout function arguments.

--- app/assets/javascripts/save-on-change.coffee
+++ app/assets/javascripts/save-on-change.coffee
@@ -45,15 +45,15 @@
 # remove events scheduled for elem
 unschedule = (elem) ->
   job = scheduled_jobs[elem]
-  clearTimeout job if job
+  clearTimeout(job) if job
   scheduled_jobs[elem] = null

 schedule = (elem) ->
-  unschedule elem # remove any existing events
+  unschedule(elem) # remove any existing events
   action = ->
     saveElement(elem)

-  scheduled_jobs[elem] = setTimeout action, update_interval_s * 1000
+  scheduled_jobs[elem] = setTimeout(action, update_interval_s * 1000)

 $(document).ready ->


initial logging for metric testing

initial logging for metric testing

--- web.coffee
+++ web.coffee
@@ -3,6 +3,7 @@
 crypto   = require("crypto")
 express  = require("express")
 fs       = require("fs")
+log      = require("logger")
 manifest = require("manifest")
 storage  = require("storage").init()
 util     = require("util")
@@ -43,11 +44,12 @@
     get.on "end",          -> res.end()

 app.get "/file/:hash", (req, res) ->
-  storage.get "/hash/#{req.params.hash}", (err, get) ->
-    res.writeHead get.statusCode,
-      "Content-Length": get.headers["content-length"]
-    get.on "data", (chunk) -> res.write chunk
-    get.on "end",          -> res.end()
+  log "file.get", hash:req.params.hash, ->
+    storage.get "/hash/#{req.params.hash}", (err, get) ->
+      res.writeHead get.statusCode,
+        "Content-Length": get.headers["content-length"]
+      get.on "data", (chunk) -> res.write chunk
+      get.on "end",          -> res.end()

 app.post "/file/:hash", (req, res) ->
   storage.verify_hash req.files.data.path, req.params.hash, (err) ->


Fix check for duplicate product type names.

--- src/coffee/validator.coffee
+++ src/coffee/validator.coffee
@@ -78,7 +78,7 @@
     rawMaster = raw.master
     ptInfo = rawMaster[@header.toIndex CONS.HEADER_PRODUCT_TYPE]

-    @errors.push "[row #{raw.startRow}] The product type name '#{ptInfo}' is not unique. Please use the ID!" if _.has(@types.duplicateNames, ptInfo)
+    @errors.push "[row #{raw.startRow}] The product type name '#{ptInfo}' is not unique. Please use the ID!" if _.contains(@types.duplicateNames, ptInfo)

     index = @types.id2index[@types.name2id[ptInfo]]
     #index = @types.id2index[ptInfo] unless index


Issue #25: add a support of hexadecimal escape sequences to the Atom plugin

--- tools/atom-plugin/language-micro/grammars/micro.cson
+++ tools/atom-plugin/language-micro/grammars/micro.cson
@@ -20,11 +20,13 @@
 	}
 	{
 		'comment': 'Symbols'
-		'match': "'(?:(\\\\.)|[^'])'"
+		'match': "'(?:(\\\\.)|(\\\\x[A-Fa-f0-9]{2})|[^'])'"
 		'captures':
 			'0':
 				'name': 'string.quoted.single.micro'
 			'1':
+				'name': 'constant.character.escape.micro'
+			'2':
 				'name': 'constant.character.escape.micro'
 	}
 	{
@@ -34,8 +36,8 @@
 		'name': 'string.quoted.double.micro'
 		'patterns': [
 			{
-				'comment': 'Escaped symbol'
-				'match': '\\\\.'
+				'comment': 'Escaped symbol (a subgroups order is significant)'
+				'match': '((\\\\x[A-Fa-f0-9]{2})|(\\\\.))'
 				'name': 'constant.character.escape.micro'
 			}
 		]


Refactor tg-us-estimation. Fuck Yeah!!! :clap: :metal: :clap: :metal:

--- app/coffee/modules/userstories/detail.coffee
+++ app/coffee/modules/userstories/detail.coffee
@@ -263,7 +263,7 @@
             total

         <% _.each(roles, function(role) { %>
-        <li class="total clickable" data-role-id="<%- role.id %>">
+        <li class="total <% if(editable){ %>clickable<% } %>" data-role-id="<%- role.id %>">
             <%- role.points %>
             <%- role.name %>

         <% }); %>
@@ -289,6 +289,11 @@
     link = ($scope, $el, $attrs, $model) ->
         saveAfterModify = $attrs.saveAfterModify or false

+        isEditable = ->
+            if $model.$modelValue.id
+                return $scope.project.my_permissions.indexOf("modify_us") != -1
+            return $scope.project.my_permissions.indexOf("add_us") != -1
+
         render = (us) ->
             totalPoints = us.total_points or 0
             computableRoles = _.filter($scope.project.roles, "computable")
@@ -301,7 +306,12 @@
                 role.points = if pointObj? and pointObj.name? then pointObj.name else "?"
                 return role

-            html = mainTemplate({totalPoints: totalPoints, roles: roles})
+            ctx = {
+                totalPoints: totalPoints
+                roles: roles
+                editable: isEditable()
+            }
+            html = mainTemplate(ctx)
             $el.html(html)

         renderPoints = (target, us, roleId) ->
@@ -337,6 +347,8 @@
         $el.on "click", ".total.clickable", (event) ->
             event.preventDefault()
             event.stopPropagation()
+            return if not isEditable()
+
             target = angular.element(event.currentTarget)
             roleId = target.data("role-id")

@@ -349,6 +361,7 @@
         $el.on "click", ".point", (event) ->
             event.preventDefault()
             event.stopPropagation()
+            return if not isEditable()

             target = angular.element(event.currentTarget)
             roleId = target.data("role-id")
@@ -356,28 +369,24 @@

             $el.find(".popover").popover().close()

-            us = $model.$modelValue
-
-            points = _.clone(us.points, true)
-            points[roleId] = pointId
-
-            # NOTE: your past self wants that you make a refactor of this
-            $scope.$apply ->
-                us.points = points
-                us.total_points = calculateTotalPoints(us)
-
-                if saveAfterModify
-                    onSuccess = ->
-                        $repo.refresh(us).then ->
-                            render(us)
-                        $rootScope.$broadcast("history:reload")
-                    onError = ->
-                        $repo.refresh(us).then ->
-                            render(us)
-                        $confirm.notify("error")
-                    $repo.save(us).then(onSuccess, onError)
-                else
-                    render(us)
+            us = angular.copy($model.$modelValue)
+            us.points[roleId] = pointId
+            us.total_points = calculateTotalPoints(us)
+            $model.$setViewValue(us)
+
+            if saveAfterModify
+                # Edit in the detail page
+                onSuccess = ->
+                    $confirm.notify("success")
+                    $rootScope.$broadcast("history:reload")
+                onError = ->
+                    $confirm.notify("error")
+                    us.revert()
+                    $model.$setViewValue(us)
+                $repo.save($model.$modelValue).then(onSuccess, onError)
+            else
+                # Create or eedit in the lightbox
+                render($model.$modelValue)

         $scope.$watch $attrs.ngModel, (us) ->
             render(us) if us


Disabled query cache test

Disabled query cache test

--- test/generators/all.coffee
+++ test/generators/all.coffee
@@ -4,7 +4,7 @@
 # beforeEach should return the models_json for the current run
 module.exports = (options, callback) ->
   queue = new Queue(1)
-  queue.defer (callback) -> require('./all_query_cache')(options, callback)
+  # queue.defer (callback) -> require('./all_query_cache')(options, callback)
   queue.defer (callback) -> require('./all_cache')(options, callback)
   queue.defer (callback) -> require('./all_collection')(options, callback)
   queue.defer (callback) -> require('./all_conventions')(options, callback)


Remove dead code

Remove dead code

--- services/web/public/coffee/directives/scroll.coffee
+++ services/web/public/coffee/directives/scroll.coffee
@@ -1,43 +1,7 @@
 define [
 	"base"
 ], (App) ->
-	fakeNgModel = (initValue) ->
-		$setViewValue: (value) ->
-			@$viewValue = value
-			return
-		$viewValue: initValue
-
 	App.directive "updateScrollBottomOn", ->
-		# return {
-		# 	priority: 1
-		# 	require: ["?ngModel"]
-		# 	restrict: "A"
-		# 	link: (scope, $el, attrs, ctrls) ->
-		# 		scrollToBottom = ->
-		# 			el.scrollTop = el.scrollHeight
-		# 			return
-		#
-		# 		shouldActivateAutoScroll = ->
-		# 			# + 1 catches off by one errors in chrome
-		# 			el.scrollTop + el.clientHeight + 1 >= el.scrollHeight
-		#
-		#
-		# 		el = $el[0]
-		# 		ngModel = ctrls[0] or fakeNgModel(true)
-		#
-		# 		scope.$watch ->
-		# 			console.log "SCOPE CHANGED", ngModel.$viewValue
-		# 			if ngModel.$viewValue
-		# 				scrollToBottom()
-		#
-		# 		$el.bind "scroll", ->
-		# 			activate = shouldActivateAutoScroll()
-		# 			scope.$apply ngModel.$setViewValue.bind(ngModel, activate)  if activate isnt ngModel.$viewValue
-		#
-		# 		return
-		# }
-
-
 		return {
 			restrict: "A"
 			link: (scope, element, attrs, ctrls) ->
@@ -63,11 +27,9 @@
 				scrollBottom = 0
 				element.on "scroll", (e) ->
 					scrollBottom = element[0].scrollHeight - element[0].scrollTop - element[0].clientHeight
-					console.log "SCROLL BOTTOM CHANGED", scrollBottom

 				scope.$on attrs.updateScrollBottomOn, () ->
 					setTimeout () ->
-						console.log "RESTORING SCROLL BOTTOM", scrollBottom
 						element.scrollTop(element[0].scrollHeight - element[0].clientHeight - scrollBottom)
 					, 0
 		}


be consistent with accessing global properties

--- src/component/looper.coffee
+++ src/component/looper.coffee
@@ -14,6 +14,8 @@
 cancelAnimationFrameRef =
     proxyWindow.resolveVendor('cancelAnimationFrame') or
     proxyWindow.resolveVendor('cancelRequestAnimationFrame')
+setIntervalRef = proxyWindow.resolveVendor('setInterval')
+clearIntervalRef = proxyWindow.resolveVendor('clearInterval')
 INTERVAL_60_FPS = (1000 // 60)

 if requestAnimationFrameRef?
@@ -32,11 +34,11 @@
     #Fall back to interval-based loop if there is no requestAnimationFrame
     looperCreator = (func) ->
         if func() isnt false
-            intervalId = window.setInterval ->
+            intervalId = setIntervalRef ->
                 if func() is false
-                    window.clearInterval intervalId
+                    clearIntervalRef intervalId
             , INTERVAL_60_FPS

-        () -> window.clearInterval intervalId
+        () -> clearIntervalRef intervalId

 module.exports = looperCreator


Show graph icon when used as component

Show graph icon when used as component

--- src/components/Graph.coffee
+++ src/components/Graph.coffee
@@ -50,7 +50,8 @@

   createNetwork: (graph) ->
     @description = graph.properties.description or ''
-
+    @icon = graph.properties.icon or @icon
+
     graph.componentLoader = @loader

     noflo.createNetwork graph, (@network) =>


Support rotation

Support rotation

--- app/scripts/lib/script_modules/entity.coffee
+++ app/scripts/lib/script_modules/entity.coffee
@@ -239,6 +239,14 @@
     )
     defer.promise

+  rotate: (degrees, duration) ->
+    (sequence) =>
+      @_verify(sequence)
+      defer = WhenJS.defer()
+      @entity.tween({ rotation: degrees }, duration)
+        .one 'TweenEnd', -> defer.resolve()
+      defer.promise
+
   location: (settings = {}) ->
     =>
       x: (@enemy.location.x ? (@entity.x + Crafty.viewport.x) + (@entity.w / 2))


simplify tests of static module methods

simplify tests of static module methods

--- tests/unit/module.unit.coffee
+++ tests/unit/module.unit.coffee
@@ -15,11 +15,9 @@
       expect(Space.Module.published[fakeModule.identifier]).to.equal fakeModule

     it 'throws an error if two modules try to publish under same name', ->
-      subModule1 = identifier: 'test'
-      subModule2 = identifier: 'test'
       publishTwoModulesWithSameName = ->
-        Space.Module.publish subModule1, subModule1.identifier
-        Space.Module.publish subModule2, subModule2.identifier
+        Space.Module.publish {}, 'test'
+        Space.Module.publish {}, 'test'
       expect(publishTwoModulesWithSameName).to.throw Error

   describe '@require', ->


Fix #3174: save button triggering on delete/reset account form fields

--- app/views/account/AccountSettingsView.coffee
+++ app/views/account/AccountSettingsView.coffee
@@ -37,11 +37,9 @@

   #- Form input callbacks
   onChangePanelInput: (e) ->
+    return if $(e.target).closest('.form').attr('id') in ['reset-progress-form', 'delete-account-form']
     $(e.target).addClass 'changed'
-    if (JSON.stringify(document.getElementById('email1').className)).indexOf("changed") > -1 or (JSON.stringify(document.getElementById('password1').className)).indexOf("changed") > -1
-      $(e.target).removeClass 'changed'
-    else
-      @trigger 'input-changed'
+    @trigger 'input-changed'

   onClickToggleAllButton: ->
     subs = @getSubscriptions()


use readable event to handle article

use readable event to handle article

--- scripts/feed_checker.coffee
+++ scripts/feed_checker.coffee
@@ -41,15 +41,17 @@
         robot.logger.debug "checking feed: #{url}"

         next = []
-        request(url).pipe(new FeedParser())
+        request.get(url).pipe(new FeedParser())
           .on 'error', (err) ->
             robot.logger.warning "feed parsing error in #{url}: #{err}"
-          .on 'meta', (meta) ->
-            next.push meta
+          # ignore meta
+          # .on 'meta', (meta) ->
+          .on 'readable', ->
+            next.push this.read()
           .on 'end', ->
             if prev.length != 0 then next.forEach (article) ->
               if is_new prev, article.guid
-                robot.send user, "new item #{article.title} #{article.link}"
+                robot.send user, "new article: #{article.title} #{article.link}"
                 if article.summary? then robot.send user, "summary: #{article.summary}"

             # save feed


Nodejs Web Server Tests: add fetchOrCreateAppStorage test suite

--- workers/social/lib/social/models/account.test.coffee
+++ workers/social/lib/social/models/account.test.coffee
@@ -1,5 +1,6 @@
 { daisy
   expect
+  expectRelation
   withDummyClient
   withConvertedUser
   generateDummyClient
@@ -162,9 +163,9 @@
           withDummyClient ({ client, account }) ->
             # expecting to be able to get permissions
             account.fetchMyPermissions client, (err, permissions) ->
-              expect(err)          .to.not.exist
-              expect(permissions)  .to.exist
-              expect(permissions)  .to.be.an 'object'
+              expect(err).to.not.exist
+              expect(permissions).to.exist
+              expect(permissions).to.be.an 'object'
               done()

   describe '#leaveFromAllGroups()', ->
@@ -240,6 +241,85 @@
           daisy queue

+  describe 'fetchOrCreateAppStorage()', ->
+
+    describe 'when storage does not exist', ->
+
+      it 'should create a new appStorage', (done) ->
+
+        withConvertedUser ({ account }) ->
+          appId      = generateRandomString()
+          version    = '1.0.0'
+          options    = { appId, version }
+          appStorage = null
+
+          queue = [
+
+            ->
+              # creating a new app storage
+              account.fetchOrCreateAppStorage options, (err, storage) ->
+                expect(err).to.not.exist
+                appStorage = storage
+                expect(storage.appId).to.be.equal appId
+                expect(storage.version).to.be.equal version
+                queue.next()
+
+            ->
+              # expecting relationship to be created
+              options =
+                as         : 'appStorage'
+                data       : { appId, version }
+                targetId   : appStorage.getId()
+                sourceId   : account.getId()
+                targetName : 'JAppStorage'
+                sourceName : 'JAccount'
+
+              expectRelation.toExist options, ->
+                queue.next()
+
+            -> done()
+
+          ]
+
+          daisy queue
+
+
+    describe 'when storage exists', ->
+
+      it 'should return the existing appStorage', (done) ->
+
+        withConvertedUser ({ account }) ->
+          appId      = generateRandomString()
+          version    = '1.0.0'
+          options    = { appId, version }
+          appStorage = null
+
+          queue = [
+
+            ->
+              # creating a new app storage
+              account.fetchOrCreateAppStorage options, (err, storage) ->
+                expect(err).to.not.exist
+                appStorage = storage
+                expect(storage.appId).to.be.equal appId
+                expect(storage.version).to.be.equal version
+                queue.next()
+
+            ->
+              # expecting previously created app storage to be fetched
+              account.fetchOrCreateAppStorage options, (err, storage) ->
+                expect(err).to.not.exist
+                expect(storage._id.toString()).to.be.equal appStorage._id.toString()
+                expect(storage.appId).to.be.equal appStorage.appId
+                expect(storage.version).to.be.equal appStorage.version
+                queue.next()
+
+            -> done()
+
+          ]
+
+          daisy queue
+
 beforeTests()

 runTests()


管理項目が一般ユーザに見えていた不具合を修正

管理項目が一般ユーザに見えていた不具合を修正

--- app/assets/javascripts/components/navbar/admin.js.cjsx
+++ app/assets/javascripts/components/navbar/admin.js.cjsx
@@ -1,6 +1,6 @@
 class @Admin extends React.Component
   render: ->
-    return null unless @props.current_user?
+    return null unless @props.current_user.id?
     role = @props.current_user.role
     return null if role < 50
     <li className='uk-parent admin-parent' data-uk-dropdown=''>


#812 Adjust client to allow readOnly annotations

--- app/assets/javascripts/main/non_backbone_router.coffee
+++ app/assets/javascripts/main/non_backbone_router.coffee
@@ -8,11 +8,11 @@
 class NonBackboneRouter extends Backbone.Router

   routes :
-    "admin/tasks/overview"          : "taskOverview"
-    "admin/taskTypes"               : "hideLoading"
-    "annotations/:typ/:id"          : "tracingTrace"
-    "datasets/:id/view"             : "tracingView"
-    "*url"                          : "hideLoading"
+    "admin/tasks/overview"            : "taskOverview"
+    "admin/taskTypes"                 : "hideLoading"
+    "annotations/:typ/:id(/readOnly)" : "tracingTrace"
+    "datasets/:id/view"               : "tracingView"
+    "*url"                            : "hideLoading"

   hideLoading : ->


Run the callback anyway

Run the callback anyway

--- app/assets/javascripts/models/expense_report.js.coffee
+++ app/assets/javascripts/models/expense_report.js.coffee
@@ -43,6 +43,7 @@
             callback() if callback
         else
           expense.exchangeRate = 0
+          callback() if callback

       removeExpense: (expenseToRemove) ->
         @expenses = (item for item in @expenses when item != expenseToRemove)


BW & XY: Add Heart Swap message

BW & XY: Add Heart Swap message

--- server/bw/data/moves.coffee
+++ server/bw/data/moves.coffee
@@ -1167,6 +1167,7 @@
 extendMove 'Heart Swap', ->
   @afterSuccessfulHit = (battle, user, target) ->
     [user.stages, target.stages] = [target.stages, user.stages]
+    battle.message "#{user.name} switched stat changes with the target!"

 extendMove 'Hex', ->
   @basePower = (battle, user, target) ->


missing comma

missing comma

--- node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
+++ node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
@@ -162,7 +162,7 @@
     options.heartbeat = heartbeat if heartbeat?

     readiness = 0
-    process.on 'exit' ->
+    process.on 'exit', ->
       require('fs').writeFileSync(
         "/tmp/readiness-#{process.pid}-#{readiness}", ''+readiness, 'utf-8'
       )


improve resize function

improve resize function

--- server/helpers/thumb.coffee
+++ server/helpers/thumb.coffee
@@ -13,7 +13,7 @@
         height: 800

     options.srcPath = raw
-    options.dstPath = "/tmp/#{photo.id}2"
+    options.dstPath = "/tmp/#{file.id}2"

     # create files
     fs.openSync options.dstPath, 'w'
@@ -21,15 +21,15 @@
     # create a resized file and push it to db
     im[options.mode] options, (err, stdout, stderr) =>
         return callback err if err
-        photo.attachBinary options.dstPath, {name}, (err) ->
+        file.attachBinary options.dstPath, {name}, (err) ->
             fs.unlink options.dstPath, ->
                 callback err

 module.exports.create = (file, callback) ->
-    console.log "createThumb #{file.id}"
     return callback new Error('no binary') unless file.binary?
     if file.binary?.thumb?
+        console.log "createThumb #{file.id} : already done"
         callback()
     else
         rawFile = "/tmp/#{file.id}"
@@ -42,4 +42,5 @@
             resize rawFile, file, 'thumb', (err) ->
                 return callback err if err
                 fs.unlink rawFile, ->
+                    console.log "createThumb #{file.id} : done"
                     callback()


sonos bug fix

sonos bug fix

--- roc_dashboard/widgets/text/text.coffee
+++ roc_dashboard/widgets/text/text.coffee
@@ -83,6 +83,7 @@
       $(@get('node')).find('.title').css "margin-left", 0
       $(@get('node')).find('.artist').css "margin-left", 0
       $(@get('node')).find('.album').css "margin-left", 0
+      @prev_title = ""
       clearInterval @interval
     else if @title != @prev_title
       $(@get('node')).find('.title').css "margin-left", 0


When retrieving feeds in a paginated fashion, if a page with less than the maximum number of feeds per page is retrieved, stop paginating.

Previously the next page of feeds was requested, until the server returned a 404. Now if the client receives less than the maximum number of feeds in a page it can assume that it is the last page, and the last feeds page request (which most of the time resulted in a 404) is avoided most of the time. It can still happen if the number of feeds is an integer multiple of the maximum number of feeds per page.

--- app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
@@ -8,6 +8,10 @@
 ($rootScope, $http, $timeout, timerFlagSvc, findSvc, entriesPaginationSvc,
  feedsPaginationSvc, cleanupSvc, favicoSvc)->

+  # Maximum number of feeds in each page.
+  # This MUST match the feeds page size set in the server!
+  feeds_page_size = 25
+
   #--------------------------------------------
   # PRIVATE FUNCTION: Load feeds. Reads the boolean flag "show_read" to know if
   # we want to load all feeds (true) or only feeds with unread entries (false).
@@ -25,8 +29,16 @@
     .success (data)->
       feedsPaginationSvc.load_feeds_page page, data.slice()
       feedsPaginationSvc.set_busy false
-      # Load the next page of feeds, until a 404 (no more feeds) is received
-      load_feeds page
+
+      # Load the next page of feeds until no more feeds are available
+      if data.length < feeds_page_size
+        # there are no more pages of feeds to retrieve
+        $rootScope.feeds_loaded = true
+        feedsPaginationSvc.pagination_finished()
+        favicoSvc.update_unread_badge()
+      else
+        # There is probably at least one more page of feeds available
+        load_feeds page
     .error (data, status)->
       feedsPaginationSvc.set_busy false
       if status == 404


Refactor stripe code in billing.coffee

Refactor stripe code in billing.coffee

--- js/inner/billing.coffee
+++ js/inner/billing.coffee
@@ -164,26 +164,31 @@
       success: =>
         @loadExistingCard()

+  stripeDefaults: () =>
+    key: @stripeKey()
+    name: "CircleCI"
+    address: false
+    email: VM.current_user().selected_email()
+
   createCard: (plan, event) =>
-    StripeCheckout.open
-      key: @stripeKey()
-      name: 'CircleCi',
+    vals =
       panelLabel: 'Add card',
+      price: 100 * plan.price
       description: "#{plan.name} plan"
-      price: 100 * plan.price
       token: (token) =>
         @chosenPlan(plan)
         @recordStripeTransaction event, token

+    StripeCheckout.open($.extend @stripeDefaults(), vals)
+

   updateCard: (data, event) =>
-    StripeCheckout.open
-      key: @stripeKey()
-      name: 'CircleCI',
-      email: VM.current_user().selected_email(),
+    vals =
       panelLabel: 'Update card',
       token: (token) =>
         @ajaxSetCard(event, token.id, "PUT")
+
+    StripeCheckout.open($.extend @stripeDefaults(), vals)

   load: (hash="small") =>


Fix issue where clicking between list items errors

Without ensuring that the meta-clicked element has a model before attempting
to select/deselect the element, the select/deselect will raise an error.

--- app/assets/javascripts/cartilage/views/list_view.js.coffee
+++ app/assets/javascripts/cartilage/views/list_view.js.coffee
@@ -295,10 +295,11 @@

     if event.metaKey
       model = ($ element).data("model")
-      if model in @_selected.models
-        @deselect element
-      else
-        @selectAnother element
+      if model?
+        if model in @_selected.models
+          @deselect element
+        else
+          @selectAnother element
       event.preventDefault()

     else if event.shiftKey


Adjust checkout error handling

Adjust checkout error handling

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -131,9 +131,10 @@
           deferred.resolve(Cart.current)

         .error (response) ->
-          errors = response.errors || response.exception
-
-          Cart.errors(base: errors) if errors
+          if response.errors
+            Cart.errors(response.errors)
+          else if response.exception
+            Cart.errors(base: response.exception)

           deferred.reject(Cart.current)


commented out largestRect console messages

--- src/geom/largestRect.coffee
+++ src/geom/largestRect.coffee
@@ -48,12 +48,12 @@
   # cy - the y coordinate of the rectangle's center
   # angle - rotation angle in degrees. The anchor of rotation is the center point

-print    = require "../core/console/print.coffee"
+# print    = require "../core/console/print.coffee"
 simplify = require "simplify-js"

 module.exports = (poly, options) ->
   if poly.length < 3
-    print.error 'polygon has to have at least 3 points'
+    # print.error 'polygon has to have at least 3 points'
     return null
   ## For visualization debugging purposes ##
   events = []
@@ -95,7 +95,7 @@
   ########################################
   area = Math.abs(d3.geom.polygon(poly).area()) # take absolute value of the signed area
   if area is 0
-    print.error 'polygon has 0 area'
+    # print.error 'polygon has 0 area'
     return null
   # get the width of the bounding box of the original polygon to determine tolerance
   [minx, maxx] = d3.extent poly, (d) -> d[0]


Add displayName so components match tests

--- tutor/src/components/scores/assignment-header.cjsx
+++ tutor/src/components/scores/assignment-header.cjsx
@@ -17,6 +17,7 @@
       Review
     </TutorLink>
   </span>
+ReviewLink.displayName = 'ReviewLink'

 AverageLabel = (props) ->
   if props.average_score
@@ -39,6 +40,7 @@
       </span>
     else
       null
+AverageLabel.displayName = 'AverageLabel'

 getCellWidth = ({isConceptCoach, heading}) ->
   if isConceptCoach


Allow function for startposition of grid

--- src/lib/LocationGrid.coffee
+++ src/lib/LocationGrid.coffee
@@ -32,8 +32,9 @@

   _coordList: (listSettings) ->
     avoid = listSettings.avoid?() ? listSettings.avoid
+    start = listSettings.start?() ? listSettings.start
     for i in [0...listSettings.steps] when i not in avoid
-      listSettings.start + (i * listSettings.stepSize)
+      start + (i * listSettings.stepSize)

   getLocation: ->
     @freeCoords.pop()


included stack in pretty error

included stack in pretty error

--- src/lib/pretty-error.coffee
+++ src/lib/pretty-error.coffee
@@ -8,12 +8,21 @@
  #             MIST BUILD SYSTEM
  # Copyright (c) 2015 On Demand Solutions, inc.

+# for implementations using this:
+#   note the distinction between cases of `filename` and `fileName`:
+#   the former is for mistfile problems; the latter is generated automateically
+#   by the Error object to indicate the origin in the program.
+#
+#   Do not set `fileName`, but rather `filename`.
+#   I know it's confusing. Bite me.
+
 chalk = require 'chalk'
 fs = require 'fs'
+path = require 'path'

 col = [
   "\x1b[0G"
-  "\x1b[2G"
+  "\x1b[3G"
   "\x1b[13G"
   "\x1b[26G"
 ]
@@ -43,6 +52,7 @@
 compileFileMarker = (filename, line, column)->
   try
     compileSourceMarker fs.readFileSync(filename).toString(), line, column
+
 compileSourceMarker = (src, line, column)->
   lines = src.split /\r?\n/g
   line = lines[line-1]
@@ -59,6 +69,20 @@
     result.push "\x1b[#{c}#{d}#{chalk.red.bold('^')}"

   result.join "\n#{col[1]}"
+
+betterStack = (stack)->
+  lines = stack.split /\r?\n/g
+  lines = lines.slice 1 # get rid of the error
+  lines = lines.map (line)->
+    line.match /^\s*at\s+((?:(?!\s+\().)+)\s+\(([^:)]+)(:\d+:\d+)?\)$/
+  found = no
+  lines = lines.filter (line)->
+    if abs = path.isAbsolute line[2]
+      found = yes
+    abs || not found
+  lines = lines.map (line)->
+    "#{col[1]}in#{col[2]}#{line[1]} (#{line[2]}#{line[3]})"
+  lines.join '\n'

 module.exports = (e)->
   if e.constructor is String then e = message:e
@@ -80,10 +104,14 @@
       # peg error
       if e.expected then compileExpected e.expected

-    line null
+    line null,
       # line/col resolution
       if e.source and e.line then compileSourceMarker e.source, e.line, e.column
       else if e.filename and e.line then compileFileMarker e.filename, e.line,
         e.column

+    line null
+      # stack
+      if e.stack then chalk.dim.grey betterStack e.stack
+
   process.exit 10


FinderController: cleanup FS Watcher helpers

--- client/Finder/filetree/controllers/findercontroller.coffee
+++ client/Finder/filetree/controllers/findercontroller.coffee
@@ -37,39 +37,35 @@
     mainController.on "accountChanged.to.loggedIn", @bound 'reset'

     if options.useStorage
+
       @appStorage.ready =>
+
         @treeController.on "file.opened", @bound 'setRecentFile'
+
         @treeController.on "folder.expanded", (folder)=>
           @setRecentFolder folder.path
+
         @treeController.on "folder.collapsed", ({path})=>
           @unsetRecentFolder path
           @stopWatching path

+
+    @noMachineFoundWidget = new NoMachinesFoundWidget
     @cleanup()

-    vmc = KD.getSingleton("vmController")
-    vmc.on "StateChanged", @bound "checkVMState"
-    vmc.on "VMDestroyed",  @bound "unmountVm"
-
-  registerWatcher:(path, stopWatching)->
-    @watchers[path] = stop: stopWatching
-    @noMachineFoundWidget = new NoMachinesFoundWidget
-
-  stopAllWatchers:->
-    (watcher.stop() for own path, watcher of @watchers)
-    @watchers = {}
-
-  stopWatching:(pathToStop)->
-    for own path, watcher of @watchers  when (path.indexOf pathToStop) is 0
-      watcher.stop()
-      delete @watchers[path]
+    # TODO ~ GG
+    # vmc = KD.getSingleton("vmController")
+    # vmc.on "StateChanged", @bound "checkVMState"
+    # vmc.on "VMDestroyed",  @bound "unmountVm"
+

   loadView:(mainView)->
+
     mainView.addSubView @treeController.getView()
-    @viewLoaded = yes
     mainView.addSubView @noMachineFoundWidget

-    @reset()  if @getOptions().loadFilesOnInit
+    if @getOption 'loadFilesOnInit' then do @reset
+

   reset:->

@@ -287,6 +283,22 @@

     @appStorage.setValue "mountedMachines", machines

+
+  # FS Watcher helpers
+  #
+
+  registerWatcher:(path, stopWatching)->
+    @watchers[path] = stop: stopWatching
+
+  stopAllWatchers:->
+    (watcher.stop() for own path, watcher of @watchers)
+    @watchers = {}
+
+  stopWatching:(pathToStop)->
+    for own path, watcher of @watchers  when (path.indexOf pathToStop) is 0
+      watcher.stop()
+      delete @watchers[path]
+
   # Basics
   #


stop body from reloading all images on ever like/comment (FIXME: cache images)

--- client/app/CommonViews/opinions/opinionlistitemview.coffee
+++ client/app/CommonViews/opinions/opinionlistitemview.coffee
@@ -80,6 +80,8 @@
       delegate : @commentBox.commentList
       cssClass : "opinion-comment-header"
     , data
+
+    @bodyView = new OpinionBodyView {}, data

     @tags = new ActivityChildViewTagGroup
       itemsToShow   : 3
@@ -142,6 +144,7 @@
                   if err
                     new KDNotificationView title : "Your changes weren't saved.", type :"mini"
                   else
+                    @bodyView.render yes
                     @getDelegate().emit "RefreshTeaser", ->
                     @emit "OwnOpinionWasAdded", opinion
                     @editForm.setClass "hidden"
@@ -240,7 +243,7 @@
         {{> @editLink}}

-          {{@utils.expandUsernames(@utils.applyMarkdown(#(body)),"pre")}}
+          {{> @bodyView}}

           {{> @larger}}
@@ -262,3 +265,22 @@

     """
+
+class OpinionBodyView extends KDView
+  constructor:(options,data)->
+    super options, data
+
+  viewAppended:->
+    @setTemplate @pistachio()
+    @template.update()
+
+  render:(force=no)->
+    if force
+      super
+    else
+      no
+
+  pistachio:->
+    """
+      {{@utils.expandUsernames(@utils.applyMarkdown(#(body)),"pre")}}
+    """


Workspace: Separated constructor logic into methods for TerminalPane to override these methods at child classes.

--- client/app/CommonViews/workspace/panes/terminalpane.coffee
+++ client/app/CommonViews/workspace/panes/terminalpane.coffee
@@ -6,17 +6,20 @@

     super options, data

+    @createWebTermView()
+    @webterm.on "WebTermConnected", (@remote) => @onWebTermConnected()
+
+    @on "PaneResized", @bound "forceResize"
+
+  createWebTermView: ->
     @webterm           = new WebTermView
       delegate         : @
       cssClass         : "webterm"
       advancedSettings : no

-    @webterm.on "WebTermConnected", (@remote)=>
-      {command} = @getProperties()
-      @runCommand command if command
-      @webterm.terminal.setScrollbackLimit 50
-
-    @on "PaneResized", @bound "forceResize"
+  onWebTermConnected: ->
+    {command} = @getProperties()
+    @runCommand command if command

   runCommand: (command) ->
     return unless command


Environments: update for high FPS eyes like Farslan's

--- client/ide/filetree/itemsubviews/vmtogglebuttonview.coffee
+++ client/ide/filetree/itemsubviews/vmtogglebuttonview.coffee
@@ -42,13 +42,13 @@
       KD.logToExternal "oskite: vm failed to turn on", {error:err.message, vm}
       return warn err

+    if info.state is "RUNNING"
+    then @toggle.setDefaultValue yes
+    else @toggle.setDefaultValue no
+
     @menuTitle.updatePartial 'Change state'
     @toggle.show()
     @loader.hide()
-
-    if info.state is "RUNNING"
-    then @toggle.setDefaultValue yes
-    else @toggle.setDefaultValue no

   pistachio:->
     """{{> @menuTitle}}{{> @toggle}}{{> @loader}}"""


major refactor

major refactor

--- lib/autocomplete-elixir-provider.coffee
+++ lib/autocomplete-elixir-provider.coffee
@@ -8,6 +8,7 @@

   constructor: ->
     @rsenseClient = new RsenseClient()
+

   requestHandler: (options) ->
     return new Promise (resolve) =>


Fix #188

Fix #188

 - Use `Meteor.absoluteUrl` to get WebWorker

--- files.coffee
+++ files.coffee
@@ -1478,7 +1478,7 @@
           console.time('loadFile ' + @config.file.name)

         if Worker and @config.allowWebWorkers
-          @worker = new Worker '/packages/ostrio_files/worker.js'
+          @worker = new Worker Meteor.absoluteUrl 'packages/ostrio_files/worker.js'
         else
           @worker = null


Export instead of window

Export instead of window

--- app/javascript/src/classes/thumbnail_user_image.coffee
+++ app/javascript/src/classes/thumbnail_user_image.coffee
@@ -17,7 +17,7 @@
 # On success:
 # canvas: On success, the canvas containing the thumbnailed image.
 #
-class window.ThumbnailUserImage
+export default class ThumbnailUserImage
   constructor: (file, onComplete) ->
     # Create the shared image pool, if we havn't yet.
     image_pool ?= new ImgPoolHandler


add formatting options

add formatting options

--- lib/edit-in-new-tab.coffee
+++ lib/edit-in-new-tab.coffee
@@ -8,6 +8,39 @@
       type: "boolean"
       default: false
       order: 1
+    outputFormat:
+      title: "Output Format"
+      type: 'object'
+      properties:
+        select:
+          title: "Select"
+          description: "Selects the newly added text"
+          type: "boolean"
+          default: false
+          order: 2
+        autoIndent:
+          title: "Auto-indent"
+          description: "Indents all inserted text appropriately"
+          type: "boolean"
+          default: false
+          order: 3
+        autoIndentNewline:
+          title: "Auto-indent New Line"
+          description: "Indent newline appropriately"
+          type: "boolean"
+          default: false
+          order: 4
+        autoDecreaseIndent:
+          title: "Auto-decreate Indent"
+          description: "Decreases indent level appropriately (for example, when a closing bracket is inserted)"
+          type: "boolean"
+          default: false
+          order: 5
+        normalizeLineEndings:
+          title: "Normalize Line Endings"
+          type: "boolean"
+          default: true
+          order: 6
   subscriptions: null

   activate: (state) ->
@@ -23,17 +56,26 @@
   editInNewTab: () ->
     return unless editor = atom.workspace.getActiveTextEditor()

-    scopeName = editor.getGrammar().scopeName
     selection = editor.getLastSelection()
-    text = selection.getText()

-    unless text.length > 0
+    target =
+      scope: editor.getGrammar().scopeName
+      text: selection.getText()
+
+    unless target.text.length > 0
       return atom.beep()

     atom.workspace.open()
       .then (newTab) ->
-        newTab.setText(text)
+        options =
+          select: atom.config.get('edit-in-new-tab.outputFormat.select')
+          autoIndent: atom.config.get('edit-in-new-tab.outputFormat.autoIndent')
+          autoIndentNewline: atom.config.get('edit-in-new-tab.outputFormat.autoIndentNewline')
+          autoDecreaseIndent: atom.config.get('edit-in-new-tab.outputFormat.autoDecreaseIndent')
+          normalizeLineEndings: atom.config.get('edit-in-new-tab.outputFormat.normalizeLineEndings')
+
+        newTab.insertText(target.text, options)
         unless atom.config.get('edit-in-new-tab.ignoreScope') is true
-          newTab.setGrammar(atom.grammars.grammarForScopeName(scopeName))
+          newTab.setGrammar(atom.grammars.grammarForScopeName(target.scope))
       .catch (error) ->
         atom.notifications.addError(error, dismissable: true)


Style fix

Style fix

--- views/components/settings/parts/plugin-config.cjsx
+++ views/components/settings/parts/plugin-config.cjsx
@@ -528,9 +528,9 @@
             <Col xs={12} className='div-row'>

                 <FontAwesome name={value.icon} />
-                  {value[window.language]}
+                  {' ' + value[window.language]}

-               @
+               @

                   {value.author}


Remove junk test

Remove junk test

--- spec/autocomplete-manager-integration-spec.coffee
+++ spec/autocomplete-manager-integration-spec.coffee
@@ -797,59 +797,3 @@

       runs ->
         expect(editorView.querySelector('.autocomplete-plus')).toExist()
-
-  # describe 'when prefix length is 0', ->
-  #   [registration] = []
-  #   runs ->
-  #     atom.config.set('autocomplete-plus.enableAutoActivation', false)
-  #   beforeEach ->
-  #     testProvider =
-  #       getSuggestions: (options) ->
-  #         [{
-  #           word: 'ohai',
-  #           prefix: ''
-  #         }]
-  #       selector: '.source.js'
-  #     registration = atom.packages.serviceHub.provide('autocomplete.provider', '1.0.0', {provider: testProvider})
-  #
-  #     waitsForPromise -> atom.workspace.open('sample.js').then (e) ->
-  #       editor = e
-  #       editorView = atom.views.getView(e)
-  #
-  #     # Activate the package
-  #     waitsForPromise -> atom.packages.activatePackage('autocomplete-plus').then (a) ->
-  #       mainModule = a.mainModule
-  #       autocompleteManager = mainModule.autocompleteManager
-  #
-  #     runs ->
-  #
-  #   afterEach -> registration.dispose()
-  #
-  #
-  #   it 'inserts suggestion correctly', =>
-  #     # Trigger an autocompletion
-  #     editor.moveToBottom()
-  #     editor.moveUp()
-  #     editor.moveToEndOfLine()
-  #     atom.commands.dispatch(editorView, 'autocomplete-plus:activate')
-  #
-  #     waitForAutocomplete()
-  #
-  #     runs ->
-  #       expect(editorView.querySelector('.autocomplete-plus')).toExist()
-  #       expect(editor.getBuffer().getLastLine()).toEqual('f')
-  #       expect(editor.getBuffer()
-  #         .lineForRow(
-  #           editor.getLineCount() - 2
-  #         )
-  #       ).toEqual('};')
-  #       suggestionListView = atom.views.getView(autocompleteManager.suggestionList)
-  #       expect(suggestionListView).not.toExist()
-  #       atom.commands.dispatch(suggestionListView, 'autocomplete-plus:confirm')
-  #       expect(editor.getBuffer()
-  #         .lineForRow(
-  #           editor.getLineCount() - 2
-  #         )
-  #       ).toEqual('}function')
-  #
-  #       expect(editor.getBuffer().getLastLine()).toEqual('f')


install now button menu fixed

install now button menu fixed

--- client/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -97,15 +97,10 @@

     if app.versions?.length > 1
-      menu =
-        type : "contextmenu"
-        items : []
+      menu = {}

       for version,i in app.versions
-        menu.items.push
-          id       : i
-          title    : "Install version #{version}"
-          parentId : null
+        menu["Install version #{version}"] =
           callback : (item)=>
             appsController.installApp app, app.versions[item.data.id], (err)=>
               if err then warn err
@@ -118,7 +113,7 @@
           diameter: 30
           color   : "#ffffff"
         delegate      : @
-        menu          : [menu]
+        menu          : menu
         callback      : ->
           appsController.installApp app, 'latest', (err)=>
             @hideLoader()


make the test use the dist instead of src

--- test/ErrorManager.coffee
+++ test/ErrorManager.coffee
@@ -1,4 +1,4 @@
-ErrorManager = require "../src/ErrorManager"
+ErrorManager = require "../dist/ErrorManager"

 describe "ErrorManager.create", ->
     it "should exist", ->


fix bug mediaModal form content

fix bug mediaModal form content

--- MediaAdminBundle/Resources/public/coffee/bindExtendView.coffee
+++ MediaAdminBundle/Resources/public/coffee/bindExtendView.coffee
@@ -1,6 +1,6 @@
 callback_tinymce_init = null
 widgetChannel.bind 'ready', (view) ->
-  if $('[data-prototype*=\'mediaModalOpen\']', view.$el).length > 0
+  if $('[data-prototype*=\'mediaModalOpen\']', view.$el).length > 0 or $('.mediaModalOpen', view.$el).length > 0
     $.extend true, view, extendView['orchestraMediaAbstractType'], extendView['orchestraMediaType']
     view.delegateEvents()
   if $('.tinymce', view.$el).length > 0


missed multer require

missed multer require

--- web.coffee
+++ web.coffee
@@ -4,6 +4,7 @@
 serveStatic = require('serve-static')
 auth = require('http-auth')
 bodyParser = require('body-parser')
+multer = require('multer')

 converter = require('./lib/converter')


Fix error due to assignment in test.

Fix error due to assignment in test.

--- resources/tools/list-templates/heading-controller.coffee
+++ resources/tools/list-templates/heading-controller.coffee
@@ -16,10 +16,10 @@
         model: -> connect.then (s) -> s.fetchModel()
         templates: -> connect.then (s) -> s.fetchTemplates()
         service: -> connect
-      if list = scope.listName?
-        injected.list = -> connect.then (s) -> s.fetchList list
+      if scope.listName?
+        injected.list = -> connect.then (s) -> s.fetchList scope.listName
         injected.items = -> null
-      else if ids = scope.ids?
+      else if scope.ids?
         injected.list = -> null
         injected.items = -> {ids: scope.ids, type: scope.type}


engine: session creation

engine: session creation

--- packages/engine/src/index.coffee
+++ packages/engine/src/index.coffee
@@ -21,5 +21,4 @@

 module.exports = new Proxy create,
   get: (target, name) ->
-    n = create()
-    n[name]
+    create()[name]


Removed temporary fixes for language parsing bug

--- lib/operator-config.coffee
+++ lib/operator-config.coffee
@@ -4,14 +4,14 @@
     leftSpace:  true
     rightSpace: true
     prefixes:   ["+", "-", "&", "|", "<", ">", "!", "~", "%", "/", "*", "."]
-    scope:      "operator|assignment|source"
+    scope:      "operator|assignment"
   }
   ":": {
     alignment:  "right"
     leftSpace:  false
     rightSpace: true
     prefixes:   []
-    scope:      "operator|assignment|source"
+    scope:      "operator|assignment"
   }
   ",": {
     leftSpace:  true


Removed newlines for @RSO

Removed newlines for @RSO

--- core/app/backbone/views/fact_relations/add_evidence_view.coffee
+++ core/app/backbone/views/fact_relations/add_evidence_view.coffee
@@ -37,10 +37,7 @@
       @inputRegion.switchTo 'preview_view'
     @bindTo searchView, 'createFactRelation', (fact_relation) =>
       @createFactRelation(fact_relation)
-
     @bindTo searchView, 'switch_to_comment_view', @switchToCommentView, @
-
-
     searchView

   previewView: ->


updated ldap to match new login api structure

--- _source/packages/newspring:core/server/lib/apollos/ldap.coffee
+++ _source/packages/newspring:core/server/lib/apollos/ldap.coffee
@@ -1,13 +1,14 @@
+Ldapjs = Npm.require("ldapjs")
+
+ldapCreated = false
+ldap = -> return

+class _LDAPclient
+
+  constructor: ->

-class _LDAPsession
-
-  constructor: (username, password) ->
-
-    # ensure no email
-    username = username.split("@")[0]

     @.opts =
       "url": "ldap://cen-dc001.ad.newspring.cc:389"
@@ -16,13 +17,16 @@
       "connectTimeout": 10000
       "nameAttribute": "samaccountname"

-    @.ldapClient = Npm.require("ldapjs").createClient(
+    @.ldapClient = Ldapjs.createClient(
       url: @.opts.url
       timeout: @.opts.timeout
     )

   authenticate: (username, password) =>
+
+    # ensure no email
+    username = username.split("@")[0]

     username = "ns\\#{username}"

@@ -53,14 +57,17 @@
 ###
 Apollos.user.login.ldap = (username, password) ->

+  if not ldapCreated
+    ldap = new _LDAPclient()
+
   # this will need to be a different check because non newspring emails can be in ldap
   if not username.match /@newspring.cc/
     return

   try
-    authenticated = new _LDAPsession(username, password)
+    authenticated = ldap.authenticate username, password

-    authenticated.ldapClient.unbind( (err) ->
+    ldap.ldapClient.unbind( (err) ->

       if err
         console.log err
@@ -71,7 +78,8 @@

   catch error
     debug error
-    throw new Meteor.Error(JSON.stringify(error), false)
+    return false
+    # throw new Meteor.Error(JSON.stringify(error), false)
     return


removing deprecated: Atom.Object.defineProperty.get is deprecated closes #5,6,7

--- lib/python-debugger.coffee
+++ lib/python-debugger.coffee
@@ -4,8 +4,8 @@
   pythonDebuggerView: null

   activate: ->
-    atom.workspaceView.command "python-debugger:insert", => @insert()
-    atom.workspaceView.command "python-debugger:remove", => @remove()
+    atom.commands.add 'atom-workspace', "python-debugger:insert", => @insert()
+    atom.commands.add 'atom-workspace', "python-debugger:remove", => @remove()

   insert: ->
     IMPORT_STATEMENT = "import ipdb\n"


Update KeypathObserver to have better state management, passing in the previous target to the callback for unbinding.

--- src/keypath_observer.coffee
+++ src/keypath_observer.coffee
@@ -1,15 +1,27 @@
 class KeypathObserver
   constructor: (@view, @model, @keypath, @callback) ->
-    @interfaces = (k for k, v of @view.adapters)
+    @parse()
     @objectPath = []
-    @tokens = Rivets.KeypathParser.parse @keypath, @interfaces, @view.config.rootInterface
-    @root = @tokens.shift()
+    @target = @realize()
+
+  parse: =>
+    interfaces = (k for k, v of @view.adapters)
+
+    if @keypath[0] in interfaces
+      root = @keypath[0]
+      path = @keypath.substr 1
+    else
+      root = @view.config.rootInterface
+      path = @keypath
+
+    @tokens = Rivets.KeypathParser.parse path, interfaces, root
     @key = @tokens.pop()
-    @target = @realize()

   update: =>
     unless (next = @realize()) is @target
-      @callback @target = next
+      prev = @target
+      @target = next
+      @callback @, prev

   realize: =>
     current = @model


ok, allow 200ms

ok, allow 200ms

--- controllers/websocket.coffee
+++ controllers/websocket.coffee
@@ -3,7 +3,7 @@

 app.ws?.sockets.on 'connection', (client) ->
   client.on 'message', (data) ->
-    return if client.lastMessageAt && Date.now() - client.lastMessageAt < 1000
+    return if client.lastMessageAt && Date.now() - client.lastMessageAt < 200
     #console.log client.id, data.length, Date.now() - (client.lastMessageAt || 0)
     client.lastMessageAt = Date.now()
     data = JSON.parse(data)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -4,5 +4,6 @@
   player = GameObject
     x: 10
     y: 20
+    width:

 module()


run: print multiple errors

run: print multiple errors

--- lib/commands/run.coffee
+++ lib/commands/run.coffee
@@ -111,8 +111,13 @@
     rl.write "#{styles.final_status_success 'Installation is finished'}\n"
     rl.close()
   .on 'error', (err) ->
-    rl.write '\n'
-    rl.write "{styles.final_status_error err.stack or err.message}"
+    if err.errors
+      rl.write '\n'
+      rl.write "#{err.message}\n"
+      for err in err.errors
+        rl.write "#{styles.final_status_error err.stack.trim() or err.message}\n"
+    else
+      rl.write "#{styles.final_status_error err.stack.trim() or err.message}\n"
     rl.close()


Bootstrapper: Remove dummy code.

Bootstrapper: Remove dummy code.

--- src/lib/Bootstrapper.coffee
+++ src/lib/Bootstrapper.coffee
@@ -22,25 +22,6 @@
   ###
   constructor: ->

-    k = new spark.components.ComboBox {}, [
-      { title: 'Fiat', value: 'fiat' }
-      { title: 'Ford', value: 'ford' }
-      { title: 'Mazda', value: 'mazda' }
-      { title: 'BMW', value: 'bmw' }
-      { title: 'Honda', value: 'honda' }
-      { title: 'Ferrari', value: 'ferrari' }
-    ]
-
-    k.appendToDocumentBody()
-
-    k.on 'selected', (event) ->
-      {title, value, data} = event.data
-
-      console.log title, value, data
-
-
-    window.k = k
-

 window.addEventListener "load", (->
   new spark.Bootstrapper


Trigger button click

Trigger button click

--- source/molecule/form.coffee
+++ source/molecule/form.coffee
@@ -27,3 +27,7 @@

   inputKeyup: (event, atom) =>
     @trigger "keyup", event.keyCode, atom
+
+  buttonClick: (event, atom) =>
+    event.preventDefault()
+    @trigger "click", event, atom


Combine duplicate code from placholder and value set methods

--- src/coffee/cilantro/ui/field/controls/date.coffee
+++ src/coffee/cilantro/ui/field/controls/date.coffee
@@ -39,21 +39,30 @@
         parseMaxStat: (value) ->
             @_parseDate(value)

-        # Override the default behavior so the date is formatted correctly
-        # for use in the placeholder.
-        setLowerBoundPlaceholder: (value) ->
+        # Sets the placeholder value on the supplied element to the date value
+        _setPlaceholder: (element, value) ->
             date = new Date(value)

             # We want to display the date in mm/dd/yyyy format. Also, since
             # months are 0 based, we need to add one there when formatting the
             # date string for placeholder use.
             dateStr = "#{date.getMonth() + 1}/#{date.getDate()}/#{date.getFullYear()}"
-            @ui.lowerBound.attr('placeholder', dateStr)
+            element.attr('placeholder', dateStr)

-        # We need to override the default behavior here so that the value is
-        # applied to the datepicker rather than the textbox. The datepicker
-        # will handle the updating of the textbox.
-        setLowerBoundValue: (value) ->
+        # Override the default behavior so the date is formatted correctly
+        # for use in the placeholder.
+        setLowerBoundPlaceholder: (value) ->
+            @_setPlaceholder(@ui.lowerBound, value)
+
+        # Override the default behavior so the date is formatted correctly
+        # for use in the placeholder.
+        setUpperBoundPlaceholder: (value) ->
+            @_setPlaceholder(@ui.upperBound, value)
+
+        # Sets the value of the supplied element to the date value or clears
+        # the value of the element if the value or the call to val() is
+        # null or empty.
+        _setValue: (element, value) ->
             # Since the changeDate event is causing this method to receive a
             # query object we need to read the value of the date here. The
             # setValue method is always getting an array with the .range-lower
@@ -62,36 +71,21 @@
             dateString = value?.val()

             if dateString? and dateString != ""
-                @ui.lowerBound.datepicker('setDate', new Date(dateString))
+                element.datepicker('setDate', new Date(dateString))
             else
-                @ui.lowerBound.datepicker('_clearDate', this)
+                element.datepicker('_clearDate', this)

-        # Override the default behavior so the date is formatted correctly
-        # for use in the placeholder.
-        setUpperBoundPlaceholder: (value) ->
-            date = new Date(value)
-
-            # We want to display the date in mm/dd/yyyy format. Also, since
-            # months are 0 based, we need to add one there when formatting the
-            # date string for placeholder use.
-            dateStr = "#{date.getMonth() + 1}/#{date.getDate()}/#{date.getFullYear()}"
-            @ui.upperBound.attr('placeholder', dateStr)
+        # We need to override the default behavior here so that the value is
+        # applied to the datepicker rather than the textbox. The datepicker
+        # will handle the updating of the textbox.
+        setLowerBoundValue: (value) ->
+            @_setValue(@ui.lowerBound, value)

         # We need to override the default behavior here so that the value is
         # applied to the datepicker rather than the textbox. The datepicker
         # will handle the updating of the textbox.
         setUpperBoundValue: (value) ->
-            # Since the changeDate event is causing this method to receive a
-            # query object we need to read the value of the date here. The
-            # setValue method is always getting an array with the .range-lower
-            # and .range-upper jquery objects in it so checking for empty
-            # values is done here to keep setValue generic.
-            dateString = value?.val()
-
-            if dateString? and dateString != ""
-                @ui.upperBound.datepicker('setDate', new Date(dateString))
-            else
-                @ui.upperBound.datepicker('_clearDate', this)
+            @_setValue(@ui.upperBound, value)

     {DateControl}


Repositions private module method for readability

--- source/server/module.coffee
+++ source/server/module.coffee
@@ -58,6 +58,9 @@
     @injector.map('Space.eventSourcing.Log').to =>
       console.log.apply(null, arguments) if @Configuration.eventSourcing.log.enabled

+  _setupMongoConfiguration: ->
+    @Configuration.eventSourcing.mongo.connection = @_mongoConnection()
+
   _setupCommitsCollection: ->
     if Space.eventSourcing.commitsCollection?
       CommitsCollection = Space.eventSourcing.commitsCollection
@@ -79,9 +82,6 @@
     else
       return {}

-  _setupMongoConfiguration: ->
-    @Configuration.eventSourcing.mongo.connection = @_mongoConnection()
-
   _externalMongo: ->
     true if Space.getenv('SPACE_ES_COMMITS_MONGO_URL', '').length > 0


Fix multline auto-complete typo

Fix multline auto-complete typo

--- services/web/public/coffee/auto-complete/Snippets.coffee
+++ services/web/public/coffee/auto-complete/Snippets.coffee
@@ -4,7 +4,7 @@
 		"align", "align*",
 		"equation", "equation*",
 		"gather", "gather*",
-		"mutliline", "multiline*",
+		"multline", "multline*",
 		"split",
 		"verbatim"
 	]


Admin: Stacks: Show and update stack preview.

--- client/admin/lib/views/stacks/onboarding/onboardingview.coffee
+++ client/admin/lib/views/stacks/onboarding/onboardingview.coffee
@@ -35,6 +35,11 @@

   bindPageEvents: ->
+
+    @pages.forEach (page) =>
+      page.on 'StackTemplateNeedsToBeUpdated', =>
+        @stackPreview.show()
+        @updateStackTemplate()

     @on 'PageNavigationRequested', (direction) =>
       pageIndex  = @pages.indexOf @currentPage
@@ -99,6 +104,8 @@
     @stackPreview.addSubView @stackContent = new kd.CustomHTMLView

+  updateStackTemplate: ->
+
   pistachio: ->

     return """


Workspace: Fixed loading for collaborative workspace.

--- client/app/CommonViews/workspace/collaborativeworkspace.coffee
+++ client/app/CommonViews/workspace/collaborativeworkspace.coffee
@@ -140,8 +140,8 @@
       partial  : """<span class="text">Loading...<span>"""

     @loader.addSubView loaderView = new KDLoaderView size: width : 36
+    @loader.on "viewAppended", -> loaderView.show()
     @container.addSubView @loader
-    @loader.on "viewAppended", -> loaderView.show()

   joinSession: (sessionKey) ->
     {parent}           = @


stop console stealing focus

stop console stealing focus

fixes #131

--- lib/console/view.coffee
+++ lib/console/view.coffee
@@ -114,8 +114,10 @@
     return unless @hasFocus()
     y = @scrollTop
     atom.views.getView(@lastInput)?.focus()
-    s = @lastInput.onDidChange =>
-      atom.views.getView(@lastInput)?.focus()
+    s = @lastInput.onDidChange (e) =>
+      # check if e is empty object
+      if not (Object.keys(e).length is 0 and e.constructor is Object)
+        atom.views.getView(@lastInput)?.focus()
       s?.dispose()
     @scrollTop = y


make arc angle conventions consistent

make arc angle conventions consistent

--- bokehjs/src/coffee/renderer/glyph/arc.coffee
+++ bokehjs/src/coffee/renderer/glyph/arc.coffee
@@ -22,7 +22,7 @@
             continue

           ctx.beginPath()
-          ctx.arc(sx[i], sy[i], radius[i], -@start_angle[i], -@end_angle[i], @direction[i])
+          ctx.arc(sx[i], sy[i], radius[i], @start_angle[i], @end_angle[i], @direction[i])

           glyph_props.line_properties.set_vectorize(ctx, i)
           ctx.stroke()


Re-enable setSource tests for browser

Re-enable setSource tests for browser

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -370,9 +370,6 @@
             ins.send 'CoffeeScript'

     describe 'with non-working code', ->
-      before ->
-        # setSource not implemented in webpack loader yet
-        return @skip() if noflo.isBrowser()
       nonWorkingSource = """
       var noflo = require('noflo');
       var notFound = require('./this_file_does_not_exist.js');


try to fix iphone click

try to fix iphone click

--- app/assets/javascripts/man.coffee
+++ app/assets/javascripts/man.coffee
@@ -10,9 +10,16 @@
   $('body').on 'click', '@button', ->
     window.app.request()

+  $('body').on 'touchstart', '@button', ->
+    window.app.request()
+
   $('body').on 'click', '@logo', ->
     window.location.hash = ''
     window.location.reload()
+
+    $('body').on 'touchstart', '@logo', ->
+      window.location.hash = ''
+      window.location.reload()

   $('@urlfield').on 'keypress', (e) ->
     $('@form').removeClass('error')
@@ -22,6 +29,10 @@
     $('@form').removeClass('error')

   $('body').on 'click', '@suggest-button', (e) ->
+    $('@urlfield').val($(@).data('domain'))
+    window.app.request()
+
+  $('body').on 'touchstart', '@suggest-button', (e) ->
     $('@urlfield').val($(@).data('domain'))
     window.app.request()

@@ -35,9 +46,15 @@
   $('body').on 'click', ->
     $('@sharing-result-mobile').hide()

+    $('body').on 'touchstart', '@sharing-button', (e) ->
+      e.stopPropagation()
+      if $('@sharing-result-mobile').css('display') != 'none'
+        $('@sharing-result-mobile').hide()
+      else
+        $('@sharing-result-mobile').show()

-  $('@iframe').contents().find('body').on 'click', ->
-    $('@sharing-result-mobile').hide()
+    $('body').on 'click', ->
+      $('@sharing-result-mobile').hide()

 window.app = window.app || {}
 window.app.request = ->


Remove double-quotes

Remove double-quotes

--- test/spec/test_reactive.coffee
+++ test/spec/test_reactive.coffee
@@ -81,17 +81,17 @@
         y: 20
         height: 100
         width: 100
-        fill: "blue"
-        stroke: "red"
+        fill: 'blue'
+        stroke: 'red'
       }, [
         rxt.tags.animatetransform({
-          attributeName: "transform"
-          begin: "0s"
-          dur: "20s"
-          type: "rotate"
-          from: "0 60 60"
-          to: "360 60 60"
-          repeatCount: "indefinite"
+          attributeName: 'transform'
+          begin: '0s'
+          dur: '20s'
+          type: 'rotate'
+          from: '0 60 60'
+          to: '360 60 60'
+          repeatCount: 'indefinite'
         })
       ]


set min option to zero

set min option to zero

--- app/assets/javascripts/top.coffee
+++ app/assets/javascripts/top.coffee
@@ -89,4 +89,5 @@
     options:
       scale:
         ticks:
+          min: 0,
           suggestedMax: 5)


Index does not need $location

Index does not need $location

--- src/javascript/controllers/IndexController.coffee
+++ src/javascript/controllers/IndexController.coffee
@@ -1,6 +1,6 @@
 ###*
 IndexController. Responsible for the index view.
 ###
-app.controller "IndexController", ($scope, $location) ->
+app.controller "IndexController", ($scope) ->
   $scope.whatsMyName = "Welcome my friend"
   return


More verbose error reporting in start timeout test

--- test/start_timeout.test.coffee
+++ test/start_timeout.test.coffee
@@ -33,8 +33,13 @@
         done exitCodeError

   it 'mentions helpful details', ->
-    assert.include 'command: coffee', @stderr
-    assert.include 'test/app', @stderr
-    assert.include 'timeout: 250ms', @stderr
-    assert.include 'test/start_timeout_log/application.log', @stderr
-    assert.include '> Refusing to listen', @stderr
+    try
+      assert.include 'command: coffee', @stderr
+      assert.include 'test/app', @stderr
+      assert.include 'timeout: 250ms', @stderr
+      assert.include 'test/start_timeout_log/application.log', @stderr
+      assert.include '> Refusing to listen', @stderr
+    catch error
+      console.log "stdout: #{@stdout}"
+      console.log "stderr: #{@stderr}"
+      throw error


operations: syntax fixes

operations: syntax fixes

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -1198,8 +1198,7 @@
             notifications = []
             if (notification = op.notification?())
                 # Extend by subscriptions node notifications
-                notification = notification.concat
-                    generateSubscriptionsNotifications(notification)
+                notification = notification.concat generateSubscriptionsNotifications(notification)
                 # Call Notify operation grouped by node
                 # (looks up subscribers by node)
                 for own node, notifications of groupByNode(notification)
@@ -1228,10 +1227,9 @@
         update.type is 'affiliation'
     ).map((update) ->
         followee = update.node.match(NODE_OWNER_TYPE_REGEXP)?[1]
-        return
-            type: 'items'
-            node: "/user/#{update.user}/subscriptions"
-            items: [{ id: update.followee }]
+        type: 'items'
+        node: "/user/#{update.user}/subscriptions"
+        items: [{ id: update.followee }]
         # Actual item payload will be completed by Notify transaction
     ).filter((update) ->
         itemId = update.items[0].id


removed console logs

removed console logs


added more tests for passing functions as arguments and grouped them

--- test/test-chain-builder.coffee
+++ test/test-chain-builder.coffee
@@ -113,6 +113,7 @@
       it 'should return an noop function', ->
         fn = builder.chain []
         noop = /function\s?\(\)\s?{\n\s*return true;\n\s*}/
+        console.log 'noop:',fn
         assert.equal noop.test(''+fn), true

     describe 'test empty pipeline', ->
@@ -144,26 +145,79 @@
         assert.equal ctx.ran, true
         assert.equal result, true

-    describe 'test passing function to chain', ->
-
-      it 'should give a chain for the single function', ->
-        ctx =
-          ran: false
+  describe 'test providing functions instead of array', ->
+
+    describe 'test one function to chain', ->
+
+      it 'should give a chain for the one function', ->
+        ctx = ran:false
         com = (context) -> context.ran = true
         fn = builder.chain com
         result = fn ctx
         assert.equal ctx.ran, true
         assert.equal result, true

-    describe 'test passing function to pipeline', ->
-
-      it 'should give a pipeline for the single function', ->
-        ctx =
-          ran: false
+    describe 'test one function to pipeline', ->
+
+      it 'should give a pipeline for the one function', ->
+        ctx = ran:false
         com = (next, context) -> context.ran = true ; next context
         fn = builder.pipeline com
         result = fn ctx
         assert.equal ctx.ran, true
+        assert.equal result, true
+
+    describe 'test two functions to chain', ->
+
+      it 'should give a chain for the two functions', ->
+        ctx = ran1: false, ran2: false
+        com1 = (context) -> context.ran1 = true
+        com2 = (context) -> context.ran2 = true
+        fn = builder.chain com1, com2
+        result = fn ctx
+        assert.equal ctx.ran1, true
+        assert.equal ctx.ran2, true
+        assert.equal result, true
+
+    describe 'test two functions to pipeline', ->
+
+      it 'should give a pipeline for the two functions', ->
+        ctx = ran1: false, ran2: false
+        com1 = (next, context) -> context.ran1 = true ; next context
+        com2 = (next, context) -> context.ran2 = true ; next context
+        fn = builder.pipeline com1, com2
+        result = fn ctx
+        assert.equal ctx.ran1, true
+        assert.equal ctx.ran2, true
+        assert.equal result, true
+
+    describe 'test three functions to chain', ->
+
+      it 'should give a chain for the three functions', ->
+        ctx = ran1: false, ran2: false, ran3: false
+        com1 = (context) -> context.ran1 = true
+        com2 = (context) -> context.ran2 = true
+        com3 = (context) -> context.ran3 = true
+        fn = builder.chain com1, com2, com3
+        result = fn ctx
+        assert.equal ctx.ran1, true
+        assert.equal ctx.ran2, true
+        assert.equal ctx.ran3, true
+        assert.equal result, true
+
+    describe 'test three functions to pipeline', ->
+
+      it 'should give a pipeline for the three functions', ->
+        ctx = ran1: false, ran2: false, ran3: false
+        com1 = (next, context) -> context.ran1 = true ; next context
+        com2 = (next, context) -> context.ran2 = true ; next context
+        com3 = (next, context) -> context.ran3 = true ; next context
+        fn = builder.pipeline com1, com2, com3
+        result = fn ctx
+        assert.equal ctx.ran1, true
+        assert.equal ctx.ran2, true
+        assert.equal ctx.ran3, true
+        assert.equal result, true
         assert.equal result, true

   describe 'test array is cloned in chain', ->


test passing no context, add corresponding pipeline test for context edits

--- test/test-chain-builder.coffee
+++ test/test-chain-builder.coffee
@@ -181,7 +181,7 @@

     it 'should be unaffected by altering array after build', ->
       ctx = ran1: false, ran2: false
-      com1 = (context) -> context.ran1 = true
+      com1 = (context, next) -> context.ran1 = true ; next context
       com2 = (context) -> context.ran2 = true
       array = [com1]
       fn = builder.pipeline array
@@ -221,7 +221,7 @@

     describe 'test adding value to context', ->

-      it 'should be available to next command', ->
+      it 'should be available to next command in chain', ->
         ctx = {}
         com1 = (context) -> context.add = 'added'
         com2 = (context) -> if context?.add? then context.seen = true
@@ -230,3 +230,41 @@
         assert.equal result, true
         assert.equal ctx.add, 'added'
         assert.equal ctx.seen, true
+
+      it 'should be available to next command in pipeline', ->
+        ctx = {}
+        com1 = (context, next) -> context.add = 'added' ; next context
+        com2 = (context, next) ->
+          if context?.add? then context.seen = true
+          next context
+        fn = builder.pipeline [com1, com2]
+        result = fn ctx
+        assert.equal ctx.seen, true
+        assert.equal ctx.add, 'added'
+
+    describe 'test not passing a context to chain', ->
+
+      it 'should have an empty object context', ->
+        test = ran: false, context: false
+        com1 = (context) ->
+          test.ran = true
+          if context? then test.context = true
+        fn = builder.chain [com1]
+        result = fn()
+        assert.equal result, true, 'result should be true'
+        assert.equal test.ran, true
+        assert.equal test.context, true
+
+    describe 'test not passing a context to pipeline', ->
+
+      it 'should have an empty object context', ->
+        test = ran: false, context: false
+        com1 = (context, next) ->
+          test.ran = true
+          if context? then test.context = true
+          next context
+        fn = builder.pipeline [com1]
+        result = fn()
+        assert.equal result, true, 'result should be true'
+        assert.equal test.ran, true
+        assert.equal test.context, true


fixed invert bug where frame resets to first

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -133,20 +133,20 @@
           # 'xlink:href': DEV_SUBJECTS[i]   # use hardcoded static images

     @stopLoading()
-    @markingSurface.enable()
+
+    #@markingSurface.enable() # why is this here again?
+    # console.log "Showing frame: " + @currentFrameIdx
+    # @showFrame(@currentFrameIdx) unless @currentFrameIdx is null

   onClickRadioButton: ->
-    console.log "radio button clicked"
     for i in [0...@frameRadioButtons.length]
       if @frameRadioButtons[i].checked
-        console.log "  - show frame: " + i
         @showFrame(i)

   onClickPlay: ->
     @play()

   play: ->
-
     @markingSurface.disable()

     # flip the images back and forth once
@@ -174,9 +174,7 @@
     @currentFrameIdx = frame_idx
     # find way to communicate current frame with marking tool

-
   hideAllFrames: ->
-    console.log "hiding all frames"
     for i in [0...@frameRadioButtons.length]
       @hideFrame(i)

@@ -188,6 +186,8 @@

     @frameRadioButtons[frame_idx].checked = "true"
     @setCurrentFrameIdx(frame_idx)
+
+    console.log "show frame: " + frame_idx

   hideFrame: (frame_idx) ->
     document.getElementById("frame-id-#{frame_idx}").style.visibility="hidden"
@@ -207,6 +207,8 @@
       # console.log "invert: true"

     @loadFrames()
+
+    @showFrame(@currentFrameIdx) unless @currentFrameIdx is undefined

   onClickFinishMarking: ->
     @showSummary()


:green_heart:

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -294,7 +294,7 @@
       editorFullWidth = editor.getScrollWidth() + editor.getVerticalScrollbarWidth()

       for lineNode in lineNodes
-        expect(lineNode.style.width).toBe editorFullWidth + 'px'
+        expect(lineNode.getBoundingClientRect().width).toBe(editorFullWidth)

       componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px'
       component.measureDimensions()
@@ -302,7 +302,7 @@
       scrollViewWidth = scrollViewNode.offsetWidth

       for lineNode in lineNodes
-        expect(lineNode.style.width).toBe scrollViewWidth + 'px'
+        expect(lineNode.getBoundingClientRect().width).toBe(scrollViewWidth)

     it "renders an nbsp on empty lines when no line-ending character is defined", ->
       atom.config.set("editor.showInvisibles", false)


Fix spec failure

Fix spec failure

@as-cii this was just a typo, but there’s a failure later in the spec
that you’re probably in a better position to fix quickly.

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -680,7 +680,7 @@

           expect(presenter.getState().content.tiles[8]).toBeUndefined()

-          expectStateUpdate -> presenter.setScrollTop(3)
+          expectStateUpdate presenter, -> presenter.setScrollTop(3)

           expect(presenter.getState().content.tiles[0]).toBeUndefined()


change suffix so hipchat shows image

change suffix so hipchat shows image

hipchat wasn't matching the $png to show the image, so change to &.png

--- src/scripts/graphite.coffee
+++ src/scripts/graphite.coffee
@@ -43,7 +43,7 @@
   server = process.env.GRAPHITE_URL.match(serverRegex)[2]
   port = construct_port()
   timestamp = '#' + new Date().getTime()
-  suffix = '&png'
+  suffix = '&.png'
   if process.env.GRAPHITE_AUTH
     newUrl = proto + process.env.GRAPHITE_AUTH + '@' + server + port + uri + timestamp + suffix
   else


Carry out actions on Sprint.ly items

Carry out actions on Sprint.ly items

--- src/scripts/sprintly.coffee
+++ src/scripts/sprintly.coffee
@@ -2,34 +2,16 @@
 #
 # sprintly [product_id] [status] [limit] - list items in status (default status is in-progress, other values: backlog, completed, accepted; default limit is 20)
 # sprintly [product_id] mine - list items assigned to me
-# sprintly [product_id] #42 - show item number 42
-# sprintly [product_id] #42 tasks - list unfinished subtasks of story number 42
+# sprintly [product_id] #42 - show item #42
+# sprintly [product_id] #42 tasks - list unfinished subtasks of story #42
+# sprintly [product_id] <action> #42 - carry out action on item #42 (available actions: start, stop, finish, accept, reject, delete)
 # sprintly token <email:apitoken> - set/update credentials for user (required for other commands to work)
 # sprintly default 1234 - set default product_id
 #

+qs = require 'querystring'
+
 module.exports = (robot) ->
-
-  sprintlyUser = (msg) ->
-    robot.brain.data.sprintly ?= {}
-    robot.brain.data.sprintly[msg.message.user.id] ?= {}
-
-  sprintly = (msg, auth) ->
-    if auth ?= sprintlyUser(msg).auth
-      client = msg.http('https://sprint.ly')
-        .header('accept', 'application/json')
-        .header('authorization', "Basic #{new Buffer(auth).toString('base64')}")
-        .path('/api')
-      client.product = ->
-        if product_id = msg.match[1] ? robot.brain.data.sprintly?.product_id
-          @path("/api/products/#{product_id}")
-        else
-          msg.send "No Product Id has been specified, you can set a default with 'sprintly default 1234'"
-          new DummyClient()
-      client
-    else
-      msg.send "API token not found, set it with 'sprintly token <email:apitoken>'"
-      new DummyClient()

   robot.respond /sprintly +token +(.*)/i, (msg) ->
     sprintly(msg, msg.match[1])
@@ -55,24 +37,11 @@
       .get()(formatItems(msg))

   robot.respond /sprintly +(?:(\d+) +)?mine *$/, (msg) ->
-    client = sprintly(msg).product()
-    user = sprintlyUser(msg)
-
-    listMine = -> client.scope('items.json').query(assigned_to: user.user_id).get()(formatItems(msg))
-
-    if user.user_id
-      listMine()
-    else
-      client.scope('people.json').get() (err, res, body) ->
-        if res.statusCode == 200
-          payload = JSON.parse(body)
-          user_email = user.auth.split(':')[0]
-          for {id, email} in payload when email == user_email
-            user.user_id = id
-            listMine()
-            break
-        else
-          msg.send "Something came up: #{body}"
+    withUserId msg, (user_id) ->
+      sprintly(msg).product()
+        .scope('items.json')
+        .query(assigned_to: user_id)
+        .get()(formatItems(msg))

   robot.respond /sprintly +(?:(\d+) +)?#(\d+) *$/, (msg) ->
     sprintly(msg).product()
@@ -81,7 +50,7 @@
         if res.statusCode == 200
           item = JSON.parse(body)
           msg.send itemSummary(item)
-          msg.send item.description
+          msg.send item.description if item.description
           meta = [
             "status: #{item.status}"
             "assigned_to: #{if u = item.assigned_to then "#{u.first_name} #{u.last_name}" else "nobody"}"
@@ -97,12 +66,62 @@
       .scope("items/#{msg.match[2]}/children.json")
       .get()(formatItems(msg, true))

+  robot.respond /sprintly +(?:(\d+) +)?(start|stop|finish|accept|reject|delete) +#?(\d+) *$/, (msg) ->
+    withUserId msg, (user_id) ->
+      query = {}
+      method = 'post'
+      switch msg.match[2]
+        when 'start'
+          query.status = 'in-progress'
+          query.assigned_to = user_id
+        when 'stop'
+          query.status = 'backlog'
+        when 'finish'
+          query.status = 'completed'
+        when 'accept'
+          query.status = 'accepted'
+        when 'reject'
+          query.status = 'in-progress'
+        when 'delete'
+          method = 'delete'
+      sprintly(msg).product()
+        .scope("items/#{msg.match[3]}.json")[method](qs.stringify(query)) (err, res, body) ->
+          if res.statusCode < 400
+            item = JSON.parse(body)
+            if msg.match[2] is 'delete'
+              msg.send "##{item.number} has been archived"
+            else
+              msg.send "##{item.number} status: #{item.status}"
+          else
+            msg.send "Something came up: #{body}"
+
 DummyClient = ->
 self = -> this
 for method in ['scope', 'query', 'product']
   DummyClient::[method] = self
 for method in ['get', 'post', 'put', 'delete']
   DummyClient::[method] = -> self
+
+sprintlyUser = (msg) ->
+  sp = msg.robot.brain.data.sprintly ?= {}
+  sp[msg.message.user.id] ?= {}
+
+sprintly = (msg, auth) ->
+  if auth ?= sprintlyUser(msg).auth
+    client = msg.http('https://sprint.ly')
+      .header('accept', 'application/json')
+      .header('authorization', "Basic #{new Buffer(auth).toString('base64')}")
+      .path('/api')
+    client.product = ->
+      if product_id = msg.match[1] ? msg.robot.brain.data.sprintly?.product_id
+        @path("/api/products/#{product_id}")
+      else
+        msg.send "No Product Id has been specified, you can set a default with 'sprintly default 1234'"
+        new DummyClient()
+    client
+  else
+    msg.send "API token not found, set it with 'sprintly token <email:apitoken>'"
+    new DummyClient()

 itemSummary = (item, subtask=false) ->
   parts = ["##{item.number}"]
@@ -124,3 +143,21 @@
         msg.send no_items_msg
     else
       msg.send "Something came up: #{body}"
+
+withUserId = (msg, callback) ->
+  user = sprintlyUser(msg)
+  if user.user_id
+    callback(user.user_id)
+  else
+    sprintly(msg).product()
+      .scope('people.json')
+      .get() (err, res, body) ->
+        if res.statusCode == 200
+          payload = JSON.parse(body)
+          user_email = user.auth.split(':')[0]
+          for {id, email} in payload when email == user_email
+            user.user_id = id
+            callback(id)
+            break
+        else
+          msg.send "Something came up: #{body}"


Modify default keymap setting.

Modify default keymap setting.

--- keymaps/atom-markdown-katex.cson
+++ keymaps/atom-markdown-katex.cson
@@ -8,4 +8,4 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/behind-atom-keymaps-in-depth
 'atom-workspace':
-  'ctrl-alt-o': 'atom-markdown-katex:toggle'
+  'ctrl-alt-o': 'markdown-katex-preview:toggle'


:lipstick:

:lipstick:

--- src/app/atom-package.coffee
+++ src/app/atom-package.coffee
@@ -6,9 +6,7 @@
 module.exports =
 class AtomPackage extends Package
   metadata: null
-
-  constructor: ->
-    super
+  packageMain: null

   load: ({activateImmediately}={}) ->
     try


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

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

 [1, 3, 5, 7].average()
-# => [3, 3, 5]
+# => 4

 @name compact


Add a small note about where ipc 'open'.

--- src/atom-application.coffee
+++ src/atom-application.coffee
@@ -145,6 +145,7 @@
       event.preventDefault()
       @applicationMenu.showDownloadUpdateItem(version, quitAndUpdateCallback)

+    # A request from the associated browser process to open a new browser process.
     ipc.on 'open', (processId, routingId, options) =>
       if options?
         if options.pathsToOpen?.length > 0


Add LineFolder.lastRow

Add LineFolder.lastRow

--- src/atom/line-folder.coffee
+++ src/atom/line-folder.coffee
@@ -86,6 +86,9 @@
   lineCount: ->
     @lineMap.screenLineCount()

+  lastRow: ->
+    @lineCount() - 1
+
   screenRowForBufferRow: (bufferRow) ->
     @screenPositionForBufferPosition([bufferRow, 0]).row


webapp/zendesk: fine tuning email box

webapp/zendesk: fine tuning email box

--- src/smc-webapp/support.cjsx
+++ src/smc-webapp/support.cjsx
@@ -245,7 +245,7 @@
         for i, ticket of @props.support_tickets
             style = switch ticket.status
                 when 'open' or 'new'
-                    'warning'
+                    'danger'
                 when 'closed'
                     'success'
                 else
@@ -429,9 +429,14 @@
         if not @props.show
             return

-        alert = if @props.email_err?.length > 0
+        ee = @props.email_err
+        email_info = if ee?.length > 0
             <Alert bsStyle='danger'>
-
{@props.email_err}

+
{ee}

+            </Alert>
+        else
+            <Alert bsStyle='info'>
+                Please make sure the email address is correct.
             </Alert>

         <form>
@@ -441,13 +446,10 @@
                 type        = 'text'
                 tabIndex    = 1
                 placeholder = 'your_email@address.com'
-                bsStyle     = {if ee? then 'warning'}
+                validationState = {if ee?.length > 0 then 'error'}
                 value       = {@props.email}
                 onChange    = {@email_change} />
-            {alert if alert?}
-

-                Please make sure your email address is correct.
-

+            {email_info}
             <Space />
             <Input
                 ref         = 'subject'


encourage more helpful support requests....

--- src/smc-webapp/support.cjsx
+++ src/smc-webapp/support.cjsx
@@ -479,6 +479,9 @@
                     value       = {@props.subject}
                     onChange    = {@data_change} />
             </FormGroup>
+            <div style={margin:'10px', color:'#666'}>
+                1. What did you do exactly?  2. What happened?  3. How did this differ from what you expected?
+            </div>
             <FormGroup>
                 <FormControl
                     componentClass = "textarea"


Syepped okSubmits date

Syepped okSubmits date

--- server/api/dashboard.coffee
+++ server/api/dashboard.coffee
@@ -56,7 +56,7 @@
 export default dashboard = () ->
     queries =
         # remember that months start from 0
-        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2017, 9, 4)}},
+        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2017, 9, 5)}},
         wa: {solved: 0, ok: 0, ignored: 0, attempts: {$gt: 0}},
         ig: {ignored: 1},
         ac: {solved: 1}


Update CLI tests

Update CLI tests

--- test/scriptscript.coffee
+++ test/scriptscript.coffee
@@ -36,6 +36,22 @@

   it "without arguments starts a REPL", (done)->
     child = chipro.spawn sscript
+    output = ""
+    child.stdout.on "data", (data)->
+      output += data.toString()
+    child.stdin.write sample1 + '\n'
+    child.stdin.end   sample2 + '\n'
+    child.on "close", (code)->
+      # p output
+      assert.equal output, """
+        > #{output1}#{res1}
+        > #{output2}#{res2}
+        >
+        """
+      done()
+
+  it "s-s -i also starts a REPL", (done)->
+    child = chipro.spawn sscript, ["-i"]
     output = ""
     child.stdout.on "data", (data)->
       output += data.toString()
@@ -106,7 +122,7 @@
     assert.equal stdout, compiled1 + '\n' + compiled2 + '\n'
     done err

-  it "FIXME cat *.ss | s-s -s" # need semicolon after (2+2)
+  it "FIXME cat *.ss | s-s -se" # need semicolon after (2+2)

   it "cat 2.ss | s-s -se", (done)->
     await chipro.exec "cat 2.ss | #{sscript} -se", defer err, stdout, stderr


Moves miniScrollView affectation

Moves miniScrollView affectation

There’s no reason the minimap scroll view changes over time as we
changes its content. We can store it once after creation.

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -25,6 +25,7 @@
     @scaleX = 0.2
     @scaleY = @scaleX * 0.8
     @minimapScale = @scale(@scaleX, @scaleY)
+    @miniScrollView = @miniEditorView.scrollView

   initialize: ->
     @attach()
@@ -86,7 +87,6 @@
   updateMinimapView: ->
     unless @paneView.find('.minimap').length
       @miniEditorView.css width: @scrollView.width()
-      @miniScrollView = @miniEditorView.scrollView
       @paneView.addClass('with-minimap').append(this)

     if !@editor


Fix broken require for text subtasks (#4761)

--- app/classifier/tasks/drawing-task-details-editor.cjsx
+++ app/classifier/tasks/drawing-task-details-editor.cjsx
@@ -119,7 +119,7 @@
       when 'single'
         TaskChoice = require('./single').default
       when 'text'
-        TaskChoice = require './text'
+        TaskChoice = require('./text').default
       when 'slider'
         TaskChoice = require('./slider').default
       when 'dropdown'


add static files server

add static files server

client js is now served through express server

--- test/statics.spec.coffee
+++ test/statics.spec.coffee
@@ -10,4 +10,3 @@
       .expect("200")
       .expect(/Rauschen/, done)

-


Prevent install of incompatible packages.  Fixes #732

--- lib/package-card.coffee
+++ lib/package-card.coffee
@@ -102,45 +102,38 @@
     @updateButtonGroup.hide() unless @newVersion or @newSha

     @hasCompatibleVersion = true
-    @updateForUninstalledCommunityPackage() unless @isInstalled()
     @updateInterfaceState()

-  updateForUninstalledCommunityPackage: ->
-    # The package is not bundled with Atom and is not installed so we'll have
-    # to find a package version that is compatible with this Atom version.
-
-    @uninstallButton.hide()
-    atomVersion = @packageManager.normalizeVersion(atom.getVersion())
-    # The latest version is not compatible with the current Atom version,
-    # we need to make a request to get the latest compatible version.
-    unless @packageManager.satisfiesVersion(atomVersion, @pack)
-      @packageManager.loadCompatiblePackageVersion @pack.name, (err, pack) =>
-        return console.error(err) if err?
-
-        packageVersion = pack.version
-
-        # A compatible version exist, we activate the install button and
-        # set @installablePack so that the install action installs the
-        # compatible version of the package.
-        if packageVersion
-          @versionValue.text(packageVersion)
-          if packageVersion isnt @pack.version
-            @versionValue.addClass('text-warning')
-            @packageMessage.addClass('text-warning')
-            @packageMessage.text """
-            Version #{packageVersion} is not the latest version available for this package, but it's the latest that is compatible with your version of Atom.
-            """
-
-          @installablePack = pack
-        else
-          @hasCompatibleVersion = false
-          @installButtonGroup.hide()
-          @versionValue.addClass('text-error')
-          @packageMessage.addClass('text-error')
-          @packageMessage.append """
-          There's no version of this package that is compatible with your Atom version. The version must satisfy #{@pack.engines.atom}.
+  locateCompatiblePackageVersion: (callback) ->
+    @packageManager.loadCompatiblePackageVersion @pack.name, (err, pack) =>
+      return console.error(err) if err?
+
+      packageVersion = pack.version
+
+      # A compatible version exist, we activate the install button and
+      # set @installablePack so that the install action installs the
+      # compatible version of the package.
+      if packageVersion
+        @versionValue.text(packageVersion)
+        if packageVersion isnt @pack.version
+          @versionValue.addClass('text-warning')
+          @packageMessage.addClass('text-warning')
+          @packageMessage.text """
+          Version #{packageVersion} is not the latest version available for this package, but it's the latest that is compatible with your version of Atom.
           """
-          console.error("No available version compatible with the installed Atom version: #{atom.getVersion()}")
+
+        @installablePack = pack
+        @hasCompatibleVersion = true
+      else
+        @hasCompatibleVersion = false
+        @versionValue.addClass('text-error')
+        @packageMessage.addClass('text-error')
+        @packageMessage.append """
+        There's no version of this package that is compatible with your Atom version. The version must satisfy #{@pack.engines.atom}.
+        """
+        console.error("No available version compatible with the installed Atom version: #{atom.getVersion()}")
+
+      callback()

   handleButtonEvents: (options) ->
     if options?.onSettingsView
@@ -282,6 +275,16 @@
     @uninstallButton.show()

   displayNotInstalledState: ->
+    @uninstallButton.hide()
+    atomVersion = @packageManager.normalizeVersion(atom.getVersion())
+    if not @packageManager.satisfiesVersion(atomVersion, @pack)
+      @hasCompatibleVersion = false
+      @setNotInstalledStateButtons()
+      @locateCompatiblePackageVersion => @setNotInstalledStateButtons()
+    else
+      @setNotInstalledStateButtons()
+
+  setNotInstalledStateButtons: ->
     if not @hasCompatibleVersion
       @installButtonGroup.hide()
       @updateButtonGroup.hide()


act cache wip

act cache wip

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -186,7 +186,7 @@
   continueLoadingTeasers:->
     unless isLoading
       lastDate = new Date @listController.itemsOrdered.last.getData().meta.createdAt
-      @populateActivity {slug : "to/#{lastDate.getTime()}"}
+      @populateActivity {slug : "before/#{lastDate.getTime()}"}

   teasersLoaded:->
     {scrollView} = @listController


Fixes call to Object.prototype.map in Component.prototype.isLoggedIn for IE8

--- src/client/component.coffee
+++ src/client/component.coffee
@@ -148,7 +148,8 @@
       loggedIn: =>
         return false unless @sandbox.data.api.model('me').id?
         identities = {}
-        @sandbox.data.api.model('me').get("identities").map (i)-> identities[i.provider] = i
+        _.map @sandbox.data.api.model('me').get("identities"), (i)->
+          identities[i.provider] = i
         identities

       getTemplate: (tpl, data)=>


Remove unused require.

Remove unused require.

--- src/coffee/validator.coffee
+++ src/coffee/validator.coffee
@@ -1,6 +1,5 @@
 _ = require('underscore')._
 Csv = require 'csv'
-package_json = require '../package.json'

 class Validator
   constructor: (options) ->


Simplify workaround, and describe issue with Object.create + CustomEvent

--- src/command-registry.coffee
+++ src/command-registry.coffee
@@ -180,9 +180,11 @@
       preventDefault: value: ->
       stopPropagation: value: ->
       stopImmediatePropagation: value: ->
-    # In Chrome 43, Object.create doesn't work well with CustomEvent.
-    for k, v of event when k not in eventWithTarget
-      eventWithTarget[k] = v
+    # NOTE: In Chrome 43, Object.create doesn't work well with CustomEvent;
+    # However utilizing _.defaults here doesn't really do anything since all properties have been
+    # moved from "own" to the prototype, so we should update this to fully shadowing the properties
+    # from Event.prototype (+ detail property from CustomEvent).
+    eventWithTarget = _.defaults(eventWithTarget, event)
     @handleCommandEvent(eventWithTarget)

   # Public: Invoke the given callback before dispatching a command event.
@@ -220,7 +222,7 @@
     syntheticEvent = Object.create {},
       eventPhase: value: Event.BUBBLING_PHASE
       currentTarget: get: -> currentTarget
-      target: value: currentTarget # TODO: Find a better way to solve this.
+      target: value: currentTarget
       preventDefault: value: ->
         originalEvent.preventDefault()
       stopPropagation: value: ->
@@ -232,10 +234,8 @@
         immediatePropagationStopped = true
       abortKeyBinding: value: ->
         originalEvent.abortKeyBinding?()
-    # In Chrome 43, Object.create doesn't work well with CustomEvent.
-    # NOTE: We should "properly" shadow the prototype chain (CustomEvent + Event) here.
-    for k, v of originalEvent when k not in syntheticEvent
-      syntheticEvent[k] = v
+    # NOTE: See similar scenario in ::dispatch.
+    syntheticEvent = _.defaults(syntheticEvent, originalEvent)

     @emitter.emit 'will-dispatch', syntheticEvent


мелкие правки в тестах билдера

мелкие правки в тестах билдера

--- test/test-builder.coffee
+++ test/test-builder.coffee
@@ -21,11 +21,7 @@
 WORK_DIR    = "#{__dirname}/sample"
 CURRENT_DIR = process.cwd()

-g_createdDirs = []
-
-mkdirp(WORK_DIR, 0755, g_createdDirs)
-
-vows.describe('builder').addBatch({
+vows.describe('Builder').addBatch({
   'load config':
     topic: ->
       return new Builder
@@ -38,7 +34,7 @@
     'setting environment manually': (builder) ->
       assert.equal(builder.environment, 'production')
     'builder should be unlocked': (builder) ->
-      assert.isFalse(builder.lock)
+      assert.isFalse(builder._lock)
   'load multiple config':
     topic: ->
       return new Builder


Get indicators on all theme pages

Get indicators on all theme pages

--- server/routes/themes.coffee
+++ server/routes/themes.coffee
@@ -51,9 +51,11 @@

     theme = theme.toObjectWithNestedPage(draft: true)
     .then((themeObject) ->
-      res.render("themes/show",
-        theme: theme,
-        themeJSON: JSON.stringify(themeObject)
+      Theme.getIndicatorsByTheme( themeObject.externalId, (err, indicators) ->
+        res.render "themes/show",
+          theme: themeObject,
+          themeJSON: JSON.stringify(themeObject),
+          indicators: indicators
       )
     ).fail((err) ->
       console.error err
@@ -80,9 +82,11 @@
     .then( (publishedPage) ->
       theme.toObjectWithNestedPage()
     ).then( (themeObject) ->
-      res.render("themes/show",
-        theme: theme,
-        themeJSON: JSON.stringify(themeObject)
+      Theme.getIndicatorsByTheme( themeObject.externalId, (err, indicators) ->
+        res.render "themes/show",
+          theme: themeObject,
+          themeJSON: JSON.stringify(themeObject),
+          indicators: indicators
       )
     ).fail((err) ->
       console.error err
@@ -109,9 +113,11 @@
     .then( (publishedPage) ->
       theme.toObjectWithNestedPage()
     ).then( (themeObject) ->
-      res.render("themes/show",
-        theme: theme,
-        themeJSON: JSON.stringify(themeObject)
+      Theme.getIndicatorsByTheme( themeObject.externalId, (err, indicators) ->
+        res.render "themes/show",
+          theme: themeObject,
+          themeJSON: JSON.stringify(themeObject),
+          indicators: indicators
       )
     ).fail((err) ->
       console.error err