Making the LEVEL constants a bit more readable.

--- nodes.coffee
+++ nodes.coffee
@@ -42,7 +42,7 @@
     o.level  = lvl if lvl?
     node     = @unfoldSoak(o) or this
     node.tab = o.indent
-    if o.level is LVL_TOP or node.isPureStatement() or not node.isStatement(o)
+    if o.level is LEVEL.TOP or node.isPureStatement() or not node.isStatement(o)
       node.compileNode o
     else
       node.compileClosure o
@@ -69,7 +69,7 @@

   # Compile to a source/variable pair suitable for looping.
   compileLoopReference: (o, name) ->
-    src = tmp = @compile o, LVL_LIST
+    src = tmp = @compile o, LEVEL.LIST
     unless NUMBER.test(src) or IDENTIFIER.test(src) and o.scope.check(src, immediate: on)
       src = "#{ tmp = o.scope.freeVariable name } = #{src}"
     [src, tmp]
@@ -205,7 +205,7 @@
   compileRoot: (o) ->
     o.indent = @tab = if o.bare then '' else TAB
     o.scope  = new Scope null, this, null
-    o.level  = LVL_TOP
+    o.level  = LEVEL.TOP
     code     = @compileWithDeclarations o
     code     = code.replace TRAILING_WHITESPACE, ''
     if o.bare then code else "(function() {\n#{code}\n}).call(this);\n"
@@ -228,7 +228,7 @@
     while node isnt node = node.unwrap() then
     node = node.unfoldSoak(o) or node
     node.tags.front = on
-    o.level = LVL_TOP
+    o.level = LEVEL.TOP
     code    = node.compile o
     if node.isStatement o then code else @tab + code + ';'

@@ -281,7 +281,7 @@
     if expr and expr not instanceof Return then expr.compile o, lvl else super o, lvl

   compileNode: (o) ->
-    o.level = LVL_PAREN
+    o.level = LEVEL.PAREN
     @tab + "return#{ if @expression then ' ' + @expression.compile o else '' };"

 #### Value
@@ -365,7 +365,7 @@
   compileNode: (o) ->
     @base.tags.front = @tags.front
     props = @properties
-    code  = @base.compile o, if props.length then LVL_ACCESS else null
+    code  = @base.compile o, if props.length then LEVEL.ACCESS else null
     code  = "(#{code})" if props[0] instanceof Accessor and @isSimpleNumber()
     (code += prop.compile o) for prop in props
     code
@@ -473,11 +473,11 @@
     @variable?.tags.front = @tags.front
     for arg in @args when arg instanceof Splat
       return @compileSplat o
-    args = (arg.compile o, LVL_LIST for arg in @args).join ', '
+    args = (arg.compile o, LEVEL.LIST for arg in @args).join ', '
     if @isSuper
       @compileSuper args, o
     else
-      (if @isNew then 'new ' else '') + @variable.compile(o, LVL_ACCESS) + "(#{args})"
+      (if @isNew then 'new ' else '') + @variable.compile(o, LEVEL.ACCESS) + "(#{args})"

   # `super()` is converted into a call against the superclass's implementation
   # of the current function.
@@ -495,9 +495,9 @@
       base = Value.wrap @variable
       if (name = base.properties.pop()) and base.isComplex()
         ref = o.scope.freeVariable 'this'
-        fun = "(#{ref} = #{ base.compile o, LVL_LIST })#{ name.compile o }"
+        fun = "(#{ref} = #{ base.compile o, LEVEL.LIST })#{ name.compile o }"
       else
-        fun = ref = base.compile o, LVL_ACCESS
+        fun = ref = base.compile o, LEVEL.ACCESS
         fun += name.compile o if name
       return "#{fun}.apply(#{ref}, #{splatargs})"
     idt = @idt 1
@@ -506,7 +506,7 @@
     #{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, LVL_LIST }, #{splatargs}, function() {})
+    #{@tab}})(#{ @variable.compile o, LEVEL.LIST }, #{splatargs}, function() {})
     """

 #### Extends
@@ -553,7 +553,7 @@
   constructor: (@index) -> super()

   compile: (o) ->
-    (if @proto then '.prototype' else '') + "[#{ @index.compile o, LVL_PAREN }]"
+    (if @proto then '.prototype' else '') + "[#{ @index.compile o, LEVEL.PAREN }]"

   isComplex: -> @index.isComplex()

@@ -613,7 +613,7 @@
       return @compileSplatLiteral o
     objects = []
     for obj, i in @objects
-      code = obj.compile o, LVL_LIST
+      code = obj.compile o, LEVEL.LIST
       objects.push (if obj instanceof Comment
         "\n#{code}\n#{o.indent}"
       else if i is @objects.length - 1
@@ -742,22 +742,22 @@
     if isValue = @variable instanceof Value
       return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
       return @compileConditional  o if @context in @CONDITIONAL
-    name = @variable.compile o, LVL_LIST
+    name = @variable.compile o, LEVEL.LIST
     if @value instanceof Code and match = @METHOD_DEF.exec name
       @value.name  = match[2]
       @value.klass = match[1]
-    val = @value.compile o, LVL_LIST
+    val = @value.compile o, LEVEL.LIST
     return "#{name}: #{val}" if @context is 'object'
     o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
     val = name + " #{ @context or '=' } " + val
-    if o.level <= LVL_LIST then val else "(#{val})"
+    if o.level <= LEVEL.LIST then val else "(#{val})"

   # Brief implementation of recursive pattern matching, when assigning array or
   # object literals to a value. Peeks at their properties to assign inner names.
   # See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
   # for details.
   compilePatternMatch: (o) ->
-    top       = o.level is LVL_TOP
+    top       = o.level is LEVEL.TOP
     {value}   = this
     {objects} = @variable.base
     return value.compile o unless olength = objects.length
@@ -774,7 +774,7 @@
       accessClass = if IDENTIFIER.test idx.value then Accessor else Index
       (value = Value.wrap value).properties.push new accessClass idx
       return new Assign(obj, value).compile o
-    valVar  = value.compile o, LVL_LIST
+    valVar  = value.compile o, LEVEL.LIST
     assigns = []
     splat   = false
     if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
@@ -800,10 +800,10 @@
         if typeof idx isnt 'object'
           idx = new Literal(if splat then "#{valVar}.length - #{ olength - idx }" else idx)
         val = new Value new Literal(valVar), [new accessClass idx]
-      assigns.push new Assign(obj, val).compile o, LVL_LIST
+      assigns.push new Assign(obj, val).compile o, LEVEL.LIST
     assigns.push valVar unless top
     code = assigns.join ', '
-    if o.level < LVL_LIST then code else "(#{code})"
+    if o.level < LEVEL.LIST then code else "(#{code})"

   # When compiling a conditional assignment, take care to ensure that the
   # operands are only evaluated once, even though we have to reference them
@@ -898,7 +898,7 @@
     super()
     @value = new Literal @name

-  compile: (o) -> @value.compile o, LVL_LIST
+  compile: (o) -> @value.compile o, LEVEL.LIST

   toString: ->
     {name} = this
@@ -957,7 +957,7 @@
     args = []
     end  = -1
     for arg, i in list
-      code = arg.compile o, LVL_LIST
+      code = arg.compile o, LEVEL.LIST
       prev = args[end]
       if arg not instanceof Splat
         if prev and starts(prev, '[') and ends(prev, ']')
@@ -1001,14 +1001,14 @@
     o.indent = @idt 1
     set      = ''
     {body}   = this
-    if o.level > LVL_TOP or @returns
+    if o.level > LEVEL.TOP or @returns
       rvar = o.scope.freeVariable 'result'
       set  = "#{@tab}#{rvar} = [];\n"
       body = Push.wrap rvar, body if body
     body = Expressions.wrap [new If @guard, body] if @guard
     code = set + @tab + """
-      while (#{ @condition.compile o, LVL_PAREN }) {
-      #{ body.compile o, LVL_TOP }
+      while (#{ @condition.compile o, LEVEL.PAREN }) {
+      #{ body.compile o, LEVEL.TOP }
       #{@tab}}
     """
     if @returns
@@ -1086,7 +1086,7 @@
     return @compileChain o     if @isChainable() and @first.unwrap().isChainable()
     return @compileExistence o if @operator is '?'
     @first.tags.front = @tags.front
-    "#{ @first.compile o, LVL_OP } #{@operator} #{ @second.compile o, LVL_OP }"
+    "#{ @first.compile o, LEVEL.OP } #{@operator} #{ @second.compile o, LEVEL.OP }"

   # Mimic Python's chained comparisons when multiple comparison operators are
   # used sequentially. For example:
@@ -1095,7 +1095,7 @@
   #     true
   compileChain: (o) ->
     [@first.second, shared] = @first.unwrap().second.cache o
-    "#{ @first.compile o, LVL_OP } && #{ shared.compile o } #{@operator} #{ @second.compile o, LVL_OP }"
+    "#{ @first.compile o, LEVEL.OP } && #{ shared.compile o } #{@operator} #{ @second.compile o, LEVEL.OP }"

   compileExistence: (o) ->
     if @first.isComplex()
@@ -1104,13 +1104,13 @@
     else
       fst = @first
       ref = fst.compile o
-    new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LVL_LIST }"
+    new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LEVEL.LIST }"

   # Compile a unary **Op**.
   compileUnary: (o) ->
     space = if @operator in @PREFIX_OPERATORS or @first instanceof Op and
         @first.operator is @operator and @operator in ['+', '-'] then ' ' else ''
-    parts = [@operator, space, @first.compile(o, LVL_OP)]
+    parts = [@operator, space, @first.compile(o, LEVEL.OP)]
     (if @flip then parts.reverse() else parts).join ''

 #### In
@@ -1131,20 +1131,20 @@
       @compileLoopTest o

   compileOrTest: (o) ->
-    [sub, ref] = @object.cache o, LVL_OP
+    [sub, ref] = @object.cache o, LEVEL.OP
     [cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
     tests = for item, i in @array.base.objects
       (if i then ref else sub) + cmp + item.compile o
     tests = tests.join cnj
-    if o.level < LVL_OP then tests else "(#{tests})"
+    if o.level < LEVEL.OP then tests else "(#{tests})"

   compileLoopTest: (o) ->
-    [sub, ref] = @object.cache o, LVL_LIST
+    [sub, ref] = @object.cache o, LEVEL.LIST
     code = utility('indexOf') + ".call(#{ @array.compile o }, #{ref}) " +
            if @negated then '< 0' else '>= 0'
     return code if sub is ref
     code = sub + ', ' + code
-    if o.level < LVL_LIST then code else "(#{code})"
+    if o.level < LEVEL.LIST then code else "(#{code})"

   toString: (idt) ->
     super idt, @constructor.name + if @negated then '!' else ''
@@ -1171,14 +1171,14 @@
     o.indent  = @idt 1
     errorPart = if @error then " (#{ @error.compile o }) " else ' '
     catchPart = if @recovery
-      " catch#{errorPart}{\n#{ @recovery.compile o, LVL_TOP }\n#{@tab}}"
+      " catch#{errorPart}{\n#{ @recovery.compile o, LEVEL.TOP }\n#{@tab}}"
     else unless @ensure or @recovery
       ' catch (_e) {}'
     """
     #{@tab}try {
-    #{ @attempt.compile o, LVL_TOP }
+    #{ @attempt.compile o, LEVEL.TOP }
     #{@tab}}#{ catchPart or '' }
-    """ + if @ensure then " finally {\n#{ @ensure.compile o, LVL_TOP }\n#{@tab}}" else ''
+    """ + if @ensure then " finally {\n#{ @ensure.compile o, LEVEL.TOP }\n#{@tab}}" else ''

 #### Throw

@@ -1213,7 +1213,7 @@
       "typeof #{code} !== \"undefined\" && #{code} !== null"
     else
       "#{code} != null"
-    if o.level <= LVL_COND then code else "(#{code})"
+    if o.level <= LEVEL.COND then code else "(#{code})"

 #### Parens

@@ -1237,8 +1237,8 @@
     if expr instanceof Value and expr.isAtomic()
       expr.tags.front = @tags.front
       return expr.compile o
-    bare = o.level < LVL_OP and (expr instanceof Op or expr instanceof Call)
-    code = expr.compile o, LVL_PAREN
+    bare = o.level < LEVEL.OP and (expr instanceof Op or expr instanceof Call)
+    code = expr.compile o, LEVEL.PAREN
     if bare then code else "(#{code})"

 #### For
@@ -1301,9 +1301,9 @@
       if name or @object and not @raw
         [sourcePart, svar] = @source.compileLoopReference o, 'ref'
       else
-        sourcePart = svar = @source.compile o, LVL_PAREN
+        sourcePart = svar = @source.compile o, LEVEL.PAREN
       namePart = if @pattern
-        new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LVL_TOP
+        new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LEVEL.TOP
       else if name
         "#{name} = #{svar}[#{ivar}]"
       unless @object
@@ -1325,7 +1325,7 @@
         when  1 then '++'
         when -1 then '--'
         else (if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar)
-    if o.level > LVL_TOP or @returns
+    if o.level > LEVEL.TOP or @returns
       rvar     = scope.freeVariable 'result'
       defPart += @tab + rvar + ' = [];\n'
       retPart  = @compileReturnValue rvar, o
@@ -1335,7 +1335,7 @@
     o.indent = idt
     defPart + """
     #{@tab}for (#{forPart}) {
-    #{ guardPart or '' }#{varPart}#{ body.compile o, LVL_TOP }
+    #{ guardPart or '' }#{varPart}#{ body.compile o, LEVEL.TOP }
     #{@tab}}
     """ + retPart

@@ -1358,17 +1358,17 @@
   compileNode: (o) ->
     idt1 = @idt 1
     idt2 = o.indent = @idt 2
-    code = @tab + "switch (#{ @subject?.compile(o, LVL_PAREN) or true }) {\n"
+    code = @tab + "switch (#{ @subject?.compile(o, LEVEL.PAREN) or true }) {\n"
     for [conditions, block], i in @cases
       for cond in flatten [conditions]
         cond  = cond.invert().invert() unless @subject
-        code += idt1 + "case #{ cond.compile o, LVL_PAREN }:\n"
-      code += block.compile(o, LVL_TOP) + '\n'
+        code += idt1 + "case #{ cond.compile o, LEVEL.PAREN }:\n"
+      code += block.compile(o, LEVEL.TOP) + '\n'
       break if i is @cases.length - 1 and not @otherwise
       for expr in block.expressions by -1 when expr not instanceof Comment
         code += idt2 + 'break;\n' unless expr instanceof Return
         break
-    code += idt1 + "default:\n#{ @otherwise.compile o, LVL_TOP }\n" if @otherwise
+    code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL.TOP }\n" if @otherwise
     code +  @tab + '}'

 #### If
@@ -1404,7 +1404,7 @@
   # The **If** only compiles into a statement if either of its bodies needs
   # to be a statement. Otherwise a conditional operator is safe.
   isStatement: (o) ->
-    o?.level is LVL_TOP or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
+    o?.level is LEVEL.TOP or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)

   compileNode: (o) ->
     if @isStatement o then @compileStatement o else @compileExpression o
