Cosmetic change from `super arguments...` to simply `super`

--- lib/backbone-rels.coffee
+++ lib/backbone-rels.coffee
@@ -18,7 +18,7 @@
     new: -> @constructor.new arguments...

     initialize: ->
-      super arguments...
+      super
       @_previousId = @id = @_generateId()
       @cache().add @
       @_hookRels()
@@ -150,7 +150,7 @@
     change: ->
       @_previousId = @id
       @id = @_generateId()
-      super arguments...
+      super

   class Collection extends Backbone.Collection
     model: Model
@@ -159,7 +159,7 @@
       if model and event is 'change' and model.id isnt model._previousId
         delete @_byId[model._previousId];
         @_byId[model.id] = model if model.id?
-      super arguments...
+      super

     fetch: (options) ->
       options = if options then _.clone options else {}


Add in /status end point

Add in /status end point

--- services/track-changes/app.coffee
+++ services/track-changes/app.coffee
@@ -9,6 +9,9 @@
 app.use express.logger()

 app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock
+
+app.get "/status", (req, res, next) ->
+	res.send "track-changes is alive"

 app.use (error, req, res, next) ->
 	logger.error err: error, "an internal error occured"


remove base again

remove base again

--- lib/command-table.coffee
+++ lib/command-table.coffee
@@ -1,7 +1,6 @@
 # This file is auto generated by `vim-mode-plus:write-command-table-on-disk` command.
 # DONT edit manually.
 module.exports =
-Base: {}
 Operator:
   file: "./operator"
 SelectBase:


Cancel pending update when editor is removed

--- lib/git-diff-view.coffee
+++ lib/git-diff-view.coffee
@@ -18,6 +18,7 @@
     @subscribeToBuffer()

     @subscribe @editorView, 'editor:will-be-removed', =>
+      @cancelUpdate()
       @unsubscribe()
       @unsubscribeFromBuffer()

@@ -86,8 +87,12 @@
       @scheduleUpdate()
       @buffer.on 'contents-modified', @updateDiffs

+  cancelUpdate: ->
+    clearImmediate(@immediateId)
+
   scheduleUpdate: ->
-    setImmediate(@updateDiffs)
+    @cancelUpdate()
+    @immediateId = setImmediate(@updateDiffs)

   updateDiffs: =>
     @removeDecorations()


Add metadata for core themes

Add metadata for core themes

--- lib/list-packages.coffee
+++ lib/list-packages.coffee
@@ -47,13 +47,9 @@
       packageDependencies ?= {}
       _atomPackages ?= {}

-      if options?.json
-        packageMetadata = (v['metadata'] for k, v of _atomPackages)
-        packages = packageMetadata.filter ({name}) ->
-          packageDependencies.hasOwnProperty(name)
-      else
-        packages = packages.filter ({name}) ->
-          packageDependencies.hasOwnProperty(name)
+      packageMetadata = (v['metadata'] for k, v of _atomPackages)
+      packages = packageMetadata.filter ({name}) ->
+        packageDependencies.hasOwnProperty(name)

       callback?(null, packages)


Return empty array when prefix is empty

Return empty array when prefix is empty

Prevents an error when the users types an invalid character (e.g. german
umlauts).

--- lib/love-provider.coffee
+++ lib/love-provider.coffee
@@ -10,6 +10,9 @@

     # Required: Return a promise, an array of suggestions, or null.
     getSuggestions: ( { editor, bufferPosition, scopeDescriptor, prefix, activatedManually } ) ->
+        if prefix.length is 0
+            return []
+
         suggestions = @findSuggestions( @completions, prefix )
         suggestions


Add function calls to component debug

Add function calls to component debug

--- components.coffee
+++ components.coffee
@@ -77,6 +77,22 @@
     delete reactSpec.state
     delete reactSpec.reactives
     delete reactSpec.debug
+
+    # Wrap function calls with debug logs
+    for memberName, member of componentSpec
+        if memberName isnt 'render' and _.isFunction member
+            reactSpec[memberName] = do (memberName, member) -> ->
+                _pushDebugFlag componentSpec.debug
+                if componentDebug
+                    console.debug _getDebugPrefix(@), "#{memberName}!"
+                    _debugDepth += 1
+
+                ret = member.apply @, arguments
+
+                if componentDebug
+                    _debugDepth -= 1
+                    _popDebugFlag()
+                ret

     propSpecs = _.clone componentSpec.props ? {}
     propSpecs.className ?=


Bug fix for null relationship

Bug fix for null relationship

--- lib/relations/one.coffee
+++ lib/relations/one.coffee
@@ -76,7 +76,7 @@

         # already set, merge
         previous_related_model = model.get(@key)
-        if previous_related_model and (previous_related_model.id is json.id)
+        if json and previous_related_model and (previous_related_model.id is json.id)
           Utils.updateModel(previous_related_model, json)
         else
           model.set(@key, related_model = if json then Utils.updateOrNew(json, @reverse_model_type) else null)


Document resin/log

Document resin/log

--- lib/resin/log/log.coffee
+++ lib/resin/log/log.coffee
@@ -1,28 +1,82 @@
 _ = require('lodash')

+# @nodoc
 isQuiet = false

+# Change log quietness
+#
+# @param {Boolean} quiet quietness
+#
+# @note If quiet is true, only resin.log.info will be quieted
+#
+# @example Set quietness
+#		resin.log.setQuiet(true)
+#
 exports.setQuiet = (quiet) ->
 	isQuiet = !!quiet

+# Check quietness
+#
+# @return {Boolean} is quiet
+#
+# @example Check quietness
+#		resin.log.isQuiet()
+#
 exports.isQuiet = ->
 	return isQuiet

-# stderr
+# Log an error
+#
+# @param {...String} message message
+#
+# @example Log an error
+#		resin.log.error('Something went wrong!')
+#
 exports.error = (args...) ->
 	console.error.apply(null, args)

+# Log a warning
+#
+# @param {...String} message message
+#
+# @example Log a warning
+#		resin.log.warning('Something might happened!')
+#
 exports.warning = (args...) ->
 	console.warn.apply(null, args)

-# stdout
+# Log info
+#
+# @param {...String} message message
+#
+# @example Log info
+#		resin.log.info('Look!')
+#
 exports.info = (args...) ->
 	return if exports.isQuiet()
 	console.info.apply(null, args)

+# Log out
+#
+# @param {...String} message message
+#
+# @note This will not be quieted even if setQuiet is set to true
+#
+# @example Log out
+#		resin.log.out('Hello World!')
+#
 exports.out = (args...) ->
 	console.log.apply(null, args)

+# Log an array
+#
+# It will iterate trough the array, calling logFunction for every item
+#
+# @param {Array} array array
+# @param {Function} logFunction log function (e.g: resin.log.info)
+#
+# @throw Will throw if logFunction is not a function
+#
 exports.array = (array, logFunction) ->
 	return if not array?


Updated cohtml-parser (throwError)

Updated cohtml-parser (throwError)

--- src/cohtml-parser.coffee
+++ src/cohtml-parser.coffee
@@ -28,12 +28,29 @@
   throwError: (message)->
     ex = new Error message
     ex.name = 'CohtmlParserError'
+
+    inputString = (@inputArray.join '')
+
     location = (@inputArray[0...@head]).join ''
-    lineCount = @countNewlines location
+
+    lineNumber = 1 + @countNewlines location
+
+    offset = location.lastIndexOf '\n'
+    index = inputString.indexOf '\n', (offset+1)
+    line = (@inputArray[offset+1...index]).join ''
+    line = line.replace /\s/g, '~'
+
+    charNumber = @head - offset - 1
+
+    nearPointer = (if charNumber is 0 then '' else ((' ' for _ in [0...charNumber-1]).join '')) + '^'
+
     text = """
            CohtmlParserError: #{message}
            head: #{@head}
-           line: #{lineCount}
+           line: #{lineNumber}
+           char: #{charNumber}
+           near: <#{line}>
+                  #{nearPointer}
            """
     ex.stack = text
     throw ex


fix arg arity in new faye bindings

fix arg arity in new faye bindings

--- lib/server.pubsub.coffee
+++ lib/server.pubsub.coffee
@@ -12,14 +12,14 @@
   faye.bind(event_type, subj.callback)
   subj

