postgres user queries: fix bug in error reporting; explicitly ignore heartbeat option (not implemented)

--- src/smc-hub/postgres-user-queries.coffee
+++ src/smc-hub/postgres-user-queries.coffee
@@ -105,7 +105,7 @@

                 if changes
                     try
-                        @_inc_changefeed_count(opts.account_id, opts.project_id, changes.id)
+                        @_inc_changefeed_count(opts.account_id, opts.project_id, table, changes.id)
                     catch err
                         opts.cb?(err)
                         return
@@ -121,7 +121,7 @@
                     cb         : (err, x) =>
                         if err and changes
                             # didn't actually make the changefeed, so don't count it.
-                            @_dec_changefeed_count(changes.id)
+                            @_dec_changefeed_count(changes.id, table)
                         opts.cb?(err, if not err then {"#{table}":x})
         else
             opts.cb?("invalid user_query of '#{table}' -- query must be an object")
@@ -130,8 +130,8 @@
     TRACK CHANGEFEED COUNTS
     ###

-    # Incremen a count of the number of changefeeds by a given client so we can cap it.
-    _inc_changefeed_count: (account_id, project_id, changefeed_id) =>
+    # Increment a count of the number of changefeeds by a given client so we can cap it.
+    _inc_changefeed_count: (account_id, project_id, table, changefeed_id) =>
         client_name = "#{account_id}-#{project_id}"
         cnt = @_user_get_changefeed_counts ?= {}
         ids = @_user_get_changefeed_id_to_user ?= {}
@@ -142,17 +142,21 @@
         else
             # increment before successfully making get_query to prevent huge bursts causing trouble!
             cnt[client_name] += 1
-        @_dbg("_inc_changefeed_count")("{#{client_name}:#{cnt[client_name]} ...}")
+        @_dbg("_inc_changefeed_count(table='#{table}')")("{#{client_name}:#{cnt[client_name]} ...}")
         ids[changefeed_id] = client_name

     # Corresonding decrement of count of the number of changefeeds by a given client.
-    _dec_changefeed_count: (id) =>
+    _dec_changefeed_count: (id, table) =>
         client_name = @_user_get_changefeed_id_to_user[id]
         if client_name?
             @_user_get_changefeed_counts?[client_name] -= 1
             delete @_user_get_changefeed_id_to_user[id]
             cnt = @_user_get_changefeed_counts
-            @_dbg("_dec_changefeed_count")("counts={#{client_name}:#{cnt[client_name]} ...}")
+            if table?
+                t = "(table='#{table}')"
+            else
+                t = ""
+            @_dbg("_dec_changefeed_count#{t}")("counts={#{client_name}:#{cnt[client_name]} ...}")

     # Handle user_query when opts.query is an array.  opts below are as for user_query.
     _user_query_array: (opts) =>
@@ -182,7 +186,7 @@
             delete @_changefeeds[opts.id]
             feed.close()
         else
-            dbg("already cancelled before (or not such feed)")
+            dbg("already cancelled before (no such feed)")
         opts.cb?()

     _query_is_cmp: (obj) =>
@@ -252,8 +256,11 @@
                         if value[0] == '-'
                             value = value.slice(1) + " DESC "
                         r.order_by = value
+                    when 'heartbeat'
+                        # not implemented
+                        @_dbg("_query_parse_options")("TODO/WARNING -- ignoring heartbeat option!")
                     when 'delete'
-                        # ignore here - is parsed elsewhere
+                        # ignore delete here - is parsed elsewhere
                     else
                         r.err = "unknown option '#{name}'"
         return r
@@ -1046,6 +1053,8 @@

     _user_get_query_changefeed: (changes, table, primary_key, user_query,
                                  where, json_fields, account_id, client_query, cb) =>
+        dbg = @_dbg("_user_get_query_changefeed(table='#{table}')")
+        dbg()
         if not misc.is_object(changes)
             cb("changes must be an object with keys id and cb")
             return
@@ -1058,7 +1067,6 @@
         if not user_query[primary_key]? and user_query[primary_key] != null
             cb("changefeed MUST include primary key (='#{primary_key}') in query")
             return
-
         watch  = []
         select = {}
         init_tracker = tracker = undefined
@@ -1194,7 +1202,7 @@
         feed that calls opts.cb on changes as well.
         ###
         dbg = @_dbg("user_get_query(table='#{opts.table}')")
-        dbg()
+        dbg("account_id='#{opts.account_id}', project_id='#{opts.project_id}', query=#{misc.to_json(opts.query)}, multi=#{opts.multi}, options=#{misc.to_json(opts.options)}, changes=#{misc.to_json(opts.changes)}")
         {err, table, client_query, require_admin, delete_option, primary_key, json_fields} = @_parse_get_query_opts(opts)

         if err
@@ -1230,7 +1238,7 @@
                 _query_opts.query = @_user_get_query_query(delete_option, table, opts.query)
                 x = @_user_get_query_options(delete_option, opts.options, opts.multi, client_query.all?.options)
                 if x.err
-                    cb(err)
+                    cb(x.err)
                     return
                 misc.merge(_query_opts, x)


Make meta attribute spec more explicit

Make meta attribute spec more explicit

--- src/spec/utils/product-utils.spec.coffee
+++ src/spec/utils/product-utils.spec.coffee
@@ -573,14 +573,12 @@
         en: 'A title'
       metaDescription:
         en: 'A description'
+      metaKeywords:
+        en: 'foo, bar'
     NEW =
       id: '123'
       metaTitle:
         en: 'A new title'
-      metaDescription:
-        en: 'A description'
-      metaKeywords:
-        en: 'foo, bar'

     delta = @utils.diff OLD, NEW
     update = @utils.actionsMapMetaAttributes(delta, OLD)


Fix error with php-cs-fixer being run from phar file

--- src/beautifiers/executable.coffee
+++ src/beautifiers/executable.coffee
@@ -389,6 +389,9 @@
   installedWithDocker: false
   init: () ->
     super()
+      .then(() =>
+        return @
+      )
       .catch((error) =>
         return Promise.reject(error) if not @docker?
         return @
@@ -399,6 +402,13 @@
         if shouldTryWithDocker
           return @initDocker()
         return @
+      )
+      .catch((error) =>
+        if not @.required
+          @verbose("Not required")
+          @
+        else
+          Promise.reject(error)
       )

   initDocker: () ->


Log out deduped module names

Log out deduped module names

--- src/dedupe-package-modules.coffee
+++ src/dedupe-package-modules.coffee
@@ -38,6 +38,17 @@
       packagePaths.push(packagePath)

     packagePaths
+
+  # Get all the modules installed to ~/.atom/packages/node_modules
+  getInstalledModules: ->
+    modulePaths = []
+    nodeModulesPath = path.join(@userPackagesDirectory, 'node_modules')
+    for child in fs.list(nodeModulesPath)
+      modulePath = path.join(nodeModulesPath, child)
+      continue unless fs.isDirectorySync(modulePath)
+      modulePaths.push(modulePath)
+
+    modulePaths

   # Move Atom packages from ~/.atom/packages to  ~/.atom/packages/node_modules
   movePackagesToNodeModulesFolder: (packagePaths) ->
@@ -119,6 +130,7 @@
     options = @parseOptions(options.commandArgs)

     packagePaths = @getInstalledPackages()
+    currentModulePaths = @getInstalledModules()
     @createPackageJson(packagePaths)
     modulesToDedupe = @getModulesToDedupe(packagePaths)
     @removePackageNames(packagePaths, modulesToDedupe)
@@ -138,4 +150,10 @@
       catch moveError
         return callback(moveError) unless dedupeError?

+      unless dedupeError?
+        updatedModulePaths = @getInstalledModules()
+        for modulePath in updatedModulePaths
+          if currentModulePaths.indexOf(modulePath) is -1
+            console.log "Deduped #{path.basename(modulePath)}"
+
       callback(dedupeError)


Creates ability to specify the type of sub expression in js exp builder

--- math/build_expression_from_javascript_object_spec.coffee
+++ math/build_expression_from_javascript_object_spec.coffee
@@ -46,18 +46,19 @@
       expect(exponentiation).toBeInstanceOf @components.exponentiation

       base = exponentiation.base()
-      expect(base).toBeInstanceOf @components.number
-      expect(base.value()).toEqual 10
+      expect(base).toBeInstanceOf @components.expression
+      expect(base.first().value()).toEqual 10

       power = exponentiation.power()
-      expect(power).toBeInstanceOf @components.number
-      expect(power.value()).toEqual 11
+      expect(power).toBeInstanceOf @components.expression
+      expect(power.first().value()).toEqual 11

-    it 'supports incomplete exponentiations (as blank member)', ->
+    it 'supports incomplete exponentiations (as an empty expression)', ->
       expression = @builder '^': [10, null]
       exponentiation = expression.nth(0)
       expect(exponentiation).toBeInstanceOf @components.exponentiation
-      expect(exponentiation.power()).toBeInstanceOf @components.blank
+      expect(exponentiation.power()).toBeInstanceOf @components.expression
+

   it "handles parenthetical sub-expressions via arrays", ->
     expression = @builder []


Clean up #createLocationRangeFromDOMRange

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -72,18 +72,18 @@
     selection.addRange(range)

   createLocationRangeFromDOMRange: (range) ->
-    return unless range?
+    return unless range? and @rangeWithinElement(range)
+    start = @findLocationFromContainerAtOffset(range.startContainer, range.startOffset)
+    end = @findLocationFromContainerAtOffset(range.endContainer, range.endOffset) unless range.collapsed
+    new Trix.LocationRange start, end
+
+  rangeWithinElement: (range) ->
     if range.collapsed
-      if Trix.DOM.within(@element, range.endContainer)
-        start = @findLocationRangeAttributesFromContainerAtOffset(range.endContainer, range.endOffset)
-        new Trix.LocationRange start
+      Trix.DOM.within(@element, range.startContainer)
     else
-      if Trix.DOM.within(@element, range.startContainer) and Trix.DOM.within(@element, range.endContainer)
-        start = @findLocationRangeAttributesFromContainerAtOffset(range.startContainer, range.startOffset)
-        end = @findLocationRangeAttributesFromContainerAtOffset(range.endContainer, range.endOffset)
-        new Trix.LocationRange start, end
+      Trix.DOM.within(@element, range.startContainer) and Trix.DOM.within(@element, range.endContainer)

-  findLocationRangeAttributesFromContainerAtOffset: (container, offset) ->
+  findLocationFromContainerAtOffset: (container, offset) ->
     if container.nodeType is Node.TEXT_NODE
       index = container.trixIndex
       position = container.trixPosition + offset


webapp/latex: immediately remove temporary pdf after creating preview images (pdf could be huge!)

--- src/smc-webapp/latex/document.coffee
+++ src/smc-webapp/latex/document.coffee
@@ -573,6 +573,15 @@
                     timeout : 120
                     cb      : (err, output) ->
                         cb(err)
+
+            # delete the copied PDF file, we no longer need it (might use up a lot of disk/memory space)
+            (cb) =>
+                @_exec
+                    command : 'rm'
+                    args    : [pdf]
+                    timeout : 15
+                    err_on_exit : true
+                    cb      : cb

             # get the new sha1 hashes
             (cb) =>


added target generation

added target generation

--- src/mist-ninja-builder.coffee
+++ src/mist-ninja-builder.coffee
@@ -19,7 +19,7 @@
     @rootDir
     @registry.vars = @registry.vars || []
     @registry.rules = @registry.rules || {}
-    @registry.targets = @registry.rules || []
+    @registry.targets = @registry.targets || []

     @commandDict = @compileCommandDict()
     @commandDictRefs = {}
@@ -105,6 +105,8 @@
     targets.forEach (target)=>
       build_vars = @compileDict target.main_inputs

+      target.rule = commandHash
+
       target.main_inputs =
         MistGlobber.doAllGlobs target.main_inputs, @mistDir

@@ -137,7 +139,14 @@

         target.build_vars[d] = target.build_vars[d].join ' '

+      target.main_inputs = target.main_inputs.join ' '
+      target.dep_inputs = target.dep_inputs.join ' '
+      target.order_inputs = target.order_inputs.join ' '
+      target.main_outputs = target.main_outputs.join ' '
+      target.aux_outputs = target.aux_outputs.join ' '
+
       console.log target
+      @registry.targets.push target

   render: ->
     lines = []
@@ -156,12 +165,14 @@
         lines.pushScoped "#{k}=#{v}"

     for target in @registry.targets