@@ -1424,7 +1424,7 @@
   # force inner *else* bodies into statement form.
   compileStatement: (o) ->
     child    = del o, 'chainChild'
-    cond     = @condition.compile o, LVL_PAREN
+    cond     = @condition.compile o, LEVEL.PAREN
     o.indent = @idt 1
     body     = @ensureExpressions(@body).compile o
     ifPart   = "if (#{cond}) {\n#{body}\n#{@tab}}"
@@ -1433,14 +1433,14 @@
     ifPart + ' else ' + if @isChain
        @elseBodyNode().compile merge o, indent: @tab, chainChild: true
     else
-      "{\n#{ @elseBody.compile o, LVL_TOP }\n#{@tab}}"
+      "{\n#{ @elseBody.compile o, LEVEL.TOP }\n#{@tab}}"

   # Compile the If as a conditional operator.
   compileExpression: (o) ->
-    code = @condition .compile(o, LVL_COND) + ' ? ' +
-           @bodyNode().compile(o, LVL_LIST) + ' : ' +
-           @elseBodyNode()?.compile o, LVL_LIST
-    if o.level >= LVL_COND then "(#{code})" else code
+    code = @condition .compile(o, LEVEL.COND) + ' ? ' +
+           @bodyNode().compile(o, LEVEL.LIST) + ' : ' +
+           @elseBodyNode()?.compile o, LEVEL.LIST
+    if o.level >= LEVEL.COND then "(#{code})" else code

   unfoldSoak: -> @soakNode and this

@@ -1532,12 +1532,13 @@
   slice  : 'Array.prototype.slice'

 # Levels indicates a node's position in the AST.
-LVL_TOP    = 0  # ...;
-LVL_PAREN  = 1  # (...)
-LVL_LIST   = 2  # [...]
-LVL_COND   = 3  # ... ? x : y
-LVL_OP     = 4  # !...
-LVL_ACCESS = 5  # ...[0]
+LEVEL =
+  TOP    : 0  # ...;
+  PAREN  : 1  # (...)
+  LIST   : 2  # [...]
+  COND   : 3  # ... ? x : y
+  OP     : 4  # !...
+  ACCESS : 5  # ...[0]

 # Tabs are two spaces for pretty printing.
 TAB = '  '


removed remnant of 341f511b

removed remnant of 341f511b

--- nodes.coffee
+++ nodes.coffee
@@ -1225,13 +1225,6 @@
     [left, rite] = @first.cacheReference o
     rite = new AssignNode rite, @second
     return new OpNode(@operator.slice(0, -1), left, rite).compile o
-
-    [first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
-    second = @second.compile o
-    second = "(#{second})" if @second instanceof OpNode
-    o.scope.find(first) if first.match(IDENTIFIER)
-    return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar))[0] } ? #{firstVar} : #{second}" if @operator is '?='
-    "#{first} #{ @operator.substr(0, 2) } (#{firstVar} = #{second})"

   compileExistence: (o) ->
     if @first.isComplex()


changed iteration strategy

changed iteration strategy

--- src/answer-checker.coffee
+++ src/answer-checker.coffee
@@ -4,9 +4,9 @@
 # @module answer-checker

 FuzzySet = require 'fuzzyset.js'
-
+
 goodConfidence = 0.65
-
+
 # check if the guess is valid for the given answer
 # this calls FuzzySet on every consecutive combination of words from the guess
 # by https://github.com/natgaertner
@@ -19,13 +19,12 @@
       match[0][0] # return the confidence level
   )
   return result.maxScore >= goodConfidence
-
+
 # return element and keyfunc result for the element for which keyfunc returns the highest value
 keymax = (set, keyfunc) ->
   maxElement = null
   maxScore = 0
-  for idx of set
-    element = set[idx]
+  for element in set
     score = keyfunc(element)
     if score > maxScore
       maxElement = element
@@ -34,7 +33,7 @@
     maxElement: maxElement
     maxScore: maxScore
   }
-
+
 # ['thin', 'crust', 'pizza'] => ['thin', 'crust', 'pizza', 'thin crust', 'crust pizza', 'thin crust pizza']
 pseudoPowerSet = (words) ->
   set = []


Fixed sidebar resize handler disappearing issue.

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -677,7 +677,7 @@
     dragGlobDir.y = if y > 0 then 'bottom' else 'top'

     el = @$()
-    if @dragIsAllowed # is this line should be here or at the top ?
+    if @dragIsAllowed
       dragMeta   = @dragState.meta
       targetPosX = if dragMeta.right  and not dragMeta.left then 'right'  else 'left'
       targetPosY = if dragMeta.bottom and not dragMeta.top  then 'bottom' else 'top'


Fixes #44.  Set message edit/delete, show message edit/delete history to default to false in RocketChat.settings

--- packages/rocketchat-lib/settings/server/startup.coffee
+++ packages/rocketchat-lib/settings/server/startup.coffee
@@ -38,9 +38,9 @@
 	RocketChat.settings.add 'SMTP_Password', '', { type: 'string', group: 'SMTP' }

 	RocketChat.settings.addGroup 'Message'
-	RocketChat.settings.add 'Message_AllowEditing', true, { type: 'boolean', group: 'Message', public: true }
-	RocketChat.settings.add 'Message_AllowDeleting', true, { type: 'boolean', group: 'Message', public: true }
-	RocketChat.settings.add 'Message_ShowEditedStatus', true, { type: 'boolean', group: 'Message', public: true }
+	RocketChat.settings.add 'Message_AllowEditing', false, { type: 'boolean', group: 'Message', public: true }
+	RocketChat.settings.add 'Message_AllowDeleting', false, { type: 'boolean', group: 'Message', public: true }
+	RocketChat.settings.add 'Message_ShowEditedStatus', false, { type: 'boolean', group: 'Message', public: true }
 	RocketChat.settings.add 'Message_ShowDeletedStatus', false, { type: 'boolean', group: 'Message', public: true }
 	RocketChat.settings.add 'Message_KeepHistory', false, { type: 'boolean', group: 'Message', public: true }
 	RocketChat.settings.add 'Message_MaxAllowedSize', 5000, { type: 'int', group: 'Message', public: true }


OPCE6: added tests for Selection object

OPCE6: added tests for Selection object

--- test/rte-test.coffee
+++ test/rte-test.coffee
@@ -104,10 +104,13 @@
 describe 'Selection object should', ->
   sel = sel2 = rte = word = null

-  it 'be initialized with three parameters', ->
+  it 'initialized with three parameters [constructor]', ->
     rte = new Rte "Zero One two three four five"
     sel = new Selection 1, 7, rte

+  it 'should convert positions correctly [_relativeFromAbsolute]', ->
+    rte = new Rte "Zero One two three four five"
+    sel = new Selection 1, 7, rte
     sel.should.have.property('startPos')
     sel.should.have.deep.property('startPos.word', 0)
     sel.should.have.deep.property('startPos.pos', 1)
@@ -116,7 +119,21 @@
     sel.should.have.deep.property('endPos.word', 1)
     sel.should.have.deep.property('endPos.pos', 2)

-  it 'merge correctly the selections', ->
+  it 'should have working functions [equals,notEquals,in,contains,overlaps,atLeftOf]', ->
+    sel0 = new Selection 0, 1, rte
+    sel1 = new Selection 1, 2, rte
+    sel2 = new Selection 0, 2, rte
+    sel3 = new Selection 0, 3, rte
+    sel0.equals(sel0).should.be.true
+    sel0.equals(sel1).should.be.false
+    sel0.notEquals(sel0).should.be.false
+    sel0.notEquals(sel1).should.be.true
+    sel0.atLeftOf(sel1).should.be.true
+    sel0.atLeftOf(sel2).should.be.false
+    sel1.contains(sel0).should.be.false
+    sel2.contains(sel0).should.be.false
+
+  it 'merge correctly the selections [merge]', ->
     rte = new Rte "Zero One two three four five"
     sel = new Selection 0, 1, rte
     sel2 = new Selection 1, 10, rte
@@ -130,9 +147,20 @@
     sel2.should.have.deep.property 'endPos.word', 2
     sel2.should.have.deep.property 'endPos.pos', 1

+  it 'unbind correctly', ->
+    rte = new Rte "Zero One two three four five"
+    sel = new Selection 0, 1, rte
+    sel2 = new Selection 0, 5, rte
+    sel.unbind()
+    (sel.left == null).should.be.true
+    (sel.right == null).should.be.true
+    rte.getWord(0).left.length.should.equal 1
+    rte.getWord(0).right.length.should.equal 0
+
+
 describe 'Word objects should', ->
   sel = sel2 = rte = word = null
-  it 'be linked correctly to selections', ->
+  it 'be linked correctly to selections [constructor]', ->
     rte = new Rte "This is a test"
     sel = new Selection 0, 6, rte
     word = rte.getWord(0)
@@ -142,3 +170,13 @@
     word = rte.getWord(1)
     word.left.length.should.equals 0
     word.right[0].equals(sel).should.be.true
+
+  it 'should remove selection correctly [removeSel]', ->
+    rte = new Rte "This is a test"
+    sel = new Selection 0, 6, rte
+    word = rte.getWord(0)
+    word.removeSel sel, "left"
+    word.left.length.should.equals 0
+
+    word = rte.getWord(1)
+    word.right.length.should.equals 1