-faye.bind('subscribe', ([clientId, channel]) ->
+faye.bind('subscribe', (clientId, channel) ->
   console.log("FAYE subscribe", clientId, channel))

 faye.bind('handshake', (clientId) ->
   console.log("FAYE handshake", clientId))

-faye.bind('disconnect', ([clientId, channel]) ->
+faye.bind('disconnect', (clientId, channel) ->
   console.log("FAYE disconnect", clientId, channel))

-faye.bind('unsubscribe', ([clientId, channel]) ->
+faye.bind('unsubscribe', (clientId, channel) ->
   console.log("FAYE unsubscribe", clientId, channel))


Fix pivoting

Fix pivoting

--- client/pivoting.coffee
+++ client/pivoting.coffee
@@ -46,9 +46,7 @@
         value = value + "/" + value
       when "sTime", "eTime"
         value = moment.utc(value, "YYYY/MM/DDTHH:mm:ss.SSS").format("YYYY/MM/DD")
-      when "scc", "dcc"
-        value = [value]
-      when "type"
+      when "type", "scc", "dcc"
         value = [value]
     $set = {}
     if query.interface is "builder"
@@ -64,9 +62,8 @@
     share.Queries.update(query._id, {$set: {isStale: true}})
     if query.interface is "builder"
       switch _id
-        when "scc", "dcc"
+        when "type", "scc", "dcc"
           _.defer ->
-            cl $(".query-" + _id + "-editor").val()
             $(".query-" + _id + "-editor").trigger("chosen:updated")
   replace: (cmd, option, value) ->
     regexp = new RegExp("--" + option + "=[^\s]+", "i")


Add auto go

Add auto go

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -74,6 +74,12 @@

           controller.comment()

+        else
+          route.go()
+
+      request.onerror = ->
+        route.go()
+
       request.send()

     comment: ->
@@ -123,17 +129,20 @@
     show: ->
       document.getElementById('main').innerHTML = template 'article-list', {posts: this.data}

-  routing = ->
-    hash = decodeURIComponent location.hash
+  route =
+    go: (hash)->
+      location.hash = '#!/' + if hash then hash else 'home'

-    return location.hash = '#!/home' if hash.substr(0, 3) != '#!/' or !hash.substr 3
+    maping: ->
+      hash = decodeURIComponent location.hash
+      return route.go() if hash.substr(0, 3) != '#!/' or !hash.substr 3

-    if 'home' == hash.substr 3 then controller.home() else controller.article hash.substr 3
+      if 'home' == hash.substr 3 then controller.home() else controller.article hash.substr 3

   return console.log 'Cannot find any available repo. Complete config please.' if !getConfig 'repo', 'github'

-  window.onhashchange = routing
-  routing()
+  window.onhashchange = route.maping
+  route.maping()

   updateList: list.update
 )(this, document)


Fix adding logic

Fix adding logic

--- src/collections.coffee
+++ src/collections.coffee
@@ -1,20 +1,16 @@
 exports.addToList = (list, entity) ->
   found = false
-  i = 0
-  while i < list.length
-    if list[i] is entity
+  for existing in list
+    if existing is entity
       # Entity is already in list as-is, skip
       return
-    if list[i].id is entity.id
+    if existing.id is entity.id
       # id match, replace
       for key of entity
-        unless entity.hasOwnProperty(key)
-          i++
-          continue
-        list[i][key] = entity[key]
+        continue unless entity.hasOwnProperty(key)
+        existing[key] = entity[key]
       found = true
       break
-    i++
-  return unless found
+  return if found
   list.push entity
   return


:fire: useless specs

:fire: useless specs

--- spec/minimap-git-diff-spec.coffee
+++ spec/minimap-git-diff-spec.coffee
@@ -1,30 +0,0 @@
-MinimapGitDiff = require '../lib/minimap-git-diff'
-{WorkspaceView} = require 'atom'
-
-# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
-#
-# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
-# or `fdescribe`). Remove the `f` to unfocus the block.
-
-describe "MinimapGitDiff", ->
-  activationPromise = null
-
-  beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    activationPromise = atom.packages.activatePackage('minimap-git-diff')
-
-  describe "when the minimap-git-diff:toggle event is triggered", ->
-    it "attaches and then detaches the view", ->
-      expect(atom.workspaceView.find('.minimap-git-diff')).not.toExist()
-
-      # This is an activation event, triggering it will cause the package to be
-      # activated.
-      atom.workspaceView.trigger 'minimap-git-diff:toggle'
-
-      waitsForPromise ->
-        activationPromise
-
-      runs ->
-        expect(atom.workspaceView.find('.minimap-git-diff')).toExist()
-        atom.workspaceView.trigger 'minimap-git-diff:toggle'
-        expect(atom.workspaceView.find('.minimap-git-diff')).not.toExist()


Ensure discovered participants exist as nodes

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -193,6 +193,11 @@
     definition.extra.firstSeen = new Date
     definition.extra.lastSeen = new Date
     @participants[definition.id] = definition
+
+    # Ensure we have a node also for discovered participants
+    @nodes[definition.role] = { metadata: {} } if not @nodes[definition.role]
+    @nodes[definition.role].component = definition.component
+
     @library._updateDefinition definition.component, definition
     @emit 'participant-added', definition
     @emit 'participant', 'added', definition


Using crazy new include lookup for non-string modules

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -172,7 +172,7 @@
         self[name](args...)

     # Initial module inclue, for reconstructing objects from JSON
-    for
+    for moduleName of

     return self
 )()


Bugfix

Bugfix

--- homedisplay/display/static/js/clock.coffee
+++ homedisplay/display/static/js/clock.coffee
@@ -74,7 +74,7 @@
   @startInterval = startInterval
   @stopInterval = stopInterval
   @getMoment = getMoment
-  @*.coffee = getDate
+  @getDate = getDate
   return @


now uses simpler console statements for firefox

--- src/general/console.coffee
+++ src/general/console.coffee
@@ -4,7 +4,7 @@
 wiki = require "./wiki.coffee"
 d3plus.console = (type, message, style) ->
   style = style or ""
-  if d3plus.ie
+  if d3plus.ie or typeof InstallTrigger isnt 'undefined'
     console.log "[ D3plus ] " + message
   else if type is "groupCollapsed"
     if window.chrome and navigator.onLine
@@ -63,11 +63,11 @@
   return

 d3plus.console.time = (message) ->
-  console.time message  unless d3plus.ie
+  console.time message unless d3plus.ie
   return

 d3plus.console.timeEnd = (message) ->
-  console.timeEnd message  unless d3plus.ie
+  console.timeEnd message unless d3plus.ie
   return

 d3plus.console.warning = (message, url) ->


Use passed in window implementation for testing

--- src/helpers/mathjax.coffee
+++ src/helpers/mathjax.coffee
@@ -9,10 +9,9 @@
 COMBINED_MATH_SELECTOR = "#{MATH_DATA_SELECTOR}, #{MATH_ML_SELECTOR}"

 # Search document for math and [data-math] elements and then typeset them
-typesetDocument = ->
+typesetDocument = (windowImpl) ->
   latexNodes = []
-
-  for node in document.querySelectorAll(MATH_DATA_SELECTOR)
+  for node in windowImpl.document.querySelectorAll(MATH_DATA_SELECTOR)
     formula = node.getAttribute('data-math')
     # divs should be rendered as a block, others inline
     if node.tagName.toLowerCase() is 'div'
@@ -22,14 +21,14 @@
     latexNodes.push(node)

   unless _.isEmpty(latexNodes)
-    window.MathJax.Hub.Typeset(latexNodes)
+    windowImpl.MathJax.Hub.Typeset(latexNodes)

-  mathMLNodes = _.toArray(document.querySelectorAll(MATH_ML_SELECTOR))
+  mathMLNodes = _.toArray(windowImpl.document.querySelectorAll(MATH_ML_SELECTOR))
   unless _.isEmpty(mathMLNodes)
     # style the entire document because mathjax is unable to style individual math elements
-    window.MathJax.Hub.Typeset( window.document )
+    windowImpl.MathJax.Hub.Typeset( windowImpl.document )

-  window.MathJax.Hub.Queue ->
+  windowImpl.MathJax.Hub.Queue ->
     # Queue a call to mark the found nodes as rendered so are ignored if typesetting is called repeatedly
     for node in latexNodes.concat(mathMLNodes)
       node.classList.add(MATH_RENDERED_CLASS)
@@ -38,13 +37,12 @@
 # every Xms even if called multiple times in that period
 typesetDocument = _.debounce( typesetDocument, 100)

-
 # typesetMath is the main exported function.
 # It's called by components like HTML after they're rendered
-typesetMath = (root) ->
+typesetMath = (root, windowImpl = window) ->
   # schedule a Mathjax pass if there is at least one [data-math] or <math> element present
-  if window.MathJax?.Hub?.Queue? and root.querySelector(COMBINED_MATH_SELECTOR)
-    typesetDocument()
+  if windowImpl.MathJax?.Hub?.Queue? and root.querySelector(COMBINED_MATH_SELECTOR)
+    typesetDocument(windowImpl)

 # The following should be called once and configures MathJax.


Remove unused code.

Remove unused code.

--- app/client/js/songs/store.coffee
+++ app/client/js/songs/store.coffee
@@ -24,20 +24,6 @@

     ###* @type {Array.<app.songs.Song>} ###
     @foundSongs = []
-
-    # @dispatcherId = @dispatcher.register @handleAction_.bind @
-
-  # TODO: Move to app.Storage.
-  # handleAction_: (action, payload) ->
-  #   switch action
-  #     when Store.Actions.PUBLISH_SONG
-  #       @xhr
-  #         .put @routes.api.songs.id.url(id: payload.song.id), payload.json
-  #         .then => @userStore.setSongPublisher payload.song
-  #     when Store.Actions.UNPUBLISH_SONG
-  #       @xhr
-  #         .delete @routes.api.songs.id.url(id: payload.song.id)
-  #         .then => @userStore.removeSongPublisher payload.song

   ###*
     @override


read wordcount output asynchronously

read wordcount output asynchronously

--- app/coffee/CompileManager.coffee
+++ app/coffee/CompileManager.coffee
@@ -233,12 +233,11 @@

 		CommandRunner.run compileName, command, directory, image, timeout, {}, (error) ->
 			return callback(error) if error?
-			try
-				stdout = fs.readFileSync(directory + "/" + file_name + ".wc", "utf-8")
-			catch err
-				logger.err err:err, command:command, directory:directory, project_id:project_id, user_id:user_id, "error reading word count output"
-				return callback(err)
-			callback null, CompileManager._parseWordcountFromOutput(stdout)
+			fs.readFile directory + "/" + file_name + ".wc", "utf-8", (err, stdout) ->
+				if err?
+					logger.err err:err, command:command, directory:directory, project_id:project_id, user_id:user_id, "error reading word count output"
+					return callback(err)
+				callback null, CompileManager._parseWordcountFromOutput(stdout)

 	_parseWordcountFromOutput: (output) ->
 		results = {


avoid duplicate filling of UserInfo in getDocUpdates

--- app/coffee/UpdatesManager.coffee
+++ app/coffee/UpdatesManager.coffee
@@ -119,9 +119,7 @@
 			#console.log "options", options
 			PackManager.getOpsByVersionRange project_id, doc_id, options.from, options.to, (error, updates) ->
 				return callback(error) if error?
-				UpdatesManager.fillUserInfo updates, (err, results) ->
-					return callback(err) if err?
-					callback null, results
+				callback null, updates

 	getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) ->
 		UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) ->


Fix default mail client detection on Linux.

--- src/launch-services.coffee
+++ src/launch-services.coffee
@@ -25,7 +25,7 @@
     throw new Error "isRegisteredForURLScheme is async, provide a callback" unless callback
     exec "xdg-mime query default x-scheme-handler/#{scheme}", (err, stdout, stderr) ->
       return callback(err) if callback and err
-      callback(null, stdout is 'nylas.desktop')
+      callback(stdout.trim() is 'nylas.desktop')

   resetURLScheme: (scheme, callback) ->
     exec "xdg-mime default thunderbird.desktop x-scheme-handler/#{scheme}", (err, stdout, stderr) ->


add assert check on method

add assert check on method

--- src/layout/language.coffee
+++ src/layout/language.coffee
@@ -21,7 +21,7 @@
   send_message = (word) ->
     message = { target: sprite, name: word, action: language[word] }
     console.log 'send_message', word, message, sprite
-    sprite.send('apply', message)
+    sprite.call('apply', message)

   buttons = make.buttons(
     'command',


Allow editor.completions to be an array or an object.

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -99,9 +99,7 @@

       # These can be used as globals or scoped, thus defaults.
       completions:
-        type: "array"
-        items:
-          type: "string"
+        type: ['array', 'object']
         default: []
       fontFamily:
         type: 'string'


stub child

stub child

--- src/config/layout.coffee
+++ src/config/layout.coffee
@@ -3,13 +3,9 @@
   _LABEL: "layout"
   _CHILDREN: [
     {
-      _LABEL: "background"
+      _LABEL: "controls"
       fill: "#ccffcc"
       stroke: "black"
     }
-    {
-      _LABEL: "gridlines"
-      stroke: "white"
-    }
   ]
 }


defer DB saving until I have the serialization under control

--- content/db.coffee
+++ content/db.coffee
@@ -1,6 +1,6 @@
 Loki = require('./loki.coffee')

-DB = Loki('db.json', { autosave: true })
+DB = Loki('db.json', { autosave: false })

 DB.loadDatabase()


Update View.Template to make `this` bound to context within Lo-Dash templates

--- src/marbles/view.js.coffee
+++ src/marbles/view.js.coffee
@@ -24,7 +24,8 @@

     constructor: (@compiled_template) ->
     render: (context, partials = {}) =>
-      @compiled_template(_.extend({ partials: partials }, context))
+      context = _.extend({ partials: partials }, context)
+      @compiled_template.call(context, context)

   @instances: {
     all: {}


Keep mouse move handler from going out of control

Previously the editor could be hidden and the mouse move handler
would start logging errors since it will still trying to compute
positions based on the move events still firing on the document.

Now the display property is checked in the handler and also the selection
is finalized when a move event occurs and the button is no longer pressed.

Closes #1755

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -461,20 +461,28 @@

   selectOnMousemoveUntilMouseup: ->
     lastMoveEvent = null
-    moveHandler = (event = lastMoveEvent) =>
-      if event
-        @editor.selectToScreenPosition(@screenPositionFromMouseEvent(event))
-        lastMoveEvent = event
-
-    $(document).on "mousemove.editor-#{@id}", moveHandler
-    interval = setInterval(moveHandler, 20)
-
-    $(document).one "mouseup.editor-#{@id}", =>
+
+    finalizeSelections = =>
       clearInterval(interval)
       $(document).off 'mousemove', moveHandler
+      $(document).off 'mouseup', finalizeSelections
+
       @editor.mergeIntersectingSelections(isReversed: @editor.getLastSelection().isReversed())
       @editor.finalizeSelections()
       @syncCursorAnimations()
+
+    moveHandler = (event = lastMoveEvent) =>
+      return unless event?
+
+      if event.which is 1 and @[0].style.display isnt 'none'
+        @editor.selectToScreenPosition(@screenPositionFromMouseEvent(event))
+        lastMoveEvent = event
+      else
+        finalizeSelections()
+
+    $(document).on "mousemove.editor-#{@id}", moveHandler
+    interval = setInterval(moveHandler, 20)
+    $(document).one "mouseup.editor-#{@id}", finalizeSelections

   afterAttach: (onDom) ->
     return unless onDom


Subtract the scrollbar's width when calculating editor's width in chars

Fixes #1342

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -667,10 +667,14 @@
     @editor.setSoftWrap(not @editor.getSoftWrap())

   calculateWidthInChars: ->
-    Math.floor(@scrollView.width() / @charWidth)
+    Math.floor((@scrollView.width() - @getScrollbarWidth()) / @charWidth)

   calculateHeightInLines: ->
     Math.ceil($(window).height() / @lineHeight)
+
+  getScrollbarWidth: ->
+    scrollbarElement = @verticalScrollbar[0]
+    scrollbarElement.offsetWidth - scrollbarElement.clientWidth

   # Public: Enables/disables soft wrap on the editor.
   #


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/emitterable.coffee
+++ src/emitterable.coffee
@@ -9,6 +9,8 @@
     generator: {}
     particles: []
     particleCount: Infinity
+    x: I.x
+    y: I.y
     particleData:
       acceleration: Point(0, 0.1)
       age: 0


engine data module

engine data module

--- src/engine_data.coffee
+++ src/engine_data.coffee
@@ -11,7 +11,7 @@
   Object.reverseMerge I,
     data: {}

-
+

   get: (key) ->
     I.data[key]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -146,7 +146,7 @@
       I.active = false
   }

