Adding proper submodule support

Adding proper submodule support

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -207,6 +207,7 @@
     @classParams = newMap()
     @types = []
     @isMalformed = no
+    @moduleName = null
     @importedModules = newSet()
     @_requested = newMap()

@@ -368,6 +369,7 @@
     scope.deferred = []
     scope.deferredBindings = []
     scope.boundTypeVariables = newSet()
+    scope.exportedNames = []
     scope.classes = newMap()
     scope.typeNames = newMap()
     scope.typeAliases = newMap()
@@ -396,6 +398,9 @@

   isInTopScope: ->
     @_scope().topLevel
+
+  exportName: (name) ->
+    @_scope().exportedNames.push name

   addTypeName: (dataType) ->
     if dataType instanceof TypeApp
@@ -1664,6 +1669,44 @@
     [(jsReturn (jsDictionary exportList, exportList))]]))

 ms = {}
+
+
+# module+ (syntax [..args]
+#   (` export (module ,..args)))
+ms['module+'] = ms_module_export = (ctx, call) ->
+  (call_ (token_ 'export'), [(call_ (token_ 'module'), (_arguments call))])
+
+ms.module = ms_module = (ctx, call) ->
+  requireName ctx, 'Module name required'
+  name = ctx.definitionName()
+  if name
+    # TODO: here we need to propagate request and use compiled js
+    result = compileModuleTopLevelAst (form_ (_arguments call)), joinModuleNames ctx.moduleName, name
+    if result.request
+      ctx.req result.request, newSet()
+    else
+      console.log "submodule", result.js
+      if result.errors
+        ctx.extendSubstitution fails: result.errors # propagate errors TODO: rename substitution which is only used for type errors now
+      jsValue result.js
+  else
+    jsNoop()
+
+joinModuleNames = (parent, child) ->
+  "#{parent}/#{child}"
+
+ms.export = ms_export = (ctx, call) ->
+  requireName ctx, 'Name to export required'
+  name = ctx.definitionName()
+  if name
+    ctx.exportName name
+  expressionCompile ctx, _fst _arguments call
+
+# fn+ (syntax [..args]
+#   (` export (fn ,..args)))
+ms['fn+'] = ms_fn_export = (ctx, call) ->
+  (call_ (token_ 'export'), [(call_ (token_ 'fn'), (_arguments call))])
+
 ms.fn = ms_fn = (ctx, call) ->
     # For now expect the curried constructor call
     args = _arguments call
@@ -3221,10 +3264,12 @@
   if atom then curried atom else curried

 call_ = (op, args) ->
+  form_ join [op], args
+
+form_ = (terms) ->
   concat [
     tokenize '('
-    [op]
-    args
+    terms
     tokenize ')'
   ]

@@ -3872,6 +3917,7 @@

 jsWrapTranslate = ({value}) ->
   "(#{value})"
+

 blockOfLines = (lines) ->
   if lines.length is 0
@@ -5747,12 +5793,14 @@
 lookupCompiledModule = (name) ->
   lookupInMap compiledModules, name

+compileModule = (source) ->
+  compileModuleTopLevel source

 compileModuleTopLevel = (source, moduleName = '@unnamed', requiredMap = newMap()) ->
   compileModuleTopLevelAst (astFromSource "(#{source})", -1, -1), moduleName, requiredMap

 compileModuleTopLevelAst = (ast, moduleName = '@unnamed', requiredMap = newMap()) ->
-  addToMap requiredMap, 'Prelude', yes # TODO: Hardcoded prelude dependency
+  # addToMap requiredMap, 'Prelude', yes # TODO: Hardcoded prelude dependency
   removeFromMap requiredMap, moduleName
   for requiredModuleName of values requiredMap
     if not lookupCompiledModule requiredModuleName
@@ -5760,7 +5808,8 @@
   replaceOrAddToMap moduleGraph, moduleName, requires: requiredMap
   toInject = requiresFor moduleName
   ctx = injectedContext toInject
-  defaultImports = importsFor (subtractSets (newSetWith 'Prelude'), (newSetWith moduleName))
+  ctx.moduleName = moduleName
+  defaultImports = importsFor newSet()#(subtractSets (newSetWith 'Prelude'), (newSetWith moduleName))
   declareImportedByDefault ctx, defaultImports
   compilationFn = (topLevelModule moduleName, defaultImports)
   {request, ir, js} = compileCtxAstToJs compilationFn, ctx, ast
@@ -5770,7 +5819,7 @@
     else
       {js} = compileCtxIrToJs ctx, ir
   errors = checkTypes ctx
-  (finalizeTypes ctx, ast)
+  # (finalizeTypes ctx, ast) # TODO: this doesn't work when the AST includes multiple modules with different contexts than the current one
   replaceOrAddToMap compiledModules, moduleName,
     declared: (subtractContexts ctx, (injectedContext toInject)) # must recompute because ctx is mutated
     js: js
@@ -7669,7 +7718,7 @@
     (filter _not, results).length + " failed"
 # end of tests

-
+exports.compileModule = compileModule
 exports.compileModuleTopLevel = compileModuleTopLevel
 exports.compileModuleWithDependencies = compileModuleWithDependencies
 exports.compileExpression = compileExpression


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -1,6 +1,6 @@
 ###*
 The Cooldown module provides a declarative way to manage cooldowns on
-GameObjects based on the velocity and acceleration. It does not check
+GameObject's p
 collisions so is probably best suited to particle effect like things.

 <code><pre>


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -9,7 +9,7 @@
       I[name] = I[name].approach(target, approachBy)

   cooldown: (name, options={target: 0, approachBy: 1, value: null}) ->
-    options.target |
+    options.target ||=

     unless I.cooldowns[name]
       I.cooldowns[name] = options


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -16,7 +16,6 @@

 # Shoot Timeout
 player = GameObject
-  shootTimer:

 player.cooldown "health",
   target: 100


Switch to using gutil for proper formatting

--- bokehjs/gulp/tasks/default.coffee
+++ bokehjs/gulp/tasks/default.coffee
@@ -1,8 +1,9 @@
 # default - message + brief help

 gulp = require "gulp"
+gutil = require "gulp-util"
 runSequence = require "run-sequence"

 gulp.task "default", (cb) ->
-  console.log('Building BokehJS for developer mode ...')
+  gutil.log('Building BokehJS for developer mode ...')
   runSequence("build", "install", "watch", cb)


Reopen #44 Not fixed

Reopen #44 Not fixed

--- static/coffee/nmis.coffee
+++ static/coffee/nmis.coffee
@@ -1052,7 +1052,6 @@
       else if what is "nav"
         if !nav
           nav = $('.lga-nav').on 'submit', 'form', (evt)->
-            NMIS.clear()
             d = NMIS.findDistrictById nav.find('select').val()
             dashboard.setLocation NMIS.urlFor.extendEnv state: d.group, lga: d
             evt.preventDefault()


removing open layers from nmis.coffee

removing open layers from nmis.coffee

--- static/coffee/nmis.coffee
+++ static/coffee/nmis.coffee
@@ -10,7 +10,6 @@
 # to influence which URLs the application uses to look for things.
 unless NMIS.settings
   NMIS.settings =
-    openLayersRoot: "./openlayers/"
     leafletRoot: "./leaflet/"
     pathToMapIcons: "./images"

@@ -1702,28 +1701,9 @@
         document.body.appendChild s
       googleMapsDfd.promise()

-  NMIS.loadOpenLayers = (url)->
-    url = "#{NMIS.settings.openLayersRoot}OpenLayers.js" if !url and NMIS.settings.openLayersRoot
-    $.ajax url: url, dataType: "script", cache: false
-
   NMIS.loadLeaflet = (url)->
     url = "#{NMIS.settings.leafletRoot}leaflet.js" if !url and NMIS.settings.leafletRoot
     $.ajax url: url, dataType: "script", cache: false
-
-  NMIS.loadGmapsAndOpenlayers = do ->
-    launchDfd = $.Deferred()
-    scriptsStarted = false
-    () ->
-      unless scriptsStarted
-        scriptsStarted = true
-        gmLoad = NMIS.loadGoogleMaps()
-        gmLoad.done (gmaps)->
-          olLoad = NMIS.loadOpenLayers()
-          olLoad.done (ol)->
-            launchDfd.resolve()
-          olLoad.fail (o, err, message)-> launchDfd.reject o, err, message
-        gmLoad.fail (o, err, message)-> launchDfd.reject o, err, message
-      launchDfd.promise()

   NMIS.loadGmapsAndLeaflet = do ->
     launchDfd = $.Deferred()
@@ -2636,7 +2616,6 @@
   #

   NMIS.settings =
-    openLayersRoot: "/static/openlayers/"
     leafletRoot: "/static/leaflet/"
     pathToMapIcons: "/static/images"


Fix for webkit detection

Fix for webkit detection

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -212,7 +212,7 @@
 # ENVIROMENT FUNCTIONS

 Utils.isWebKit = ->
-	window.WebKitCSSMatrix isnt null
+	window.WebKitCSSMatrix isnt undefined

 Utils.webkitVersion = ->
 	version = -1


autoflush method added

autoflush method added

--- src/dataflux.coffee
+++ src/dataflux.coffee
@@ -34,14 +34,16 @@
     @options = _.defaults options, defaultOptions
     @ts.timestampElements = @options.timestamp.elements
     @ts.timestampSeparator = @options.timestamp.separator
-    @autoFlushIntervall = 1000
-    @autoFlush = ->
-      setInterval =>
-        @flushBackupCache()
-      , @autoFlushIntervall
+    @autoFlushIntervall = 20000
+  autoFlush: ->
+    @log.debug "autoFlush method called"
+    setInterval =>
+      @log.debug "Call flushBackupCache()"
+      @flushBackupCache()
+    , @autoFlushIntervall

   watch: ->
-    watcher.createMonitor @srcFolder, (monitor) ->
+    @watcher.createMonitor @srcFolder, (monitor) =>
       monitor.on "created", (f, stat) =>
         @log.debug "#{f} is new"
         @addFileForBackup f


No need to refresh logsrel

No need to refresh logsrel

--- storage/lib/geolog.coffee
+++ storage/lib/geolog.coffee
@@ -1,15 +1,11 @@
 debug = require('debug') 'gc:geologs'
 upsert = require './upsert'
-refreshView = require './refresh-view'
 Promise = require 'bluebird'

 module.exports = (db) ->
-    _refresh = refreshView db, 'logsRel', 5000, debug
-
     upsert: (data) ->
         debug "upsert #{data.Code?.toLowerCase()}"
         upsert db, 'logs', data
-        _refresh()

     latest: Promise.coroutine (username) ->
         debug "latest #{username}"


Fix CoverImage usage of outdated React API.

--- components/CoverImage.cjsx
+++ components/CoverImage.cjsx
@@ -1,4 +1,5 @@
 React           = require 'react'
+ReactDOM        = require 'react-dom'
 { Classes }     = require 'shiny'

 selectImageSize = (width, height, px_ratio) ->
@@ -50,7 +51,7 @@

     _updateSize: ->
         if @isMounted()
-            _el = React.findDOMNode(@refs.el)
+            _el = ReactDOM.findDOMNode(@refs.el)
             @setState
                 width   : _el.offsetWidth
                 height  : _el.offsetHeight


Load dependencies only when needed

Load dependencies only when needed

--- components/Dataflow.coffee
+++ components/Dataflow.coffee
@@ -1,14 +1,4 @@
 noflo = require 'noflo'
-Dataflow = require('/meemoo-dataflow').Dataflow
-
-# Load NoFlo library plugin
-require '../src/plugins/nofloLibrary'
-# Load NoFlo Graph sync plugin
-require '../src/plugins/nofloGraph'
-# Load NoFlo settings plugin
-require '../src/plugins/nofloSettings'
-# Load NoFlo preview plugin
-require '../src/plugins/preview'

 class DataflowComponent extends noflo.Component
   constructor: ->
@@ -35,6 +25,17 @@

   loadDataflow: ->
     return unless @graph and @container and @runtime
+    Dataflow = require('/meemoo-dataflow').Dataflow
+
+    # Load NoFlo library plugin
+    require '../src/plugins/nofloLibrary'
+    # Load NoFlo Graph sync plugin
+    require '../src/plugins/nofloGraph'
+    # Load NoFlo settings plugin
+    require '../src/plugins/nofloSettings'
+    # Load NoFlo preview plugin
+    require '../src/plugins/preview'
+

     # Load a Dataflow instance
     dataflow = new Dataflow


PwmWrite: only apply values once enabled