Refactored functions out of startPairing()

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -43,8 +43,10 @@
     @subscriptions.dispose()

   disconnect: ->
+    # @pairingChannel.trigger 'client-disconnected', {colour: @markerColour}
     @pusher.disconnect()
-    @clearMarkers(@markerColour)
+    _.each @friendColours, (colour) => @clearMarkers(colour)
+    atom.views.getView(@editor).removeAttribute('id')
     @hidePanel()

   serialize: ->
@@ -156,9 +158,8 @@

   startPairing: ->
     @subscriptions.add atom.commands.add 'atom-workspace', 'pusht:disconnect': => @disconnect()
-    triggerPush = true
+    @triggerPush = true
     @editor = atom.workspace.getActiveEditor()
-
     atom.views.getView(@editor).setAttribute('id', 'pusht')

     buffer = @buffer = @editor.buffer
@@ -181,13 +182,10 @@
       @sendGrammar()
       @syncGrammars()
       @shareCurrentFile(buffer)
-
       @receiveFriendInfo(data)
-
       @pairingChannel.trigger 'client-broadcast-initial-marker', {colour: @markerColour}

-    @pairingChannel.bind 'client-broadcast-initial-marker', (data) =>
-      @receiveFriendInfo(data)
+    @pairingChannel.bind 'client-broadcast-initial-marker', (data) => @receiveFriendInfo(data)

     @pairingChannel.bind 'client-text-select', (data) =>
       @clearMarkers(data.colour)
@@ -198,47 +196,58 @@
       @editor.setGrammar(grammar)
       @syncGrammars()

-    @pairingChannel.bind 'client-share-whole-file', (file) ->
-      triggerPush = false
+    @pairingChannel.bind 'client-share-whole-file', (file) =>
+      @triggerPush = false
       buffer.setText(file)
-      triggerPush = true
-
-    @pairingChannel.bind 'client-share-partial-file', (chunk) ->
-      triggerPush = false
+      @triggerPush = true
+
+    @pairingChannel.bind 'client-share-partial-file', (chunk) =>
+      @triggerPush = false
       buffer.append(chunk)
-      triggerPush = true
-
-    @pairingChannel.bind 'client-change', (data) =>
-      newRange = Range.fromObject(data.event.newRange)
-      oldRange = Range.fromObject(data.event.oldRange)
-      newText = data.event.newText
-
-      triggerPush = false
-
-      @clearMarkers(data.colour)
-
-
-      if data.deletion
-        buffer.delete oldRange
-        @editor.scrollToBufferPosition(oldRange.start)
-        @addMarker oldRange.start.toArray()[0], data.colour
-      else if oldRange.containsRange(newRange)
-        buffer.setTextInRange oldRange, newText
-        @editor.scrollToBufferPosition(oldRange.start)
-        @addMarker oldRange.start.toArray()[0], data.colour
-      else
-        buffer.insert newRange.start, newText
-        @editor.scrollToBufferPosition(newRange.start)
-        @addMarker(newRange.end.toArray()[0], data.colour)
-
-
-      triggerPush = true
-
-    buffer.onDidChange (event) =>
-      return unless triggerPush
+      @triggerPush = true
+
+    @pairingChannel.bind 'client-change', (data) => @changeBuffer(data)
+
+    # @pairingChannel.bind 'client-disconnected', (data) =>
+    #   @clearMarkers(data.colour)
+    #   disconnectView = new AlertView "Your pair buddy has left the session."
+    #   atom.workspace.addModalPanel(item: disconnectView, visible: true)
+
+
+    @listenToBufferChanges()
+    @syncSelectionRange()
+
+  listenToBufferChanges: ->
+    @buffer.onDidChange (event) =>
+      return unless @triggerPush
       deletion = !(event.newText is "\n") and (event.newText.length is 0)
       @pairingChannel.trigger 'client-change', {deletion: deletion, event: event, colour: @markerColour}

+  changeBuffer: (data) ->
+    newRange = Range.fromObject(data.event.newRange)
+    oldRange = Range.fromObject(data.event.oldRange)
+    newText = data.event.newText
+
+    @triggerPush = false
+
+    @clearMarkers(data.colour)
+
+    if data.deletion
+      @buffer.delete oldRange
+      @editor.scrollToBufferPosition(oldRange.start)
+      @addMarker oldRange.start.toArray()[0], data.colour
+    else if oldRange.containsRange(newRange)
+      @buffer.setTextInRange oldRange, newText
+      @editor.scrollToBufferPosition(oldRange.start)
+      @addMarker oldRange.start.toArray()[0], data.colour
+    else
+      @buffer.insert newRange.start, newText
+      @editor.scrollToBufferPosition(newRange.start)
+      @addMarker(newRange.end.toArray()[0], data.colour)
+
+    @triggerPush = true
+
+  syncSelectionRange: ->
     @editor.onDidChangeSelectionRange (event) =>
       rows = event.newBufferRange.getRows()
       return unless rows.length > 1


Removed lastEvent var

Removed lastEvent var

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -42,12 +42,9 @@

     channel = pusher.subscribe('presence-pairing')

-    me = null
-
-    lastEvent = {}

     channel.bind 'pusher:subscription_succeeded', (members) ->
-      me =  members.me.id
+      console.log members.me.id

     triggerPush = true

@@ -56,14 +53,19 @@
       triggerPush = false
       if data.deletion
         buffer.delete data.oldRange
+      # else if data.insertNewLine
+        # buffer.setTextInRange data.newRange, data.oldText
       else
-        if !lastEvent or !((data.newText is lastEvent.newText) and (_.isEqual data.newRange, lastEvent.newRange))
           buffer.setTextInRange data.newRange, data.newText
       triggerPush = true

     buffer.onDidChange (event) ->
+
       console.log event
-      lastEvent = {newRange: event.newRange, newText: event.newText}
+
+
+      return if (_.isEqual event.newRange, event.oldRange) and (event.newText is event.oldText)
+

       insertNewLine = (event.newText is "\n")
       deletion = !insertNewLine and (event.newText.length is 0)


Updated promises usage

Updated promises usage