-  modules = GameObject.defaultModules.concat(I.includedModules)
+  modules = .concat(I.includedModules)

   modules.each (Module) ->
     self.include Module


Remove console.log

Remove console.log

--- src/lib/command.coffee
+++ src/lib/command.coffee
@@ -127,7 +127,6 @@
             cb(null, project)
         )
       (project,cb)->
-        console.log project.origin
         project.deploy(cb)
     ],(err, project)->
       throw err if err


bongo: emit error when failed to load a class

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -151,8 +151,8 @@
     findCode(paths).map (codePath) =>
       Promise.try =>
         @registerModel require codePath
-      .catch (err) ->
-        throw new Error "Error loading a class: #{ codePath } #{ err.message }"
+      .catch (err) =>
+        @emit "error", new Error "Error loading a class: #{ codePath } #{ err.message }"
     .then =>
       @emit 'apiReady'


Fixed errors in hangout.coffee

Fixed errors in hangout.coffee

--- src/scripts/hangout.coffee
+++ src/scripts/hangout.coffee
@@ -15,8 +15,8 @@
 #   nicoritschel

 module.exports = (robot) ->
-  robot.listen /hangout(.*)/i, (msg) ->
-    if HUBOT_HANGOUT_URL
+  robot.respond /hangout\s?(.*)?/i, (msg) ->
+    if process.env.HUBOT_HANGOUT_URL
       msg.send process.env.HUBOT_HANGOUT_URL
     else
-      msg.send "Environment variable HUBOT_HANGOUT_URL has not been set. Add EXPORT HUBOT_HANOUT_URL="..." to your profile"
+      msg.send 'Environment variable HUBOT_HANGOUT_URL has not been set. Add EXPORT HUBOT_HANOUT_URL="..." to your profile'


Improve coffeescript using unless attribute

--- app/assets/javascripts/admin/settings.js.coffee
+++ app/assets/javascripts/admin/settings.js.coffee
@@ -2,8 +2,7 @@
   if $('#setting_show_map').length
     $this = $('#setting_show_map')

-    if $this.is(':checked')
-    else
+    unless $this.is(':checked')
       $('.map-settings').hide()

     $this.on 'click', (e) ->


Implement ctrl-k and ctrl-u

Implement ctrl-k and ctrl-u

--- app/assets/javascripts/console-ish-input.coffee
+++ app/assets/javascripts/console-ish-input.coffee
@@ -27,3 +27,21 @@
   valueWithWordRemoved = currentValue.slice(0, previousWhitespaceIndex) + currentValue.slice(currentIndex)
   input.val(valueWithWordRemoved)
   input.setCursorPosition(previousWhitespaceIndex)
+
+input.bind "keydown", (e) ->
+  # ctrl-k
+  return unless e.ctrlKey == true and e.keyCode == 75
+
+  currentIndex = input.getCursorPosition()
+  currentValue = input.val()
+  input.val(currentValue.slice(0, currentIndex))
+  input.setCursorPosition(currentIndex)
+
+input.bind "keydown", (e) ->
+  # ctrl-u
+  return unless e.ctrlKey == true and e.keyCode == 85
+
+  currentIndex = input.getCursorPosition()
+  currentValue = input.val()
+  input.val(currentValue.slice(currentIndex))
+  input.setCursorPosition(0)


Fixed flatter to work with SSL.

Fixed flatter to work with SSL.

--- app/assets/javascripts/modules/flattr.js.coffee
+++ app/assets/javascripts/modules/flattr.js.coffee
@@ -33,7 +33,7 @@
             var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
             s.type = 'text/javascript';
             s.async = true;
-            s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto';
+            s.src = 'https://api.flattr.com/js/0.6/load.js?mode=auto';
             t.parentNode.insertBefore(s, t);
             })();
           </script>")


Remove superfluous parentheses

Remove superfluous parentheses

--- app/assets/javascripts/views/map_view.js.coffee
+++ app/assets/javascripts/views/map_view.js.coffee
@@ -4,7 +4,7 @@

   initialize: (options) ->
     @currentStudent = @collection.selected()
-    @mapEl    = document.getElementById('map-canvas')
+    @mapEl          = document.getElementById 'map-canvas'

     @listenTo @collection, 'reset', @updateMarker

@@ -31,9 +31,9 @@
       @marker.setMap null

     @marker = new google.maps.Marker
-      position: @currentStudent.get('latLng')
+      position: @currentStudent.get 'latLng'
       map: @map
-      title: @currentStudent.get('student_name')
+      title: @currentStudent.get 'student_name'

   updateCurrentStudent: (event) ->
     @currentStudent.set 'selected', false


fix this bug in task lists: create a new task with done not checked, mark done, then check the done box. Tasks doesn't appear unless one reloads the task list.... even though it is claimed to be displayed.

--- salvus/page/tasks.coffee
+++ salvus/page/tasks.coffee
@@ -614,6 +614,7 @@
             task.changed = true

         t = task.element
+        t.show()

         if t.hasClass('salvus-task-editing-desc')
             cm = t.data('cm')


fix typo in what @j3 did.

fix typo in what @j3 did.

--- src/smc-webapp/smc-react.coffee
+++ src/smc-webapp/smc-react.coffee
@@ -157,8 +157,8 @@
     getIn: (path, notSetValue) =>
         return @redux._redux_store.getState().getIn([@name].concat(path), notSetValue)

-    unsafe_getIn: (...args) =>
-        return @getIn(...args)
+    unsafe_getIn: (args...) =>
+        return @getIn(args...)

     # wait: for the store to change to a specific state, and when that
     # happens call the given callback.


Workspace: Implemented not active session view.

--- client/landing/app/CommonViews/workspace/collaborativeworkspace.coffee
+++ client/landing/app/CommonViews/workspace/collaborativeworkspace.coffee
@@ -10,6 +10,15 @@
     @workspaceRef      = @firepadRef.child @sessionKey

     @workspaceRef.once "value", (snapshot) =>
+      if @getOptions().sessionKey
+        log "user wants to join a session"
+        if snapshot.val() is null
+          log "session is not active"
+          @showNotActiveView()
+          return false
+
+        log "session is valid, trying to recover"
+
       log "everything is something happened", "value", snapshot.val(), snapshot.name()

       keys = snapshot.val()?.keys
@@ -57,6 +66,28 @@
   ready: -> # have to override for collaborative workspace

   showSessionModal: ->
+  showNotActiveView: ->
+    notValid = new KDView
+      cssClass : "not-valid"
+      partial  : "This session is not valid or no longer available."
+
+    notValid.addSubView new KDView
+      cssClass : "description"
+      partial  : """
+        If there is nothing wrong with our servers, this usually means,
+        the person who is hosting this session is disconnected or closed the session.
+      """
+
+    notValid.addSubView new KDButtonView
+      cssClass : "cupid-green"
+      title    : "Start New Session"
+      callback : =>
+        @destroySubViews()
+        options = @getOptions()
+        delete options.sessionKey
+        @addSubView new CollaborativeWorkspace options
+
+    @container.addSubView notValid
     modal                       = new KDModalViewWithForms
       title                     : "Manage Your Session"
       content                   : ""


More cleanup of sentence selection

More cleanup of sentence selection

--- annotran/static/scripts/annotator/plugin/sentenceselection.coffee
+++ annotran/static/scripts/annotator/plugin/sentenceselection.coffee
@@ -2,12 +2,6 @@
 $ = Annotator.$
 xpathRange = Annotator.Range
 Util = Annotator.Util
-
-RangeAnchor = require('../../../../../../h/h/static/scripts/annotator/anchoring/types').RangeAnchor
-FragmentAnchor = require('../../../../../../h/h/static/scripts/annotator/anchoring/types').FragmentAnchor
-TextPositionAnchor = require('../../../../../../h/h/static/scripts/annotator/anchoring/types').TextPositionAnchor
-TextQuoteAnchor = require('../../../../../../h/h/static/scripts/annotator/anchoring/types').TextQuoteAnchor
-

 # This plugin implements the UI code for selecting sentences by clicking
 module.exports = class SentenceSelection extends Annotator.Plugin
@@ -39,11 +33,6 @@
     desiredText = event.target.innerText || event.target.textContent
     desiredText = desiredText.split('.')[0]

-    range = document.createRange()
-    range.selectNodeContents(event.target)
-
-    selector = new Selector_Class(event.target, event.target, 0, desiredText.length)
-
     full_xpath = Util.xpathFromNode($(event.target), document)

     data = {
@@ -51,8 +40,6 @@
       startOffset: 0
       end: full_xpath
       endOffset: desiredText.length
-      commonAncestor: document
-      commonAncestorContainer: document
     }

     anchor = new xpathRange.SerializedRange(data).normalize(document)
@@ -60,10 +47,3 @@
     window.getSelection().removeAllRanges()
     window.getSelection().addRange(anchor.toRange())

-class Selector_Class
-  constructor: (startC, endC, startO, endO) ->
-    @startContainer = -> startC
-    @endContainer = -> endC
-    @startOffset = -> startO
-    @endOffset = -> endO
-


ignoring duplicated error-catch while compiling for debug

--- src/toaster/core/builder.coffee
+++ src/toaster/core/builder.coffee
@@ -308,10 +308,12 @@
 			try
 				fs.writeFileSync absolute_path, cs.compile file.raw, {bare:@bare}
 			catch err
-				msg = err.message.replace '"', '\\"'
-				console.log "MSG:::: " + msg
-				msg = "#{msg.white} at file: " + "#{file.filepath}".bold.red
-				error msg
+				## dont show nothing because the error was alreary shown
+				## in the compile rotine above
+				#
+				# msg = err.message.replace '"', '\\"'
+				# msg = "#{msg.white} at file: " + "#{file.filepath}".bold.red
+				# error msg
 				continue

 			# adds to the files buffer


add comments #651

add comments #651

--- app/assets/javascripts/admin/views/task/task_overview_view.coffee
+++ app/assets/javascripts/admin/views/task/task_overview_view.coffee
@@ -71,6 +71,7 @@
   DEFAULT_TIME_PERIOD_TIME : 3
   MS_PER_HOUR : 3600000

+  # These values will be configured by the user, using the RangeSlider
   chosenMinHours : 0
   chosenMaxHours : 24 * 356 * 100

@@ -96,6 +97,7 @@

   getMinMaxHours : ->

+    # This function calculates the min/max working hours of the whole data set
     if _.isEmpty(@minMaxHours)
       minTime = Math.min(_.pluck(@model.attributes.userInfos, "workingTime")...)
       maxTime = Math.max(_.pluck(@model.attributes.userInfos, "workingTime")...)


Ask for confirmation before deleting an option

--- app/assets/javascripts/angular/controllers/options_ctrl.js.coffee
+++ app/assets/javascripts/angular/controllers/options_ctrl.js.coffee
@@ -36,11 +36,12 @@
   )

   $scope.removeOption = (option) ->
-    $scope.options.splice($scope.options.indexOf(option), 1)
-    option.project_id = $scope.project_id
-    option.instrument_id = $scope.instrument_id
-    option.question_id = $scope.question_id
-    option.$delete()
+    if confirm("Are you sure you want to delete this option?")
+      $scope.options.splice($scope.options.indexOf(option), 1)
+      option.project_id = $scope.project_id
+      option.instrument_id = $scope.instrument_id
+      option.question_id = $scope.question_id
+      option.$delete()

   $scope.addOption = ->
     option = new Option


already filling info after querying for cliente