-      lines.push "build #{target.outputs.join ' '}: " +
-           "#{if target.phony then "phony"} #{target.rule} " +
-           "#{target.inputs.join ' '}" +
-           "#{if target.deps.length then " | "}#{target.deps.join ' '}" +
-           "#{if target.odeps.length then " || "}#{target.deps.join ' '}"
-      for k,v of target.vars
+      outputs = target.main_outputs + target.aux_outputs
+      lines.push "build #{outputs}: " +
+           "#{target.rule} " +
+           "#{target.main_inputs}" +
+           "#{if target.dep_inputs.length then " | "}#{target.dep_inputs}" +
+           "#{if target.order_inputs.length then " || "}" +
+           "#{target.order_inputs}"
+      for k, v of target.build_vars
         lines.pushScoped "#{k}=#{v}"

     lines.push '' # Ninja requires a newline at the end :)


linkTo now accepts a class parameter

linkTo now accepts a class parameter

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/_module.coffee
+++ vendor/assets/javascripts/fustrate/_module.coffee
@@ -47,8 +47,12 @@
     else
       Fustrate._arrayToClass pieces.slice(1), root[pieces[0]]

-  @linkTo: (text, path) ->
-    $('<a>').prop('href', path).html(text).outerHTML()
+  @linkTo: (text, path, options = {}) ->
+    link = $('<a>').prop('href', path).html(text)
+
+    link.addClass(options.class) if options.class?
+
+    link.outerHTML()

   @ajaxUpload: (url, data) ->
     formData = new FormData


style(quote): use simple quote

style(quote): use simple quote

--- index.coffee
+++ index.coffee
@@ -3,11 +3,11 @@
     coloured:
       type: 'boolean'
       default: true
-      description: "Untick this for colourless icons"
+      description: 'Untick this for colourless icons'
     forceShow:
       type: 'boolean'
       default: false
-      description: "Force show icons - for themes that hide icons"
+      description: 'Force show icons - for themes that hide icons'
   activate: (state) ->
     self = @
     atom.config.onDidChange 'file-icons.coloured', ({newValue, oldValue}) ->


:fire: Unnecessary audio hack

:fire: Unnecessary audio hack

No longer needed with the `backgroundThrottling` option

--- src/initialize-test-window.coffee
+++ src/initialize-test-window.coffee
@@ -48,13 +48,6 @@

     document.title = "Spec Suite"

-    # Avoid throttling of test window by playing silence
-    # See related discussion in https://github.com/atom/atom/pull/9485
-    context = new AudioContext()
-    source = context.createBufferSource()
-    source.connect(context.destination)
-    source.start(0)
-
     testRunner = require(testRunnerPath)
     legacyTestRunner = require(legacyTestRunnerPath)
     buildDefaultApplicationDelegate = -> new ApplicationDelegate()


Ability to open new tabs w/ CMD + (mouse click)

Signed-off-by: Valérian Saliou <d691b9c2718f7451d77666d2dda50423f81cd2b8@valeriansaliou.name>

--- src/javascripts/progressio.coffee
+++ src/javascripts/progressio.coffee
@@ -66,6 +66,8 @@

       # Launch the Progressio wrapper :)
       @ProgressioPage.register()
+      @ProgressioMisc.register()
+
       @ProgressioRegistry.events()

       # Done constructing!
@@ -232,11 +234,11 @@
     register: ->
       try
         @__.ProgressioRegistry.register_event(
-          'page:events_state_change', @events_state_change, @
+          'progressio:page:events_state_change', @events_state_change, @
         )

         @__.ProgressioRegistry.register_event(
-          'page:async_load', @events_async_load, @
+          'progressio:page:async_load', @events_async_load, @
         )
       catch error
         @_options.console.error 'Progressio.ProgressioPage.register', error
@@ -895,10 +897,32 @@
         @_options = options
         @_jQuery  = deps.jquery

+        # Selectors
+        @_window_sel = @_jQuery window
+
         # Storage
         @_key_event = {}
       catch error
         @_options.console.error 'Progressio.ProgressioMisc.constructor', error
+
+
+    register: ->
+      try
+        @__.ProgressioRegistry.register_event(
+          'progressio:misc:events_key', @events_key, @
+        )
+      catch error
+        @_options.console.error 'Progressio.ProgressioMisc.register', error
+
+
+    events_key: ->
+      try
+        self = @
+
+        @_window_sel.keydown (evt) -> (self._key_event = evt)
+        @_window_sel.keyup -> (self._key_event = {})
+      catch error
+        @_options.console.error 'Progressio.ProgressioMisc.events_key', error

     key_event: ->


Add all operators of interest

Add all operators of interest

--- assets/javascripts/react_nodes/operator_nodes.coffee
+++ assets/javascripts/react_nodes/operator_nodes.coffee
@@ -18,51 +18,194 @@
 getReturningFunctionDeclaration = (args) ->
   "function(#{args}) { return "

-simpleOperators =
+simpleOperatorsDefaults = useScheduler: false
+simpleOperators = R.mapObj(R.mixin(simpleOperatorsDefaults))(
+  average:
+    getDefaultArgs: R.always("function(x) { return x; }")
+  bufferWithCount:
+    getDefaultArgs: R.always("2")
+  bufferWithTime:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  bufferWithTimeOrCount:
+    getDefaultArgs: R.always("1000", 3)
+    useScheduler: true
+  concatAll: {}
+  count: {}
+  debounce:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  defaultIfEmpty:
+    getDefaultArgs: R.always("'defaultValue'")
+  delay:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  distinct:
+    getDefaultArgs: R.always("function(x) { return x % 2 }")
+  distinctUntilChanged:
+    getDefaultArgs: R.always("function(x) { return x % 2 }")
+  elementAt:
+    getDefaultArgs: R.always("1")
+  elementAtOrDefault:
+    getDefaultArgs: R.always("1, 'defaultValue'")
+  every:
+    getDefaultArgs: R.always("function(x) { return x % 2 == 0 }")
+  find:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  first:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  firstOrDefault:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;") + ", 'defaultValue'")
+  filter:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  groupBy:
+    getDefaultArgs: R.always(defaultFunc("return value % 2") + "," + defaultFunc("return value;"))
+  includes:
+    getDefaultArgs: R.always("5")
+  ignoreElements: {}
+  indexOf:
+    getDefaultArgs: R.always("5")
+  isEmpty: {}
+  last:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  lastOrDefault:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;") + ", 'defaultValue'")
   map:
     getDefaultArgs: R.always(defaultFunc("return value * value;"))
-    useScheduler: false
+  max:
+    getDefaultArgs: R.always(defaultFunc("return value;"))
+  mergeAll: {}
+  min:
+    getDefaultArgs: R.always(defaultFunc("return value;"))
+  pairwise: {}
+  pluck:
+    getDefaultArgs: R.always("'property'")
+  reduce:
+    getDefaultArgs: R.always("function(acc, x) { return acc * x; }, 1")
+  repeat:
+    getDefaultArgs: R.always("2")
+  retry:
+    getDefaultArgs: R.always("3")
+  sample:
+    getDefaultArgs: R.always("500")
+    useScheduler: true
+  scan:
+    getDefaultArgs: R.always("1, function(acc, x) { return acc * x; }")
+  single:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  singleOrDefault:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;") + ", 'defaultValue'")
+  skip:
+    getDefaultArgs: R.always("5")
+  skipLast:
+    getDefaultArgs: R.always("3")
+  skipLastWithTime:
+    useScheduler: true
+    getDefaultArgs: R.always("1000")
+  skipUntilWithTime:
+    useScheduler: true
+    getDefaultArgs: R.always("2000")
+  startWith:
+    getDefaultArgs: R.always("1, 2, 3")
+  some:
+    getDefaultArgs: R.always(defaultFunc("return value > 10;"))
+  sum:
+    getDefaultArgs: R.always("function(x, idx) { return x; }")
   take:
     getDefaultArgs: R.always("4")
-    useScheduler: true
-  filter:
-    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
-    useScheduler: false
-  delay:
-    getDefaultArgs: R.always("1000")
-    useScheduler: true
-  bufferWithTime:
-    getDefaultArgs: R.always("1000")
-    useScheduler: true
-
-recursiveFunctionOperators =
-  flatMap:
-    recursive: true
-    recursionType: 'function'
-    getDefaultArgs: R.compose(getReturningFunctionDeclaration, getClosedOverArgName)
-    getDefaultObservable: R.compose(createSimpleObservable('just'), getClosedOverArgName)
-
-recursiveOperators =
+  takeLast:
+    getDefaultArgs: R.always("4")
+  takeLastBuffer:
+    getDefaultArgs: R.always("4")
+  takeLastBufferWithTime:
+    useScheduler: true
+    getDefaultArgs: R.always("2000")
+  takeLastWithTime:
+    useScheduler: true
+    getDefaultArgs: R.always("2000")
+  takeUntilWithTime:
+    useScheduler: true
+    getDefaultArgs: R.always("5000")
+  takeWhile:
+    getDefaultArgs: R.always(defaultFunc("return value < 20;"))
+  timeInterval:
+    useScheduler: true
+  throttleFirst:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  timeout:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  timestamp:
+    useScheduler: true
+  toArray: {}
+  windowWithCount:
+    getDefaultArgs: R.always("2")
+  windowWithTime:
+    getDefaultArgs: R.always("1000")
+    useScheduler: true
+  windowWithTimeOrCount:
+    getDefaultArgs: R.always("1000", 3)
+    useScheduler: true
+)
+
+getReturningFunctionWithClosedOver = R.compose(getReturningFunctionDeclaration, getClosedOverArgName)
+
+recursiveFunctionOperatorsDefaults =
+  recursive: true
+  recursionType: 'function'
+  getDefaultArgs: getReturningFunctionWithClosedOver
+  getDefaultObservable: R.compose(createSimpleObservable('just'), getClosedOverArgName)
+recursiveFunctionOperators = R.mapObj(R.mixin(recursiveFunctionOperatorsDefaults))(
+  flatMap: {}
+  flatMapLatest: {}
+  buffer:
+    getDefaultObservable: R.compose(createSimpleObservable('timer'), R.always("1000"))
+  concatMap:
+    getDefaultObservable: R.compose(createSimpleObservable('range'), ((closedOverArg) -> "0, #{closedOverArg}"), getClosedOverArgName)
+  debounceWithSelector:
+    getDefaultObservable: R.compose(createSimpleObservable('timer'), getClosedOverArgName)
+  expand:
+    getDefaultObservable: R.compose(createSimpleObservable('just'), ((arg) -> "#{arg} + #{arg}" ), getClosedOverArgName)
+  timeoutWithSelector:
+    getDefaultObservable: R.compose(createSimpleObservable('timer'), ((arg) -> "#{arg} * 100"), getClosedOverArgName)
+  window:
+    getDefaultObservable: R.compose(createSimpleObservable('timer'), R.always("1000"))
+)
+
+recursiveOperatorDefaults =
+  recursive: true
+  recursionType: 'observable'
+recursiveOperators = R.mapObj(R.mixin(recursiveOperatorDefaults))(
   merge:
-    recursive: true
-    recursionType: 'observable'
     getDefaultObservable: R.always(createSimpleObservable('just')('1,2'))
   amb:
-    recursive: true
-    recursionType: 'observable'
     getDefaultObservable: R.always
       root:
         type: 'of'
       operators: [
         { type: 'delay', args: '1000' }
       ]
-
-recursiveOperatorsWithTrailingArgs =
-  combineLatest:
-    recursive: true
-    recursionType: 'observableWithSelector'
-    getDefaultObservable: R.always(createSimpleObservable('just')('1,2'))
-    getDefaultArgs: R.always(simpleCombinerFunction)
+  concat:
+    getDefaultObservable: R.always(createSimpleObservable('just')('5'))
+  sequenceEqual:
+    getDefaultObservable: R.always(createSimpleObservable('just')('5'))
+  skipUntil:
+    getDefaultObservable: R.always(createSimpleObservable('timer')('1000'))
+  takeUntil:
+    getDefaultObservable: R.always(createSimpleObservable('timer')('1000'))
+)
+
+recursiveOperatorsWithTrailingArgsDefaults =
+  recursive: true
+  recursionType: 'observableWithSelector'
+  getDefaultObservable: R.always(createSimpleObservable('just')('1,2'))
+  getDefaultArgs: R.always(simpleCombinerFunction)
+recursiveOperatorsWithTrailingArgs = R.mapObj(R.mixin(recursiveOperatorsWithTrailingArgsDefaults))(
+  combineLatest: {}
+  withLatestFrom: {}
+  zip: {}
+)

 SimpleOperator = React.createClass
   onArgsChange: (args) ->


fixing random test failure

fixing random test failure

--- test/hearts/engine/statesTest.coffee
+++ test/hearts/engine/statesTest.coffee
@@ -300,6 +300,8 @@
         trick.should.equal @game.currentRound().currentTrick()
         done()
       @game.currentState.run()
