non essential refactoring in the extends

--- library/nucleus/extends.coffee
+++ library/nucleus/extends.coffee
@@ -104,13 +104,13 @@
     # Otherwise, an exception will be triggered to indicare usage error.
     Object.defineProperty Object::, "identify",
         enumerable: no, value: (identificator) ->
-            shadowed = _.isObject @watermark
-            set = => @$identify = identificator
+            shadowed = _.isObject this.watermark
+            set = => this.$identify = identificator
             return set() if _.isString identificator
             return @$identify if _.isString @$identify
-            return @name unless _.isEmpty @name
-            return @watermark.name if shadowed
-            return @identify typeof this
+            return @name unless _.isEmpty this.name
+            return this.watermark.name if shadowed
+            return this.identify typeof this # gen

     # A universal method to both check and set the indicator of whether
     # an object is an abstract class or not. This is very useful for


Fix #193

Fix #193

Also removed use of 'debugger' in git-show.coffee

--- lib/models/git-show.coffee
+++ lib/models/git-show.coffee
@@ -22,7 +22,7 @@
   git.cmd
     args: args
     cwd: repo.getWorkingDirectory()
-    stdout: (data) -> prepFile data, objectHash
+    stdout: (data) -> prepFile(data, objectHash) if data.length > 0

 prepFile = (text, objectHash) ->
   fs.writeFileSync showCommitFilePath(objectHash), text, flag: 'w+'
@@ -62,7 +62,6 @@
     @panel?.destroy()

 module.exports = (repo, objectHash, file) ->
-  debugger
   if not objectHash?
     new InputView(repo)
   else


Refactor Quantity Selector

Refactor Quantity Selector

--- src/coffee/portal-quantity-selector.coffee
+++ src/coffee/portal-quantity-selector.coffee
@@ -14,6 +14,7 @@
 		@render()
 		@bindEvents()
 		@element.trigger 'vtex.quantity.ready', [@productId, @quantity]
+		@element.trigger 'vtex.quantity.changed', [@productId, @quantity]

 	render: =>
 		renderData =
@@ -31,10 +32,9 @@
 			@update()

 	bindEvents: =>
-		$(window).on 'vtex.quantity.changed', @quantityChanged
+		@bindProductEvent 'vtex.quantity.changed', @quantityChanged
 		@element.find('.unitSelector input').on 'change', @unitInputChanged
 		@element.find('.quantitySelector input').on 'click', @quantityInputChanged
-		@element.trigger 'vtex.quantity.changed', [@productId, @quantity]

 	update: =>
 		@element.find('.unitSelector input').val(@units)


Cursor only restore in normal text

Cursor only restore in normal text

--- jquery.colorfy.coffee
+++ jquery.colorfy.coffee
@@ -59,7 +59,6 @@
     return openSpan + content + closeSpan

   node.process = ->
-    debugger
     if @descriptor.length == 0
       @terminate = true
     else
@@ -118,9 +117,74 @@
   formattedText = formattedText.replace(/ /g, ' ')           #   -> ' '
   return formattedText

-saveCursorLocation = () ->
-
-restoreCursorLocation = () ->
+cursorLocationFromNodeAndOffset = (rootNode, anchorNode, anchorOffset) ->
+  location = 0
+  if rootNode == anchorNode
+    if rootNode.nodeType == Node.TEXT_NODE
+      location += anchorOffset
+    else if rootNode.tagName == "SPAN"
+      for i in [0..anchorOffset] by 1
+        location += cursorLocationFromNodeAndOffset(rootNode.childNodes[i], anchorNode, anchorOffset)
+  else
+    if rootNode.nodeType == Node.TEXT_NODE
+      location += rootNode.nodeValue.length
+    else if rootNode.tagName == "BR"
+      location += 1
+    else
+      for childNode in rootNode.childNodes
+        if childNode == anchorNode
+          location += cursorLocationFromNodeAndOffset(childNode, anchorNode, anchorOffset)
+          return location
+        else
+          location += cursorLocationFromNodeAndOffset(childNode, anchorNode, anchorOffset)
+  return location
+
+nodeAndOffsetFromCursorLocation = (cursorLocation, rootNode) ->
+  if rootNode.nodeType == Node.TEXT_NODE
+    if rootNode.nodeValue.length >= cursorLocation
+      return [rootNode, cursorLocation]
+    else
+      return [null, rootNode.nodeValue.length]
+  else if rootNode.tagName == "BR"
+    if cursorLocation == 1
+      return [rootNode, 0]
+    return [null, 1]
+#  else if rootNode.tagName == "SPAN"
+  else
+    toMinus = 0
+    for childNode in rootNode.childNodes
+      [retNod, retOff] = nodeAndOffsetFromCursorLocation(cursorLocation, childNode)
+      if retOff and !retNod
+        cursorLocation -= retOff
+        toMinus += retOff
+      else if retOff and retNod
+        return [retNod, retOff]
+  [null, toMinus]
+
+saveCursorLocation = (jObject) ->
+  plainObject = jObject[0]
+  return if document.activeElement != plainObject
+  sel = window.getSelection()
+  return unless sel.isCollapsed
+  anchorNode = sel.anchorNode
+  anchorOffset = sel.anchorOffset
+  return unless plainObject.contains(anchorNode)
+  cursorLo = cursorLocationFromNodeAndOffset(plainObject, anchorNode, anchorOffset)
+  console.log("CursorLo: #{cursorLo}")
+  jObject.data("cursorlocation", cursorLo)
+
+
+restoreCursorLocation = (jObject) ->
+  plainObject = jObject[0]
+  return if document.activeElement != plainObject
+  sel = window.getSelection()
+  return unless sel.isCollapsed
+  # return unless plainObject.contains(sel.anchorNode)
+  [anchorNode, anchorOffset] = nodeAndOffsetFromCursorLocation(jObject.data("cursorlocation"), plainObject)
+  console.log(anchorNode)
+  console.log(anchorOffset)
+  if anchorNode && anchorOffset >= 0
+    sel.collapse(anchorNode, anchorOffset)

 $.fn.colorfy = (plainTextProcessor) ->

@@ -155,6 +219,9 @@
     else
       div.css("display", "inline-block")
     div.html(dataTextToFormattedText(div.data("content")))
+    restoreCursorLocation(div)
+    return
+
   div.on "send-content", ->
     # Fix big cursor issue
     if div.text().length == 0
@@ -164,6 +231,7 @@
     area.val(div.data("content"))

   div.on "keyup paste", ->
+    saveCursorLocation(div)
     div.data("content", formattedTextToDataText(div.html())).trigger("send-content").trigger("receive-content")


:books:

:books:

--- lib/operators/input.coffee
+++ lib/operators/input.coffee
@@ -66,6 +66,7 @@
     row is @editor.getBuffer().getLastRow()

 # Takes a transaction and turns it into a string of what was typed.
+# This class is an implementation detail of Insert
 class TransactionBundler
   constructor: (@transaction) ->

@@ -78,6 +79,8 @@
     chars.join("")

   isTypedChar: (patch) ->
+    # Technically speaking, a typed char will be of length 1, but >= 1
+    # happens to let us test with editor.setText, so we'll look the other way.
     return false unless patch.newText?.length >= 1 and patch.oldText?.length == 0
     patch.newText


pass args to run command

pass args to run command

--- lib/peon-web-socket.coffee
+++ lib/peon-web-socket.coffee
@@ -85,7 +85,7 @@
             ))
           else if Object.keys(that.tasks).indexOf(msg) > -1
             connection.send("Running Task: #{msg}")