--- app/assets/javascripts/views/facturas/facturas_new_view.js.coffee
+++ app/assets/javascripts/views/facturas/facturas_new_view.js.coffee
@@ -1,6 +1,15 @@
 Bandango.FacturasNewView = Ember.View.extend Bandango.GravatarImagenOnForm,
   tagName: "form"
   tiposIds: ["RUC", "Cédula", "Pasaporte - otro"]
+
+
+# cliente methods
+  clienteChanged: (->
+    cliente = @get("cliente") || Ember.Object.create()
+    attributes = ["tipoId", "nombres", "direccion", "telefono", "email"]
+    for attribute in attributes
+      @set attribute, cliente.get(attribute)
+  ).observes("cliente")

   willInsertElement: ->
     @$(".identificacion_input").on("keyup" , $.proxy(@identificacionKeyUpped, @))
@@ -18,11 +27,10 @@
       @queryForCliente value

   gotCliente: (clientes) ->
-    cliente = clientes.get("firstObject")
-    console.log cliente.get("nombres")
+    @set "cliente", clientes.get("firstObject")

   didNotGetCliente: ->
-    console.log arguments
+    @set "cliente", null

   queryForCliente: (identificacion) ->
     unless @get("currentId") == identificacion
@@ -30,6 +38,7 @@
       store = @get("controller").get("store")
       store.find("cliente", identificacion: identificacion).then $.proxy(@gotCliente, @), $.proxy(@didNotGetCliente, @)

+# view methods
   submit: ->
     console.log "submit!"
     false


Template

Template

- Maintain "cui-template-empty" correcty, when slots
  are accessed directly with "empty", "append", "prepend".

--- src/base/Template/Template.coffee
+++ src/base/Template/Template.coffee
@@ -105,6 +105,17 @@
 				report.push("+ #{k}: found")
 				el_map[k] = CUI.jQueryCompat(map_obj[0])

+				do (k) =>
+					el_map[k].empty = =>
+						@empty(k)
+						el_map[k]
+					el_map[k].append = (value) =>
+						@append(value, k)
+						el_map[k]
+					el_map[k].prepend = (value) =>
+						@prepend(value, k)
+						el_map[k]
+
 		if misses
 			alert("Not all required elements were found for Template:\n\n\"#{@_name}\"\n\n"+report.join("\n"))
 		el_map
@@ -161,7 +172,7 @@
 					break

 			if is_empty
-				DOM.addClass(@DOM, "cui-template-empty")
+				CUI.DOM.addClass(@DOM, "cui-template-empty")

 			return @map[key]

@@ -172,7 +183,7 @@
 			# with map we empty each individual map entry
 			for key of @map
 				DOM.empty(@map[key])
-			DOM.addClass(@DOM, "cui-template-empty")
+			CUI.DOM.addClass(@DOM, "cui-template-empty")

 		return @DOM


Fixed bug in ResourceProxy when fetching resources when port != 80.

--- src/browser/resource_proxy.coffee
+++ src/browser/resource_proxy.coffee
@@ -29,7 +29,7 @@
             throw new Error("Tried to fetch invalid id: #{id}")
         type = MIME.lookup(url.href) #TODO: how does this deal with hashes?
         opts =
-            host : url.host
+            host : url.hostname
             port : url.port || 80
             path : url.pathname + (url.search || '')
         console.log(opts)


Code clarification in Luca.instanceCache.  Luca.registry.findInstancesByClass

--- src/core/registry.coffee
+++ src/core/registry.coffee
@@ -4,7 +4,7 @@
   collection_classes: {}
   namespaces:['Luca.containers','Luca.components']

-component_cache =
+componentCacheStore =
   cid_index: {}
   name_index: {}

@@ -99,10 +99,16 @@

 Luca.registry.instances = ()->
   _( component_cache.cid_index ).values()
+
+Luca.registry.findInstancesByClass = (componentClass)->
+  Luca.registry.findInstancesByClassName( componentClass.displayName )

 Luca.registry.findInstancesByClassName = (className)->
+  className = className.displayName if not _.isString( className )
   instances = Luca.registry.instances()
   _( instances ).select (instance)->
+    isClass = instance.displayName is className
+
     instance.displayName is className or instance._superClass?()?.displayName is className

 Luca.registry.classes = (toString=false)->
@@ -113,21 +119,25 @@
       className: className
       ctype: ctype

-Luca.cache = (needle, component)->
-  component_cache.cid_index[ needle ] = component if component?
+Luca.cache = Luca.instanceCache = (cacheKey, object)->
+  return unless cacheKey?
+  return object if object.doNotCache is true

-  component = component_cache.cid_index[ needle ]
+  if object?
+    componentCacheStore.cid_index[ cacheKey ] = object
+
+  object = componentCacheStore.cid_index[ cacheKey ]

   # optionally, cache it by tying its name to its cid for easier lookups
-  if component?.component_name?
-    component_cache.name_index[ component.component_name ] = component.cid
-  else if component?.name?
-    component_cache.name_index[ component.name ] = component.cid
+  if object?.component_name?
+    componentCacheStore.name_index[ object.component_name ] = object.cid
+  else if object?.name?
+    componentCacheStore.name_index[ object.name ] = object.cid

-  return component if component?
+  return object if object?

-  # perform a lookup by name if the component_id didn't turn anything
-  lookup_id = component_cache.name_index[ needle ]
+  # perform a lookup by name if the cid lookup didn't turn anything
+  lookup_id = componentCacheStore.name_index[ cacheKey ]

   component_cache.cid_index[ lookup_id ]


Added stubs for zooming and centering

Added stubs for zooming and centering

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -253,8 +253,11 @@
         true

     # TODO
-    center: ->
-        true
-
+    center: (params...) ->
+        true
+
+    # TODO
+    zoom: (params...) ->
+        true
 # Export the TimeSlider object for use in the browser
 this.TimeSlider = TimeSlider


Provide functions to hide / show the slider

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -2,7 +2,6 @@

     # TODO
     #  * The @draw function is still accessible from outside
-    #  * Create a show() / hide() function pair
     #  * Allow for the registration of datasets to be shown beneath the brush
     #  * Rename pixelPerDay to something more generic (could also be hours, ... depending on the time frame)
     #  * Cleanup the mess that is the axis labels right now
@@ -220,6 +219,16 @@
                 # TODO remove hardcoded height
                 .attr('height', "#{@options.height - 20 - 2}px")
                 .attr('y', 0)
+
+    # Function pair to allow for easy hiding and showing the time slider
+    hide: ->
+        @originalDisplay = @element.style.display
+        @element.style.display = 'none'
+        true
+
+    show: ->
+        @element.style.display = @originalDisplay
+        true

 # Export the TimeSlider object for use in the browser
 this.TimeSlider = TimeSlider


Add ability to pass a default data object to be used as parameters

--- cilantro/static/cilantro/scripts/coffeescript/views/charts.coffee
+++ cilantro/static/cilantro/scripts/coffeescript/views/charts.coffee
@@ -48,6 +48,7 @@
         tagName: 'select'

         initialize: (options) ->
+            @options = options
             @enumerableOnly = options.enumerableOnly

             @collection.deferred.done =>
@@ -214,6 +215,7 @@
             if @model then @model.destroy()

         renderChart: (url, data, fields, seriesIdx) ->
+            data = _.extend {}, data, @options.data
             Backbone.ajax
                 url: url
                 data: data


Collect more information about lines

Collect more information about lines

This will log detailed data for the 10 lines before the one where the error occurred, and the line lengths for all the other tokenized lines, spatial screen lines, and buffer lines.

Signed-off-by: Antonio Scandurra <0e17b4d558b07b12cd221e35c438be378ba118cf@github.com>

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -848,20 +848,28 @@
           iteratorPosition = decorationIterator.getPosition()
           error = new Error("Invalid text decoration iterator position")

-          # Collect subset of tokenized line data on rows around to this error
-          interestingBufferRows = new Set([Math.max(0, bufferStart.row - 3)..bufferStart.row].concat([Math.max(0, iteratorPosition.row - 3)..iteratorPosition.row]))
-          interestingBufferRows.add(decorationIteratorPositionBeforeSeek.row)
-          tokenizedLines = {}
-          interestingBufferRows.forEach (row) =>
-            if tokenizedLine = @textDecorationLayer?.tokenizedLineForRow?(row)
+          bufferLines = []
+          for bufferLine, bufferRow in @buffer.getLines()
+            bufferLines[bufferRow] = bufferLine.length
+
+          tokenizedLines = []
+          for tokenizedLine, bufferRow in @textDecorationLayer?.tokenizedLines
+            if bufferRow - 10 <= bufferStart.row <= bufferRow
               {tags, openScopes} = tokenizedLine
-              tokenizedLines[row] = {tags, openScopes}
-
-          # Collect spatial screen lines on rows around to this error
+              tokenizedLines[bufferRow] = [tags, openScopes]
+            else
+              tokenizedLines[bufferRow] = tokenizedLine.text.length
+
           screenLines = @displayIndex.getScreenLines()
-          spatialScreenLines = {}
-          for screenRow in [Math.max(0, @spatialLineIterator.getScreenRow() - 3)..@spatialLineIterator.getScreenRow()]
-            spatialScreenLines[screenRow] = screenLines[screenRow]
+          spatialScreenLines = []
+          for screenLine, screenRow in screenLines
+            if @spatialLineIterator.getScreenRow() - 10 <= screenRow <= @spatialLineIterator.getScreenRow()
+              spatialScreenLines[screenRow] = screenLines[screenRow]
+            else
+              spatialScreenLines[screenRow] = [
+                screenLines[screenRow].screenExtent,
+                Point.fromObject(screenLines[screenRow].bufferExtent).toString()
+              ]

           error.metadata = {
             spatialLineBufferStart: Point.fromObject(bufferStart).toString(),
@@ -870,6 +878,7 @@
             decorationIteratorPositionBeforeSeek: Point.fromObject(decorationIteratorPositionBeforeSeek).toString(),
             spatialScreenLines: spatialScreenLines,
             tokenizedLines: tokenizedLines,
+            bufferLines: bufferLines,
             grammarScopeName: @textDecorationLayer.grammar?.scopeName,
             tabLength: @tabLength,
             invisibles: JSON.stringify(@invisibles),


[fix] redis values from HGETALL are unparsed JSON

--- src/redis-model.coffee
+++ src/redis-model.coffee
@@ -234,8 +234,12 @@
     return callback "id required"    unless id
     ideally = errify callback

-    await @redis.hgetall "bull:#{queue}:#{id}", ideally defer err, result
-    callback null, result
+    await @redis.hgetall "bull:#{queue}:#{id}", ideally defer result
+    allJobData = {}
+    for property, stringified of result
+      allJobData[property] = JSON.parse stringified
+
+    callback null, allJobData

   dataForJobs: (jobs, callback) ->
     ideally = errify callback


fixes Application.routeTo() method to support specifying methods via routeTo(page).action(customAction)

--- src/components/application.coffee
+++ src/components/application.coffee
@@ -337,14 +337,14 @@
   instances: {}
   registerInstance: (app)->
     Luca.Application.instances[ app.name ] = app
+
+  # Public: Luca.Application
   routeTo: (pages...)->
     last = _( pages ).last()
     first = _( pages ).first()

     callback = undefined
     specifiedAction = undefined
-
-    console.log "Generating Route To", pages

     routeHelper = (args...)->
       path = @app || Luca()
@@ -359,20 +359,23 @@
         nextItem = pages[++index]
         target = Luca(page)

-        if page is last and target.routeHandler?
-          callback = target.routeHandler
-
-        if _.isFunction(nextItem)
-          callback = nextItem
-
-        if _.isObject(nextItem) and action = nextItem.action and target[action]?
-          callback = _.bind(target[action], target)
+        if page is last
+          callback = if specifiedAction? and target[ specifiedAction ]?
+            _.bind(target[ specifiedAction ], target)
+          else if target.routeHandler?
+            target.routeHandler
+
+        callback ||= if _.isFunction(nextItem)
+          _.bind(nextItem, target)
+        else if _.isObject(nextItem)
+          if action = nextItem.action and target[action]?
+            _.bind(target[action], target)

         path = path.navigate_to page, ()->
           callback?.apply(target, args)

     routeHelper.action = (action)->
-      pages.push(action:(specifiedAction = action)) if action?
+      specifiedAction = action
       routeHelper

     routeHelper


now ignores twitter links

now ignores twitter links

--- src/scripts/web.coffee
+++ src/scripts/web.coffee
@@ -43,8 +43,9 @@
             msg.send if response.status_code is 200 then responseTitle else response.status_txt
           else
             httpResponse(url)
-
-    if url.match /^http\:\/\/bit\.ly/
+    if url.match /https?:\/\/(mobile\.)?twitter\.com/i
+      console.log "Twitter link; ignoring"
+    else if url.match /^http\:\/\/bit\.ly/
       httpBitlyResponse(url)
     else
       httpResponse(url)


:memo: Convert ScrollView docs

:memo: Convert ScrollView docs

--- src/scroll-view.coffee
+++ src/scroll-view.coffee
@@ -6,17 +6,26 @@
 # the following events won't be handled by the ScrollView.
 #
 # ## Events
-#   * `core:move-up`
-#   * `core:move-down`
-#   * `core:page-up`
-#   * `core:page-down`
-#   * `core:move-to-top`
-#   * `core:move-to-bottom`
+#   * `core:move-up` Scrolls the view up
+#   * `core:move-down` Scrolls the view down
+#   * `core:page-up`   Scrolls the view up by the height of the page
+#   * `core:page-down` Scrolls the view down by the height of the page
+#   * `core:move-to-top` Scrolls the editor to the top
+#   * `core:move-to-bottom` Scroll the editor to the bottom
 #
-# ## Requiring in packages
+# ## Examples
 #
 # ```coffee
-#   {ScrollView} = require 'atom'
+# {ScrollView} = require 'atom'
+#
+# class MyView extends ScrollView
+#   @content: ->
+#     @div()
+#
+#   initialize: ->
+#     super
+#
+#     @text('super long content that will scroll')
 # ```
 module.exports =
 class ScrollView extends View


added minor comments

added minor comments

--- static/coffee/create_job.coffee
+++ static/coffee/create_job.coffee
@@ -1,7 +1,8 @@
 # `JobContainer` class that resembles Spark job to create, with all options
 class JobContainer extends Reactable
   constructor: ->
-    # State includes: name, (main) klass, driver memory, executor memory
+    # State includes: name, (main) klass, driver memory, executor memory, delay, jar
+    # Also features finder state (ls array, path array and current url accessed)
     @state =
       name: "#{Namer.generate()}"
       klass: "com.test.Main"
@@ -13,11 +14,13 @@
       finder_ls: []
       finder_path: []

+  # Fetch finder data using url provided
   finder: (url) ->
     Api.doGet url, null, null, (ok, json) =>
       if ok
         @setState(finder_url: url, finder_ls: json.ls, finder_path: json.path)
       else
+        # Here we just fail silently, UI will not be updated
         console.error "ERROR failed to parse finder object", ok, json

   componentWillMount: ->
@@ -88,6 +91,7 @@
       FinderLs.new(ls: @props.ls, jar: @props.jar)
     )