+      action = new actions.PlayCard(@game.positions.north, @card)
+      @game.currentState.handleAction(action)

     it "goes to the next state", ->
       action = new actions.PlayCard(@game.positions.north, @card)
@@ -322,7 +324,6 @@
         @game.currentState.handleAction(action)
         @nextStateCalls.should.equal(1)

-        # test the message
         @game.positions.north.recvTurn (err, trick) =>
           err.type.should.equal("invalidMove")
           err.message.should.equal("Your action took longer than allowed")


One more fix in updateAttribute test with uniqueness validation

--- test/validations_test.coffee
+++ test/validations_test.coffee
@@ -96,7 +96,7 @@
   bkk = new Airport code: 'BKK', city: 'Bangkok'
   bkk.isValid (valid) ->
     test.ok valid
-    bkk.save ->
+    bkk.updateAttribute 'code', 'BKK', ->
       dmk = new Airport code: 'DMK', city: 'Bangkok'
       dmk.isValid (valid) ->
         test.ok valid


Environment variable consistancy is awesome

--- src/scripts/virustotal.coffee
+++ src/scripts/virustotal.coffee
@@ -5,7 +5,7 @@
 #   None
 #
 # Configuration:
-#   VIRUS_TOTAL_API_KEY environment variable needs to be set with your API key https://www.virustotal.com/en/documentation/public-api/
+#   VIRUSTOTAL_API_KEY environment variable needs to be set with your API key https://www.virustotal.com/en/documentation/public-api/
 #
 # Commands:
 #   hubot virustotal hash <hash> - Searches VirusTotal for a hash
@@ -15,7 +15,7 @@
 # Author:
 #   Scott J Roberts - @sroberts

-VIRUS_TOTAL_API_KEY = process.env.VIRUS_TOTAL_API_KEY
+VIRUSTOTAL_API_KEY = process.env.VIRUSTOTAL_API_KEY
 vt_url = "https://www.virustotal.com"

 vt_file_report_url = vt_url + "/vtapi/v2/file/report"


increase syncstring timeout parameter

increase syncstring timeout parameter

--- src/smc-project/client.coffee
+++ src/smc-project/client.coffee
@@ -19,7 +19,7 @@

 # close our copy of syncstring (so stop watching it for changes, etc) if
 # not active for this long (should be at least 5 minutes).
-SYNCSTRING_MAX_AGE_M = 7
+SYNCSTRING_MAX_AGE_M = 15
 #SYNCSTRING_MAX_AGE_M = .4 # TESTING

 # CRITICAL: The above SYNCSTRING_MAX_AGE_M idle timeout does *NOT* apply to Sage worksheet


db schema: add accounts last_active index for clustering

--- src/smc-util/db-schema.coffee
+++ src/smc-util/db-schema.coffee
@@ -221,7 +221,8 @@
         '(lower(last_name)  text_pattern_ops)',
         'created_by',
         'created',
-        'api_key'
+        'api_key',
+        'last_active'
         ]
     user_query :
         get :


fix #4336 -- subtle issue with sage worksheets, undo and snippets of code

--- src/smc-webapp/syncdoc.coffee
+++ src/smc-webapp/syncdoc.coffee
@@ -330,7 +330,6 @@
                     if changeObj.origin != 'setValue'
                         @_last_change_time = new Date()
                         @save_state_debounce?()
-                        @_syncstring.exit_undo_mode()
                 update_unsaved_uncommitted_changes()

             @emit('connect')   # successful connection
@@ -356,7 +355,9 @@
         #console.log 'user action so setting'
         @_user_action = false
         @_last_val = val = @codemirror.getValue()
-        @_syncstring.from_str(val)
+        if val != @_syncstring.to_str()
+            @_syncstring.exit_undo_mode()
+            @_syncstring.from_str(val)
         #@_debug_sync_state('after')

     # Set value of the codemirror editor to equal current value of the syncstring


all simulator errors to console

all simulator errors to console

--- src/steroids/Simulator.coffee
+++ src/steroids/Simulator.coffee
@@ -69,7 +69,7 @@
         cmd: cmd
         args: args
         stdout: steroidsCli.debugEnabled?
-        stderr: steroidsCli.debugEnabled?
+        stderr: true

       @simulatorSession.on "exit", () =>
         @running = false


fixed window open

fixed window open

--- lms/static/coffee/src/teacher_dashboard/data_download.coffee
+++ lms/static/coffee/src/teacher_dashboard/data_download.coffee
@@ -89,7 +89,7 @@
           if data.success
             @$download_submissions_response.text data['msg']
           else
-            window.url(data['url'])
+            window.open(data['url'])
             @$download_submissions_response.text data['msg']


Sync up volume color generation with color generator