--- prviewer.coffee
+++ prviewer.coffee
@@ -455,13 +455,13 @@
       </html>
     """

-  fetchAllPulls()
+  Promise.resolve()
+    .then(fetchAllPulls)
     .then(annotatePulls)
     .then(hideOldPulls)
     .then(sortPulls)
     .then(renderDashboard)
-    .then(null, renderError)
-    .done()
+    .catch(renderError)

 # -------------------------------------------------------------------------
 # SERVER STARTUP


changed global/window modification to avoid added var from the CS->JS translation

--- src/index.coffee
+++ src/index.coffee
@@ -1,2 +1,2 @@
-global = window = `window? window : (global? global: null)`
+`global = window = window? window : (global? global: null)`
 if window? then window.Clan = require './Clan.coffee'


added support for chrome apps, or anything else where the parent window isn't accessed via window.parent

http://stackoverflow.com/a/30453032

--- src/index.coffee
+++ src/index.coffee
@@ -2,7 +2,6 @@

 RPCClient = require './rpc_client'

-IS_FRAMED = window.self isnt window.top
 HANDSHAKE_TIMEOUT_MS = 10 * 1000 # 10 seconds

 class PortalGun
@@ -15,15 +14,22 @@
     @isParentValidFn ?= -> true
     timeout ?= null
     @isListening = false
+    @isFramed = window.self isnt window.top
+    @parent = window.parent
+
     @client = new RPCClient({
       timeout: timeout
-      postMessage: (msg, origin) ->
-        window.parent?.postMessage msg, origin
+      postMessage: (msg, origin) =>
+        @parent?.postMessage msg, origin
     })
     # All parents must respond to 'ping' with 'pong'
     @registeredMethods = {
       ping: -> 'pong'
     }
+
+  setParent: (parent) =>
+    @parent = parent
+    @isFramed = true

   # Binds global message listener
   # Must be called before .call()
@@ -52,7 +58,7 @@
         throw new Error 'Method not found'
       return fn.apply null, params

-    if IS_FRAMED
+    if @isFramed
       frameError = null
       @validation
       .then =>


remove u and t query params on client

remove u and t query params on client

--- @client/browser_location.coffee
+++ @client/browser_location.coffee
@@ -34,6 +34,8 @@
     loc.query_params[k] = v

   loc.query_params = url_parts.searchObject
+  delete loc.query_params.u if loc.query_params.u
+  delete loc.query_params.t if loc.query_params.t

   # ...and parse anchors
   hash = url_parts.hash


update rundeck plugin

update rundeck plugin

--- hubot/scripts/slack_rundeck.coffee
+++ hubot/scripts/slack_rundeck.coffee
@@ -188,7 +188,7 @@
     if robot.auth.hasRole(msg.envelope.user, rundeck.adminRole)
       rundeck.jobs().run name, null, (job, results) ->
         if job
-          msg.send "Running job #{name}: #{results.result.executions[0].execution[0]['$'].href}"
+          msg.send "Running job #{name}: #{results.executions[0].execution[0]['$'].href}"
         else
           msg.send "Could not execute Rundeck job \"#{name}\"."
     else
@@ -204,7 +204,7 @@
     if robot.auth.hasRole(msg.envelope.user, rundeck.adminRole)
       rundeck.jobs().run name, query, (job, results) ->
         if job
-          msg.send "Running job #{name}: #{results.result.executions[0].execution[0]['$'].href}"
+          msg.send "Running job #{name}: #{results.executions[0].execution[0]['$'].href}"
         else
           msg.send "Could not execute Rundeck job \"#{name}\"."
     else


add test for property aliases

add test for property aliases

--- test/lib/test.coffee
+++ test/lib/test.coffee
@@ -82,6 +82,66 @@
         key : helperFile 'server.private.pem'
         cert: helperFile 'server.cert.pem'
         ca  : helperFile 'ca.cert.pem'
+        onSecureConnect: (connection) ->
+          serverConnection = connection
+          serverConnection.on 'data', (data) -> received = data.toString 'utf8'
+          serverConnection.on 'end', -> server.close()
+
+      # once the server is listening do the client stuffs
+      server.on 'listening', ->
+        listening = true
+
+        # create a client via `cio` with its transform and the same port as the server
+        client = cio.client
+          port: server.address().port
+          key : helperFile 'client.private.pem'
+          cert: helperFile 'client.cert.pem'
+          ca  : helperFile 'ca.cert.pem'
+          onConnect: ->
+            connected = true
+            client.end 'done', 'utf8'
+
+      before 'wait for server to listen', (done) -> server.listen done
+
+      before 'wait for server to close', (done) ->
+        server.on 'close', ->
+          closed = true
+          done()
+
+      it 'should listen', -> assert.equal listening, true
+
+      it 'should connect', -> assert.equal connected, true
+
+      it 'should receive', -> assert.equal received, 'done'
+
+      it 'should close', -> assert.equal closed, true
+
+
+
+    describe 'with security cert property aliases', ->
+
+      certs = {}
+
+      before 'generate certs', (done) ->
+        # if certs already exist, we're done
+        if fs.existsSync helperFile 'ca.cert.pem' then return done()
+
+        # otherwise, run the script to generate them...
+        # NOTE: expects openssl .. so, nix environments...?
+        require(helperFile 'genscripts.js')(done)
+
+      # remember these for assertions
+      client    = null
+      received  = null
+      listening = false
+      connected = false
+      closed    = false
+
+      # use `cio` to create a server with a tranform (and an arbitrary port)
+      server = cio.server
+        private: helperFile 'server.private.pem'
+        public : helperFile 'server.cert.pem'
+        root   : helperFile 'ca.cert.pem'
         onSecureConnect: (connection) ->
           serverConnection = connection
           serverConnection.on 'data', (data) -> received = data.toString 'utf8'


edit list functionality => a modal, and separate the code

--- @client/list.coffee
+++ @client/list.coffee
@@ -85,10 +85,8 @@
         proposals_count: proposals.length
         fresh: @props.fresh
         allow_editing: !@props.allow_editing? || @props.allow_editing
-        edit_immediately: @props.edit_immediately
-        done_callback: @props.done_callback
-
-      if !is_collapsed && !@props.fresh && !@props.edit_immediately
+
+      if !is_collapsed && !@props.fresh

         permitted = permit('create proposal', list_key)
         DIV null,
@@ -246,6 +244,16 @@
   else
     __remove_this_list(list_key, page)

+
+
+
+
+
+
+
+
+
+
 EditList = ReactiveComponent
   displayName: 'EditList'

@@ -261,86 +269,7 @@

     return SPAN null if !current_user.is_admin

-    if (@props.fresh || (@props.edit_immediately && !@initialized)) && !edit_list.editing
-      edit_list.editing = true
-      save edit_list
-
-    if @props.edit_immediately
-      @initialized = true
-      if edit_list.editing == false
-        @props.done_callback?()
-      edit_list.editing ?= true
-
-
-
-    submit = =>
-
-
-      customizations = subdomain.customizations
-
-      customizations[list_key] ?= {}
-      list_config = customizations[list_key]
-
-      fields = ['list_title', 'list_description', 'list_permit_new_items', 'list_category', 'slider_pole_labels', 'list_opinions_title', 'discussion_enabled', 'list_is_archived']
-
-      for f in fields
-        val = edit_list[f]
-
-        if val?
-          list_config[f] = val
-
-      description = fetch("#{list_key}-description").html
-      if description == "

"
-        description = ""
-
-      list_config.list_description = description
-
-
-      if @props.fresh
-        new_name = "#{slugify(list_config.list_title or list_config.list_category or 'Proposals')}-#{Math.round(Math.random() * 100)}"
-        new_key = "list/#{new_name}"
-        customizations[new_key] = customizations[list_key]
-        _.defaults customizations[new_key],
-          created_by: current_user.user
-          created_at: Date.now()
-        delete customizations[list_key]
-
-        # if tabs are enabled, add it to the current tab
-        if get_tabs()
-          tab = get_tab()
-          if tab
-            tab.lists.push new_key
-          else
-            console.error "Cannot add the list to the current tab #{current_tab}"
-        else
-          customizations.lists ?= get_all_lists()
-          if customizations.lists.indexOf('*') == -1 && customizations.lists.indexOf(new_key) == -1
-            customizations.lists.push new_key
-          console.log 'NEW CUSTOMZIATION', customizations.lists, new_key
-
-
-      save subdomain, =>
-        if subdomain.errors
-          console.error "Failed to save list changes", subdomain.errors
-
-        exit_edit()
-
-    cancel_edit = =>
-      customizations = subdomain.customizations
-
-      if @props.fresh && list_key of customizations
-        delete customizations[list_key]
-        save subdomain
-      else
-        exit_edit()
-
-    exit_edit = =>
-      edit_list.editing = false
-      for k,v of edit_list
-        if k != 'key' && k != 'editing'
-          delete edit_list[k]
-
-      save edit_list
+

     admin_actions = [{action: 'edit', label: translator('edit')},
                      {action: 'list_order', label: translator('engage.list-configuration.copy_link', 'reorder lists')},
@@ -447,44 +376,24 @@
                 show_flash("Link copied to clipboard")
               , (err) ->
                 show_flash_error("Problem copying link to clipboard")
-
-
-
       else
-
-        DIV
-          style:
-            marginTop: 24
-
-
-          BUTTON
-            className: 'btn'
-            style:
-              backgroundColor: focus_color()
-            disabled: !(edit_list.list_title?.length > 0)
-            onClick: submit
-            onKeyDown: (e) =>
-              if e.which == 13 || e.which == 32 # ENTER or SPACE
-                e.target.click()
-                e.preventDefault()
-
-            translator 'engage.save_changes_button', 'Save'
-
-          BUTTON
-            className: 'like_link'
-            style:
-              color: '#777'
-              fontSize: 18
-              marginLeft: 12
-              position: 'relative'
-              top: 2
-            onClick: cancel_edit
-            onKeyDown: (e) =>
-              if e.which == 13 || e.which == 32 # ENTER or SPACE
-                e.target.click()
-                e.preventDefault()
-
-            translator 'shared.cancel_button', 'cancel'
+        ModalNewList
+          list: list
+          fresh: false
+          combines_these_lists: @props.combines_these_lists
+
+  componentDidMount: -> @setFocusOnTitle()
+
+  componentDidUpdate: -> @setFocusOnTitle()
+
+  setFocusOnTitle: ->
+    edit_list = fetch "edit-#{@props.list.key}"
+    focus_now = @last_edit_state != edit_list.editing && edit_list.editing
+    @last_edit_state = edit_list.editing
+
+    if focus_now
+      setTimeout =>
+        moveCursorToEnd @refs.input?.getDOMNode()

@@ -515,13 +424,10 @@
     if edit_list.editing
       _.extend wrapper_style,
         backgroundColor: '#f3f3f3'
-        marginLeft: if !@props.edit_immediately then -36
-        marginTop: if !@props.edit_immediately then -36
-        padding: if !@props.edit_immediately then "18px 36px 36px 36px"
+        marginLeft: -36
+        marginTop: -36
+        padding: "18px 36px 36px 36px"
         width: HOMEPAGE_WIDTH() + 36 * 2
-
-      if @props.edit_immediately
-        wrapper_style.width -= 250

     edit_list.discussion_enabled ?= customization('discussion_enabled', list_key)
     edit_list.list_is_archived ?= customization('list_is_archived', list_key)
@@ -538,13 +444,12 @@

         DIV
           style:
-            width:  if !@props.edit_immediately then HOMEPAGE_WIDTH()
-            margin:  if !@props.edit_immediately then 'auto'
+            width:  HOMEPAGE_WIDTH()
+            margin:  'auto'

           EditableTitle
             list: @props.list
             fresh: @props.fresh
-            edit_immediately: @props.edit_immediately

           if edit_list.editing || !is_collapsed
             DIV null,
@@ -552,27 +457,299 @@
                 EditableDescription
                   list: @props.list
                   fresh: @props.fresh
-                  edit_immediately: @props.edit_immediately
-
-          # if edit_list.editing || !is_collapsed
-          #   DIV
-          #     style:
-          #       position: 'relative'
-          #       marginTop: if get_list_title(list_key, null, subdomain)?.length > 0 || edit_list.editing then  18
-          #       display: if !edit_list.editing && category_value(list_key, null, subdomain).length + histo_title(list_key).length == 0 then 'none'
-
-          #     EditableListCategory
-          #       list: @props.list
-          #       fresh: @props.fresh
-
-          #     EditableOpinionLabel
-          #       list: @props.list
-          #       fresh: @props.fresh
-
-        if edit_list.editing
-
-          option_block =
-            marginTop: 8
+
+
+      if @props.allow_editing
+        EditList
+          list: @props.list
+          fresh: @props.fresh
+          combines_these_lists: @props.combines_these_lists
+
+      if !edit_list.editing && @props.proposals_count > 0 && !customization('questionaire', list_key, subdomain) && !is_collapsed && !customization('list_no_filters', list_key, subdomain)
+        list_actions
+          list: @props.list
+          add_new: !@props.combines_these_lists && customization('list_permit_new_items', list_key, subdomain) && !is_collapsed && @props.proposals_count > 4
+          can_sort: customization('homepage_show_search_and_sort', null, subdomain) && @props.proposals_count > 1
+          fresh: @props.fresh
+
+
+
+window.NewList = ReactiveComponent
+  displayName: 'NewList'
+
+  render: ->
+    subdomain = fetch '/subdomain'
+
+    if !@local.edit_key
+      @local.edit_key = "list/new-list-#{Math.round(Math.random() * 1000)}"
+
+    list =
+      key: @local.edit_key
+
+    list_key = list.key
+    edit_list = fetch "edit-#{list_key}"
+
+    @local.hovering ?= false
+
+    if edit_list.editing || @props.edit_immediately
+      ModalNewList
+        fresh: true
+        list: list
+        done_callback: @props.done_callback
+
+    else
+      BUTTON
+        style:
+          textAlign: 'left'
+          marginTop: 35
+          display: 'block'
+          padding: '18px 24px'
+          position: 'relative'
+          left: -24
+          width: '100%'
+          borderRadius: 8
+          backgroundColor: if @local.hovering then '#eaeaea' else '#efefef'
+          border: '1px solid'
+          borderColor: if @local.hovering then '#bbb' else '#ddd'
+
+        onMouseEnter: =>
+          @local.hovering = true
+          save @local
+        onMouseLeave: =>
+          @local.hovering = false
+          save @local
+
+        onClick: =>
+          edit_list.editing = true
+          save edit_list
+
+        onKeyDown: (e) =>
+          if e.which == 13 || e.which == 32 # ENTER or SPACE
+            e.target.click()
+            e.preventDefault()
+
+        H1
+          style:
+            fontSize: 28
+            fontWeight: 700
+            color: if @local.hovering then '#444' else '#666'
+
+          translator 'engage.create_new_list_button', "Create a new list"
+
+        DIV
+          style:
+            fontSize: 14
+            marginTop: 4
+          'A list defines a category like "Recommendations" or poses an open-ended question like "What are your ideas?"'
+
+
+
+
+styles += """
+  [data-widget="ModalNewList"] .LIST-fat-header-field {
+    margin-left: 0;
+  }
+
+"""
+window.ModalNewList = ReactiveComponent
+  displayName: 'ModalNewList'
+  mixins: [Modal]
+
+  render: ->
+    list = @props.list
+    list_key = list.key
+
+    current_user = fetch '/current_user'
+    edit_list = fetch "edit-#{list_key}"
+    subdomain = fetch '/subdomain'
+
+
+    return SPAN null if !current_user.is_admin
+
+
+
+
+    submit = =>
+
+
+      customizations = subdomain.customizations
+
+      customizations[list_key] ?= {}
+      list_config = customizations[list_key]
+
+      fields = ['list_title', 'list_description', 'list_permit_new_items', 'list_category', 'slider_pole_labels', 'list_opinions_title', 'discussion_enabled', 'list_is_archived']
+
+      for f in fields
+        val = edit_list[f]
+
+        if val?
+          list_config[f] = val
+
+      description = fetch("#{list_key}-description").html
+      if description == "

"
+        description = ""
+
+      list_config.list_description = description
+
+
+      if @props.fresh
+        new_name = "#{slugify(list_config.list_title or list_config.list_category or 'Proposals')}-#{Math.round(Math.random() * 100)}"
+        new_key = "list/#{new_name}"
+        customizations[new_key] = customizations[list_key]
+        _.defaults customizations[new_key],
+          created_by: current_user.user
+          created_at: Date.now()
+        delete customizations[list_key]
+
+        # if tabs are enabled, add it to the current tab
+        if get_tabs()
+          tab = get_tab()
+          if tab
+            tab.lists.push new_key
+          else
+            console.error "Cannot add the list to the current tab #{current_tab}"
+        else
+          customizations.lists ?= get_all_lists()
+          if customizations.lists.indexOf('*') == -1 && customizations.lists.indexOf(new_key) == -1
+            customizations.lists.push new_key
+          console.log 'NEW CUSTOMZIATION', customizations.lists, new_key
+
+
+      save subdomain, =>
+        if subdomain.errors
+          console.error "Failed to save list changes", subdomain.errors
+
+        exit_edit()
+
+    cancel_edit = =>
+      customizations = subdomain.customizations
+
+      if @props.fresh && list_key of customizations
+        delete customizations[list_key]
+        save subdomain
+      else
+        exit_edit()
+
+    exit_edit = =>
+      edit_list.editing = false
+      for k,v of edit_list
+        if k != 'key' && k != 'editing'
+          delete edit_list[k]
+
+      save edit_list
+
+      @props.done_callback?()
+
+    edit_list.discussion_enabled ?= customization('discussion_enabled', list_key)
+    edit_list.list_is_archived ?= customization('list_is_archived', list_key)
+
+    title = get_list_title list_key, true, subdomain
+    description = edit_list.description or customization('list_description', list_key, subdomain)
+    if Array.isArray(description)
+      description = description.join('\n')
+
+    description_style = customization 'list_description_style', list_key
+
+    option_block =
+      marginTop: 8
+
+    children = \
+        DIV
+          style:
+            marginTop: 24
+
+          DIV null,
+            DIV
+              className: 'LIST-field-edit-label'
+
+              TRANSLATE
+                id: "engage.list-config-title"
+                span:
+                  component: SPAN
+                  args:
+                    style:
+                      fontWeight: 700
+
+                "Title. Usually an open-ended question like \"What are your ideas?\" or a list label like \"Recommended actions for mitigation\"."
+
+            H1
+              className: 'LIST-header'
+
+              AutoGrowTextArea
+                id: "title-#{list_key}"
+                className: 'LIST-header LIST-fat-header-field'
+                ref: 'input'
+                focus_on_mount: true
+                style: _.defaults {}, customization('list_label_style', list_key, subdomain) or {},
+                  fontFamily: header_font()
+                  width: HOMEPAGE_WIDTH() + -200
+
+                defaultValue: if !@props.fresh then title
+                onChange: (e) ->
+                  edit_list.list_title = e.target.value
+                  save edit_list
+
+
+          DIV null,
+            if description?.length > 0 || typeof(description) == 'function' || edit_list.editing
+
+
+              DIV
+                style: _.defaults {}, (description_style or {})
+                className: 'LIST-description'
+
+                if typeof description == 'function'
+                  description()
+                else
+
+                  DIV null,
+
+                    DIV
+                      className: 'LIST-field-edit-label'
+
+                      TRANSLATE
+                        id: "engage.list-config-description"
+                        span:
+                          component: SPAN
+                          args:
+                            style:
+                              fontWeight: 700
+
+                        "Description [optional]. Give any additional information or direction here."
+
+                    DIV
+                      id: 'edit_description'
+                      style:
+                        # marginTop: -12
+                        width:  HOMEPAGE_WIDTH() - 200
+
+                      STYLE
+                        dangerouslySetInnerHTML: __html: """
+                          #edit_description .ql-editor {
+                            min-height: 48px;
+                            padding: 12px 12px;
+                            border: 1px solid #eaeaea;
+                            border-radius: 8px;
+                            background-color: white;
+
+                          }
+                        """
+
+                      WysiwygEditor
+                        key: "#{list_key}-description"
+                        horizontal: true
+                        html: customization('list_description', list_key)
+                        # placeholder: if !@props.fresh then translator("engage.list_description", "(optional) Description")
+                        toolbar_style:
+                          right: 0
+                        container_style:
+                          borderRadius: 8
+                        style:
+                          fontSize: if browser.is_mobile then 32
+
+
+
+

           DIV null,

@@ -897,115 +1074,50 @@

-      if @props.allow_editing
-        EditList
-          list: @props.list
-          fresh: @props.fresh
-          edit_immediately: @props.edit_immediately
-          done_callback: @props.done_callback
-
-      if !edit_list.editing && @props.proposals_count > 0 && !customization('questionaire', list_key, subdomain) && !is_collapsed && !customization('list_no_filters', list_key, subdomain)
-        list_actions
-          list: @props.list
-          add_new: !@props.combines_these_lists && customization('list_permit_new_items', list_key, subdomain) && !is_collapsed && @props.proposals_count > 4
-          can_sort: customization('homepage_show_search_and_sort', null, subdomain) && @props.proposals_count > 1
-          fresh: @props.fresh
-
-
-styles += """
-  [data-widget="ModalNewList"] .LIST-fat-header-field {
-    margin-left: 0;
-  }
-
-"""
-window.ModalNewList = ReactiveComponent
-  displayName: 'ModalNewList'
-  mixins: [Modal]
-
-  render: ->
-    children = DIV null,
-      if @props.list
-        List @props
-      else
-        NewList @props
+
+
+
+
+
+          BUTTON
+            className: 'btn'
+            style:
+              backgroundColor: focus_color()
+            disabled: !(edit_list.list_title?.length > 0)
+            onClick: submit
+            onKeyDown: (e) =>
+              if e.which == 13 || e.which == 32 # ENTER or SPACE
+                e.target.click()
+                e.preventDefault()
+
+            translator 'engage.save_changes_button', 'Save'
+
+          BUTTON
+            className: 'like_link'
+            style:
+              color: '#777'
+              fontSize: 18
+              marginLeft: 12
+              position: 'relative'
+              top: 2
+            onClick: cancel_edit
+            onKeyDown: (e) =>
+              if e.which == 13 || e.which == 32 # ENTER or SPACE
+                e.target.click()
+                e.preventDefault()
+
+            translator 'shared.cancel_button', 'cancel'
+
+
+
+
+
+
+
+
+
+
     wrap_in_modal children, HOMEPAGE_WIDTH() + 72
-
-
-window.NewList = ReactiveComponent
-  displayName: 'NewList'
-
-  render: ->
-    subdomain = fetch '/subdomain'
-
-    if !@local.edit_key
-      @local.edit_key = "list/new-list-#{Math.round(Math.random() * 1000)}"
-
-    list =
-      key: @local.edit_key
-
-    list_key = list.key
-    edit_list = fetch "edit-#{list_key}"
-
-    if @props.edit_immediately
-
-      if edit_list.editing == false
-        @props.done_callback?()
-
-      edit_list.editing ?= true
-
-
-    @local.hovering ?= false
-
-    if edit_list.editing
-      List
-        fresh: true
-        list: list
-        edit_immediately: @props.edit_immediately
-
-    else
-      BUTTON
-        style:
-          textAlign: 'left'
-          marginTop: 35
-          display: 'block'
-          padding: '18px 24px'
-          position: 'relative'
-          left: -24
-          width: '100%'
-          borderRadius: 8
-          backgroundColor: if @local.hovering then '#eaeaea' else '#efefef'
-          border: '1px solid'
-          borderColor: if @local.hovering then '#bbb' else '#ddd'
-
-        onMouseEnter: =>
-          @local.hovering = true
-          save @local
-        onMouseLeave: =>
-          @local.hovering = false
-          save @local
-
-        onClick: =>
-          edit_list.editing = true
-          save edit_list
-
-        onKeyDown: (e) =>
-          if e.which == 13 || e.which == 32 # ENTER or SPACE
-            e.target.click()
-            e.preventDefault()
-
-        H1
-          style:
-            fontSize: 28
-            fontWeight: 700
-            color: if @local.hovering then '#444' else '#666'
-
-          translator 'engage.create_new_list_button', "Create a new list"
-
-        DIV
-          style:
-            fontSize: 14
-            marginTop: 4
-          'A list defines a category like "Recommendations" or poses an open-ended question like "What are your ideas?"'

@@ -1022,7 +1134,6 @@
     list_state = fetch list_key
     is_collapsed = list_state.collapsed

-    edit_list = fetch "edit-#{list_key}"
     subdomain = fetch '/subdomain'

     title = get_list_title list_key, true, subdomain
@@ -1040,230 +1151,58 @@

     DIV null,
-      if edit_list.editing
-        DIV
-          className: 'LIST-field-edit-label'
-
-          TRANSLATE
-            id: "engage.list-config-title"
-            span:
-              component: SPAN
-              args:
-                style:
-                  fontWeight: 700
-
-            "Title. Usually an open-ended question like \"What are your ideas?\" or a list label like \"Recommended actions for mitigation\"."

       H1
         className: 'LIST-header'
         style: # ugly...we only want to show the expand/collapse icon
-          fontSize: if !edit_list.editing && title.replace(/^\s+|\s+$/g, '').length == 0 then 0
-
-        if edit_list.editing
-          AutoGrowTextArea
-            id: "title-#{list_key}"
-            className: 'LIST-header LIST-fat-header-field'
-            ref: 'input'
-            focus_on_mount: true
-            style: _.defaults {}, customization('list_label_style', list_key, subdomain) or {},
-              fontFamily: header_font()
-              width: HOMEPAGE_WIDTH() + (if @props.edit_immediately then -200 else 24)
-
-            defaultValue: if !@props.fresh then title
-            onChange: (e) ->
-              edit_list.list_title = e.target.value
-              save edit_list
-
-        else
-
-          TITLE_WRAPPER
-            tabIndex: if !list_uncollapseable then 0
-            'aria-label': "#{title}. #{translator('Expand or collapse list.')}"
-            'aria-pressed': !is_collapsed
-            onMouseEnter: => @local.hover_label = true; save @local
-            onMouseLeave: => @local.hover_label = false; save @local
-            className: 'LIST-header'
-            style: _.defaults {}, customization('list_label_style', list_key, subdomain) or {},
-              fontFamily: header_font()
-              cursor: if !list_uncollapseable then 'pointer'
-              position: 'relative'
-              textAlign: 'left'
-              outline: 'none'
-
-            onKeyDown: if !list_uncollapseable then (e) ->
-              if e.which == 13 || e.which == 32 # ENTER or SPACE
-                toggle_list()
-                e.preventDefault()
-            onClick: if !list_uncollapseable then (e) ->
+          fontSize: if title.replace(/^\s+|\s+$/g, '').length == 0 then 0
+
+        TITLE_WRAPPER
+          tabIndex: if !list_uncollapseable then 0
+          'aria-label': "#{title}. #{translator('Expand or collapse list.')}"
+          'aria-pressed': !is_collapsed
+          onMouseEnter: => @local.hover_label = true; save @local
+          onMouseLeave: => @local.hover_label = false; save @local
+          className: 'LIST-header'
+          style: _.defaults {}, customization('list_label_style', list_key, subdomain) or {},
+            fontFamily: header_font()
+            cursor: if !list_uncollapseable then 'pointer'
+            position: 'relative'
+            textAlign: 'left'
+            outline: 'none'
+
+          onKeyDown: if !list_uncollapseable then (e) ->
+            if e.which == 13 || e.which == 32 # ENTER or SPACE
               toggle_list()
-              document.activeElement.blur()
-
-            title
-
-            if !list_uncollapseable
+              e.preventDefault()
+          onClick: if !list_uncollapseable then (e) ->
+            toggle_list()
+            document.activeElement.blur()
+
+          title
+
+          if !list_uncollapseable
+            SPAN
+              'aria-hidden': true
+              style:
+                position: 'absolute'
+                left: -tw - 20
+                top: if is_collapsed then 0 else 3
+                paddingRight: 20
+                paddingTop: 12
+                display: 'inline-block'
+
               SPAN
-                'aria-hidden': true
-                style:
-                  position: 'absolute'
-                  left: -tw - 20
-                  top: if is_collapsed then 0 else 3
-                  paddingRight: 20
-                  paddingTop: 12
+
+                style: cssTriangle (if is_collapsed then 'right' else 'bottom'), ((customization('list_label_style', list_key, subdomain) or {}).color or 'black'), tw, th,
+                  width: tw
+                  height: th
+                  opacity: if @local.hover_label or is_collapsed then 1 else .1
+                  outline: 'none'
                   display: 'inline-block'
-
-                SPAN
-
-                  style: cssTriangle (if is_collapsed then 'right' else 'bottom'), ((customization('list_label_style', list_key, subdomain) or {}).color or 'black'), tw, th,
-                    width: tw
-                    height: th
-                    opacity: if @local.hover_label or is_collapsed then 1 else .1
-                    outline: 'none'
-                    display: 'inline-block'
-                    verticalAlign: 'top'
-
-  componentDidMount: -> @setFocusOnTitle()
-
-  componentDidUpdate: -> @setFocusOnTitle()
-
-  setFocusOnTitle: ->
-    edit_list = fetch "edit-#{@props.list.key}"
-    focus_now = @last_edit_state != edit_list.editing && edit_list.editing
-    @last_edit_state = edit_list.editing
-
-    if focus_now
-      setTimeout =>
-        moveCursorToEnd @refs.input?.getDOMNode()
+                  verticalAlign: 'top'
+

-
-
-# EditableListCategory = ReactiveComponent
-#   displayName: 'EditableListCategory'
-#   render: ->
-#     subdomain = fetch '/subdomain'
-#     current_user = fetch '/current_user'
-
-#     list = @props.list
-#     list_key = list.key
-#     list_state = fetch list_key
-#     edit_list = fetch "edit-#{list_key}"
-
-#     category = category_value list_key, @props.fresh, subdomain
-
-#     has_title = customization('list_description', list_key)?.length > 0 || customization('list_title', list_key)?.length > 0
-#     heading_style = _.defaults {}, customization('list_label_style', list_key),
-#       fontSize: if !has_title then 44 else 36
-#       fontWeight: if !has_title then 700 else 500
-#       fontFamily: header_font()
-
-#     show_opinion_header = edit_list.editing || widthWhenRendered(category, heading_style) <= column_sizes().first + column_sizes().gutter
-
-#     DIV
-#       style:
-#         width: if show_opinion_header then column_sizes().first else '100%'
-#         display: 'inline-block'
-
-#       if edit_list.editing
-#         DIV
-#           className: 'LIST-field-edit-label'
-
-#           TRANSLATE
-#             id: "engage.list-config-category"
-#             span:
-#               component: SPAN
-#               args:
-#                 style:
-#                   fontWeight: 700
-
-#             "Category. e.g. \"Ideas\", \"Policies\", \"Questions\", \"Strategies\"."
-
-#       H1 null,
-
-#         if edit_list.editing
-#           AutoGrowTextArea
-#             id: "category-#{list_key}"
-#             ref: 'input'
-#             className: "LIST-header LIST-fat-header-field #{if has_title then 'LIST-smaller-header'}"
-#             style: _.defaults {}, customization('list_label_style', list_key) or {},
-#               fontFamily: header_font()
-#               width: column_sizes().first + 24
-
-#             defaultValue: category
-#             onChange: (e) ->
-#               edit_list.list_category = e.target.value
-#               save edit_list
-#         else
-#           SPAN
-#             className: if !has_title then 'LIST-header' else 'LIST-header LIST-smaller-header'
-#             style: _.defaults {}, customization('list_label_style', list_key) or {}
-#             category
-
-
-# EditableOpinionLabel = ReactiveComponent
-#   displayName: 'EditableOpinionLabel'
-#   render: ->
-#     subdomain = fetch '/subdomain'
-#     current_user = fetch '/current_user'
-
-#     list = @props.list
-#     list_key = list.key
-#     list_state = fetch list_key
-#     edit_list = fetch "edit-#{list_key}"
-
-#     opinion_title = histo_title list_key
-
-#     has_title = customization('list_description', list_key)?.length > 0 || customization('list_title', list_key)?.length > 0
-#     heading_style = _.defaults {}, customization('list_label_style', list_key),
-#       fontSize: if !has_title then 44 else 36
-#       fontWeight: if !has_title then 700 else 500
-#       fontFamily: header_font()
-
-#     show = edit_list.editing || widthWhenRendered(category_value(list_key, null, subdomain), heading_style) <= column_sizes().first + column_sizes().gutter
-
-#     DIV
-#       style:
-#         width: column_sizes().second
-#         display: if show then 'inline-block' else 'none'
-#         marginLeft: column_sizes().gutter
-#         textAlign: 'center'
-
-#       if edit_list.editing
-#         DIV
-#           className: 'LIST-field-edit-label'
-#           style:
-#             textAlign: 'right'
-
-#           TRANSLATE
-#             id: "engage.list-config-opinion-title"
-#             span:
-#               component: SPAN
-#               args:
-#                 style:
-#                   fontWeight: 700
-
-#             "Opinion title. e.g. \"Ratings\", \"Gut checks\"."
-
-
-#       H1 null,
-#         if edit_list.editing
-#           AutoGrowTextArea
-#             id: "list_opinions_title-#{list_key}"
-#             ref: 'input'
-#             className: "LIST-header LIST-fat-header-field #{if has_title then 'LIST-smaller-header'}"
-#             style: _.defaults {}, customization('list_label_style', list_key) or {},
-#               fontFamily: header_font()
-#               width: column_sizes().second + 24
-#               textAlign: 'right'
-
-#             defaultValue: opinion_title
-#             onChange: (e) ->
-#               edit_list.list_opinions_title = e.target.value
-#               save edit_list
-
-#         else
-#           SPAN
-#             className: if !has_title then 'LIST-header' else 'LIST-header LIST-smaller-header'
-#             style: _.defaults {}, customization('list_label_style', list_key) or {}
-#             opinion_title

 styles += """