--- components/PwmWrite.coffee
+++ components/PwmWrite.coffee
@@ -36,8 +36,10 @@
   startGpio: () ->
     return unless @pinNumber != null
     @pwm = pwm.export(@chipNumber, @pinNumber, () =>
-      @pwm.setEnable(1)
-      @applyParameters())
+      @ready = true
+      console.log('pwm ready')
+      @pwm.setEnable(1, () =>
+        @applyParameters()))

   applyParameters: () ->
     return unless @pwm


Proxy unsubscribe to gravity

Proxy unsubscribe to gravity

--- lib/middleware/proxy_to_gravity.coffee
+++ lib/middleware/proxy_to_gravity.coffee
@@ -12,7 +12,7 @@
 app = module.exports.app = express()

 ROUTES = [
-  '/oauth2*', '/robots.txt', '/humans.txt', '/sitemap*', "/auction-registration/*", '/assets/common-e1*.js'
+  '/oauth2*', '/robots.txt', '/humans.txt', '/sitemap*', "/auction-registration/*", '/assets/common-e1*.js', '/unsubscribe*'
 ]
 AUTH_ROUTES = [
   '/post', "/users/sign_in", "/users/sign_out", "/user/delete", "/user/edit", "/profile/edit"


Definition for underscore patterns

Definition for underscore patterns


:art:

:art:

--- lib/operators/general-operators.coffee
+++ lib/operators/general-operators.coffee
@@ -14,21 +14,24 @@
   @extend()
   vimState: null
   target: null
-  complete: null
-
-  constructor: (@editor, @vimState) ->
-    @complete = false
+  complete: false
+  recodable: true
+
+  constructor: (@editor, @vimState, options={}) ->
+    {@complete} = options if options.complete?

   # Public: Determines when the command can be executed.
   #
   # Returns true if ready to execute and false otherwise.
-  isComplete: -> @complete
+  isComplete: ->
+    @complete

   # Public: Determines if this command should be recorded in the command
   # history for repeats.
   #
   # Returns true if this command should be recorded.
-  isRecordable: -> true
+  isRecordable: ->
+    @recodable

   # Public: Marks this as ready to execute and saves the motion.
   #
@@ -42,7 +45,8 @@
     @target = target
     @complete = true

-  canComposeWith: (operation) -> operation.select?
+  canComposeWith: (operation) ->
+    operation.select?

   # Public: Preps text and sets the text register
   #
@@ -109,7 +113,6 @@
 #
 class ToggleCase extends Operator
   @extend()
-  constructor: (@editor, @vimState, {@complete}={}) ->

   execute: ->
     if @target?
@@ -208,9 +211,7 @@
 #
 class Join extends Operator
   @extend()
-  constructor: ->
-    super
-    @complete = true
+  complete: true

   # Public: Combines the current with the following lines
   #
@@ -228,11 +229,8 @@
 #
 class Repeat extends Operator
   @extend()
-  constructor: ->
-    super
-    @complete = true
-
-  isRecordable: -> false
+  complete: true
+  recodable: false

   execute: ->
     @editor.transact =>
@@ -264,10 +262,10 @@
 class Increase extends Operator
   @extend()
   step: 1
+  complete: true

   constructor: ->
     super
-    @complete = true
     @numberRegex = new RegExp(settings.numberRegex())

   execute: ->


Return persisted object in bulk update/insert.

--- server/common/crudControllerFactory.coffee
+++ server/common/crudControllerFactory.coffee
@@ -34,7 +34,7 @@
             errors.push {message: err, obj: obj}
             cb()
           else if result
-            results.push {message: "ok", obj: obj}
+            results.push {message: "ok", obj: result}
             cb()
           else
             errors.push {message: "create failed for reasons unknown", obj: obj}
@@ -45,7 +45,7 @@
             errors.push {message: err, obj: obj}
             cb()
           else if result
-            results.push {message: "ok", obj: obj}
+            results.push {message: "ok", obj: result}
             cb()
           else
             errors.push {message: "create failed for reasons unknown", obj: obj}


Changed a function name.

Changed a function name.

--- server/lib/crawler/staticpages/feed.coffee
+++ server/lib/crawler/staticpages/feed.coffee
@@ -89,20 +89,20 @@
     end   = currentPage + PAGERWINDOW

   if start > 1
-    pagination += getEmptyLink " ... "
+    pagination += getNoHrefLink " ... "

   [start..end].map (pageNumber)=>
     pagination += getSinglePageLink pageNumber, contentType

   if end < numberOfPages
-    pagination += getEmptyLink " ... "
+    pagination += getNoHrefLink " ... "

   pagination += nextLink
   pagination += lastLink

   return pagination

-getEmptyLink = (linkText)->
+getNoHrefLink = (linkText)->
   "<a href='#'>#{linkText}  </a>"

 getSinglePageLink = (pageNumber, contentType, linkText=pageNumber)->


IE8 parentNode passes ? even when node in DOM

--- src/document.coffee
+++ src/document.coffee
@@ -42,11 +42,11 @@

   insertLineBefore: (newLineNode, refLine) ->
     line = new Line(this, newLineNode)
-    if refLine != null
-      @root.insertBefore(newLineNode, refLine.node) unless newLineNode.parentNode?
+    if refLine?
+      @root.insertBefore(newLineNode, refLine.node) unless DOM.isElement(newLineNode.parentNode)
       @lines.insertAfter(refLine.prev, line)
     else
-      @root.appendChild(newLineNode) unless newLineNode.parentNode?
+      @root.appendChild(newLineNode) unless DOM.isElement(newLineNode.parentNode)
       @lines.append(line)
     @lineMap[line.id] = line
     return line


Remove vocabulary and 'open ended' format options

--- src/components/question-format-type.cjsx
+++ src/components/question-format-type.cjsx
@@ -9,8 +9,10 @@
 TYPES =
   'multiple-choice' : 'Multiple Choice'
   'true-false'      : 'True/False'
-  'vocabulary'      : 'Vocabulary'
-  'open-ended'      : 'Open Ended'
+
+# Temporarily removed as options (not needed & causes 500 on BE)
+#  'vocabulary'      : 'Vocabulary'
+#  'open-ended'      : 'Open Ended'

 QuestionFormatType = React.createClass


fixed an issue where freeze couldn't be called immediately after unfreeze

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -722,6 +722,8 @@
       # Swap the visibility of the elements.
       @cleanEl.style[css.transform] = 'translate3d(-9999px, 0, 0)'
       @stageEl.style[css.transform] = 'translate3d(0, 0, 0)'
+      # Set `lastAngle` to 0 so an immediately subsequent call to `freeze` triggers the callback.
+      @lastAngle = 0

   # Removes the oriDomi element and deletes its instance from memory.


added change to image size

added change to image size

--- src/entities.coffee
+++ src/entities.coffee
@@ -302,7 +302,7 @@
         # adjust the size setting information to match the sprite size
         # so that the entity object is created with the right size
         settings.spritewidth = settings.width = 32
-        settings.spriteheight = settings.height =
+        settings.spriteheight = settings.height = 32
         # call the parent constructor
         @_super(me.Entity, 'init', [x, y, settings])
         # set start/end position based on the initial area size


adding noop calls

adding noop calls

--- src/expander.coffee
+++ src/expander.coffee
@@ -1,4 +1,21 @@
+_ = require 'underscore'
+estools = require './estools'
+
 exports.expand = (ast) ->
-
+
+  estools.traverse ast, ['BlockStatement', 'Program'], (node) ->
+    node.body.forEach (x, i) ->
+      if x.type == 'IfStatement' && !x.alternate?
+        x.alternate =
+          type: 'BlockStatement'
+          body: [{
+            type: 'ExpressionStatement'
+            expression:
+              arguments: []
+              type: 'CallExpression'
+              callee:
+                type: 'Identifier'
+                name: 'noop'
+          }]
+
   ast
-


Only show stderr if the process fails

Only show stderr if the process fails

--- tasks/task-helpers.coffee
+++ tasks/task-helpers.coffee
@@ -32,9 +32,18 @@
     grunt.file.delete(args..., force: true) if grunt.file.exists(args...)

   spawn: (options, callback) ->
-    grunt.util.spawn options, (error, results, code) ->
-      grunt.log.errorlns results.stderr if results.stderr
-      callback(error, results, code)
+    childProcess = require 'child_process'
+    stdout = []
+    stderr = []
+    error = null
+    proc = childProcess.spawn(options.cmd, options.args, options.opts)
+    proc.stdout.on 'data', (data) -> stdout.push(data.toString())
+    proc.stderr.on 'data', (data) -> stderr.push(data.toString())
+    proc.on 'exit', (exitCode, signal) ->
+      error = new Error(signal) if exitCode != 0
+      results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
+      grunt.log.error results.stderr if exitCode != 0
+      callback(error, results, exitCode)

   isAtomPackage: (packagePath) ->
     try


minor - a couple of comments to the tests

--- tests.coffee
+++ tests.coffee
@@ -1,21 +1,31 @@
-
-
-
-
 tests = [

+  # surprise! this language "chains" to the right
+  # so "streams" of things are run right to left.
   "1plus 1 print"
   "1"

+  # parens can help
   "(1plus 1)print"
   "2"

+  # here "print" takes "print" and does
+  # nothing with it, so first (1 plus 1) is
+  # printed, and then the result of that is
+  # printed again.
   "(1plus 1)print print"
   "22"

+  # there are two ways to assign things, this is
+  # the most technically thorough but it's
+  # more difficult to decypher.
+  #
+  # The dot separates stataments.
   "@a ← \"test string\". @b ← a. @c ← @a. @a eval1 print.@b eval1 print.@c eval1 print"
   "test stringtest stringa"

+  # the equal sign is less technically thorough but
+  # it's obvious to the eye.
   "a=\"test string\".b=a.c=@a.a eval print.b eval print.c eval print"
   "test stringtest stringa"


minor typo

minor typo


Used only for debugging.

Used only for debugging.

--- tests.coffee
+++ tests.coffee
@@ -2,8 +2,6 @@

 Users = new Meteor.Collection USERS_COLLECTION
 LogActions = new Meteor.Collection 'LogActionsActions_meteor_middleware_tests'
-
-Error.stackTraceLimit = Infinity;

 if Meteor.isServer
   # Initialize the database


menus: MyShows: Fix episodesMenu not being correctly filled

--- src/js/menus.coffee
+++ src/js/menus.coffee
@@ -331,7 +331,9 @@

   initHandlers: ->
     @menu.on 'select', (e) =>
+      element = e.item
       data = e.item.data
+
       showTitle = e.item.data.showTitle

       item = i for i in @shows when i.show.ids.trakt == data.showID
@@ -348,11 +350,18 @@

       seasonsMenu.show()

-      seasonsMenu.on 'select', (e) ->
-        data = e.item.data
+      seasonsMenu.on 'select', (seasonMenuEvent) ->
+        element = seasonMenuEvent.item
+        data = seasonMenuEvent.item.data
+
+        displaySubtitle = (text) =>
+          element.subtitle = text
+          seasonsMenu.item seasonMenuEvent.sectionIndex, seasonMenuEvent.itemIndex, element
+
+        originalSubtitle = element.subtitle
+        displaySubtitle "Loading..."
+
         season = s for s in item.seasons when s.number == data.seasonNumber
-        episodesMenu = createDefaultMenu()
-        episodesMenu.show()

         async.map(
           season.episodes,
@@ -364,19 +373,23 @@
                   episode = {}
                 cb(null, episode)
           (err, episodes) ->
-            episodesMenuSections = [{
-              items:
-                {
-                  title: episode.title
-                  subtitle: "Season #{episode.season} Ep. #{episode.number}"
-                  data:
-                    episodeTitle: episode.title
-                    overview: episode.overview
-                    seasonNumber: episode.season
-                    episodeNumber: episode.number
-                } for episode in episodes
-            }]
-            updateMenuSections episodesMenu, episodesMenuSections
+            episodesMenu = createDefaultMenu(
+              sections: [{
+                items:
+                  {
+                    title: episode.title
+                    subtitle: "Season #{episode.season} Ep. #{episode.number}"
+                    data:
+                      episodeTitle: episode.title
+                      overview: episode.overview
+                      seasonNumber: episode.season
+                      episodeNumber: episode.number
+                  } for episode in episodes
+              }]
+            )
+
+            episodesMenu.show()
+            displaySubtitle originalSubtitle

             episodesMenu.on 'select', (e) ->
               data = e.item.data


rename KD rollbar wrapper to KD.logToExternal

--- client/app/MainApp/rollbar.coffee
+++ client/app/MainApp/rollbar.coffee
@@ -1,5 +1,5 @@
 # Wrapper for pushing events to Rollbar.
-logToExternal = KD.rollbar = (args) ->
+logToExternal = KD.logToExternal = (args) ->
   _rollbar.push args

 # Push status events to Rollbar.


Fixed: an error occurred at task creation

--- client/app/views/task_view.coffee
+++ client/app/views/task_view.coffee
@@ -35,13 +35,6 @@
         @firstDel = false
         @isDeleting = false

-        if !@list.tasks.listId?
-            @$el.unbind('dragstart')
-            @$el.unbind('dragover')
-            @$el.unbind('drop')
-            @$el.unbind('dragend')
-            @$el.unbind('hover')
-
         @list

     # Render wiew and bind it to model.
@@ -70,7 +63,14 @@
             else
                 @todoButton.html("todo")

-        @$el.prop 'draggable', true
+        if !@list.tasks.listId?
+            @$el.unbind('dragstart')
+            @$el.unbind('dragover')
+            @$el.unbind('drop')
+            @$el.unbind('dragend')
+            @$el.unbind('hover')
+        else
+            @$el.prop 'draggable', true

         @el


home/flux: add selected machine flux for VM

--- client/home/lib/flux/index.coffee
+++ client/home/lib/flux/index.coffee
@@ -5,5 +5,7 @@
     HomeBillingFlux = require '../billing/flux'
     HomeBillingFlux.register reactor

-    VirtualMachinesFlux = require '../virtualmachines/flux'
-    VirtualMachinesFlux.register reactor
+    VirtualMachinesSearchFlux = require '../virtualmachines/flux/search'
+    VirtualMachinesSearchFlux.register reactor
+    VirtualMachinesSelectedMachineFlux = require '../virtualmachines/flux/selectedmachine'
+    VirtualMachinesSelectedMachineFlux.register reactor


landing: fix email address submit button states

--- client/landing/landingview.coffee
+++ client/landing/landingview.coffee
@@ -29,9 +29,10 @@
       placeholder : "Type one email address per line"

     @emailAddressSubmit = new KDButtonView
-      style    : "submit-email-addresses"
-      title    : "Send"
-      loader   : yes
+      style       : "submit-email-addresses"
+      title       : "Send"
+      loader      :
+        diameter  : 24
       callback    : =>
         @emailAddressSubmit.hideLoader()
         if @emailAddressInput.getValue() is ""
@@ -80,12 +81,16 @@
       @requireLogin cb

   submitEmailAddresses: ->
-    @emailAddressSubmit.showLoader()
     emails = @emailAddressInput.getValue().split "\n"
     emails = emails.filter (email) -> email.length > 0

+    unless emails.length
+      @emailAddressSubmit.hideLoader()
+      return
+
     fails = []

+    @emailAddressSubmit.showLoader()
     async.map emails, (email, callback) ->
       KD.remote.api.JReferrableEmail.invite email, (err) ->
         fails.push email if err
@@ -93,9 +98,13 @@
     , =>
       @errorMessage.hide()
       @emailAddressSubmit.hideLoader()
-      @emailAddressSubmit.hide()
-      @invitationSentButton.show()
-      @decorateEmailAddressError fails if fails.length
+
+      if fails.length
+        @decorateEmailAddressError fails
+      else
+        @emailAddressSubmit.hide()
+        @invitationSentButton.show()
+

   enable: ->
     return  unless KD.isLoggedIn()


Tests: IDE: Search: Disabled toggleInvisibleFiles test.

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -83,25 +83,26 @@
     browser.end()

-  toggleInvisibleFiles: (browser) ->
+  # toggleInvisibleFiles: (browser) ->

-    user = helpers.beginTest(browser)
-    helpers.waitForVMRunning(browser)
+  #   user = helpers.beginTest(browser)
+  #   helpers.waitForVMRunning(browser)

-    pathSelector = "span[title='/home/#{user.username}/.bashrc']"
+  #   pathSelector = "span[title='/home/#{user.username}/.bashrc']"

-    browser.element 'css selector', pathSelector, (result) =>
-      if result.status is 0
-       ideHelpers.toggleInvisibleFiles(browser, user)
-      else
-        helpers.clickVMHeaderButton(browser)
-        browser
-          .click '.context-list-wrapper .toggle-invisible-files'
-          .waitForElementVisible    pathSelector, 20000 # Assertion
+  #   browser.element 'css selector', pathSelector, (result) =>
+  #     if result.status is 0
+  #      ideHelpers.toggleInvisibleFiles(browser, user)
+  #     else
+  #       helpers.clickVMHeaderButton(browser)
+  #       browser
+  #         .click '.context-list-wrapper .toggle-invisible-files'
+  #         .waitForElementVisible    pathSelector, 20000 # Assertion

-          ideHelpers.toggleInvisibleFiles(browser, user)
+  #         ideHelpers.toggleInvisibleFiles(browser, user)

-      browser.end()
+  #     browser.end()
+

   # openAnExistingFileAndSave: (browser) ->


update keyup_checker

update keyup_checker

add breaks and comments

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -271,21 +271,25 @@
     this.search_field_scale()

     switch stroke
-      when 8
+      when 8 # backspace
         if @is_multiple and @backstroke_length < 1 and this.choices_count() > 0
           this.keydown_backstroke()
         else if not @pending_backstroke
           this.result_clear_highlight()
           this.results_search()
-      when 13
+        break
+      when 13 # enter
         evt.preventDefault()
         this.result_select(evt) if this.results_showing
-      when 27
+        break
+      when 27 # escape
         this.results_hide() if @results_showing
-        return true
-      when 9, 38, 40, 16, 91, 17, 18
+        break
+      when 9, 16, 17, 18, 38, 40, 91
         # don't do anything on these keys
-      else this.results_search()
+      else
+        this.results_search()
+        break

   clipboard_event_checker: (evt) ->
     setTimeout (=> this.results_search()), 50


Fix restriction for quick-release cargo locks.

--- coffeescripts/cards-common.coffee
+++ coffeescripts/cards-common.coffee
@@ -6107,7 +6107,7 @@
             slot: 'Hardpoint'
             points: 2
             restriction_func: (ship) ->
-                ship.canonical_name in [ 'C-ROC Cruiser'.canonicalize(), 'GR-75 Medium Transport'.canonicalize() ]
+                ship.data.canonical_name in [ 'C-ROC Cruiser'.canonicalize(), 'GR-75 Medium Transport'.canonicalize() ]
         }
         {
             name: 'Supercharged Power Cells'


CLI: fixing a symlinks selfreference issue

--- cli/commands/activate.coffee
+++ cli/commands/activate.coffee
@@ -12,7 +12,7 @@

   for entry in fs.readdirSync(location)
     if fs.statSync("#{location}/#{entry}").isDirectory()
-      versions.push(entry)
+      versions.push(entry) if entry isnt 'active'

   versions.sort()
   versions[versions.length - 1]


 Merge branch 'master.forcedRegister' into 'master'

Master.Forced Register

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -42,7 +42,11 @@
     options.styles       or= []           # <Array<string>> list of stylesheets

     enforceLogin=->
-      return if KD.isLoggedIn()
+      return  if KD.isLoggedIn()
+      if $.cookie("doNotForceRegistration") or location.search.indexOf("sr=1") > -1
+        $.cookie("doNotForceRegistration", "true")
+        return
+
       appManager = KD.getSingleton "appManager"
       appManager.tell "Account", "showRegistrationNeededModal"


typo in french translation

typo in french translation

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -13,7 +13,7 @@
     "public" : "public"
     "hidden" : "masqué"
     "There is no photos in this album" : "Pas de photos dans cet album"
-    "There is no public albums." : "Pas d'albumes publics"
+    "There is no public albums." : "Pas d'albums publics"
     "This album is private" : "Cet album est Privé"
     "This album is hidden" : "Cet album est Masqué"
     "This album is public" : "Cet album est Public"


fix indentation

fix indentation

--- client/app/components/message_header.coffee
+++ client/app/components/message_header.coffee
@@ -22,20 +22,20 @@
         isDeleted: React.PropTypes.bool

-   formatUsers: (users) ->
-       return unless users?
+    formatUsers: (users) ->
+        return unless users?

-       if _.isArray users
-           items = []
-           for user in users
-              items.push ContactLabel
-                   contact: user
+        if _.isArray users
+            items = []
+            for user in users
+               items.push ContactLabel
+                    contact: user

                items.push ", " if user isnt _.last users
-           return items
-       else
-           return ContactLabel
-               contact: user
+            return items
+        else
+            return ContactLabel
+                contact: user

     getInitialState: ->


Routes: Keep selected / activated workspace id for Teams scope.

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -1,12 +1,13 @@
-kd             = require 'kd'
-nick           = require 'app/util/nick'
-whoami         = require 'app/util/whoami'
-globals        = require 'globals'
-remote         = require('app/remote').getInstance()
-Machine        = require 'app/providers/machine'
-lazyrouter     = require 'app/lazyrouter'
-dataProvider   = require 'app/userenvironmentdataprovider'
-registerRoutes = require 'app/util/registerRoutes'
+kd              = require 'kd'
+nick            = require 'app/util/nick'
+whoami          = require 'app/util/whoami'
+remote          = require('app/remote').getInstance()
+actions         = require 'app/flux/environment/actions'
+globals         = require 'globals'
+Machine         = require 'app/providers/machine'
+lazyrouter      = require 'app/lazyrouter'
+dataProvider    = require 'app/userenvironmentdataprovider'
+registerRoutes  = require 'app/util/registerRoutes'

 selectWorkspaceOnSidebar = (data) ->
@@ -170,6 +171,7 @@
   else if machineLabel and workspaceSlug
     dataProvider.fetchMachineByLabel machineLabel, (machine, workspace) =>
       if machine and workspace
+        actions.setSelectedWorkspaceId worksace._id
         router.handleRoute "/IDE/#{machineLabel}/#{workspaceSlug}"
       else if machine
         routeToMachineWorkspace machine


Adjust zscale some

Adjust zscale some

--- client/terrain/object.coffee
+++ client/terrain/object.coffee
@@ -6,7 +6,7 @@
 class TerrainObject extends THREE.Object3D
   constructor: (lat, lon, radius) ->
     super
-    @builder = new TerrainBuilder(200, 200, 1.3, 0.001)
+    @builder = new TerrainBuilder(200, 200, 1, 0.0008)
     @builder.applyElevation()
     @textureCanvas = document.createElement("canvas")
     @textureCanvas.width = 400


client: no need fat arrow

client: no need fat arrow

--- client/app/lib/helpers/integration.coffee
+++ client/app/lib/helpers/integration.coffee
@@ -205,7 +205,7 @@
         data.isAuthorized = isAuthorized

         if integration.name is 'github'
-          fetchAllGithubRepos (err, repositories) =>
+          fetchAllGithubRepos (err, repositories) ->
             if err
               if err.code is 401
                 data.isAuthorized = no


MachineShareManager: Define default callback

--- client/app/lib/machinesharemanager.coffee
+++ client/app/lib/machinesharemanager.coffee
@@ -62,7 +62,7 @@
     @set machineUId, {type, workspaceId}

-  registerChannelEvent: (channelId) ->
+  registerChannelEvent: (channelId, callback = kd.noop) ->

     { socialapi, groupsController } = kd.singletons


social: add integration list endpoint accessor

--- client/app/lib/socialapicontroller.coffee
+++ client/app/lib/socialapicontroller.coffee
@@ -785,3 +785,15 @@

         return callback null, mapChannel response.data

+  integrations           :
+    list                 : (callback) ->
+
+      doXhrRequest {
+        type     : 'GET'
+        endPoint : "/api/integration/list"
+      }, (err, response) ->
+
+        return callback err  if err
+
+        return callback null, response.data
+


ChatSettingsPane: Added session warning.

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -180,13 +180,17 @@

     return """

-      {{> @back}}
-

-        {{> @startSession}} {{> @endSession}}
-

+        {{> @back}}

Anyone who joins{{> @defaultSetting}}

       {{> @everyone}}
+

+
Please be advised

+        When you start a session, you share your "Entire VM".
+

+

+        {{> @startSession}} {{> @endSession}}
+

     """


ide: fix for context menu in tests

ide: fix for context menu in tests

--- client/test/lib/helpers/idehelpers.coffee
+++ client/test/lib/helpers/idehelpers.coffee
@@ -22,7 +22,7 @@

   openContextMenu: (browser) ->

-    fileSelector    = "#{tabHandleSelector} .active"
+    fileSelector    = "#{tabHandleSelector} .kdtabhandle.active"
     optionsSelector = "#{fileSelector} span.options"

     browser


JSON.stringfy was causing an infinite loop

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -607,37 +607,20 @@
       event.preventDefault()
       event.stopPropagation()
       return no
+
     @dragIsActive = yes
     e = event.originalEvent
     e.dataTransfer.effectAllowed = 'copyMove' # only dropEffect='copy' will be dropable
-    transferredData = (JSON.stringify node.getData() for node in @selectedNodes)
+
+    # We need to look it at later FIXME GG
+    # transferredData = (JSON.stringify node.getData() for node in @selectedNodes)
+    transferredData = (@getNodeId node.getData() for node in @selectedNodes)
+
     e.dataTransfer.setData('Text', transferredData.join()) # required otherwise doesn't work

     if @selectedNodes.length > 1
       e.dataTransfer.setDragImage dragHelper, -10, 0

-    # this doesnt work in webkit only firefox
-
-    # items = for node in @selectedNodes
-    #   node.getData()
-    #
-    # options = @getOptions()
-    # draggedListController = new KDListViewController
-    #   wrapper        : no
-    #   scrollView     : no
-    #   selection      : no
-    #   view           : new JTreeView
-    #     tagName      : "ul"
-    #     type         : "jtree"
-    #     cssClass     : "drag-helper-list"
-    #     itemClass : JTreeItemView
-    # , {items}
-    #
-    # @tempDragList = draggedListController.getView()
-    # KDView.appendToDOMBody @tempDragList
-    # @tempDragList.$().css top : e.pageY, left : e.pageY
-    # draggedList = @tempDragList.$()[0]
-    # e.dataTransfer.addElement(draggedList);
     nodeView.setClass "drag-started"

   dragEnter: (nodeView, event)->


Revert " Check node if its real before doing anything"

This reverts commit 987396bd6cada56974eead3fe45501bc2000dbbe.

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -107,7 +107,7 @@
     @nodes[@getNodeId nodeData] = {}

     if options.putDepthInfo
-      if @nodes[@getNodePId nodeData]?.getData?
+      if @nodes[@getNodePId nodeData]
         nodeData.depth = @nodes[@getNodePId(nodeData)].getData().depth + 1
       else
         nodeData.depth = 0
@@ -160,8 +160,6 @@
   addNode:(nodeData, index)->

     nodeData = @repairIds nodeData
-
-
     return unless nodeData
     @getData().push nodeData
     @addIndexedNode nodeData
@@ -226,12 +224,11 @@
     nodeData = nodeView.getData()
     nodeView.$().attr "draggable","true" if @getOptions().dragdrop
     {id, parentId} = nodeData
-
     @nodes[@getNodeId nodeData] = nodeView
     if @nodes[@getNodePId nodeData]
       @expand @nodes[@getNodePId nodeData] unless @getOptions().addListsCollapsed
       # todo: make decoration with events
-      @nodes[@getNodePId nodeData]?.decorateSubItemsState?()
+      @nodes[@getNodePId nodeData].decorateSubItemsState()
     return unless @listControllers[id]
     @addSubList nodeView, id

@@ -257,7 +254,7 @@

     # if node parent is present
     parentNodeView = @nodes[@getNodePId nodeData]
-    if parentNodeView?.getData?
+    if parentNodeView
       prevNeighbor  = getPreviousNeighbor parentNodeView.getData()
       neighborIndex = @indexedNodes.indexOf prevNeighbor
       @indexedNodes.splice neighborIndex + 1, 0, nodeData
@@ -306,7 +303,7 @@

     o = @getOptions()
     listToBeAdded = @listControllers[id].getView()
-    if nodeView?.$?
+    if nodeView
       nodeView.$().after listToBeAdded.$()
       listToBeAdded.parentIsInDom = yes
       listToBeAdded.propagateEvent KDEventType: 'viewAppended'
@@ -435,10 +432,9 @@

   expand:(nodeView)->

-    if nodeView?.getData?
-      nodeData = nodeView.getData()
-      nodeView.expand()
-      @listControllers[@getNodeId nodeData]?.getView().expand()
+    nodeData = nodeView.getData()
+    nodeView.expand()
+    @listControllers[@getNodeId nodeData]?.getView().expand()

   collapse:(nodeView)->


add check for Rock is alive

add check for Rock is alive

--- _source/packages/newspring:core/server/lib/workers/workers.coffee
+++ _source/packages/newspring:core/server/lib/workers/workers.coffee
@@ -5,8 +5,10 @@

     query = Apollos.queuedApiRequests.find(requestSent: false)

-    query.observe
-      added: (request) ->
-        console.log 'processing', request._id
-        normanConn.call "processRockApiRequest", request
+    Rock.isAlive.onChange ->
+      if Rock.isAlive()
+        query.observe
+          added: (request) ->
+            console.log 'processing', request._id
+            normanConn.call "processRockApiRequest", request


syntax

syntax

--- app/assets/javascripts/effective_datatables/inline_crud.js.coffee
+++ app/assets/javascripts/effective_datatables/inline_crud.js.coffee
@@ -161,9 +161,8 @@
   $('table.dataTable.initialized').each ->
     $table = $(this)

-    if EffectiveForm.remote_form_refresh_datatables.find((id) -> $table.attr('id').startsWith(id) || id == 'all') && $table != $except
-      if $table != $source
-        $table.DataTable().draw()
+    if EffectiveForm.remote_form_refresh_datatables.find((id) -> $table.attr('id').startsWith(id) || id == 'all')
+      $table.DataTable().draw() if $table != $source

 # Cancel button clicked. Blow away new tr, or restore edit tr
 # No data will have changed at this point


removing yytext mentions from the grammar

--- grammar.coffee
+++ grammar.coffee
@@ -65,19 +65,19 @@
     o "Body TERMINATOR Line",                   -> $1.push $3
     o "Body TERMINATOR"
   ]
-
+
   # Expressions and statements, which make up a line in a body.
   Line: [
     o "Expression"
     o "Statement"
   ]
-
+
   # Pure statements which cannot be expressions.
   Statement: [
     o "Return"
     o "Throw"
-    o "BREAK",                                  -> new LiteralNode yytext
-    o "CONTINUE",                               -> new LiteralNode yytext
+    o "BREAK",                                  -> new LiteralNode $1
+    o "CONTINUE",                               -> new LiteralNode $1
   ]

   # All the different types of expressions in our language. The basic unit of
@@ -100,8 +100,8 @@
     o "Class"
     o "Splat"
     o "Existence"
-    o "Comment"
-    o "Extension"
+    o "COMMENT"
+    o "EXTENSION"
   ]

   # A an indented block of expressions. Note that the [Rewriter](rewriter.html)
@@ -110,27 +110,22 @@
   Block: [
     o "INDENT Body OUTDENT",                    -> $2
     o "INDENT OUTDENT",                         -> new Expressions()
-    o "TERMINATOR Comment",                     -> Expressions.wrap [$2]
-  ]
-
-  # A literal identifier, a variable name or property.
-  Identifier: [
-    o "IDENTIFIER",                             -> new LiteralNode yytext
+    o "TERMINATOR COMMENT",                     -> Expressions.wrap [$2]
   ]

   # Alphanumerics are separated from the other **Literal** matchers because
   # they can also serve as keys in object literals.
   AlphaNumeric: [
-    o "NUMBER",                                 -> new LiteralNode yytext
-    o "STRING",                                 -> new LiteralNode yytext
+    o "NUMBER",                                 -> new LiteralNode $1
+    o "STRING",                                 -> new LiteralNode $1
   ]

   # All of our immediate values. These can (in general), be passed straight
   # through and printed to JavaScript.
   Literal: [
     o "AlphaNumeric"
-    o "JS",                                     -> new LiteralNode yytext
-    o "REGEX",                                  -> new LiteralNode yytext
+    o "JS",                                     -> new LiteralNode $1
+    o "REGEX",                                  -> new LiteralNode $1
     o "TRUE",                                   -> new LiteralNode true
     o "FALSE",                                  -> new LiteralNode false
     o "YES",                                    -> new LiteralNode true
@@ -147,22 +142,15 @@
   # Assignment when it happens within an object literal. The difference from
   # the ordinary **Assign** is that these allow numbers and strings as keys.
   AssignObj: [
-    o "Identifier ASSIGN Expression",           -> new AssignNode new ValueNode($1), $3, 'object'
+    o "IDENTIFIER ASSIGN Expression",           -> new AssignNode new ValueNode($1), $3, 'object'
     o "AlphaNumeric ASSIGN Expression",         -> new AssignNode new ValueNode($1), $3, 'object'
-    o "Comment"
+    o "COMMENT"
   ]

   # A return statement from a function body.
   Return: [
     o "RETURN Expression",                      -> new ReturnNode $2
     o "RETURN",                                 -> new ReturnNode new ValueNode new LiteralNode 'null'
-  ]
-
-  # A comment. Because CoffeeScript passes comments through to JavaScript, we
-  # have to parse comments like any other construct, and identify all of the
-  # positions in which they can occur in the grammar.
-  Comment: [
-    o "COMMENT",                                -> new CommentNode yytext
   ]

   # [The existential operator](http://jashkenas.github.com/coffee-script/#existence).
@@ -195,8 +183,8 @@
   # A single parameter in a function definition can be ordinary, or a splat
   # that hoovers up the remaining arguments.
   Param: [
-    o "PARAM",                                  -> new LiteralNode yytext
-    o "Param . . .",                            -> new SplatNode $1
+    o "PARAM",                                  -> new LiteralNode $1
+    o "PARAM . . .",                            -> new SplatNode $1
   ]

   # A splat that occurs outside of a parameter list.
@@ -206,7 +194,7 @@

   # Variables and properties that can be assigned to.
   SimpleAssignable: [
-    o "Identifier",                             -> new ValueNode $1
+    o "IDENTIFIER",                             -> new ValueNode $1
     o "Value Accessor",                         -> $1.push $2
     o "Invocation Accessor",                    -> new ValueNode $1, [$2]
     o "ThisProperty"
@@ -233,10 +221,10 @@
   # The general group of accessors into an object, by property, by prototype
   # or by array index or slice.
   Accessor: [
-    o "PROPERTY_ACCESS Identifier",             -> new AccessorNode $2
-    o "PROTOTYPE_ACCESS Identifier",            -> new AccessorNode $2, 'prototype'
+    o "PROPERTY_ACCESS IDENTIFIER",             -> new AccessorNode $2
+    o "PROTOTYPE_ACCESS IDENTIFIER",            -> new AccessorNode $2, 'prototype'
     o "::",                                     -> new AccessorNode(new LiteralNode('prototype'))
-    o "SOAK_ACCESS Identifier",                 -> new AccessorNode $2, 'soak'
+    o "SOAK_ACCESS IDENTIFIER",                 -> new AccessorNode $2, 'soak'
     o "Index"
     o "Slice",                                  -> new SliceNode $1
   ]
@@ -338,7 +326,7 @@

   # A reference to a property on *this*.
   ThisProperty: [
-    o "@ Identifier",                           -> new ValueNode new LiteralNode('this'), [new AccessorNode($2)]
+    o "@ IDENTIFIER",                           -> new ValueNode new LiteralNode('this'), [new AccessorNode($2)]
   ]

   # The CoffeeScript range literal.
@@ -392,7 +380,7 @@

   # A catch clause names its error and runs a block of code.
   Catch: [
-    o "CATCH Identifier Block",                 -> [$2, $3]
+    o "CATCH IDENTIFIER Block",                 -> [$2, $3]
   ]

   # Throw an exception object.
@@ -406,12 +394,6 @@
   # the trick.
   Parenthetical: [
     o "( Line )",                               -> new ParentheticalNode $2
-  ]
-
-  # A language extension to CoffeeScript from the outside. We simply pass
-  # it through unaltered.
-  Extension: [
-    o "EXTENSION",                              -> yytext
   ]

   # The condition portion of a while loop.
@@ -441,8 +423,8 @@
   # (optional) reference to the current index. Or, *key, value*, in the case
   # of object comprehensions.
   ForVariables: [
-    o "Identifier",                             -> [$1]
-    o "Identifier , Identifier",                -> [$1, $3]
+    o "IDENTIFIER",                             -> [$1]
+    o "IDENTIFIER , IDENTIFIER",                -> [$1, $3]
   ]

   # The source of a comprehension is an array or object with an optional filter
@@ -476,7 +458,7 @@
   When: [
     o "LEADING_WHEN SimpleArgs Block",            -> new IfNode $2, $3, null, {statement: true}
     o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, null, {statement: true}
-    o "Comment TERMINATOR When",                  -> $3.comment: $1; $3
+    o "COMMENT TERMINATOR When",                  -> $3.comment: $1; $3
   ]

   # The most basic form of *if* is a condition and an action. The following


r/w updates

r/w updates

--- grovepi.coffee
+++ grovepi.coffee
@@ -63,7 +63,7 @@

 		readArgs.push (error, data) ->
 			callback data
-			debug.log "Receive", (Array.slice.call data, 0), 'from port', port
+			debug.log "Receive", (Array.prototype.slice.call data, 0), 'from port', port
 			debug.log "ERROR:", error if error

 		wire[readCmd] readArgs...


Fix for deprecation of context menu

Fix for deprecation of context menu

--- menus/git-plus.cson
+++ menus/git-plus.cson
@@ -1,7 +1,11 @@
 # See https://atom.io/docs/latest/creating-a-package#menus for more details
 'context-menu':
-  '.overlayer':
-    'Git add file': 'git-plus:add'
+  'atom-text-editor': [
+    {
+      'label': 'Git add file',
+      'command': 'git-plus:add'
+    }
+  ]

 'menu': [
   {


[track] changed parameter sending pattern

--- client/landing/app/MainApp/analytic.coffee
+++ client/landing/app/MainApp/analytic.coffee
@@ -3,7 +3,7 @@

   # to-do log to mixpanel after subscription is upgraded
   KD.track = (rest...)->
-    logToGoogle rest
+    logToGoogle rest...

   logToGoogle = (rest...)->
     category = rest[0]


controlsViewSpec: add looks-good test

controlsViewSpec: add looks-good test

--- coffee/test/views/controls_view.spec.coffee
+++ coffee/test/views/controls_view.spec.coffee
@@ -165,3 +165,14 @@
   # TODO: test shell controls subdivision functionality
   # this is waiting on the construction of ControlViews
   xit 'should enable subdivision of shell controls panel', ->
+
+
+describe 'ControlsViews, generally', ->
+  it 'should look good', ->
+    # setup DOM
+    acorn.util.appendCss()
+    $body = $ 'body'
+
+    # add a SplashView into the DOM to see how it looks.
+    view = new acorn.player.ControlsView
+    $body.append view.render().el


Added shims

Added shims

--- test/core/test-amd.coffee
+++ test/core/test-amd.coffee
@@ -4,16 +4,18 @@
       'underscore': "../../vendor/underscore-1.4.4"
       'backbone': "../../vendor/backbone-1.0.0"
       'backbone-modelref': "../../backbone-modelref"
+    shim:
+      underscore:
+        exports: '_'
+      backbone:
+        exports: 'Backbone'
+        deps: ['underscore']
   })

   # library and dependencies
   require ['underscore', 'backbone', 'backbone-modelref', 'qunit_test_runner'], (_, Backbone, ModelRef, runner) ->
-    # window._ = window.Backbone = null # force each test to require dependencies synchronously
-
-    require [
-      './build/test'
-    ], ->
-      runner.start()
+    window._ = window.Backbone = null # force each test to require dependencies synchronously
+    require ['./build/test'], -> runner.start()

 catch error
   alert("AMD tests failed: '#{error}'")


removed unused data

removed unused data

--- test/filterPayload.coffee
+++ test/filterPayload.coffee
@@ -8,13 +8,6 @@
     {id: 6, description: '???'}
     {id: 42, description: 'take over the world'}
     {id: 43, description: 'profit'}
-  ]
-
-simpleList = [
-    'take out the trash'
-    '???'
-    'take over the world'
-    'profit'
   ]

 tests = [


inverted --spawn's default for Windows

inverted --spawn's default for Windows

--- init-nw.coffee
+++ init-nw.coffee
@@ -99,7 +99,7 @@
   D.opts = nomnom.options(
     connect: abbr: 'c', flag: true, metavar: 'HOST[:PORT]'
     listen:  abbr: 'l', flag: true
-    spawn:   abbr: 's', flag: true, default: true
+    spawn:   abbr: 's', flag: true, default: !/^win/i.test process.platform
   ).parse gui.App.argv

   # Mac menu


Bind promptForRestart since it calls a method now

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@github.com>

--- src/main-process/atom-application.coffee
+++ src/main-process/atom-application.coffee
@@ -83,7 +83,7 @@
   initialize: (options) ->
     global.atomApplication = this

-    @config.onDidChange 'core.useCustomTitleBar', @promptForRestart
+    @config.onDidChange 'core.useCustomTitleBar', @promptForRestart.bind(this)

     @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config)
     @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)


Add functionality in API to delete or retry a task

--- api.coffee
+++ api.coffee
@@ -8,6 +8,7 @@

 # regular expression objects
 TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/?$/
+TASK_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/([a-zA-Z0-9\-\_\%]+)(\/[a-zA-Z_]*)?$/
 TAGS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tags\/([a-zA-Z0-9\-\_\%]+)\/?$/
 PRESETS_REGEX = /^\/presets\/([a-zA-Z0-9\-\_\%]+)\/?$/
 FILES_REGEX = /^\/files\/([a-zA-Z0-9\-\_\%\ \.\/\(\)]+)\/?$/
@@ -211,8 +212,38 @@
             response.writeHead(400)
             response.end()
         else
-          response.writeHead 405, {'Allow': 'GET'}
-          response.end('405 Method Not Allowed')
+          response.writeHead 405, {'Allow': 'POST'}
+          response.end('405 Method Not Allowed')
+      else if TASK_REGEX.test(urlParts.pathname)
+        r = TASK_REGEX.exec(urlParts.pathname)
+        deviceId = querystring.unescape(r[1])
+        taskId = mongodb.ObjectID(querystring.unescape(r[2]))
+        action = try querystring.unescape(r[3])
+        if not action? or action is '/'
+          if request.method == 'DELETE'
+            db.tasksCollection.remove({'_id' : taskId}, (err, removedCount) ->
+              if err
+                response.writeHead(500)
+                response.end(err)
+                return
+              response.writeHead(200)
+              response.end()
+            )
+          else
+            response.writeHead 405, {'Allow': 'PUT DELETE'}
+            response.end('405 Method Not Allowed')
+        else if action is '/retry'
+          if request.method == 'POST'
+            db.tasksCollection.update({_id : taskId}, {$unset : {fault : 1}}, (err, count) ->
+              response.writeHead(200)
+              response.end()
+            )
+          else
+            response.writeHead 405, {'Allow': 'POST'}
+            response.end('405 Method Not Allowed')
+        else
+          response.writeHead(404)
+          response.end()
       else if FILES_REGEX.test(urlParts.pathname)
         filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
         if request.method == 'PUT'


fix(featureName): extract path from featureName

Allow for yo fatarrow:feature user/login
The feature is the last segment after the last slash

Closes #5

--- generators/feature/index.coffee
+++ generators/feature/index.coffee
@@ -18,7 +18,7 @@

 	splash: ->
 		@log logo
-		@log chalk.magenta 'Get ready to create your fatarrow feature!'
+		@log chalk.magenta "Get ready to create your '#{@name}' fatarrow feature!"
 		@log()

 	askFor: ->
@@ -59,20 +59,22 @@
 			regex = /\\/g
 			p.replace regex, '/'

-		normalized      = _.str.underscored @name
-		dasherized      = _.str.dasherize normalized
-		@className      = _.str.classify normalized
-		featureName     = _.str.camelize normalized
-		@directory      = path.join 'src', featureName
-		@controllerName = "#{featureName}Controller"
-		@controllerPath = path.join @directory, @controllerName + '.coffee'
-		routeName       = "#{featureName}Routes"
-		@routePath      = path.join @directory, routeName + '.coffee'
-		@routeUrl       = unixifyPath path.join('/', dasherized)
-		@serviceName    = "#{featureName}Service"
-		@servicePath    = path.join @directory, @serviceName + '.coffee'
-		@viewName       = unixifyPath path.join(featureName, dasherized + '.html')
-		@viewPath       = path.join @directory, dasherized + '.html'
+		normalized               = _.str.underscored @name
+		featureDirectory         = _.str.camelize normalized
+		featureDirectorySegments = featureDirectory.split '/'
+		featureName              = featureDirectorySegments[featureDirectorySegments.length - 1]
+		dasherized               = _.str.dasherize featureName
+		@className               = _.str.classify featureName
+		@directory               = path.join 'src', featureDirectory
+		@controllerName          = "#{featureName}Controller"
+		@controllerPath          = path.join @directory, @controllerName + '.coffee'
+		routeName                = "#{featureName}Routes"
+		@routePath               = path.join @directory, routeName + '.coffee'
+		@routeUrl                = unixifyPath path.join('/', featureDirectory)
+		@serviceName             = "#{featureName}Service"
+		@servicePath             = path.join @directory, @serviceName + '.coffee'
+		@viewName                = unixifyPath path.join(featureDirectory, dasherized + '.html')
+		@viewPath                = path.join @directory, dasherized + '.html'

 	scaffold: ->
 		@log '\n', chalk.magenta 'Tree:'


Added basic window.location test, pending more thought.

--- test/renderer_test.coffee
+++ test/renderer_test.coffee
@@ -131,8 +131,21 @@
           expect(res.text).to.contain '<title>false</title>'
           done()

-    context 'when window.location is assigned', ->
-      it 'throws an error'
+    context 'when window.location is assigned to an external URL', ->
+      beforeEach ->
+        src = """
+        <head><title>false</title></head>
+        <body>
+          <script>
+            window.location = "http://www.example.com/static"
+          </script>
+        </body>
+        """
+      it 'throws an error', (done) ->
+        supertest(app).get('/').end (err, res) ->
+          expect(res.text).to.not.contain '<title>true</title>'
+          done()
+

   context 'when renderer.controller has "code.jquery.com" in allowedHosts', ->
     beforeEach ->


add tests for view deletion

add tests for view deletion

--- test/request_tests.coffee
+++ test/request_tests.coffee
@@ -1,6 +1,6 @@
 should = require('chai').Should()
 async = require('async')
-Client = require('../common/test/client').Client
+Client = require('request-json').JsonClient
 app = require('../server')

 client = new Client("http://localhost:8888/")
@@ -18,8 +18,6 @@
     delete @body
     delete @response

-parseBody = (response, body) ->
-    if typeof body is "object" then body else JSON.parse body

 randomString = (length=32) ->
     string = ""
@@ -106,7 +104,7 @@
                     done()

             it "Then error 404 should be returned", ->
-                @response.statusCode.should.equal
+                @response.statusCode.should.equal 404

         describe "Access to an existing view : every_docs", (done) ->
             before cleanRequest
@@ -114,7 +112,7 @@
             it "When I send a request to access view every_docs", (done) ->
                 client.get "request/every_docs/", (error, response, body) =>
                     response.statusCode.should.equal 200
-                    @body = parseBody response, body
+                    @body = body
                     done()

             it "Then I should have 101 documents returned", ->
@@ -126,7 +124,7 @@
             it "When I send a request to access view every_docs", (done) ->
                 client.get "request/even_num/", (error, response, body) =>
                     response.statusCode.should.equal 200
-                    @body = parseBody response, body
+                    @body = body
                     done()

             it "Then I should have 51 documents returned", ->
@@ -159,6 +157,23 @@
             it "And I should retrieve the good values", (done) ->
                 client.get "request/even_num/", (error, response, body) =>
                     response.statusCode.should.equal 200
-                    @body = parseBody response, body
+                    @body = body
                     @body.should.have.length 50
                     done()
+
+    describe "Deletion of an existing view", ->
+        before cleanRequest
+
+        it "When I send a request to delete view even_num", (done) ->
+            client.del "request/even_num/", (error, response, body) =>
+                    response.statusCode.should.equal 204
+
+        it "And I send a request to access view even_num", (done) ->
+            client.get "request/even_num", (error, response, body) =>
+                @response = response
+                done()
+
+        it "Then error 404 should be returned", ->
+            @response.statusCode.should.equal 404
+
+


fixed typo

fixed typo

--- grammars/language-klartext.cson
+++ grammars/language-klartext.cson
@@ -1,6 +1,5 @@
 'fileTypes': [
   'klartext'
-  'h'
   'H'
 ]
 'firstLineMatch': '^(\\d+) +(BEGIN PGM) (.*\\.H) (MM|INCH) ?$'


commenting out the failing test for the time being, to be fixed later.

--- test/test_comments.coffee
+++ test/test_comments.coffee
@@ -67,11 +67,12 @@
   Kind of like a heredoc.
 ###

-func = ->
-  ###
-  Another block comment.
-  ###
-  code
+# FIXME:
+# func = ->
+#   ###
+#   Another block comment.
+#   ###
+#   code

 func = ->
   one = ->


adding a top-level object literal test for Issue #542

--- test/test_literals.coffee
+++ test/test_literals.coffee
@@ -96,6 +96,10 @@
 ok not obj.not()

+# Top-level object literal doesn't break things.
+obj: 1
+
+
 # Funky indentation within non-comma-seperated arrays.
 result = [['a']
  {b: 'c'}]


Fix depth system

Fix depth system

When params or body exist, increment depth at child node.

--- lib/Refactor.coffee
+++ lib/Refactor.coffee
@@ -34,7 +34,7 @@
     second
     base
     name
-  }, @parentNode, @depth = -1, @type = 'body') ->
+  }, @parentNode, @depth = 0, @type = 'body') ->

     @isRoot = !@parentNode?
     @hasScope = @isRoot or body?