--- app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.coffee
+++ app/assets/javascripts/oxalis/geometries/materials/plane_material_factory.coffee
@@ -154,7 +154,7 @@
         /* Color map (<= to fight rounding mistakes) */

         if ( id > 0.1 ) {
-          vec4 HSV = vec4( mod( 6.0 * id * golden_ratio, 6.0), 1.0, 1.0, 1.0 );
+          vec4 HSV = vec4( mod( id * golden_ratio, 1.0), 1.0, 1.0, 1.0 );
           gl_FragColor = vec4(mix( data_color, hsv_to_rgb(HSV), alpha ), 1.0);
         } else {
           gl_FragColor = vec4(data_color, 1.0);


Render optional button from props

Render optional button from props

--- tutor/src/components/no-periods.cjsx
+++ tutor/src/components/no-periods.cjsx
@@ -3,27 +3,44 @@
 BS = require 'react-bootstrap'
 TutorLink = require './link'
 CourseGroupingLabel = require './course-grouping-label'
+Router = require '../helpers/router'

 NoPeriods = React.createClass

   propTypes:
     courseId: React.PropTypes.string.isRequired
-    noPanel:  React.PropTypes.bool
-    link:  React.PropTypes.bool
+    button:   React.PropTypes.element
+
+  contextTypes:
+    router: React.PropTypes.object

   onAddSection: ->
+    @context.router.transitionTo(
+      Router.makePathname('courseSettings',
+        {courseId: @props.courseId},
+        query: {add: true}
+      )
+    )

+  AddButton: ->
+    <BS.Button
+      className='no-periods-course-settings-link'
+      bsStyle="primary"
+      onClick={@onAddSection}
+    >
+      Add a <CourseGroupingLabel courseId={@props.courseId} />
+    </BS.Button>
+
   render: ->
-

+
+

         Please add at least
         one <CourseGroupingLabel courseId={@props.courseId} lowercase /> to the course.

-      <BS.Button bsStyle="primary" onClick={@onAddSection}>
-        Add a <CourseGroupingLabel courseId={@props.courseId} />
-      </BS.Button>
+      {@props.button or <@AddButton />}


AppThumbView: Called hideLoader when app manager showed an app.

--- client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -230,6 +230,7 @@

     appManager.once "AppCouldntBeCreated", couldntCreate
     appManager.once "AppCreated",          appCreated
+    appManager.once "AppManagerWantsToShowAnApp", => @hideLoader()

     route = if manifest.route
       if "string" is typeof manifest.route


Fix app thumb views tooltip offsets

Fix app thumb views tooltip offsets

--- client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -50,6 +50,9 @@
       cssClass : "icon compile"
       tooltip  :
         title  : "Click to compile"
+        offset :
+          top  : 4
+          left : -5
       click    : =>
         @showLoader()
         @getSingleton("kodingAppsController").compileApp manifest.name, (err)=>
@@ -60,6 +63,9 @@
       tagName  : "span"
       cssClass : "icon info"
       tooltip  :
+        offset :
+          top  : 4
+          left : -5
         title  : """

             #{name} #{version} by #{author}
@@ -74,6 +80,9 @@
       cssClass : "icon delete"
       tooltip  :
         title  : "Click to delete"
+        offset :
+          top  : 4
+          left : -5
       click    : =>
         @delete.hideTooltip()
         @deleteModal = new KDModalView


Fix typo

Fix typo

--- lib/assets/javascripts/views/repost_visibility.js.coffee
+++ lib/assets/javascripts/views/repost_visibility.js.coffee
@@ -53,7 +53,7 @@

         @render() if Object.keys(@reposter_profile_cids).length

-        if res.pages.next
+        if res.pages?.next
           _.extend(params, Marbles.history.deserializeParams(res.pages.next))
           @fetchReposters(params)
     )


Activity: adding the inputwidget the right way for announcements

--- client/Social/Activity/views/announcementpane.coffee
+++ client/Social/Activity/views/announcementpane.coffee
@@ -4,13 +4,14 @@
     options.cssClass = "activity-pane announcement-pane #{options.type}"
     super options, data

+
   viewAppended: ->
-    super()

-    # TODO: this will be a false negative if the roles are not loaded yet.
-    #       this should be fixed upstream by making sure the roles are
-    #       available when the page is loaded. C.T.
-    @input.hide()  unless 'admin' in KD.config.roles
+    @addSubView @channelTitleView
+    KD.singletons.mainView.ready =>
+      @addSubView @input  if 'admin' in KD.config.roles
+      @addSubView @tabView
+

   createContentViews: ->
     data = @getData()


Client: Activity: Updated champagne to wrap chatlist with infinitescroll component

--- client/activity/lib/components/chatpane/index.coffee
+++ client/activity/lib/components/chatpane/index.coffee
@@ -2,6 +2,7 @@
 React = require 'kd-react'
 ChatInputWidget = require 'activity/components/chatinputwidget'
 ChatList = require 'activity/components/chatlist'
+InfiniteScroll = require 'app/components/infinitescroll'

 module.exports = class ChatPane extends React.Component
@@ -30,15 +31,21 @@
   onSubmit: (event) -> @props.onSubmit? event

+  getScrollProps: ->
+    scrollDirection          : 'up'
+    onScrollThresholdReached : @props.onScrollThresholdReached
+    isDataLoading            : @props.thread.getIn ['flags', 'isMessagesLoading']
+
+
   renderBody: ->
     return null  unless @props.messages

     messages = @props.messages.sortBy (m) -> m?.get 'createdAt'

-

-        <ChatList messages={messages}/>
-

+      <InfiniteScroll {...@getScrollProps()} ref="ChatList">
+        <ChatList messages={messages} />
+      </InfiniteScroll>


Client: Activity: Set loadedWithScroll flag and props in chatpane

--- client/activity/lib/components/chatpane/index.coffee
+++ client/activity/lib/components/chatpane/index.coffee
@@ -23,6 +23,8 @@
     return  unless nextProps?.thread

     { thread } = nextProps
+
+    @loadedWithScroll       = thread.getIn ['flags', 'loadedWithScroll']
     isMessageBeingSubmitted = thread.getIn ['flags', 'isMessageBeingSubmitted']
     @shouldScrollToBottom   = yes  if isMessageBeingSubmitted

@@ -54,6 +56,7 @@
       onTopThresholdReached={@bound 'onTopThresholdReached'}>
       <ChatList
         isMessagesLoading={@props.thread?.getIn ['flags', 'isMessagesLoading']}
+        loadedWithScroll={@props.thread?.getIn ['flags', 'loadedWithScroll']}
         messages={@props.messages}
         showItemMenu={@props.showItemMenu}
         channelId={@channel 'id'}


MachineSettingsModal: Destroy modal when subview requested for.

--- client/app/lib/providers/machinesettingsmodal.coffee
+++ client/app/lib/providers/machinesettingsmodal.coffee
@@ -76,6 +76,9 @@

     for item in PANE_CONFIG when item.title and item.viewClass

+      subView = new item.viewClass item.viewOptions, @getData()
+      subView.once 'ModalDestroyRequested', @bound 'destroy'
+
       @tabView.addPane pane = new KDTabPaneView
         name     : item.title
         cssClass : 'AppModal-content'


client: add payment customer free credit selector

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/redux/modules/payment/customer.coffee
+++ client/app/lib/redux/modules/payment/customer.coffee
@@ -63,12 +63,21 @@

 email = (state) -> state.customer?.email

+freeCredit = (state) ->
+  # negative account balance means user has credits.
+  if state.customer?.account_balance < 0
+  # the amount is in cents, we want dollars.
+  then ((state.customer.account_balance * -1) / 100).toFixed 2
+  # if the balance is greater than 0 that means 0
+  # free credit.
+  else 0
+

 module.exports = {
   namespace: withNamespace()
   reducer

-  coupon, email
+  coupon, email, freeCredit

   load, create, update, remove
   LOAD, CREATE, UPDATE, REMOVE


Added backbone collection change options

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -9,7 +9,7 @@

 window.ReactFeedActivitiesAutoLoading = React.createBackboneClass
   displayName: 'ReactFeedActivitiesAutoLoading'
-  changeOptions: 'sync'
+  changeOptions: 'add remove sort reset sync'

   # this function loads more activities, if we're almost at the bottom of the list
   checkScrolledPosition: ->


Fixed jquery 1.9 issue.

Fixed jquery 1.9 issue.

--- app/assets/javascripts/app/lib/app_post/ajax.js.coffee
+++ app/assets/javascripts/app/lib/app_post/ajax.js.coffee
@@ -26,7 +26,7 @@
     @_run()

     # bindings
-    $('body').bind( 'ajaxSend', =>
+    $(document).bind( 'ajaxSend', =>
       @_show_spinner()
     ).bind( 'ajaxComplete', =>
       @_hide_spinner()


Fix breadcrumb for IE

Fix breadcrumb for IE

--- app/assets/javascripts/gricer_backend_jquery.js.coffee
+++ app/assets/javascripts/gricer_backend_jquery.js.coffee
@@ -67,7 +67,7 @@
     for alt in alternatives
       do(alt) ->
         if alt.uri
-          container.append($('').attr('href', alt.uri).addClass(alt.type).attr('data-gricer-' + alt.type, true).append(alt.type))
+          container.append($('').attr('href', alt.uri).addClass(alt.type).attr('data-gricer-' + alt.type, true).attr('data-label', alt.type).attr('data-type-selector', true).append(alt.type))
         else
           container.append($('').addClass(alt.type).append(alt.type))

@@ -90,16 +90,18 @@
   updateBreadcrumb: (elem) ->
     $.gricer.last_called = $(elem)

+    label = $(elem).text() || $(elem).attr('data-label')
+
     if $(elem).parents('#gricer-menu').length > 0
-      $('#gricer-header .path').html( $(elem).clone() )
+      $('#gricer-header .path').empty()
     else if $(elem).parents('#gricer-header .path').length > 0
       $(elem).nextAll().remove()
-    else if $(elem).parent('.type-selector').length > 0
-      link = $('#gricer-header .path a:last').html()
+      return
+    else if $(elem).attr('data-type-selector')
+      label = $('#gricer-header .path a:last').attr('data-label')
       $('#gricer-header .path a:last').remove()
-      $('#gricer-header .path').append( $(elem).clone().html(link))
-    else
-      $('#gricer-header .path').append( $(elem).clone() )
+
+    $('#gricer-header .path').append( $(elem).clone().attr('data-label', label).html(label) )

 $('a[data-gricer-process]').live 'click', ->
   $.gricer.loading()
@@ -155,7 +157,7 @@

         label = label_row = $('')
         if data.detail_uri
-          label = $('').attr('href', data.detail_uri.replace('%25%7Bself%7D', encodeURI(key)))
+          label = $('').attr('href', data.detail_uri.replace('%25%7Bself%7D', encodeURI(key)))
           label_row.append(label)

         if graph
@@ -163,7 +165,9 @@
         else
           label.append($(''))

-        label.append($.gricer.labelFor key)
+        lable_text = $.gricer.labelFor key
+        label.attr('data-label', lable_text)
+        label.append(lable_text)

         row = $('
').append( label_row )

@@ -235,7 +239,7 @@
           graph = data.data.length < 4 or $.gricer.colors[i] and line[1]/data.total > 0.05
           label = label_row = $('')
           if data.detail_uri
-            label = $('').attr('href', data.detail_uri.replace('%25%7Bself%7D', encodeURI(line[0])))
+            label = $('').attr('href', data.detail_uri.replace('%25%7Bself%7D', encodeURI(line[0])))
             label_row.append(label)

           if graph
@@ -243,8 +247,10 @@
           else
             label.append($(''))

-          label.append($.gricer.labelFor line[0])
-
+          label_text = $.gricer.labelFor(line[0])
+          label.attr('data-label', label_text)
+          label.append(label_text)
+
           row = $('
')
           .append( label_row )
           .append( $('').append(line[1]) )


Remove merge marker.

Remove merge marker.

--- app/assets/javascripts/views/circles/tasks/form.coffee
+++ app/assets/javascripts/views/circles/tasks/form.coffee
@@ -91,7 +91,6 @@
       $('#task_organizer_id').empty()
       $('#task_organizer_id').parent().hide()

-<<<<<<< HEAD
   init = ->
     if $("form.edit_task, form.new_task").length > 0
       showOrHideStartDate()


Fix JS handler that had gone missing

Fix JS handler that had gone missing

--- app/assets/javascripts/views/circles/tasks/show.coffee
+++ app/assets/javascripts/views/circles/tasks/show.coffee
@@ -53,5 +53,5 @@
   .on('click', '#button-open-find-helpers', openSourcingOptionsModal)
   .on('click', '.assign-helpers .button', assignVolunteer)
   .on('click', '.invite-helpers .button', inviteHelpers)
-  .on('click', '.volunteers .unassign-user-icon', unassignVolunteer)
+  .on('click', '.users-box .unassign-user-icon', unassignVolunteer)


Add the enableLegacyRegistration option to acceptance test settings

--- services/web/test/acceptance/config/settings.test.coffee
+++ services/web/test/acceptance/config/settings.test.coffee
@@ -3,6 +3,7 @@

 module.exports =
 	enableSubscriptions: true
+	enableLegacyRegistration: true

 	features: features =
 		v1_free:


fixed last conflict

fixed last conflict

--- packages/rocketchat-ui-message/message/messageBox.coffee
+++ packages/rocketchat-ui-message/message/messageBox.coffee
@@ -77,7 +77,15 @@
 		return RocketChat.settings.get('FileUpload_MediaTypeWhiteList')

 	showFileUpload: ->
-		return RocketChat.settings.get('FileUpload_Enabled')
+		if (RocketChat.settings.get('FileUpload_Enabled'))
+			roomData = Session.get('roomData' + this._id)
+			if roomData?.t is 'd'
+				return RocketChat.settings.get('FileUpload_Enabled_Direct')
+			else
+				return true
+		else
+			return RocketChat.settings.get('FileUpload_Enabled')
+

 	showMic: ->
 		return Template.instance().showMicButton.get()
@@ -88,13 +96,6 @@
 	showSend: ->
 		if not Template.instance().isMessageFieldEmpty.get()
 			return 'show-send'
-
-	showFileUpload: ->
-		roomData = Session.get('roomData' + this._id)
-		if roomData?.t is 'd'
-			return RocketChat.settings.get('FileUpload_Enabled_Direct')
-		else
-			return true

 	showLocation: ->
 		return RocketChat.Geolocation.get() isnt false


small change to mongo query

small change to mongo query

--- packages/tower-store/server/mongodb/serialization.coffee
+++ packages/tower-store/server/mongodb/serialization.coffee
@@ -155,6 +155,9 @@
               _value = _.castArray(_value)
             if _key == '$match'
               _key = '$regex' # @todo tmp
+            # @todo tmp
+            if _key == '$regex' && _value instanceof Array
+              _value = _value[0]

             result[key][_key] = @encode field, _value, _key
       else


fix expected line number

fix expected line number

--- test/browser/src/error.coffee
+++ test/browser/src/error.coffee
@@ -91,7 +91,7 @@

     it "has the expected line number in the stack", ->
       if myError.stack? and not isOpera
-        (expect getStackLine myError.stack).toBe 94 # set JavaScript line number where myError is thrown
+        (expect getStackLine myError.stack).toBe 100 # set JavaScript line number where myError is thrown

   describe "snatch()", ->


Fix object removal

Fix object removal

--- entity.coffee
+++ entity.coffee
@@ -6,7 +6,7 @@
     objects.push object
   del: (object) ->
     object.doDetach()
-    objects = obj for obj in objects when obj isnt object
+    objects = (obj for obj in objects when obj isnt object)
   all: (callback) ->
     callback(obj) for obj in objects
   select: (callback) ->


Fixing manipulation tests #13

Fixing manipulation tests #13

--- test/manipulation.spec.coffee
+++ test/manipulation.spec.coffee
@@ -33,7 +33,7 @@
         contents = '
Hello
'
         $(div).append(contents)
         assert.lengthOf div.childNodes, 2
-        assert.equal div.getElementsByTagName('p')[0].innerText, 'Hello'
+        assert.equal div.getElementsByTagName('p')[0].textContent, 'Hello'
         assert.equal div.getElementsByTagName('*')[1].tagName, 'P'

     test 'append element', ->
@@ -54,7 +54,7 @@
         contents = '
Hello
'
         $(div).prepend(contents)
         assert.lengthOf div.childNodes, 2
-        assert.equal div.getElementsByTagName('p')[0].innerText, 'Hello'
+        assert.equal div.getElementsByTagName('p')[0].textContent, 'Hello'
         assert.equal div.getElementsByTagName('*')[0].tagName, 'P'

     test 'prepend element', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -123,6 +123,6 @@

   4.circularPoints (p, i) ->
     equalEnough p.x, points[i].x, 0.001 "#{p.x} == #{points[i].x}"
-    equalEnough p.y, points[i].y, 00"#{p.y} == #{points[i].y}"
+    equalEnough p.y, points[i].y, 0.001,"#{p.y} == #{points[i].y}"

 module undefined


String#extension

String#extension

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -21,7 +21,7 @@

   equals "jquery.min.js".extension(), "js"

-  equals "src/bouse."
+  equals "src/bouse.js.coffee".ex

 test "#humanize", ->
   equals "employee_salary".humanize(), "Employee salary"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -31,6 +31,9 @@

   ok ''.parse() == '', "Empty string parses to exactly the empty string"

+test "#startsWith", ->
+  eq
+
 test "#titleize", ->
   equals "man from the boondocks".titleize(), "Man From The Boondocks"
   equals "x-men: the last stand".titleize(), "X Men: The Last Stand"


:nail_care: whitespace

:nail_care: whitespace

--- test/unit/test_updates.coffee
+++ test/unit/test_updates.coffee
@@ -28,7 +28,7 @@
       class: 'task-list-item-checkbox'
       disabled: true
       checked: false
-
+
     @blockquote = $ '<blockquote>'

     @quotedList = $ '<ul>', class: 'task-list'
@@ -46,7 +46,7 @@
       class: 'task-list-item-checkbox'
       disabled: true
       checked: false
-
+
     @innerBlockquote = $ '<blockquote>'

     @innerList = $ '<ul>', class: 'task-list'
@@ -232,7 +232,7 @@

     @blockquote.append @innerBlockquote

-    @container.append @blockquote
+    @container.append @blockquote

     @orderedCompleteItem.append @orderedCompleteCheckbox
     @orderedList.append @orderedCompleteItem
@@ -241,9 +241,9 @@
     @orderedIncompleteItem.append @orderedIncompleteCheckbox
     @orderedList.append @orderedIncompleteItem
     @orderedIncompleteItem.expectedIndex = 9
-
+
     @container.append @orderedList
-
+
     @blockquote.append @field

     $('#qunit-fixture').append(@container)


fix file errors

fix file errors

--- app/notebook.coffee
+++ app/notebook.coffee
@@ -329,11 +329,11 @@
         try
           imported = JSON.parse file.content
         catch e
-          run_context.fns.error "File #{file.filename} isn't a lead.js notebook:\n#{e}"
+          run_context.error "File #{file.filename} isn't a lead.js notebook:\n#{e}"
           return
         version = imported.lead_js_version
         unless version?
-          run_context.fns.error "File #{file.filename} isn't a lead.js notebook"
+          run_context.error "File #{file.filename} isn't a lead.js notebook"
           return
         import_notebook run_context.notebook, run_context.output_cell, imported, options


Updated _checkOpenedDatabasesAndSelectDefault

--- www-dev/src/elements/page-search/page-search.coffee
+++ www-dev/src/elements/page-search/page-search.coffee
@@ -184,12 +184,11 @@
     return text

   _checkOpenedDatabasesAndSelectDefault: ->
-    openedDatabaseMeta = (app.db.find 'opened-database-meta')[0]
-    if (not openedDatabaseMeta) or openedDatabaseMeta.count is 0
+    if @domHost.openedDatabaseList.length is 0
       @domHost.showModalDialog "Please open a database before querying.", =>
         @domHost.navigateToPage '#/'
     else
-      if openedDatabaseMeta.count > 1
+      if @domHost.openedDatabaseList.length > 1
         @domHost.showModalDialog "Please note that opening multiple database are not yet supported. The last opened database will be automatically selected."

   navigatedIn: ->


Also link to parent from comments index

Also link to parent from comments index

--- resources/assets/coffee/react/comments-index/main.coffee
+++ resources/assets/coffee/react/comments-index/main.coffee
@@ -62,6 +62,7 @@
             showReplies: false
             showCommentableMeta: true
             showPermalink: true
+            linkParent: true
             depth: 0

         div ref: @pagination


do create root symlink after all

do create root symlink after all

--- src/smc-project/raw_server.coffee
+++ src/smc-project/raw_server.coffee
@@ -46,9 +46,6 @@

     async.series([
         (cb) ->
-            if kucalc.IN_KUCALC # no need, since this link is already created in the Docker image (and not creating it saves a little time)
-                cb()
-                return
             # create the root symbolic link, so that it is possible to
             # browse the entire filesystem, including tmp
             target = process.env.SMC + '/root'


peer grading; fix number of peers

peer grading; fix number of peers

--- src/smc-webapp/editor_course.cjsx
+++ src/smc-webapp/editor_course.cjsx
@@ -707,7 +707,8 @@
             assignment = store.get_assignment(assignment)
             if assignment.getIn(['peer_grade', 'map'])?
                 return  # nothing to do
-            map = misc.peer_grading(store.get_student_ids())
+            N = assignment.getIn(['peer_grade','number']) ? 1
+            map = misc.peer_grading(store.get_student_ids(), N)
             @set_peer_grade(assignment, map:map)

         # Copy the files for the given assignment_id from the given student to the
@@ -3997,7 +3998,7 @@

{misc.trunc(@props.settings?.get('title'),40)}

     render : ->
-        window.s = {a:@props.redux?.getActions(@props.name), s:@props.redux?.getStore(@props.name)}  # for DEV
+        #window.s = {a:@props.redux?.getActions(@props.name), s:@props.redux?.getStore(@props.name)}  # for DEV

             {@render_save_button()}
             {@render_error()}


cleanup onEvent element attributes and meta.refresh

--- src/back/back-transport.coffee
+++ src/back/back-transport.coffee
@@ -6,6 +6,8 @@
 xhr = require '../modules/xhr.coffee'
 gonzales = require '../modules/gonzales.coffee'

+META_ATTRIBS_FOR_DEL = ['Content-Security-Policy', 'refresh']
+ONEVENT_ATTRIBS = [ 'onload', 'onclick', 'onkeypress' ]

 class TreeElementNotFound extends Error

@@ -74,11 +76,10 @@
       element.parentElement?.removeChild(element)

   deleteMeta: (document) ->
-    metaElements = document.querySelectorAll(
-      'meta[http-equiv="Content-Security-Policy"]'
-    )
+    metaElements = document.querySelectorAll('meta[http-equiv]')
     metaElements.forEach (element) ->
-      element.parentElement?.removeChild(element)
+      if element.getAttribute('http-equiv') in META_ATTRIBS_FOR_DEL
+        element.parentElement?.removeChild(element)

   deleteSendBoxAttrib: (document) ->
     iframes = document.querySelectorAll('iframe[sendbox]')
@@ -99,10 +100,18 @@
     inputs.forEach (input) ->
       input.setAttribute('value', '') if input.getAttribute('value')

+  clearOnEventAttribs: (document) ->
+    elements = document.querySelectorAll("[#{ONEVENT_ATTRIBS.join('],[')}]")
+    elements.forEach (element) ->
+      for attr in element.attributes
+        if attr?.name in ONEVENT_ATTRIBS
+          element.removeAttribute(attr.name)
+
   cleanUp: (document) ->
     console.log "DOCUMENT=", document
     @deleteScripts(document)
     @deleteMeta(document)
+    @clearOnEventAttribs(document)
     @deleteSendBoxAttrib(document)
     @deleteAxtElements(document)
     @deleteAxtAttribs(document)
@@ -167,10 +176,10 @@
               callback counter
           else
             href = convertURL(tag.getAttribute('href'), dom.url)
-            Base64 href,tag,(error, tag, result) ->
+            Base64 href, tag, (error, tag, result) ->
               counter--
               if error?
-                console.error "(href) Base 64 error:", error.stack
+                console.error "(href) Base64 error (href=#{href}):", error.stack
               else
                 tag.setAttribute "href", result
               callback counter
@@ -179,6 +188,7 @@
             counter--
             if error?
               console.error "(style)gonzales error:", error.stack
+              console.error tag.innerHTML
             else
               tag.innerHTML = result
             callback counter


Allow linking to myapp.com/root/ when using root

I could be wrong, no tests except within my app, but it seems like setting a root option for the Router, like `root: "/admin/"`, then linking to `href="/admin/"` within the app, redirects to localhost/admin instead of localhost/admin/. I'm not sure if both of these checks are necessary (could even just do "url isnt root" instead of the current "url isnt '/'") but I wasn't sure if that would cause any chaos for other situations-

--- src/chaplin/lib/history.coffee
+++ src/chaplin/lib/history.coffee
@@ -104,7 +104,7 @@
     @fragment = fragment

     # Don't include a trailing slash on the root.
-    if fragment.length is 0 and url isnt '/'
+    if fragment.length is 0 and url isnt '/' and ( url isnt @root or @options.trailing isnt true )
       url = url.slice 0, -1

     # If pushState is available, we use it to set the fragment as a real URL.
@@ -132,4 +132,4 @@
     if options.trigger
       return @loadUrl(fragment)

-module.exports = if Backbone.$ then History else Backbone.History+module.exports = if Backbone.$ then History else Backbone.History


utils/query-string-encoder

utils/query-string-encoder

--- src/client/sharer/index.coffee
+++ src/client/sharer/index.coffee
@@ -1,17 +1,12 @@
-_        = require '../../utils/lodash'
-findUrl  = require '../../utils/find-url'
-assign      = require '../../polyfills/assign'
+_         = require '../../utils/lodash'
+findUrl   = require '../../utils/find-url'
+assign    = require '../../polyfills/assign'
 domWalker = require '../../utils/dom-walker'
-
-to_qs = (params)->
-  _.map params, (v,k) ->
-    encodeURIComponent(k)+'='+encodeURIComponent(v)
-  .join('&')
+qs        = require '../../utils/query-string-encoder'

 popup = (location, opts={}, params={})->
   return unless location?
-  querystring = to_qs(params)
-  share = window.open("#{location}?#{querystring}", 'hull_share', "location=0,status=0,width=#{opts.width},height=#{opts.height}")
+  share = window.open("#{location}?#{qs.encode(params)}", 'hull_share', "location=0,status=0,width=#{opts.width},height=#{opts.height}")
   new Promise (resolve, reject)->
     interval = setInterval ->
       try


remove un intended class name

remove un intended class name

--- src/command_center_view.coffee
+++ src/command_center_view.coffee
@@ -49,7 +49,7 @@
     e.stopPropagation()
     if @menu isnt "current_users"
       $('.parley div.controller-view').children().remove()
-      $('.parley div.controller-view').append('<input class="search entypo-comment" placeholder="Start  Chat">')
+      $('.parley div.controller-view').append('<input class="search" placeholder="Start  Chat">')
       for user in app.current_users
         view = new UserView(user, this)
         view.render()
@@ -137,7 +137,7 @@
       e.stopPropagation()
     @new_convo_params = []
     $('.parley div.controller-view').children().remove()
-    $('.parley div.controller-view').append('<input class="search entypo-comment" placeholder="Start  Chat">')
+    $('.parley div.controller-view').append('<input class="search" placeholder="Start  Chat">')
     for user in app.current_users
       view = new UserView(user, this)
       view.render()


internal node list management tests for Server

--- src/tests/spec/server.spec.coffee
+++ src/tests/spec/server.spec.coffee
@@ -108,3 +108,84 @@
 							expect(sendSpy.mostRecentCall.args).toEqual([
 									fakeMessage
 								])
+
+					describe 'when manipulating the nodes-list', ->
+						it 'should be possible to add nodes', ->
+							fakeNode1 = {
+								a: 1
+								b: 2
+							}
+							fakeNode2 = {
+								c: 3
+								d: 4
+							}
+							server.addNode(fakeNode1)
+							server.addNode(fakeNode1)
+							server.addNode(fakeNode2)
+							savedNodes = server.getNodes()
+							expect(savedNodes[0]).toEqual(fakeNode1)
+							expect(savedNodes[1]).toEqual(fakeNode2)
+							expect(savedNodes.length).toEqual(2)
+
+						it 'should be possible to remove nodes', ->
+							fakeNode1 = {
+								a: 1
+								b: 2
+							}
+							fakeNode2 = {
+								c: 3
+								d: 4
+							}
+							server.addNode(fakeNode1)
+							server.addNode(fakeNode2)
+							server.removeNode(fakeNode1)
+							server.removeNode(fakeNode1)
+							savedNodes = server.getNodes()
+							expect(savedNodes[0]).toEqual(fakeNode2)
+							expect(savedNodes.length).toEqual(1)
+
+						it 'should be possible to find nodes by id', ->
+							fakeNode1 = {
+								a: 1
+								b: 2
+								id: '1'
+							}
+							fakeNode2 = {
+								c: 3
+								d: 4
+								id: '2'
+							}
+							server.addNode(fakeNode1)
+							server.addNode(fakeNode2)
+							expect(server.getNode('1')).toEqual(fakeNode1)
+							expect(server.getNode('2')).toEqual(fakeNode2)
+							expect(server.getNode('3')).toEqual(null)
+
+						it 'should be possible to find nodes by type', ->
+							fakeNode1 = {
+								a: 1
+								b: 2
+								type: 'supernode'
+							}
+							fakeNode2 = {
+								c: 3
+								d: 4
+								type: 'node'
+							}
+							fakeNode3 = {
+								e: 5
+								f: 6
+								type: 'node'
+							}
+							server.addNode(fakeNode1)
+							server.addNode(fakeNode2)
+							server.addNode(fakeNode3)
+							expect(server.getNodes('supernode')).toEqual([
+									fakeNode1
+								])
+							expect(server.getNodes('node')).toEqual([
+									fakeNode2
+									fakeNode3
+								])
+							allNodes = server.getNodes()
+							expect(allNodes.length).toEqual(3)


added notification if current hours < 3

added notification if current hours < 3

--- widgets/charts.widgets/zoho-timer.coffee
+++ widgets/charts.widgets/zoho-timer.coffee
@@ -35,9 +35,13 @@
   total = json?.hours_daily
   if total?
     $(".zoho-this-month").html(total + " hrs")
-    if total < 2
+    if total < 2 and json.billable.slice(-1)[0].y < 4
       setTimeout ( =>
         $(".zoho-notice").html("You are going slow this month. Pace up!")
+      ), 1000
+    else if json.billable.slice(-1)[0].y < 3
+      setTimeout ( =>
+        $(".zoho-notice").html("You are not done for the day!")
       ), 1000

 update: (output, domEl) ->


Removing (currently) useless file.

Removing (currently) useless file.

--- src/toaster/misc/inject-ns.coffee
+++ src/toaster/misc/inject-ns.coffee
@@ -1,18 +0,0 @@
-fs = require 'fs'
-path = require 'path'
-exec = (require "child_process").exec
-
-Toast = require '../toast'
-
-module.exports = class InjectNS
-
-  constructor:( @builders )->
-
-    console.log "Declaring namespaces for files..."
-    for builder in @builders
-      for f in builder.files
-        f.getinfo true
-        fs.writeFileSync f.realpath, f.raw
-        f.getinfo false
-        console.log f.realpath
-    console.log "...done."


Activity: public/followed feed link titles

--- client/Social/Activity/views/activitylistheader.coffee
+++ client/Social/Activity/views/activitylistheader.coffee
@@ -48,6 +48,7 @@

     @feedFilterNav = new KDMultipleChoice
       labels       : ["Public", "Followed"]
+      titles       : ["Click to see the posts on public feed", "Click to see only the posts you follow"]
       cssClass     : 'feed-type-selection'
       defaultValue : "Public"
       callback     : (selection)->


AdminModal: Show group slug in url input.

--- client/admin/lib/views/groupgeneralsettingsview.coffee
+++ client/admin/lib/views/groupgeneralsettingsview.coffee
@@ -100,7 +100,7 @@
       label        : 'URL'
       description  : 'Your team URL can only contain lowercase letters, numbers and dashes (and must start with a letter or number). We don\'t recommend changing this.'
       name         : 'url'
-      defaultValue : Encoder.htmlDecode group.title ? ''
+      defaultValue : Encoder.htmlDecode group.slug ? ''
       placeholder  : 'Please enter a title here'

     addInput form,


AdminMembersView: check if it's koding based on the provided data not for current group

--- client/admin/lib/views/members/adminmembersview.coffee
+++ client/admin/lib/views/members/adminmembersview.coffee
@@ -50,7 +50,7 @@
     , data

-    if isKoding()
+    if isKoding data
       blocked.addSubView new GroupsBlockedUserView {}, data
     else
       blocked.addSubView @blockedView = new TeamMembersCommonView


Properly handle websockets close.

Properly handle websockets close.

--- src/trans-websocket-hybi10.coffee
+++ src/trans-websocket-hybi10.coffee
@@ -62,9 +62,14 @@
             console.error('fin flag not set')
             @didClose(1002, "Fin flag not set")
             return
-        if (buf[0] & 0xF) isnt 1
-            console.error('not a text frame', buf[0] & 0xF)
-            @didClose(1002, "not a text frame")
+        opcode = buf[0] & 0xF
+        if opcode isnt 1 and opcode isnt 8
+            console.error('not a text nor close frame', buf[0] & 0xF)
+            @didClose(1002, "not a text nor close frame")
+            return
+        if opcode is 8 and not ((buf[1] & 0x7F) < 126)
+            console.error('wrong length for close frame')
+            @didClose(1002, 'wrong length for close frame')
             return
         masking = not(not(buf[1] & 128))
         if (buf[1] & 0x7F) < 126
@@ -96,11 +101,22 @@
             for i in [0...length]
                 payload[i] = payload[i] ^ key[i % 4]
         @recv_buffer = buf.slice(l + length)
-        payload = payload.toString('utf-8')
         #console.log('ok', masking, length)
-        @session.didMessage(JSON.parse(payload))
-        if @recv_buffer
-            return @didMessage()
+        if opcode is 1
+            payload_str = payload.toString('utf-8')
+            @session.didMessage(JSON.parse(payload_str))
+            if @recv_buffer
+                return @didMessage()
+        else if opcode is 8
+            if payload.length >= 2
+                status = (payload[0] << 8) | (payload[1] << 0)
+            else
+                status = 1002
+            if payload.length > 2
+                reason = payload.slice(2).toString('utf-8')
+            else
+                reason = "Connection cosed by user"
+            @didClose(status, reason)
         return

     doSendFrame: (payload) ->


NavigationMachineItem: Hijack click and stop DOM event if machine is not approved.

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -151,6 +151,16 @@
       kd.utils.wait 1000, @progress.bound 'hide'

+  click: (e) ->
+
+    m = @machine
+
+    if not m.isMine() and m.isPermanent() and not m.isApproved()
+      kd.utils.stopDOMEvent e
+
+    super
+
+
   pistachio: ->

     return """


NavigationMachineItem: show machine settings for only running and stopped machines in Teams [completes #106633670]

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -1,15 +1,19 @@
 kd                           = require 'kd'
-nick                         = require '../util/nick'
-JView                        = require '../jview'
-Machine                      = require '../providers/machine'
 globals                      = require 'globals'
 htmlencode                   = require 'htmlencode'
-groupifyLink                 = require '../util/groupifyLink'
+
+JView                        = require 'app/jview'
 KDCustomHTMLView             = kd.CustomHTMLView
 KDProgressBarView            = kd.ProgressBarView
-MachineSettingsModal         = require '../providers/machinesettingsmodal'
+
+nick                         = require 'app/util/nick'
+Machine                      = require 'app/providers/machine'
+isKoding                     = require 'app/util/isKoding'
+groupifyLink                 = require 'app/util/groupifyLink'
+userEnvironmentDataProvider  = require 'app/userenvironmentdataprovider'
+
+MachineSettingsModal         = require 'app/providers/machinesettingsmodal'
 SidebarMachineSharePopup     = require 'app/activity/sidebar/sidebarmachinesharepopup'
-userEnvironmentDataProvider  = require 'app/userenvironmentdataprovider'
 SidebarMachineConnectedPopup = require 'app/activity/sidebar/sidebarmachineconnectedpopup'

@@ -124,6 +128,9 @@
     { status: { state } } = @machine
     { NotInitialized, Running, Stopped, Terminated, Unknown } = Machine.State

+    unless isKoding()
+      return state in [ Running, Stopped ]
+
     return state in [ NotInitialized, Running, Stopped, Terminated, Unknown ]


refactory stuffs

refactory stuffs

--- src/authentication/actions/authenticateWithGoogle.coffee
+++ src/authentication/actions/authenticateWithGoogle.coffee
@@ -3,6 +3,7 @@
 request = require 'request'

 module.exports = (req, res, next) ->
+
     oauth2Client = new OAuth2(config.CLIENT_ID, config.CLIENT_SECRET, config.REDIRECT_URL)
     oauth2Client.getToken req.params.code, (err, tokens) ->
       next.ifError err


Sidebar: Fix condition to instantiate a machine settings icon

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -67,11 +67,9 @@
     isMine     = @machine.isMine()
     isApproved = @machine.isApproved()

-    if isMine or isApproved
-      if @settingsEnabled()
-        @createSettingsIcon()
-    else
-        @createSettingsIconPlaceholder()
+    if (isMine or isApproved) and @settingsEnabled()
+    then @createSettingsIcon()
+    else @createSettingsIconPlaceholder()

     kd.singletons.computeController
       .on "reconnecting-#{@machine.uid}", =>


No need these on client side anymore

No need these on client side anymore

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -315,35 +315,6 @@

         log "App to run:", name
         callback?()
-
-  addScript:(app, scriptInput, callback)->
-
-    scriptPath = "#{getAppPath(app)}/#{scriptInput}"
-    if /^\.\//.test scriptInput
-      @kiteController.run "cat #{escapeFilePath scriptPath}", (err, response)=>
-        if err then warn err
-
-        if /.coffee$/.test scriptInput
-          require ["coffee-script"], (coffee)->
-            js = coffee.compile response, { bare : yes }
-            callback err, js
-        else
-          callback err, response
-    else
-      callback null, scriptInput
-
-  saveCompiledApp:(app, script, callback)->
-
-    @getSingleton("kiteController").run
-      method        : "uploadFile"
-      withArgs    : {
-        path      : escapeFilePath "#{getAppPath app}/index.js"
-        contents  : script
-      }
-    , (err, response)=>
-      if err then warn err
-      # log response, "App saved!"
-      callback?()

   publishApp:(path, callback)->


Home: changed slogan

Home: changed slogan

--- client/landing/site.landing/coffee/home/AppView.coffee
+++ client/landing/site.landing/coffee/home/AppView.coffee
@@ -102,7 +102,7 @@

-
Say goodbye to localhost

+
Your next localhost is here.

             Develop in Go, Python, Node, Ruby, PHP, etc or play with Docker, Wordpress, Django, Laravel or create Android, IOS/iPhone, HTML5 apps. All for FREE!


Remove iceberg inserted event on update

Remove iceberg inserted event on update

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -49,7 +49,6 @@
         @toggleInputVisibility()
         @setFormSubmitable()
         @setCocoonFormSubmitable()
-        @line.trigger "iceberg:inserted"

     _bindSelectorsInitialization: ->
       that = this


Fix position scrolling issues

Fix position scrolling issues

--- src/views/position-details.coffee
+++ src/views/position-details.coffee
@@ -135,15 +135,11 @@
             app.commands.execute 'setRadiusFilter', 750

         onRender: ->
-            @routeRegion.show new RouteView
-                model: @model
-                route: @route
-                parentView: @
-                routingParameters: @routingParameters
-                selectedUnits: null
-                selectedPosition: @selectedPosition
+            super()
             @renderAdminDivs()
-            @listenTo app.vent, 'hashpanel:render', (hash) -> @_triggerPanel(hash)
+            # Force this to fix scrolling issues with collapsing divs
+            app.getRegion('navigation').currentView.updateMaxHeights()
+
         renderAdminDivs: ->
             divsWithUnits = @divList.filter (x) -> x.has('unit')
             emergencyDiv = @divList.find (x) ->


A student can see the snapshot as they annotate the image question answers.

--- app/assets/javascripts/image_question.js.coffee
+++ app/assets/javascripts/image_question.js.coffee
@@ -19,6 +19,7 @@

     @shutterbug       = new Shutterbug(".interactive-mod > *:first-child", null,(image_tag)=>
       @set_image(image_tag)
+      @set_svg_background()
     ,@image_question_id)

     @$answer_text     = $("#{@form_sel} .image_answer_text")
@@ -63,6 +64,9 @@
     $("#{@button_sel} .snapshot_thumbnail").attr("src",@current_src)
     $("#{@button_sel} .snapshot_thumbnail").hide() unless @current_src
     $("#{@button_sel} .take_snapshot").html("take snapshot") unless @current_src
+
+  set_svg_background: =>
+    svgCanvas.embeddable_image_question_answer_annotation.setBackground('#FFF', @current_src)(@set_svg_background_cb)

   save_failed: ->
     $("#save").html("Save failed!")
@@ -125,4 +129,3 @@

 # export our class
 window.ImageQuestion = image_question
-


add hubot file

add hubot file

--- src/hubot-scfes-command.coffee
+++ src/hubot-scfes-command.coffee
@@ -1,4 +1,27 @@
-console.log "aaaaaaa"
+HubotScfes = require('./scfes/src/scfes/hubot-scfes.coffee').HubotScfes
+
 module.exports = (robot) ->
-  robot.respond /scfes remind/, (msg) ->
-    msg.reply "unimplemented function"
+  scfes = new HubotScfes
+  robot.respond /scfes remind stamina (\d+) (\d+)^( \d+)/, (msg) ->
+    now_stamina = parseInt msg.match[1]
+    max_stamina = parseInt msg.match[2]
+
+    next_date = scfes.remindMaxStamina(now_stamina, max_stamina, ->
+      msg.reply "stamina max"
+    )
+    msg.reply "registerd " + next_date
+
+  robot.respond /scfes remind stop/, (msg) ->
+    if scfes.remindStop
+      msg.reply "remind stop"
+    else
+      msg.reply "remind stop error"
+
+  robot.respond /scfes remind stamina (\d+) (\d+) (\d+)/, (msg) ->
+    now_stamina = parseInt msg.match[1]
+    max_stamina = parseInt msg.match[2]
+    multiple = parseInt msg.match[3]
+    next_date = scfes.remindMultipleRecoveryTime(now_stamina, max_stamina, multiple, ->
+      msg.reply multiple + " stamina gather"
+    )
+    msg.reply "registerd " + next_date


remove speak in favor of it being a plugin

--- src/hubot/scripts/speak.coffee
+++ src/hubot/scripts/speak.coffee
@@ -1,38 +0,0 @@
-# Allows Hubot to speak many languages.
-#
-# speak me <phrase> - Detects the language 'phrase' is written in, then
-#                     sends back a spoken version of that phrase in its native
-#                     language.
-
-module.exports = (robot) ->
-  robot.hear /(speak)( me)? (.*)/i, (msg) ->
-    term   = "\"#{msg.match[3]}\""
-    apiKey = process.env.HUBOT_MSTRANSLATE_APIKEY
-    langs = ["en"]
-
-    getLanguagesForSpeak = "http://api.microsofttranslator.com/V2/Ajax.svc/GetLanguagesForSpeak"
-    detect = "http://api.microsofttranslator.com/V2/Ajax.svc/Detect"
-    speak = "http://api.microsofttranslator.com/V2/Ajax.svc/Speak"
-
-    unless apiKey
-      msg.send "MS Translate API key isn't set, get a key at http://www.bing.com/developers/appids.aspx"
-      msg.send "Then, set the HUBOT_MSTRANSLATE_APIKEY environment variable"
-      return
-
-    msg.http(getLanguagesForSpeak)
-      .query({ appId: apiKey })
-      .get() (err, res, body) ->
-        langs = eval(body) unless err
-
-        msg.http(detect)
-          .query({appId: apiKey, text: term})
-          .get() (err, res, body) ->
-            if err or (langs.indexOf(eval(body)) == -1)
-              msg.send "Sorry, I can't speak #{err or eval(body)}"
-              return
-            lang = eval(body)
-
-            msg.http(speak)
-              .query({ appId: apiKey, text: term, language: lang, format: "audio/wav" })
-              .get() (err, res, body) ->
-                msg.send(eval(body)) unless err


Skip dino trailer

Skip dino trailer

--- src/lazerscripts/stage1.coffee
+++ src/lazerscripts/stage1.coffee
@@ -10,10 +10,11 @@
 IntroBarrel = require('./stage1/barrel').default
 JumpMine    = require('./stage1/jump_mine').default
 ShipBoss    = require('./stage1/ship_boss')
-TrailerDino = require('./trailer-dino').default
+#TrailerDino = require('./trailer-dino').default
+Stage1End = require('./stage1end').default

 class Stage1 extends LazerScript
-  nextScript: TrailerDino
+  nextScript: Stage1End

   assets: ->
     @loadAssets('explosion', 'playerShip', 'general')


Delete redirection on Trello authenticated

--- src/login/config/routes.coffee
+++ src/login/config/routes.coffee
@@ -5,12 +5,3 @@
     url: '/login/trello'
     controller: 'TrelloLoginCtrl'
     templateUrl: 'login/states/trello/view.html'
-    resolve:
-      dummy: (localStorageService, $state, $q) ->
-        deferred = $q.defer()
-        if localStorageService.get 'trello_token'
-          $state.go 'tab.current-sprint'
-        else
-          deferred.resolve()
-
-        deferred.promise


Change bower back to bower/bin/bower

Change bower back to bower/bin/bower

--- build_detail.coffee
+++ build_detail.coffee
@@ -42,7 +42,7 @@
   use_taobao_mirror = false
 log "Download electron from #{if use_taobao_mirror then 'taobao mirror' else 'github'}"
 npm_exec_path = path.join __dirname, 'node_modules', 'npm', 'bin', 'npm-cli.js'
-bower_exec_path = path.join __dirname, 'node_modules', '.bin', 'bower'
+bower_exec_path = path.join __dirname, 'node_modules', 'bower', 'bin', 'bower'

 plugin_json_path = path.join ROOT, 'assets', 'data', 'plugin.json'
 mirror_json_path = path.join ROOT, 'assets', 'data', 'mirror.json'


Add support for URLs without http:// prefix

--- cURLImporter.coffee
+++ cURLImporter.coffee
@@ -60,16 +60,21 @@
         else
             throw new Error "Invalid curl file unable to clean string: #{string}"

+    @cleanURL = (string) ->
+      string = @clean string
+      if not string.match(/^https?\:\/\//)
+        string = 'http://' + string
+      return string

     @resolveURL = (curlArgs, rawString, curlObj) ->
         if "url" of curlArgs
             urls = curlArgs.url
-            curlObj.url = @clean urls[urls.length-1]
+            curlObj.url = @cleanURL urls[urls.length-1]
         else
             re = /([^\ ]+)[\ ]*$/;
             m = re.exec(rawString)
             if m != null
-                curlObj.url = @clean m[1]
+                curlObj.url = @cleanURL m[1]

     @resolveMethod =  (curlArgs, curlObj) ->
         methods = ["GET"]


Wrap event handler in function to strip its non-serializable arguments

--- core/app/backbone/controllers/client_controller.coffee
+++ core/app/backbone/controllers/client_controller.coffee
@@ -10,7 +10,7 @@
       newClientModal = new DiscussionModalContainer
       FactlinkApp.discussionModalRegion.show newClientModal
       view = new DiscussionView model: fact
-      view.on 'render', parent.onModalReady
+      view.on 'render', -> parent.remote.onModalReady()
       newClientModal.mainRegion.show view

   showNewFact: (params={}) ->
@@ -23,7 +23,7 @@
       fact_text: params.fact
       title: params.title
       url: params.url
-    factsNewView.on 'render', parent.onModalReady
+    factsNewView.on 'render', -> parent.remote.onModalReady()
     factsNewView.on 'factCreated', (fact) ->
       parent.highlightLastCreatedFactlink(fact.id, params.fact)
     clientModal.mainRegion.show factsNewView


Added search query back to mixpanel

Added search query back to mixpanel

--- core/app/backbone/controllers/search_controller.coffee
+++ core/app/backbone/controllers/search_controller.coffee
@@ -15,6 +15,7 @@
       collection: results

     results.fetch()
-    mp_track 'Search: Top bar search'
+    mp_track 'Search: Top bar search',
+      query: query

   onClose: -> $('.js-navbar-search-box').val('')


Modified cms API url

Modified cms API url

--- src/scripts/collections/featured-openstax-books.coffee
+++ src/scripts/collections/featured-openstax-books.coffee
@@ -8,7 +8,7 @@
   openstaxcms = "#{location.protocol}//#{settings.openstaxcms.host}#{openstaxcmsport}"

   return new class FeaturedOpenStaxBooks extends Backbone.Collection
-    url: "#{openstaxcms}/api/v2/pages/?type=books.Book&limit=250" +
+    url: "#{openstaxcms}/apps/cms/api/v2/pages/?type=books.Book&limit=250" +
          "&fields=cover_url,description,title,cnx_id"
     model: FeaturedBook


warn when using selections of markers with webgl on IE

--- bokehjs/src/coffee/models/glyphs/bokehgl.coffee
+++ bokehjs/src/coffee/models/glyphs/bokehgl.coffee
@@ -1,5 +1,6 @@
 _ = require "underscore"
 gloo2 = require "gloo2"
+{logger} = require "../../core/logging"
 color = require "../../core/util/color"
 color2rgba = color.color2rgba

@@ -1195,6 +1196,13 @@
     else if indices.length == nvertices
       @prog.draw(@gl.POINTS, [0, nvertices])
     else if nvertices < 65535
+      # On IE the marker size is reduced to 1 px when using an index buffer
+      # A MS Edge dev on Twitter said on 24-04-2014: "gl_PointSize > 1.0 works
+      # in DrawArrays; gl_PointSize > 1.0 in DrawElements is coming soon in the
+      # next renderer update.
+      ua = window.navigator.userAgent
+      if ua.indexOf("MSIE ") + ua.indexOf("Trident/") + ua.indexOf("Edge/") > 0
+         logger.warn('WebGL warning: IE is known to produce 1px sprites whith selections.')
       @index_buffer.set_size(indices.length*2)
       @index_buffer.set_data(0, new Uint16Array(indices))
       @prog.draw(@gl.POINTS, @index_buffer)


Add navigation to the dictionary #284

Add navigation to the dictionary #284

Corrected JS error when running on other pages.
Removed the second $ -> and added the lines to the first, as it makes sense for them to run together.

--- app/assets/javascripts/dictionary.js.coffee
+++ app/assets/javascripts/dictionary.js.coffee
@@ -23,6 +23,7 @@
 # Sets up the sidenav on the dictionary.
 $ ->
   sidenav = $('#pdf-guide-sidenav')
+  return unless sidenav.length
   # this is needed because when affix starts, the sidenav loses its width
   sidenav.width(sidenav.width())
   # sets up positioning when affix starts and stops
@@ -46,6 +47,5 @@
       bottom: $('img.footer-logo').outerHeight(true)
     }
   })
