HTML5 LocalStorage persistence.

HTML5 LocalStorage persistence.

--- src/Rickshaw.Persistence.coffee
+++ src/Rickshaw.Persistence.coffee
@@ -122,13 +122,40 @@
       newData = {}
       newData[@paramName] = data
       newData
+
+})

-    _successCallbackWrapper: (callback) ->
-      ( json, text ) ->
+# HTML5 localStorage persistance
+window.Rickshaw.Persistence.LocalStorage = new Class({
+
+  Extends: Rickshaw.Persistence._Base
+
+  # TODO: These methods need to be bound to the model instance.
+  store:
+    prefix: "rickshaw"
+
+    create: (model, callback, failure_callback) ->
+      this.update( model, callback, failure_callback )
+
+    read: (model, callback, failure_callback) ->
+      try
+        callback( JSON.decode( localStorage[this._key( model )] ) )
+      catch error
+        failure_callback() if failure_callback
+
+    update: (model, callback, failure_callback) ->
+      try
+        localStorage[this._key( model )] = JSON.encode( model.data )
         callback()
-
-    _failCallbackWrapper: (model, callback) ->
-      ( xhr ) ->
-        model._persistFail(callback, xhr)
-
+      catch error
+        failure_callback() if failure_callback
+
+    delete: (model, callback, failure_callback) ->
+      try
+        localStorage.removeItem( this._key( model ) )
+        callback()
+      catch error
+        failure_callback() if failure_callback
+
+    _key: (model) -> "#{@prefix}:#{model.id}"
 })


set title to blank string to silence warning