@@ -46,12 +46,12 @@
       console.log pad '', 50, '='

     if @hasScope
-      @depth++
+      # @depth++
       if Refactor.verbose
         console.log "#{pad '', 15}|#{pad 'SCOPE', 10}|#{@getIndent()}-"

     if params?
-      nodes = (new Node param, @, @depth, 'param' for param in params)
+      nodes = (new Node param, @, @depth + 1, 'param' for param in params)
       @children = @children.concat nodes
       @params = @params.concat nodes

@@ -79,9 +79,9 @@
     if name?
       @children.push new BottomNode name, @, @depth, 'name'
     if parent?
-      @parentNode.children.push new Node parent, @, @depth - 1, 'parent'
+      @parentNode.children.push new Node parent, @, @depth, 'parent'
     if body?
-      @children.push new Node body, @, @depth, 'body'
+      @children.push new Node body, @, @depth + 1, 'body'

   find: (range) ->
     for child, i in @children


CLI Fixing the 'server' command output log

--- cli/commands/server.coffee
+++ cli/commands/server.coffee
@@ -90,7 +90,7 @@

   server.listen(port)

-  print "Listening: http://127.0.0.0:#{port}\n"+
+  print "Listening: http://127.0.0.1:#{port}\n"+
     "Press Ctrl+C to hung up".grey