-
-$ ->
+  # starts scrollspy
   $('body').scrollspy({ target: '#pdf-guide-sidenav' })


made graphs responsive to window size changes

--- app/assets/javascripts/facilities.js.coffee
+++ app/assets/javascripts/facilities.js.coffee
@@ -59,6 +59,13 @@
 # returns:
 #   the svg object, so it can be further manipulated
 drawGraph = (elem, figures) ->
+    $_elem = $(elem)
+    $_elem.empty()
+    facility_id = parseInt($_elem.attr("facility-id"))
+    data = $_elem.data("data")
+    if not data
+        data = getData(facility_id)
+        $_elem.data({data: data})
     # dimensions for the graph
     margin = getMargins()
     width  = getWidth(elem)
@@ -91,25 +98,18 @@
         .y1( (d) -> y(d.washers + d.driers) )
         .interpolate("monotone")

-    facility_id = parseInt(elem.getAttribute("facility-id"))
-    data = getData(facility_id)

     x.domain(d3.extent(data, (d) -> d.date))
     y.domain([0, 100])

-    # draw the axes
-    svg.append("g")
-        .attr("class", "x axis")
-        .attr("transform", "translate(0," + height + ")")
-        .call(xAxis)
-
     # draw the figures
     for fig in figures
         svg.append("path")
             .datum(data)
             .attr("class", fig.class)
             .attr("d", fig.getFig(x, y, width, height))
