add gulp task of compass

add gulp task of compass

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -25,7 +25,7 @@

 # create server
-gulp.task "connect", () ->
+gulp.task 'connect', () ->
   options =
     root: path.resolve base
     livereload: true
@@ -34,11 +34,10 @@
   connect.server options

-
-gulp.task "watch", () ->
-  gulp.watch srcs["watching"], ["compass", "coffee", "reload"]
-
-gulp.task "compass", () ->
+gulp.task 'watch', () ->
+  gulp.watch srcs['watching'], ['compass', 'coffee', 'reload']
+
+gulp.task 'compass', () ->
   options =
     config_file: base + 'config.rb'
     css: base + 'css/'
@@ -53,7 +52,7 @@
     .pipe gulp.dest base + 'css/'

-gulp.task "coffee", () ->
+gulp.task 'coffee', () ->
   gulp.src base + 'coffee/*.coffee'
     .pipe plumber()
     .pipe coffee(bare: false)
@@ -62,13 +61,13 @@
     .pipe gulp.dest base + 'js/'

-gulp.task "reload", ["compass", "coffee"] , () ->
-  gulp.src srcs["watching"]
+gulp.task 'reload', ['compass', 'coffee'] , () ->
+  gulp.src srcs['watching']
     .pipe connect.reload()

-gulp.task "default", ["coffee" ]
-gulp.task "dev", ["compass","coffee","connect", "watch" ]
+gulp.task 'default', ['coffee','compass' ]
+gulp.task 'dev', ['compass','coffee','connect', 'watch' ]

 gulp.task 'sketch', () ->
   gulp.src base + 'sketch/svg/*.sketch'
@@ -101,45 +100,45 @@

 # list of National Park
 entries =
-  "利尻礼文サロベツ":"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_rishirirebunLinkEnt.kml",
-  "知床"          :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_shiretokoLinkEnt.kml",
-  "阿寒"          :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_akanLinkEnt.kml",
-  "釧路湿原"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_kushiroLinkEnt.kml",
-  "大雪山"        :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_daisetsuzanLinkEnt.kml",
-  "支笏洞爺"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_shikotsutouyaLinkEnt.kml",
-  "十和田八幡平"    :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_towadahatimantaiLinkEnt.kml",
-  "三陸復興"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_sanrikufukkouLinkEnt.kml",
-  "磐梯朝日"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_bandaiasahiLinkEnt.kml",
-  "日光"          :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_nikkouLinkEnt.kml",
-  "尾瀬"          :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ozeLinkEnt.kml",
-  "秩父多摩甲斐"   :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_chichibutamaLinkEnt.kml",
-  "小笠原"        :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ogasawaraLinkEnt.kml",
-  "富士箱根伊豆"   :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_fujihakoneizuLinkEnt.kml",
-  "南アルプス"     :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_southalpsLinkEnt.kml",
-  "上信越高原"     :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_joshinetsuLinkEnt.kml",
-  "中部山岳"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_chubusangakuLinkEnt.kml",
-  "白山"          :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_hakusanLinkEnt.kml",
-  "伊勢志摩"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_iseshimaLinkEnt.kml",
-  "吉野熊野"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_yoshinokumanoLinkEnt.kml",
-  "山陰海岸"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_saninkaiganLinkEnt.kml",
-  "瀬戸内海"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_setonaikaiLinkEnt.kml",
-  "大山隠岐"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_daisenLinkEnt.kml",
-  "足摺宇和海"    :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ashizuriuwakaiLinkEnt.kml",
-  "西海"         :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_nishikaiLinkEnt.kml",
-  "雲仙天草"      :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_unzenamakusaLinkEnt.kml",
-  "阿蘇くじゅう"   :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_asokujuLinkEnt.kml",
-  "霧島錦江湾"    :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_kirishimakinkowanLinkEnt.kml",
-  "屋久島"       :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_yakushimaLinkEnt.kml",
-  "西表石垣"     :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_iriomoteishigakiLinkEnt.kml",
-  "慶良間諸島"   :"http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_keramashotouLinkEnt.kml"
+  '利尻礼文サロベツ':'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_rishirirebunLinkEnt.kml',
+  '知床'          :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_shiretokoLinkEnt.kml',
+  '阿寒'          :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_akanLinkEnt.kml',
+  '釧路湿原'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_kushiroLinkEnt.kml',
+  '大雪山'        :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_daisetsuzanLinkEnt.kml',
+  '支笏洞爺'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_shikotsutouyaLinkEnt.kml',
+  '十和田八幡平'    :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_towadahatimantaiLinkEnt.kml',
+  '三陸復興'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_sanrikufukkouLinkEnt.kml',
+  '磐梯朝日'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_bandaiasahiLinkEnt.kml',
+  '日光'          :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_nikkouLinkEnt.kml',
+  '尾瀬'          :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ozeLinkEnt.kml',
+  '秩父多摩甲斐'   :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_chichibutamaLinkEnt.kml',
+  '小笠原'        :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ogasawaraLinkEnt.kml',
+  '富士箱根伊豆'   :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_fujihakoneizuLinkEnt.kml',
+  '南アルプス'     :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_southalpsLinkEnt.kml',
+  '上信越高原'     :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_joshinetsuLinkEnt.kml',
+  '中部山岳'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_chubusangakuLinkEnt.kml',
+  '白山'          :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_hakusanLinkEnt.kml',
+  '伊勢志摩'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_iseshimaLinkEnt.kml',
+  '吉野熊野'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_yoshinokumanoLinkEnt.kml',
+  '山陰海岸'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_saninkaiganLinkEnt.kml',
+  '瀬戸内海'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_setonaikaiLinkEnt.kml',
+  '大山隠岐'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_daisenLinkEnt.kml',
+  '足摺宇和海'    :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_ashizuriuwakaiLinkEnt.kml',
+  '西海'         :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_nishikaiLinkEnt.kml',
+  '雲仙天草'      :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_unzenamakusaLinkEnt.kml',
+  '阿蘇くじゅう'   :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_asokujuLinkEnt.kml',
+  '霧島錦江湾'    :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_kirishimakinkowanLinkEnt.kml',
+  '屋久島'       :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_yakushimaLinkEnt.kml',
+  '西表石垣'     :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_iriomoteishigakiLinkEnt.kml',
+  '慶良間諸島'   :'http://www.biodic.go.jp/trialSystem/LinkEnt/nps/NPS_keramashotouLinkEnt.kml'
 styles =
-    "特別保護地区" : "#dd6"
-    "海域公園地区" : "#23d"
-    "海中公園地区" : "#23d"
-    "第1種特別地域": "#d6d"
-    "第2種特別地域": "#d66"
-    "第3種特別地域": "#6d6"
-    "普通地域"    : "#6dd"
+    '特別保護地区' : '#dd6'
+    '海域公園地区' : '#23d'
+    '海中公園地区' : '#23d'
+    '第1種特別地域': '#d6d'
+    '第2種特別地域': '#d66'
+    '第3種特別地域': '#6d6'
+    '普通地域'    : '#6dd'

 gulp.task 'download', () ->
   for npname, url of entries


better test is now

better test is now

--- src/keygen.iced
+++ src/keygen.iced
@@ -56,20 +56,3 @@
     cb null, @p3skb

 #=====================================================
-
-test = (cb) ->
-  init_env()
-  env().set_argv {}
-  init()
-  kg = new KeyGen {
-    username : "tacotime",
-    config : constants.keygen,
-    passphrase : "now is the time for all men"
-  }
-  await kg.gen defer err
-  console.log err
-  await kg.encrypt_to_p3skb defer err
-  console.log err
-  console.log kg.p3skb
-  cb()
-


duplicate word in inventory cancel itself

--- src/kick.coffee
+++ src/kick.coffee
@@ -1,16 +1,30 @@
 Array::pop = (index=@length-1) -> @splice(index, 1)[0]
 Array::random = -> @[Math.floor(@length * Math.random())]
+Array::remove = (item) -> @pop(index) if (index = @indexOf(item)) > -1

 stage_height = 300
 stage_width = 1000

 pool_words = []
-keep_words = []
 shoot_word = null

 lvl = 1
 point = 0
 player_die = false
+
+inventory = new class Inventory
+    constructor: ->
+        @words = []
+
+    add: (word) ->
+        if word in @words
+            @words.remove(word)
+        else
+            @words.push(word)
+
+    show: ->
+        (word for word in @words).join(' ')
+

 class ShootingWord
     constructor: (@full) ->
@@ -52,7 +66,7 @@

 draw = ->
     $('#point').html(point)
-    $('#keep').html((word.full for word in keep_words).join(' '))
+    $('#keep').html(inventory.show())
     $('#playground').empty()
     if shoot_word?
         shoot_word.move()
@@ -87,7 +101,7 @@
         shoot_word.shot()
     if shoot_word?.remain == ''
         point += shoot_word.full.length
-        keep_words.push(shoot_word)
+        inventory.add(shoot_word.full)
         shoot_word = null
         lvl += 1
         for i in [1..lvl-pool_words.length]


defensive for tests

defensive for tests

--- lib/analytics.coffee
+++ lib/analytics.coffee
@@ -23,7 +23,7 @@

 module.exports.trackPageview = =>
   @ga? 'send', 'pageview'
-  mixpanel.track? 'Viewed page', { path: location.pathname }
+  mixpanel?.track? 'Viewed page', { path: location?.pathname }

 # Delta tracking pixel


Clears up sharepanes on disconnect and amends error with unassigned new sharepanes

--- lib/atom_pair.coffee
+++ lib/atom_pair.coffee
@@ -79,6 +79,7 @@
   disconnect: ->
     @pusher.disconnect()
     _.each @friendColours, (colour) => SharePane.each (pane) -> pane.clearMarkers(colour)
+    SharePane.all = []
     @markerColour = null

   joinSession: ->
@@ -207,10 +208,8 @@
     atom.workspace.onDidOpen (e) =>
       return unless @engageTabListener
       editor = e.item
-      @createSharePane(editor)
-
+      sharePane = @createSharePane(editor)
       return unless @triggerPush