Add support for CS@2 when looking up local CS

--- lib/providers/coffee-provider.coffee
+++ lib/providers/coffee-provider.coffee
@@ -16,11 +16,17 @@
       return require('coffee-script')

 # Check if there is a local coffee-script package, use that package if it exists
+# Convert this lookup to an option
 maybeGetLocalCoffeescript = (filepath) ->
   basedir = path.dirname(filepath)

   rst = null
-  try rst = path.dirname(resolve.sync('coffee-script', { basedir }))
+  try
+    # Check for CS v1
+    rst = path.dirname(resolve.sync('coffee-script', { basedir }))
+    if not rst
+      rst = path.dirname(resolve.sync('coffeescript', { basedir }))
+
   if rst then require rst

 module.exports =


restore location.reload

restore location.reload

--- components/clock/test/view.coffee
+++ components/clock/test/view.coffee
@@ -28,6 +28,7 @@
   after ->
     benv.teardown()
     Backbone.sync.restore()
+    location.reload.restore()

   beforeEach ->
     @triggerSpy = sinon.stub()


Optional results limit

Optional results limit

--- components/search_bar/view.coffee
+++ components/search_bar/view.coffee
@@ -13,7 +13,8 @@
     return unless @$el.length

     # Search takes a fair_id param specific to fairs. Doesn't work for other models