-    svg
+    # Redraw this if the window is resized
+    $(window).resize(() -> drawGraph(elem, figures))

 # ######### GRAPHING FUNCTIONS ######### #


Add base layers by default

Add base layers by default

--- app/assets/javascripts/map_editor.js.coffee
+++ app/assets/javascripts/map_editor.js.coffee
@@ -19,7 +19,7 @@
         height: 400
         width: null
       customClass: ''
-      back: "Esri.WorldImagery"
+      back: ['Esri.WorldImagery','OpenStreetMap.HOT','OpenStreetMap.Mapnik','Thunderforest.Landscape']
       show: null
       edit: null
       change: null


Issue 4989: Edit tags in lightbox mode

Issue 4989: Edit tags in lightbox mode

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -339,7 +339,7 @@
             inserted = _.find itemtags, (it) -> it[0] == value

             if !inserted
-                itemtags.push([tag , color])
+                itemtags.push([value , color])
                 $scope.us.tags = itemtags

         $scope.deleteTag = (tag) ->


Update styleguide for React 0.13.3

Update styleguide for React 0.13.3

fixes CNVS-25159

Test Plan:
  - Go to /styleguide
  - The styleguide should function as expected
  - There should be no React related errors/warnings in the
    console.

Change-Id: I9c9891b9c170e4c0675b110a6b41fe59814b60f3
Reviewed-on: https://gerrit.instructure.com/67486
Reviewed-by: Jennifer Stern <7e594e5cba47371ac9ee694a84b9d94f9f6ec815@instructure.com>
Tested-by: Jenkins
QA-Review: Jahnavi Yetukuri <ed2b2672ccc2e59e56c0615df7f01b579202e014@instructure.com>
Product-Review: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>

