Only wait for success if we're restarting due to an update attempt.

If we're restarting for binds/mounts then we're already in a non-working state, and starting the new supervisor may have to wait for bootstrap to complete (which takes an indefinite amount of time to complete, meaning the timeout was killing the new container before it could bootstrap in some cases)

--- src/supervisor-update.coffee
+++ src/supervisor-update.coffee
@@ -24,7 +24,7 @@
 	getContainerId().then (containerId) ->
 		docker.getContainer(containerId).inspectAsync()

-startNewSupervisor = (currentSupervisor) ->
+startNewSupervisor = (currentSupervisor, waitForSuccess = true) ->
 	console.log('Creating supervisor container:', localImage)
 	docker.createContainerAsync(
 		Image: localImage
@@ -39,6 +39,8 @@
 			Binds: config.supervisorContainer.Binds
 		)
 	.then (container) ->
+		if !waitForSuccess
+			return
 		# check that next supervisor outputs config.successMessage before this supervisor exits
 		container.attachAsync({ stream: true, stdout: true, stderr: false, tty: true })
 		.then (stream) ->
@@ -76,7 +78,14 @@
 	# Check all the expected binds and volumes exist, if not then start a new supervisor (which will add them correctly)
 	if !_.isEqual(expectedVolumes, actualVolumes) or !_.isEqual(expectedBinds, actualBinds)
 		console.log('Supervisor restart (for binds/mounts)')
-		startNewSupervisor(currentSupervisor)
+		restart = ->
+			# When restarting for just binds/mounts we just wait for the supervisor updates to start.
+			startNewSupervisor(currentSupervisor, false)
+			.catch (err) ->
+				console.error('Error restarting', err)
+				# If there's an error just keep attempting to restart to get to a useful state.
+				restart()
+		restart()

 # This is a promise that resolves when we have fully initialised.
 exports.initialised = currentSupervisor.then (currentSupervisor) ->


Fix up the html parser to work with managed attachments

--- src/trix/utilities/html_parser.coffee
+++ src/trix/utilities/html_parser.coffee
@@ -60,12 +60,13 @@
         unless nodeIsExtraBR(node)
           @appendString("\n", getAttributes(node))
       when "img"
-        attributes = { contentType: "image", url: node.getAttribute("src") }
-        identifier = node.getAttribute("data-trix-identifier") if node.hasAttribute("data-trix-identifier")
-        for key in ["width", "height"] when value = node.getAttribute(key)
-          attributes[key] = value
-
-        @appendAttachment(attributes, getAttributes(node), identifier)
+        attributes = getAttributes(node)
+        attributes.url = node.getAttribute("src")
+        # TODO: we lose the true content type here
+        attributes.contentType = "image"
+        attributes.identifier = identifier if identifier = node.getAttribute("data-trix-identifier")
+        attributes[key] = value for key in ["width", "height"] when value = node.getAttribute(key)
+        @appendAttachment(attributes)

   appendBlock: (attributes = {}) ->
     text = new Trix.Text
@@ -76,17 +77,22 @@
     text = Trix.Text.textForStringWithAttributes(string, attributes)
     @block.text = @block.text.appendText(text)

-  appendAttachment: (attachmentAttributes, attributes, identifier) ->
-    if attachment = @attachments?.findWhere(url: attachmentAttributes.url)
-      if attachmentAttributes.width?
-        attributes.width = attachmentAttributes.width
-        attributes.height = attachmentAttributes.height
+  appendAttachment: (attributes) ->
+    if managedAttachment = @findManagedAttachmentByAttributes(attributes)
+      attachment = managedAttachment.attachment
     else
-      attachment = new Trix.Attachment attachmentAttributes
-      attachment.setIdentifier(identifier) if identifier?
+      attachment = new Trix.Attachment

     text = Trix.Text.textForAttachmentWithAttributes(attachment, attributes)
     @block.text = @block.text.appendText(text)
+
+  findManagedAttachmentByAttributes: (attributes) ->
+    return unless @attachments
+    {identifier, url} = attributes
+    if identifier?
+      @attachments.findWhere({identifier})
+    else if url?
+      @attachments.findWhere({url})

   getDocument: ->
     new Trix.Document @blocks


single place for validating mixin hooks

single place for validating mixin hooks

--- src/mixinfactory.coffee
+++ src/mixinfactory.coffee
@@ -16,9 +16,19 @@
   @MutabilityError: class MutabilityError extends Error
   @ArgumentError: class ArgumentError extends Error

+  @mixinhook_keys: [
+    'premixin_hook'
+    'postmixin_hook'
+  ]
+
   @validate_mixin: (mixin) ->
     unless mixin instanceof @
       throw new TypeError "Expected a Mixin instance"
+    for mixinhook_key in @mixinhook_keys
+      hook = mixin[mixinhook_key]
+      if hook? && !_.isFunction hook
+        throw new TypeError "Expected a function for #{mixinhook_key}"
+

   @from_obj: (obj) ->
     unless _.isObject(obj) && !_.isArray(obj)


Remove console.log from delta.coffee

Remove console.log from delta.coffee

The SDK caller should have full control over what console output the
SDK generates. The caller can subscribe to these events and log
whatever they so desire.

--- src/models/delta.coffee
+++ src/models/delta.coffee
@@ -141,12 +141,10 @@
     @timeoutId = setTimeout @restartBackoff.backoff.bind(@restartBackoff), Delta.streamingTimeoutMs

   _onError: (err) ->
-    console.error 'Nylas DeltaStream error:', err
     @emit('error', err)
     @restartBackoff.reset()

   _restartConnection: (n) ->
-    console.log "Restarting Nylas DeltaStream connection (attempt #{n + 1}):", @request?.href
     @emit('info', "Restarting Nylas DeltaStream connection (attempt #{n + 1}): #{@request?.href}")
     @close()
     @open()


Rename API key EnvVar to follow convention

Hubot EnvVars are, by convention, faux namespaced w/ HUBOT_

--- src/forecast.coffee
+++ src/forecast.coffee
@@ -2,7 +2,7 @@
 #   A hubot script to alert for inclement weather
 #
 # Configuration:
-#   LIST_OF_ENV_VARS_TO_SET
+#   HUBOT_FORECAST_KEY
 #
 # Commands:
 #   none
@@ -251,8 +251,8 @@

   fetchForecast = (callback) ->

-    forecastKey = process.env.FORECAST_KEY
     location = 'LAT,LNG'
+    forecastKey = process.env.HUBOT_FORECAST_KEY
     exclude = 'hourly,daily,flags'

     base_url = "https://api.forecast.io/forecast/#{forecastKey}/#{location}"


set referer to the current window's location.href when creating a new iframe

--- src/zombie/dom_iframe.coffee
+++ src/zombie/dom_iframe.coffee
@@ -29,7 +29,7 @@
       contentWindow = active
     # Need to bypass JSDOM's window/document creation and use ours
     open = createHistory(parentWindow.browser, focus)
-    contentWindow = open(name: frame.name, parent: parentWindow, url: url)
+    contentWindow = open(name: frame.name, parent: parentWindow, url: url, referer: parentWindow.location.href)
     return contentWindow

 # This is also necessary to prevent JSDOM from messing with window/document


Add collision test with sun for skyline

Add collision test with sun for skyline

--- src/scenery/City.coffee
+++ src/scenery/City.coffee
@@ -211,12 +211,20 @@
   @addBackground(0, @level.visibleHeight - 310, d, .5)

 levelGenerator.defineElement 'cityFrontTop', ->
-  bb = Crafty.e('2D, WebGL, bigBuildingTop, ColorEffects, RiggedExplosion').attr(z: -20).crop(1, 1, 446, 6 * 32)
-  bb.colorOverride('#001fff', 'partial')
+  bb = Crafty.e('2D, WebGL, bigBuildingTop, RiggedExplosion').attr(z: -20).crop(1, 1, 446, 6 * 32)
   @add(0, @level.visibleHeight - 1200, bb)
+  cs = Crafty.e('2D, SunBlock')
+    .attr(
+      x: bb.x + 10
+      y: bb.y + 20
+      z: -10
+      h: 650
+      w: bb.w - 45
+    )
+  bb.attach(cs)

   for i in [0...3]
-    floor = Crafty.e('2D, WebGL, bigBuildingLayer, ColorEffects').attr(z: -20).crop(1, 0, 446, 4 * 32)
+    floor = Crafty.e('2D, WebGL, bigBuildingLayer').attr(z: -20).crop(1, 0, 446, 4 * 32)
     floor.attr(x: bb.x, y: bb.y + bb.h + (i * floor.h))
     bb.attach(floor)

@@ -756,7 +764,6 @@
     @unbind('BridgeDamage')
     @unbind('BridgeCollapse')

-
   deck: (gradient, flipped, attr) ->
     aspectR = 1024 / 180
     attr.h = attr.w / aspectR


Set start to 1

Set start to 1

--- src/scenes/Intro.coffee
+++ src/scenes/Intro.coffee
@@ -57,7 +57,7 @@
   Crafty('Player').each ->
     @reset()
     @one 'Activated', ->
-      Crafty.enterScene('Game', checkpoint: 3) # checkpoint: value
+      Crafty.enterScene('Game') # checkpoint: value
   Crafty.e('Delay').delay ->
       Crafty.enterScene('Scores')
     , 20000


upgrade logging

upgrade logging

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -249,9 +249,11 @@
     @pref.prefs.clearUserPref('brace-all')

     for table in tables
+      @debug('attachdatabase: backing up', table)
       Zotero.DB.query("alter table betterbibtex.#{table} rename to \"-#{table}-\"")

     ### clean slate ###