-            command = spawn('grunt')
+            command = spawn('grunt', [msg])
             that.workers.push(command)
             command.stdout.on('data', (data) ->
               if data


Fix bug when updating querystring

Fix bug when updating querystring

When the value to be deleted is the first one, we were not updating the '&' of the
next value to a '?', for now we leave it as a TODO


reconcile startup/teardown logs

reconcile startup/teardown logs

--- imports/server/stateReconciler.coffee
+++ imports/server/stateReconciler.coffee
@@ -81,6 +81,18 @@

   updateCreated: (created, labels) -> setServiceField 'container.created', created, labels

+  updateStartupLogs: (instanceName, logData) ->
+    console.log 'updateStartupLogs', instanceName, logData
+    Instances.update {name: instanceName},
+      $push: 'logs.startup': logData
+      $set: status: logData
+
+  updateTeardownLogs: (instanceName, logData) ->
+    console.log 'updateTeardownLogs', instanceName, logData
+    Instances.update {name: instanceName},
+      $push: 'logs.teardown': logData
+      $set: status: logData
+
   updateServiceState: (mappedState, labels) ->
     unless mappedState in SERVICE_STATES
       throw "Service state '#{mappedState}' is not supported."


show checkbox when completed

show checkbox when completed

--- app/assets/javascripts/ajax.js.coffee
+++ app/assets/javascripts/ajax.js.coffee
@@ -1,9 +1,4 @@
-$("[data-remote=true]").on('ajax:before', (data, status, xhr) ->
-  $newDiv = $("<div>").html("Sending")
-  $(@).html("<div>").html($newDiv)
-)
-
 $("[data-remote=true]").on('ajax:complete', (data, status, xhr) ->
-  $newDiv = $("<div>").html("Sent")
+  $newDiv = $("<i>").addClass("icon-ok")
   $(@).replaceWith($newDiv)
 )


Fix setting sidebar width

Fix setting sidebar width

--- lib/repository-view.coffee
+++ lib/repository-view.coffee
@@ -13,7 +13,7 @@

   @statusListView: null

-  initialize: ({uri, width}) ->
+  initialize: ({@uri, width}) ->
     # Elements
     @el             = $(@)
     @innerHTML      = BaseTemplate
@@ -45,7 +45,7 @@
     if setter
       @resizeableNode.style.width = "#{setter}px"
     else
-      @resizeableNode.offsetWidth
+      $(@resizeableNode).width()

   serialize: ->
     deserializer: 'GitRepositoryView'


rewriting to avoid an uglify bug

rewriting to avoid an uglify bug

--- src/ClassData.coffee
+++ src/ClassData.coffee
@@ -74,7 +74,8 @@
     return true if @initialized
     return false unless @is_resolved()
     return false if @get_method('<clinit>()V')?
-    @initialized = if @get_super_class()?.is_initialized() else false
+    scls = @get_super_class()
+    @initialized = scls? and scls.is_initialized()
     return @initialized
   is_resolved: -> @resolved
   is_subinterface: -> false


using `@subscribe` to subscribe to the buffer

for garbage collection

--- src/tab.coffee
+++ src/tab.coffee
@@ -17,11 +17,10 @@
     @updateBufferHasModifiedText(@buffer.isModified())

   subscribeToBuffer: ->
-    @buffer?.off '.tabs'
     @buffer = @editSession.buffer
-    @buffer.on 'contents-modified.tabs', (e) => @updateBufferHasModifiedText(e.differsFromDisk)
-    @buffer.on 'after-save.tabs', => @updateTab()
-    @buffer.on 'git-status-change.tabs', => @updateTab()
+    @subscribe @buffer, 'contents-modified.tabs', (e) => @updateBufferHasModifiedText(e.differsFromDisk)
+    @subscribe @buffer, 'after-save.tabs', => @updateTab()
+    @subscribe @buffer, 'git-status-change.tabs', => @updateTab()
     @updateTab()

   updateBufferHasModifiedText: (differsFromDisk) ->


Allow CoffeeScript files to be directly required.

--- src/impromptu.coffee
+++ src/impromptu.coffee
@@ -1,3 +1,6 @@
+# Allow `.coffee` files in `require()`.
+require 'coffee-script'
+
 # Expose `Impromptu`.
 exports = module.exports = {}


Placeholders for coffee and python

Placeholders for coffee and python

--- lib/supported-files.coffee
+++ lib/supported-files.coffee
@@ -6,6 +6,10 @@
         function\\s*\\('
     'ts':
       regExpStr: 'function\\s*REPLACE\\s*\\(|REPLACE\\s*=\\s*'
+    # 'coffee':
+    #   regExpStr: ''
+    # 'py':
+    #   regExpStr: ''
     'java':
       regExpStr: '(public|private)\\s*[\\w\\s]*\\s*REPLACE\\s*\\('
     # 'c':


Allow for snippets as well as text

Allow for snippets as well as text

--- lib/symbol-provider.coffee
+++ lib/symbol-provider.coffee
@@ -210,8 +210,9 @@
     # Probably inefficient to do a linear search
     candidates = []
     for symbol in symbolList
-      continue unless prefix[0].toLowerCase() is symbol.text[0].toLowerCase() # must match the first char!
-      score = fuzzaldrin.score(symbol.text, prefix)
+      text = (symbol.snippet or symbol.text)
+      continue unless prefix[0].toLowerCase() is text[0].toLowerCase() # must match the first char!
+      score = fuzzaldrin.score(text, prefix)
       score *= @getLocalityScore(bufferPosition, symbol.bufferRowsForBufferPath?(bufferPath))
       candidates.push({symbol, score, locality, rowDifference}) if score > 0


calculator widget code refactor & update

--- javascripts/widgets/calculator.coffee
+++ javascripts/widgets/calculator.coffee
@@ -7,8 +7,9 @@

 ttm.define "calculator",
-  ["lib/class_mixer", "lib/math", "widgets/ui_elements", "lib/math/buttons"],
-  (class_mixer, math, ui_elements, math_buttons)->
+  ["lib/class_mixer", "lib/math", "widgets/ui_elements", "lib/math/buttons", 'lib/math/expression_to_string'],
+  (class_mixer, math, ui_elements, math_buttons,
+    expression_to_string)->

     open_widget_dialog = (element)->
       if element.empty()
@@ -28,7 +29,7 @@

       displayValue: ->
         if !@current_expression.isError()
-          val = @current_expression.display()
+          val = expression_to_string.toString(@current_expression)
           if val.length == 0
             '0'
           else
@@ -37,7 +38,8 @@
           @errorMsg()

       display: ->
-        @view.display(@displayValue())
+        to_disp = @displayValue()
+        @view.display(to_disp)

       errorMsg: -> "Error"

@@ -57,7 +59,7 @@
           @reset_on_next_number = false
           @updateCurrentExpression @math.expression.build()

-        cmd = @math.commands.number.build(value: button_options.value)
+        cmd = @math.commands.add_number_to_end.build(value: button_options.value)
         @updateCurrentExpressionWithCommand cmd

       exponentClick: ->
@@ -117,39 +119,39 @@

     class_mixer(Calculator)

-    class LogicController
-      initialize: (@expression)->
-        @current_expression = @expression.build()
-
-      onResultChange: ((@handler)->)
-
-      calculate: ->
-        @resetIfError()
-        @current_expression.calculate()
-        @display()
-
-      resetIfError: ->
-        @current_expression.isError() && @current_expression.reset()
-
-      numberClick: ->
-      msg: (part)->
-        @resetIfError()
-        if part.invoke
-          part.invoke(@current_expression)
-        else
-          @current_expression.msg(part)
-        @display()
-
-
-      reset: ->
-        @current_expression.reset()
-        @display()
-
-      buttonPressed: (component)->
-        component.action(@current_expression)
-        @display()
-
-    class_mixer(LogicController)
+    # class LogicController
+    #   initialize: (@expression)->
+    #     @current_expression = @expression.build()
+
+    #   onResultChange: ((@handler)->)
+
+    #   calculate: ->
+    #     @resetIfError()
+    #     @current_expression.calculate()
+    #     @display()
+
+    #   resetIfError: ->
+    #     @current_expression.isError() && @current_expression.reset()
+
+    #   numberClick: ->
+    #   msg: (part)->
+    #     @resetIfError()
+    #     if part.invoke
+    #       part.invoke(@current_expression)
+    #     else
+    #       @current_expression.msg(part)
+    #     @display()
+
+
+    #   reset: ->
+    #     @current_expression.reset()
+    #     @display()
+
+    #   buttonPressed: (component)->
+    #     component.action(@current_expression)
+    #     @display()
+
+    # class_mixer(LogicController)


Clean up PodController tests

Clean up PodController tests

--- karma/test-controllers.coffee
+++ karma/test-controllers.coffee
@@ -767,6 +767,10 @@

     beforeEach(() ->
       $scope = $rootScope.$new()
+
+      $scope.podId = 21
+      $scope.caseId = 23
+      $scope.podConfig = {title: 'Foo'}

       $scope.podData = {
         items: [],
@@ -939,12 +943,6 @@
       it('should trigger the given action', () ->
         $scope.podId = 21
         $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: []
-        }

         bindController()

@@ -962,22 +960,15 @@
       )

       it('should mark the action as busy', () ->
-        $scope.podId = 21
-        $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: [{
-            type: 'grault'
-            isBusy: false,
-            payload: {}
-          }, {
-            type: 'fred',
-            isBusy: false,
-            payload: {}
-          }]
-        }
+        $scope.podData.actions = [{
+          type: 'grault'
+          isBusy: false,
+          payload: {}
+        }, {
+          type: 'fred',
+          isBusy: false,
+          payload: {}
+        }]

         bindController()

@@ -998,15 +989,6 @@
       )

       it('should emit a notification event if unsuccessful', (done) ->
-        $scope.podId = 21
-        $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: []
-        }
-
         bindController()

         spyOn(PodApi, 'trigger').and.returnValue($q.resolve({
@@ -1028,15 +1010,6 @@
       )

       it('should emit a notification if trigger api method fails', (done) ->
-        $scope.podId = 21
-        $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: []
-        }
-
         bindController()

         spyOn(PodApi, 'trigger')
@@ -1055,15 +1028,6 @@
       )

       it('should emit a notification if get api method fails', (done) ->
-        $scope.podId = 21
-        $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: []
-        }
-
         bindController()

         spyOn(PodApi, 'get')
@@ -1082,15 +1046,6 @@
       )

       it('should fetch and attach data to the scope if successful', () ->
-        $scope.podId = 21
-        $scope.caseId = 23
-        $scope.podConfig = {title: 'Foo'}
-
-        $scope.podData = {
-          items: [],
-          actions: []
-        }
-
         bindController()

         spyOn(PodApi, 'get').and.returnValue($q.resolve({


don't override cmd-q

don't override cmd-q

--- src/keybinder.coffee
+++ src/keybinder.coffee
@@ -7,12 +7,11 @@
   key shortcut, -> callback(); false

 window.handleKeyEvent = (event) ->
-  if event.keyCode == 50
-    console.log "Got `"
-    true
-  else if (event.modifierFlags & OSX.NSCommandKeyMask) and event.keyCode is 12 # cmd and q pushed
-    console.log "Cmd-Q"
+  if (event.modifierFlags & OSX.NSCommandKeyMask) and event.keyCode == 50
+    console.log "Got Cmd-`"
     true
   else
-    console.log event.keyCode
     false
+
+
+


addListener it's "throw", not "raise"

addListener it's "throw", not "raise"

--- src/ki-router.coffee
+++ src/ki-router.coffee
@@ -259,7 +259,7 @@
     else if (element.attachEvent) # // IE DOM
       element.attachEvent("on"+event, fn);
     else
-      raise "addListener can not attach listeners!"
+      throw new Error("addListener can not attach listeners!")

 class SinatraRouteParser
   constructor: (route) ->


Ignore multi-clicks on the container selectors

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -642,7 +642,17 @@
       str += "#{username}@#{ip}"
     str

-  select_container: (container) =>
+  select_container: (container, event) =>
+    # Multiple clicks cause the transition to get out of sync with the selected
+    # container, ignore double clicks etc.
+    # http://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
+    #
+    # Not every call of select_container is be in response to a Knockout
+    # click binding so 'event' may be undefined.
+    if event? and event?.originalEvent?.detail != 1
+      console.log("ignoring multiple clicks, click-count: " + event.originalEvent.detail)
+      return
+
     console.log("selected container " + container.name)
     @current_container(container)
     @switch_container_viewport(@current_container())


Added PistachioThis to the Bin

Added PistachioThis to the Bin

--- bin/kdc-plus.coffee
+++ bin/kdc-plus.coffee
@@ -181,6 +181,7 @@
     'for the previous transform'
   compileCmd.option '-p, --pipe', 'Pipe to STDOUT instead of to a file'
   compileCmd.option '-f, --file <file>', 'Choose the output file'
+  compileCmd.option '--pistachiothis', 'Convert @ to this in your pistachio'
   compileCmd.action -> compile arguments..., log

@@ -212,12 +213,21 @@
   # And finally, parse the args
   program.parse argv

+  # See below defaults
   validArgs = [
     'command'
     'install'
     'outdated'
   ]

+  # Our legacy options, used for the legacy calls below
+  legacyOptions =
+    coffee        : true
+    bare          : true
+    pistachiothis : true
+
+  # ### No Options Default, Legacy
+  #
   # If there are no args, run compile with legacy kdc args
   # Note: We're using `rawArgs` instead of `args` due to inconsistent
   # behavior with args with the following two commands:
@@ -229,11 +239,11 @@
   # find a way to handle this neatly
   if program.rawArgs.length is 2
     # If `kdc-plus` was called with no args, compile
-    compile process.cwd(), [], coffee: true, bare: true, log
+    compile process.cwd(), [], legacyOptions, log
   else if (program.rawArgs.length is 3 and
       validArgs.indexOf(program.rawArgs[2]) < 0)
     # If `kdc-plus unknown-arg` was called, assume it is a directory, compile
-    compile program.rawArgs[2], [], coffee: true, bare: true, log
+    compile program.rawArgs[2], [], legacyOptions, log


Node 0.6 compat

Node 0.6 compat

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -190,9 +190,3 @@
 exports.Graph = graph.Graph
 exports.graph = graph
 exports.internalSocket = internalSocket
-
-# Method for extending include paths for
-# NoFlo components
-exports.addComponentIncludePaths = (paths) ->
-  for path in paths
-    require.paths.unshift path


Refactor: extract method display_field

Refactor: extract method display_field

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -54,16 +54,12 @@
 #

 display_data = ->
-  display_user_name()
-  display_user_title()
+  display_field(user_name())
+  display_field(user_title())

-display_user_name = ->
-  is_visible = is_field_populated(user_name()) or is_edit_mode()
-  set_field_visibility(user_name(), is_visible)
-
-display_user_title = ->
-  is_visible = is_field_populated(user_title()) or is_edit_mode()
-  set_field_visibility(user_title(), is_visible)
+display_field = (element) ->
+  is_visible = is_field_populated(element) or is_edit_mode()
+  set_field_visibility(element, is_visible)

 #


Fix incorrect App.scenario_url

Fix incorrect App.scenario_url

--- app/assets/javascripts/lib/app.coffee
+++ app/assets/javascripts/lib/app.coffee
@@ -109,7 +109,7 @@

   # Used on the console for debugging
   scenario_url: =>
-    "#{globals.api_url.replace('api/v3', 'scenarios')}/#{@scenario.api_session_id()}"
+    "#{globals.api_url}/data/#{@scenario.api_session_id()}"

   # Prepares the Merit Order abd FCE checkboxes
   setup_checkboxes: =>


fixed bug

fixed bug

--- app/assets/javascripts/root.js.coffee
+++ app/assets/javascripts/root.js.coffee
@@ -197,6 +197,7 @@
 		success: (file, response) ->
 			console.log "success: " + file.name + " created bit with id: " +  response.id
 			this.removeFile(file)
+			$("#data .cluster").append( $('<div>').load("/bits/#{   response.id  }") )
 			m = showNotification "success: " + file.name + " created bit with id: " +  response.id

 		sending: (file, xhr, formData) ->


fix js

fix js

--- app/assets/javascripts/wall.js.coffee
+++ app/assets/javascripts/wall.js.coffee
@@ -26,7 +26,6 @@

       main.draw(0)
   draw: (start) ->
-    console.log("===>", start)
     normal = wall.messages
     sticky = wall.sticky_messages
     current = wall.messages.slice(start, start + per_page)
@@ -48,6 +47,8 @@
         main.load(wall_id)
       , INTERVAL

+}
+
 $(document).ready ->
   if screenfull.isFullscreen
     $("#exit_full_screen").show()


Output basic target structure by remade rdfa extractor

--- js/rdfa/parser.coffee
+++ js/rdfa/parser.coffee
@@ -4,13 +4,8 @@
   RDF_XMLLiteral = RDF_IRI + 'XMLLiteral'

-  extract = (doc, base) ->
-    resolver = doc.createElement('a')
-    resolveURI = (ref) ->
-      resolver.href = ref
-      return resolver.href
-    state = new State(base, resolveURI)
-    builder.init(state)
+  extract = (doc, base, profile='html') ->
+    state = builder.init(doc, base, profile)
     walk(doc.documentElement, state)
     return builder.complete(state)

@@ -24,39 +19,88 @@

   builder =

-    init: (state) ->
-      state.result = []
-      #top = {}
-      #result.current = top
-      #result.graph = [top]
-      #result.context = {}
+    init: (doc, base, profile) ->
+      resolver = doc.createElement('a')
+      resolveURI = (ref) ->
+        resolver.href = ref
+        return resolver.href
+      state = new State(base, profile, resolveURI)
+      docObj = {"@id": base}
+      all = {}
+      all[base] = docObj
+      #context = {}
+      state.current = docObj
+      state.result = {all: all}
+      return state

     visit: (el, state) ->
       desc = new Description(el, state)
-      state.result.push(desc)
+      res = state.result
+      current = state.current
+      nextObj = current
+
+      s = desc.getSubject()
+      if s
+        current = res.all[s]
+        unless current
+          current = res.all[s] = {"@id": s}
+
+      o = desc.getReference()
+      if o
+        nextObj = res.all[o]
+        unless nextObj
+          nextObj = res.all[o] = {"@id": o}
+
+        for link in desc.getLinks()
+          refs = current[link]
+          unless refs
+            refs = current[link] = []
+          refs.push({"@id": o})
+
+      value = desc.getValue()
+      if value
+        for prop in desc.getValueProperties()
+          values = current[prop]
+          unless values
+            values = current[prop] = []
+          values.push(value)
+
       subState = desc.getChildState()
-      subState.result = desc.items = []
-      delete desc.state
+      subState.current = nextObj
       return subState

     complete: (state) ->
-      state.result
-      #result = state.result
-      #{'@context': result.context, '@graph': result.graph}
+      items = []
+      for s, obj of state.result.all
+        add = true
+        if obj["@id"]
+          add = false
+          for key of obj
+            if key != "@id"
+              add = true
+              break
+        if add
+          items.push(obj)
+      items
+      #{'@context': state.result.context, '@graph': items}

   class State
-    constructor: (base=null, @resolveURI) ->
+    constructor: (base=null, @profile, @resolveURI) ->
       @mappings = {base: base, lang: null, vocab: null, prefixes: {}}
       @lists = {}
       @hanging = {rels: [], revs: [], lists: []}
       @result = null
+      @current = null

     newLocal: (base, lang, vocab, prefixes) ->
       # TODO: new State with updated members
       ctor = () ->
       ctor.prototype = this
       return new ctor
+
+
+  class Mappings

   ##
@@ -105,26 +149,58 @@
       @data.errors

     getSubject: ->
+      if @about
+        return @about
+      else if @resource and not (@rels or @revs) # or @property and @typeof
+        return @resource
+      else if @typeof
+        return @newBNode()

     getReference: ->
-      #resource else if typeof then bnode
+      if @resource
+        return @resource
+      else if @typeof
+       return @newBNode()

     getValue: ->
+      if @content
+        if @datatype
+          return {"@value": @content, "@datatype": @datatype}
+        else if @lang
+          # TODO: inherited (in compact: if diff. from top-level lang)
+          return {"@value": @content, "@language": @lang}
+        else
+          return @content
+      else# if @xml
+        return {"@value": @xml, "@datatype": @datatype}

     getLinks: ->
+      if @rels
+        return @rels
+      else if @properties # TODO: and not content.. and getReference()
+        return @properties
+      else
+        return []

     getValueProperties: ->
+      if @properties and (not @resource or @types)
+        return @properties
+      else
+        return []

     getListLinks: ->

     getListValueProperties: ->

     getRevLinks: ->
+
+    newBNode: ->
+      "_:" # TODO: gen named or marker for proper blank?

   ##
   # A representation of the logical data expressed by an element. This takes
-  # context mappings into account, but does not interpret the # attribute
+  # context mappings into account, but does not interpret the attribute
   # interplay and generation of triples.
   class ElementData
     constructor: (@el, parentState) ->
@@ -156,15 +232,15 @@

     getAbout: ->
       if @attrs.about? #and not parent
-        next = {'@id': @state.resolveURI(@attrs.about.value)}
+        next = @state.resolveURI(@attrs.about.value)

     getResource: ->
       if @attrs.resource?
-        next = {'@id': @state.resolveURI(@attrs.resource.value)}
+        next = @state.resolveURI(@attrs.resource.value)
       else if @attrs.href?
-        next = {'@id': @el.href}
+        next = @el.href
       else if @attrs.src?
-        next = {'@id': @state.resolveURI(@attrs.src.value)}
+        next = @state.resolveURI(@attrs.src.value)

     getTypes: ->
       @attrs.typeof?.value.split(/\s+/)
@@ -181,7 +257,7 @@
     getContent: ->
       if @attrs.content?
         return @attrs.content.value
-      else if @profile is 'html' and @tagName is 'time'
+      else if @state.profile is 'html' and @tagName is 'time'
         if @attrs.datetime?
           return @attrs.datetime.value
       return @el.textContent
@@ -192,7 +268,8 @@
     getDatatype: ->
       if @attrs.datatype?
         return @attrs.datatype.value
-      else if @profile is 'html' and @tagName is 'time'
+      else if @state.profile is 'html' and @tagName is 'time'
+        value = @getContent()
         if value.indexOf('T') > -1
           return 'xsd:dateTime'
         else


fixed coffeelint error

fixed coffeelint error

--- app/client/dashboard/dashboard.coffee
+++ app/client/dashboard/dashboard.coffee
@@ -96,7 +96,7 @@
 		days.push next.toDate()

 	# for every work item do the following:
-	# from start to end date, per each day determine status of work item in this day
+	# from start to end date, per each day determine status of work item
 	items = WorkItems.find({}).fetch()
 	dataset = items.map (it) -> item_statuses it, days


Rough-in tile collisions working

Rough-in tile collisions working

--- src/javascript/game/collision_spike.coffee
+++ src/javascript/game/collision_spike.coffee
@@ -85,42 +85,82 @@
         when 'fall'
           velocity.y = 0

-      samus.action = null

       # TODO: Gravity system?
       # TODO: always apply? or just when airborn?
       # velocity.y += 100/1000 * dt
+      # if samus.action != 'run' and samus.action != 'stand'
       max = 200/1000
       velocity.y += max/10
       velocity.y = max if velocity.y > max
-      # console.log "v #{velocity.x} #{velocity.y}"
-
+        # console.log "v #{velocity.x} #{velocity.y}"
+      samus.action = null
+
+
+tileWidth = 16
+samusWidth = 12
+halfSamusWidth = samusWidth/2
+samusHeight = 32

 Systems.register 'velocity_position', class GestureAction
   run: (estore,dt,input) ->
     for velocity in estore.getComponentsOfType('velocity')
       if position = estore.getComponent(velocity.eid, 'position')
-        dx = velocity.x * dt
-        dy = velocity.y * dt
-
+        # Current pos and velocity:
+        velX = velocity.x
+        velY = velocity.y
+
+        # Proposed movement:
+        dx = velX * dt
+        dy = velY * dt
+
+        # Proposed resulting position:
         newX = position.x + dx
         newY = position.y + dy

-        # Detect map collisions
-        tileRow = Math.floor(newY/16)
-        tileCol = Math.floor(newX/16)
-        row = roomData[tileRow]
-        block = null
-        if row?
-          block = row[tileCol]
-          if block?
-            if velocity.y > 0
-              newY = (tileRow * 16)
-              velocity.y = 0
-
-        if newY > 240
-          newY = 240
-          velocity.y = 0
+        # Samus box:
+        left = position.x - halfSamusWidth
+        right = left + samusWidth
+        bottom = position.y
+        top = bottom - samusHeight
+
+        newLeft = newX - samusWidth/2
+        newRight = left + samusWidth
+        newBottom = newY
+        newTop = bottom - samusHeight
+
+        # Collisions:
+        leftHits = tileSearchVertical(roomData, newLeft, top+1,bottom-1)
+        rightHits = tileSearchVertical(roomData, newRight, top+1,bottom-1)
+        topHits = tileSearchHorizontal(roomData, newTop, left+1,right-1)
+        bottomHits = tileSearchHorizontal(roomData, newBottom, left+1,right-1)
+
+
+        if leftHits.length > 0
+          newX = (Math.floor(newLeft/tileWidth) + 1) * tileWidth + halfSamusWidth
+          velX = 0 if velX < 0
+
+        if rightHits.length > 0
+          newX = Math.floor(newRight/tileWidth) * tileWidth - halfSamusWidth
+          velX = 0 if velX > 0
+
+        if bottomHits.length > 0
+          newY = Math.floor(newBottom/tileWidth) * tileWidth
+          velY = 0 if velY > 0
+
+        if topHits.length > 0
+          newY = (Math.floor(newBottom/tileWidth) + 1) * tileWidth
+          velY = 0 if velY < 0
+
+
+        # Floor safety check:
+        screenBottom = 240
+        if newY > screenBottom
+          newY = screenBottom
+          velY = 0
+
+        velocity.x = velX
+        velocity.y = velY

         position.x = newX
         position.y = newY
@@ -269,7 +309,7 @@
     @handleAdminControls()

     p1in = @p1Controller.update()
-    console.log p1in if p1in
+    # console.log p1in if p1in
     @input.controllers.player1 = p1in
     # @input.controllers.player2 = @p2Controller.update()

@@ -326,3 +366,28 @@
   [ null,null,null,null, null,null,null,null, null,null,null,null, null,null,null,null ]
   [ null,null,null,null, null,null,null,null, null,null,null,null, null,null,null,null ]
 ]
+
+tileSearchVertical = (grid, x, topY, bottomY) ->
+  hits = []
+  c = Math.floor(x/16)
+  for r in [Math.floor(topY/16)..Math.floor(bottomY/16)]
+    row = grid[r]
+    if row?
+      hit = grid[r][c]
+      if hit?
+        hits.push [r,c,hit]
+    # else
+    #   console.log "vsearch NO row? #{r}",grid
+  hits
+
+tileSearchHorizontal = (grid, y, leftX, rightX) ->
+  hits = []
+  r = Math.floor(y/16)
+  row = grid[r]
+  if row?
+    for c in [Math.floor(leftX/16)..Math.floor(rightX/16)]
+      hit = grid[r][c]
+      if hit?
+        # hits.push hit
+        hits.push [r,c,hit]
+  hits


Fixing #197. You can't reuse RuntimeState between runs, but you can reuse BootstrapClassloader.

--- console/runner.coffee
+++ console/runner.coffee
@@ -108,7 +108,8 @@
       process.stdin.pause()
       resume data

-  rs = new runtime.RuntimeState(stdout, read_stdin, new BootstrapClassLoader(jvm.read_classfile))
+  bs_cl = new BootstrapClassLoader(jvm.read_classfile)
+  rs = new runtime.RuntimeState(stdout, read_stdin, bs_cl)

   if argv.jar?
     jar_dir = extract_jar argv.jar
@@ -161,6 +162,8 @@
       ->  # done_cb runs it again, for hot cache timing
         mid_point = (new Date).getTime()
         console.log 'Starting hot-cache run...'
+        # Reset runtime state.
+        rs = new runtime.RuntimeState(stdout, read_stdin, bs_cl)
         run ->
           finished = (new Date).getTime()
           console.log "Timing:\n\t#{mid_point-cold_start} ms cold\n\t#{finished-mid_point} ms hot"


Fix bug in constraint binding

Fix bug in constraint binding

--- layout.coffee
+++ layout.coffee
@@ -142,7 +142,7 @@
 		applyConstraint: (name, expression) ->
 			@constraints ?= {}
 			@constraints[name] = (new Function([], 'return ' + expression)).bind(@)
-#				console.log 'adding constraint', name, constraint[1], this
+#			console.log 'adding constraint', name, expression, @
 #				console.log 'eval', @constraints[name]()

 			scopes = propertyBindings.find(expression)
@@ -152,10 +152,10 @@
 			bindings = constraintBinding.bindings or= {}

 			for scope in scopes
-				{binding, property} = scope
-				bindingfn = (new Function([], 'return ' + binding)).bind(@)
-				bindings[property] = bindingfn
-#					console.log('bound', name, expression, property, binding)
+				bindexpression = scope.binding
+				scope.compiled = (new Function([], 'return ' + bindexpression)).bind(@)
+				bindings[bindexpression] = scope
+#				console.log 'applied', scope.property, bindexpression, 'for', @

 #				console.log 'matched constraint', name, @, expression

@@ -200,12 +200,14 @@
 		bindConstraints: () ->
 			# register constraints last
 			for name, value of @constraints
-#				console.log 'applying constraint', name, value, this
-#				console.log 'applied', value()
+#				console.log 'binding constraint', name, value, this
 				@setAttribute(name, value())
-				for ev, binding of @constraints[name].bindings
-#					console.log 'binding to scope', js, binding, name, value()
-					binding().bind(ev, @constraintCallback(name, value))
+				for bindexpression, binding of @constraints[name].bindings
+					property = binding.property
+					boundref = binding.compiled()
+					boundref = boundref.$view if boundref.$view
+#					console.log 'binding to', property, 'on', boundref
+					boundref.bind(property, @constraintCallback(name, value))

 		# generate a callback for a constraint expression, e.g. x="${this.parent.baz.x + 10}"
 		constraintCallback: (name, value) ->


When printing out --tokens, hide the location data.

--- command.coffee
+++ command.coffee
@@ -282,13 +282,12 @@
   jsl.stdin.write js
   jsl.stdin.end()

-# Pretty-print a stream of tokens.
+# Pretty-print a stream of tokens, sans location data.
 printTokens = (tokens) ->
   strings = for token in tokens
     tag = token[0]
     value = token[1].toString().replace(/\n/, '\\n')
-    locationData = helpers.locationDataToString token[2]
-    "[#{tag} #{value} #{locationData}]"
+    "[#{tag} #{value}]"
   printLine strings.join(' ')

 # Use the [OptionParser module](optparse.html) to extract all options from


Turning --compile on when --output is set, because it's probably what was intended.

--- command.coffee
+++ command.coffee
@@ -154,11 +154,12 @@
 # Use the [OptionParser module](optparse.html) to extract all options from
 # `process.argv` that are specified in `SWITCHES`.
 parseOptions: ->
-  optionParser:  new optparse.OptionParser SWITCHES, BANNER
-  o: options:    optionParser.parse(process.argv[2...process.argv.length])
-  options.run:   not (o.compile or o.print or o.lint)
-  options.print: !!  (o.print or (o.eval or o.stdio and o.compile))
-  sources:       options.arguments
+  optionParser:     new optparse.OptionParser SWITCHES, BANNER
+  o: options:       optionParser.parse(process.argv[2...process.argv.length])
+  options.compile:  or  !!o.output
+  options.run:      not (o.compile or o.print or o.lint)
+  options.print:    !!  (o.print or (o.eval or o.stdio and o.compile))
+  sources:          options.arguments

 # The compile-time options to pass to the CoffeeScript compiler.
 compileOptions: (source) ->


kite: helper method for unwrapping kite protocol

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -93,11 +93,15 @@
     @emit 'info', "#{ @options.url } error: #{ err.data }"
     return

+  unwrapMessage: (message) ->
+    { withArgs:[{ error: err, result }]} = message
+    { err, result }
+
   wrapMessage: (method, params, callback) ->
     authentication    : @options.auth
     withArgs          : params
-    responseCallback  : (response) ->
-      { withArgs:[{ error: err, result }]} = response
+    responseCallback  : (response) =>
+      { err, result } = @unwrapMessage response
       callback err, result
     kite              :
       username        : "#{ @options.username ? 'anonymous' }"


Use built-ins instead of _

Use built-ins instead of _

--- lib/wrap-guide-view.coffee
+++ lib/wrap-guide-view.coffee
@@ -1,4 +1,4 @@
-{_, $, View} = require 'atom'
+{$, View} = require 'atom'

 module.exports =
 class WrapGuideView extends View
@@ -21,8 +21,8 @@

   getGuideColumn: (path) ->
     customColumns = atom.config.get('wrap-guide.columns')
-    return @getDefaultColumn() unless _.isArray(customColumns)
-    for customColumn in customColumns when _.isObject(customColumn)
+    return @getDefaultColumn() unless Array.isArray(customColumns)
+    for customColumn in customColumns when typeof customColumn is 'object'
       {pattern, column} = customColumn
       return parseInt(column) if pattern and new RegExp(pattern).test(path)
     @getDefaultColumn()


add more informations to healthcheck according to Daniel s recommandations

--- main/src/module.coffee
+++ main/src/module.coffee
@@ -1,7 +1,8 @@
-express = require 'express'
-Promise = require 'bluebird'
+express      = require 'express'
+Promise      = require 'bluebird'
 prettyHrtime = require 'pretty-hrtime'
-_ = require 'lodash'
+_            = require 'lodash'
+os           = require 'os'

 module.exports = (params = {}) ->
   defaultParams =
@@ -13,6 +14,30 @@

   uptime = ->
     prettyHrtime process.hrtime start
+
+  processInfos = ()->
+    processInfosList = [
+      pid         : process.pid,
+      arch        : process.arch,
+      platform    : process.platform,
+      memoryUsage : process.memoryUsage(),
+      uptime      : process.uptime(),
+      execPath    : process.execPath,
+      execArgv    : process.execArgv
+    ]
+    processInfosList
+
+  osInfos = ()->
+    osInfosList = [
+      platform : os.platform(),
+      arch     : os.arch(),
+      release  : os.release(),
+      uptime   : os.uptime(),
+      loadavg  : os.loadavg(),
+      totalmem : os.totalmem(),
+      freemem  : os.freemem()
+    ]
+    osInfosList

   globalStatus = (statuses) ->
     for status in statuses
@@ -121,6 +146,8 @@
       if _.isArray custom
         for result in custom
           body[result.key] = result.result
-      body.status = globalStatus statuses
+      body.status       = globalStatus statuses
+      body.processInfos = processInfos()
+      body.osInfos      = osInfos()
       res.send body
   return app


add test for selecting repositories and querying packages by labels

Labels are currently mutually exclusive to IDs: so you can either select mirrors or a label. Multiple mirror selection will follow.
The tests make sure you can query by label and that labels and IDs are mutually exclusive

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- test/spec/controllers/main.coffee
+++ test/spec/controllers/main.coffee
@@ -249,7 +249,7 @@
         expect(p.version).toBe(v.version)
         expect(p.versions).toBe(undefined)

-  it 'should list /packages compared to upstream repositories', () ->
+  it 'should list /packages compared to upstream repositories (by ID)', () ->
     # get repos
     paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
     expect(scope.repos.length).toBe(allRepos1.length)
@@ -273,6 +273,46 @@
     expect( scope.isPackageOutdated( scope.packages[0]) ).toBe(false)
     expect( scope.isPackageOutdated( scope.packages[1]) ).toBe(true)

+  it 'should list /packages compared to upstream repositories (by label)', () ->
+    # get repos
+    paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
+    expect(scope.repos.length).toBe(allRepos1.length)
+    # set packages list when selecting a repository
+    ps = allPackages2
+    scope.selectRepoLabel(allRepos1[0].label)
+    opts = {'query': [['repolabel',allRepos1[0].label],['outdated','true']]}
+    dontPaginateResponse @httpBackend, '/v1/packages', ps, (() -> scope.fetchPackages()), opts
+    # results
+    expect(scope.allPackages.length).toBe(ps.length)
+    expect(scope.packages.length).toBe(ps.length)
+    # test both packages
+    for idx in [0,1]
+      res_p = scope.packages[idx]
+      expect(@typeOf(res_p)).toBe('object')
+      expect(res_p.name).toBe(ps[idx].name)
+      expect(res_p.versions.length).toBe(ps[idx].versions.length)
+      expect(res_p.upstream).toBe(ps[idx].upstream)
+
+    # test if it is outdated
+    expect( scope.isPackageOutdated( scope.packages[0]) ).toBe(false)
+    expect( scope.isPackageOutdated( scope.packages[1]) ).toBe(true)
+
+  it 'should only allow selecting either repo label or ID', () ->
+    # get repos
+    paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
+    expect(scope.repos.length).toBe(allRepos1.length)
+    # for now...
+    expect(scope.repoSelected).toBe(null)
+    expect(scope.repoSelectedLabel).toBe(null)
+
+    scope.selectRepoLabel(allRepos1[0].label)
+    expect(scope.repoSelected).toBe(null)
+    expect(scope.repoSelectedLabel).toBe(allRepos1[0].label)
+
+    scope.selectRepo(allRepos1[0].id)
+    expect(scope.repoSelected).toBe(allRepos1[0].id)
+    expect(scope.repoSelectedLabel).toBe(null)
+
   it 'should be able to access /package/xyz info (empty one)', () ->
     id = 'xyz'
     @httpBackend.whenGET('/v1/package/'+id).respond({})


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -7,7 +7,7 @@

   self.bind "update", ->
     if I.fadeOut
-      I.alpha =
+      I.alpha = 5
         ;

     if I.duration != -1 && I.age >= I.duration


Age module

Age module

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -17,7 +17,7 @@
 # => true

 5.times ->
-  enemy.update()
+  enemy.update(1)

 enemy.I.active
 # => false


Adding nsolve

Adding nsolve

--- src/Expression.coffee
+++ src/Expression.coffee
@@ -1,172 +1,213 @@
 define ["parse", "nodes"], (parse, nodes) ->

-	# Public interface for nodes.
-	# This is the main object that the user will deal with in Coffeequate.
-	# It wraps underlying nodes and terminals in a neat interface.
-	class Expression
-
-		# Make a new Expression.
-		#
-		# @param val [String, Expression, BasicNode, Terminal] A string representation of an expression to parse or a node/terminal/expression to convert into an Expression.
-		# @return [Expression]
-		constructor: (val) ->
-			if val instanceof String or typeof val == "string"
-				# The string we pass in is just a representation to parse.
-				@expr = parse.stringToExpression(val)
-				if @expr.simplify?
-					@expr = @expr.simplify()
-			else if val.copy?
-				@expr = val.copy().simplify()
-			else
-				console.log("Received argument: ", val)
-				throw new Error("Unknown argument: `#{val}'.")
-
-		# Convert this Expression to a string.
-		#
-		# @return [String] A string representation of this Expression.
-		toString: ->
-			@expr.toString()
-
-		# Convert this Expression to a MathML string.
-		#
-		# @return [String] A MathML representation of this Expression.
-		toMathML: ->
-			@expr.toMathML()
-
-		# Convert this Expression to a LaTeX string.
-		#
-		# @return [String] A LaTeX representation of this Expression.
-		toLaTeX: ->
-			@expr.toLaTeX()
-
-		# Equate the Expression to 0 and solve for a variable.
-		#
-		# @param variable [String] The variable to solve for.
-		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
-		# @return [Array<Expression>] An array of Expressions representing the variable for each solution.
-		solve: (variable, equivalencies={}) ->
-			(new Expression(solution) for solution in @expr.solve(variable, equivalencies))
-
-		# Substitute values into the Expression.
-		#
-		# @param substitutions [Object] A map of variable labels to their values. Values can be integers, Expressions, Terminals, or BasicNodes.
-		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
-		# @param substituteUncertainties [Boolean] Optional. Whether to substitute values into uncertainties instead of variables (default false).
-		# @param evaluateSymbolicConstants [Boolean] Optional. Whether to evaluate symbolic constants (as opposed to leaving them symbolic) (default false).
-		# @return [Expression] The Expression with substituted values.
-		sub: (substitutions, equivalencies={}, substituteUncertainties=false, evaluateSymbolicConstants=false) ->
-			# If there are any Expressions in here, we should remove them.
-			newsubs = {}
-			for key of substitutions
-				if substitutions[key] instanceof Expression
-					newsubs[key] = substitutions[key].expr
-				else
-					newsubs[key] = substitutions[key]
-
-			if substituteUncertainties
-				uncertaintySubs = newsubs
-				variableSubs = {}
-			else
-				uncertaintySubs = {}
-				variableSubs = newsubs
-
-			subbed = @expr.sub(variableSubs, uncertaintySubs, equivalencies, false, evaluateSymbolicConstants)
-			if subbed.simplify?
-				subbed = subbed.simplify(equivalencies)
-			return new Expression(subbed)
-
-		# Get all variable names in this expression.
-		#
-		# @example CQ("x*t").getAllVariables(); // Returns ["t", "x"]
-		#
-		# @return [Array<String>] An array of variable labels.
-		getAllVariables: ->
-			return @expr.getAllVariables()
-
-		# Map a function over all variables in this expression.
-		#
-		# @param fun [Function] A function to map over variables.
-		# @return [Expression] A copy of this expression with the function mapped over all variables.
-		mapOverVariables: (fun) ->
-			return @expr.mapOverVariables(fun)
-
-		# Deep-copy this Expression.
-		#
-		# @return [Expression] A copy of this Expression.
-		copy: ->
-			new Expression(@expr.copy())
-
-		# Simplify this Expression.
-		#
-		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
-		# @return [Expression] A simplified Expression.
-		simplify: (equivalencies={}) ->
-			if @expr.expandAndSimplify?
-				expr = @expr.expandAndSimplify(equivalencies)
-			else if @expr.simplify?
-				expr = @expr.simplify(equivalencies)
-			else
-				expr = @expr.copy()
-			new Expression(expr)
-
-		# Expand this Expression.
-		#
-		# @return [Expression] An expanded Expression.
-		expand: ->
-			if @expr.expand?
-				expr = @expr.expand()
-			else
-				expr = @expr.copy()
-			new Expression(expr)
-
-		# Differentiate this expression with respect to a variable.
-		#
-		# @param variable [String] The label of the variable to differentiate with respect to.
-		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
-		# @return [Expression] A differentiated expression.
-		differentiate: (variable, equivalencies={}) ->
-			new Expression(@expr.differentiate(variable, equivalencies))
-
-		# Get the uncertainty in this Expression.
-		#
-		# @return [Expression] An Expression representing the uncertainty in this Expression.
-		getUncertainty: ->
-			new Expression(@expr.getUncertainty())
-
-		# Convert this expression to a JavaScript function.
-		#
-		# @param variables... [Array<String>] An array of variables for the function to accept, in order that they should appear in the final function.
-		# @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels. This will be used in the returned function.
-		# @return [Function] A function that takes variable values and returns an Expression object.
-		toFunction: (variables..., equivalencies) ->
-			if typeof equivalencies == "string" or equivalencies instanceof String # We had no equivalencies object after all.
-				variables.push(equivalencies)
-				equivalencies = {}
-
-			# The function we will return.
-			fun = (subs...) =>
-				# Zip variables and subs together into an object.
-				substitutions = {}
-				for variable, index in variables
-					if subs[index]?
-						substitutions[variable] = subs[index]
-
-				# Substitute these values into this expression and return the result.
-				return @sub(substitutions, equivalencies)
-
-			return fun
-
-		approx: -> @expr.approx()
-
-		equals: (other, equivalencies={}) ->
-			return undefined unless other instanceof Expression
-
-			# convert to canonical form
-			lhs = @expr.expandAndSimplify(equivalencies)
-			rhs = other.expr.expandAndSimplify(equivalencies)
-
-			return lhs.equals(rhs)
-
+    # Public interface for nodes.
+    # This is the main object that the user will deal with in Coffeequate.
+    # It wraps underlying nodes and terminals in a neat interface.
+    class Expression
+
+        # Make a new Expression.
+        #
+        # @param val [String, Expression, BasicNode, Terminal] A string representation of an expression to parse or a node/terminal/expression to convert into an Expression.
+        # @return [Expression]
+        constructor: (val) ->
+            if val instanceof String or typeof val == "string"
+                # The string we pass in is just a representation to parse.
+                @expr = parse.stringToExpression(val)
+                if @expr.simplify?
+                    @expr = @expr.simplify()
+            else if val.copy?
+                @expr = val.copy().simplify()
+            else
+                console.log("Received argument: ", val)
+                throw new Error("Unknown argument: `#{val}'.")
+
+        # Convert this Expression to a string.
+        #
+        # @return [String] A string representation of this Expression.
+        toString: ->
+            @expr.toString()
+
+        # Convert this Expression to a MathML string.
+        #
+        # @return [String] A MathML representation of this Expression.
+        toMathML: ->
+            @expr.toMathML()
+
+        # Convert this Expression to a LaTeX string.
+        #
+        # @return [String] A LaTeX representation of this Expression.
+        toLaTeX: ->
+            @expr.toLaTeX()
+
+        # Equate the Expression to 0 and solve for a variable.
+        #
+        # @param variable [String] The variable to solve for.
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
+        # @return [Array<Expression>] An array of Expressions representing the variable for each solution.
+        solve: (variable, equivalencies={}) ->
+            (new Expression(solution) for solution in @expr.solve(variable, equivalencies))
+
+        # Substitute values into the Expression.
+        #
+        # @param substitutions [Object] A map of variable labels to their values. Values can be integers, Expressions, Terminals, or BasicNodes.
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
+        # @param substituteUncertainties [Boolean] Optional. Whether to substitute values into uncertainties instead of variables (default false).
+        # @param evaluateSymbolicConstants [Boolean] Optional. Whether to evaluate symbolic constants (as opposed to leaving them symbolic) (default false).
+        # @return [Expression] The Expression with substituted values.
+        sub: (substitutions, equivalencies={}, substituteUncertainties=false, evaluateSymbolicConstants=false) ->
+            # If there are any Expressions in here, we should remove them.
+            newsubs = {}
+            for key of substitutions
+                if substitutions[key] instanceof Expression
+                    newsubs[key] = substitutions[key].expr
+                else
+                    newsubs[key] = substitutions[key]
+
+            if substituteUncertainties
+                uncertaintySubs = newsubs
+                variableSubs = {}
+            else
+                uncertaintySubs = {}
+                variableSubs = newsubs
+
+            subbed = @expr.sub(variableSubs, uncertaintySubs, equivalencies, false, evaluateSymbolicConstants)
+            if subbed.simplify?
+                subbed = subbed.simplify(equivalencies)
+            return new Expression(subbed)
+
+        # Get all variable names in this expression.
+        #
+        # @example CQ("x*t").getAllVariables(); // Returns ["t", "x"]
+        #
+        # @return [Array<String>] An array of variable labels.
+        getAllVariables: ->
+            return @expr.getAllVariables()
+
+        # Map a function over all variables in this expression.
+        #
+        # @param fun [Function] A function to map over variables.
+        # @return [Expression] A copy of this expression with the function mapped over all variables.
+        mapOverVariables: (fun) ->
+            return @expr.mapOverVariables(fun)
+
+        # Deep-copy this Expression.
+        #
+        # @return [Expression] A copy of this Expression.
+        copy: ->
+            new Expression(@expr.copy())
+
+        # Simplify this Expression.
+        #
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
+        # @return [Expression] A simplified Expression.
+        simplify: (equivalencies={}) ->
+            if @expr.expandAndSimplify?
+                expr = @expr.expandAndSimplify(equivalencies)
+            else if @expr.simplify?
+                expr = @expr.simplify(equivalencies)
+            else
+                expr = @expr.copy()
+            new Expression(expr)
+
+        # Expand this Expression.
+        #
+        # @return [Expression] An expanded Expression.
+        expand: ->
+            if @expr.expand?
+                expr = @expr.expand()
+            else
+                expr = @expr.copy()
+            new Expression(expr)
+
+        # Differentiate this expression with respect to a variable.
+        #
+        # @param variable [String] The label of the variable to differentiate with respect to.
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels.
+        # @return [Expression] A differentiated expression.
+        differentiate: (variable, equivalencies={}) ->
+            new Expression(@expr.differentiate(variable, equivalencies))
+
+        # Get the uncertainty in this Expression.
+        #
+        # @return [Expression] An Expression representing the uncertainty in this Expression.
+        getUncertainty: ->
+            new Expression(@expr.getUncertainty())
+
+        # Convert this expression to a JavaScript function.
+        #
+        # @param variables... [Array<String>] An array of variables for the function to accept, in order that they should appear in the final function.
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels. This will be used in the returned function.
+        # @return [Function] A function that takes variable values and returns an Expression object.
+        toFunction: (variables..., equivalencies) ->
+            if typeof equivalencies == "string" or equivalencies instanceof String # We had no equivalencies object after all.
+                variables.push(equivalencies)
+                equivalencies = {}
+
+            # The function we will return.
+            fun = (subs...) =>
+                # Zip variables and subs together into an object.
+                substitutions = {}
+                for variable, index in variables
+                    if subs[index]?
+                        substitutions[variable] = subs[index]
+
+                # Substitute these values into this expression and return the result.
+                return @sub(substitutions, equivalencies)
+
+            return fun
+
+        approx: -> @expr.approx()
+
+        equals: (other, equivalencies={}) ->
+            return undefined unless other instanceof Expression
+
+            # convert to canonical form
+            lhs = @expr.expandAndSimplify(equivalencies)
+            rhs = other.expr.expandAndSimplify(equivalencies)
+
+            return lhs.equals(rhs)
+
+        # Get the numerical solution of the Expression
+        #
+        # @param guess [Float] The initial guess for the solution of the Expression
+        # @param tol [Float] Optional. The tolerance in the final solution
+        # @param variable [String] The parameter that is being solved for
+        # @param equivalencies [Object] Optional. A map of variable labels to a list of equivalent variable labels. This will be used for differentiation.
+        # @param [Int] Optional. The maximum number of iterations allowed for convergence to occur
+        # @return [Float] The numerical solution of the Expression
+        nsolve: -> (guess, tol=1e-9, variable, equivalencies={}, max_iterations=75) ->

-
-	return Expression
+            # Record the initial guess
+            p0 = guess
+
+            # Get the functions that need to be evaluated
+            f = @expr.toFunction(x)
+
+            diff = @expr.differentiate(variable, equivalencies)
+            fDash = diff.toFunction()
+
+            # Attempt to find a solution
+            for iteration in [0 ... max_iterations]
+                f1 = fDash(p0)
+                if f1 == 0
+                    console.log("The derivative is zero")
+                    return p0
+
+                f0 = f(p0)
+
+                # Iterate the formula
+                p1 = p0 - (f0/f1)
+
+                # See if we have reached the tolerance
+                if abs (p1-p0) < tol:
+                    return p1
+
+            # Reaching here means that the method didn't converge
+            console.log("Maximum number of Iterations")
+
+            return p1
+
+
+
+
+    return Expression


add Bacon.fromEvent as shorthand for Bacon.fromEventTarget

--- src/factories.coffee
+++ src/factories.coffee
@@ -56,6 +56,8 @@
     -> unsub.call(target, eventName, handler)
   , eventTransformer)

+Bacon.fromEvent = Bacon.fromEventTarget
+
 Bacon.constant = (value) ->
   new Property describe(Bacon, "constant", value), (sink) ->
     sink (initialEvent value)


Add some important notes

Add some important notes

--- library/nucleus/api.coffee
+++ library/nucleus/api.coffee
@@ -106,12 +106,14 @@
     # implementation. Please refer to this prototype signature for
     # information on the parameters it accepts. If this returns a
     # truthful boolean, the service will NOT call implementation.
+    # Please be sure invoke the super implementation, if override!
     preprocess: (request, response, resource, domain) ->

     # A hook that will be called after invoking the API method
     # implementation. Please refer to this prototype signature for
     # information on the parameters it accepts. This method accepts
     # a value that was returned by the implementation as extra param.
+    # Please be sure invoke the super implementation, if override!
     postprocess: (request, response, result, resource, domain) ->

     # Delete the contents of the resources at the establushed path. It


add params to subscriptions, only unsubscribe if the element is not inserted back into the dom by the next animation frame.

--- lib/assets/javascripts/cable_bindings.coffee
+++ lib/assets/javascripts/cable_bindings.coffee
@@ -6,17 +6,26 @@
 class @ActionCableSubscription extends HTMLElement

   attachedCallback: ->
-    @subscription = cable.subscriptions.create channel: @getAttribute('Channel'),
-      received: (data)=>
-        $(this).trigger("cable:received", data)
+    if @unsubscriber
+      cancelAnimationFrame(@unsubscriber)
+    else
+      subscriptionParams = @getAttribute('params')
+      if subscriptionParams
+        subscriptionOptions = JSON.parse(subscriptionParams)
+        subscriptionOptions.channel = @getAttribute('channel')
+      else
+        subscriptionOptions = @getAttribute('channel')
+      @subscription = cable.subscriptions.create subscriptionOptions,
+        received: (data)=>
+          $(this).trigger("cable:received", data)

-    $(this).on 'cable:perform', (e, action, params)=>
-      console.log(this, @subscription)
-      @subscription.perform action, params
+      $(this).on 'cable:perform', (e, action, params)=>
+        @subscription.perform action, params

   detachedCallback: ->
-    @subscription.unsubscribe()
-
+    @unsubscriber = requestAnimationFrame =>
+      @subscription.unsubscribe()
+
 document.registerElement 'action-cable-subscription', ActionCableSubscription


Move Map tab to the end of Tab Bar

Move Map tab to the end of Tab Bar

--- testApp/config/application.coffee
+++ testApp/config/application.coffee
@@ -22,11 +22,6 @@
 #   - http://www.google.com (directly from internet)
 #
 steroids.config.tabBar.tabs = [
-   {
-     title: "FUT"
-     icon: "icons/telescope@2x.png"
-     location: "http://localhost/views/mapview/index.html"
-   }
   {
     title: "HTTP"
     icon: "icons/pill@2x.png"
@@ -51,6 +46,11 @@
     title: "Config-iOS"
     icon: "icons/shoebox@2x.png"
     location: "http://localhost/views/config/index.html"
+  }
+  {
+    title: "Map"
+    icon: "icons/telescope@2x.png"
+    location: "http://localhost/views/mapview/index.html"
   }
 ]

@@ -108,5 +108,3 @@
 # steroids.config.hooks.postMake.args = ["cleaning up files"]

 steroids.config.copyToUserFiles = ["test.txt", "test.png"]
-
-


Utility functions for message Id generation

--- src/lib/utils.coffee
+++ src/lib/utils.coffee
@@ -6,15 +6,15 @@
 #         list of "Firstname, Lastname" <email@addre.ss>
 #
 exports.extractEmails = (users) -> users.map (user) ->
-    email = user.match /<([a-z0-9\._-]+@[a-z0-9\.-]+)>/
-    email = user.match /([a-z0-9\._-]+@[a-z0-9\.-]+)/ unless email?
+    email = user.match /<([a-z0-9\._-]+@[a-z0-9\.-]+)>/i
+    email = user.match /([a-z0-9\._-]+@[a-z0-9\.-]+)/i unless email?
 	email?[1] ? ''

 # Extract lists of usernames from list of email addresses
 #
 exports.extractUsernames = (emails) -> emails.map (email) ->
-    email.replace /@[a-z0-9\.-]+$/, ''
+    email.replace /@[a-z0-9\.-]+$/i, ''

 # Queue Id (16-char upper case random string) generator
@@ -23,3 +23,13 @@
     s = ''
     s += Math.random().toString(36).substr(2) while s.length < length
     (s.substr 0, length).toUpperCase()
+
+
+# Generate an almost unique Message-ID (32-char timestamp+random string)
+#
+exports.generateMsgId = (senderEmail) ->
+    length = 32
+    d = new Date()
+    s = '' + d.getTime() + '-'
+    s += Math.random().toString(36).substr(2) while s.length < length
+    (s.substr 0, length) + '@' + senderEmail.replace /^[a-z0-9\._-]+@/i, ''


changed default return value of utils.timeEnd to avoid miscalculations

--- src/lib/utils.coffee
+++ src/lib/utils.coffee
@@ -351,14 +351,14 @@

   # End the timer for the specified `key` and return the time difference in
   # milliseconds.
-  # If no timer exists for `key`, simply return `-1'.
+  # If no timer exists for `key`, simply return `0'.
   timeEnd: (key) ->
     if timings.hasOwnProperty key
       start = timings[key]
       delete timings[key]
       new Date().getTime() - start
     else
-      -1
+      0

 # Initialize logging.
 store.init 'logger', {}


don't select disabled options

don't select disabled options

--- lib/capybara/poltergeist/client/agent.coffee
+++ lib/capybara/poltergeist/client/agent.coffee
@@ -227,7 +227,9 @@
     @element.removeAttribute(name)

   select: (value) ->
-    if value == false && !@element.parentNode.multiple
+    if @isDisabled()
+      false
+    else if value == false && !@element.parentNode.multiple
       false
     else
       this.trigger('focus', @element.parentNode)


Collaboration: Update authorization URL

Collaboration: Update authorization URL

--- client/ide/RealTimeManager.coffee
+++ client/ide/RealTimeManager.coffee
@@ -25,7 +25,7 @@
   auth: ->

     $.ajax
-      url: '/-/google-api',
+      url: '/-/google-api/authorize/drive',
       dataType: 'JSON'
       success: (authToken) =>
         # TODO: Error handling please
@@ -42,7 +42,7 @@
   reauth: ->

     $.ajax
-      url: '/-/google-api'
+      url: '/-/google-api/authorize/drive'
       dataType: 'JSON'
       success: (authToken) =>
         gapi.auth.setToken authToken


enable range requests on files via `?range=m-n` query string.

--- services/web/app/coffee/Features/FileStore/FileStoreHandler.coffee
+++ services/web/app/coffee/Features/FileStore/FileStoreHandler.coffee
@@ -42,6 +42,11 @@
 			method : "get"
 			uri: "#{@_buildUrl(project_id, file_id)}#{queryString}"
 			timeout:fiveMinsInMs
+			headers: {}
+		if query? and query['range']?
+			rangeText = query['range']
+			if rangeText && rangeText.match? && rangeText.match(/\d+-\d+/)
+				opts.headers['range'] = "bytes=#{query['range']}"
 		readStream = request(opts)
 		readStream.on "error", (err) ->
 			logger.err {err, project_id, file_id, query}, "error in file stream"


Use notifications in the "logger" controller

--- testApp/app/controllers/logger.coffee
+++ testApp/app/controllers/logger.coffee
@@ -12,13 +12,13 @@
   @testShowLastMessage: ->
     lastMessage = steroids.logger.messages[steroids.logger.messages.length-1]
     if lastMessage?
-      alert "Last message: #{lastMessage.message}"
+      notification "Last message: #{lastMessage.message}"
     else
-      alert "no messages!"
+      notification "no messages!"

   @testStartFlushing: ->
     if steroids.logger.queue.startFlushing(100)
-      alert "started"
+      notification "started"
     else
       alert "not started"


clear pending delay on unmount

clear pending delay on unmount

--- coach/src/user/login-gateway.cjsx
+++ coach/src/user/login-gateway.cjsx
@@ -65,7 +65,7 @@
     ev?.preventDefault()
     loginWindow = LoginGateway.openWindow(@props.windowImpl, {type: @props.loginType})
     @setState({loginWindow})
-    _.delay(@windowClosedCheck, SECOND)
+    @setState(pendingDelay: _.delay(@windowClosedCheck, SECOND))

   parseAndDispatchMessage: (msg) ->
     try
@@ -78,14 +78,18 @@

   componentWillUnmount: ->
     @props.windowImpl.removeEventListener('message', @parseAndDispatchMessage)
+    clearTimeout(@state.pendingDelay) if @state.pendingDelay
+
   componentWillMount: ->
     @props.windowImpl.addEventListener('message', @parseAndDispatchMessage)

   windowClosedCheck: ->
-    if @state.loginWindow and @state.loginWindow.closed
+    pendingDelay = if @state.loginWindow and @state.loginWindow.closed
       User.ensureStatusLoaded(true)
+      pendingDelay = null
     else
-      _.delay( @windowClosedCheck, SECOND)
+      pendingDelay = _.delay( @windowClosedCheck, SECOND)
+    @setState({pendingDelay})

   getActionText: ->
     if @props.loginType is SIGNUP_TYPE


Change some texts

Change some texts

--- client/admin/lib/views/integrations/adminintegrationdetailsview.coffee
+++ client/admin/lib/views/integrations/adminintegrationdetailsview.coffee
@@ -309,13 +309,13 @@
           label         : '
Descriptive Label
Use this label to provide extra context in your list of integrations (optional).'
           defaultValue  : data.description  or integration.summary
         repository      :
-          label         : '
Repository
Choose the repository that you would like to listen.'
+          label         : '
Repository
Choose the repository that you would like to listen to.'
           type          : 'select'
           selectOptions : repositories
           cssClass      : unless repositories.length then 'hidden'
           defaultValue  : data.selectedRepository
         events          :
-          label         : '
Customize Events
Choose the events you would like to receive events for.'
+          label         : '
Customize Events
Choose the events you would like to receive notifications for'
           type          : 'hidden'
           cssClass      : unless data.settings?.events?.length then 'hidden'
         name            :


Workspace: Now every user in drawing pane has a different color. Yay!

--- client/app/CommonViews/workspace/panes/collaborativedrawingpane.coffee
+++ client/app/CommonViews/workspace/panes/collaborativedrawingpane.coffee
@@ -7,6 +7,7 @@
     @pointRef    = @workspaceRef.child "point"
     @linesRef    = @workspaceRef.child "lines"
     @stateRef    = @workspaceRef.child "state"
+    @usersRef    = @workspaceRef.child "users"

     @canvas      = new KDCustomHTMLView
       tagName    : "canvas"
@@ -19,6 +20,8 @@

     @context     = @canvas.getElement().getContext "2d"
     @drawedQueue = []
+    @userColors  = {}
+    @setUserColor()

     @canvas.on "mousedown", (e) =>
       KD.utils.stopDOMEvent e
@@ -31,7 +34,7 @@

       @addPoint x, y
       @drawedQueue.push "#{x},#{y}"
-      @pointRef.set { x, y }
+      @pointRef.set { x, y, nickname: KD.nick() }
       @stateRef.set yes

     @canvas.on "mouseup", (e) =>
@@ -39,6 +42,7 @@
       @startDrawing = no
       @stateRef.set   no

+      @drawedQueue.unshift KD.nick(), @userColors[KD.nick()]
       @linesRef.push @drawedQueue.join "|"
       @drawedQueue.length = 0

@@ -47,25 +51,32 @@
         x = e.offsetX
         y = e.offsetY

-        @addPoint   x, y
+        @addPoint x, y
         @drawedQueue.push "#{x},#{y}"
-        @pointRef.set { x, y }
+
+        @pointRef.set { x, y, nickname: KD.nick() }

     @container.addSubView @canvas

     @workspaceRef.onDisconnect().remove()  if @amIHost

     if @isJoinedASession
+      @context.closePath()
       @linesRef.once "value", (snapshot) =>
         value = snapshot.val()
         return unless value
         @context.beginPath()
         for key, points of value
           pointsArr = points.split "|"
+          [username, color]     = pointsArr.splice 0, 2
+          @userColors[username] = color
+
+          @context.closePath()
+          @context.beginPath()
           for point, index in pointsArr
             [x, y] = point.split ","
             @context.moveTo x, y  if index is 0
-            @addPoint x, y
+            @addPoint x, y, username

     @isContextMoved = yes

@@ -78,13 +89,26 @@
           @context.moveTo value.x, value.y
           @isContextMoved = yes

-        @addPoint value.x, value.y
+        @addPoint value.x, value.y, value.nickname

     @stateRef.on "value", (snapshot) =>
-      if snapshot.val() is no
-        @isContextMoved  = no
+      @isContextMoved = no  if snapshot.val() is no

-  addPoint: (x, y) ->
-    ctx = @context
+    @usersRef.on "value", (snapshot) =>
+      value = snapshot.val()
+      return unless value
+      for key, userData of value
+        @userColors[userData.nickname] = userData.color
+
+
+  addPoint: (x, y, nickname = KD.nick()) ->
+    ctx             = @context
+    ctx.strokeStyle = @userColors[nickname]
     ctx.lineTo x, y
-    ctx.stroke()+    ctx.stroke()
+
+  setUserColor: () ->
+    nickname              = KD.nick()
+    color                 = KD.utils.getRandomHex()
+    @userColors[nickname] = color
+    @usersRef.push { nickname, color }


minor variable name change

minor variable name change

--- client/home/lib/myteam/components/hometeamsendinvites/container.coffee
+++ client/home/lib/myteam/components/hometeamsendinvites/container.coffee
@@ -44,7 +44,7 @@

     modal = new UploadCSVModal
       success : @bound 'uploadCSVSuccess'
-      error : @bound 'uploadCsvFail'
+      error : @bound 'uploadCSVFail'

   uploadCSVSuccess: (result) ->
@@ -54,7 +54,7 @@
     TeamFlux.actions.loadPendingInvitations()

-  uploadCsvFail: ->
+  uploadCSVFail: ->
     new kd.NotificationView
       title : 'Error Occured while handling invitations'
       duration : 5000


advanced settings repos

advanced settings repos

--- client/landing/app/Applications/Ace.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Ace.kdapplication/AppController.coffee
@@ -25,12 +25,12 @@
       { type      : "separator" }
       { title     : "Preview",             eventName : "preview" }
       { type      : "separator" }
+      { title     : "Advanced Settings",   viewName  : "advancedSettings" }
+      { type      : "separator" }
       { title     : "Recently Opened",     viewName  : "recents"}
       { title     : "Reopen Latest Files", eventName : "reopen" }
       { type      : "separator" }
       { title     : "Exit",                eventName : "exit" }
-      { type      : "separator" }
-      { title     : "Advanced Settings",   viewName  : "advancedSettings" }
     ]
     fileTypes     : [
       "php", "pl", "py", "jsp", "asp", "aspx", "htm", "html", "phtml","shtml",


test: util - moved declaration into describe block.

--- coffee/test/util/util.spec.coffee
+++ coffee/test/util/util.spec.coffee
@@ -2,10 +2,10 @@

 goog.require 'athena.lib.util'

-util = athena.lib.util
-

 describe 'athena.lib.util', ->
+  util = athena.lib.util
+
   it 'should be part of athena.lib', ->
     expect(athena.lib.util).toBeDefined()


Update generic collections page Remove PromiseRenderer. Remove HandlePropChanges mixin. Don't load the owner resource twice from the API.

--- app/collections/show.cjsx
+++ app/collections/show.cjsx
@@ -1,12 +1,10 @@
 React = require 'react'
 apiClient = require 'panoptes-client/lib/api-client'
-PromiseRenderer = require '../components/promise-renderer'
 {IndexLink, Link} = require 'react-router'
 Translate = require 'react-translate-component'
 counterpart = require 'counterpart'
 Avatar = require '../partials/avatar'
 Loading = require '../components/loading-indicator'
-HandlePropChanges = require '../lib/handle-prop-changes'
 TitleMixin = require '../lib/title-mixin'
 classNames = require 'classnames'

@@ -32,6 +30,15 @@
   getDefaultProps: ->
     collection: null

+  getInitialState: ->
+    owner: null
+
+  componentWillMount: ->
+    apiClient.type 'users'
+      .get @props.collection.links.owner.id
+      .then (owner) =>
+        @setState {owner}
+
   componentDidMount: ->
     document.documentElement.classList.add 'on-collection-page'

@@ -39,49 +46,49 @@
     document.documentElement.classList.remove 'on-collection-page'

   render: ->
-    <PromiseRenderer promise={@props.collection.get('owner')}>{(owner) =>
-      baseType = if @props.collection.favorite then "favorites" else "collections"
-      baseLink = ""
-      if @props.project?
-        baseLink = "/projects/#{@props.project.slug}"
-      baseCollectionLink = "#{baseLink}/#{baseType}/#{@props.collection.slug}"
-      baseCollectionsLink = "#{baseLink}/#{baseType}/#{owner.login}"
-      isOwner = @props.user?.id is owner.id
-      profileLink = "#{baseLink}/users/#{owner.login}"
-      collectionsLinkMessageKey = "collectionPage.#{baseType}Link"
+    return null unless @state.owner?
+
+    baseType = if @props.collection.favorite then "favorites" else "collections"
+    baseLink = ""
+    if @props.project?
+      baseLink = "/projects/#{@props.project.slug}"
+    baseCollectionLink = "#{baseLink}/#{baseType}/#{@props.collection.slug}"
+    baseCollectionsLink = "#{baseLink}/#{baseType}/#{@state.owner?.login}"
+    isOwner = @props.user?.id is @state.owner?.id
+    profileLink = "#{baseLink}/users/#{@state.owner?.login}"
+    collectionsLinkMessageKey = "collectionPage.#{baseType}Link"

-

-
-          <IndexLink to={baseCollectionLink} activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'view-collection')}>
-            <Avatar user={owner} />
-            {@props.collection.display_name}
-          </IndexLink>
-          {if isOwner
-
-              <Link to="#{baseCollectionLink}/settings" activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'settings-collection')}>
-                <Translate content="collectionPage.settings" />
-              </Link>
-              <Link to="#{baseCollectionLink}/collaborators" activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'collab-collection')}>
-                <Translate content="collectionPage.collaborators" />
-              </Link>
-            }
-          <Link to={baseCollectionsLink} className="tabbed-content-tab">
-            <Translate content="#{collectionsLinkMessageKey}" user={@props.collection.links.owner.display_name} />
-          </Link>
-          <Link to={profileLink} activeClassName="active" className="tabbed-content-tab">
-            <Translate content="collectionPage.userLink" user={@props.collection.links.owner.display_name} />
-          </Link>
-
-

