fix requestAnimation scope problems

fix requestAnimation scope problems

--- src/game/Game.coffee
+++ src/game/Game.coffee
@@ -13,7 +13,6 @@
 	# @option options {String} render Render (default: Rotten.Render.CANVAS)
 	# @option options {String} background Background color (default: #000)
 	# @option options {Object} canvas You can use a custom canvas to draw in
-	#
 	constructor: (options) ->

 		# Represents all the scenes (or game states) of the game
@@ -41,6 +40,23 @@
 		# Game assets manager
 		@assetManager = new Rotten.AssetLoader()

+		# Main game loop : Due to coffee script scope problems, I had to put
+		# that here
+		@requestAnimation = () =>
+			@currentScene.update()
+			@currentScene.draw()
+
+			if @lastLoop is null
+				@lastLoop = new Date().getTime()
+				requestAnimationFrame @requestAnimation
+				return
+
+			delta = (new Date().getTime() - @lastLoop) / 1000
+			@lastLoop = new Date().getTime()
+			@fps = (1 / delta).toFixed 1
+
+			requestAnimationFrame @requestAnimation
+

 	# Add a scene to your game (a config screen, a start screen, ...)
 	# and set it as default if default scene is null
@@ -56,39 +72,12 @@
 		if @currentScene is null then @currentScene = @scenes[name]

-	# Start your game with a scene name. If no scene name is provided,
+	# Start your game with a scene name. If no scene name is provided, then
+	# the first scene in game is used
+	#
+	# @param {String} sceneName Your scene name
 	start: (sceneName) ->
-		if sceneName is not null then @currentScene = @scenes[name]
-		if @currentScene is null then return
+		if sceneName is not null then @currentScene = @scenes[name] else @currentScene = @scenes[0]
+		if @currentScene is undefined or null then return

-		requestAnimation()
-
-
-	# Main game loop : Everything is done here
-	requestAnimation: () ->
-
-
-		#if(that.currentGameState.isStarted){
-		#	that.currentGameState.update();
-
-		#	//Clearing
-		#	FF.Render.clearRect(0,0,FF.Render.getWidth(),FF.Render.getHeight());
-
-		#	//Setting the background color
-		#	FF.Render.setBackgroundColor(that.backgroundColor);
-
-		#	that.currentGameState.draw();
-		#}
-
-		if @lastLoop is null
-			@lastLoop = new Date().getTime()
-			requestAnimationFrame @requestAnimation
-			return
-
-		delta = (new Date().getTime() - @lastLoop) / 1000
-		@lastLoop = new Date().getTime()
-		@fps = (1 / delta).toFixed 1
-
-		console.log fps
-
-		requestAnimationFrame @requestAnimation
+		@requestAnimation()


we need to send info about the browser

we need to send info about the browser

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -80,6 +80,14 @@
       responseCallback  : (response) ->
         { withArgs:[{ error: err, result }]} = response
         callback err, result
+      kite             :
+        username       : "#{KD.nick()}"
+        environment    : "#{KD.config.environment}"
+        name           : "browser"
+        version        : "1.0.#{KD.config.version}"
+        region         : "browser"
+        hostname       : "browser"
+        id             : uniqueID

     # by default, remove this callback after it is called once.
     callback.times ?= 1


work on #58 for popup.coffee

work on #58 for popup.coffee

--- src/lib/popup.coffee
+++ src/lib/popup.coffee
@@ -33,5 +33,5 @@
   # Send a request to the background page using the information provided.
   sendRequest: (item) ->
     chrome.extension.sendRequest
-      data: name: item.getAttribute 'name'
+      data: key: item.getAttribute 'key'
       type: 'popup'


Teambuilder: Update abbreviations

Teambuilder: Update abbreviations

--- client/app/js/models/pokemon.coffee
+++ client/app/js/models/pokemon.coffee
@@ -188,8 +188,8 @@
       label = name
       if invertedStats[PLUS]
         # This nature has an effect, so update the label
-        plusStat = shortenedStats[invertedStats[PLUS]]
-        minusStat = shortenedStats[invertedStats[MINUS]]
+        plusStat = statAbbreviations[invertedStats[PLUS]]
+        minusStat = statAbbreviations[invertedStats[MINUS]]
         label = "#{name} (+#{plusStat}, -#{minusStat})"

       natureResults.push [name, label]
@@ -244,12 +244,13 @@
     attributes

 # TODO: These shortenings really should be stored somewhere else.
-shortenedStats =
-  attack: "Atk"
-  defense: "Def"
-  specialAttack: "SpAtk"
-  specialDefense: "SpDef"
-  speed: "Spd"
+statAbbreviations =
+  'hp'             : 'HP'
+  'attack'         : 'Atk'
+  'defense'        : 'Def'
+  'specialAttack'  : 'SAtk'
+  'specialDefense' : 'SDef'
+  'speed'          : 'Spe'

 # A hash that keys a nature with the stats that it boosts.
 # Neutral natures are ignored.


where do undefined usernames come from, bah

--- client/shared/helpers/listen.coffee
+++ client/shared/helpers/listen.coffee
@@ -12,9 +12,10 @@
     updateMentionables = ->
       chain = Records.users.collection.chain().find(id: {'$in': $scope.mentionableUserIds})
       chain = chain.where (u) ->
-        u.name.toLowerCase().startsWith($scope.q) or
-        (u.username || "").toLowerCase().startsWith($scope.q) or
-        u.name.toLowerCase().includes(" #{$scope.q}")
+        _.isString(u.username) &&
+        (u.name.toLowerCase().startsWith($scope.q) or
+         (u.username || "").toLowerCase().startsWith($scope.q) or
+         u.name.toLowerCase().includes(" #{$scope.q}"))
       $scope.mentionables = _.sortBy(chain.data(), (u) -> (0 - Records.events.find(actorId: u.id).length))

     fetchThenUpdate = _.throttle ->


Adding russian language

Adding russian language

--- imports/api/messages/methods.coffee
+++ imports/api/messages/methods.coffee
@@ -12,7 +12,7 @@
 				message: type: String
 				language:
 					type: String
-					allowedValues: ['de', 'en', 'hu', 'pt', 'pl']
+					allowedValues: ['de', 'en', 'hu', 'pt', 'pl', 'ru']
 			.validator()
 		run: (args) -> if Meteor.isServer
 			newDoc =


setSelection fixes

setSelection fixes

--- src/selection.coffee
+++ src/selection.coffee
@@ -24,6 +24,7 @@
     line = line.prev
     lineOffset += line.length + 1
   leaf = line.findLeaf(node)
+  leaf = line.leaves.first unless leaf?
   leafOffset = 0
   while leaf.prev?
     leaf = leaf.prev
@@ -33,8 +34,15 @@
 _getPosition = (index) ->
   [line, lineOffset] = @doc.findLineAt(index)
   [leaf, leafOffset] = line.findLeafAt(lineOffset)
+  unless leaf?
+    leaf = line.leaves.first
+    leafOffset = 0
   node = leaf.node
-  node = node.firstChild if node.firstChild?
+  if DOM.isTextNode(node.firstChild)
+    node = node.firstChild
+  else if node.tagName == DOM.DEFAULT_BREAK_TAG
+    node = node.parentNode
+    leafOffset = node.childNodes.length - 1   # Set right before break tag
   return [node, leafOffset]

@@ -71,6 +79,7 @@
     selection = @document.getSelection()
     selection.removeAllRanges()
     if startNode?
+      @doc.root.focus()
       nativeRange = @document.createRange()
       nativeRange.setStart(startNode, startOffset)
       nativeRange.setEnd(endNode, endOffset)


Make ghost mouse appear in a straight line

--- app/lib/tutorial-steps.coffee
+++ app/lib/tutorial-steps.coffee
@@ -1,5 +1,7 @@
 GhostMouse = require 'ghost-mouse'
-ghostMouse = new GhostMouse events: false
+ghostMouse = new GhostMouse
+  events: false
+  swing: 0

 module.exports =
   welcome:


fix merge problem

fix merge problem

--- doghouse/frontend/index/index.cjsx
+++ doghouse/frontend/index/index.cjsx
@@ -172,5 +172,3 @@
       </section>

     </comp.layout>)
-
-#pellet.addComponentRoute "/index", indexPage


does the same for JS

does the same for JS

--- drivers/javascript2/src/ast.coffee
+++ drivers/javascript2/src/ast.coffee
@@ -41,7 +41,7 @@
     pluck: (fields...) -> new Pluck {}, @, fields...
     without: (fields...) -> new Without {}, @, fields...
     merge: ar (other) -> new Merge {}, @, other
-    between: ar (left, right) -> new Between {leftBound:left, rightBound:right}, @
+    between: ar (left, right) -> new Between {leftBound: (if left? then left else undefined), rightBound: (if right? then right else undefined)}, @
     reduce: (func, base) -> new Reduce {base:base}, @, funcWrap(func)
     map: ar (func) -> new Map {}, @, funcWrap(func)
     filter: ar (predicate) -> new Filter {}, @, funcWrap(predicate)


updates for auth headers

updates for auth headers

--- src/superflux.coffee
+++ src/superflux.coffee
@@ -3,12 +3,17 @@

 module.exports = class SuperFlux
     constructor: (args) ->
-        @socket = args.socket
+        @socket = args.socket or ->
+        @requestAuthHeaderKey = args.requestAuthHeaderKey or null
+        @requestAuthHeaderValue = args.requestAuthHeaderValue or null

         @actions = {}
         @stores = {}
         @callbacks = {}

+    setAuthHeader: (key, value) ->
+        @requestAuthHeaderKey = key
+        @requestAuthHeaderValue = value
     createStore: (store) ->
         store.notify = m.redraw

@@ -28,6 +33,9 @@
         # Call the constructor if present
         if 'init' of store
             store.init()
+
+        store.waitFor = (otherStore) ->
+

         return store

@@ -60,6 +68,8 @@
                     for callback in callbacks[name]
                         callback(args)

+        requestAuthHeaderKey = @requestAuthHeaderKey
+        requestAuthHeaderValue = @requestAuthHeaderValue
         Object.keys(async).map (name) ->
             configFn = async[name]

@@ -90,6 +100,13 @@
                     url: options.url
                     data: options.data
                     background: true
+                    config: (xhr) ->
+                        xhr.setRequestHeader('Content-Type', 'application/json')
+                        if requestAuthHeaderKey? and requestAuthHeaderValue?
+                            xhr.setRequestHeader(
+                                requestAuthHeaderKey
+                                requestAuthHeaderValue
+                            )
                 .then(successFn, failureFn)

         return @actions


Math is power

Math is power

--- SingularityUI/app/controllers/TaskDetail.coffee
+++ SingularityUI/app/controllers/TaskDetail.coffee
@@ -144,7 +144,7 @@
             avg = Math.round avg / times.length
             current =  new Date().getTime() - @models.task.get('task').taskId.startedAt
             # Alert if current uptime is longer than twice the average
-            if current < (avg * 2)
+            if current > (avg * 2)
                 alerts.push
                   title: 'Warning:',
                   message: 'This scheduled task has been running longer than twice the average for the request and may be stuck.',


Disable timer for now

Disable timer for now

--- src/MainMenu.coffee
+++ src/MainMenu.coffee
@@ -27,6 +27,6 @@
     # TODO this is hacky, make it cleaner
     @game.state.states['Game'].playerName = playerName
     @game.state.start 'Game'
-    @timer.startInterval()
+    #@timer.startInterval()

 module.exports = MainMenu


adds "awesome print" function

adds "awesome print" function

--- support/spec_helpers.coffee
+++ support/spec_helpers.coffee
@@ -57,3 +57,5 @@
   )

+window.AP = (object)->
+  "#{object.constructor.name}(#{jasmine.pp object})"


At least keep my own call sites consistent

--- src/ThreeCSG.coffee
+++ src/ThreeCSG.coffee
@@ -147,7 +147,7 @@

   invert: =>
     for poly in @polygons
-      do poly.flip
+      do poly.invert
     for flipper in [@divider, @front, @back]
       flipper?.invert()
     [@front, @back] = [@back, @front]


Write a function with attributes as if it were a class.