+
     Zotero.DB.query('create table betterbibtex.keys (itemID primary key, citekey not null, citekeyFormat)')

     Zotero.DB.query("
@@ -296,18 +298,20 @@
     ### migrate data where needed ###

     if Zotero.DB.tableExists('betterbibtex."-keys-"')
-      Zotero.BetterBibTeX.debug('Migrate betterbibtex.keys')
+      @debug('attachdatabase: migrating keys')
       if Zotero.DB.columnsHash('betterbibtex."-keys-"').pinned
+        @debug('attachdatabase: migrating old-sty;e keys')
         Zotero.BetterBibTeX.debug('Upgrading betterbibtex.keys')
         Zotero.DB.query('insert into betterbibtex.keys (itemID, citekey, citekeyFormat)
                         select itemID, citekey, case when pinned = 1 then null else ? end from betterbibtex."-keys-"', [@pref.get('citekeyFormat')])
       else
+        @debug('attachdatabase: migrating keys')
         Zotero.BetterBibTeX.debug('Copying betterbibtex.keys')
         Zotero.DB.query('insert into betterbibtex.keys (itemID, citekey, citekeyFormat)
                         select itemID, citekey, citekeyFormat from betterbibtex."-keys-"')

     if Zotero.DB.tableExists('betterbibtex."-autoexport-"')
-      Zotero.BetterBibTeX.debug('Copying betterbibtex.autoexport')
+      @debug('attachdatabase: migrating autoexport')
       Zotero.DB.query('insert into betterbibtex.autoexport (
         collection,
         path,
@@ -337,6 +341,7 @@
     ### cleanup ###

     for table in Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name like '-%-'") || []
+      @debug('attachdatabase: deleting', table)
       Zotero.DB.query("drop table if exists betterbibtex.\"#{table}\"")

     Zotero.DB.commitTransaction() unless tip


activity/flux: make sure realtime message is working correctly

--- client/activity/lib/flux/actions/realtime/actioncreators.coffee
+++ client/activity/lib/flux/actions/realtime/actioncreators.coffee
@@ -58,10 +58,36 @@

 bindNotificationEvents = ->

-  _dispatchFn = _createUnreadCountDispatchFn kd.singletons.reactor
+  { reactor, socialapi } = kd.singletons
+
+  # create specialized functions for dispatching into reactor. These functions
+  # will be bound to the reactor instance that is passed.
+  _dispatchFn = _createUnreadCountDispatchFn reactor
+  _loadMessageFn = _createLoadMessageFn reactor

   kd.singletons.notificationController
-    .on 'MessageAddedToChannel', _dispatchFn
+    .on 'MessageAddedToChannel', (payload) ->
+      { channel, channelMessage } = payload
+
+      # TODO: FIXME
+      # The reason we are doing these extra fetches is that right now backend
+      # doesn't send us extra information about channel (it already shouldn't).
+      # But for private messages the events from `SocialChannel` instance
+      # itself is not arriving to participants. So we are making sure that
+      # everything is in place before doing anything. This will be fixed once
+      # instance events of `SocialChannel` instances for private messages are
+      # fixed. ~Umut
+      socialapi.channel.byId { id: channel.id }, (err, _channel) ->
+        return  if err
+
+        bindChannelEvents _channel
+        socialapi.message.byId { id: channelMessage.id }, (err, _message) ->
+          return  if err
+
+          bindMessageEvents _message
+          _loadMessageFn { channel, channelMessage: _message }
+          _dispatchFn { unreadCount: payload.unreadCount, channel }
+
     .on 'MessageRemovedFromChannel', _dispatchFn
     .on 'RemovedFromChannel', _dispatchFn
     .on 'ReplyAdded', _dispatchFn


home/integrations: don't cleanup to default values on cancel

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/home/lib/integrations/components/gitlab/container.coffee
+++ client/home/lib/integrations/components/gitlab/container.coffee
@@ -69,8 +69,9 @@

   onRemoveCancel: ->

-    state = @makeInitialState()
-    state.enabled = getGroup().config.gitlab.enabled
+    state = _.assign {}, @state,
+      enabled: getGroup().config.gitlab.enabled
+      isConfirmModalOpen: no

     @setState state


Alphabetize requires

Alphabetize requires

--- app._coffee
+++ app._coffee
@@ -1,6 +1,6 @@
+config = require './config'
 express = require 'express'
 app = require('streamline-express') express()
-config = require './config'

 ## MIDDLEWARE:


Make the pre-formatted code regular expression a lot more permissive

--- packages/rocketchat-highlight/highlight.coffee
+++ packages/rocketchat-highlight/highlight.coffee
@@ -5,31 +5,41 @@

 class Highlight

-	# If message starts with ```, replace it for text formatting
 	constructor: (message) ->

 		if _.trim message.html
-			# Separate text in code blocks and non code blocks
-			msgParts = message.html.split(/(```.*\n[\s\S]*?\n```)/)

-			for part, index in msgParts
-				# Verify if this part is code
-				codeMatch = part.match(/```(.*)\n([\s\S]*?)\n```/)
-				if codeMatch?
-					# Process highlight if this part is code
-					lang = codeMatch[1]
-					code = _.unescapeHTML codeMatch[2]
-					if lang not in hljs.listLanguages()
-						result = hljs.highlightAuto code
+			# Count occurencies of ```
+			count = (message.html.match(/```/g) || []).length
+
+			if count
+
+				# Check if we need to add a final ```
+				if (count % 2 > 0)
+					console.log "Even"
+					message.html = message.html + "\n```"
+					message.msg = message.msg + "\n```"
+
+				# Separate text in code blocks and non code blocks
+				msgParts = message.html.split(/(```\w*[\n\ ]?[\s\S]*?```+?)/)
+
+				for part, index in msgParts
+					# Verify if this part is code
+					codeMatch = part.match(/```(\w*)[\n\ ]?([\s\S]*?)```+?/)
+					if codeMatch?
+						# Process highlight if this part is code
+						lang = codeMatch[1]
+						code = _.unescapeHTML codeMatch[2]
+						if lang not in hljs.listLanguages()
+							result = hljs.highlightAuto code
+						else
+							result = hljs.highlight lang, code
+						msgParts[index] = "
```<br></span>" + result.value + "<span class='copyonly'><br>```
"
 					else
-						result = hljs.highlight lang, code
-					msgParts[index] = "
```<br></span>" + result.value + "<span class='copyonly'><br>```
"
-				else
-					# Escape html and fix line breaks for non code blocks
-					msgParts[index] = part
+						msgParts[index] = part

-			# Re-mount message
-			message.html = msgParts.join('')
+				# Re-mount message
+				message.html = msgParts.join('')

 		return message


change order of args

change order of args

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -117,7 +117,7 @@
       when "linux"
         shell.showItemInFolder(path)

-    @isVerbose(path, "Revealed")
+    @isVerbose("Revealed", path)

   openFolder: (path) ->
     # Custom file manager
@@ -136,9 +136,9 @@
       when "linux"
         shell.openItem(path)

-    @isVerbose(path, "Opened")
+    @isVerbose("Opened", path)

-  isVerbose: (fullPath, verb) ->
+  isVerbose: (verb, fullPath) ->
     if atom.config.get('browse.notify') is true
       # Get base name
       path = require 'path'


fix lyrics without chords

fix lyrics without chords

--- src/opensong.coffee
+++ src/opensong.coffee
@@ -198,11 +198,12 @@

         # attach the line again in front (we cut it off in the while loop)
         lyricsLines.unshift textLine if textLine isnt 'undefined'
-      when " "
-        dataObject.lines.push {lyrics: line.substr(1)}
       when ";"
         dataObject.lines.push {comments: line.substr(1)}
       else
-        console?.log "no support for: #{line}"
+        if /^[ 0-9]/.test(line)
+          dataObject.lines.push {lyrics: [line.substr(1)]}
+        else
+          console?.log "no suppport for '#{line}'"
   dataModel


Use full filename (`%DOC.%EXT`) as default for `custom_toolchain`

--- lib/config.coffee
+++ lib/config.coffee
@@ -26,7 +26,7 @@
                   and `%DOC` will be replaced by the main LaTeX file which \
                   is either automatically detected or manually set'
     type: 'string'
-    default: '%TEX %ARG %DOC && %BIB %DOC && %TEX %ARG %DOC && %TEX %ARG %DOC'
+    default: '%TEX %ARG %DOC.%EXT && %BIB %DOC && %TEX %ARG %DOC.%EXT && %TEX %ARG %DOC.%EXT'
   compiler:
     title: 'LaTeX compiler to use'
     order: 4


Use restructuring assignment

Use restructuring assignment

--- src/property.coffee
+++ src/property.coffee
@@ -75,8 +75,8 @@
   registerGlobal: ->
     return if @_isRegistered
     @_isRegistered = true
-    @definition.globalDependencies.forEach (dep) =>
-      { name, type, subname } = dep
+
+    @definition.globalDependencies.forEach ({ name, type, subname }) =>
       switch type
         when "singular"
           updateItemBinding = (before, after) =>


protocol: Also send components from Library, add component:componentsready

Needed for fbp-spec

--- src/protocol.coffee
+++ src/protocol.coffee
@@ -35,20 +35,35 @@
         out.push m
       return out

-    debug 'attempting to list componenents'
-
+    debug 'attempting to list components'
     components = []
     for name, part of proto.coordinator.participants
       return if part.component in components # Avoid duplicates
       components.push part.component
-      component =
+      info =
         name: part.component
         description: part.label or "" # FIXME: should be .description instead?
         icon: part.icon
         subgraph: false # TODO: implement
         inPorts: getPorts part, 'inports'
         outPorts: getPorts part, 'outports'
-      proto.transport.send 'component', 'component', component, ctx
+      proto.transport.send 'component', 'component', info, ctx
+
+    for name, cmd of proto.coordinator.library.components
+      # XXX: we don't know anything about these apart from the name and command
+      # when it has been instantiated first time we'll know the correct values, and should re-send
+      return if name in components
+      components.push name
+      info =
+        name: name
+        description: cmd
+        icon: null
+        subgraph: false
+        inPorts: []
+        outPorts: []
+
+    proto.transport.send 'component', 'componentsready', components.length, ctx
+    debug 'sent components', components.length

   else if sub == 'component' and cmd == 'getsource'
     return debug 'ERROR: cannot get source for #{payload.name}' if payload.name != defaultGraph


Changes on AJAX module

Changes on AJAX module

--- src/quo.ajax.coffee
+++ src/quo.ajax.coffee
@@ -1,5 +1,5 @@
 ###
-  QuoJS 2.0
+  QuoJS 2.0.3
   (c) 2011, 2012 Javi Jiménez Villar (@soyjavi)
   http://quojs.tapquo.com
 ###
@@ -72,7 +72,7 @@
                 , settings.timeout)
             xhr
         else
-            console.error "ERROR: Unable to make jsonp synchronous call."
+            console.error "QuoJS.ajax: Unable to make jsonp synchronous call."

     $$.get = (url, data, success, dataType) ->
         url += $$.serializeParameters(data)
@@ -111,7 +111,7 @@
                 _xhrSuccess _parseResponse(xhr, settings), xhr, settings
                 return
         else
-            _xhrError "QuoJS » $$.ajax", xhr, settings
+            _xhrError "QuoJS.ajax: Unsuccesful request", xhr, settings
             return

     _xhrSuccess = (response, xhr, settings) ->
@@ -123,8 +123,8 @@
         return

     _xhrHeaders = (xhr, settings) ->
-        settings.headers["Content-Type"] = settings.contentType  if settings.contentType
-        settings.headers["Accept"] = MIME_TYPES[settings.dataType]  if settings.dataType
+        settings.headers["Content-Type"] = settings.contentType if settings.contentType
+        settings.headers["Accept"] = MIME_TYPES[settings.dataType] if settings.dataType
         for header of settings.headers
             xhr.setRequestHeader header, settings.headers[header]
         return
@@ -132,7 +132,7 @@
     _xhrTimeout = (xhr, settings) ->
         xhr.onreadystatechange = {}
         xhr.abort()
-        _xhrError "QuoJS » $$.ajax : timeout exceeded", xhr, settings
+        _xhrError "QuoJS.ajax: Timeout exceeded", xhr, settings
         return

     _parseResponse = (xhr, settings) ->
@@ -143,7 +143,7 @@
                     response = JSON.parse(response)
                 catch error
                     response = error
-                    _xhrError "Parse Error", xhr, settings
+                    _xhrError "QuoJS.ajax: Parse Error", xhr, settings
             else response = xhr.responseXML    if settings.dataType is "xml"
         response


console.error

console.error

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -904,7 +904,7 @@
       else if _.isString(contents)
         updateSVGContents(elt, [contents])
       else
-        console.log 'ERROR: updateSVGContents', elt, contents
+        console.error 'updateSVGContents', elt, contents
         throw "Must wrap contents #{contents} as array or string"

     rxt.svg_mktag = mktag = (tag) ->


no need for scala-lang hack since we depend on it now

--- lib/ensime.coffee
+++ lib/ensime.coffee
@@ -90,14 +90,14 @@
     @stoppedCommands = new CompositeDisposable
     @stoppedCommands.add atom.commands.add 'atom-workspace', "ensime:update-ensime-server", => updateEnsimeServer()

-    @stoppedCommands.add atom.commands.add 'atom-workspace', "ensime:start", => @startAnEnsime()
+    @stoppedCommands.add atom.commands.add 'atom-workspace', "ensime:start", => @selectAndStartAnEnsime()

   addCommandsForStartedState: ->
     @startedCommands = new CompositeDisposable
     @startedCommands.add atom.commands.add 'atom-workspace', "ensime:stop", => @stopEnsime()
-    @stoppedCommands.add atom.commands.add 'atom-workspace', "ensime:start", => @startAnEnsime()
+    @stoppedCommands.add atom.commands.add 'atom-workspace', "ensime:start", => @selectAndStartAnEnsime()

     @startedCommands.add atom.commands.add scalaSourceSelector, "ensime:mark-implicits", => @markImplicits()
     @startedCommands.add atom.commands.add scalaSourceSelector, "ensime:unmark-implicits", => @unmarkImplicits()
@@ -132,16 +132,7 @@
     @autotypecheckControllers = new WeakMap

     @addCommandsForStoppedState()
-    # https://discuss.atom.io/t/ok-to-use-grammar-cson-for-just-file-assoc/17801/11
-    Promise.resolve(
-      atom.packages.isPackageLoaded('language-scala') && atom.packages.activatePackage('language-scala')
-    ).then (languageScalaPkg) ->
-      if languageScalaPkg
-        # language-scala is loaded and activated
-      else
-        # language-scala is not loaded
-        grammar = atom.packages.resolvePackagePath('Ensime') + path.sep + 'grammars-hidden' + path.sep + 'scala.cson'
-        atom.grammars.loadGrammar grammar
+

@@ -263,7 +254,7 @@

   # to start an ensime, first we need to to select a .ensime file under any project path
-  startAnEnsime: ->
+  selectAndBootAnEnsime: ->
     read = require('fs-readdir-recursive')

     dirs = atom.project.getPaths()
@@ -272,12 +263,10 @@
       read(dir, (f) ->
         f == ".ensime").map((f) -> dir + path.sep + f))

-    new SelectFile(dotEnsimes, (selected) =>
-        console.log('selected' + selected)
-      )
-
-    # TODO: modal dialog to select which .ensime to use
-    # @initProject()
+    new SelectFile(dotEnsimes, (selectedDotEnsime) =>
+      @bootDotEnsime(selectedDotEnsime)
+    )
+

   stopEnsime: ->
     if not atom.config.get('Ensime.runServerDetached')


Remove unneeded auto-orient call

Remove unneeded auto-orient call

We're not dealing with rotation

--- lib/imager.coffee
+++ lib/imager.coffee
@@ -20,7 +20,6 @@
       .mosaic()
       .resize(size.width, size.height)
       .trim()
-      .autoOrient()
       .gravity('Center')
       .extent(size.width, size.height)
       .background(face.color)


attach message object into envelope

attach message object into envelope

--- src/response.coffee
+++ src/response.coffee
@@ -10,6 +10,7 @@
     @envelope =
       room: @message.room
       user: @message.user
+      message: @message

   # Public: Posts a message back to the chat source
   #


:new: Register ViewProvider using the new API

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -7,8 +7,8 @@
 class Linter

   constructor: ->
-    @View = new (require './panel-old')(this)
-    @ViewPanel = atom.workspace.addBottomPanel item: @View.Root, visible: false
+    @View = new (require './linter-view')(this)
+#    @ViewPanel = atom.workspace.addBottomPanel item: @View.Root, visible: false
     @StatusBar = null
     @MessagesProject = new Map
     @ActiveEditor = atom.workspace.getActiveTextEditor()
@@ -18,11 +18,10 @@
     @Subscriptions = new CompositeDisposable
     @EditorLinters = new Map # An object of Editor <--> Linter
     @Linters = [] # I </3 coffee-script
-    @Subscriptions.add atom.views.addViewProvider
-      modelConstructor: Panel
-      viewConstructor: PanelView
+    @Subscriptions.add atom.views.addViewProvider Panel, (Model)->
+      ( new PanelView() ).registerModel(Model)
     @Panel = new Panel this
-    @Panel.registerView atom.views.getView @Panel
+    @PanelView = atom.views.getView @Panel
     @Subscriptions.add atom.workspace.onDidChangeActivePaneItem (Editor) =>
       @ActiveEditor = Editor
       ActiveLinter = @getActiveEditorLinter()


fix logger

fix logger

--- lib/logger.coffee
+++ lib/logger.coffee
@@ -5,27 +5,21 @@
   constructor: (@subject, @options={}) ->

   write: (subject, options={}) ->
-    message = subject
-    message = "foo #{message} #{key}=\"#{val}\"" for key, val of options
-    console.log message
+    message = ("#{key}=\"#{val}\"" for key, val of coffee.helpers.merge(subject:subject, options))
+    console.log message.join(" ")

   log: (opts={}, cb) ->
     options = coffee.helpers.merge(@options, opts)
     if cb?
       logger = new Logger(@subject, options)
       start  = new Date().getTime()
-      write @subject, coffee.helpers.merge(options, start:start)
+      @write @subject, coffee.helpers.merge(options, start:start)
       cb(logger.log)
       finish  = new Date().getTime()
       elapsed = (finish - start)
-      write subject, coffee.helpers.merge(options, finish:finish, elapsed:elapsed)
+      @write @subject, coffee.helpers.merge(options, finish:finish, elapsed:elapsed)
     else
-      write subject, options
-
-log = (subject, options={}) ->
-  message = subject
-  message = "#{message} #{key}=\"#{val}\"" for key, val of options
-  console.log message
+      @write @subject, options

 module.exports = (subject, options={}, cb=null) ->
   new Logger(subject).log(options, cb)


:art: refactor

:art: refactor

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -609,9 +609,6 @@
       timeout: timeout

   finish: ->
-    @vimState.hoverSearchCounter.reset()
-    @subscriptions?.dispose()
-    @subscriptions = null
     @matches?.destroy()
     @matches = null

@@ -620,30 +617,34 @@
     if @matches.isEmpty()
       @flash getVisibleBufferRange(@editor), timeout: 100 # screen beep.
       atom.beep()
+    else
+      if @isComplete()
+        @visit(cursor)
+        @vimState.searchHistory.save(@input)
+      else
+        @visit(null)
+
+    if @isComplete()
       @finish()
-      return
-
+
+  # If cursor is passed, it move actual move, otherwise
+  # just visit matched point with decorate other matching.
+  visit: (cursor=null) ->
     current = @matches.get()
-    @scroll(current)
-    if @isComplete()
+    current.visit()
+    if cursor
+      # In isearch, we already displayed hover and flash
+      # So I prefer being silent when landing.
+      @showEffect(current) unless @isIncrementalSearch()
       cursor.setBufferPosition(current.getStartPoint())
-      @vimState.searchHistory.save(@input)
-      @finish()
     else
       @matches.show()
-
-  scroll: (match) ->
-    match.visit()
-    @showEffect(match)
+      @showEffect(current)

   isIncrementalSearch: ->
     settings.get('enableIncrementalSearch') and @isSearch()

   showEffect: (match) ->
-    # In isearch, we already show hover and flash
-    # So in UX perspective, I prefer being silent on confirm(isComplete()).
-    return if (@isComplete() and @isIncrementalSearch())
-
     if settings.get('flashOnSearch')
       @flash match.range, timeout: settings.get('flashOnSearchDurationMilliSeconds')

@@ -657,8 +658,8 @@
   scan: (cursor) ->
     return [] if @input is ''
     cursorPosition = cursor.getBufferPosition()
+    ranges = []
     pattern = @getPattern(@input)
-    ranges = []
     @editor.scan pattern, ({range}) ->
       ranges.push range

@@ -699,6 +700,13 @@
   isRepeatLastSearch: (input) ->
     input in ['', (if @isBackwards() then '?' else '/')]

+  finish: ->
+    if @isIncrementalSearch()
+      @vimState.hoverSearchCounter.reset()
+    @subscriptions?.dispose()
+    @subscriptions = null
+    super
+
   onConfirm: (@input) => # fat-arrow
     if @isRepeatLastSearch(@input)
       unless @input = @vimState.searchHistory.get('prev')
@@ -723,7 +731,7 @@

   onCommand: (direction) => # fat-arrow
     return if @matches.isEmpty()
-    @scroll @matches.get(direction)
+    @visit @matches.get(direction)

 class SearchBackwards extends Search
   @extend()
@@ -789,6 +797,7 @@
 CloseBrackets = [')', '}', ']']
 AnyBracket = new RegExp(OpenBrackets.concat(CloseBrackets).map(_.escapeRegExp).join("|"))

+# TODO: refactor.
 class BracketMatchingMotion extends SearchBase
   @extend()
   inclusive: true


:fire: Motion::at utility #73

:fire: Motion::at utility #73

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -94,7 +94,7 @@
   # Cursor motion wrapper
   # -------------------------
   moveCursorUp: (cursor) ->
-    unless @at('FirstScreenRow', cursor)
+    if cursor.getScreenRow() isnt 0
       cursor.moveUp()

   moveCursorDown: (cursor) ->
@@ -120,12 +120,8 @@
     _.times @getCount(), ->
       fn({stop}) unless stopped

-  at: (where, cursor) ->
-    switch where
-      when 'EOF' then pointIsAtEndOfBuffer(@editor, cursor.getBufferPosition())
-      when 'FirstScreenRow' then cursor.getScreenRow() is 0
-      when 'LastBufferRow' then cursor.getBufferRow() is getLastBufferRow(@editor)
-      when 'LastScreenRow' then cursor.getScreenRow() is getLastScreenRow(@editor)
+  cursorIsAtEndOfFile: (cursor) ->
+    pointIsAtEndOfBuffer(@editor, cursor.getBufferPosition())

   # Debuging purpose
   # -------------------------
@@ -240,9 +236,9 @@
       cursor.getBeginningOfNextWordBufferPosition(wordRegex: @wordRegex)

   moveCursor: (cursor) ->
-    return if @at('EOF', cursor)
+    return if @cursorIsAtEndOfFile(cursor)
     @countTimes =>
-      if cursor.isAtEndOfLine() and not @at('EOF', cursor)
+      if cursor.isAtEndOfLine() and not @cursorIsAtEndOfFile(cursor)
         cursor.moveDown()
         cursor.moveToFirstCharacterOfLine()
       else
@@ -275,7 +271,7 @@
       point = @getNext(cursor)
       if point.isEqual(cursor.getBufferPosition())
         cursor.moveRight()
-        if cursor.isAtEndOfLine() and not @at('EOF', cursor)
+        if cursor.isAtEndOfLine() and not @cursorIsAtEndOfFile(cursor)
           cursor.moveDown()
           cursor.moveToBeginningOfLine()
         point = Point.min(getEofBufferPosition(@editor), @getNext(cursor))


Better handling of mapping with no map exported

Previously, the mapping defaulted to `{}` which led to a useless cache
of keys mapping to `undefined`.  Now the mapping defaults to
`undefined`, bypassing the cache system.

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -946,7 +946,7 @@
     }
     @settings = {...@settings, dirname: path.dirname @filename} if @filename?
   parse: (@map) ->
-    unless typeof @map in ['function', 'object']
+    unless typeof @map in ['function', 'object', 'undefined']
       console.warn "Mapping file #{@filename} returned invalid mapping data of type (#{typeof @map}): should be function or object"
       @map = null
     if isPreact @map
@@ -954,6 +954,8 @@
       @map = null
   lookup: (key, context) ->
     ## `key` normally should be a String (via `AutoDrawing::parse` coercion).
+    ## Don't do anything if this is an empty mapping.
+    return unless @map?
     ## Check cache (used for static tiles).
     if (found = @cache.get key)?
       return found
@@ -1058,7 +1060,8 @@
       #  console.log code
       pirates?.settings = @settings
       @module = runWithMapping @, -> require filename
-      super @module.default ? {}
+      #console.log filename, @module
+      super @module.default
       @walkDeps filename
     else
       ## But if file has been explicitly loaded (e.g. in browser),


Some code reshuffling and debugging on number.coffee

--- lib/number.coffee
+++ lib/number.coffee
@@ -23,11 +23,6 @@

 quicktest = module? and not module.parent
 log = if quicktest then (str) -> console.log str else (str) ->
-
-# Some helper functions for debugging.
-
-rdump = (s) -> "#{if s then s.into([]).join('|') else '[]'}"
-dump = (s) -> rdump s?.reverse()

 # ----

@@ -105,7 +100,7 @@
       [tp1, tp2] = [op1.constructor, op2.constructor]
       throw new Error "operands of types #{tp1} and #{tp2} not supported"

-  downcast = (x) ->
+  @downcast: (x) ->
     if x instanceof LongInt and x.cmp(BASE) < 0
       if x.digits?
         new CheckedInt x.digits.first() * x.sign
@@ -123,7 +118,7 @@
       namex = "#{name}__"
       operator name, (a, b) ->
         [x, y] = upcast a, b
-        downcast x[namex] y
+        @downcast x[namex] y

 # ----
@@ -193,6 +188,21 @@

   sgn: -> @sign

+  zeroes = BASE.toString()[1..]
+
+  rdump = (s) ->
+    if s
+      s.map((t) -> "#{zeroes[t.toString().length..]}#{t}").into([]).join('|')
+    else
+      '[]'
+  dump = (s) -> rdump s?.reverse()
+
+  ZERO = seq [0]
+  ONE  = seq [1]
+  TWO  = seq [2]
+
+  cleanup = (s) -> s?.reverse()?.dropWhile((x) -> x == 0)?.reverse() or null
+
   sqrt = (s) ->
     n = s.size()
     step = (r) ->
@@ -204,7 +214,7 @@
     if @sign == 0
       asNum 0
     else if @sign > 0
-      new LongInt 1, sqrt @digits
+      NumberBase.downcast new LongInt 1, sqrt @digits
     else
       throw new Error "expected a non-negative number, got #{this}"

@@ -221,12 +231,6 @@
     else
       @sign * cmp @digits, x.digits

-  ZERO = seq [0]
-  ONE  = seq [1]
-  TWO  = seq [2]
-
-  cleanup = (s) -> s?.reverse()?.dropWhile((x) -> x == 0)?.reverse() or null
-
   add = (r, s, c = 0) ->
     if c or (r and s)
       [r_, s_] = [r or ZERO, s or ZERO]
@@ -334,8 +338,6 @@
       asNum 0
     else
       new LongInt @sign * x.sign, mod @digits, x.digits
-
-  zeroes = BASE.toString()[1..]

   toString: ->
     parts = @digits?.reverse()?.dropWhile((d) -> d == 0)?.map (d) -> d.toString()
@@ -464,8 +466,14 @@
   show -> number(111111).div 37
   show -> number(111111111).div 37
   show -> number(111111111).div 12345679
+  show -> number(99980001).div 49990001

   log ''
   show -> number(111).mod 37
   show -> number(111112).mod 37
   show -> number(111111111).mod 12345679
+
+  log ''
+  show -> number(9801).sqrt()
+  show -> number(998001).sqrt()
+  show -> number(99980001).sqrt()


Up time-to-snapshot

Up time-to-snapshot

--- lib/render.coffee
+++ lib/render.coffee
@@ -85,6 +85,6 @@

 page.onLoadFinished = (status) ->
   loaded = page.evaluate -> !!document.body
-  setTimeout render, 300  if status is "success" or loaded is true
+  setTimeout render, 5000  if status is "success" or loaded is true

 page.open url


Fixed a small scoping issue with start_time.

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -27,7 +27,7 @@
   #   Run this runner.
   run: (callback) =>
     running = true
-    start_time
+    start_time = undefined

     # Our `@_fn` callback.
     done = =>


up the limit from 100 accounts per 6 hours to 5000

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -3751,7 +3751,7 @@
             else
                 cb()

-        # make sure this ip address hasn't requested more than 100
+        # make sure this ip address hasn't requested more than 5000
         # accounts in the last 6 hours (just to avoid really nasty
         # evils, but still allow for demo registration behind a wifi
         # router -- say)
@@ -3766,7 +3766,7 @@
                     if not value?
                         ip_tracker.set(key: client.ip_address, value:1, ttl:6*3600)
                         cb()
-                    else if value < 100
+                    else if value < 5000
                         ip_tracker.set(key: client.ip_address, value:value+1, ttl:6*3600)
                         cb()
                     else # bad situation


0 cost = go again, like before

0 cost = go again, like before

--- src/time-manager.coffee
+++ src/time-manager.coffee
@@ -27,26 +27,26 @@

 	tick: (callback) ->
 		if @targets.length > 0
-			target = @targets.pop()
+			[..., target] = @targets

-			winston.silly "begin tick '#{target.name} #{target.constructor.name}'"
+			winston.silly "begin tick '#{target.name} #{target.constructor.name}' #{target.x},#{target.y}"

 			Q.fcall -> target.tick()
 			.then (cost = 0) =>
-				nextTick = cost / (_.result target, 'tickRate')
+				rate = _.result target, 'tickRate'

-				# finite nextTick means this target should be scheduled again
-				if _.isFinite nextTick
+				# rate > 0 means this target should be scheduled again
+				if rate > 0
 					@adjustNextTicks -target.nextTick
-					target.nextTick = nextTick
+					target.nextTick = cost / rate

-				# otherwise, if infinite, it should never be scheduled
-				# logically, this means an infinite amount of time until next schedule
+				# otherwise, if 0, it should never be scheduled
+				# AKA an infinite amount of time until next schedule
 				else target.nextTick = Infinity

-				@add target
+				@add @targets.pop() if rate is 0 or cost isnt 0

-			.then -> winston.silly "end tick '#{target.name} #{target.constructor.name}'"
+			.then -> winston.silly "end tick '#{target.name} #{target.constructor.name}' #{target.x},#{target.y}"

 			.nodeify callback


Tiny refactor for code

Tiny refactor for code

--- main.coffee
+++ main.coffee
@@ -56,11 +56,10 @@
   [key] = Object.keys obj
   val = obj[key]

+  res = if "#{Number key}" is "NaN" then {} else []
   if key of target
-    res = if "#{Number key}" is "NaN" then {} else []
     res[key] = rescueObjStruct val, target[key]
   else
-    res = if "#{Number key}" is "NaN" then {} else []
     res[key] = val

   return merge target, res


Can't travel fighting, closes #369

Can't travel fighting, closes #369

--- main.coffee
+++ main.coffee
@@ -257,7 +257,7 @@
 	try
 		location = lib.game.getUserLocation.sync null, dbConnection, userid
 	catch e
-		console.log e.stack
+		console.error e.stack
 		location = lib.game.getDefaultLocation.sync null, dbConnection
 		lib.game.changeLocation.sync null, dbConnection, userid, location.id

@@ -295,8 +295,9 @@
 	userid = request.uonline.basicOpts.userid
 	to = request.param('to')

-	if lib.game.canChangeLocation.sync null, dbConnection, userid, to
-		lib.game.changeLocation.sync null, dbConnection, userid, to
+	unless lib.game.isInFight.sync null, dbConnection, userid
+		if lib.game.canChangeLocation.sync null, dbConnection, userid, to
+			lib.game.changeLocation.sync null, dbConnection, userid, to

 	response.redirect '/game/'


:smirk::sleepy: Updated at https://danielx.net/editor/

--- main.coffee
+++ main.coffee
@@ -13,14 +13,7 @@

 Template = require "./templates/app"

-# TouchScreen = require "./lib/touchscreen"
-# touchScreen = TouchScreen document.createElement "screen"
-# touchScreen.on "touch", (e) -> console.log e
-
 activeTool = require("./tools")(document).move
-console.log activeTool
-workspace = null
-context = null

 imageUrls = [
   "https://0.pixiecdn.com/sprites/138612/original.png"
@@ -43,8 +36,12 @@

   render: ->
     {width, height} = scene.getBoundingClientRect()
+
+    canvas = document.createElement("canvas")
     canvas.width = width
     canvas.height = height
+
+    context = canvas.getContext("2d")

     context.clearRect(0, 0, canvas.width, canvas.height)
     renderer.render(context, scene)
@@ -117,12 +114,7 @@

 document.body.appendChild Template app

-workspace = document.querySelector("workspace")
 scene = document.querySelector("scene")
-canvas = document.createElement("canvas")
-
-context = canvas.getContext("2d")
-

 Matrix::toCSS3Transform ?= ->


Fix static constant reference in brainstem collection

--- vendor/assets/javascripts/brainstem/brainstem-collection.coffee
+++ vendor/assets/javascripts/brainstem/brainstem-collection.coffee
@@ -19,7 +19,7 @@
     return (a, b) -> a.get(field) - b.get(field)

   @pickFetchOptions: (options) ->
-    _.pick options, Brainstem.Collection.OPTION_KEYS
+    _.pick options, @OPTION_KEYS

   #


debug min_height form redactor rails to minHeight

--- vendor/assets/javascripts/simple_form_extension/redactor.coffee
+++ vendor/assets/javascripts/simple_form_extension/redactor.coffee
@@ -8,11 +8,11 @@
       'unorderedlist', 'orderedlist', 'outdent', 'indent',
       'image', 'file','link', 'alignment', 'horizontalrule']
     removeEmpty: ['strong', 'em', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'div']
-    min_height: 250
+    minHeight: 250
     buttonSource: true
     replaceDivs: false
-    linebreaks: false
     toolbarFixed: false
+    cleanStyleOnEnter: true
     formattingAdd: [
       {
         tag: 'img'


Re-purpose existing REPL if it's already there

--- lib/atom-slime-repl-view.coffee
+++ lib/atom-slime-repl-view.coffee
@@ -17,8 +17,27 @@
     @createRepl()

+  # Make a new pane / REPL text editor, or find one
+  # that already exists
   createRepl: () ->
-    # Create a new pane
+    @editor = @replPane = null
+    editors = atom.workspace.getTextEditors()
+    for editor in editors
+      if editor.getPath() == '/tmp/repl.lisp-repl'
+        # We found the editor! Now search for the pane it's in.
+        allPanes = atom.workspace.getPanes()
+        for pane in allPanes
+          if editor in pane.getItems()
+            # Found the pane too!
+            @editor = editor
+            @replPane = pane
+            @editorElement = atom.views.getView(@editor)
+
+    if @editor and @replPane
+      @setupRepl()
+      return
+
+    # Create a new pane and editor if we didn't find one
     paneCurrent = atom.workspace.getActivePane()
     @replPane = paneCurrent.splitDown() #.splitRight
     # Open a new REPL there
@@ -27,6 +46,7 @@
       @editor = editor
       @editorElement = atom.views.getView(@editor)
       @setupRepl()
+

   # Set up the REPL GUI for use
   setupRepl: () ->


:art: We’re tracking the buffer, let's use it

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -199,7 +199,7 @@
   isCurrentFileBlackListed: =>
     blacklist = atom.config.get('autocomplete-plus.fileBlacklist')?.map((s) -> s.trim())
     return false unless blacklist? and blacklist.length
-    fileName = path.basename(@editor.getBuffer().getPath())
+    fileName = path.basename(@buffer.getPath())
     for blacklistGlob in blacklist
       return true if minimatch(fileName, blacklistGlob)


fix name issue

fix name issue

--- lib/breakpoint-item-view.coffee
+++ lib/breakpoint-item-view.coffee
@@ -33,7 +33,3 @@
       @scripts
         .getByName(breakpoint.scriptName)
         .then handleScriptLoad
-
-    else
-      @find('.path').text(script.name)
-      @removeClass('loading')


Workspace: Added initial content support for EditorPane.

--- client/app/CommonViews/workspace/panes/editorpane.coffee
+++ client/app/CommonViews/workspace/panes/editorpane.coffee
@@ -17,9 +17,12 @@
     , file

   createSingleEditor: ->
-    path = @files or "localfile:/Untitled.txt"
-    file = FSHelper.createFileFromPath path
-    @ace = @createEditorInstance file
+    path      = @files or "localfile:/Untitled.txt"
+    file      = FSHelper.createFileFromPath path
+    @ace      = @createEditorInstance file
+    {content} = @getOptions()
+    @ace.on "ace.ready", =>
+      @ace.editor.setValue content  if content

   createEditorTabs: ->
     @tabHandleContainer = new ApplicationTabHandleHolder


home/billing: make sure form is on viewport

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/home/lib/billing/components/paymentsection.coffee
+++ client/home/lib/billing/components/paymentsection.coffee
@@ -1,5 +1,6 @@
 kd = require 'kd'
 React = require 'react'
+{ findDOMNode } = require 'react-dom'
 { Grid, Row, Col } = require 'react-flexbox-grid'

 { Footer } = DashboardSection = require 'lab/DashboardSection'
@@ -49,7 +50,9 @@

   onToggleForm: ->
-    @setState { formVisible: not @state.formVisible }
+    @setState { formVisible: not @state.formVisible }, =>
+      if @state.formVisible
+        (findDOMNode @_form).scrollIntoView?()

   render: ->


fix KDEventEmitter#once - proper binding

--- client/landing/app/MainApp/notificationcontroller.coffee
+++ client/landing/app/MainApp/notificationcontroller.coffee
@@ -91,7 +91,7 @@
             view.destroy()
         else
           # ask chris if KD.remote.cacheable is good for this
-          KD.remote.api[subject.constructorName].one _id : subject.id, (err, post)->
+          KD.remote.api[subject.constructorName].one _id : subject.id, (err, post) ->
             # appManager.tell "Activity", "createContentDisplay", post
             KD.getSingleton('router').handleRoute "/Activity/#{post.slug}", state:post
             view.destroy()


updated msa url

updated msa url

--- src/utils/export.coffee
+++ src/utils/export.coffee
@@ -39,7 +39,7 @@

   shareLink: (that, cb) ->
     url = that.g.config.get "importURL"
-    msaURL = "http://biojs-msa.org/app/?seq="
+    msaURL = "http://msa.biojs.net/app/?seq="
     fCB = (link) ->
       fURL = msaURL + link
       if cb


removed .coffee bin from dist

removed .coffee bin from dist

--- dist/node/bin/pegcoffee.coffee
+++ dist/node/bin/pegcoffee.coffee
@@ -1,133 +0,0 @@
-#!/usr/bin/env node
-
-util = require 'util'
-fs   = require 'fs'
-PEG  = require 'pegjs'
-PEGjsCoffeePlugin = require '../lib/pegjs-coffee-plugin.js'
-
-# Helpers
-
-
-printVersion = -> util.puts("PEG.js CoffeeScript Plugin #{PEGjsCoffeePlugin.VERSION}")
-
-
-printHelp = ->
-  util.puts """
-    Usage: pegcoffee [options] [--] [<input_file>] [<output_file>]
-
-    Generates a parser from the PEG grammar specified in the <input_file> and
-    writes it to the <output_file>.
-
-    If the <output_file> is omitted, its name is generated by changing the
-    <input_file> extension to \".js\". If both <input_file> and <output_file> are
-    omitted, standard input and output are used.
-
-    Options:
-      -e, --export-var <variable>  name of the variable where the parser object
-                                   will be stored (default: \"module.exports\")
-          --cache                  make generated parser cache results
-          --track-line-and-column  make generated parser track line and column
-          --js                     use plain javascript in actions
-      -v, --version                print version information and exit
-      -h, --help                   print help and exit
-  """
-
-exitSuccess = -> process.exit(0)
-
-exitFailure = -> process.exit(1)
-
-abort = (message) ->
-  util.error message
-  exitFailure()
-
-# Arguments
-
-#Trim "node" and the script path.
-args = process.argv.slice 2
-
-isOption = (arg) -> /^-/.test(arg)
-
-nextArg = -> args.shift()
-
-# Files
-
-readStream = (inputStream, callback) ->
-  input = ""
-  inputStream.on("data", (data) -> input += data )
-  inputStream.on("end", -> callback input )
-
-
-# Main
-
-# This makes the generated parser a CommonJS module by default.
-exportVar = "module.exports"
-# Set the usage of CoffeeScript as default
-options =
-  cache:              false
-  trackLineAndColumn: false
-  js:                 false
-
-
-while args.length > 0 and isOption args[0]
-  switch (args[0])
-    when "-e", "--export-var"
-      nextArg()
-      if args.length is 0
-        abort "Missing parameter of the -e/--export-var option."
-      exportVar = args[0]
-
-    when "--js"
-      options.js = true
-
-    when "--cache"
-      options.cache = true
-
-    when "--track-line-and-column"
-      options.trackLineAndColumn = true
-
-    when "-v","--version"
-      printVersion()
-      exitSuccess()
-
-    when "-h", "--help"
-      printHelp()
-      exitSuccess()
-
-    when "--"
-      nextArg()
-
-    else abort "Unknown option: #{args[0]}."
-  nextArg()
-
-switch args.length
-  when 0
-    process.stdin.resume()
-    inputStream = process.stdin
-    outputStream = process.stdout
-
-  when 1, 2
-    inputFile = args[0]
-    inputStream = fs.createReadStream inputFile
-    inputStream.on "error", ->
-      abort "Can't read from file \"#{inputFile}\"."
-
-    outputFile =
-      if args.length is 1 then args[0].replace(/\.[^.]*$/, ".js") else args[1]
-    outputStream = fs.createWriteStream outputFile
-    outputStream.on "error", ->
-      abort "Can't write to file \"#{outputFile}\"."
-  else abort "Too many arguments."
-
-
-readStream inputStream, (input) ->
-  PEGjsCoffeePlugin.addTo PEG unless options.js
-  try
-    parser = PEG.buildParser(input, options)
-  catch e
-    if e.line? and e.column?
-      abort "#{e.line}:#{e.column}:#{e.message}"
-    else
-      abort e.message
-
-  outputStream.write "#{exportVar} = #{parser.toSource()};\n"
-  outputStream.end() if outputStream isnt process.stdout


Bugfix

Bugfix

--- lib/client/autoform-file.coffee
+++ lib/client/autoform-file.coffee
@@ -20,7 +20,7 @@
   @_interceptValue = (ctx) =>
     unless @_stopInterceptValue
       t = Template.instance()
-      if t.value.get() isnt false and t.value.get() isnt ctx.value
+      if t.value.get() isnt false and t.value.get() isnt ctx.value and ctx.value?.length > 0
         t.value.set ctx.value
         @_stopInterceptValue = true


:guardsman: Add guard on selection update after destruction

--- lib/color-buffer-element.coffee
+++ lib/color-buffer-element.coffee
@@ -77,6 +77,7 @@
       @updateSelections()

   updateSelections: ->
+    return if @editor.isDestroyed()
     for marker in @displayedMarkers
       view = @viewsByMarkers.get(marker)
       view.style.display = ''


Update for the new theme

Update for the new theme

--- lib/command-palette-view.coffee
+++ lib/command-palette-view.coffee
@@ -50,7 +50,7 @@
     $$ ->
       @li class: 'event', 'data-event-name': eventName, =>
         @span eventDescription, title: eventName
-        @div class: 'right', =>
+        @div class: 'pull-right', =>
           for binding in keyBindings[eventName] ? []
             @kbd binding, class: 'key-binding'


Skip stack entries from files in node_modules

--- lib/deprecation-cop-view.coffee
+++ lib/deprecation-cop-view.coffee
@@ -123,6 +123,9 @@

       # Empty when it was run from the dev console
       return unless fileName
+
+      # Continue to next stack entry if call is in node_modules
+      continue if fileName.includes(path.sep + "node_modules" + path.sep)

       for packageName, packagePath of packagePaths
         relativePath = path.relative(packagePath, fileName)


Clear cached views in a sensible spot

Clear cached views in a sensible spot

--- core/app/backbone/controllers/channels_controller.coffee
+++ core/app/backbone/controllers/channels_controller.coffee
@@ -59,8 +59,6 @@

       @restoreChannelView channel_id, => new ChannelView(model: channel)

-      @channel_views.clear()
-
   getChannelActivities: (username, channel_id) ->
     app.mainRegion.show(@channel_views)

@@ -71,8 +69,6 @@
       @restoreChannelView channel_id, =>
         activities = new ChannelActivities([],{ channel: channel })
         new ChannelActivitiesView(model: channel, collection: activities)
-
-      @channel_views.clear()

   getChannelFactForActivity: (username, channel_id, fact_id) ->
     @getChannelFact(username, channel_id, fact_id, true)
@@ -113,6 +109,7 @@
     $('body').scrollTo(@lastChannelStatus.scrollTop) if view == @lastChannelStatus?.view
     delete @lastChannelStatus
     @channel_views.renderCacheView(channel_id, new_callback()) if not view?
+    @channel_views.clear()

   makePermalinkEvent: (baseUrl) ->
     @permalink_event = @bindTo FactlinkApp.vent, 'factlink_permalink_clicked', (e, fact) =>


made properties private

made properties private

--- core/app/backbone/factlink/make_tooltip_for_view.coffee
+++ core/app/backbone/factlink/make_tooltip_for_view.coffee
@@ -1,16 +1,16 @@
 Backbone.Factlink ||= {}

 class TooltipCreator
-  constructor: (@$offsetParent, @positioning, @tooltipViewFactory)->
+  constructor: (@_$offsetParent, @positioning, @_tooltipViewFactory)->
     region_options = _.defaults fadeTime: 100, positioning
     @positionedRegion = new Backbone.Factlink.PositionedRegion region_options

   createTooltip: ($target) ->
     popoverOptions = _.defaults
-      contentView: @tooltipViewFactory(),
+      contentView: @_tooltipViewFactory(),
       @positioning

-    @positionedRegion.bindToElement $target, @$offsetParent
+    @positionedRegion.bindToElement $target, @_$offsetParent
     @positionedRegion.show new PopoverView popoverOptions
     @positionedRegion.updatePosition()


Separate command subscriptions

Separate command subscriptions

--- lib/mixed-indent-warning.coffee
+++ lib/mixed-indent-warning.coffee
@@ -7,28 +7,30 @@
   mixedIndentWarningView: null
   modalPanel: null
   subscriptions: null
+  commandSubscriptions: null
   markers: []

   config:
     liveUpdate:
       type: 'boolean'
-      default: false
+      default: true
       title: 'Live Update'
       description: 'Scan files all the time.'

   activate: (state) ->
-    # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
     @subscriptions = new CompositeDisposable
+    @commandSubscriptions = new CompositeDisposable

     @beginScans()

     # Register command that toggles this view
-    @subscriptions.add atom.commands.add 'atom-workspace', 'mixed-indent-warning:toggle': => @toggle()
+    @commandSubscriptions.add atom.commands.add 'atom-workspace', 'mixed-indent-warning:toggle': => @toggle()

-    @subscriptions.add atom.commands.add 'atom-workspace', 'mixed-indent-warning:file': => @scanActiveFile()
+    @commandSubscriptions.add atom.commands.add 'atom-workspace', 'mixed-indent-warning:file': => @scanActiveFile()

   deactivate: ->
     @subscriptions.dispose()
+    @commandSubscriptions.dispose()

   toggle: ->
     console.log 'MixedIndentWarning was toggled!'


only change title when it differs

only change title when it differs

--- scripts/views/workspace/sidebar/toc-branch.coffee
+++ scripts/views/workspace/sidebar/toc-branch.coffee
@@ -216,6 +216,7 @@
       # Use `.toJSON().title` instead of `.get('title')` to support
       # TocPointerNodes which inherit their title if it is not overridden
       title = prompt('Edit Title:', @model.toJSON().title)
-      if title then @model.set('title', title)
+      if title and title != @model.toJSON().title
+        @model.set('title', title)

       @renderModelOnly()


Use Slugify directly in app/core/utils.coffee (#61)

--- app/core/utils.coffee
+++ app/core/utils.coffee
@@ -1,4 +1,4 @@
-slugify = _.str?.slugify ? _.string?.slugify # TODO: why _.string on client and _.str on server?
+slugify = require('underscore.string').slugify # TODO: why _.string on client and _.str on server?

 clone = (obj) ->
   return obj if obj is null or typeof (obj) isnt 'object'


change some default settings for non-logged-in users

--- salvus/page/account.coffee
+++ salvus/page/account.coffee
@@ -439,15 +439,15 @@
         auto_close_brackets       : true
         spaces_instead_of_tabs    : true
         multiple_cursors          : true
-        track_revisions           : true
+        track_revisions           : false
         first_line_number         : 1
         indent_unit               : 4
         tab_size                  : 4
-        bindings                  : "sublime"
+        bindings                  : "default"
         theme                     : "default"
         undo_depth                : 300
     other_settings  :
-        confirm_close : true
+        confirm_close : false  # non-logged in user shouldn't have to confirm leave.
         mask_files    : true
     email_address   : 'anonymous@example.com'
     groups          : []


Scroll & Hide when user clicks a nav entry

--- app/js/lineman.coffee
+++ app/js/lineman.coffee
@@ -1,2 +1,9 @@
 $ ->
-  new Sidebar().init()
+  sidebar = new Sidebar().init()
+
+  $('nav').find('a[href^=#]').on 'click', (e) ->
+    e.preventDefault()
+    $('html, body').animate({
+        scrollTop: $($(e.target).attr('href')).offset().top
+    }, 500);
+    sidebar.close()


Let's try this...

Let's try this...

--- src/Carrot.coffee
+++ src/Carrot.coffee
@@ -142,7 +142,7 @@
         'object_properties': objectProperties
       }
       params['object_instance_id'] = objectInstanceId if objectInstanceId
-      @getSignedRequest("/me/feed_post.json", params, (jqXHR) =>
+      @postSignedRequest("/me/feed_post.json", params, (jqXHR) =>
         FB.ui(jqXHR.response.fb_data,
           (response) ->
             if response and response.post_id


405 = not authorized, 404 = resource/user not found

--- src/Carrot.coffee
+++ src/Carrot.coffee
@@ -93,7 +93,7 @@
             @status = Carrot.Status.Authorized
           when 401
             @status = Carrot.Status.ReadOnly
-          when 404
+          when 405
             @status = Carrot.Status.NotAuthorized
           else
             @status = Carrot.Status.Unknown
@@ -111,6 +111,8 @@
         when 401
           @status = Carrot.Status.ReadOnly
         when 404
+          # No change to status, resource not found
+        when 405
           @status = Carrot.Status.NotAuthorized
         else
           @status = Carrot.Status.Unknown


rename property to key

rename property to key

--- ccss.coffee
+++ ccss.coffee
@@ -5,11 +5,11 @@
     children = {}
     text = ''

-    for property, value of declarations
+    for key, value of declarations
       if typeof value is 'object'
-        children["#{selector} #{property}"] = value
+        children["#{selector} #{key}"] = value
       else
-        text += "  #{property}: #{value};\n"
+        text += "  #{key}: #{value};\n"

     if text
       #we have to check; this level could just be for nesting.


Fix video socket connections when changing leagues

--- elements/stream-channel.coffee
+++ elements/stream-channel.coffee
@@ -1,8 +1,13 @@
 activeSessions = {}

-SocketConnection = (api, leagueId, element) ->
+SocketConnection = (socket, api, leagueId, element) ->
   onMessageCallbacks = {}
-  socket = io.connect(api.getSocketUrl(), {query: api.getSocketUrlQuery(leagueId)})
+  query = api.getSocketUrlQuery(leagueId)
+  if socket
+    socket.io.opts.query = query
+    socket.connect()
+  else
+    socket = io.connect(api.getSocketUrl(), {query: query})
   socket.on 'message', (data) ->
     frameId = data.message?.sessionid
     if frameId
@@ -21,10 +26,9 @@
     leagueId:
       type: String,
       observer: '_onLeagueChange'
-    socketUrl: String

   createConnection: ->
-    SocketConnection(@$.api, @leagueId, @)
+    SocketConnection(@socket, @$.api, @leagueId, @)

   clearOldSessions: ->
     for frameId, val of activeSessions
@@ -41,14 +45,12 @@

   _onLeagueChange: ->
     @whenReady.then =>
-      if @connection
-        @connection.close()
-        clearInterval(@intervalId)
-        @connection = null
+      if @socket
+        @socket.disconnect()
       if @leagueId
-        @connection = @createConnection()
-        if @connection
-          @intervalId = setInterval(@clearOldSessions.bind(@, @connection), 10000)
+        unless @socket
+          setInterval(@clearOldSessions.bind(@), 10000)
+        @socket = SocketConnection(@socket, @$.api, @leagueId, @)

   ready: ->
     @$.api.data.ready.then =>


Implemented Vector methods

Implemented Vector methods

--- src/Vector.coffee
+++ src/Vector.coffee
@@ -1,4 +1,4 @@
-class DivSugar._Vector
+class DivSugar.Vector
   constructor: (x, y, z) ->
     switch arguments.length
       when 0
@@ -16,7 +16,7 @@
         @z = z

   set: (x, y, z) ->
-    if arguments.length == 1
+    if arguments.length is 1
       vec = x
       @x = vec.x
       @y = vec.y
@@ -28,7 +28,7 @@

     return @

-  negate: ->
+  neg: ->
     @x = -@x
     @y = -@y
     @z = -@z
@@ -40,49 +40,40 @@
     @z += vec.z
     return @

-  subtract: (vec) ->
+  sub: (vec) ->
     @x -= vec.x
     @y -= vec.y
     @z -= vec.z
     return @

-  multiply: (s) ->
+  mul: (s) ->
     @x *= s
     @y *= s
     @z *= s
     return @

-  divide: (s) ->
+  div: (s) ->
     rs = 1 / s
     @x *= rs
     @y *= rs
     @z *= rs
     return @

-  norm: ->
-    Math.sqrt @x * @x + @y * @y + @z * @z
+  norm: -> Math.sqrt @x * @x + @y * @y + @z * @z

-  squaredNorm: ->
-    @x * @x + @y * @y + @z * @z
+  sqNorm: -> @x * @x + @y * @y + @z * @z

-  distance: (vec) ->
-    DivSugar.Vector._tmpVec.set(@).subtract(vec).norm()
+  dist: (vec) -> DivSugar.Vector._tmpVec1.set(@).sub(vec).norm()

-  squaredDistance: (vec) ->
-    DivSugar.Vector._tmpVec.set(@).subtract(vec).squaredNorm()
+  sqDist: (vec) -> DivSugar.Vector._tmpVec1.set(@).sub(vec).sqNorm()

-  dot: (vec) ->
-    @x * vec.x + @y * vec.y + @z * vec.z
+  dot: (vec) -> @x * vec.x + @y * vec.y + @z * vec.z

-  cross: (vec) ->
-    @set @y * vec.z - @z * vec.y, @z * vec.x - @x * vec.z, @x * vec.y - @y * vec.x
+  cross: (vec) -> @set @y * vec.z - @z * vec.y, @z * vec.x - @x * vec.z, @x * vec.y - @y * vec.x

   normalize: ->
-    norm = @norm
-    if norm < DivSugar.EPSILON
-      @set DivSugar.Vector.X_UNIT
-    else
-      @div norm
+    norm = @norm()
+    if norm < DivSugar.EPSILON then @set DivSugar.Vector.X_UNIT else @div norm

   rotateX: (deg) ->
     sin = Math.sin deg * DivSugar.DEG_TO_RAD
@@ -103,19 +94,51 @@
     if ratio > 1 - DivSugar.EPSILON
       @set to
     else if ratio >= DivSugar.EPSILON
-      vec = DivSugar.Vector._tmpVec
-      vec.set(to).multiply(ratio)
-      @multiply(1 - ratio).add(vec)
+      vec = DivSugar.Vector._tmpVec1
+      vec.set(to).mul(ratio)
+      @mul(1 - ratio).add(vec)

-  equals: (vec) ->
-    @x == vec.x && @y == vec.y && @z == vec.z
+  toLocal: (mat) ->
+    vec = DivSugar.Vector._tmpVec1
+    vec.set @.sub mat.trans
+    @set vec.dot(mat.xAxis) / mat.xAxis.sqNorm(), vec.dot(mat.yAxis) / mat.yAxis.sqNorm(), vec.dot(mat.zAxis) / mat.zAxis.sqNorm()

-  toString: ->
-    "(#{@x}, #{@y}, #{@z})"
+  toGlobal: (mat) ->
+    vec1 = DivSugar.Vector._tmpVec1
+    vec2 = DivSugar.Vector._tmpVec2
+    vec3 = DivSugar.Vector._tmpVec3

-DivSugar._Vector.ZERO = DivSugar.createVector 0, 0, 0
-DivSugar._Vector.X_UNIT = DivSugar.createVector 1, 0, 0
-DivSugar._Vector.Y_UNIT = DivSugar.createVector 0, 1, 0
-DivSugar._Vector.Z_UNIT = DivSugar.createVector 0, 0, 1
+    vec1.set(mat.xAxis).mul(@x)
+    vec2.set(mat.yAxis).mul(@y)
+    vec3.set(mat.zAxis).mul(@z)

-DivSugar._Vector._tmpVec = DivSugar.createVector
+    @set(vec1).add(vec2).add(vec3).add(mat.trans)
+
+  toLocal_noTrans: (mat) ->
+    vec = DivSugar.Vector._tmpVec1
+    vec.set @dot(mat.xAxis) / mat.xAxis.sqNorm(), @dot(mat.yAxis) / mat.yAxis.sqNorm(), @dot(mat.zAxis) / mat.zAxis.sqNorm()
+    @set vec
+
+  toGlobal_noTrans: (mat) ->
+    vec1 = DivSugar.Vector._tmpVec1
+    vec2 = DivSugar.Vector._tmpVec2
+    vec3 = DivSugar.Vector._tmpVec3
+
+    vec1.set(mat.xAxis).mul(@x)
+    vec2.set(mat.yAxis).mul(@y)
+    vec3.set(mat.zAxis).mul(@z)
+
+    @set(vec1).add(vec2).add(vec3)
+
+  equals: (vec) -> @x is vec.x and @y is vec.y and @z is vec.z
+
+  toString: -> "(#{@x}, #{@y}, #{@z})"
+
+DivSugar.Vector.ZERO = new DivSugar.Vector 0, 0, 0
+DivSugar.Vector.X_UNIT = new DivSugar.Vector 1, 0, 0
+DivSugar.Vector.Y_UNIT = new DivSugar.Vector 0, 1, 0
+DivSugar.Vector.Z_UNIT = new DivSugar.Vector 0, 0, 1
+
+DivSugar.Vector._tmpVec1 = new DivSugar.Vector
+DivSugar.Vector._tmpVec2 = new DivSugar.Vector
+DivSugar.Vector._tmpVec3 = new DivSugar.Vector


Added ADA 2016 Badge

Added ADA 2016 Badge

--- src/badges.coffee
+++ src/badges.coffee
@@ -18,7 +18,8 @@
 badgeList = [
   'abaddon',
   'acolyte',
-  'ada',
+  'ada-1',
+  'ada-2',
   'aegis',
   'builder1', 'builder2', 'builder3', 'builder4', 'builder5',
   'connector1', 'connector2', 'connector3', 'connector4', 'connector5',
@@ -83,7 +84,8 @@
 badgeTypes = {
   'abaddon': 1,
   'acolyte': 1,
-  'ada': 1,
+  'ada-1': 1,
+  'ada-2': 1,
   'aegis': 1,
   'builder': 5,
   'connector': 5,


wording

wording

--- src/cachet.coffee
+++ src/cachet.coffee
@@ -22,8 +22,8 @@
 #   hubot cachet maintenance at <scheduled_at> <name>: <message> - Schedule a maintenance (e.g. `cachet maintenance at 2015-08-15 10:00:00 Database upgrade: Message`)
 #
 # Notes:
-#   Components MUST be registered with `cachet component set` before being able
-#   to use them (e.g. `<component name>`).
+#   Components MUST be registered with `cachet component set` before you are
+#   able to use them (e.g. `<component name>`).
 #
 # Author:
 #   William Durand


Correctly set default canvas dimensions if not specified

--- src/canvas.coffee
+++ src/canvas.coffee
@@ -7,16 +7,19 @@

   # Construct our canvas object.
   #
-  # * __id__ (String) - the id of the HTML element
+  # * __id__ (String) - the id of the HTML element (without leading '#')
   #
   # Ex:
   #
   #   `Kona.Canvas.init('gameCanvas')`
+  #
   init: (id) ->
-    @elem    = document.getElementById(id) or fail("Can't find element with id: #{id}")
-    @ctx     = @elem.getContext('2d')
-    @width   = @elem.width  || @defaults.width
-    @height  = @elem.height || @defaults.height
+    @elem = document.getElementById(id) or fail("Can't find element with id: #{id}")
+    @ctx  = @elem.getContext('2d')
+    @elem.width  = @defaults.width  unless @elem.getAttribute('width')?
+    @elem.height = @defaults.height unless @elem.getAttribute('height')?
+    @width       = @elem.width
+    @height      = @elem.height

   # Safely perform a draw that may change the ctx fillStyle or other properties
@@ -27,6 +30,7 @@
   #     Kona.Canvas.safe =>
   #       Kona.Canvas.ctx.fillStyle = 'red'
   #       Kona.Canvas.ctx.fillRect(100, 100, 50, 50)
+  #
   safe: (fn) ->
     @ctx.save()
     fn()


Fixed bug calling trigger before DOM is loaded (AngularJS 1.3)

--- src/chosen.coffee
+++ src/chosen.coffee
@@ -1,6 +1,6 @@
 angular.module('localytics.directives', [])

-angular.module('localytics.directives').directive 'chosen', ->
+angular.module('localytics.directives').directive 'chosen', ['$timeout', ($timeout) ->

   # This is stolen from Angular...
   NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/
@@ -94,11 +94,15 @@
       valuesExpr = match[7]

       scope.$watchCollection valuesExpr, (newVal, oldVal) ->
-        # There's no way to tell if the collection is a promise since $parse hides this from us, so just
-        # assume it is a promise if undefined, and show the loader
-        if angular.isUndefined(newVal)
-          startLoading()
-        else
-          removeEmptyMessage() if empty
-          stopLoading()
-          disableWithMessage() if isEmpty(newVal)
+      	# Defer execution until DOM is loaded
+      	$timeout(() ->
+	        # There's no way to tell if the collection is a promise since $parse hides this from us, so just
+	        # assume it is a promise if undefined, and show the loader
+	        if angular.isUndefined(newVal)
+	          startLoading()
+	        else
+	          removeEmptyMessage() if empty
+	          stopLoading()
+	          disableWithMessage() if isEmpty(newVal)
+	    )
+]


Lower precedence of last argument of function call.

--- src/coffee.coffee
+++ src/coffee.coffee
@@ -116,7 +116,7 @@
       tokens = CoffeeScript.tokens @text,
         rewrite: false
         preserveComments: true
-
+
       # In the @lines record, replace all
       # comments with spaces, so that blocks
       # avoid them whenever possible.
@@ -153,7 +153,7 @@

     locationsAreIdentical: (a, b) ->
       return a.line is b.line and a.column is b.column
-
+
     boundMin: (a, b) ->
       if a.line < b.line then a
       else if b.line < a.line then b
@@ -165,7 +165,7 @@
       else if b.line < a.line then a
       else if a.column < b.column then b
       else a
-
+
     # ## getBounds ##
     # Get the boundary locations of a CoffeeScript node,
     # using CoffeeScript location data and
@@ -180,7 +180,7 @@
         end:
           line: node.locationData.last_line
           column: node.locationData.last_column + 1
-
+
       # There are four cases where CoffeeScript
       # actually gets location data wrong.

@@ -197,7 +197,7 @@
         #If we have no child expressions, make the bounds actually empty.
         else
           bounds.start = bounds.end
-
+
       # The second is 'If' statements,
       # which do not surround the elseBody
       # when it exists.
@@ -207,7 +207,7 @@

         if node.elseBody?
           bounds.end = @boundMax bounds.end, @getBounds(node.elseBody).end
-
+
       # The third is 'While', which
       # fails to surround the loop body,
       # or sometimes the loop guard.
@@ -229,7 +229,7 @@
       while @lines[bounds.end.line][...bounds.end.column].trim().length is 0
         bounds.end.line -= 1
         bounds.end.column = @lines[bounds.end.line].length + 1
-
+
       # When we have a 'Value' object,
       # its base may have some exceptions in it,
       # in which case we want to pass on to
@@ -240,15 +240,15 @@
         if node.properties? and node.properties.length > 0
           for property in node.properties
             bounds.end = @boundMax bounds.end, @getBounds(property).end
-
+
       return bounds
-
+
     flagLineAsMarked: (line) ->
       @hasLineBeenMarked[line] = true
       while @lines[line][@lines[line].length - 1] is '\\'
         line += 1
         @hasLineBeenMarked[line] = true
-
+
     # ## addMarkupAtLocation ##
     # Add a Model container into the markup that we will
     # ultimately return at a given location.
@@ -260,7 +260,7 @@
         container: container
         bounds: bounds
         depth: depth
-
+
     # ## addMarkup ##
     # A general utility function for adding markup around
     # a given node.
@@ -270,25 +270,25 @@
       # we may actually want to enclose the parenthesis
       # by the new block and not the node itself.
       bounds = @getBounds (wrappingParen ? node)
-
+
       # Add the markup.
       @addMarkupAtLocation container, bounds, depth
-
+
     # ## addBlock ##
     # A general utility function for adding an ICE editor
     # block around a given node.
     addBlock: (node, depth, precedence, color, wrappingParen, socketLevel) ->
       # Create the block.
       block = new model.Block precedence, color, node.nodeType(), socketLevel
-
+
       # Add it
       @addMarkup block, node, wrappingParen, depth
-
+
       # If necessary, flag it as paren-wrapped.
       block.currentlyParenWrapped = wrappingParen?

       return block
-
+
     # ## addSocket ##
     # A similar utility function for adding sockets.
     addSocket: (node, depth, precedence, accepts = YES) ->
@@ -299,14 +299,14 @@
       return socket

     # ## addSocketAndMark ##
-    # Adds a socket around a block, and @marks it.
+    # Adds a socket for a node, and recursively @marks it.
     addSocketAndMark: (node, depth, precedence, indentDepth, accepts = YES) ->
       socket = @addSocket node, depth, precedence, accepts

       @mark node, depth + 1, precedence, null, indentDepth

       return socket
-
+
     # ## wrapSemicolonLine ##
     # Wrap a single line in a block
     # for semicolons.
@@ -334,7 +334,7 @@
       # nodes on the current line, and their bounds.
       firstNode = lastNode =
         firstBounds = lastBounds = null
-
+
       # We will also keep track of the nodes
       # that are on this line, so that
       # we can surround them in sockets
@@ -344,7 +344,7 @@
       for expr in expressions
         # Get the bounds for this expression
         bounds = @getBounds expr
-
+
         # If we are on the same line as the last expression, update
         # lastNode to reflect.
         if bounds.start.line is firstBounds?.end.line
@@ -358,18 +358,18 @@
         # they do need a semicolon wrapper.
         else
           if lastNode? then @wrapSemicolonLine firstBounds, lastBounds, nodesOnCurrentLine, depth
-
+
           # Regardless of whether or not we added semicolons on the last line,
           # clear the records to make way for the new line.
           firstNode = expr; lastNode = null
           firstBounds = @getBounds expr; lastBounds = null
           nodesOnCurrentLine = [expr]
-
+
       # Wrap up the last line if necessary.
       if lastNode? then @wrapSemicolonLine firstBounds, lastBounds, nodesOnCurrentLine, depth
-
+
     # ## mark ##
-    # Mark a single node.
+    # Mark a single node.  The main recursive function.
     mark: (node, depth, precedence, wrappingParen, indentDepth) ->
       switch node.nodeType()

@@ -379,15 +379,15 @@
         when 'Block'
           # Abort if empty
           if node.expressions.length is 0 then return
-
+
           # Otherwise, get the bounds to determine
           # whether we want to do it on one line or multiple lines.
           bounds = @getBounds node
-
+
           # See if we want to wrap in a socket
           # rather than an indent.
           shouldBeOneLine = false
-
+
           # Check to see if any parent node is occupying a line
           # we are on. If so, we probably want to wrap in
           # a socket rather than an indent.
@@ -396,10 +396,10 @@

           if @lines[bounds.start.line][...bounds.start.column].trim().length isnt 0
             shouldBeOneLine = true
-
+
           if shouldBeOneLine
             @addSocket node, depth, 0
-
+
           # Otherwise, wrap in an indent.
           else
             # Determine the new indent depth by literal text inspection
@@ -409,16 +409,16 @@
             # Create the indent with the proper
             # depth delta
             indent = new model.Indent @lines[node.locationData.first_line][indentDepth...trueIndentDepth]
-
+
             # Then update indent depth data to reflect.
             indentDepth = trueIndentDepth
-
+
             # As a block, we also want to consume as much whitespace above us as possible
             # (to free it from actual ICE editor blocks).
             while bounds.start.line > 0 and @lines[bounds.start.line - 1].trim().length is 0
               bounds.start.line -= 1
               bounds.start.column = @lines[bounds.start.line].length + 1
-
+
             # Move the boundaries back by one line,
             # as per the standard way to add an Indent.
             bounds.start.line -= 1
@@ -431,7 +431,7 @@
           # make room for semicolon wrappers where necessary.
           for expr in node.expressions
             @mark expr, depth + 3, 0, null, indentDepth
-
+
           # Wrap semicolons.
           @wrapSemicolons node.expressions, depth

@@ -472,19 +472,19 @@
             secondBounds = @getBounds node.second

             lines = @lines[firstBounds.end.line..secondBounds.start.line].join('\n')
-
+
             infix = lines[firstBounds.end.column...-(@lines[secondBounds.start.line].length - secondBounds.start.column)]

             if infix.indexOf('+') is -1
               return

           @addBlock node, depth, OPERATOR_PRECEDENCES[node.operator], 'value', wrappingParen, VALUE_ONLY
-
+
           @addSocketAndMark node.first, depth + 1, OPERATOR_PRECEDENCES[node.operator], indentDepth

           if node.second?
             @addSocketAndMark node.second, depth + 1, OPERATOR_PRECEDENCES[node.operator], indentDepth
-
+
         # ### Existence ###
         # Color VALUE, socket @expression, precedence 100
         when 'Existence'
@@ -497,7 +497,7 @@
           @addBlock node, depth, 0, 'value', wrappingParen, VALUE_ONLY
           @addSocketAndMark node.object, depth + 1, 0, indentDepth
           @addSocketAndMark node.array, depth + 1, 0, indentDepth
-
+
         # ### Value ###
         # Completely pass through to @base; we do not care
         # about this node.
@@ -510,7 +510,7 @@
                 @addSocketAndMark property.name, depth + 1, precedence, indentDepth, NO
               else if property.nodeType() is 'Index'
                 @addSocketAndMark property.index, depth + 1, precedence, indentDepth
-
+
           # Fake-remove backticks hack
           else if node.base.nodeType() is 'Literal' and
               node.base.value is ''
@@ -523,7 +523,7 @@
         # ### Literal ###
         # No-op. Translate directly to text
         when 'Literal', 'Bool', 'Undefined', 'Null' then 0
-
+
         # ### Call ###
         # Color COMMAND, sockets @variable and @args.
         # We will not add a socket around @variable when it
@@ -544,20 +544,24 @@
               @addSocketAndMark node.variable.base, depth + 1, 0, indentDepth
           else
             @addBlock node, depth, precedence, 'command', wrappingParen, ANY_DROP
-
+
           unless node.do
-            for arg in node.args
-              @addSocketAndMark arg, depth + 1, 0, indentDepth
-
+            for arg, index in node.args
+              precedence = 0
+              # special case: the last argument slot of a function
+              # gathers anything inside it, without parens needed.
+              if index is node.args.length - 1 then precedence = -1
+              @addSocketAndMark arg, depth + 1, precedence, indentDepth
+
         # ### Code ###
         # Function definition. Color VALUE, sockets @params,
         # and indent @body.
         when 'Code'
           @addBlock node, depth, precedence, 'value', wrappingParen, VALUE_ONLY
-
+
           for param in node.params
             @addSocketAndMark param, depth + 1, 0, indentDepth, NO
-
+
           @mark node.body, depth + 1, 0, null, indentDepth

         # ### Assign ###
@@ -568,13 +572,13 @@
             block.nodeType is 'Value'

           @addSocketAndMark node.value, depth + 1, 0, indentDepth
-
+
         # ### For ###
         # Color CONTROL, options sockets @index, @source, @name, @from.
         # Indent/socket @body.
         when 'For'
           @addBlock node, depth, precedence, 'control', wrappingParen, MOSTLY_BLOCK
-
+
           for childName in ['source', 'from', 'guard', 'step']
             if node[childName]? then @addSocketAndMark node[childName], depth + 1, 0, indentDepth

@@ -582,7 +586,7 @@
             if node[childName]? then @addSocketAndMark node[childName], depth + 1, 0, indentDepth, NO

           @mark node.body, depth + 1, 0, null, indentDepth
-
+
         # ### Range ###
         # Color VALUE, sockets @from and @to.
         when 'Range'
@@ -598,7 +602,7 @@
         # we want to skip the Op that wraps the condition.
         when 'If'
           @addBlock node, depth, precedence, 'control', wrappingParen, MOSTLY_BLOCK
-
+
           # Check to see if we are an "unless".
           # We will deem that we are an unless if:
           #   - Our starting line contains "unless" and
@@ -622,7 +626,7 @@
           ###

           @addSocketAndMark node.rawCondition, depth + 1, 0, indentDepth
-
+
           @mark node.body, depth + 1, 0, null, indentDepth

           if node.elseBody?
@@ -660,9 +664,9 @@
         # indent/socket @cases[x][1]. indent/socket @otherwise.
         when 'Switch'
           @addBlock node, depth, 0, 'control', wrappingParen, MOSTLY_BLOCK
-
+
           if node.subject? then @addSocketAndMark node.subject, depth + 1, 0, indentDepth
-
+
           for switchCase in node.cases
             if switchCase[0].constructor is Array
               for condition in switchCase[0]
@@ -673,7 +677,7 @@

           if node.otherwise?
             @mark node.otherwise, depth + 1, 0, null, indentDepth
-
+
         # ### Class ###
         # Color CONTROL. Optional sockets @variable, @parent. Optional indent/socket
         # @obdy.
@@ -700,28 +704,28 @@
     transpile: ->
       # Get the CoffeeScript AST from the text
       nodes = CoffeeScript.nodes(@text).expressions
-
+
       # Mark all the nodes
       # in the block.
       for node in nodes
         @mark node, 3, 0, null, 0
-
+
       # Deal with semicoloned lines
       # at the root level
       @wrapSemicolons nodes, 0
-
+
       # Return the markup.
       return @markup
-
+
   # Wrap up the things we need to do
   # to package ourselves as an ICE editor parser
   # and export to require.js.
   coffeeScriptParser = new parser.Parser (text) ->
     transpiler = new CoffeeScriptTranspiler text
     return transpiler.transpile()
-
+
   exports.parse = (text, opts) ->
     opts ?= wrapAtRoot: true
     return coffeeScriptParser.parse text, opts
-
+
   return exports


Change url to /logging

Change url to /logging

--- scripts/helpers/logging.coffee
+++ scripts/helpers/logging.coffee
@@ -8,6 +8,6 @@
     args = _.toArray(arguments).slice(1)

     log = {'msg': _.flatten(['trigger', args, console])}
-    $.post('/api/logging', log)
+    $.post('/logging', log)

     originalTrigger.apply(@, args)


Support latest meteorjs

Support latest meteorjs

--- batsignal.coffee
+++ batsignal.coffee
@@ -8,13 +8,18 @@

 messages.add = (message) -> if @isValid message then @insert message: message, isRead: false

-if Meteor.is_server
+messages.allow
+  insert: (userId, doc) ->
+    true
+
+if Meteor.isServer
 	Meteor.publish 'messages', -> latestMessage
-else
+
+if Meteor.isClient
 	Meteor.subscribe 'messages'

 	allMessages.observe added: (msg) -> if not msg.isRead then speak msg.message, speed: 140, pitch: 30, wordgap: 10
-
+
 	getMessage = -> messages.findOne {}

 	isRead = -> not getMessage() or getMessage().isRead
@@ -32,4 +37,4 @@

 	_.extend Template.message,
 		className: -> if @isRead then 'read' else 'unread'
-		events: click: -> messages.update this, $set: isRead: true+		events: click: -> messages.update this, $set: isRead: true


Added creationix's haml-js to the benchmark.

--- benchmark.coffee
+++ benchmark.coffee
@@ -2,6 +2,7 @@
 jade = require 'jade'
 ejs = require 'ejs'
 eco = require 'eco'
+haml = require 'haml'

 data =
   title: 'test'
@@ -80,7 +81,7 @@
           - each user in users
             li= user.name
             li
-              a(href=user.email)= user.email
+              a(href="mailto:"+user.email)= user.email
 '''

 ejs_template = '''
@@ -107,7 +108,7 @@

           <% for (user in users) { %>

<%= user.name %>

-
<a href="<%= user.email %>"><%= user.email %>

+
<a href="mailto:<%= user.email %>"><%= user.email %>

           <% } %>

@@ -139,13 +140,39 @@

           <% for user in @users: %>

<%= user.name %>

-
<a href="<%= user.email %>"><%= user.email %>

+
<a href="mailto:<%= user.email %>"><%= user.email %>

           <% end %>

 '''
+
+haml_template = '''
+  !!! 5
+  %html{lang: "en"}
+    %head
+      %meta{charset: "utf-8"}
+      %title= title
+      :css
+        body {font-family: "sans-serif"}
+        section, header {display: block}
+    %body
+      %section
+        %header
+          %h1= title
+        :if inspired
+          %p Create a witty example
+        :if !inspired
+          %p Go meta
+        %ul
+          :each user in users
+            %li= user.name
+            %li
+              %a{href: "mailto:#{user.email}"}= user.email
+'''
+
+haml_template_compiled = haml(haml_template)

 benchmark = (title, code) ->
   start = new Date
@@ -163,3 +190,5 @@
   benchmark 'ejs (cache off)', -> ejs.render ejs_template, locals: data
   benchmark 'ejs (cache on)', -> ejs.render ejs_template, locals: data, cache: on, filename: 'test'
   benchmark 'Eco', -> eco.render eco_template, data
+  benchmark 'haml-js', -> haml.render haml_template, locals: data
+  benchmark 'haml-js (pre-compiled)', -> haml_template_compiled(data)


Fix out-of-date Metric documentation.

Fix out-of-date Metric documentation.


don't map ctr-r for vim-mode-plus:redo

don't map ctr-r for vim-mode-plus:redo

--- keymaps/narrow.cson
+++ keymaps/narrow.cson
@@ -1,7 +1,6 @@
 # Need improve following example for normal user.
 'atom-text-editor.narrow.narrow-editor':
   'enter': 'core:confirm'
-# 'ctrl-u': 'narrow-ui:preview-item'
   'ctrl-r': 'narrow-ui:toggle-auto-preview'
   'tab': 'narrow-ui:move-to-prompt-or-selected-item'
   'f5': 'narrow-ui:refresh-force'
@@ -16,9 +15,7 @@
   'enter': 'core:confirm'
   'q': 'core:close'
   'o': 'narrow-ui:confirm-keep-open'
-  # 'u': 'narrow-ui:preview-item'
   'r': 'narrow-ui:toggle-auto-preview'
-  'ctrl-r': 'narrow-ui:refresh-force'

 'atom-text-editor.narrow.narrow-editor.vim-mode-plus.insert-mode':
   'enter': 'core:confirm'


Make buttonStates static.

Make buttonStates static.

--- app/assets/javascripts/components/start_editing_button.js.coffee
+++ app/assets/javascripts/components/start_editing_button.js.coffee
@@ -16,22 +16,19 @@
         'canEdit'
   ).property('canEdit', 'isEditing')

-  setButtonStates: (->
-    states =
-      isEditing:
-        buttonColor: 'button--purple'
-        prompt: 'STOP WRITING'
-        iconClass: ''
-      canEdit:
-        buttonColor: 'button--green'
-        prompt: 'START WRITING'
-        iconClass: 'glyphicon-pencil'
-      disabled:
-        buttonColor: 'button--disabled'
-        prompt: 'START WRITING'
-        iconClass: 'glyphicon-pencil'
-    @set('buttonStates', states)
-  ).on('init')
+  buttonStates:
+    isEditing:
+      buttonColor: 'button--purple'
+      prompt: 'STOP WRITING'
+      iconClass: ''
+    canEdit:
+      buttonColor: 'button--green'
+      prompt: 'START WRITING'
+      iconClass: 'glyphicon-pencil'
+    disabled:
+      buttonColor: 'button--disabled'
+      prompt: 'START WRITING'
+      iconClass: 'glyphicon-pencil'

   buttonColor: (->
     state = @get('buttonState')


Keep unreferenced entries when dereferencing in js

--- app/assets/javascripts/darkswarm/services/dereferencer.js.coffee
+++ app/assets/javascripts/darkswarm/services/dereferencer.js.coffee
@@ -13,6 +13,5 @@
           if data.hasOwnProperty(key)
             target[i] = data[key]
           else
-            delete target[i]
             unreferenced[i] = object
       unreferenced


Add format parameter

Add format parameter

--- app/assets/javascripts/embeditor/adapters/fire_tracker.js.coffee
+++ app/assets/javascripts/embeditor/adapters/fire_tracker.js.coffee
@@ -4,3 +4,4 @@
     @QueryDefaults =
         maxwidth    : 510
         maxheight   : 374
+        format      : 'json'


removed model additions / Habberdash.Model

--- app/assets/javascripts/habberdash/lib/spine_extensions.js.coffee
+++ app/assets/javascripts/habberdash/lib/spine_extensions.js.coffee
@@ -26,39 +26,3 @@
         @releaseEvents.push({element: document, unbindMethod: 'unbind', eventName: 'keydown', method: method})
       else
         @el.delegate(selector, eventName, method)
-
-
-# Model Additions
-#----------------------------------------------------------------------------------------------------/
-makeArray = (args) ->
-  Array::slice.call(args, 0)
-
-class Habberdash.Model extends Spine.Model
-  @options = (attributes...) ->
-    @optionAttributes = attributes if attributes.length
-    @optionAttributes and= makeArray(@optionAttributes)
-    @optionAttributes or= []
-    this
-
-  @configure = (name, attributes...) ->
-    @className  = name
-    @records    = {}
-    @crecords   = {}
-    @attributes = attributes if attributes.length
-    @attributes and= makeArray(@attributes)
-    @attributes or= []
-    @attributes.push(optionAttribute) for optionAttribute in (@optionAttributes || [])
-    @unbind()
-    this
-
-  options: ->
-    result = {}
-    return result unless @constructor.optionAttributes
-    for key in @constructor.optionAttributes when key of this
-      if typeof @[key] is 'function'
-        result[key] = @[key]()
-      else
-        result[key] = @[key]
-    result.id = @id if @id
-    result
-


Removing the video on modal close

Removing the video on modal close

--- app/assets/javascripts/em.js.coffee
+++ app/assets/javascripts/em.js.coffee
@@ -78,6 +78,9 @@

     initializeShowMapTipsBtn()

-    $('a.icon-close').click -> $('.modal-wrapper').hide()
+    $('a.icon-close').click ->
+        $('.modal-wrapper').hide(0, ->
+                $('.em-preview-modal').remove()
+            )
     $('a.btn').click -> $('.modal-wrapper').hide()
 )


Nicer styles for annotation summary

Nicer styles for annotation summary

--- app/classify/annotation.cjsx
+++ app/classify/annotation.cjsx
@@ -6,9 +6,9 @@

   render: ->
     annotation = @props.range.annotation
-

+

       {annotation.text}
-

+

 module.exports = React.createClass
   displayName: 'Annotation'
@@ -18,9 +18,7 @@
   render: ->

       {@tasks[@props.tool.type].label} <button className="secret-button" aria-label='Edit' onClick={@edit}></button>
-

-        <TextRange range={@props.tool.issue} />
-

+      <TextRange range={@props.tool.issue} />

   edit: (e) ->


fix: make all the test cases pass

fix: make all the test cases pass

--- app/models/invitation.coffee
+++ app/models/invitation.coffee
@@ -4,7 +4,7 @@

   role : DS.attr 'number'
   email: DS.attr 'string'
-  user : DS.belongsTo 'user', inverse: 'collaboration'
+  user : DS.belongsTo 'user', inverse: 'collaborations'
   project : DS.belongsTo 'project', inverse: 'collaborations'


Fixed up mixin a bit

Fixed up mixin a bit

--- scripts/mixins/tree.coffee
+++ scripts/mixins/tree.coffee
@@ -30,7 +30,7 @@
         #

         # Remove the child if it is already attached somewhere
-        child._tree_parent.removeChild(child) if child._tree_parent
+        child._tree_parent.removeChild(child) if child._tree_parent and child._tree_parent != @

         child._tree_parent = @
         child._tree_root = @_tree_root
@@ -57,15 +57,14 @@

     newNode: (options) -> throw 'BUG: Only the root can create new Pointer Nodes'

-    getParent: () -> @_tree_parent
-    getChildren: () -> @_tree_children
+    getParent:   () -> @_tree_parent
+    getChildren: () -> @_tree_children or throw 'BUG! This node has no children. Call _initializeTreeHandlers ?'

     removeChild: (model) ->
       children = @getChildren()
-      #throw 'BUG: child is not in this node' if not model.parent and children.get(model.id) or children.get(model)
-      @getChildren().remove(model.id)
-      @getChildren().remove(model)
-
+      throw 'BUG: child is not in this node' if not (children.contains(model) or children.get(model.id))
+      model = children.get(model.id) if !children.contains(model)
+      children.remove(model)

     addChild: (model, at=0) ->
       children = @getChildren()
@@ -80,6 +79,7 @@
         if children.indexOf(realModel) < at
           at = at - 1
         children.remove(realModel)
+        realModel._tree_parent = null

       # Before adding the model make sure it's a Tree Node (does it have `._children`.
       # If not, wrap it in a node


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -19,7 +19,9 @@
   player.flicker
     frequency: 20

-  equals
+  equals player.I.flickerAlpha, 0.5
+  equals player.I.flickerFrequency, 20
+  equals player.I.flickerDuration, 30

 test 'it should change the alpha value when the flicker duration has elapsed', ->
   player = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -11,7 +11,7 @@

   player.include Flickerable

-test 'it should change the ', ->
+test 'it should change the alpha value', ->

 module()


Server: Tests: refactor optout test file

--- servers/lib/server/handlers/optout.test.coffee
+++ servers/lib/server/handlers/optout.test.coffee
@@ -1,9 +1,6 @@
-Bongo                           = require 'bongo'
-koding                          = require './../bongo'
+async                           = require 'async'
 request                         = require 'request'
 querystring                     = require 'querystring'
-
-{ daisy }                       = Bongo
 { expect }                      = require 'chai'
 { generateOptoutRequestParams } = require '../../../testhelper/handler/optouthelper'

@@ -15,37 +12,35 @@

     optoutRequestParams = generateOptoutRequestParams()

-    queue       = []
-    methods     = ['put', 'patch', 'delete']
+    queue   = []
+    methods = ['put', 'patch', 'delete']

-    addRequestToQueue = (queue, method) -> queue.push ->
+    addRequestToQueue = (queue, method) -> queue.push (next) ->
       optoutRequestParams.method = method
       request optoutRequestParams, (err, res, body) ->
-        expect(err)             .to.not.exist
-        expect(res.statusCode)  .to.be.equal 404
-        queue.next()
+        expect(err).to.not.exist
+        expect(res.statusCode).to.be.equal 404
+        next()

     for method in methods
       addRequestToQueue queue, method

-    queue.push -> done()
-
-    daisy queue
+    async.series queue, done

   it 'should send HTTP 301 and redirect and set useOldKoding cookie', (done) ->

-    cookieJar           = request.jar()
+    cookieJar = request.jar()

     optoutRequestParams = generateOptoutRequestParams
       jar : cookieJar

-    url                 = optoutRequestParams.url
+    url = optoutRequestParams.url

     request.post optoutRequestParams, (err, res, body) ->
-      expect(err)                           .to.not.exist
-      expect(res.statusCode)                .to.be.equal 301
-      expect(cookieJar.getCookieString url) .to.contain 'useOldKoding=true'
+      expect(err).to.not.exist
+      expect(res.statusCode).to.be.equal 301
+      expect(cookieJar.getCookieString url).to.contain 'useOldKoding=true'
       done()


increase mongo cache size

increase mongo cache size

--- services/spelling/app/coffee/MongoCache.coffee
+++ services/spelling/app/coffee/MongoCache.coffee
@@ -1,7 +1,7 @@
 LRU = require("lru-cache")
 cacheOpts =
-	max: 5000
-	maxAge: 1000 * 60 * 60
+	max: 15000
+	maxAge: 1000 * 60 * 60 * 10

 cache = LRU(cacheOpts)


Update entity search service to use latest Marbles.HTTP.Client

--- lib/assets/javascripts/services/entity_search.js.coffee
+++ lib/assets/javascripts/services/entity_search.js.coffee
@@ -2,12 +2,12 @@
   constructor: (@options = {}) ->
     @client = new Marbles.HTTP.Client(middleware: [Marbles.HTTP.Middleware.SerializeJSON])

-  # options:
+  # callback can either be a function or an object:
   #   - success: fn
   #   - error: fn
   #   - complete: fn
-  search: (query, options = {}) =>
-    @client.get @options.api_root, { q: query }, options
+  search: (query, callback) =>
+    @client.get(url: @options.api_root, params: { q: query }, callback: callback)

 _.extend EntitySearchService::, Marbles.Events
 _.extend EntitySearchService::, Marbles.Accessors


ini: test multi levels in multi brakets

ini: test multi levels in multi brakets

--- packages/core/test/misc.ini/parse_multi_brackets.coffee
+++ packages/core/test/misc.ini/parse_multi_brackets.coffee
@@ -71,3 +71,24 @@
             key1b1: 'value1b1'
         group2:
           key1: 'value1b'
+
+    it 'jump multi levels', ->
+      ini.parse_multi_brackets """
+      # Some = comments
+      [group1]
+        [[group1b]]
+          [[[group1c]]]
+            [[[[group1d]]]]
+              key1d1 = value1d1
+      [group2]
+        key1=value1b
+      """, comment: '#'
+      .should.eql
+        '# Some = comments': null
+        group1:
+          group1b:
+            group1c:
+              group1d:
+                key1d1: 'value1d1'
+        group2:
+          key1: 'value1b'


Added overdue and priority indicators.

Added overdue and priority indicators.

--- reminders.widget/reminders.coffee
+++ reminders.widget/reminders.coffee
@@ -1,5 +1,4 @@
 # TODO
-# * Parse due date into a more presentable format
 # * Limit lists displayed
 # * Clean up code
 update: (output, domEl) ->


Fix db saving in PlayGameDevLevel view

Fix db saving in PlayGameDevLevel view

--- app/views/play/level/PlayGameDevLevelView.coffee
+++ app/views/play/level/PlayGameDevLevelView.coffee
@@ -27,6 +27,7 @@
   subscriptions:
     'god:new-world-created': 'onNewWorld'
     'surface:ticked': 'onSurfaceTicked'
+    'god:streaming-world-updated': 'onStreamingWorldUpdated'

   events:
     'click #edit-level-btn': 'onEditLevelButton'
@@ -53,6 +54,7 @@
     @levelLoader = new LevelLoader({ @supermodel, @levelID, @sessionID, observing: true, team: TEAM, @courseID })
     @supermodel.setMaxProgress 1 # Hack, why are we setting this to 0.2 in LevelLoader?
     @listenTo @state, 'change', _.debounce @renderAllButCanvas
+    @updateDb = _.throttle(@updateDb, 1000)

     @levelLoader.loadWorldNecessities()

@@ -115,7 +117,8 @@
       }
       window.tracker?.trackEvent 'Play GameDev Level - Load', @eventProperties, ['Mixpanel']
       @insertSubView new GameDevTrackView {} if @level.isType('game-dev')
-      @god.createWorld(@spells, false, false, true)
+      sessionDb = @session.get('keyValueDb') ? {}
+      @god.createWorld(@spells, false, false, true, sessionDb)

     .catch (e) =>
       throw e if e.stack
@@ -132,7 +135,8 @@
     }

   onClickPlayButton: ->
-    @god.createWorld(@spells, false, true)
+    sessionDb = @session.get('keyValueDb') ? {}
+    @god.createWorld(@spells, false, true, false, sessionDb)
     Backbone.Mediator.publish('playback:real-time-playback-started', {})
     Backbone.Mediator.publish('level:set-playing', {playing: true})
     action = if @state.get('playing') then 'Play GameDev Level - Restart Level' else 'Play GameDev Level - Start Level'
@@ -172,6 +176,15 @@
     @studentGoals = goals?.map((g) -> JSON.parse(g))
     @renderSelectors '#directions'

+  onStreamingWorldUpdated: (e) ->
+    @updateDb()
+
+  updateDb: ->
+    return unless @state.get('playing')
+    if @surface.world.keyValueDb and not _.isEqual(@surface.world.keyValueDb, @session.attributes.keyValueDb)
+      @session.updateKeyValueDb(_.cloneDeep(@surface.world.keyValueDb))
+      @session.saveKeyValueDb()
+
   destroy: ->
     @levelLoader?.destroy()
     @surface?.destroy()


On travis the inaccuracy is big

On travis the inaccuracy is big

--- test/test-airbud.coffee
+++ test/test-airbud.coffee
@@ -48,6 +48,6 @@
       Airbud.fetch opts, (err, data, info) ->
         err.should.have.property("message").that.match /Operation timeout of \d+ms reached/
         info.should.have.property("attempts").that.equals 2
-        info.should.have.property("totalDuration").that.is.within 97, 103
+        info.should.have.property("totalDuration").that.is.within 80, 120
         #         should be 100. but allow for timer inaccuracy --^
         done()