-          {React.cloneElement @props.children, {user: @props.user, collection: @props.collection, roles: @props.roles}}
-

+

+
+        <IndexLink to={baseCollectionLink} activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'view-collection')}>
+          <Avatar user={@state.owner} />
+          {@props.collection.display_name}
+        </IndexLink>
+        {if isOwner
+
+            <Link to="#{baseCollectionLink}/settings" activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'settings-collection')}>
+              <Translate content="collectionPage.settings" />
+            </Link>
+            <Link to="#{baseCollectionLink}/collaborators" activeClassName="active" className="tabbed-content-tab" onClick={@logClick?.bind(this, 'collab-collection')}>
+              <Translate content="collectionPage.collaborators" />
+            </Link>
+          }
+        <Link to={baseCollectionsLink} className="tabbed-content-tab">
+          <Translate content="#{collectionsLinkMessageKey}" user={@props.collection.links.owner.display_name} />
+        </Link>
+        <Link to={profileLink} activeClassName="active" className="tabbed-content-tab">
+          <Translate content="collectionPage.userLink" user={@props.collection.links.owner.display_name} />
+        </Link>
+
+

+        {React.cloneElement @props.children, {user: @props.user, collection: @props.collection, roles: @props.roles}}

-    }</PromiseRenderer>
+

 module.exports = React.createClass
   displayName: 'CollectionPageWrapper'

