fix roster model

fix roster model

--- api/models/Roster.coffee
+++ api/models/Roster.coffee
@@ -36,7 +36,7 @@
 			if sails.services.jid.isMuc(@jid)
 				@group?.name
 			else
-				@user?.fullname()
+				@user?.fullname

 	afterUpdate: (updatedRecord, cb) ->
 		@publishUpdate updatedRecord.id, _.omit(updatedRecord, 'user', 'group')


Fix typo

Fix typo

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -32,7 +32,7 @@
     else if document.body.createTextRange
       textRange = document.body.createTextRange()
       textRange.moveToPoint(@getPointAtEndOfSelection()...)
-      if direction is "forwrad"
+      if direction is "forward"
         textRange.moveEnd(granularity, 1)
       else
         textRange.moveStart(granularity, -1)


Mounts the lightbox app

Mounts the lightbox app

--- apps/index.coffee
+++ apps/index.coffee
@@ -93,6 +93,10 @@
 app.use(require('../apps/block'))
 console.timeEnd('block')

+console.time('lightbox')
+app.use(require('../apps/lightbox'))
+console.timeEnd('lightbox')
+
 console.time('channel')
 app.use(require('../apps/channel'))
 console.timeEnd('channel')


adding abstract access flag to disas

adding abstract access flag to disas

--- src/disassembler.coffee
+++ src/disassembler.coffee
@@ -10,7 +10,7 @@
 @disassemble = (class_file) ->
   access_string = (access_flags) ->
     # TODO other flags
-    ordered_flags = [ 'public', 'protected', 'private', 'static' ]
+    ordered_flags = [ 'public', 'protected', 'private', 'static', 'abstract' ]
     privacy = (("#{flag} " if access_flags[flag]) for flag in ordered_flags).join ''

   source_file = _.find(class_file.attrs, (attr) -> attr.constructor.name == 'SourceFile')


Removing console.log.

Removing console.log.

--- src/sources/wms.coffee
+++ src/sources/wms.coffee
@@ -15,7 +15,6 @@
                     internalCallback(response)

     parseLayer: (url, layerName) ->
-        console.log "parsing layer " + layerName + " of " + url
         doc = d3.select(@responses[url].document)
         for e in doc.selectAll('Layer > Dimension[name="time"]')[0]
             if layerName == d3.select(e.parentNode).select('Name').text()


Add shim for 'row' property

Add shim for 'row' property

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -2133,6 +2133,9 @@
       break if row >= argument.buffer.getLineCount()
       argument.trailingContextLines.push(argument.buffer.lineForRow(row))

+  Object.defineProperty this.prototype, 'row',
+    get: -> @range.start.row
+
   Object.defineProperty this.prototype, 'lineText',
     get: -> @buffer.lineForRow(@range.start.row)


Move emissary require to deprecation block

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -3,7 +3,6 @@
 Serializable = require 'serializable'
 Delegator = require 'delegato'
 {includeDeprecatedAPIs, deprecate} = require 'grim'
-EmitterMixin = require('emissary').Emitter
 {CompositeDisposable, Emitter} = require 'event-kit'
 {Point, Range} = TextBuffer = require 'text-buffer'
 LanguageMode = require './language-mode'
@@ -3015,6 +3014,7 @@
     deprecate("Use TextEditor::isSoftWrapped instead")
     @displayBuffer.isSoftWrapped()

+  EmitterMixin = require('emissary').Emitter
   TextEditor::on = (eventName) ->
     switch eventName
       when 'title-changed'


Cause an assertion failure if defaultMarkerLayer is destroyed early

This is to investigate a case where the default marker layer of the
editor is destroyed without the editor itself or its buffer being
destroyed, which is causing `Cannot decorate a destroyed marker`
exceptions.

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -192,6 +192,9 @@

     @displayLayer.setTextDecorationLayer(@tokenizedBuffer)
     @defaultMarkerLayer = @displayLayer.addMarkerLayer()
+    @disposables.add(@defaultMarkerLayer.onDidDestroy =>
+      @assert(false, "defaultMarkerLayer destroyed at an unexpected time")
+    )
     @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true)
     @selectionsMarkerLayer.trackDestructionInOnDidCreateMarkerCallbacks = true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -20,7 +20,7 @@
 ###

 ###*
-The default draw method
+The default  draw method

 @name update
 @methodOf TextEffect#


change dir to hooks directory before SHing local hook

--- code/instance.coffee
+++ code/instance.coffee
@@ -120,7 +120,9 @@
       @sh "sh /root/#{hook.file} #{@name}", callback
     else if /^\d+_.+\.l\.\w/.test hook.file
       console.log "Running local #{hook.file}"
-      @_local_sh "#{@config_path}/#{hook.config_name}/hooks/#{hook.file}", [@name], callback
+      hooks_dir="#{@config_path}/#{hook.config_name}/hooks"
+      process.chdir hooks_dir
+      @_local_sh "#{hooks_dir}/#{hook.file}", [@name], callback
     else
       console.log "(Ignoring #{hook.file})"
       callback()


refactored JS bridge, all methods now use prototype via a cool little private data sharing technique I came up with.

--- coffee/gitteh.coffee
+++ coffee/gitteh.coffee
@@ -8,26 +8,20 @@
 {minOidLength, types, NativeRepository, NativeRemote} = bindings
 module.exports = Gitteh = {}

-oidRegex = /^[a-zA-Z0-9]{0,40}$/
-args.validators.oid = (val) ->
-	return false if typeof val isnt "string"
-	return false if not oidRegex.test val
-	return false if val.length < minOidLength
-	return true
-
-objectTypes = ["any", "blob", "commit", "tag", "tree"]
-args.validators.objectType = (val) ->
-	return objectTypes.indexOf val > -1
-
-remoteDirs = ["push", "fetch"]
-args.validators.remoteDir = (val) ->
-	return remoteDirs.indexOf val > -1
-
-checkOid = (str, allowLookup = true) ->
-	throw new TypeError "OID should be a string" if typeof str isnt "string"
-	throw new TypeError "Invalid OID" if not oidRegex.test str
-	throw new Error "OID is too short" if str.length < Gitteh.minOidLength
-	throw new TypeError "Invalid OID" if not allowLookup and str.length isnt 40
+getPrivate = (obj) ->
+	getPrivate.lock++
+	return obj._private
+getPrivate.lock = 0
+
+createPrivate = (obj) ->
+	_priv = {}
+	Object.defineProperty obj, "_private",
+		enumerable: false
+		configurable: false
+		get: ->
+			throw new Error "Bad request" if not getPrivate.lock--
+			return _priv
+	return _priv

 wrapCallback = (orig, cb) ->
 	return (err) ->
@@ -51,7 +45,28 @@
 			return o
 	}

-Signature = (obj) ->
+oidRegex = /^[a-zA-Z0-9]{0,40}$/
+args.validators.oid = (val) ->
+	return false if typeof val isnt "string"
+	return false if not oidRegex.test val
+	return false if val.length < minOidLength
+	return true
+
+objectTypes = ["any", "blob", "commit", "tag", "tree"]
+args.validators.objectType = (val) ->
+	return objectTypes.indexOf val > -1
+
+remoteDirs = ["push", "fetch"]
+args.validators.remoteDir = (val) ->
+	return remoteDirs.indexOf val > -1
+
+checkOid = (str, allowLookup = true) ->
+	throw new TypeError "OID should be a string" if typeof str isnt "string"
+	throw new TypeError "Invalid OID" if not oidRegex.test str
+	throw new Error "OID is too short" if str.length < bindings.minOidLength
+	throw new TypeError "Invalid OID" if not allowLookup and str.length isnt 40
+
+Gitteh.Signature = Signature = (obj) ->
 	immutable(@, obj)
 		.set("name")
 		.set("email")
@@ -60,26 +75,29 @@
 	return @

 Gitteh.Refspec = Refspec = (src, dst) ->
-	srcRoot = src
-	srcRoot = srcRoot[0...-1] if srcRoot? and srcRoot[-1..] is "*"
-	dstRoot = dst
-	dstRoot = dstRoot[0...-1] if dstRoot? and dstRoot[-1..] is "*"
+	_priv = createPrivate @
+
+	_priv.srcRoot = if src? and src[-1..] is "*" then src[0...-1] else src
+	_priv.dstRoot = if dst? and dst[-1..] is "*" then dst[0...-1] else dst
+
 	immutable(@, {src, dst})
 		.set("src")
 		.set("dst")
-	@matchesSrc = (ref) =>
-		return false if ref.length <= srcRoot.length
-		return ref.indexOf(srcRoot) is 0
-	@matchesDst = (ref) =>
-		return false if ref.length <= dstRoot.length
-		return ref.indexOf(dstRoot) is 0
-	@transformTo = (ref) =>
-		throw new Error "Ref doesn't match src." if not @matchesSrc ref
-		return "#{dst[0...-2]}#{ref[(src.length-2)..]}"
-	@transformFrom = (ref) =>
-		throw new Error "Ref doesn't match dst." if not @matchesDst ref
-		return "#{src[0...-2]}#{ref[(dst.length-2)..]}"
-	return @
+	return @
+Refspec.prototype.matchesSrc = (ref) ->
+	_priv = getPriv @
+	return false if ref.length <= _priv.srcRoot.length
+	return ref.indexOf(_priv.srcRoot) is 0
+Refspec.prototype.matchesDst = (ref) ->
+	_priv = getPriv @
+	return false if ref.length <= _priv.dstRoot.length
+	return ref.indexOf(_priv.dstRoot) is 0
+Refspec.prototype.transformTo = (ref) ->
+	throw new Error "Ref doesn't match src." if not @matchesSrc ref
+	return "#{@dst[0...-2]}#{ref[(@src.length-2)..]}"
+Refspec.prototype.transformFrom = (ref) ->
+	throw new Error "Ref doesn't match dst." if not @matchesDst ref
+	return "#{@src[0...-2]}#{ref[(@dst.length-2)..]}"

 Gitteh.Commit = Commit = (@repository, obj) ->
 	obj.author = new Signature obj.author
@@ -92,9 +110,9 @@
 		.set("messageEncoding")
 		.set("author")
 		.set("committer")
-	@tree = (cb) =>
-		@repository.tree obj.tree, cb
-	return @
+	return @
+Commit.prototype.tree = (cb) ->
+	@repository.tree @treeId, cb

 Gitteh.Tree = Tree = (@repository, obj) ->
 	obj._entries = obj.entries
@@ -126,19 +144,20 @@
 		.set("tagger")
 		.set("target", "targetId")
 		.set("type")
-	@target = (cb) =>
-		@repository.object @targetId, @type, cb
-	return @
-
-<<<<<<< HEAD
+	return @
+Tag.prototype.target = (cb) ->
+	@repository.object @targetId, @type, cb
+
 Gitteh.Remote = Remote = (@repository, nativeRemote) ->
+	_priv = createPrivate @
+	_priv.native = nativeRemote
+	_priv.connected = false
+
 	if nativeRemote not instanceof NativeRemote
 		throw new Error "Don't construct me, see Repository.remote()"

-	connected = false
-
 	Object.defineProperty @, "connected",
-		get: -> return connected
+		get: -> return _priv.connected
 		enumerable: true
 		configurable: false

@@ -151,58 +170,56 @@
 	immutable(@, {fetchSpec, pushSpec})
 		.set("fetchSpec")
 		.set("pushSpec")
-
-	@connect = =>
-		[dir, cb] = args
-			dir: type: "remoteDir"
-			cb: type: "function"
-		dir = if dir is "push" then Gitteh.GIT_DIR_PUSH else Gitteh.GIT_DIR_FETCH
-		nativeRemote.connect dir, wrapCallback cb, (refs) =>
-			refNames = Object.keys refs
-
-			# Determine symref for HEAD.
-			headOid = refs["HEAD"]
-			for ref, oid of refs
-				continue if ref is "HEAD"
-				if oid is headOid
-					headRef = fetchSpec.transformTo ref
-					immutable(@, {headRef}).set "headRef", "HEAD"
-					break
-
-			immutable(@, {refNames}).set "refNames", "refs"
-			connected = true
+	return @
+
+Remote.prototype.connect = ->
+	_priv = getPrivate @
+	[dir, cb] = args
+		dir: type: "remoteDir"
+		cb: type: "function"
+	dir = if dir is "push" then bindings.GIT_DIR_PUSH else bindings.GIT_DIR_FETCH
+	_priv.native.connect dir, wrapCallback cb, (refs) =>
+		refNames = Object.keys refs
+
+		# Determine symref for HEAD.
+		headOid = refs["HEAD"]
+		for ref, oid of refs
+			continue if ref is "HEAD"
+			if oid is headOid
+				headRef = fetchSpec.transformTo ref
+				immutable(@, {headRef}).set "headRef", "HEAD"
+				break
+
+		immutable(@, {refNames}).set "refNames", "refs"
+		_priv.connected = true
+		cb()
+Remote.prototype.fetch = ->
+	_priv = getPrivate @
+	throw new Error "Remote isn't connected." if not connected
+	[progressCb, cb] = args
+		progressCb: type: "function"
+		cb: type: "function"
+
+	updateTimer = null
+	update = =>
+		{bytes, total, done} = _priv.native.stats
+		progressCb bytes, total, done
+		updateTimer = setTimeout update, 500
+	setTimeout update, 500
+
+	_priv.native.download (err) =>
+		clearTimeout updateTimer
+		return cb err if err?
+		_priv.native.updateTips wrapCallback cb, =>
 			cb()
