fix #1962 by reverting mathjax to SVG output; also cleaned up some formatting of mathjax config.

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -62,55 +62,58 @@
 window.MathJax = exports.MathJaxConfig =
     skipStartupTypeset: true
     extensions: ["tex2jax.js","asciimath2jax.js"]  # "static/mathjax_extensions/xypic.js"
-    # common html is the output default: http://docs.mathjax.org/en/latest/output.html
-    jax: ["input/TeX","input/AsciiMath", "output/CommonHTML"]
+    # NOTE: "output/CommonHTML" is the output default: http://docs.mathjax.org/en/latest/output.html
+    # However, **DO NOT** use "output/CommonHTML" for the output JAX; it completely breaks
+    # Sage worksheet output right now.  Maybe when/if worksheets are rewritten
+    # using React, we can change, but not now.  Using "output/SVG" works just fine.
+    #   https://github.com/sagemathinc/smc/issues/1962
+    jax: ["input/TeX","input/AsciiMath", "output/SVG"]
     # http://docs.mathjax.org/en/latest/options/tex2jax.html
     tex2jax:
-        inlineMath: [ ['$','$'], ["\\(","\\)"] ]
-        displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
-        processEscapes: true
-        ignoreClass: "tex2jax_ignore"
-        skipTags: ["script","noscript","style","textarea","pre","code"]
+        inlineMath     : [ ['$','$'], ["\\(","\\)"] ]
+        displayMath    : [ ['$$','$$'], ["\\[","\\]"] ]
+        processEscapes : true
+        ignoreClass    : "tex2jax_ignore"
+        skipTags       : ["script","noscript","style","textarea","pre","code"]

     TeX:
-        MAXBUFFER: 100000  # see https://github.com/mathjax/MathJax/issues/910
-        extensions: ["autoload-all.js", "noUndefined.js", "noErrors.js"]
-        Macros:  # get these from sage/misc/latex.py
-            Bold:  ["\\mathbb{#1}",1]
-            ZZ:    ["\\Bold{Z}",0]
-            NN:    ["\\Bold{N}",0]
-            RR:    ["\\Bold{R}",0]
-            CC:    ["\\Bold{C}",0]
-            FF:    ["\\Bold{F}",0]
-            QQ:    ["\\Bold{Q}",0]
-            QQbar: ["\\overline{\\QQ}",0]
-            CDF:   ["\\Bold{C}",0]
-            CIF:   ["\\Bold{C}",0]
-            CLF:   ["\\Bold{C}",0]
-            RDF:   ["\\Bold{R}",0]
-            RIF:   ["\\Bold{I} \\Bold{R}",0]
-            RLF:   ["\\Bold{R}",0]
-            CFF:   ["\\Bold{CFF}",0]
-            GF:    ["\\Bold{F}_{#1}",1]
-            Zp:    ["\\ZZ_{#1}",1]
-            Qp:    ["\\QQ_{#1}",1]
-            Zmod:  ["\\ZZ/#1\\ZZ",1]
+        MAXBUFFER  : 100000  # see https://github.com/mathjax/MathJax/issues/910
+        extensions : ["autoload-all.js", "noUndefined.js", "noErrors.js"]
+        Macros     : # get these from sage/misc/latex.py
+            Bold  : ["\\mathbb{#1}",1]
+            ZZ    : ["\\Bold{Z}",0]
+            NN    : ["\\Bold{N}",0]
+            RR    : ["\\Bold{R}",0]
+            CC    : ["\\Bold{C}",0]
+            FF    : ["\\Bold{F}",0]
+            QQ    : ["\\Bold{Q}",0]
+            QQbar : ["\\overline{\\QQ}",0]
+            CDF   : ["\\Bold{C}",0]
+            CIF   : ["\\Bold{C}",0]
+            CLF   : ["\\Bold{C}",0]
+            RDF   : ["\\Bold{R}",0]
+            RIF   : ["\\Bold{I} \\Bold{R}",0]
+            RLF   : ["\\Bold{R}",0]
+            CFF   : ["\\Bold{CFF}",0]
+            GF    : ["\\Bold{F}_{#1}",1]
+            Zp    : ["\\ZZ_{#1}",1]
+            Qp    : ["\\QQ_{#1}",1]
+            Zmod  : ["\\ZZ/#1\\ZZ",1]
         noErrors:  # http://docs.mathjax.org/en/latest/tex.html#noerrors
             inlineDelimiters : ["$","$"]
             multiLine        : true
             style:
-                "font-size"      : "85%"
-                "text-align"     : "left"
-                "color"          : "red"
-                "padding"        : "1px 3px"
-                "background"     : "#FFEEEE"
-                "border"         : "none"
+                "font-size"  : "85%"
+                "text-align" : "left"
+                "color"      : "red"
+                "padding"    : "1px 3px"
+                "background" : "#FFEEEE"
+                "border"     : "none"
         noUndefined:  # http://docs.mathjax.org/en/latest/tex.html#noundefined
             attributes:
-                mathcolor        : "red"
-                mathbackground   : "#FFEEEE"
-                mathsize         : "90%"
-
+                mathcolor      : "red"
+                mathbackground : "#FFEEEE"
+                mathsize       : "90%"

     # do not use "xypic.js", frequently causes crash!
     "HTML-CSS":


don't show mathjax until rendered in react version (just looks a lot better)

--- src/smc-webapp/r_misc.cjsx
+++ src/smc-webapp/r_misc.cjsx
@@ -787,6 +787,7 @@
             return
         if @props.has_mathjax
             $(ReactDOM.findDOMNode(@)).mathjax
+                hide_when_rendering : true
                 cb : () =>
                     # Awkward code, since cb may be called more than once if there
                     # where more than one node.


remove unused code

remove unused code

--- vendor/assets/javascripts/pullentity-backbone/app/views/main.js.coffee
+++ vendor/assets/javascripts/pullentity-backbone/app/views/main.js.coffee
@@ -7,9 +7,6 @@
   initialize: ->
     #console.info "pullentity LOADED!!!"
     @site = new Pullentity.Models.Site
-    -#@site.on("change", @initModels )
-    -#@listenTo(@site, "change", @initModels)
-
     @site.fetch
       success: =>
         @initModels()


Frontend now explicitly connects to port 443 - localhost now works

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

     window.Game = @

-    @socket = io.connect('https://snapgame.herokuapp.com', {secure: true})
+    @socket = io.connect('https://snapgame.herokuapp.com:443', {secure: true})
     @socket.emit 'new player', 'name'

     @socket.on 'snap', (data) =>


fix firebase call

fix firebase call

--- routes/index.coffee
+++ routes/index.coffee
@@ -41,10 +41,10 @@
   req.body.secret = undefined

   for friendId in req.body.friends
-    firebase.child('notifications').child(friendId).add
-        dts: req.body.dts
-        type: 'newUser'
-        friendId: req.body.userId
+    firebase.child('notifications').child(friendId).push
+      dts: req.body.dts
+      type: 'newUser'
+      friendId: req.body.userId

   msg = "submitting new user notification to firebase: "+ JSON.stringify req.body
   log msg


Removed console log

Removed console log

--- vendor/assets/javascripts/simple_form_extension/selectize.coffee
+++ vendor/assets/javascripts/simple_form_extension/selectize.coffee
@@ -17,7 +17,6 @@

     @$el.val('')

-    console.log _.defaults @options, @selectizeDefaults()
     @$el.selectize(
       _.defaults @options, @selectizeDefaults()
     )


Prevent `cursor.each` from reading an additional row after iteration has stopped

--- drivers/javascript/cursor.coffee
+++ drivers/javascript/cursor.coffee
@@ -217,15 +217,12 @@
         if onFinished? and typeof onFinished isnt 'function'
             throw new err.ReqlDriverError "Optional second argument to each must be a function."

-        stopFlag = false
         self = @
         nextCb = (err, data) =>
-            if stopFlag isnt true
-                if err?.message is 'No more rows in the cursor.'
-                    onFinished?()
-                else
-                    stopFlag = cb(err, data) is false
-                    @_next nextCb
+            if err?.message is 'No more rows in the cursor.'
+                onFinished?()
+            else if cb(err, data) isnt false
+                @_next nextCb
             else
                 onFinished?()
         @_next nextCb


correction on non existing variable

correction on non existing variable

--- BackofficeBundle/Resources/public/coffee/page/SubElementFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/page/SubElementFormView.coffee
@@ -1,7 +1,7 @@
 SubElementFormView = OrchestraModalView.extend(

   onViewReady: ->
-    displayMenu(displayRoute) if @options.submitted
+    displayMenu() if @options.submitted

 )


validations

validations

--- tinymodel.coffee
+++ tinymodel.coffee
@@ -1,5 +1,6 @@
 class @TinyModel
   @collection: undefined
+  @validators: []
   errors: []

   # Intialize the model.
@@ -19,6 +20,14 @@
     obj.updatedAt  = doc.updatedAt
     obj

+  @validates: (field, validations) ->
+    for validator, condition of validations
+      switch validator
+        when 'presence'
+          @validators.push new PresenceValidator(field, condition)
+        when 'length'
+          @validators.push new LengthValidator(field, condition)
+
   # Find all documents that match the selector.
   #
   # selector - selection criteria
@@ -121,7 +130,7 @@
   @toString: ->
     @collection._name

-  # Taken from -
+  # Modified from -
   # https://coffeescript-cookbook.github.io/chapters/classes_and_objects/cloning
   #
   # Clone a model
@@ -222,9 +231,10 @@
   persisted: ->
     @_id?

-  # Override in sub classes to perform validations
+  # Runs all validators on this model
   validate: ->
-    true
+    val.run(@) for val in @constructor.validators
+    @errors.length == 0

   # Check if this model is valid
   #
@@ -239,7 +249,6 @@
   isValid: ->
     @errors = []
     @validate()
-    @errors.length == 0

   # Get the own properties of this model
   #
@@ -254,27 +263,23 @@
     own   = Object.getOwnPropertyNames( @ )
     props = _.pick( @, own )
     attrs = _.omit( props, '_id' )
-
-  # Add an error to this model
+
   error: (field, message) ->
     @errors ||= []
     e = {}
     e[field] = message
     @errors.push e
-
-  # Check if any errors
+
   hasErrors: ->
     @errors.length > 0
-
-  # Return string of error messages
+
   errorMessages: ->
     msg = []
     for i in @errors
       for key, value of i
         msg.push value
     msg.join(', ')
-
-  # Create un-persisted copy of this model.
+
   copy: (obj) ->
     @constructor.clone( @ )


Change library task

Change library task

--- src/task/library/index.coffee
+++ src/task/library/index.coffee
@@ -3,67 +3,13 @@
 config = require('../../config')
 util = require('../../util')

-name2id = (name) ->
-  name.replace(/-([a-zA-Z0-9])/g, (match, ch) -> ch.toUpperCase()).replace(/\W/g, '')
-
-module.exports = (spec, options) ->
-  options = {}
-
-  name = util.loadPackageJson(options.packageJson)
-  id = if options.isPlugin then name2id('apply-' + name) else name2id(name)
-
-  umdOptions = lib.util.extend(
-    {}
-    {
-      params: options.params
-      transpile: {}
-    }
-  )
-  umdCoverageOptions = lib.util.extend(
-    {}
-    {
-      params: options.params
-      transpile: {
-        coffeeCoverage: {}
-      }
-    }
-  )
-
-  lib.gulp
-  .src(spec)
-  .pipe(lib.pipe.mirror(
-    (
-      lib.pipe.lazypipe()
-      .pipe -> lib.transform.sourceMaps.init()
-      .pipe -> pipe.sort()
-      .pipe -> lib.pipe.concat(name + '.coffee')
-      .pipe -> lib.metadata.data((file) ->
-        {
-          exports: options.exports ? id
-          namespace: options.namespace ? id
-          dependencies: options.dependencies ? []
-        }
-      )
-      .pipe -> pipe.lint()
-      .pipe -> pipe.umd(umdOptions)
-      .pipe -> gulp.dest('dist')
-    )()
-    (
-      lib.pipe.lazypipe()
-      .pipe -> lib.transform.sourceMaps.init()
-      .pipe -> pipe.sort()
-      .pipe -> lib.pipe.concat(name + '.coffee')
-      .pipe -> lib.metadata.data((file) ->
-        {
-          exports: options.exports ? id
-          namespace: options.namespace ? id
-          dependencies: options.dependencies ? []
-        }
-      )
-      .pipe -> pipe.lint()
-      .pipe -> pipe.umd(umdCoverageOptions)
-      .pipe -> gulp.dest('dist')
-    )()
-  ))
-
-
+module.exports = util.fnOption(
+  {
+    spec: 'src/**/*.{js,coffee}'
+    library: {}
+  }
+  (options) ->
+    lib.gulp
+    .src(spec)
+    .pipe(pipe.library(options.library))
+)


