Fix: don't use current_entity when undefined

refs tentd#44

--- assets/javascripts/helpers/url.js.coffee
+++ assets/javascripts/helpers/url.js.coffee
@@ -14,7 +14,7 @@
     return unless entity and post_id
     if TentStatus.Helpers.isEntityOnTentHostDomain(entity)
       "#{entity}/posts/#{post_id}"
-    else if TentStatus.Helpers.isEntityOnTentHostDomain(TentStatus.config.current_entity)
+    else if TentStatus.config.current_entity && TentStatus.Helpers.isEntityOnTentHostDomain(TentStatus.config.current_entity)
       "#{TentStatus.config.current_entity.toStringWithoutSchemePort()}/posts/#{encodeURIComponent entity}/#{post_id}"
     else
       "/posts/#{encodeURIComponent entity}/#{post_id}"


Fix mis-ordered tags

Fix mis-ordered tags

--- assets/javascripts/mailcatcher.js.coffee
+++ assets/javascripts/mailcatcher.js.coffee
@@ -291,7 +291,7 @@
         text = text.replace(/>/g, ">")
         text = text.replace(/\n/g, "
")
         text = text.replace(/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?)/g, """$1""")
-        message_iframe.find("html").html("#{text}")
+        message_iframe.find("html").html("#{text}")

   refresh: ->
     $.getJSON "messages", (messages) =>


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -47,7 +47,7 @@
   Collision.collide player, [enemy, enemy2], (p, e) ->
     ok(true)

-test "Collision.rayRectangle with both types of", ->
+test "Collision.rayRectangle with both types of bounds", ->
   rect = GameObject
     x: 25
     y: 0


Test: Added an additional test around responseTime

--- test/timing.coffee
+++ test/timing.coffee
@@ -7,6 +7,10 @@
     $.fauxjax
       url: "/faux-regular"
       responseTime: 50
+
+    $.fauxjax
+      url: "/faux-fase"
+      responseTime: 0

   teardown: ->
     $.fauxjax.clear()
@@ -54,6 +58,16 @@
       ok(actualDelay >= 150, "Fauxjax response time was set to 150 the acutal delay was: #{actualDelay}")
       start()

+asyncTest "Fauxjax response time is correctly simulated fast", ->
+  startTime = new Date()
+  $.ajax
+    type: "GET"
+    url: "/faux-fast"
+    complete: (xhr, textStatus) ->
+      actualDelay = ((new Date()) - startTime)
+      ok(actualDelay < 9, "Fauxjax response time was set to 0 the acutal delay was: #{actualDelay}")
+      start()
+
 asyncTest "Forcing timeout", ->
   $.fauxjax
     type: "GET"


no longer need cleanLines

no longer need cleanLines