-	@fetch = =>
-		throw new Error "Remote isn't connected." if not connected
-		[progressCb, cb] = args
-			progressCb: type: "function"
-			cb: type: "function"
-
-		updateTimer = null
-		update = =>
-			{bytes, total, done} = nativeRemote.stats
-			progressCb bytes, total, done
-			updateTimer = setTimeout update, 500
-		setTimeout update, 500
-
-		nativeRemote.download (err) =>
-			clearTimeout updateTimer
-			return cb err if err?
-			nativeRemote.updateTips wrapCallback cb, =>
-				cb()
-
-	return @
-
-wrapCallback = (orig, cb) ->
-	return (err) ->
-		return orig err if err?
-		cb.apply null, Array.prototype.slice.call arguments, 1

 Gitteh.Index = Index = (nativeIndex) ->

 Gitteh.Repository = Repository = (nativeRepo) ->
 	if nativeRepo not instanceof NativeRepository
 		throw new Error "Don't construct me, see gitteh.(open|init)Repository"
+	_priv = createPrivate @
+	_priv.native = nativeRepo

 	immutable(@, nativeRepo)
 		.set("bare")
@@ -210,50 +227,55 @@
 		.set("workDir", "workingDirectory")
 		.set("remotes")
 		.set("references")
-	@exists = =>
-		[oid, cb] = args
-			oid: type: "oid"
-			cb: type: "function"
-		nativeRepo.exists oid, cb
-	@object = =>
-		[oid, type, cb] = args
-			oid: type: "oid"
-			type: type: "objectType", default: "any"
-			cb: type: "function"
-		nativeRepo.object oid, type, wrapCallback cb, (object) =>
-			clazz = switch object._type
-				when types.commit then Commit
-				when types.tree then Tree
-				when types.blob then Blob
-				when types.tag then Tag
-				else undefined
-			return cb new TypeError("Unexpected object type") if clazz is undefined
-			return cb null, new clazz @, object
-	@blob = (oid, cb) => @object oid, "blob", cb
-	@commit = (oid, cb) => @object oid, "commit", cb
-	@tag = (oid, cb) => @object oid, "tag", cb
-	@tree = (oid, cb) => @object oid, "tree", cb
-	@reference = =>
-		[name, resolve, cb] = args
-			name: type: "string"
-			resolve: type: "bool"
-			cb: type: "function"
-		nativeRepo.reference name, resolve, cb
-	@ref = @reference
-	@remote = =>
-		[name, cb] = args
-			name: type: "string"
-			cb: type: "function"
-		nativeRepo.remote name, wrapCallback cb, (remote) =>
-			return cb null, new Remote @, remote
-	@createRemote = =>
-		[name, url, cb] = args
-			name: type: "string"
-			url: type: "string"
-			cb: type: "function"
-		nativeRepo.createRemote name, url, wrapCallback cb, (remote) =>
-			return cb null, new Remote @, remote
-	return @
+	return @
+Repository.prototype.exists = ->
+	_priv = getPrivate @
+	[oid, cb] = args
+		oid: type: "oid"
+		cb: type: "function"
+	_priv.native.exists oid, cb
+Repository.prototype.object = ->
+	_priv = getPrivate @
+	[oid, type, cb] = args
+		oid: type: "oid"
+		type: type: "objectType", default: "any"
+		cb: type: "function"
+	_priv.native.object oid, type, wrapCallback cb, (object) =>
+		clazz = switch object._type
+			when types.commit then Commit
+			when types.tree then Tree
+			when types.blob then Blob
+			when types.tag then Tag
+			else undefined
+		return cb new TypeError("Unexpected object type") if clazz is undefined
+		return cb null, new clazz @, object
+Repository.prototype.blob = (oid, cb) -> @object oid, "blob", cb
+Repository.prototype.commit = (oid, cb) -> @object oid, "commit", cb
+Repository.prototype.tag = (oid, cb) -> @object oid, "tag", cb
+Repository.prototype.tree = (oid, cb) -> @object oid, "tree", cb
+Repository.prototype.reference = ->
+	_priv = getPrivate @
+	[name, resolve, cb] = args
+		name: type: "string"
+		resolve: type: "bool"
+		cb: type: "function"
+	nativeRepo.reference name, resolve, cb
+Repository.prototype.ref = Repository.prototype.reference
+Repository.prototype.remote = ->
+	_priv = getPrivate @
+	[name, cb] = args
+		name: type: "string"
+		cb: type: "function"
+	_priv.native.remote name, wrapCallback cb, (remote) =>
+		return cb null, new Remote @, remote
+Repository.prototype.createRemote = ->
+	_priv = getPrivate @
+	[name, url, cb] = args
+		name: type: "string"
+		url: type: "string"
+		cb: type: "function"
+	_priv.native.createRemote name, url, wrapCallback cb, (remote) =>
+		return cb null, new Remote @, remote

 Gitteh.openRepository = ->
 	[path, cb] = args
@@ -261,7 +283,6 @@
 		cb: type: "function"
 	bindings.openRepository path, wrapCallback cb, (repo) ->
 		cb null, new Repository repo
-
 Gitteh.initRepository = () ->
 	[path, bare, cb] = args
 		path: type: "string"
@@ -269,7 +290,6 @@
 		cb: type: "function"
 	bindings.initRepository path, bare, wrapCallback cb, (repo) ->
 		cb null, new Repository repo
-
 Gitteh.clone = =>
 	[url, path, cb] = args
 		url: type: "string"


Select first visible element on load.

Select first visible element on load.

--- foreground.coffee
+++ foreground.coffee
@@ -4,7 +4,7 @@
     config: false
     selector: false

-  constructor: (@config) ->
+  constructor: (@config, element = null) ->
     @element = null

     @config.offset ?= 100
@@ -20,6 +20,9 @@
     Common.installListener window, "keyup", @KeyUpHandler = (event) => @onKeyUp event
     Common.installListener window, "scroll", @scrollHandler = (event) => @onScroll event
     chrome.runtime.sendMessage name: "icon", show: @config.selectors?
+
+    @selectElement element false if element
+    @onScroll()

   onKeyDown: (event) ->
     # console.clear()
@@ -216,12 +219,12 @@

     (event) ->
       cancel()
-      return unless @element
-      return if Common.isInViewport @element
-      if element = @getElements("down")[0]
-        Common.setTimeout 200, =>
-          timer = null
-          @selectElement element, false unless Common.isInViewport @element
+      unless @element and Common.isInViewport @element
+        if element = @getElements("down")[0]
+          timer = Common.setTimeout 200, =>
+            timer = null
+            unless @element and Common.isInViewport @element
+              @selectElement element, false

 init = ->
   chrome.runtime.sendMessage { name: "config", url: document.location.toString() }, (config) ->


Update to split messages into 150 char blocks

--- src/hubot/twilio.coffee
+++ src/hubot/twilio.coffee
@@ -12,10 +12,8 @@
   send: (user, strings...) ->
     message = strings.join "\n"

-    if message.length > 160
-      console.log "Cannot send reply, body is greater than 160 characters"
-    else
-      @send_sms message, user.id, (err, body) =>
+    for msg in @split_long_sms(message)
+      @send_sms message, user.id, (err, body) ->
         if err or not body?
           console.log "Error sending reply SMS: #{err}"
         else
@@ -63,4 +61,16 @@
           json = JSON.parse(body)
           callback body.message

+  split_long_sms: (message) ->
+    strs = []
+    while str.length > 150
+      pos = str.substring(0, 150).lastIndexOf(" ")
+      pos = (if pos <= 0 then 150 else pos)
+      strs.push str.substring(0, pos)
+      i = str.indexOf(" ", pos) + 1
+      i = pos  if i < pos or i > pos + 150
+      str = str.substring(i)
+    strs.push str
+    strs
+
 module.exports = Twilio


Taking care of key handling issues again

--- src/key-handling.coffee
+++ src/key-handling.coffee
@@ -5,7 +5,7 @@

 module.exports = exports = (game) ->
 	game.on 'key.*', (ch, key) ->
-		action = bindings[key.full ? key.name]
+		action = bindings[key.full] ? bindings[key.name]

 		if action?
 			parts = action.split('.')


Prep BasePort for decaffeination

Prep BasePort for decaffeination

--- src/lib/BasePort.coffee
+++ src/lib/BasePort.coffee
@@ -59,16 +59,16 @@
     if options.schema and options.schema.indexOf('/') is -1
       throw new Error "Invalid port schema '#{options.schema}' specified. Should be URL or MIME type"

-    options
+    return options

   getId: ->
     unless @node and @name
       return 'Port'
-    "#{@node} #{@name.toUpperCase()}"
+    return "#{@node} #{@name.toUpperCase()}"

-  getDataType: -> @options.datatype
-  getSchema: -> @options.schema or null
-  getDescription: -> @options.description
+  getDataType: -> return @options.datatype
+  getSchema: -> return @options.schema or null
+  getDescription: -> return @options.description

   attach: (socket, index = null) ->
     if not @isAddressable() or index is null
@@ -79,8 +79,9 @@
       @emit 'attach', socket, index
       return
     @emit 'attach', socket
+    return

-  attachSocket: ->
+  attachSocket: -> return

   detach: (socket) ->
     index = @sockets.indexOf socket
@@ -91,32 +92,33 @@
       @emit 'detach', socket, index
       return
     @emit 'detach', socket
+    return

   isAddressable: ->
     return true if @options.addressable
-    false
+    return false

   isBuffered: ->
     return true if @options.buffered
-    false
+    return false

   isRequired: ->
     return true if @options.required
-    false
+    return false

   isAttached: (socketId = null) ->
     if @isAddressable() and socketId isnt null
       return true if @sockets[socketId]
       return false
     return true if @sockets.length
-    false
+    return false

   listAttached: ->
     attached = []
     for socket, idx in @sockets
       continue unless socket
       attached.push idx
-    attached
+    return attached

   isConnected: (socketId = null) ->
     if @isAddressable()
@@ -131,6 +133,6 @@
         connected = true
     return connected

-  canAttach: -> true
+  canAttach: -> return true

 module.exports = BasePort


Array#eachPair

Array#eachPair

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -137,6 +137,37 @@
       iterator.call context, element, i, this

   return this
+
+###*
+Call the given iterator once for each pair of objects in the array.
+
+Ex. [1, 2, 3, 4].eachPair (a, b) ->
+  # 1, 2
+  # 1, 3
+  # 1, 4
+  # 2, 3
+  # 2, 4
+  # 3, 4
+
+@name eachPair
+@methodOf Array#
+@param {Function} iterator Function to be called once for
+each pair of elements in the array.
+@param {Object} [context] Optional context parameter to be
+used as `this` when calling the iterator function.
+###
+Array::eachPair = (iterator, context) ->
+  i = 0
+  while i < objects.length
+    a = objects[i]
+    j = i + 1
+    i += 1
+
+    while j < objects.length
+      b = objects[j]
+      j += 1
+
+      iterator.call context, a, b

 ###*
 Call the given iterator once for each element in the array,


Load base style sheets in AtomEnvironment again

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -240,6 +240,7 @@

     @config.load()

+    @themes.loadBaseStylesheets()
     @initialStyleElements = @styles.getSnapshot()
     @themes.initialLoadComplete = true if onlyLoadBaseStyleSheets
     @setBodyPlatformClass()


CUI.Layer

CUI.Layer

- Fixed setting pointer class for ng / non-ng.

--- src/base/Layer/Layer.coffee
+++ src/base/Layer/Layer.coffee
@@ -351,6 +351,12 @@

 		dim_window = CUI.getViewport()