--- src/components/questions/section-questions.cjsx
+++ src/components/questions/section-questions.cjsx
@@ -58,7 +58,7 @@
     if isSelected and ExerciseStore.isExcludedAtMinimum(@props.exercises)
       Dialog.show(
         className: 'question-library-min-exercise-exclusions'
-        title: undefined, body: @renderMinimumExclusionWarning()
+        title: '', body: @renderMinimumExclusionWarning()
         buttons: [
           <BS.Button key='exclude'
             onClick={->


Fixup e064b12, Tip documentation.

Fixup e064b12, Tip documentation.

--- src/js/jquery.hlf.tip.coffee
+++ src/js/jquery.hlf.tip.coffee
@@ -179,18 +179,18 @@
       for own name, animation of options.animations when name isnt 'base'
         _.defaults animation, options.animations.base

-    # 𝒇 `init` offloads non-trivial setup to other subroutines
+    # 𝒇 `init` offloads non-trivial setup to other subroutines.
     init: ->
       _.bindAll @, '_onTriggerMouseMove', '_setBounds'
-      # Initialize tip element.
+      # - Initialize tip element.
       @_setTip $ '<div>'
-      # Infer `doStem` and `doFollow` flags from respective `classNames` entries.
+      # - Infer `doStem` and `doFollow` flags from respective `classNames` entries.
       @doStem = @classNames.stem isnt ''
       @doFollow = @classNames.follow isnt ''
-      # Initialize state, which is either: `awake`, `asleep`, `waking`,
+      # - Initialize state, which is either: `awake`, `asleep`, `waking`,
       # `sleeping`; respectively show, hide.
       @_setState 'asleep'
-      # The tip should remain visible and `awake` as long as there is a high
+      # - The tip should remain visible and `awake` as long as there is a high
       # enough frequency of relevant mouse activity. In addition to using
       # `hoverIntent`, this is achieved with a simple base implementation around
       # timers `_sleepCountdown` and `_wakeCountdown` and an extra reference to
@@ -198,12 +198,12 @@
       @_wakeCountdown = null
       @_sleepCountdown = null
       @_$currentTrigger = null
-      # Initialize tip. Note the initial render.
+      # - Initialize tip. Note the initial render.
       @_render()
       @_bind()
-      # Initialize context.
+      # - Initialize context.
       @_bindContext()
-      # Initialize triggers. Note the initial processing.
+      # - Initialize triggers. Note the initial processing.
       @_processTriggers()
       @_bindTriggers()


...try this?

...try this?

--- src/application-delegate.coffee
+++ src/application-delegate.coffee
@@ -72,7 +72,8 @@
     remote.getCurrentWindow().toggleDevTools()

   executeJavaScriptInWindowDevTools: (code) ->
-    remote.getCurrentWindow().webContents.executeJavaScriptInDevTools(code)
+    webContents = remote.getCurrentWindow.webContents
+    webContents.executeJavaScript(code)

   setWindowDocumentEdited: (edited) ->
     ipc.send("call-window-method", "setDocumentEdited", edited)


Pass xhr object to webcoroutine error handler

Makes ajax errors and webcoroutine errors more symmetric

--- data/web/js/utils.coffee
+++ data/web/js/utils.coffee
@@ -36,12 +36,14 @@

     api: (url, data={}, type='GET') ->
         dfd = $.Deferred()
-        $.ajax(url: @root + url, data: data, type: type.toUpperCase())
+        xhr = $.ajax(url: @root + url, data: data, type: type.toUpperCase())
             .done (response) =>
                 if not response.jobid?
                     # Not a webcoroutine
+                    response.xhr = xhr
                     return dfd.resolve(response)
-                @jobs[response.jobid] = dfd
+
+                @jobs[response.jobid] = [dfd, xhr]
                 if response.pending
                     dfd.notify response.jobid
                 if response.pending and @interval > response.interval
@@ -55,7 +57,7 @@
                     @poll(1000)
                 @handle_response response

-            .fail (xhr, status, error) =>
+            .fail (xhr, status) =>
                 dfd.reject message: "HTTP #{xhr.status}: #{xhr.statusText}", xhr: xhr
         return dfd.promise()

@@ -63,11 +65,13 @@
     handle_response: ({jobs, notifications}) ->
         for job in jobs
             if @jobs[job.id]
+                [dfd, xhr] = @jobs[job.id]
+                delete @jobs[job.id]
                 if job.error
-                    @jobs[job.id].reject job.error
+                    job.error.xhr = xhr
+                    dfd.reject job.error
                 else
-                    @jobs[job.id].resolve job.result
-                delete @jobs[job.id]
+                    dfd.resolve job.result

         for n in notifications
             if @handlers[n._ntype]?


FIX: the value of the animators prop can be used to SET the animators prop - w/o breaking

--- source/Art/Engine/Core/EpochedObject.coffee
+++ source/Art/Engine/Core/EpochedObject.coffee
@@ -501,34 +501,42 @@
   ######################
   # ANIMATORS for PROPS
   ######################
+  processedAnimators = null
+  _addAnimator = (prop, options) =>
+    processedAnimators ||= {}
+    if match = prop.match /^_(.*)/
+      internalName = prop
+      [__, prop] = match
+    else
+      internalName = "_#{prop}"
+
+    processedAnimators[internalName] = if options instanceof PersistantAnimator
+      options
+    else if isFunction options
+      new PersistantAnimator prop, animate: options
+    else if isNumber options?.period
+      new PeriodicPersistantAnimator prop, merge options, continuous: true
+    else if options?.animate
+      new PersistantAnimator prop, options
+    else
+      new EasingPersistantAnimator prop, options
+
+  _addAnimators = (v) ->
+    return unless v
+    if isString v
+      _addAnimator prop for prop in v.match /[a-z]+/gi
+    else if isPlainArray v
+      _addAnimators el for el in v
+    else
+      _addAnimator prop, options for prop, options of v
+
   @concreteProperty
     animators:
       default: null
       preprocess: (v) ->
         processedAnimators = null
-        addProp = (prop, options) =>
-          processedAnimators ||= {}
-          processedAnimators["_" + prop] = if options instanceof PersistantAnimator
-            options
-          else if isFunction options
-            new PersistantAnimator prop, animate: options
-          else if isNumber options?.period
-            new PeriodicPersistantAnimator prop, merge options, continuous: true
-          else if options?.animate
-            new PersistantAnimator prop, options
-          else
-            new EasingPersistantAnimator prop, options
-
-        addProps = (v) ->
-          return unless v
-          if isString v
-            addProp prop for prop in v.match /[a-z]+/gi
-          else if isPlainArray v
-            addProps el for el in v
-          else
-            addProp prop, options for prop, options of v
-
-        addProps v
+
+        _addAnimators v

         processedAnimators


reactivity: chatinputwidget: search command fix

--- client/activity/lib/flux/actions/command.coffee
+++ client/activity/lib/flux/actions/command.coffee
@@ -25,6 +25,7 @@
         channelActions.unfollowChannel channelId
     when '/search'
       { initialChannelId, messageId } = params
+      return  unless initialChannelId and messageId
       channelActions.loadChannel(initialChannelId).then ({ channel }) ->
         kd.singletons.router.handleRoute "/Channels/#{channel.name}/#{messageId}"
     when 'CreateChannel'


Updated checkbox.js sample.

Updated checkbox.js sample.

--- samples/checkbox.js/public/checkbox.coffee
+++ samples/checkbox.js/public/checkbox.coffee
@@ -29,7 +29,7 @@
     @renderTask(task) for task in @tasks.active
     @renderTask(task) for task in @tasks.done

-  # Renders a task into the
+  # Renders a task into the list that it belongs to.
   renderTask: (task) ->
     $list = if task.done then @$doneList else @$activeList
     $list.append @$taskDom(task)
@@ -54,14 +54,12 @@
   # Called when the user wants to create a new task.
   onNewTask: (event) ->
     event.preventDefault()
-    name = $('#new-task-name').val()
-    if @tasks.findByName name
-      alert "You already have this task on your list!"
+    task = new Task name: $('#new-task-name').val(), done: false
+    if @tasks.findByName task.name
+      window.alert "You already have this task on your list!"
     else
       $('#new-task-button').attr 'disabled', 'disabled'
       $('#new-task-name').attr 'disabled', 'disabled'
-      task = new Task()
-      task.name = name
       @tasks.addTask task, =>
         $('#new-task-name').removeAttr('disabled').val ''
         $('#new-task-button').removeAttr 'disabled'
@@ -134,13 +132,13 @@
       # In most cases, this will fail, so don't bother checking for errors.
       @dbClient.readdir '/active', (error, entries, dir_stat, entry_stats) =>
         return @showError(error) if error
-        @active = ((new Task()).fromStat(stat) for stat in entry_stats)
+        @active = (Task.fromStat(stat) for stat in entry_stats)
         readActive = true
         done() if readActive and readDone
     @dbClient.mkdir '/done', (error, stat) =>
       @dbClient.readdir '/done', (error, entries, dir_stat, entry_stats) =>
         return @showError(error) if error
-        @done = ((new Task()).fromStat(stat) for stat in entry_stats)
+        @done = (Task.fromStat(stat) for stat in entry_stats)
         readDone = true
         done() if readActive and readDone
     @
@@ -151,7 +149,6 @@
   # @param {function()} done called when the task is saved to the user's
   #     Dropbox
   addTask: (task, done) ->
-    task.cleanupName()
     @dbClient.writeFile task.path(), '', (error, stat) =>
       return @showError(error) if error
       @addTaskToModel task
@@ -218,23 +215,18 @@
 # Model for a single user task.
 class Task
   # Creates a task with default values.
-  constructor: ->
-    @name = null
-    @done = false
-
-  # Reads data about a task from the stat of is file in a user's Dropbox.
-  #
-  # @param {Dropbox.Stat} entry the directory entry representing the task
-  fromStat: (entry) ->
-    @name = entry.name
-    @done = entry.path.split('/', 3)[1] is 'done'
-    @
-
-  # Cleans up the task name so that it's valid Dropbox file name.
-  cleanupName: (name) ->
+  constructor: (properties) ->
+    @name = properties?.name or '(no name)'
+    @done = properties?.done or false
     # English-only hack that removes slashes from the task name.
     @name = @name.replace(/\ \/\ /g, ' or ').replace(/\//g, ' or ')
-    @
+
+  # Creates a Task from the stat of its file in a user's Dropbox.
+  #
+  # @param {Dropbox.Stat} entry the directory entry representing the task
+  # @return {Task} the newly created task
+  @fromStat: (entry) ->
+    new Task name: entry.name, done: entry.path.split('/', 3)[1] is 'done'

   # Path to the file representing the task in the user's Dropbox.
   # @return {String} fully-qualified path


added prop types

added prop types

--- client/components/flow-status/status-gauge.cjsx
+++ client/components/flow-status/status-gauge.cjsx
@@ -1,8 +1,16 @@
 React = require 'react'
 moment = require 'moment'
+PropTypes = React.PropTypes

 AvgElapsedTimeGauge = React.createClass
   displayName: 'AvgElapsedTimeGauge'
+
+  propTypes:
+    title: PropTypes.string.isRequired,
+    total: PropTypes.number.isRequired,
+    failures: PropTypes.number.isRequired,
+    successes: PropTypes.number.isRequired,
+    successPercentage: PropTypes.number.isRequired,

   formatPercentage: (percentage) =>
     return "..." unless percentage?


added a response parser

added a response parser

--- src/coffee/ajax.coffee
+++ src/coffee/ajax.coffee
@@ -39,6 +39,25 @@
     deferred.reject.apply deferred, arguments

 ###
+ * @param {Object} header
+ * @param {*} data
+ * @type String
+###
+getDataByHeader = ( header, data )->
+
+  if !util.isString data
+    for headerName, headerVal of header
+      if /content-type/i.test headerName
+        if /application\/json/i.test headerVal
+          return JSON.stringify data
+        else if /application\/x-www-form-urlencoded/i.test headerVal
+          return queryString.stringify data
+
+    data.toString()
+  else
+    data
+
+###
  * @function
  * @param {String} url
  * @param {Object} settings
@@ -69,7 +88,7 @@
   if headers and util.isObject headers
     xhr.setRequestHeader headerName, headerVal for headerName, headerVal of headers

-  if isURLRequest then xhr.send() else xhr.send data
+  if isURLRequest then xhr.send() else xhr.send getDataByHeader( headers, data )

   xhr.done = deferred.done.bind deferred
   xhr.fail = deferred.fail.bind deferred
@@ -85,8 +104,10 @@
   ( url, params = {} )-> ajax url, util.clone( params, settings )

 ajax.get = buildAjax {method:'get'}
-ajax.post = buildAjax {method:'post'}
+ajax.post = buildAjax {method:'post', headers:{"Content-type":"application/x-www-form-urlencoded"}}
 ajax.put = buildAjax {method:'put'}
+
+ajax.postJson = buildAjax {method:'post', headers:{"Content-type":"application/json"}}

 EH[JSONP_KEY] = {}


Change the way model serializer works. Get 100% model cov.

Use hash instead of array as a backend to model stack.

This should improve performance from O(n) to ~O(log(n)) or even O(1).

Implementation uses `cid` property that should be unique to each backbone-based model.

--- src/chaplin/models/model.coffee
+++ src/chaplin/models/model.coffee
@@ -13,10 +13,8 @@
   delegator = utils.beget attributes

   # Add model to stack
-  if modelStack
-    modelStack.push model
-  else
-    modelStack = [model]
+  modelStack ?= {}
+  modelStack[model.cid] = true

   # Map model/collection to their attributes. Create a property
   # on the delegator that shadows the original attribute.
@@ -35,8 +33,8 @@
         )
       delegator[key] = serializedModels

-  # Remove model from stack
-  modelStack.pop()
+  # Remove model from stack.
+  delete modelStack[model.cid]

   # Return the delegator
   delegator
@@ -45,7 +43,7 @@
 # in the context of a given tree
 serializeModelAttributes = (model, currentModel, modelStack) ->
   # Nullify circular references
-  return null if model is currentModel or model in modelStack
+  return null if model is currentModel or _(modelStack).has model.cid
   # Serialize recursively
   attributes = if typeof model.getAttributes is 'function'
     # Chaplin models


EventMonitor now handles drop down menu changes correctly.

--- src/client/event_monitor.coffee
+++ src/client/event_monitor.coffee
@@ -119,23 +119,20 @@
             #   input, select, textarea
             change : (remoteEvent, clientEvent) ->
                 target = clientEvent.target
+                # TODO: use batch mechanism once it exists...this is inefficient.
                 if target.tagName.toLowerCase() == 'select'
-                    if target.multiple
-                        console.log("detected multiple")
-                        # TODO: batch these or make a specialized method
-                        for option in target.options
-                            server.DOMUpdate(
-                                method : 'setAttribute'
-                                rvID : null
-                                targetID : option.__nodeID
-                                args : ['selected', option.selected])
-                    else
-                        console.log("selectedIndex is now: #{clientEvent.target.selectedIndex}")
+                    for option in target.options
                         server.DOMUpdate(
                             method : 'setAttribute'
                             rvID : null
-                            targetID : clientEvent.target.__nodeID
-                            args : ['selectedIndex', clientEvent.target.selectedIndex])
+                            targetID : option.__nodeID
+                            args : ['selected', option.selected])
+                        console.log("selectedIndex is now: #{clientEvent.target.selectedIndex}")
+                    server.DOMUpdate(
+                        method : 'setAttribute'
+                        rvID : null
+                        targetID : clientEvent.target.__nodeID
+                        args : ['selectedIndex', clientEvent.target.selectedIndex])
                 else # input or textarea
                     server.DOMUpdate(
                         method : 'setAttribute'


Bug fix for touch devices

Bug fix for touch devices

--- src/code/views/node-view.coffee
+++ src/code/views/node-view.coffee
@@ -119,7 +119,7 @@
     return if not @props.selectionManager

     selectionKey = if actually_select then @props.nodeKey else "dont-select-anything"
-    multipleSelections = evt.ctrlKey || evt.metaKey || evt.shiftKey
+    multipleSelections = evt && (evt.ctrlKey || evt.metaKey || evt.shiftKey)
     @props.selectionManager.selectNodeForInspection(@props.data, multipleSelections)

     # open the relationship panel on double click if the node has incombing links


Fixed component doc links.

Fixed component doc links.

--- src/coffee/dom/component.coffee
+++ src/coffee/dom/component.coffee
@@ -7,7 +7,7 @@
   # ** `rootNodeType`: the tag name of the root node to create, default = 'div'
   # ** `props`: an object representing the DOM attributes to append to the root node
   # ** `styles`: an object representing the CSS attributes to append to the root node
-  # ** `events`: an object reprenting the named DOM events (and corresponding callback methods) to bind to the root node
+  # ** `events`: an object representing the named DOM events (and corresponding callback methods) to bind to the root node
   # * `owner` the parent to which the component node will be appended
   # * `tagName` the name of of the component, which will always be prefixed with 'x-'
   component = (options = OJ.object(), owner, tagName) ->
@@ -15,14 +15,14 @@
     if not tagName.startsWith 'x-' then tagName = 'x-' + tagName
     # web components are really just ordinary OJ (element)[element.html]'s with a special name.
     # Until HTML Web Components are fully supported (and OJ is refactored accordingly), the element will be treated as an unknown element.
-    # In most cases, the default behavior of the browser is acceptable (see also (HTML Semantics)[http://diveintohtml5.info/semantics.html]), but
+    # In most cases, the default behavior of the browser is acceptable (see also [HTML Semantics](http://diveintohtml5.info/semantics.html)), but
     # in some cases this is problematic (firstly, because these elements are always rendered inline).
-    # In such conditions, the (controls)[controls.html] class and name space is better suited to classes which require complete control (e.g. (icon)[icon.html]).
+    # In such conditions, the [controls](controls.html) class and name space is better suited to classes which require complete control (e.g. [icon](icon.html)).
     widget = OJ.element tagName #, options.props, options.styles, options.events, options.text
     OJ.nodes.factory widget, owner

     # Since the behavior of styling is not well controlled/controllable on unknown elements, it is necessary to create a root node for the component.
-    # In most cases, (div)[div.html] is perfectly acceptable, but this is configurable at the name space level or at runtime.
+    # In most cases, [div](div.html) is perfectly acceptable, but this is configurable at the name space level or at runtime.
     rootNodeType = options.rootNodeType or OJ['DEFAULT_COMPONENT_ROOT_NODETYPE'] or 'div'

     # `ret` is the the instance of the rootNodeType, not the `widget` wrapped in this closure


Added support for exporting JSON data for a given project in the same format expected when converting to KMZ. Refactored logic to reuse this method when converting KMZ.

--- src/EntityUtils.coffee
+++ src/EntityUtils.coffee
@@ -558,24 +558,29 @@

   _.extend EntityUtils,

-    convertToKmz: (args) ->
-      Logger.info('Converting entities to KMZ', args)
+    _getProjectAndScenarioIdentifier: (args) ->
       args = @_getProjectAndScenarioArgs(args)
       projectId = args.projectId
       scenarioId = args.scenarioId

       scenarioStr = if scenarioId then '-' + scenarioId else ''
-      filePrefix = ProjectUtils.getDatedIdentifier(projectId) + scenarioStr
-      filename = filePrefix + '.kmz'
-      type = 'application/vnd.google-earth.kmz'
-
-      Logger.info('Generating C3ML entities...')
+      ProjectUtils.getDatedIdentifier(projectId) + scenarioStr
+
+    convertToC3ml: (args) ->
+      Logger.info('Generating C3ML entities...', args.projectId)
       c3mlData = Promises.runSync -> EntityUtils.getEntitiesAsJson(args)
       Logger.info('Wrote C3ML entities to', FileLogger.log(c3mlData))
       if c3mlData.c3mls.length == 0
         throw new Meteor.Error(500, 'No entities to convert')
-      buffer = AssetConversionService.export(c3mlData)
-
+      filename = @_getProjectAndScenarioIdentifier(args)
+      {filename: filename + '.c3ml.json', type: 'application/json', data: c3mlData}
+
+    convertToKmz: (args) ->
+      Logger.info('Converting entities to KMZ', args.projectId)
+      result = @convertToC3ml(args)
+      buffer = AssetConversionService.export(result.data)
+      filename = @_getProjectAndScenarioIdentifier(args) + '.kmz'
+      type = 'application/vnd.google-earth.kmz'
       if args.createFile
         file = new FS.File()
         file.name(filename)


Change event

Change event

--- views/services/layout.coffee
+++ views/services/layout.coffee
@@ -213,7 +213,7 @@
 window.addEventListener 'webview.width.change', handleResize
 window.addEventListener 'game.start', adjustSize

-remote.getCurrentWebContents().on 'did-finish-load', ->
+remote.getCurrentWebContents().on 'dom-ready', ->
   $('kan-game webview').loadURL config.get 'poi.homepage', 'http://www.dmm.com/netgame/social/application/-/detail/=/app_id=854854/'
   $('kan-game webview').addEventListener 'page-title-set', handleTitleSet
   # Editing DMM Cookie's Region Flag


adds environment specific methods

adds environment specific methods

--- lib/AppSpine.coffee
+++ lib/AppSpine.coffee
@@ -18,12 +18,32 @@
     super @config.emitter
     @setupLogger()

-  isDev: -> @getEnv() is 'development'
-
-  configure: (env, fn) -> fn() if @getEnv() is env
+  getEnvironment: ->
+    @config.env or process.env.NODE_ENV or 'development'

   getEnv: ->
-    process.env.NODE_ENV or 'development'
+    @getEnvironment()
+
+  isDevelopment: ->
+    @getEnvironment() is 'development'
+
+  isDev: ->
+    @isDevelopment()
+
+  isTesting: ->
+    @getEnvironment() is 'testing'
+
+  isStaging: ->
+    @getEnvironment() is 'staging'
+
+  isProduction: ->
+    @getEnvironment() is 'production'
+
+  isProd: ->
+    @isProduction()
+
+  configure: (env, fn) ->
+    fn() if @getEnvironment() is env

   require: (path) ->
     require(path)(@)
@@ -33,8 +53,6 @@
     @config.logger.format ?= "[{{title}}] {{timestamp}} ({{file}}:{{line}}): {{message}}"
     @config.logger.filters ?= loggerColorFilters
     @config.logger.dateformat ?= "HH:MM:ss"
-
-    unless @config.logger.level?
-      @config.logger.level = if @isDev() then 'log' else 'warn'
+    @config.logger.level ?= if @isDev() then 'log' else 'warn'

     @logger = tracer.colorConsole @config.logger


KodingAppsController: disable compileAppOnServer for now

--- client/landing/Core/kodingappscontroller.coffee
+++ client/landing/Core/kodingappscontroller.coffee
@@ -437,6 +437,8 @@

   @compileAppOnServer = (path, callback)->

+    return
+
     app = KodingAppsController.getAppInfoFromPath path, yes
     return  unless app


Dont expand tags by default.

Dont expand tags by default.

--- client/landing/app/CommonViews/LinkViews.coffee
+++ client/landing/app/CommonViews/LinkViews.coffee
@@ -73,7 +73,7 @@
 class TagLinkView extends LinkView

   constructor:(options = {}, data)->
-    options.expandable ?= yes
+    options.expandable ?= no
     if not options.expandable and data.title.length > 16
       options.tooltip =
         title     : data.title


Include unselected classrooms/courses for some stats

--- app/views/admin/OutcomesReportView.coffee
+++ app/views/admin/OutcomesReportView.coffee
@@ -70,8 +70,10 @@
     indexedSessions: ->
       _.indexBy(@sessions, '_id')
     numProgramsWritten: ->
-      _.size(@selectedSessions)
+      # Include unselected classrooms and courses because we don't particularly need to filter these numbers down
+      _.size(@indexedSessions)
     numShareableProjects: ->
+      # Include unselected classrooms and courses because we don't particularly need to filter these numbers down
       shareableLevels = _.flatten @classrooms.map (classroom) ->
         classroom.courses.map (course) ->
           _.filter course.levels, (level) ->
@@ -310,8 +312,9 @@
         type: 'GET',
         url: '/admin/calculate-lines-of-code'
         data: {
-          classroomIDs: @selectedClassrooms.map (c) -> c._id
-          courseIDs: @selectedCourses.map (c) -> c._id
+          # Include unselected classrooms and courses because we don't particularly need to filter these numbers down
+          classroomIDs: @classrooms.map (c) -> c._id
+          courseIDs: @courses.map (c) -> c._id
         }
         success: (data) =>
           @linesOfCode = parseInt(data.linesOfCode)


adding max line length

adding max line length

--- lib/CoffeeJs.coffee
+++ lib/CoffeeJs.coffee
@@ -45,7 +45,7 @@
     @B = browserify({debug: @debug, extensions: ['.coffee']})
     #@B.transform(Base64.transform)
     @B.transform(coffeeify, {sourceMap: true})
-    @B.transform(uglifyify, { global: true, 'max-line-len': 150000 })
+    @B.transform(uglifyify, { global: true, max_line_len: 150000 })
     @B.add(@source)

     @B.on('file', (file, id, parent)=>


Fix stray line indent

Fix stray line indent

--- app/views/courses/TeacherClassView.coffee
+++ app/views/courses/TeacherClassView.coffee
@@ -534,7 +534,7 @@
     language = @classroom.get('aceConfig')?.language
     for level in @levels.models
       levelIncludeMap[level.get('original')] = not level.get('practice') and (not language? or level.get('primerLanguage') isnt language)
-      completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and levelIncludeMap[s.get('level')?.original]
+    completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and levelIncludeMap[s.get('level')?.original]
     stats.averageLevelsComplete = if @students.size() then (_.size(completeSessions) / @students.size()).toFixed(1) else 'N/A'  # '
     stats.totalLevelsComplete = _.size(completeSessions)


Update for Escort Duty

Update for Escort Duty

Fix hero to Omarn and pet to griffin.


correcting the parallel-sets.coffee file

--- scripts/test-instance/parallel-sets.coffee
+++ scripts/test-instance/parallel-sets.coffee
@@ -29,7 +29,7 @@
   ]

   [
-    { name: 'invalidCardDetails'}
+    { name: 'pricing invalidCardDetails'}
     { name: 'pricing payment' }
     { name: 'activity post' }
     { name: 'staticpages loggedout' }


Set up the relationship and implement a comparator that favors fairs as the first option

--- apps/artwork/client/layered-search.coffee
+++ apps/artwork/client/layered-search.coffee
@@ -12,20 +12,32 @@
   _.extend @prototype, Markdown

   initialize: (options = {}) ->
-    @artworkId = @collection.artworkId
+    @set artwork_id: @collection.artwork.id
+    @set fair: @collection.fair if @get('type') is 'fair'
     @artworks = new Artworks
-    @artworks.url = "#{ARTSY_URL}/api/v1/related/layer/#{@get('type')}/#{@id}/artworks?artwork[]=#{@artworkId}"
+    @artworks.url = "#{ARTSY_URL}/api/v1/related/layer/#{@get('type')}/#{@id}/artworks?artwork[]=#{@get('artwork_id')}"

 module.exports.Layers = class Layers extends Backbone.Collection
   url: "#{ARTSY_URL}/api/v1/related/layers"
   model: Layer

   initialize: (options) ->
-    { @artworkId } = options
+    { @artwork, @fair } = options

   fetch: (options = {}) ->
-    _.extend options, data: 'artwork[]': @artworkId
+    _.extend options, data: 'artwork[]': @artwork.id
     Backbone.Collection::fetch.call this, options
+
+  # Ensure fairs are always first, followed by 'Most Similar',
+  # and that 'For Sale' is always last
+  sortMap: (type, id) ->
+    return  -1 if (type is 'fair')
+    return   0 if (type is 'synthetic') and (id is 'main')
+    return   @length if (type is 'synthetic') and (id is 'for-sale')
+    1
+
+  comparator: (model) ->
+    @sortMap model.get('type'), model.id

 module.exports.LayeredSearchView = class LayeredSearchView extends Backbone.View
   template: template


updating date picker input to float label when clicked on

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -34,7 +34,7 @@
     {expandCalendar: false}

   expandCalendar: ->
-    @setState({expandCalendar: true})
+    @setState({expandCalendar: true, hasFocus: true})

   isValid: (value) ->
     valid = true
@@ -59,22 +59,27 @@
     if (event.target.tagName is "INPUT" and not @state.expandCalendar)
       @setState({expandCalendar: true})

+  onBlur: (event) ->
+    @setState({hasFocus: false})
+
   render: ->
     classes = ['form-control']
-    unless @props.value then classes.push('empty')
+    value = @props.value
     open = false
+
+    if not @props.value and not @state.hasFocus
+      classes.push('empty')

     if @state.expandCalendar and not @props.readOnly
       open = 'calendar'
       onToggle = @onToggle
-
-    value = @props.value

       <input type='text' disabled className={classes.join(' ')} />

{@props.label}

       <DateTimePicker onClick={@clickHandler}
         onFocus={@expandCalendar}
+        onBlur={@onBlur}
         id={@props.id}
         format='MMM dd, yyyy'
         time={false}


fix human error pointed out by gokmen

fix human error pointed out by gokmen

--- client/app/MainApp/KiteController.coffee
+++ client/app/MainApp/KiteController.coffee
@@ -60,7 +60,7 @@
       options.withArgs or= {}

     # notify "Talking to #{options.kiteName} asking #{options.withArgs.command}, #{options.toDo}"
-    return warn "you are not logged in."  if KD.whoami()?.tellKite?
+    return warn "you are not logged in."  unless KD.whoami()?.tellKite?

     KD.whoami().tellKite options, (err, response)=>
       @parseKiteResponse {err, response}, options, callback


fix typo

fix typo

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -2187,7 +2187,7 @@
                 throw new Error("import sources must be strings") if not is_string_literal(n.source)

                 if isInternalModule(n.source.value)
-                        n.source_path = create_string_literal(n.source_value)
+                        n.source_path = create_string_literal(n.source.value)
                         return n

                 if n.source[0] is "/"


reorder the slotref lookup and the store instruction to after we've evaluated the rhs

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -1772,13 +1772,13 @@
                 ir.createLoad slot_ref, "slot_ref_load"

         handleSetSlot: (exp, opencode) ->
-                slotref = @handleSlotRef exp, opencode
                 if exp.arguments.length is 4
                         new_slot_val = @visit exp.arguments[3]
-                        ir.createStore new_slot_val, slotref
                 else
                         new_slot_val = @visit exp.arguments[2]
-                        ir.createStore new_slot_val, slotref
+
+                slotref = @handleSlotRef exp, opencode
+                ir.createStore new_slot_val, slotref
                 new_slot_val

         handleSlotRef: (exp, opencode) ->


clean up comment

clean up comment

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -694,7 +694,7 @@
                 insertFunc = body_bb.parent

                 # now pull the named parameters from our args array for the ones that were passed in.
-                # any arg that isn't specified
+                # any arg that isn't specified isn't pulled in, and is only accessible via the arguments object.
                 if n.params.length > BUILTIN_PARAMS.length
                         load_argc = @createLoad allocas[2], "argc" # FIXME, magic number alert


Remove debug logging

Remove debug logging

--- app/partials/io-status.cjsx
+++ app/partials/io-status.cjsx
@@ -24,28 +24,22 @@
     writes: @props.target.writes

   componentDidMount: ->
-    console.log 'IOStatus componentDidMount'
     @props.target.listen 'change', @handleTargetChange

   componentWillReceiveProps: (nextProps) ->
-    console.log 'IOStatus componentWillReceiveProps'
     @props.target.stopListening 'change', @handleTargetChange
     nextProps.target.listen 'change', @handleTargetChange

   componentWillUnmount: ->
-    console.log 'IOStatus componentWillUnmount'
     @props.target.stopListening 'change', @handleTargetChange

   handleTargetChange: ->
     setTimeout => # TODO: I have no idea why this is necessary.
       {reads, writes} = @props.target
-      console.log 'IOStatus handleTargetChange', this, {reads, writes}
       @setState {reads, writes}

   render: ->
     {reads, writes} = @state
-
-    console.log 'IOStatus render', {reads, writes}

     rootStyle =
       pointerEvents: 'none'


Fix context menu docs Closes #4963

Fix context menu docs Closes #4963

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -19,7 +19,7 @@
 #
 # ## Context Menu CSON Format
 #
-# # ```coffee
+# ```coffee
 # 'atom-workspace': [{label: 'Help', command: 'application:open-documentation'}]
 # 'atom-text-editor': [{
 #   label: 'History',


Reload git-plus commands when a beta command is toggled

--- lib/git-plus.coffee
+++ lib/git-plus.coffee
@@ -126,11 +126,6 @@
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:show', -> git.getRepo().then((repo) -> GitShow(repo))
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:log', -> git.getRepo().then((repo) -> GitLog(repo))
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:log-current-file', -> git.getRepo().then((repo) -> GitLog(repo, onlyCurrentFile: true))
-      if atom.config.get('git-plus.experimental') and atom.config.get('git-plus.stageFilesBeta')
-        @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stage-files', -> git.getRepo().then(GitStageFilesBeta)
-      else
-        @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stage-files', -> git.getRepo().then((repo) -> GitStageFiles(repo))
-      @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:unstage-files', -> git.getRepo().then((repo) -> GitUnstageFiles(repo))
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stage-hunk', -> git.getRepo().then((repo) -> GitStageHunk(repo))
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stash-save', -> git.getRepo().then((repo) -> GitStashSave(repo))
       @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stash-save-message', -> git.getRepo().then((repo) -> GitStashSaveMessage(repo))
@@ -156,6 +151,14 @@
       @subscriptions.add atom.commands.add '.tree-view', 'git-plus-context:unstage-file', -> GitUnstageFileContext()
       @subscriptions.add atom.config.observe 'git-plus.syntaxHighlighting', setDiffGrammar
       @subscriptions.add atom.config.observe 'git-plus.wordDiff', setDiffGrammar
+      if atom.config.get('git-plus.experimental') and atom.config.get('git-plus.stageFilesBeta')
+        @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stage-files', -> git.getRepo().then(GitStageFilesBeta)
+      else
+        @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:unstage-files', -> git.getRepo().then(GitUnstageFiles)
+        @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:stage-files', -> git.getRepo().then(GitStageFiles)
+      @subscriptions.add atom.config.onDidChange 'git-plus.stageFilesBeta', =>
+        @subscriptions.dispose()
+        @activate()

   deactivate: ->
     @subscriptions.dispose()


:lipstick:

:lipstick:

--- lib/git-view.coffee
+++ lib/git-view.coffee
@@ -55,6 +55,16 @@

     @update()

+  subscribeToRepo: ->
+    @statusChangedSubscription?.dispose()
+    @statusesChangedSubscription?.dispose()
+
+    if repo = atom.project.getRepo()
+      @statusChangedSubscription = repo.onDidChangeStatus ({path, status}) =>
+        @update() if path is @getActiveItemPath()
+      @statusesChangedSubscription = repo.onDidChangeStatuses =>
+        @update()
+
   destroy: ->
     @activeItemSubscription.dispose()
     @projectPathSubscription.off()
@@ -68,16 +78,6 @@

   getActiveItem: ->
     atom.workspace.getActivePaneItem()
-
-  subscribeToRepo: ->
-    @statusChangedSubscription?.dispose()
-    @statusesChangedSubscription?.dispose()
-
-    if repo = atom.project.getRepo()
-      @statusChangedSubscription = repo.onDidChangeStatus ({path, status}) =>
-        @update() if path is @getActiveItemPath()
-      @statusesChangedSubscription = repo.onDidChangeStatuses =>
-        @update()

   update: =>
     @updateBranchText()


FIX: Reset default state of `showHeader` to true

--- src/app/common/services/header-service.coffee
+++ src/app/common/services/header-service.coffee
@@ -5,6 +5,8 @@
 .factory("headerService", ($rootScope) ->
   # Internal object that stores the menus
   $rootScope.headerMenuData = [ ]
+  # Internal object that stores the visbility of the header
+  $rootScope.showHeader = true

   headerService = {}


Registering complete

Registering complete

--- bot.iced
+++ bot.iced
@@ -14,7 +14,7 @@
 if err
 	console.error err
 	process.exit 1
-await db.collection "users", defer(err, collection)
+await db.collection "users", defer(err, Users_collection)
 if err
 	console.error err
 	process.exit 1
@@ -35,7 +35,14 @@
 	bot.sendMessage DogeTipGroupID, "dogetippingbot is back online"
 # Functions
 getNameFromID = (steamID) ->
-	bot.users[steamID].playerName
+	name = bot.users[steamID]?.playerName
+checkIfRegistered = (steamID, cb) ->
+	await Users_collection.findOne {id: steamID}, defer(err, user)
+	if err
+		console.error err
+		cb undefined
+	registered = if user then true else false
+	cb registered

 bot.on "chatMsg", (sourceID, message, type, chatterID) ->
 	if message is "Me"
@@ -47,9 +54,25 @@
 	switch message.split(" ")[0] # The command part
 		when "+register"
 			name = getNameFromID chatterID
-			bot.sendMessage chatterID, "Welcome #{name}"
-			bot.sendMessage chatterID, "Reply '+add <AMOUNT> doge' to add dogecoins to your account"
-			bot.sendMessage chatterID, "Tip users with '+tip <STEAM NAME> <AMOUNT> doge'"
+
+			await checkIfRegistered chatterID, defer(registered)
+			if registered is undefined
+				return bot.sendMessage chatterID, "The database ran into an error"
+			if registered
+				return bot.sendMessage chatterID, "You've already registered"
+			else
+				userEntry =
+					id: chatterID
+					name: name
+					funds: 0
+					history: []
+				await Users_collection.insert userEntry, {w:1}, defer(err)
+				if err
+					console.error "DBError: #{err}"
+					return bot.sendMessage chatterID, "DBError: #{err}"
+				bot.sendMessage chatterID, "Welcome #{name}"
+				bot.sendMessage chatterID, "Reply '+add <AMOUNT> doge' to add dogecoins to your account"
+				bot.sendMessage chatterID, "Tip users with '+tip <STEAM NAME> <AMOUNT> doge'"
 		when "+add"
 			previousAdds = no
 			for payment in pendingPayments


Kill associated processes on will-quit

Kill associated processes on will-quit

Previously processes were only killed when the window was destroyed.

This affected `atom --wait` when using cmd-q to quit the app.

Closes #1679

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -198,8 +198,13 @@
     app.on 'window-all-closed', ->
       app.quit() if process.platform is 'win32'

-    app.on 'will-quit', => @deleteSocketFile()
-    app.on 'will-exit', => @deleteSocketFile()
+    app.on 'will-quit', =>
+      @killAllProcesses()
+      @deleteSocketFile()
+
+    app.on 'will-exit', =>
+      @killAllProcesses()
+      @deleteSocketFile()

     app.on 'open-file', (event, pathToOpen) =>
       event.preventDefault()
@@ -354,13 +359,25 @@
       @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow

     openedWindow.browserWindow.on 'destroyed', =>
-      for pid, trackedWindow of @pidsToOpenWindows when trackedWindow is openedWindow
-        try
-          process.kill(pid)
-        catch error
-          if error.code isnt 'ESRCH'
-            console.log("Killing process #{pid} failed: #{error.code}")
-        delete @pidsToOpenWindows[pid]
+      @killProcessForWindow(openedWindow)
+
+  # Kill all processes associated with opened windows.
+  killAllProcesses: ->
+    @killProcess(pid) for pid of @pidsToOpenWindows
+
+  # Kill process associated with the given opened window.
+  killProcessForWindow: (openedWindow) ->
+    for pid, trackedWindow of @pidsToOpenWindows
+      @killProcess(pid) if trackedWindow is openedWindow
+
+  # Kill the process with the given pid.
+  killProcess: (pid) ->
+    try
+      process.kill(pid)
+    catch error
+      if error.code isnt 'ESRCH'
+        console.log("Killing process #{pid} failed: #{error.code}")
+    delete @pidsToOpenWindows[pid]

   # Open an atom:// url.
   #


Namespacing accordion state by ID

Namespacing accordion state by ID

--- app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee
@@ -9,16 +9,18 @@

   $scope.CheckoutFormState = CheckoutFormState
   #$scope.order = Order.order
-  $scope.accordion = {}
+  $scope.accordion = {user: true}

   $scope.show = (name)->
     $scope.accordion[name] = true

-  storage.bind $scope, "accordion.user", { defaultValue: true}
-  storage.bind $scope, "accordion.details"
-  storage.bind $scope, "accordion.billing"
-  storage.bind $scope, "accordion.shipping"
-  storage.bind $scope, "accordion.payment"
+  storage.bind $scope, "accordion", {storeName: "accordion_#{$scope.order.id}"}
+  console.log "==============================="
+  console.log "Order.order.id"
+
+  #storage.bind $scope, "accordion.billing", {storeName: "billing_#{Order.order.id}"}
+  #storage.bind $scope, "accordion.shipping", {storeName: "shipping_#{Order.order.id}"}
+  #storage.bind $scope, "accordion.payment", {storeName: "payment_#{Order.order.id}"}

   storage.bind $scope, "CheckoutFormState.ship_address_same_as_billing", { defaultValue: true}


:art: new utility method eachSelection which wrap select() and selection iteration

And include lots of refactoring.

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -76,11 +76,11 @@
       markerBySelections[selection.id] = marker
     markerBySelections

-  flash: (range) ->
+  flash: (range, fn=null) ->
     marker = @editor.markBufferRange range,
       invalidate: 'never',
       persistent: false
-
+    fn?()
     @editor.decorateMarker marker,
       type: 'highlight'
       class: 'vim-mode-flash'
@@ -89,31 +89,14 @@
       marker.destroy()
     , settings.get('flashOnOperateDurationMilliSeconds')

-  withFlashing: (callback) ->
-    unless settings.get('flashOnOperate')
-      callback()
-      return
-
-    markerBySelections = @markSelections()
-    callback()
-    for selection in @editor.getSelections()
-      @editor.decorateMarker markerBySelections[selection.id],
-        type: 'highlight'
-        class: 'vim-mode-flash'
-
-    # Ensure destroy all marker
-    setTimeout  ->
-      marker.destroy() for __, marker of markerBySelections
-    , settings.get('flashOnOperateDurationMilliSeconds')
-
-  selectTarget: (callback) ->
-    if @target.select()
-      callback()
-
-  selectTargetWithFlashing: (callback) ->
-    @selectTarget =>
-      @withFlashing ->
-        callback()
+  eachSelection: (fn) ->
+    return unless @target.select()
+    @editor.transact =>
+      for s in @editor.getSelections()
+        if @flashTarget and settings.get('flashOnOperate')
+          @flash s.getBufferRange(), -> fn(s)
+        else
+          fn(s)

 class Select extends Operator
   @extend()
@@ -153,14 +136,10 @@
   hoverIcon: ':delete:'

   execute: ->
-    @selectTarget =>
-      @setTextToRegister @editor.getSelectedText()
-      @editor.transact =>
-        for selection in @editor.getSelections()
-          selection.deleteSelectedText()
-      if @target.isLinewise?()
-        for cursor in @editor.getCursors()
-          cursor.skipLeadingWhitespace()
+    @eachSelection (s) =>
+      @setTextToRegister s.getText() if s.isLastSelection()
+      s.deleteSelectedText()
+      s.cursor.skipLeadingWhitespace() if @target.isLinewise?()
     @vimState.activateNormalMode()

 class DeleteRight extends Delete
@@ -184,16 +163,16 @@
 class TransformString extends Operator
   @extend()
   adjustCursor: true
+  flashTarget: true

   # [FIXME] duplicate to Yank, need to consolidate as like adjustCursor().
   execute: ->
     if @target.isLinewise?() or settings.get('stayOnTransformString')
       points = _.pluck(@editor.getSelectedBufferRanges(), 'start')
-    @selectTargetWithFlashing =>
-      for selection in @editor.getSelections()
-        range = selection.insertText @getNewText(selection.getText())
-        if @adjustCursor
-          selection.cursor.setBufferPosition(points?.shift() ? range.start)
+    @eachSelection (s) =>
+      range = s.insertText @getNewText(s.getText())
+      if @adjustCursor
+        s.cursor.setBufferPosition(points?.shift() ? range.start)
     @vimState.activateNormalMode()

 class ToggleCase extends TransformString
@@ -335,14 +314,14 @@
   @extend()
   hoverText: ':clipboard:'
   hoverIcon: ':yank:'
+  flashTarget: true
   execute: ->
     if @target.isLinewise?()
       points = (s.getBufferRange().start for s in @editor.getSelections())
-    @selectTargetWithFlashing =>
-      @setTextToRegister @editor.getSelectedText()
-      for selection in @editor.getSelections()
-        point = points?.shift() ? selection.getBufferRange().start
-        selection.cursor.setBufferPosition point
+    @eachSelection (s) =>
+      @setTextToRegister s.getText() if s.isLastSelection()
+      point = points?.shift() ? s.getBufferRange().start
+      s.cursor.setBufferPosition point
     @vimState.activateNormalMode()

 class YankLine extends Yank
@@ -414,33 +393,33 @@
 class Indent extends Operator
   @extend()
   hoverText: ':point_right:'
-  # hoverIcon: ':indent:'
   hoverIcon: ':indent:'
-  execute: ->
-    @target.select()
-    startRow = @editor.getSelectedBufferRange().start.row
-    @indent()
-    @editor.setCursorBufferPosition([startRow, 0])
-    @editor.moveToFirstCharacterOfLine()
-    @vimState.activateNormalMode()
-
-  indent: ->
-    @editor.indentSelectedRows()
+  flashTarget: true
+  execute: ->
+    @eachSelection (s) =>
+      startRow = s.getBufferRange().start.row
+      @indent(s)
+      s.cursor.setBufferPosition([startRow, 0])
+      s.cursor.moveToFirstCharacterOfLine()
+    @vimState.activateNormalMode()
+
+  indent: (s) ->
+    s.indentSelectedRows()

 class Outdent extends Indent
   @extend()
   hoverText: ':point_left:'
   hoverIcon: ':outdent:'

-  indent: ->
-    @editor.outdentSelectedRows()
+  indent: (s) ->
+    s.outdentSelectedRows()

 class AutoIndent extends Indent
   @extend()
   hoverText: ':open_hands:'
   hoverIcon: ':auto-indent:'
-  indent: ->
-    @editor.autoIndentSelectedRows()
+  indent: (s) ->
+    s.autoIndentSelectedRows()

 # Put
 # -------------------------
@@ -498,25 +477,25 @@
   @extend()
   hoverText: ':pencil:'
   hoverIcon: ':replace-with-register:'
-  execute: ->
-    @selectTargetWithFlashing =>
-      points = _.pluck(@editor.getSelectedBufferRanges(), 'start')
-      @editor.replaceSelectedText {}, (text) =>
-        @vimState.register.get().text ? text
-      ranges = (new Range(p, p) for p in points)
-      @editor.setSelectedBufferRanges(ranges)
+  flashTarget: true
+  execute: ->
+    @eachSelection (s) =>
+      range = s.getBufferRange()
+      newText = @vimState.register.get().text ? s.getText()
+      s.deleteSelectedText()
+      s.insertText(newText)
+      s.cursor.setBufferPosition(range.start)
     @vimState.activateNormalMode()

 class ToggleLineComments extends Operator
   @extend()
   hoverText: ':mute:'
   hoverIcon: ':toggle-line-comment:'
+  # flashTarget: true
   execute: ->
     markerByCursor = @markCursorBufferPositions()
-    @selectTargetWithFlashing =>
-      @editor.transact =>
-        for s in @editor.getSelections()
-          s.toggleLineComments()
+    @eachSelection (s) ->
+      s.toggleLineComments()
     @restoreMarkedCursorPositions markerByCursor
     @vimState.activateNormalMode()

@@ -619,7 +598,7 @@

     @target.setOptions? excludeWhitespace: true

-    @selectTarget =>
+    if @target.select()
       @setTextToRegister @editor.getSelectedText()
       if @target.isLinewise?() and not @typedText?
         for selection in @editor.getSelections()
@@ -732,7 +711,7 @@

     @editor.transact =>
       if @target?
-        @selectTarget =>
+        if @target.select()
           @editor.replaceSelectedText null, (text) =>
             text.replace(/./g, @input)
           for selection in @editor.getSelections()


:gift: general insertion operator MotionByTarget

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -1111,31 +1111,6 @@
       @setCheckpoint('insert')
       @vimState.activate('insert', @finalSubmode)

-class InsertAtLastInsert extends ActivateInsertMode
-  @extend()
-  execute: ->
-    if (point = @vimState.mark.get('^'))
-      @editor.setCursorBufferPosition(point)
-      @editor.scrollToCursorPosition({center: true})
-    super
-
-class InsertAtStartOfSelection extends ActivateInsertMode
-  @extend()
-  which: 'start'
-  execute: ->
-    if @isMode('visual', 'blockwise')
-      @getBlockwiseSelections().forEach (bs) =>
-        bs.removeEmptySelections()
-        bs.setPositionForSelections(@which)
-    else
-      for selection in @editor.getSelections()
-        swrap(selection).setBufferPositionTo('start')
-    super
-
-class InsertAtEndOfSelection extends InsertAtStartOfSelection
-  @extend()
-  which: 'end'
-
 class ActivateReplaceMode extends ActivateInsertMode
   @extend()
   finalSubmode: 'replace'
@@ -1165,47 +1140,82 @@
     @editor.moveToFirstCharacterOfLine()
     super

-class InsertByMotion extends ActivateInsertMode
-  @extend()
-  @description: "Move by specified motion then enter insert-mode(`i`)"
+class InsertAtLastInsert extends ActivateInsertMode
+  @extend()
+  execute: ->
+    if (point = @vimState.mark.get('^'))
+      @editor.setCursorBufferPosition(point)
+      @editor.scrollToCursorPosition({center: true})
+    super
+
+class InsertAboveWithNewline extends ActivateInsertMode
+  @extend()
+  execute: ->
+    @insertNewline()
+    super
+
+  insertNewline: ->
+    @editor.insertNewlineAbove()
+
+  repeatInsert: (selection, text) ->
+    selection.insertText(text.trimLeft(), autoIndent: true)
+
+class InsertBelowWithNewline extends InsertAboveWithNewline
+  @extend()
+  insertNewline: ->
+    @editor.insertNewlineBelow()
+
+# Advanced Insertion
+# -------------------------
+class InsertByTarget extends ActivateInsertMode
+  @extend(false)
   requireTarget: true
-  execute: ->
-    if @target.isMotion()
-      @target.execute()
-    if @instanceof('InsertAfterByMotion')
-      moveCursorRight(cursor) for cursor in @editor.getCursors()
-    super
-
-class InsertAfterByMotion extends InsertByMotion
-  @description: "Move by specified motion then enter insert-mode(`a`)"
-  @extend()
-
-class InsertAtPreviousFoldStart extends InsertByMotion
+  which: null # one of ['start', 'end', 'head', 'tail']
+  execute: ->
+    @selectTarget()
+    if @isMode('visual', 'blockwise')
+      @getBlockwiseSelections().forEach (bs) =>
+        bs.removeEmptySelections()
+        bs.setPositionForSelections(@which)
+    else
+      for selection in @editor.getSelections()
+        swrap(selection).setBufferPositionTo(@which)
+    super
+
+class InsertAtStartOfTarget extends InsertByTarget
+  @extend()
+  which: 'start'
+
+# Alias for backward compatibility
+class InsertAtStartOfSelection extends InsertAtStartOfTarget
+  @extend()
+
+class InsertAtEndOfTarget extends InsertByTarget
+  @extend()
+  which: 'end'
+
+# Alias for backward compatibility
+class InsertAtEndOfSelection extends InsertAtEndOfTarget
+  @extend()
+
+class InsertAtHeadOfTarget extends InsertByTarget
+  @extend()
+  which: 'head'
+
+class InsertAtTailOfTarget extends InsertByTarget
+  @extend()
+  which: 'tail'
+
+class InsertAtPreviousFoldStart extends InsertAtHeadOfTarget
   @extend()
   @description: "Move to previous fold start then enter insert-mode"
   target: 'MoveToPreviousFoldStart'

-class InsertAtNextFoldStart extends InsertAtPreviousFoldStart
+class InsertAtNextFoldStart extends InsertAtHeadOfTarget
   @extend()
   @description: "Move to next fold start then enter insert-mode"
   target: 'MoveToNextFoldStart'

-class InsertAboveWithNewline extends ActivateInsertMode
-  @extend()
-  execute: ->
-    @insertNewline()
-    super
-
-  insertNewline: ->
-    @editor.insertNewlineAbove()
-
-  repeatInsert: (selection, text) ->
-    selection.insertText(text.trimLeft(), autoIndent: true)
-
-class InsertBelowWithNewline extends InsertAboveWithNewline
-  @extend()
-  insertNewline: ->
-    @editor.insertNewlineBelow()
 # -------------------------
 class Change extends ActivateInsertMode
   @extend()


:gift: new AddSelection operator #245

:gift: new AddSelection operator #245

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -993,6 +993,27 @@

     @activateMode('normal')

+class AddSelection extends Operator
+  @extend()
+
+  # word
+  execute: ->
+    word = @editor.getSelectedText() or @editor.getWordUnderCursor()
+    return if word is ''
+    return unless @selectTarget()
+
+    ranges = []
+    pattern = ///#{_.escapeRegExp(word)}///g
+    for selection in @editor.getSelections()
+      scanRange = selection.getBufferRange()
+      @editor.scanInBufferRange pattern, scanRange, ({range}) ->
+        ranges.push(range)
+
+    if ranges.length
+      @editor.setSelectedBufferRanges(ranges)
+      unless @isMode('visual', 'characterwise')
+        @activateMode('visual', 'characterwise')
+
 # Insert entering operation
 # -------------------------
 class ActivateInsertMode extends Operator


fix(iscroll): don't destroy iscroll until transition ends

fixes #34

--- modules/scripts/directives/iscroll.coffee
+++ modules/scripts/directives/iscroll.coffee
@@ -48,12 +48,5 @@
     scope.$on 'bpRefreshIScrollInstances', ->
       scope.getIScroll()?.refresh?()

-    scope.$on '$destroy', -> iscroll.destroy()
-
-    scope.$on '$stateChangeStart', ->
+    element.on '$destroy', ->
       iscroll.destroy()
-      element.removeAttr 'bp-iscroll'
-      element.find('bp-iscroll-wrapper').css
-        position: 'static'
-        transform: ''
-        transition: ''


Fix for big files with a lot of custom vars and functions

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -28,6 +28,7 @@
     if !force
       return if @userVars? and @lastPath == editor.getPath()

+    @compileData = ''
     phpEx = 'get_user_all.php'

     proc = exec.spawn 'php', [__dirname + '/php/' + phpEx]
@@ -36,12 +37,19 @@
     proc.stdin.end()

     proc.stdout.on 'data', (data) =>
-      @userSuggestions = JSON.parse('' + data)
-      @lastPath = editor.getPath()
-      # @lastTimeEx = new Date()
+      @compileData = @compileData + data

     proc.stderr.on 'data', (data) ->
       console.log 'err: ' + data
+
+    proc.on 'close', (code) =>
+      try
+        @userSuggestions = JSON.parse(@compileData)
+      catch error
+        # console.log error
+
+      @lastPath = editor.getPath()
+      # @lastTimeEx = new Date()

   # Required: Return a promise, an array of suggestions, or null.
   # {editor, bufferPosition, scopeDescriptor, prefix}
@@ -57,7 +65,6 @@
       if @notShowAutocomplete(request)
         resolve([])
       else if @isAll(request)
-        console.log 'teste'
         @execute(request, typeEx)
         resolve(@getAllCompletions(request))
       else if @isVariable(request)


Fixes #12 #13 #19 #23. "Atom.Object.defineProperty.get is deprecated."

The same as PR #14. (Thanks @maxkorp).

--- lib/refactor.coffee
+++ lib/refactor.coffee
@@ -21,9 +21,9 @@
     @moduleManager = new ModuleManager
     @watchers = []

-    atom.workspaceView.eachEditorView @onCreated
-    atom.workspaceView.command @renameCommand, @onRename
-    atom.workspaceView.command @doneCommand, @onDone
+    atom.workspace.observeTextEditors @onCreated
+    atom.commands.add 'atom-text-editor', @renameCommand, @onRename
+    atom.commands.add 'atom-text-editor', @doneCommand, @onDone

   deactivate: ->
     @moduleManager.destruct()


Do not save project specific settings

Do not save project specific settings

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -14,6 +14,7 @@
     _ = require 'underscore-plus'
     flatSettings = {}
     options = if scope then {scopeSelector: scope} else {}
+    options.save = false

     @flatten flatSettings, settings
     for setting, value of flatSettings


Fix Settings failure in Firefox iframes.

This was failing in iframes in Firefox (causing all sorts of other stuff
to fail).

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -11,7 +11,8 @@
 # In all cases except Settings.defaults, values are stored as jsonified strings.

 # If the current frame is the Vomnibar or the HUD, then we'll need our Chrome stubs for the tests.
-window.chrome ?= window.top?.chrome
+# We use "try" because this fails within iframes on Firefox (where failure doesn't actually matter).
+try window.chrome ?= window.top?.chrome

 storageArea = if chrome.storage.sync? then "sync" else "local"


:bug: Fix reset of repo subscriptions

:bug: Fix reset of repo subscriptions

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -202,10 +202,8 @@
     return unless repo?

     # Remove previous repo subscriptions.
-    if @repoSubscriptions?
-      @repoSubscriptions.dispose()
-    else
-      @repoSubscriptions = new CompositeDisposable()
+    @repoSubscriptions?.dispose()
+    @repoSubscriptions = new CompositeDisposable()

     @repoSubscriptions.add repo.onDidChangeStatus (event) =>
       @updateVcsStatus(repo, event.pathStatus) if event.path is @path


Addresses #1232 in the web UI -- still needs to be done in CLI

--- admin/static/coffee/namespaces/shards.coffee
+++ admin/static/coffee/namespaces/shards.coffee
@@ -42,7 +42,7 @@
                 error_msg = "The number of shards must be an integer."
                 @display_msg error_msg
                 return
-
+
             if new_num_shards < 1 or new_num_shards > MAX_SHARD_COUNT
                 error_msg = "The number of shards must be beteen 1 and " + MAX_SHARD_COUNT + "."
                 @display_msg error_msg
@@ -79,7 +79,7 @@
                 if distr_keys.length-i <= new_num_shards-split_points.length # We don't add 1 because there is null
                     split_points.push key
                     continue
-
+
                 # Else we check if we have enough keys
                 if current_count >= rows_per_shard*split_points.length
                     split_points.push key
@@ -124,7 +124,7 @@

                 $.ajax
                      processData: false
-                     url: "/ajax/semilattice/#{@model.attributes.protocol}_namespaces/#{@model.get('id')}"
+                     url: "/ajax/semilattice/#{@model.attributes.protocol}_namespaces/#{@model.get('id')}?prefer_distribution=1"
                      type: 'POST'
                      contentType: 'application/json'
                      data: JSON.stringify(data)
@@ -225,7 +225,7 @@
         check_has_unsatisfiable_goals: =>
             if @should_be_hidden
                 should_be_hidden_new = false
-                for issue in issues.models
+                for issue in issues.models
                     if issue.get('type') is 'UNSATISFIABLE_GOALS' and issue.get('namespace_id') is @model.get('id') # If unsatisfiable goals, the user should not change shards
                         should_be_hidden_new = true
                         break
@@ -304,7 +304,7 @@

                 # size of all bars and white space between bars
                 width_of_all_bars = bar_width*shards.length + margin_bar*(shards.length-1)
-
+
                 # Update the margin on the right of the y-axis
                 margin_width_inner = Math.floor((svg_width-margin_width*2-width_of_all_bars)/2)

@@ -340,7 +340,7 @@
                         result += '<br />'+d.num_keys+' keys'
                         return result
                     )
-
+

                 # Create axes
                 extra_data = []
@@ -382,7 +382,7 @@
                     .attr('y', (d) -> return d.y)
                     .attr('text-anchor', (d) -> return d.anchor)
                     .text((d) -> return d.string)
-
+
                 # Create ticks
                 # First the tick's background
                 svg.selectAll('.cache').data(y.ticks(5))


Recreate client on hangouts

Recreate client on hangouts

--- lib/uploader.coffee
+++ lib/uploader.coffee
@@ -22,7 +22,7 @@

     if not @objectParams.Bucket then throw new Error "Bucket must be given"

-    @client = new aws.S3().client
+    @createNewClient()

     @initiated       = false
     @receivedAllData = false
@@ -39,6 +39,8 @@
     @handleStream stream
     process.nextTick => @initiateTransfer()

+  createNewClient: ->
+    @client = new aws.S3().client

   initiateTransfer: ->
     @client.createMultipartUpload @objectParams, (err, data) =>
@@ -114,7 +116,13 @@

           if err
             if not callbackCalled
-              next err
+              if err.code is 'RequestTimeout'
+                # create new client as old one died..and try again in next iteration
+                @createNewClient()
+                # do not propagate err as that whould kill rest of uploads
+                next null
+              else
+                next err
             else
               console.error 'This callback was already called, WTF; chunk', chunk
             @emit 'error', err


JS fix with bento theme

JS fix with bento theme

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -14,6 +14,7 @@
 #= require jquery_ujs
 #= require twitter/bootstrap
 #= require bootstrap-datepicker
+#= require bootstrap
 #= require cocoon
 #


Only filter asynchronously if option remote is true.

We need to update the results right away when we do bulk assignment.

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -37,20 +37,20 @@
       if keyCode is 13
         return false

-      clearTimeout timeout
-      timeout = setTimeout =>
-        blur_field = @shouldBlur keyCode
-        search_text = @input.val()
-
-        if blur_field and @filterInputBlur
-          @input.blur()
-
-        if @options.remote
-          @options.query search_text, (data) =>
+      # Only filter asynchronously only if option remote is set
+      if @options.remote
+        clearTimeout timeout
+        timeout = setTimeout =>
+          blur_field = @shouldBlur keyCode
+
+          if blur_field and @filterInputBlur
+            @input.blur()
+
+          @options.query @input.val(), (data) =>
             @options.callback(data)
-        else
-          @filter search_text
-      , 250
+        , 250
+      else
+        @filter @input.val()

   shouldBlur: (keyCode) ->
     return BLUR_KEYCODES.indexOf(keyCode) >= 0


track dirty status in save-on-change

track dirty status in save-on-change

--- app/assets/javascripts/save-on-change.coffee
+++ app/assets/javascripts/save-on-change.coffee
@@ -1,5 +1,6 @@

 scheduled_jobs     = {}
+previous_values    = {}
 update_interval_s  = 0.6
 saveTimer          = null

@@ -26,9 +27,20 @@
   $("#save").animate({'opacity': '1.0'}, 'fast')

 saveElement = (elem) ->
+    form = $(elem).parents('form:first')
+    data = $(elem).parents('form:first').serialize()
+    last_data = previous_values[elem]
+    return if last_data == data
     showSaving()
-    $(elem).parents('form:first').submit()
-    unschedule elem
+    $.ajax({
+      type: "POST",
+      url: form.attr( 'action' ),
+      data: form.serialize(),
+      success:  (response) ->
+        previous_values[elem] = data
+        console.log response
+    })
+    # unschedule elem

 # remove events scheduled for elem


hide button automaticaly

hide button automaticaly

--- app/assets/javascripts/transaction.js.coffee
+++ app/assets/javascripts/transaction.js.coffee
@@ -14,7 +14,7 @@
       dataType: "script"
       success: ->
         $(".loading").hide()
-        if $("tbody").children("tr").length >= 35
+        if $("tbody").children("tr").length >= $("#total").text()
           $("#more_transactions").hide()
         else
           $("#more_transactions").show()


Fix typo

Fix typo

--- app/coffee/modules/common/tag-manager.coffee
+++ app/coffee/modules/common/tag-manager.coffee
@@ -34,7 +34,7 @@

     initialize: ->
         if not @.accountId
-            @log.debug "Tag Manager: no acount id provided. Disabling."
+            @log.debug "Tag Manager: no account id provided. Disabling."
             return

         @.injectTagManager()


Fix missing Promise import

Fix missing Promise import

--- src/client/embeds/deployment.coffee
+++ src/client/embeds/deployment.coffee
@@ -3,6 +3,7 @@
 clone                    = require '../../utils/clone'
 throwErr                 = require '../../utils/throw'
 logger                   = require '../../utils/logger'
+Promise                  = require '../../utils/promises'

 RawDeploymentStrategy    = require './strategies/raw'
 ScopeDeploymentStrategy  = require './strategies/scope'


fix missing require

fix missing require

--- src/client/script-tag-config.coffee
+++ src/client/script-tag-config.coffee
@@ -1,3 +1,5 @@
+_ = require '../utils/lodash'
+
 initParams = {
   'jsUrl'      : 'js-url'
   'platformId' : 'platform-id'


adding loadable mixin to plan shell

adding loadable mixin to plan shell

--- src/components/task-plan/index.cjsx
+++ src/components/task-plan/index.cjsx
@@ -3,6 +3,7 @@
 ConfirmLeaveMixin = require '../confirm-leave-mixin'
 {HomeworkPlan} = require './homework'
 {ReadingPlan} = require './reading'
+LoadableItem = require '../loadable-item'

 {TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'

@@ -14,7 +15,7 @@
   type = PLAN_TYPES[typeName]

 PlanShell = React.createClass
-  mixins: [LoadableMixin, ConfirmLeaveMixin]
+  mixins: [ConfirmLeaveMixin]

   contextTypes:
     router: React.PropTypes.func
@@ -63,10 +64,16 @@
     else
       @setState({})

-  renderLoaded: ->
+  render: ->
     id = @getId()
     {courseId} = @context.router.getCurrentParams()
     Type = @getType()
-    <Type id={id} courseId={courseId} />
+
+    <LoadableItem
+      id={id}
+      store={TaskPlanStore}
+      actions={TaskPlanActions}
+      renderItem={-> <Type id={id} courseId={courseId} />}
+    />

 module.exports = {PlanShell, ReadingPlan, HomeworkPlan}


inbox loadmore fixed

inbox loadmore fixed

--- client/app/Applications/Inbox.kdapplication/Views/InboxMessagesList.coffee
+++ client/app/Applications/Inbox.kdapplication/Views/InboxMessagesList.coffee
@@ -99,7 +99,3 @@

   partial:(data)->
     "Load more messages..."
-
-  click:(event)->
-    controller = @getDelegate()
-    controller.continueLoadingMessages @


Prevent redundant save dialogs

Prevent redundant save dialogs

With the changes in PR#65, some code paths resulted in the presentation of multiple save dialogs. This occurred because in CloudFileManagerClient the saveFileDialog() and saveFileAsDialog() methods call _dialogSave() once the dialog is dismissed. The _dialogSave() method calls saveFile(), which optionally brings up the save dialog. The changes in PR#65 triggered this latent issue. The fix here is to pull out the actual saving code from saveFile() into a saveFileNoDialog() method. With this change, saveFile() calls saveFileDialog() or saveFileNoDialog() as appropriate, and _dialogSave() calls saveFileNoDialog(), thus eliminating the potential for redundant dialogs.

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -337,26 +337,29 @@
   saveFile: (stringContent, metadata, callback = null) ->
     # must be able to 'resave' to save silently, i.e. without save dialog
     if metadata?.provider?.can('resave', metadata)
-      @_setState
-        saving: metadata
-      currentContent = @_createOrUpdateCurrentContent stringContent, metadata
-      metadata.provider.save currentContent, metadata, (err, statusCode) =>
-        if err
-          # disable autosave on save failure; clear "Saving..." message
-          metadata.autoSaveDisabled = true
-          @_setState { metadata: metadata, saving: null }
-          if statusCode is 403
-            return @confirmAuthorizeAndSave stringContent, callback
-          else
-            return @alert(err)
-        if @state.metadata isnt metadata
-          @_closeCurrentFile()
-        # reenable autosave on save success if this isn't a local file save
-        delete metadata.autoSaveDisabled if metadata.autoSaveDisabled?
-        @_fileChanged 'savedFile', currentContent, metadata, {saved: true}, @_getHashParams metadata
-        callback? currentContent, metadata
+      @saveFileNoDialog stringContent, metadata, callback
     else
       @saveFileDialog stringContent, callback
+
+  saveFileNoDialog: (stringContent, metadata, callback = null) ->
+    @_setState
+      saving: metadata
+    currentContent = @_createOrUpdateCurrentContent stringContent, metadata
+    metadata.provider.save currentContent, metadata, (err, statusCode) =>
+      if err
+        # disable autosave on save failure; clear "Saving..." message
+        metadata.autoSaveDisabled = true
+        @_setState { metadata: metadata, saving: null }
+        if statusCode is 403
+          return @confirmAuthorizeAndSave stringContent, callback
+        else
+          return @alert(err)
+      if @state.metadata isnt metadata
+        @_closeCurrentFile()
+      # reenable autosave on save success if this isn't a local file save
+      delete metadata.autoSaveDisabled if metadata.autoSaveDisabled?
+      @_fileChanged 'savedFile', currentContent, metadata, {saved: true}, @_getHashParams metadata
+      callback? currentContent, metadata

   saveFileDialog: (stringContent = null, callback = null) ->
     @_ui.saveFileDialog (metadata) =>
@@ -640,10 +643,10 @@

   _dialogSave: (stringContent, metadata, callback) ->
     if stringContent isnt null
-      @saveFile stringContent, metadata, callback
+      @saveFileNoDialog stringContent, metadata, callback
     else
       @_event 'getContent', { shared: @_sharedMetadata() }, (stringContent) =>
-        @saveFile stringContent, metadata, callback
+        @saveFileNoDialog stringContent, metadata, callback

   _fileChanged: (type, content, metadata, additionalState={}, hashParams=null) ->
     metadata?.overwritable ?= true


Add Module for Game Definition

Add Module for Game Definition

--- src/coffee/game.coffee
+++ src/coffee/game.coffee
@@ -0,0 +1 @@
+angular.module 'gameDefinition', ['qbn.quality', 'qbn.storylet']


Fix cannot read correctly

Fix cannot read correctly

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -20,14 +20,14 @@

     (D.getElementsByTagName('head')[0] || D.getElementsByTagName('body')[0]).appendChild script

-  getConfig = (name, context, extra) ->
+  getConfig = (name, context, after, before) ->
     return '' if context and !config[context]

     value = (config[context] || config)[name]
-    return if value then value + (if extra then extra else '') else ''
+    return if value then (if before then before else '') + value + (if after then after else '') else ''

   getList = ->
-    loadScript 'https://api.github.com/repos/' + getConfig('repo', 'github') + getConfig('path', 'github') + '/contents/?' + getConfig('branch', 'github', '&') + 'callback=Koishumi.showList'
+    loadScript 'https://api.github.com/repos/' + getConfig('repo', 'github') + getConfig('path', 'github') + '/contents/?' + getConfig('branch', 'github', '&', 'ref=') + 'callback=Koishumi.showList'

   showList = (data) ->
     data = data.data


update

update

--- src/experiments/mkts-table-experiments.coffee
+++ src/experiments/mkts-table-experiments.coffee
@@ -148,7 +148,7 @@
       yield from @_walk_cellquad_sides me, cellquad, 'bottom'
       yield return
     else
-      throw new Error "xxx"
+      throw new Error "(MKTS/TABLE 2658) illegal argument for side #{rpr side}"
   for row_nr in [ row_nr_1 .. row_nr_2 ]
     for col_nr in [ col_nr_1 .. col_nr_2 ]
       ### TAINT don't use EXCJSCC directly ###
@@ -159,21 +159,10 @@

 #-----------------------------------------------------------------------------------------------------------
 cellquad = { top: 1, left: 1, bottom: 4, right: 3, tl: 'a1', br: 'c4' }
-for d from @_walk_cellquad_sides null, cellquad, 'left'
-  urge 'left',   jr d
-for d from @_walk_cellquad_sides null, cellquad, 'right'
-  urge 'right',  jr d
-for d from @_walk_cellquad_sides null, cellquad, 'top'
-  urge 'top',    jr d
-for d from @_walk_cellquad_sides null, cellquad, 'bottom'
-  urge 'bottom', jr d
-for d from @_walk_cellquad_sides null, cellquad, '*'
-  urge '*',      jr d

-
-
-
-
-
-
-
+( urge 'cellquad left   ', jr d ) for d from @_walk_cellquad_sides null, cellquad, 'left'
+( urge 'cellquad right  ', jr d ) for d from @_walk_cellquad_sides null, cellquad, 'right'
+( urge 'cellquad top    ', jr d ) for d from @_walk_cellquad_sides null, cellquad, 'top'
+( urge 'cellquad bottom ', jr d ) for d from @_walk_cellquad_sides null, cellquad, 'bottom'
+( urge '*',      jr d ) for d from @_walk_cellquad_sides null, cellquad, '*'
+echo()


Deprecate EditorView::split* methods

Deprecate EditorView::split* methods

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -213,23 +213,23 @@
   unmountComponent: ->
     React.unmountComponentAtNode(@element) if @component.isMounted()

-  # Public: Split the editor view left.
   splitLeft: ->
+    deprecate 'Use Pane::splitLeft instead. To duplicate this editor into the split use: editorView.getPane().splitLeft(editorView.getPane().copyActiveItem())'
     pane = @getPane()
     pane?.splitLeft(pane?.copyActiveItem()).activeView

-  # Public: Split the editor view right.
   splitRight: ->
+    deprecate 'Use Pane::splitRight instead. To duplicate this editor into the split use: editorView.getPane().splitRight(editorView.getPane().copyActiveItem())'
     pane = @getPane()
     pane?.splitRight(pane?.copyActiveItem()).activeView

-  # Public: Split the editor view up.
   splitUp: ->
+    deprecate 'Use Pane::splitUp instead. To duplicate this editor into the split use: editorView.getPane().splitUp(editorView.getPane().copyActiveItem())'
     pane = @getPane()
     pane?.splitUp(pane?.copyActiveItem()).activeView

-  # Public: Split the editor view down.
   splitDown: ->
+    deprecate 'Use Pane::splitDown instead. To duplicate this editor into the split use: editorView.getPane().splitDown(editorView.getPane().copyActiveItem())'
     pane = @getPane()
     pane?.splitDown(pane?.copyActiveItem()).activeView


rename editorLineHeight to lineHeight

rename editorLineHeight to lineHeight

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -44,7 +44,7 @@
   @configDefaults:
     fontFamily: ''
     fontSize: 16
-    editorLineHeight: 1.3
+    lineHeight: 1.3
     showInvisibles: false
     showIndentGuide: false
     showLineNumbers: true
@@ -341,7 +341,7 @@
     @subscribe atom.config.observe 'editor.invisibles', (invisibles) => @setInvisibles(invisibles)
     @subscribe atom.config.observe 'editor.fontSize', (fontSize) => @setFontSize(fontSize)
     @subscribe atom.config.observe 'editor.fontFamily', (fontFamily) => @setFontFamily(fontFamily)
-    @subscribe atom.config.observe 'editor.editorLineHeight', (editorLineHeight) => @setEditorLineHeight(editorLineHeight)
+    @subscribe atom.config.observe 'editor.lineHeight', (lineHeight) => @setLineHeight(lineHeight)

   handleEvents: ->
@@ -752,10 +752,10 @@

   # Public: Sets the line height of the editor
   #
-  # editorLineHeight - A {Number} without a unit suffix identifying the CSS
+  # lineHeight - A {Number} without a unit suffix identifying the CSS
   # `line-height`.
-  setEditorLineHeight: (editorLineHeight) ->
-    @css('line-height', editorLineHeight)
+  setLineHeight: (lineHeight) ->
+    @css('line-height', lineHeight)
     @redraw()

   # Public: Gets the line height for the editor


fix al-src

fix al-src

--- alight/directives.coffee
+++ alight/directives.coffee
@@ -519,7 +519,7 @@
         f$.attr element, 'src', value
         '$scanNoChanges'

-    scope.$watchText name, setter
+    scope.$watchText name, setter,
         init: true


More environment clean-up.

More environment clean-up.

--- src/environment.coffee
+++ src/environment.coffee
@@ -26,25 +26,20 @@
     # @param element {Array} the element to update
     # @param milliseconds {Number} the number of ms that have elapsed
     elapse: (elements, milliseconds) ->
-        for element in elements
-            resultant = @resultant(element, milliseconds)
-            element.applyForce(resultant, milliseconds)
+        (@applyForces(e, milliseconds) for e in elements)
         # HACK: Don't compile the loop into an expression.
         return this

-    # Return the resultant of all environmental forces on the element.
-    resultant : (element, milliseconds) ->
-        forces = [
-            @drag(element),
-            @gravity(element)
-        ]
-        #FIXME: not portable
-        return forces.reduce (t, s) -> t.add(s)
+    # Apply environmental forces to the element for the given number of
+    # milliseconds.
+    applyForces : (element, milliseconds) ->
+        # FIXME: possible optimization? ignore zero gravity,
+        # zero density environments.
+        forces = [@drag(element), @gravity(element)]
+        resultant = forces.reduce (t, s) -> t.add(s) # FIXME: not portable.
+        element.applyForce(resultant, milliseconds)

     # Return the force of drag on the element.
-    #
-    # @param {Object} the element moving through the environment
-    # @return {Object} the drag on the element.
     drag : (element) ->

         # FIXME: this is broken at low speeds, and will in fact return a force
@@ -59,8 +54,6 @@

     # Return the force of gravity on the given element.
-    #
-    # @param element {Object}
     gravity : (element) ->
         # FIXME: technically, we should add drag as well.
         # FIXME: Optimize? Singleton?


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -8,7 +8,7 @@

     # called with no arguments, flicker will toggle the player's alpha
     # value between 0.9 (value provided above) and 0.5 (flickerable default)
-  every 0.1 second, lasting 2 seconds total
+    # every 0.1 second, lasting 2 seconds total
     player.flicker()

 @name Flickerable


making create event more like destroy event

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -139,6 +139,16 @@
       I.active

     ###*
+    Triggers the create event if the object has not already been created.
+
+    @name create
+    @methodOf GameObject#
+    ###
+    create: ->
+      self.trigger('create') unless I.created
+      I.created = true
+
+    ###*
     Destroys the object and triggers the destroyed event.

     @name destroy
@@ -168,9 +178,6 @@
       else
         self.bind(eventName, eval( "(function() {#{event}})" ))

-  self.trigger('create') unless I.created
-  I.created = true
-
   self

 ###*


inverse context order

inverse context order

--- src/haml-coffee.coffee
+++ src/haml-coffee.coffee
@@ -303,9 +303,10 @@
   # Render a standalone version
   renderStandalone: ->
     template = """
-    return ->
-      (context) ->
+    return (context) ->
+      (->
     #{indent(@precompile(), 2)}
+      ).call(context)
     """

   # Render the parsed source code as CoffeeScript template wrapped in a


:art:

:art:

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/default-history-provider.coffee
+++ src/default-history-provider.coffee
@@ -263,9 +263,9 @@
     for entry in @undoStack by -1
       switch entry.constructor
         when Checkpoint
-          undoStack.unshift(getCheckpointSnapshot(entry))
-        when Transaction
-          undoStack.unshift(getTransactionSnapshot(entry))
+          undoStack.unshift(snapshotFromCheckpoint(entry))
+        when Transaction
+          undoStack.unshift(snapshotFromTransaction(entry))
           undoStackPatches.unshift(entry.patch)

       break if undoStack.length is maxEntries
@@ -274,9 +274,9 @@
     for entry in @redoStack by -1
       switch entry.constructor
         when Checkpoint
-          redoStack.unshift(getCheckpointSnapshot(entry))
-        when Transaction
-          redoStack.unshift(getTransactionSnapshot(entry))
+          redoStack.unshift(snapshotFromCheckpoint(entry))
+        when Transaction
+          redoStack.unshift(snapshotFromTransaction(entry))

       break if redoStack.length is maxEntries

@@ -365,7 +365,7 @@
       layers[id] = snapshot
     layers

-getCheckpointSnapshot = (checkpoint) ->
+snapshotFromCheckpoint = (checkpoint) ->
   {
     type: 'checkpoint',
     id: checkpoint.id,
@@ -375,27 +375,9 @@
 checkpointFromSnapshot = ({id, markers}) ->
   new Checkpoint(id, markers, false)

-getTransactionSnapshot = (transaction) ->
-  {
-    type: 'transaction',
-    changes: getPatchSnapshot(transaction.patch),
-    markersBefore: transaction.markerSnapshotBefore
-    markersAfter: transaction.markerSnapshotAfter
-  }
-
-transactionFromSnapshot = ({changes, markersBefore, markersAfter}) ->
-  patch = new Patch
-  for {oldStart, oldEnd, oldText, newStart, newEnd, newText} in changes by -1
-    oldExtent = traversal(oldEnd, oldStart)
-    newExtent = traversal(newEnd, newStart)
-    patch.splice(oldStart, oldExtent, newExtent, oldText, newText)
-
-  # TODO: Return raw patch if there's no markersBefore && markersAfter
-  new Transaction(markersBefore, patch, markersAfter)
-
-getPatchSnapshot = (patch) ->
+snapshotFromTransaction = (transaction) ->
   changes = []
-  for change in patch.getChanges() by 1
+  for change in transaction.patch.getChanges() by 1
     changes.push({
       oldStart: change.oldStart,
       oldEnd: change.oldEnd,
@@ -405,4 +387,19 @@
       newText: change.newText
     })

-  changes
+  {
+    type: 'transaction',
+    changes,
+    markersBefore: transaction.markerSnapshotBefore
+    markersAfter: transaction.markerSnapshotAfter
+  }
+
+transactionFromSnapshot = ({changes, markersBefore, markersAfter}) ->
+  patch = new Patch
+  for {oldStart, oldEnd, oldText, newStart, newEnd, newText} in changes by -1
+    oldExtent = traversal(oldEnd, oldStart)
+    newExtent = traversal(newEnd, newStart)
+    patch.splice(oldStart, oldExtent, newExtent, oldText, newText)
+
+  # TODO: Return raw patch if there's no markersBefore && markersAfter
+  new Transaction(markersBefore, patch, markersAfter)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -6,7 +6,7 @@

   self.bind "enter", ->
     # TODO: Adjustable Fade In
-    engine.fadeIn 10
+    engine.fadeIn()

     engine.loadMap I.level, ->
       engine.I.transitioning = false


Renamed `IO.rewriteBranch` to `IO.writeBranch`.

Branch page files are always written out in their entirety. They contain
only a single line of JSON with a checksum. There's no real rewriting as
there is with leaf page files, where we rewrite the file in order to
cleanup records that have been deleted and the journal entries that
deleted them.

The name `writeBranch` has less going on than `rewriteBranch` and there
is less going on.

Closes #27.

--- src/lib/strata._coffee
+++ src/lib/strata._coffee
@@ -1251,7 +1251,7 @@
   # until place using `replace`.

   #
-  rewriteBranch: (page, suffix, _) ->
+  writeBranch: (page, suffix, _) ->
     filename = @filename page.address, suffix
     record = [ page.right, page.addresses ]
     json = JSON.stringify(record)
@@ -1309,7 +1309,7 @@
     leaf = @encache @createLeaf -(@nextAddress++)
     @splice root, 0, 0, leaf.address
     # Write the root branch.
-    @rewriteBranch root, ".replace", _
+    @writeBranch root, ".replace", _
     @rewriteLeaf leaf, ".replace", _
     @replace leaf, ".replace", _
     @replace root, ".replace", _
@@ -3090,7 +3090,7 @@
       replacements.push split

       # Write the branches
-      @io.rewriteBranch penultimate.page, ".pending", _
+      @io.writeBranch penultimate.page, ".pending", _

       # Now rename the last action, committing to our balance.
       @io.rename penultimate.page, ".pending", ".commit", _
@@ -3166,9 +3166,9 @@
     @io.splice child.page, partition, child.page.length - partition
     @io.splice parent.page, parent.index + 1, 0, right.address

-    @io.rewriteBranch root, "pending", _
-    @io.rewriteBranch left, "replace", _
-    @io.rewriteBranch right, "replace", _
+    @io.writeBranch root, "pending", _
+    @io.writeBranch left, "replace", _
+    @io.writeBranch right, "replace", _

     @io.rename root, "pending", "commit", _

@@ -3231,10 +3231,10 @@
     @io.splice root, 0, 0, (page.address for page in children)

     # Write the child branch pages.
-    @io.rewriteBranch page, ".replace", _ for page in children
+    @io.writeBranch page, ".replace", _ for page in children

     # Rewrite our root.
-    @io.rewriteBranch root, ".pending", _
+    @io.writeBranch root, ".pending", _

     # Commit the changes.
     @io.rename root, ".pending", ".commit", _
@@ -3362,7 +3362,7 @@
       @io.uncacheKey pivot.page, pivot.page.addresses[pivot.index]
       @io.splice penultimate.right.page, penultimate.right.index, 1

-      @io.rewriteBranch penultimate.right.page, ".pending", _
+      @io.writeBranch penultimate.right.page, ".pending", _

       # **TODO**: If I succeed, how will I know to test the parents for balance?
       # **TODO**: Uh, can't the medic just note that this page needs to be
@@ -3462,10 +3462,10 @@

     @io.size -= child.right.page.size

-    @io.rewriteBranch child.left.page, "replace", _
+    @io.writeBranch child.left.page, "replace", _

     child.right.page.addresses.length = 0
-    @io.rewriteBranch child.right.page, "unlink", _
+    @io.writeBranch child.right.page, "unlink", _

     if parent.isPivot
       right = pivot.page
@@ -3476,7 +3476,7 @@
         @io.uncacheKey right, right.addresses[1]
       right.addresses.shift()

-    @io.rewriteBranch right, "pending", _
+    @io.writeBranch right, "pending", _

     # **TODO**: If I succeed, how will I know to test the parents for balance?
     # Got to think all over again in medic about who is whose parent?
@@ -3509,10 +3509,10 @@
       @io.uncache page for page in pages
       root.addresses = left.addresses.concat(right.addresses)

-    @io.rewriteBranch left, "unlink", _
-    @io.rewriteBranch right, "unlink", _
-
-    @io.rewriteBranch root, "pending", _
+    @io.writeBranch left, "unlink", _
+    @io.writeBranch right, "unlink", _
+
+    @io.writeBranch root, "pending", _

     @io.rename root, "pending", "commit", _


Reopen Strata.

Reopen Strata.

--- src/lib/strata_.coffee
+++ src/lib/strata_.coffee
@@ -161,7 +161,7 @@
   constructor: (options) ->
     options       or= {}
     @locks          = {}
-    @cache          = {}
+    @_cache         = {}
     @head           = { segment: -1 }
     @head.next = @head.previous = @head
     @leafSize       = options.leafSize    or 12
@@ -179,31 +179,66 @@
     catch e
       throw e if e.code isnt "ENOENT"
       fs.mkdir @directory, 0755, _
-    root = @_allocate _
-    leaf = @_allocate _
+    root = @_allocate true, _
+    leaf = @_allocate false, _
     @_appendSegment root, leaf.segment, _

-  _allocate: (_) ->
+  open: (_) ->
+    try
+      stat = fs.stat @directory, _
+      if not stat.isDirectory()
+        throw new Error "database #{@directory} is not a directory."
+    catch e
+      if e.code isnt "ENOENT"
+        throw new Error "database #{@directory} does not exist."
+      else
+        throw e
+    @_load 0, _
+
+  _load: (segment, _) ->
+    if not entry = @_cache[segment]
+      filename = @_filename 0
+      fd = fs.open filename, "r+", _
+
+      buffer = new Buffer((1 + @leafSize) * 4)
+      fs.read fd, buffer, 0, buffer.length, 0, _
+
+      header      = buffer.readInt32BE(0)
+      penultimate = header < 0
+      size        = Math.abs header
+      objects     = []
+
+      for i in [0...size]
+        objects.push(buffer.readInt32BE((i + 1) * 4))
+
+      entry = @_link({ fd, penultimate, segment, objects })
+    entry
+
+  _filename: (segment) ->
+    padding = "00000000".substring(0, 8 - String(segment).length)
+    "#{@directory}/segment#{padding}#{segment}"
+
+  _allocate: (penultimate, _) ->
     segment = @nextSegment++
-    padding = "00000000".substring(0, 8 - String(segment).length)
-    filename = "#{@directory}/segment#{padding}#{segment}"
+    filename = @_filename segment
     fd = fs.open filename, "w+", 0600, _
     buffer = new Buffer(4)
     buffer.writeInt32BE(0, 0)
     fs.write fd, buffer, 0, buffer.length, 0, _
-    @_link({ fd, segment, objects: [] })
+    @_cache[segment] = @_link({ fd, penultimate, segment, objects: [] })

   _appendSegment: (inner, segment, _) ->
-    console.log { inner }
     inner.objects.push(segment)
     @_writeInner inner, _

   _writeInner: (inner, _) ->
-    length = inner.objects.length
-    buffer = new Buffer((1 + length) * 4)
-    buffer.writeInt32BE(0, length)
-    for i in [1...length]
-      buffer.writeInt32BE(i * 4, @objects[i - 1])
+    size = inner.objects.length
+    header = if inner.penultimate then -size else size
+    buffer = new Buffer((1 + size) * 4)
+    console.log header
+    buffer.writeInt32BE(header, 0)
+    for i in [1..size]
+      buffer.writeInt32BE(inner.objects[i - 1], i * 4)
     written = fs.write inner.fd, buffer, 0, buffer.length, 0, _
     throw new Error "incomplete write" if written isnt buffer.length


:art: Fix a typo in spec name

:art: Fix a typo in spec name

--- spec/buffer-modifying-provider-spec.coffee
+++ spec/buffer-modifying-provider-spec.coffee
@@ -29,7 +29,7 @@
     waitsForPromise ->
       linter.getActiveEditorLinter().lint(false).then ->
         expect(last).toBe('normal')
-  it 'runs in parallel', ->
+  it 'runs in sequence', ->
     linter = getModuleMain()
     activeEditor = atom.workspace.getActiveTextEditor()
     wasTriggered = false


:art: Update spec names, again

:art: Update spec names, again

--- spec/buffer-modifying-provider-spec.coffee
+++ spec/buffer-modifying-provider-spec.coffee
@@ -1,11 +1,11 @@
-describe 'buffer modifying linters', ->
+describe 'buffer modifying linter', ->
   getModuleMain = -> atom.packages.getActivePackage('linter').mainModule.instance
   beforeEach ->
     waitsForPromise ->
       atom.packages.activatePackage('linter')
     waitsForPromise ->
       atom.workspace.open('test.txt')
-  it 'are triggered before other linters', ->
+  it 'triggers before other linters', ->
     linter = getModuleMain()
     last = null
     bufferModifying =
@@ -29,7 +29,7 @@
     waitsForPromise ->
       linter.getActiveEditorLinter().lint(false).then ->
         expect(last).toBe('normal')
-  it 'run in sequence', ->
+  it 'runs in sequence', ->
     linter = getModuleMain()
     activeEditor = atom.workspace.getActiveTextEditor()
     wasTriggered = false


Applied new forward icon.

Applied new forward icon.

--- app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee
@@ -54,8 +54,7 @@
       actions.push {
         name: 'forward'
         type: 'emailForward'
-        #icon: 'forward'
-        icon: 'line-right-arrow'
+        icon: 'forward'
         href: '#'
       }

@@ -69,8 +68,7 @@
       actions.push {
         name: 'forward'
         type: 'emailForward'
-        #icon: 'forward'
-        icon: 'line-right-arrow'
+        icon: 'forward'
         href: '#'
       }
     if article.sender.name is 'Agent' && article.type.name is 'phone'
@@ -83,8 +81,7 @@
       actions.push {
         name: 'forward'
         type: 'emailForward'
-        #icon: 'forward'
-        icon: 'line-right-arrow'
+        icon: 'forward'
         href: '#'
       }


Make sure not to train on blank messages

--- src/markov.coffee
+++ src/markov.coffee
@@ -40,11 +40,15 @@
   # Read markov-specific configuration from the environment.
   ply = process.env.HUBOT_MARKOV_PLY or 1
   max = process.env.HUBOT_MARKOV_MAX or 50
+  min_train = process.env.HUBOT_MARKOV_MIN_TRAIN or 1

   model = new MarkovModel(storage, ply)

   # The robot hears ALL. You cannot run.
   robot.catchAll (msg) ->
+
+    # Return on empty messages
+    return if !msg.message.text

     # Don't learn from commands sent to the bot directly.
     name = robot.name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
@@ -57,6 +61,7 @@
     # Test to see if the command was directed at the bot
     return if r.test msg.message.text

+
     model.learn msg.message.text

   # Generate markov chains on demand, optionally seeded by some initial state.


config: remove debug change

config: remove debug change

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -107,7 +107,7 @@

   customDomain        = { public: "#{scheme}://#{host}", public_: host, local: "http://#{local}", local_: "#{local}", host: "http://#{hostname}", port: 8090 }

-  email               = { host:     "#{customDomain.public_}"                     , defaultFromMail:    'hello@koding.com'                      , defaultFromName:    'Koding'                    , forcedRecipientEmail: "aionochkin@tula.co", forcedRecipientUsername: "#{process.env.USER}"                      }
+  email               = { host:     "#{customDomain.public_}"                     , defaultFromMail:    'hello@koding.com'                      , defaultFromName:    'Koding'                    , forcedRecipientEmail: "#{process.env.USER}@koding.com", forcedRecipientUsername: "#{process.env.USER}"                      }
   kontrol             = { url:      "#{customDomain.public}/kontrol/kite"         , port:               3000                                    , useTLS:             no                          , certFile:        ""                                   , keyFile:  ""                          , publicKeyFile: "./certs/test_kontrol_rsa_public.pem"    , privateKeyFile: "./certs/test_kontrol_rsa_private.pem"}
   broker              = { name:     "broker"                                      , serviceGenericName: "broker"                                , ip:                 ""                          , webProtocol:     "http:"                              , host:     "#{customDomain.public}"    , port:          8008                                     , certFile:       ""                                       , keyFile:         ""          , authExchange: "auth"                , authAllExchange: "authAll" , failoverUri: "#{customDomain.public}" }
   regions             = { kodingme: "#{configName}"                               , vagrant:            "vagrant"                               , sj:                 "sj"                        , aws:             "aws"                                , premium:  "vagrant"                 }


Simplified tasks by introducing compile/test tasks


Blender Internal: Revert change in misnamed matrix constant.

--- engine/material_shaders/blender_internal.coffee
+++ engine/material_shaders/blender_internal.coffee
@@ -153,10 +153,8 @@
                     code # Ignored, used only for get_model_view_matrix_name()
                 when 2, GPU_DYNAMIC_OBJECT_MAT # object_matrix
                     code.push "gl.uniformMatrix4fv(locations[#{loc_idx}], false, ob.world_matrix);"
-                when 3, GPU_DYNAMIC_OBJECT_VIEWIMAT # inverse model_view_matrix
-                    # NOTE: Objects with zero scale are not drawn, otherwise m4 could be null
-                    code.push "m4 = mat4.invert(render._m4, render._model_view_matrix);"
-                    code.push "gl.uniformMatrix4fv(locations[#{loc_idx}], false, m4);"
+                when 3, GPU_DYNAMIC_OBJECT_VIEWIMAT # inverse view_matrix (not model_view_matrix!)
+                    code.push "gl.uniformMatrix4fv(locations[#{loc_idx}], false, render._cam2world);"
                 when 4, GPU_DYNAMIC_OBJECT_IMAT # inverse object_matrix
                     # NOTE: Objects with zero scale are not drawn, otherwise m4 could be null
                     code.push "m4 = mat4.invert(render._m4, ob.world_matrix);"


Bug fix for folder name edition according to selected emotion

--- ers_frontend/app/scripts/dataset/dataset.coffee
+++ ers_frontend/app/scripts/dataset/dataset.coffee
@@ -687,7 +687,8 @@
 			try
 				folder = ""
 				if $scope.uploader.emotion != undefined
-					folder = "/" + $scope.emotions[$scope.uploader.emotion].label
+					emotion = _.result(_.find($scope.emotions, {'value': $scope.uploader.emotion}), 'label')
+					folder = "/" + emotion

 				$scope.uploader.formData = [{'path': $scope.dataset.video_path + folder}]
 			catch
@@ -697,8 +698,9 @@
 			try
 				folder = ""
 				if $scope.uploader.emotion != undefined
-					folder = "/" + $scope.emotions[$scope.uploader.emotion].label
-
+					emotion = _.result(_.find($scope.emotions, {'value': $scope.uploader.emotion}), 'label')
+					folder = "/" + emotion
+
 				$scope.uploader.formData = [{'path': $scope.dataset.video_path + folder}]
 			catch
 				# No values for the moment