-  mixins: [TitleMixin, HandlePropChanges]
+  mixins: [TitleMixin]

   title: ->
     @state.collection?.display_name ? '(Loading)'
@@ -95,42 +102,42 @@
     error: false
     loading: false

-  propChangeHandlers:
-    'params.collection_owner': 'fetchCollection'
-    'params.collection_name': 'fetchCollection'
-    'user': 'fetchCollection'
+  componentWillMount: ->
+    @fetchCollection()

   fetchCollection: ->
     @setState
-      error: false
       loading: true

     apiClient.type('collections')
       .get(slug: @props.params.collection_owner + '/' + @props.params.collection_name, include: 'owner')
       .then ([collection]) =>
-        unless collection then @setState error: true

         apiClient.type('collection_roles')
           .get(collection_id: collection.id)
           .then (roles) =>
             @setState
+              error: false
+              loading: false
               collection: collection
               roles: roles
-      .catch =>
+      .catch (e) =>
         @setState
           error: true
-          collection: null
-      .then =>
-        @setState loading: false
+          loading: false

   render: ->
     classes = classNames {
       "content-container"
       "collection-page-with-project-context": @props.project?
     }
+    {project, user} = @props

       {if @state.collection
-        <CollectionPage {...@props} collection={@state.collection} roles={@state.roles} />}
+        <CollectionPage project={project} user={user} collection={@state.collection} roles={@state.roles}>
+          {@props.children}
+        </CollectionPage>
+      }

       {if @state.error
         <Translate component="p" content="collectionsPageWrapper.error" />}