+		get_pointer_class = (direction) =>
+			if CUI.__ng__
+				"cui-layer-pointer--"+direction
+			else
+				"cui-pointer-placement-"+pointer_direction
+
 		if @__pointer
 			# reset pointer
 			CUI.DOM.setStyle(@__pointer,
@@ -360,10 +366,7 @@
 			)

 			for direction in ["w", "s", "e", "n"]
-				if CUI.__ng__
-					CUI.DOM.removeClass(@__pointer, "cui-layer-pointer--"+direction)
-				else
-					CUI.DOM.removeClass(@__pointer, "cui-pointer-placement-"+direction)
+				CUI.DOM.removeClass(@__pointer, get_pointer_class(direction))

 		# measure all 4 directions for all pointers
 		dim_pointer = {}
@@ -377,18 +380,11 @@
 			}[placement]

 			if @__pointer
-				if CUI.__ng__
-					CUI.DOM.addClass(@__pointer, "cui-layer-pointer--"+pointer_direction)
-				else
-					CUI.DOM.addClass(@__pointer, "cui-pointer-placement-"+pointer_direction)
+				CUI.DOM.addClass(@__pointer, get_pointer_class(pointer_direction))

 				dim_pointer[placement] = CUI.DOM.getDimensions(@__pointer)

-				if CUI.__ng__
-					CUI.DOM.addClass(@__pointer, "cui-pointer-placement-"+pointer_direction)
-				else
-					CUI.DOM.removeClass(@__pointer, "cui-layer-pointer--"+pointer_direction)
-
+				CUI.DOM.removeClass(@__pointer, get_pointer_class(pointer_direction))
 			else
 				dim_pointer[placement] =
 					borderBoxWidth: 0
@@ -840,7 +836,7 @@
 				left: vp.pointer_pos.left
 				margin: 0

-			CUI.DOM.addClass(@__pointer, "cui-layer-pointer-"+vp.pointer_pos.direction)
+			CUI.DOM.addClass(@__pointer, get_pointer_class(vp.pointer_pos.direction))

 		if @__backdrop_crop
 			DOM.setStyle @__backdrop_crop,


Ignore firstline check on Windows

Ignore firstline check on Windows

--- lib/code-context-builder.coffee
+++ lib/code-context-builder.coffee
@@ -67,6 +67,7 @@
     return codeContext

   getShebang: (editor) ->
+    return unless process.platform isnt 'win32'
     text = editor.getText()
     lines = text.split("\n")
     firstLine = lines[0]


added a text placeholder attribute

added a text placeholder attribute

--- src/coffee/ScoreView.coffee
+++ src/coffee/ScoreView.coffee
@@ -17,12 +17,13 @@

     @commentWindowView = @findViewByName 'commentWindow'
     @commentView = @findViewByName 'comment'
-    @commentView.setTextAreaPlaceHolder '請留下您對此影片的意見或心得想法，以協助我們製作出更符合您學習需求的教學影片'
     @onLoadData()
     @registerEvent()
     @commentWindowMaxWidth = 500
     domUtils.css @commentWindowView.el, {position:'absolute', width:"#{@commentWindowMaxWidth}px", display:'none',zIndex:1}
     document.addEventListener 'click', => @closeComment()
+
+  TEXT_PLACEHOLDER: '請留下您對此影片的意見或心得想法，以協助我們製作出更符合您學習需求的教學影片'

   ###
    *
@@ -33,6 +34,19 @@
     {ehAttrUser, ehAttrType, ehAttrTarget} = dataset
     deferred = api.getComment ehAttrUser, ehAttrType, ehAttrTarget, 1

+    @onUpdate()
+
+    deferred.done ( resp )=>
+      respData = if resp and resp.result and resp.result.length then resp.result[0] else {}
+      if !(typeof respData.like is 'undefined') then @data_ = like:respData.like else @data_ = {}
+      @syncAttrToData()
+      @renderByData()
+
+  ###
+   *
+  ###
+  onUpdate:->
+    dataset = domUtils.getDataset( @el )
     selects = JSON.parse( dataset.ehCommentSelect or "[]" )

     if selects.length
@@ -41,11 +55,9 @@
     else
       @commentView.hideSelector()

-    deferred.done ( resp )=>
-      respData = if resp and resp.result and resp.result.length then resp.result[0] else {}
-      if !(typeof respData.like is 'undefined') then @data_ = like:respData.like else @data_ = {}
-      @syncAttrToData()
-      @renderByData()
+    textPlaceholder = if dataset.ehTextPlaceholder then dataset.ehTextPlaceholder else @TEXT_PLACEHOLDER
+    @commentView.setTextAreaPlaceHolder textPlaceholder
+

   ###
    *


fix(logging): Fix wrong condition

fix(logging): Fix wrong condition

--- src/coffee/io/reader.coffee
+++ src/coffee/io/reader.coffee
@@ -9,7 +9,7 @@
 class Reader

   constructor: (@options = {}) ->
-    logLevel = if !@options.debug then 'debug' else 'info'
+    logLevel = if @options.debug then 'debug' else 'info'
     @Logger = require('../logger')('IO::Reader', logLevel)

     @Logger.debug "READER::options:", JSON.stringify(@options)


Corrige notação do dolar para Uruguai

Corrige notação do dolar para Uruguai

--- src/coffee/vtex-i18n.coffee
+++ src/coffee/vtex-i18n.coffee
@@ -42,7 +42,7 @@
 			when 'USA'
 				return 'US$ '
 			when 'URY'
-				return 'US$ '
+				return 'USD '
 			else
 				return '$ '


Load views and modules in parallel

Load views and modules in parallel

--- src/core/environment.coffee
+++ src/core/environment.coffee
@@ -226,10 +226,13 @@
   load: (callback) ->
     ### Convenience method to load plugins, views, contents, templates and locals. ###
     async.waterfall [
-      (callback) => @loadPlugins callback
-      (callback) =>
+      (callback) =>
+        async.parallel [
+          (callback) => @loadPlugins callback
+          (callback) => @loadViews callback
+        ], callback
+      (_, callback) =>
         async.parallel
-          _views: (callback) => @loadViews callback
           contents: (callback) => @getContents callback
           templates: (callback) => @getTemplates callback
           locals: (callback) => @getLocals callback


Increase priority to go before grammar

Increase priority to go before grammar

--- lib/encoding-status-view.coffee
+++ lib/encoding-status-view.coffee
@@ -9,7 +9,7 @@
     @handleEvents()

   attach: ->
-    @tile = @statusBar.addRightTile(priority: -2, item: this)
+    @tile = @statusBar.addRightTile(priority: 11, item: this)

   handleEvents: ->
     @activeItemSubscription = atom.workspace.onDidChangeActivePaneItem =>


simplify logic and changing less from before

--- browser.coffee
+++ browser.coffee
@@ -32,7 +32,7 @@
     "#{js}\n//@ sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//@ sourceURL=coffeescript"

 # Load a remote script from the current domain via XHR.
-CoffeeScript.load = (url, callback = (->), options = {}, hold = false) ->
+CoffeeScript.load = (url, callback, options = {}, hold = false) ->
   options.sourceFiles = [url]
   xhr = if window.ActiveXObject
     new window.ActiveXObject('Microsoft.XMLHTTP')
@@ -43,13 +43,11 @@
   xhr.onreadystatechange = ->
     if xhr.readyState is 4
       if xhr.status in [0, 200]
-        run = ->
-          CoffeeScript.run xhr.responseText, options
-          null
+        param = [xhr.responseText, options]
+        CoffeeScript.run param... unless hold
       else
         throw new Error "Could not load #{url}"
-      run() unless hold
-      callback run
+      callback param if callback
   xhr.send null

 # Activate CoffeeScript in the browser by having it compile and evaluate
@@ -59,31 +57,30 @@
   scripts = window.document.getElementsByTagName 'script'
   coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
   coffees = (s for s in scripts when s.type in coffeetypes)
+  index = 0

-  check = ->
-    for run, index in coffees
-      continue unless run
-      return unless run instanceof Function
-      coffees[index] = run()
+  execute = ->
+    param = coffees[index]
+    if param instanceof Array
+      CoffeeScript.run param...
+      index++
+      execute()

-  for script, index in coffees
-    do (script, index) ->
+  for script, i in coffees
+    do (script, i) ->
       options = literate: script.type is coffeetypes[1]
       if script.src
         CoffeeScript.load script.src,
-          (run) ->
-            coffees[index] = run
-            check()
+          (param) ->
+            coffees[i] = param
+            execute()
           options
           true
       else
         options.sourceFiles = ['embedded']
-        coffees[index] = ->
-          CoffeeScript.run script.innerHTML, options
-          null
+        coffees[i] = [script.innerHTML, options]

-  check()
-  null
+  execute()

 # Listen for window load, both in decent browsers and in IE.
 if window.addEventListener


Fixed duplicate email issue when saving profile

--- lib/initializers/globals.coffee
+++ lib/initializers/globals.coffee
@@ -26,11 +26,8 @@
     console.info "thisuserId":this.userId
     id = this.userId
     user = Meteor.users.findOne _id:id
-    email = user?.emails[0]
-
-    console.log email:email
-    if not email or email.address isnt obj.email
-      emails = [address: obj.email or null, verified: false]
+
+    emails = if obj.email isnt "" then [address: obj.email, verified: false] else []

     result = Meteor.users.update { _id: id }, $set:
       emails: emails or user.emails


Tweak the markup to look a little better.

--- lib/merge-conflicts-view.coffee
+++ lib/merge-conflicts-view.coffee
@@ -10,11 +10,11 @@
   @content: ->
     @div class: 'overlay from-top merge-conflicts-message', =>
       @div class: 'panel', click: 'dismiss', =>
-        @p class: "panel-heading highlight-#{@highlightClass} padded", @headingText
+        @div class: "panel-heading text-#{@headingClass}", @headingText
         @div class: 'panel-body', =>
-          @p class: 'block', =>
+          @div class: 'block', =>
             @bodyMarkup()
-          @p class: 'block text-subtle', 'click to dismiss'
+          @div class: 'block text-subtle', 'click to dismiss'

   initialize: ->

@@ -25,7 +25,7 @@

   @headingText = 'Merge Complete'

-  @highlightClass = 'success'
+  @headingClass = 'success'

   @bodyMarkup: ->
     @text "That's everything. "
@@ -36,7 +36,7 @@

   @headingText = 'Maybe Later'

-  @highlightClass = 'warning'
+  @headingClass = 'warning'

   @bodyMarkup: ->
     @text "Careful, you've still got conflict markers left!"
@@ -50,8 +50,8 @@
         @text 'Conflicts'
         @span class: 'pull-right icon icon-fold', click: 'minimize', 'Hide'
         @span class: 'pull-right icon icon-unfold', click: 'restore', 'Show'
-      @div class: 'block', outlet: 'body', =>
-        @ul class: 'list-group', outlet: 'pathList', =>
+      @div outlet: 'body', =>
+        @ul class: 'block list-group', outlet: 'pathList', =>
           for p in conflicts
             @li click: 'navigate', class: 'list-item navigate', =>
               @span class: 'inline-block icon icon-diff-modified status-modified path', p
@@ -59,7 +59,7 @@
                 @span class: 'inline-block text-subtle', "modified by both"
                 @progress class: 'inline-block', max: 100, value: 0
                 @span class: 'inline-block icon icon-dash staged'
-        @div class: 'block pull-right padded', =>
+        @div class: 'block pull-right', =>
           @button class: 'btn btn-sm', click: 'quit', 'Quit'

   initialize: (@conflicts) ->


Reordered count

Reordered count

--- src/types/count.coffee
+++ src/types/count.coffee
@@ -1,4 +1,7 @@
 # This is a simple type used for testing other OT code. Each op is [expectedSnapshot, increment]
+
+exports.name = 'count'
+exports.initialVersion = () -> 1

 exports.apply = (snapshot, op) ->
 	[v, inc] = op
@@ -17,6 +20,3 @@
 exports.generateRandomOp = (doc) ->
 	[[doc, 1], doc + 1]

-exports.initialVersion = () -> 1
-
-exports.name = 'count'


minor: color of the composite patterns

minor: color of the composite patterns

--- flight-app.coffee
+++ flight-app.coffee
@@ -71,7 +71,7 @@
     if m = instruction.match FLD.rle
       for [x,y],i in parseRle m[1]
         pattern.push [x+pos[0], y+pos[1]]
-        colors.push curColors[i % curColors.length]
+        colors.push curColors[colors.length % curColors.length]
     else if m = instruction.match FLD.at
       pos = [parseInt(m[1], 10), parseInt(m[2],10)]
     else if m = instruction.match FLD.size


Separate view init from jQuery styletap init

--- layout.coffee
+++ layout.coffee
@@ -469,8 +469,6 @@
   init = (selector = $('view')) ->
     for el, i in selector
       initFromElement(el) unless el.$defer or el.$view
-    # listen for jQuery style changes
-    hackstyle(true)
     # console.log 'caches', compileCache

   # http://stackoverflow.com/questions/1248849/converting-sanitised-html-back-to-displayable-html
@@ -701,4 +699,8 @@
   }

 lz.writeDefaultStyle()
