improve tests syntax

improve tests syntax

--- tests/poller.coffee
+++ tests/poller.coffee
@@ -18,10 +18,9 @@
             done()
     after (done) ->
         Konnector.all (err, body) =>
-            for konnector in body
-                if konnector.slug is 'free'
-                    konnector.destroy()
-                    done()
+            for konnector in body when konnector.slug is 'free'
+                konnector.destroy()
+                done()

     describe "Initialize of auto import", ->

@@ -67,14 +66,13 @@
             it 'When the cron function is called', (done) ->
                 @timeout 4000
                 Konnector.all (err, body) =>
-                    for konnector in body
-                        if konnector.slug is 'free'
-                            konnector.importInterval = 'day'
-                            konnector.lastAutoImport = moment().format()
-                            konnector.save (err, res, body) =>
-                                poller.start true, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                    for konnector in body when konnector.slug is 'free'
+                        konnector.importInterval = 'day'
+                        konnector.lastAutoImport = moment().format()
+                        konnector.save (err, res, body) =>
+                            poller.start true, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 23 hours', ->
                 @sandbox.clock.tick 23 * hour
@@ -98,14 +96,13 @@
             it 'When the cron function is called', (done) ->
                 @timeout 4000
                 Konnector.all (err, body) =>
-                    for konnector in body
-                        if konnector.slug is 'free'
-                            konnector.importInterval = 'hour'
-                            konnector.lastAutoImport = moment().format()
-                            konnector.save (err, res, body) =>
-                                poller.start true, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                    for konnector in body when konnector.slug is 'free'
+                        konnector.importInterval = 'hour'
+                        konnector.lastAutoImport = moment().format()
+                        konnector.save (err, res, body) =>
+                            poller.start true, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 59 minutes', ->
@@ -130,14 +127,13 @@
             it 'When the cron function is called', (done) ->
                 @timeout 4000
                 Konnector.all (err, body) =>
-                    for konnector in body
-                        if konnector.slug is 'free'
-                            konnector.importInterval = 'month'
-                            konnector.lastAutoImport = moment().format()
-                            konnector.save (err, res, body) =>
-                                poller.start true, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                    for konnector in body when konnector.slug is 'free'
+                        konnector.importInterval = 'month'
+                        konnector.lastAutoImport = moment().format()
+                        konnector.save (err, res, body) =>
+                            poller.start true, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 22 days', ->
                 @sandbox.clock.tick 22 * day
@@ -169,14 +165,13 @@
                 @timeout 4000
                 poller.start true, ()=>
                     Konnector.all (err, body) =>
-                        for konnector in body
-                            if konnector.slug is 'free'
-                                konnector.importInterval = 'week'
-                                konnector.lastAutoImport = moment().format()
-                                konnector.fieldValues = {}
-                                poller.handleTimeout konnector, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                        for konnector in body when konnector.slug is 'free'
+                            konnector.importInterval = 'week'
+                            konnector.lastAutoImport = moment().format()
+                            konnector.fieldValues = {}
+                            poller.handleTimeout konnector, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 6 days', (done) ->
                 @sandbox.clock.tick 6 * day
@@ -202,14 +197,13 @@
                 @timeout 4000
                 poller.start true, ()=>
                     Konnector.all (err, body) =>
-                        for konnector in body
-                            if konnector.slug is 'free'
-                                konnector.importInterval = 'day'
-                                konnector.lastAutoImport = moment().format()
-                                konnector.fieldValues = {}
-                                poller.handleTimeout konnector, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                        for konnector in body when konnector.slug is 'free'
+                            konnector.importInterval = 'day'
+                            konnector.lastAutoImport = moment().format()
+                            konnector.fieldValues = {}
+                            poller.handleTimeout konnector, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 23 hours', ->
                 @sandbox.clock.tick 23 * hour
@@ -234,14 +228,13 @@
                 @timeout 4000
                 poller.start true, ()=>
                     Konnector.all (err, body) =>
-                        for konnector in body
-                            if konnector.slug is 'free'
-                                konnector.importInterval = 'hour'
-                                konnector.lastAutoImport = moment().format()
-                                konnector.fieldValues = {}
-                                poller.handleTimeout konnector, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                        for konnector in body when konnector.slug is 'free'
+                            konnector.importInterval = 'hour'
+                            konnector.lastAutoImport = moment().format()
+                            konnector.fieldValues = {}
+                            poller.handleTimeout konnector, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 59 minutes', ->
                 @sandbox.clock.tick 59 * minute
@@ -266,14 +259,13 @@
                 @timeout 4000
                 poller.start true, ()=>
                     Konnector.all (err, body) =>
-                        for konnector in body
-                            if konnector.slug is 'free'
-                                konnector.importInterval = 'month'
-                                konnector.lastAutoImport = moment().format()
-                                konnector.fieldValues = {}
-                                poller.handleTimeout konnector, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                        for konnector in body when konnector.slug is 'free'
+                            konnector.importInterval = 'month'
+                            konnector.lastAutoImport = moment().format()
+                            konnector.fieldValues = {}
+                            poller.handleTimeout konnector, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 22 days', ->
                 @sandbox.clock.tick 22 * day
@@ -304,14 +296,13 @@
             it 'When the cron function is called', (done) ->
                 @timeout 8000
                 Konnector.all (err, body) =>
-                    for konnector in body
-                        if konnector.slug is 'free'
-                            konnector.importInterval = 'week'
-                            konnector.lastAutoImport = moment().add month, 'ms'
-                            konnector.save (err, res, body) =>
-                                poller.start true, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                    for konnector in body when konnector.slug is 'free'
+                        konnector.importInterval = 'week'
+                        konnector.lastAutoImport = moment().add month, 'ms'
+                        konnector.save (err, res, body) =>
+                            poller.start true, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 7 days', (done) ->
                 @sandbox.clock.tick 7 * day
@@ -346,14 +337,13 @@
                 @timeout 8000
                 poller.start true, () =>
                     Konnector.all (err, body) =>
-                        for konnector in body
-                            if konnector.slug is 'free'
-                                konnector.importInterval = 'week'
-                                konnector.lastAutoImport = moment().format()
-                                konnector.fieldValues = {date: moment().add month, 'ms'}
-                                poller.handleTimeout konnector, () =>
-                                    @spy.callCount.should.equal 0
-                                    done()
+                        for konnector in body when konnector.slug is 'free'
+                            konnector.importInterval = 'week'
+                            konnector.lastAutoImport = moment().format()
+                            konnector.fieldValues = {date: moment().add month, 'ms'}
+                            poller.handleTimeout konnector, () =>
+                                @spy.callCount.should.equal 0
+                                done()

             it 'Then the cron function should not have been called after 7 days', (done) ->
                 @sandbox.clock.tick 7 * day


remove logout for VIEWS

remove logout for VIEWS

--- src/concept-coach/base.cjsx
+++ src/concept-coach/base.cjsx
@@ -23,7 +23,7 @@

 # TODO Move this and auth logic to user model
 # These views are used with an authLevel (0, 1, 2, or 3) to determine what views the user is allowed to see.
-VIEWS = ['loading', 'login', 'logout', 'registration', ['task', 'progress', 'profile', 'dashboard', 'registration']]
+VIEWS = ['loading', 'login', 'registration', ['task', 'progress', 'profile', 'dashboard', 'registration']]

 ConceptCoach = React.createClass
   displayName: 'ConceptCoach'