-
       @queue.add(@globalChannel.name, 'client-create-share-pane', {
         to: 'all',
         from: @markerColour,


:art: if not -> unless

:art: if not -> unless

--- src/list.coffee
+++ src/list.coffee
@@ -72,7 +72,7 @@
       if options.argv.themes
         packages.push(manifest) if manifest.theme
       else if options.argv.packages
-        packages.push(manifest) if not manifest.theme
+        packages.push(manifest) unless manifest.theme
       else
         packages.push(manifest)


Sets current

Sets current

--- src/main.coffee
+++ src/main.coffee
@@ -1,7 +1,7 @@
 # Preamble
-
 _.mixin(_.str.exports())
 rx =  require('../../reactive-coffee/src/reactive')
+assert = require 'assert'

 # Dependencies

@@ -12,6 +12,10 @@
 {test} = require('./test')

 world = god(rx, config)
+turtles = world.find_children('turtles')[0]
+assert turtles, "should be one turtles object"
+current = turtles.find_children()[0]
+world.put('current', current)

 main = ->
   $('body').append(


When poperty is object and is replaced with other then setter have to search for new property and model so it is changed to search it always

--- src/neck.coffee
+++ src/neck.coffee
@@ -88,15 +88,16 @@
       resolvers = []

       unless checkString.match /[-+=\(\)\{\}\:]+/
-        model = string.split('.')
-        resolvers.push model[1]
-
-        property = model.pop()
-        model = eval model.join('.')
+        resolvers.push string.split('.')[1]

         Object.defineProperty @, name,
           get: -> eval string
-          set: (val)-> model[property] = val
+          set: (val)->
+            model = string.split('.')
+            property = model.pop()
+            model = eval model.join('.')
+
+            model[property] = val
       else
         if checkString.match /\:/
           string = "(" + string + ")"


Handle EEXIST error with a better message

Closes #5518

--- src/pane.coffee
+++ src/pane.coffee
@@ -679,7 +679,7 @@
       atom.notifications.addWarning("Unable to save file: #{error.message}")
     else if error.code is 'EACCES' and error.path?
       atom.notifications.addWarning("Unable to save file: Permission denied '#{error.path}'")
-    else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN'] and error.path?
+    else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST'] and error.path?
       atom.notifications.addWarning("Unable to save file '#{error.path}'", detail: error.message)
     else if error.code is 'EROFS' and error.path?
       atom.notifications.addWarning("Unable to save file: Read-only file system '#{error.path}'")


Remove last bit of underscore

Remove last bit of underscore

--- src/path.coffee
+++ src/path.coffee
@@ -112,7 +112,7 @@
     @_removeAllSubpaths()
     parsedPath = PathParser.parsePath(pathString)
     @_createSubpath(parsedSubpath) for parsedSubpath in parsedPath.subpaths
-    @currentSubpath = _.last(@subpaths)
+    @currentSubpath = @subpaths[@subpaths.length - 1]
     @_updatePathString()
     null


[refactor] Made it easier for subclasses to override a Collection View's context (1m)

--- vendor/assets/javascripts/neat/collection_editor.coffee
+++ vendor/assets/javascripts/neat/collection_editor.coffee
@@ -97,7 +97,7 @@

   render: ->
     $el = $(@el)
-    $el.html @template(collection: @collection)
+    $el.html @template(@context())
     $el.cssHover '.neat-row.neat-interactive'

     @afterRender()
@@ -107,6 +107,9 @@
     @paginator.renderPaginationIn($el.find('.pagination'))
     @repaginate()
     @
+
+  context: ->
+    collection: @collection

   afterRender: ->
     @


Add onLoad function to handlers

Add onLoad function to handlers

--- handlers.coffee
+++ handlers.coffee
@@ -49,13 +49,17 @@
 		try
 			loadedHandlers[name] = require './handlers/' + name
 			if loadedHandlers[name].handleMessage? and loadedHandlers[name].handleUser?
-				callback?()
+				if loadedHandlers[name].onLoad?
+					loadedHandlers[name].onLoad callback
+				else
+					callback?()
 			else
 				winston.error "Invalid handler, unloading:", name
 				unloadHandler name
 				callback? "invalid"
 		catch e
 			winston.error "Failed to compile handler “#{name}”: #{e.message}"
+			unloadHandler name
 			callback? "compile"

 unloadHandler = (name, callback) ->


Ace: Optionally create find and replace view.

--- client/Ace/AppView.coffee
+++ client/Ace/AppView.coffee
@@ -13,15 +13,22 @@

     super options, file

+    options.createFindAndReplaceView ?= yes
+
     @listenWindowResize()

-    @ace = new Ace
+    aceOptions        =
       delegate        : this
       enableShortcuts : yes
-    , file
+      createFindAndReplaceView : options.createFindAndReplaceView

-    @findAndReplaceView = new AceFindAndReplaceView delegate: this
-    @findAndReplaceView.hide()
+    @ace = new Ace aceOptions, file
+
+    if options.createFindAndReplaceView
+      @findAndReplaceView = new AceFindAndReplaceView delegate: this
+      @findAndReplaceView.hide()
+    else
+      @findAndReplaceView = new KDCustomHTMLView

     @setViewListeners()


enumeration: Fix bug in set_index.

enumeration: Fix bug in set_index.

--- client/enumeration.coffee
+++ client/enumeration.coffee
@@ -51,7 +51,7 @@
   set_index: ( index ) ->
     if index != @_.index
       @_.index = @i( index )
-      @on_set( @value(), index )
+      @_.on_set.fire( @value(), index )
     @value

   # Set value by value.
@@ -64,7 +64,7 @@
     if i == -1
       throw "Value not found"
     else
-      return set_index( i )
+      return @set_index( i )

   # Current index.
   #


Default route points to Game list

Default route points to Game list

--- client/src/app/app.coffee
+++ client/src/app/app.coffee
@@ -36,7 +36,7 @@
 ])

 .config( ( $stateProvider, $urlRouterProvider ) ->
-  $urlRouterProvider.otherwise( '/game/065ffe64aa2928a3/edit' )
+  $urlRouterProvider.otherwise( '/game/list' )
 )

 .controller('AppCtrl', ( $scope, $location ) ->


fixed shifts not displaying properly on /shifts

--- client/tournaments.coffee
+++ client/tournaments.coffee
@@ -230,7 +230,7 @@
     dayShifts =
       dayOfWeek: moment(day).format('ddd')
       dayOfMonth: moment(day).format('Do')
-      activeShifts: (shift for shift in sortedShifts when shift.day is day)
+      activeShifts: (shift for shift in sortedShifts when shift.day.valueOf() is day.valueOf())
   # return the result
   result =
     defs: shiftDefs


Add support for lower_units and upper_units

--- bokehjs/src/coffee/models/annotations/band.coffee
+++ bokehjs/src/coffee/models/annotations/band.coffee
@@ -19,11 +19,26 @@
     @visuals.warm_cache(source)

   _map_data: () ->
+    @_canvas = @plot_model.canvas
     [i, j] = @model._normals()
     lower = [@_lower, @_base]
     upper = [@_upper, @_base]
-    @_lower_sx_sy = @map_to_screen(lower[i], lower[j])
-    @_upper_sx_sy = @map_to_screen(upper[i], upper[j])
+
+    switch @model.lower_units
+      when "data"
+        @_lower_sx_sy = @map_to_screen(lower[i], lower[j])
+      when "screen"
+        @_lower_sx_sy = [[], []]
+        @_lower_sx_sy[0] = @_canvas.v_vx_to_sx(lower[i])
+        @_lower_sx_sy[1] = @_canvas.v_vy_to_sy(lower[j])
+
+    switch @model.upper_units
+      when "data"
+        @_upper_sx_sy = @map_to_screen(upper[i], upper[j])
+      when "screen"
+        @_upper_sx_sy = [[], []]
+        @_upper_sx_sy[0] = @_canvas.v_vx_to_sx(upper[i])
+        @_upper_sx_sy[1] = @_canvas.v_vy_to_sy(upper[j])

   render: () ->
     if not @model.visible
@@ -32,7 +47,6 @@
     @_map_data()

     ctx = @plot_view.canvas_view.ctx
-

     sx = @_lower_sx_sy[0]
     sy = @_lower_sx_sy[1]


make sure it returns something

make sure it returns something

--- lib/sort-lines.coffee
+++ lib/sort-lines.coffee
@@ -52,3 +52,4 @@
       return (if a < b then -1 else 1) if aWord isnt bWord
       return (+aLeadingNum) - (+bLeadingNum) if aLeadingNum isnt bLeadingNum
       return (+aTrailingNum) - (+bTrailingNum) if aTrailingNum isnt bTrailingNum
+      return 0


Don't bootstrap from gruntfile

Don't bootstrap from gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -169,14 +169,10 @@
     mkdir path.dirname(INSTALL_DIR)
     cp SHELL_APP_DIR, INSTALL_DIR

-  grunt.registerTask 'bootstrap', 'Bootstrap modules and atom-shell', ->
-    done = @async()
-    commands = []
+  grunt.registerTask 'update-atom-shell', 'Update atom-shell', ->
+    done = @async()
     commands.push (callback) ->
-      spawn cmd: 'script/bootstrap', (error) -> callback(error)
-    commands.push (callback) ->
-      spawn cmd: 'script/update-atom-shell', (error) -> callback(error)
-    grunt.util.async.waterfall commands, (error) -> done(error)
+      spawn cmd: 'script/update-atom-shell', (error) -> done(error)

   grunt.registerTask 'test', 'Run the specs', ->
     done = @async()
@@ -190,9 +186,9 @@

   grunt.registerTask('compile', ['coffee', 'less', 'cson'])
   grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
-  grunt.registerTask('ci', ['clean', 'bootstrap', 'build', 'test'])
-  grunt.registerTask('deploy', ['clean', 'bootstrap', 'build', 'codesign'])
-  grunt.registerTask('default', ['bootstrap', 'build', 'set-development-version', 'install'])
+  grunt.registerTask('ci', ['clean', 'update-atom-shell', 'build', 'test'])
+  grunt.registerTask('deploy', ['clean', 'update-atom-shell', 'build', 'codesign'])
+  grunt.registerTask('default', ['update-atom-shell', 'build', 'set-development-version', 'install'])

   spawn = (options, callback) ->
     grunt.util.spawn options, (error, results, code) ->


Fix Gruntfile to look at local Jasmine

Fix Gruntfile to look at local Jasmine

Couldn't get the tests to run without this

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -48,7 +48,7 @@
           failOnError: true

       test:
-        command: 'jasmine-focused --captureExceptions --coffee spec/'
+        command: 'node_modules/jasmine-focused/bin/jasmine-focused --captureExceptions --coffee spec/'
         options:
           stdout: true
           stderr: true


Complete Grunt tasks for custom theme workflow. :wrench:

- Document tasks.
- Auto-save image changes.
- Harden image file patterns.
- Fix switch-theme task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -26,19 +26,21 @@
       'new-theme':
         nonull: yes
         files: [
-          { expand: yes, cwd: 'images/themes/default/', src: ['*'], dest: "images/themes/#{theme}/" }
+          { expand: yes, cwd: 'images/themes/default/', src: ['*.png'], dest: "images/themes/#{theme}/" }
           { src: 'stylesheets/themes/_default.scss', dest: "stylesheets/themes/_#{theme}.scss" }
         ]
       'save-theme':
         nonull: yes
         files: [
+          { expand: yes, cwd: 'images/', src: ['*.png'], dest: "images/themes/#{theme}/" }
           { src: 'stylesheets/_theme.scss', dest: "stylesheets/themes/_#{theme}.scss" }
         ]
       'switch-theme':
         nonull: yes
-        files:
-          'images/': "images/themes/#{theme}/*"
-          'stylesheets/_theme.scss': "stylesheets/themes/_#{theme}.scss"
+        files: [
+          { expand: yes, cwd: "images/themes/#{theme}/", src: ['*.png'], dest: 'images/' }
+          { src: "stylesheets/themes/_#{theme}.scss", dest: 'stylesheets/_theme.scss' }
+        ]

     sass:
       stylesheets:
@@ -50,12 +52,28 @@
         files: 'stylesheets/**/*.scss'
         tasks: ['sass', 'newer:autoprefixer']
       theme:
-        files: ['stylesheets/_theme.scss']
+        files: ['stylesheets/_theme.scss', 'images/*.png']
         tasks: ['copy:save-theme']

   grunt.loadNpmTasks plugin for plugin in matchdep.filterDev 'grunt-*'

-  grunt.registerTask 'default', ['copy:lib', 'sass', 'autoprefixer', 'watch']
+  grunt.registerTask 'default',
+    "Main development task. Remember to provide the '--theme' option for a custom theme.",
+    ['copy:lib', 'sass', 'autoprefixer', 'watch']

-  grunt.registerTask 'new-theme', ['copy:new-theme']
-  grunt.registerTask 'switch-theme', ['copy:switch-theme']
+  grunt.registerTask 'new-theme-notes', 'Internal log task.', ->
+    grunt.log.ok """
+      #{theme} is now ready to be customized! Run the default task first. Then:
+      1. Change the variables in _theme.scss. It will automatically be saved.
+      2. Adjust the colors of the images in images/. It will automatically be
+         saved. Please use the comments in _theme.scss as a guide. Using Sketch
+         is suggested.
+    """
+
+  grunt.registerTask 'new-theme',
+    "Create a new theme for images and colors. Use the '--theme' option."
+    ['copy:new-theme', 'new-theme-notes']
+
+  grunt.registerTask 'switch-theme',
+    "Switch to the images and colors of an existing theme. Use the '--theme' option."
+    ['copy:switch-theme', 'sass', 'autoprefixer']


webpack: Tweak grunt-noflo-browser options to mostly work

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -14,6 +14,29 @@
     noflo_browser:
       options:
         baseDir: './'
+        webpack:
+          externals:
+            'repl': 'commonjs repl' # somewhere inside coffee-script
+            'child_process': 'commonjs child_process' # somewhere inside coffee-script
+            'module': 'commonjs module' # somewhere inside coffee-script
+            'jison': 'commonjs jison'
+            'should': 'commonjs should' # used by tests in octo
+            'express': 'commonjs express' # used by tests in octo
+            'highlight': 'commonjs highlight' # used by octo?
+            'emitter': 'commonjs emitter' # used by octo?
+            'microflo-emscripten': 'commonjs microflo-emscripten' # optional?
+            'acorn': 'commonjs acorn' # optional?
+          module:
+            loaders: [
+              { test: /\.coffee$/, loader: "coffee-loader" }
+              { test: /\.json$/, loader: "json-loader" }
+              { test: /\.fbp$/, loader: "fbp-loader" }
+            ]
+          resolve:
+            extensions: ["", ".coffee", ".js"]
+        ignores: [
+          /bin\/coffee/
+        ]
       main:
         files:
           'browser/noflo-ui.js': ['./app/main.js']


Fixed coffee build grunt task. It did not traverse folders recursivelly

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,7 +6,7 @@
         files: [{
           expand: true,
           cwd: 'src'
-          src: '{,*/}*.coffee',
+          src: './**/*.coffee',
           dest: 'lib'
           ext: '.js'
         }]


Fixed GruntFile issue due to updates jsHint

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -112,7 +112,6 @@
       files:        [ '<%= SERVER_DIR + JS_FILES %>'
                       '<%= CLIENT_DIR + JS_FILES %>' ]
       options:
-        es5:        true
         laxcomma:   true  # Common in Express-derived libraries

     # Browser-based testing


Make grunt invalidate_cloudfront work

Make grunt invalidate_cloudfront work

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -89,8 +89,8 @@

       production:
         files: [
-          '/bugsnag-2.min.js',
-          '/bugsnag-2.js'
+          {dest: 'bugsnag-2.min.js'},
+          {dest: 'bugsnag-2.js'}
         ]

         # Version bumping


disable saucelabs tests

disable saucelabs tests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -122,4 +122,4 @@
   grunt.registerTask 'default', ['connect','watch']
   grunt.registerTask 'build', ['concat','uglify','test']
   grunt.registerTask 'test', ['jshint','qunit']
-  grunt.registerTask 'test-travis', ['build','jshint','connect','saucelabs-qunit']
+  grunt.registerTask 'test-travis', ['build','jshint']


Install directories on initial installation

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -111,7 +111,7 @@
 	grunt.registerTask 'install:all', "Download and set up all ShareLaTeX services",
 		["check:make"].concat(
 			("install:#{service.name}" for service in SERVICES)
-		).concat(["install:config"])
+		).concat(["install:config", "install:dirs"])
 	grunt.registerTask 'install', 'install:all'
 	grunt.registerTask 'update:all', "Checkout and update all ShareLaTeX services",
 		["check:make"].concat(


Forgot to put quotes around template strings.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -27,7 +27,7 @@
           'mochaTest'
         ]
       gruntfile:
-        files: <%= gruntfile %>
+        files: '<%= gruntfile %>'
         tasks: [
           'coffeelint:gruntfile'
         ]
@@ -39,7 +39,7 @@
           'test/**/*.coffee'
         ]
       gruntfile:
-        src: <%= gruntfile %>
+        src: '<%= gruntfile %>'
       options:
         configFile: 'coffeelint.json'

@@ -64,13 +64,6 @@
       upload:
           src: 'coverage/coverage.lcov'

-#    shell:
-#      coveralls:
-#        command: './node_modules/coveralls/bin/coveralls.js lib < coverage/coverage.lcov'
-#
-#  grunt.registerTask 'uploadCoverage', ->
-#    grunt.task.run 'shell:coveralls'
-
   grunt.registerTask 'uploadCoverage', ->
     grunt.task.run 'coveralls:upload'


remove gzip reporter

remove gzip reporter

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -5,7 +5,6 @@

     uglify:
       options:
-        report: 'gzip'
         banner: '/*!\n' +
           ' * <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
           ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> (<%= pkg.author.url %>)\n' +


add externs section into Gruntfile

add externs section into Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -123,34 +123,46 @@
       options:
         root: clientDirs
         depsPath: clientDepsPath
-        # Enable faster compilation for Windows with Java 1.7+
+
+        # Enable faster compilation for Windows with Java 1.7+.
         # javaFlags: ['-XX:+TieredCompilation']
         compilerFlags: do ->
-          # you will love advanced compilation with verbose warning level
+
+          # Default compiler settings. You will love advanced compilation with
+          # verbose warning level.
           flags = [
-            '--output_wrapper="(function(){%output%})();"'
-            '--compilation_level="ADVANCED_OPTIMIZATIONS"'
-            '--warning_level="VERBOSE"'
+            '--output_wrapper=(function(){%output%})();'
+            '--compilation_level=ADVANCED_OPTIMIZATIONS'
+            '--warning_level=VERBOSE'
             # experimental stuff
             # '--use_types_for_optimization'
           ]
-          # remove code for ancient browsers (IE<8, very old Gecko/Webkit)
+
+          # Remove some code workarounds for ancient browsers.
+          # IE<8 and very old Gecko and Webkit.
           flags = flags.concat [
             '--define=goog.net.XmlHttp.ASSUME_NATIVE_XHR=true'
             '--define=este.json.SUPPORTS_NATIVE_JSON=true'
             '--define=goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS=true'
           ]
+
+          # Enable debug compiled mode for --stage=debug.
           if grunt.option('stage') == 'debug'
             flags = flags.concat [
               '--debug=true'
-              '--formatting="PRETTY_PRINT"'
+              '--formatting=PRETTY_PRINT'
               '--define=goog.DEBUG=true'
             ]
           else
             flags = flags.concat [
               '--define=goog.DEBUG=false'
             ]
-          flags
+
+          # Externs. They allow us to use thirdparty code without [] syntax.
+          # Uncomment if your app requires este.trirdParty.react namespace.
+          # flags.concat [
+          #   '--externs=bower_components/este-library/externs/react/react-3.3.js'
+          # ]

       app:
         options:


feat(grunt): tasks minor tweak

feat(grunt): tasks minor tweak

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,7 +80,7 @@
           interrupt: true

       jekyll:
-        files: ["<%= config.app %>/**/*", "!_*"]
+        files: ["<%= config.app %>/**/*"]
         tasks: ['jekyll:serve']

     uglify:
@@ -101,6 +101,8 @@
       dist:
         options:
           report: "gzip"
+          compress:
+            drop_console: true

         files: [
           expand: true
@@ -151,12 +153,6 @@

     htmlmin:
       dist:
-        files: [
-          expand: true
-          cwd: "<%= config.dist %>"
-          src: "**/*.html"
-          dest: "<%= config.dist %>/"
-        ]
         options:
           removeComments: true
           removeCommentsFromCDATA: true
@@ -175,6 +171,13 @@
           caseSensitive: true
           minifyJS: true
           minifyCSS: true
+
+        files: [
+          expand: true
+          cwd: "<%= config.dist %>"
+          src: "**/*.html"
+          dest: "<%= config.dist %>/"
+        ]

     xmlmin:
       dist:


remove whitespace

remove whitespace

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -23,7 +23,7 @@
           reporter: 'min'
         src: [
           'test/**/*.coffee'
-        ]
+        ]

     coffee:
       glob:


Only watch important files for livereload

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -217,7 +217,11 @@
       livereload:
         options:
           livereload: true
-        files: ['public/**/*']
+        files: [
+          'public/css/buckets.css'
+          'public/js/{buckets,vendor}.css'
+          'public/plugins/**/*.{css,js}'
+        ]

   grunt.registerTask 'checkDatabase', (next, stuff...)->
     connection = mongoose.createConnection config.db, (err) ->


Fix popup for conflicts too.

Fix popup for conflicts too.

--- admin/static/coffee/dashboard.coffee
+++ admin/static/coffee/dashboard.coffee
@@ -498,8 +498,8 @@
         popup_template: Handlebars.compile $('#cluster_status-consistency-popup-template').html()

         events:
-            'mouseup .show_details': 'show_details'
-            'mouseup .close': 'hide_details'
+            'click .show_details': 'show_details'
+            'click .close': 'hide_details'

         initialize: =>
             @data = ''


Fixes zombie fiber bug, and all associated bugs, as far as I can tell

--- publish.coffee
+++ publish.coffee
@@ -102,10 +102,7 @@

         session.updateObserversFiber = Fiber.current
         updateObservers.call dataSessionPublisherContexts[dataSessionId], dataSessionId
-        ### TODO: What exactly is going on here? Seems to be forcing GC
-            which is causing a zombie Fiber.
-        ###
-        # session.updateObserversFiber = null
+        session.updateObserversFiber = null

         jDataSession = new $$.JDataSession
             _id: dataSessionId
@@ -145,18 +142,33 @@

     @onStop =>
         console.log "[#{dataSessionId}] PUBLISHER STOPPED"
-        session.observerByQsString().forEach (qsString, observer) =>
-            observer.stop()
-        session.stop()
-
-        if session.updateObserversFiber?
-            console.warn "Uh oh, we were in the middle of updating observers."
-            session.updateObserversFiber.reset()
-
-        delete J._dataSessionFieldsByModelIdQuery[dataSessionId]
-        delete dataSessionPublisherContexts[dataSessionId]
-        delete J._dataSessions[dataSessionId]
-        $$.JDataSession.remove dataSessionId
+
+        removeSession = ->
+            session.stop()
+            delete J._dataSessionFieldsByModelIdQuery[dataSessionId]
+            delete dataSessionPublisherContexts[dataSessionId]
+            delete J._dataSessions[dataSessionId]
+            $$.JDataSession.remove dataSessionId
+
+        stopObservers = ->
+            session.observerByQsString().forEach (qsString, observer) =>
+                observer.stop()
+
+        if not session.updateObserversFiber? or not session.updateObserversFiber.started
+            console.log "Resetting everything."
+            ###
+            Current Fiber was either never set, or is stopped and ready to be reset.
+            We can stop observers, delete our session, and attempt to restart the Fiber if it exists.
+            ###
+            stopObservers()
+            if session.updateObserversFiber? and not session.updateObserversFiber.started
+                console.warn "Uh oh, we were in the middle of updating observers."
+                session.updateObserversFiber?.reset()
+            removeSession()
+        else if session.updateObserversFiber?.started
+            # Current Fiber is still running. Stop the observers, but keep our session.
+            console.log "Session is still intact."
+            stopObservers()

     @ready()

@@ -188,7 +200,7 @@

     fieldsByModelIdQuery = J._dataSessionFieldsByModelIdQuery[dataSessionId]

-
+
     getMergedSubfields = (a, b) ->
         return a if b is undefined
         return b if a is undefined


fixed bug in _sequence2

fixed bug in _sequence2

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -18,7 +18,7 @@
     gameSequence = root.getGameSequence()
     sel = new gameSelector(mapDiv, false)
     addGameToMap = (game) ->
-        console.log "Game: #{game}"
+        # console.log "Game: #{game}"
         sel.buildDiv(game, descriptions[game], player.games[game], root.canPlay(game), codeland)
     addGameToMap game for game in gameSequence
     #TODO FADE IN
@@ -228,7 +228,8 @@

 _sequence2= deepcopy _sequence1
 _sequence2.name = 'Code Sequence Puzzle #2'
-_sequence2.game = { startpos: [6,7], targetpos: [5,5]}
+_sequence2.game.startpos = [6,7]
+_sequence2.game.targetpos = [5,5]
 _sequence2.editor.commands = {go : { inputs:0, maxUses:7 }, turnLeft : { inputs:0, maxUses:4 }}
 _sequence2.code.initial = 'go();\ngo();\ngo();\ngo();\ngo();\ngo();\n\nturnLeft();\nturnLeft();\nturnLeft();\ngo();'
 _sequence2.depends = ['sequence1']
@@ -239,9 +240,9 @@
 _sequence3= deepcopy _sequence2
 _sequence3.name = 'Code Sequence Puzzle #3'
 _sequence3.editor.buttons = ['delete']
-_sequence2.editor.commands = {go : { inputs:0, maxUses:3 },goNorth : { inputs:0, maxUses:2 }, turnLeft : { inputs:0, maxUses:6 }, turnRight: { inputs:0, maxUses:2 }}
+_sequence3.editor.commands = {go : { inputs:0, maxUses:3 },goNorth : { inputs:0, maxUses:2 }, turnLeft : { inputs:0, maxUses:6 }, turnRight: { inputs:0, maxUses:2 }}
 _sequence3.code.initial = 'turnLeft();\nturnRight();\nturnLeft();\nturnRight();go();\ngoNorth();\ngo();\nturnLeft();\nturnLeft();\nturnLeft();\nturnLeft();\ngo();\ngoNorth();'
-_sequence2.game = { startpos: [7,3], targetpos: [5,2]}
+_sequence3.game = { startpos: [7,3], targetpos: [5,2]}
 _sequence3.depends = ['sequence1']
 _sequence3.help = ['Time to debug this code!','This code has too many statements. Delete the unnecessary statements.']
 _sequence3.dyk = ['When your code runs but does not work correctly you have a bug in your code!','Finding the cause and fixing the problem is called \'Debugging\'',]


rework getSelectionFocusElement so that it always returns an element or null

This is not a replacement of commit "Don't focus non-focusable node"
    (351489c7c5e093531aa8cd5870a3f78c99fb2923),
because we can not ensure each Element instance has a non-null `focus`
member function:
* As far as now, `Element.prototype` has no `focus` property;
* Though all sub-classes of `Element` has their own `focus` property, like `HTMLElement` and `SVGElement`.

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -409,15 +409,13 @@

   getSelectionFocusElement: ->
     sel = window.getSelection()
-    if not sel.focusNode?
-      null
-    else if sel.focusNode == sel.anchorNode and sel.focusOffset == sel.anchorOffset
+    node = sel.focusNode
+    if not node?
+      return null
+    if node == sel.anchorNode and sel.focusOffset == sel.anchorOffset
       # The selection either *is* an element, or is inside an opaque element (eg. <input>).
-      sel.focusNode.childNodes[sel.focusOffset]
-    else if sel.focusNode.nodeType != sel.focusNode.ELEMENT_NODE
-      sel.focusNode.parentElement
-    else
-      sel.focusNode
+      node = node.childNodes[sel.focusOffset]
+    if node.nodeType != Node.ELEMENT_NODE then node.parentElement else node

   # Get the element in the DOM hierachy that contains `element`.
   # If the element is rendered in a shadow DOM via a <content> element, the <content> element will be


Ignore keybiard repeats.

Ignore keybiard repeats.