Using a function to create another function with a surrounding
closure containing variables is similar to a class with instance
variables.

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -107,33 +107,41 @@
       fail(messages.PreExistingProperty, method_name) if has_property(self, method_name)
       fail(messages.ReservedMethodName, method_name) if method_name == expects_method_name        # TODO: extract is_reserved_method_name()

-    build_mock_method = (object, method_name) ->
-      mock_method = (args...) ->
-        expectations.check_for_duplicate_expectations(method_name)                              # TODO: explain why we do this here
-        expectation = expectations.find_expectation(args...)
-        fail(messages.UnknownExpectation, method_name, args) unless expectation
-        expectation._called = yes
-        throw expectation._throws if expectation._throws
-        expectation._returns
-      original_method = object[method_name]
-      mock_method.expectations = expectations = new ExpectationList()
-      mock_method.restore_original_method = ->
-        if original_method?
-          object[method_name] = original_method
-        else
-          delete object[method_name]
-      mock_method.find_errors = ->
-        expectations.find_errors(method_name)
-      mock_method
-
     expects_method = (method_name) ->
       check_expects_usage(@, method_name, arguments.length)
-      if not @[method_name]?.expectations
+      if not mock_methods.contains(@[method_name])
         @[method_name] = build_mock_method(@, method_name)
         mock_methods.add(@[method_name])
       @[method_name].expectations.create_expectation()

     expects_method
+
+
+build_mock_method = (object, method_name) ->
+
+  expectations = new ExpectationList()
+  original_method = object[method_name]
+
+  mock_method = (args...) ->
+    expectations.check_for_duplicate_expectations(method_name)                              # TODO: explain why we do this here
+    expectation = expectations.find_expectation(args...)
+    fail(messages.UnknownExpectation, method_name, args) unless expectation
+    expectation._called = yes
+    throw expectation._throws if expectation._throws
+    expectation._returns
+
+  mock_method.expectations = expectations
+
+  mock_method.restore_original_method = ->
+    if original_method?
+      object[method_name] = original_method
+    else
+      delete object[method_name]
+
+  mock_method.find_errors = ->
+    expectations.find_errors(method_name)
+
+  mock_method

 class MockObject
@@ -227,6 +235,9 @@

   add: (mock_method) ->
     @_list.push(mock_method)
+
+  contains: (mock_method) ->
+    @_list.indexOf(mock_method) != -1

   # returns [ "an error re method1()", "another error re method1()", "an error re method2()", ... ]
   find_errors: ->


Calculator fixes. #3739040 , #3739033

Calculator fixes. #3739040 , #3739033

Fixed problem with subespression parsing/evaluation
multiplication symbols will be added in front right parentheses and pi.

--- calculator_spec.coffee
+++ calculator_spec.coffee
@@ -91,19 +91,35 @@
       @handle.press_buttons("( 2 ) 1 0 0 =")
       expect(@handle.output_content()).toEqual("200")

+    it "correctly handles side by side parentheticals", ->
+      @handle.press_buttons("8 ( 9 ) ( 8 ) =")
+      expect(@handle.output_content()).toEqual("576")
+
   it "divides", ->
     @handle.press_buttons("1 0 / 2 =")
     expect(@handle.output_content()).toEqual("5")
-

   describe "pi", ->
     it "does pi", ->
       @handle.press_buttons("pi =")
       expect(@handle.output_content()).toMatch /3.14/

+    it "multiplies pis side by side", ->
+      @handle.press_buttons("pi pi =")
+      expect(@handle.output_content()).toMatch /9.86/
+
+    it "handles the sample instance presented by mario", ->
+      @handle.press_buttons("2 pi ( 1 + 3 ) =")
+      expect(@handle.output_content()).toMatch /25.13/
+
+    it "handles another compilcated example", ->
+      @handle.press_buttons("pi 2 pi ( 1 + 3 ) ( 1 0 ) =")
+      expect(@handle.output_content()).toMatch /789.57/
+
     it "multiplies pi", ->
       @handle.press_buttons("5 pi =")
       expect(@handle.output_content()).toMatch /15.708/
+
     it "multiplies pi reversed", ->
       @handle.press_buttons("pi 5 =")
       expect(@handle.output_content()).toMatch /15.708/


make powershell check async

make powershell check async

this noticably froze atom's ui

--- lib/connection/process/basic.coffee
+++ lib/connection/process/basic.coffee
@@ -43,37 +43,43 @@

   # Windows Stuff

+  wrapperEnabled: -> atom.config.get "julia-client.enablePowershellWrapper"
+
   sendSignalToWrapper: (signal, port) ->
     wrapper = net.connect({port})
     wrapper.setNoDelay()
     wrapper.write(signal)
     wrapper.end()

+  checkPowershellVersion: ->
+    new Promise (resolve) ->
+      p = child_process.spawn("powershell", ["-NoProfile", "$PSVersionTable.PSVersion.Major"])
+      p.stdout.on 'data', (data) -> resolve parseInt(data.toString()) >= 3
+      p.on 'error', -> resolve false
+      p.on 'exit', -> resolve false
+
   getWindows: (path, args) ->
-    useWrapper = atom.config.get "julia-client.enablePowershellWrapper"
-    powershell = parseInt(child_process.spawnSync("powershell",
-                                                  ["-NoProfile", "$PSVersionTable.PSVersion.Major"])
-                            .output[1].toString()) > 2
-    if !(useWrapper && powershell)
+    return @getUnix(path, args) unless @wrapperEnabled()
+    @checkPowershellVersion().then (powershell) =>
       if not powershell
         client.stderr "PowerShell version < 3 encountered. Running without wrapper (interrupts won't work)."