@@ -1284,9 +1223,7 @@
     list = @props.list
     list_key = list.key

-    edit_list = fetch "edit-#{list_key}"
-
-    description = edit_list.list_description or customization('list_description', list_key)
+    description = customization('list_description', list_key)
     if Array.isArray(description)
       description = description.join('\n')

@@ -1300,65 +1237,16 @@
       if typeof description == 'function'
         description()
       else
-
-        if current_user.is_admin && edit_list.editing
-          DIV null,
-
-            DIV
-              className: 'LIST-field-edit-label'
-
-              TRANSLATE
-                id: "engage.list-config-description"
-                span:
-                  component: SPAN
-                  args:
-                    style:
-                      fontWeight: 700
-
-                "Description [optional]. Give any additional information or direction here."
-
-            DIV
-              id: 'edit_description'
-              style:
-                marginLeft:  if !@props.edit_immediately then -13
-                # marginTop: -12
-                width:  HOMEPAGE_WIDTH() +  if !@props.edit_immediately then 13 * 2 else -200
-
-              STYLE
-                dangerouslySetInnerHTML: __html: """
-                  #edit_description .ql-editor {
-                    min-height: 48px;
-                    padding: 12px 12px;
-                    border: 1px solid #eaeaea;
-                    border-radius: 8px;
-                    background-color: white;
-
-                  }
-                """
-
-              WysiwygEditor
-                key: "#{list_key}-description"
-                horizontal: true
-                html: customization('list_description', list_key)
-                # placeholder: if !@props.fresh then translator("engage.list_description", "(optional) Description")
-                toolbar_style:
-                  right: 0
-                container_style:
-                  borderRadius: 8
-                style:
-                  fontSize: if browser.is_mobile then 32
-
-        else
-          desc = description
-          if typeof desc == 'string'
-            desc = [description]
-
-          for para, idx in desc
-            DIV
-              key: idx
-              style:
-                marginBottom: 10
-              dangerouslySetInnerHTML: {__html: para}
+        desc = description
+        if typeof desc == 'string'
+          desc = [description]
+
+        for para, idx in desc
+          DIV
+            key: idx
+            style:
+              marginBottom: 10
+            dangerouslySetInnerHTML: {__html: para}

 window.list_actions = (props) ->