Keyboard repeats were interfering with smooth scrolling.  But we should
be ignoreing them anyway.

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -318,19 +318,20 @@
   consumeKeyup: (event, callback = null) ->
     @suppressEvent event
     keyChar = KeyboardUtils.getKeyCharString event
-    handlerStack.push
-      _name: "dom_utils/consumeKeyup"
-      keydown: (event) ->
-        @remove()
-        handlerStack.continueBubbling
-      keyup: (event) ->
-        return handlerStack.continueBubbling unless keyChar == KeyboardUtils.getKeyCharString event
-        @remove()
-        handlerStack.suppressEvent
-      # We cannot track keyup events if we lose the focus.
-      blur: (event) ->
-        @remove() if event.target == window
-        handlerStack.continueBubbling
+    unless event.repeat
+      handlerStack.push
+        _name: "dom_utils/consumeKeyup"
+        keydown: (event) ->
+          @remove()
+          handlerStack.continueBubbling
+        keyup: (event) ->
+          return handlerStack.continueBubbling unless keyChar == KeyboardUtils.getKeyCharString event
+          @remove()
+          handlerStack.suppressEvent
+        # We cannot track keyup events if we lose the focus.
+        blur: (event) ->
+          @remove() if event.target == window
+          handlerStack.continueBubbling
     callback?()
     handlerStack.suppressEvent


Fix route position

Fix route position

--- server/app/assets/javascript/map_image_2nd.coffee
+++ server/app/assets/javascript/map_image_2nd.coffee
@@ -46,7 +46,7 @@
         $.getJSON posUrl + '?suffix=' + s, (data) ->
           data.forEach (d) ->
             that.positions[d.cell] = {x: d.posX, y: d.posY}
-            that.image.setLayerPoint(s, d.cell, that.positions[d.cell])
+            that.image.setLayerPoint(s, d.cell, {name: d.routeName, x: d.posX + d.routeX, y: d.posY + d.routeY})
           that.image.setLayer(s)
           if infoUrl
             $.getJSON infoUrl + '?suffix=' + s, (data) ->
@@ -170,7 +170,7 @@
       return

   setLayerPoint: (s, cell, point) ->
-    frameName = 'route_'+cell+'_1'
+    frameName = point.name ? 'route_'+cell+'_1'
     if @layerFrames[s].hasOwnProperty frameName
       Object.assign @layerFrames[s][frameName], {point: {x: point.x, y: point.y}}


Cleanup Transformers.function.

Cleanup Transformers.function.

--- lib/js2coffee.coffee
+++ lib/js2coffee.coffee
@@ -226,9 +226,6 @@
   # A return statement. Has `@value` of type `id`.

   'return': ->
-    # **Caveat 1:**
-    # *Empty returns need to always be `return`, regardless if it's the last
-    # statement in the block or not.*
     if not @value?
       "return\n"

@@ -690,8 +687,8 @@

     # *Unwrap the `return`s.*
     walk null, @body, (parent, node) ->
-      if parent and node and node.isA('return')
-        parent.children[parent.children.length-1] = node.value  if node.value
+      if node.isA('return') and node.value
+        parent.children[parent.children.length-1] = node.value

   'switch': ->
     _.each @cases, (item) =>


don't waste time on notes and attachments

--- chrome/content/zotero-better-bibtex/keymanager.coffee
+++ chrome/content/zotero-better-bibtex/keymanager.coffee
@@ -161,6 +161,15 @@
       item = arguments[1]
       pinmode = arguments[2]

+    type = item.itemType
+    if !type
+      item = Zotero.item.get(item.itemID) unless item.itemTypeID
+      type = switch item.itemTypeID
+        when 0 then 'note'
+        when 14 then 'attachment'
+        else 'reference'
+    return if type in ['note', 'attachment']
+
     # pinmode can be:
     #  reset: clear any pinned key, generate new dynamic key
     #  manual: generate and pin


sidebar: increase event listener count to 20

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -28,6 +28,7 @@
   constructor: (options = {}) ->

     options.cssClass  = 'activity-sidebar'
+    options.maxListeners = 20

     super options


PreferredDomain: add preferred domain support

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -279,6 +279,22 @@
       ,
     (err, status)-> console.log "Status of fetching stuff from external: #{status}"

+
+  preferredDomainCookieName = 'kdproxy-preferred-domain'
+
+  # This function checks current user's preferred domain and
+  # set's it to preferredDomainCookie
+  setPreferredDomain:(account)->
+
+    {preferredKDProxyDomain} = account
+    if preferredKDProxyDomain and preferredKDProxyDomain isnt ""
+      # if cookie name is already same do nothing
+      return  if $.cookie(preferredDomainCookieName) is preferredKDProxyDomain
+      $.cookie preferredDomainCookieName, preferredKDProxyDomain
+      # there can be conflicts between first(which is defined below) route
+      # and the currect builds router, so reload to main page from server
+      location.reload(true)
+
   afterLoginCallback: (err, params={})->
     @loginForm.button.hideLoader()
     {entryPoint} = KD.config
@@ -288,6 +304,9 @@
     else
       {account, replacementToken} = params
       $.cookie 'clientId', replacementToken  if replacementToken
+      # check and set preferred BE domain for Koding
+      @setPreferredDomain account
+
       mainController = KD.getSingleton('mainController')
       mainView       = mainController.mainViewController.getView()
       mainController.accountChanged account


Main: optional behavior for maintabview to not to show a created tab right away

--- client/landing/app/coffee/core/maintabview.coffee
+++ client/landing/app/coffee/core/maintabview.coffee
@@ -36,12 +36,14 @@

   createTabPane:(options = {}, mainView)->

-    o              = {}
-    o.cssClass     = @utils.curry 'content-area-pane', options.cssClass
-    o.name         = options.name
-    o.view         = mainView
-    paneInstance   = new MainTabPane o
+    options.shouldShow ?= yes

-    @addPane paneInstance
+    o            = {}
+    o.cssClass   = @utils.curry 'content-area-pane', options.cssClass
+    o.name       = options.name
+    o.view       = mainView
+    paneInstance = new MainTabPane o
+
+    @addPane paneInstance, options.shouldShow

     return paneInstance


client: stacks: definestackview to work without template

--- client/stacks/lib/views/stacks/definestackview.coffee
+++ client/stacks/lib/views/stacks/definestackview.coffee
@@ -33,11 +33,11 @@

 module.exports = class DefineStackView extends KDView

-  constructor: (options = {}, data) ->
+  constructor: (options = {}, data = {}) ->

     options.cssClass = kd.utils.curry 'define-stack-view', options.cssClass

-    { stackTemplate } = data ? {}
+    { stackTemplate } = data

     if stackTemplate
       unless provider = stackTemplate.selectedProvider
@@ -50,7 +50,6 @@

     super options, data

-    { stackTemplate }    = @getData()
     { selectedProvider } = @getOptions()

     options.delegate = this


first attempt at detecting v1.9 install

first attempt at detecting v1.9 install

--- lib/learn-ide.coffee
+++ lib/learn-ide.coffee
@@ -18,6 +18,7 @@

   activate: (state) ->
     console.log 'activating learn ide'
+    @checkForV1WindowsInstall()
     @disableFormerPackage()

     @subscriptions = new CompositeDisposable
@@ -144,6 +145,16 @@
     atomHelper.closePaneItems()
     atom.reload()

+  checkForV1WindowsInstall: ->
+    isWindows = process.platform == 'win32'
+    return if !isWindows
+
+    installLocationX86 = path.join(process.env['ProgramFiles(x86)'], 'Learn IDE')
+    installLocation = path.join(process.env['ProgramFiles'], 'Learn IDE')
+
+    if (fs.existsSync(installLocationX86) || fs.existsSync(installLocation))
+      alert('old copy of Learn IDE detected')
+
   disableFormerPackage: ->
     ilePkg = atom.packages.loadPackage('integrated-learn-environment')


reseting code now scrolls to active line

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -522,6 +522,7 @@
         @editor.clearSelection()
         @reIndentCode()
         @gotoLine @codePrefixLength + 1
+        @editor.renderer.scrollToRow @codePrefixLength
         for name, command of @commands
             command['usesRemaining'] = command['maxUses']
         return


OX-356 Fix Add plane to display volume texture

--- app/assets/javascripts/oxalis/geometries/plane.coffee
+++ app/assets/javascripts/oxalis/geometries/plane.coffee
@@ -38,15 +38,24 @@
   createMeshes : (pWidth, tWidth) ->
     # create plane
     planeGeo = new THREE.PlaneGeometry(pWidth, pWidth, 1, 1)
+    volumePlaneGeo = new THREE.PlaneGeometry(pWidth, pWidth, 1, 1)

     # create texture
     texture             = new THREE.DataTexture(new Uint8Array(tWidth*tWidth), tWidth, tWidth, THREE.LuminanceFormat, THREE.UnsignedByteType, new THREE.UVMapping(), THREE.ClampToEdgeWrapping , THREE.ClampToEdgeWrapping, THREE.LinearMipmapLinearFilter, THREE.LinearMipmapLinearFilter )
     texture.needsUpdate = true
     textureMaterial     = new THREE.MeshBasicMaterial({wireframe : false})
+    volumeTexture             = new THREE.DataTexture(new Uint8Array(tWidth*tWidth), tWidth, tWidth, THREE.LuminanceFormat, THREE.UnsignedByteType, new THREE.UVMapping(), THREE.ClampToEdgeWrapping , THREE.ClampToEdgeWrapping, THREE.LinearMipmapLinearFilter, THREE.LinearMipmapLinearFilter )
+    volumeTexture.needsUpdate = true
+    volumeTextureMaterial     = new THREE.MeshBasicMaterial({wireframe : false})

     # create mesh
     @plane = new THREE.Mesh( planeGeo, textureMaterial )
     @plane.texture = texture
+    @volumePlane = new THREE.Mesh( volumePlaneGeo, volumeTextureMaterial )
+    @volumePlane.texture = volumeTexture
+    @volumePlane.visible = false
+    # Never interpolate
+    @plane.texture.magFilter = THREE.NearestFilter

     # create crosshair
     crosshairGeometries = new Array(2)
@@ -110,25 +119,29 @@

   setScale : (factor) =>
     scaleVec = new THREE.Vector3().multiply(new THREE.Vector3(factor, factor, factor), @scaleVector)
-    @plane.scale = @prevBorders.scale = @crosshair[0].scale = @crosshair[1].scale = scaleVec
+    @plane.scale = @volumePlane.scale = @prevBorders.scale = @crosshair[0].scale = @crosshair[1].scale = scaleVec

   setRotation : (rotVec) =>
-    @plane.rotation = @prevBorders.rotation = @crosshair[0].rotation = @crosshair[1].rotation = rotVec
+    @plane.rotation = @volumePlane.rotation = @prevBorders.rotation = @crosshair[0].rotation = @crosshair[1].rotation = rotVec

   setPosition : (posVec) =>
     @prevBorders.position = @crosshair[0].position = @crosshair[1].position = posVec
+
     offset = new THREE.Vector3(0, 0, 0)
     if      @planeID == PLANE_XY then offset.z =  1
     else if @planeID == PLANE_YZ then offset.x = -1
     else if @planeID == PLANE_XZ then offset.y = -1
     @plane.position = offset.add(posVec, offset)

+    volumeOffset = new THREE.Vector3(offset.x / 2, offset.y / 2, offset.z / 2)
+    @volumePlane.position = volumeOffset.add(posVec, volumeOffset)
+
   setVisible : (visible) =>
     @plane.visible = @prevBorders.visible = visible
     @crosshair[0].visible = @crosshair[1].visible = visible and @displayCosshair

   getMeshes : =>
-    [@plane, @prevBorders, @crosshair[0], @crosshair[1]]
+    [@plane, @volumePlane, @prevBorders, @crosshair[0], @crosshair[1]]

   setLinearInterpolationEnabled : (value) =>
     @plane.texture.magFilter = if value==true then THREE.LinearFilter else THREE.NearestFilter


実行時に.xmlを保存するように修正

実行時に.xmlを保存するように修正

--- app/assets/javascripts/views/main_menu_view.js.coffee
+++ app/assets/javascripts/views/main_menu_view.js.coffee
@@ -83,6 +83,10 @@

     sourceCode = @getSourceCode()