+# Sub-option of the finder, represents breadcrumbs of path being currently accessed
 class FinderPath extends Reactable
   render: ->
     elems = []
@@ -100,6 +104,7 @@
         elems.push FinderPathElem.new(key: "#{elem.realpath}", active: false, elem: elem)
     @div({className: "breadcrumb"}, elems)

+# Element of finder path sub-option
 class FinderPathElem extends Reactable
   handleClick: (event, url) ->
     event.preventDefault()
@@ -113,6 +118,7 @@
     else
       @div({className: "active section"}, "#{@props.elem.name}")

+# Sub-option of finder, represents list of files/folders for the path accessed
 class FinderLs extends Reactable
   select: (elem) ->
     !elem.isdir and elem.realpath == @props.jar
@@ -121,6 +127,7 @@
     elems = (FinderLsElem.new(key: "#{elem.realpath}", elem: elem, selected: @select(elem)) for elem in @props.ls)
     @div({className: "menu"}, elems)

+# Element of finder ls sub-option
 class FinderLsElem extends Reactable
   handleClick: (event, elem) ->
     event.preventDefault()


Run tests using production API server

Run tests using production API server

--- src/test/common.coffee
+++ src/test/common.coffee
@@ -6,7 +6,7 @@
       this.message = -> "#{this.actual} has not been written yet."
       false

-window.testApiRoot = "http://localhost:8080"
+window.testApiRoot = "http://api.mynaweb.com"

 # Helper function - grab a suggestion and pass it to the argument:
 window.withSuggestion = (expt, fn) ->


chore(refactor): clarify difference between bar chart and poll common barr chart

--- client/angular/config/vue.coffee
+++ client/angular/config/vue.coffee
@@ -27,6 +27,7 @@
 angular.module('loomioApp').value('UserAvatar', require 'vue/components/user_avatar/user_avatar.coffee')
 angular.module('loomioApp').value('UserAvatarBody', require 'vue/components/user_avatar_body/user_avatar_body.coffee')
 angular.module('loomioApp').value('PollCommonChartPreview', require 'vue/components/poll_common_chart_preview/poll_common_chart_preview.coffee')