[#22] silent mode

[#22] silent mode

とりあえず。
start 内の実装ちょっと整理する必要あり。

--- src/coffee/kazitori.coffee
+++ src/coffee/kazitori.coffee
@@ -67,8 +67,6 @@
   notFound:null
   beforeAnytimeHandler:null
   direct:null
-  #ルーターをネストできる最大回数
-  maxDepth: 4
   isIE:false
   #URL パラメーター
   _params:
@@ -119,7 +117,7 @@

     if options.routes
       @.routes = options.routes
-
+
     @.root = if options.root then options.root else '/'
     @.isTemae = if options.isTemae then options.isTemae else false

@@ -212,6 +210,9 @@
       else if not atRoot
         override = @.fragment
       return  @loadURL(override)
+    else
+      #んー
+      return @loadURL(@.fragment)

   #止める
@@ -313,16 +314,17 @@
     if not options
       options = @._changeOptions
     url = @.root + @.fragment.replace(routeStripper, '')
-    if @._hasPushState
-      @.history[ if options.replace then 'replaceState' else 'pushState' ]({}, document.title, url)
-    else if @._wantChangeHash
-      @_updateHash(@.location, fragment, options.replace)
-      if @.iframe and (fragment isnt @getFragment(@getHash(@.iframe)))
-        if !options.replace
-          @.iframe.document.open().close()
-        @_updateHash(@.iframe.location, fragment, options.replace)
-    else
-      return @.location.assign(url)
+    if not @.options.silent
+      if @._hasPushState
+        @.history[ if options.replace then 'replaceState' else 'pushState' ]({}, document.title, url)
+      else if @._wantChangeHash
+        @_updateHash(@.location, fragment, options.replace)
+        if @.iframe and (fragment isnt @getFragment(@getHash(@.iframe)))
+          if !options.replace
+            @.iframe.document.open().close()
+          @_updateHash(@.iframe.location, fragment, options.replace)
+      else
+        return @.location.assign(url)

     #イベントディスパッチ
     @dispatchEvent(new KazitoriEvent(KazitoriEvent.CHANGE, @.fragment, @.lastFragment))
@@ -359,7 +361,7 @@
     @[@._processStep.status](@._processStep.args)
     @._dispatcher.dispatchEvent( new KazitoriEvent(KazitoriEvent.RESUME, @.fragment, @.lastFragment))
     return
-
+
   registerHandler:(rule, name, isBefore, callback )->
     if not callback
       if isBefore
@@ -409,7 +411,7 @@
   #ルールを後から追加する
   appendRouter:(child, childRoot)->
     if not child instanceof Kazitori and typeof child isnt "function"
-      throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
+      throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
       return

     if child instanceof Kazitori
@@ -424,7 +426,7 @@
           @_bindChild(rule, child)
           return @
         catch e
-          throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
+          throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
     return @

   _getChildRule:(child, childRoot)->
@@ -441,7 +443,7 @@
   #ルールを削除する
   removeRouter:(child, childRoot)->
     if not child instanceof Kazitori and typeof child isnt "function"
-      throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
+      throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
       return
     if child instanceof Kazitori
       @_unbindChild(child, childRoot)
@@ -452,7 +454,7 @@
           @_unbindChild(child, childRoot)
           return @
         catch e
-          throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")
+          throw new Error("引数の値が不正です。 引数として与えられるオブジェクトは Kazitori を継承している必要があります。")

   _unbindChild:(child, childRoot)->
     rule = @_getChildRule(child, childRoot)
@@ -497,12 +499,12 @@
     return matched.length > 0

-
+
   #before で登録した処理が無難に終わった
   beforeComplete:(event)=>
     @._beforeDeffer.removeEventListener(KazitoriEvent.TASK_QUEUE_COMPLETE, @beforeComplete)
     @._beforeDeffer.removeEventListener(KazitoriEvent.TASK_QUEUE_FAILED, @beforeFailed)
-
+
     @._dispatcher.dispatchEvent( new KazitoriEvent(KazitoriEvent.BEFORE_EXECUTED, @.fragment, @.lastFragment))
     #ここではんだんするしかないかなー
     if @.isTemae
@@ -522,7 +524,7 @@
         d.execute(d)
         return
       )
-
+
     matched = @._matchCheck(fragment, @.beforeHandlers)
     for handler in matched
       @._beforeDeffer.deffered((d)->
@@ -533,7 +535,7 @@
     @._beforeDeffer.addEventListener(KazitoriEvent.TASK_QUEUE_COMPLETE, @beforeComplete)
     @._beforeDeffer.addEventListener(KazitoriEvent.TASK_QUEUE_FAILED, @beforeFailed)
     @._beforeDeffer.execute(@._beforeDeffer)
-
+
   #routes で登録されたメソッドを実行
   executeHandlers:()=>
     @._processStep.status = 'executeHandlers'
@@ -566,10 +568,10 @@
     else
       if isMatched
         @._dispatcher.dispatchEvent( new KazitoriEvent(KazitoriEvent.EXECUTED, @.fragment, @.lastFragment))
-
+
     return matched

-
+
   beforeFailed:(event)=>
     @.beforeFailedHandler.apply(@, arguments)
     @._beforeDeffer.removeEventListener(KazitoriEvent.TASK_QUEUE_FAILED, @beforeFailed)
@@ -637,7 +639,7 @@
       notFoundFragment = @_keys(@.notFound)[0]

     notFoundFuncName = @.notFound[notFoundFragment]
-
+
     if typeof notFoundFuncName is "function"
       callback = notFoundFuncName
     else
@@ -670,7 +672,7 @@
     matched = []
     tmpFrag = fragment
     if tmpFrag isnt undefined and tmpFrag isnt 'undefined'
-
+
       hasQuery = @_match.apply(tmpFrag, [/(\?[\w\d=|]+)/g])
     if hasQuery
       fragment = fragment.split('?')[0]
@@ -693,7 +695,7 @@
                 argsMatch.push true
               else
                 argsMatch.push @_typeCheck(a,t)
-            i++
+            i++
           argsMatched = true
           for match in argsMatch
             if not match
@@ -731,14 +733,14 @@
           fragment = @.root
         fragment = fragment + @.location.search
         root = @.root.replace(trailingSlash, '')
-
+
         if fragment.indexOf(root) > -1
           fragment = fragment.substr(root.length)
       else
         fragment = @getHash()
     else
       root = @.root.replace(trailingSlash, '')
-
+
       if fragment.indexOf(@.root) > -1 and fragment.indexOf(root) > -1
         fragment = fragment.substr(root.length)
     return fragment
@@ -985,7 +987,7 @@

 #うーん…
 class InternalGroup
-  prefix: null #root ?
+  prefix: null #root ?
   rules: []
   constructor:(prefix, rules)->
     @.prefix = prefix
@@ -1039,7 +1041,7 @@
         return i
       i++
     return -1
-
+

 ## Deffered
 # **internal**
@@ -1051,7 +1053,7 @@
   constructor:()->
     @queue = []
     @isSuspend = false
-
+
   deffered:(func)->
     @queue.push func
     return @


Wrapped inputs in .ui.input again.

Wrapped inputs in .ui.input again.

--- src/collectionTable.coffee
+++ src/collectionTable.coffee
@@ -214,6 +214,9 @@
         _.each getSelectedIds(domNode), (id) -> collection.remove id

   @$('.ui.filter.input .input-group-addon').replaceWith('')
+  @$('input.form-control').each ->
+    $em = $(@)
+    unless $em.parent().hasClass('ui input') then $em.wrap('

')
   $nav = @$('.reactive-table-navigation')
   $footer = $('

')
   colCount = $('tr:first th', $table).length


add object/property support so that failures are described better

--- configs/test/matchers.coffee
+++ configs/test/matchers.coffee
@@ -1,3 +1,5 @@
+get = require 'lodash/get'
+
 jasmine.addMatchers

   toHaveRendered: ->
@@ -17,8 +19,11 @@
   toHaveChanged: (fn, tests) ->
     failures = []
     utils = @utils
-    testValue = (i, type, expected, actual) ->
+
+    testValue = (i, test, type, expected) ->
+      actual = get(test.object, test.property, test.value?())
       actual = parseFloat(actual) unless typeof actual is 'number'
+
       if test.precision
         utils.ensureNumbers(actual, expected, "test index #{i}")
         pass = Math.abs(expected - actual) < Math.pow(10, -test.precision) / 2
@@ -26,17 +31,21 @@
       else
         pass = expected is actual
         hint = utils.matcherHint('.toEqual')
+
+      name = test.property or i
+
       failures.push(
-        "test #{i} #{type} #{hint}: #{utils.printExpected(expected)}, received #{utils.printReceived(actual)}"
+        "#{name}: #{type} #{hint}: #{utils.printExpected(expected)}, received #{utils.printReceived(actual)}"
       ) unless pass

-    testValue(i, 'from value', test.from, test.value()) for test, i in tests
+    for test, i in tests
+      testValue(i, test, 'from value', test.from)

     fn()

     for test, i in tests
       expected = if test.by? then (test.from + test.by) else test.to
-      testValue(i, 'changed value', expected, test.value())
+      testValue(i, test, 'changed value', expected)

     if failures.length
       { pass: false, message: -> failures.join("\n") }


Better documentation for grocery-list.coffee

--- src/scripts/grocery-list.coffee
+++ src/scripts/grocery-list.coffee
@@ -1,11 +1,20 @@
 # Description:
 #   Organize your grocery list
+#
+# Dependencies:
+#   None
+#
+# Configuration:
+#   None
 #
 # Commands:
 #   hubot grocery list - list items on your grocery list
 #   hubot remind me to buy <item> - add item to grocery list
 #   hubot i bought <item> - mark item as purchased
 #   hubot remove <item> from my grocery lists
+#
+# Author:
+#   parkr

 module.exports = (robot) ->


JS: ES6.

JS: ES6.

--- src/js/index.coffee
+++ src/js/index.coffee
@@ -17,22 +17,30 @@
 		gf.addSection 'jshint',
 			options:
 				jshintrc: '.jshintrc'
-			files: ["#{@htdocs_prefix}js/mylibs/*.js", "#{@htdocs_prefix}js/*.js"]
+			files: [
+				"#{@htdocs_prefix}js/components/*.js",
+				"#{@htdocs_prefix}js/*.js"
+			]

-	unless gf.hasSection 'concat'
-		gf.addSection 'concat',
+	unless gf.hasSection 'traceur_build'
+		gf.addSection 'traceur_build',
+			options:
+				blockBinding: true
+				freeVariableChecker: false
 			main:
 				src: [
+					"#{@htdocs_prefix}tamia/tamia/traceur-rt-light.js"
 					"#{@htdocs_prefix}tamia/tamia/tamia.js"
+					"#{@htdocs_prefix}tamia/tamia/component.js"
 					"#{@htdocs_prefix}tamia/blocks/*/*.js"
 					"#{@htdocs_prefix}js/components/*.js"
 					"#{@htdocs_prefix}js/main.js"
 				]
-				dest: "#{@htdocs_prefix}build/scripts.js"
+				dest: "#{@htdocs_prefix}build/script.js"

-		gf.addWatcher 'concat',
-			files: '<%= concat.main.src %>'
-			tasks: 'concat'
+		gf.addWatcher 'traceur_build',
+			files: '<%= traceur_build.main.src %>'
+			tasks: 'traceur_build'

 	unless gf.hasSection 'uglify'
 		gf.addSection 'uglify',
@@ -43,10 +51,10 @@
 						global_defs:
 							DEBUG: gf.JS 'debug'
 				files:
-					'<%= concat.main.dest %>': '<%= concat.main.dest %>'
+					'<%= traceur_build.main.dest %>': '<%= traceur_build.main.dest %>'

-	gf.addTask 'default', ['jshint', 'concat', 'uglify']
-	gf.addTask 'deploy', ['concat', 'uglify']
+	gf.addTask 'default', ['jshint', 'traceur_build', 'uglify']
+	gf.addTask 'deploy', ['traceur_build', 'uglify']

 	gf.save()

@@ -55,4 +63,4 @@
 	@copyIfNot '.jshintrc'

 Generator::dependencies = ->
-	@installFromNpm ['grunt', 'matchdep', 'grunt-contrib-jshint', 'grunt-contrib-concat', 'grunt-contrib-uglify', 'grunt-contrib-watch']
+	@installFromNpm ['grunt', 'matchdep', 'grunt-contrib-jshint', 'grunt-traceur-build', 'grunt-contrib-uglify', 'grunt-contrib-watch']


properly name spree base

properly name spree base

--- vendor/assets/javascripts/services/base_service.coffee
+++ vendor/assets/javascripts/services/base_service.coffee
@@ -1,3 +1,3 @@
-ngSpree.factory 'BaseService', [->
+ngSpree.factory 'SpreeBase', [() ->
   current_order: gon.current_order
 ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -101,7 +101,7 @@
   @name find
   @methodOf Engine.selector

-  @param {String}
+  @param {String} selector
   ###
   find: (selector) ->
     results = []


improve css

improve css

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -38,7 +38,7 @@
 	lightgreen = '#d6e685'
 	grassgreen = '#44a340'
 	darkgreen = '#1e6823'
-	square = paper.rect(col * 11, row * 11 + 20, 10, 10)
+	square = paper.rect(col * 11, row * 11, 10, 10)
 	if model.commitsLength > 0 && model.commitsLength < 3
 		square.attr("fill", lightgreen)
 	else if model.commitsLength >= 3 && model.commitsLength < 6
@@ -51,13 +51,17 @@
 		square.attr("fill", "#ccc")
 	square.attr("stroke-opacity", "0")
 	square.hover ( ->
-			$('#github-calendar > .brief').append ' commits:' + model.commitsLength + ' '
-			$('#github-calendar > .brief').text model.created_at.format()
+			$('#github-calendar > .brief').html '
'+model.created_at.format()+'
'
+			$('#github-calendar > .brief').append '
'+model.commitsLength+' commits
'
+			$('#github-calendar > .brief').css('opacity', 1);
+			$('#github-calendar > .description').css('opacity', 1);
 			$('#github-calendar > .description').html('')
 			!!model.commits && model.commits.forEach (c)->
-				$('#github-calendar > .description').append '
'+model.repo.name+' - '+c.message+'
'
+				$('#github-calendar > .description').append '
'+model.repo.name+' - '+c.message+'
'
 			square.attr("stroke-opacity", "1")
 		), (->
+			$('#github-calendar > .brief').css('opacity', 0);
+			$('#github-calendar > .description').css('opacity', 0);
 			square.attr("stroke-opacity", "0")
 		)

@@ -95,8 +99,8 @@

 # MAIN
-$('#github-calendar').append $('
')
-$('#github-calendar').append $('
')
+$('#github-calendar').append $('
')
+$('#github-calendar').append $('
')

 today = new Date()
 end = 364 + today.getUTCDay()
@@ -109,4 +113,4 @@
 	$.get( url )
 		.done eventsHandler

-paper = Raphael(10, 10, 600, 100)+paper = Raphael(10, 40, 600, 100)


profile: added lazydom to kdcustomhtmlview, added events to skilltagview

--- core/KDCustomHTMLView.coffee
+++ core/KDCustomHTMLView.coffee
@@ -11,5 +11,8 @@

   setDomElement:(cssClass)->

-    @domElement = $ "<#{@tagName}/>",
-      class : cssClass+    if @getOptions().lazyDomId
+      KDView::setDomElement.call @, cssClass
+    else
+      @domElement = $ "<#{@tagName}/>",
+        class : cssClass


a bit more of work, but not mcuh

a bit more of work, but not mcuh

--- src/lib/client.iced
+++ src/lib/client.iced
@@ -5,7 +5,7 @@

 class Client

-  constructor : (@_eng, @_vo) ->
+  constructor : (@_eng) ->

   derive_key : (mode, cb) ->
     inp = @_eng.new_input mode, config.server
@@ -16,5 +16,7 @@
     inp.derive_key cb

   login : () ->
+    await @derive_key derive.keymodes.LOGIN_PW, defer pwh
     args =
       email : @_eng._inp.get 'email'
+      pwh : pwh


cursor and brush helpers

cursor and brush helpers

--- app/compat.coffee
+++ app/compat.coffee
@@ -15,7 +15,42 @@

 requireables = q: Q, _: _, moment: moment, colors: colors

-compat = modules.export exports, 'compat', ({doc, component_fn} ) ->
+compat = modules.export exports, 'compat', ({doc, component_fn, cmd, fn} ) ->
+  cmd 'shareCursor', (ctx, share=true) ->
+    options = ctx.options()
+    if share == false
+      delete options.cursor
+    else
+      if share instanceof Bacon.Observable
+        options.cursor = share
+      else
+        options.cursor = new Bacon.Model
+
+  cmd 'shareBrush', (ctx, share=true) ->
+    options = ctx.options()
+    if share == false
+      delete options.brush
+    else
+      if share instanceof Bacon.Observable
+        options.brush = share
+      else
+        options.brush = new Bacon.Model
+
+  fn 'brushParams', (ctx, brush) ->
+    Context.value compat.brushParams brush ? ctx.options().brush
+
+  bindToBrush = (brush, allParams) ->
+    serverParams = new Bacon.Model allParams.server
+    serverParams.addSource compat.brushParams(brush).map (brushParams) ->
+      _.extend {}, allParams.server, brushParams
+
+    serverParams.flatMapLatest (params) ->
+      Bacon.fromPromise Server.get_data params
+
+  fn 'bindToBrush', (ctx, args...) ->
+    allParams = Server.args_to_params {args, default_options: ctx.options()}
+    Context.value bindToBrush allParams.client.brush, allParams
+
   doc 'graph',
     'Loads and graphs time-series data'
     Documentation.load_file 'compat.graph'
@@ -33,7 +68,15 @@
     else
       all_params = Server.args_to_params {args, default_options: ctx.options()}
       params = all_params.client
-      promise = Server.get_data all_params.server
+      if params.bindToBrush == true
+        params.brush ?= new Bacon.Model
+        data = bindToBrush params.brush, all_params
+      else if params.bindToBrush instanceof Bacon.Observable
+        data = bindToBrush params.bindToBrush, all_params
+        params.brush ?= params.bindToBrush
+        delete params.bindToBrush
+      else
+        promise = Server.get_data all_params.server

     if promise
       data = Bacon.fromPromise(promise)
@@ -47,6 +90,13 @@
       React.DOM.div {style: {width: '-webkit-min-content'}},
         graph.create_component(data, params)

+  brushParams: (brush) ->
+    brush.filter(({brushing}) -> !brushing).map ({extent}) ->
+      if extent?
+        start: moment(extent[0]).unix(), end: moment(extent[1]).unix()
+      else
+        {}
+
   context_vars:
     moment: moment
     CodeMirror: CodeMirror


add renderer switch

add renderer switch

--- src/renderer/Renderer.coffee
+++ src/renderer/Renderer.coffee
@@ -27,6 +27,7 @@
 		@context.textBaseline = 'top'

 		tickCount = 0
+		@isRunning = true;

 		# API
 		@shapes = []
@@ -41,6 +42,8 @@
 		@dom.oncontextmenu = => false

 		tick = =>
+			return if not _self.isRunning
+
 			tickCount += 1
 			_self.triggerEvent "update", {"tickCount": tickCount}
 			clearCanvas()
@@ -49,6 +52,16 @@
 		setInterval(tick, 1000 / @fps)
 		clearCanvas = =>
 			@context.clearRect(0, 0, _self.width, _self.height)
+
+
+	pause: =>
+		@isRunning = false
+
+	continue: =>
+		@isRunning = true
+
+	toggle: =>
+		@isRunning = not @isRunning

 	append: (shape) ->


string_extensions

--- src/string_extensions.coffee
+++ src/string_extensions.coffee
@@ -64,7 +64,7 @@

 "Constant".constantize()
 # => Constant
-# notice this isn't a string. Useful for calling methods on class named Constant
+# notice this isn't a string. Useful for calling methods on class named Constant.
 </pre></code>

 @name constantize


search entire document for crsf meta

search entire document for crsf meta

--- tutor/src/helpers/dom.coffee
+++ tutor/src/helpers/dom.coffee
@@ -13,7 +13,7 @@

   readBootstrapData: (root = document) ->
-    User.csrf_token = root.head.querySelector('meta[name=csrf-token]')?.getAttribute('content')
+    User.csrf_token = root.querySelector('meta[name=csrf-token]')?.getAttribute('content')

     el = root.querySelector('#tutor-boostrap-data')
     if el


Kontrol::authenticate: simplify code

Kontrol::authenticate: simplify code

--- src/kontrol/kontrol.coffee
+++ src/kontrol/kontrol.coffee
@@ -21,24 +21,23 @@
     @authenticate()  if @options.autoConnect

   authenticate: (@options = @options) ->
-    { url, auth, username, environment, version, region, hostname, name, logLevel, transportClass, transportOptions, prefix } = @options

-    @kite = new @constructor.Kite
-      username        : username
-      environment     : environment
-      version         : version
-      region          : region
-      hostname        : hostname
-      name            : name ? 'kontrol'
-      url             : url
-      auth            : auth
-      logLevel        : logLevel
-      transportClass  : transportClass
-      transportOptions: transportOptions
-      prefix          : prefix
+    { url, auth, username, environment,
+      version, region, hostname, name,
+      logLevel, transportClass,
+      transportOptions, prefix } = @options
+
+    name ?= 'kontrol'
+
+    @kite = new @constructor.Kite {
+      url, auth, username, environment,
+      version, region, hostname, name,
+      logLevel, transportClass,
+      transportOptions, prefix
+    }

     @kite.on 'error', @emit.bind this, 'error'  # forward kite error events
-    @kite.on 'open', @emit.bind this, 'open'
+    @kite.on 'open',  @emit.bind this, 'open'

     return


Ensure ghost doesn't return to start point on drop

Use `effectAllowed` and `dropEffect` native DnD features to tell the
browser not to 'cancel' the drag and animate the ghost back home,
even on a successful drop.

--- src/sordid-dragon.jquery.coffee
+++ src/sordid-dragon.jquery.coffee
@@ -57,6 +57,7 @@
     hidePlaceholder = ->
       if $placeholder?.is(":visible")
         $placeholder.after $activePlaceholderChild
+        $placeholder.off ".sordidDragon"
         $placeholder.remove()
         $activePlaceholderChild.show()

@@ -111,6 +112,15 @@
       else if newPosition < oldPosition
         $besideChild.before $placeholder

+    # This allows the browser provided ghose to stay where it's dropped
+    # rather than returning home
+    attachDragOverHandler = ($el) ->
+      $el.on "dragover.sordidDragon", (e) ->
+        return unless isDragging
+
+        e.preventDefault()
+        e.originalEvent.dataTransfer?.dropEffect = "move"
+
     $parent.children(options.childSelector).each (_, child) ->
       $child = $(child)
       $handle = if options.handle
@@ -122,8 +132,8 @@
       $handle.attr "draggable", String(command != "destroy")

       # Clear out the existing events (so they aren't duplicated).
-      $handle.off "touchstart.sordidDragon dragstart.sordidDragon touchmove.sordidDragon drag.sordidDragon touchend.sordidDragon dragend.sordidDragon"
-      $child.off "dragenter.sordidDragon"
+      $handle.off ".sordidDragon"
+      $child.off ".sordidDragon"

       # If we are destroying, then just move on to the next child rather
       # than setting up any event listeners.
@@ -137,9 +147,11 @@
           calculatePositions()
           $ghost = $child.clone()
           e.preventDefault()
-        # Firefox won't trigger "drag" events without this.
+        # Firefox needs this for drag events, and others to now animate the ghost home
+        e.originalEvent.dataTransfer?.effectAllowed = "move"
         e.originalEvent.dataTransfer?.setData "text", ""
         $placeholder = $child.clone()
+        attachDragOverHandler($placeholder)
         isDragging = true
         options.sortStart?(e, $child)

@@ -163,6 +175,8 @@

         moveChild $child

+      attachDragOverHandler($child)
+
       $handle.on "touchend.sordidDragon dragend.sordidDragon", (e) ->
         return unless isDragging


Push filler tokens when no patterns match an empty line

This ensures that empty lines always contain tokens even when no
patterns match on empty lines.

--- src/text-mate-grammar.coffee
+++ src/text-mate-grammar.coffee
@@ -145,7 +145,7 @@
         break if position is line.length and nextTokens.length is 0 and ruleStack.length is previousRuleStackLength

       else # push filler token for unmatched text at end of line
-        if position < line.length
+        if position < line.length or line.length == 0
           tokens.push(new Token(
             value: line[position...line.length]
             scopes: scopes


Remove unused method

Remove unused method

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -286,10 +286,6 @@
       @blockList = @blockList.consolidateFromIndexToIndex(start.index, end.index)
     @locationRangeFromRange(range)

-  consolidateBlocks: ->
-    @blockList = @blockList.consolidate()
-    this
-
   getDocumentAtLocationRange: (locationRange) ->
     range = @rangeFromLocationRange(locationRange)
     blocks = @blockList.getSplittableListInRange(range).toArray()


change fullscreen element style based on state.

--- js/3panorama.coffee
+++ js/3panorama.coffee
@@ -222,6 +222,32 @@
             else
                 console.log("The bowser doesn't support fullscreen mode")

+    changeFullscreenState = (target) ->
+        # TODO [important] make a wrapper for styling??
+        fullscreenElement = document.fullscreenElement ||
+            document.mozFullScreenElement ||
+            document.webkitFullscreenElement ||
+            document.msFullscreenElement
+
+        clazz = "fullscreen-mode"
+
+        if (fullscreenElement?) # fullscreen
+            target.className += " " + clazz
+
+            target.style.width = "100vw"
+            target.style.height = "100vh"
+            target.style["max-width"] = "unset"
+            target.style["max-height"] = "unset"
+        else
+            # remove class name
+            # TODO clean up. make a helper to handle class name.
+            target.className = target.className.replace(new RegExp('(\\s|^)' + clazz + '(\\s|$)'), '')
+
+            target.style.width = null
+            target.style.height = null
+            target.style["max-width"] = null
+            target.style["max-height"] = null
+

     initControls = (container) ->
         controls = document.createElement("div")
@@ -245,7 +271,19 @@
                 toggleTargetFullscreen(container)
             , false)

-
+        # TODO CLEAN UP
+        document.addEventListener("webkitfullscreenchange", ->
+                changeFullscreenState(container)
+            , false)
+        document.addEventListener("mozfullscreenchange ", ->
+                changeFullscreenState(container)
+            , false)
+        document.addEventListener("msfullscreenchange ", ->
+                changeFullscreenState(container)
+            , false)
+        document.addEventListener("fullscreenchange ", ->
+                changeFullscreenState(container)
+            , false)

         controls.appendChild(fullscreen)


Fix try to apply undefined methods.

Fix try to apply undefined methods.

Behaviour may change later and produce a warning or kill your hamster.

--- app/core/Entity.coffee
+++ app/core/Entity.coffee
@@ -24,7 +24,7 @@

     # Call all mixin setUpMethods before applying
     # the TILED properties
-    method.apply @ for method in @setUpMethods
+    method.apply @ for method in @setUpMethods when method?

     # Copy all properties from the TILED object
     @[prop] = content for prop, content of object.properties
@@ -111,11 +111,11 @@

   load: =>
     # call all load methods
-    method.apply(@) for method in @loadMethods
+    method.apply @ for method in @loadMethods when method?

   kill: =>
-    method.apply @ for method in @unloadMethods
+    method.apply @ for method in @unloadMethods when method?

     # TODO: go to physics mixin
     @level.physicsManager.world.DestroyBody(@physBody)
@@ -133,15 +133,15 @@

   # TODO: move to physics mixin
   onTouch: =>
-    method.apply @, arguments for method in @onTouchMethods
+    method.apply @, arguments for method in @onTouchMethods when method?

   onTouchBegin: =>
-    method.apply @, arguments for method in @onTouchBeginMethods
+    method.apply @, arguments for method in @onTouchBeginMethods when method?

   onTouchEnd: =>
-    method.apply @, arguments for method in @onTouchEndMethods
+    method.apply @, arguments for method in @onTouchEndMethods when method?

   # I won't move no matter what! Don't even try it.
@@ -168,7 +168,7 @@
     @position.y = @physBody.GetPosition().y if @physBody.GetPosition().y?

     # call all update methods
-    method.apply(@) for method in @updateMethods
+    method.apply @ for method in @updateMethods when method?

   # TODO: move input mixin


Add max length of string validation to role model

--- app/models/role.coffee
+++ app/models/role.coffee
@@ -5,6 +5,7 @@
     name:
       type: 'string'
       required: true
+      maxLength: 100
     users:
       collection: 'user'
       via: 'role'


Changed way for supporting ng-repeat.

Changed way for supporting ng-repeat.

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -1,7 +1,7 @@
 'use strict'

 angular.module('slick', [])
-  .directive "slick", () ->
+  .directive "slick", ($timeout) ->
     restrict: "AEC"
     scope:
       currentIndex: "="
@@ -34,7 +34,7 @@
       vertical: "@"
     link: (scope, element, attrs) ->

-      angular.element(document).ready(() ->
+      $timeout(() ->
         slider = $(element)
         currentIndex = scope.currentIndex if scope.currentIndex?


Drop page.coffee, useless now

Drop page.coffee, useless now

--- static/script/pages/page.coffee
+++ static/script/pages/page.coffee
@@ -1,16 +0,0 @@
-window.settlers.setup_menu = () ->
-  #  pn = new window.settlers.PullNotify
-
-  $('#menu_logout').click () ->
-    new window.hlib.Ajax
-      url:              '/logout/'
-
-    return false
-
-window.settlers.setup_settlers = () ->
-  if window.settlers.setup_menu
-    window.settlers.setup_menu()
-
-#  window.settlers.PULL_NOTIFY.update()
-
-  return true


move Handlebars helpers

move Handlebars helpers

--- src/opensong.coffee
+++ src/opensong.coffee
@@ -11,23 +11,6 @@
   jQuery.fn.extend
     openSongLyrics: (lyrics) ->
       new OpenSong this, lyrics
-
-
-Handlebars.registerHelper 'human_header', (abbr) ->
-  switch abbr
-    when "C"
-      "Chorus "
-    when "V"
-      "Verse "
-    when "B"
-      "Bridge "
-    when "T"
-      "Tag "
-    when "P"
-      "Pre-Chorus "
-    else
-      abbr
-

 class OpenSong

@@ -199,3 +182,22 @@

     outputChords.join "/"

+  ###
+  Handlebars Helpers
+  ###
+
+  Handlebars.registerHelper 'human_header', (abbr) ->
+    switch abbr
+      when "C"
+        "Chorus "
+      when "V"
+        "Verse "
+      when "B"
+        "Bridge "
+      when "T"
+        "Tag "
+      when "P"
+        "Pre-Chorus "
+      else
+        abbr
+


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/physical.coffee
+++ src/physical.coffee
@@ -34,6 +34,7 @@
       center.x,
       center.y
     )
+    bodyDef.angle = I.rotation if I.rotation

     body = I.world.CreateBody(bodyDef)
     body.CreateFixture(fixDef)


add selection save/restore test

add selection save/restore test

--- test/unit/core/selection.coffee
+++ test/unit/core/selection.coffee
@@ -293,4 +293,27 @@
       )
     )
   )
+
+  describe('save / restore selection', ->
+    it('focus on another input', (done) ->
+      @container.innerHTML = '<input type="textbox">
0123
'
+      @quill = new Quill(@container.lastChild)
+      @quill.setSelection(2, 3)
+      @container.firstChild.focus()
+      _.defer( =>
+        expect(@quill.editor.selection.checkFocus()).not.toBeTruthy()
+        expect(@quill.getSelection()).not.toBeTruthy()
+        savedRange = @quill.editor.selection.getRange(true)
+        expect(savedRange).toBeTruthy()
+        expect(savedRange.start).toEqual(2)
+        expect(savedRange.end).toEqual(3)
+        @quill.focus()
+        range = @quill.getSelection()
+        expect(range).toBeTruthy()
+        expect(range.start).toEqual(2)
+        expect(range.end).toEqual(3)
+        done()
+      )
+    )
+  )
 )


preserve head from app/assets/index.html

--- src/renderer.coffee
+++ src/renderer.coffee
@@ -11,13 +11,28 @@
 	console.log 'Usage: render.js <url> <dir> <filename>'
 	phantom.exit()

+unless fs.isFile 'app/assets/index.html'
+	console.error 'no index.html found at \'app/assets\''
+	phantom.exit()
+
+index = fs.read 'app/assets/index.html'
+
 page.open address, (status) ->
 	if (status isnt 'success')
 		console.log('FAIL to load the address')
 		phantom.exit()
 	else
-		html = page.evaluate () ->
-			return document.getElementsByTagName('html')[0].outerHTML
+		page.viewportSize = { width: 768, height: 1024 }
+		body = page.evaluate () ->
+			return document.getElementsByTagName('body')[0].outerHTML
+
+		bodyIndex = index.indexOf index.match(/<body>/)[0]
+
+		header = index.slice 0, bodyIndex
+
+		html = header + body + '</hmtl>'
+
+		#console.log html

 		console.log 'writing ', dir + '/' + filename
 		fs.write dir + '/' + filename , html, 'w'


Improved documentation

Improved documentation

--- src/snippets.coffee
+++ src/snippets.coffee
@@ -1,12 +1,20 @@
 # Description:
-#   Example scripts for you to examine and try out.
+#   Implements a method to perform Google Snippets task tracking
+#
+# Commands:
+#   #standup <message>                              # Record a standup message for the day
+#   #sitdown <message>                              # Record a sitdown message for the day
+#   #sitdown (<YYYY-MM-DD> || yesterday) <message>  # Record a sitdown message for a give date#
 #
 # Notes:
-#   They are commented out by default, because most of them are pretty silly and
-#   wouldn't be useful and amusing enough for day to day huboting.
-#   Uncomment the ones you want to try and experiment with.
+#   REST Endpoints
+#   /hubot/standups
+#   /hubot/standups/users
+#   /hubot/standups/users/count
+#   /hubot/standups/user/:name
 #
-#   These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md
+# Author:
+#   bladow

 module.exports = (robot) ->
     #


Remove *.getLastCursor()

Remove *.getLastCursor()

--- src/snippets.coffee
+++ src/snippets.coffee
@@ -26,7 +26,7 @@
   enableSnippetsInEditor: (editor) ->
     editor.command 'snippets:expand', (e) =>
       editSession = editor.activeEditSession
-      prefix = editSession.getLastCursor().getCurrentWordPrefix()
+      prefix = editSession.getCursor().getCurrentWordPrefix()
       if snippet = @snippetsByExtension[editSession.getFileExtension()]?[prefix]
         editSession.transact ->
           snippetExpansion = new SnippetExpansion(snippet, editSession)


deploy no longer uses execSync

deploy no longer uses execSync

--- src/steroids.coffee
+++ src/steroids.coffee
@@ -367,13 +367,17 @@
           process.exit 1

         util.log "Building application locally"
-        @runSteroidsCommandSync "push"
-
-        Deploy = require "./steroids/deploy"
-        deploy = new Deploy(otherOptions)
-        deploy.uploadToCloud ()=>
-          # all complete
-          process.exit 0
+
+        project = new Project
+        project.make
+          onSuccess: =>
+            Deploy = require "./steroids/deploy"
+            deploy = new Deploy(otherOptions)
+            deploy.uploadToCloud ()=>
+              # all complete
+              process.exit 0
+          onFailure: =>
+            console.log "Can not build project locally, not deploying to cloud."

       else
         Help.logo() unless argv.noLogo


this is simply not needed (and ugly as well)

--- src/template.coffee
+++ src/template.coffee
@@ -158,17 +158,10 @@
                 @text "", force:yes if @isempty
                 end_tag.call this, arguments...

-        ##
-        # pipe events through and add hooks to be able to alter event behavior
-        # (eg injecting events before others)
-        # override method "on#{event}" to access the hook
+        # pipe events through
         EVENTS.forEach (event) =>
-            # build hook
-            @["on#{event}"] = =>
-                @emit event, arguments...
-            # listen
-            @xml.on event, =>
-                @["on#{event}"](arguments...)
+            @xml.on event, (args...) =>
+                @emit event, args...

         ##
         # start the templating process after user listened for events


Better error handling on client

Better error handling on client

--- client/accounts.coffee
+++ client/accounts.coffee
@@ -155,19 +155,17 @@
       Accounts._loginButtonsSession.set 'enrollAccountToken', null

 # We extend Meteor's Accounts.resetPassword functionality with username so that
-# user can choose username in the enroll form
+# user must choose username in the enroll form
 Accounts.resetPasswordWithUsername = (token, password, username, callback) ->
-  throw new Error "Need to pass token" unless token
-  throw new Error "Need to pass new password" unless password
-  throw new Error "Need to pass username" unless username
+  try
+    throw new Meteor.Error 400, "Invalid token." unless token
+    User.validateUsername username
+    User.validatePassword password

-  if password.length <= 6
-    callback new Meteor.Error 400, "Password must be at least 6 characters long"
-    return
-
-  verifier = SRP.generateVerifier password
-  Accounts.callLoginMethod
-    methodName: 'reset-password-with-username'
-    methodArguments: [token, verifier, username]
-    userCallback: callback
-
+    verifier = SRP.generateVerifier password
+    Accounts.callLoginMethod
+      methodName: 'reset-password-with-username'
+      methodArguments: [token, verifier, username]
+      userCallback: callback
+  catch error
+    callback error


Workspace: Added workspace related files to includes.coffee.

--- client/includes.coffee
+++ client/includes.coffee
@@ -215,6 +215,7 @@

   "app/CommonViews/markdownmodal.coffee",

+  # FATIH
   "app/CommonViews/fatih/plugins/fatihpluginabstract.coffee",
   "app/CommonViews/fatih/plugins/fatihlistitem.coffee",
   "app/CommonViews/fatih/plugins/fatihfilelistitem.coffee",
@@ -225,8 +226,19 @@
   "app/CommonViews/fatih/fatihprefpane.coffee",
   "app/CommonViews/fatih/fatih.coffee",

+  # INTRODUCTION TOOLTIP
   "app/CommonViews/introductiontooltip/introductiontooltip.coffee",
   "app/CommonViews/introductiontooltip/introductiontooltipcontroller.coffee",
+
+  # WORKSPACE
+  "app/CommonViews/workspace/panes/pane.coffee",
+  "app/CommonViews/workspace/panes/editorpane.coffee",
+  "app/CommonViews/workspace/panes/previewpane.coffee",
+  "app/CommonViews/workspace/panes/terminalpane.coffee",
+  "app/CommonViews/workspace/panes/videopane.coffee",
+  "app/CommonViews/workspace/panel/panel.coffee",
+  "app/CommonViews/workspace/workspace.coffee",
+

   "app/CommonViews/ModalViewWithTerminal.coffee",


Update publishing.iced

Update publishing.iced

--- .gulp/publishing.iced
+++ .gulp/publishing.iced
@@ -10,10 +10,7 @@
   # publish core package
   await execute "npm publish --tag preview",{cwd:package_folder, silent:false }, defer c,o,e
   echo  "\n\nPublished Core:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"
-  echo "stdout:\n #o"
-  echo "stderr:\n #e"

-
   # update bootstrapper package.json
   package_path = "#{basefolder}/src/autorest/package.json"
   package_folder = "#{basefolder}/src/autorest"
@@ -23,7 +20,5 @@

   await execute "npm publish --tag preview",{cwd:package_folder, silent:false }, defer c,o,e
   echo  "\n\nPublished Bootstrapper:  #{package_json.name}@#{info package_json.version} (tagged as @preview)\n\n"
-  echo "stdout:\n #o"
-  echo "stderr:\n #e"

   done()


Match only at the start

Match only at the start

--- commands/places.coffee
+++ commands/places.coffee
@@ -30,6 +30,6 @@
 # search for shit on twitter
 # search shit on twitter
 brain.addPattern 'places',
-    match: /where is ([^?]+) ?\??/i
+    match: /^where is ([^?]+) ?\??/i
     fn: (user, m, cb) -> placeSearch m[1], cb


qemux86-64.coffee: Set yocto version to sumo

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@resin.io>

--- qemux86-64.coffee
+++ qemux86-64.coffee
@@ -38,7 +38,7 @@
 		machine: 'qemux86-64'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-pyro'
+		version: 'yocto-sumo'
 		deployArtifact: 'resin-image-qemux86-64.resinos-img'
 		compressed: true


Translate success message on invitation login form

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -554,8 +554,10 @@
             $analytics.trackEvent("auth", "invitationAccept", "invitation accept with new user", 1)

             $location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
-            $confirm.notify("success", "You've successfully joined this project",
-                                       "Welcome to #{_.escape($scope.invitation.project_name)}")
+            text = $translate.instant("INVITATION_LOGIN_FORM.SUCCESS", {
+                "project_name": $scope.invitation.project_name
+            })
+            $confirm.notify("success", text)

         onErrorSubmitRegister = (response) ->
             if response.data._error_message


Extract a `getAvailablePackages` method and start using it

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -10,6 +10,7 @@
 Package = require './package'
 ThemePackage = require './theme-package'
 {isDeprecatedPackage, getDeprecatedPackageMetadata} = require './deprecated-packages'
+packageJSON = require('../package.json')

 # Extended: Package manager for coordinating the lifecycle of Atom packages.
 #
@@ -39,7 +40,8 @@
     @packageDirPaths = []
     @deferredActivationHooks = []
     @triggeredActivationHooks = new Set()
-    @packagesCache = require('../package.json')?._atomPackages ? {}
+    @packagesCache = packageJSON._atomPackages ? {}
+    @packageDependencies = packageJSON.packageDependencies ? {}
     @initialPackagesLoaded = false
     @initialPackagesActivated = false
     @loadedPackages = {}
@@ -288,32 +290,46 @@

   # Public: Returns an {Array} of {String}s of all the available package paths.
   getAvailablePackagePaths: ->
-    packagePaths = []
-
-    for packageDirPath in @packageDirPaths
-      for packagePath in fs.listSync(packageDirPath)
-        packagePaths.push(packagePath) if fs.isDirectorySync(packagePath)
-
-    packagesPath = path.join(@resourcePath, 'node_modules')
-    for packageName of @getPackageDependencies()
-      packagePath = path.join(packagesPath, packageName)
-      packagePaths.push(packagePath) if fs.isDirectorySync(packagePath)
-
-    _.uniq(packagePaths)
+    @getAvailablePackages().map((a) -> a.path)

   # Public: Returns an {Array} of {String}s of all the available package names.
   getAvailablePackageNames: ->
-    _.uniq _.map @getAvailablePackagePaths(), (packagePath) -> path.basename(packagePath)
+    @getAvailablePackages().sort((a) -> a.name)

   # Public: Returns an {Array} of {String}s of all the available package metadata.
   getAvailablePackageMetadata: ->
     packages = []
-    for packagePath in @getAvailablePackagePaths()
-      name = path.basename(packagePath)
-      metadata = @getLoadedPackage(name)?.metadata ? @loadPackageMetadata(packagePath, true)
+    for pack in @getAvailablePackages()
+      metadata = @getLoadedPackage(pack.name)?.metadata ? @loadPackageMetadata(pack.path, true)
       packages.push(metadata)
     packages

+  getAvailablePackages: ->
+    packages = []
+    packagesByName = new Set()
+
+    for packageDirPath in @packageDirPaths
+      if fs.isDirectorySync(packageDirPath)
+        for packagePath in fs.readdirSync(packageDirPath)
+          packageName = path.basename(packagePath)
+          if not packageName.startsWith('.') and not packagesByName.has(packageName) and fs.isDirectorySync(packagePath)
+            packages.push({
+              name: packageName,
+              path: path.join(packageDirPath, packagePath),
+              isBundled: false
+            })
+            packagesByName.add(packageName)
+
+    for packageName of @packageDependencies
+      unless packagesByName.has(packageName)
+        packages.push({
+          name: packageName,
+          path: path.join(@resourcePath, 'node_modules', packageName),
+          isBundled: true
+        })
+
+    packages.sort((a, b) -> a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
+
   ###
   Section: Private
   ###
@@ -325,11 +341,6 @@
     @packageStates[name] = state

   getPackageDependencies: ->
-    unless @packageDependencies?
-      try
-        @packageDependencies = require('../package.json')?.packageDependencies
-      @packageDependencies ?= {}
-
     @packageDependencies

   hasAtomEngine: (packagePath) ->
@@ -369,11 +380,10 @@
     # of the first package isn't skewed by being the first to require atom
     require '../exports/atom'

-    packagePaths = @getAvailablePackagePaths()
-    packagePaths = packagePaths.filter (packagePath) => not @isPackageDisabled(path.basename(packagePath))
-    packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath)
+    packages = @getAvailablePackages().filter((p) => not @isPackageDisabled(p.name))
     @config.transact =>
-      @loadPackage(packagePath) for packagePath in packagePaths
+      for pack in packages
+        @loadPackage(pack.path)
       return
     @initialPackagesLoaded = true
     @emitter.emit 'did-load-initial-packages'


Spaces

Spaces

--- app/assets/javascripts/submission_file.js.coffee
+++ app/assets/javascripts/submission_file.js.coffee
@@ -2,4 +2,4 @@
   $('#submission').fileupload
     done: (e, data)->
       console.log "Done", data.result
-      $('
'+data.result+'
').appendTo(this)+      $('
' + data.result + '
').appendTo(this)


HoverDetail that can cope with unstacked bar charts

--- app/assets/javascripts/utils/RickshawUtil.coffee
+++ app/assets/javascripts/utils/RickshawUtil.coffee
@@ -61,6 +61,59 @@
             @onClick @hoveredSeries
       super()

+    # TODO: contribute this back to Rickshaw.js
+    updateGroupedHover: (e) ->
+      e ?= @lastEvent
+      return  unless e
+
+      @lastEvent = e
+      return  unless e.target.nodeName.match(/^(path|svg|rect)$/)
+
+      graph = @graph
+      $el = $(@element)
+
+      eventX = e.offsetX or e.layerX
+      eventY = e.offsetY or e.layerY
+      domainX = Math.floor(graph.x.invert(eventX))
+      barsXStart = graph.x(domainX)
+      formattedXValue = @xFormatter(domainX)
+      activeSeries = graph.series.active()
+
+      seriesBarWidth = graph.renderer.barWidth() / activeSeries.length
+      activeSeriesIndex = Math.floor((eventX - barsXStart) / seriesBarWidth)
+
+      # Bail out if the cursor's in the margin between two X values
+      return  if activeSeriesIndex > activeSeries.length - 1
+
+      detailXOffset = barsXStart + (activeSeriesIndex * seriesBarWidth)
+      detail = for series, index in activeSeries
+        data = series.data[domainX]
+        {
+          series
+          formattedYValue: @yFormatter(data.y)
+          graphX: barsXStart
+          graphY: graph.y(data.y)
+          order: index
+          name: series.name
+          value: data
+          active: index == activeSeriesIndex
+        }
+
+      $el.html('').css(left: "#{detailXOffset}px")
+
+      if @visible then @render
+        detail: detail
+        domainX: domainX
+        formattedXValue: formattedXValue
+        mouseX: eventX
+        mouseY: eventY
+
+    update: (e) ->
+      if @graph.renderer.unstack
+        @updateGroupedHover(e)
+      else
+        super
+
   class @ThresholdLine
     constructor: (args) ->
       if args.graph.renderer.name != "bar"


style(stats title): add version

style(stats title): add version

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -727,7 +727,7 @@
 gulp.task 'plato', ['clean:working'], ->
 	options =
 		plato:
-			title: pkg.name
+			title: "#{pkg.name} v#{pkg.version}"

 	srcs = []


changed the ignore options to regex patterns

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -177,18 +177,19 @@
       html: ['build/**/*.html'],
       ignore: [
                 new RegExp('^.no-touch.*'),
-                new RegExp('^.Header*'),
+                new RegExp('\.Header*(.)\S+'),
                 new RegExp('^.js-side-nav*'),
                 new RegExp('^.side-nav*'),
-                '.Header-logo','.logotype','.Header-bkg-transparent','.Header-bkg-transparent .Header-nav-item','.Header-bkg-opaque','.Header-nav-item','.Header-nav-item:after','.Header-nav-item:hover:after','.Header-cart-button-container',
-                '.cart-full','.Cart-table-container','.Cart-rowContainer',
-                'cursor default:hover','.no-touch','.no-touch a:hover','.no-touch .Button:hover','.no-touch .button-flex:hover','.no-touch .Button-inverse:hover','.no-touch .Button-sm:hover','.no-touch .Button--cart:hover','.no-touch .Button-player:hover','.no-touch .Button-dataset:hover','.no-touch .Button--cart:active',
-                '.Wallop-dot','.Wallop-dot--current','.Wallop-item','.Wallop-item--hidePrevious','.Wallop-item--current','.Wallop-item--showNext',
-                '.StoreConfig-world:last-child','.StoreConfig-world','.StoreConfig-eye','.StoreConfig-eye:last-child','.StoreConfig--state-active','.StoreConfig--state-inactive','.Store-license','.no-touch .Store-license:hover',
-                '.AddtoCart','.Button-cart','.Cart--triangle-up','.Cart--triangle-down','.Cart-itemQuant--increment','.no-touch .Cart-itemQuant--increment:hover','.Cart-itemQuant--increment:active','.Cart-itemQuant--increment:active >p.Cart--triangle-up','.Cart-itemQuant--increment:active >p.Cart--triangle-down','.no-touch .Cart-itemQuant--increment:hover >p.Cart--triangle-up',
-                '.Grid-cell--1of6','.Grid-cell--top','.Grid--cartFormula-break',
-                '.Aligner-item','.Grid--gutters-lg > .Aligner-item','hr,[role="button"]',
-                '.TechSpecs-table','.TechSpecs-txt--eye', '.TechSpecs-table td', '.TechSpecs-table .TechSpecs-table--column', '.TechSpecs-table--column'
+                new RegExp('\.logotype*(.)\S+')
+                new RegExp('\.cart-*(.)\S+')
+                new RegExp('\.no-touch*(.)\S+')
+                new RegExp('\.Wallop.*(.)\S+')
+                new RegExp('\.Store*(.)\S+')
+                new RegExp('\.Add*(.)\S+')
+                new RegExp('\.Button*(.)\S+')
+                new RegExp('\.Grid-*(.)\S+')
+                new RegExp('\.Aligner-*(.)\S+')
+                new RegExp('\.TechSpecs-*(.)\S+')
                 ]))
       .pipe(gulp.dest('build/css'))


remove gulp default task

remove gulp default task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -39,8 +39,6 @@
     .pipe sourcemaps.write()
     .pipe gulp.dest './.tmp/'

-gulp.task 'default', ['build']
-
 gulp.task 'test', ['build', 'build-test'], ->
   gulp.src './.tmp/*'
     .pipe mocha()


fix styles join

fix styles join

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -167,8 +167,8 @@
         .pipe(concat("app.css"))
         .pipe(gulp.dest(paths.tmp))

-gulp.task "css-app", ->
-    runSequence("sass-compile", "css-join")
+gulp.task "css-app", (cb) ->
+    runSequence("sass-compile", "css-join", cb)

 gulp.task "css-vendor", ->
     gulp.src(paths.css)


Terminate load task when package is deactivated

--- src/packages/snippets/index.coffee
+++ src/packages/snippets/index.coffee
@@ -15,8 +15,12 @@
     @loadAll()
     @rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor)

+  deactivate: ->
+    @loadSnippetsTask?.terminate()
+
   loadAll: ->
-    new LoadSnippetsTask(this).start()
+    @loadSnippetsTask = new LoadSnippetsTask(this)
+    @loadSnippetsTask.start()

   loadDirectory: (snippetsDirPath) ->
     for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0


Have hubot repond to <factoid>?

Have hubot repond to <factoid>?

--- src/scripts/factoid.coffee
+++ src/scripts/factoid.coffee
@@ -6,6 +6,7 @@
 # ~<factoid> - Prints the factoid, if it exists. Otherwise tells you there is no factoid.
 # ~tell <user> about <factoid> - Tells the user about a factoid, if it exists.
 # ~~<user> <factoid> - Same as ~tell, less typing.
+# <factoid>? - Same as ~<factiod> except for there is no response if not found.

 class Factoids
   constructor: (@robot) ->
@@ -29,8 +30,11 @@
     else
       "No factoid for #{key}. It can't also be #{val} if it isn't already something."

+  niceGet: (key) ->
+    @cache[key] or "No factoid for #{key}"
+
   get: (key) ->
-    @cache[key] or "No factoid for #{key}"
+    @cache[key]

   tell: (person, key) ->
     factoid = this.get key
@@ -47,7 +51,7 @@
     else if match = (/^~tell (.+) about (.+)/i.exec text) or (/^~~(.+) (.+)/.exec text)
       this.tell match[1], match[2]
     else if match = /^~(.+)/i.exec text
-      this.get match[1]
+      this.niceGet match[1]

 module.exports = (robot) ->
   factoids = new Factoids robot
@@ -57,3 +61,8 @@
       msg.send factoids.handleFactoid msg.message.text
     else
       msg.reply factoids.handleFactoid msg.message.text
+
+  robot.hear /(.+)\?/i, (msg) ->
+    factoid = factoids.get msg.match[1]
+    if factoid
+      msg.reply msg.match[1] + " is " + factoid


Make 404 error message more explicit

Make 404 error message more explicit

--- src/scripts/jenkins.coffee
+++ src/scripts/jenkins.coffee
@@ -61,7 +61,7 @@
         else if 400 == res.statusCode
           jenkinsBuild(msg, true)
         else if 404 == res.statusCode
-          msg.reply "Build not found"
+          msg.reply "Build not found, double check that it exists and is spelt correctly."
         else
           msg.reply "Jenkins says: Status #{res.statusCode} #{body}"


Add 'last' command for details of latest build for specified job.  Have 'describe' command use configured URL for subsequent call for details of latest build.  Add help for 'last' command and include others in help index.

--- src/scripts/jenkins.coffee
+++ src/scripts/jenkins.coffee
@@ -16,6 +16,7 @@
 #   hubot jenkins build <job>, <params> - builds the specified Jenkins job with parameters as key=value&key2=value2
 #   hubot jenkins list <filter> - lists Jenkins jobs
 #   hubot jenkins describe <job> - Describes the specified Jenkins job
+#   hubot jenkins last <job> - Details about the last build for the specified Jenkins job

 #
 # Author:
@@ -83,7 +84,7 @@
           try
             content = JSON.parse(body)
             response += "JOB: #{content.displayName}\n"
-            response += "URL: #{url}/job/#{job}\n"
+            response += "URL: #{content.url}\n"

             if content.description
               response += "DESCRIPTION: #{content.description}\n"
@@ -115,7 +116,7 @@
             if not content.lastBuild
               return

-            path = "#{content.lastBuild.url}/api/json"
+            path = "#{url}/job/#{job}/#{content.lastBuild.number}/api/json"
             req = msg.http(path)
             if process.env.HUBOT_JENKINS_AUTH
               auth = new Buffer(process.env.HUBOT_JENKINS_AUTH).toString('base64')
@@ -141,6 +142,36 @@
           catch error
             msg.send error

+jenkinsLast = (msg) ->
+    url = process.env.HUBOT_JENKINS_URL
+    job = msg.match[1]
+
+    path = "#{url}/job/#{job}/lastBuild/api/json"
+
+    req = msg.http(path)
+
+    if process.env.HUBOT_JENKINS_AUTH
+      auth = new Buffer(process.env.HUBOT_JENKINS_AUTH).toString('base64')
+      req.headers Authorization: "Basic #{auth}"
+
+    req.header('Content-Length', 0)
+    req.get() (err, res, body) ->
+        if err
+          msg.send "Jenkins says: #{err}"
+        else
+          response = ""
+          try
+            content = JSON.parse(body)
+            response += "NAME: #{content.fullDisplayName}\n"
+            response += "URL: #{content.url}\n"
+
+            if content.description
+              response += "DESCRIPTION: #{content.description}\n"
+
+            response += "BUILDING: #{content.building}\n"
+
+            msg.send response
+
 jenkinsList = (msg) ->
     url = process.env.HUBOT_JENKINS_URL
     filter = new RegExp(msg.match[2], 'i')
@@ -184,7 +215,12 @@
   robot.respond /j(?:enkins)? describe (.*)/i, (msg) ->
     jenkinsDescribe(msg)

+  robot.respond /j(?:enkins)? last (.*)/i, (msg) ->
+    jenkinsLast(msg)
+
   robot.jenkins = {
     list: jenkinsList,
     build: jenkinsBuild
+    describe: jenkinsDescribe
+    last: jenkinsLast
   }


Fix respond regexp to support job names with dashes

--- src/scripts/jenkins.coffee
+++ src/scripts/jenkins.coffee
@@ -11,7 +11,7 @@
 # build branch master - starts a build for branch origin/master
 # build branch master on job Foo - starts a build for branch origin/master on job Foo
 module.exports = (robot) ->
-  robot.respond /build\s*(branch\s+)?([\w\d\-\/]+)(\s+(on job)?\s*(\w+))?/i, (msg)->
+  robot.respond /build\s*(branch\s+)?([\w\/-]+)(\s+(on job)?\s*([\w-]+))?/i, (msg) ->

     url = process.env.HUBOT_JENKINS_URL


comment clarification

comment clarification

--- src/sigchain/sigchain.iced
+++ src/sigchain/sigchain.iced
@@ -43,10 +43,11 @@
     cb null, parsed_keys, default_eldest_kid_for_testing

   constructor : ({kids_to_key_managers}) ->
-    # ParsedKeys should only be used as a map from KIDs to KeyManagers. It MUST
-    # NOT be interpreted as a set of valid KIDs for a user, because it's
-    # computed from untrusted data. To forestall that mistake, we keep the map
-    # _private and only expose the get_key_manager() method.
+    # Callers should use this class to get KeyManagers for KIDs they already
+    # have, but callers MUST NOT iterate over the set of KIDs here as though it
+    # were the valid set for a given user. The set of KIDs is *untrusted*
+    # because it comes from the server. We keep the map private to prevent that
+    # mistake.
     @_kids_to_key_managers = kids_to_key_managers

   get_key_manager : (kid) ->


show share url in the right places

show share url in the right places

--- query_runner.coffee
+++ query_runner.coffee
@@ -79,7 +79,7 @@
   constructor: (@replyContext, @querySlug) ->
     super @replyContext

-  showShareUrl: -> true
+  showShareUrl: -> false

   postImage: (query, imageData, options = {}) ->
     success = (url) =>
@@ -197,6 +197,8 @@
   constructor: (@replyContext, @textQuery, @visualization) ->
     super @replyContext

+  showShareUrl: -> true
+
   work: ->

     [txt, limit, path, ignore, fields] = @textQuery.match(/([0-9]+ )?(([\w]+\/){0,2})(.+)/)


fix grouping when selecting multiple keys

--- src/scripts/editor/Property.coffee
+++ src/scripts/editor/Property.coffee
@@ -47,6 +47,8 @@
       # Get the property container.
       $container = @getContainer(lineData)

+      $tween_container = $container
+
       # Basic data, loop through properties.
       for key, instance_prop of lineData.properties
         # show all properties or only 1 if we selected a key.
@@ -55,9 +57,13 @@
           numberProp = @addNumberProperty(instance_prop, lineData, key_val, $grp_container)
           @items.push(numberProp)

+          if instance_prop.name == property_name
+            $tween_container = $grp_container
+
       if property_name
         # Add tween select if we are editing a key.
-        tweenProp = @addTweenProperty(instance_prop, lineData, key_val, $container, propertyData, domElement)
+        #$grp_container = @getGroupContainer(instance_prop, $container)
+        tweenProp = @addTweenProperty(instance_prop, lineData, key_val, $tween_container, propertyData, domElement)
         @items.push(tweenProp)

       return


don't map core:move-up for vim-mode-plus

--- keymaps/narrow.cson
+++ keymaps/narrow.cson
@@ -44,7 +44,7 @@
 'atom-text-editor.narrow.narrow-editor:not(.read-only)[data-grammar="source narrow"]':
   'escape': 'narrow-ui:stop-insert'

-'atom-text-editor.narrow.narrow-editor.read-only[data-grammar="source narrow"]':
+'atom-text-editor.narrow.narrow-editor.read-only[data-grammar="source narrow"]:not(.vim-mode-plus)':
   'k': 'core:move-up'
   'j': 'core:move-down'


.

Former-commit-id: 9105b6b7bf7652b8f789b518e47d9aaebe728eb5 [formerly fae60b186e6848133baa8c561adfe2a4cf5e0626] [formerly 4ba6a67f347d396d3bdcc235562c7a5254902218 [formerly 0402c716b567fbcd2b4e075e2e0ba03f9fbda697]] [formerly e6753be44d833052eddec65cfa5f6f1468f06879 [formerly 5f924c9cc8ec5675735a00b641c36ef7454c4ea0] [formerly fb1cb42c24fa63d6238339d088b9ac9378e22164 [formerly fb1cb42c24fa63d6238339d088b9ac9378e22164 [formerly 1bda9d51503462bea53cd70ad85deacb54892413]]]]
Former-commit-id: 78909c90535845e35730579eb52a2e08f55e33e3 [formerly 3fc9d2db7de25b2cb2a4464c4e1336ab60002d7f] [formerly 7bfc1b0ee78999f1505070dac41ad6c04ca3a516 [formerly 9ee64ec015f3f2a5a0cd8ef8a3793169aedf35b8]]
Former-commit-id: ce7893223e1e8cd342d8fe74abb1007849736441 [formerly 28cadcdcc24023a2c7b8f29aee1ee49114a1be2e]
Former-commit-id: 481480dbf24a48d61b98f54dc96500c16a12fc17
Former-commit-id: d50f3144c9133c7549423ae1f2618bb87ce76b3f

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -146,14 +146,14 @@

     if !@local.initialized
       # We store the current value of the HTML at
-      # this component's key. This is so that the
-      # parent component can fetch the value outside
+      # this component's key. This allows the
+      # parent component to fetch the value outside
       # of this generic wysiwyg component.
       # However, we "dangerously" set the html of the
       # editor to the original @props.html. This is
       # because we don't want to interfere with the
       # wysiwyg editor's ability to manage e.g.
-      # selection location.
+      # the selection location.
       my_data.html = @props.html
       @local.initialized = true
       save @local; save my_data


.

Former-commit-id: 0e01fad600fd9fc9fbc9be1a7ec66e02d4e49c09 [formerly 9b880b1afc623ca551743eb954a8f6bdad3f3448] [formerly fb0f52246bd6542819b51ca8d9682df7637a8435 [formerly 4cb10f4f4a2769ff6648faaf189406e2985825f0]] [formerly 875b2b05121b11333557057e6b4b3a8abd8ef291 [formerly 9afbe1e0e7b714dd78ca15813b8cbde79445287a] [formerly 7523f2aa16b0ebd09fd75ce25756b1b94cc09dd1 [formerly 7523f2aa16b0ebd09fd75ce25756b1b94cc09dd1 [formerly d4a2a9a0610eb62cba036abbb868438ea4ad922a]]]]
Former-commit-id: 7b5ab41e3b4a2158d54d7c0624100de3566ec625 [formerly e22797d211f8aee52694286f1cf15a763367d7fe] [formerly 7862ac408600bc100aee558bd7623570eb872251 [formerly 272bb4a5231b96baa200d861f9bf6f903caf9b58]]
Former-commit-id: db4c7cd83b6a8021f619aaefdf5e79b609fde079 [formerly 53c739e8a6f7df6fab0a80cf20da5906006db418]
Former-commit-id: 756abd1165f2505eb240bdfb386c72448ac88b80
Former-commit-id: 940caffca76907016001b088323afa2680f45440

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -25,6 +25,10 @@
               # Instruct Backbone to trigger routing events
               window.app_router.navigate href, { trigger : true }

+              # When we navigate to another internal page, we typically want the
+              # page to be scrolled to the top of the new page. The programmer can
+              # set "data-no-scroll" on the link if they wish to prevent this
+              # behavior.
               if !@getDOMNode().getAttribute('data-no-scroll')
                 window.scrollTo(0, 0)
               return false


change stats ttl to 60s; waste less resources...

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -2168,7 +2168,7 @@
     # in cache for ttl seconds.
     get_stats: (opts) =>
         opts = defaults opts,
-            ttl : 30         # how long cached version lives (in seconds)
+            ttl : 60         # how long cached version lives (in seconds)
             cb  : undefined
         stats = undefined
         dbg = @dbg('get_stats')


fix bugin previous commit

fix bugin previous commit

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -778,7 +778,7 @@
             account_id    : undefined
             email_address : undefined
             cb            : required
-        if not opts.account_id? or opts.email_address?
+        if not opts.account_id? and not opts.email_address?
             opts.cb("one of email address or account_id must be specified")
             return


grid: loadWatcher and showWatcher

grid: loadWatcher and showWatcher

--- app/_src/components/grid.coffee
+++ app/_src/components/grid.coffee
@@ -56,7 +56,8 @@
 	currentBreakpointKey = null
 	debouncedResizeFn = null
 	spinnerTimerId = null
-	watcher = null
+	showWatcher = null
+	loadWatcher = null
 	isWorking = false
 	hasBeenShown = false
 	imagesLoaded = null
@@ -88,9 +89,11 @@
 			@initializeGrid()

 	destroy: =>
-		@removeWatcherListeners()
+		@destroyShowWatcher()
+		@destroyLoadWatcher()
 		@removeEventListeners()
 		@resetDOMModel()
+		@spinner.hide()
 		hasBeenShown = false
 		if spinnerTimerId isnt null then clearTimeout spinnerTimerId

@@ -116,23 +119,20 @@
 			navigator.msMaxTouchPoints or
 			typeof scrollMonitor isnt 'object'
 				# Except for mobile...
-				@onEnterViewport()
+				@onShowEnterViewport()
 		else
-			watcher = scrollMonitor.create( @$context, 500 )
-			watcher.recalculateLocation()
-			@addWatcherListeners()
-
-	###
-		Add watcher event callbacks
-	###
-	addWatcherListeners: =>
-		if watcher isnt null
-			watcher.enterViewport @onEnterViewport
-			watcher.exitViewport @onExitViewport
-
-
-	###
-		Add event callbacks
+			loadWatcher = scrollMonitor.create( @$context, 2000 )
+			loadWatcher.enterViewport @onLoadEnterViewport
+
+			showWatcher = scrollMonitor.create( @$context, -150 )
+			showWatcher.enterViewport @onShowEnterViewport
+			showWatcher.exitViewport @onShowExitViewport
+
+		@addEventListeners()
+
+
+	###
+		Add general events callbacks
 	###
 	addEventListeners: =>
 		# Check if we're not coming back here again
@@ -141,17 +141,27 @@
 		$(window).on 'resize', debouncedResizeFn
 		@

+	###
+		Remove general events callbacks
+	###
 	removeEventListeners: =>
 		$(window).off 'resize', debouncedResizeFn
 		debouncedResizeFn = null

 	###
-		Removes watcher events callbacks
-	###
-	removeWatcherListeners: () =>
-		if watcher isnt null
-			watcher.destroy()
+		Removes showWatcher events callbacks
+	###
+	destroyShowWatcher: () =>
+		if showWatcher isnt null
+			showWatcher.destroy()
+
+	###
+		Removes loadWatcher event callbacks
+	###
+	destroyLoadWatcher: () =>
+		if loadWatcher isnt null
+			loadWatcher.destroy()

 	###
 		This function detects which breakpoint are we working on now
@@ -179,24 +189,24 @@
 				@

-
 	###

 		Callbacks

 	####
-
-	###
-		Callback for when it's visible on the page view
-	###
-	onEnterViewport: () =>
-		return if hasBeenShown
+	onLoadEnterViewport: () =>
+		@destroyLoadWatcher()
 		if currentBreakpointKey isnt null
 			@createGrid()
-			@showGrid()
-		@addEventListeners()
-		@onWindowResize() # in case we have resized before all this happened
+
+	###
+		Callback for when it's visible on the page view
+	###
+	onShowEnterViewport: () =>
+		return if hasBeenShown
+		@showGrid()
 		hasBeenShown = true
+

 	###
 		Shows all items when the images have been loaded.
@@ -238,8 +248,8 @@
 	###
 		Callback for when it's exiting the page view
 	###
-	onExitViewport: () =>
-		if hasBeenShown then @removeWatcherListeners()
+	onShowExitViewport: () =>
+		if hasBeenShown then @destroyShowWatcher()

 	###
 		Callback for when the window is resized


storage: zfs snapshot timeout = 2^23 works

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -1365,15 +1365,17 @@

             task_ensure_zfs_snapshots_are_mounted = (cb) ->
                 misc_node.execute_code
-                    command : "ls /projects/.zfs/snapshot/*/XXX"
+                    command : "mountpoint -q /projects && ls /projects/.zfs/snapshot/*/XXX"
                     bash    : true
                     timeout : 60*5    # it can take a while to get the listing (usually like 20-30s first time)
                     cb      : (err, output) ->
                         if err and output?.stderr?.indexOf("Object is remote") == -1
                             # Object is remote *is* an expected error
-                            dbg("ERROR: task_ensure_zfs_snapshots_stay_mounted failed! -- #{misc.to_json(err)}")
+                            dbg("ERROR: task_ensure_zfs_snapshots_are_mounted failed! -- #{misc.to_json(err)}")
+                            dbg("will try again in 15s")
+                            setInterval(task_ensure_zfs_snapshots_are_mounted, 15000)
                         else
-                            dbg("SUCCESS: task_ensure_zfs_snapshots_stay_mounted")
+                            dbg("SUCCESS: task_ensure_zfs_snapshots_are_mounted")
                         cb?(err)

             # check which bup snapshots need updates once every 13 minutes
@@ -1388,10 +1390,13 @@
             task_update_snapshots()

             # mount all of the ZFS snapshots
-            # they should stay mounted due to
-            #      echo "options zfs zfs_expire_snapshot=99999999" >> /etc/modprobe.d/zfs.conf
+            # they should stay mounted due to
+            #      echo "options zfs zfs_expire_snapshot=8388608" >> /etc/modprobe.d/zfs.conf
             task_ensure_zfs_snapshots_are_mounted () ->
                 task_mount_snapshots_on_all_compute_vms()
+
+            # anything more than 8388608s (=2^23s) for the zfs_expire_snapshot parameter fails to work, causing instant unmount :-(
+            setInterval(task_ensure_zfs_snapshots_are_mounted, 8388608*1000)

             cb()


Remove obsolete MainCtrl

Remove obsolete MainCtrl

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -4,10 +4,6 @@

   .controller 'NavCtrl', ($scope, $location) ->
     $scope.location = $location
-
-  .controller 'MainCtrl', ($scope, Waterpoint) ->
-    Waterpoint.query (waterpoints) ->
-      $scope.waterpoints = waterpoints._items

   .controller 'LocaleCtrl', ($scope, $cookies, $rootScope, gettextCatalog) ->
     # get the current language from the cookie if available


do not run share server in dev mode, since it wastes resources and slows startup a lot

--- src/smc-hub/hub_http_server.coffee
+++ src/smc-hub/hub_http_server.coffee
@@ -456,21 +456,15 @@
         app.post(raw_regexp, dev_proxy_raw)

         # Also create and expose the share server
-        PROJECT_PATH = conf.project_path()
-        share_server = require('./share/server')
-        share_router = share_server.share_router
-            database : opts.database
-            path     : "#{PROJECT_PATH}/[project_id]"
-            base_url : opts.base_url
-            logger   : winston
-        app.use(opts.base_url + '/share', share_router)
-        ### -- delete
-        raw_router = share_server.raw_router
-            database : opts.database
-            path     : "#{PROJECT_PATH}/[project_id]"
-            logger   : winston
-        app.use(opts.base_url + '/raw',   raw_router)
-        ###
+        if false
+            PROJECT_PATH = conf.project_path()
+            share_server = require('./share/server')
+            share_router = share_server.share_router
+                database : opts.database
+                path     : "#{PROJECT_PATH}/[project_id]"
+                base_url : opts.base_url
+                logger   : winston
+            app.use(opts.base_url + '/share', share_router)

     app.on 'upgrade', (req, socket, head) ->


chainable methods

chainable methods

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -1,5 +1,5 @@
 # hexaFlip
-# 0.0.2
+# 0.0.3
 # Dan Motzenbecker
 # http://oxism.com
 # Copyright 2013, MIT License
@@ -41,8 +41,8 @@
     unless @sets
       @el.classList.add cssClass + '-timepicker'
       @sets =
-        hour: (i + '' for i in [1..12])
-        minute: (i + '0' for i in [0..5])
+        hour:     (i + '' for i in [1..12])
+        minute:   (i + '0' for i in [0..5])
         meridian: ['am', 'pm']

     setsKeys = Object.keys @sets
@@ -75,12 +75,12 @@

   _createCube: (set) ->
     cube =
-      set: set
+      set:    set
       offset: 0
-      start: 0
-      delta: 0
-      last: 0
-      el: document.createElement 'div'
+      start:  0
+      delta:  0
+      last:   0
+      el:     document.createElement 'div'

     cube.el.className = "#{ cssClass }-cube #{ cssClass }-cube-#{ set }"
     cube.el.style.margin = "0 #{ @margin }px"
@@ -89,7 +89,7 @@

     for side in faceNames
       cube[side] = document.createElement 'div'
-      cube[side].className = cssClass + '-' + side
+      cube[side].className = "#{ cssClass }-#{ side }"
       rotation = do ->
         switch side
           when 'front'
@@ -232,6 +232,7 @@
       cube.delta = cube.last = 90 * index
       @_setSides cube
       @_setContent cube[faceSequence[index % 4]], value
+    @

   getValue: ->
@@ -259,6 +260,7 @@
       continue if cube.touchStarted
       cube.delta = cube.last += delta
       @_setSides cube
+    @

   flipBack: ->


Remove minimal-ui because iOS8 removed it.

--- app/server/js/react/app.coffee
+++ app/server/js/react/app.coffee
@@ -15,8 +15,7 @@
         html lang: 'en', className: @getHtmlClassName(),
           head {},
             meta charSet: 'utf-8'
-            # http://www.mobilexweb.com/blog/ios-7-1-safari-minimal-ui-bugs
-            meta name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimal-ui, maximum-scale=1.0, user-scalable=no'
+            meta name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'
             title {}, this.props.title
             link href: '/app/client/img/favicon.ico?v=' + this.props.version, rel: 'shortcut icon'
             link href: '/app/client/build/app.css?v=' + this.props.version, rel: 'stylesheet'


Remove console output.

Remove console output.

--- src/spec/services/base.spec.coffee
+++ src/spec/services/base.spec.coffee
@@ -200,7 +200,6 @@
             expect(result).toEqual ['done']
             done()
           .fail (err) ->
-            console.err err
             done err

         it 'should should call process function for several page', (done) ->
@@ -215,7 +214,6 @@
             expect(result[2]).toMatch /\?limit=20&offset=80$/
             done()
           .fail (err) ->
-            console.err err
             done err

         it 'should fail if the process functions rejects', (done) ->
@@ -239,7 +237,6 @@
             expect(result[1]).toMatch /\?where=foo%3Dbar&limit=20&offset=20&sort=name%20DESC%20asc$/
             done()
           .fail (err) ->
-            console.err err
             done err

       describe ':: fetch', ->


Adjust specs

Adjust specs

--- src/spec/services/base.spec.coffee
+++ src/spec/services/base.spec.coffee
@@ -89,12 +89,10 @@
         expect(@service._currentEndpoint).toBe o.path

       it 'should reset default params', ->
-        expect(@service._params).toEqual
-          query:
-            where: []
-            operator: 'and'
-            sort: []
-            expand: []
+        expect(@service._params.query.where).toEqual []
+        expect(@service._params.query.operator).toBe 'and'
+        expect(@service._params.query.sort).toEqual []
+        expect(@service._params.query.expand).toEqual []

       it 'should build endpoint with id', ->
         @service.byId(ID)
@@ -188,25 +186,21 @@
       ], (f) ->
         it "should reset params after creating a promise for #{f[0]}", ->
           _service = @service.byId('123-abc').where('name = "foo"').page(2).perPage(10).sort('id').expand('foo.bar')
-          expect(@service._params).toEqual
-            id: '123-abc'
-            query:
-              where: [encodeURIComponent('name = "foo"')]
-              operator: 'and'
-              sort: [encodeURIComponent('id asc')]
-              page: 2
-              perPage: 10
-              expand: [encodeURIComponent('foo.bar')]
+          expect(@service._params.id).toBe '123-abc'
+          expect(@service._params.query.where).toEqual [encodeURIComponent('name = "foo"')]
+          expect(@service._params.query.operator).toBe 'and'
+          expect(@service._params.query.sort).toEqual [encodeURIComponent('id asc')]
+          expect(@service._params.query.page).toBe 2
+          expect(@service._params.query.perPage).toBe 10
+          expect(@service._params.query.expand).toEqual [encodeURIComponent('foo.bar')]
           if f[1]
             _service[f[0]](f[1])
           else
             _service[f[0]]()
-          expect(@service._params).toEqual
-            query:
-              where: []
-              operator: 'and'
-              sort: []
-              expand: []
+          expect(@service._params.query.where).toEqual []
+          expect(@service._params.query.operator).toBe 'and'
+          expect(@service._params.query.sort).toEqual []
+          expect(@service._params.query.expand).toEqual []

       it 'should pass original request to failed response', (done) ->
         spyOn(@service._rest, 'POST').andCallFake (endpoint, payload, callback) -> callback(null, {statusCode: 400}, {statusCode: 400, message: 'Oops, something went wrong'})


YQLł̉ðÍ

YQLł̉ðÍ

--- AppStoreClient.coffee
+++ AppStoreClient.coffee
@@ -143,6 +143,42 @@
     client.open "GET", url

     client.send()
+
+  ###*
+  get data
+  @param {function} callback
+  @param {function} errorcallback
+  @param {object} callback
+  ###
+  @getItunesRssDataYql:(callback,errorcallback,obj)->
+    country = obj?.country ? require("AppStoreClient/AppStoreClient").getTzOff()
+    limit = obj?.limit ? 200
+    url = "https://itunes.apple.com/"+country.toLowerCase()+"/rss/topsongs/limit=#{limit}/explicit=true/xml"
+    unless ENV_PRODUCTION then Ti.API.debug "url=#{url}"
+
+    Titanium.Yahoo.yql 'select * from xml where url="'+url+'"', (e) ->
+      if e.success and e.data?
+        arr = e.data?.feed?.entry ? []
+        objs = for item in arr
+          obj = {}
+          obj.trackName = item.name
+          obj.artistName = item.artist?.content ? ""
+          tmpurl = null
+          tmpsizemax = 0
+          for image in item.image
+            nowsize = image.height - 0
+            if tmpsizemax < nowsize
+              tmpsizemax = nowsize
+              obj.artworkUrl100 = image.content
+          for link in item.link
+            obj.previewUrl = link.href if link.assetType is "preview"
+
+          #購入リンク
+          obj.trackViewUrl = item.id?.content
+          obj
+        callback objs
+      else errorcallback e
+

   ###*
   get data


Makes dist task debug off

Makes dist task debug off

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -14,7 +14,7 @@
         ext: ".js"
         dest: "app"
         options:
-          debug: true
+          debug: false
           transform: ["coffeeify"]
       test:
         files:


coffeelint passing gruntfile

coffeelint passing gruntfile

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -46,7 +46,7 @@
         files: [ '<%%= config.app %>/{,*/}*.html' ]
         tasks: [ 'replace:pages' ]
         options:
-          livereload: true;
+          livereload: true

       livereload:
         options:


subkey-iterator inherits EncodingIterator now

--- src/subkey-iterator.coffee
+++ src/subkey-iterator.coffee
@@ -1,5 +1,5 @@
 # Copyright (c) 2015 Riceball LEE, MIT License
-AbstractIterator      = require("abstract-iterator")
+EncodingIterator      = require("encoding-iterator")
 inherits              = require("abstract-object/lib/util/inherits")
 isArray               = require("abstract-object/lib/util/isArray")
 extend                = require("abstract-object/lib/util/_extend")
@@ -11,13 +11,13 @@
 encodeKey             = codec.encodeKey
 decodeKey             = codec.decodeKey

-module.exports =  class SubkeyIterator
-  inherits EncodingIterator, AbstractIterator
+module.exports = class SubkeyIterator
+  inherits SubkeyIterator, EncodingIterator

   encodeOptions: (options)->
     vOptions = extend {}, options
     vPath = options.path || []
-    options.valueEncoding = valueEncoding = Codec(options.valueEncoding)
+    options.valueEncoding = Codec(options.valueEncoding)
     options.keyEncoding = keyEncoding = Codec(options.keyEncoding)
     #the key is lowerBound or upperBound.
     #if opts.start is exists then lowBound key is opt.start


Fixes for issue #271 newvm

Fixes for issue #271 newvm

--- app/modules/new-vm/index.coffee
+++ app/modules/new-vm/index.coffee
@@ -1,5 +1,6 @@
 angular = require 'angular'
 cloneDeep = require 'lodash.clonedeep'
+filter = require 'lodash.filter'
 forEach = require 'lodash.foreach'

 #=====================================================================
@@ -33,17 +34,57 @@
         result

     pool = default_SR = null
+    host = null
     do (
       networks = xoApi.getIndex('networksByPool')
+      srsByContainer = xoApi.getIndex('srsByContainer')
+      vmTemplatesByContainer = xoApi.getIndex('vmTemplatesByContainer')
+      poolSrs = null
+      hostSrs = null
+      poolTemplates = null
+      hostTemplates = null
     ) ->
       Object.defineProperties($scope, {
         networks: {
           get: () => pool && networks[pool.id]
         }
       })
-
-    srsByContainer = xoApi.getIndex('srsByContainer')
-    vmTemplatesByContainer = xoApi.getIndex('vmTemplatesByContainer')
+      updateSrs = () =>
+        srs = []
+        poolSrs and forEach(poolSrs, (sr) => srs.push(sr))
+        hostSrs and forEach(hostSrs, (sr) => srs.push(sr))
+        $scope.writable_SRs = filter(srs, (sr) => sr.content_type isnt 'iso')
+        $scope.ISO_SRs = filter(srs, (sr) => sr.content_type is 'iso')
+      updateTemplates = () =>
+        templates = []
+        poolTemplates and forEach(poolTemplates, (template) => templates.push(template))
+        hostTemplates and forEach(hostTemplates, (template) => templates.push(template))
+        $scope.templates = templates
+      $scope.$watchCollection(
+        () => pool and srsByContainer[pool.id],
+        (srs) =>
+          poolSrs = srs
+          updateSrs()
+      )
+      $scope.$watchCollection(
+        () => host and srsByContainer[host.id],
+        (srs) =>
+          hostSrs = srs
+          updateSrs()
+      )
+      $scope.$watchCollection(
+        () => pool and vmTemplatesByContainer[pool.id],
+        (templates) =>
+          poolTemplates = templates
+          updateTemplates()
+      )
+      $scope.$watchCollection(
+        () => host and vmTemplatesByContainer[host.id],
+        (templates) =>
+          hostTemplates = templates
+          updateTemplates()
+      )
+
     $scope.$watch(
       -> get $stateParams.container
       (container) ->
@@ -60,38 +101,7 @@
           pool = container

         default_SR = get pool.default_SR
-        default_SR = if default_SR
-          default_SR.id
-        else
-          ''
-
-        # Computes the list of templates.
-        templates = $scope.templates = []
-        forEach(vmTemplatesByContainer[host.id], (template) =>
-          templates.push(template)
-          return
-        )
-        forEach(vmTemplatesByContainer[pool.id], (template) =>
-          templates.push(template)
-          return
-        )
-
-        # Computes the list of srs.
-        SRs = []
-        forEach(srsByContainer[host.id], (template) =>
-          SRs.push(template)
-          return
-        )
-        forEach(srsByContainer[pool.id], (template) =>
-          SRs.push(template)
-          return
-        )
-
-        # Computes the list of ISO SRs.
-        $scope.ISO_SRs = (SR for SR in SRs when SR.content_type is 'iso')
-
-        # Computes the list of writable SRs.
-        $scope.writable_SRs = (SR for SR in SRs when SR.content_type isnt 'iso')
+        default_SR = if default_SR then default_SR.id else ''
     )

     $scope.availableMethods = {}


Adding tutorial doc link to project builder landing page

--- app/pages/lab/landing-page.cjsx
+++ app/pages/lab/landing-page.cjsx
@@ -9,7 +9,7 @@
 counterpart.registerTranslations 'en',
   landing:
     title: "Zooniverse Project Builder"
-    content: "Anyone can build a Zooniverse project. Just upload your data and choose the tasks you want the volunteers to do. To find out more, read our How to Build a Project document, or click the button below to get started."
+    content: "Anyone can build a Zooniverse project. Just upload your data and choose the tasks you want the volunteers to do. To find out more, read our [How to Build a Project document](https://docs.google.com/document/d/1EpiOJFMGFzIq34NXkRvsO8-Hixl4MzxvwPm0_aiF9xo), or click the button below to get started."
     buttons:
       getStarted: "Get started!"
       signIn: "Sign in"


fix promise being returned

fix promise being returned

--- test/helpers/integration.coffee
+++ test/helpers/integration.coffee
@@ -31,6 +31,7 @@
     @child.on "message", -> callback?()
     @child.stdout.pipe process.stdout
     @child.stderr.pipe process.stdout
+  return

 # Closes the server child process, used in an `after` hook and on
 # `process.exit` in case the test suite is interupted.


movable velocity and acceleration getters

--- test/movable.coffee
+++ test/movable.coffee
@@ -25,6 +25,8 @@
 test "should be able to get velocity", ->
   object = GameObject

+  ok object.velocity()
+
 # TODO fix me
 #test "should increase velocity according to acceleration", ->
 #  particle = GameObject


fix old path to gruntfile

fix old path to gruntfile

--- lib/cli/main.coffee
+++ lib/cli/main.coffee
@@ -61,7 +61,7 @@
     description("Run a grunt command with lineman's version of grunt").
     action ->
       cli.tasks = grunt.util._(arguments).chain().toArray().initial().without("grunt").value()
-      grunt.cli(gruntfile: (__dirname + "/Gruntfile.coffee"))
+      grunt.cli(gruntfile: (__dirname + "/../../Gruntfile.coffee"))

   commander.command("*").description("unknown command").action ->
     commander.help()