Don't defer scroll after cursor changes position to make scrolling feel smoother

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -688,9 +688,8 @@

   scrollTo: (pixelPosition) ->
     return unless @attached
-    _.defer => # Optimization
-      @scrollVertically(pixelPosition)
-      @scrollHorizontally(pixelPosition)
+    @scrollVertically(pixelPosition)
+    @scrollHorizontally(pixelPosition)

   scrollVertically: (pixelPosition) ->
     linesInView = @scrollView.height() / @lineHeight


Fix tabs, and fix some syntax errors

Fix tabs, and fix some syntax errors

--- app/lib/soundcloud.coffee
+++ app/lib/soundcloud.coffee
@@ -1,35 +1,33 @@
-http = require( 'http' )
+https = require( 'https' )

 clientID = ""

 module.exports = {

-  setApiKey: ( ApiKey ) ->
-    clientID = ApiKey
+	setApiKey: ( ApiKey ) ->
+		clientID = ApiKey

-  getJSON: ( trackID ) ->
-    https.get(url, (res) ->
+	getJSON: ( trackID ) ->
+		https.get(url, (res) ->

-      body = ''
+			body = ''

-      res.on('data', ( chunk ) ->
-          body += chunk;
-      )
+			res.on('data', ( chunk ) ->
+				body += chunk;
+			)

-      res.on('end', ->
-        return JSON.parse(body)
-      )
-    ).on('error', (e) ->
-        console.log("Got an error: ", e)
-    )
+			res.on('end', ->
+				return JSON.parse(body)
+			)
+		).on('error', (e) ->
+			console.log("Got an error: ", e)
+		)

-    getTracksId: ( trackID ) ->
-      response = jetJSON(trackID)
-      trackArray = []
-      for i in resp.collection.length
-        trackArray.push(response.collection[i].user_id)
-      return trackArray
-
-
+	getTracksId: ( trackID ) ->
+		response = jetJSON(trackID)
+		trackArray = []
+		for track in resp.collection
+			trackArray.push( track )
+		return trackArray

 }