+angular.module('loomioApp').value('PollCommonBarChart', require 'vue/components/poll_common_bar_chart/poll_common_bar_chart.coffee')
 angular.module('loomioApp').value('BarChart', require 'vue/components/bar_chart/bar_chart.coffee')
 angular.module('loomioApp').config ['$ngVueProvider', ($ngVueProvider) ->
   $ngVueProvider.setRootVueInstanceProps({


invite

invite

--- client/app/MainApp/routes.coffee
+++ client/app/MainApp/routes.coffee
@@ -36,6 +36,7 @@
       else bongo.api.JInvitation.byCode inviteToken, (err, invite)->
         if err or !invite? or invite.status not in ['active','sent']
           if err then error err
+          console.log invite
           new KDNotificationView
             title: 'Invalid invitation code!'
         else


verify mail notification

verify mail notification

--- client/app/MainApp/routes.coffee
+++ client/app/MainApp/routes.coffee
@@ -62,5 +62,11 @@
     '/verify/:confirmationToken': ({confirmationToken})->
       confirmationToken = decodeURIComponent confirmationToken
       bongo.api.JEmailConfirmation.confirmByToken confirmationToken, (err)->
-        throw err if err
-        location.replace '#'+        location.replace '#'
+        if err
+          throw err
+          new KDNotificationView
+            title     : "Something went wrong, please try again later!"
+        else
+          new KDNotificationView
+            title     : "Thanks for confirming your email address!"


Revert "Remove the ` on undefined"

Revert "Remove the ` on undefined"

This reverts commit caf9b6774bfbb2aa9498db7e1b10be5f7cf34d81.

Once https://github.com/atom/atomdoc/pull/16 is merged this will be
fixed, until then there is no point in removing the ` as the word
`undefined` is removed even without them.

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -963,7 +963,7 @@
   #
   # * `id` The id of the marker layer to retrieve.
   #
-  # Returns a {MarkerLayer} or undefined if no layer exists with the given
+  # Returns a {MarkerLayer} or `undefined` if no layer exists with the given
   # id.
   getMarkerLayer: (id) ->
     @markerLayers[id]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -13,8 +13,6 @@
 Updates the position of the text based on the effect velocity. Updates the
 alpha based on the elapsed time since the effect creation.

-Observe or configure a gameObject that has been added
-to the engine.
 @name update
 @methodOf TextEffect#
 @event


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -11,7 +11,7 @@

 ###*
 Updates the position of the text based on the effect velocity. Updates the
-alpha based on
+alpha based on the elap

 Observe or configure a gameObject that has been added
 to the engine.


accept any, fn or string

accept any, fn or string

--- client/lanes/models/Query.coffee
+++ client/lanes/models/Query.coffee
@@ -18,7 +18,7 @@
         flex:       type: 'number',  default: 1
         fixedWidth: type: 'number'
         textAlign:  type: 'string', default: 'left'
-        sortBy:     type: 'function'
+        sortBy:     type: 'any'
         onColumnClick: type: 'function'

     derived:
@@ -219,6 +219,10 @@
                 else
                     new Lanes.Models.Query.SyncedResult(this, pageSize: @pageSize)

+    events:
+        'change:sortField':     '_updateSort'
+        'change:sortAscending': '_updateSort'
+
     constructor: (options = {}) ->
         super
         @fields = new AvailableFields([], query: this)
@@ -237,6 +241,8 @@
         this.on('change:src change:results', ->
             this.trigger('load')
         )
+        if options.defaultSort
+            @setSortField( @fields.findWhere(id: options.defaultSort)  )
         if @initialFieldIndex
             @initialField = this.fields.at(@initialFieldIndex)
         @initialField ||= this.fields.findWhere(id: "code") ||
@@ -261,7 +267,6 @@
     loadSingle: (code, options = {}) ->
         options.query = {}
         options.query[ @initialField.id ] = code
-
         _.extend(options, _.result(this, 'syncOptions'))
         @src.fetch(options)

@@ -270,3 +275,5 @@

     addNewClause: ->
         @clauses.add(query: this, available_fields: @fields, field: @initialField)
+
+    _updateSort: -> @results._updateSort()


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -14,7 +14,7 @@
       canvas.font "#{size}px #{font}"

       canvas.centerText
-        y: App.height / 2 - 20
+        y:
         text: text
         color: color


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -14,7 +14,7 @@
       canvas.font "#{size}px #{font}"

       canvas.centerText
-        y:
+        y: I.yPositoin
         text: text
         color: color


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -11,7 +11,7 @@
       canvas.centerText
         y: App.height / 2 - 20
         text: titleText
-        color:
+        color: I.

       canvas.font '14px Helvetica'


ApplicationManager: add checkOnLoadOnly option to preCondition which disables preConditions for already passed app instances if exists

--- client/Main/ApplicationManager.coffee
+++ client/Main/ApplicationManager.coffee
@@ -61,6 +61,10 @@
       # method of preCondition if exists

       if appOptions?.preCondition? and not options.conditionPassed
+
+        if appOptions.preCondition.checkOnLoadOnly
+          if @get name then return do defaultCallback
+
         appOptions.preCondition.condition appParams, (state, newParams)=>
           if state
             options.conditionPassed = yes


mainapp: groupcontroller.ready event fix

--- client/Main/activitycontroller.coffee
+++ client/Main/activitycontroller.coffee
@@ -9,7 +9,7 @@
     groupsController = KD.getSingleton "groupsController"
     groupChannel     = null

-    groupsController.on 'ready', =>
+    groupsController.ready =>
       groupChannel.close().off()  if groupChannel?
       groupChannel = groupsController.groupChannel
       groupChannel.on 'feed-new', (activities) =>


FSHelper.getGlobList returns a list of files which mathches with the provided pattern, a simple wrapper function for fs.glob from oskite.

--- client/app/MainApp/fs/fshelper.coffee
+++ client/app/MainApp/fs/fshelper.coffee
@@ -85,6 +85,14 @@
       method   : "fs.getInfo"
       vmName   : vmName
       withArgs : {path}
+    , callback
+
+  @getGlobList = (pattern, vmName, callback)->
+    [vmName, callback] = [callback, vmName]  if typeof vmName is "function"
+    KD.getSingleton('vmController').run
+      method   : "fs.glob"
+      vmName   : vmName
+      withArgs : {pattern}
     , callback

   @ensureNonexistentPath = (path, vmName, callback=noop)->


rethink: add a convenience function that makes it easy to watch some tables

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -370,6 +370,23 @@
             cb    : required
         new SyncTable(opts.query, opts.primary_key, @, opts.cb)
         return
+
+    # This is mainly for interactive debugging use. Any time a record changes in any of
+    # the given tables, calls the cb with the change.
+    watch: (opts) =>
+        opts = defaults opts,
+            tables : required   # array of strings (names of tables) -- or a single string
+            cb     : required
+        if typeof(opts.tables) == 'string'
+            opts.tables = [opts.tables]
+        if opts.tables.length == 0
+            # nothing ever changes
+            return
+        query = db.table(opts.tables[0])
+        for i in [1...opts.tables.length]
+            query = query.union(db.table(opts.tables[i]))
+        query.changes(includeInitial:false).run (err, feed) =>
+            feed.each(opts.cb)

     # Wait until the query results in at least one result obj, and
     # calls cb(undefined, obj).


add some commented out logging code, which is useful for understanding db queries from client

--- src/smc-util/client.coffee
+++ src/smc-util/client.coffee
@@ -1756,7 +1756,8 @@
             cb      : undefined
         if opts.options? and not misc.is_array(opts.options)
             throw Error("options must be an array")
-        #console.log("query=#{misc.to_json(opts.query)}")
+        #@__query_id ?= 0; @__query_id += 1; id = @__query_id
+        #console.log("#{(new Date()).toISOString()} -- #{id}: query=#{misc.to_json(opts.query)}")
         err = validate_client_query(opts.query, @account_id)
         if err
             opts.cb?(err)
@@ -1770,7 +1771,9 @@
             message     : mesg
             error_event : true
             timeout     : opts.timeout
-            cb          : opts.cb
+            cb          : (args...) ->
+                #console.log("#{(new Date()).toISOString()} -- #{id}: query_resp=#{misc.to_json(args)}")
+                opts.cb?(args...)

     query_cancel: (opts) =>
         opts = defaults opts,


make this branch also include 'prevent-accidental-drops'

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -111,3 +111,11 @@
         MathJax.Hub?.Queue([mathjax_finish_startup])
     document.getElementsByTagName("head")[0].appendChild(mjscript)

+    # register a default drag and drop handler, that prevents accidental file drops
+    # therefore, dropping files only works when done right above the dedicated dropzone
+    $(document).on 'dragover', (e) ->
+        e.preventDefault()
+    $(document).on 'drop', (e) ->
+        e.preventDefault()
+        {alert_message} = require('./alerts')
+        alert_message(type:'info', message: 'To upload a file, drop it into the "Drop files to upload" area!')


Remove math processing from HTML

Remove math processing from HTML

--- src/smc-webapp/r_misc.cjsx
+++ src/smc-webapp/r_misc.cjsx
@@ -765,38 +765,48 @@
                                          # sanitizing input.  Use this as an opportunity to modify the HTML structure
                                          # before it is exported to text and given to react.   Obviously, you can't
                                          # install click handlers here.
-        highlight      : rtypes.immutable.Set

     getDefaultProps: ->
         safeHTML    : true

     shouldComponentUpdate: (next) ->
         return @props.value != next.value or \
-             @props.highlight != next.highlight or \
              not underscore.isEqual(@props.style, next.style) or \
              @props.safeHTML != next.safeHTML

-    _process_math: (html_before) ->
-
-        return html_after
-
-    _process_links: (html_before) ->
-
+    shouldComponentUpdate: (next) ->
+        return @props.value != next.value or \
+             not underscore.isEqual(@props.style, next.style) or \
+             @props.safeHTML != next.safeHTML
+
+    _update_links: ->
+        if not @_is_mounted
+            return
         $(ReactDOM.findDOMNode(@)).process_smc_links
             project_id     : @props.project_id
             file_path      : @props.file_path
             href_transform : @props.href_transform

-        return html_after
-
-    _process_tables: (html_before) ->
-
+    _update_tables: ->
+        if not @_is_mounted
+            return
         $(ReactDOM.findDOMNode(@)).find("table").addClass('table')
-        return html_after
-
-    _process_highlighting: ->
-        # Use jquery-highlight, which is a pretty serious walk of the DOM tree, etc.
-        $(ReactDOM.findDOMNode(@)).highlight(@props.highlight.toJS())
+
+    update_content: ->
+        @_update_links()   # this MUST be after update_escaped_chars -- see https://github.com/sagemathinc/cocalc/issues/1391
+        @_update_tables()
+
+    componentDidUpdate: ->
+        @update_content()
+
+    componentDidMount: ->
+        @_is_mounted = true
+        @update_content()
+
+    componentWillUnmount: ->
+        # see https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
+        # and https://github.com/sagemathinc/cocalc/issues/1689
+        @_is_mounted = false

     render_html: ->
         if @props.value
@@ -804,11 +814,6 @@
                 html = require('./misc_page').sanitize_html_safe(@props.value, @props.post_hook)
             else
                 html = require('./misc_page').sanitize_html(@props.value, true, true, @props.post_hook)
-
-            html = @_process_math(html)
-            html = @_process_links(html)
-            html = @_process_tables(html)
-            html = @_process_highlight(html)

             {__html: html}
         else
@@ -849,7 +854,7 @@
         if @props.value
             # change escaped characters back for markdown processing
             v = @props.value.replace(/>/g, '>').replace(/</g, '<')
-            return markdown.markdown_to_html(v)
+            return markdown.markdown_to_html(v, {process_math : true})

     render: ->
         HTML = exports.HTML


Default map to showing PAs

Default map to showing PAs

--- client/src/views/map_view.coffee
+++ client/src/views/map_view.coffee
@@ -59,6 +59,7 @@

   renderDataToMap: ->
     serviceName = @visualisation.get('indicator').get('indicatorDefinition').serviceName
+    serviceName ||= "NRT_AD_ProtectedArea"
     L.esri.dynamicMapLayer("http://196.218.36.14/ka/rest/services/#{serviceName}/MapServer",
       opacity: 0.6
     ).addTo(@map)


Re-order compile rate limits to do project/user check before everyone check

--- services/web/app/coffee/Features/Compile/CompileManager.coffee
+++ services/web/app/coffee/Features/Compile/CompileManager.coffee
@@ -18,15 +18,16 @@
 			timer.done()
 			_callback(args...)

-		@_checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, "everyone", (err, canCompile)->
-			if !canCompile
-				return callback null, "autocompile-backoff", []
-			logger.log project_id: project_id, user_id: user_id, "compiling project"
-			CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
-				return callback(error) if error?
-				if recentlyCompiled
-					logger.warn {project_id, user_id}, "project was recently compiled so not continuing"
-					return callback null, "too-recently-compiled", []
+		logger.log project_id: project_id, user_id: user_id, "compiling project"
+		CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
+			return callback(error) if error?
+			if recentlyCompiled
+				logger.warn {project_id, user_id}, "project was recently compiled so not continuing"
+				return callback null, "too-recently-compiled", []
+
+			CompileManager._checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, "everyone", (err, canCompile)->
+				if !canCompile
+					return callback null, "autocompile-backoff", []

 				CompileManager._ensureRootDocumentIsSet project_id, (error) ->
 					return callback(error) if error?


enter no longer uses execSync

enter no longer uses execSync

--- src/steroids/Prompt.coffee
+++ src/steroids/Prompt.coffee
@@ -42,7 +42,11 @@
           process.exit(0)
         when "", "push"
           console.log "Updating code to all connected devices ..."
-          @options.context.runSteroidsCommandSync "push", exitOnFailure: false
+          Project = require "./Project"
+          project = new Project
+          project.push
+            onSuccess: => @refresh()
+            onFailure: => @refresh()

         when "s", "sim", "simulator"
           console.log "Starting iPhone Simulator"


Fixed graphics

Fixed graphics

--- src/js/app/editor.coffee
+++ src/js/app/editor.coffee
@@ -483,9 +483,11 @@
 """

 sourceArea = ace.edit 'sourceArea'
-sourceArea.setTheme "ace/theme/monokai"
+sourceArea.setTheme "ace/theme/cobalt"
 sourceArea.getSession().setMode "ace/mode/coffee"
 sourceArea.setHighlightActiveLine true
+sourceArea.getSession().setTabSize 2
+sourceArea.setShowPrintMargin false

 sourceArea.getSession().on 'change', sourceChange

@@ -496,9 +498,11 @@
     commandArea.focus()

 commandArea = ace.edit 'commandArea'
-commandArea.setTheme "ace/theme/monokai"
+commandArea.setTheme "ace/theme/cobalt"
 commandArea.getSession().setMode "ace/mode/coffee"
 commandArea.setHighlightActiveLine false
+commandArea.setShowPrintMargin false
+commandArea.renderer.setShowGutter false

 # Execute on enter
 commandArea.commands.addCommand
@@ -529,6 +533,13 @@

 $("#commandWrap").mouseenter -> commandArea.focus()
 $("#sourceArea").mouseenter -> sourceArea.focus()
+
+# set correct background for border
+
+commandArea.renderer.on 'themeLoaded', ->
+  color = $("#sourceArea").css 'background-color'
+  $("#sourceArea").parent().css 'background-color', color
+  $("#commandArea").parent().css 'background-color', color

 $("#centerBar").draggable
   axis: "x"


add emotion_level none

add emotion_level none

--- src/lib/voicetext.coffee
+++ src/lib/voicetext.coffee
@@ -22,6 +22,7 @@
     ANGER: 'anger'
     SADNESS :'sadness'
   EMOTION_LEVEL:
+    NONE: undefined
     HIGH: '2'
     NORMAL: '1'
   SPEAKER:


Add status 377

Add status 377

--- server/testSystems/informatics/InformaticsSubmitDownloader.coffee
+++ server/testSystems/informatics/InformaticsSubmitDownloader.coffee
@@ -33,6 +33,7 @@
     13: "Security error",
     96: "Тестирование...",
     98: "Компилирование..."
+    377: "Задача в очереди на тестирование"

 export default class InformaticsSubmitDownloader extends TestSystemSubmitDownloader
@@ -43,7 +44,7 @@
     IG: 'Проигнорировано'
     DQ: 'Дисквалифицировано'
     CE: 'Ошибка компиляции'
-    CT: ["Тестирование...", "Компилирование...", "Перетестировать"]
+    CT: ["Тестирование...", "Компилирование...", "Перетестировать", "Задача в очереди на тестирование"]

     parseRunId: (runid) ->
         if runid.includes("r")


Fix bug showing tab when coming from another page

--- app/assets/javascripts/app/tabs.js.coffee
+++ app/assets/javascripts/app/tabs.js.coffee
@@ -1,15 +1,15 @@
-show_tab = () ->
+show_tab = ->
   if location.hash.match(/^#tab-/)
     $(".nav-tabs a[href=##{location.hash.replace('#tab-', '')}]").tab('show')

-if $('.nav-tabs').length > 0
-  default_hash = $('.nav-tabs .active a')[0].hash.replace('#', '')
-  window.history.replaceState({}, '', ("#tab-" + default_hash))
+show_default_tab = ->
+  if $('.nav-tabs').length > 0
+    default_hash = $('.nav-tabs .active a')[0].hash.replace('#', '')
+    window.history.replaceState({}, '', ("#tab-" + default_hash))

-show_tab()
+show_tab() || show_default_tab()

-$(window).on 'popstate', (e)->
-    show_tab()
+$(window).on 'popstate', show_tab

 $('a[data-toggle="tab"]').on 'shown.bs.tab', (e) ->
   window.location.hash = e.target.hash.replace('#', '#tab-')


Fix Chronograph refresh time.

Fix Chronograph refresh time.

--- app/assets/javascripts/chronograph.coffee
+++ app/assets/javascripts/chronograph.coffee
@@ -121,12 +121,15 @@
     ctx.closePath()

   run: ->
+    # window.requestAnimationFrame: Must > IE10
+    # window.setImmediate: only IE10
+    # setInterval: delay minimum is 15.6ms(CPU 64 fps) ~= 16ms
     i = 0
     timer = setInterval((=>
       @millisecond = 0 if @millisecond >= 360
-      @millisecond = @millisecond + 1000 / Math.PI * 2 / 360
+      @millisecond = @millisecond + 360 / 1000 * 16
       @dashboard()
-    ), 1)
+    ), 16)

 window.Chronograph = Chronograph


narrow selector to only be used on upload form

--- app/assets/javascripts/upload_form.coffee
+++ app/assets/javascripts/upload_form.coffee
@@ -1,4 +1,4 @@
 $ ->
-  $("input:file").on "change", ->
+  $("form[id='upload'] input:file").on "change", ->
     window.showSpinner "#000"
     $("form#upload").submit()


Disable dropzone in ZEN mode

Disable dropzone in ZEN mode

--- app/assets/javascripts/zen_mode.js.coffee
+++ app/assets/javascripts/zen_mode.js.coffee
@@ -32,6 +32,8 @@
     @active_zen_area = @active_checkbox.parent().find('textarea')
     @active_zen_area.focus()
     window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
+    # Disable dropzone in ZEN mode
+    Dropzone.forElement('.div-dropzone').disable()

   exitZenMode: =>
     if @active_zen_area isnt null
@@ -41,6 +43,8 @@
       @active_checkbox = null
       window.location.hash = ''
       window.scrollTo(window.pageXOffset, @scroll_position)
+      # Enable dropzone when leaving ZEN mode
+      Dropzone.forElement('.div-dropzone').enable()

   checkboxFromLocationHash: (e) ->
     id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, ''))


parseInt routeParams id

parseInt routeParams id

--- app/scripts/services/budget_loader.coffee
+++ app/scripts/services/budget_loader.coffee
@@ -40,7 +40,7 @@

   setBudgetByRoute: () ->
     if $routeParams.budgetId
-      @setBudget($routeParams.budgetId)
+      @setBudget(parseInt($routeParams.budgetId))
     else
       @defaultToFirstBudget()


chat: add a handler to call when video collab is active

--- client/ide/lib/views/chat/idechatview.coffee
+++ client/ide/lib/views/chat/idechatview.coffee
@@ -42,6 +42,8 @@
     @once 'CollaborationStarted',        @bound 'removeLoader'
     @once 'CollaborationNotInitialized', @bound 'removeLoader'
     @once 'CollaborationEnded',          @bound 'destroy'
+
+    @once 'VideoCollaborationActive', @bound 'handleVideoActive'

     @on 'ParticipantSelected', @bound 'handleParticipantSelected'

@@ -91,6 +93,12 @@

     @loaderView.destroy()
     @unsetClass 'loading'
+
+
+  handleVideoActive: (publisher) ->
+
+    @setClass 'is-videoActive'
+    @chatVideoView.show()

   getVideoContainer: -> @chatVideoView.getContainer()


Tests: Added wait selector for textarea

Tests: Added wait selector for textarea

--- client/test/lib/teams/teamslikeunlike.coffee
+++ client/test/lib/teams/teamslikeunlike.coffee
@@ -66,9 +66,9 @@
     browser
       .waitForElementVisible  textSelector, 20000
       .setValue               chatInputSelector, browser.Keys.UP_ARROW
+      .pause                  3000 # Wait for textarea
       .waitForElementVisible  editingSelector, 20000
       .clearValue             editingSelector
-      .pause                  3000 #slow down
       .setValue               editingSelector, 'Message after editing' + browser.Keys.ENTER
       .waitForElementVisible  chatItem, 20000
       .pause                  3000 #waiting for text to be changed


throwing exception object instead of an empty string

--- Src/coffeescript/cql-execution/src/elm/aggregate.coffee
+++ Src/coffeescript/cql-execution/src/elm/aggregate.coffee
@@ -1,6 +1,7 @@
 { Expression } = require './expression'
 { typeIsArray , allTrue, anyTrue, compact, numerical_sort} = require '../util/util'
 { build } = require './builder'
+{ Exception } = require '../datatypes/exception'
 Quantity = require './quantity'

 quantitiesOrArg = (arr) ->
@@ -18,7 +19,7 @@
       values.push i.convertUnits(unit)
     return compact(values) # need to make sure that there are not any null values from the quntities
   else if someQs
-    throw "" # need and expection cannot do calcs of qunatities and non quantities
+    throw new Exception("Cannot perform aggregate operations on mixed values of Quantities and non Quantities")
   else
     arr


finish support for ctrl-delete and ctrl-backspace in smartNumber, working properly

--- app/assets/javascripts/angular/directives/smartNumber.js.coffee
+++ app/assets/javascripts/angular/directives/smartNumber.js.coffee
@@ -12,6 +12,35 @@
     inertKeys = [37, 39, 9, 33, 34]

     keyCodes = { 48:0, 49:1, 50:2, 51:3, 52:4, 53:5, 54:6, 55:7, 56:8, 57:9 }
+
+    numberCharacterPressed = (keycode)->
+      keycode >= 48 and keycode <= 57 # insert number character
+
+    # Ctrl key helpers
+    initCtrlKeys = (scope)->
+      scope.ctrlDownCount = 0
+
+    isCtrl = (event) ->
+      event.which == 17
+
+    ctrlDownCount = (scope)->
+      scope.ctrlDownCount
+
+    ctrlActive = (scope)->
+      scope.ctrlDownCount > 0
+
+    registerCtrl = (scope)->
+      scope.ctrlDownCount += 1
+
+    unregisterCtrl = (scope)->
+      scope.ctrlDownCount -= 1
+
+    # key recognition helpers
+    isDelete = (event)->
+      event.which == 46
+
+    isBackspace = (event)->
+      event.which == 8

     getOptions = (scope, attrs) ->
         options = angular.copy defaultOptions
@@ -198,12 +227,13 @@
     {
         restrict: 'A'
         require: 'ngModel'
-        scope:
-          true
+        scope: {
+        }
         link: (scope, elem, attrs, ngModelCtrl) ->
             options = getOptions scope, attrs
             isValid = makeIsValid options
             reformatRequired = false
+            scope.ctrlDownCount = 0

             ngModelCtrl.$parsers.unshift (viewVal) ->
                 noCommasVal = viewVal.replace /,/g, ''
@@ -227,6 +257,8 @@
                 val

             elem.on 'keyup', (event) ->
+              # record that a ctrl key has been released
+              unregisterCtrl(scope) if isCtrl(event)

               return if invalidInput(elem, event)

@@ -244,86 +276,120 @@
             elem.on 'keydown', (event) ->
               keycode = event.which

+              # allow normal functionality if the key doesn't interfere with input
               killEvent(event) unless isInertKey(keycode)

+              # indicate that a ctrl key has been pressed
+              registerCtrl(scope) if isCtrl(event)
+
+              # do nothing if the key pressed isn't a permissable operation
               return if invalidInput(elem, event)
-              # unless isControlKey(keycode)
-                # return if maxDigitsReached(event, 9)

+              # find the cursor position
               initialPosition = elem[0].selectionStart

-              if keycode >= 48 and keycode <= 57 # insert number character
-                unless maxDigitsReached(elem, 9)
-                  newCursorPosition = findNewCharacterCursorPosition(elem, initialPosition)
-                  insertCharacter(elem, event)
-                  elem.val resetCommas(elem.val()) # reformat the number in place
-                  elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
-                  triggerChange(elem)
-
-              if keycode == 8 # backspace is pressed
-
-                if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
+              # do different stuff if ctrl is pressed
+              if scope.ctrlDownCount > 0
+
+                if isDelete(event)
                   # get the string before the selection and figure out how many commas it has
                   originalCursorPosition = elem[0].selectionStart
                   stringBeforeCursor = elem.val().slice(0, originalCursorPosition)
-                  startingCommasBeforeCursor = (stringBeforeCursor.match(/,/g) || []).length
-
-                  # delete the selected stuff and reset the commas
-                  deleteSelectedRange(elem, event)
-                  elem.val resetCommas(elem.val()) # reformat the number in place
-
-                  # figure out how many commas were lost
-                  finalStringBeforeCursor = elem.val().slice(0, originalCursorPosition)
-                  endingCommasBeforeCursor = (finalStringBeforeCursor.match(/,/g) || []).length
-                  commaDiff = startingCommasBeforeCursor - endingCommasBeforeCursor
+                  elem.val resetCommas(stringBeforeCursor) # reformat the number in place

                   # set the final cursor position
-                  finalCursorPosition = originalCursorPosition - commaDiff
+                  finalCursorPosition = elem.val().length
                   elem[0].setSelectionRange(finalCursorPosition, finalCursorPosition) # set the new cursor position

                   # trigger final change event
                   triggerChange(elem)

-                else unless elem[0].selectionStart == 0 # do nothing if it's at the end of the end of the input
-                  newCursorPosition = findNewBackspaceCursorPosition(elem, initialPosition)
-                  backspacePressed(elem, event) # handle logic for backspace
-                  elem.val resetCommas(elem.val()) # reformat the number in place
-                  elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
+                if isBackspace(event)
+                  # get the string before the selection and figure out how many commas it has
+                  originalCursorPosition = elem[0].selectionStart
+                  stringAfterCursor = elem.val().slice(originalCursorPosition, elem.val().length)
+                  elem.val resetCommas(stringAfterCursor) # reformat the number in place
+
+                  # set the final cursor position
+                  elem[0].setSelectionRange(0, 0) # set the cursor at the beginning of the input field

                   # trigger final change event
                   triggerChange(elem)
-
-              if keycode == 46 # delete is pressed
-                if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
-                  # get the string before the selection and figure out how many commas it has
-                  originalCursorPosition = elem[0].selectionStart
-                  stringBeforeCursor = elem.val().slice(0, originalCursorPosition)
-                  startingCommasBeforeCursor = (stringBeforeCursor.match(/,/g) || []).length
-
-                  # delete the selected stuff and reset the commas
-                  deleteSelectedRange(elem, event)
-                  elem.val resetCommas(elem.val()) # reformat the number in place
-
-                  # figure out how many commas were lost
-                  finalStringBeforeCursor = elem.val().slice(0, originalCursorPosition)
-                  endingCommasBeforeCursor = (finalStringBeforeCursor.match(/,/g) || []).length
-                  commaDiff = startingCommasBeforeCursor - endingCommasBeforeCursor
-
-                  # set the final cursor position
-                  finalCursorPosition = originalCursorPosition - commaDiff
-                  elem[0].setSelectionRange(finalCursorPosition, finalCursorPosition) # set the new cursor position
-
-                  # trigger final change event
-                  triggerChange(elem)
-
-                else unless elem[0].selectionStart == elem.val().length
-                  newCursorPosition = findNewDeleteCursorPosition(elem, initialPosition)
-                  deletePressed(elem, event) # handle logic for backspace
-                  elem.val resetCommas(elem.val()) # reformat the number in place
-                  elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
-
-                  # trigger final change event
-                  triggerChange(elem)
+
+              else
+
+                if numberCharacterPressed(keycode)
+                  unless maxDigitsReached(elem, 9)
+                    newCursorPosition = findNewCharacterCursorPosition(elem, initialPosition)
+                    insertCharacter(elem, event)
+                    elem.val resetCommas(elem.val()) # reformat the number in place
+                    elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
+                    triggerChange(elem)
+
+                if isBackspace(event) # backspace is pressed
+
+                  if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
+                    # get the string before the selection and figure out how many commas it has
+                    originalCursorPosition = elem[0].selectionStart
+                    stringBeforeCursor = elem.val().slice(0, originalCursorPosition)
+                    startingCommasBeforeCursor = (stringBeforeCursor.match(/,/g) || []).length
+
+                    # delete the selected stuff and reset the commas
+                    deleteSelectedRange(elem, event)
+                    elem.val resetCommas(elem.val()) # reformat the number in place
+
+                    # figure out how many commas were lost
+                    finalStringBeforeCursor = elem.val().slice(0, originalCursorPosition)
+                    endingCommasBeforeCursor = (finalStringBeforeCursor.match(/,/g) || []).length
+                    commaDiff = startingCommasBeforeCursor - endingCommasBeforeCursor
+
+                    # set the final cursor position
+                    finalCursorPosition = originalCursorPosition - commaDiff
+                    elem[0].setSelectionRange(finalCursorPosition, finalCursorPosition) # set the new cursor position
+
+                    # trigger final change event
+                    triggerChange(elem)
+
+                  else unless elem[0].selectionStart == 0 # do nothing if it's at the end of the end of the input
+                    newCursorPosition = findNewBackspaceCursorPosition(elem, initialPosition)
+                    backspacePressed(elem, event) # handle logic for backspace
+                    elem.val resetCommas(elem.val()) # reformat the number in place
+                    elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
+
+                    # trigger final change event
+                    triggerChange(elem)
+
+                if isDelete(event) # delete is pressed
+                  if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
+                    # get the string before the selection and figure out how many commas it has
+                    originalCursorPosition = elem[0].selectionStart
+                    stringBeforeCursor = elem.val().slice(0, originalCursorPosition)
+                    startingCommasBeforeCursor = (stringBeforeCursor.match(/,/g) || []).length
+
+                    # delete the selected stuff and reset the commas
+                    deleteSelectedRange(elem, event)
+                    elem.val resetCommas(elem.val()) # reformat the number in place
+
+                    # figure out how many commas were lost
+                    finalStringBeforeCursor = elem.val().slice(0, originalCursorPosition)
+                    endingCommasBeforeCursor = (finalStringBeforeCursor.match(/,/g) || []).length
+                    commaDiff = startingCommasBeforeCursor - endingCommasBeforeCursor
+
+                    # set the final cursor position
+                    finalCursorPosition = originalCursorPosition - commaDiff
+                    elem[0].setSelectionRange(finalCursorPosition, finalCursorPosition) # set the new cursor position
+
+                    # trigger final change event
+                    triggerChange(elem)
+
+                  else unless elem[0].selectionStart == elem.val().length
+                    newCursorPosition = findNewDeleteCursorPosition(elem, initialPosition)
+                    deletePressed(elem, event) # handle logic for backspace
+                    elem.val resetCommas(elem.val()) # reformat the number in place
+                    elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position
+
+                    # trigger final change event
+                    triggerChange(elem)

             elem.on 'keypress', (event) ->
               killEvent(event)


Fix bad if statement sending doc open into infinite loop

--- services/web/public/coffee/ide/history/controllers/HistoryV2DiffController.coffee
+++ services/web/public/coffee/ide/history/controllers/HistoryV2DiffController.coffee
@@ -29,10 +29,11 @@
 			do tryOpen = () ->
 				if iterations > 5
 					return
+				iterations += 1
 				entity = ide.fileTreeManager.findEntityById(id)
 				if entity? and type == 'doc'
 					ide.editorManager.openDoc(entity)
-				if entity? and type == 'file'
+				else if entity? and type == 'file'
 					ide.binaryFilesManager.openFile(entity)
 				else
 					setTimeout(tryOpen, 500)


TAP5-2554:  Using Confirm mixin in conjunction with an EventLink or
ActionLink having "async" set to true causes double invocation of event
handler

- apply patch from
https://patch-diff.githubusercontent.com/raw/apache/tapestry-5/pull/4.patch
without changes

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/confirm-click.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/confirm-click.coffee
@@ -82,13 +82,9 @@
       # Cancel the original click event
       return false

-    ($ document).on "click", "a[data-confirm-message]:not(.disabled)", (event) ->
+    ($ document).on "click", "a[data-confirm-message]:not(.disabled, [data-update-zone], [data-async-trigger])", (event) ->

       target = $ event.target
-
-      # Order of event handlers on an element is not predictable. From testing, I found this could happen.
-      # A bit ugly.
-      return if target.attr "data-update-zone"

       # See note above; this replicates the default behavior of a link element that is lost because
       # of the


Clean up test

Clean up test

--- test/elvis-backbone-test.coffee
+++ test/elvis-backbone-test.coffee
@@ -125,51 +125,47 @@

 describe 'Elvis Backbone.View', ->
+  class View extends Backbone.View
+    transform: (value) -> value
+    render: ->
+      el(@el, @_binding = @bindTo('value').get(@transform))
+      this
+
+  beforeEach ->
+    @model = new Backbone.Model(value: 'foo')
+    @view = new View(model: @model)
+
   it 'can bind using bindTo in a Backbone View', ->
-    class View extends Backbone.View
-      render: ->
-        el(@el, @bindTo('value'))
-        this
+    @view.render()
+    expect(@view.el.innerHTML).to.equal('foo')

-    model = new Backbone.Model(value: 'foo')
-    view = new View(model: model).render()
-
-    expect(view.el.innerHTML).to.equal('foo')
-
-    model.set(value: 'bar')
-    expect(view.el.innerHTML).to.equal('bar')
+    @model.set(value: 'bar')
+    expect(@view.el.innerHTML).to.equal('bar')

   it 'calls transform with proper context', ->
-    class View extends Backbone.View
-      transform: (value) -> value
-      render: ->
-        el(@el, @bindTo('value').get(@transform))
-        this
-
-    model = new Backbone.Model(value: 'foo')
-    view = new View(model: model).render()
-    spy = sinon.spy(view, 'transform')
-    view.render()
+    spy = sinon.spy(@view, 'transform')
+    @view.render()

     spy.should.be.calledOnce
     spy.should.be.calledWith('foo')
-    spy.should.be.calledOn(view)
+    spy.should.be.calledOn(@view)
+
+  it 'calls transform with proper context', ->
+    spy = sinon.spy(@view, 'transform')
+    @view.render()
+
+    spy.should.be.calledOnce
+    spy.should.be.calledWith('foo')
+    spy.should.be.calledOn(@view)

   it 'removes Backbone View bindings when the view is destroyed', ->
-    binding = null
-    class View extends Backbone.View
-      render: ->
-        el(@el, binding = @bindTo('value'))
-        this
+    @view.render()
+    spy = sinon.spy(@view._binding, 'update')

-    model = new Backbone.Model(value: 'foo')
-    view = new View(model: model).render()
-    spy = sinon.spy(binding, 'update')
-
-    model.set(value: 'bar')
-    expect(spy).to.be.calledOnce
+    @model.set(value: 'bar')
+    spy.should.be.calledOnce
     spy.reset()

-    view.remove()
-    model.set(value: 'quux')
-    expect(spy).to.not.be.called
+    @view.remove()
+    @model.set(value: 'quux')
+    spy.should.not.be.called


html entities in input fields fixed

html entities in input fields fixed

--- core/utils.coffee
+++ core/utils.coffee
@@ -198,6 +198,11 @@

   nextTick: (fn) ->
     setTimeout fn, 0
+
+  htmlDecode:(str)->
+    el = document.createElement('div')
+    el.innerHTML = str
+    return el.textContent

   ###
   //     Underscore.js 1.3.1


Clear categories before updating.

Clear categories before updating.

--- app/assets/javascripts/application/pods_store.js.coffee
+++ app/assets/javascripts/application/pods_store.js.coffee
@@ -71,6 +71,7 @@
       categoriesArray = []
       for categoryName, category of categories
         categoriesArray.push category
+      @db.clear('category')
       @db.put('category', categoriesArray)
       @didUpdateCategories()
       categories


Update index.cjsx

Update index.cjsx

fix replace all mistake

--- app/assets/javascripts/components/transcribe/index.cjsx
+++ app/assets/javascripts/components/transcribe/index.cjsx
@@ -148,7 +148,7 @@
                 annotation={currentAnnotation}
                 subject={@getCurrentSubject()}
                 onChange={@handleDataFromTool}
-                subjectsubjectCurrentPage={@props.query.page}
+                subjectCurrentPage={@props.query.page}
                 onComplete={@handleTaskComplete}
                 onBack={@makeBackHandler()}
                 workflow={@getActiveWorkflow()}


Remove js logging statements

Remove js logging statements

--- app/assets/javascripts/flyover_comments/comments.coffee
+++ app/assets/javascripts/flyover_comments/comments.coffee
@@ -42,11 +42,8 @@
     $modal.modal("hide")

   $(document).on "ajax:success", "form.vote-button", (e, response, status, err)->
-    console.log "ajax:success - form.vote-button - start"
     voteButtons = $(@).closest(".vote-buttons")
     sameVoteableButtons = $("[data-comment-id='#{voteButtons.data('comment-id')}']")
-    console.log "ajax:success - form.vote-button - replacing buttons"
-    console.log response.updated_buttons_html
     sameVoteableButtons.replaceWith(response.updated_buttons_html)

   $(document).on "click", ".flag-flyover-comment-modal-link", (e)->


Fix typo

Fix typo

--- app/assets/javascripts/mass_enrollment_emails.js.coffee
+++ app/assets/javascripts/mass_enrollment_emails.js.coffee
@@ -40,7 +40,7 @@

   if emailIndex == undefined
-    alert "Can't find email feild in the file"
+    alert "Can't find email field in the file"
     return false
   contacts = []
   contacts.push {name: row.split(',')[nameIndex].trim(), email: row.split(',')[emailIndex].trim()} for row in raw_list[1..(raw_list.length - 1)] when row.split(',')[emailIndex] != undefined


Fix closestLabel() coffee syntax and returning.

--- app/assets/javascripts/ultimate/improves/form.js.coffee
+++ app/assets/javascripts/ultimate/improves/form.js.coffee
@@ -25,9 +25,9 @@
       jLabel = jInput.data 'closestLabel'  unless passCache
       unless jLabel
         if jLabel is false
-          jLabel = length: 0
+          return null
         else
-          jLabel = length: 0
+          jLabel = {length: 0}
           jEdge = jInput.closestEdge()   # TODO may be need params
           inputId = jInput.attr 'id'
           if inputId
@@ -39,19 +39,24 @@
               # try search in the closest form
               jLabel = jInput.closest('form').find labelSelectorByFor
               # trust only unique label on the form
-              jLabel = length: 0  if jLabel.length > 1
+              jLabel = {length: 0}  if jLabel.length > 1
           # try get first labet in the edge area
           jLabel = jEdge.find('label:first')  unless jLabel.length
           # taken!
           if jLabel.length
             # oh, this label already linked
             if jLabel.data 'closestInput'
-              jLabel = length: 0
+              jLabel = {length: 0}
             else
               jLabel.data 'closestInput', jInput
           jInput.data 'closestLabel', jLabel
-      jLabel = length: 0  if strongById and jLabel.length and jLabel.attr('for') isnt jInput.attr('id')
-      if jLabel.length then jLabel[0] else null
+      if jLabel.length
+        if strongById and jLabel.attr('for') isnt jInput.attr('id')
+          null
+        else
+          jLabel[0]
+      else
+        null


Fix freehand segment line Return a set of changes from initStart and initMove

--- app/classifier/drawing-tools/freehand-segment-line.cjsx
+++ app/classifier/drawing-tools/freehand-segment-line.cjsx
@@ -24,12 +24,16 @@
       _currentlyDrawing: false

     initStart: (coords, mark) ->
-      mark.points.push roundCoords coords
-      _inProgress: true
-      _currentlyDrawing: true
+      points = mark.points.slice()
+      points.push roundCoords coords
+      _inProgress = true
+      _currentlyDrawing = true
+      { _inProgress, _currentlyDrawing, points }

     initMove: (coords, mark) ->
-      mark.points.push roundCoords coords
+      points = mark.points.slice()
+      points.push roundCoords coords
+      { points }

     initRelease: (coords, mark) ->
       _currentlyDrawing: false


Confirm repost action

Confirm repost action

--- assets/javascripts/views/post/001_post_action.js.coffee
+++ assets/javascripts/views/post/001_post_action.js.coffee
@@ -4,7 +4,15 @@
   constructor: ->
     super

-    DOM.on(@el, 'click', @performAction)
+    @text = {
+      confirm: DOM.attr(@el, 'data-confirm')
+    }
+
+    DOM.on(@el, 'click', @confirmAction)
+
+  confirmAction: =>
+    return @performAction() unless @text.confirm
+    @performAction() if confirm(@text.confirm)

   performAction: =>
     console.warn "#{@constructor.name}::performAction needs to be defined"


Rewrite CartesianFrame.{x,y}_ranges not to use get()

--- bokehjs/src/coffee/models/canvas/cartesian_frame.coffee
+++ bokehjs/src/coffee/models/canvas/cartesian_frame.coffee
@@ -15,8 +15,8 @@
   type: 'CartesianFrame'

   @getters {
-    x_ranges: () -> @_get_ranges('x')
-    y_ranges: () -> @_get_ranges('y')
+    x_ranges: () -> @_get_ranges(@x_range, @extra_x_ranges)
+    y_ranges: () -> @_get_ranges(@y_range, @extra_y_ranges)

     x_mappers: () -> @_get_mappers(@x_mapper_type, @x_ranges, @h_range)
     y_mappers: () -> @_get_mappers(@y_mapper_type, @y_ranges, @v_range)
@@ -70,13 +70,12 @@
     sy = canvas.v_vy_to_sy(vy)
     return [sx, sy]

-  _get_ranges: (dim) ->
+  _get_ranges: (range, extra_ranges) ->
     ranges = {}
-    ranges['default'] = @get("#{dim}_range")
-    extra_ranges = @get("extra_#{dim}_ranges")
+    ranges['default'] = range
     if extra_ranges?
-      for name, range of extra_ranges
-        ranges[name] = range
+      for name, extra_range of extra_ranges
+        ranges[name] = extra_range
     return ranges

   _get_mappers: (mapper_type, ranges, frame_range) ->