--- app/coffeescripts/bundles/styleguide.coffee
+++ app/coffeescripts/bundles/styleguide.coffee
@@ -23,18 +23,24 @@

     # React Modal
-    React.renderComponent(
-      ReactModalExample
-        className: 'ReactModal__Content--canvas'
-        overlayClassName: 'ReactModal__Overlay--canvas'
+    React.render(
+      React.createElement(
+        ReactModalExample, {
+          className: 'ReactModal__Content--canvas',
+          overlayClassName: 'ReactModal__Overlay--canvas'
+        }
+      )
     , document.getElementById('react-modal-example')
     );

-    React.renderComponent(
-      ReactModalExample
-        label: 'Trigger Confirm Dialog'
-        className: 'ReactModal__Content--canvas ReactModal__Content--mini-modal'
-        overlayClassName: 'ReactModal__Overlay--canvas'
+    React.render(
+      React.createElement(
+        ReactModalExample, {
+          label: 'Trigger Confirm Dialog',
+          className: 'ReactModal__Content--canvas ReactModal__Content--mini-modal',
+          overlayClassName: 'ReactModal__Overlay--canvas'
+        }
+      )
     , document.getElementById('react-modal-confirm-example')
     )


clear comment form after submit

clear comment form after submit

--- app/components/draft-page/draft-page.coffee
+++ app/components/draft-page/draft-page.coffee
@@ -26,8 +26,9 @@
       $scope.showFullDescription = false

     $scope.newComment = Records.comments.build(bucketId: draftId)
-
+
     $scope.createComment = ->
       $scope.newComment.save()
+      $scope.newComment = Records.comments.build(bucketId: draftId)

     return


preview version not required

preview version not required

--- app/scripts/collections/repositories.coffee
+++ app/scripts/collections/repositories.coffee
@@ -6,7 +6,3 @@
     for item in response
       item.repository.unread_count = item.unread_count
       item.repository
-
-  fetch: (options = {}) ->
-    options.headers = {'Accept': 'application/vnd.github.preview+json'}
-    super(options)


Only show size options on previewable attachment

--- src/trix/controllers/attachment_editor_controller.coffee
+++ src/trix/controllers/attachment_editor_controller.coffee
@@ -55,10 +55,14 @@
       className: "attachment__toolbar"
       data: trixMutable: true

-    toolbarElement.innerHTML = """
-      <button type="button" data-size="small">small</button>
-      <button type="button" data-size="medium">medium</button>
-      <button type="button" data-size="large">large</button>
+    if @attachment.isPreviewable()
+      toolbarElement.innerHTML += """
+        <button type="button" data-size="small">small</button>
+        <button type="button" data-size="medium">medium</button>
+        <button type="button" data-size="large">large</button>
+      """
+
+    toolbarElement.innerHTML += """
       <button type="button" data-remove="true" class="#{css.attachmentRemove} #{css.attachmentRemove}--icon">#{lang.remove}</button>
     """


popularpost: minor fix

popularpost: minor fix

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -32,10 +32,10 @@
     @bindInputEvents()

     @fakeMessageMap = {}
+
     @mostLikedPostsCount = 0
-
-    @changeFilter MOST_LIKED
-    @realFilter = MOST_LIKED
+    @changeFilter  MOST_LIKED
+    @setRealFilter MOST_LIKED

     {socialapi} = KD.singletons
     @once 'ChannelReady', @bound 'bindChannelEvents'
@@ -216,10 +216,8 @@
       @listController.showLazyLoader()

       filter = if data.active then MOST_LIKED else MOST_RECENT
-      @changeFilter filter
-      @realFilter = filter
-
-      @skipItemCount = 0
+      @changeFilter(filter)
+      @setRealFilter(filter)

       @populate()

@@ -383,3 +381,5 @@
     @listController.showLazyLoader()
     @populate()

+  setRealFilter: (filter)-> @realFilter = filter
+


video/helper: Align require assignments

video/helper: Align require assignments

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -1,10 +1,9 @@
-$ = require 'jquery'
-_ = require 'lodash'
-kd = require 'kd'
-remote = require('app/remote').getInstance()
-whoami = require 'app/util/whoami'
-getNick = require 'app/util/nick'
-
+$               = require 'jquery'
+_               = require 'lodash'
+kd              = require 'kd'
+remote          = require('app/remote').getInstance()
+whoami          = require 'app/util/whoami'
+getNick         = require 'app/util/nick'
 ProfileTextView = require 'app/commonviews/linkviews/profiletextview'

 ###*


fix: compare summary test case

fix: compare summary test case

--- tests/integration/components/compare-summary-test.coffee
+++ tests/integration/components/compare-summary-test.coffee
@@ -6,7 +6,7 @@
 }

 test 'it renders', (assert) ->
-  assert.expect 2
+  assert.expect 1
   #
   # # Set any properties with @set 'myProperty', 'value'
   # # Handle any actions with @on 'myAction', (val) ->
@@ -24,4 +24,4 @@
   #
   # assert.equal @$().text().trim(), 'template block text'

-  assert.equal true
+  assert.ok true


early fall animation

early fall animation

--- web/scripts/gameVisual.coffee
+++ web/scripts/gameVisual.coffee
@@ -1,6 +1,7 @@
 class window.GameVisual
     ticker = 0 #counts the number of frames that have passed
     imgArray = [] #array of arrays of preloaded images
+    univImg = [] #array of universally used images
     objArray = [] #array of objects that permeate the game board
     frameClock = null #reference to the setInterval object that runs the game
     lyr1 = null #object for referencing the first of the two canvas objects to be used as a frame
@@ -55,6 +56,7 @@
     #preload all the necessary images.  Sounds will eventually be included in here as well.
     ###
     initResources: (config) ->
+        univImg[0] = "img/x.png"
         tmp = []
         for key,imgar of config
             tmp = []
@@ -125,6 +127,7 @@
     #   < 3 4 1 >
     #       2
     #       v
+    #   5 -> Falling Animation
     #More documenation to be added when the code is more concrete and permanent
     ###
     class charObj
@@ -132,6 +135,7 @@
             @antickerAdd = 0
             @ldir = @dir
             @cstate = 4
+            @fallticker = 0
             return

         absPos: (@xpos,@ypos) ->
@@ -143,12 +147,27 @@
             return

         current: (anticker) ->
+            objState = []
             num = 0
             if @animated and ((anticker + @antickerAdd) % (2 * ar)) >= ar
                 num = 1
             num = num + (2 * @dir)
             num = num % @animarray.length
-            return @animarray[num]
+
+            objState[0] = @animarray[num]
+            objState[1] = @xSize
+            objState[2] = @ySize
+
+            if @cstate == 5
+                @fallticker++
+                objState[1] = @xSize/(@fallticker)
+                objState[2] = @ySize/(@fallticker)
+                if @fallticker > 20
+                    objState[0] = univImg[0]
+            else
+                @fallticker = 0
+
+            return objState

         imFace: (@dir) ->
             return
@@ -198,8 +217,8 @@
     drawChar: (frame) ->
         td = frame.getContext('2d')
         for obj in objArray by -1
-            img = obj.current(@ticker)
-            td.drawImage(img,obj.xpos+obj.xOff,obj.ypos+obj.yOff,obj.xSize,obj.ySize)
+            s = obj.current(@ticker)
+            td.drawImage(s[0],obj.xpos+obj.xOff,obj.ypos+obj.yOff,s[1],s[2])
         return

     chckMv: (config) ->


Cleanup featured book close

Cleanup featured book close

--- src/scripts/modules/featured-books/featured-books.coffee
+++ src/scripts/modules/featured-books/featured-books.coffee
@@ -16,7 +16,7 @@
       @listenTo(library, 'reset', @render)

       # Don't run any animations while the window is being resized
-      @_resizer = () ->
+      @_resizer = () =>
         @stopCarousel({finish: true})
         @startCarousel()
       $(window).resize(@_resizer)
@@ -90,5 +90,5 @@

     close: () ->
       $(window).off("resize", @_resizer)
-      @stopCarousel({finish: true})
+      clearInterval(@_carousel)
       super()


Remove console.log (ugh)

Remove console.log (ugh)

--- static/src/scripts/app/mixens/BaseCollectionMixen.coffee
+++ static/src/scripts/app/mixens/BaseCollectionMixen.coffee
@@ -23,5 +23,4 @@
       _.each keys, (key) =>
         uri.addQueryParam(key, @urlParams[key])

-      console.log uri.toString()
       uri.toString()


Remove extra delay from association test

--- tests/batman/model/associations/associations_test.coffee
+++ tests/batman/model/associations/associations_test.coffee
@@ -66,12 +66,11 @@

   namespace.Store.find 1, (err, store) =>
     products = store.get('products')
-    delay ->
-      products.load {key1: 'value1'}, (err, comments) ->
-        throw err if err
-        equal associationSpy.lastCallArguments[2].data['store_id'], 1
-        equal associationSpy.lastCallArguments[2].data['key1'], 'value1'
-        QUnit.start()
+    products.load {key1: 'value1'}, (err, comments) ->
+      throw err if err
+      equal associationSpy.lastCallArguments[2].data['store_id'], 1
+      equal associationSpy.lastCallArguments[2].data['key1'], 'value1'
+      QUnit.start()

 asyncTest "support custom model namespaces and class names", 2, ->
   namespace = {}


No need to prefetch repos in recentRepos

Repo#withLastBuilds already does a fetch, so this is redundant

--- assets/scripts/app/controllers/repos.coffee
+++ assets/scripts/app/controllers/repos.coffee
@@ -36,7 +36,6 @@
     Visibility.every Travis.INTERVALS.updateTimes, @updateTimes.bind(this)

   recentRepos: (->
-    Travis.Repo.find()
     Travis.LimitedArray.create
       content: Em.ArrayProxy.extend(Em.SortableMixin).create(
         sortProperties: ['sortOrder']


Adds refresh on draft item

Adds refresh on draft item

--- app/assets/javascripts/backend/general_ledger.coffee
+++ app/assets/javascripts/backend/general_ledger.coffee
@@ -16,7 +16,7 @@
       E.ledgers.toggleDraftItemsVisibility.call($(this))

     $('input[data-mask-draft-items]').on 'change', (e) ->
-      E.ledgers.toggleDraftItemsVisibility.call($(this))
+      E.ledgers.toggleDraftItemsVisibility.call($(this), true)

   E.ledgers =
     toggleLetteredItemsVisibility:(changed) ->


Do not render 'delete week' button in Arrange mode

Resolves #570

--- app/assets/javascripts/components/timeline/week.cjsx
+++ app/assets/javascripts/components/timeline/week.cjsx
@@ -94,7 +94,7 @@
       Add Block
     )

-    delete_week = if !@props.week.is_new then (
+    delete_week = if !@props.reorderable && !@props.week.is_new then (
       Delete Week
     )


fix a crash on network errors

fix a crash on network errors

--- background_scripts/completion_search.coffee
+++ background_scripts/completion_search.coffee
@@ -48,12 +48,19 @@
     xhr = new XMLHttpRequest()
     xhr.open "GET", url, true
     xhr.timeout = 2500
-    xhr.ontimeout = xhr.onerror = -> callback null
-    xhr.send()
+    finished = 0
+    # Note(gdh1995): Maybe the 2 events below are not needed, but I just leave it unchanged for robustness
+    xhr.ontimeout = xhr.onerror = ->
+      finished++
+      if finished == 1
+        callback null

     xhr.onreadystatechange = ->
       if xhr.readyState == 4
-        callback if xhr.status == 200 then xhr else null
+        finished++
+        if finished == 1
+          callback if xhr.status == 200 then xhr else null
+    xhr.send()

   # Look up the completion engine for this searchUrl.  Because of DummyCompletionEngine, we know there will
   # always be a match.


refactor keeping of nodes

refactor keeping of nodes

--- lib/assets/javascripts/turbograft/turbolinks.coffee
+++ lib/assets/javascripts/turbograft/turbolinks.coffee
@@ -180,19 +180,24 @@

     refreshedNodes

+  keepNodes = (body, allNodesToKeep) ->
+    for existingNode in allNodesToKeep
+      unless nodeId = existingNode.getAttribute('id')
+        throw new Error("TurboGraft refresh: Kept nodes must have an id.")
+
+      remoteNode = body.querySelector("##{ nodeId }")
+      continue unless remoteNode
+      remoteNode.parentNode.replaceChild(existingNode, remoteNode)
+
+
   persistStaticElements = (body) ->
     allNodesToKeep = []

     nodes = document.querySelectorAll("[tg-static]")
-    for node in nodes
-      allNodesToKeep.push(node)
-
-    for existingNode in allNodesToKeep
-      unless nodeId = existingNode.getAttribute('id')
-        throw new Error("TurboGraft refresh: Static elements must have an id.")
-
-      remoteNode = body.querySelector("##{ nodeId }")
-      remoteNode.parentNode.replaceChild(existingNode, remoteNode)
+    allNodesToKeep.push(node) for node in nodes
+
+    keepNodes(body, allNodesToKeep)
+    return

   refreshAllExceptWithKeys = (keys, body) ->
     allNodesToKeep = []
@@ -201,12 +206,8 @@
       for node in document.querySelectorAll("[refresh=#{key}]")
         allNodesToKeep.push(node)

-    for existingNode in allNodesToKeep
-      unless nodeId = existingNode.getAttribute('id')
-        throw new Error "Turbolinks refresh: Refresh except key elements must have an id."
-
-      remoteNode = body.querySelector("##{ nodeId }")
-      remoteNode.parentNode.replaceChild(existingNode, remoteNode)
+    keepNodes(body, allNodesToKeep)
+    return

   executeScriptTags = ->
     scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'


Copy to `session.user`, for compatibility

--- services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
+++ services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
@@ -46,6 +46,8 @@
 					req.sessionStore.generate(req)
 					for key, value of oldSession
 						req.session[key] = value
+					# copy to the old `session.user` location, for backward-comptability
+					req.session.user = req.session.passport.user
 					req.session.save (err) ->
 						if err?
 							logger.err {user_id: user._id}, "error saving regenerated session after login"


simplify/improve cache selection for font attribute

--- bokehjs/src/coffee/common/properties.coffee
+++ bokehjs/src/coffee/common/properties.coffee
@@ -191,7 +191,6 @@
   initialize: (attrs, options) ->
     @cache = {}
     super(attrs, options)
-

   warm_cache: (source, attrs) ->
     for attr in attrs
@@ -335,36 +334,20 @@
       values: "top middle bottom alphabetic hanging"

   warm_cache: (source) ->
-    super(source, ["color", "alpha", "align", "baseline"])
-    if (@font.fixed_value? and @font_size.fixed_value? and
-        @font_style.fixed_value?)
-      @cache["font"] = @font_value()
-    else
-      @cache["font_array"] = @font_array(source)
+    super(source, ["font", "font_size", "font_style", "color", "alpha", "align", "baseline"])

   cache_select: (name, i) ->
     if name == "font"
-      if @font.fixed_value? and @font_size.fixed_value? and @font_style.fixed_value?
-        @cache.font = @font_value()
-      else
-        @cache.font = @cache.font_array[i]
-      return
-    super(name, i)
+      val = super("font_style", i) + " " + super("font_size", i) + " " + super("font", i)
+      @cache.font = val
+    else
+      super(name, i)

   font_value: () ->
     font       = @font.value()
     font_size  = @font_size.value()
     font_style = @font_style.value()
     return font_style + " " + font_size + " " + font
-
-  font_array: (source) ->
-    font       = @font.array(source)
-    font_size  = @font_size.array(source)
-    font_style = @font_style.array(source)
-    result = []
-    for i in [0...font.length]
-      result.push(font_style[i] + " " + font_size[i] + " " + font[i])
-    return result

   set_value: (ctx) ->
     ctx.font         = @font_value()
@@ -451,7 +434,6 @@
       when "string" then result[field] = new String({obj: model, attr: field})

   return result
-

 visuals = (model, attr="visuals") ->
   result = {}