-    { @mode, @restrictType, @$input, @fairId, @includePrivateResults } = options
+    { @mode, @restrictType, @$input, @fairId, @includePrivateResults, @limit } =
+      _.defaults(options, limit: 10)

     @$input ?= @$('input')
     throw new Error('Requires an input field') unless @$input?
@@ -69,7 +70,7 @@

   setupBloodHound: ->
     @hound = new Bloodhound
-      limit          : 10
+      limit          : @limit
       datumTokenizer : Bloodhound.tokenizers.obj.whitespace 'value'
       queryTokenizer : Bloodhound.tokenizers.whitespace
       remote:


Refactored after comments.

Refactored after comments.

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -397,7 +397,7 @@

   showGroupSubmissionView:->

-    verifySlug = ()->
+    verifySlug = ->
       slugInput = modal.modalTabs.forms["General Settings"].inputs.Slug
       KD.remote.api.JName.one
         name: slugInput.getValue()
@@ -407,11 +407,13 @@
         else
           slugInput.unsetClass 'slug-taken'

-    makeSlug = ()=>
+    makeSlug = =>
       titleInput = modal.modalTabs.forms["General Settings"].inputs.Title
       slugInput = modal.modalTabs.forms["General Settings"].inputs.Slug
       KD.remote.api.JGroup.suggestUniqueSlug titleInput.getValue(), (err, newSlug)->