correction

correction

--- views/single-tabarea.cjsx
+++ views/single-tabarea.cjsx
@@ -124,7 +124,7 @@
         <TabPane key={1000} eventKey={1000} tab={settings.displayName} id={settings.name} className='poi-app-tabpane'>
         {
           React.createElement settings.reactClass,
-            selectedKey: [@state.key, @state.key]
+            selectedKey: @state.key
             index: 1000
         }
         </TabPane>


JMachine: tidy up the model and add publicId field which will be filled by kloud kite

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -40,13 +40,16 @@
       kiteId            :
         type            : String

+      publicIp          :
+        type            : String
+
       provider          :
         type            : String
         required        : yes

       label             :
         type            : String
-        default         : ""
+        default         : -> ""

       initScript        :
         type            : String
@@ -99,7 +102,6 @@
         callback err, machine

   reviveUsers: permit 'populate users',
-
   success: (client, callback)->

     JUser = require '../user'
@@ -117,7 +119,7 @@
         else
           queue.next()

-    queue.push =>
+    queue.push ->
       callback null, accounts

     daisy queue


update settings.cson: labels in left-side, headings in sections

--- def/template/settings.cson
+++ def/template/settings.cson
@@ -1,60 +1,59 @@
 Settings:
-  "tab-title": "設定"
+  "tab-title": "Settings"
   menu: [
     {
       label: "Core"
-      value: "コア設定"
-      # TODO experiment on removing label
+      value: "Core"
     }
     {
       label: "Editor"
-      value: "エディタ設定"
+      value: "Editor"
     }
     {
       label: "Keybindings"
-      value: "キーバインド"
+      value: "Keybindings"
     }
     {
       label: "Packages"
-      value: "パッケージ"
+      value: "Packages"
     }
     {
       label: "Themes"
-      value: "テーマ"
+      value: "Themes"
     }
     {
       label: "Updates"
-      value: "アップデート"
+      value: "Updates"
     }
     {
       label: "Install"
-      value: "インストール"
+      value: "Install"
     }
   ]
-  "btn-open-config-folder": "設定フォルダを開く"
+  "btn-open-config-folder": "Open Config Folder"
   sectionHeadings: [
-    {label: "Core Settings", value: "コア設定"}
-    {label: "Editor Settings", value: "エディタ設定"}
-    {label: "Keybindings", value: "キーバインド"}
-    {label: "Installed Packages", value: "インストール済みのパッケージ"}
-    {label: "Choose a Theme", value: "テーマの選択"}
-    {label: "Installed Themes", value: "インストール済みのテーマ"}
-    {label: "Install Packages", value: "パッケージのインストール"}
-    {label: "Featured Packages", value: "おすすめのパッケージ"}
-    {label: "Install Themes", value: "テーマのインストール"}
-    {label: "Featured Themes", value: "おすすめのテーマ"}
+    {label: "Core Settings", value: "Core Settings"}
+    {label: "Editor Settings", value: "Editor Settings"}
+    {label: "Keybindings", value: "Keybindings"}
+    {label: "Installed Packages", value: "Installed Packages"}
+    {label: "Choose a Theme", value: "Choose a Theme"}
+    {label: "Installed Themes", value: "Installed Themes"}
+    {label: "Install Packages", value: "Install Packages"}
+    {label: "Featured Packages", value: "Featured Packages"}
+    {label: "Install Themes", value: "Install Themes"}
+    {label: "Featured Themes", value: "Featured Themes"}
   ]