@@ -1740,9 +1628,9 @@

         if @local.add_new_list || @local.edit_list
-          ModalNewList
+          NewList
+            edit_immediately: true
             list: @local.edit_list
-            edit_immediately: true
             done_callback: =>
               @local.add_new_list = @local.edit_list = false
               save @local


XSS encode input value for resize calculation

--- components/inputs/KDInputView.coffee
+++ components/inputs/KDInputView.coffee
@@ -312,6 +312,7 @@
   setAutoGrow:->

     $input = @$()
+    $input.css "overflow", "hidden"

     @setClass "autogrow"

@@ -323,12 +324,14 @@
         @_clone.appendTo 'body'
         @_clone.css
           height        : "auto"
-          "z-index"     : 100000
+          zIndex        : 100000
           width         : $input.width()
-          padding       : $input.css('padding')
-          "word-break"  : $input.css('word-break')
-          "font-size"   : $input.css('font-size')
-          "line-height" : $input.css('line-height')
+          border        : $input.css 'border'
+          padding       : $input.css 'padding'
+          wordBreak     : $input.css 'wordBreak'
+          fontSize      : $input.css 'fontSize'
+          lineHeight    : $input.css 'lineHeight'
+          whiteSpace    : "pre"

     @on "blur", =>
       @_clone.detach()
@@ -340,14 +343,16 @@
   resize: ->
     return  unless @_clone
     @_clone.appendTo 'body' unless document.body.contains @_clone[0]
-    @_clone.html @getValue().replace /\n/gm, "<br />"
+    @_clone.html Encoder.XSSEncode @getValue()
+    @_clone.append document.createElement "br"
+
     height = @_clone.height()
-    if @$().css('box-sizing') is "border-box"
-      padding    = parseInt(@_clone.css("padding-top"), 10) + parseInt(@_clone.css("padding-bottom"), 10)
-      border     = parseInt(@_clone.css("border-top-width"), 10) + parseInt(@_clone.css("border-bottom-width"), 10)
-      height     = height + border + padding
-
-    @setHeight height
+    if @$().css("boxSizing") is "border-box"
+      padding = parseInt(@_clone.css("paddingTop"),     10) + parseInt(@_clone.css("paddingBottom"),     10)
+      border  = parseInt(@_clone.css("borderTopWidth"), 10) + parseInt(@_clone.css("borderBottomWidth"), 10)
+      height  = height + border + padding
+
+    @setHeight Math.max @initialHeight, height

   enableTabKey:-> @inputTabKeyEnabled = yes

@@ -438,5 +443,5 @@
       event.preventDefault()
       t.selectionStart = t.selectionEnd = ss + tabLength

-
-
+  viewAppended: ->
+    @initialHeight = @$().height()


check in the long waiting fix for the single mode

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -145,7 +145,8 @@
         compiled = granite.collectPackages no, library
         assert _.isObject(compiled), "invalid library"
         conf = new Object master: yes, instance: yes
-        granite.cachedKernel(library).bootstrap conf
+        kernel = granite.nucleus.kernel.GraniteKernel
+        kernel.bootstrap conf # call original kernel

     # This task launches an instance of application where this task
     # is invoked at. It should be either an application build within


Added Android condition for create new instance of xhr as having problems with new param posts

--- Framework/Network.coffee
+++ Framework/Network.coffee
@@ -29,8 +29,8 @@
     @reset(options)
     @activeRequest = options

-    # Ti.API.info("#{@xhr.readyState}: READY STATE CHANGED")
-    # @xhr.setOnreadystatechange((e) => Ti.API.info("#{e.source.readyState}: READY STATE CHANGED"))
+    if Ti.Platform.osname != 'iphone' || Ti.Platform.osname != 'ipad'        # CMC put this here as Android was having trouble making multiple requests with params
+      @xhr = Ti.Network.createHTTPClient({ timeout: @settings.timeout })

     @xhr.open('POST', options.url) #TODO: GJ: add support for GET
     @xhr.setRequestHeader('Content-Type', options.contentType)


replace dots with underscores in the hostname for unique kite names (node-kite-amqp)

--- node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
+++ node_modules_koding/kite-amqp/lib/kite-amqp/index.coffee
@@ -93,7 +93,7 @@
         else
           console.log "Unknown routing key: #{routingKey}.  Dropping message on the floor."

-  getKiteId:-> "|#{process.pid}|#{require('os').hostname()}"
+  getKiteId:-> "|#{process.pid}|#{require('os').hostname().replace /\./, '_'}"

   getServiceType:-> "kite-#{@kiteName}"


fixed activity widget

fixed activity widget

--- client/landing/app/Applications/Activity.kdapplication/widgets/widgetview.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/widgetview.coffee
@@ -79,6 +79,7 @@
         {
           title       : "Ask a Question"
           type        : "default question disabledForBeta"
+          disabled    : yes
           callback    : (treeItem, event)=> @changeTab "question", treeItem.getData().title
         }
         {
@@ -89,16 +90,19 @@
         {
           title       : "Start a Discussion"
           type        : "default discussion disabledForBeta"
+          disabled    : yes
           callback    : (treeItem, event)=> @changeTab "discussion", treeItem.getData().title
         }
         {
           title       : "Link"
+          disabled    : yes
           type        : "default link disabledForBeta"
           callback    : (treeItem, event)=> @changeTab "link", treeItem.getData().title
         }
         {
           title       : "Tutorial"
           type        : "default tutorial disabledForBeta"
+          disabled    : yes
           callback    : (treeItem, event)=> @changeTab "tutorial", treeItem.getData().title
         }
       ]


Also close the channel when leave requested for conversation

--- client/landing/app/Applications/Chat.kdapplication/Views/conversationmenu.coffee
+++ client/landing/app/Applications/Chat.kdapplication/Views/conversationmenu.coffee
@@ -37,7 +37,8 @@
               'Leave'      :
                 style      : "modal-clean-red"
                 callback   : =>
-                  {conversation} = @getData()
+                  {conversation, chatChannel} = @getData()
+                  chatChannel?.close()?.off()
                   conversation.leave (err)=>
                     warn err  if err
                     @emit 'DestroyConversation'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