--- test/wd/e2e.coffee
+++ test/wd/e2e.coffee
@@ -1,9 +1,3 @@
-cleanLines = (html) ->
-  html = html.replace(/\ class\="ql-line"/g, '')
-  html = html.replace(/\ id\="ql-line-\d+"/g, '')
-  return html
-
-
 describe('Editing text', ->
   browser.get('/test/fixtures/e2e.html')
   startRange = element(By.id('start-range'))
@@ -34,7 +28,7 @@
     text = 'The Whale'
     editor.sendKeys(text)
     updateEditor()
-    expect(editor.getInnerHtml().then(cleanLines)).toEqual("
#{text}
")
+    expect(editor.getInnerHtml()).toEqual("
#{text}
")
     expectedDelta = {
       ops: [{ insert: text }]
     }
@@ -66,7 +60,7 @@
     editor.sendKeys(protractor.Key.RETURN)
     expectedDelta = { ops: [{ retain: 31 }, { insert: '\n' }] }
     expect(deltaOutput.getText()).toEqual(JSON.stringify(expectedDelta))
-    expect(editor.getInnerHtml().then(cleanLines)).toEqual([
+    expect(editor.getInnerHtml()).toEqual([
       '
The Whale
'
       '

'
       "
#{text}
"
@@ -81,7 +75,7 @@
     editor.sendKeys(protractor.Key.RETURN, protractor.Key.TAB, text1)
     editor.sendKeys(protractor.Key.RETURN, protractor.Key.RETURN, text2)
     updateEditor()
-    expect(editor.getInnerHtml().then(cleanLines)).toEqual([
+    expect(editor.getInnerHtml()).toEqual([
       '
The Whale
'
       '

'
       '
Chapter 1. Loomings.
'
@@ -119,7 +113,7 @@
     )
     updateEditor()
     firstLine = element.all(By.css('.ql-editor div')).first()
-    expect(firstLine.getOuterHtml().then(cleanLines)).toEqual('
Whale
')
+    expect(firstLine.getOuterHtml()).toEqual('
Whale
')
     browser.call( -> )
   )

@@ -129,8 +123,8 @@
     )
     updateEditor()
     lines = element.all(By.css('.ql-editor div'))
-    expect(lines.get(0).getOuterHtml().then(cleanLines)).toEqual('

')
-    expect(lines.get(1).getOuterHtml().then(cleanLines)).toEqual('

')
+    expect(lines.get(0).getOuterHtml()).toEqual('

')
+    expect(lines.get(1).getOuterHtml()).toEqual('

')
     browser.call( -> )
   )

@@ -138,8 +132,8 @@
     editor.sendKeys(protractor.Key.DELETE)
     updateEditor()
     lines = element.all(By.css('.ql-editor div'))
-    expect(lines.get(0).getOuterHtml().then(cleanLines)).toEqual('

')
-    expect(lines.get(1).getOuterHtml().then(cleanLines)).toEqual('
Chapter 1. Loomings.
')
+    expect(lines.get(0).getOuterHtml()).toEqual('

')
+    expect(lines.get(1).getOuterHtml()).toEqual('
Chapter 1. Loomings.
')
     browser.call( -> )
   )

@@ -150,7 +144,7 @@
     editor.sendKeys(text)
     updateEditor()
     firstLine = element.all(By.css('.ql-editor div')).first()
-    expect(firstLine.getOuterHtml().then(cleanLines)).toEqual(
+    expect(firstLine.getOuterHtml()).toEqual(
       "
#{text}
"
     )
     expectedDelta = {
@@ -171,7 +165,7 @@
     editor.sendKeys(protractor.Key.chord(protractor.Key.META, 'b'))
     updateEditor()
     lines = element.all(By.css('.ql-editor div'))
-    expect(lines.get(1).getOuterHtml().then(cleanLines)).toEqual(
+    expect(lines.get(1).getOuterHtml()).toEqual(
       '
Chapter 1. Loomings.
'
     )
     expectedDelta = {
@@ -194,10 +188,10 @@
     element(By.cssContainingText('.ql-align option', 'Center')).click()
     updateEditor()
     lines = element.all(By.css('.ql-editor div'))
-    expect(lines.get(0).getOuterHtml().then(cleanLines)).toEqual(
+    expect(lines.get(0).getOuterHtml()).toEqual(
       '
Moby Dick
'
     )
-    expect(lines.get(1).getOuterHtml().then(cleanLines)).toEqual(
+    expect(lines.get(1).getOuterHtml()).toEqual(
       '
Chapter 1. Loomings.
'
     )
     expectedDelta = {


added tests for .match

added tests for .match

--- test/nwmatcher.coffee
+++ test/nwmatcher.coffee
@@ -28,6 +28,9 @@
 assertEqual s.length, 1
 assertEqual x.constructor.name, "Array"
 assertEqual x.length, 1
+assert matcher.match(c[0], '.c')
+assert matcher.match(c[1], '.c')
+assert !matcher.match(c[1], 'c')
 console.log "All tests passed."

 # vim: ft=coffee


Fix #134.

Fix #134.

--- templates/pane.coffee
+++ templates/pane.coffee
@@ -3,8 +3,8 @@
 class PaneView extends View
   focus: -> @input.focus()
   initialize: (@pane) =>
-    pane.setView @
-    this[0].appendChild(pane.inner);
+    @pane.setView @
+    this[0].appendChild(@pane.inner)

   detached: ->
     console.log "detached"


Add tests for repo.reset

Add tests for repo.reset


Added snippet to exeception output.

Added snippet to exeception output.

--- bin/tail.coffee
+++ bin/tail.coffee
@@ -90,7 +90,7 @@
                   json.unshift line
                 json.unshift line
                 json = json.join "\n"
-              if stderr.length >= 4 and stderr[0] is "" and location = /^(.*):(\d+)$/.exec(stderr[1]) and column = /^(\s*)\^$/.exec(stderr[3])
+              if stderr.length >= 4 and stderr[0] is "" and (location = /^(.*):(\d+)$/.exec(stderr[1])) and (column = /^(\s*)\^$/.exec(stderr[3]))
                 snippet =
                   file: location[1]
                   text: stderr[2]
@@ -120,6 +120,11 @@
               process.stdout.write "#{json}\n\n"
             if record.exception
               process.stdout.write "  Uncaught exception - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n\n"
+              if record.exception.location
+                location = record.exception.location
+                process.stdout.write "  #{location.file}:#{location.line}.\n\n"
+                process.stdout.write "  #{location.text}\n"
+                process.stdout.write "  #{new Array(location.column).join(" ")}^\n"
               process.stdout.write "#{record.exception.message.replace(/^(\s*\S.*)$/mg, "  $1")}\n\n"
               if record.exception.json
                 json = inspect(record.json, 1000).replace(/^(\s*\S.*)$/mg, "    $1")


Add editor-event spec

Add editor-event spec

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -1777,6 +1777,17 @@
       nextTick()
       expect(node.querySelector('.line').textContent).toBe "var quicksort "

+  describe "legacy editor compatibility", ->
+    it "triggers the screen-lines-changed event before the editor:display-update event", ->
+      editor.setSoftWrap(true)
+
+      callingOrder = []
+      editor.on 'screen-lines-changed', -> callingOrder.push 'screen-lines-changed'
+      wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated'
+      editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ")
+
+      expect(callingOrder).toEqual ['screen-lines-changed', 'editor:display-updated']
+
   buildMouseEvent = (type, properties...) ->
     properties = extend({bubbles: true, cancelable: true}, properties...)
     event = new MouseEvent(type, properties)


:arrow_upper_right::nail_care: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -8,7 +8,7 @@
 dependencies:
   "jquery-utils": "STRd6/jquery-utils:v0.1.2"
   runtime: "STRd6/runtime:v0.1.1"
-  "touch-canvas": "STRd6/touch-canvas:v0.2.0-alpha.20"
+  "touch-canvas": "STRd6/touch-canvas:v0.2.0"
   "commando": "STRd6/commando:v0.9.0"
 width: 480
 height: 320


Remove unused dependency.

Remove unused dependency.

--- server/api.coffee
+++ server/api.coffee
@@ -1,5 +1,4 @@
 request = require 'superagent'
-_ = require 'lodash'

 module.exports = (host='localhost', port=5000) ->
   (req, res, next) ->


Use .workspace rather than body in keymaps

--- keymaps/toggle-quotes.cson
+++ keymaps/toggle-quotes.cson
@@ -1,3 +1,2 @@
-# DOCUMENT: link to keymap documentation
-'body':
+'workspace':
   'ctrl-"': 'toggle-quotes:toggle'


removed test logging

removed test logging

--- client/coffeescript/templates/post_wrapper.coffee
+++ client/coffeescript/templates/post_wrapper.coffee
@@ -112,7 +112,6 @@

   start_carousel: (parent_post) ->
     Session.set('carousel_parent', parent_post)
-    console.log("before:", window.carousel_handle)
     Meteor.clearInterval(window.carousel_handle)
     window.carousel_handle = Meteor.setInterval( ->
       parent = Session.get('carousel_parent')
@@ -125,5 +124,4 @@
             idx = (i + 1) % all_replies.length
         Session.set("reply_#{parent._id}", all_replies[idx]._id)
     , 3000)
-    console.log("after:", window.carousel_handle)
 )


Prevents calling routeComplete more than once if a redirect is placed in the controller.

--- src/app.coffee
+++ src/app.coffee
@@ -225,7 +225,9 @@
                 container.html @template(name)
                 parentController = container.parent().controller() or @rootController
                 @createController element: container, parent: parentController, name: name
-                @trigger 'routeComplete'
+                unless parentController.routedPath is parentController.path
+                  @trigger 'routeComplete'
+                  parentController.routedPath = parentController.path
               @rootController.sync()
               window.scrollTo(0, 0)
               next(req, next) if subroutes


Ensure units always have services included.

--- src/app.coffee
+++ src/app.coffee
@@ -105,7 +105,7 @@
             else
                 unit.fetch
                     data:
-                        include: 'department,municipality'
+                        include: 'department,municipality,services'
                     success: =>
                         select(unit)


add function wrapper to have a try

add function wrapper to have a try

--- coffeescript/004-webworkers/multiplication.coffee
+++ coffeescript/004-webworkers/multiplication.coffee
@@ -3,16 +3,19 @@

   a demo webworker for mutiplication:
 ###
+( ->

-global = this
+    global = this

-define [
-  'underscore'
-], (_) ->
-    postMessage('before binding')
-    global.onmessage = (e) ->
-        postMessage('hello from worker!')
-        result = _.reduce(_.values(e.data), ((memo, elem) -> memo * elem), 1)
-        postMessage(result)
-        false
+    define [
+      'underscore'
+    ], (_) ->
+        postMessage('before binding')
+        global.onmessage = (e) ->
+            postMessage('hello from worker!')
+            result = _.reduce(_.values(e.data), ((memo, elem) -> memo * elem), 1)
+            postMessage(result)
+            false

+)()
+


Require spec files so Travis runs them

Require spec files so Travis runs them

--- test/all-tests.coffee
+++ test/all-tests.coffee
@@ -1,4 +1,2 @@
-{expect} = require 'chai'
-
-it 'should pass a stub test', ->
-  expect(true).to.be.true
+testsContext = require.context("./", true, /\.spec\.(cjsx|coffee)$/)
+testsContext.keys().forEach(testsContext)


fincm3.coffee: Updating steps to power device for each Fin version

Changelog-entry: Update steps to power device during flashing for Fin versions.
Connects-to: #386
Signed-off-by: Gareth Davies <2f9181a725196cd0a5e07c6a6e161d83fcc71157@balena.io>

--- fincm3.coffee
+++ fincm3.coffee
@@ -1,10 +1,10 @@
 deviceTypesCommon = require '@resin.io/device-types/common'
 { networkOptions, commonImg, instructions } = deviceTypesCommon

-FIN_DEBUG = "While not having the Fin board powered, connect your system to the board's DBG/PRG port via a micro-USB cable.."
-FIN_POWER = "Power on the Fin by attaching power to either the Barrel or the Phoenix connector."
+FIN_DEBUG = "While not having the Fin board powered, connect your system to the board's DBG/PRG port via a micro-USB cable. Note for the Fin v1.1, only power the Fin from the PRG port for flashing."
+FIN_POWER = "Only for the Fin v1.0, power on the Fin by attaching power to either the Barrel or the Phoenix connector."
 FIN_WRITE = "Write the OS to the internal MMC storage device. We recommend using Etcher."
-FIN_POWEROFF = "When flashing is complete, power off the board by detaching the power and unplug the DGB micro-USB cable."
+FIN_POWEROFF = "When flashing is complete, power off the board by detaching the power if connected, and unplug the DGB micro-USB cable."

 module.exports =
 	version: 1


Duh it's results.items

Duh it's results.items

--- src/bgg.coffee
+++ src/bgg.coffee
@@ -36,7 +36,7 @@
         console.log results
         if results.items.total > 0
           items = if results.items.total is 1
-            [items.item]
+            [results.items.item]
           else
             results.items.item.slice(0,4)
           for item in items


Attach the workspace to the DOM in specs.

--- spec/util.coffee
+++ spec/util.coffee
@@ -1,5 +1,8 @@
 module.exports =
   openPath: (path, callback) ->
+    workspaceElement = atom.views.getView(atom.workspace)
+    jasmine.attachToDOM(workspaceElement)
+
     waitsForPromise -> atom.workspace.open(path)

     runs ->


atom: fix ng-component snippet

atom: fix ng-component snippet

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -171,7 +171,7 @@
             controller: function() {
                 $0
             },
-            controllersAs: 'vm',
+            controllerAs: 'vm',
         };
         """


`binaryGet`

`binaryGet`

--- src/6.web.coffee
+++ src/6.web.coffee
@@ -114,6 +114,13 @@
         responseBodyType: "json"
     Object.assign(actualOptions, options)
     web.request(actualOptions)
+web.binaryGet = (url, options) ->
+    actualOptions =
+        method: "GET"
+        url: url
+        responseBodyType: "binary"
+    Object.assign(actualOptions, options)
+    web.request(actualOptions)
 web.post = (url, body, options) ->
     actualOptions =
         method: "POST"


Remove destroyAll()

Remove destroyAll()

--- packages/shrub-orm/index.coffee
+++ packages/shrub-orm/index.coffee
@@ -113,11 +113,6 @@

 				new @_model @_schema.cleanValues @_transformer.serialize values

-			# Destroy all instances of a model.
-			collection.destroyAll = ->
-				@find().then (models) ->
-					Promise.all model.destroy() for model in models
-
 			collection.attributes.toJSON ?= ->
 				O = @toObject()


coffee syntax for view destroy method

coffee syntax for view destroy method

--- lib/atom-clock-view.coffee
+++ lib/atom-clock-view.coffee
@@ -79,4 +79,4 @@
   destroy: =>
     @clearTicker()
     @subscriptions.dispose()
-    this.remove()
+    @remove()


bugfix: testing

bugfix: testing

 On branch master

	modified:   test/test-kill.coffee

--- test/test-kill.coffee
+++ test/test-kill.coffee
@@ -26,6 +26,15 @@

 it 'should resolve `true` if the process exists', (done) ->
     @expectCount(2)
+
+    gotData = no
+    gotExit = no
+    maybeDone = (ondata, onexit) ->
+        if ondata then gotData = yes
+        if onexit then gotExit = yes
+        if gotData and gotExit then done()
+        return
+
     cmd =
         command: 'node'
         args: [PATH.join(FIXTURES, 'server.js'), 8254]
@@ -36,11 +45,11 @@

         promise.fail(done).then (result) ->
             expect(result).toBe(true)
-            return
+            return maybeDone(true)
         return

     child.on 'exit', (code) ->
         expect(code).toBe(1)
-        return done()
+        return maybeDone(null, true)

     return


mo teams

mo teams

--- models/team.coffee
+++ models/team.coffee
@@ -43,7 +43,7 @@
 TeamSchema.static 'canRegister', (next) ->
   Team.count {}, (err, count) ->
     return next err if err
-    max = 310 + 1 # +1 because team fortnight labs doesn't count
+    max = 312 + 1 # +1 because team fortnight labs doesn't count
     next null, count < max, max - count

 TeamSchema.static 'uniqueName', (name, next) ->


bugfix

bugfix

--- models/team.coffee
+++ models/team.coffee
@@ -1,9 +1,9 @@
 Backbone ?= require '../lib/backbone-mongo'
 out = (exports ? this)

-class out.Teams extends Backbone.Model
+class out.Teams extends Backbone.Collection
   name: 'teams' # for mongo
-  url: -> @name
+  url: -> "/#{@name}"

 class out.Team extends Backbone.Model
   collection: out.Teams


Ajax character name validation, #517

Ajax character name validation, #517

--- browser.coffee
+++ browser.coffee
@@ -42,6 +42,30 @@
 		if confirm('Вы уверены?') == false
 			event.preventDefault()
 		return
+
+	# Common func for user and character name checking
+	nameStatusHelper = (formGroupSelector, feedbackSelector) ->
+		formGroup = $(formGroupSelector)
+		feedback = $(feedbackSelector)
+		(state, hint) ->
+			$(formGroup).removeClass 'has-error has-success'
+			$(feedback).removeClass 'glyphicon-ok glyphicon-remove glyphicon-refresh'
+			$(feedback).hide()
+			# state
+			if state is 'checking'
+				$(feedback).addClass 'glyphicon-refresh'
+				$(feedback).show()
+			if state is 'ok'
+				$(feedback).addClass 'glyphicon-ok'
+				$(feedback).show()
+				$(formGroup).addClass 'has-success'
+			if state is 'error'
+				$(feedback).addClass 'glyphicon-remove'
+				$(feedback).show()
+				$(formGroup).addClass 'has-error'
+			# hint
+			if not hint? then hint = ''
+			$(feedback).attr('title', hint)

 	# Registration page stuff

@@ -70,27 +94,8 @@
 		else
 			$(passwordFormGroup).removeClass 'has-success'
 			$(passwordFormGroup).addClass 'has-error'
-	# Username validation helper function
-	usernameStatus = (state, hint) ->
-		$(usernameFormGroup).removeClass 'has-error has-success'
-		$(usernameFeedback).removeClass 'glyphicon-ok glyphicon-remove glyphicon-refresh'
-		$(usernameFeedback).hide()
-		# state
-		if state is 'checking'
-			$(usernameFeedback).addClass 'glyphicon-refresh'
-			$(usernameFeedback).show()
-		if state is 'ok'
-			$(usernameFeedback).addClass 'glyphicon-ok'
-			$(usernameFeedback).show()
-			$(usernameFormGroup).addClass 'has-success'
-		if state is 'error'
-			$(usernameFeedback).addClass 'glyphicon-remove'
-			$(usernameFeedback).show()
-			$(usernameFormGroup).addClass 'has-error'
-		# hint
-		if not hint? then hint = ''
-		$(usernameFeedback).attr('title', hint)
 	# Username validation
+	usernameStatus = nameStatusHelper(usernameFormGroup, usernameFeedback)
 	$("body").on 'change', usernameField, ->
 		if validation.usernameIsValid($(usernameField).val())
 			usernameStatus 'checking', 'Проверяем, свободен ли логин'
@@ -104,3 +109,20 @@
 					usernameStatus 'ok', 'Логин свободен'
 		else
 			usernameStatus 'error', 'Логин неправильный'
+
+	# Character name validation
+	charnameStatus = nameStatusHelper('#character-name-form-group', '#character-name-feedback')
+	$("body").on 'change', '#character-name', ->
+		name = this.value
+		if validation.characterNameIsValid(name)
+			charnameStatus 'checking', 'Проверяем, свободно ли имя'
+			$.getJSON "/ajax/isCharacterNameBusy/#{encodeURIComponent(name)}", (data) ->
+				if data.name != name
+					charnameStatus 'empty'
+					return
+				if data.isCharacterNameBusy
+					charnameStatus 'error', 'Такое имя уже занято'
+				else
+					charnameStatus 'ok', 'Имя свободно'
+		else
+			charnameStatus 'error', 'Имя неправильное'


Float all requires to top of file

Float all requires to top of file

--- src/cli.coffee
+++ src/cli.coffee
@@ -1,3 +1,4 @@
+fs = require 'fs'
 path = require 'path'
 chalk = require 'chalk'
 argv = require('optimist').argv
@@ -5,7 +6,14 @@
 signin = require './signin'
 manifest = require './manifest'
 pkg = require '../package.json'
-fs = require 'fs'
+create = require('./create')
+preview = require('./preview')
+localIp = require('./local-ip')()
+upload = require('./upload')
+version = require('./version')
+codio = require('./codio')
+# Legacy
+compileIfLegacy = require('./compile').compileIfLegacy

 if argv.env then config.env argv.env

@@ -26,7 +34,6 @@
           console.log l

   create: (argv) ->
-    create = require('./create')
     name = argv._.shift()

     create name, argv, (err) ->
@@ -45,7 +52,6 @@
       console.log key, value

   preview: (argv) ->
-    preview = require('./preview')
     if argv._.length == 0
       dirs = [process.cwd()]
     else
@@ -56,7 +62,6 @@
     preview dirs, argv, (err, projects) ->
       if err then return logError err

-      localIp = require('./local-ip')()
       localIpString = if localIp then " or http://#{localIp}:#{argv.port}" else ""
       console.log chalk.green("\\\\\\  ///  versal #{pkg.version}")
       console.log chalk.yellow(" \\\\\\///   http://localhost:#{argv.port}#{localIpString}")
@@ -79,11 +84,9 @@
       if argv.verbose || argv.v then console.log sessionId

   upload: (argv) ->
-    upload = require('./upload')
     dir = argv._.shift() || process.cwd()

     # Legacy
-    compileIfLegacy = require('./compile').compileIfLegacy
     compileIfLegacy dir, (err) ->
       if err then console.error err
       else console.log chalk.green('compile legacy gadget, ok')
@@ -108,14 +111,12 @@
     versionArg = argv._.shift()
     unless versionArg then return commands.help(argv)

-    version = require('./version')
     version versionArg, (err, oldVersion, newVersion) ->
       if err then return logError err
       message = "version bumped from v#{oldVersion} to v#{newVersion}"
       console.log chalk.green message

   codio: (argv) ->
-    codio = require('./codio')
     codio (err) ->
       if err then return logError err
       console.log chalk.green '.codio created'


add the rest of the html global attributes

--- lib/completions-jsx.coffee
+++ lib/completions-jsx.coffee
@@ -77,6 +77,18 @@
 	{ name: 'data-'}
 	{ name: 'aria-'}
 	{ name: 'style'}
+        { name: 'accesskey'}
+        { name: 'contenteditable'}
+        { name: 'contextmenu'}
+        { name: 'dir'}
+        { name: 'draggable'}
+        { name: 'dropzone'}
+        { name: 'hidden'}
+        { name: 'lang'}
+        { name: 'spellcheck'}
+        { name: 'tabindex'}
+        { name: 'title'}
+        { name: 'translate'}
 ]

 htmlElements: [


simple examples and more complex integratione example (which fails)

--- spec/javascripts/group_spec.coffee
+++ spec/javascripts/group_spec.coffee
@@ -1,2 +1,100 @@
 describe 'groups', ->
-
+
+  describe 'simple examples', ->
+    it "correctly gets the liberties of a single stone", ->
+      string =  """
+                ---
+                -X-
+                ---
+                """
+      board = board_from_string string
+      stone = get_stone(1, 1, board)
+      group = stone.group
+      expect(group.libertyCount).toEqual 4
+      expect(group.stones[0]).toBe stone
+
+    it "correctly gets the liberties of 2 stones", ->
+      string =  """
+                ----
+                -XX-
+                ----
+                ----
+                """
+      board = board_from_string string
+      stone = get_stone(1, 1, board)
+      group = stone.group
+      expect(group.libertyCount).toEqual 6
+      expect(group.stones.length).toEqual 2
+
+    it "correctly gets the liberties on the edge", ->
+      string =  """
+                ---
+                X--
+                ---
+                """
+      board = board_from_string string
+      stone = get_stone(0, 1, board)
+      group = stone.group
+      expect(group.libertyCount).toEqual 3
+      expect(group.stones[0]).toBe stone
+
+    it "correctly gets the liberties on the edge", ->
+      string =  """
+                X--
+                ---
+                ---
+                """
+      board = board_from_string string
+      stone = get_stone(0, 0, board)
+      group = stone.group
+      expect(group.libertyCount).toEqual 2
+      expect(group.stones[0]).toBe stone
+
+  describe "first integration test board", ->
+    board = null
+
+    createFirstTestBoard = ->
+      testBoard = """
+                  OOOO-
+                  OXXX-
+                  OOOO-
+                  -X---
+                  O--XX
+                  """
+      board_from_string(testBoard)
+
+    beforeEach -> board = createFirstTestBoard()
+
+    it "correctly handles the big white group on the top", ->
+      whiteStone = get_stone(0, 0, board)
+      group = whiteStone.group
+      expect(group.libertyCount).toEqual 5
+      expect(group.stones.length).toEqual 9
+      sameGroup = get_stone(3, 2, board).group
+      expect(sameGroup).toEqual group
+
+    it "correctly handles the larger black group on the top", ->
+      blackStone = get_stone(1, 1, board)
+      group = blackStone.group
+      expect(group.libertyCount).toEqual 1
+      expect(group.stones.length).toEqual 3
+      sameGroup = get_stone(3, 1, board).group
+      expect(sameGroup).toEqual group
+
+    it "correctly handles the one black stone", ->
+      blackStone = get_stone(1, 3, board)
+      group = blackStone.group
+      expect(group.libertyCount).toEqual 3
+      expect(group.stones.length).toEqual 1
+
+    it "correctly handles the bottom corner black stones", ->
+      blackStone = get_stone(4, 4, board)
+      group = blackStone.group
+      expect(group.libertyCount).toEqual 3
+      expect(group.stones.length).toEqual 2
+
+    it "correctly handles the one white corner stone", ->
+      whiteStone = get_stone(0, 4, board)
+      group = whiteStone.group
+      expect(group.libertyCount).toEqual 2
+      expect(group.stones.length).toEqual 1


:art:

:art:

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -1814,6 +1814,7 @@

               waitsFor "tree view selection to be updated", ->
                 treeView.element.querySelector('.file.selected')
+
               runs ->
                 expect(treeView.element.querySelector('.selected').textContent).toBe path.basename(newPath)
                 expect(callback).toHaveBeenCalledWith({path: newPath})


Show emoji icons in suggestion

Show emoji icons in suggestion

--- lib/emojis-provider.coffee
+++ lib/emojis-provider.coffee
@@ -1,11 +1,13 @@
 {Provider, Suggestion} = require 'autocomplete-plus'
 fuzzaldrin = require 'fuzzaldrin'
 emoji = require 'emoji-images'
+path = require 'path'

 module.exports =
 class EmojiProvider extends Provider
   wordRegex: /:[a-zA-Z0-9\.\/_\+-]*/g
   possibleWords: emoji.list
+  emojiFolder: path.join path.dirname(require.resolve 'emoji-images'), 'pngs'
   buildSuggestions: ->
     selection = @editor.getSelection()
     prefix = @prefixOfSelection selection
@@ -19,6 +21,7 @@
     words = fuzzaldrin.filter @possibleWords, prefix

     suggestions = for word in words when word isnt prefix
-      new Suggestion this, word: word, prefix: prefix
+      emojiImg = emoji word, @emojiFolder, 20
+      new Suggestion this, word: word, prefix: prefix, label: emojiImg, renderLabelAsHtml: true

     return suggestions


More ENOENT-catching

More ENOENT-catching

--- command.coffee
+++ command.coffee
@@ -94,7 +94,8 @@
     if stats.isDirectory()
       watchDir source, base if opts.watch
       fs.readdir source, (err, files) ->
-        throw err if err
+        throw err if err and err.code isnt 'ENOENT'
+        return if err?.code is 'ENOENT'
         files = files.map (file) -> path.join source, file
         index = sources.indexOf source
         sources[index..index] = files
@@ -103,7 +104,8 @@
     else if topLevel or path.extname(source) is '.coffee'
       watch source, base if opts.watch
       fs.readFile source, (err, code) ->
-        throw err if err
+        throw err if err and err.code isnt 'ENOENT'
+        return if err?.code is 'ENOENT'
         compileScript(source, code.toString(), base)
     else
       notSources[source] = yes


replaced mouseup for click #53

replaced mouseup for click #53

--- lib/go-outline-view.coffee
+++ lib/go-outline-view.coffee
@@ -487,7 +487,7 @@
       else
         d.name
     )
-    expanderIcon.on("mouseup", (d)=>
+    expanderIcon.on("click", (d)=>
       if @jumpToEntry(d)
         d3.event.stopPropagation()

@@ -535,7 +535,7 @@

     createChildren = (selection, recurse) ->
       item = selection.append('li')
-      item.on("mouseup", (d)->
+      item.on("click", (d)->
         d.expanded = !d.expanded
         d3.event.stopPropagation()
         updateExpanderIcon.apply(this, [d])


js state machine

js state machine

--- src/State.coffee
+++ src/State.coffee
@@ -170,9 +170,7 @@
   callbacks =
     before: []
     after: []
-
-  callbacks = CallbackCollection()
-
+
   machine_name = name

   internal_state = options && (if options.initial then options.initial else '')
@@ -205,10 +203,10 @@
       return event

     before_transition: (options, block) ->
-      callback = callbacks.add('before', options, this, block)
+      callback = callbacks["before"].push(Callback(options, self, block))

     after_transition: (options, block) ->
-      callback = callbacks.add('after', options, this, block)
+      callback = callbacks["after"].push(Callback(options, self, block))

     state: ->
       return internal_state


changed batch time for fetch archive

changed batch time for fetch archive

--- src/batch.coffee
+++ src/batch.coffee
@@ -55,7 +55,7 @@
 # batch to fetch json file from githubarchive and import to mongoDB
 archiveRule = new schedule.RecurrenceRule()
 archiveRule.hour = [new schedule.Range(0, 23)]
-archiveRule.minute = [10]
+archiveRule.minute = [5]

 schedule.scheduleJob archiveRule, ->
   datetime = getOneDayAgo()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp.coffee
+++ src/clamp.coffee
@@ -1,3 +1,28 @@
 Clamp = (I={}, self) ->
   Object.reverseMerge I,
-  +    clamps: {}
+
+      self.bind "update", ->
+    for name, cooldownOptions of I.cooldowns
+      {approachBy, target} = cooldownOptions
+
+      I[name] = I[name].approach(target, approachBy)
+
+  cooldown: (name, options={}) ->
+    {target, approachBy, value} = options
+
+    target ||= 0
+    approachBy = 1 unless approachBy?
+
+    # Set the cooldown data
+    I.cooldowns[name] = {
+      target
+      approachBy
+    }
+
+    if value?
+      # Use the value given if any
+      I[name] = options.value
+    else
+      # Initialize field if it doesn't exist
+      I[name] = 0 unless I[name]


automatically set autopoweron on the pool

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -317,6 +317,7 @@

     if auto_poweron
       yield xapi.call 'VM.add_to_other_config', ref, 'auto_poweron', 'true'
+      yield xapi.setPoolProperties({autoPowerOn: true})
     else
       yield xapi.call 'VM.remove_from_other_config', ref, 'auto_poweron'


Fix CD drive creation in vm.create().

Fix CD drive creation in vm.create().

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -140,13 +140,12 @@
       # Finds the VBD associated to the newly created VM which is a
       # CD.
       CD_drive = null
-      $forEach VM.VBDs, (ref) ->
+      for ref in VM.VBDs
         VBD = yield xapi.call 'VBD.get_record', ref
         # TODO: Checks it has been correctly retrieved.
         if VBD.type is 'CD'
           CD_drive = VBD.ref
-          return false
-        return
+          break

       # No CD drives have been found, creates one.
       unless CD_drive


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -440,18 +440,26 @@
       return this

     ###*
-    A new color. The calling color mixed with `other` using `amount` as the mixing ratio. If amount is not passed, then the colors are mixed evenly.
+    A new color. A copy of the calling color with its saturation increased by `amount`

     @name saturate
     @methodOf Color#
-    @param {Color} other the other color to mix
-    @param {Number} [amount] the mixing ratio of the calling color to `other`
-
-    @returns {Color} The modified calling color after mixing it with `other`
+    @param {Number} amount the amount to increase saturation by
+
+    @returns {Color} A copy of the calling color with its saturation increased by `amount`
     ###
     saturate: (amount) ->
       @copy().saturate$(amount)

+    ###*
+    The calling color with its saturation increased by `amount`
+
+    @name saturate$
+    @methodOf Color#
+    @param {Number} amount the amount to increase saturation by
+
+    @returns {Color} The calling color with its saturation increased by `amount`
+    ###
     saturate$: (amount) ->
       hsl = @toHsl()
       hsl[1] += amount


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -14,7 +14,7 @@
       bits[1]
       bits[2]
       bits[3]
-      bits[4] || 1
+      if bits[4]? then bits[4] else 1
     ]

   window.Color = (color) ->


fixed tests after merge

fixed tests after merge

--- platform/src/tests/tests.coffee
+++ platform/src/tests/tests.coffee
@@ -367,7 +367,7 @@
       # parameters: node, modelData, assets, actions, services, inputServiceData = null, outputServiceData = null
       modelPatches = GE.stepLoop(null, {}, {}, {}, services, null, outputServiceData)

-      expect(services.myService.establishData).toHaveBeenCalledWith(outputServiceData.myService)
+      expect(services.myService.establishData).toHaveBeenCalledWith(outputServiceData.myService, {})
       expect(modelPatches).toBeEmpty()

     it "send service input data to visitNode", ->
@@ -395,7 +395,7 @@
       # parameters: node, modelData, assets, actions, services, inputServiceData = null, outputServiceData = null
       modelPatches = GE.stepLoop(layout, {}, {}, actions, services, inputServiceData)

-      expect(services.myService.establishData).toHaveBeenCalledWith({ a: 2 })
+      expect(services.myService.establishData).toHaveBeenCalledWith({ a: 2 }, {})
       expect(modelPatches).toBeEmpty()

     it "gathers service input data, visits nodes, and gives output to services", ->
@@ -422,6 +422,6 @@
       # parameters: node, modelData, assets, actions, services, inputServiceData = null, outputServiceData = null
       modelPatches = GE.stepLoop(layout, {}, {}, actions, services)

-      expect(services.myService.provideData).toHaveBeenCalled()
-      expect(services.myService.establishData).toHaveBeenCalledWith({ a: 2 })
+      expect(services.myService.provideData).toHaveBeenCalledWith({})
+      expect(services.myService.establishData).toHaveBeenCalledWith({ a: 2 }, {})
       expect(modelPatches).toBeEmpty()


Fix skeleton clone checker.

Fix skeleton clone checker.

--- src/brunch.coffee
+++ src/brunch.coffee
@@ -51,7 +51,7 @@

   sysPath.exists rootPath, (exists) ->
     return logger.error "Directory '#{rootPath}' already exists" if exists
-    if /(https?|git):\/\//.test skeleton
+    if /(https?|git)(:\/\/|@)/.test skeleton
       cloneSkeleton skeleton, callback
     else
       copySkeleton skeleton, callback


Added damage/cost to bullets

Added damage/cost to bullets

--- src/bullet.coffee
+++ src/bullet.coffee
@@ -8,5 +8,8 @@
       y: vy
     @owner = id
     @speed = 100
+    @damage = 10
+    @cost = 5
+    @size = 1

 exports.Bullet = Bullet


Change working dir during bundling (Fixes #124)

--- src/bundle.coffee
+++ src/bundle.coffee
@@ -45,8 +45,14 @@
       bundlePath = path.join tmpdir, 'bundle.tar.gz'
       bundleOutput = fstream.Writer bundlePath

+      # Due to the way tar works we need to chdir
+      # to the directory specified on CLI
+      initialRoot = process.cwd()
+      gadgetRoot = path.resolve dir
+      process.chdir gadgetRoot
+
       reader = fstream.Reader({
-        path: dir
+        path: gadgetRoot
         type: 'directory'
         # `root` option is undocumented. It's used here to
         # indicate that we want to strip off the parent dir
@@ -57,6 +63,8 @@
         .pipe(bundleOutput)

       bundleOutput.on 'close', ->
+        # Switch back to the original project root
+        process.chdir initialRoot
         callback null, bundlePath

 createIgnoreFilter = (dir, callback) ->


Use BufferedProcess instead of BufferedNodeProcess

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -1,5 +1,5 @@
 _ = require 'underscore-plus'
-{BufferedNodeProcess} = require 'atom'
+{BufferedProcess} = require 'atom'
 {Emitter} = require 'emissary'
 Q = require 'q'
 semver = require 'semver'
@@ -23,7 +23,7 @@
       callback(code, outputLines.join('\n'), errorLines.join('\n'))

     args.push('--no-color')
-    new BufferedNodeProcess({command, args, stdout, stderr, exit})
+    new BufferedProcess({command, args, stdout, stderr, exit})

   loadFeatured: (callback) ->
     args = ['featured', '--json']


Modal fixes.

Modal fixes.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -1973,8 +1973,9 @@

       $(document).on 'hidden.bs.modal', () ->
         $("body").removeClass("modal-shown")
-        rgb = $("[data-theme=#{feedbin.data.theme}]").css("backgroundColor")
-        feedbin.setNativeTitleColor(rgb)
+        if feedbin.data && feedbin.data.theme
+          rgb = $("[data-theme=#{feedbin.data.theme}]").css("backgroundColor")
+          feedbin.setNativeTitleColor(rgb)

     scrollLeft: ->
       entries = $('.entries-column')


Add new area map

Add new area map

--- app/assets/javascripts/areas.js.coffee
+++ app/assets/javascripts/areas.js.coffee
@@ -43,6 +43,6 @@

 findAreaCoordinates = ->
   return window.areaCoordinates if window.areaCoordinates?
-  return JSON.parse($('#area_coordinates').val()) if $('#area_coordinates').length > 0
+  return JSON.parse($('#area_coordinates').val()) if $('#area_coordinates').length > 0 && $('#area_coordinates').val() != ''

   return false


replacestate detect

replacestate detect

--- app/assets/javascripts/building.coffee
+++ app/assets/javascripts/building.coffee
@@ -29,7 +29,7 @@
 		$("#map-about").hide()
 		$("#buttons").hide()
 		$("#tweet").hide()
-		history.replaceState("fixer","inspector","building")
+		history.replaceState?("fixer","inspector","building")
 		@map = L.mapbox.map('map', 'https://s3.amazonaws.com/maptiles.nypl.org/859-final/859spec.json',
 			zoomControl: false
 			scrollWheelZoom: false


Taught the project dictionary to add words to the dictionary.

--- lib/project-checker.coffee
+++ lib/project-checker.coffee
@@ -22,7 +22,11 @@
     if not project or not project.valid
       false
     true
-  providesAdding: (buffer) -> false
+  providesAdding: (buffer) ->
+    project = @getProject buffer
+    if not project or not project.valid
+      false
+    true

   check: (buffer, text) ->
     # If we don't have language settings, we don't do anything.
@@ -82,6 +86,30 @@
     for key in keys
       results.push s[key].text
     results
+
+  getAddingTargets: (buffer) ->
+    [
+      {sensitive: false, label: "Add to " + @getName() + " (case-insensitive)"},
+      {sensitive: true, label: "Add to " + @getName() + " (case-sensitive)"}
+    ]
+
+  add: (buffer, target) ->
+    # If we don't have language settings, we don't do anything.
+    project = @getProject buffer
+    if not project or not project.valid
+      return {}
+
+    # Build up the pattern we'll be using.
+    flag = "i"
+    if target.sensitive
+      flag = ""
+    pattern = "/" + target.word + "/" + flag
+
+    # Add the project and force it to reload.
+    if not project.json.ignoreWords
+      project.json.ignoreWords = []
+    project.json.ignoreWords.push pattern
+    @saveProject buffer, project

   setIgnores: (project) ->
     project.ignores = []
@@ -144,4 +172,18 @@
     # We have a `language.json`, so make sure it is loaded.
     console.log "Checking project", languagePath

+  saveProject: (buffer, project) ->
+    path = require "path"
+    fs = require "fs"
+
+    try
+      # Figure out the path and save the file. The file watcher will cause this
+      # to reload.
+      [projectPath, relativePath] = atom.project.relativizePath(buffer.file.path)
+      languagePath = path.join projectPath, "language.json"
+      jsonText = JSON.stringify project.json, null, "\t"
+      fs.writeFileSync languagePath, jsonText
+    catch
+      console.log "error"
+
 module.exports = ProjectChecker


remove log putput in modal.js

remove log putput in modal.js

--- app/assets/javascripts/modal.js.coffee
+++ app/assets/javascripts/modal.js.coffee
@@ -14,9 +14,7 @@

   @init= () ->
     $(document).on 'click', 'a[data-modal=true]', -> MoModal.load(this)
-    $(document).on 'ajax:beforeSend',"#{modal_holder_selector} form", (event, xhr, settings) ->
-      console.log 'ajax:beforeSend'
-      settings.data += "&modal=true"
+    $(document).on 'ajax:beforeSend',"#{modal_holder_selector} form", (event, xhr, settings) -> settings.data += "&modal=true"
     $(document).on 'ajax:success', "#{modal_holder_selector} form", handleAjaxSuccess

   @load= (anker)->
@@ -55,7 +53,6 @@
     $(document).trigger(type: 'modal:contentUpdated', target: target)

   handleAjaxSuccess= (event, data, status, xhr)->
-    console.log 'ajax:success'
     url = xhr.getResponseHeader('Location')
     response_type = (xhr.getResponseHeader("content-type") || "")


cleanup

cleanup

--- lib/provider/search.coffee
+++ lib/provider/search.coffee
@@ -60,8 +60,7 @@

   getItems: ->
     @options.projects ?= atom.project.getPaths()
-    regexp = @getRegExpForSearchTerm()#@options.search)
-    search = @search.bind(this, regexp)
+    search = @search.bind(this, @getRegExpForSearchTerm())
     Promise.all(@options.projects.map(search)).then (values) ->
       _.flatten(values)


highscore fix

highscore fix

--- app/client/highscore/highscores.coffee
+++ app/client/highscore/highscores.coffee
@@ -6,7 +6,7 @@
   highscores: ->
     Games.find(
       { score: { $gt: 0 } },
-      { state: { $eq: 'finished' } }
+      { state: { $eq: 'finished' } },
       { sort: { score: -1 } }
     ).fetch()


Fixed bug #765: More robust tags list on filters for user stories

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -350,7 +350,7 @@

         @scope.filters = {}

-        plainTags = _.flatten(_.map(@scope.userstories, "tags"))
+        plainTags = _.flatten(_.filter(_.map(@scope.userstories, "tags")))
         @scope.filters.tags = _.map _.countBy(plainTags), (v, k) =>
             obj = {
                 id: k,


Mark the automatic publication as ready

Mark the automatic publication as ready

This fixes a bug when both `3stack:presence` and `meteorhacks:fast-render` are added on the same application.

See https://github.com/kadirahq/fast-render/issues/146#issuecomment-152364747 for more details.

--- lib/server/presence.coffee
+++ lib/server/presence.coffee
@@ -47,6 +47,7 @@
       loginToken: hashedToken
       userId: @userId
       lastSeen: new Date()
+  @ready()
   return

 # allow the client to provide stateful information about itself


Some attempts to make the QD work. IMages now appear, but scales are wrong... #284

--- app/controllers/quick_dashboard.coffee
+++ app/controllers/quick_dashboard.coffee
@@ -20,15 +20,18 @@
     0:
       alpha: 0.09
       Q: 1.0
-      scales: [0.4, 0.6, 1.7]
+      # scales: [0.4, 0.6, 1.7]
+      scales: [1.7, 0.6, 0.4]
     1:
       alpha: 0.17
       Q: 1.0
-      scales: [0.4, 0.6, 1.7]
+      # scales: [0.4, 0.6, 1.7]
+      scales: [1.7, 0.6, 0.4]
     2:
       alpha: 0.11
-      Q: 2
-      scales: [0.4, 0.6, 2.5]
+      Q: 2.0
+      # scales: [0.4, 0.6, 2.5]
+      scales: [2.5, 0.6, 0.4]

     # VICS82 VISTA IR data, PROV = 'V':
     # 0:
@@ -179,7 +182,10 @@

   # NOTE: Using exposure time = 1.0
   getCalibration: (header) ->
-    zeroPoint = header.get('MZP_AB')
+    # CFHTLS:
+    zeroPoint = 30.0
+    # VICS82:
+    # zeroPoint = header.get('MZP_AB')
     return Math.pow(10, 0.4*(30.0 - zeroPoint))

   # VICS82:
@@ -268,13 +274,20 @@
     # console.log "preset, @parameters = ",preset,@parameters

     # CFHTLS:
-    @wfits.setCalibrations(1, 1, 1)
+    # @wfits.setCalibrations(1, 1, 1)
+    @wfits.setCalibrations(@calibrations['g'],@calibrations['r'],@calibrations['i'])

     # VICS82: not sure this is the best way to pass down the calibrations array but it works:
     # @wfits.setCalibrations(@calibrations['Ks'],@calibrations['J'],@calibrations['i'])
-    # @wfits.setScales.apply(@wfits, parameters.scales)
-    # @wfits.setAlpha(parameters.alpha)
-    # @wfits.setQ(parameters.Q)
+
+    @wfits.setScales.apply(@wfits, parameters.scales)
+    @wfits.setAlpha(parameters.alpha)
+    @wfits.setQ(parameters.Q)
+
+    # CFHTLS:
+    @wfits.drawColor('g', 'r', 'i')
+
+    # VICS82:
     # @wfits.drawColor('Ks', 'J', 'i')

   teardown: =>


Improved dump prefix

Improved dump prefix

--- src/esy.coffee
+++ src/esy.coffee
@@ -8,21 +8,18 @@
     @color = new Esy.color

   listProperties: (obj) ->
-    $.write obj.numProperties
+    @log "Esy.listProperties:"
     for property in [1..obj.numProperties]
       @log property

   log: (str) ->
     try $.write "#{str?.toString()}\n"

-  dump: (obj, prefix = true) ->
+  dump: (obj) ->
     if obj
-      $.write "\n Dumping: \n --- \n" if prefix
+      @log "Esy.dump:"
       for propertyName, propertyValue of obj
-        if typeof propertyValue is "object"
-          @dump propertyValue, false
-        else
-          @log "#{propertyName}: #{propertyValue?.toString()} \n"
+        @log "#{propertyName}: #{propertyValue?.toString()} \n"

 class Esy.file


Adding home directive tests

Adding home directive tests

--- app/modules/home/home.directive.coffee
+++ app/modules/home/home.directive.coffee
@@ -1,5 +1,6 @@
 HomeDirective = (homeService) ->
     link = (scope, el, attrs, ctrl) ->
+        console.log 111111
         scope.vm = {}
         taiga.defineImmutableProperty(scope.vm, "workInProgress", () -> homeService.workInProgress)

@@ -15,12 +16,14 @@
                 issues = workInProgress.get("watching").get("issues")
                 scope.vm.watching = userStories.concat(tasks).concat(issues)

-    directive = {
+    return {
         templateUrl: "home/home.html"
         scope: {}
         link: link
     }

-    return directive
+HomeDirective.$inject = [
+    "tgHomeService"
+]

-angular.module("taigaHome").directive("tgHome", ["tgHomeService", HomeDirective])
+angular.module("taigaHome").directive("tgHome", HomeDirective)


aliases for events

aliases for events

--- src/event.coffee
+++ src/event.coffee
@@ -2,9 +2,11 @@
     alight.hooks.attribute.unshift
         code: 'events'
         fn: ->
-            d = @.attrName.match /^\(([\w\.]+)\)$/
+            d = @.attrName.match /^\@([\w\.]+)$/
             if not d
-                return
+                d = @.attrName.match /^\(([\w\.]+)\)$/
+                if not d
+                    return

             @.ns = 'al'
             @.name = 'on'
@@ -12,17 +14,14 @@
             return

     alight.d.al.on =
+        alias: {}
         priority: 10
         init: (scope, element, expression, env) ->
             if not env.attrArgument
                 return
-            parts = env.attrArgument.split '.'
-            event = parts[0]
-
-            handler = directives[event]
-            if not handler
-                handler = makeEvent event
-            handler(env.attrArgument, parts)(scope, element, expression, env)
+            event = env.attrArgument.split('.')[0]
+            handler = makeEvent env.attrArgument, directiveOption[event]
+            handler scope, element, expression, env

     keyCodes =
         enter: 13
@@ -42,91 +41,118 @@
         meta: (event) -> event.metaKey
         shift: (event) -> event.shiftKey

-    makeEvent = (event, option) ->
+    makeEvent = (attrArgument, option) ->
         option = option or {}
-        (code, args) ->
-            stop = option.stop or false
-            prevent = option.prevent or false
-            filtered = false
-            scan = true
-            filter = {}
-            filterExt = []
-            for k in args.slice(1)
-                if k is 'stop'
-                    stop = true
-                    continue
-                if k is 'prevent'
-                    prevent = true
-                    continue
-                if k is 'nostop'
-                    stop = false
-                    continue
-                if k is 'noprevent'
-                    prevent = false
-                    continue
-                if k is 'noscan'
-                    scan = false
-                    continue
-                if not option.filtered
-                    continue
+        args = attrArgument.split '.'
+        eventName = args[0]
+        stop = option.stop or false
+        prevent = option.prevent or false
+        filtered = false
+        scan = true
+        eventList = null
+        eventCondition = null

-                filtered = true
-                if keyModifiers[k]
-                    filterExt.push k
-                    continue
+        alias = alight.d.al.on.alias[eventName]
+        if alias
+            if typeof(alias) is 'string'
+                alias =
+                    event: alias.split /\s+/
+            else if Array.isArray alias
+                alias =
+                    event: alias
+            eventList = alias.event
+            eventCondition = alias.condition

-                if keyCodes[k]
-                    k = keyCodes[k]
-                filter[k] = true
+        filter = {}
+        filterExt = []
+        for k in args.slice(1)
+            if k is 'stop'
+                stop = true
+                continue
+            if k is 'prevent'
+                prevent = true
+                continue
+            if k is 'nostop'
+                stop = false
+                continue
+            if k is 'noprevent'
+                prevent = false
+                continue
+            if k is 'noscan'
+                scan = false
+                continue
+            if not option.filtered
+                continue

-            (scope, element, expression, env) ->
-                fn = scope.$compile expression,
-                    no_return: true
-                    input: ['$event']
+            filtered = true
+            if keyModifiers[k]
+                filterExt.push k
+                continue

-                handler = (e) ->
-                    if filtered
-                        if not filter[e.keyCode]
-                            return
-                        if filterExt.length
-                            for extraKey in filterExt
-                                if not keyModifiers[extraKey](e)
-                                    return
+            if keyCodes[k]
+                k = keyCodes[k]
+            filter[k] = true

-                    if prevent
-                        e.preventDefault()
-                    if stop
-                        e.stopPropagation()
-                    try
-                        fn scope, e
-                    catch e
-                        alight.exceptionHandler e, "Error in event: #{code} = #{expression}",
-                            attr: code
-                            exp: expression
-                            scope: scope
-                            element: element
-                    if scan
-                        scope.$scan()
-                    return
+        (scope, element, expression, env) ->
+            fn = scope.$compile expression,
+                no_return: true
+                input: ['$event']

-                element.addEventListener event, handler
-                scope.$watch '$destroy', ->
-                    element.removeEventListener event, handler
+            handler = (event) ->
+                if filtered
+                    if not filter[event.keyCode]
+                        return
+                    if filterExt.length
+                        for extraKey in filterExt
+                            if not keyModifiers[extraKey](event)
+                                return
+                if eventCondition
+                    if not eventCondition scope, event
+                        return
+
+                if prevent
+                    event.preventDefault()
+                if stop
+                    event.stopPropagation()
+                try
+                    fn scope, event
+                catch error
+                    alight.exceptionHandler error, "Error in event: #{attrArgument} = #{expression}",
+                        attr: attrArgument
+                        exp: expression
+                        scope: scope
+                        element: element
+                        event: event
+                if scan
+                    scope.$scan()
                 return

-    directives =
-        click: makeEvent 'click',
+            if eventList
+                for e in eventList
+                    element.addEventListener e, handler
+            else
+                element.addEventListener eventName, handler
+            scope.$watch '$destroy', ->
+                if eventList
+                    for e in eventList
+                        element.removeEventListener e, handler
+                else
+                    element.removeEventListener eventName, handler
+            return
+
+    directiveOption =
+        click:
             stop: true
             prevent: true
-        dblclick: makeEvent 'dblclick',
+        dblclick:
             stop: true
             prevent: true
-        submit: makeEvent 'submit',
+        submit:
             stop: true
             prevent: true
-        keyup: makeEvent 'keyup',
+        keyup:
             filtered: true
-        keypress: makeEvent 'keypress',
+        keypress:
             filtered: true
-        keydown: makeEvent 'keydown',
+        keydown:
             filtered: true


Deprecate old status bar APIs

Deprecate old status bar APIs

--- lib/status-bar-view.coffee
+++ lib/status-bar-view.coffee
@@ -1,4 +1,5 @@
 {$} = require 'space-pen'
+Grim = require 'grim'
 {Disposable} = require 'atom'
 Tile = require './tile'

@@ -77,34 +78,33 @@
   getRightTiles: ->
     @rightTiles

-  # Public: Append the view to the left side of the status bar.
+  # Deprecated
+
   appendLeft: (view) ->
+    Grim.deprecate("Use ::addLeftTile({item, priority}) instead.")
     $(@leftPanel).append(view)

-  # Public: Prepend the view to the left side of the status bar.
   prependLeft: (view) ->
+    Grim.deprecate("Use ::addLeftTile({item, priority}) instead.")
     $(@leftPanel).prepend(view)

-  # Public: Append the view to the right side of the status bar.
   appendRight: (view) ->
+    Grim.deprecate("Use ::addRightTile({item, priority}) instead.")
     $(@rightPanel).append(view)

-  # Public: Prepend the view to the right side of the status bar.
   prependRight: (view) ->
+    Grim.deprecate("Use ::addRightTile({item, priority}) instead.")
     $(@rightPanel).prepend(view)

-  # Public:
   getActiveBuffer: ->
     @buffer

-  # Public:
   getActiveItem: ->
     atom.workspace.getActivePaneItem()

   storeActiveBuffer: ->
     @buffer = @getActiveItem()?.getBuffer?()

-  # Public:
   subscribeToBuffer: (event, callback) ->
     @bufferSubscriptions.push([event, callback])
     @buffer.on(event, callback) if @buffer


Refactor activation

Refactor activation

--- lib/transient-emacs.coffee
+++ lib/transient-emacs.coffee
@@ -51,6 +51,10 @@
     @addTextEditorListener = atom.workspace.onDidAddTextEditor (event) ->
       addEditorEventListner(event.textEditor)

+    @addIsearchCommands()
+    @killring = new KillRing()
+
+  addIsearchCommands: ->
     searchKeybindings = {}
     isearchCommandMap = {}
     searchSelector = 'atom-workspace atom-text-editor.searching'
@@ -62,8 +66,6 @@
       isearchCommandMap[command] = ((s_)=>(=> @incrementSearch s_))(s)
     @isearchKeymaps = atom.keymaps.add 'emacs-iserch-keymap', searchKeybindings, 0
     @isearchCommands = atom.commands.add 'atom-text-editor', isearchCommandMap
-
-    @killring = new KillRing()

   deactivate: ->
     @commands?.dispose()


added minlength validation to encrypted fields

The default minlength is 8. Field will be undefined if validation fails.

--- lib/types/encrypted.coffee
+++ lib/types/encrypted.coffee
@@ -1,14 +1,23 @@
+_ = require 'lodash'
 mongoose = require 'mongoose'
 strategies = require '../encryption/strategies'

 class Encrypted extends mongoose.SchemaTypes.String
+    defaults:
+        minLength: 8
     method: (strategyKey) ->
         @set (val, self) ->
-            return '' if not val
+            options = _.extend {}, @defaults, self.options.encryptOptions
+            minLength = options.minLength or 1
+            val = if val then val?.trim() else val
+            return '' unless !!val
+            if val?.length < minLength
+                return @invalidate 'length', "Field must be at least #{minLength} characters."
+
             Strategy = strategies[strategyKey]
             throw new Error('unrecognised encryption method') if not Strategy?
-            strategy = new Strategy self.options.encryptOptions
+            strategy = new Strategy options
             strategy.encrypt val

 module.exports = (mongoose) ->


Changes from broken if to switch statement

--- src/ghe.coffee
+++ src/ghe.coffee
@@ -24,12 +24,10 @@
       msg.send "token isn't set."

     info_term = msg.match[2]
-    if info_term is "license"
-      ghe_license msg,auth,url
-    if info_term is "largerepo"
-      ghe_largerepo msg,auth,url
-    else
-      msg.send "I don't know about #{info_term}"
+    switch info_term
+      when "license" then ghe_license msg,token,url
+      when "largerepo" then ghe_largerepo msg,token,url
+      else msg.send "I don't know about #{info_term}"

 ghe_license = (msg, token, url) ->
     msg.http("#{url}/enterprise/settings/license")


Don't transition tooltip position when fading in closes #4

--- src/index.cjsx
+++ src/index.cjsx
@@ -82,18 +82,32 @@
             # Measure height/width of tooltip.
             tipWidth = tooltip[0][0].offsetWidth
             tipHeight = tooltip[0][0].offsetHeight
+            if tooltip[0][0].style.opacity < 0.5
+              fadingIn = true

             # Account for window scroll.
             scrollTop  = document.documentElement.scrollTop || document.body.scrollTop
             scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft

-            tooltip
-              .style("visibility", "visible")
-              .transition()
-              .duration(100)
-              .style("opacity", 1)
-              .style("left", coords.x + scrollLeft - tipWidth/2 + barWidth/2 - self.props.tipOffset[0]  + "px")
-              .style("top", coords.y + scrollTop - tipHeight - self.props.tipOffset[1] + "px")
+            if fadingIn
+              # When fading in, don't transition to new position as that doesn't
+              # feel natural.
+              tooltip
+                .style("visibility", "visible")
+                .style("left", coords.x + scrollLeft - tipWidth/2 + barWidth/2 - self.props.tipOffset[0]  + "px")
+                .style("top", coords.y + scrollTop - tipHeight - self.props.tipOffset[1] + "px")
+                .transition()
+                .duration(100)
+                .style("opacity", 1)
+            else
+              tooltip
+                .style("visibility", "visible")
+                .transition()
+                .duration(100)
+                .style("opacity", 1)
+                .style("left", coords.x + scrollLeft - tipWidth/2 + barWidth/2 - self.props.tipOffset[0]  + "px")
+                .style("top", coords.y + scrollTop - tipHeight - self.props.tipOffset[1] + "px")
+
         )
         .on("mouseout", (d) ->
           d3.select(@).attr("fill", self.props.fillColor)


Fixes c<?> keys that vim-mode broke.

Fixes c<?> keys that vim-mode broke.

- Fixes #8.

--- keymaps/jumpy.cson
+++ keymaps/jumpy.cson
@@ -10,7 +10,9 @@
 '.editor':
   'cmd-space': 'jumpy:toggle'

-'.editor.jumpy-specificity-1.jumpy-specificity-2.jumpy-jump-mode':
+
+'.editor.jumpy-specificity-1.jumpy-specificity-2.jumpy-jump-mode,
+.editor.jumpy-specificity-1.jumpy-specificity-2.jumpy-jump-mode.vim-mode:not(.insert-mode)':
   'space': 'jumpy:clear'
   'escape': 'jumpy:clear'
   'cmd-space': 'jumpy:clear'


Cleaned up the workspace utils module

Cleaned up the workspace utils module

--- lib/workspace-utils.coffee
+++ lib/workspace-utils.coffee
@@ -1,68 +1,57 @@
-{Point}          = require('atom')
-path             = require('path')
-
+{Point}          = require 'atom'
+path             = require 'path'
 languageRegistry = require('./language-registry').getInstance()

-# ------------------------------------------------------------------------------
-
 module.exports =
-
-  ## Constants -----------------------------------------------------------------
-
   DEFAULT_WHEN_TO_WRITE_FILE_HEADER: 'before saving the buffer'
   DEFAULT_CLEAR_TERMINAL_ON_EXECUTION: true
   FILE_HEADER_PATTERN: 'Language: <languageName>, Level: <levelName>'
-  FILE_HEADER_REG_EXP:  /Language:\s+(.+),\s+Level:\s+(.+)/
+  FILE_HEADER_REG_EXP: /Language:\s+(.+),\s+Level:\s+(.+)/

-  ## Writing and reading language information to and from text editors ---------
+  writeLanguageInformationFileHeader: (textEditor, language, level) ->
+    fileHeader = @FILE_HEADER_PATTERN
+    fileHeader = fileHeader.replace /<languageName>/, language.getName()
+    fileHeader = fileHeader.replace /<levelName>/, level.getName()

-  writeLanguageInformationFileHeader: (textEditor,language,level) ->
-    fileHeader = @FILE_HEADER_PATTERN
-    fileHeader = fileHeader.replace(/<languageName>/,language.getName())
-    fileHeader = fileHeader.replace(/<levelName>/,level.getName())
-    # TODO toggle lines comments from grammar?
     lineCommentPattern = language.getLineCommentPattern()
-    if lineCommentPattern and lineCommentPattern.indexOf('<comment>') > -1
-      fileHeader = lineCommentPattern.replace(/<comment>/,fileHeader)
-      textEditor.getBuffer().insert(new Point(0,0),"#{fileHeader}\n")
+    if lineCommentPattern && lineCommentPattern.includes('<comment>')
+      fileHeader = lineCommentPattern.replace /<comment>/, fileHeader
+      textEditor.getBuffer().insert new Point(0, 0), "#{fileHeader}\n"
+
+    return

   deleteLanguageInformationFileHeader: (textEditor) ->
     textBuffer = textEditor.getBuffer()
-    fileHeader = textBuffer.lineForRow(0)
-    if @FILE_HEADER_REG_EXP.exec(fileHeader)?
-      textBuffer.deleteRow(0)
+    fileHeader = textBuffer.lineForRow 0
+    if @FILE_HEADER_REG_EXP.exec fileHeader
+      textBuffer.deleteRow 0
+    return

   readLanguageInformationFromTextEditor: (textEditor) ->
-    result = @readLanguageInformationFromFileHeader(textEditor)
+    result = @readLanguageInformationFromFileHeader textEditor
     language = result?.language
     level = result?.level
-    language ?= @readLanguageFromFileExtension(textEditor)
-    language ?= languageRegistry.getLanguageForGrammar(textEditor.getGrammar())
-    if language?
-      return {language,level}
-    undefined
+    language ?= @readLanguageFromFileExtension textEditor
+    language ?= languageRegistry.getLanguageForGrammar textEditor.getGrammar()
+
+    if language
+      return {language, level}

   readLanguageInformationFromFileHeader: (textEditor) ->
-    fileHeader = textEditor.getBuffer().lineForRow(0)
-    if (match = @FILE_HEADER_REG_EXP.exec(fileHeader))?
+    fileHeader = textEditor.getBuffer().lineForRow 0
+    if match = @FILE_HEADER_REG_EXP.exec fileHeader
       languageName = match[1]
       levelName = match[2]
-      if (language = languageRegistry.getLanguageForName(languageName))?
+
+      if language = languageRegistry.getLanguageForName languageName
         result = {language}
-        if (level = language.getLevelForName(levelName))?
+        if level = language.getLevelForName levelName
           result.level = level
-        else
-          # TODO maybe show a notification here
-          console.log "Levels: level malformed in file header?"
-    result
+        return result

   readLanguageFromFileExtension: (textEditor) ->
-    if (filePath = textEditor.getBuffer().getPath())?
-      fileType = path.extname(filePath).substr(1)
-      results = languageRegistry.getLanguagesForFileType(fileType)
-      # TODO maybe show a notification for the >1 case
+    if filePath = textEditor.getPath()
+      fileType = path.extname(filePath).substr 1
+      results = languageRegistry.getLanguagesForFileType fileType
       if results.length >= 1
-        result = results[0]
-    result
-
-# ------------------------------------------------------------------------------
+        return results[0]


Hack in fix for apple keybindings.

Hack in fix for apple keybindings.

The real problem is that they keybinding match algorithm is wrong.

--- src/atom/keybindings/apple.coffee
+++ src/atom/keybindings/apple.coffee
@@ -5,9 +5,9 @@
   'meta-left': 'move-to-beginning-of-line'
   'alt-left': 'move-to-beginning-of-word'
   'alt-right': 'move-to-end-of-word'
-  'shift-meta-up': 'select-to-top'
-  'shift-meta-down': 'select-to-bottom'
-  'shift-meta-left': 'select-to-beginning-of-line'
-  'shift-meta-right': 'select-to-end-of-line'
-  'shift-alt-left': 'select-to-beginning-of-word'
-  'shift-alt-right': 'select-to-end-of-word'+  'meta-shift-up': 'select-to-top'
+  'meta-shift-down': 'select-to-bottom'
+  'meta-shift-left': 'select-to-beginning-of-line'
+  'meta-shift-right': 'select-to-end-of-line'
+  'alt-shift-left': 'select-to-beginning-of-word'
+  'alt-shift-right': 'select-to-end-of-word'


Simplify index view boilerplate.

Simplify index view boilerplate.

--- public/scripts/src/views/index.coffee
+++ public/scripts/src/views/index.coffee
@@ -1,38 +1,10 @@
 client.views.index = Backbone.View.extend(
   build: (data) ->
-    ['#page-index', [
-      ['.container-narrow', [
-        ['.masthead', [
-          ['h3 a.muted', {href: '/'}, 'Project Name']
-        ]]
-        ['hr']
-        ['.jumbotron', [
-          ['h1', 'This project is going to be so awesome!']
-          ['p.lead', 'Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.']
-        ]]
-        ['hr']
-        ['.row-fluid.marketing', [
-          ['.span6', [
-            ['h4', 'Subheading']
-            ['p', 'Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.']
-            ['h4', 'Subheading']
-            ['p', 'Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.']
-            ['h4', 'Subheading']
-            ['p', 'Maecenas sed diam eget risus varius blandit sit amet non magna.']
-          ]]
-          ['.span6', [
-            ['h4', 'Subheading']
-            ['p', 'Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.']
-            ['h4', 'Subheading']
-            ['p', 'Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.']
-            ['h4', 'Subheading']
-            ['p', 'Maecenas sed diam eget risus varius blandit sit amet non magna.']
-          ]]
-        ]]
-        ['hr']
-        ['.footer', [
-          ['p', { html: true }, '© Project Name']
-        ]]
+    [
+      ['section#top', [
+        ['header', [
+          ['h1.page-title', 'Awesome Project']
+          ['h2.page-subtitle', 'This project is going to be awesome!']
       ]]
-    ]]
+    ]
   )


Fixes #7 - karma does not want to exit

Fixes #7 - karma does not want to exit

--- src/index.coffee
+++ src/index.coffee
@@ -29,11 +29,12 @@

   kill: (done) ->
     # Close the IE window.
-    @vm.close().then =>
+    @vm.close().then(=>
       # Bail out and leave the motor running if it was previously.
       return done() if @wasRunning
       # Stop the vm (saving its state).
       @vm.stop().then => done()
+    ).catch(done)

   markCaptured: -> @captured = true
   isCaptured: -> @captured


* [bug] the TemplateEngines.get method can not get the proper engine.

--- src/index.coffee
+++ src/index.coffee
@@ -36,10 +36,10 @@
     result
   get: (aName, aOptions)->
     if aName
-      result = super aName, aOptions
+      result = Task.get.call @, aName, aOptions
       unless result
         aName += 'TemplateEngine'
-        result = super aName, aOptions
+        result = Task.get.call @, aName, aOptions
     result = @getDefault(aOptions) unless result
     result
   _executeSync: (aOptions)->


Make all tabs closable.

Make all tabs closable.

--- src/index.coffee
+++ src/index.coffee
@@ -233,6 +233,7 @@

     coffeeconsole = new CoffeeConsoleWidget()
     coffeeconsole.title.text = 'CoffeeScript REPL'
+    coffeeconsole.title.closable = true

     linkViewWidget = new LinkViewWidget(syncMaster)
     linkViewWidget.title.text = 'Link View'
@@ -252,6 +253,8 @@
         gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
         tabSize: 4
     })
+    cmTaskpaper.title.text = 'CodeMirror View'
+    cmTaskpaper.title.closable = true

     # Initialize CodeMirror document
     doc = cmTaskpaper.editor.doc
@@ -266,10 +269,13 @@
     spy.docView = docView
     spy.outlineView = outlineView

-    cmTaskpaper.title.text = 'CodeMirror View'
     panel.insertRight(cmTaskpaper)
     panel.insertBottom(coffeeconsole, cmTaskpaper)
     panel.insertLeft(linkViewWidget)
+
+    spy.ctp = spy.cmTaskpaper = cmTaskpaper
+    spy.cc = spy.coffeeconsole = coffeeconsole
+    spy.lvw = spy.cmTaskpaper = linkViewWidget

     panel.attach(document.body)


Template.hide/show: Extend to support call without key, hide/show entire @DOM.

--- src/base/Template/Template.coffee
+++ src/base/Template/Template.coffee
@@ -186,10 +186,16 @@
 		!!@map[key]

 	hide: (key) ->
-		CUI.dom.hideElement(@map[key])
+		if not key
+			CUI.dom.hideElement(@DOM)
+		else
+			CUI.dom.hideElement(@map[key])

 	show: (key) ->
-		CUI.dom.showElement(@map[key])
+		if not key
+			CUI.dom.showElement(@DOM)
+		else
+			CUI.dom.showElement(@map[key])

 	empty: (key) ->
 		CUI.util.assert(@map, "Template[#{@_name}].empty [#{@getUniqueId()}]", "Already destroyed", template: @, name: @_name)


Fix Gruntfile to avoid updating file timestamp.

--- app/templates/website/Gruntfile.coffee
+++ app/templates/website/Gruntfile.coffee
@@ -137,28 +137,28 @@
           logConcurrentOutput: true
           gruntPath: path.join __dirname, 'node_modules', 'grunt-cli', 'bin', 'grunt'

+    newer:
+      options:
+        override: (detail, include) ->
+          cwd = path.join __dirname, grunt.config("#{detail.task}.#{detail.target}.files.0.cwd")
+          baseFile = path.join __dirname, detail.path
+          content = fs.readFileSync baseFile, 'utf8'
+          compile = needsCompile cwd, baseFile, detail.time, content
+          include(compile)

-  searchWord = (word, file, callback) ->
-    fs.readdir file, (err, files) ->
-      return unless err is null
-      files.forEach (f) ->
+  needsCompile = (file, baseFile, time, content) ->
+    stat = fs.statSync file
+    if stat.isDirectory()
+      dirFiles = fs.readdirSync(file)
+      for f in dirFiles
         filepath = path.join file, f
-        stat = fs.statSync filepath
-        if stat.isDirectory()
-          searchWord word, filepath, callback
-        else
-          fs.readFile filepath, 'utf8', (err, data) ->
-            return unless err is null
-            if data.indexOf(word) > -1
-              callback filepath
-
-  grunt.event.on 'watch', (action, filepath, task) ->
-    return unless task in ['coffee', 'stylesheets', 'views']
-    cwd = path.join __dirname, grunt.config("watch.#{task}.cwd")
-    filename = path.basename filepath, path.extname(filepath)
-    searchWord filename, cwd, (file) ->
-      date = new Date()
-      fs.utimes file, date, date
+        return true if needsCompile filepath, baseFile, time, content
+    else
+      return false if file == baseFile || stat.mtime < time
+      filename = path.basename(file)
+      word = filename.substr(0, filename.lastIndexOf('.'))
+      return true if content.indexOf(word) > -1
+    return false

   grunt.registerTask 'compile:dev', [
     'copy'<% if (options.html == 'jade') { %>


new Date() → Date.now()

new Date() → Date.now()

--- lib/Default.coffee
+++ lib/Default.coffee
@@ -33,10 +33,10 @@

   outputCounter: ->
     return '' unless @timestamp
-    now = new Date
+    now = Date.now()
     diff = now - @timestamp
-    ++@counter if (diff > 1000)
-    @timestamp = new Date()
+    ++@counter if (diff >= 1000)
+    @timestamp = Date.now()
     " [#{@decorateCounter(@counter)}]"

   outputSeparator: (type) ->
@@ -57,11 +57,11 @@

     if @diff
       if @diff[type]
-        diff = humanizeMs(new Date() - @diff[type])
+        diff = humanizeMs(Date.now() - @diff[type])
         diff = " +#{diff}"
-        @diff[type] = new Date()
+        @diff[type] = Date.now()
       else
-        @diff[type] = new Date()
+        @diff[type] = Date.now()
         diff = " +0ms"

     messageType = @outputType type


Explicitly disable prompt().

Explicitly disable prompt().

--- renderer/lib/override.coffee
+++ renderer/lib/override.coffee
@@ -48,3 +48,7 @@
   dialog = remote.require 'dialog'
   buttons = ['OK', 'Cancel']
   not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}
+
+# But we do not support prompt().
+window.prompt = ->
+  throw new Error('prompt() is and will not be supported in atom-shell.')


Fix silly mistake.

Fix silly mistake.


Update# black magic, don't touch ...

Update# black magic, don't touch ...

--- coffeescripts/services/Staff.coffee
+++ coffeescripts/services/Staff.coffee
@@ -8,7 +8,7 @@

 App.factory "Staff", ["$resource", "$http", ($resource, $http) ->
   $http.defaults.useXDomain = true
-  return $resource "http://staff.sitcon.org/users/:username", {
+  return $resource "http://staff.sitcon.org/users/:username/ /", { # This url have some black magic, don't touch it...
   }, {
     get: {
       method: "GET"


Trigger on names of pokemon

Trigger on names of pokemon

--- scripts/pokebattle.coffee
+++ scripts/pokebattle.coffee
@@ -166,4 +166,11 @@
   robot.hear /.*(pokemon|grass|bush|cave|forest|wildf).*/i, (msg) ->
     poke = msg.random(pokemon)
     msg.send "A wild #{poke} appears!"
-    msg.send "http://serebii.net/Pkmn_Green/#{pad(pokemon.indexOf(poke) + 1, 3)}.gif"+    msg.send "http://serebii.net/Pkmn_Green/#{pad(pokemon.indexOf(poke) + 1, 3)}.gif"
+
+
+for poke of pokemon
+  module.exports = (robot) ->
+    robot.hear new RegExp(poke, 'i'), (msg) ->
+      msg.send "#{poke}, I choose you!"
+      msg.send "http://serebii.net/Pkmn_Green/#{pad(pokemon.indexOf(poke) + 1, 3)}.gif"


Fix indent

Fix indent

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

 # test task
 gulp.task 'test', ->
-   gulp.src(['scripts/*.coffee', 'test/*.coffee'])
+  gulp.src(['scripts/*.coffee', 'test/*.coffee'])
     .pipe mocha {reporter: 'spec'}

 gulp.task 'lint', ->
-   gulp.src(['scripts/*.coffee', 'test/*.coffee'])
-    .pipe coffeelint()
+  gulp.src(['scripts/*.coffee', 'test/*.coffee'])
+    .pipe coffeelint()
     .pipe coffeelint.reporter()

 # watch task


no debug outputs

no debug outputs

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -97,7 +97,6 @@
         return ''
       if links then file.contents = new Buffer data.replace /(^#{1,6}.+?$)/gmi, "$1\n#{links}\n"
       callback null, file
-      console.log "LINKS", links
     .pipe $.docco()
     .pipe $.gulp.dest _.doc


Add coffeelint in coffee task

Add coffeelint in coffee task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -9,6 +9,8 @@

 gulp.task 'coffee', ->
   gulp.src './src/*.coffee'
+    .pipe coffeelint()
+    .pipe coffeelint.reporter()
     .pipe coffee()
     .pipe gulp.dest './lib'


Fix typo

Fix typo

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -62,6 +62,7 @@
 gulp.task 'merge', ->
   current = branch()
   git.checkout 'master'
-  git.merge current, ((err) -> if (err) throw err)
+  git.merge current, (err) ->
+    throw err if err
   git.checkout current


gulp watches for all *.less files, but only compiles main.less, fixes #204

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -17,6 +17,7 @@
 paths =
   src :
     css : "app/assets/stylesheets/main.less"
+    css_watch : "app/assets/stylesheets/**/*.less"
     js : "app/assets/javascripts/**/*.{coffee,js}"
     version : "#{__dirname}/version"
   dest :
@@ -50,14 +51,16 @@

 makeStyles = (dest) ->
-  return eventStream.pipeline(
-    plumber()
-    less({}).on("error",
-      (err) -> util.log(util.colors.red("!!"), err.toString())
+  return gulp.src(paths.src.css)
+    .pipe(plumber())
+    .pipe(
+      less({}).on("error",
+        (err) -> util.log(util.colors.red("!!"), err.toString())
+      )
     )
-    gulp.dest(dest)
-    logger()
-  )
+    .pipe(gulp.dest(dest))
+    .pipe(logger())
+

 bumpVersion = (src) ->
   return src.on("data", (versionFile) ->
@@ -81,8 +84,7 @@
 )

 gulp.task("compile:styles", ->
-  return gulp.src(paths.src.css)
-    .pipe(makeStyles(paths.dest.css))
+  return makeStyles(paths.dest.css)
 )

 gulp.task("combine:scripts:production", ->
@@ -112,8 +114,9 @@
 )

 gulp.task("watch:styles", ->
-  return watch(glob : paths.src.css, name : "Style-Watcher")
-    .pipe(makeStyles(paths.dest.css))
+  return watch(glob : paths.src.css_watch, emitOnGlob : false, name : "Style-Watcher", ->
+    return makeStyles(paths.dest.css)
+  )
 )

 gulp.task("watch:version", ->
@@ -136,7 +139,7 @@

 gulp.task("debug:scripts", ["watch:scripts:development"])
-gulp.task("debug:styles", ["watch:styles"])
+gulp.task("debug:styles", ["compile:styles", "watch:styles"])
 gulp.task("debug:version", ["watch:version"])

 gulp.task("debug", (callback) ->


remove old coffee gulpfile

remove old coffee gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,161 +0,0 @@
-_ = require('lodash')
-fs = require('fs')
-glob = require('glob')
-gulp = require('gulp')
-lr = require('tiny-lr')
-Path = require('path')
-
-browserify = require('gulp-browserify')
-concat = require('gulp-concat')
-exec = require('gulp-exec')
-gutil = require('gulp-util')
-jshint = require('gulp-jshint')
-minifyCss = require('gulp-minify-css')
-mocha = require('gulp-mocha')
-plumber = require('gulp-plumber')
-refresh = require('gulp-livereload')
-rename = require('gulp-rename')
-uglify = require('gulp-uglify')
-stylus = require('gulp-stylus')
-
-server = lr()
-
-config = _.extend
-  port: 8080
-  lrport: 35729
-  env: 'development'
-, gutil.env
-
-path =
-  js:
-    bin: 'src/js/fs/usr/bin/*'
-    lib:
-      all: 'src/js/**/*.js'
-      entry: 'src/js/terminal.coffee'
-    fs:
-      all: 'src/js/fs/**/*.js'
-      entry: 'src/js/file-system.json'
-    commands:
-      all: 'src/js/commands/**/*.js'
-      entry: 'src/js/commands.js'
-    spec:
-      all: 'spec/js/**/*.js'
-  css:
-    all: 'src/css/**/*.styl'
-  build: 'build/'
-
-production = config.env is 'production'
-
-gulp.task 'jshint', ->
-  gulp.src(['Gulpfile.js', path.js.spec.all, path.js.lib.all])
-    .pipe(plumber())
-    .pipe(jshint())
-    .pipe(jshint.reporter('jshint-stylish'))
-
-gulp.task 'clean', ->
-  gulp.src([path.build, path.js.bin])
-    .pipe(plumber())
-    .pipe(exec('rm -r <%= file.path %>'))
-
-gulp.task 'commands', ['clean'], (cb)->
-  commands = fs.readdirSync('src/js/commands')
-    .filter((f) ->
-      f[0] != '.' && f.substr(-3) == '.js'
-    ).map (f) ->
-      exec('ln -fh ' +
-        __dirname +
-        '/src/js/commands/' +
-        f +
-        ' src/js/fs/usr/bin/' +
-        f.slice(0, -3)) if production
-
-      "require('./commands/#{f}');"
-
-  fs.writeFileSync(path.js.commands.entry, commands.join('\n'))
-
-  cb(null)
-
-gulp.task 'file-system', ['commands'], (cb) ->
-  _fs = {}
-  _ignore = [
-    '\\.DS_Store',
-    '.*\\.swp'
-  ]
-  root = 'src/js/fs'
-
-  readdir = (path, container) ->
-    files = fs.readdirSync(path)
-
-    files.forEach (file) ->
-      for i in _ignore
-        return if file.match(new RegExp('^' + i + '$'))
-
-      stat = fs.statSync(path + '/' + file)
-
-      if stat.isDirectory()
-        f = {}
-        readdir(path + '/' + file, f)
-        container[file] = f
-      else
-        container[file] = fs.readFileSync(path + '/' + file).toString()
-
-  readdir(root, _fs)
-
-  fs.writeFileSync(path.js.fs.entry, JSON.stringify(_fs, null, 2))
-
-  cb(null)
-
-gulp.task 'js', ['jshint', 'file-system'], () ->
-  gulp.src(path.js.lib.entry, read: false)
-    .pipe(plumber())
-    .pipe(browserify( debug: !production, extensions: ['.coffee'], transform: ['coffeeify'] ))
-    .on('prebundle', (bundle)->
-      bundle.require('./fs', expose: 'fs')
-      bundle.require('./terminal', expose: 'terminal')
-      bundle.require('./args-parser', expose: 'args-parser')
-      bundle.require('./command-manager', expose: 'command-manager')
-      bundle.require('./console', expose: 'console')
-      bundle.require('./file', expose: 'file')
-      glob.sync(path.js.commands.all).forEach (command)->
-        command = Path.basename(command, '.js')
-        bundle.require("./commands/#{command}", expose: "commands/#{command}")
-    )
-    .pipe(rename( 'terminal.js' ))
-    .pipe(gulp.dest(path.build))
-    .pipe(rename( suffix: '.min' ))
-    .pipe(uglify())
-    .pipe(gulp.dest(path.build))
-    .pipe(refresh(server))
-
-gulp.task 'css', () ->
-  gulp.src(path.css.all)
-    .pipe(plumber())
-    .pipe(stylus( urlFunc: ['inline-image'] ))
-    .pipe(concat('terminal.css'))
-    .pipe(gulp.dest(path.build))
-    .pipe(rename( suffix: '.min' ))
-    .pipe(minifyCss())
-    .pipe(gulp.dest(path.build))
-    .pipe(refresh(server))
-
-gulp.task 'build', ['js', 'css']
-
-gulp.task 'watch', ['build'], (cb) ->
-  server.listen config.lrport, (err) ->
-    console.log(err) if err
-
-  gulp.watch(path.css.all, ['css'])
-  gulp.watch(path.js.lib.all, ['js'])
-
-  cb(null)
-
-gulp.task 'spec', ['js'], ->
-  gulp
-    .src('spec/**/*-spec.js')
-    .pipe(mocha())
-
-gulp.task 'spec-live', ['spec'], ->
-  gulp.watch('src/js/**/*.js', ['js', 'spec'])
-  gulp.watch('spec/**/*.js', ['spec'])
-
-gulp.task 'default', ['watch']


shim for browserify.

shim for browserify.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,3 +1,4 @@
+args       = require('yargs').argv
 gulp       = require('gulp')
 sync       = require('gulp-sync')(gulp).sync
 notify     = require('gulp-notify')
@@ -9,6 +10,7 @@
 replace    = require('gulp-replace')
 browserify = require('gulp-browserify')
 rename     = require('gulp-rename')
+shim       = require('./package.json').shim ? {}

 paths = {
   sass:
@@ -66,6 +68,7 @@
   gulp.src(paths.coffee.start)
     .pipe(plumber(notify.onError('<%=error.message%>')))
     .pipe(browserify({
+      shim: shim
       transform: [
         'traceurify'
       ]


remove useless vars in gulp file

remove useless vars in gulp file

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -9,7 +9,7 @@
 coffee = require 'gulp-coffee'

 gulp.task 'clean', ->
-    gulp.src([destDir, "#{viewsDir}/*.html", tempDir]).pipe(clean())
+    gulp.src([destDir, "#{viewsDir}/*.html"]).pipe(clean())

 gulp.task 'html', ->
     gulp.src "#{sourceDir}/jade/**/*.jade"


fix(#32): only process changed files with gulp-newer

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -22,6 +22,7 @@
 markdown              = require 'gulp-markdown'
 minifyCss             = require 'gulp-minify-css'
 minifyHtml            = require 'gulp-minify-html'
+newer				  = require 'gulp-newer'
 ngAnnotate            = require 'gulp-ng-annotate'
 ngClassify            = require 'gulp-ng-classify'
 open                  = require 'gulp-open'
@@ -378,7 +379,7 @@

 # Clean working directories
 gulp.task 'clean:working', ->
-	sources = [TEMP_DIRECTORY, DIST_DIRECTORY].concat(if getBower and firstRun then [COMPONENTS_DIRECTORY, BOWER_FILE] else [])
+	sources = [DIST_DIRECTORY].concat(if firstRun then [TEMP_DIRECTORY] else []).concat(if getBower and firstRun then [COMPONENTS_DIRECTORY, BOWER_FILE] else [])

 	gulp
 		.src sources, {read: false}
@@ -410,28 +411,31 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe coffeeLint options.coffeeLint
+			.on 'error', onError
+
+			.pipe template templateOptions
+			.on 'error', onError
+
+			.pipe ngClassify ngClassifyOptions
+			.on 'error', onError
+
+	srcs.push src =
+		gulp
+			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

-			.pipe coffeeLint options.coffeeLint
-			.on 'error', onError
-
-			.pipe template templateOptions
-			.on 'error', onError
-
-			.pipe ngClassify ngClassifyOptions
-			.on 'error', onError
-
-	srcs.push src =
-		gulp
-			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
-			.on 'error', onError
-
-			.pipe gulp.dest TEMP_DIRECTORY
-			.on 'error', onError
-
-	es
-		.merge.apply @, srcs
+	es
+		.merge.apply @, srcs
+		.on 'error', onError
+
+		.pipe newer TEMP_DIRECTORY
+		.on 'error', onError
+
+		.pipe gulp.dest TEMP_DIRECTORY
 		.on 'error', onError

 		.pipe sourceMaps.init()
@@ -454,6 +458,9 @@
 	srcs.push src =
 		gulp
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
+			.on 'error', onError
+
+			.pipe newer TEMP_DIRECTORY
 			.on 'error', onError

 			.pipe gulp.dest TEMP_DIRECTORY
@@ -570,6 +577,9 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe newer TEMP_DIRECTORY
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

@@ -608,9 +618,6 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

-			.pipe gulp.dest TEMP_DIRECTORY
-			.on 'error', onError
-
 			.pipe template templateOptions
 			.on 'error', onError

@@ -621,6 +628,9 @@

 	es
 		.merge.apply @, srcs
+		.on 'error', onError
+
+		.pipe newer TEMP_DIRECTORY
 		.on 'error', onError

 		.pipe gulp.dest TEMP_DIRECTORY
@@ -683,6 +693,9 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe newer TEMP_DIRECTORY
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

@@ -692,9 +705,6 @@
 	srcs.push src =
 		gulp
 			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
-			.on 'error', onError
-
-			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

 	es
@@ -737,9 +747,6 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

-			.pipe gulp.dest TEMP_DIRECTORY
-			.on 'error', onError
-
 			.pipe jsHint options.jsHint
 			.on 'error', onError

@@ -756,6 +763,9 @@

 	es
 		.merge.apply @, srcs
+		.on 'error', onError
+
+		.pipe newer TEMP_DIRECTORY
 		.on 'error', onError

 		.pipe gulp.dest TEMP_DIRECTORY
@@ -814,6 +824,9 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe newer TEMP_DIRECTORY
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

@@ -823,6 +836,9 @@
 	srcs.push src =
 		gulp
 			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
+			.on 'error', onError
+
+			.pipe newer TEMP_DIRECTORY
 			.on 'error', onError

 			.pipe gulp.dest TEMP_DIRECTORY
@@ -848,22 +864,25 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe template templateOptions
+			.on 'error', onError
+
+	srcs.push src =
+		gulp
+			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

-			.pipe template templateOptions
-			.on 'error', onError
-
-	srcs.push src =
-		gulp
-			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
-			.on 'error', onError
-
-			.pipe gulp.dest TEMP_DIRECTORY
-			.on 'error', onError
-
-	es
-		.merge.apply @, srcs
+	es
+		.merge.apply @, srcs
+		.on 'error', onError
+
+		.pipe newer TEMP_DIRECTORY
+		.on 'error', onError
+
+		.pipe gulp.dest TEMP_DIRECTORY
 		.on 'error', onError

 		.pipe liveScript()
@@ -882,6 +901,9 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe newer TEMP_DIRECTORY
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

@@ -891,9 +913,6 @@
 	srcs.push src =
 		gulp
 			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
-			.on 'error', onError
-
-			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

 	es
@@ -1024,7 +1043,8 @@
 # Reload the app in the default browser
 gulp.task 'reload', ['build'], ->
 	browserSync.reload()
-
+	firstRun = false;
+
 # Compile Sass
 gulp.task 'sass', ['prepare'], ->
 	options =
@@ -1039,6 +1059,9 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe newer TEMP_DIRECTORY
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

@@ -1048,6 +1071,9 @@
 	srcs.push src =
 		gulp
 			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
+			.on 'error', onError
+
+			.pipe newer TEMP_DIRECTORY
 			.on 'error', onError

 			.pipe gulp.dest TEMP_DIRECTORY
@@ -1194,7 +1220,7 @@
 		.on 'error', onError

 # Compile templateCache
-gulp.task 'templateCache', ['haml', 'html', 'jade', 'markdown'], ->
+gulp.task 'templateCache', ['html'].concat(LANGUAGES.VIEWS), ->
 	options =
 		templateCache:
 			module: APP_NAME
@@ -1234,22 +1260,25 @@
 			.src sources, {cwd: SRC_DIRECTORY, nodir: true}
 			.on 'error', onError

+			.pipe template templateOptions
+			.on 'error', onError
+
+	srcs.push src =
+		gulp
+			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
+			.on 'error', onError
+
 			.pipe gulp.dest TEMP_DIRECTORY
 			.on 'error', onError

-			.pipe template templateOptions
-			.on 'error', onError
-
-	srcs.push src =
-		gulp
-			.src sources, {cwd: COMPONENTS_DIRECTORY, nodir: true}
-			.on 'error', onError
-
-			.pipe gulp.dest TEMP_DIRECTORY
-			.on 'error', onError
-
-	es
-		.merge.apply @, srcs
+	es
+		.merge.apply @, srcs
+		.on 'error', onError
+
+		.pipe newer TEMP_DIRECTORY
+		.on 'error', onError
+
+		.pipe gulp.dest TEMP_DIRECTORY
 		.on 'error', onError

 		.pipe typeScript()
@@ -1288,6 +1317,9 @@

 	sources = [].concat ("**/*#{extension}" for extension in extensions)

-	gulp
-		.watch sources, {cwd: SRC_DIRECTORY, maxListeners: 999}, tasks
-		.on 'error', onError+	watcher = gulp.watch sources, {cwd: SRC_DIRECTORY, maxListeners: 999}, tasks
+
+	watcher
+		.on 'change', (event) ->
+			firstRun = true if event.type is 'deleted'
+		.on 'error', onError


fixed Storage subscription.

fixed Storage subscription.

--- public/models/storage.coffee
+++ public/models/storage.coffee
@@ -35,7 +35,7 @@

   _subscribeToStorageEvents: ->
     $(window).on "storage", (event) =>
-      if _eventIsOurs(event) and not _eventIsFeatureDetection(event)
+      if @_eventIsOurs(event) and not @_eventIsFeatureDetection(event)
         @trigger "update", event

   _eventIsOurs: (event) ->


Have macUpload return full response in $response instead of just $data.result.data

--- src/directives/file_upload.coffee
+++ src/directives/file_upload.coffee
@@ -34,7 +34,7 @@
         $scope.$apply ->
           $status        = $data.jqXHR?.status
           args           = {$event, $data, $status}
-          args.$response = $data.result.data if action is "Success"
+          args.$response = $data.result if action is "Success"
           callbackFn $scope, args

     options =


fix routing on group page (vue)

fix routing on group page (vue)

--- vue/src/routes.coffee
+++ vue/src/routes.coffee
@@ -34,11 +34,11 @@
 Vue.use(Router)

 groupPageChildren = [
-  {path: '', component: GroupDiscussionsPanel},
-  {path: 'polls', component: GroupPollsPanel},
-  {path: 'members', component: GroupMembersPanel},
+  {path: 'polls', component: GroupPollsPanel}
+  {path: 'members', component: GroupMembersPanel}
   {path: 'subgroups', component: GroupSubgroupsPanel}
-  {path: 'files', component: GroupFilesPanel},
+  {path: 'files', component: GroupFilesPanel}
+  {path: ':stub?', component: GroupDiscussionsPanel}
 ]

 threadPageChildren = [
@@ -62,11 +62,6 @@
     {path: '/profile', component: ProfilePage},
     {path: '/contact', component: ContactPage},
     {path: '/email_preferences', component: EmailSettingsPage },
-    {path: '/d/new', component: StartDiscussionPage },
-    {path: '/d/:key', component: ThreadPage, children: threadPageChildren },
-    # {path: '/d/:key', component: ThreadPage },
-    # {path: '/d/:key/comment/:comment_id', component: ThreadPage},
-    # {path: '/d/:key/:stub/:sequence_id', component: ThreadPage },
     {path: '/p/new', component: StartPollPage},
     {path: '/p/new/:poll_type', component: StartPollPage},
     {path: '/p/:key/:stub?', component: PollPage},
@@ -76,11 +71,11 @@
     {path: '/apps/registered/:id', component: RegisteredAppPage},
     {path: '/apps/registered/:id/:stub', component: RegisteredAppPage},
     {path: '/slack/install', component: InstallSlackPage},
+    {path: '/d/new', component: StartDiscussionPage },
+    {path: '/d/:key', component: ThreadPage, children: threadPageChildren },
     {path: '/g/new', component: StartGroupPage},
     {path: '/g/:key/membership_requests', component: MembershipRequestsPage},
-    {path: '/g/:key/:tab?', component: GroupPage, children: groupPageChildren},
-    # key is actually handle
-    {path: '/:key/:tab', component: GroupPage, children: groupPageChildren},
+    {path: '/g/:key', component: GroupPage, children: groupPageChildren},
     {path: '/:key', component: GroupPage, children: groupPageChildren},
     {path: '/', redirect: '/dashboard' }
   ]


Rename scopes -> Scope selectors in config::set docs

--- src/config.coffee
+++ src/config.coffee
@@ -486,7 +486,7 @@
   # atom.config.get(['source.js'], 'editor.tabLength') # => 4
   # ```
   #
-  # * `scope` (optional) {String}. eg. '.source.ruby'
+  # * `scopeSelector` (optional) {String}. eg. '.source.ruby'
   # * `keyPath` The {String} name of the key.
   # * `value` The value of the setting. Passing `undefined` will revert the
   #   setting to the default value.
@@ -494,11 +494,11 @@
   # Returns a {Boolean}
   # * `true` if the value was set.
   # * `false` if the value was not able to be coerced to the type specified in the setting's schema.
-  set: (scope, keyPath, value) ->
+  set: (scopeSelector, keyPath, value) ->
     if arguments.length < 3
       value = keyPath
-      keyPath = scope
-      scope = undefined
+      keyPath = scopeSelector
+      scopeSelector = undefined

     unless value == undefined
       try
@@ -506,8 +506,8 @@
       catch e
         return false

-    if scope?
-      @setRawScopedValue(scope, keyPath, value)
+    if scopeSelector?
+      @setRawScopedValue(scopeSelector, keyPath, value)
     else
       @setRawValue(keyPath, value)