Update ru.coffee (#5646)

Update ru.coffee (#5646)

Few lines were translated on russian

--- app/locale/ru.coco.coffee
+++ app/locale/ru.coco.coffee
@@ -24,17 +24,17 @@
 #    century_skills_subblurb3: "Throughout the game, there are opportunities for students to collaborate when they get stuck and to work together using our pair programming guide."
 #    century_skills_quote4: "I’ve always had aspirations of designing video games and learning how to code ... this is giving me a great starting point."
     century_skills_subtitle4: "Коммуникация"
-#    century_skills_subblurb4: "Coding requires kids to practice new forms of communication, including communicating with the computer itself and conveying their ideas using the most efficient code."
-#    classroom_in_box_title: "We Strive To:"
-#    classroom_in_box_blurb1: "Engage every student so that they believe coding is for them."
-#    classroom_in_box_blurb2: "Empower any educator to feel confident when teaching coding."
-#    classroom_in_box_blurb3: "Inspire all school leaders to create a world-class computer science program."
-#    creativity_rigor_title: "Where Creativity Meets Rigor"
-#    creativity_rigor_subtitle1: "Make coding fun and teach real-world skills"
-#    creativity_rigor_blurb1: "Students type real Python and JavaScript while playing games that encourage trial-and-error, critical thinking, and creativity. Students then apply the coding skills they’ve learned by developing their own games and websites in project-based courses."
-#    creativity_rigor_subtitle2: "Reach students at their level"
-#    creativity_rigor_blurb2: "Every CodeCombat level is scaffolded based on millions of data points and optimized to adapt to each learner. Practice levels and hints help students when they get stuck, and challenge levels assess students' learning throughout the game."
-#    creativity_rigor_subtitle3: "Built for all teachers, regardless of experience"
+    century_skills_subblurb4: "Кодирование требует от детей практиковать новые формы общения, включая общение с самим компьютером и передачу своих идей с использованием наиболее эффективного кода."
+    classroom_in_box_title: "Мы стремимся:"
+    classroom_in_box_blurb1: "Вовлеките каждого ученика, чтобы он поверил, что кодирование для них."
+    classroom_in_box_blurb2: "Дайте возможность любому преподавателю чувствовать себя уверенно при обучении кодированию."
+    classroom_in_box_blurb3: "Вдохновите всех школьных лидеров создать программу компьютерных наук мирового класса."
+    creativity_rigor_title: "Где творчество встречается со строгостью"
+    creativity_rigor_subtitle1: "Занимайтесь программированием и обучайтесь реальным навыкам"
+    creativity_rigor_blurb1: "Учащиеся пишут на настоящем Python и JavaScript, играя в игры, которые поощряют метод проб и ошибок, критическое мышление и творческий подход. Затем студенты применяют приобретенные навыки кодирования, разрабатывая свои собственные игры и веб-сайты на курсах, основанных на проектах."
+    creativity_rigor_subtitle2: "Достигните студентов на их уровне"
+    creativity_rigor_blurb2: "Каждый уровень CodeCombat создается на основе миллионов точек данных и оптимизируется для адаптации к каждому учащемуся. Уровни практики и советы помогают учащимся, когда они застряли, и уровни сложности оценивают обучение студентов на протяжении всей игры."
+    creativity_rigor_subtitle3: "Создан для всех учителей, независимо от опыта"
     creativity_rigor_blurb3: "Самостоятельная, стандартизированная учебная программа CodeCombat делает преподавание информатики возможным для всех. CodeCombat предоставляет учителям учебные, учебные ресурсы и специальную поддержку, чтобы чувствовать себя уверенно и успешно в классе."
     featured_partners_title1: "Зарекомендован в"
     featured_partners_title2: "Награды и Партнеры"


Update uk.coffee (#5568)

Update uk.coffee (#5568)

--- app/locale/uk.coco.coffee
+++ app/locale/uk.coco.coffee
@@ -1442,7 +1442,7 @@
   courses:
     create_new_class: "Створити новий клас"
 #    hoc_blurb1: "Try the"
-#    hoc_blurb2: "Code, Play, Share"
+    hoc_blurb2: "Програмуй, грай, ділись"
 #    hoc_blurb3: "activity! Construct four different minigames to learn the basics of game development, then make your own!"
 #    solutions_require_licenses: "Level solutions are available for teachers who have licenses."
     unnamed_class: "Безіменний клас"
@@ -2322,7 +2322,7 @@
 #    certificate_stats: "Course Stats"
 #    certificate_lines_of: "lines of"
 #    certificate_levels_completed: "levels completed"
-#    certificate_for: "For"
+    certificate_for: "Для"
 #    certificate_number: "No."

   achievements:
@@ -2411,12 +2411,12 @@
     you_can2: "купити передплачений код"
     you_can3: "який може бути застосовано до вашої обліківки або переданий іншим."

-#  ozaria_chrome:
-#    sound_off: "Sound Off"
-#    sound_on: "Sound On"
-#    back_to_map: "Back to Map"
-#    level_options: "Level Options"
-#    restart_level: "Restart Level"
+  ozaria_chrome:
+    sound_off: "Вимкнути звук"
+    sound_on: "Увімкнути звук"
+    back_to_map: "Повернутись до карти"
+    level_options: "Налаштування рівня"
+    restart_level: "Перезавантажити рівень"

 #  impact:
 #    hero_heading: "Building A World-Class Computer Science Program"


Update zh-HANT.coffee (#6173)

Update zh-HANT.coffee (#6173)

修改 become_investor

--- app/locale/zh-HANT.coffee
+++ app/locale/zh-HANT.coffee
@@ -5,7 +5,7 @@
 #    meta_keywords: "CodeCombat, python, javascript, Coding Games"
     meta_description: "通過遊戲來學習程式編碼。 學習 Python、JavaScript 和 HTML，解決其中的難題並學會製作自己的遊戲和網站。"
 #    meta_og_url: "https://codecombat.com"
-#    become_investor: "to become an investor in CodeCombat"
+    become_investor: "成為CodeCombat的投資人"
     built_for_teachers_title: "一個由教師建構的編碼遊戲"
     built_for_teachers_blurb: "教孩子們編寫代碼往往會令人感到招架不住。 透過 CodeCombat 可以幫助所有教育工作者教授學生如何使用 JavaScript 或 Python（兩種最流行的編程語言）進行編碼。 透過包含六個計算機科學的綜合課程，以及遊戲開發和 Web 開發專題來進行加強學習，孩子們將在從基本語法往遞迴程式設計中前進！"
     built_for_teachers_subtitle1: "電腦科學"


Drive can't handle empty files correctly

--- src/linkpaper.coffee
+++ src/linkpaper.coffee
@@ -32,6 +32,8 @@
 update = do ->
   id = null
   -> fs.readFile process.argv[2], encoding: 'utf8', (err, data) ->
+    if data is ""
+      data = " "
     if err?
       if err.errno is 'ENOENT'
         console.error "Error reading #{process.argv[2]}", err


clearRect before drawing a sprite to the texture atlas

--- src/gfx.coffee
+++ src/gfx.coffee
@@ -66,6 +66,7 @@

     draw: (context) ->
       if @image?
+        context.clearRect @left, @top, @width, @height
         context.drawImage @image, @left, @top
       else if @a? and @b?
         @a.draw context


Existing test now invalid

Existing test now invalid

--- test/axiom-cli.coffee
+++ test/axiom-cli.coffee
@@ -12,16 +12,16 @@

 tests = [
-  {
-    description: 'should fail when no module is specified'
-    args: []
-    expected:
-      code: 1
-      stderr: [
-        'Options:\n  --moduleName, --module, -m    [required]\n  --serviceName, --service, -s  [required]\n  --data                        [default: {}]\n\n'
-        'Missing required arguments: moduleName, serviceName\n'
-      ]
-  }
+  # {
+  #   description: 'should fail when no module is specified'
+  #   args: []
+  #   expected:
+  #     code: 1
+  #     stderr: [
+  #       'Options:\n  --moduleName, --module, -m    [required]\n  --serviceName, --service, -s  [required]\n  --data                        [default: {}]\n\n'
+  #       'Missing required arguments: moduleName, serviceName\n'
+  #     ]
+  # }
   # # This will be a valid test once in the actual execution environment.
   # # We can't use mockery to intercept the 'require' call because we are
   # # spawning a subprocess.


code cleanup

code cleanup

--- src/xml.coffee
+++ src/xml.coffee
@@ -12,10 +12,18 @@
     opts ?= {}
     return [name, attrs, children, opts]

+flush = ->
+    if @buffer.length
+        @write data for data in @buffer
+        @buffer = []
+
+rmevents = (events) ->
+    @removeListener 'data', events.data
+    @removeListener event, events[event] for event in EVENTS

 new_tag = ->
     [name, attrs, children, opts] = parse_args arguments...
-    pipe = {}
+    events = {}
     opts.level ?= @level+1

     opts = deep_merge @builder.opts, opts # possibility to overwrite existing opts, like pretty
@@ -28,18 +36,18 @@
         tag.end.apply tag, arguments if opts.end
         this

-    tag.on 'data', pipe.data = (data) =>
+    tag.on 'data', events.data = (data) =>
         if @pending[0] is tag
             @write data
         else
             @buffer.push data

-    EVENTS.forEach (event) =>
-        tag.on event, pipe[event] = (args...) =>
-            @emit event, args...
-
-    tag.on 'end', on_end = =>
-        tag.removeListener 'end', on_end
+    pipe = (event) =>
+        tag.on event, events[event] = =>
+            @emit event, arguments...
+    pipe event for event in EVENTS
+
+    tag.once 'end', on_end = =>
         if @pending[0] is tag
             if tag.pending.length
                 tag.pending[0].once 'end', on_end
@@ -48,13 +56,8 @@
                     @buffer = @buffer.concat tag.buffer
                     tag.buffer = []
                 @pending = @pending.slice(1)
-                tag.removeListener 'data', pipe.data
-                for event in EVENTS
-                    tag.removeListener event, pipe[event]
-                if @buffer.length
-                    for data in @buffer
-                        @write data
-                    @buffer = []
+                rmevents.call tag, events
+                flush.call this
                 if @closed and @pending.length is 0
                     @end()
         else
@@ -65,14 +68,9 @@
                         before = @pending[i-1]
                         before.buffer = before.buffer.concat @buffer
                         @buffer = []
-                    tag.removeListener 'data', pipe.data
-                    for event in EVENTS
-                        tag.removeListener event, pipe[event]
+                    rmevents.call tag, events
                     if @closed is 'pending'
-                        if @buffer.length
-                            for data in @buffer
-                                @write data
-                            @buffer = []
+                        flush.call this
                         @end()
                     return
             throw new Error("this shouldn't happen D:")
@@ -81,7 +79,6 @@
     @emit 'add', tag
     tag.children children, opts if children?
     return tag
-

 sync_tag = ->
     [name, attrs, children, opts] = parse_args arguments...


Label current turtle

Label current turtle

--- src/config/controls.coffee
+++ src/config/controls.coffee
@@ -17,11 +17,12 @@

   _CHILDREN: [
     {
-      stroke: undefined
-      fill: undefined
+      stroke: "white"
+      fill: "white"
       name: (world, args) ->
-        world.get("current")
+        cur = world.get("current")
+        "Current turtle: #{cur}"
     }
-    {col: 1, row: 1}
+    {col: 0, row: 1}
   ]
 }


updating button style and logic

updating button style and logic

--- src/components/task-plan/footer.cjsx
+++ src/components/task-plan/footer.cjsx
@@ -37,14 +37,14 @@

     plan = TaskPlanStore.get(id)

-    publishable = TaskPlanStore.isValid(id)
+    publishable = TaskPlanStore.isValid(id) and not TaskPlanStore.isPublished(id)
     deleteable = not TaskPlanStore.isNew(id) and not TaskPlanStore.isPublished(id)

     classes = ['-publish']
     classes.push('disabled') unless publishable
     classes = classes.join(' ')

-    publishButton = <BS.Button bsStyle='link' className={classes} onClick={@onPublish}>Publish</BS.Button>
+    publishButton = <BS.Button bsStyle='primary' className={classes} onClick={@onPublish}>Publish</BS.Button>

     if deleteable
       deleteLink = <BS.Button bsStyle='link' className='-delete' onClick={@onDelete}>Delete</BS.Button>