use .25-second delay before loading indicator is removed

--- services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfJs.coffee
@@ -94,6 +94,7 @@
 					$timeout () ->
 						scope.loading = false
 						delete scope.progress
+					, 250

 				#scope.$watch "highlights", (areas) ->
 					# console.log 'got HIGHLIGHTS in pdfJS', areas


tweaks

tweaks

--- top-tracks.coffee
+++ top-tracks.coffee
@@ -5,6 +5,8 @@
   pluck
   sortBy
 } = require 'lodash-node'
+
+market = 'US'

 _apiUrl = (method, params) ->
   """The URL for an API call"""
@@ -21,6 +23,7 @@
   """Calls Spotify's API"""

   url = _apiUrl method, params
+  console.log "Spotify API call: ", url
   response = request.sync url
   try
     JSON.parse response.body
@@ -36,7 +39,7 @@
   result = apiCall.sync 'search',
     q: artistName
     type: 'artist'
-    market: 'US'
+    market: market
     limit: 1
     offset: 0

@@ -48,7 +51,7 @@
   # https://api.spotify.com/v1/artists/2qc41rNTtdLK0tV3mJn2Pm/albums?market=US&limit=50&offset=0

   result = apiCall.sync "artists/#{ artistId }/albums",
-    market: 'US'
+    market: market
     limit: 50
     offset: 0

@@ -114,7 +117,7 @@
   """Returns HTML for a bunch of tracks"""

   fields = ['id', 'popularity', 'name', 'uri']
-  html = "

#	"
+  html = "

#	"
   for f in fields
     html += "#{ f }	"
   html += "
"


use URL object to better concatenate and strip unnecessary port numbers

--- core/app/backbone/models/fact.coffee
+++ core/app/backbone/models/fact.coffee
@@ -31,7 +31,7 @@
       data: {provider_names, message}

   friendly_fact_url: ->
-    Factlink.Global.core_url + '/f/' + @id
+    new URL('/f/' + @id, Factlink.Global.core_url).toString()

   toJSON: ->
     _.extend super(),


Admin: Stacks: Implemented stack preview.

--- client/admin/lib/views/stacks/onboarding/onboardingview.coffee
+++ client/admin/lib/views/stacks/onboarding/onboardingview.coffee
@@ -4,6 +4,7 @@
 GetStartedView        = require './getstartedview'
 ConfigurationView     = require './configurationview'
 ProviderSelectionView = require './providerselectionview'
+{jsonToYaml}   = require '../yamlutils'

 module.exports = class OnboardingView extends JView
@@ -61,6 +62,9 @@
       @emit 'PageNavigationRequested', 'next'

+    @getStartedView.emit 'NextPageRequested'
+
+
   createFooter: ->

     @backButton = new kd.ButtonView
@@ -105,6 +109,82 @@

   updateStackTemplate: ->
+
+    selectedProvider    = @providerSelectionView.selected?.getOption 'provider'
+    selectedCodeService = @codeSetupView.selected?.getOption 'service'
+    serverConfigPanes   = @configurationView.tabView.panes
+    selectedInstances   = {}
+    cloneRepoTemplates  =
+      github            : 'git clone git@github.com/username/reponame.git'
+      owngitserver      : 'git clone git@yourgitserver.com/reponame.git'
+    providerTemplates   =
+      aws               :
+        'access_key'    : '${var.aws_access_key}'
+        'secret_key'    : '${var.aws_secret_key}'
+
+
+    serverConfigPanes.forEach (pane, index) ->
+
+      selectedServices = []
+
+      serverConfig = selectedInstances["example_#{++index}"] =
+        instance_type: 't2.micro'
+        ami: ''
+
+      { configurationToggles } = pane.configView
+
+      configurationToggles.forEach (toggle) ->
+        selectedServices.push toggle.getOption 'name'  if toggle.getValue()
+
+      if selectedServices.length
+        serverConfig.user_data = "apt-get -y install #{selectedServices.join ' '}"
+
+    stackTemplate    =
+      provider       : providerTemplates[selectedProvider]
+      resources      :
+        aws_instance : selectedInstances
+
+    if selectedCodeService
+      cloneText = cloneRepoTemplates[selectedCodeService]
+
+      for serverName, serverConfig of stackTemplate.resources.aws_instance
+        { user_data } = serverConfig
+
+        if user_data
+          serverConfig.user_data = "#{user_data}\\n#{cloneText}"
+        else
+          serverConfig.user_data = cloneText
+
+
+    { content, err } = jsonToYaml JSON.stringify stackTemplate
+
+    if err
+      return new kd.NotificationView 'Unable to update stack template preview'
+
+    @createStackPreviewFromYaml content
+
+
+  createStackPreviewFromYaml: (content) ->
+
+    linesMarkup   = ''
+    codeMarkup    = ''
+    templateLines = content.split '\n'
+
+    for index in [1...templateLines.length]
+      linesMarkup += "
#{index}
"
+
+    for line  in templateLines
+      codeMarkup  += "

#{line}

"
+
+    @stackContent.destroySubViews()
+    @stackContent.addSubView new kd.CustomHTMLView
+      cssClass : 'lines'
+      partial  : "#{linesMarkup}"
+
+    @stackContent.addSubView new kd.CustomHTMLView
+      cssClass : 'code'
+      partial  : "#{codeMarkup}"
+

   pistachio: ->


Workspace: Added showShareView method.

Workspace: Added showShareView method.

--- client/app/CommonViews/workspace/collaborativeworkspace.coffee
+++ client/app/CommonViews/workspace/collaborativeworkspace.coffee
@@ -219,6 +219,29 @@
     @joinSession sessionKey
     modal.destroy()

+  showShareView: (panel, workspace, event) ->
+    button   = KD.instances[event.currentTarget.id]
+    shareUrl = "#{location.origin}/Develop/#{@getOptions().name}?sessionKey=#{@sessionKey}"
+    new JContextMenu
+      cssClass    : "activity-share-popup"
+      type        : "activity-share"
+      delegate    : this
+      x           : button.getX() + 25
+      y           : button.getY() + 25
+      arrow       :
+        placement : "top"
+        margin    : -10
+      lazyLoad    : yes
+    , customView  : new SharePopup {
+        url       : shareUrl
+        shortenURL: false
+        twitter   :
+          text    : "Learn, code and deploy together to powerful VMs - @koding, the dev environment from the future! #{shareUrl}"
+        linkedin  :
+          title   : "Join me @koding!"
+          text    : "Learn, code and deploy together to powerful VMs - @koding, the dev environment from the future! #{shareUrl}"
+      }
+
   createUserListContainer: ->
     @container.addSubView @userListContainer = new KDView
       cssClass : "user-list"


Workspace: Fixed starting a new session from session is not available view.

--- client/app/CommonViews/workspace/collaborativeworkspace.coffee
+++ client/app/CommonViews/workspace/collaborativeworkspace.coffee
@@ -137,7 +137,8 @@
     notValid.addSubView new KDButtonView
       cssClass : "cupid-green"
       title    : "Start New Session"
-      callback : @bound "startNewSession"
+      callback : =>
+        @startNewSession()

     @container.addSubView notValid
     @loader.hide()