-$(window).on('load', () -> lz.initViews() )+$(window).on('load', () ->
+  lz.initViews()
+  # listen for jQuery style changes
+  hackstyle(true)
+)


Add boolean type coercion

Add boolean type coercion

--- layout.coffee
+++ layout.coffee
@@ -111,6 +111,7 @@

   typemappings =
     number: parseFloat
+    boolean: (val) -> (!! val)

   installMethod = (scope, methodname, method) ->
     if methodname of scope


init repo on create, if it's not already a repo

--- packages/gitrt-core/repo.coffee
+++ packages/gitrt-core/repo.coffee
@@ -17,6 +17,9 @@
 class Repo extends EventEmitter
   constructor: (@repoPath) ->
     self = this
+
+    unless fs.existsSync(Path.join(@repoPath, '.git'))
+      @spawnInRepoPath 'git', ['init']

     watcher = chokidar.watch(@repoPath,
       ignored: /\/\.git/,


Fix the Document ctor invocation

Fix the Document ctor invocation

--- library/nucleus/document.coffee
+++ library/nucleus/document.coffee
@@ -42,7 +42,7 @@
     validated = _.isFunction(descriptor)
     missing = "No descriptor function has been given"
     throw new Error(missing) unless validated
-    method.document = new Document(method)
+    method.document = new Document
     descriptor.apply(method.document)

 # Traverse all of the services that are registered with the router


Fix upload rate calculation exception.

Fix upload rate calculation exception.

--- app/assets/javascripts/directives/uploader.js.coffee
+++ app/assets/javascripts/directives/uploader.js.coffee
@@ -127,7 +127,7 @@
           # discard entries not in window
           now = Date.now()
           cutoff = now - RATE_WINDOW_SIZE
-          @rateWindow.shift() while @rateWindow[0][0] < cutoff
+          @rateWindow.shift() while @rateWindow.length != 0 && @rateWindow[0][0] < cutoff

           if @rateWindow.length >= 2 # rate = 0 if there aren't 2 entries to compare
             first = @rateWindow[0]


Update batch_export.coffee

Update batch_export.coffee

--- app/assets/javascripts/incidents/batch_export.coffee
+++ app/assets/javascripts/incidents/batch_export.coffee
@@ -1,8 +1,7 @@
-alreadyExportedWarning = -> (count)
-  words = []
-  if count == 1
-    words.push 'incident has', 'this incident'
-  else words.push 'incidents have', 'these incidents'
+alreadyExportedWarning = (count) ->
+  words = if count == 1
+    ['incident has', 'this incident']
+  else ['incidents have', 'these incidents']
   """
   #{count} selected #{words[0]} already been exported.
   Please doublecheck that you would like to re-export #{words[1]}.


proper scope of toggleIcon for uploadFile method

--- app/assets/javascripts/oxalis/view/mergemodal.coffee
+++ app/assets/javascripts/oxalis/view/mergemodal.coffee
@@ -245,7 +245,7 @@
       Toast.message(data.messages)
     ).fail( (xhr) ->
       Toast.message(xhr.responseJSON.messages)
-    ).always( ->
+    ).always( =>
       @toggleIcon()
     )


Prevents unwanted menu toggling

Prevents unwanted menu toggling

--- app/assets/javascripts/search_autocomplete.js.coffee
+++ app/assets/javascripts/search_autocomplete.js.coffee
@@ -149,9 +149,9 @@
     # Avoid falsy value to be returned
     return

-  onSearchInputClick: =>
-    if (@searchInput.val() is '')
-      @disableAutocomplete()
+  onSearchInputClick: (e) =>
+    # Prevents closing the dropdown menu
+    e.stopImmediatePropagation()

   onSearchInputFocus: =>
     @wrap.addClass('search-active')


set path so its global to the site

set path so its global to the site

--- app/assets/javascripts/type_station/lib/ts.js.coffee
+++ app/assets/javascripts/type_station/lib/ts.js.coffee
@@ -2,7 +2,7 @@
   d = new Date
   d.setTime d.getTime() + exdays * 24 * 60 * 60 * 1000
   expires = 'expires=' + d.toUTCString()
-  document.cookie = cname + '=' + cvalue + '; ' + expires
+  document.cookie = cname + '=' + cvalue + '; ' + expires + '; path=/'
   return

 getCookie = (cname) ->


Only detach and move list items if the index changed

--- app/assets/javascripts/views/relation_view.js.coffee
+++ app/assets/javascripts/views/relation_view.js.coffee
@@ -38,8 +38,9 @@
       @onInsert?(element, record, index)

     @subscribe relation, 'onUpdate', (record, changes, newIndex, oldIndex) =>
-      element = @elementForRecord(record, newIndex)
-      @insertAtIndex(element, newIndex)
+      if newIndex != oldIndex
+        element = @elementForRecord(record, newIndex)
+        @insertAtIndex(element, newIndex)
       @onUpdate?(element, record, changes, newIndex, oldIndex);

     @subscribe relation, 'onRemove', (record, index) =>


Added class names to KDMultipleChoice items.

--- components/inputs/KDMultipleChoice.coffee
+++ components/inputs/KDMultipleChoice.coffee
@@ -19,8 +19,8 @@

     options.size         or= "small"             # a String tiny/small/big
     options.labels       or= ["ON", "OFF"]       # supports multiple labels as string
-    options.multiple     or= no
-    options.defaultValue or= options.labels[0]
+    options.multiple     ?= no
+    options.defaultValue or= if options.multiple then options.labels[0]

     if not options.multiple and Array.isArray options.defaultValue
       options.defaultValue = options.defaultValue[0]
@@ -33,15 +33,14 @@
     @oldValue     = null
     @currentValue = [] if options.multiple

-    @setValue options.defaultValue, no
-
   setDomElement:(cssClass)->
     {title, labels, name} = @getOptions()
     title = if title then "#{title}" else ''

     labelItems = ""
     for label in labels
-      labelItems += "#{label}"
+      clsName     = "multiple-choice-#{label}"
+      labelItems += "#{label}"

     @domElement = $ """


Adds X-Forwarded-* headers.

Adds X-Forwarded-* headers.

--- grammars/eml.cson
+++ grammars/eml.cson
@@ -77,6 +77,8 @@
     { 'match': '^(?i)x-forefront-antispam-report.*?:', 'name': 'constant.other.eml' },
     { 'match': '^(?i)x-forefront-prvs.*?:', 'name': 'constant.other.eml' },
     { 'match': '^(?i)x-forwarded-for.*?:', 'name': 'constant.other.eml' },
+    { 'match': '^(?i)x-forwarded-for.*?:', 'name': 'constant.other.eml' },
+    { 'match': '^(?i)x-forwarded-to.*?:', 'name': 'constant.other.eml' },
     { 'match': '^(?i)x-forwarded-to.*?:', 'name': 'constant.other.eml' },
     { 'match': '^(?i)x-gm-message-state.*?:', 'name': 'constant.other.eml' },
     { 'match': '^(?i)x-gmail-received.*?:', 'name': 'constant.other.eml' },


Remove useless comments

Remove useless comments

--- lib/db.coffee
+++ lib/db.coffee
@@ -51,7 +51,6 @@

         callback(found)

-  ## CREATE
   add: (props, callback) ->
     projects = CSON.readFileSync(@file()) || {}
     id = props.title.replace(/\s+/g, '').toLowerCase()
@@ -68,7 +67,6 @@
       else
         atom.notifications?.addError errorMessage

-  ## UPDATE
   update: (props, callback) ->
     return false if not props._id

@@ -89,8 +87,7 @@
       if key is id
         delete(projects[key])

-  ## DELETE
-  # delete: (project) ->
+    CSON.writeFileSync @file(), projects

   onUpdate: (callback) ->
     @emitter.on 'db-updated', () =>
@@ -109,7 +106,6 @@
     @fileWatcher.close() if @fileWatcher?
     @fileWatcher = fs.watch @file(), (event, filename) =>
       @emitter.emit 'db-updated'
-      # Send update event

   updateFile: ->
     fs.exists @file(true), (exists) =>


Remove logging

Remove logging

--- src/menu-manager.coffee
+++ src/menu-manager.coffee
@@ -49,7 +49,6 @@
   update: ->
     keystrokesByCommand = {}
     for binding in atom.keymap.getKeyBindings() when @includeSelector(binding.selector)
-      console.log binding.selector
       keystrokesByCommand[binding.command] ?= []
       keystrokesByCommand[binding.command].push binding.keystroke
     @sendToBrowserProcess(@template, keystrokesByCommand)


also execute the app taks on serve

also execute the app taks on serve

--- gulp-serve.coffee
+++ gulp-serve.coffee
@@ -30,7 +30,7 @@
                     }

 # Start the live reload server. Live reload will be triggered when a file in the `dist` folder or the index.html changes. This will add a live-reload script to the page, which makes it all happen.
-gulp.task 'live-reload', ['watch'], ->
+gulp.task 'live-reload', ['watch', 'app'], ->
   # first delete the index.html
   # from the dist folder as we will copy it later
   # del ['dist/index.html']


reclean .tmp after build

reclean .tmp after build

--- gulp/build.coffee
+++ gulp/build.coffee
@@ -6,10 +6,12 @@
 gulp.task 'clean:tmp', ->
   del(['.tmp/**/*'])

-gulp.task 'default', ['clean:tmp', 'js:concat', 'uglify', 'app:css'], ->
+gulp.task 'build', ['clean:tmp', 'js:concat', 'uglify', 'app:css'], ->
   gulp.src('.tmp/app/js/uglify/**/*')
       .pipe(gulp.dest('public/js'))