-      @getUnix path, args
-    else
-      @freePort().then (port) =>
-        wrapPort = port+1
-        jlargs = [args..., '`"' + paths.script('boot.jl') + '`"', port]
-        proc = child_process.spawn("powershell",
-                                    ["-NoProfile", "-ExecutionPolicy", "bypass",
-                                     "& \"#{paths.script "spawnInterruptible.ps1"}\"
-                                     -wrapPort #{wrapPort}
-                                     -jlpath \"#{path}\"
-                                     -jlargs #{jlargs}"])
+        @getUnix path, args
+      else
+        @freePort().then (port) =>
+          wrapPort = port+1
+          jlargs = [args..., '`"' + paths.script('boot.jl') + '`"', port]
+          proc = child_process.spawn("powershell",
+                                     ["-NoProfile", "-ExecutionPolicy", "bypass",
+                                      "& \"#{paths.script "spawnInterruptible.ps1"}\"
+                                      -wrapPort #{wrapPort}
+                                      -jlpath \"#{path}\"
+                                      -jlargs #{jlargs}"])

-        @createProc proc,
-          wrapper: true
-          kill: => @sendSignalToWrapper 'KILL', wrapPort
-          interrupt: => @sendSignalToWrapper 'SIGINT', wrapPort
-          socket: @socket proc, port
+          @createProc proc,
+            wrapper: true
+            kill: => @sendSignalToWrapper 'KILL', wrapPort
+            interrupt: => @sendSignalToWrapper 'SIGINT', wrapPort
+            socket: @socket proc, port

   get: (a...) ->
     if process.platform is 'win32'


Add `atom.activatePackage(id)` method

Add `atom.activatePackage(id)` method

It will load the specified package if it hasn't already been loaded.

--- src/app/atom.coffee
+++ src/app/atom.coffee
@@ -78,12 +78,18 @@
       _.detect @activePackages, (pack) -> pack.path is path

   activatePackages: ->
-    for pack in @loadedPackages
+    @activatePackage(pack.path) for pack in @getLoadedPackages()
+
+  activatePackage: (id) ->
+    if pack = @loadPackage(id)
       @activePackages.push(pack)
       pack.activate()

   getLoadedPackages: ->
     _.clone(@loadedPackages)
+
+  getActivePackages: ->
+    _.clone(@activePackages)

   getPackagePaths: ->
     packagePaths = []


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -8,7 +8,7 @@
     x: 0
     y: 5

-  engine.add
+  obj2engine.add
     class: "Awesome"
     x: 5
     y: 10


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -25,6 +25,6 @@
     x: 4
     y: 20

-  deepEquals engine.first('.type=testObj'), obj1
+  deepEqual engine.first('.type=testObj'), obj1

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -1,7 +1,7 @@
 module "Engine"

 test "#each", ->
-
+  engine =

 test "#find", ->
   engine = Engine()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -8,7 +8,7 @@
     x: 0
     y: 50

-  equals engine.find('.findable')
+  equals engine.find('.findable'),

 module()


Organize workspace API into sections. Add ::getActiveTextEditor

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -16,17 +16,6 @@
 # editors, and manipulate panes. To add panels, you'll need to use the
 # {WorkspaceView} class for now until we establish APIs at the model layer.
 #
-# ## Events
-#
-# ### uri-opened
-#
-# Extended: Emit when something has been opened. This can be anything, from an
-# editor to the settings view. You can get the new item via {::getActivePaneItem}
-#
-# ### editor-created
-#
-# Extended: Emit when an editor is created (a file opened).
-#
 # * `editor` {Editor} the new editor
 #
 module.exports =
@@ -90,7 +79,11 @@
   editorAdded: (editor) ->
     @emit 'editor-created', editor

-  # Public: Invoke the given callback when a pane is added to the workspace.
+  ###
+  Section: Event Subscription
+  ###
+
+  # Extended: Invoke the given callback when a pane is added to the workspace.
   #
   # * `callback` {Function} to be called panes are added.
   #   * `event` {Object} with the following keys:
@@ -99,7 +92,7 @@
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onDidAddPane: (callback) -> @paneContainer.onDidAddPane(callback)

-  # Public: Invoke the given callback with all current and future panes in the
+  # Extended: Invoke the given callback with all current and future panes in the
   # workspace.
   #
   # * `callback` {Function} to be called with current and future panes.
@@ -107,7 +100,7 @@
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   observePanes: (callback) -> @paneContainer.observePanes(callback)

-  # Public: Invoke the given callback when a pane item is added to the
+  # Extended: Invoke the given callback when a pane item is added to the
   # workspace.
   #
   # * `callback` {Function} to be called panes are added.
@@ -119,7 +112,7 @@
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback)

-  # Public: Invoke the given callback with all current and future panes items in
+  # Extended: Invoke the given callback with all current and future panes items in
   # the workspace.
   #
   # * `callback` {Function} to be called with current and future pane items.
@@ -127,7 +120,7 @@
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   observePaneItems: (callback) -> @paneContainer.observePaneItems(callback)

-  # Public: Invoke the given callback when a text editor is added to the
+  # Extended: Invoke the given callback when a text editor is added to the
   # workspace.
   #
   # * `callback` {Function} to be called panes are added.
@@ -142,8 +135,8 @@
     @onDidAddPaneItem ({item, pane, index}) ->
       callback({textEditor: item, pane, index}) if item instanceof Editor

-  # Public: Invoke the given callback with all current and future text editors
-  # in the workspace.
+  # Essential: Invoke the given callback with all current and future text
+  # editors in the workspace.
   #
   # * `callback` {Function} to be called with current and future text editors.
   #
@@ -173,13 +166,11 @@

     editors

-  # Public: Get all text editors in the workspace.
-  #
-  # Returns an {Array} of {Editor}s.
-  getTextEditors: ->
-    @getPaneItems().filter (item) -> item instanceof Editor
-
-  # Public: Open a given a URI in Atom asynchronously.
+  ###
+  Section: Opening
+  ###
+
+  # Essential: Open a given a URI in Atom asynchronously.
   #
   # * `uri` A {String} containing a URI.
   # * `options` (optional) {Object}
@@ -269,7 +260,7 @@
       .catch (error) ->
         console.error(error.stack ? error)

-  # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been
+  # Extended: Asynchronously reopens the last-closed item's URI if it hasn't already been
   # reopened.
   #
   # Returns a promise that is resolved when the item is opened
@@ -285,7 +276,7 @@
     if uri = @destroyedItemUris.pop()
       @openSync(uri)

-  # Public: Register an opener for a uri.
+  # Extended: Register an opener for a uri.
   #
   # An {Editor} will be used if no openers return a value.
   #
@@ -301,64 +292,52 @@
   registerOpener: (opener) ->
     @openers.push(opener)

-  # Public: Unregister an opener registered with {::registerOpener}.
+  # Extended: Unregister an opener registered with {::registerOpener}.
   unregisterOpener: (opener) ->
     _.remove(@openers, opener)

   getOpeners: ->
     @openers

-  # Public: Get all panes in the workspace.
-  #
-  # Returns an {Array} of {Pane}s.
-  getPanes: ->
-    @paneContainer.getPanes()
-
-  # Public: Get the active {Pane}.
-  #
-  # Returns a {Pane}.
-  getActivePane: ->
-    @paneContainer.getActivePane()
-
-  # Public: Get all pane items in the workspace.
+  ###
+  Section: Pane Items
+  ###
+
+  # Essential: Get all pane items in the workspace.
   #
   # Returns an {Array} of items.
   getPaneItems: ->
     @paneContainer.getPaneItems()

-  # Public: Get the active {Pane}'s active item.
+  # Essential: Get the active {Pane}'s active item.
   #
   # Returns an pane item {Object}.
   getActivePaneItem: ->
     @paneContainer.getActivePaneItem()

-  # Public: Get all {Pane}s.
-  #
-  # Returns an {Array} of {Pane}s.
-  getPanes: ->
-    @paneContainer.getPanes()
-
-  # Public: Save all pane items.
+  # Essential: Get all text editors in the workspace.
+  #
+  # Returns an {Array} of {Editor}s.
+  getTextEditors: ->
+    @getPaneItems().filter (item) -> item instanceof Editor
+
+  # Essential: Get the active item if it is an {Editor}.
+  #
+  # Returns an {Editor} or `undefined` if the current active item is not an
+  # {Editor}.
+  getActiveTextEditor: ->
+    activeItem = @getActiveItem()
+    activeItem if activeItem instanceof Editor
+
+  # Deprecated:
+  getActiveEditor: ->
+    @activePane?.getActiveEditor()
+
+  # Extended: Save all pane items.
   saveAll: ->
     @paneContainer.saveAll()

-  # Public: Make the next pane active.
-  activateNextPane: ->
-    @paneContainer.activateNextPane()
-
-  # Public: Make the previous pane active.
-  activatePreviousPane: ->
-    @paneContainer.activatePreviousPane()
-
-  # Public: Get the first pane {Pane} with an item for the given URI.
-  #
-  # * `uri` {String} uri
-  #
-  # Returns a {Pane} or `undefined` if no pane exists for the given URI.
-  paneForUri: (uri) ->
-    @paneContainer.paneForUri(uri)
-
-  # Public: Save the active pane item.
+  # Save the active pane item.
   #
   # If the active pane item currently has a URI according to the item's
   # `.getUri` method, calls `.save` on the item. Otherwise
@@ -367,7 +346,7 @@
   saveActivePaneItem: ->
     @activePane?.saveActiveItem()

-  # Public: Prompt the user for a path and save the active pane item to it.
+  # Prompt the user for a path and save the active pane item to it.
   #
   # Opens a native dialog where the user selects a path on disk, then calls
   # `.saveAs` on the item with the selected path. This method does nothing if
@@ -375,34 +354,59 @@
   saveActivePaneItemAs: ->
     @activePane?.saveActiveItemAs()

-  # Public: Destroy (close) the active pane item.
+  # Destroy (close) the active pane item.
   #
   # Removes the active pane item and calls the `.destroy` method on it if one is
   # defined.
   destroyActivePaneItem: ->
     @activePane?.destroyActiveItem()

-  # Public: Destroy (close) the active pane.
+  ###
+  Section: Panes
+  ###
+
+  # Extended: Get all panes in the workspace.
+  #
+  # Returns an {Array} of {Pane}s.
+  getPanes: ->
+    @paneContainer.getPanes()
+
+  # Extended: Get the active {Pane}.
+  #
+  # Returns a {Pane}.
+  getActivePane: ->
+    @paneContainer.getActivePane()
+
+  # Extended: Make the next pane active.
+  activateNextPane: ->
+    @paneContainer.activateNextPane()
+
+  # Extended: Make the previous pane active.
+  activatePreviousPane: ->
+    @paneContainer.activatePreviousPane()
+
+  # Extended: Get the first pane {Pane} with an item for the given URI.
+  #
+  # * `uri` {String} uri
+  #
+  # Returns a {Pane} or `undefined` if no pane exists for the given URI.
+  paneForUri: (uri) ->
+    @paneContainer.paneForUri(uri)
+
+  # Destroy (close) the active pane.
   destroyActivePane: ->
     @activePane?.destroy()

-  # Public: Get the active item if it is an {Editor}.
-  #
-  # Returns an {Editor} or `undefined` if the current active item is not an
-  # {Editor}.
-  getActiveEditor: ->
-    @activePane?.getActiveEditor()
-
-  # Public: Increase the editor font size by 1px.
+  # Increase the editor font size by 1px.
   increaseFontSize: ->
     atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)

-  # Public: Decrease the editor font size by 1px.
+  # Decrease the editor font size by 1px.
   decreaseFontSize: ->
     fontSize = atom.config.get("editor.fontSize")
     atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1

-  # Public: Restore to a default editor font size.
+  # Restore to a default editor font size.
   resetFontSize: ->
     atom.config.restoreDefault("editor.fontSize")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -14,6 +14,7 @@

   # this will call coolEventHandler when
   # yourObject.trigger "someCustomEvent" is called
+  # Alternatively you can pass in an anonymous function as the handler
   yourObject.bind "someCustomEvent", coolEventHandler

   </pre></code>


Don't set the root interface on the sightglass global module (we don't want to be setting this from within Rivets).

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -28,7 +28,6 @@
     @binder = {routine: @binder} if @binder instanceof Function

   observe: (obj, keypath, callback) =>
-    Rivets.sightglass.root = '.'
     Rivets.sightglass obj, keypath, callback,
       root: @view.rootInterface
       adapters: @view.adapters


fix promise status when promise resolves before render

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -185,9 +185,7 @@

     PromiseStatusComponent = React.createClass
       render: ->
-        if @props.promise.isPending()
-          text = "Loading"
-        else
+        if @state?
           ms = @state.duration
           duration = if ms >= 1000
             s = (ms / 1000).toFixed 1
@@ -198,6 +196,8 @@
             text = "Loaded in #{duration}"
           else
             text = "Failed after #{duration}"
+        else
+          text = "Loading"
         React.DOM.div {className: 'promise-status'}, text
       getInitialState: ->
         unless @props.promise.isPending()


kbd component

kbd component

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -57,11 +57,14 @@
         fns = @imported_context_fns
       help fns

+    KeySequenceComponent = React.createClass
+      render: -> React.DOM.span {}, _.map @props.keys, (k) -> React.DOM.kbd {}, k
+
     KeyBindingComponent = React.createClass
       render: ->
         React.DOM.table {}, _.map @props.keys, (command, key) =>
           React.DOM.tr {}, [
-            React.DOM.th {}, _.map key.split('-'), (k) -> React.DOM.kbd {}, k
+            React.DOM.th {}, KeySequenceComponent keys: key.split('-')
             React.DOM.td {}, React.DOM.strong {}, command.name
             React.DOM.td {}, command.doc
           ]
@@ -142,7 +145,12 @@

     component_cmd 'intro', 'Shows the intro message', ->
       React.DOM.div {}, [
-        TextComponent value: "Welcome to lead.js!\n\nPress Shift+Enter to execute the CoffeeScript in the console. Try running"
+        React.DOM.p {}, 'Welcome to lead.js!'
+        React.DOM.p {}, [
+          'Press '
+          KeySequenceComponent(keys: ['Shift', 'Enter'])
+          ' to execute the CoffeeScript in the console. Try running'
+        ]
         ExampleComponent value: "browser '*'", ctx: @, run: true
         TextComponent value: 'Look at'
         ExampleComponent value: 'docs', ctx: @, run: true


getInitialState can't return undefined

getInitialState can't return undefined

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -200,7 +200,9 @@
           text = "Loading"
         React.DOM.div {className: 'promise-status'}, text
       getInitialState: ->
-        unless @props.promise.isPending()
+        if @props.promise.isPending()
+          null
+        else
           return duration: 0
       finished: ->
         @setState duration: new Date - @props.start_time


use react for all builtins

use react for all builtins

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -2,7 +2,6 @@
   CodeMirror = require 'cm/codemirror'
   require 'cm/runmode'
   URI = require 'URIjs'
-  $ = require 'jquery'
   _ = require 'underscore'
   marked = require 'marked'
   modules = require 'modules'
@@ -20,20 +19,18 @@
     else
       target.textContent = code

+  FunctionDocumentationComponent = React.createClass
+    render: ->
+      React.DOM.dl {}, _.map @props.fns, (fn) ->
+        [
+          React.DOM.dt {}, fn.name
+          React.DOM.dd {}, fn.doc
+        ]
+
   help = (fns) ->
     documented_fns = (name for name, c of fns when c?.doc?)
     documented_fns.sort()
-    $dl = $ '
'
-    for cmd in documented_fns
-      $tt = $ '<tt/>'
-      $tt.text cmd
-      $dt = $ '
'
-      $dt.append $tt
-      $dl.append $dt
-      $dd = $ '
'
-      $dd.text fns[cmd].doc
-      $dl.append $dd
-    $dl
+    FunctionDocumentationComponent fns: _.map documented_fns, (name) -> {name, doc: fns[name].doc}

   modules.create 'builtins', ({fn, cmd}) ->
     cmd 'help', 'Shows this help', (cmd) ->
@@ -53,27 +50,31 @@
           return
       else
         fns = @imported_context_fns
-      @add_rendered help(fns)
+      @add_component help(fns)
+
+    KeyBindingComponent = React.createClass
+      render: ->
+        React.DOM.table {}, _.map @props.keys, (command, key) =>
+          React.DOM.tr {}, [
+            React.DOM.th {}, _.map key.split('-'), (k) -> React.DOM.kbd {}, k
+            React.DOM.td {}, React.DOM.strong {}, command.name
+            React.DOM.td {}, command.doc
+          ]

     cmd 'keys', 'Shows the key bindings', ->
       all_keys = {}
+      # TODO some commands are functions instead of names
       build_map = (map) ->
         for key, command of map
-          unless key == 'fallthrough' or all_keys[key]?
-            all_keys[key] = command
+          fn = CodeMirror.commands[command]
+          unless key == 'fallthrough' or all_keys[key]? or not fn?
+            all_keys[key] = name: command, doc: fn.doc
         fallthroughs = map.fallthrough
         if fallthroughs?
           build_map CodeMirror.keyMap[name] for name in fallthroughs
       build_map CodeMirror.keyMap.lead

-      $table = $ '
'
-      for key, command of all_keys
-        fn = CodeMirror.commands[command]
-        if fn
-          doc = fn.doc ? ''
-          kbd = key.split('-').map((k) -> "#{k}").join ' + '
-          $table.append "
#{kbd}	#{command}	#{doc}
"
-      @add_rendered $table
+      @add_component KeyBindingComponent keys: all_keys, commands: CodeMirror.commands

     fn 'In', 'Gets previous input', (n) ->
       @value @get_input_value n
@@ -86,10 +87,11 @@
       s ||= new String o
       @add_component source value: s, language: 'json'

+    MarkdownComponent = React.createClass
+      render: -> React.DOM.div className: 'user-html', dangerouslySetInnerHTML: __html: marked @props.value
+
     fn 'md', 'Renders Markdown', (string) ->
-      $html = $ '
'
-      $html.html marked string
-      @add_rendered $html
+      @add_component MarkdownComponent value: string

     text = React.createClass
       render: -> React.DOM.p {}, @props.value
@@ -109,10 +111,11 @@
     fn 'html', 'Adds some HTML', (string) ->
       @add_component html value: string

+    ErrorComponent = React.createClass
+      render: -> React.DOM.pre {className: 'error'}, @props.message
+
     fn 'error', 'Shows a preformatted error message', (message) ->
-      $pre = $ '
'
-      $pre.text message
-      @add_rendered $pre
+      @add_component ErrorComponent {message}

     example = React.createClass
       render: -> React.DOM.div {className: 'example', onClick: @on_click}, @transferPropsTo source()
@@ -148,14 +151,17 @@
         _.extend @current_options, options
       @value @current_options

+    LinkComponent = React.createClass
+      render: -> React.DOM.a {href: @props.href}, @props.value
+
     cmd 'permalink', 'Create a link to the code in the input cell above', (code) ->
       a = document.createElement 'a'
+      # TODO app should generate links
       a.href = location.href
       a.hash = null
       code ?= @previously_run()
       a.search = '?' + encodeURIComponent btoa code
-      a.innerText = a.href
-      @add_rendered a
+      @add_component LinkComponent href: a.href, value: a.href

     fn 'websocket', 'Runs commands from a web socket', (url) ->
       ws = new WebSocket url


Fix multiple search-replace for -p

Fix multiple search-replace for -p

--- app/assets/javascripts/console-ish-input.coffee
+++ app/assets/javascripts/console-ish-input.coffee
@@ -50,10 +50,10 @@
 old_submit = TS.view.submit
 TS.view.submit = (event) ->
   current = input.val()
-  if current.endsWith("!$")
+  if current.indexOf("!$") != -1
     history = TS.chat_history.initializeChannelHistory(TS.model.active_cid)
     last_message = history.entries[0]
-    last_word = last_message.slice(last_message.lastIndexOf(" ") + 1)
-    replaced = current.replace(/!\$$/, last_word)
+    last_word = last_message.slice(last_message.trim().lastIndexOf(" ") + 1)
+    replaced = current.replace(/!\$/g, last_word)
     input.val(replaced)
   old_submit(event)


added interaction to buttons in tutorial

--- app/assets/javascripts/inspector_address.coffee
+++ app/assets/javascripts/inspector_address.coffee
@@ -261,6 +261,7 @@
         intro: "Hit next to save it and move on. "
         position: "top"
         polygon_index: 1
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -270,9 +271,10 @@
       }
       {
         element: "#submit-button"
-        intro: "Boom! Hit next to save!"
+        intro: "Boom! Hit next to save and continue!"
         position: "top"
         polygon_index: 2
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -285,6 +287,7 @@
         intro: "Next!"
         position: "top"
         polygon_index: 3
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -297,6 +300,7 @@
         intro: "Skip it like a jump rope!"
         position: "top"
         polygon_index: 4
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -309,6 +313,7 @@
         intro: "Hit next to save 'em."
         position: "top"
         polygon_index: 5
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -321,6 +326,7 @@
         intro: "You know what to do."
         position: "top"
         polygon_index: 6
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -333,6 +339,7 @@
         intro: "Yep, hit next."
         position: "top"
         polygon_index: 7
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -345,6 +352,7 @@
         intro: "I know, I know \" Next, next, next, next,  NEXT!\" Sorry for all the redundancy but this is just how tutorials work!"
         position: "top"
         polygon_index: 8
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -357,6 +365,7 @@
         intro: "Next. (at least you'll be an expert at hitting next by the end of this)"
         position: "top"
         polygon_index: 9
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -369,6 +378,7 @@
         intro: "Clearly, next."
         position: "top"
         polygon_index: 10
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -381,6 +391,7 @@
         intro: "Skip it like your weird cousin's wedding!"
         position: "top"
         polygon_index: 11
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -393,6 +404,7 @@
         intro: "Ext-nay (a little pig latin always keeps things interesting right?)"
         position: "top"
         polygon_index: 12
+        ixactive: true
       }
       {
         element: ".leaflet-control-zoom.leaflet-bar.leaflet-control"
@@ -411,6 +423,7 @@
         intro: "You know what to do."
         position: "top"
         polygon_index: 13
+        ixactive: true
       }
       {
         element: ".leaflet-control-zoom.leaflet-bar.leaflet-control"
@@ -429,6 +442,7 @@
         intro: "You know the rest!"
         position: "top"
         polygon_index: 14
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -441,6 +455,7 @@
         intro: "!TXEN (hold computer up to mirror to reveal this secret instruction.)"
         position: "top"
         polygon_index: 15
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -453,6 +468,7 @@
         intro: "And submit it the best way you know how."
         position: "top"
         polygon_index: 16
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -465,6 +481,7 @@
         intro: "Whichever is cool with us. Hit next to continue."
         position: "top"
         polygon_index: 17
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -477,6 +494,7 @@
         intro: "Next, of course. You're showing remarkable patience and fortitude with this. You should really be proud of yourself!"
         position: "top"
         polygon_index: 18
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -489,6 +507,7 @@
         intro: "Duh."
         position: "top"
         polygon_index: 19
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -501,6 +520,7 @@
         intro: "What do we want?
  NEXT!
 When do we want it?
  NOW! "
         position: "top"
         polygon_index: 20
+        ixactive: true
       }
       {
           element: ".leaflet-control-zoom.leaflet-bar.leaflet-control"
@@ -519,6 +539,7 @@
         intro: "Next."
         position: "top"
         polygon_index: 26
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -531,6 +552,7 @@
         intro: "[So skip it like a flat stone on a calm lake!"
         position: "top"
         polygon_index: 21
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -543,6 +565,7 @@
         intro: "Aaaaaaaaaaaaaand NEXT!"
         position: "top"
         polygon_index: 22
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -555,6 +578,7 @@
         intro: "[no text provided]"
         position: "top"
         polygon_index: 24
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -567,6 +591,7 @@
         intro: " Like Wild Horses on a Rolling Stones playlist!"
         position: "top"
         polygon_index: 25
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -579,6 +604,7 @@
         intro: "I swear this is the last one - Next!"
         position: "top"
         polygon_index: 27
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -591,6 +617,7 @@
         intro: "Or skip it like your 10-year high school reunion!"
         position: "top"
         polygon_index: 28
+        ixactive: true
       }
       {
         element: "#link-help"


Added event handler to track loading during route change/success/error. closes #11

--- app/assets/javascripts/sprangular/module.coffee
+++ app/assets/javascripts/sprangular/module.coffee
@@ -36,6 +36,7 @@

   $rootScope.$on '$routeChangeStart', (event, next, current) ->
     requirements = next.requires || {}
+    Status.routeChanging = true

     if requirements.user && !Account.isLogged
       Status.requestedPath = next.$$route.originalPath
@@ -52,3 +53,9 @@
       Flash.error('Sorry, there are no items in your cart.')
       $location.path('/')
       event.preventDefault()
+
+  $rootScope.$on '$routeChangeSuccess', ->
+    Status.routeChanging = false
+
+  $rootScope.$on '$routeChangeError', ->
+    Status.routeChanging = false


Set pointer events back to fill on delete button

--- app/classifier/drawing-tools/freehand-line.cjsx
+++ app/classifier/drawing-tools/freehand-line.cjsx
@@ -66,7 +66,7 @@

       {if @props.selected
         deletePosition = @getDeletePosition points
-        <g>
+        <g pointerEvents="fill">
           <DeleteButton tool={this}
             x={deletePosition.x}
             y={deletePosition.y}


Clean the custom exception module.

Clean the custom exception module.

--- lib/helpers/custom_exception.coffee
+++ lib/helpers/custom_exception.coffee
@@ -1,10 +1,6 @@
 #global $, _
-((NS) ->
   "use strict"
-
   # Filename: helpers/error_helper.js */
-  NS = NS or {}
-  isInBrowser = typeof module is 'undefined' and typeof window isnt 'undefined'
   # Exception class
   ###
    * Creates a new CustomException.
@@ -42,9 +38,4 @@
     ArgumentInvalidException: ArgumentInvalidException
     DependencyException: DependencyException
   }
-  if isInBrowser
-    NS.Helpers = NS.Helpers or {}
-    NS.Helpers.Exceptions = mod
-  else
-    module.exports = mod
-)(if typeof module is 'undefined' and typeof window isnt 'undefined' then window.Fmk else module.exports)+  module.exports = mod


Add package name to panel heading

Add package name to panel heading

--- lib/keybinding-resolver-view.coffee
+++ lib/keybinding-resolver-view.coffee
@@ -5,7 +5,9 @@
 class KeybindingResolverView extends View
   @content: ->
     @div class: 'keybinding-resolver tool-panel pannel panel-bottom padding', =>
-      @div outlet: 'keystroke', class: 'panel-heading padded', 'Press any key'
+      @div class: 'panel-heading padded', =>
+        @span 'Keybinding Resolver: '
+        @span outlet: 'keystroke', 'Press any key'
       @div outlet: 'commands', class: 'panel-body padded'

   initialize: ({attached})->


remove some pesky c.logs

remove some pesky c.logs

--- lib/live_document_collection.coffee
+++ lib/live_document_collection.coffee
@@ -40,11 +40,8 @@
       # if the place where we WOULD insert it, if we were to reinsert it
       # is not 0 (right before itself) or 1 (right after itself)
         if 0 >= (oldIndex - newIndex) <= 1
-          console.log(@length)
           @handleRemove(document)
-          console.log(@length)
           @handleInsert(document)
-          console.log(@length)

     handleLoad: (documents) ->
       _.each documents, (doc) =>


fixed #77: SYNTAX ERROR -> VALUE ERROR

fixed #77: SYNTAX ERROR -> VALUE ERROR

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -57,8 +57,10 @@
           if (v = vars["get_#{name}"])?.category is VERB
             NOUN
           else
-            # x ⋄ x←0 !!! SYNTAX ERROR
-            vars[name]?.category or err node, "Symbol '#{name}' is referenced before assignment."
+            # x ⋄ x←0 !!! VALUE ERROR
+            vars[name]?.category or
+              valueError "Symbol '#{name}' is referenced before assignment.",
+                file: opts.file, line: node.startLine, col: node.startCol, aplCode: opts.aplCode
         when 'lambda'
           for i in [1...node.length]
             queue.push extend (body = node[i]),


WIP to replace strings with Expr objects.

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -115,6 +115,29 @@
         # java bytecode verification ensures that the stack height and stack /
         # local table types match up across blocks
         next_block.compile @stack[..], @locals[..]
+
+class Expr
+
+  constructor: (str, subexps...) ->
+    @fragments = str.split /($\d+)/
+    for frag, i in @fragments
+      if /$\d+/.test frag
+        @fragments[i] = subexps[parseInt frag[1..], 10]
+
+  eval: (b) ->
+    temp = b.new_temp()
+    b.add_line "$0 = #{@}", temp
+    new Primitive b
+
+  toString: -> @fragments.join ''
+
+class Primitive extends Expr
+
+  constructor: (@str) ->
+
+  eval: -> @
+
+  toString: -> @str

 cmpMap =
   eq: '=='
@@ -217,29 +240,30 @@
         b.push temp

 compile_obj_handlers = {
-  aconst_null: { compile: (b) -> b.push "null"; }
-  iconst_m1: { compile: (b) -> b.push "-1"; }
-  iconst_0: { compile: (b) -> b.push "0"; }
-  iconst_1: { compile: (b) -> b.push "1"; }
-  iconst_2: { compile: (b) -> b.push "2"; }
-  iconst_3: { compile: (b) -> b.push "3"; }
-  iconst_4: { compile: (b) -> b.push "4"; }
-  iconst_5: { compile: (b) -> b.push "5"; }
-  lconst_0: { compile: (b) -> b.push2 "gLong.ZERO"; }
-  lconst_1: { compile: (b) -> b.push2 "gLong.ONE"; }
-  fconst_0: { compile: (b) -> b.push "0"; }
-  fconst_1: { compile: (b) -> b.push "1"; }
-  fconst_2: { compile: (b) -> b.push "2"; }
-  dconst_0: { compile: (b) -> b.push2 "0"; }
-  dconst_1: { compile: (b) -> b.push2 "1"; }
-  iastore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  lastore: {compile: (b) -> v=b.pop2();i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  fastore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  dastore: {compile: (b) -> v=b.pop2();i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  aastore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  bastore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  castore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
-  sastore: {compile: (b) -> v=b.pop(); i=b.pop();"b.check_null(#{b.pop()}).array[#{i}]=#{v}"}
+  aconst_null: { compile: (b) -> b.push new Primitive "null"; }
+  iconst_m1: { compile: (b) -> b.push new Primitive "-1"; }
+  iconst_0: { compile: (b) -> b.push new Primitive "0"; }
+  iconst_1: { compile: (b) -> b.push new Primitive "1"; }
+  iconst_2: { compile: (b) -> b.push new Primitive "2"; }
+  iconst_3: { compile: (b) -> b.push new Primitive "3"; }
+  iconst_4: { compile: (b) -> b.push new Primitive "4"; }
+  iconst_5: { compile: (b) -> b.push new Primitive "5"; }
+  lconst_0: { compile: (b) -> b.push2 new Primitive "gLong.ZERO"; }
+  lconst_1: { compile: (b) -> b.push2 new Primitive "gLong.ONE"; }
+  fconst_0: { compile: (b) -> b.push new Primitive "0"; }
+  fconst_1: { compile: (b) -> b.push new Primitive "1"; }
+  fconst_2: { compile: (b) -> b.push new Primitive "2"; }
+  dconst_0: { compile: (b) -> b.push2 new Primitive "0"; }
+  dconst_1: { compile: (b) -> b.push2 new Primitive "1"; }
+  # the *astore commands don't work yet...
+  iastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  lastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  fastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  dastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  aastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  bastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  castore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  sastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
   pop: {compile: (b) -> b.pop()}
   pop2: {compile: (b) -> b.pop2()}
   # TODO: avoid duplicating non-primitive expressions so as to save on computation
@@ -250,14 +274,14 @@
   dup2_x1: {compile: (b) -> [v1,v2,v3]=[b.pop(),b.pop(),b.pop()];b.push(v2,v1,v3,v2,v1)}
   dup2_x2: {compile: (b) -> [v1,v2,v3,v4]=[b.pop(),b.pop(),b.pop(),b.pop()];b.push(v2,v1,v4,v3,v2,v1)}
   swap: {compile: (b) -> v2=b.pop(); v1=b.pop(); b.push(v2,v1)}
-  iadd: { compile: (b) -> b.push "util.wrap_int(#{b.pop()}+#{b.pop()})" }
-  ladd: { compile: (b) -> b.push2 "#{b.pop2()}.add(#{b.pop2()})" }
-  fadd: { compile: (b) -> b.push "util.wrap_float(#{b.pop()}+#{b.pop()})" }
-  dadd: { compile: (b) -> b.push2 "#{b.pop()}+#{b.pop()}" }
-  isub: { compile: (b) -> b.push "util.wrap_int(-#{b.pop()}+#{b.pop()})" }
-  lsub: { compile: (b) -> b.push2 "#{b.pop2()}.add(#{b.pop2()})" }
-  fsub: { compile: (b) -> b.push "util.wrap_float(-#{b.pop()}+#{b.pop()})" }
-  dsub: { compile: (b) -> b.push2 "-#{b.pop()}+#{b.pop()}" }
+  iadd: { compile: (b) -> b.push new Expr "util.wrap_int($0+$1)",b.pop(),b.pop() }
+  ladd: { compile: (b) -> b.push2 new Expr "$0.add($1)",b.pop2(),b.pop2() }
+  fadd: { compile: (b) -> b.push new Expr "util.wrap_float($0+$1)",b.pop(),b.pop() }
+  dadd: { compile: (b) -> b.push2 new Expr "$0+$1",b.pop(),b.pop() }
+  isub: { compile: (b) -> b.push new Expr "util.wrap_int($1-$0)",b.pop(),b.pop() }
+  lsub: { compile: (b) -> b.push2 new Expr "$1.subtract($0)",b.pop2(),b.pop2() }
+  fsub: { compile: (b) -> b.push new Expr "util.wrap_float($1-$0)",b.pop(),b.pop() }
+  dsub: { compile: (b) -> b.push2 new Expr "$1-$0",b.pop2(),b.pop2() }
   imul: { compile: (b) -> b.push "gLong.fromInt(#{b.pop()}).multiply(gLong.fromInt(#{b.pop()})).toInt()" }
   lmul: { compile: (b) -> b.push2 "#{b.pop2()}.multiply(#{b.pop2()})" }
   fmul: { compile: (b) -> b.push "util.wrap_float(#{b.pop()}*#{b.pop()})" }
@@ -315,7 +339,7 @@
     b.add_line "#{temp} = rs.init_object(#{JSON.stringify @class})"
     b.push temp }

-  goto: { compile: (b, idx) ->
+  goto: { compile: (b, idx) ->
     b.next = [@offset + idx]
     b.compile_epilogue()
     b.add_line "label = #{@offset + idx}; continue"


We can drop the "Logger." prefix of rootLogger inside Logger.coffee.

--- application/chrome/content/wesabe/Logger.coffee
+++ application/chrome/content/wesabe/Logger.coffee
@@ -81,7 +81,7 @@
     @_enabled = enabled

-  constructor: (@name='', @_parent=Logger.rootLogger) ->
+  constructor: (@name='', @_parent=rootLogger) ->
     @_enabled = null

   @__defineGetter__ 'rootLogger', ->
@@ -178,7 +178,7 @@
 Logger.getFileAppender = ->
   return fileAppender if fileAppender

-  Logger.rootLogger.debug 'Registering file logger'
+  rootLogger.debug 'Registering file logger'

   try
     # Wesabe Logger registration - if not already registered.
@@ -196,7 +196,7 @@
           shouldRegister = false if catEntryName is "Wesabe Logger"

     if shouldRegister
-      Logger.rootLogger.debug 'registering Wesabe Logger with category manager'
+      rootLogger.debug 'registering Wesabe Logger with category manager'
       catMgr.addCategoryEntry "loggers", "Wesabe Logger", "@wesabe.com/logger;1", false, true

     fileLogger = Cc["@wesabe.com/logger;1"].getService(Ci.nsIWesabeLogger)
@@ -205,7 +205,7 @@
       fileLogger.log text

   catch ex
-    Logger.rootLogger.error '!! error registering file logger: ', ex
+    rootLogger.error '!! error registering file logger: ', ex

 module.exports = Logger


peillis/helpapp#3455 controller.addWidget and controller.bind tests

--- test/test_controller.coffee
+++ test/test_controller.coffee
@@ -284,7 +284,7 @@
       # first search
       controller.search 'silla'

-    it 'addParam adds parameter to use in reresh', (done)->
+    it 'addParam adds parameter to use in refresh', (done)->
       controller = new doofinder.Controller client_mock, [widget_mock]
       # when first search done, add params and refresh
       controller.bind 'df:search', ()->
@@ -330,3 +330,69 @@
         done()

       controller.refresh()
+
+  context ' addWidget method ', ->
+
+    beforeEach ()->
+      client_mock =
+        search: ()->
+        hit: ()->
+        hashid: mock.request.hashid
+
+    it ' should call the widget "init" upon adding', (done) ->
+      widget_mock.init = ()->
+        true.should.be.true
+        done()
+      controller = new doofinder.Controller client_mock
+      controller.addWidget widget_mock
+
+    it ' should increase the internal widgets list', (done) ->
+      widget_mock.init = ()->
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      controller.widgets.should.have.length 1
+      controller.addWidget widget_mock
+      controller.widgets.should.have.length 2
+      done()
+
+  context ' bind method can ', ->
+
+    beforeEach ()->
+      client_mock =
+        search: ()->
+        hit: ()->
+        hashid: mock.request.hashid
+
+    it ' bind events to callbacks (duhhh)', (done) ->
+      res_mock = { results: [1,2,3,4,5,6,7,8,9,10,11], query_name: 'test', query_coounter: 2 }
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      client_mock.search = (query, params, cb)->
+        cb null, res_mock
+      events_count = 0
+      # bind to df:results_received
+      controller.bind 'df:results_received', (e, res) ->
+        events_count++
+        res.query_name.should.eql 'test'
+        res.results.should.have.length 11
+
+      # bind to df:search
+      controller.bind 'df:search', (e, params) ->
+        events_count++
+        params.query_counter.should.be.eql 2
+
+      # bind to df:next_page
+      controller.bind 'df:next_page', (e) ->
+        events_count.should.be.eql 2
+        events_count++ # three events triggered, counting this one
+
+      # bind to df:getPage
+      controller.bind 'df:get_page', (e) ->
+        events_count.should.be.eql 3
+        done()
+
+      controller.search 'silla' # to tests df:search and df:results_received
+
+      # before getPage and nextPage bindings, turn off searching to not repeat tests
+      client_mock.search = () ->
+
+      controller.nextPage() # to test df:next_page
+      controller.getPage 3  # to test df:get_page


Updated as looking forward and backwards to help get a better view on whether it's a function or property

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -413,7 +413,8 @@
     foundEnd = false
     startBufferPosition = []
     endBufferPosition = []
-    regex = /\(|\s|\)|;|'|,|"|\|/
+    forwardRegex = /-|(?:\()[\w\[\$\(\\]|\s|\)|;|'|,|"|\|/
+    backwardRegex = /\(|\s|\)|;|'|,|"|\|/
     index = -1
     previousText = ''

@@ -422,7 +423,7 @@
       startBufferPosition = [position.row, position.column - index - 1]
       range = [[position.row, position.column], [startBufferPosition[0], startBufferPosition[1]]]
       currentText = editor.getTextInBufferRange(range)
-      if regex.test(editor.getTextInBufferRange(range)) || startBufferPosition[1] == -1 || currentText == previousText
+      if backwardRegex.test(editor.getTextInBufferRange(range)) || startBufferPosition[1] == -1 || currentText == previousText
           foundStart = true
       previousText = editor.getTextInBufferRange(range)
       break if foundStart
@@ -432,7 +433,7 @@
       endBufferPosition = [position.row, position.column + index + 1]
       range = [[position.row, position.column], [endBufferPosition[0], endBufferPosition[1]]]
       currentText = editor.getTextInBufferRange(range)
-      if regex.test(currentText) || endBufferPosition[1] == 500 || currentText == previousText
+      if forwardRegex.test(currentText) || endBufferPosition[1] == 500 || currentText == previousText
           foundEnd = true
       previousText = editor.getTextInBufferRange(range)
       break if foundEnd


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -21,6 +21,7 @@
       }

     if value?
+
       I[name] = options.value
     else
       # Initialize field if it doesn't exist


Slightly tweaked new prototype protection test.

--- test/protect_api_spec.coffee
+++ test/protect_api_spec.coffee
@@ -344,4 +344,6 @@
     expect(aether.problems.errors.length).toEqual 0
     that = {}
     method = aether.createMethod that
-    expect(method()).toBe 1
+    result = aether.run method
+    expect(result).toBe 1
+    expect(aether.problems.errors).toEqual []


whitespace fix

whitespace fix

--- examples/reference/geom.hem.coffee
+++ examples/reference/geom.hem.coffee
@@ -45,7 +45,7 @@
         geom = geom.toFlatGeometry()
       else if i == 5
         geom = hem().fromGeometry(new Cube(1, 0.1, 1, 3, 1, 3))
-        geom.selectFacesBy((f) ->
+        geom.selectFacesBy((f) ->
           c = f.getCenter()
           n = f.getNormal()
           return c.length() < 0.1 && n.y > 0
@@ -85,4 +85,4 @@
       obj.viewport.bind()
       obj.solid.draw(@camera)
       obj.wireframe.draw(@camera)
-      obj.viewport.unbind()+      obj.viewport.unbind()


Add Error Callback Test

Add Error Callback Test

--- test/unit/extras_spec.coffee
+++ test/unit/extras_spec.coffee
@@ -67,10 +67,15 @@

       tick()

-  describe "like incremental updates", ->
+  describe "with bound callbacks", ->
+    Messages = null
+
     early = 20131220
     okay  = 20131222
     late  = 20131224
+
+    errorCheck = "It failed."
+
     # Mock Server on `/messages`
     beforeEach inject ($httpBackend) ->
       id = 0
@@ -86,13 +91,17 @@
       messagesSinceUrl = /\/messages\?since=(\d.*)/
       $httpBackend.whenGET(messagesSinceUrl).respond (method, url, data) ->
         log "GET #{url}"
+        since = +messagesSinceUrl.exec(url)[1]
+
+        if since is 42
+          return [403, {err: 'Classified'}, {}]
+
         [
           200
           [{id: ++id, created: late}]
           {}
         ]

-    it "should work using bound callbacks", (done) ->
       Messages = Collection.new "Messages", {},
         update:
           method: 'GET'
@@ -111,7 +120,12 @@
               _.each response.data, (item) =>
                 @Data.updateEntry item, @matchingCriteria(item)
             return response
+          # Test failure callback by manipulating return value
+          onFail: (response) ->
+            response.data = errorCheck
+            return response

+    it "should enable incremental updates", (done) ->
       messages = Messages.all()
       messages.$promise
       .then ->
@@ -128,3 +142,14 @@
         done new Error JSON.stringify err

       tick()
+
+    it "should offer failure handling", (done) ->
+      Messages.update(params: since: 42)
+      .then ->
+        done new Error "Should have been an error."
+      .then null, ({data}) ->
+        expect(data).to.eql errorCheck
+        done(null)
+
+      tick()
+


Add access to sockjs mock instances

Add access to sockjs mock instances

--- test/unit/mock/sockjs.coffee
+++ test/unit/mock/sockjs.coffee
@@ -4,7 +4,12 @@
   onclose: undefined
   onmessage: undefined

+  @mockInstances: []
+  @currentMockInstance: null
+
   constructor: (@url, @whitelist, @options, mockOptions) ->
+    SockJS.mockInstances.push this
+    SockJS.currentMockInstance = this
     console.debug "[MOCK] SockJS Constructur(#{url})"
     fn = =>
         @onopen() if typeof @onopen is 'function'


removed debug log

removed debug log

--- imports/server/mqtt/docker/events.coffee
+++ imports/server/mqtt/docker/events.coffee
@@ -19,7 +19,6 @@
     reconciler.updateServiceState mappedState, labels

     if id = msg.id
-      console.log 'updateContainer id from event', id, labels
       reconciler.updateContainerId id, labels

 handleImageEvent = (msg) ->


only name and desc in sections

only name and desc in sections

classes to an object

--- src/digester.coffee
+++ src/digester.coffee
@@ -7,7 +7,7 @@
     @current = {}

   digest: (metadata) ->
-    classes = []
+    classes = {}
     for packageObject in metadata
       @current.package = packageObject
       files = packageObject.files
@@ -20,8 +20,8 @@
           for column, object of columnsObj
             switch object.type
               when 'class'
-                klass = @digestClass(object)
-                classes.push klass if klass?
+                classResult = @digestClass(object)
+                classes[classResult.name] = classResult if classResult?

     {classes}

@@ -38,7 +38,7 @@
     for section in sections
       for method in classMethods.concat instanceMethods
         if section.name is method.sectionName
-          filteredSections.push(section)
+          filteredSections.push(_.pick section, 'name', 'description')
           break

     parsedAttributes = ['visibility', 'summary', 'description', 'events', 'examples']


Fix #189: Speed up scoll to top/bottom commands

--- extension/packages/commands.coffee
+++ extension/packages/commands.coffee
@@ -100,13 +100,11 @@

 # Scroll to the top of the page
 command_scroll_to_top = (vim) ->
-  for i in [0...1000]
-    utils.simulateWheel(vim.window, 0, -1, utils.WHEEL_MODE_PAGE)
+  vim.rootWindow.goDoCommand('cmd_scrollTop')

 # Scroll to the bottom of the page
 command_scroll_to_bottom = (vim) ->
-  for i in [0...1000]
-    utils.simulateWheel(vim.window, 0, 1, utils.WHEEL_MODE_PAGE)
+  vim.rootWindow.goDoCommand('cmd_scrollBottom')

 # Scroll down a bit
 command_scroll_down = (vim) ->


tween with arbitrary easing function

tween with arbitrary easing function

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -87,6 +87,8 @@
     hflip: false
     vflip: false
     scale: 1
+    scaleX: 1
+    scaleY: 1
     zIndex: 0

   setSizeCallback = (sprite) ->


tween with arbitrary easing function

tween with arbitrary easing function

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -78,9 +78,7 @@
 @param {PowerCanvas} canvas A reference to the canvas to draw on.
 ###

-Drawable = (I, self) ->
-  I ||= {}
-
+Drawable = (I={}, self) ->
   Object.reverseMerge I,
     alpha: 1
     color: "#196"


Supports sessions between update and install

Solves https://github.com/paulyoung/fontello-cli/issues/16

--- src/fontello.coffee
+++ src/fontello.coffee
@@ -8,13 +8,8 @@

 HOST = 'http://fontello.com'

-
-apiRequest = (options, successCallback, errorCallback) ->
-  options.host ?= HOST
-
-  requestOptions = { multipart: true }
-  requestOptions.proxy = options.proxy if options.proxy?
-
+getSession = (options, requestOptions, successCallback, errorCallback) ->
+  print 'Creating a new session'.green;
   data =
     config:
       file: options.config
@@ -25,10 +20,35 @@
     sessionId = body

     if response.statusCode is 200
+      fs.writeFile '.fontello-session', sessionId, (err) ->
+          if not err
+            print 'Session was saved as .fontello-session \n'.green;
+          else
+            print err + "\n";
       sessionUrl = "#{options.host}/#{sessionId}"
       successCallback? sessionUrl
     else
       errorCallback? response
+
+apiRequest = (options, successCallback, errorCallback) ->
+  options.host ?= HOST
+
+  requestOptions = { multipart: true }
+  requestOptions.proxy = options.proxy if options.proxy?
+  if fs.existsSync(".fontello-session")
+    stats = fs.statSync(".fontello-session")
+
+    timeDiff = Math.abs(new Date().getTime() - stats.mtime.getTime());
+
+    if timeDiff < (1000 * 3600 * 24)
+      print 'Using .fontello-session'.green
+      sessionId = fs.readFileSync('.fontello-session');
+      sessionUrl = "#{options.host}/#{sessionId}"
+      return successCallback? sessionUrl
+
+
+  getSession(options, requestOptions, successCallback, errorCallback)
+

 fontello =
@@ -38,7 +58,6 @@
     # Begin the download
     #
     apiRequest options, (sessionUrl) ->
-
       requestOptions = {}
       requestOptions.proxy = options.proxy if options.proxy?


cancel editing and show error message when task was not found

--- app/assets/javascripts/admin/views/task/task_edit_view.coffee
+++ app/assets/javascripts/admin/views/task/task_edit_view.coffee
@@ -7,6 +7,7 @@
 admin/models/project/project_collection : ProjectCollection
 admin/models/dataset/dataset_collection : DatasetCollection
 admin/views/selection_view : SelectionView
+libs/toast : Toast
 ###

 class TaskCreateView extends Backbone.Marionette.LayoutView
@@ -187,7 +188,17 @@
     @team.show(teamSelectionView)
     @project.show(projectSelectionView)

-
+    return
+
+  ###*
+   * Handle not-found errors.
+   *
+   * @method syncError
+   ###
   syncError : ->
-
-    alert("error")
+    # TODO: notify the user that the task was not found
+    Toast.error("The task was not found.")
+
+    window.app.router.navigate("tasks", {trigger: true})
+
+    return


modal testing and not working

modal testing and not working

--- app/assets/javascripts/app/manage/statistics_filter.js.coffee
+++ app/assets/javascripts/app/manage/statistics_filter.js.coffee
@@ -3,39 +3,36 @@
   @bind: ->
     start = $(statistics_starts_on_time)
     end = $(statistics_ends_on_time)
-    mconf.DateTimeInput.setDate(start, new Date())
-    mconf.DateTimeInput.setStartDate(start, new Date())
-    mconf.DateTimeInput.setDate(end, new Date())
-    mconf.DateTimeInput.setStartDate(end, new Date())

     filterByDate()
     $('.starts-at-wrapper .btn-group .btn').on 'click', ->
       filterByDate(this)

-    # when submitting, set the starts on date to now, so that
-    # 'now' means when the user submitted the form
-    $('form', '#manage').on 'submit', ->
-      setStartsOnToAll() if isAllSelected()
-      console.log("oi")
+    $('.datetime-picker-input').on 'click', ->
+      checkDate()

 isAllSelected = ->
   selected = $('.starts-at-wrapper .btn.active').data('attr-value')
   selected is 0

 filterByDate = (el) ->
-  setStartsOnToAll()

   if el
     selected = $(el).data('attr-value')
+    console.log(selected)
   else
     selected = $('.starts-at-wrapper .btn.active').data('attr-value')
-  if selected is 0
-    $(statistics_starts_on_time).parent().hide()
-    $(statistics_ends_on_time).parent().hide()
-  else
-    $(statistics_starts_on_time).parent().show()
-    $(statistics_ends_on_time).parent().show()
-    $(statistics_starts_on_time).focus()

-setStartsOnToAll = ->
-  mconf.DateTimeInput.setDate($(statistics_starts_on_time), new Date())
+checkDate = ->
+  console.log("vo chora")
+  startDate = statistics_starts_on_time.value
+  endDate = statistics_ends_on_time.value
+
+  $('btn-primary').data('disabled', false)
+
+  console.log(startDate)
+  console.log(endDate)
+  if  startDate != "" && endDate != ""
+    console.log("aqui entrou")
+    if startDate < endDate
+      console.log("OLA")


table views: create series on demand

table views: create series on demand

The old code relies on db-stored series to fill the table charts. With
this update we can drop them if they're defined in the markup

--- app/assets/javascripts/lib/views/html_table_chart_view.coffee
+++ app/assets/javascripts/lib/views/html_table_chart_view.coffee
@@ -8,6 +8,7 @@
 class @HtmlTableChartView extends BaseChartView
   initialize : ->
     @initialize_defaults()
+    @build_gqueries()

   render : =>
     @clear_container()
@@ -21,19 +22,19 @@
   # Being stored in a hash, we can save the markup of multiple tables
   # and eventually show multiple tables at the same time
   #
-  table_html: ->
-    charts.html[@model.get("id")]
+  table_html: => charts.html[@model.get("id")]

   # normal charts have their series added when the /output_element/X.js
   # action is called. Tables have the gqueries defined in the markup instead.
   # This method will parse the HTML and create the gqueries as needed.
-  # See charts#load to see this method in action.
   #
   build_gqueries: =>
+    return if @gqueries_built
     html = $(@table_html())
     for cell in html.find("td[data-gquery]")
       gquery = $(cell).data('gquery')
       @model.series.add({gquery_key: gquery})
+    @gqueries_built = true

   # The table is already in the DOM; let's find the cells whose content
   # is the result of a gquery and write the output


Add `type=button` for the `file-source` buttons

--- app/assets/javascripts/uploadcare/widget/tabs/file-tab.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/file-tab.coffee
@@ -74,6 +74,7 @@
         .addClass('uploadcare--file-source')
         .addClass("uploadcare--file-source_#{name}")
         .addClass('uploadcare--file-sources__item')
+        .attr('type', 'button')
         .attr('title', t("dialog.tabs.names.#{name}"))
         .attr('data-tab', name)
         .append(tabIcon)


ModelBackedView#failureCallback now accepts an errorMessage

--- app/assets/javascripts/views/base/model_backed_view.js.coffee
+++ app/assets/javascripts/views/base/model_backed_view.js.coffee
@@ -55,10 +55,10 @@
     null

 # callbacks:
-  failureCallback: (response) ->
+  failureCallback: (response, errorMessage = "Ups, algo salió mal") ->
     @setErrors response.errors
     @set "submitting", false
-    alertify.log "Ups, algo salió mal"
+    alertify.log errorMessage
     null

   successCallback: (message) ->


Fixed bug: image browser in WYSIWYG editor doesn't load course files

--- app/coffeescripts/views/ckeditor/InsertUpdateImageView.coffee
+++ app/coffeescripts/views/ckeditor/InsertUpdateImageView.coffee
@@ -56,9 +56,13 @@
         when 'tabUploaded'
           loadTab (done) =>
             require [
-              'compiled/views/FileBrowserView'
-            ], (FileBrowserView) =>
-              new FileBrowserView(contentTypes: 'image').render().$el.appendTo(ui.panel)
+              'compiled/views/TreeBrowserView'
+              'compiled/views/RootFoldersFinder'
+            ], (TreeBrowserView, RootFoldersFinder) =>
+              rootFoldersFinder = new RootFoldersFinder({
+                contentTypes: 'image'
+              })
+              new TreeBrowserView(rootModelsFinder: rootFoldersFinder).render().$el.appendTo(ui.panel)
               done()
         when 'tabFlickr'
           loadTab (done) =>


Implement LIST/NLIS command

Implement LIST/NLIS command

--- coffee/BabyFtpd.coffee
+++ coffee/BabyFtpd.coffee
@@ -2,7 +2,9 @@
 #
 # # Baby FTP Deamon
 #
-net = require("net")
+net  = require "net"
+fs   = require "fs"
+exec = require("child_process").exec

 module.exports = class BabyFtpd
   authUser = {}
@@ -151,8 +153,6 @@
       @reply 202
     "MKD": ()->
       @reply 202
-    "LIST": ()->
-      @reply 202
     "SITE": ()->
       @reply 202
     "STAT": ()->
@@ -179,8 +179,14 @@
       @reply 257, '"/"'

     "NLST": ()->
-      @dtpServer.dataQueue = "ABC\r\n"
-      @reply 150
+      fs.readdir ".", (err, files)=>
+        @dtpServer.dataQueue = files.join("\n")
+        @reply 150
+
+    "LIST": ()->
+      exec "export LANG=en_US.UTF-8; ls -l", (err, stdout, stderr)=>
+        @dtpServer.dataQueue = stdout
+        @reply 150

     "RETR": ()->
       @dtpServer.dataQueue = "<html></html>"


Bail out if history API request fails

Bail out if history API request fails

--- services/web/app/coffee/Features/History/HistoryController.coffee
+++ services/web/app/coffee/Features/History/HistoryController.coffee
@@ -46,9 +46,14 @@
 				"X-User-Id": user_id
 		}, (error, response, body) ->
 			return next(error) if error?
-			HistoryManager.injectUserDetails body, (error, data) ->
-				return next(error) if error?
-				res.json data
+			if 200 <= res.statusCode < 300
+				HistoryManager.injectUserDetails body, (error, data) ->
+					return next(error) if error?
+					res.json data
+			else
+				error = new Error("history api responded with non-success code: #{res.statusCode}")
+				logger.error err: error, user_id: user_id, "error proxying request to history api"
+				next(error)

 	buildHistoryServiceUrl: (useProjectHistory) ->
 		# choose a history service, either document-level (trackchanges)


Update js view elements on page load

Update js view elements on page load

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -79,6 +79,9 @@
   # Category Markdown Description Textarea Autogrow
   $('#category_description').autoGrow()

+  # Update view to calculate visibility of 'show more' button and more
+  update_view()
+
   ##
   # Event handlers
   # Show all matching items


add proposition methods to local store

add proposition methods to local store

--- lib/argumenta/storage/local_store.coffee
+++ lib/argumenta/storage/local_store.coffee
@@ -8,6 +8,7 @@
   constructor: () ->
     # Init users hash
     @users = {}
+    @propositions = { bySha1: {} }

   # Store a User in memory.
   addUser: (user, cb) ->
@@ -57,4 +58,22 @@
     # Success
     return cb null, publicUsers

+  # Add an array of valid propositions to the store.
+  addPropositions: (propositions, cb) ->
+    for p in propositions
+      hash = p.sha1()
+      if not @propositions.bySha1[hash]
+        @propositions.bySha1[hash] = p
+
+    return cb null
+
+  # Get propositions from the store by hashes.
+  getPropositions: (hashes, cb) ->
+    results = []
+    for hash in hashes
+      p = @propositions.bySha1[hash]
+      results.push p
+
+    return cb null, results
+
 module.exports = LocalStore


Fix wrong event selector that causes attachment uploading not working (fix #960)

--- app/coffee/modules/common/attachments.coffee
+++ app/coffee/modules/common/attachments.coffee
@@ -220,11 +220,7 @@
             $ctrl.reorderAttachment(attachment, newIndex)
             $ctrl.saveAttachments()

-        $el.on "click", "a.add-attach", (event) ->
-            event.preventDefault()
-            $el.find("input.add-attach").trigger("click")
-
-        $el.on "change", "input.add-attach", (event) ->
+        $el.on "change", ".attachments-header input", (event) ->
             files = _.toArray(event.target.files)
             return if files.length < 1


EnvironmentMachineItem: disable 'Run build script' on not running machines

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -144,6 +144,7 @@
         callback          : @bound "showBuildScriptEditorModal"

       'Run build script'  :
+        disabled          : !isRunning
         callback          : @bound "runBuildScript"
         separator         : yes


activity: show the correct comment count

--- client/Social/Activity/views/comments/listpreviouslink.coffee
+++ client/Social/Activity/views/comments/listpreviouslink.coffee
@@ -14,8 +14,7 @@
     {replies, repliesCount} = @getData()
     {linkCopy}              = @getOptions()

-    listedCount  = @getDelegate().getItemCount()
-    listedCount ?= replies.length
+    listedCount  = Math.max @getDelegate().getItemCount(), replies.length
     count        = Math.min (repliesCount - listedCount), 10

     partial = if linkCopy then linkCopy


we need to use "for own" for iterating over objects

--- client/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -202,7 +202,7 @@
       recentFilePaths = storage.getAt('bucket.recentFiles')
       # log "updating views", recentFilePaths.length

-      for path, view of @recentFileViews
+      for own path, view of @recentFileViews
         @recentFileViews[path].destroy()
         delete @recentFileViews[path]


Fix form submitting errors

Fix form submitting errors

It fixes "Form submission canceled because the form is not connected" error.
More to read: http://stackoverflow.com/questions/42053775/getting-error-form-submission-canceled-because-the-form-is-not-connected

--- lib/assets/javascripts/buttons.js.coffee
+++ lib/assets/javascripts/buttons.js.coffee
@@ -26,7 +26,7 @@
         unless perform then return

         if button.attr('data-method')
-          form = $('<form/>').attr(action: button.attr('href'), method: 'POST')
+          form = $('<form/>').attr(action: button.attr('href'), method: 'POST').appendTo document.body
           $('<input/>').attr(type: 'hidden', name: '_method', value: button.attr('data-method')).appendTo form
           $('<input/>').attr(type: 'hidden', name: $('meta[name=csrf-param]').attr('content'), value: $('meta[name=csrf-token]').attr('content')).appendTo form
           form.submit()


Changed internal classname

Changed internal classname

--- core/app/backbone/views/evidence/ndp_add_evidence_view.coffee
+++ core/app/backbone/views/evidence/ndp_add_evidence_view.coffee
@@ -1,4 +1,4 @@
-class NDPAddEvidenceButtonsView extends Backbone.Marionette.Layout
+class AddEvidenceButtonsView extends Backbone.Marionette.Layout
   className: 'evidence-add-buttons'
   template: 'evidence/ndp_add_evidence_buttons'

@@ -53,7 +53,7 @@

   cancel: ->
     @ui.box.hide()
-    @buttonsRegion.show new NDPAddEvidenceButtonsView
+    @buttonsRegion.show new AddEvidenceButtonsView
       parentView: this

   showAdd: (type) ->


Config: dev mainUri made https

Config: dev mainUri made https

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -160,7 +160,7 @@
     socialApiUri      : "/xhr"
     apiUri            : null
     sourceMapsUri     : "/sourcemaps"
-    mainUri           : "http://koding-#{process.env.USER}.ngrok.com"
+    mainUri           : "https://koding-#{process.env.USER}.ngrok.com"
     broker            : uri  : "/subscribe"
     appsUri           : "/appsproxy"
     uploadsUri        : 'https://koding-uploads.s3.amazonaws.com'


fix marked

fix marked

--- apps/marklet/client/save_connect_view.coffee
+++ apps/marklet/client/save_connect_view.coffee
@@ -80,8 +80,7 @@
         src: options.src

   updateStatus: ->
-    console.log 'updateStatus'
-    if @marked()?.length
+    if @marked()?.length or @$el.hasClass 'is-saving'
       @$('.grid__block--image, #save-content').addClass 'is-connected'
       @$('.new-connection__done-button').text 'Save and close'
     else
@@ -119,6 +118,7 @@
           marked.get('slug')

       _.map marked, (block) ->
+        block.unset('marked')
         mediator.shared.recent_connections.shove block

       attr = if @isURL() then 'source' else 'content'


API uses match to filter by slug

API uses match to filter by slug

--- assets/scripts/app/controllers/caches.coffee
+++ assets/scripts/app/controllers/caches.coffee
@@ -49,7 +49,7 @@
       if confirm('Are you sure?')
         @set('isDeleting', true)

-        data = { branch: @get('branch'), slug: @get('slug') }
+        data = { branch: @get('branch'), match: @get('slug') }

         deletingDone = => @set('isDeleting', false)


[fix] banner random sort element (#1378)

--- app/assets/javascripts/ads/lib/banner.coffee
+++ app/assets/javascripts/ads/lib/banner.coffee
@@ -2,7 +2,7 @@
   # randomize banners
   @randomize: (selector) ->
     wrap = $(selector)
-    list = wrap.find("a")
+    list = wrap.find("a").parent("span")
     list = list.sort ->
       return Math.random() - .5
     list.each ->


Fixing leave popup issue on submitting form

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -262,6 +262,14 @@

   changesUnsaved = false

+  $(".qae-form").on "submit", (e) ->
+    if changesUnsaved
+      e.preventDefault()
+      e.stopPropagation()
+
+      autosave ->
+        $(".qae-form").trigger("submit")
+
   $(document).on "click", ".js-step-link", (e) ->
     e.preventDefault()
     if !$(this).hasClass("step-current")


removes debugging print

removes debugging print

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -123,7 +123,6 @@

       # Fetch the data by calling the data funcfion
       @dataEndpoint "", (data) =>
-        console.log(data)
         if @options.success
           @options.success(data)


Fix question deletion bug

Fix question deletion bug

--- app/assets/javascripts/instruments.js.coffee
+++ app/assets/javascripts/instruments.js.coffee
@@ -1,7 +1,7 @@
 jQuery ->
   $('form').on 'click', '.remove_fields', (event) ->
     $(this).prev('input[type=hidden]').val('1')
-    $(this).closest('div[class="well"]').hide()
+    $(this).closest('div.question').hide()
     $(this).closest('fieldset').hide()
     event.preventDefault()


use https for gravatar.com

use https for gravatar.com

--- app/assets/javascripts/models/user.js.coffee
+++ app/assets/javascripts/models/user.js.coffee
@@ -7,6 +7,6 @@
       @get("name") || @get("username")

     gravatar: ->
-      url = "http://www.gravatar.com/avatar/"
+      url = "https://www.gravatar.com/avatar/"
       email = $.trim(@get("email"))
       return url + md5(email) + "?d=mm"


simplified code

simplified code

--- app/assets/javascripts/pages/websites.coffee
+++ app/assets/javascripts/pages/websites.coffee
@@ -12,33 +12,25 @@

 $('#platform').on 'click', (platform) ->
   manipulateWizard(3)
+  reset_platforms()
   switch platform.target.parentElement.name
     when 'javascript'
-      reset_platforms()
       $('#javascript').show()
     when 'nod_js'
-      reset_platforms()
       $('#nod_js').show()
     when 'rails'
-      reset_platforms()
       $('#rails').show()
     when 'ruby'
-      reset_platforms()
       $('#ruby').show()
     when 'python'
-      reset_platforms()
       $('#python').show()
     when 'ios'
-      reset_platforms()
       $('#ios').show()
     when 'php'
-      reset_platforms()
       $('#php').show()
     when 'java'
-      reset_platforms()
       $('#java').show()
     when 'django'
-      reset_platforms()
       $('#django').show()

 reset_platforms = () ->


Fix display of message with remote images

--- server/controllers/messages.coffee
+++ server/controllers/messages.coffee
@@ -18,6 +18,7 @@
 formatMessage = (message) ->
     if message.html?
         message.html = sanitizer.sanitize message.html, (url) ->
+            url = url.toString()
             if 0 is url.indexOf 'cid://'
                 cid = url.substring 6
                 attachment = message.attachments.filter (att) ->
@@ -28,7 +29,7 @@
                 else
                     return null

-            else return url
+            else return url.toString()

     if not message.text?
         message.text = htmlToText.fromString message.html, htmlToTextOptions


fix wrong dates used when creating timeframe

--- app/assets/javascripts/angular/worklogs/worklog.js.coffee
+++ app/assets/javascripts/angular/worklogs/worklog.js.coffee
@@ -21,14 +21,24 @@
     addBlankTimeframe: ->
       t = new Timeframe
       if @timeframes.length
-        t.started = new Date(_.last(@timeframes).started)
-        t.ended = new Date(_.last(@timeframes).ended)
+        last_started = _.last(@timeframes).started
+        if last_started
+          t.started = new Date(last_started)
+        else
+          t.started = new Date
+        last_ended = _.last(@timeframes).ended
+        if last_ended
+          t.ended = new Date(last_ended)
+        else
+          t.ended = new Date
+      else
+        t.started = new Date
+        t.ended = new Date
       @addNewTimeframe t

     calcTotal: ->
       totals = _.map @timeframes, (t)=>
         tTotal = t.calcTotal(if @client then @client.secondlyRate() else 0)
-        console.log @client.secondlyRate()
         tTotal
       _.inject(totals, (memo, num)->
           memo + num


Fixes measure display on view mode. Related to #954.

--- app/assets/javascripts/leaflet/reactive_measure.js.coffee
+++ app/assets/javascripts/leaflet/reactive_measure.js.coffee
@@ -379,7 +379,7 @@

     if layers.getLayers().length > 0
       layers.eachLayer (layer) =>
-        if typeof layer.getMeasure() is 'function'
+        if typeof layer.getMeasure is 'function'
           m = layer.getMeasure()
           @options.measure.perimeter += m.perimeter
           @options.measure.area += m.area


[runbooks] sort scheduled commands

[runbooks] sort scheduled commands

--- app/assets/javascripts/models/scheduled_command.js.coffee
+++ app/assets/javascripts/models/scheduled_command.js.coffee
@@ -93,6 +93,9 @@
     @key: "scheduled_commands"
     url: "/rest/scheduled_commands"

+    comparator: (cmd) ->
+      cmd.scheduled_at || cmd.id
+
   class exports.ScheduledCommandHistoryList extends Stark.Collection
     model: exports.ScheduledCommand
     @key: "scheduled_commands"


Fixed: auto-import was removing password information

--- server/lib/konnector_poller.coffee
+++ server/lib/konnector_poller.coffee
@@ -179,15 +179,20 @@
         # Create the timeout and place it timeouts
         timeouts[konnector.slug] = setTimeout @checkImport.bind(@, konnector, interval), interval

-    checkImport: (konnector, interval) ->
+    checkImport: (reference, interval) ->
+        # retrieves the last version of the konnector
+        Konnector.find reference.id, (err, konnector) =>

-        # if there is time left, do not import
-        if not konnector.time?
-            importer konnector
+            # mandatory injection of encrypted fields
+            konnector.injectEncryptedFields()

-            # Update if actual interval is not the same in the konnector
-            interval = periods[konnector.importInterval]
+            # if there is time left, do not import
+            if not konnector.time?
+                importer konnector

-        @prepareNextCheck konnector, interval
+                # Update if actual interval is not the same in the konnector
+                interval = periods[konnector.importInterval]
+
+            @prepareNextCheck konnector, interval

 module.exports = new KonnectorPoller


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animated.coffee
+++ test/animated.coffee
@@ -517,8 +517,7 @@
 test "should not advance frame if set to manual mode", ->
   manualAnimation = GameObject
     data: animationData
-    includedModules: ["Animated"]
-
+    includedModules: ["Animated"]

 module()


tween with arbitrary easing function

tween with arbitrary easing function

--- test/drawable.coffee
+++ test/drawable.coffee
@@ -32,7 +32,7 @@
   equal object2.I.alpha, 0.5

 test "scale", ->
-  tr
+  transform = object.transform()

 module()


Add ability to trigger actions on entities

--- app/scripts/lib/script_modules/entity.coffee
+++ app/scripts/lib/script_modules/entity.coffee
@@ -526,3 +526,9 @@
         if @enemy.alive
           @entity.destroy()
       return
+
+  action: (name, args) ->
+    (sequence) =>
+      @_verify(sequence)
+      return WhenJS() if @_skippingToCheckpoint()
+      WhenJS(@entity.execute(name, args, this))


add phone number to bidder registration

add phone number to bidder registration

--- apps/auction/client/registration_form.coffee
+++ apps/auction/client/registration_form.coffee
@@ -26,6 +26,7 @@
       'card number': { el: @$('input[name=card_number]'), validator: @isCardNumber }
       expiration: { el: @$('.card-expiration select'), validator: @isPresent }
       'security code': { el: @$('input[name=card_security_code]'), validator: @isPresent }
+      telephone: { el: @$('input.telephone'), validator: @isPresent }
       billing_street: { el: @$('input.street'), validator: @isPresent, label: 'street' }
       billing_city: { el: @$('input.city'), validator: @isPresent, label: 'city' }
       billing_state: { el: @$('input.region'), validator: @isState, label: 'state' }
@@ -68,6 +69,10 @@
       error: =>
         @showError @errors.other, "Error fetching the balanced marketplace"

+  savePhoneNumber: ->
+    if @fields.telephone.el.val()?.length > 0
+      @currentUser.set(phone: @fields.telephone.el.val()).save()
+
   onSubmit: =>
     return if @$submit.hasClass('is-loading')
     @$submit.addClass 'is-loading'
@@ -76,6 +81,7 @@

     if @validateForm()
       @tokenizeCard()
+      @savePhoneNumber()
     else
       @showError 'Please review the error(s) above and try again.'
     false


Fix buildArray not working when index=0

Fix buildArray not working when index=0

--- views/env-parts/utils.coffee
+++ views/env-parts/utils.coffee
@@ -30,7 +30,8 @@
 window.buildArray = (pairs) =>
   ret = []
   pairs.forEach(([index, value]=[]) =>
-    if (!parseInt(index))
+    index = parseInt(index)
+    if (isNaN(index) || index < 0)
       return
     ret[index] = value
   )


Change $.andSelf() to $.addBack(), which is now preferred.

--- port/EditableText.Editable.coffee
+++ port/EditableText.Editable.coffee
@@ -14,7 +14,7 @@
   editing: ( editing ) ->
     result = super editing
     # Switching to edit mode; put focus in the text box.
-    @editControl().find( "input" ).andSelf().focus()  if editing
+    @editControl().find( "input" ).addBack().focus()  if editing
     result

   # True if the control should switch to editing mode when it's clicked.
@@ -32,7 +32,7 @@
   _createEditControl: ->
     result = super()
     # Wire up events bound to input elements.
-    @editControl().find( "input" ).andSelf().on
+    @editControl().find( "input" ).addBack().on
       blur: =>
         # Implicitly save when control loses focus.
         @save()  if @editing()


restrict zoom levels

restrict zoom levels

--- web/js/views/map_view.coffee
+++ web/js/views/map_view.coffee
@@ -35,6 +35,8 @@
         mapTypeId: google.maps.MapTypeId.ROADMAP
         mapTypeControlOptions:
           position: google.maps.ControlPosition.TOP_CENTER
+        minZoom: 10,
+        maxZoom: 18
       )
       @


NavigationItem: Code refactor and clean-up.

--- client/Main/navigation/navigationitem.coffee
+++ client/Main/navigation/navigationitem.coffee
@@ -8,35 +8,46 @@

     super options, data

-
     data  = @getData()
     @type = data.type

-    if data.jMachine
-      @type  = 'machine'
-      @setClass 'machine'
-      @child = new NavigationMachineItem {}, data
-    else if data.type is 'title'
-      @setClass 'sub-title'
-      @child = new KDCustomHTMLView tagName : 'h3', partial : data.title
-    else if data.type is 'workspace'
-      @setClass 'workspace'
-      @child = new KDCustomHTMLView
-        partial : """
-

-          #{data.title}
-          """
-    else if data.type is 'app'
-      @setClass 'app'
-      @child = new KDCustomHTMLView
-        partial : """
-

-          #{data.title}
-          """
+    if      data.jMachine                then @createMachineItem      data
+    else if data.type is 'title'         then @createTitleView        data
+    else if data.type is 'workspace'     then @createWorkspaceItem    data
+    else if data.type is 'app'           then @createAppItem          data
+
+
+  createMachineItem: (data) ->
+    @type  = 'machine'
+    @setClass 'machine'
+    @child = new NavigationMachineItem {}, data
+
+
+  createTitleView: (data) ->
+    @setClass 'sub-title'
+    @child = new KDCustomHTMLView tagName : 'h3', partial : data.title
+
+
+  createWorkspaceItem: (data) ->
+    @setClass 'workspace'
+    @child    = new KDCustomHTMLView
+      partial : """
+

+        #{data.title}
+      """
+
+
+  createAppItem: ->
+    @setClass 'app'
+    @child    = new KDCustomHTMLView
+      partial : """
+

+        #{data.title}
+      """

   pistachio: ->
-
     """
-    {{> @child}}
-    """+
+      {{> @child}}
+    """


Activity: fixes followers pluralization on sidebar items

--- client/Social/Activity/sidebar/topicitem.coffee
+++ client/Social/Activity/sidebar/topicitem.coffee
@@ -1,24 +1,32 @@
 class SidebarTopicItem extends SidebarItem
+
+  getSuffix = (c)-> str : if c > 1 then ' Followers' else ' Follower'

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

-    options.type = 'sidebar-item'
+    options.type            = 'sidebar-item'
+    options.pistachioParams = getSuffix data.participantCount

     super options, data

+    data = @getData()
+
     @followButton = new TopicFollowButton {}, data
+

   viewAppended: JView::viewAppended

+  render: ->

-  pistachio:->
+    @setOption 'pistachioParams', getSuffix @getData().participantCount

-    {participantCount} = @getData()
+    super

-    # str = if participantCount > 1 then 'Followers' else 'Follower'
+
+  pistachio: ->

     """
     {span.ttag{ #(name)}}
-    {span.tag-info{ #(participantCount) + ' followers'}}
+    {span.tag-info{ #(participantCount) + str}}
     {{> @followButton}}
     """


Fixed login page scroll problem on ios chrome

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -317,6 +317,7 @@
       type        : "submit"
       cssClass    : "solid green"
       iconOnly    : no
+      loader      : yes
       title       : "Done editing"
       callback    : @bound "submit"


video: add helper to detect user audio changes

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -118,6 +118,45 @@

 ###*
+ * Subscribes to audio changes of given subscriber. It will call given
+ * `callbacks.started` when talking started, and will call `callbacks.stopped`
+ * when talking stopped.
+ *
+ * @param {OT.Subscriber} subscriber
+ * @param {object<string, function>} callbacks
+###
+subscribeToAudioChanges = (subscriber, callbacks) ->
+
+  # this object will be used to keep track of talking activity.
+  activity = null
+
+  subscriber.on 'audioLevelUpdated', (event) ->
+    now = Date.now()
+    # we detected a sound from subscriber
+    if event.audioLevel > 0.2
+      # create initial activity with talking flag is off when there is no
+      # talking activity.
+      if not activity
+        activity = {timestamp: now, talking: off}
+
+      # if it's already talking just updated the timestamp.
+      else if activity.talking
+        activity.timestamp = now
+
+      # detected that user is talking more than 1 second.
+      # call `started` function of given `callbacks`.
+      else if now - activity.timestamp > 1000
+        activity.talking = on
+        callbacks.started()
+
+    # we have an talking activity record, it's not updated for the past 3 secs.
+    # call `stopped` function of given `callbacks`
+    else if activity and now - activity.timestamp > 3000
+      callbacks.stopped()  if activity.talking
+      activity = null
+
+
+###*
  * get user's gravatar, return the default avatar if user doesn't have an avatar.
  *
  * @param {JAccount} account