-        slugInput.setValue newSlug
+        if err then slugInput.setValue ''
+        else
+          slugInput.setValue newSlug

     getGroupType = ->
       modal.modalTabs.forms["Select group type"].inputs.type.getValue()
@@ -558,8 +560,9 @@
               #   defaultValue         : no

     modal = new KDModalViewWithForms modalOptions
-    modal.modalTabs.forms["General Settings"].on "FormValidationFailed", ->
-      modal.modalTabs.forms["General Settings"].buttons.Save.hideLoader()
+    form = modal.modalTabs.forms["General Settings"]
+    form.on "FormValidationFailed", ->
+      form.buttons.Save.hideLoader()

   handleError =(err, buttons)->
     unless buttons


VariablesEditorView: update on default text

--- client/stacks/lib/views/stacks/editors/variableseditorview.coffee
+++ client/stacks/lib/views/stacks/editors/variableseditorview.coffee
@@ -6,15 +6,14 @@

     unless options.content
       options.content = '''
-        # Define custom variables here
         # You can define your custom variables, and use them in your stack template.
         # These variables will not be visible to your non-admins.
         #
-        # This is a YAML file which you can define key-value pairs like;
+        # This is a YAML file, you can define a key-value pair like this here;
         #
         #   foo: bar
         #
-        # and you can use this variable in your stack template like this;
+        # and you can use that variable in your stack template as below;
         #
         #   ${var.custom_foo}


fix(rb-modal): set scope.a.show to false when the modal closes

--- src/client/scripts/controllers/components/modal.coffee
+++ src/client/scripts/controllers/components/modal.coffee
@@ -1,5 +1,5 @@
-angular.module('rapid-build').controller 'rbModalController', ['$scope', '$element',
-	($scope, $element) ->
+angular.module('rapid-build').controller 'rbModalController', ['$scope', '$element', '$timeout',
+	($scope, $element, $timeout) ->
 		# Builder
 		# =======
 		createMarkup = ->
@@ -34,7 +34,25 @@
 		# =======
 		markupWatch = $scope.$watch 'a', (newVal, oldVal) ->
 			$scope.markup = createMarkup()
+			showHelper newVal.show
 		, true
+
+		# Show Helper
+		# ===========
+		showHelper = (show) -> # needed for builder
+			return unless show
+			# console.log 'SHOW:', show
+
+			updateShow = (evt) ->
+				$scope.a.show = evt.detail.show
+				$scope.$apply()
+				$timeout.cancel timer
+				@removeEventListener 'show-changed', updateShow
+
+			timer = $timeout ->
+				modal = $element[0].querySelector 'rb-modal[id^="built"]'
+				modal.addEventListener 'show-changed', updateShow
+			, 75

 		# Event Handlers
 		# ==============


fix a sync race condition

fix a sync race condition

--- src/smc-hub/postgres-user-queries.coffee
+++ src/smc-hub/postgres-user-queries.coffee
@@ -1160,6 +1160,8 @@
                                  where, json_fields, account_id, client_query, cb) =>
         dbg = @_dbg("_user_get_query_changefeed(table='#{table}')")
         dbg()
+        # WARNING: always call changes.cb!  Do not do something like f = changes.cb, then call f!!!!
+        # This is because the value of changes.cb may be changed by the caller.
         if not misc.is_object(changes)
             cb("changes must be an object with keys id and cb")
             return
@@ -1353,7 +1355,9 @@
             return

         _query_opts = {}  # this will be the input to the @_query command.
-        result = undefined
+        locals =
+            result     : undefined
+            changes_cb : undefined
         async.series([
             (cb) =>
                 if client_query.get.check_hook?
@@ -1381,16 +1385,34 @@
                 misc.merge(_query_opts, x)

                 if opts.changes?
+                    locals.changes_cb = opts.changes.cb
+                    locals.changes_queue = []
+                    # see note about why we do the following at the bottom of this file
+                    opts.changes.cb = (err, obj) ->
+                        locals.changes_queue.push({err:err, obj:obj})
                     @_user_get_query_changefeed(opts.changes, table, primary_keys,
                                                 opts.query, _query_opts.where, json_fields,
                                                 opts.account_id, client_query, cb)
                 else
                     cb()
             (cb) =>
-                @_user_get_query_do_query _query_opts, client_query, opts.query, opts.multi, json_fields, (err, x) =>
-                    result = x; cb(err)
+                @_user_get_query_do_query _query_opts, client_query, opts.query, opts.multi, json_fields, (err, result) =>
+                    if err
+                        cb(err)
+                        return
+                    locals.result = result
+                    cb()
         ], (err) =>
-            opts.cb(err, result if not err)
+            if err
+                opts.cb(err)
+                return
+            opts.cb(undefined, locals.result)
+            if opts.changes?
+                opts.changes.cb = locals.changes_cb
+                ##dbg("sending queued #{JSON.stringify(locals.changes_queue)}")
+                for {err, obj} in locals.changes_queue
+                    ##dbg("sending queued changes #{JSON.stringify([err, obj])}")
+                    opts.changes.cb(err, obj)
         )

     ###
@@ -1520,3 +1542,15 @@
                             # This is so the project can find out that the user wants to save a file (etc.)
                             db.ensure_connection_to_project?(project_id)

+###
+Note about opts.changes.cb:
+
+Regarding sync, what was happening I think is:
+ - (a) https://github.com/sagemathinc/cocalc/blob/master/src/smc-hub/postgres-user-queries.coffee#L1384 starts sending changes
+ - (b) https://github.com/sagemathinc/cocalc/blob/master/src/smc-hub/postgres-user-queries.coffee#L1393 sends the full table.
+
+(a) could result in changes actually getting to the client before the table itself has been initialized.  The client code assumes that it only gets changes *after* the table is initialized.  The browser client seems to be smart enough that it detects this situation and resets itself, so the browser never gets messed up as a result.
+However, the project definitely does NOT do so well, and it can get messed up.  Then it has a broken version of the table, missing some last minute change.    It is broken until the project forgets about that table entirely, which is can be a pretty long time (or project restart).
+
+My fix is to queue up those changes on the server, then only start sending them to the client **after** the (b) query is done.  I tested this by using setTimeout to manually delay (b) for a few seconds, and fully seeing the "file won't save problem".   The other approach would make it so clients are more robust against getting changes first.  However, it would take a long time for all clients to update (restart all projects), and it's an annoying assumption to make in general -- we may have entirely new clients later and they could make the same bad assumptions about order...
+###


Adjust the selection if the current and previous DOM ranges occupy the same cursor position or if selection is uneditable

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -41,7 +41,14 @@
       else
         textRange.moveStart(granularity, -1)
       textRange.select()
-    @selectionDidChange(null, direction)
+    @selectionObserver.tick()
+
+  # TODO: Combine with #expandSelectionInDirectionWithGranularity and add IE compatibility
+  adjustSelectionInDirectionWithGranularity: (direction, granularity) ->
+    return unless selection = getDOMSelection()
+    alter = if selection.isCollapsed then "move" else "extend"
+    selection.modify(alter, direction, granularity)
+    @selectionObserver.tick()

   lock: ->
     if @lockCount++ is 0
@@ -62,29 +69,16 @@

   # Selection observer delegate

-  selectionDidChange: (domRange, direction) ->
-    @adjustSelectionInDirection(direction) if direction
-    @updateCurrentLocationRange()
+  selectionDidChange: (range, previousRange) ->
+    direction = getDirectionFromDOMRanges(range, previousRange)
+
+    if direction and @rangeWithinElement(range) and
+         (domRangesHaveSameCursorPositionForDirection(range, previousRange, direction) or domSelectionIsUneditable())
+      @adjustSelectionInDirectionWithGranularity(direction, "character")
+    else
+      @updateCurrentLocationRange()

   # Private
-
-  adjustSelectionInDirection: (direction) ->
-    selection = window.getSelection()
-    focusElement = DOM.findElementForContainerAtOffset(selection.focusNode, selection.focusOffset)
-    return unless focusElement?.isContentEditable
-
-    selectionNeedsAdjustment = =>
-      result = if focusElement.trixCursorTarget and @previousFocus?
-        focusElement is @previousFocus.element and selection.focusOffset isnt @previousFocus.offset
-      else
-        not focusElement.isContentEditable and @element.contains(focusElement)
-
-      @previousFocus = element: focusElement, offset: selection.focusOffset
-      focusElement = DOM.findElementForContainerAtOffset(selection.focusNode, selection.focusOffset)
-      result
-
-    alter = if selection.isCollapsed then "move" else "extend"
-    selection.modify(alter, direction, "character") while selectionNeedsAdjustment()

   updateCurrentLocationRange: (domRange = getDOMRange()) ->
     locationRange = @createLocationRangeFromDOMRange(domRange)
@@ -235,6 +229,10 @@
   getDOMRange = ->
     getDOMSelection()?.getRangeAt(0)

+  getClientRects = ->
+    rects = getDOMRange()?.getClientRects()
+    rects if rects?.length
+
   # ClientRect position properties should be relative to the viewport,
   # but in some browsers (like mobile Safari), they're relative to the body.
   getRectTop = ->
@@ -253,3 +251,24 @@
       "backward"
     else if range.compareBoundaryPoints(Range.END_TO_END, previousRange) is 1
       "forward"
+
+  domRangesHaveSameCursorPositionForDirection = (range, otherRange, direction) ->
+    rects = range.getClientRects()
+    otherRects = otherRange.getClientRects()
+
+    if rects.length and otherRects.length
+      if direction is "backward"
+        a = rects[0]
+        b = otherRects[0]
+      else
+        a = rects[rects.length - 1]
+        b = otherRects[otherRects.length - 1]
+
+      for prop in ["left", "right", "bottom", "width"]
+        return false if Math.ceil(a[prop]) isnt Math.ceil(b[prop])
+      true
+
+  domSelectionIsUneditable = ->
+    selection = getDOMSelection()
+    focusElement = DOM.findElementForContainerAtOffset(selection.focusNode, selection.focusOffset)
+    not focusElement?.isContentEditable


commandstream: Support for removenode

commandstream: Support for removenode

--- lib/commandstream.coffee
+++ lib/commandstream.coffee
@@ -163,7 +163,6 @@
   runtime: {}
   microflo: {}

-# TODO: support graph.removenode
 commands.graph.addnode = (payload, buffer, index, componentLib) ->
   nodeName = payload.id
   componentName = payload.component
@@ -174,6 +173,14 @@
   # Add normal component
   parentId = undefined # subgraph not supported yet
   index += writeCmd(buffer, index, cmdFormat.commands.CreateComponent.id, comp.id, parentId or 0)
+  return index
+
+commands.graph.removenode = (payload, buffer, index, componentLib, nodeMap) ->
+  nodeName = payload.id
+  nodeId = nodeMap[nodeName].id
+
+  # Add normal component
+  index += writeCmd(buffer, index, cmdFormat.commands.RemoveNode.id, nodeId)
   return index

 commands.graph.addedge = (payload, buffer, index, componentLib, nodeMap, componentMap) ->
@@ -306,6 +313,15 @@
     payload:
       id: nodeName
       component: component
+  return m
+responses.NodeRemoved = (componentLib, graph, cmdData) ->
+  nodeName = nodeNameById(graph.nodeMap, cmdData.readUInt8(1))
+  m =
+    protocol: 'graph'
+    command: 'removenode'
+    payload:
+      id: nodeName
+  return m

 responses.NodesConnected = (componentLib, graph, cmdData) ->
   # TODO: implement


feat(regexp): catch info message

feat(regexp): catch info message

--- lib/linter-jshint.coffee
+++ lib/linter-jshint.coffee
@@ -19,7 +19,7 @@
     '.+?: line (?<line>[0-9]+), col (?<col>[0-9]+), ' +
     '(?<message>.+) ' +
     # capture error, warning and code
-    '\\(((?<error>E)|(?<warning>W))(?<code>[0-9]+)\\)'+
+    '\\(((?<error>E)|(?<warning>W)|(?<level>I))(?<code>[0-9]+)\\)'+
     ')'

   isNodeExecutable: yes


添加模块模板创建方式

添加模块模板创建方式

--- lib/module/module.coffee
+++ lib/module/module.coffee
@@ -66,26 +66,49 @@
         if isSuccess is yes
           entryFile.writeSync(htmlString)
           @addProjectModule info
-          isInProject = false
-          atom.project.getDirectories().forEach (dir) =>
-            # console.log dir,filePath
-            flag = dir.contains filePath
-            # console.log flag
-            if flag
-              isInProject = flag
-
-          console.log isInProject
-          if isInProject is no
-            atom.project.addPath(filePath)
-            Util.rumAtomCommand 'tree-view:toggle' if ChameleonBox.$('.tree-view-resizer').length is 0
+          @openTreeView filePath
           alert "新建模块成功！"
           @chameleonBox.closeView()
       # .finally =>
         # console.log 'CreateModule Success',@

   CreateSimpleModule: (options) ->
+    console.log 'SimpleModule'
+    console.log options
+    @chameleonBox.closeView()
+

   CreateTemplateModule: (options) ->
+    console.log 'tmp'
+    console.log options
+    info = options.moduleInfo
+    sourcePath = pathM.join desc.getFrameworkPath(),options.source
+    targetPath = pathM.join info.modulePath,info.moduleId
+    copyCallback = (err) =>
+      return console.error err if err
+      console.log 'success'
+      moduleConfigPath = pathM.join targetPath,desc.moduleConfigFileName
+      moduleConfig = Util.readJsonSync moduleConfigPath
+      console.log moduleConfig
+      moduleConfig = @editModuleConfig moduleConfig,info
+      Util.writeJson moduleConfigPath,moduleConfig,(err) ->
+        console.log err
+      @addProjectModule info
+      @openTreeView targetPath
+      alert "新建模块成功！"
+    console.log sourcePath,targetPath
+    Util.copy sourcePath,targetPath,copyCallback
+    @chameleonBox.closeView()
+
+  editModuleConfig: (config,info) ->
+    config.template = config.identifier
+    config.name = info.moduleName
+    config.identifier = info.moduleId
+    config.version = desc.minVersion
+    config.build = 1
+    config.hidden ?= false
+    config.dependencies ?= {}
+    return config

   addProjectModule: (moduleInfo) ->
     console.log moduleInfo
@@ -96,3 +119,17 @@
       appConfig.modules[moduleInfo.moduleId] = desc.minVersion
       Util.writeJson projectConfigPath,appConfig,(err) ->
         console.log err
+
+  openTreeView: (filePath) ->
+    isInProject = false
+    atom.project.getDirectories().forEach (dir) =>
+      # console.log dir,filePath
+      flag = dir.contains filePath
+      # console.log flag
+      if flag
+        isInProject = flag
+
+    console.log isInProject
+    if isInProject is no
+      atom.project.addPath(filePath)
+      Util.rumAtomCommand 'tree-view:toggle' if ChameleonBox.$('.tree-view-resizer').length is 0


only add disabled packages if they werent loaded

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -103,7 +103,8 @@
         try
           metadata = CSON.readFileSync(metadataPath)
           name = metadata?.name ? packageName
-          @packages.push({name, metadata, path: packagePath})
+          unless _.findWhere(@packages, {name})
+            @packages.push({name, metadata, path: packagePath})

     @packages.sort (pack1, pack2) =>
       title1 = @packageManager.getPackageTitle(pack1)


support roswell

support roswell

--- lib/swank-starter.coffee
+++ lib/swank-starter.coffee
@@ -15,6 +15,7 @@
       return false
     command = @lisp
     args = []
+    args.push 'run' if command.match(/ros/)
     args.push '--load' unless command.match(/clisp/)  # CLISP does not have a --load option
     args.push @swank_script
     @process = new BufferedProcess({


Coffeeish way bugfix for #23

Coffeeish way bugfix for #23

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -53,9 +53,7 @@
       cb?(err, res)

   getPackages: ->
-    packages = atom.packages.getLoadedPackages()
-    for name,info of packages
-      continue unless packages.hasOwnProperty(name)
+    for own name,info of atom.packages.getLoadedPackages()
       {name, version, theme} = info.metadata
       {name, version, theme}


fix(tasks/plugins): Done with error handling

--- lib/tasks/plugins.coffee
+++ lib/tasks/plugins.coffee
@@ -42,7 +42,10 @@
           success = false
         next()
     , (err) ->
-      done(success || err)
+      if err?
+        done err
+      else
+        done success

   ###
   grunt.registerMultiTask 'sauce-connect', 'Run or kill sauce connect', ->


TOUCH IT #12

TOUCH IT #12

--- lib/touch-gesture.coffee
+++ lib/touch-gesture.coffee
@@ -33,7 +33,7 @@
     @element.addEventListener name, (e) => @fortifyEvent e,callBack

   startTouches: null
-  sum : (event) ->
+  sum : (touches) ->
     sumX = 0
     sumY = 0
     for touch in touches


use negative progress instead of `determinate=false`

--- lib/util/progress.coffee
+++ lib/util/progress.coffee
@@ -40,8 +40,8 @@
 # Properties of a progress bar object:
 #
 #    .id          - A string uniquely identifing the progress bar. Required.
-#    .determinate - Boolean, determines if progress is shown.
-#    .progress    - Number between 0 and 1. Required for determinate progress bars.
+#    .progress    - Number ∈ [-∞, 1]. If negative, the progress bar will be
+#                   indeterminate.
 #    .leftText    - String displayed to the left of the progress bar (e.g. name).
 #    .rightText   - String displayed to the right of the progress bar (e.g. remaining time).
 #    .msg         - String displayed when hovering over the progress bar in the stack.
@@ -93,8 +93,8 @@
     oldp = @stack[i]
     @stack[i] = p
     @emitter.emit 'did-update-progress', p
-    # if p.determinate changes, update the stack display
-    @emitter.emit 'did-update-stack' unless oldp.determinate is p.determinate
+    # if determinate-ness changes, update the stack display
+    @emitter.emit 'did-update-stack' unless oldp.progress*p.progress > 0

   emptyStack: () ->
     @stack = []
@@ -102,16 +102,15 @@

   emptyProgress: (id = 'empty') ->
     id: id
-    determinate: true
     progress: 0

   indeterminateProgress: (id = 'indeterminate') ->
     id: id
-    determinate: false
+    progress: -1

   # update logic
   hasNoDeterminateBars: ->
-    @stack.filter((p) => p.determinate).length is 0
+    @stack.filter((p) => p.progress >= 0).length is 0

   onDidUpdateStack: (f) -> @emitter.on 'did-update-stack', f

@@ -128,7 +127,7 @@

     updateView = (prg) =>
       return unless p.id is prg.id
-      if prg.determinate
+      if prg.progress >= 0
         prog.setAttribute 'value', prg.progress
       else
         prog.removeAttribute 'value'
@@ -198,7 +197,7 @@
       # backwards iteration
       for i in [0...@stack.length].reverse()
         p = @stack[i]
-        continue unless p.determinate
+        continue unless p.progress >= 0
         table.appendChild @tableRowView p
         @emitter.emit 'did-update-progress', p

@@ -212,7 +211,7 @@
     @onDidUpdateStack =>
       span.removeChild span.firstChild
       # find the first determinate progress bar
-      global = @stack.find (p) => p.determinate
+      global = @stack.find (p) => p.progress >= 0
       # if there is none, use the first one in the stack
       global = @stack[0] unless global?
       # display an empty progress bar if the stack is empty


:art: Views/Message: Split renderLink into separate function

--- lib/views/message.coffee
+++ lib/views/message.coffee
@@ -4,34 +4,32 @@
   initialize: (@message, @addPath)->

   attachedCallback: ->
-
-    # The link
-    if @message.file
-      @message.displayFile = @message.file
-      try
-        atom.project.getPaths().forEach (path) =>
-          return unless @message.file.indexOf(path) is 0
-          @message.displayFile = @message.file.substr( path.length + 1 ) # Remove the trailing slash as well
-          throw null
-      file = document.createElement 'a'
-      file.addEventListener 'click', Message.onClick.bind(null, @message.file, @message.position)
-      if @message.position
-        file.textContent =
-          'at line ' + @message.position[0][0] + ' col ' + @message.position[0][1] + ' '
-      if @addPath
-        file.textContent += 'in ' + @message.displayFile
-    else
-      file = null
     @appendChild Message.renderRibbon(@message.type)
     @appendChild Message.renderMessage(@message)
-    @appendChild file if file
+    @appendChild Message.renderLink(@message, @addPath) if @message.file

-  @renderRibbon: (Type)->
+  @renderLink: (message, addPath)->
+    displayFile = message.file
+    try
+      atom.project.getPaths().forEach (path) =>
+        return if message.file.indexOf(path) isnt 0 or displayFile isnt message.file # Avoid double replacing
+        displayFile = message.file.substr( path.length + 1 ) # Remove the trailing slash as well
+        throw null
+    El = document.createElement 'a'
+    El.addEventListener 'click', ->
+      Message.onClick message.file, message.position
+    if message.position
+      El.textContent = "at line #{message.position[0][0]} col #{message.position[0][1]} "
+    if addPath
+      El.textContent += "in #{displayFile}"
+    El
+
+  @renderRibbon: (type)->
     El = document.createElement 'span'
     El.classList.add 'badge'
     El.classList.add 'badge-flexible'
-    El.classList.add "badge-#{Type.toLowerCase()}"
-    El.textContent = Type
+    El.classList.add "badge-#{type.toLowerCase()}"
+    El.textContent = type
     El

   @renderMessage: (message)->


Simplify hud.coffee

Simplify hud.coffee

--- content_scripts/hud.coffee
+++ content_scripts/hud.coffee
@@ -6,7 +6,9 @@
   tween: null
   hudUI: null
   findMode: null
-  abandon: -> @hudUI?.hide false
+  abandon: ->
+    if @hudUI
+      @hudUI.hide(false)

   pasteListener: null # Set by @pasteFromClipboard to handle the value returned by pasteResponse

@@ -15,7 +17,10 @@
   # it doesn't sit on top of horizontal scrollbars like Chrome's HUD does.

   init: (focusable = true) ->
-    @hudUI ?= new UIComponent "pages/hud.html", "vimiumHUDFrame", ({data}) => this[data.name]? data
+    @hudUI ?= new UIComponent "pages/hud.html", "vimiumHUDFrame", ({data}) =>
+      if (this[data.name])
+        this[data.name](data)
+      # this[data.name]? data
     @tween ?= new Tween "iframe.vimiumHUDFrame.vimiumUIComponentVisible", @hudUI.shadowDOM
     if focusable
       @hudUI.toggleIframeElementClasses "vimiumNonClickable", "vimiumClickable"
@@ -27,11 +32,13 @@
       getComputedStyle(@hudUI.iframeElement).display
     else
       @hudUI.toggleIframeElementClasses "vimiumClickable", "vimiumNonClickable"
+    return

   showForDuration: (text, duration) ->
     @show(text)
     @_showForDurationTimerId = setTimeout((=> @hide()), duration)
+    return

   show: (text) ->
     DomUtils.documentComplete =>
@@ -40,12 +47,16 @@
       clearTimeout(@_showForDurationTimerId)
       @hudUI.activate {name: "show", text}
       @tween.fade 1.0, 150
+      return
+    return

   showFindMode: (@findMode = null) ->
     DomUtils.documentComplete =>
       @init()
       @hudUI.activate name: "showFindMode"
       @tween.fade 1.0, 150
+      return
+    return

   search: (data) ->
     # NOTE(mrmr1993): On Firefox, window.find moves the window focus away from the HUD. We use postFindFocus
@@ -56,6 +67,7 @@
     matchCount = if FindMode.query.parsedQuery.length > 0 then FindMode.query.matchCount else 0
     showMatchText = FindMode.query.rawQuery.length > 0
     @hudUI.postMessage {name: "updateMatchesCount", matchCount, showMatchText}
+    return

   # Hide the HUD.
   # If :immediate is falsy, then the HUD is faded out smoothly (otherwise it is hidden immediately).
@@ -66,9 +78,13 @@
       clearTimeout @_showForDurationTimerId
       @tween.stop()
       if immediate
-        if updateIndicator then Mode.setIndicator() else @hudUI.hide()
+        if updateIndicator
+           Mode.setIndicator()
+        else
+           @hudUI.hide()
       else
         @tween.fade 0, 150, => @hide true, updateIndicator
+      return

   # These parameters describe the reason find mode is exiting, and come from the HUD UI component.
   hideFindMode: ({exitEventIsEnter, exitEventIsEscape}) ->
@@ -81,7 +97,9 @@

     focusNode = DomUtils.getSelectionFocusElement()
     document.activeElement?.blur()
-    focusNode?.focus?()
+
+    if focusNode and focusNode.focus
+      focusNode.focus()

     if exitEventIsEnter
       FindMode.handleEnter()
@@ -93,7 +111,9 @@
       postExit = FindMode.handleEscape

     @findMode.exit()
-    postExit?()
+    if postExit
+      postExit()
+    return

   # These commands manage copying and pasting from the clipboard in the HUD frame.
   # NOTE(mrmr1993): We need this to copy and paste on Firefox:
@@ -104,25 +124,31 @@
     DomUtils.documentComplete =>
       @init()
       @hudUI.postMessage {name: "copyToClipboard", data: text}
+      return
+    return

   pasteFromClipboard: (@pasteListener) ->
     DomUtils.documentComplete =>
       @init()
       @tween.fade 0, 0
       @hudUI.postMessage {name: "pasteFromClipboard"}
+      return
+    return

   pasteResponse: ({data}) ->
     # Hide the HUD frame again.
     @hudUI.toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden"
     @unfocusIfFocused()
     @pasteListener data
+    return

   unfocusIfFocused: ->
     # On Firefox, if an <iframe> disappears when it's focused, then it will keep "focused",
     # which means keyboard events will always be dispatched to the HUD iframe
-    if @hudUI?.showing
+    if @hudUI && @hudUI.showing
       @hudUI.iframeElement.blur()
       window.focus()
+    return

 class Tween
   opacity: 0
@@ -152,15 +178,20 @@
       if (elapsed >= duration)
         clearInterval @intervalId
         @updateStyle toAlpha
-        onComplete?()
+        if onComplete
+          onComplete()
       else
         value = (elapsed / duration) * alphaStep + fromAlpha
         @updateStyle value
+      return

     @updateStyle @opacity
     @intervalId = setInterval performStep, 50
-
-  stop: -> clearInterval @intervalId
+    return
+
+  stop: ->
+    clearInterval @intervalId
+    return

   updateStyle: (@opacity) ->
     @styleElement.innerHTML = """
@@ -168,6 +199,7 @@
         opacity: #{@opacity};
       }
     """
+    return

 root = exports ? (window.root ?= {})
 root.HUD = HUD


Update Atom config

Update Atom config

--- files/.atom/config.cson
+++ files/.atom/config.cson
@@ -17,11 +17,12 @@
     ]
     telemetryConsent: "no"
     titleBar: "custom"
-    useTreeSitterParsers: true
   editor:
     autoIndentOnPaste: false
+    fontSize: 15
     invisibles: {}
     showIndentGuide: true
+    showInvisibles: true
     softWrap: true
   "exception-reporting":
     userId: "44fd5f6f-7665-42b3-8855-24c5dde06098"
@@ -30,15 +31,16 @@
   linter: {}
   "linter-eslint":
     disableWhenNoEslintrcFileInPath: true
-    global:
-      useGlobalEslint: true
+    disabling: {}
+    global: {}
   "linter-shellcheck":
     enableNotice: true
     shellcheckExecutablePath: "/usr/local/bin/shellcheck"
   "linter-ui-default":
     alwaysTakeMinimumSpace: true
-    panelHeight: 208
+    panelHeight: 128
     panelRepresents: "Entire Project"
+    showPanel: true
   "markdown-preview": {}
   minimap:
     plugins:
@@ -53,6 +55,8 @@
   "one-dark-ui":
     tabSizing: "Even"
   "spell-check": {}
+  "split-diff":
+    ignoreWhitespace: true
   tabs:
     usePreviewTabs: true
   "theme-switcher":


Asynchronous anchors recalculating added

--- impl/base/level1/anchors.coffee
+++ impl/base/level1/anchors.coffee
@@ -11,6 +11,33 @@
 	NOP = ->

 	MAX_LOOPS = 10
+
+	queueIndex = 0
+	queues = [[], []]
+	queue = queues[queueIndex]
+	pending = false
+
+	updateItems = ->
+		pending = false
+		currentQueue = queue
+		queue = queues[++queueIndex % queues.length]
+
+		while currentQueue.length
+			anchor = currentQueue.pop()
+			anchor.pending = false
+			anchor.update()
+		return
+
+	update = ->
+		if @pending
+			return
+
+		@pending = true
+		queue.push @
+		unless pending
+			setImmediate updateItems
+			pending = true
+		return

 	getItemProp =
 		left: 'x'
@@ -171,57 +198,57 @@
 	onParentChange = (oldVal) ->
 		if oldVal
 			for handler in getTargetWatchProps[@line].parent
-				oldVal[handler].disconnect @update, @
+				oldVal[handler].disconnect update, @

 		if val = @targetItem = @item._parent
 			for handler in getTargetWatchProps[@line].parent
-				val[handler] @update, @
-
-		@update()
+				val[handler] update, @
+
+		update.call @
 		return

 	onNextSiblingChange = (oldVal) ->
 		if oldVal
 			for handler in getTargetWatchProps[@line].sibling
-				oldVal[handler].disconnect @update, @
+				oldVal[handler].disconnect update, @

 		if val = @targetItem = @item._nextSibling
 			for handler in getTargetWatchProps[@line].sibling
-				val[handler] @update, @
-
-		@update()
+				val[handler] update, @
+
+		update.call @
 		return

 	onPreviousSiblingChange = (oldVal) ->
 		if oldVal
 			for handler in getTargetWatchProps[@line].sibling
-				oldVal[handler].disconnect @update, @
+				oldVal[handler].disconnect update, @

 		if val = @targetItem = @item._previousSibling
 			for handler in getTargetWatchProps[@line].sibling
-				val[handler] @update, @
-
-		@update()
+				val[handler] update, @
+
+		update.call @
 		return

 	onChildInsert = (child) ->
-		child.onVisibleChange @update, @
+		child.onVisibleChange update, @
 		if @source is 'fillWidth'
-			child.onWidthChange @update, @
+			child.onWidthChange update, @
 		if @source is 'fillHeight'
-			child.onHeightChange @update, @
-
-		@update()
+			child.onHeightChange update, @
+
+		update.call @
 		return

 	onChildPop = (child) ->
-		child.onVisibleChange.disconnect @update, @
+		child.onVisibleChange.disconnect update, @
 		if @source is 'fillWidth'
-			child.onWidthChange.disconnect @update, @
+			child.onWidthChange.disconnect update, @
 		if @source is 'fillHeight'
-			child.onHeightChange.disconnect @update, @
-
-		@update()
+			child.onHeightChange.disconnect update, @
+
+		update.call @
 		return

 	class Anchor
@@ -239,6 +266,7 @@
 			line ?= source
 			@target = target
 			@line = line
+			@pending = false
 			@updateLoops = 0

 			if target is 'parent' or item._parent is target
@@ -249,7 +277,7 @@
 				@type = 'sibling'

 			for handler in getSourceWatchProps[source]
-				item[handler] @update, @
+				item[handler] update, @

 			@prop = getItemProp[source]
 			@getSourceValue = getSourceValue[source]
@@ -281,8 +309,8 @@
 				else
 					@targetItem = item._component.objects[target]
 					for handler in getTargetWatchProps[line][@type]
-						@targetItem[handler] @update, @
-					@update()
+						@targetItem[handler] update, @
+					update.call @

 			Object.preventExtensions @

@@ -345,11 +373,11 @@
 					@item.onPreviousSiblingChange.disconnect onPreviousSiblingChange, @

 			for handler in getSourceWatchProps[@source]
-				@item[handler].disconnect @update, @
+				@item[handler].disconnect update, @

 			if @targetItem
 				for handler in getTargetWatchProps[@line][@type]
-					@targetItem[handler].disconnect @update, @
+					@targetItem[handler].disconnect update, @

 			@item = @targetItem = null

@@ -383,6 +411,7 @@
 			assert.lengthOf def, 1
 			@anchors = []
 			def = [def[0], '']
+			@pending = false

 			baseAnchors = getBaseAnchorsPerAnchorType[def[0]]?[source]
 			baseAnchors ?= getBaseAnchors[source]


do not call kite.fetchState() from outside

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -270,8 +270,6 @@
     kite.ready =>
       @terminalKites[alias] = @getKite vm, 'terminal'

-    kite.fetchState()
-
   listenToVmState: (vm, kite) ->
     alias = vm.hostnameAlias
     kite.on 'vm.progress.start', (update) =>
@@ -285,8 +283,6 @@

     kite.on 'vm.progress.error', (error) =>
       @emit 'vm.progress.error', {alias, error}
-
-    kite.fetchState()

   getKiteByVmName: (vmName) ->
     @kites[vmName]


resurrection.activity: fix typo

resurrection.activity: fix typo

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -407,5 +407,5 @@
     {inputWidget} = @getView()
     inputWidget.edit activity
     window.scrollTo 0, 0
-    @once inputWidget.on "Submit", ->
+    inputWidget.once "Submit", ->
       window.scrollTo 0, item.getElement().getBoundingClientRect().top


stack-editor: warning view will not be shown in credentials tab

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -135,6 +135,12 @@

     @tabView.showPaneByIndex 0

+    @tabView.on 'PaneDidShow', (pane) =>
+      if pane.name is 'Credentials'
+        @warningView.hide()
+      else
+        @warningView.show()
+
     @createOutputView()
     @createMainButtons()