FinderTreeController: fix duplicateFile issue [fixes #79889828]

--- client/finder/filetree/controllers/findertreecontroller.coffee
+++ client/finder/filetree/controllers/findertreecontroller.coffee
@@ -338,18 +338,14 @@
     results = nodes.map (node) =>
       sourceItem = node.getData()
       targetItem = @nodes[sourceItem.parentPath].getData()
-      FSItem.copy(sourceItem, targetItem).then ->
+      sourceItem.copy(targetItem.path).then ->
         duplicatedNodes.push node

     Promise.all(results).then =>
       @notify "#{duplicatedNodes.length} item#{if duplicatedNodes.length > 1 then 's' else ''} duplicated!", "success"
-      parentNodes = []
-      duplicatedNodes.forEach (node)=>
-        parentNode = @nodes[node.getData().parentPath]
-        parentNodes.push parentNode unless parentNode in parentNodes
-      @refreshFolder parentNode for parentNode in parentNodes

     .catch (err) =>
+      warn "Duplicate file failed with error:", err
       @notify null, null, err

     .nodeify callback


Add selected class to selected code switch element

--- src/coffee/executr.coffee
+++ src/coffee/executr.coffee
@@ -201,6 +201,7 @@

   $('.executr-switch').click ->
     $this = $(@)
+    $this.addClass('selected').siblings().removeClass('selected')
     codeType = $this.attr('data-code-type')
     codeSelectors.trigger 'executrSwitchType', codeType


Fixed bug in add comment view when pressing enter the wrong form content is sent.

--- core/app/backbone/views/fact_relations/add_comment_view.coffee
+++ core/app/backbone/views/fact_relations/add_comment_view.coffee
@@ -13,6 +13,7 @@

   parseKeyDown: (e) =>
     code = e.keyCode || e.which
+    @updateModel()
     @addDefaultModel() if code is 13

   initializeModel: ->


was breaking the url by removing query/hash

--- src/components/app.coffee
+++ src/components/app.coffee
@@ -138,7 +138,7 @@

     window.stuff = @
     document.title = @props.title ? defaultTitle
-    history.replaceState obj, '', window.location.pathname
+    history.replaceState obj, '', window.location

     unless @props.isUser == false
       console.log 'isUser', @props.isUser


Use the user factory to make the fakeUser

--- test/javascripts/integration/edit_paper_test.js.coffee
+++ test/javascripts/integration/edit_paper_test.js.coffee
@@ -7,7 +7,7 @@
     authorId = 19932347

     dashboard =
-      users: [fakeUser.user]
+      users: [fakeUser]
       affiliations: []
       lite_papers: [
         id: paperId
@@ -25,11 +25,11 @@
           type: "FigureTask"
         title: "Upload Figures"
         lite_paper_id: paperId
-        assignee_id: fakeUser.user.id
+        assignee_id: fakeUser.id
       ]
       dashboards: [
         id: 1
-        user_id: fakeUser.user.id
+        user_id: fakeUser.id
         submission_ids: [paperId]
         assigned_task_ids: [figureTaskId]
         administered_journals: []
@@ -58,7 +58,7 @@
         paper_id: paperId
         lite_paper_id: paperId
         assignee_ids: []
-        assignee_id: fakeUser.user.id
+        assignee_id: fakeUser.id
       ]
       lite_papers: [
         id: paperId
@@ -67,7 +67,7 @@
         short_title: "Paper"
         submitted: false
       ]
-      users: [fakeUser.user]
+      users: [fakeUser]
       affiliations: []
       figures: []
       author_groups: [
@@ -126,7 +126,7 @@
         figure_ids: []
         author_group_ids: [41]
         supporting_information_file_ids: []
-        assignee_ids: [fakeUser.user.id]
+        assignee_ids: [fakeUser.id]
         editor_ids: []
         reviewer_ids: []
         tasks: [
@@ -143,7 +143,7 @@
         short_title: "Paper"
         submitted: false
       ]
-      users: [fakeUser.user]
+      users: [fakeUser]
       affiliations: []
       task:
         id: figureTaskId
@@ -157,7 +157,7 @@
         paper_id: paperId
         lite_paper_id: paperId
         assignee_ids: []
-        assignee_id: fakeUser.user.id
+        assignee_id: fakeUser.id

     server.respondWith 'GET', "/dashboards", [
       200, {"Content-Type": "application/json"}, JSON.stringify dashboard


make practice component reload when a new practice is loaded into store, addresses number 2 of 2 issues in comment on #71

--- src/components/index.cjsx
+++ src/components/index.cjsx
@@ -76,6 +76,15 @@
 SinglePractice = React.createClass
   mixins: [Router.State, LoadableMixin]

+  componentWillMount: ->
+    CourseStore.on('practice.loaded', @update)
+
+  componentWillUnmount: ->
+    CourseStore.off('practice.loaded', @update)
+
+  getInitialState: ->
+      taskId: CourseStore.getPracticeId(@getId())
+
   getFlux: ->
     store: CourseStore
     actions: CourseActions
@@ -83,9 +92,13 @@
   getId: ->
     @getParams().courseId

+  update: ->
+    @setState({
+      taskId:  CourseStore.getPracticeId(@getId())
+    })
+
   renderLoaded: ->
-    taskId = CourseStore.getPracticeId(@getId())
-    @transferPropsTo(<Task key={taskId} id={taskId} />)
+    @transferPropsTo(<Task key={@state.taskId} id={@state.taskId} />)

 TaskResult = React.createClass


Fix application page link for now

Fix application page link for now

--- client/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
+++ client/app/Applications/Apps.kdapplication/Views/AppsListItemView.coffee
@@ -34,33 +34,6 @@
     @setTemplate @pistachio()
     @template.update()

-  #   if @getData().installedA
-  #     @alreadyInstalledText()
-  #   else
-  #     @createInstallButton()
-
-  # createInstallButton:->
-  #   app = @getData()
-
-  #   @installButton.destroy() if @installButton?
-  #   @installButton = new KDButtonView
-  #     title : "Install"
-  #     icon  : no
-  #     callback: =>
-  #       list = @getDelegate()
-  #       list.propagateEvent KDEventType : "AppWantsToExpand", app
-
-  #   @addSubView @installButton, '.button-container'
-
-  # alreadyInstalledText:->
-
-  #   @installButton.destroy() if @installButton?
-  #   @installButton = new KDButtonView
-  #     title     : "Installed"
-  #     icon      : no
-  #     disabled  : yes
-  #   @addSubView @installButton, '.button-container'
-
   pistachio:->
     """

@@ -82,6 +55,6 @@

{a[href="#"]{#(title)}}

{{@utils.shortenText #(body)}}

-      Application Page →
+      Application Page →

     """


Close sidebar when user selects print option (#3922)

--- angular/core/components/thread_page/context_panel/context_panel.coffee
+++ angular/core/components/thread_page/context_panel/context_panel.coffee
@@ -33,6 +33,7 @@
       ModalService.open MoveThreadForm, discussion: => $scope.discussion

     $scope.requestPagePrinted = ->
+      $rootScope.$broadcast('toggleSidebar', false)
       if $scope.discussion.allEventsLoaded()
         $timeout -> $window.print()
       else


Add JSON.stringify to #getTrip() method

Add JSON.stringify to #getTrip() method

--- test/util/data.coffee
+++ test/util/data.coffee
@@ -14,7 +14,8 @@
 rand = (max, min) -> Math.floor (Math.random() * (max - min + 1) + min)

 exports.getTypes = -> Object.keys(data)
-exports.getTrip = (vendor, status) -> data.turer[turer[vendor or 'DNT'][status or 'Offentlig']]
+exports.getTrip = (vendor, status) ->
+  JSON.parse(JSON.stringify(data.turer[turer[vendor or 'DNT'][status or 'Offentlig']]))
 exports.new = (type, i) -> generator.gen (i or 0), type: type
 exports.get = (type, all, n) ->
   return data[type] if all


slight cleanup

slight cleanup

--- lib/editor/result.coffee
+++ lib/editor/result.coffee
@@ -77,13 +77,13 @@
       class: 'ink-underlay'
       invalidate: 'never'
     @expDecoration = @editor.decorateMarker @expMarker, mark
-    # setTimeout (=> @updateWidth()), 50
+    setTimeout (=> @updateWidth()), 50

   collapseView: () ->
     @expanded = false
     @expMarker?.destroy()
     @decorateMarker()
-    # setTimeout (=> @updateWidth()), 50
+    setTimeout (=> @updateWidth()), 50

   createView: (opts) ->
     {content, fade, loading} = opts
@@ -133,29 +133,23 @@
       when 'block' then mark.type = 'block'; mark.position = 'after'
     @decoration = @editor.decorateMarker @marker, mark
     if @type == 'inline'
-      # setTimeout (=> @updateWidth()), 50
       ed = @editor
-      el = @editor.editorElement
       if not results.hasOwnProperty(ed.id)
         results[ed.id] = true
         listener = debounce((->
           window.requestAnimationFrame listener
-          # ed.element.component.requestAnimationFrame ->
           res = ed.findMarkers().filter((m) -> m.result?).map((m) -> m.result)
           # reads
           rect = null
           fastdom.measure ->
-            rect = el.getBoundingClientRect()
+            rect = ed.editorElement.getBoundingClientRect()
             res.forEach (m) -> m.readOffsetLeft()
           # writes
           fastdom.mutate ->
             res.forEach (m) -> m.updateWidth(rect)
           ), 100)
-
-        # ed.presenter.onDidUpdateState ->
-        #   console.log "didUpdateState"
-
-        resizer.listenTo el, listener
+        resizer.listenTo ed.editorElement, listener
+        # ed.presenter.onDidUpdateState -> listener

   initMarker: ->
     @marker = @editor.markBufferRange @lineRange(@start, @end)


Make leaf nodes non-clickable

Make leaf nodes non-clickable

--- src/d3-flame-graph.coffee
+++ src/d3-flame-graph.coffee
@@ -282,6 +282,9 @@
         .selectAll('.node')
           .on 'mouseover', @tip.show
           .on 'mouseout', @tip.hide
+        .selectAll('.label')
+          .on 'mouseover', () -> console.log('x'); d3.event.preventDefault()
+          .on 'mouseout', () -> console.log('y'); d3.event.preventDefault()
       @

     _renderAncestors: () ->
@@ -322,11 +325,13 @@
       @

     _enableNavigation: () ->
+      clickable = (d) => Math.round(@width() - @x(d.dx)) > 0 and d.children?.length
       @container
         .selectAll('.node')
+        .classed('clickable', (d) => clickable(d))
         .on 'click', (d) =>
           @tip.hide()
-          @zoom(d) if Math.round(@width() - @x(d.dx)) > 0
+          @zoom(d) if clickable(d)
       @container
         .selectAll('.ancestor')
         .on 'click', (d, idx) => @tip.hide(); @zoom(@_ancestors[idx])


Update master

Update master

--- example.coffee
+++ example.coffee
@@ -20,7 +20,7 @@
   --testsuite=dir      run regression tests from dir
   --doctest            run doctest on myself"

-docopt = require('./docopt').docopt
+{docopt} = require('docopt')

 options = docopt(doc, void 0, true, '1.0.0')  # parse options based on doc above


Undasherize section headings

Undasherize section headings

--- lib/general-panel.coffee
+++ lib/general-panel.coffee
@@ -27,8 +27,7 @@
     return if _.isEmpty(settings)

     @append $$ ->
-      @section class: 'bordered', =>
-        @h1 class: 'section-heading', "#{_.uncamelcase(namespace)} settings"
+        @h1 class: 'section-heading', "#{_.undasherize(_.uncamelcase(namespace))} Settings"
         for name in _.keys(settings).sort()
           appendSetting.call(this, namespace, name, settings[name])


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -30,7 +30,7 @@

   5.times ->
     player.update()
-    equals player.I.alpha
+    equals player.I.alpha, 0.9

 module()


Fixed integration tests

Fixed integration tests

--- test/integration.coffee
+++ test/integration.coffee
@@ -26,12 +26,12 @@
 doubleOpen = (c1, c2, docName, type, callback) ->
   doc1 = doc2 = undefined

-  c1.open docName, type, (d, error) ->
+  c1.open docName, type, (error, d) ->
     doc1 = d
     assert.ok doc1
     callback doc1, doc2 if doc1 && doc2

-  c2.open docName, type, (d, error) ->
+  c2.open docName, type, (error, d) ->
     doc2 = d
     assert.ok doc2
     callback doc1, doc2 if doc1 && doc2
@@ -94,7 +94,7 @@
     host = "http://127.0.0.1:#{@port}/sjs"

     more = ->
-      client.open nextDocName(), 'text', host, (doc, error) =>
+      client.open nextDocName(), 'text', host, (error, doc) =>
         nowOrLater ->
           doc.close()


adding in homework and exercise summary specs

--- test/all-tests.coffee
+++ test/all-tests.coffee
@@ -1,6 +1,9 @@
 {expect} = require 'chai'

+require './components/homework/homework-plan.spec'
+require './components/homework/exercise-summary.spec'
 require './components/reading-plan.spec'
+
 require './components/task.spec'
 require './components/practice.spec'
 require './components/learning-guide.spec'


Tweak rate limiting fallback numbers to be more conservative

--- src/desktop/config.coffee
+++ src/desktop/config.coffee
@@ -104,7 +104,7 @@
   BURST_REQUEST_BLOCK_FOR: 180,       # The number of seconds to block bursts after their limit is reached
   REQUEST_EXPIRE: 60                  # The period in seconds to measure rate limits
   REQUEST_LIMIT: 300                  # The maximum number of requests allowed by 1 IP in REQUEST_EXPOR
-  REQUEST_PER_INSTANCE_FALLBACK: 100  # If Redis fails, fall back to this amount of requests per force instance for rate limiting
+  REQUEST_PER_INSTANCE_FALLBACK: 60   # If Redis fails, fall back to this amount of requests per force instance for rate limiting
   REQUEST_PER_INSTANCE_LIMIT: 100     # The process level rate limit. If a single instance of force hits this, it falls back to a performant in memory rate limiting strategy
   REQUEST_PER_INSTANCE_EXPIRE: 300    # The amount of time the requesting ip has to wait before the in-memory rate limit is disabled
   ENABLE_RATE_LIMITING: false         # Enable/disable the entire rate limiting middleware


Corrigindo a exibição de múltiplos erros

--- lib/assets/javascripts/flash_msg.js.coffee
+++ lib/assets/javascripts/flash_msg.js.coffee
@@ -1,7 +1,20 @@
 $.fn.flashMsg = (obj) ->
-  $elem = $('

')
-  for errType, errText of obj
-    $elem.addClass('alert-' + errType)
-    $elem.fadeIn().find('p').text(errText)
+  $wrap = $('

')
+  $ul = $wrap.find('ul')
+
   $(this).find('div.alert').remove()
-  $(this).prepend($elem)
+
+  for errType, err of obj
+    errType = errType.replace('errors', 'error')
+    $wrap.addClass('alert-' + errType)
+    if typeof err == 'object'
+      $wrap.prepend('
Ops, aconteceu alguns erros no seu cadastro:
')
+      for field, errMsgs of err
+        $list = $('
').html("#{field}: #{errMsgs.join(', ')}")
+        $ul.append($list)
+    else
+      $ul.append($('
').text(err))
+
+  $(this).prepend($wrap)
+
+  $wrap.fadeIn()


Remove unused variable

Remove unused variable

--- src/directory-view.coffee
+++ src/directory-view.coffee
@@ -21,7 +21,6 @@
   initialize: ({@directory, isExpanded, @project, parent} = {}) ->
     @expand() if isExpanded
     @disclosureArrow.on 'click', => @toggleExpansion()
-    repositoryPath = parent?.getPath() or @directory.getPath()
     @directoryName.addClass('ignored') if @project.repo.isPathIgnored(@directory.getPath())

   getPath: ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/expirable.coffee
+++ test/expirable.coffee
@@ -27,6 +27,6 @@
   5.times ->
     obj.update()

-  equals obj.I.alpha, 0.8 * (obj.I.age - 1 / obj.I.duration)
+  equals obj.I.alpha, 0.8 * (obj.I.age / obj.I.duration)

 module()


using slightly different syntax to do the same thing

--- jsapp/xlform/src/model.base.coffee
+++ jsapp/xlform/src/model.base.coffee
@@ -137,12 +137,11 @@
       #   @on "change:list", (rd, val, ctxt)=>
       #     @_parent.trigger "change", @key, val, ctxt

-      mark_unsaved_attributes= ["name", "label", "hint", "required",
-                                'calculation', "default", "appearance",
-                                "constraint_message"]  # , "constraint", "relevant"]
-      if @key in mark_unsaved_attributes
-        on_change_callback= (changes)=>
-          @getSurvey().trigger('change', changes)
-        @on('change', on_change_callback)
+      # when attributes change, register changes with parent survey
+      if @key in ["name", "label", "hint", "required",
+                  "calculation", "default", "appearance",
+                  "constraint_message"]
+        @on "change", (changes)=>
+          @getSurvey().trigger "change", changes

   base