-  "heading-available-updates": {label: "Available Updates", value: "利用可能なアップデート"}
+  "heading-available-updates": {label: "Available Updates", value: "Available Updates"}
   subSectionHeadings: [
-    {label: "Invisible", value: "不可視文字"}
-    {label: "Community Packages", value: "コミュニティパッケージ"}
-    {label: "Core Packages", value: "コアパッケージ"}
-    {label: "Development Packages", value: "開発パッケージ"}
-    {label: "Git Packages", value: "Git パッケージ"}
-    {label: "Community Themes", value: "コミュニティテーマ"}
-    {label: "Core Themes", value: "コアテーマ"}
-    {label: "Development Themes", value: "開発テーマ"}
-    {label: "Git Themes", value: "Git テーマ"}
+    {label: "Invisible", value: "Invisible"}
+    {label: "Community Packages", value: "Community Packages"}
+    {label: "Core Packages", value: "Core Packages"}
+    {label: "Development Packages", value: "Development Packages"}
+    {label: "Git Packages", value: "Git Packages"}
+    {label: "Community Themes", value: "Community Themes"}
+    {label: "Core Themes", value: "Core Themes"}
+    {label: "Development Themes", value: "Development Themes"}
+    {label: "Git Themes", value: "Git Themes"}
   ]
   settings: {
     notes: [


comment out some mildly troublesome code

--- app/assets/javascripts/effective_ckeditor/init.js.coffee
+++ app/assets/javascripts/effective_ckeditor/init.js.coffee
@@ -19,7 +19,7 @@
     $('body').prepend("

").addClass('effective-ckeditor-editting')

     $(window).on 'beforeunload', (event) -> promptToSaveIfDirty(event)
-    $(window).on 'unload', (event) -> $.cookie('effective_regions_editting', '', {path: '/', expires: -1})
+    # $(window).on 'unload', (event) -> $.cookie('effective_regions_editting', '', {path: '/', expires: -1})

     ckeditors.each ->
       editor_div = $(this)


request addie launch on cad entry

request addie launch on cad entry

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -22,6 +22,15 @@

 dsg = ""

+launchAddieInstance = () =>
+  ($.get "/gatekeeper/launchAddie?user="+g.user+"&design="+dsg, (data) =>
+    console.log("addie instance launched and ready!")
+    g.ve.addie.init()
+  ).fail () ->
+    console.log("failed to launch addie instance")
+    true
+
+
 #Entry point
 root.go = ->
   ($.get "/gatekeeper/thisUser", (data) =>
@@ -31,7 +40,7 @@
     dsg = g.xp
     console.log("the xp is " + g.xp)
     initViz()
-    g.ve.addie.init()
+    launchAddieInstance()
     true
   ).fail () ->
     console.log("fail to get current user, going back to login screen")


replace `() ->` with just `->`

replace `() ->` with just `->`

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -40,7 +40,7 @@
         $input.val ''
         tmp

-      processSaved: () ->
+      processSaved: ->
         try
           compiled = CoffeeScript.compile @saved
           compiled = compiled[14...-17]
@@ -60,7 +60,7 @@
         @saved = ''
         @print output

-      setPrompt: () ->
+      setPrompt: ->
         s = if @multiline then '------' else 'coffee'
         @prompt.html "#{s}> "

@@ -128,16 +128,16 @@
       $input.height $inputcopy.height()

-    scrollToBottom = () ->
+    scrollToBottom = ->
       window.scrollTo 0, $prompt[0].offsetTop

-    clear = () ->
+    clear = ->
       $output[0].innerHTML = ''
       undefined

-    init = () ->
+    init = ->

       # instantiate our REPL
       repl = new CoffeeREPL $output, $input, $prompt


removing erroneous tabs from nodes.coffee.

--- nodes.coffee
+++ nodes.coffee
@@ -542,11 +542,11 @@
     if @isNew
       idt = @tab + TAB
       return """
-		(function(func, args, ctor) {
-		#{idt}ctor.prototype = func.prototype;
-		#{idt}var child = new ctor, result = func.apply(child, args);
-		#{idt}return typeof result === "object" ? result : child;
-		#{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
+        (function(func, args, ctor) {
+        #{idt}ctor.prototype = func.prototype;
+        #{idt}var child = new ctor, result = func.apply(child, args);
+        #{idt}return typeof result === "object" ? result : child;
+        #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
       """
     base = new Value @variable
     if (name = base.properties.pop()) and base.isComplex()


Add validation to forms rendered through PJAX.

Signed-off-by: Philip Vieira <3ba3974e96044dcde818e6310ed799abe10671d4@vallin.se>

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -41,6 +41,8 @@
   $('.topbar').dropdown()

   $(document).bind 'application.load', =>
+    $("form[data-validate='true']").validate()
+
     $(".alert-message > .close").bind "click", (e) ->
       hide_alert_message $(@).parent()


Fiddling with For#compileNode.

Fiddling with For#compileNode.

--- nodes.coffee
+++ nodes.coffee
@@ -1451,6 +1451,7 @@
     scope.find(index, immediate: yes) if index
     rvar          = scope.freeVariable 'results' if @returns and not hasPureLast
     ivar          = (if @range then name else index) or scope.freeVariable 'i'
+    name          = ivar if @pattern
     varPart       = ''
     guardPart     = ''
     defPart       = ''
@@ -1464,23 +1465,23 @@
       if (name or @own) and not IDENTIFIER.test svar
         defPart = "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
         svar = ref
-      namePart = if @pattern
-        new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LEVEL_TOP
-      else if name
-        "#{name} = #{svar}[#{ivar}]"
+      if name and not @pattern
+        namePart = "#{name} = #{svar}[#{ivar}]"
       unless @object
         lvar        = scope.freeVariable 'len'
         stepPart    = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
         forPart     = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
     if @scoped
       body          = Closure.wrap body, true, not @returns
-    defPart         += @pluckDirectCall o, body, name, index unless @pattern
+    defPart         += @pluckDirectCall o, body, name, index
     if @returns and not hasPureLast
       resultPart    = "#{@tab}#{rvar} = [];\n"
       returnResult  = '\n' + (new Return(new Literal(rvar)).compile o, LEVEL_PAREN)
       body          = Push.wrap rvar, body
     if @guard
       body          = Expressions.wrap [new If @guard, body]
+    if @pattern
+      body.expressions.unshift new Assign @name, new Literal "#{svar}[#{ivar}]"
     varPart         = "\n#{idt1}#{namePart};" if namePart
     if @object
       forPart       = "#{ivar} in #{svar}"


fixing bound functions with an __bind helper.

--- nodes.coffee
+++ nodes.coffee
@@ -184,12 +184,6 @@
     return this if not last or last instanceof ReturnNode
     @expressions[idx]: last.makeReturn()
     this
-
-  # A bound function uses a local `__this` variable instead of the real `this`.
-  rewriteThis: ->
-    @traverseChildren false, (child) ->
-      if child instanceof ValueNode and child.base.value is 'this'
-        child.base: literal '__this'

   # An **Expressions** is the only node that can serve as the root.
   compile: (o) ->
@@ -866,7 +860,7 @@
     top:          del o, 'top'
     o.scope:      sharedScope or new Scope(o.scope, @body, this)
     o.top:        true
-    o.indent:     @idt(if @bound then 2 else 1)
+    o.indent:     @idt(1)
     del o, 'noWrap'
     del o, 'globals'
     i: 0
@@ -886,13 +880,11 @@
       i: + 1
     params: (param.compile(o) for param in params)
     @body.makeReturn()
-    @body.rewriteThis() if @bound
     (o.scope.parameter(param)) for param in params
     code: if @body.expressions.length then "\n${ @body.compileWithDeclarations(o) }\n" else ''
-    func: "function(${ params.join(', ') }) {$code${ code and @idt(if @bound then 1 else 0) }}"
-    func: "($func)" if top and not @bound
-    return func unless @bound
-    "(function(__this) {\n${@idt(1)}return $func;\n$@tab})(this)"
+    func: "function(${ params.join(', ') }) {$code${ code and @tab }}"
+    return "${utility('bind')}($func, this)" if @bound
+    if top then "($func)" else func

   topSensitive: ->
     true
@@ -1496,6 +1488,13 @@
               }
             """

+  # Create a function bound to the current value of "this".
+  bind: """
+        function(func, context) {
+            return function(){ return func.apply(context, arguments); };
+          }
+        """
+
   # Shortcuts to speed up the lookup time for native functions.
   hasProp: 'Object.prototype.hasOwnProperty'
   slice:   'Array.prototype.slice'


stripped a line from __extends

stripped a line from __extends

--- nodes.coffee
+++ nodes.coffee
@@ -1647,10 +1647,9 @@
   # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206).
   extends:  '''
     function(child, parent) {
-      var ctor = function() {};
+      function ctor() { this.constructor = child; }
       ctor.prototype = parent.prototype;
-      child.prototype = new ctor();
-      child.prototype.constructor = child;
+      child.prototype = new ctor;
       if (typeof parent.extended === "function") parent.extended(child);
       child.__super__ = parent.prototype;
     }


Closes #3008 -- Fix scope of constructor reference

* Make scope of `Code` nodes accessible (prior to `compileNode`)
* Use correct scope for reference of external constructor.
* Remove unreachable code.

--- nodes.coffee
+++ nodes.coffee
@@ -1031,7 +1031,7 @@
           if func instanceof Code
             assign = @ctor = func
           else
-            @externalCtor = o.scope.freeVariable 'class'
+            @externalCtor = o.classScope.freeVariable 'class'
             assign = new Assign new Literal(@externalCtor), func
         else
           if assign.variable.this
@@ -1098,6 +1098,7 @@
     lname = new Literal name
     func  = new Code [], Block.wrap [@body]
     args  = []
+    o.classScope = func.makeScope o.scope

     @hoistDirectivePrologue()
     @setContext name
@@ -1105,11 +1106,10 @@
     @ensureConstructor name
     @addBoundFunctions o
     @body.spaced = yes
-    @body.expressions.unshift @ctor unless @ctor instanceof Code
     @body.expressions.push lname

     if @parent
-      superClass = new Literal o.scope.freeVariable 'super', no
+      superClass = new Literal o.classScope.freeVariable 'super', no
       @body.expressions.unshift new Extends lname, superClass
       func.params.push new Param superClass
       args.push @parent
@@ -1305,6 +1305,8 @@

   jumps: NO

+  makeScope: (parentScope) -> new Scope parentScope, @body, this
+
   # Compilation creates a new scope unless explicitly asked to share with the
   # outer scope. Handles splat parameters in the parameter list by peeking at
   # the JavaScript `arguments` object. If the function is bound with the `=>`
@@ -1320,7 +1322,7 @@
       boundfunc.updateLocationDataIfMissing @locationData
       return boundfunc.compileNode(o)

-    o.scope         = new Scope o.scope, @body, this
+    o.scope         = del(o, 'classScope') or @makeScope o.scope
     o.scope.shared  = del(o, 'sharedScope')
     o.indent        += TAB
     delete o.bare


Fix setting Cyclestreets caption

Fix setting Cyclestreets caption

--- app/assets/javascripts/map_display.js.coffee
+++ app/assets/javascripts/map_display.js.coffee
@@ -157,7 +157,7 @@

             form.find("#image-preview").html(peekImgEl)

-            form.find("input[id$='caption']").changeVal(feature.properties.caption)
+            form.find("textarea[id$='caption']").changeVal(feature.properties.caption)
             form.find("input[id$='cyclestreets_id']").changeVal(feature.properties.id)
             form.find("input[id$='photo_url']").changeVal(thumbnailUrl)
             form.find("input[id$='icon_properties']").changeVal(JSON.stringify(iconProperties))


fixed more info textarea

fixed more info textarea

--- app/assets/javascripts/movie_ideas.js.coffee
+++ app/assets/javascripts/movie_ideas.js.coffee
@@ -21,7 +21,7 @@

 $ ->
   maxLength = 200
-  $("textarea").keyup ->
+  $("#movie_idea_synopsis").keyup ->
     length = $(this).val().length
     length = maxLength - length
     $("#chars").text length


maths is hard

maths is hard

--- app/assets/javascripts/modules/country/base.js.coffee
+++ app/assets/javascripts/modules/country/base.js.coffee
@@ -2,11 +2,11 @@
   $landStatsCube = $('#land-stats .cube__inner')
   $marineStatsCube = $('#marine-stats .cube__inner')

-  percentageLandCoverage = $landStatsCube.data('fill-value')
+  percentageLandCoverage = Math.sqrt($landStatsCube.data('fill-value')*100)
   $landStatsCube.css('height', "#{percentageLandCoverage}%")
   $landStatsCube.css('width', "#{percentageLandCoverage}%")

-  percentageMarineCoverage = $marineStatsCube.data('fill-value')
+  percentageMarineCoverage = Math.sqrt($marineStatsCube.data('fill-value')*100)
   $marineStatsCube.css('height', "#{percentageMarineCoverage}%")
   $marineStatsCube.css('width', "#{percentageMarineCoverage}%")


Update es.js.coffee

Update es.js.coffee

--- app/assets/javascripts/uploadcare/locale/es.js.coffee
+++ app/assets/javascripts/uploadcare/locale/es.js.coffee
@@ -1,4 +1,3 @@
-# Note: Spanish translation. English locale is the default and used as a fallback.
 uploadcare.namespace 'uploadcare.locale.translations', (ns) ->
   ns.es =
     uploading: 'Subiendo... Por favor espere.'


Add view to context in initContext

Add view to context in initContext

--- src/View.coffee
+++ src/View.coffee
@@ -37,7 +37,7 @@
     if _.isString(@template) then @template else @template(@getContext())

   initContext: (pickPredicate) ->
-    context = {}
+    context = { view: @ }
     context.pathname = document.location.pathname  # ex. '/play/level'
     context = _.defaults context, View.globalContext
     context = _.extend context, _.pick(@, pickPredicate, @) if pickPredicate


toStartWith checks for equality deeply

toStartWith checks for equality deeply

--- test/CustomMatchers.coffee
+++ test/CustomMatchers.coffee
@@ -7,7 +7,7 @@
 			message: "Expected at least #{expected.length} elements"
 		else
 			matching = expected.every (element, index) ->
-				actual[index] == element
+				util.equals actual[index], element

 			if matching
 				pass: true


autofocus input but allow user selection

--- src/ui/views/applayout.coffee
+++ src/ui/views/applayout.coffee
@@ -87,7 +87,11 @@
         div class:'span12', ->
             statusView model
             conversationsListView model.conversations
-      div class:'main span9', region('main'), ->
+      focusTextAreaOnClick = onclick: (e) ->
+        if window.getSelection().toString().length > 1
+          return # let the user select
+        document.body.querySelector('.message-xinput textarea').focus()
+      div class:'main span9', focusTextAreaOnClick, region('main'), ->
         div class:'messages', ->
           div class:'message-list-scroll', ->
             div class:'message-list', ->


Fix comment vote button style

Fix comment vote button style

--- resources/assets/coffee/react/_components/comment.coffee
+++ resources/assets/coffee/react/_components/comment.coffee
@@ -401,13 +401,12 @@
   renderVoteButton: (inline = false) =>
     hasVoted = @hasVoted()

-    className = classWithModifiers 'comment-vote', @props.modifiers
-    className += classWithModifiers 'comment-vote',
+    className = classWithModifiers 'comment-vote',
+      @props.modifiers
       disabled: !@props.comment.canVote
       inline: inline
       on: hasVoted
       posting: @state.postingVote
-      true

     hover = div className: 'comment-vote__hover', '+1' if !inline && !hasVoted


Fix check to display comment owner badge

--- resources/assets/coffee/react/_components/comment.coffee
+++ resources/assets/coffee/react/_components/comment.coffee
@@ -254,7 +254,7 @@

   renderOwnerBadge: (meta) =>
-    return null unless @props.comment.userId == meta.owner_id
+    return null unless meta.owner_id? && @props.comment.userId == meta.owner_id

     div className: 'comment__row-item',
       div className: 'comment__owner-badge', meta.owner_title


Medal

Medal

--- resources/assets/coffee/react/profile-page/medals.coffee
+++ resources/assets/coffee/react/profile-page/medals.coffee
@@ -38,6 +38,15 @@
     div
       className: 'page-extra'
       el ProfilePage.ExtraHeader, name: @props.name, withEdit: @props.withEdit
+
+      div null,
+        for achievement in @props.userAchievements[0..7]
+          el ProfilePage.AchievementBadge,
+            key: achievement.achievement_id
+            additionalClasses: 'badge-achievement--listing'
+            achievement: @props.achievements[achievement.achievement_id]
+            userAchievement: achievement
+
       div className: 'medals-group',
         all
       if all.length == 0
@@ -59,9 +68,9 @@
       .value()

-  medal: (achievement, i) =>
+  medal: (achievement) =>
     div
-      key: i
+      key: achievement.id
       className: 'medals-group__medal'
       el ProfilePage.AchievementBadge,
         additionalClasses: 'badge-achievement--listing'


Restore timeout in slider specs.

Restore timeout in slider specs.

--- spec/javascripts/lib/components/slider_spec.coffee
+++ spec/javascripts/lib/components/slider_spec.coffee
@@ -113,7 +113,7 @@
           expect($('.slider__slide').eq(2)).toHaveClass('is-current')
           expect($('.slider__slide').eq(3)).toHaveClass('is-next')
           expect($('.slider__slide').eq(1)).toHaveClass('is-prev')
-        , 1
+        , 1250

     describe 'events:', ->


kitecontroller: return the kite explicitly for readability

--- client/landing/app/MainApp/kite/kitecontroller.coffee
+++ client/landing/app/MainApp/kite/kitecontroller.coffee
@@ -31,6 +31,7 @@
   createKite:(kiteName, correlationName, kiteKey)->
     kite = new Kite { kiteName, correlationName, kiteKey }
     kite.on 'destroy', => @destroyKite kite
+    return kite

   # notification = null
   notify = (options = {})->


Sentence-by-sentence mode can now move between sentences

--- annotran/static/scripts/annotator/plugin/sentenceselection.coffee
+++ annotran/static/scripts/annotator/plugin/sentenceselection.coffee
@@ -9,21 +9,114 @@
   pluginInit: ->
     # Register the event handlers required for creating a selection
     $(document).bind({
-      "click": @makeSentenceSelection
+      "click": @makeSentenceSelection,
+      "keypress": @keyPressHandler
     })

     this.operational = false
+    this.currentIndex = 0

     null

   destroy: ->
     $(document).unbind({
-      "click": @makeSentenceSelection
+      "click": @makeSentenceSelection,
+      "keypress": @keyPressHandler
     })
     super

   toggleOperation: () ->
     this.operational = not this.operational
+
+  prepareData: (target) ->
+    desiredText = $(target).text()
+    desiredText = desiredText.split('.')
+
+    offset_to_use = 0
+    counter = 0
+
+    if desiredText.length == 1
+      console.log("Current selection does not contain a full stop. Cannot split by sentence.")
+      desiredText = []
+      desiredText.push $(target).text()
+
+    for sentence in desiredText
+      if counter == this.currentIndex
+        break
+      offset_to_use = offset_to_use + sentence.length
+      counter = counter + 1
+
+    if desiredText.length == 1
+      desiredText = $(target).text()
+    else
+      desiredText = desiredText[this.currentIndex]
+
+    full_xpath = Util.xpathFromNode($(target), document)
+
+    data = {
+      start: full_xpath
+      startOffset: offset_to_use + this.currentIndex
+      end: full_xpath
+      endOffset: offset_to_use + desiredText.length + this.currentIndex + 1
+    }
+
+    console.log(data)
+
+    return data
+
+  secondaryJump: (target) ->
+    console.log("Trying secondary jump")
+    this.currentIndex = 0
+    nextSibling = $(target).next()
+
+    if nextSibling != undefined and nextSibling.length != 0
+      console.log("Using next sibling for jump")
+      this.selectSentence nextSibling
+    else
+      nextSibling = $($(target).parent()).next()
+      console.log("Using parent next sibling for jump")
+      this.selectSentence nextSibling
+
+  selectSentence: (target) ->
+     # Get the currently selected ranges.
+    tagName = $(target).prop("tagName").toLowerCase()
+
+    # this loop checks that we are not within a formatting cell and that we should recurse upwards through parent elements
+    while tagName is "i" or tagName is "strong" or tagName is "em" or tagName is "b" or tagName is "mark" or tagName is "small" or tagName is "del" or tagName is "ins" or tagName is "sub" or tagName is "sup"
+      target = $(target).parent()
+      tagName = $(target).prop("tagName").toLowerCase()
+
+    data = this.prepareData target
+
+    try
+      anchor = new xpathRange.SerializedRange(data).normalize(document)
+
+      window.getSelection().removeAllRanges()
+      window.getSelection().addRange(anchor.toRange())
+    catch error
+      console.log("Jumping siblings")
+      # if we get here the most likely thing is that the next sentence is beyond the range, so we can just pass a call
+      # to the first sentence selection function with the target set to the next element
+      this.currentIndex = 0
+      nextSibling = $(window.getSelection().focusNode.parentElement).next()
+
+      # now try again
+      data = this.prepareData nextSibling
+
+      if data.start.length == 0
+        this.secondaryJump(target)
+        return
+
+      try
+        anchor = new xpathRange.SerializedRange(data).normalize(document)
+
+        window.getSelection().removeAllRanges()
+        window.getSelection().addRange(anchor.toRange())
+      catch error
+        this.secondaryJump(target)
+
+    return null
+

   # This is called when the mouse is clicked on a DOM element.
   # Checks to see if there is a sentence that we can select, if so
@@ -38,29 +131,22 @@
       # we are not in sentence selection mode
       return

-    # Get the currently selected ranges.
+    this.currentIndex = 0
+    this.selectSentence event.target

-    tagName = $(event.target).prop("tagName").toLowerCase()
+  moveToNextSentence: () ->
+    this.currentIndex = this.currentIndex + 1
+    currentSelection = window.getSelection()

-    # this loop checks that we are not within a formatting cell and that we should recurse upwards through parent elements
-    while tagName is "i" or tagName is "strong" or tagName is "em" or tagName is "b" or tagName is "mark" or tagName is "small" or tagName is "del" or tagName is "ins" or tagName is "sub" or tagName is "sup"
-      event.target = $(event.target).parent()
-      tagName = $(event.target).prop("tagName").toLowerCase()
+    this.selectSentence(currentSelection.baseNode.parentElement)

-    desiredText = $(event.target).text()
-    desiredText = desiredText.split('.')[0]
+    return null

-    full_xpath = Util.xpathFromNode($(event.target), document)
+  keyPressHandler: (event = {}) =>
+    if this.operational == false
+      # we are not in sentence selection mode
+      return

-    data = {
-      start: full_xpath
-      startOffset: 0
-      end: full_xpath
-      endOffset: desiredText.length
-    }
-
-    anchor = new xpathRange.SerializedRange(data).normalize(document)
-
-    window.getSelection().removeAllRanges()
-    window.getSelection().addRange(anchor.toRange())
-
+    if event.which == 13
+      # handle the enter key
+      this.moveToNextSentence()


select2 create choise js refactor

select2 create choise js refactor

--- app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
+++ app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
@@ -4,6 +4,12 @@
     @el.prop('required', null)
     @modal = $('#modal_form')
     @el.select2 @buildOptions()
+    @initHandlers()
+
+  initHandlers: ->
+    @el.select2('container').on 'click', '.select2-choices a', (e) ->
+      log 's click'
+      e.stopPropagation()

   buildOptions: ->
     @options = _.defaults(@el.data('select2_opts') || {},  @defaults())
@@ -23,9 +29,10 @@

   initCreateChoise: ->
     @options.createSearchChoice = (term, data) =>
-      @el.siblings('.select2-create-choise').remove()
+      @clearCreateChoise()
+      @cont = @el.select2('container')
       @btn_add = $("
#{I18n.t('admin_js.add')} - #{term}
")
-      @btn_add.insertAfter @el
+      @btn_add.prependTo @cont
       @btn_add.click =>
         @modal.data('target', this)
         fn = =>
@@ -38,18 +45,22 @@
     return if Select2Bridge.initedCreateChoiseOnce
     @modal.on 'ajax:success', 'form', =>
       @modal.modal('hide')
-      $el = @modal.data('target').el
-      select2 = $el.data('select2')
+      that = @modal.data('target')
       new_item = window.ab_admin_last_created
-      if select2.opts.multiple
-        data = $el.select2('data')
-        data.push new_item
-        $el.select2('data', data)
-      else
-        $el.select2('data', new_item)
-      $el.siblings('.select2-create-choise').remove()
-      $el.change()
+      that.addItem(new_item)
+      that.clearCreateChoise()
     Select2Bridge.initedCreateChoiseOnce = true
+
+  addItem: (item) ->
+    if @el.data('select2').opts.multiple
+      data = @el.select2('data')
+      data.push item
+      @el.select2('data', data)
+    else
+      @el.select2('data', item)
+
+  clearCreateChoise: ->
+    @el.select2('container').children('.select2-create-choise').remove()

   defaults: ->
     opts =
@@ -88,7 +99,7 @@
       html = '
'
       html += "" if item.image
       if item.url
-        html += "#{item.text}"
+        html += "#{item.text}"
       else
         html += "#{item.text}"
       html += "
"
@@ -96,7 +107,7 @@
       html = '
'
       html += "" if item.image
       if item.url
-        html += "#{item.text}"
+        html += "#{item.text}"
       else
         html += "#{item.text}"


use Backbone.Collection sorting instead of bacon.paginator sorting, fixes #522

--- app/assets/javascripts/admin/views/dataset/dataset_list_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_list_view.coffee
@@ -46,8 +46,8 @@

   initialize : ->

-    @collection.setSort("created", {side: "client"})
-    #@collection.models.sort()
+    @collection.comparator = (a, b) -> a.get("created") < b.get("created")
+    @collection.sort()

     @collection.fetch(
       silent : true


save button saves name change and triggers toast

--- app/components/profile-settings-page/profile-settings-page.coffee
+++ app/components/profile-settings-page/profile-settings-page.coffee
@@ -6,7 +6,7 @@
       global.cobudgetApp.membershipsLoaded
   url: '/profile_settings?previous_group_id'
   template: require('./profile-settings-page.html')
-  controller: (CurrentUser, $location, $scope, $stateParams) ->
+  controller: (CurrentUser, $location, Records, $scope, $stateParams, Toast) ->

     $scope.currentUser = CurrentUser()
     previousGroupId = $stateParams.previous_group_id || CurrentUser().primaryGroup().id
@@ -18,7 +18,9 @@
       $location.path("/groups/#{previousGroupId}")

     $scope.save = ->
-      console.log('save called!')
+      params = _.pick $scope.currentUser, ['name']
+      Records.users.updateProfile(params).then ->
+        Toast.show('Profile settings updated!')

     $scope.openChangePasswordDialog = ->
       console.log('openChangePasswordDialog called!')


let the editor view get disposed of normally

--- app/assets/javascripts/states/inventory/views/host_editor.js.coffee
+++ app/assets/javascripts/states/inventory/views/host_editor.js.coffee
@@ -16,16 +16,12 @@

       # delete
       "click button.delete": (e) ->
-        @deleting = true # set a flag so we don't dispose yet
         @hide_editor()
         c = @create_partial Bixby.view.Confirm,
           message: "Are you sure you want to delete this host?",
-          show_cb: null,
           hidden_cb: (confirmed) =>
-            @deleting = false
             if confirmed
               @host.destroy()
-              @hide_editor()
               @transition "inventory"
             else
               @$el.modal("show")
@@ -72,10 +68,7 @@

       @$el.modal({ show: false })
       @$el.on "shown.bs.modal", => @$("input.alias").putCursorAtEnd()
-      @$el.on "hidden.bs.modal", =>
-        @dispose() if !@deleting

     dispose: ->
-      @$el.off "hidden"
       @$el.off "shown"
       super()


use early is_image info in preview tab

use early is_image info in preview tab

--- app/assets/javascripts/uploadcare/widget/tabs/preview-tab.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/preview-tab.js.coffee
@@ -22,17 +22,27 @@

     __setFile: (@file) =>
       @__setState 'unknown'
+
+      stateKnown = utils.once (info) =>
+        if info.isImage
+          @__setState 'image'
+        else
+          @__setState 'regular'
+
       file = @file
-      @file
-        .done (info) =>
-          if file == @file
-            if info.isImage
-              @__setState 'image'
-            else
-              @__setState 'regular'
-        .fail (error) =>
-          if file == @file
-            @__setState 'error', {error}
+      ifCur = (fn) =>
+        => fn.apply(null, arguments) if file == @file
+
+      @file.done ifCur (info) =>
+        stateKnown info
+
+      @file.fail ifCur (error) =>
+        @__setState 'error', {error}
+
+      @file.progress ifCur (progressInfo) ->
+        info = progressInfo.incompleteFileInfo
+        if info.isImage? and info.previewUrl?
+          stateKnown info

     # error
     # unknown


fix redirection after creating new post

fix redirection after creating new post

Fix #38

--- app/assets/javascripts/vocal/controllers/post_new_controller.coffee
+++ app/assets/javascripts/vocal/controllers/post_new_controller.coffee
@@ -3,5 +3,5 @@
     # TODO: this should be extracted in a componnent
     save: ->
       promise = Vocal.CustomPostAdapter.create(Vocal.postProperties())
-      promise.done (post) =>
-        @transitionToRoute('post.edit', post)+      promise.done (data) =>
+        @transitionToRoute('post.edit', data.post)


return failed and succesfully flushed projects when flushing everything

--- services/document-updater/app/coffee/ProjectFlusher.coffee
+++ services/document-updater/app/coffee/ProjectFlusher.coffee
@@ -56,8 +56,15 @@
 			jobs = _.map project_ids, (project_id)->
 				return (cb)->
 					ProjectManager.flushAndDeleteProjectWithLocks project_id, cb
-			async.parallelLimit jobs, options.concurrency, (error)->
-				return callback(error, project_ids)
+			async.parallelLimit async.reflectAll(jobs), options.concurrency, (error, results)->
+				success = []
+				failure = []
+				_.each results, (result, i)->
+					if result.error?
+						failure.push(project_ids[i])
+					else
+						success.push(project_ids[i])
+				return callback(error, {success:success, failure:failure})

 module.exports = ProjectFlusher


Fix bug #3389: invitation/<token> is broken

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -380,10 +380,7 @@
             templateUrl: "auth/invitation.html",
             title: "INVITATION.PAGE_TITLE",
             description: "INVITATION.PAGE_DESCRIPTION",
-            disableHeader: true,
-            access: {
-                requiresLogin: true
-            }
+            disableHeader: true
         }
     )
     $routeProvider.when("/external-apps",


Fix retraction stanza sent to remote services

The item ID was not properly included.

Fixes test "Retracting a remote item must be submitted to the remote service".

--- src/xmpp/pubsub_client.coffee
+++ src/xmpp/pubsub_client.coffee
@@ -164,7 +164,7 @@
     pubsubChild: ->
         publishEl = new xmpp.Element('retract', node: @opts.node)
         for item in @opts.items
-            publishEl.c('item', { id: item.id })
+            publishEl.c('item', id: item)
         publishEl

 class Subscribe extends PubsubRequest


Fixed react comment warnings about key

Fixed react comment warnings about key

--- core/app/backbone/views/evidence/react_comment.coffee
+++ core/app/backbone/views/evidence/react_comment.coffee
@@ -46,7 +46,6 @@
     @setState show_subcomments: !@state.show_subcomments

   _content: ->
-
     _div ["comment-content spec-comment-content",
       dangerouslySetInnerHTML: {__html: @model().get('formatted_content')}]

@@ -54,7 +53,7 @@
     sub_comment_count = @model().get('sub_comments_count')

     [
-      _span ["comment-post-bottom"], [
+      _span ["comment-post-bottom"],
         if @model().can_destroy()
           _span ["comment-post-delete"],
             ReactDeleteButton
@@ -63,7 +62,6 @@
         _span ["comment-reply"],
           _a ["spec-sub-comments-link", href:"javascript:", onClick: @_toggleSubcomments],
             "(#{sub_comment_count}) Comment"
-      ]
       if @state.show_subcomments
         ReactSubComments(model: @model())
     ]
@@ -79,4 +77,4 @@
           votes: @props.votes
           model: @model()
         @_content()
-        @_bottom()
+        @_bottom()...


remove trailing whitespace

remove trailing whitespace

--- src/atom.coffee
+++ src/atom.coffee
@@ -526,7 +526,7 @@
   #   https://github.com/atom/atom-shell/issues/473
   #   https://github.com/atom/atom/issues/7555
   #   https://github.com/atom/atom/issues/8909
-  # note: OS X has continous coordinates space shared by all displays
+  # note: OS X has continous coordinates space shared by all displays
   # the main display screen is naturally [0,0,widht,height], additional displays are placed around it
   # it is perfectly fine for a secondary display to occupy negative coordinates!
   isValidDimensions: ({x, y, width, height}={}) ->


finder getData of undefined error fix

finder getData of undefined error fix

--- client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -46,7 +46,7 @@
     delete @_storage
     {initialPath} = @treeController.getOptions() # not used, fix this

-    mount = if KD.isLoggedIn()
+    @mount = if KD.isLoggedIn()
       {nickname}    = KD.whoami().profile
       FSHelper.createFile
         name        : nickname
@@ -59,7 +59,7 @@
         path        : "/Users/guest"
         type        : "mount"
     @defaultStructureLoaded = no
-    @treeController.initTree [mount]
+    @treeController.initTree [@mount]

     if @treeController.getOptions().useStorage
       @loadDefaultStructureTimer = @utils.wait @treeController.getOptions().initDelay, =>
@@ -86,9 +86,10 @@
         ]

       timer = Date.now()
-      mount = @treeController.nodes["/Users/#{nickname}"].getData()
+      # @utils.wait =>
+      # mount = @treeController.nodes["/Users/#{nickname}"].getData()

-      mount.emit "fs.fetchContents.started"
+      @mount.emit "fs.fetchContents.started"
       kiteController.run
         withArgs  :
           command : "ls #{recentFolders.join(" ")} -lpva --group-directories-first --time-style=full-iso"
@@ -99,7 +100,7 @@
         log "#{(Date.now()-timer)/1000}sec !"
         # temp fix this doesn't fire in kitecontroller
         kiteController.emit "UserEnvironmentIsCreated"
-        mount.emit "fs.fetchContents.finished"
+        @mount.emit "fs.fetchContents.finished"

   fetchStorage:(callback)->


kite: get homeDir from info instead of relying on hardcoded path

--- client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -146,13 +146,17 @@
     pipedVm = @_pipedVmName vmName
     path or= vmRoots[pipedVm] or "/home/#{KD.nick()}"

-    if vmName is "local-#{KD.nick()}"
-      # path = "/Users/#{KD.nick()}"
-      path = "/Users/fatih"
-
     if vmItem = @getVmNode vmName
       return warn "VM #{vmName} is already mounted!"

+    if vmName is "local-#{KD.nick()}"
+      KD.getSingleton('vmController').info vm, (err, vm, info)=>
+        path = if info.homeDir then info.homeDir else "/Users/#{KD.nick()}"
+        return @_mountVMHelper vmName, path
+    else
+      @_mountVMHelper vmName, path, fetchContent
+
+  _mountVMHelper: (vmName, path, fetchContent)->
     @updateMountState vmName, yes

     @vms.push FSHelper.createFile
@@ -176,6 +180,7 @@
           @utils.defer =>
             if @getOptions().useStorage then @reloadPreviousState vmName
         , yes
+

   unmountVm:(vmName)->
     vmItem = @getVmNode vmName


Refresh AOS

Refresh AOS

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -89,6 +89,7 @@
         timeFilter = $(this).data("time-filter")
         showEngagementChart(metricSlug, segmentType, timeFilter)
   )
+  AOS.refresh()
   e.stopPropagation()
   return false
 )


Redraw the charts immediately

Redraw the charts immediately

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -443,14 +443,14 @@
       metricSlug = graphDiv.data("metric-slug")
       segmentType = graphDiv.data("segment-type")
       timeFilter = graphDiv.data("time-filter")
-      setTimeout (-> showEngagementChart(metricSlug, segmentType, timeFilter)), 50
+      showEngagementChart(metricSlug, segmentType, timeFilter)

     if page == "opinions"
       graphDiv = $("#" + evt.detail.id).find(".poll-chart")
       questionID = graphDiv.data("question")
       segment = graphDiv.data("segment")
       $("#chart-" + questionID).find('.chart-progress').show()
-      setTimeout (-> showChart(questionID, segment)), 50
+      showChart(questionID, segment)

   document.addEventListener 'aos:in', redrawChart
 )


gardening

gardening

--- ui/submit.coffee
+++ ui/submit.coffee
@@ -44,13 +44,13 @@

   render: ->
     item = this.state.model
-    `

+    `

-        <input onKeyDown={this.onKeyDown}
+        <input
           type="text" class="uri" ref="uri" value={item.uri} placeholder="URL" />
-        <input onKeyDown={this.onKeyDown}
+        <input
           type="text" class="title" ref="title" value={item.title} placeholder="Title" />
-        <Textarea onKeyDown={this.onKeyDown}
+        <Textarea
           autosize class="post" ref="post" placeholder="Description">
           {item.post}
         </Textarea>


Put the paste handler timer back to 5 because otherwise the scroll position system fails on paste.

--- src/base.coffee
+++ src/base.coffee
@@ -292,7 +292,7 @@
             target.set('/', newData)
           else
             console.log "not pasting", newData, "because it's not valid:", result
-        ), 0
+        ), 5
       else
         # We don't want the paste data to our clipboard textarea, so let's not even let it happen so we don't scroll
         e.preventDefault()


ensure that LongPoll#_error() doesn't start the next request when @_stopRequestLoop is set

--- lib/assets/javascripts/firehose/long_poll.js.coffee
+++ lib/assets/javascripts/firehose/long_poll.js.coffee
@@ -98,12 +98,11 @@
     console?.log "_error", arguments
     @_isConnected = false
     @config.disconnected()
-
-    # Ping the server to make sure this isn't a network connectivity error
-    setTimeout @_ping, @_retryDelay + @_lagTime
-
-    # Reconnect with delay
-    setTimeout @_request, @_retryDelay
+    unless @_stopRequestLoop
+      # Ping the server to make sure this isn't a network connectivity error
+      setTimeout @_ping, @_retryDelay + @_lagTime
+      # Reconnect with delay
+      setTimeout @_request, @_retryDelay

 # NB: This is a stupid hack to deal with CORS short-comings in jQuery in
 # Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338


let Jax.Material.Layer set source code from its options

--- lib/assets/javascripts/jax/material/layer.js.coffee
+++ lib/assets/javascripts/jax/material/layer.js.coffee
@@ -6,10 +6,15 @@
     new EJS({text:source}).render(options)

   constructor: (options, material) ->
+    throw new Error "Second argument must be an instance of Jax.Material" unless material
+    @setVariables = options?.setVariables if options?.setVariables
     if options?.shader and (data = Jax.shader_data options.shader)?.fragment || data?.vertex
       fmap = material.fragment.append shaderSource data, 'fragment' if data.fragment
       vmap = material.vertex.append   shaderSource data, 'vertex'   if data.vertex
     else if (data = this.__proto__.constructor.shaderSource) isnt undefined
+      fmap = material.fragment.append shaderSource data, 'fragment' if data.fragment
+      vmap = material.vertex.append   shaderSource data, 'vertex'   if data.vertex
+    else if (data = options) and data?.fragment || data?.vertex
       fmap = material.fragment.append shaderSource data, 'fragment' if data.fragment
       vmap = material.vertex.append   shaderSource data, 'vertex'   if data.vertex
     else


adding fix to partial page replace as well

--- lib/assets/javascripts/turbograft/turbolinks.coffee
+++ lib/assets/javascripts/turbograft/turbolinks.coffee
@@ -120,7 +120,9 @@

     if onlyKeys.length
       nodesToRefresh = [].concat(getNodesWithRefreshAlways(), getNodesMatchingRefreshKeys(onlyKeys))
-      return refreshNodes(nodesToRefresh, body)
+      nodes = refreshNodes(nodesToRefresh, body)
+      setAutofocusElement() if anyAutofocusElement(nodes)
+      return nodes
     else
       refreshNodes(getNodesWithRefreshAlways(), body)
       persistStaticElements(body)
@@ -154,6 +156,13 @@
       matchingNodes.push(node)

     return matchingNodes
+
+  anyAutofocusElement = (nodes) ->
+    for node in nodes
+      if node.querySelectorAll('input[autofocus], textarea[autofocus]').length > 0
+        return true
+
+    false

   setAutofocusElement = ->
     autofocusElement = (list = document.querySelectorAll 'input[autofocus], textarea[autofocus]')[list.length - 1]


Unify and beautify errors about WebRTC

Unify and beautify errors about WebRTC

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -163,8 +163,10 @@

         @connections = []

-        peer.on "error", (error) ->
-            alert error.type
+        peer.on "error", (error) =>
+            console.log error
+
+            @props.addAlert type: "danger", "Something went terribly wrong! See console."

         peer.on "open", (peerId) =>
             if isFounder
@@ -187,9 +189,6 @@

                 connection = peer.connect peerIdForFounder

-                connection.on "error", (error) ->
-                    alert error
-
                 connection.on "open", =>
                     @connections.push connection
                     @listenForMessage connection
@@ -200,9 +199,6 @@
                                 connection = peer.connect peerId

                                 do (connection) =>
-                                    connection.on "error", (error) ->
-                                        alert error.type
-
                                     connection.on "open", =>
                                         @connections.push connection
                                         @listenForMessage connection


Static Page : fixed more issues related with MR #460

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -123,60 +123,60 @@
     @off "publicFeedFetched_#{eventSuffix}"
     # log "------------------ bindingsCleared", dateFormat(@lastFrom, "mmmm dS HH:mm:ss"), @_e

-  fetchFeaturedActivityComment:(activityId, commentId, callback)=>
+  featuredActivityCommentRequested:(activityId, commentId, callback)=>
     return if commentId > 2
     timeoutValue = KD.utils.getRandomNumber 100000
     KD.utils.wait timeoutValue, =>
       eventName = "activity_#{activityId}_C#{commentId}_fetch"
-      @off "#{eventName}_success"
+      @off "#{eventName}_succeeded"

       # if fetching comments fails, then shut down the event
-      @once "#{eventName}_fail", ()=>
-        @off "activity_#{activityId}_fetch_success"
+      @once "#{eventName}_failed", ()=>
+        @off "activity_#{activityId}_fetch_succeeded"

       # if fetching success, add them to activity feed
       @once eventName, (activities)=>
         @listActivities activities, callback, true
-        @emit "FetchFeaturedActivityComment", activityId, commentId+1, callback
-      @getFeatureds(activityId, commentId)
-
-  startFetchingFeaturedActivity:(activityId=0, callback)=>
+        @emit "FeaturedActivityCommentRequested", activityId, commentId+1, callback
+      @fetchFeatureds(activityId, commentId)
+
+  featuredActivityRequested:(activityId=0, callback)=>
     return if activityId > 7
     @isLoading = true
     timeoutValue = KD.utils.getRandomNumber 10000

     eventName = "activity_#{activityId}_fetch"
-    @off "#{eventName}_success"
+    @off "#{eventName}_succeeded"

     KD.utils.wait timeoutValue, =>
-      @once "#{eventName}_fail", ()=>
-        @off "#{eventName}_success"
-        @emit "StartFetchingFeaturedActivity", activityId+1, callback
-
-      @once "#{eventName}_success", (activities)=>
+      @once "#{eventName}_failed", ()=>
+        @off "#{eventName}_succeeded"
+        @emit "FeaturedActivityRequested", activityId+1, callback
+
+      @once "#{eventName}_succeeded", (activities)=>
         @listActivities activities, (sanitizedCache)=>
           callback sanitizedCache
-          @emit "FetchFeaturedActivityComment", activityId, 1, callback
-
-        @emit "StartFetchingFeaturedActivity", activityId+1, callback
+          @emit "FeaturedActivityCommentRequested", activityId, 1, callback
+
+        @emit "FeaturedActivityRequested", activityId+1, callback
       #fetch featured activity
-      @getFeatureds(activityId)
+      @fetchFeatureds(activityId)

   listFeaturedActivities:(callback)->
-    @on "FetchFeaturedActivityComment", @bound "fetchFeaturedActivityComment"
-    @on "StartFetchingFeaturedActivity", @bound "startFetchingFeaturedActivity"
+    @on "FeaturedActivityCommentRequested", @bound "featuredActivityCommentRequested"
+    @on "FeaturedActivityRequested", @bound "featuredActivityRequested"

     # this is a hack, find a better way to hide inner navigation
     @getView().innerNav.hide()
     eventName = "activity_fetch"
-    @once "#{eventName}_fail", ()->
+    @once "#{eventName}_failed", ()->
       console.log "ERR : static main page activities will not work"

-    @once "#{eventName}_success", (activities)=>
+    @once "#{eventName}_succeeded", (activities)=>
       activities.overview.reverse()  if activities.overview
       @listActivities activities, callback
-      @emit "StartFetchingFeaturedActivity", 1, callback
-    @getFeatureds()
+      @emit "FeaturedActivityRequested", 1, callback
+    @fetchFeatureds()

   populateActivity:(options = {}, callback=noop)->
     return  if @isLoading
@@ -250,7 +250,7 @@
       @listController.listActivitiesFromCache sanitizedCache, 0 , {type : "slideDown", duration : 100}, yes
       callback sanitizedCache

-  getFeatureds:(activityId=0, commentId=0, likeId=0)->
+  fetchFeatureds:(activityId=0, commentId=0, likeId=0)->
     activityName = "activity"
     activityName += "_#{activityId}" unless activityId is 0
     activityName += "_C#{commentId}" unless commentId is 0
@@ -258,8 +258,8 @@

     $.ajax
       url     : "js/activity/#{activityName}.json"
-      success : (json) => @emit "#{activityName}_fetch_success", json
-      failure : =>  @emit "#{activityName}_fetch_fail"
+      success : (json) => @emit "#{activityName}_fetch_succeeded", json
+      failure : =>  @emit "#{activityName}_fetch_failed"

   fetchPublicActivities:(options = {})->
     {CStatusActivity} = KD.remote.api


Fix followers scrolling

Fix followers scrolling

--- assets/javascripts/views/followers.js.coffee
+++ assets/javascripts/views/followers.js.coffee
@@ -40,7 +40,7 @@
     setTimeout @windowScrolled, 100

   windowScrolled: =>
-    $last = ($ 'tr.follower:last', @container)
+    $last = ($ 'tr.follower:last', @container.$el)
     last_offset_top = $last.offset()?.top || 0
     bottom_position = window.scrollY + $(window).height()


hotfix

hotfix

--- client/app/Applications/Shell.kdapplication/TerminalClient.coffee
+++ client/app/Applications/Shell.kdapplication/TerminalClient.coffee
@@ -175,7 +175,6 @@
     @handler command

   esc_seq: (s) ->
-    console.log s
     String.fromCharCode(27) + "[" + s


utilities/tryonkoding: members can not toggle try on koding button

--- client/home/lib/utilities/components/tryonkoding/container.coffee
+++ client/home/lib/utilities/components/tryonkoding/container.coffee
@@ -3,6 +3,7 @@
 React           = require 'kd-react'
 View            = require './view'
 TeamFlux        = require 'app/flux/teams'
+showError       = require 'app/util/showError'
 KDReactorMixin  = require 'app/flux/base/reactormixin'
 copyToClipboard = require 'app/util/copyToClipboard'

@@ -13,6 +14,13 @@
     return {
       team: TeamFlux.getters.team
     }
+
+  constructor: (props) ->
+
+    super props
+
+    canEdit = kd.singletons.groupsController.canEditGroup()
+    @state = {canEdit : canEdit}

   componentDidMount: ->
@@ -46,8 +54,11 @@

   handleSwitch: (state) ->

-    allowedDomains = @state.team.get 'allowedDomains'
-    allowedDomains = allowedDomains.toJS()
+    unless @state.canEdit
+      return showError 'You are not allowed to toggle this button'
+
+    allowedDomains = @state.team?.get 'allowedDomains'
+    allowedDomains = allowedDomains?.toJS()
     allowedDomains = _.clone allowedDomains or []

     if state


Removed unused methods/css class

Removed unused methods/css class

--- core/app/backbone/views/sub_comments/sub_comments_add_view.coffee
+++ core/app/backbone/views/sub_comments/sub_comments_add_view.coffee
@@ -12,13 +12,9 @@
     textareaRegion: '.js-region-textarea'

   onRender: ->
-    @textareaRegion.show @textAreaView()
-    @toggleForm false
-
-  inputFocus: -> @toggleForm true
-
-  toggleForm: (active) ->
-    @$el.toggleClass 'evidence-sub-comments-form-active', active
+    @textareaRegion.show new Backbone.Factlink.TextAreaView
+      model: @textModel()
+      placeholder: 'Your comment'

   submit: ->
     return if @submitting
@@ -43,13 +39,6 @@

   text: -> @textModel().get('text')
   textModel: -> @_textModel ?= new Backbone.Model text: ''
-  textAreaView: ->
-    textAreaView = new Backbone.Factlink.TextAreaView
-      model: @textModel()
-      placeholder: 'Your comment'
-
-    @listenTo textAreaView, 'focus', @inputFocus, @
-    textAreaView

   enableSubmit: ->
     @submitting = false


fix testcase for list for new .in instead of .get for $


Scroll to files browser depending on url path

--- SingularityUI/app/views/fileBrowserSubview.coffee
+++ SingularityUI/app/views/fileBrowserSubview.coffee
@@ -13,6 +13,8 @@
         @listenTo @collection, 'sync',  @render
         @listenTo @collection, 'error', @catchAjaxError

+        @scrollAfterRender = Backbone.history.fragment.indexOf('/files') isnt -1
+
     render: ->
         # Ensure we have enough space to scroll
         offset = @$el.offset().top
@@ -24,6 +26,16 @@
             files:       _.pluck @collection.models, 'attributes'
             path:        @collection.path
             breadcrumbs: breadcrumbs
+
+        # make sure body is large enough so we can fit the browser
+        @$('body').css 'min-height', @$el.offset().top + $(window).height()
+
+        scroll = => $(window).scrollTop @$el.offset().top - 20
+        if @scrollAfterRender
+            @scrollAfterRender = false
+
+            scroll()
+            setTimeout scroll, 100

         @$('.actions-column a[title]').tooltip()


test USSD modal and menu syncing with ruleset

--- karma/flows/test_controllers.coffee
+++ karma/flows/test_controllers.coffee
@@ -26,6 +26,7 @@
       'rules_first': { id: 2, languages:[] },
       'loop_detection': { id: 3, languages:[] },
       'webhook_rule_first': { id: 4, languages:[] },
+      'ussd_example': { id: 5, languages:[] },
     }

     $http.whenGET('/contactfield/json/').respond([])
@@ -72,8 +73,9 @@

     flowController = null
     flowService = null
-
-    beforeEach inject(($controller, _Flow_) ->
+    utils = null
+
+    beforeEach inject(($controller, _Flow_, _utils_) ->

       flowService = _Flow_
       flowController = $controller 'FlowController',
@@ -81,6 +83,7 @@
         $rootScope: $rootScope
         $log: $log
         Flow: flowService
+      utils = _utils_
     )

     it 'should show warning when attempting an infinite loop', ->
@@ -323,4 +326,26 @@
         # make sure 'Default' isn't added as an option
         expect(modalScope.languages.length).toEqual(1)

-      $timeout.flush()+      $timeout.flush()
+
+    it 'should have the USSD Menu synced with ruleset', ->
+
+      # load a USSD flow
+      flowService.fetch(flows.ussd_example.id)
+      flowService.contactFieldSearch = []
+      flowService.language = {iso_code:'base'}
+      $http.flush()
+
+      ruleset = flowService.flow.rule_sets[0]
+      $scope.clickRuleset(ruleset)
+      $scope.dialog.opened.then ->
+        modalScope = $modalStack.getTop().value.modalScope
+
+        # the USSD menu generates rules, these should be synced every time
+        for [item, rule] in utils.zip(modalScope.ruleset.config.ussd_menu, modalScope.ruleset.rules)
+          expect(item.uuid).toBe(rule.uuid)
+          expect(item.category.base).toBe(rule.category.base)
+          expect(item.option).toBe(rule.test._base)
+          expect(rule._config.type).toBe('eq')
+
+      $timeout.flush()


When robot.name is '/', it gets really confused

--- scripts/help.coffee
+++ scripts/help.coffee
@@ -71,7 +71,7 @@

     msg.send emit

-  robot.router.get "/#{robot.name}/help", (req, res) ->
+  robot.router.get "/hubot/help", (req, res) ->
     cmds = robot.helpCommands().map (cmd) ->
       cmd.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')


groups/profile: correct hiding of the landing page

--- client/landing/app/MainApp/lazy/lazydomcontroller.coffee
+++ client/landing/app/MainApp/lazy/lazydomcontroller.coffee
@@ -20,8 +20,14 @@
         landingPageSideBar = new LandingPageSideBar

   hideLandingPage:->
-    $('#group-landing').css 'opacity', 0
-    @utils.wait 600, -> $('#group-landing').hide()
+
+    if $('#group-landing').length
+      $('#group-landing').css 'opacity', 0
+      @utils.wait 600, -> $('#group-landing').hide()
+
+    else if $('#profile-landing').length
+      $('#profile-landing').css 'opacity', 0
+      @utils.wait 600, -> $('#profile-landing').hide()

   userEnteredFromGroup:-> KD.config.groupEntryPoint?


Add decals_dr2 and sdss_lost_set

Add decals_dr2 and sdss_lost_set


send response before import

send response before import

--- server/controllers/konnectors.coffee
+++ server/controllers/konnectors.coffee
@@ -36,10 +36,8 @@
             , 6
             res.send error: true, msg: 'konnector is already importing', 400
         else
+            res.send success:true, 200
             fields = konnectorHash[req.konnector.slug].fields
             req.konnector.import req.body, fields, (err) ->
                 if err
-                    #console.log res
-                    res.send err, 400
-                else
-                    res.send success: true, 200
+                    console.log err


graph: remove query debug logs

graph: remove query debug logs

--- server/lib/server/graph/graph.coffee
+++ server/lib/server/graph/graph.coffee
@@ -49,8 +49,6 @@
       groupId   : @groupId
       startDate : startDate

-    console.log query, startDate, @groupId
-
     @db.query query, params, (err, results)=>
       tempRes = []
       if err then callback err
@@ -116,8 +114,6 @@
       'limit 20'
     ].join('\n');

-    console.log query, startDate
-
     params =
       groupId   : @groupId
       startDate : startDate
@@ -150,8 +146,6 @@
       'limit 20'
     ].join('\n');

-    console.log query, startDate
-
     params =
       groupId   : @groupId
       startDate : startDate
@@ -198,8 +192,6 @@
     @fetchFollows query, startDate, callback

   fetchFollows:(query, startDate, callback)->
-
-    console.log query, startDate

     params =
       groupId   : @groupId


Customized bot's email.

Customized bot's email.

--- scripts/ping.coffee
+++ scripts/ping.coffee
@@ -10,7 +10,7 @@
 request = require 'request'

 botName = "勤怠管理ボット"
-botEmail = "expensive.frog@gmail.com"
+botEmail = process.env.HUBOT_ATTENDANCE_EMAIL

 GAS_URL = process.env.HUBOT_ATTENDANCE_URL
 TOKEN   = process.env.HUBOT_ATTENDANCE_TOKEN


Closes #2078; ObserveChanges on rocketchat_room Not Using Oplog

--- server/publications/spotlight.coffee
+++ server/publications/spotlight.coffee
@@ -13,7 +13,7 @@
 		removed: (id) ->
 			self.removed("autocompleteRecords", id)

-	subHandleRooms = RocketChat.models.Rooms.findByNameContainingAndTypes(selector.name.$regex, ['c'], { limit: 10, fields: { t: 1, name: 1 } }).observeChanges
+	subHandleRooms = RocketChat.models.Rooms.findByNameContainingAndTypes(selector.name.$regex, ['c'], { limit: 10, fields: { t: 1, name: 1 }, sort: {name: 1}}).observeChanges
 		added: (id, fields) ->
 			data = { type: 'r', rid: id, name: fields.name, t: fields.t }
 			self.added("autocompleteRecords", id, data)


collision

collision

--- src/collision.coffee
+++ src/collision.coffee
@@ -73,8 +73,8 @@
   @name circular
   @methodOf Collision

-  @param a {Object}
-  @param b {Object}
+  @param a {Object} The first circle
+  @param b {Object} The second circle

   @returns {Boolean} true is the circles overlap, false otherwise
   ###


Troubleshoot: recoveryCompleted is listened once in troubleshoot recover method

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -161,13 +161,14 @@
   recover: ->
     waitingRecovery = 0
     for own name, item of @items
-      item.on "recoveryCompleted", =>
-        waitingRecovery -= 1
-        @reset "recoveryCompleted"  unless waitingRecovery
+      do (name, item) =>
+        item.once "recoveryCompleted", =>
+          waitingRecovery--
+          @reset "recoveryCompleted"  unless waitingRecovery

-      if item.status is "fail" and item.canBeRecovered()
-        waitingRecovery += 1
-        item.recover()
+        if item.status is "fail" and item.canBeRecovered()
+          waitingRecovery++
+          item.recover()

     waitingRecovery


activity: fix closure's `this` in the scope

--- client/Social/Activity/views/likelink.coffee
+++ client/Social/Activity/views/likelink.coffee
@@ -18,7 +18,7 @@
     {like, unlike} = KD.singletons.socialapi.message

     fn = if isInteracted then unlike else like
-    fn {id}, (err) ->
+    fn {id}, (err) =>

       return @showError err  if err


Log and Store relatedID as Plain Strings

--- app/partials/app.cjsx
+++ app/partials/app.cjsx
@@ -31,7 +31,7 @@
         eventType = defType if typeof eventType isnt 'string'
         @logEvent
           type: eventType
-          relatedID: "\"#{eventData}\""
+          relatedID: "#{eventData}"

   logEvent: (logEntry) -> # Accepts key/values to make appropriate Geordi logging
     newEntry = Object.assign {}, logEntry, @keys


Fixed AppDidQuit stuff.

Fixed AppDidQuit stuff.

--- client/app/MainApp/ApplicationManager.coffee
+++ client/app/MainApp/ApplicationManager.coffee
@@ -177,7 +177,7 @@

   quit:(appInstance, callback = noop)->

-    @unregister appInstance
+    appInstance.getView().destroy()
     callback()

   quitAll:->
@@ -230,7 +230,6 @@
       @appControllers[name].instances.splice index, 1
       if @appControllers[name].instances.length is 0
         delete @appControllers[name]
-      appInstance.destroy()

   createPromptModal:(appOptions, callback)->
     # show modal and wait for response
@@ -280,6 +279,7 @@
     appView?.once "KDObjectWillBeDestroyed", =>
       @unregister appInstance
       appInstance.emit "AppDidQuit"
+      KD.getSingleton('appManager').emit  "AppDidQuit", appInstance

   setLastActiveIndex:(appInstance)->


Removed: dead code

Removed: dead code

--- client/app/views/popover_screens/main.coffee
+++ client/app/views/popover_screens/main.coffee
@@ -37,7 +37,6 @@
         'click .advanced-link': 'onAdvancedClicked'
         'click .remove':        -> @switchToScreen('delete')
         'click .duplicate':     'onDuplicateClicked'
-

         'changeTime.timepicker .input-start':    'onSetStart'
         'changeTime.timepicker .input-end-time': 'onSetEnd'
@@ -146,11 +145,6 @@
             @$moreDetailsButton.hide()

-    onLeaveScreen: ->
-        value = @$('.input-details').val()
-        @model.set 'details', value
-
-
     refresh: ->
         # We must apply a delta when an event is all-day long, because it starts
         # one day, and finishes the day after (if its duration is one day). But


WorkspaceTabView: Make tabView class optional.

--- client/ide/workspace/workspacetabview.coffee
+++ client/ide/workspace/workspacetabview.coffee
@@ -16,7 +16,8 @@
       delegate      : this

   createTabView: ->
-    @tabView = new ApplicationTabView
+    TabViewClas = @getOption('tabViewClass') or ApplicationTabView
+    @tabView    = new TabViewClas
       tabHandleContainer        : @holderView
       closeAppWhenAllTabsClosed : no


Add (back) support for supplying a path in define

This has been enhanced to create branches along the way in they do not
already exist. This reduces excessive calls to define() simply to create
a path in the tree.

--- src/coffee/cilantro/models/context/nodes/branch.coffee
+++ src/coffee/cilantro/models/context/nodes/branch.coffee
@@ -92,14 +92,30 @@
                 return 'Not a valid branch type'

         # Define a node within this branch via the manager
-        define: (attrs, options) ->
+        define: (attrs, path, options) ->
+            if not path or not _.isArray(path)
+                options = path
+                path = []
+
             options = _.extend
                 manager: @manager
                 identKeys: @identKeys
             , options

-            @children.add(attrs, options)
-            return @find(_.pick(attrs, options.identKeys))
+            parent = @
+
+            for ident in path
+                # Create intermediate branches if necessary
+                if not (child = parent.find(ident))
+                    parent.children.add(ident, type: 'branch')
+                    child = parent.children.find(ident)
+
+                if child.type isnt 'branch'
+                    throw new Error('Cannot define node in non-branch')
+                parent = child
+
+            parent.children.add(attrs, options)
+            return parent.find(_.pick(attrs, options.identKeys))

         # Find itself or recurse into the children
         find: (ident, options) ->


make config populate APP_CONFIG on initialize

--- server/initializers/config.coffee
+++ server/initializers/config.coffee
@@ -4,9 +4,12 @@

 APP_CONFIG = null

-exports.get = (key) ->
+ensureAppConfig = ->
   unless APP_CONFIG?
     APP_CONFIG = readConfigFile()
+
+exports.get = (key) ->
+  ensureAppConfig()

   _.clone(APP_CONFIG[key])

@@ -27,7 +30,7 @@
   return JSON.parse(configJSON)

 exports.initialize = ->
-  readConfigFile()
+  ensureAppConfig()

   middleware = (req, res, next) ->
     req.APP_CONFIG = res.locals.APP_CONFIG = APP_CONFIG


Update news.coffee

Update news.coffee

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,6 +3,9 @@

 export default allNews = () ->
     return news([
+        newsItem("Про проблемы на школьной олимпиаде в Нижегородской области, v2", String.raw"""
+
Если ли кто-то так и не сумел нормально написать тур, т.е. не получил непрерывного отрезка времени нужной длительности, в течение которого сайт работал корректно, напишите мне.

+        """),
         newsItem("Про проблемы на школьной олимпиаде в Нижегородской области", String.raw"""

Я написал во вКонтакте про текущую ситуацию на школьной олимпиаде. Все, у кого сегодня были проблемы — прочитайте.

         """),


Update news.coffee

Update news.coffee

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,11 +3,11 @@

 export default allNews = () ->
     return news([
+        newsItem("14 ноября занятие во ВШЭ пройдет с 11.00 до 14.00", String.raw"""
+            14 ноября занятие во ВШЭ пройдет с 11.00 до 14.00.
+        """),
         newsItem("Районная олимпиада", String.raw"""
             Районная олимпиада в Нижегородской области в этом году будет, предварительно, 16 декабря. На сайт добавлена подробная информация, также в архив районных олимпиад добавлены задачи прошлого года.
-        """),
-        newsItem("Отмена занятий до 7 ноября включительно", String.raw"""
-            Очных занятий 27 октября и 3 ноября в лицее 40; а также 31 октября и 7 ноября в ВШЭ не будет. Занимайтесь из дома.
         """),
         newsItem("Командная олимпиада", String.raw"""
             Добавлена информация про командную олимпиаду


user/reset was borked

user/reset was borked

--- server/packages/user/reset.coffee
+++ server/packages/user/reset.coffee
@@ -7,18 +7,17 @@

 	filter = where: resetPasswordToken: req.body.token

-	# It may seem strange that we merely invoke fn() instead of sending
-	# useful data to the client, but the reasoning behind this is to make it
-	# impossible to tell whether an invalid token was used.
 	(User.findOne filter).then((user) ->
-		return Q.resolve() unless user?
+		return unless user?

-		User.hashPassword req.body.password, user.salt
+		User.hashPassword(req.body.password, user.salt).then (passwordHash) ->
+			user.passwordHash = passwordHash
+			user.resetPasswordToken = null
+			user.save()

-	).then((passwordHash) ->
-
-		user.passwordHash = passwordHash
-		user.resetPasswordToken = null
-		user.save()
+	).then(->
+
+		# Make it impossible to tell whether an invalid token was used.
+		return

 	).nodeify fn


Template codo"

Template codo"

--- lib/application/lib/Template.coffee
+++ lib/application/lib/Template.coffee
@@ -32,17 +32,17 @@
 #
 #   Standard template variables/regions:
 #   -------------------------------------------
-#   $doctype      html doctype (default: html5)
-#   $meta         meta tags
-#   $style        css tags
-#   $script       javascript tags
-#   $title        html title tag
-#   $site_name    displayed in banner
-#   $site_slogan  displayed in banner
-#   $menu         main menu
-#   $sidenav      optional sub menu
-#   $content      the floor show
-#   $flash        session flashdata messages
+#   $doctype      html doctype (default: html5)

+#   $meta         meta tags

+#   $style        css tags

+#   $script       javascript tags

+#   $title        html title tag

+#   $site_name    displayed in banner

+#   $site_slogan  displayed in banner

+#   $menu         main menu

+#   $sidenav      optional sub menu

+#   $content      the floor show

+#   $flash        session flashdata messages

 #
 #
 class application.lib.Template extends system.lib.Parser


Point at proxied editor, only add notes if they exist


[PUBDEV-6868] Flow does not work correctly in env which already have some context path prefixed

--- src/core/flow.coffee
+++ src/core/flow.coffee
@@ -15,18 +15,11 @@
 ko = require('./modules/knockout')

 getContextPath = (_) ->
-    if process.env.NODE_ENV == "development"
-      console.debug "Development mode, using localhost:54321"
-      _.ContextPath = "http://localhost:54321/"
-    else
-      _.ContextPath = "/"
-      $.ajax
-          url: window.referrer
-          type: 'GET'
-          success: (data, status, xhr) ->
-              if xhr.getAllResponseHeaders().search(/x-h2o-context-path/i) != -1
-                  _.ContextPath = xhr.getResponseHeader('X-h2o-context-path')
-          async: false
+    url = window.location.toString()
+    parts = url.split('/')
+    # remove flow/index.html from end of the URL
+    contextPathParts = parts.splice(0, parts.length - 2)
+    _.ContextPath = contextPathParts.join('/')

 checkSparklingWater = (context) ->
     context.onSparklingWater = false


Falsche Parameter-Übergabe in retrieveNodes gefixt.


Fix #198

Fix #198

--- lib/ghc-mod/ghc-modi-process.coffee
+++ lib/ghc-mod/ghc-modi-process.coffee
@@ -459,7 +459,7 @@
     # end of dirty hack

     if cmd is 'lint'
-      args = [].concat atom.config.get('haskell-ghc-mod.hlintOptions').map((v) -> ['--hlintOpt', v])...
+      dashArgs = [].concat atom.config.get('haskell-ghc-mod.hlintOptions').map((v) -> ['--hlintOpt', v])...

     @queueCmd 'checklint',
       interactive: fast
@@ -467,7 +467,7 @@
       command: cmd
       uri: uri
       text: text
-      args: args
+      dashArgs: dashArgs
     .then (lines) =>
       rootDir = @getRootDir buffer
       rx = /^(.*?):([0-9\s]+):([0-9\s]+): *(?:(Warning|Error): *)?/


NOOP for "Autowrap mode"

NOOP for "Autowrap mode"

--- app/assets/javascripts/ansi-interpreter.js.coffee
+++ app/assets/javascripts/ansi-interpreter.js.coffee
@@ -40,7 +40,32 @@
       mode = undefined

       for mode in modes
-        if mode is "1049"
+        if mode is "1"
+          # 1 + h / l = cursor keys stuff
+        else if mode is "7"
+          # Enables/disables autowrap mode
+        else if mode is "12"
+          if action is "h"
+            # blinking cursor
+          else action is "l"
+            # steady cursor
+        else if mode is "25"
+          if action is "h"
+            @terminal.showCursor true
+          else if action is "l"
+            @terminal.showCursor false
+        else if mode is "47"
+          if action is "h"
+            @terminal.switchToAlternateBuffer()
+          else if action is "l"
+            @terminal.switchToNormalBuffer()
+        else if mode is "1000"
+          # Enables/disables normal mouse tracking
+        else if mode is "1001"
+          # pbly sth with mouse/keys...
+        else if mode is "1002"
+          # 2002 + h / l = mouse tracking stuff
+        else if mode is "1049"
           if action is "h"
             # Save cursor position, switch to alternate screen buffer, and clear screen.
             @terminal.saveCursor()
@@ -51,29 +76,6 @@
             @terminal.clearScreen()
             @terminal.switchToNormalBuffer()
             @terminal.restoreCursor()
-        else if mode is "1000"
-          # Enables/disables normal mouse tracking
-        else if mode is "1001"
-          # pbly sth with mouse/keys...
-        else if mode is "1002"
-          # 2002 + h / l = mouse tracking stuff
-        else if mode is "1"
-          # 1 + h / l = cursor keys stuff
-        else if mode is "47"
-          if action is "h"
-            @terminal.switchToAlternateBuffer()
-          else if action is "l"
-            @terminal.switchToNormalBuffer()
-        else if mode is "25"
-          if action is "h"
-            @terminal.showCursor true
-          else if action is "l"
-            @terminal.showCursor false
-        else if mode is "12"
-          if action is "h"
-            # blinking cursor
-          else action is "l"
-            # steady cursor
         else
           throw "unknown mode: " + mode + action


Remove up.Cache#first and #all, which was never used

--- lib/assets/javascripts/unpoly/classes/cache.coffee
+++ lib/assets/javascripts/unpoly/classes/cache.coffee
@@ -117,11 +117,11 @@
       @log("Cache miss for '%s'", key) unless options.silent
       undefined

-  first: (keyOrKeys) =>
-    @all(keyOrKeys)[0]
-
-  all: (keys = @keys()) =>
-    keys = u.wrapArray(keys)
-    matches = u.map keys, (key) =>
-      @get(key)
-    u.select(matches, u.isPresent)
+#  first: (keyOrKeys) =>
+#    @all(keyOrKeys)[0]
+#
+#  all: (keys = @keys()) =>
+#    keys = u.wrapArray(keys)
+#    matches = u.map keys, (key) =>
+#      @get(key)
+#    u.select(matches, u.isPresent)


added Application._configure_modules methods, and docs for Application class

--- lib/assets/javascripts/brancusi/application/application.js.coffee
+++ lib/assets/javascripts/brancusi/application/application.js.coffee
@@ -7,50 +7,86 @@

   @dependency mediator: "Mediator"

-  # default configuration options, which may be overridden by instances
+  # Default configuration options, which may be overridden by instances
   @config:
     bootstrapper: brancusi.Bootstrapper

-  # module classes for the application
+  # Module classes for the application
   @modules: {}

-  # module instances
+  # Module instances
   modules: {}

-  # model classes for the application
+  # Controller classes for the application
+  @controllers: {}
+
+  # Controller instances
+  controllers: {}
+
+  # Model classes for the application
   @models: {}
-
-  # controller classes for the application
-  @controllers: {}

-  # instantiates, initializes, starts and returns the application instance
-  @configure_and_run: ->
-    @configure().initialize().run()
-
+  # Instantiates the application and bootstrapper, then configures the instance.
+  #
+  # @return [Application] an instance of the application.
+  #
   @configure: ->
     @instance = new @
     bootstrapper = new @config.bootstrapper
-    @instance.configure(bootstrapper)
-
+    @instance.configure(bootstrapper)

+  # Configures, initializes, and runs the application.
+  #
+  # @return [Application] an instance of the application.
+  #
+  @configure_and_run: ->
+    @configure().initialize().run()
+
+  # Configures and assigns the application container with the bootstrapper, then resolves dependencies, modules and controllers.
+  #
+  # @return [Application] the application instance.
+  #
   configure: (bootstrapper) ->
-    @container = bootstrapper.configure_container()
+    @container = bootstrapper.configure_container(@)
     @container.resolve(@)
-    @configure_modules()
+    @_configure_modules()
+    @_configure_controllers()
     @
-
+
+  # Publishes the application.initialize event.
+  #
+  # @return [Application] the application instance.
+  #
   initialize: ->
     @mediator.publish "application.initialize"
     @

+  # Publishes the application.ready event.
+  #
+  # @return [Application] the application instance.
+  #
   run: (bootstrapper) ->
     @mediator.publish "application.ready"
     @

-  configure_modules: ->
+  # @private
+  # Instantiates and resolves the application modules.
+  #
+  _configure_modules: ->
     module_regex = /(.*)Module/ # e.g. AuthModule
     for klass_name, klass of @constructor.modules when matches = module_regex.exec(klass_name)
       module_name = _.string.underscored(matches[1]) # e.g. "auth"
       module = @container.resolve(new klass(module_name))
       module.sandbox.bind_subscriptions(module)
       @modules[module_name] = module
+
+  # @private
+  # Instantiates and resolves the application controllers.
+  #
+  _configure_controllers: ->
+    controller_regex = /(.*)Controller/ # e.g. HomeController
+    for klass_name, klass of @constructor.controllers when matches = controller_regex.exec(klass_name)
+      controller_name = _.string.underscored(matches[0]) # e.g. home_controller
+      controller = @container.resolve(new klass(controller_name))
+      controller.sandbox.bind_subscriptions(controller)
+      @controllers[controller_name] = controller


moment.js-based date validation.

moment.js-based date validation.

--- app/assets/javascripts/models/event_search.coffee
+++ app/assets/javascripts/models/event_search.coffee
@@ -7,6 +7,13 @@
     if moment.isMoment(v) && v.isValid()
       @set prop, v.format('YYYY-MM-DD')
   ).observes(prop)
+
+isDate = (prop) ->
+  (->
+    v = @get prop
+
+    v && moment(v).isValid()
+  ).property(prop)

 Pancakes.EventSearch = Pancakes.Model.extend
   init: ->
@@ -25,9 +32,10 @@
   enforceStartDateAsString: format('scheduledStartDate')
   enforceEndDateAsString: format('scheduledEndDate')

-  hasDateRange: (->
-    @get('scheduledStartDate') || @get('scheduledEndDate')
-  ).property('scheduledStartDate', 'scheduledEndDate')
+  hasStartDate: isDate('scheduledStartDate')
+  hasEndDate: isDate('scheduledEndDate')
+
+  hasDateRange: Ember.computed.any 'hasStartDate', 'hasEndDate'

   serialize: ->
     id: @get('id')


router: do not follow data-pinkman=false links

--- app/assets/javascripts/pinkman_base/router.coffee
+++ app/assets/javascripts/pinkman_base/router.coffee
@@ -270,7 +270,7 @@
       Pinkman.router = this
       App.router = this
       @activate(window.location.pathname)
-      $('body').on 'click', 'a', (ev) =>
+      $('body').on 'click', 'a:not([data-pinkman="false"])', (ev) =>
         ev.preventDefault()
         path = ev.currentTarget.href
         (window.location = path) unless path? and @visit(path)


fix nagios list

fix nagios list

--- lib/dashing-contrib/assets/widgets/nagios_list/nagios_list.coffee
+++ lib/dashing-contrib/assets/widgets/nagios_list/nagios_list.coffee
@@ -14,10 +14,12 @@
     @_groupBy(@get('ok'))

   ready: ->
+    node = $(@node)
     style = 'overflow': 'hidden'
     node.parent().css(style)

   clear: ->
+

   _groupBy: (items) ->
     maps = {}


updating tests to match proper coffeescript syntax

--- lib/generators/backbone/scaffold/templates/views/show_view.coffee
+++ lib/generators/backbone/scaffold/templates/views/show_view.coffee
@@ -4,5 +4,5 @@
   template: JST["<%= jst 'show' %>"]

   render: ->
-    $(this.el).html(this.template(this.options.model.toJSON() ))
+    $(this.el).html(@template(@options.model.toJSON() ))
     return this


set fixed scriptBase

set fixed scriptBase

--- app/assets/javascripts/uploadcare/settings.coffee
+++ app/assets/javascripts/uploadcare/settings.coffee
@@ -45,7 +45,7 @@
     parallelDirectUploads: 10
     passWindowOpen: false
     # maintain settings
-    scriptBase: if SCRIPT_BASE? then SCRIPT_BASE else ''
+    scriptBase: "//ucarecdn.com/widget/#{uploadcare.version}/uploadcare/"
     debugUploads: false

   presets =


Migrate to linter 2.0.0 message format

Migrate to linter 2.0.0 message format

--- lib/linter-perl-check-syntax.coffee
+++ lib/linter-perl-check-syntax.coffee
@@ -65,13 +65,15 @@

                     message = match.capture('message')?.trim()
                     messages.push
-                        type: 'Error'
-                        text: message
-                        filePath: filePath
-                        range: [
+                        severity: 'error'
+                        excerpt: message
+                        location: {
+                          file: filePath
+                          position: [
                             [line, 0]
                             [line, endCol]
-                        ]
+                          ]
+                        }

                 return messages

@@ -164,7 +166,7 @@
             name: "Perl"
             grammarScopes: ['source.perl']
             scope: 'file'
-            lintOnFly: true
+            lintsOnChange: true
             lint: (textEditor) =>
                 linter.lint(
                     textEditor,


[healthCheck] bring back support for redis cluster

Assume `cluster-require-full-coverage=yes` is set for the redis cluster,
 which is the default value.

We are writing into a single hash slot, resulting in a partial health
 check only.
The 'cluster-require-full-coverage=yes' blocks writes as soon as any
 hash slot is not covered by an active primary.
We are leveraging the health check build into the redis cluster topology
 to bring the scope of the single read/write/delete to the full cluster.

REF: 1b3566d6c830289dc826da1d13b75d68205d8dec
Signed-off-by: Jakob Ackermann <caa3e4f01d56b797520cbfed54bdab4c5e3b0ebb@outlook.com>

--- libraries/redis-wrapper/index.coffee
+++ libraries/redis-wrapper/index.coffee
@@ -37,12 +37,7 @@
 		return healthCheck

 	clusterHealthCheckBuilder: (client) ->
-		healthCheck = (callback) ->
-			jobs = client.nodes("all").map (node) =>
-				(cb) => RedisSharelatex._checkClient(node, cb)
-			async.parallel jobs, callback
-
-		return healthCheck
+		return RedisSharelatex.singleInstanceHealthCheckBuilder(client)

 	_checkClient: (client, callback) ->
 		callback = _.once(callback)


Don't memoize this promise

Don't memoize this promise


Improvements to multi-user team registration.

--- picoCTF-web/web/coffee/front-page.coffee
+++ picoCTF-web/web/coffee/front-page.coffee
@@ -208,9 +208,11 @@
           if @state.settings.max_team_size > 1
             if @state.settings.email_verification and not @state.rid
               apiNotify verificationAlert
+              @setPage "Login"
+              document.location.hash = "#team-builder"
             else
               apiNotify resp
-            @setPage "Team Management"
+              @setPage "Team Management"
           else
             if @state.settings.email_verification
               if not @state.rid or @state.rid.length == 0


magical invoke inside run()

magical invoke inside run()

--- src/core.coffee
+++ src/core.coffee
@@ -7,8 +7,6 @@
 class Core extends yang.Yang
   @mixin events.EventEmitter

-  constructor: -> @runtime = {}; super
-
   merge: ->
     modules = ([].concat arguments...).map (core) =>
       core = @origin.load core unless core instanceof Core
@@ -17,10 +15,10 @@
     [].concat modules...

   run: (feature, args...) ->
-    f = @origin.resolve 'feature', feature
-    @invoke f, args...
-    .then (res) => @runtime[feature] = res
-    .catch (err) -> console.error err
+    f = (@origin.resolve 'feature', feature)?.bind this
+    @[feature] =
+      @invoke ((x..., y) -> y f x...), args...
+      .catch (err) -> console.error err

   dump: (opts={}) ->
     # force opts defaults (for now)