+gulp.task 'default', ['build'], ->
+  del(['.tmp/**/*'])

 gulp.task 'watch', ->
   watch [


Update es-419.coffee

Update es-419.coffee

Almost everything is done :D

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -819,12 +819,12 @@
   account:
     recently_played: "Recientemente jugado"
     no_recent_games: "No juegos jugados duramente las últimas dos semanas."
-#    payments: "Payments"
-#    purchased: "Purchased"
+    payments: "Pagos"
+    purchased: "Comprado"
     subscription: "Suscripción"
     service_apple: "Apple"
     service_web: "Web"
-#    paid_on: "Paid On"
+    paid_on: "Pagado en"
     service: "Servicio"
     price: "Precio"
     gems: "Gemas"
@@ -917,15 +917,15 @@
     multiplayer_hint_label: "Consejo:"
     multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace."
     multiplayer_coming_soon: "¡Más características de multijugador por venir!"
-#    multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
+    multiplayer_sign_in_leaderboard: "Entra o crea una cuenta y mira tu solución en la clafisicación."

   legal:
     page_title: "Legal"
-    opensource_intro: "CodeCombat is completamente open source."
+    opensource_intro: "CodeCombat es completamente open source."
     opensource_description_prefix: "Echa un vistazo "
     github_url: "nuestro GitHub"
     opensource_description_center: "y ayudanos si quieres! CodeCombat esta construido por docenas de proyectos open source, y los amamos. Mira "
-    archmage_wiki_url: "nuestra wiki de Archimago wiki"
+    archmage_wiki_url: "nuestra wiki de Archimago"
     opensource_description_suffix: "Para la lista de softwares que hacen al juego posible."
     practices_title: "Mejores prácticas respetuosas"
 #    practices_description: "These are our promises to you, the player, in slightly less legalese."
@@ -938,7 +938,7 @@
     email_settings_url: "tus opciones de mail"
     email_description_suffix: "o mediante links en los mails que mandamos, tu puedas cambiar tus preferencias y fácilmente desuscribirte en cualquier momento."
     cost_title: "Costo"
-#    cost_description: "CodeCombat is free to play for all of its core levels, with a $9.99 USD/mo subscription for access to extra level branches and 3500 bonus gems per month. You can cancel with a click, and we offer a 100% money-back guarantee."
+    cost_description: "CodeCombat es gratuito para todos sus niveles principales, con una suscripción de $9.99 USD/mes con acceso a niveles adicionales y un bonus de 3500 gemas cada mes. Puedes cancelar con un click y ofrecemos una garantía del 100%."
 #    copyrights_title: "Copyrights and Licenses"
 #    contributor_title: "Contributor License Agreement"
 #    contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"


Remove no main module logging

Remove no main module logging

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -29,8 +29,6 @@
     if childMetadata?.version
       try
         mainPath = require.resolve(childPath)
-      catch error
-        console.log "Skipping #{childPath}, no main module"

       if mainPath
         moduleCache.dependencies.push


Actually fix require paths

Actually fix require paths

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -202,12 +202,12 @@

   atomShellRoot = path.join(process.resourcesPath, 'atom.asar')

-  commonRoot = path.join(atomShellRoot, 'lib', 'common', 'api')
+  commonRoot = path.join(atomShellRoot, 'common', 'api')
   commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell']
   for builtin in commonBuiltins
     cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js")

-  rendererRoot = path.join(atomShellRoot, 'lib', 'renderer', 'api')
+  rendererRoot = path.join(atomShellRoot, 'renderer', 'api')
   rendererBuiltins = ['ipc-renderer', 'remote']
   for builtin in rendererBuiltins
     cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js")


Closes #8; Was not setting a flag in the update method for the gauge causing many animation intervals to be created (and many times not cleared).

--- coffee/time/gauge.coffee
+++ coffee/time/gauge.coffee
@@ -58,6 +58,7 @@
     @animation.delta = (value - @value) / @options.fps
     unless @animation.active
       @animation.interval = setInterval @_animate, (1000/@options.fps)
+      @animation.active = true

   radius: -> @height / 1.58
   centerX: -> @width / 2


Improved test flow

Improved test flow

--- spec/grammar-spec.coffee
+++ spec/grammar-spec.coffee
@@ -13,25 +13,7 @@
     expect(grammar.scopeName).toBe "source.perl"

   describe "tokenizes regexp replace", ->
-    it "test defaul regex syntax", ->
-      {tokens} = grammar.tokenizeLine("s/text/test/")
-      expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"]
-      expect(tokens[1]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"]
-      expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"]
-      expect(tokens[3]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
-      expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"]
-      expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
-
-    it "test underline seperator", ->
-      {tokens} = grammar.tokenizeLine("s_text_test_")
-      expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"]
-      expect(tokens[1]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"]
-      expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"]
-      expect(tokens[3]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
-      expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"]
-      expect(tokens[5]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
-
-    it "test defaul regex syntax with modifier", ->
+    it "works as expected", ->
       {tokens} = grammar.tokenizeLine("s/text/test/gxr")
       expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"]
       expect(tokens[1]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"]
@@ -41,8 +23,18 @@
       expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
       expect(tokens[6]).toEqual value: "gxr", scopes: ["source.perl", "string.regexp.replace.perl", "punctuation.definition.string.perl", "keyword.control.regexp-option.perl"]

+    it "works with underline as seperator", ->
+      {tokens} = grammar.tokenizeLine("s_text_test_gxr")
+      expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"]
+      expect(tokens[1]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"]
+      expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"]
+      expect(tokens[3]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
+      expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"]
+      expect(tokens[5]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
+      expect(tokens[6]).toEqual value: "gxr", scopes: ["source.perl", "string.regexp.replace.perl", "punctuation.definition.string.perl", "keyword.control.regexp-option.perl"]
+
   describe "tokenizes constant variables", ->
-    it "highlight constants", ->
+    it "highlights constants", ->
       {tokens} = grammar.tokenizeLine("__FILE__")
       expect(tokens[0]).toEqual value: "__FILE__", scopes: ["source.perl", "constant.language.perl"]

@@ -61,37 +53,32 @@
       {tokens} = grammar.tokenizeLine("__DATA__")
       expect(tokens[0]).toEqual value: "__DATA__", scopes: ["source.perl", "constant.language.perl"]

-    it "does not highlight custom constants like a default constant", ->
+    it "does highlight custom constants different", ->
       {tokens} = grammar.tokenizeLine("__TEST__")
       expect(tokens[0]).toEqual value: "__TEST__", scopes: ["source.perl", "string.unquoted.program-block.perl", "punctuation.definition.string.begin.perl"]

-  describe "tokenizes meta functions", ->
-    it "test BEGIN", ->
+  describe "tokenizes compile phase keywords", ->
+    it "does highlight all compile phase keywords", ->
       {tokens} = grammar.tokenizeLine("BEGIN")
       expect(tokens[0]).toEqual value: "BEGIN", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

-    it "test UNITCHECK", ->
       {tokens} = grammar.tokenizeLine("UNITCHECK")
       expect(tokens[0]).toEqual value: "UNITCHECK", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

-    it "test CHECK", ->
       {tokens} = grammar.tokenizeLine("CHECK")
       expect(tokens[0]).toEqual value: "CHECK", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

-    it "test INIT", ->
       {tokens} = grammar.tokenizeLine("INIT")
       expect(tokens[0]).toEqual value: "INIT", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

-    it "test END", ->
       {tokens} = grammar.tokenizeLine("END")
       expect(tokens[0]).toEqual value: "END", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

-    it "test DESTROY", ->
       {tokens} = grammar.tokenizeLine("DESTROY")
       expect(tokens[0]).toEqual value: "DESTROY", scopes: ["source.perl", "meta.function.perl", "entity.name.function.perl"]

   describe "tokenizes method calls", ->
-    it "ignores highlight if called like a method", ->
+    it "does not highlight if called like a method", ->
       {tokens} = grammar.tokenizeLine("$test->q;")
       expect(tokens[2]).toEqual value: "->", scopes: ["source.perl", "keyword.operator.comparison.perl"]
       expect(tokens[3]).toEqual value: "q;", scopes: ["source.perl"]
@@ -158,32 +145,36 @@
       expect(lines[4][1]).toEqual value: ";", scopes: ["source.perl"]

   describe "tokenizes double quoting", ->
-    it "\"text\"", ->
-      {tokens} = grammar.tokenizeLine("\"Test this\\nsimple one\";")
+    it "does escape characters in double-quote strings", ->
+      {tokens} = grammar.tokenizeLine("\"Test\\tthis\\nsimple one\";")
       expect(tokens[0]).toEqual value: "\"", scopes: ["source.perl", "string.quoted.double.perl", "punctuation.definition.string.begin.perl"]
-      expect(tokens[1]).toEqual value: "Test this", scopes: ["source.perl", "string.quoted.double.perl"]
-      expect(tokens[2]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.double.perl", "constant.character.escape.perl"]
-      expect(tokens[3]).toEqual value: "simple one", scopes: ["source.perl", "string.quoted.double.perl"]
-      expect(tokens[4]).toEqual value: "\"", scopes: ["source.perl", "string.quoted.double.perl", "punctuation.definition.string.end.perl"]
-      expect(tokens[5]).toEqual value: ";", scopes: ["source.perl"]
-
-    it "qq(text)", ->
-      {tokens} = grammar.tokenizeLine("qq(Test this\\nsimple one);")
+      expect(tokens[1]).toEqual value: "Test", scopes: ["source.perl", "string.quoted.double.perl"]
+      expect(tokens[2]).toEqual value: "\\t", scopes: ["source.perl", "string.quoted.double.perl", "constant.character.escape.perl"]
+      expect(tokens[3]).toEqual value: "this", scopes: ["source.perl", "string.quoted.double.perl"]
+      expect(tokens[4]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.double.perl", "constant.character.escape.perl"]
+      expect(tokens[5]).toEqual value: "simple one", scopes: ["source.perl", "string.quoted.double.perl"]
+      expect(tokens[6]).toEqual value: "\"", scopes: ["source.perl", "string.quoted.double.perl", "punctuation.definition.string.end.perl"]
+      expect(tokens[7]).toEqual value: ";", scopes: ["source.perl"]
+
+      {tokens} = grammar.tokenizeLine("qq(Test\\tthis\\nsimple one);")
       expect(tokens[0]).toEqual value: "qq(", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "punctuation.definition.string.begin.perl"]
-      expect(tokens[1]).toEqual value: "Test this", scopes: ["source.perl", "string.quoted.other.qq-paren.perl"]
-      expect(tokens[2]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "constant.character.escape.perl"]
-      expect(tokens[3]).toEqual value: "simple one", scopes: ["source.perl", "string.quoted.other.qq-paren.perl"]
-      expect(tokens[4]).toEqual value: ")", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "punctuation.definition.string.end.perl"]
-      expect(tokens[5]).toEqual value: ";", scopes: ["source.perl"]
-
-    it "qq~text~", ->
-      {tokens} = grammar.tokenizeLine("qq~Test this\\nadvanced one~;")
+      expect(tokens[1]).toEqual value: "Test", scopes: ["source.perl", "string.quoted.other.qq-paren.perl"]
+      expect(tokens[2]).toEqual value: "\\t", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "constant.character.escape.perl"]
+      expect(tokens[3]).toEqual value: "this", scopes: ["source.perl", "string.quoted.other.qq-paren.perl"]
+      expect(tokens[4]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "constant.character.escape.perl"]
+      expect(tokens[5]).toEqual value: "simple one", scopes: ["source.perl", "string.quoted.other.qq-paren.perl"]
+      expect(tokens[6]).toEqual value: ")", scopes: ["source.perl", "string.quoted.other.qq-paren.perl", "punctuation.definition.string.end.perl"]
+      expect(tokens[7]).toEqual value: ";", scopes: ["source.perl"]
+
+      {tokens} = grammar.tokenizeLine("qq~Test\\tthis\\nadvanced one~;")
       expect(tokens[0]).toEqual value: "qq~", scopes: ["source.perl", "string.quoted.other.qq.perl", "punctuation.definition.string.begin.perl"]
-      expect(tokens[1]).toEqual value: "Test this", scopes: ["source.perl", "string.quoted.other.qq.perl"]
-      expect(tokens[2]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.other.qq.perl", "constant.character.escape.perl"]
-      expect(tokens[3]).toEqual value: "advanced one", scopes: ["source.perl", "string.quoted.other.qq.perl"]
-      expect(tokens[4]).toEqual value: "~", scopes: ["source.perl", "string.quoted.other.qq.perl", "punctuation.definition.string.end.perl"]
-      expect(tokens[5]).toEqual value: ";", scopes: ["source.perl"]
+      expect(tokens[1]).toEqual value: "Test", scopes: ["source.perl", "string.quoted.other.qq.perl"]
+      expect(tokens[2]).toEqual value: "\\t", scopes: ["source.perl", "string.quoted.other.qq.perl", "constant.character.escape.perl"]
+      expect(tokens[3]).toEqual value: "this", scopes: ["source.perl", "string.quoted.other.qq.perl"]
+      expect(tokens[4]).toEqual value: "\\n", scopes: ["source.perl", "string.quoted.other.qq.perl", "constant.character.escape.perl"]
+      expect(tokens[5]).toEqual value: "advanced one", scopes: ["source.perl", "string.quoted.other.qq.perl"]
+      expect(tokens[6]).toEqual value: "~", scopes: ["source.perl", "string.quoted.other.qq.perl", "punctuation.definition.string.end.perl"]
+      expect(tokens[7]).toEqual value: ";", scopes: ["source.perl"]

   describe "tokenizes word quoting", ->
     it "quotes words", ->
@@ -194,7 +185,7 @@
       expect(tokens[3]).toEqual value: ";", scopes: ["source.perl"]

   describe "tokenizes subroutines", ->
-    it "default subroutine", ->
+    it "does highlight subroutines", ->
       lines = grammar.tokenizeLines("""sub mySub {
           print "asd";
       }""")
@@ -203,7 +194,7 @@
       expect(lines[0][4]).toEqual value: "{", scopes: ["source.perl"]
       expect(lines[2][0]).toEqual value: "}", scopes: ["source.perl"]

-    it "subroutine as variable", ->
+    it "does highlight subroutines assigned to a variable", ->
       lines = grammar.tokenizeLines("""my $test = sub {
           print "asd";
       };""")
@@ -211,7 +202,7 @@
       expect(lines[0][7]).toEqual value: "{", scopes: ["source.perl"]
       expect(lines[2][0]).toEqual value: "};", scopes: ["source.perl"]

-    it "subroutine as variable in hash", ->
+    it "does highlight subroutines assigned to a hash key", ->
       lines = grammar.tokenizeLines("""my $test = { a => sub {
           print "asd";
       }};""")


Tests for W and B motions

Tests for W and B motions

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -119,6 +119,40 @@
         it "selects the whitespace", ->
           expect(vimState.getRegister('"').text).toBe ' '

+  describe "the W keybinding", ->
+    beforeEach -> editor.setText("cde1+- ab \n xyz\n\nzip")
+
+    describe "as a motion", ->
+      beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+      it "moves the cursor to the beginning of the next word", ->
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+    describe "as a selection", ->
+      describe "within a word", ->
+
+        it "selects to the end of the whole word", ->
+          editor.setCursorScreenPosition([0, 0])
+          keydown('y')
+          keydown('W', shift:true)
+          expect(vimState.getRegister('"').text).toBe 'cde1+- '
+
+        it "doesn't go past the end of the file", ->
+          editor.setCursorScreenPosition([2, 0])
+          keydown('y')
+          keydown('W', shift:true)
+          expect(vimState.getRegister('"').text).toBe ''
+
   describe "the e keybinding", ->
     beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip")

@@ -280,6 +314,41 @@
           expect(vimState.getRegister('"').text).toBe 'ab '
           expect(editor.getCursorScreenPosition()).toEqual [0, 1]

+  describe "the B keybinding", ->
+    beforeEach -> editor.setText("cde1+- ab \n xyz-123\n\n zip")
+
+    describe "as a motion", ->
+      beforeEach -> editor.setCursorScreenPosition([4, 1])
+
+      it "moves the cursor to the beginning of the previous word", ->
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [3, 1]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+    describe "as a selection", ->
+      it "selects to the beginning of the whole word", ->
+        editor.setCursorScreenPosition([1, 8])
+        keydown('y')
+        keydown('B', shift:true)
+        expect(vimState.getRegister('"').text).toBe 'xyz-123'
+
+      it "doesn't go past the beginning of the file", ->
+        editor.setCursorScreenPosition([0, 0])
+        keydown('y')
+        keydown('B', shift:true)
+        expect(vimState.getRegister('"').text).toBe ''
+
   describe "the ^ keybinding", ->
     beforeEach ->
       editor.setText("  abcde")


Fix an assertion that was failing but was being reported as part of the wrong test.

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -231,7 +231,7 @@
       runs ->
         dirPath = directory.getRealPathSync()
         expect(the_result).toBeInstanceOf GitRepository
-        expect(the_result.getWorkingDirectory()).toBe path.join(dirPath, '.git')
+        expect(the_result.getPath()).toBe path.join(dirPath, '.git')

         expect(atom.project.repositoryPromisesByPath.size).toBe 1
         expect(atom.project.repositoryPromisesByPath.get dirPath).toBe the_promise


reword it() message

reword it() message

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -65,7 +65,7 @@
       expect(otherDummyDirectory.getPath()).toBe otherRemotePath
       expect(otherDummyDirectory instanceof DummyDirectory).toBe true

-    it "a custom DirectoryProvider that returns null defaults to the DefaultDirectoryProvider", ->
+    it "uses the default directory provider if no custom provider can handle the URI", ->
       directoryProvider =
         directoryForURISync: (uri) -> null
         directoryForURI: (uri) -> throw new Error("This should not be called.")


Update doc urls

Update doc urls

--- compiler/SDKError.coffee
+++ compiler/SDKError.coffee
@@ -18,25 +18,25 @@

 MAIN = 'http://developer.marquee.by/marquee-static-sdk/'

-DOCS = {}
-# Disable until they're actually available
-# DOCSX =
-#     main                    : MAIN
-#     configuration           : "#{ MAIN }configuration/"
-#     'configuration.deploy'  : "configuration.deploy"
-#     tokens                  : "#{ MAIN }tokens/"
-#     emitFile                : "#{ MAIN }emitFile/"
-#     'emitFile.json'         : ''
-#     'project.react'         : ''
-#     files                   : "#{ MAIN }files/"
-#     assets                  : "#{ MAIN }assets/"
-#     compiler                : "#{ MAIN }compiler/"
-#     'deploy.repo'           : "#{ MAIN }deploy/"
-#     api:
-#         404: "#{ MAIN }api/not-found/"
-#         401: "#{ MAIN }api/unauthorized/"
-#         403: "#{ MAIN }api/forbidden/"
-#         toString: -> "#{ MAIN }api/"
+
+
+DOCS =
+    main                    : ''
+    configuration           : 'configuration/'
+    'configuration.deploy'  : 'configuration/#deployment'
+    tokens                  : 'configuration/#tokens'
+    emitFile                : 'compiler/#-emitfile-'
+    'emitFile.json'         : 'compiler/#-emitfile-'
+    'project.react'         : 'compiler/#-referenceerror-react-is-not-defined-'
+    files                   : ''
+    assets                  : 'assets/'
+    compiler                : 'compiler/'
+    'deploy.repo'           : ''
+    # api:
+    #     404: "#{ MAIN }api/not-found/"
+    #     401: "#{ MAIN }api/unauthorized/"
+    #     403: "#{ MAIN }api/forbidden/"
+    #     toString: -> "#{ MAIN }api/"

 for k,v of DOCS
     if typeof v is 'object'


Server: fix error emission

Server: fix error emission

--- components/Server.coffee
+++ components/Server.coffee
@@ -36,7 +36,7 @@
         @outPorts.server.send(server)
         @outPorts.server.disconnect()
     catch e
-      sendError("Cannot listen to port #{port} : #{e.message}")
+      @sendError("Cannot listen to port #{port} : #{e.message}")

   closeServer: (port) ->
     unless @server[port]


Hackfixing around a limitation in BFS.

Hackfixing around a limitation in BFS.

--- src/natives/main.coffee
+++ src/natives/main.coffee
@@ -795,7 +795,9 @@
             rs.async_op (resume_cb, except_cb) ->
               fs.open filepath, 'r', (e, fd) ->
                 if e?
-                  if e.code == 'ENOENT'
+                  # XXX: BrowserFS hack. BFS doesn't support the code attribute
+                  # on errors yet.
+                  if e.code == 'ENOENT' || e.type == 404
                     except_cb ()-> rs.java_throw rs.get_bs_class('Ljava/io/FileNotFoundException;'), "#{filepath} (No such file or directory)"
                   else
                     except_cb ()-> throw e


Added necessary stubs to native_methods to prevent undefined errors

--- src/natives/main.coffee
+++ src/natives/main.coffee
@@ -1064,6 +1064,7 @@
         o 'getSystemGMTOffsetID()L!/lang/String;', (rs) ->
             null # XXX may not be correct
       ]
+    net: {}
   sun:
     font:
       FontManager: [
@@ -1329,3 +1330,5 @@
               # docs.
               gLong.fromNumber(-1)
         ]
+    net:
+      spi: {}


Refactoring.

Refactoring.

--- app/client/js/firebase.coffee
+++ app/client/js/firebase.coffee
@@ -78,13 +78,13 @@
   onUserLogin: (user) ->
     @userRef = @userRefOf user
     @setUserLastOnlineOnDisconnect_()
-    @listenServerChanges_ user
+    @fetchAndListenServerChanges_ user

   ###*
     @private
   ###
   setUserLastOnlineOnDisconnect_: ->
-    @userRef.child('user/lastOnline')
+    @userRef.child 'user/lastOnline'
       .onDisconnect()
       .set window.Firebase.ServerValue.TIMESTAMP

@@ -92,11 +92,10 @@
     @param {Object} user Firebase user.
     @private
   ###
-  listenServerChanges_: (user) ->
+  fetchAndListenServerChanges_: (user) ->
     userJustLogged = true
     @userRef.on 'value', (snap) =>
       return if @isLocalChange_
-      console.log 'onServerValue'
       val = snap.val()

       serverUserExists = val?.user?.uid
@@ -115,8 +114,7 @@

     , (error) ->
       # TODO: Report to server.
-      if goog.DEBUG
-        console.log 'The read failed: ' + error.code
+      console.log 'The read failed: ' + error.code if goog.DEBUG

   ###*
     @param {Object} user Firebase user.


give separate error for archive in progress vs completed

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -407,10 +407,12 @@
 		PackManager.getPackFromIndex doc_id, pack_id, (err, result) ->
 			return callback(err) if err?
 			return callback new Error("pack not found in index") if not result?
-			if result.inS3?
-				callback new Error("pack archiving already in progress")
+			if result.inS3
+				return callback new Error("pack archiving already done")
+			else if result.inS3?
+				return callback new Error("pack archiving already in progress")
 			else
-				callback()
+				return callback()

 	markPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) ->
 		logger.log {project_id, doc_id}, "marking pack as archive in progress status"


Remove PromiseRenderer from discussion listing

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -6,7 +6,6 @@
 {getErrors} = require './lib/validations'
 talkClient = require 'panoptes-client/lib/talk-client'
 Paginator = require './lib/paginator'
-PromiseRenderer = require '../components/promise-renderer'
 SingleSubmitButton = require '../components/single-submit-button'
 upvotedByCurrentUser = require './lib/upvoted-by-current-user'
 Moderation = require './lib/moderation'
@@ -38,6 +37,7 @@
     editingTitle: false
     reply: null
     moderationOpen: false
+    boards: []

   getDefaultProps: ->
     location: query: page: 1
@@ -70,6 +70,17 @@
             query: @props.location.query

       @setComments(@props.location.query.page ? 1)
+
+    if @props.user?
+      talkClient
+        .type 'roles'
+        .get
+          user_id: @props.user.id
+          section: ['zooniverse', @state.discussion.section]
+          is_shown: true
+          page_size: 100
+        .then (roles) =>
+          @setState {roles}

   commentsRequest: (page) ->
     {board, discussion} = @props.params
@@ -106,6 +117,14 @@
     @discussionsRequest(discussion)
       .then (discussion) =>
         @setState {discussion: discussion[0]}
+
+        talkClient
+          .type 'boards'
+          .get
+            section: discussion[0].section
+            page_size: 100
+          .then (boards) =>
+            @setState {boards}

   onUpdateComment: (textContent, subject, commentId) ->
     {discussion} = @props.params
@@ -285,16 +304,14 @@
                     <input name="locked" type="checkbox" defaultChecked={discussion?.locked}/>

-                  <PromiseRenderer promise={talkClient.type('boards').get({section: discussion.section, page_size: 100})}>{(boards) =>
-

-
Board:

-                      <select defaultValue={discussion.board_id}>
-                        {boards.map (board, i) =>
-                          <option key={board.id} value={board.id}>{board.title}</option>
-                          }
-                      </select>
-

-                  }</PromiseRenderer>
+

+
Board:

+                    <select defaultValue={discussion.board_id}>
+                      {@state.boards.map (board, i) =>
+                        <option key={board.id} value={board.id}>{board.title}</option>
+                        }
+                    </select>
+

                   <SingleSubmitButton type="submit" onClick={@onEditSubmit}>Update</SingleSubmitButton>
                 </form>}
@@ -357,9 +374,7 @@
               <Link to="#{baseLink}users/#{@props.user.login}">{@props.user.display_name}</Link>

@{@props.user.login}

-            <PromiseRenderer promise={talkClient.type('roles').get(user_id: @props.user.id, section: ['zooniverse', discussion.section], is_shown: true, page_size: 100)}>{(roles) =>
-              <DisplayRoles roles={roles} section={discussion.section} />
-            }</PromiseRenderer>
+            <DisplayRoles roles={@state.roles} section={discussion.section} />

           <CommentBox


Show the red left-hand-side border for all lines with errors on them.

--- js/inner/config_diagnostic.coffee
+++ js/inner/config_diagnostic.coffee
@@ -13,6 +13,9 @@
     # Keep track of the end positions of errors, so we don't have
     # to search the list of errors more than once.
     @errors_by_end = {}
+    # Keep track of which lines have errors on them.
+    # (semantically a set; all entries will be `true`)
+    @lines_with_errors = {}
     # Iterate over the errors (sorted by end positions so that the flags
     # are displayed in order) and do the following things:
     # * Save them in @errors_by_end according to the line they end on,
@@ -35,13 +38,17 @@
       # than the one we've seen, respectively.
       @first = Math.min @first, err.start.line
       @last = Math.max @last, err.end.line
+      # Mark all the lines that make up this error to be highlighted
+      # on the edge.
+      for i in [err.start.line..err.end.line]
+        @lines_with_errors[i] = true
     # Create a list of lines with associated data for easier rendering.
     @lines = (@ann.split '\n')
     .map (pieces, ix) =>
       line: ix + 1
       errors: @errors_by_end[ix]
       pieces: pieces
-      has_errors: @errors_by_end[ix]?.length > 0
+      has_errors: !!@lines_with_errors[ix]
     # And narrow the lines to three before and three after.
     .filter (_, ix) =>
       ix >= @first - 3 and ix <= @last + 3


comments hash functions for clarity on seed value

--- js/src/crypto/HashFunction.coffee
+++ js/src/crypto/HashFunction.coffee
@@ -12,9 +12,11 @@
   # Author: rbuckheit
   #

+  # sha 256
   SHA_256 = (data) ->
     return btoa(forge.md.sha256.create().update(atob(data)).digest().data)

+  # murmur3 128 bit with seed of 0.
   MURMUR3_128 = (string) ->
     return murmurhash3.x86.hash128(string)


placed search back in

placed search back in

--- app/models/appointment.coffee
+++ app/models/appointment.coffee
@@ -30,7 +30,7 @@
     search: (name) ->
       @store.query "company", makeQuery(name).toParams()
     among: -> @store.findAll "company"
-    # accessor: "appointment-company-field"
+    accessor: "appointment-company-field"
     proxyKey: "name"
     async: true


[grunt] remove not needed declarations from Nunjucks task

--- tasks/templates.coffee
+++ tasks/templates.coffee
@@ -36,14 +36,11 @@
   i18nTools = new I18nTools()
   { getLocalesNames, getLocaleProps, getLocaleDir, getLangcode, getRegioncode, isoLocale } = new I18nTools()

-  gettext = options.i18n.gettext
-  localesList = getLocalesNames(locales)
-
   # =======================
   # Config l10n of Nunjucks
   # =======================

-  localesList.forEach (currentLocale) =>
+  getLocalesNames(locales).forEach (currentLocale) =>
     localeProps   = getLocaleProps(locales, currentLocale)
     localeDir     = getLocaleDir(localeProps, baseLocale, baseLocaleAsRoot)
     localizedData = options.data(currentLocale)


Add missing keys to rendered arrays

Add missing keys to rendered arrays

--- app/pages/create-project.cjsx
+++ app/pages/create-project.cjsx
@@ -140,7 +140,7 @@

These images will be uploaded during after last step of this process, which could take a long time depending on how many you select. Make sure you’ve got a steady internet connection. You’ll have an opportunity to review and refine your selection here before continuing.

           {for manifestFilename of wizardData.manifests
-
+
               {manifestFilename}
               <button onClick={@removeManifest.bind this, manifestFilename}>×</button>

@@ -149,7 +149,8 @@

-                {{column}	 for column in MANIFEST_COLUMNS}
+                {for column in MANIFEST_COLUMNS
+                  {column}	}

@@ -199,11 +200,11 @@

-            {for line in @state.log
+            {for line, i in @state.log
               if line instanceof Error
-
{line.toString()}

+
{line.toString()}

               else
-
{line}
}
+
{line}
}

       </Route>
@@ -241,7 +242,7 @@

           {for filename, i in filenames
-

+

               {filename}
               {unless files[i]?
                  }
@@ -251,7 +252,7 @@

         {for column in MANIFEST_COLUMNS[1...]
-          {metadata?[column] ? ?}	}
+          {metadata?[column] ? ?}	}

           <button onClick={@removeSubjects.bind this, filenames...}>×</button>


Show time

Show time

--- bin/heyu-run.coffee
+++ bin/heyu-run.coffee
@@ -6,7 +6,7 @@

 print "# Heyu-run #{VERSION} by Pjotr Prins"
 time=new Date()
-print "#",time.toLocaleString()
+print "#",time.toTimeString()

 load('lib/util.js')
 load('lib/statemachine.js')


Typos and Working Compile

Typos and Working Compile

Fixed a handful of typos, and updated the compile command to work
with the new usage.

There is a failing test, addressing that next.

--- bin/kdc-plus.coffee
+++ bin/kdc-plus.coffee
@@ -17,10 +17,9 @@
 #
 # The function called when `kdc-plus compile` is used.
 compile = (appPath, unknownArgs..., opts={}, log=console.error) ->
-  if typeof appPath isnt 'string' then [appPath, opts] = [opts, undefined]
-  log 'Error: Not Implemented'
-  process.exit 1
+  if typeof appPath is 'object' then [opts, appPath] = [appPath, undefined]

+  appPath ?= process.cwd()
   appPath = path.resolve appPath
   loadOpts =
     validate: true
@@ -52,22 +51,26 @@
     else
       opts.commonjs = false

-    # Remember, coffee defaults to on. So we allow the option of turning it off
-    if opts.coffee is false
-      opts.coffee = opts.coffee
-    else if manifest.coffee is false
-      opts.coffee = manifest.coffee
-
+    if opts.coffee? or manifest.coffee?
+      opts.coffee = opts.coffee ? manifest.coffee
+    else
+      opts.coffee = false

     if vwarns? then log warning for warning in vwarns
     if vfails?
       log failure for failure in vfails
       return process.exit 1

-    cwd = process.cwd()
+    # Take all of our files and make them relative to our appPath if
+    # they are relative.
     files = manifest.source.blocks.app.files
-    files[i] = path.join(appPath, file) for file,i in files
+    for file,i in files
+      # Skip this file if it's an /absolute/path
+      if file[0] is path.sep then continue
+      files[i] = path.resolve path.join appPath, file

+    # Our cjs is a multi-file loader, so allow it to load if the caller wants
+    # cjs, otherwise use our default loader
     if opts.commonjs
       cjsopts = extensions: []
       cjsopts.extensions.push '.coffee' if opts.coffee
@@ -75,6 +78,7 @@
     else
       loader    = new LoadMulti files

+    # Add our coffee transform
     if opts.coffee
       loader.transform (file, ext) ->
         if ext is '.coffee' then return new CoffeeTransform()
@@ -96,6 +100,8 @@
       log "Error Saving Compiled KDApp: #{err.message}"
       process.exit 1

+    # Now that we've declared everything, pipe our loader to our "outer",
+    # which is the final destination for our stream.
     loader.pipe outer

@@ -116,15 +122,13 @@
   compileCmd.option '-f, --file <file>', 'Choose the output file'
   compileCmd.option '-c, --coffee', 'CoffeeScript support'
   compileCmd.option '-n, --commonjs', 'Commonjs support'
-  compileCmd.action ->
-    console.log 'Compile Command'
-    compile args..., log
+  compileCmd.action -> compile arguments..., log

   # Install command and opts
   installCmd  = program.command 'install'
   installCmd.description 'Install the KDApp dependencies, if any.'
   installCmd.option '--production', 'Install production dependencies only'
-  installCmd.action (cmd) -> install cmd, log
+  installCmd.action -> install arguments..., log

   # Outdated command and opts
   outdatedCmd = program.command 'outdated'
@@ -155,7 +159,8 @@
   #   kdc-plus outdated --production ./
   # `args.length == 2` for the first command, `args.length == 0` for the
   # second command.. which seems odd. So, i am using rawArgs instead.
-  if program.rawArgs.length == 2 then compile coffee: true, log
+  if program.rawArgs.length == 2
+    compile process.cwd(), [], coffee: true, log

@@ -164,7 +169,7 @@
 # Install any of the dependencies specified in the manifest, and also the
 # corrisponding dependency files *(package.json, bower.json, etc)*.
 install = (appPath, unknownArgs..., opts={}, log=console.error) ->
-  if typeof appPath isnt 'string' then [appPath, opts] = [opts, undefined]
+  if typeof appPath is 'object' then [opts, appPath] = [appPath, undefined]
   log 'Error: Not Implemented'
   process.exit 1

@@ -175,7 +180,7 @@
 # outdated checks if any of the dependencies specified in the manifest, and
 # also the corrisponding dependency files *(package.json, bower.json, etc)*.
 outdated = (appPath, unknownArgs..., opts={}, log=console.error) ->
-  if typeof appPath isnt 'string' then [appPath, opts] = [opts, undefined]
+  if typeof appPath is 'object' then [opts, appPath] = [appPath, undefined]
   log 'Error: Not Implemented'
   process.exit 1


catch parser errors from JSONStream and exit on the first such

--- bin/precious.coffee
+++ bin/precious.coffee
@@ -89,6 +89,10 @@
     parser.on "data", (data) ->
       (ephemeris convenient data).stdout.pipe process.stdout

+    parser.on "error", (err) ->
+      console.error "Bad JSON, parser error: ".red + err.message
+      process.exit(1)
+

   # The rest of the options are just for convenience / variety.


added double click to full screen video player

--- coffee/_player.coffee
+++ coffee/_player.coffee
@@ -209,6 +209,9 @@

 videoContainer.find(".ExpandButton").on "click", (e) ->
       $("#video-container").toggleClass "expanded"
+
+videoContainer.find("#video_player").on "dblclick", (e) ->
+      $("#video-container").toggleClass "expanded"

 $('#PlayerContainer .progress-bg').on 'mousemove', (e) ->
     if videojs('video_player').currentTime() != 0
@@ -226,4 +229,4 @@
         $('#PlayerContainer .mouse-time').css({'margin-left': (margin) + 'px'})

 $('#PlayerContainer .progress-bg').on 'mouseout', (e) ->
-    $('#PlayerContainer .mouse-time').hide()+    $('#PlayerContainer .mouse-time').hide()


More fuging. Fugueing? Something like that.

--- apps/fugue/corvid.coffee
+++ apps/fugue/corvid.coffee
@@ -1,35 +1,29 @@
+util = require 'util'
 nextId = 0

 module.exports = (makeId = -> nextId++) ->
-  behaviorFinder = (obj) ->
-    (info) ->
-      (info = {looseMatch: info}) if typeof info is 'string'
-
-      return undefined unless typeof info is 'object'
-
-      {looseMatch, name, id} = info
-
-      lookupBehavior = obj.db.lookupBehavior.bind obj
-
-      return o if id and o = obj.db.o[id]
-      return o if name and o = obj.db.names[name]
-      return o if o = lookupBehavior looseMatch
-      return undefined
-
-      return  (id and obj.db.o[id]) or
-              (name and obj.db.names[name]) or
-              (lookupBehavior looseMatch)
-
   class Referent
     constructor: (@db) ->
       @id = makeId()
       @behaviors = {}
+      @as = {}
+
       @db.registerRef this

-    addBehavior: (behavior) ->
-      self = {}
+    addBehavior: (behavior, name = behavior.name) ->
+      if util.isFunction name
+        name = name()
+
+      self = euclidic: this
       Object.setPrototypeOf self, behavior
       @behaviors[behavior.id] = self
+
+      if name
+        @as[behavior.name] = self
+
+      behavior.as.behavior.init self
+
+    delBehavior: (behavior) -> delete @behaviors[behavior.id]

     withBehavior: (behavior, methodName, args) ->
       self = @behaviors[behavior.id]
@@ -57,29 +51,37 @@

     relate: (subject, object, @params = {}) ->
       new Relation
-        relationship: @id
-        subject: subject.id
-        object: object.id
+        @rel = this
+        @sub = subject
+        @obj = object

     toString: ->
-      "#{@subj.name} #{@name} #{@obj.name}"
+      "#{@subj.name} #{} #{@obj.name}"

   class Db
     constructor: (@namespace, @prefix = '$') ->
       @names = {}
       @o = {}
-      @relationships = {}
+      @rel =
+        bySub: {}
+        byRel: {}
+        byObj: {}
       @initDb()
+
+    registerRef: (ref) ->
+      @o[ref.id] = ref
+
+    registerRel: (rel) ->
+      @rel.bySub[rel.sub.id] = rel
+      @rel.byRel[rel.rel.id] = rel
+      @rel.byObj[rel.obj.id] = rel

     initDb: ->
       @create 'sys'
       @create 'root'
       @create 'behavior', Behavior
       @create 'relation', Relation
-
-    addBehavior: (pkg) ->
-      @names.behavior.spawn pkg

     relate: (subj, rel, obj) ->
       rel.relate subj, obj
@@ -90,22 +92,28 @@
       if name
         @addName o, name

-    addName: (o, name) ->
-      ns = @names
+    _addName = (ns, name, value, prefix) ->
       [path..., name] = name.split '.'

+      if prefix
+        path[0] = prefix + path[0]

       for part in path
         ns = (ns[part] or= {})

-      ns[name] = o
+      ns[name] = value

-    util:
-      findOrCreate: ({name}) ->
-        if not name
-          throw new Error 'Can not look for empty (or falsy) name'
+    addName: (o, name) ->
+      _addName @names, name, o
+
+      if @prefix
+        _addName module, name, o, @prefix

-        @names[name] or @create arguments[0]
+    findOrCreate: ({name}) ->
+      if not name
+        throw new Error 'Can not look for empty (or falsy) name'
+
+      @names[name] or @create arguments[0]

   return {
       Referent: Referent


be verbose about files added

be verbose about files added

--- test/create_zip.coffee
+++ test/create_zip.coffee
@@ -21,7 +21,7 @@
     do (file) ->
       stack.push (cb) ->
         zip.add file, (err) ->
-          t.error err, 'Files are added to the queue'
+          t.error err, "#{file} added to the queue"
           cb()

   async.parallel stack, (err) ->


tests: cleanup karma tasks

tests: cleanup karma tasks

--- test/karma.conf.coffee
+++ test/karma.conf.coffee
@@ -1,8 +1,6 @@
 # Karma configuration
 module.exports = (config) ->
   config.set
-
-    basePath: "../"

     frameworks: [
       "jasmine"


refactor dependencies property to camelcase

--- source/injector.coffee
+++ source/injector.coffee
@@ -83,7 +83,7 @@
     # Recurse down the prototype chain
     if SuperClass? then @_mapDependencies SuperClass.constructor::, deps
     # Add dependencies of current value
-    deps[key] = id for key, id of value.Dependencies
+    deps[key] = id for key, id of value.dependencies
     return deps

   _resolveValue: (path) ->
@@ -136,7 +136,7 @@
         for dependent in @_dependents
           # Loop over their Dependencies and override the one this mapping
           # is managing if it exists (it should)
-          dependencies = dependent.Dependencies ? {}
+          dependencies = dependent.dependencies ? {}
           for key, id of dependencies
             if id is @_id
               dependent[key] = value


DE823 - Field appearing by default fixed.

--- c3/app/assets/javascripts/circs/s2_report_changes/g8_breaksFromCaringSummary.coffee
+++ c3/app/assets/javascripts/circs/s2_report_changes/g8_breaksFromCaringSummary.coffee
@@ -6,11 +6,11 @@
   showAdditionalBreaksWrap = () ->
     $("#additionalBreaksWrap").slideDown 0

+  if $("#" + additionalBreaksY).prop('checked')
+    showAdditionalBreaksWrap()
+
   if not $("#" + additionalBreaksY).prop('checked')
     hideAdditionalBreaksWrap(text)
-
-  if not $("#" + additionalBreaksN).prop('checked')
-    showAdditionalBreaksWrap()

   $("#" + additionalBreaksY).on "click", ->
     showAdditionalBreaksWrap()


use referrer from client.

use referrer from client.

For now, does not remember the first refererer, need to put this in a cookie

--- src/remote/services/track.coffee
+++ src/remote/services/track.coffee
@@ -68,9 +68,11 @@
     return false unless event?
     # @trackWithAnalytics(event,params)
     config = RemoteConfigStore.getState().remoteConfig
+    referrer = RemoteConfigStore.getState().clientConfig?.track?.referrer

     method               = 'post'
     params.event        ?= event
+    params.referrer      = referrer if referrer?
     params.hull_app_id   = config?.appId
     params.hull_app_name = config?.data?.app?.name


Better names, continued

Better names, continued

--- client/landing/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/Views/InboxNewMessageBar.coffee
@@ -5,7 +5,7 @@
     @addSubView newMessageButton = new KDButtonView
       title     : "New Message"
       style     : "clean-gray left"
-      testPath  : "inbox-new"
+      testPath  : "inbox-new-message-button"
       callback  : => @createNewMessageModal()

     @addSubView @refreshButton = new KDButtonView


fix returns

fix returns

--- extension/chrome/js/src/background.coffee
+++ extension/chrome/js/src/background.coffee
@@ -6,13 +6,17 @@
 			url: details.url
 			type: type
 		chrome.tabs.sendMessage tab.id, ret, () ->
+			return
+		return
+	return

 chrome.webNavigation.onReferenceFragmentUpdated.addListener (details) ->
 	send details, 'fragment'
+	return

 chrome.webNavigation.onDOMContentLoaded.addListener (details) ->
 	send details, 'loaded'
-
+	return

 chrome.extension.onMessage.addListener (request, sender, sendResponse) ->
 	#console.log(sender.tab "from a content script:" + sender.tab.url :


Fix for retrieving the Steam name when tipping to a Steam64 ID

--- bot.iced
+++ bot.iced
@@ -295,6 +295,7 @@
 			if shibe[0] is "#" and shibe[1..].length is 17
 				shibeID = shibe[1..] # Without the proceeding '#'
 				await Users_collection.findOne {"id": shibeID}, defer(err, registeredShibe)
+				shibe = registeredShibe.name
 			else
 				await Users_collection.findOne {"name": shibe}, defer(err, registeredShibe)
 			if err


data-attributes tests

data-attributes tests

--- test/twine_test.coffee
+++ test/twine_test.coffee
@@ -385,6 +385,108 @@
         setupView(testView, context = {})
       , "Twine error: Unable to create function on SPAN node with attributes eval='myArray.push(\"stuff)'"

+  suite "data- attributes", ->
+    test "data-bind should map to bind", ->
+      testView = "

"
+      node = setupView(testView, key: "value")
+      assert.equal node.innerHTML, "value"
+
+    test "data-bind-show should map to bind-show", ->
+      testView = "

"
+      node = setupView(testView, key: false)
+      assert.equal node.className, "hide"
+
+    test "data-bind-class should map to bind-class", ->
+      testView = "

"
+      node = setupView(testView, key: true)
+      assert.equal node.className, "cls"
+
+    test "data-bind-attribute should map to bind-attribute", ->
+      testView = "

"
+      node = setupView(testView, key: true)
+      assert.equal node.getAttribute('a'), 'true'
+
+    test "data-bind-checked should map to bind-checked", ->
+      testView = "<input type=\"checkbox\" data-bind-checked=\"key\">"
+      node = setupView(testView, context = key: true)
+      assert.isTrue node.checked
+
+    test "data-bind-placeholder should map to bind-placeholder", ->
+      testView = "
"
+      node = setupView(testView, context = key: "val")
+      assert.equal node.placeholder, "val"
+
+    test "data-bind-readonly should map to bind-readonly", ->
+      testView = "<input type=\"text\" data-bind-readonly=\"key\">"
+      node = setupView(testView, context = key: true)
+      assert.isTrue node.readOnly
+
+    test "data-bind-unsafe-html should map to bind-unsafe-html", ->
+      testView = "

"
+      node = setupView(testView, key: "&")
+      assert.equal node.innerHTML, "&"
+
+    test "data-bind-src should map to bind-src", ->
+      testView = '
'
+      node = setupView(testView, key: "image.jpg")
+      assert.match node.src, /\/image\.jpg$/
+
+    test "data-bind-event-click should map to bind-event-click", ->
+      testView = "

"
+      node = setupView(testView, context = fn: @spy())
+
+      $(node).click()
+      assert.isTrue context.fn.calledOnce
+
+    test "data-bind-event-submit should map to bind-event-submit", ->
+      testView = "<form data-bind-event-submit=\"fn()\"></form>"
+      node = setupView(testView, context = fn: @spy())
+
+      triggerEvent node, "submit"
+      assert.isTrue context.fn.calledOnce
+
+    test "data-bind-event-change should map to bind-event-change", ->
+      testView = "<input data-bind-event-change=\"fn()\" value=\"old\">"
+      node = setupView(testView, context = fn: @spy())
+      node.value = "new"
+
+      triggerEvent node, "change"
+      assert.isTrue context.fn.calledOnce
+
+    test "data-bind-event-error should map to bind-event-error", ->
+      testView = ""
+      node = setupView(testView, context = fn: @spy())
+
+      triggerEvent node, "error"
+      assert.isTrue context.fn.calledOnce
+
+    test "data-bind-event-done should map to bind-event-done", ->
+      testView = "<form bind-event-done=\"fn()\"></form>"
+      node = setupView(testView, context = fn: @spy())
+
+      triggerEvent node, "done"
+      assert.isTrue context.fn.calledOnce
+
+    test "data-bind-event-fail should map to bind-event-fail", ->
+      testView = "<form data-bind-event-fail=\"fn()\"></form>"
+      node = setupView(testView, context = fn: @spy())
+
+      triggerEvent node, "fail"
+      assert.isTrue context.fn.calledOnce
+
+    test "data-define should map to define", ->
+      testView = "

"
+      setupView(testView, context = {})
+
+      assert.equal context.key, "value"
+
+    test "data-eval should map to eval", ->
+      testView = ""
+
+      setupView(testView, context = {myArray: []})
+
+      assert.deepEqual ["stuff"], context.myArray
+
   suite "refresh", ->
     test "should defer calls and refresh once", ->
       setupView("", {})


Add methods to serialize and parameterize query strings

--- src/scripts/helpers/links.coffee
+++ src/scripts/helpers/links.coffee
@@ -17,3 +17,21 @@
           url += ":#{data.page}" if data.page

       return url
+
+    serializeQuery: (query) ->
+      queryString = {}
+
+      query.split('?').pop().split('&').forEach (prop) ->
+        item = prop.split('=')
+        queryString[decodeURIComponent(item.shift())] = decodeURIComponent(item.shift())
+
+      return queryString
+
+    param: (obj) ->
+      str = []
+
+      for p of obj
+        if obj.hasOwnProperty(p)
+          str.push("#{encodeURIComponent(p)}=#{encodeURIComponent(obj[p])}")
+
+      return str.join("&")


Removing unused import.

Removing unused import.


Disable the entire update spec.

Disable the entire update spec.

--- spec/about-spec.coffee
+++ spec/about-spec.coffee
@@ -62,12 +62,12 @@
     it "does not show the view", ->
       expect(workspaceElement).not.toContain('.about-release-notes')

-  describe "with an update", ->
+  xdescribe "with an update", ->
     it "shows the view when the update is made available", ->
       triggerUpdate()
       expect(workspaceElement).toContain('.about-release-notes')

-    xdescribe "clicking on the status", ->
+    describe "clicking on the status", ->
       it "opens the release notes", ->
         triggerUpdate()
         expect(workspaceElement).toContain('.about-release-notes')


Don't double-trace client RPC methods.

Don't double-trace client RPC methods.

--- src/server/browser_server.coffee
+++ src/server/browser_server.coffee
@@ -244,8 +244,6 @@

 RPCMethods =
     setAttribute : (targetId, attribute, value) ->
-        if Config.traceProtocol
-            @logRPCMethod('setAttribute', [targetId, attribute, value])
         if !@browserLoading
             target = @nodes.get(targetId)
             if attribute == 'src'
@@ -255,8 +253,6 @@
             target.setAttribute(attribute, value)

     processEvent : (event, specifics) ->
-        if Config.traceProtocol
-            @logRPCMethod('processEvent', [event, specifics])
         for own nodeID, value of specifics
             node = @nodes.get(nodeID) # Should cache these for the restore.
             node.__oldValue = node.value


Make event positions relative to node offset

--- src/pointer-tool.coffee
+++ src/pointer-tool.coffee
@@ -1,3 +1,5 @@
+$ = require 'jquery'
+
 utils = window.Curve

 module.exports =
@@ -29,8 +31,9 @@
     obj

   _hitWithIntersectionList: (e) ->
-    @_evrect.x = e.clientX
-    @_evrect.y = e.clientY
+    {left, top} = $(svg.node).offset()
+    @_evrect.x = e.clientX - left
+    @_evrect.y = e.clientY - top
     nodes = svg.node.getIntersectionList(@_evrect, null)

     obj = null


probably saving flow data to device

probably saving flow data to device

--- src/flow-deployer.coffee
+++ src/flow-deployer.coffee
@@ -86,7 +86,12 @@
   setupDevice: (flow, callback=->) =>
     @setupDeviceForwarding (error, result) =>
       return callback(error) if error?
-      @setupMessageSchema flow.nodes, callback
+      @setupMessageSchema flow.nodes (error, result) =>
+        return callback(error) if error?
+        @addFlowToDevice flow, callback
+
+  addFlowToDevice:(flow, callback) =>
+    @meshbluHttp.updateDangerously @flowUuid, $set: flow: flow, callback

   setupDeviceForwarding: (callback=->) =>
     messageHook =