+    filename = sourceCode.get('filename')
+    filename += '.xml' unless filename.match(/\.xml$/)
+    xmlSourceCode = new Smalruby.SourceCode({ filename: filename })
+
     @blockUI
       title:
         """
@@ -116,6 +120,10 @@
                 msg += "、#{errorInfo.column}文字"
               window.errorMessage(msg + ": #{errorInfo.message}")
           $.Deferred().reject().promise()
+      .then ->
+        xmlSourceCode.save2()
+      .then ->
+        xmlSourceCode.write(true)
       .then ->
         sourceCode.run()
       .then (data) ->


Changed tabs to spaces

Changed tabs to spaces

Uses whitespaces and was getting errors about unexpected indents

--- scripts/groupme.coffee
+++ scripts/groupme.coffee
@@ -1,6 +1,7 @@
 TextMessage = require('hubot').TextMessage

 module.exports = (robot) ->
-        robot.router.post '/bot/message', (req, res) ->
-                if(req.body.name != 'Cowzilla')
-                        robot.receive new TextMessage req.body.user_id, req.body.text
+  robot.router.post '/bot/message', (req, res) ->
+    console.log(req.body.text)
+    if req.body.name != 'Cowzilla'
+      robot.receive new TextMessage req.body.user_id, req.body.text


remove logging plugin. Now we use Slack paied plan and can search all logs on Slack archives.

--- scripts/logging.coffee
+++ scripts/logging.coffee
@@ -1,80 +0,0 @@
-# Description:
-#  output log to all messages
-# # Dependencies:
-#   None
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   hubot log search <regexp> -- このチャンネルの発言ログを検索
-#   hubot log show from <from> to <to> -- このチャンネルの発言ログの <from> から <to> までのログを表示(日時は ISO8601 表記)
-#
-# Author:
-#   nagachika
-
-module.exports = (robot) ->
-  fluent = require("fluent-logger")
-  fluent.configure("hubot.#{process.env.HUBOT_SLACK_TEAM}", { host: "172.17.42.1", port: 24224 })
-
-  robot.hear /./, (msg) ->
-    room = msg.message.room
-    username = msg.message.user.name
-    text = msg.message.text
-    fluent.emit "chatlog", {room: room, user: username, text: text}
-
-
-  format_datetime = (date) ->
-    month = date.getMonth()
-    day = date.getDate()
-    hour = date.getHours()
-    min = date.getMinutes()
-    sec = date.getSeconds()
-    if month < 10
-      month = "0" + month
-    if day < 10
-      day = "0" + day
-    if hour < 10
-      hour = "0" + hour
-    if min < 10
-      min = "0" + min
-    if sec < 10
-      sec = "0" + sec
-    return "#{date.getFullYear()}-#{month}-#{day} #{hour}:#{min}:#{sec}"
-
-
-  # use docker host mongodb
-  mongo = require("mongodb").MongoClient
-
-  mongo_query = (msg, query, cb) ->
-    mongo.connect "mongodb://172.17.42.1:27017/hubot_#{process.env.HUBOT_SLACK_TEAM}", (err, db) ->
-      if (err)
-        msg.send "mongodb connection failure: #{err}"
-      else
-        collection = db.collection("chat_logs")
-        collection.find(query).toArray (err, items) ->
-          if (err)
-            msg.send "mongodb query failure: #{err}"
-          else
-            cb(msg, items)
-          db.close
-
-  robot.respond /log\s+search\s+(.+)/, (msg) ->
-    mongo_query msg, {room: msg.message.room, text: new RegExp(msg.match[1])}, (_msg, items) ->
-      buf = "#{items.length} matches:\n"
-      for m in items
-        date = m["time"]
-        date_str = format_datetime(date)
-        buf += "#{m["user"]}:#{date_str}: #{m["text"]}\n"
-      _msg.send buf
-
-  robot.respond /log\s+show\sfrom\s+([0-9:T-]+)\s+to\s+([0-9::T-]+)/, (msg) ->
-    from = new Date(msg.match[1]+"+0900")
-    to = new Date(msg.match[2]+"+0900")
-    mongo_query msg, {room: msg.message.room, time: { "$gt": from, "$lte": to } }, (_msg, items) ->
-      buf = "#{items.length} messages:\n"
-      for m in items
-        date = m["time"]
-        date_str = format_datetime(date)
-        buf += "#{m["user"]}:#{date_str}: #{m["text"]}\n"
-      _msg.send buf


Literal period for Ohiobot command

Literal period for Ohiobot command

--- scripts/ohiobot.coffee
+++ scripts/ohiobot.coffee
@@ -19,5 +19,5 @@
     msg.send "I/O!"
     robot.brain.set("ios", robot.brain.get("ios") + 1)

-  robot.hear /^.ios/, (msg) ->
+  robot.hear /^\.ios/, (msg) ->
     msg.send robot.brain.get("ios") + " IOs have been dealt"


Don't select a node's label; only select its grouped path(s).

--- app/scripts/viewmodels/drawings/canvas_element.coffee
+++ app/scripts/viewmodels/drawings/canvas_element.coffee
@@ -89,6 +89,9 @@

     select: =>
       @canvas.clearSelection()
-      @canvasElement.selected = true
+      if @isNode()
+        @canvasElement.firstChild.selected = true
+      else
+        @canvasElement.selected = true
       @element.select()


CoffeeScripty.

CoffeeScripty.

--- application/chrome/content/wesabe/ofx/Response.coffee
+++ application/chrome/content/wesabe/ofx/Response.coffee
@@ -63,7 +63,7 @@
   # Public methods

   # Response OFX presented as a DOM tree.
-  this::__defineGetter__ 'responseXML', ->
+  @::__defineGetter__ 'responseXML', ->
     @__responseXML__ ||= new Document @response, /BANKTRANLIST/i

   # Response OFX presented as a complete DOM tree, including BANKTRANLIST nodes.
@@ -71,7 +71,7 @@
     @__fullResponseXML__ ||= new Document @response

   # List of all deposit accounts.
-  this::__defineGetter__ 'bankAccounts', ->
+  @::__defineGetter__ 'bankAccounts', ->
     @_find_accounts() unless @__bankAccounts__
     return @__bankAccounts__


Just a thought.

Just a thought.

--- scripts/watched.coffee
+++ scripts/watched.coffee
@@ -79,7 +79,7 @@
             hourcount = msg.match[1]
             savedhours = robot.brain.get 'topwatchers'

-            watchedscores = [timename + " has " + hours[timename] / 100 + " hours watched."] for timename of savedhours
+            watchedscores = [timename + " has " + hours[timename] / 100 + " hours watched."]

             hourscore = watchedscores.slice(0, hourcount)


Return the Colorized string, not an array of Colorizers.

--- application/chrome/content/wesabe/util/inspect.coffee
+++ application/chrome/content/wesabe/util/inspect.coffee
@@ -222,6 +222,8 @@
     continue if wesabe.isFunction(object[key]) || key.match(/^__/)
     s.print(_inspectAttribute(key, object[key], refs, color, tainted))

+  return s.toString()
+
 _inspectAttribute = (key, value, refs, color, tainted) ->
     new wesabe.util.Colorizer().print(' ')
                                .underlined(key)


Fixed compiling and flashing on Windows

Fixed compiling and flashing on Windows

--- lib/spark-ide.coffee
+++ lib/spark-ide.coffee
@@ -256,6 +256,7 @@

     # Including files
     fs ?= require 'fs-plus'
+    path ?= require 'path'
     settings ?= require './vendor/settings'
     utilities ?= require './vendor/utilities'

@@ -263,6 +264,9 @@
     files = fs.listSync(rootPath)
     files = files.filter (file) ->
       return !(utilities.getFilenameExt(file).toLowerCase() in settings.notSourceExtensions)
+
+    process.chdir rootPath
+    files = (path.relative(rootPath, file) for file in files)

     workspace = atom.workspaceView
     @compileCloudPromise = @spark.compileCode files
@@ -307,6 +311,7 @@
   # Flash core via the cloud
   flashCloud: (firmware=null) -> @coreRequired => @projectRequired =>
     fs ?= require 'fs-plus'
+    path ?= require 'path'
     utilities ?= require './vendor/utilities'

     rootPath = atom.project.getPath()
@@ -322,6 +327,9 @@

       if !firmware
         firmware = files[0]
+
+      process.chdir rootPath
+      firmware = path.relative rootPath, firmware

       @statusView.setStatus 'Flashing via the cloud...'

@@ -351,7 +359,7 @@
     @openPane 'serial-monitor'

   # Set up core's WiFi
-  setupWifi: (port=null) -> @loginRequired =>
+  setupWifi: (port=null) -> @loginRequired =>
     if !port
       @choosePort 'spark-ide:setup-wifi'
     else


added a couple of utility methods to framework

--- Framework/Framework.coffee
+++ Framework/Framework.coffee
@@ -56,6 +56,22 @@
           files = files.concat additional
     root._.uniq files

+  fromJson: (json, klass) ->
+    obj = new klass()
+    for key, value of JSON.parse(json)
+      obj[key] = value
+    obj
+
+  deepExtend = (object, extenders...) ->
+    return {} if not object?
+    for other in extenders
+      for own key, val of other
+        if not object[key]? or typeof val isnt "object"
+          object[key] = val
+        else
+          object[key] = deepExtend object[key], val
+    object
+
   post: (url, params, onSuccess, onError = null) =>
     @xhr.abort()
     @xhr.open('POST', url)


Error when trying to cut/paste a file that already exists

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -736,15 +736,13 @@
               fs.writeFileSync(newPath, fs.readFileSync(initialPath))
               @emitter.emit 'entry-copied', {initialPath, newPath}
         else if cutPaths
-          # Only move the target if the cut target doesn't exist
-          unless fs.existsSync(newPath)
-            try
-              @emitter.emit 'will-move-entry', {initialPath, newPath}
-              fs.moveSync(initialPath, newPath)
-              @emitter.emit 'entry-moved', {initialPath, newPath}
-            catch error
-              @emitter.emit 'move-entry-failed', {initialPath, newPath}
-              atom.notifications.addWarning("Unable to paste paths: #{initialPaths}", detail: error.message)
+          try
+            @emitter.emit 'will-move-entry', {initialPath, newPath}
+            fs.moveSync(initialPath, newPath)
+            @emitter.emit 'entry-moved', {initialPath, newPath}
+          catch error
+            @emitter.emit 'move-entry-failed', {initialPath, newPath}
+            atom.notifications.addWarning("Unable to paste paths: #{initialPaths}", detail: error.message)

   add: (isCreatingFile) ->
     selectedEntry = @selectedEntry() ? @roots[0]


implemented localBounds

implemented localBounds

--- src/tree.coffee
+++ src/tree.coffee
@@ -21,7 +21,8 @@
     constructor: (@origin, @basisX, @basisY, @left = null, @right = null) ->

     # Call spawn on @left and @right, if they exist.  If not, create them using
-    # the given angle (in radians).
+    # the given angle (in radians).  Return a list of all new nodes created by
+    # this function on this node and its children.
     spawn: (angle) ->
         newNodes = []
         if @left?
@@ -50,12 +51,27 @@
         rightBounds = @right?.bounds()
         boundAll(@localBounds(), leftBounds, rightBounds)

-    # return the boundaries [(xmin, ymin), (xmax, ymax)] of this square
+    # return the boundaries [(xMin, yMin), (xMax, yMax)] of this square
     localBounds: ->
+        corners = [
+            @origin                             # bottom left
+            @origin.add(@basisX)                # bottom right
+            @origin.add(@basisY)                # top left
+            @origin.add(@basisX).add(@basisY)   # top right
+        ]
+        # need to initialize to some point in the square
+        [xMin, yMin] = [@origin.x, @origin.y]
+        [xMax, yMax] = [xMin, yMin]
+        # look through all points and find real min and max
+        for point in cornersP
+            [xMin, yMin] = [Math.min(xMin, point.x), Math.min(yMin, point.y)]
+            [xMax, yMax] = [Math.max(xMax, point.x), Math.max(yMax, point.y)]
+        return [[xMin, yMin], [xMax, yMax]]

     toString: -> "{X:#{@basisX}, Y:#{@basisY}, O:#{@origin}, L:#{@left}, R:#{@right}}"

-# return the area defined by [(xmin, ymin), (xmax, ymax)] within the boundaries
+# return the area defined by [(xmin, ymin), (xmax, ymax)] within boundaries,
+# which is a list of point pairs of the same form
 boundAll: (boundaries...) ->

 # A representation of the Pythagoras tree in its own coordinate system.


update drawing on every event

update drawing on every event

--- src/packages/editor-stats/src/editor-stats-view.coffee
+++ src/packages/editor-stats/src/editor-stats-view.coffee
@@ -50,6 +50,12 @@
     .append('g')
       .attr('transform', "translate(#{pl},#{pt})")

+  draw: ->
+    w = @.width()
+    h = @.height()
+    [pt, pl, pb, pr] = [0,0,0,0]
+
+
   track: =>
     date = new Date
     times = time date
@@ -57,7 +63,7 @@
     @eventlog[times] += 1

     @eventlog.shift() if @eventlog.length > 60
-    console.log @eventlog
+    @draw()

   toggle: ->
     if @hasParent()


Comments.

Comments.

--- library/index.coffee
+++ library/index.coffee
@@ -53,8 +53,8 @@
 #           error is a String explaing the failure
 #           encrypted is undefined
 #
-# A `miniLockLib.EncryptOperation` is constructed when you call `encrypt`. Refer
-# to EncryptOperation.coffee to see how it works.
+# A `miniLockLib.EncryptOperation` is constructed when you call `encrypt`.
+# Refer to EncryptOperation.coffee to see how it works.

 miniLockLib.encrypt = (params) ->
   {data, name, miniLockIDs, keys, callback} = params
@@ -88,8 +88,8 @@
 #           error is a String explaing the failure
 #           encrypted is undefined
 #
-# A `miniLockLib.DecryptOperation` is constructed when you call `encrypt`. Refer
-# to DecryptOperation.coffee to see how it works.
+# A `miniLockLib.DecryptOperation` is constructed when you call `decrypt`.
+# Refer to DecryptOperation.coffee to see how it works.

 miniLockLib.decrypt = (params) ->
   {data, keys, callback} = params


Check channel connection status before sending commands upstream

--- app/assets/javascripts/action_cable_notifications/collection.coffee
+++ app/assets/javascripts/action_cable_notifications/collection.coffee
@@ -2,11 +2,15 @@
   # Private methods
   #######################################
   upstream = (command, params={}) ->
-    @channel?.perform?('action',
-      collection: @tableName
-      command: command
-      params: params
-    ) if @sync
+    if !@channel.consumer.connection.disconnected
+      @channel?.perform?('action',
+        collection: @tableName
+        command: command
+        params: params
+      ) if @sync
+    else
+      # XXX Queue commands to be sent to server when connection
+      # is resumed

   # Public methods
   #######################################
@@ -19,6 +23,8 @@
     # Tells changes should be synced with upstream collection
     @sync = false

+    # Bind private methods to class instance
+    ########################################################
     upstream = upstream.bind(this)

   fetch: (params) ->


stops the abuse of the "configure" task by passing
globs everywhere that globs are used.

next stop, remove the configure task altogether
and refer to files without the "globs" property.

--- config/application.coffee
+++ config/application.coffee
@@ -9,7 +9,7 @@
     banner: "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " + "<%= grunt.template.today(\"yyyy-mm-dd\") %>\\n" + "<%= pkg.homepage ? \"* \" + pkg.homepage + \"\\n\" : \"\" %>" + "* Copyright (c) <%= grunt.template.today(\"yyyy\") %> <%= pkg.author.company %>;" + " Licensed <%= _.pluck(pkg.licenses, \"type\").join(\", \") %> */"

   appTasks:
-    common: ["coffee", "less", "sass", "jshint", "handlebars", "jst", "configure", "concat:js", "concat:spec", "concat:css", "images:dev", "webfonts:dev", "homepage:dev"]
+    common: ["configure", "coffee", "less", "sass", "jshint", "handlebars", "jst", "concat:js", "concat:spec", "concat:css", "images:dev", "webfonts:dev", "homepage:dev"]
     dev: ["server", "watch"]
     dist: ["uglify:js", "cssmin", "images:dist", "webfonts:dist", "homepage:dist"]

@@ -18,9 +18,9 @@
   coffee:
     compile:
       files:
-        "generated/js/app.coffee.js": "<%= files.coffee.app %>"
-        "generated/js/spec.coffee.js": "<%= files.coffee.spec %>"
-        "generated/js/spec-helpers.coffee.js": "<%= files.coffee.specHelpers %>"
+        "generated/js/app.coffee.js": "<%= files.glob.coffee.app %>"
+        "generated/js/spec.coffee.js": "<%= files.glob.coffee.spec %>"
+        "generated/js/spec-helpers.coffee.js": "<%= files.glob.coffee.specHelpers %>"

   #style
@@ -52,7 +52,7 @@
         wrapped: true

       files:
-        "generated/template/handlebars.js": "<%= files.template.handlebars %>"
+        "generated/template/handlebars.js": "<%= files.glob.template.handlebars %>"

   jst:
     compile:
@@ -60,7 +60,7 @@
         namespace: "JST"

       files:
-        "generated/template/underscore.js": "<%= files.template.underscore %>"
+        "generated/template/underscore.js": "<%= files.glob.template.underscore %>"

   #quality
@@ -68,7 +68,7 @@
     files: ["<%= files.glob.js.concatenated %>", "<%= files.glob.js.concatenatedSpec %>"]

   jshint:
-    files: ["<%= files.js.app %>"]
+    files: ["<%= files.glob.js.app %>"]
     options:
       force: process.env['LINEMAN_RUN'] || false
       curly: true
@@ -86,21 +86,21 @@
   #distribution
   concat:
     js:
-      src: ["<banner:meta.banner>", "<%= files.js.vendor %>", "<%= files.template.generated %>", "<%= files.coffee.generated %>", "<%= files.js.app %>"]
+      src: ["<banner:meta.banner>", "<%= files.glob.js.vendor %>", "<%= files.template.generated %>", "<%= files.coffee.generated %>", "<%= files.glob.js.app %>"]
       dest: "<%= files.glob.js.concatenated %>"

     spec:
-      src: ["<%= files.js.specHelpers %>", "<%= files.coffee.generatedSpecHelpers %>", "<%= files.js.spec %>", "<%= files.coffee.generatedSpec %>"]
+      src: ["<%= files.glob.js.specHelpers %>", "<%= files.coffee.generatedSpecHelpers %>", "<%= files.glob.js.spec %>", "<%= files.coffee.generatedSpec %>"]
       dest: "<%= files.glob.js.concatenatedSpec %>"

     css:
       src: [
         "<%= files.less.generatedVendor %>",
         "<%= files.sass.generatedVendor %>",
-        "<%= files.css.vendor %>",
+        "<%= files.glob.css.vendor %>",
         "<%= files.less.generatedApp %>",
         "<%= files.sass.generatedApp %>",
-        "<%= files.css.app %>"
+        "<%= files.glob.css.app %>"
       ]
       dest: "<%= files.glob.css.concatenated %>"

@@ -109,8 +109,8 @@
   #        if your lib requires a different structure to counter this, you'll need to nest your img files in vendor/img accordingly, ie: vendor/img/img
   images:
     files:
-      "app/img/": "<%= files.img.app %>"
-      "vendor/img/": "<%= files.img.vendor %>"
+      "app/img/": "<%= files.glob.img.app %>"
+      "vendor/img/": "<%= files.glob.img.vendor %>"

     root: "<%= files.glob.img.root %>"
     dev:
@@ -121,7 +121,7 @@

   webfonts:
     files:
-      "vendor/webfonts/": "<%= files.webfonts.vendor %>"
+      "vendor/webfonts/": "<%= files.glob.webfonts.vendor %>"

     root: "<%= files.glob.webfonts.root %>"
     dev:
@@ -185,48 +185,48 @@
   watch:
     js:
       files: ["<%= files.glob.js.vendor %>", "<%= files.glob.js.app %>"]
-      tasks: ["configure", "concat:js"]
+      tasks: ["concat:js"]

     coffee:
       files: "<%= files.glob.coffee.app %>"
-      tasks: ["configure","coffee","configure","concat:js"]
+      tasks: ["coffee", "concat:js"]

     jsSpecs:
       files: ["<%= files.glob.js.specHelpers %>", "<%= files.glob.js.spec %>"]
-      tasks: ["configure","concat:spec"]
+      tasks: ["concat:spec"]

     coffeeSpecs:
       files: ["<%= files.glob.coffee.specHelpers %>", "<%= files.glob.coffee.spec %>"]
-      tasks: ["configure","coffee","configure","concat:spec"]
+      tasks: ["coffee", "concat:spec"]

     css:
       files: ["<%= files.glob.css.vendor %>", "<%= files.glob.css.app %>"]
-      tasks: ["configure","concat:css"]
+      tasks: ["concat:css"]

     less:
       files: ["<%= files.glob.less.vendor %>", "<%= files.glob.less.app %>"]
-      tasks: ["configure","less","configure","concat:css"]
+      tasks: ["less", "concat:css"]

     sass:
       files: ["<%= files.glob.sass.vendor %>", "<%= files.glob.sass.app %>"]
-      tasks: ["configure","sass","configure","concat:css"]
+      tasks: ["sass", "concat:css"]

     handlebars:
       files: "<%= files.glob.template.handlebars %>"
-      tasks: ["configure","handlebars","configure","concat:js"]
+      tasks: ["handlebars", "concat:js"]

     underscore:
       files: "<%= files.glob.template.underscore %>"
-      tasks: ["configure","jst","configure","concat:js"]
+      tasks: ["jst", "concat:js"]

     images:
       files: ["<%= files.glob.img.app %>", "<%= files.glob.img.vendor %>"]
-      tasks: ["configure","images:dev"]
+      tasks: ["images:dev"]

     homepage:
       files: "<%= homepage.template %>"
-      tasks: ["configure","homepage:dev"]
+      tasks: ["homepage:dev"]

     lint:
       files: "<%= files.glob.js.app %>"
-      tasks: ["configure","jshint"]
+      tasks: ["jshint"]


reverse order of tags to account for float:right

--- app/assets/javascripts/species/helpers/handlebars_helpers.js.coffee
+++ app/assets/javascripts/species/helpers/handlebars_helpers.js.coffee
@@ -15,7 +15,7 @@
   escaped = Handlebars.Utils.escapeExpression(value)
   formatted = escaped.split(',').map((item) ->
   	"" + item + ""
-  ).join ""
+  ).reverse().join ""
   new Handlebars.SafeString(formatted)

 Ember.Handlebars.registerHelper('highlight', (suggestion, options) ->


Modified how querry data is processed by the scatter chart. Column 1 is X values, 2 is Y, 3 is bubble size and 4 is for grouping

--- app/assets/javascripts/views/reports/report_scatter_chart.js.coffee
+++ app/assets/javascripts/views/reports/report_scatter_chart.js.coffee
@@ -4,39 +4,58 @@

 	postRender: ->
 		data = @prepareData()
+		colX = @getColX()
+		colY = @getColY()
+
 		nv.addGraph ()=>
 			chart = nv.models.scatterChart()
 			chart.color(d3.scale.category10().range())
-			#chart.xAxis.tickFormat(d3.time.format, '%a')
-			#chart.yAxis.tickFormat(d3.format ',.1f')
-			d3.select('#scatter_chart svg').datum(data).transition().duration(500).call(chart)
+			chart.xAxis.tickFormat(d3.format ',').axisLabel(colX)
+			chart.yAxis.tickFormat(d3.format ',').axisLabel(colY)
+			chart.showControls(false)
+			d3.fisheye = false # strange fix for fisheye causing issues with selecting legend elements
+			d3.select('#scatter_chart svg').datum(data).transition().duration(350).call(chart)
 			nv.utils.windowResize(chart.update)
 			chart

 	prepareData: ->
+
 		data = @getData()
+
 		colX = @getColX()
-
-		if @report_data.totalColumns() > 2
-			colsY = @report_data.getColumns()
-			colsY.shift()
-		else
-			colsY = [@getColY()]
-
+		colY = @getColY()
+		colSize = @getColSize()
+		colLabel = @getColLabel()
+
+		# small list to keep track of names to indexes while adding
+		indexes = []
 		streams = []
-		for y in colsY
-			prepared = []
-			for value in data
-				dt = value[colX]
-				dt = @dateToTime(value[colX]) if @convertDateToTime()
-				prepared.push {x: dt, y: value[y], series: 0}
-			streams.push {key: y, values: prepared}
-		streams
+
+		for row in data
+			groupLabel = row[colLabel]
+
+			if !(groupLabel in indexes)
+				indexes.push(groupLabel)
+				streams.push({
+      	  key: groupLabel,
+      	  values: []
+   		  })
+
+			groupIndex = indexes.indexOf(groupLabel)
+
+			streams[groupIndex].values.push({
+        x: row[colX],
+        y: row[colY],
+        size: row[colSize]
+      })
+  streams

 	getTemplateName: -> "report_scatter_chart"

 	getColX: -> @report_data.getColumns()[0]
 	getColY: -> @report_data.getColumns()[1]
+	getColSize: -> @report_data.getColumns()[2]
+	getColLabel: -> @report_data.getColumns()[3]

 	convertDateToTime: -> no


Fix a bug due to merge conflict

Fix a bug due to merge conflict

--- BackofficeBundle/Resources/public/coffee/areaView.coffee
+++ BackofficeBundle/Resources/public/coffee/areaView.coffee
@@ -74,6 +74,7 @@
       area: areaElement
       height: areaHeight
       direction: @childrenDirection
+      node_id: @node_id
     )
     $("ul.ui-model-areas", @el).append areaView.render().el
     return


Match only exact excape keywords, e.g. in but not sin, arcsin

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -341,7 +341,8 @@
     replace_escape_keyword = (match) ->
       return '"' + match + '"' # Force MathJax plain text
     for escape_keyword in escape_keywords
-      eqn = eqn.replace(escape_keyword, replace_escape_keyword)
+      escape_keyword_patt = RegExp('\\b'+escape_keyword+'\\b')
+      eqn = eqn.replace(escape_keyword_patt, replace_escape_keyword)

     return eqn


sensors: v_dist_point_to_rect is not valid for get distance of the pointer to the target in pointer_over_no_phy.

--- engine/sensors.coffee
+++ engine/sensors.coffee
@@ -307,8 +307,12 @@

     for ob in objects
         pos = ob.get_world_position()
-        vdist = r3.v_dist_point_to_rect v3, pos, cam_pos, dir
-        dist = vec3.len vdist
+
+        vec3.normalize dir, dir
+        target_dir = vec3.scale v3, dir, vec3.dist(cam_pos, pos)
+        vec3.add target_dir, cam_pos, target_dir
+        dist = vec3.dist(target_dir, pos)
+
         if dist <= ob.radius
             hits.push
                 object: ob


activity autocomplete styling fix

activity autocomplete styling fix

--- components/autocomplete/autocompletecontroller.coffee
+++ components/autocomplete/autocompletecontroller.coffee
@@ -54,7 +54,7 @@

     switch event.which
       when 13, 9 #enter, tab
-        unless autoCompleteView.getValue() is ""
+        if autoCompleteView.getValue() isnt "" and event.shiftKey isnt yes
           @submitAutoComplete autoCompleteView.getValue()
         else
           return yes
@@ -97,7 +97,7 @@
     windowController = @getSingleton('windowController')

     @dropdown = new KDListViewController
-      view          : dropdownListView
+      view : dropdownListView

     dropdownWrapper = @dropdown.getView()


[client][app][activity] added error parameter to callback

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -399,8 +399,7 @@
         data = clearQuotes data
         KD.remote.reviveFromSnapshots data, (err, instances)->
           if err then callback err
-          else
-            callback instances
+          else callback null, instances

   unhideNewItems: ->
     @listController?.activityHeader.updateShowNewItemsLink yes


adds specs for color filter

adds specs for color filter

--- components/commercial_filter/filters/color/color_filter_view.coffee
+++ components/commercial_filter/filters/color/color_filter_view.coffee
@@ -1,6 +1,6 @@
 _ = require 'underscore'
 Backbone = require 'backbone'
-template = require('./index.jade')
+template = -> require('./index.jade') arguments...

 module.exports = class ColorFilterView extends Backbone.View
   events:
@@ -38,7 +38,7 @@
     @params.set page: 1, silent: true
     @params.unset 'color'

-  renderSelectedAndEmptyStates: =>
+  renderSelectedAndEmptyStates: ->
     # First remove all empty states
     _.each(@$clickableColors, (color) =>
       $(color).removeAttr('data-empty')
@@ -56,7 +56,7 @@
       @$checkmark.hide()

     # Add empty states
-    respColors = _.pluck @aggregations.get('COLOR').get('counts'), 'name'
+    respColors = _.pluck @aggregations.get('COLOR')?.get('counts'), 'name'
     emptyColors = _.difference(@colors, respColors)
     _.each(emptyColors, (color) ->
       @$("svg path[data-value='#{color}']").attr('data-empty', true)


duplication fix + refactoring

duplication fix + refactoring

--- server/konnectors/twitter.coffee
+++ server/konnectors/twitter.coffee
@@ -52,18 +52,10 @@

     fetch: (requiredFields, callback) ->
         log.info "Import started"
-        params = limit: 1, descending: true
-        TwitterTweet.request 'byDate', params, (err, tweets) =>
-            if tweets.length > 0
-                start = moment(tweets[0].date)
-            else
-                start = moment().subtract('years', 10)
-
-            log.info "Start import since #{start.format()}"
-            saveTweets requiredFields, start, callback
+        saveTweets requiredFields, callback

-saveTweets = (requiredFields, start, callback) ->
+saveTweets = (requiredFields, callback) ->
     url = "https://api.twitter.com/1.1/"
     client = requestJson.newClient url
     client.options =
@@ -80,48 +72,48 @@
         contributor_details: false
         count: 200

-    recSave = (lastId) ->
+    params = descending: true

-        if lastId?
-            if lastId isnt 'id'
-                newPath = path + "&" + querystring.stringify
-                    max_id: lastId
+    TwitterTweet.request 'byDate', params, (err, tweets) =>
+        if tweets.length > 0
+            start = moment(tweets[0].date)
+        else
+            start = moment().subtract('years', 10)
+
+        log.info "Start import since #{start.format()}"
+
+        saveTweetGroup client, path, start, tweets.length, (err, lastId) ->
+            if err then callback err
             else
-                newPath = path
-
-            saveTweetGroup client, newPath, start, (err, lastId) ->
-                if err then callback err
-                else recSave lastId
-        else
-            log.info "Import finished"
+                log.info "Import finished"

             callback()

-    recSave 'id'

+saveTweetGroup = (client, path, start, tweetLength, callback) ->

-saveTweetGroup = (client, path, start, callback) ->
-
-    log.debug path
     client.get path, (err, res, tweets) ->
+        log.debug "#{tweetLength} tweet in DB, #{tweets.length} in the response"
         if err
             callback err
         else if res.statusCode isnt 200
             callback new Error 'Bad authentication data'
+        else if tweetLength is tweets.length
+            log.info 'No new tweet to import'
+            callback()
         else
-            log.info "#{tweets.length} tweets to import"
+            log.info "#{tweets.length - tweetLength} tweet(s) to import"
             tweets = tweets.reverse()
             tweets.pop() if path.indexOf('max_id') isnt -1

             lastId = null
             async.eachSeries tweets, (tweet, cb) ->
+
                 date = moment tweet.created_at
+                log.debug date

-                log.debug tweet.created_at
-                log.debug moment tweet.created_at
-                log.debug date
+                lastId = tweet.id_str
                 if date > start
-                    lastId = tweet.id_str

                     twitterTweet = TwitterTweet
                         date: date
@@ -141,5 +133,4 @@
                 else
                     cb()
             , (err) ->
-                log.debug lastId
                 callback err, lastId


Fix display line breaks test

Fix display line breaks test

--- test/javascripts/helpers/display_line_breaks.js.coffee
+++ test/javascripts/helpers/display_line_breaks.js.coffee
@@ -1,4 +1,4 @@
-moduleFor 'helper:displayLineBreaks', 'Unit: Display line breaks',
+module 'helper:displayLineBreaks', 'Unit: Display line breaks',
   setup: -> setupApp()
   teardown: -> ETahi.reset()


fixes #1

fixes #1

--- src/coffeegrid.coffee
+++ src/coffeegrid.coffee
@@ -36,14 +36,11 @@

     # clear <table> tag
     clearTable = () =>
-      $(@table).text("")
       $(@table).removeChildren

     # cleat only <tbody> of <table>
     clearTableBody = () =>
-      $("div.wrap.container." + @tableId).text("")
       $("div.wrap.container." + @tableId).removeChildren
-      $("#" + @tableId + " tbody").text("")
       $("#" + @tableId + " tbody").removeChildren

     # draw <thead>, values are taken from "headers" array


Whitespace clean-up.

Whitespace clean-up.

--- src/coffeelint.coffee
+++ src/coffeelint.coffee
@@ -134,7 +134,7 @@
         # since they are all reduced to a single token whose line number
         # is the start of the expression.
         indent = @line.split(regexes.indentation)[0]
-        if @lineHasToken() and  ~indent.indexOf('\t')
+        if @lineHasToken() and ~indent.indexOf('\t')
             @createLineError('no_tabs')
         else
             null


fix unit tests

fix unit tests

timekeeper must be called before SandboxedModule.require

--- test/unit/coffee/RedisManager/RedisManagerTests.coffee
+++ test/unit/coffee/RedisManager/RedisManagerTests.coffee
@@ -13,6 +13,7 @@
 			auth: () ->
 			exec: sinon.stub()
 		@rclient.multi = () => @rclient
+		tk.freeze(new Date())
 		@RedisManager = SandboxedModule.require modulePath,
 			requires:
 				"logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
@@ -50,6 +51,9 @@
 				"./Errors": Errors
 			globals:
 				JSON: @JSON = JSON
+
+		afterEach ->
+			tk.reset()

 		@doc_id = "doc-id-123"
 		@project_id = "project-id-123"
@@ -323,12 +327,8 @@

 		describe "with a consistent version", ->
 			beforeEach ->
-				tk.freeze(new Date())
 				@RedisManager.getDocVersion.withArgs(@doc_id).yields(null, @version - @ops.length)
 				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @ranges, @callback
-
-			afterEach ->
-				tk.reset()

 			it "should get the current doc version to check for consistency", ->
 				@RedisManager.getDocVersion


refactored coffeescript

refactored coffeescript

--- drm-offscreen-menu.coffee
+++ drm-offscreen-menu.coffee
@@ -1,71 +1,57 @@
 ###############################################################################
 # Displays an offscreen menu that slides into view
 ###############################################################################
+"use strict"

 ( ($) ->
+    class window.DrmOffscreen
+        constructor: (@menu = $('nav.drm-offscreen-menu'), @button = $('button.drm-menu-button'), @content = $('div.drm-offscreen-content'), @holder = $('div.drm-content-holder'), @state = 'hide') ->
+            self = @
+            menuWidth = self.getDimensions()

-	drmOffscreen = {
-
-		menu: $ '.drm-offscreen-menu'
-		button: $ '.drm-menu-button'
-		content: $ '.drm-offscreen-content'
-		holder: $ '.drm-content-holder'
+            # set menu and content positions
+            if self.state is 'hide' then self.hideMenu(menuWidth)

-		config: {
-			state: 'hide'
-		}
+            self.button.on 'click', $.proxy self.toggleMenu, self

-		init: (config) ->
-			$.extend @config, config
-			menuWidth = @.getDimensions()
+        toggleMenu: ->
+            menuPos = @menu.css 'left'
+            menuWidth = @getDimensions()

-			# set menu and content positions
-			if drmOffscreen.config.state == 'hide'
-				drmOffscreen.hideMenu(menuWidth)
+            if menuPos is '0px' then @hideMenu menuWidth else @showMenu menuWidth

-			@.button.on 'click', @toggleMenu
+        showMenu: (menuWidth) ->
+            contentWidth = 100 - menuWidth

-		toggleMenu: ->
-			menuPos = drmOffscreen.menu.css 'left'
-			menuWidth = drmOffscreen.getDimensions()
+            @menu.animate {
+                'left': '0'
+            }

-			if menuPos == '0px'
-				drmOffscreen.hideMenu(menuWidth)
-			else
-				drmOffscreen.showMenu(menuWidth)
+            @content.animate {
+                'left': "#{menuWidth}%", 'width': "#{contentWidth}%"
+            }

-		showMenu: (menuWidth) ->
-			contentWidth = 100 - menuWidth
+        hideMenu: (menuWidth) ->
+            @menu.animate {
+                'left': "-#{menuWidth}%"}
+            @content.animate {
+                'left': '0', 'width': '100%'}
+            @addScroll()

-			drmOffscreen.menu.animate {
-				'left': '0'}
-			drmOffscreen.content.animate {
-				'left': "#{menuWidth}%", 'width': "#{contentWidth}%"}
+        getDimensions: ->
+            menuWidth = parseInt @menu.css('width'), 10
+            holderWidth = parseInt @holder.css('width'), 10

-		hideMenu: (menuWidth) ->
-			drmOffscreen.menu.animate {
-				'left': "-#{menuWidth}%"}
-			drmOffscreen.content.animate {
-				'left': '0', 'width': '100%'}
+            # calculate menuWidth as a percentage of container
+            menuWidth = (menuWidth / holderWidth) * 100
+            menuWidth

-			drmOffscreen.addScroll()
+        addScroll: ->
+            menuHeight = parseInt @menu.find('ul').css('height'), 10
+            contentHeight = parseInt @content.css('height'), 10

-		getDimensions: ->
-			menuWidth = parseInt(drmOffscreen.menu.css('width'), 10)
-			holderWidth = parseInt(drmOffscreen.holder.css('width'), 10)
-
-			# calculate menuWidth as a percentage of container
-			menuWidth = (menuWidth / holderWidth) * 100
-			return menuWidth
-
-		addScroll: ->
-			menuHeight = parseInt((drmOffscreen.menu.find('ul').css 'height'), 10)
-			contentHeight = parseInt((drmOffscreen.content.css 'height'), 10)
-
-			if menuHeight > contentHeight
-				drmOffscreen.menu.css {'overflow-y': 'scroll'}
-	}
-
-	drmOffscreen.init()
+            if menuHeight > contentHeight then @menu.css {'overflow-y': 'scroll'}
+
+    new DrmOffscreen()

 ) jQuery


vulcanization process isn't erroring anymore.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,7 +29,7 @@
     expand: true,
     overwrite: true,
     cwd: dir,
-    src: ['**/*.*'],
+    src: [ '**/*.ts', '**/*.html' ],
     dest: 'build/typescript-src/' + dest} ] }

 # Use symlinkSrc with the name of the module, and it will automatically symlink
@@ -460,20 +460,12 @@
         dest: '.'

     vulcanize:
-      chrome_ui:
+      root:
         options:
           csp: true
           strip: true
         files:
-          'build/dev/chrome/extension/polymer/vulcanized.html': 'build/typescript-src/generic_ui/polymer/root.html'
-      #firefox_ui:
-      #  files: [ {
-      #      src: 'third_party/lib/paper-*/paper-*.html'
-      #      dest: firefoxDevPath + 'data/lib'
-      #    }, {
-      #      src: 'third_party/lib/core-*/core-*.html'
-      #      dest: firefoxDevPath + 'data/lib'
-      #    } ]
+          'build/typescript-src/generic_ui/polymer/vulcanized.html': 'build/typescript-src/generic_ui/polymer/root.html'

     clean: ['build/**', '.tscache']


Changed out output is piped.

Changed out output is piped.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -19,14 +19,9 @@
   grunt.loadNpmTasks "grunt-contrib-uglify"

   # Internal functions
-  setupStream = (stream) ->
-    stream.setEncoding "utf8"
-    stream.on "data", (data) -> console.log data
-
   spawn = (options, done = ->) ->
-    fork = grunt.util.spawn options, done
-    setupStream fork.stdout
-    setupStream fork.stderr
+    options.opts ?= stdio: "inherit"
+    grunt.util.spawn options, done

   grunt.initConfig
     pkg: grunt.file.readJSON "package.json"


Add clean task

Add clean task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,7 +25,15 @@
           stderr: true
           failOnError: true

+      clean:
+        command: 'rm -fr build deps/libgit2/build'
+        options:
+          stdout: true
+          stderr: true
+          failOnError: true
+
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-shell')
   grunt.registerTask('default', ['coffee', 'shell:rebuild'])
-  grunt.registerTask('test', ['coffee', 'shell'])
+  grunt.registerTask('test', ['default', 'shell:test'])
+  grunt.registerTask('clean', ['shell:clean'])


Fix Grunt build to use Bundler

Fix Grunt build to use Bundler

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,9 +22,9 @@
         tasks: ['watch', 'exec:serve']
     exec:
       build:
-        cmd: 'jekyll build'
+        cmd: 'bundle exec jekyll build'
       serve:
-        cmd: 'jekyll serve --watch'
+        cmd: 'bundle exec jekyll serve --watch'
     less:
       blogTheme:
         options:


fixed test task name

fixed test task name

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -149,4 +149,4 @@
   grunt.loadTasks 'tasks/'

   # Default task.
-  grunt.registerTask 'default', [ 'jshint:core', 'concat', 'uglify', 'mocha', 'lang:en', 'lang:ja', 'lang:de', 'build' ]
+  grunt.registerTask 'default', [ 'jshint:core', 'concat', 'uglify', 'mocha:all', 'lang:en', 'lang:ja', 'lang:de', 'build' ]


add more browsers

add more browsers

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,29 +8,14 @@

   pkg = grunt.file.readJSON "package.json"

-  browsers = [
-    browserName: "firefox"
-    version: "19"
-    platform: "XP"
-  ,
-    browserName: "chrome"
-    platform: "XP"
-  ,
-    browserName: "chrome"
-    platform: "linux"
-  ,
-    browserName: "internet explorer"
-    platform: "WIN8"
-    version: "10"
-  ,
-    browserName: "internet explorer"
-    platform: "VISTA"
-    version: "9"
-  ,
-    browserName: "internet explorer"
-    platform: "XP"
-    version: "8"
-  ]
+  browsers = []
+
+  browsers.push browser for browser in ({platform: "XP", browserName: "googlechrome", version: "#{vers}"} for vers in [26..31])
+
+  browsers.push browser for browser in ({browserName: "firefox", version: "#{vers}"} for vers in [3..26])
+
+  browsers.push browser for browser in ({browserName: "internet explorer", version: "#{vers}"} for vers in [6..11])
+

   grunt.initConfig


grunt: minor whitespace fix

grunt: minor whitespace fix

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -38,7 +38,6 @@
       'node_modules/underscore/underscore.js'        # for utilities
       'node_modules/backbone/backbone.js'            # for mvc apps
       'node_modules/grunt-jasmine-spec-server/lib/bootstrap/js/bootstrap.min.js'
-
       'lib/athena.lib.min.js'                        # for athena utils
       'lib/jquery/jquery.object-fit.js'              # for object-fit
     ]


name file according to meaning of content, not format

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,11 +62,11 @@
     sass:
       build:
         files:
-          'build/css/basic_without_base64.css': 'app/css/basic.scss'
+          'build/css/basic_without_embeds.css': 'app/css/basic.scss'
     cssUrlEmbed:
       encodeDirectly:
         files:
-          'build/css/basic.css': ['build/css/basic_without_base64.css']
+          'build/css/basic.css': ['build/css/basic_without_embeds.css']
     cssmin:
       build:
         options:


Fixed LESS task for client build

Fixed LESS task for client build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,10 +6,11 @@
 			dev:
 				options:
 					force: true
-				src: ["./**/*.js",
+				src: ["./**/*.js",
+					  "./**/*.js.map",
+					  "./client/style/*.css",
 					  "!./client/lib/**/*.*",
-					  "!./node_modules/**/*.*",
-					 "./**/*.js.map",
+					  "!./node_modules/**/*.*"
 					 ]
 			client:
 				options:
@@ -75,8 +76,8 @@

 		watch:
 			client:
-				files: ["./client/**/*.coffee"]
-				tasks: ["coffee:client"]
+				files: ["./client/**/*.coffee", "./client/**/*.less"]
+				tasks: ["coffee:client","less:dev"]
 			server:
 				files: ["./**/*.coffee","!./client/**/*.*"]
 				tasks: ["coffee:server"]
@@ -91,7 +92,7 @@
 	grunt.loadNpmTasks 'grunt-concurrent'

 	# Tasks
-	grunt.registerTask 'client', ['clean:client', 'coffee:client']
+	grunt.registerTask 'client', ['clean:client', 'coffee:client', 'less:dev']
 	grunt.registerTask 'server', ['clean:server','coffee:server']

 	grunt.registerTask 'default', ['server', 'client', 'concurrent:start']


mocha will run coffeescript just fine, no need to convert it to .js files first

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,14 +11,6 @@
         src: ['src/scripts/**/*.js']
       test:
         src: ['src/test/**/*.js']
-    copy:
-      main:
-        files: [
-            expand: true
-            cwd: 'scripts/'
-            src: ['**/*.js']
-            dest: 'out/'
-        ]
     coffeelint:
       gruntfile:
         src: 'Gruntfile.coffee'
@@ -31,27 +23,11 @@
           level: 'error'
         max_line_length:
           level: 'warn'
-    coffee:
-      #compile:
-      #  files:
-      #    'out/scripts/vender.js': ['src/vendor/*.coffee']
-      scripts:
-        expand: true
-        cwd: 'src/scripts/'
-        src: ['**/*.coffee']
-        dest: 'out/scripts/'
-        ext: '.js'
-      test:
-        expand: true
-        cwd: 'src/test/'
-        src: ['**/*.coffee']
-        dest: 'out/test/'
-        ext: '.js'
     simplemocha:
       all:
         src: [
           'node_modules/should/lib/should.js'
-          'out/test/**/*.js'
+          'src/test/**/*.coffee'
         ]
         options:
           globals: ['should']
@@ -64,12 +40,6 @@
       gruntfile:
         files: '<%= coffeelint.gruntfile.src %>'
         tasks: ['coffeelint:gruntfile']
-      jsLib:
-        files: '<%= jshint.scripts.src %>'
-        tasks: ['jshint:scripts', 'simplemocha']
-      jsTest:
-        files: '<%= jshint.test.src %>'
-        tasks: ['jshint:test', 'simplemocha']
       coffeeLib:
         files: '<%= coffeelint.scripts.src %>'
         tasks: ['coffeelint:scripts', 'coffee:scripts', 'simplemocha']
@@ -81,22 +51,14 @@
   # plugins.
   grunt.loadNpmTasks 'grunt-simple-mocha'
   grunt.loadNpmTasks 'grunt-coffeelint'
-  grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-clean'
-  grunt.loadNpmTasks 'grunt-contrib-copy'
-  grunt.loadNpmTasks 'grunt-contrib-jshint'
   grunt.loadNpmTasks 'grunt-contrib-watch'

   # tasks.
   grunt.registerTask 'compile', [
     'coffeelint'
-    'jshint'
-    'copy'
-    'coffee'
   ]

-  grunt.registerTask 'default', [
-    'compile'
-    'simplemocha'
-  ]
+  grunt.registerTask 'test', [ 'simplemocha' ]
+  grunt.registerTask 'default', ['compile', 'test']


fix gruntfile

fix gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,7 +15,7 @@
       main:
         files: [
             expand: true
-            cwd: 'src/'
+            cwd: 'scripts/'
             src: ['**/*.js']
             dest: 'out/'
         ]


Not including vendor css.

Not including vendor css.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,8 +71,6 @@
       vendorCss:
         dest: "example/css/vendor.css"
         src: [
-          "vendor/bower/*.css"
-          "vendor/bower/**/*.css"
           "tmp/vendor.css"
         ]


Fix indentation.

Fix indentation.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,7 +62,7 @@

     coveralls:
       upload:
-          src: 'coverage/coverage.lcov'
+        src: 'coverage/coverage.lcov'

   grunt.registerTask 'uploadCoverage', ->
     grunt.task.run 'coveralls:upload'


Build coffee files in source subdirs

Build coffee files in source subdirs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,7 +6,7 @@
       glob_to_multiple:
         expand: true
         cwd: 'src'
-        src: ['*.coffee']
+        src: ['**/*.coffee']
         dest: 'lib'
         ext: '.js'


run tests when making changes, and atBegin

@jrubenoff: whenever a file change is made, the tests will run.
hopefully this will make it easier to see when something breaks.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -132,7 +132,9 @@
           '<%= testFolder %>/support/fixtures/*.js',
           'fixtures/*.json'
         ]
-        tasks: 'default'
+        tasks: ['default', 'test']
+        options:
+          atBegin: true
       test:
         files: ['<%= testFolder %>/**/*_test.{coffee,js}']
         tasks: 'test'


More cleanup of the Gruntfile

More cleanup of the Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,7 +12,6 @@

   jasmine = './node_modules/jasmine-focused/bin/jasmine-focused'
   lessc = './node_modules/less/bin/lessc'
-  theme = 'themes/default'

   grunt.initConfig
     pkg: grunt.file.readJSON('package.json')


add IE9 to crossbrowser suite. Because why the hell not!

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -132,6 +132,9 @@
           ,
             browserName: 'internet explorer'
             version: '10'
+          ,
+            browserName: 'internet explorer'
+            version: '9'
           ]
           build: process.env.TRAVIS_JOB_ID
           testname: 'GSS browser tests'


temporaririly remove karma from default grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -155,7 +155,7 @@
   , 'requirejs', 'copy:dev', 'clean:temp']

   grunt.registerTask 'default'
-  , ['build', 'mocha:unit', 'karma:singleUnit', 'clean:bin']
+  , ['build', 'mocha:unit', 'clean:bin']

   grunt.registerTask 'travis'
   , ['build', 'mocha:travis', 'karma:travis' ]


handle exception in parsing retrieved json from aws

--- services/track-changes/app/coffee/MongoAWS.coffee
+++ services/track-changes/app/coffee/MongoAWS.coffee
@@ -74,8 +74,11 @@

 		inputStream.on 'data', (line) ->
 				if line.length > 2
-					ops.push(JSON.parse(line))
-					sz += line.length
+					try
+						ops.push(JSON.parse(line))
+					catch err
+						return callback(err)
+					sz += line.length
 				if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE
 					inputStream.pause()
 					MongoAWS.handleBulk ops.slice(0), sz, () ->


JS: further clean-up

JS: further clean-up

--- app/assets/javascripts/lib/models/setting.coffee
+++ app/assets/javascripts/lib/models/setting.coffee
@@ -15,13 +15,10 @@
     # Old scenarios may provide "null" instead of true/false, and this causes
     # the FCE warning to toggle on.
     use_fce = false unless use_fce is true
-
-    # Status change may be triggered by things other than the checkbox.
     $('#settings_use_fce').attr('checked', use_fce)
-
     $('.fce_notice').toggle(use_fce)

-    App.settings.save use_fce: use_fce
+    @save use_fce: use_fce
     App.call_api()

     # update dashboard item text
@@ -32,9 +29,8 @@
         else
           I18n.t 'constraints.co2_reduction.label' )

-  toggle_peak_load_tracking: ->
-    App.settings.save
-      track_peak_load: $("#track_peak_load_settings").is(':checked')
+  toggle_peak_load_tracking: =>
+    @save track_peak_load: $("#track_peak_load_settings").is(':checked')

   # Used by the bio-footprint dashboard item
   country: => @get('area_code').split('-')[0]