Revert ":green_heart: Try fixing path issues on linux"

This reverts commit adb8b2b1af88fcd53bd9d13cf9630184029c6ca1.

--- spec/csv-editor-spec.coffee
+++ spec/csv-editor-spec.coffee
@@ -15,7 +15,7 @@
 CHANGE_TIMEOUT = 400

 describe "CSVEditor", ->
-  [csvEditor, csvEditorElement, csvDest, absCsvDest, projectPath, absProjectPath, tableEditor, tableEditorElement, openSpy, destroySpy, savedContent, spy, tableEditPackage, nextAnimationFrame] = []
+  [csvEditor, csvEditorElement, csvDest, projectPath, tableEditor, tableEditorElement, openSpy, destroySpy, savedContent, spy, tableEditPackage, nextAnimationFrame] = []

   sleep = (ms) ->
     start = new Date
@@ -27,14 +27,13 @@
     projectPath = temp.mkdirSync('tablr-project')
     atom.project.setPaths([projectPath])

-    absProjectPath = atom.project.resolvePath('.')
+    projectPath = atom.project.resolvePath('.')
     csvDest = path.join(projectPath, fixtureName)
-    absCsvDest = path.join(absProjectPath, fixtureName)

     fs.writeFileSync(csvDest, fs.readFileSync(csvFixture).toString().replace(/\s+$/g,''))

     if settings?
-      tableEditPackage.csvConfig.set(absCsvDest, 'options',  settings)
+      tableEditPackage.csvConfig.set(csvDest, 'options',  settings)

     waitsForPromise ->
       atom.workspace.open(fixtureName).then (t) ->
@@ -240,7 +239,7 @@
                 ])

     describe 'when the file is moved', ->
-      [spy, configSpy, newPath, spyTitle] = []
+      [spy, newPath, spyTitle] = []

       beforeEach ->
         jasmine.useRealClock?()
@@ -251,7 +250,6 @@
           spyTitle = jasmine.createSpy('did-change-title')
           csvEditor.onDidChangePath(spy)
           csvEditor.onDidChangeTitle(spyTitle)
-          spyOn(tableEditPackage.csvConfig, 'move')

           newPath = path.join(projectPath, 'new-file.csv')
           fsp.removeSync(newPath)
@@ -260,12 +258,12 @@
         waitsFor 'change path callback called', -> spy.callCount > 0

       it 'detects the change in path', ->
-        relativePath = atom.project.relativizePath(newPath)
-        expect(atom.project.relativizePath(spy.mostRecentCall.args[0])).toEqual(relativePath)
-        expect(atom.project.relativizePath(csvEditor.getPath())).toEqual(relativePath)
+        expect(spy).toHaveBeenCalledWith(newPath)
+        expect(csvEditor.getPath()).toEqual(newPath)

       it 'changes the key path to the settings', ->
-        expect(tableEditPackage.csvConfig.move).toHaveBeenCalled()
+        expect(tableEditPackage.csvConfig.get(csvDest)).toBeUndefined()
+        expect(tableEditPackage.csvConfig.get(newPath)).toBeDefined()

       it 'dispatches a did-change-title event', ->
         expect(spyTitle).toHaveBeenCalledWith('new-file.csv')
@@ -297,7 +295,7 @@
           waitsFor 'delete callback called', -> deleteSpy.callCount > 0

         it "retains its path and reports the buffer as modified", ->
-          expect(csvEditor.getPath()).toBe(absCsvDest)
+          expect(csvEditor.getPath()).toBe(csvDest)
           expect(csvEditor.isModified()).toBeTruthy()

       describe 'when the table is not modified', ->
@@ -306,7 +304,7 @@
           waitsFor 'delete callback called', -> deleteSpy.callCount > 0

         it "retains its path and reports the buffer as not modified", ->
-          expect(csvEditor.getPath()).toBe(absCsvDest)
+          expect(csvEditor.getPath()).toBe(csvDest)
           expect(csvEditor.isModified()).toBeFalsy()

       describe 'when resaved', ->
@@ -836,7 +834,7 @@
       runs ->
         expect(csvEditor.serialize()).toEqual({
           deserializer: 'CSVEditor'
-          filePath: absCsvDest
+          filePath: csvDest
           options: csvEditor.options
           choice: undefined
         })
@@ -848,7 +846,7 @@
         runs ->
           expect(csvEditor.serialize()).toEqual({
             deserializer: 'CSVEditor'
-            filePath: absCsvDest
+            filePath: csvDest
             options: csvEditor.options
             choice: 'TableEditor'
             layout:
@@ -873,7 +871,7 @@

           expect(csvEditor.serialize()).toEqual({
             deserializer: 'CSVEditor'
-            filePath: absCsvDest
+            filePath: csvDest
             options: csvEditor.options
             choice: 'TableEditor'
             layout:
@@ -898,7 +896,7 @@

           expect(csvEditor.serialize()).toEqual({
             deserializer: 'CSVEditor'
-            filePath: absCsvDest
+            filePath: csvDest
             options: csvEditor.options
             choice: 'TableEditor'
             editor: csvEditor.editor.serialize()


Fixed type error for bare ambiguous types and improved style of type errors

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -3225,6 +3225,7 @@
   if bare and _notEmpty reducedConstraints
     ctx.extendSubstitution substitutionFail
       message: "Ambiguous class constraints: #{map prettyPrintForError, reducedConstraints}"
+      conflicts: reducedConstraints
     return "null";
   ctx.updateClassParams()
   # TODO: what about the class dictionaries order?


Fixed malformed slightly and added a test for multiple instances

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -416,18 +416,19 @@

 expressionCompile = (ctx, expression) ->
   throw new Error "invalid expressionCompile args" unless ctx instanceof Context and expression
-  (if isAtom expression
-    atomCompile
-  else if isTuple expression
-    tupleCompile
-  else if isSeq expression
-    seqCompile
-  else if isCall expression
-    callCompile
-  else
-    # log "Not handled", expression
+  compileFn =
+    if isAtom expression
+      atomCompile
+    else if isTuple expression
+      tupleCompile
+    else if isSeq expression
+      seqCompile
+    else if isCall expression
+      callCompile
+  if not compileFn
     malformed expression, 'not a valid expression'
-  )? ctx, expression
+  else
+    compileFn ctx, expression

 # -- This was used to use compiled results from some parent macro while
 #    compiling as something else
@@ -459,9 +460,7 @@
   op = _operator call
   op.label = 'keyword'
   expanded = ctx.macros()[op.symbol] ctx, call
-  if not isWellformed expanded
-    'malformed'
-  else if isTranslated expanded
+  if isTranslated expanded
     expanded
   else
     expressionCompile ctx, expanded
@@ -481,7 +480,7 @@
   labeledArgs = labeledToMap args

   if tagFreeLabels args
-    return malformed 'labels without values inside call', call
+    return malformed call, 'labels without values inside call'

   paramNames = ctx.arity operator.symbol
   if not paramNames
@@ -676,7 +675,7 @@
         requiredElems++

     if hasSplat and requiredElems is 0
-      return malformed 'Matching with splat requires at least one element name', form
+      return malformed form, 'Matching with splat requires at least one element name'

     compiledArgs = (for elem, i in elems
       [lhs, rhs] =
@@ -782,9 +781,9 @@
       op.label = 'operator'
       new ClassContraint op.symbol, typeCompile args[0] # TODO: support multiparameter type classes
     else
-      malformed 'Class name required in a constraint', expression
-  else
-    malformed 'Class constraint expected', expression
+      malformed expression, 'Class name required in a constraint'
+  else
+    malformed expression, 'Class constraint expected'

 typeConstraintsCompile = (expressions) ->
   map typeConstraintCompile, expressions
@@ -844,6 +843,7 @@
         ctx.declare name
     #log "exiting pattern early", pattern, "for", ctx.shouldDefer()
     return {}
+

   # Properly bind types according to the pattern
   if pattern.tea
@@ -1025,6 +1025,8 @@
         map (syntaxNameAs '', 'param'), filterAst isUsedParam, expression
       map labelUsedParams, if body then join [body], wheres else wheres

+      if body and not isWellformed body
+        return 'malformed'

       assignCompile ctx, call,
         if ctx.shouldDefer()
@@ -1032,7 +1034,6 @@
         else
           # Typing
           if body and not body.tea
-            #log body
             throw new Error "Body not typed"
           call.tea =
             if body
@@ -1049,11 +1050,12 @@
             type: call.tea
             params: paramNames
             body: (join compiledWheres, [(jsReturn compiledBody)]))
-          # (jsCall "λ#{paramNames.length}", [
-          #   (jsFunction
-          #     name: (ctx.definitionName() if ctx.isAtSimpleDefinition())
-          #     params: paramNames
-          #     body: (join compiledWheres, [(jsReturn compiledBody)]))])
+            # (jsCall "λ#{paramNames.length}", [
+            #   (jsFunction
+            #     name: (ctx.definitionName() if ctx.isAtSimpleDefinition())
+            #     params: paramNames
+            #     body: (join compiledWheres, [(jsReturn compiledBody)]))])
+
   # data
   #   listing or
   #     pair
@@ -1173,7 +1175,7 @@

     [instanceConstraint, defs...] = _arguments call
     if not isCall call
-      return malformed 'Instance requires a single class constraint', call
+      return malformed call, 'Instance requires a single class constraint'
     else
       instanceType = typeConstraintCompile instanceConstraint
     if defs.length % 2 == 0
@@ -1242,9 +1244,9 @@
     [subject, cases...] = _arguments call
     varNames = []
     if not subject
-      return malformed 'match `subject` missing', call
+      return malformed call, 'match `subject` missing'
     if cases.length % 2 != 0
-      return malformed 'match missing result for last pattern', call
+      return malformed call, 'match missing result for last pattern'
     subjectCompiled = termCompile ctx, subject

     # To make sure all results have the same type
@@ -1284,6 +1286,7 @@

     callTyping ctx, call
     assignCompile ctx, call, (jsBinary "===", compiledA, compiledB)
+

 # Creates the condition and body of a branch inside match macro
@@ -1448,7 +1451,7 @@
 malformed = (expression, message) ->
   # TODO support multiple malformations
   expression.malformed = message
-  message
+  (jsMalformed message)

 isWellformed = (expression) ->
   if expression.malformed
@@ -1485,7 +1488,7 @@
   if seq
     constraints = _terms seq
     if (not isSeq seq)
-      malformed 'Constraints must be specified in a list', seq
+      malformed seq, 'Constraints must be specified in a list'
     else
       constrainTypes = filter ((t) -> t instanceof ClassContraint),
         typeConstraintsCompile ctx, constraints
@@ -1676,6 +1679,8 @@
 irDefinition = (type, expression) ->
   {ir: irDefinitionTranslate, type, expression}

+# TODO: This must always wrap a function, because if the expression
+#       is not a function then it can't need type class dictionaries
 irDefinitionTranslate = (ctx, {type, expression}) ->
   finalType = substitute ctx.substitution, type
   # deferConstraints ctx, ctx.boundTypeVariables(), (findFree finalType)
@@ -1740,10 +1745,9 @@
     method

 instanceDictFor = (ctx, constraint) ->
-  {className, type} = constraint
-  for {name, type} in (ctx.classNamed className).instances
+  for {name, type} in (ctx.classNamed constraint.className).instances
     # TODO: support lookup of composite types, by traversing left depth-first
-    if type.name is type.name
+    if type.type.type.name is constraint.type.name
       return validIdentifier name
   throw new Error "no instance for #{printType constraint}"

@@ -1958,6 +1962,12 @@
 jsFunctionTranslate = ({name, params, body}) ->
   "function #{name or ''}(#{listOf params}){#{blockOfLines body}}"

+
+jsMalformed = (message) ->
+  {js: jsMalformedTranslate, message: message}
+
+jsMalformedTranslate = ({message}) ->
+  message

 jsNew = (classFun, args) ->
   {js: jsNewTranslate, classFun, args}
@@ -3094,9 +3104,8 @@
   collapse toHtml astize tokenize string

 compileTopLevel = (source) ->
-  ast = astize tokenize "(#{source})", -1
-  compiled = topLevel (ctx = new Context), ast
-  jsWithAstTypes ctx, ast, compiled
+  {js, ast, ctx} = compileToJs topLevel, "(#{source})", -1
+  {js, ast, types: typeEnumaration ctx}

 compileTopLevelAndExpression = (source) ->
   topLevelAndExpression source
@@ -3104,31 +3113,32 @@
 topLevelAndExpression = (source) ->
   ast = astize tokenize "(#{source})", -1
   [terms..., expression] = _terms ast
-  ctx = new Context
-  compiledDefinitions = compileDefinitionList ctx, terms
-  compiledExpression = compileExpression ctx, expression
+  {ctx} = compiledDefinitions = compileAstToJs definitionList, pairs terms
+  compiledExpression = compileCtxAstToJs topLevelExpression, ctx, expression
   types: ctx._scope()
   subs: filterMap ((name) -> name is 'could not unify'), ctx.substitution
   ast: ast
-  compiled: library + compiledDefinitions + compiledExpression
-
-jsWithAstTypes = (ctx, ast, js) ->
-  types = values mapMap _type, ctx._scope()
-  {js, ast, types}
-
-compileDefinitionList = (ctx, terms) ->
-  ir = definitionList ctx, pairs terms
+  compiled: library + compiledDefinitions.js + compiledExpression.js
+
+typeEnumaration = (ctx) ->
+  values mapMap _type, ctx._scope()
+
+compileToJs = (compileFn, source, offset = 0) ->
+  ast = astize tokenize source, offset
+  compileAstToJs compileFn, ast
+
+compileAstToJs = (compileFn, ast) ->
+  ctx = new Context
+  compileCtxAstToJs compileFn, ctx, ast
+
+compileCtxAstToJs = (compileFn, ctx, ast) ->
+  ir = compileFn ctx, ast
   jsIr = translateIr ctx, ir
-  translateStatementsToJs jsIr
-
-compileDefinitions = (source) ->
-  ast = astize tokenize "(#{source})", -1
-  compileDefinitionList (new Context), _terms ast
-
-compileExpression = (ctx, expression) ->
-  expressionIr = topLevelExpression ctx, expression
-  expressionJsIr = translateIr ctx, expressionIr
-  compiledExpression = translateToJs expressionJsIr
+  js = (if Array.isArray jsIr
+      translateStatementsToJs
+    else
+      translateToJs) jsIr
+  {ctx, ast, js}

 astizeList = (source) ->
   parentize astize tokenize "(#{source})", -1
@@ -3346,6 +3356,20 @@
       (read (show string)))"""
   """(test "Hello")""", "Hello"

+  'multiple instances'
+  """Show (class [a]
+  show (fn [x] (: (Fn a String))))
+
+  show-string (instance (Show String)
+    show (fn [x] x))
+
+  show-bool (instance (Show Bool)
+    show (fn [x]
+      (match x
+        True "True"
+        False "False")))"""
+  "(show False)", "False"
+
 ]

 testNamed = (givenName) ->


Fixed multiple branches in matching on tuples

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -228,7 +228,9 @@
     @_assignTos.push compiled

   assignTo: ->
-    @_assignTos[@_assignTos.length - 1]
+    rhs = @_assignTos[@_assignTos.length - 1]
+    cacheName = @_translationCache()?[0]
+    if rhs then cacheName or rhs

   resetAssignTo: ->
     @_assignTos.pop()
@@ -378,7 +380,7 @@
       # Replace assign to with cache name
       cacheName = @newJsVariable()
       cache = [cacheName, @assignTo()]
-      @_assignTos[@_assignTos.length - 1] = cacheName
+      # @_assignTos[@_assignTos.length - 1] = cacheName #instead change assignTo
       @cacheScopes[@cacheScopes.length - 1][0] = cache

   _translationCache: ->
@@ -828,7 +830,7 @@

 patternCompile = (ctx, pattern, matched, translatedMatched) ->

-  ctx.setAssignTo translatedMatched
+  ctx.setAssignTo ctx._translationCache() or translatedMatched
   # caching can occur while compiling the pattern
   # precs are {cond}s and {cache}s, sorted in order they need to be executed
   {precs, assigns} = expressionCompile ctx, pattern
@@ -1175,7 +1177,7 @@
         ctx.addClass name, classConstraint, superClasses, declarations
         declareMethods ctx, classConstraint, declarations

-        translateDict name, keysOfMap declarations
+        translateDict name, (keysOfMap declarations), superClasses
     else
       'malformed'

@@ -1202,6 +1204,7 @@
   for name, {arity, type} of values methodDeclarations
     type = quantifyUnbound ctx, addConstraints type.type, [classConstraint]
     ctx.declare name, {arity, type}
+  return

 ms.instance = ms_instance = (ctx, call) ->
     hasName = requireName ctx, 'Name required to declare a new instance'
@@ -1220,24 +1223,20 @@

     # TODO: defer if class does not exist
     className = instanceType.className
-
-    # TODO: I should check that methods correspond to the class declaration
+    classDefinition = ctx.classNamed className
+
+    # TODO: defer if super class instances don't exist yet
+    superClassInstances = findSuperClassInstances ctx, instanceType.type, classDefinition

     if hasName
       instanceName = ctx.definitionName()

       ctx.newScope()
-      # TODO: I need to unify the type of each method with their declared types
-      #       from the type class
-      # ctx.bindTypeVariables keysOfMap findFree instanceType.type
-      # TODO: I need to run them as explicitly typed and not only I need to add
-      #       the binded variables I also have to add their constraints
       freshConstrains = assignMethodTypes ctx, instanceName,
-        (ctx.classNamed className), instanceType, constraints
+        classDefinition, instanceType, constraints
       definitions = pairs wheres
       methodsDeclarations = definitionList ctx,
         (prefixWithInstanceName definitions, instanceName)
-      declarations = ctx.currentDeclarations()
       ctx.closeScope()

       methods = map (({rhs}) -> rhs), methodsDeclarations
@@ -1252,10 +1251,11 @@
       ##   malformed 'instance overlaps with another', instance
       ## else
       ctx.addInstance instanceName, instance
+
       # """var #{instanceName} = new #{className}(#{listOf methods});"""
       (jsVarDeclaration (validIdentifier instanceName),
         (irDefinition (new Constrained freshConstrains, (tupleOfTypes methodTypes).type),
-          (jsNew className, methods)))
+          (jsNew className, (join superClassInstances, methods))))
     else
       'malformed'

@@ -1290,6 +1290,13 @@

 instancePrefix = (instanceName, methodName) ->
   "#{instanceName}_#{methodName}"
+
+findSuperClassInstances = (ctx, instanceType, classDefinition) ->
+  toConstraint = (superName) ->
+    new ClassContraint superName, instanceType
+  superConstraints = map toConstraint, classDefinition.supers
+  instanceDictFor ctx, constraint for constraint in superConstraints
+

   # TODO:
   # For now support the simplest function macros, just compiling down to source
@@ -1354,14 +1361,14 @@
       varList varNames
       compiledCases])

-ms['='] = ms_eq = (ctx, call) ->
-    [a, b] = _arguments call
-    operatorCompile ctx, call
-    compiledA = termCompile ctx, a
-    compiledB = termCompile ctx, b
-
-    callTyping ctx, call
-    assignCompile ctx, call, (jsBinary "===", compiledA, compiledB)
+# ms['='] = ms_eq = (ctx, call) ->
+#     [a, b] = _arguments call
+#     operatorCompile ctx, call
+#     compiledA = termCompile ctx, a
+#     compiledB = termCompile ctx, b
+
+#     callTyping ctx, call
+#     assignCompile ctx, call, (jsBinary "===", compiledA, compiledB)

 builtInMacros = ms

@@ -1526,12 +1533,13 @@
           return no
     yes

-translateDict = (dictName, fieldNames) ->
-  paramAssigns = fieldNames.map (name) ->
+translateDict = (dictName, fieldNames, additionalFields = []) ->
+  allFieldNames = (join additionalFields, fieldNames)
+  paramAssigns = allFieldNames.map (name) ->
     (jsAssignStatement (jsAccess "this", name), (validIdentifier name))
   constrFn = (jsFunction
     name: dictName
-    params: (map validIdentifier, fieldNames)
+    params: (map validIdentifier, allFieldNames)
     body: paramAssigns)
   accessors = fieldNames.map (name) ->
     (jsAssignStatement (jsAccess dictName, name), (jsFunction
@@ -2561,30 +2569,31 @@

     # TODO match

-    'if', '(Fn Bool a a a)'
+    # 'if', '(Fn Bool a a a)'
     # TODO JS interop

-    'sqrt', '(Fn Num Num)'
-    'not', '(Fn Bool Bool)'
-
-    '^', binaryMathOpType
-
-    '~', '(Fn Num Num)'
-
-    '+', binaryMathOpType
-    '*', binaryMathOpType
-    '=', comparatorOpType
-    '!=', comparatorOpType
-    'and', '(Fn Bool Bool Bool)'
-    'or', '(Fn Bool Bool Bool)'
-
-    '-', binaryMathOpType
-    '/', binaryMathOpType
-    'rem', binaryMathOpType
-    '<', comparatorOpType
-    '>', comparatorOpType
-    '<=', comparatorOpType
-    '>=', comparatorOpType),
+    # 'sqrt', '(Fn Num Num)'
+    # 'not', '(Fn Bool Bool)'
+
+    # '^', binaryMathOpType
+
+    # '~', '(Fn Num Num)'
+
+    # '+', binaryMathOpType
+    # '*', binaryMathOpType
+    # '=', comparatorOpType
+    # '!=', comparatorOpType
+    # 'and', '(Fn Bool Bool Bool)'
+    # 'or', '(Fn Bool Bool Bool)'
+
+    # '-', binaryMathOpType
+    # '/', binaryMathOpType
+    # 'rem', binaryMathOpType
+    # '<', comparatorOpType
+    # '>', comparatorOpType
+    # '<=', comparatorOpType
+    # '>=', comparatorOpType
+    ),
     newMapWith 'empty-array', (type: (parseUnConstrainedType '(Fn (Array a))'), arity: [])
       'cons-array', (type: (parseUnConstrainedType '(Fn a (Array a) (Array a))'), arity: ['what', 'onto'])

@@ -3455,23 +3464,47 @@
   """(show ["Adam" "Michal"])""", "Michal"

   # TODO: will need more work
-  # 'superclasses'
-  # """
-  #   Eq (class [a]
-  #     = (fn [x y] (: (Fn a a Bool))))
-
-  #   Ord (class [a]
-  #     {(Eq a)}
-  #     <= (fn [x y] (: (Fn a a Bool))))
-
-  #   eq-string (instance (Eq String)
-  #     = (fn [x y] False))
-
-  #   ord-string (instance (Ord String)
-  #     <= (fn [x y] (= x y)))"""
-  # """(<= "Michal" "Adam")""", no
-
+  'superclasses'
+  """
+    Eq (class [a]
+      = (fn [x y] (: (Fn a a Bool))))
+
+    Ord (class [a]
+      {(Eq a)}
+      <= (fn [x y] (: (Fn a a Bool))))
+
+    eq-bool (instance (Eq Bool)
+      = (fn [x y]
+        (match [x y]
+          [True True] True
+          [False False] False)))
+
+    ord-bool (instance (Ord Bool)
+      <= (fn [x y]
+        (match [x y]
+          [True any] True
+          [w z] (= w z))))
+
+    """
+  """(<= "Michal" "Adam")""", no
+
+  # TODO: support matching with the same name
+  #       to implement this we need the iife to take as arguments all variables
+  #       with the same names, since JavaScript shadows it too strongly and
+  #       replaces the value with undefined
+  # test "test", "f (fn [x] (match x x x)) (f 2)", 2
+  # so:
+  #function f(x) {
+  # return (function (x){
+  #   var x = x;
+  #   return x;
+  # })(x);
+  #}
+  # This is necessary because we might be reusing the name for something else
+  # Or we can just mangle the name like PureScript does it
 ]
+
+

 testNamed = (givenName) ->
   for [name, source, expression, result] in tuplize 4, tests when name is givenName


Improved the macro macro compilation

Improved the macro macro compilation

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1754,18 +1754,19 @@

   if hasName
     macroName = ctx.definitionName()
-    if ctx.macros()[macroName]
-      return malformed ctx.definitionPattern(), "Macro with this name already defined"
-    if not type or not isTypeAnnotation type
-      return malformed call, "Type annotation required"
+    redefining = ctx.macros()[macroName]

     # Register type
     params = _terms paramTuple
     paramNames = map _symbol, params
-    ctx.declare macroName,
-      arity: paramNames
-      type: type = quantifyUnbound ctx, typeConstrainedCompile ctx, type
-    call.tea = type
+    if not type or not isTypeAnnotation type
+      malformed call, "Type annotation required"
+      rest = join [type], rest
+    else if not redefining
+      ctx.declare macroName,
+        arity: paramNames
+        type: type = quantifyUnbound ctx, typeConstrainedCompile ctx, type
+      call.tea = type

     if not rest.length > 0
       return malformed call, "Macro body missing"
@@ -1774,10 +1775,13 @@
     compiledMacro = translateToJs translateIr ctx,
       (termCompile ctx, call_ (token_ 'fn'), (join [paramTuple], rest))
     # log compiledMacro
-    ctx.addMacro ctx.definitionPattern(), simpleMacro eval compiledMacro
     params = (map token_, paramNames) # freshen

-    fn_ params, call_ (token_ macroName), params
+    if redefining
+      malformed ctx.definitionPattern(), "Macro with this name already defined"
+    else
+      ctx.addMacro ctx.definitionPattern(), simpleMacro eval compiledMacro
+      fn_ params, call_ (token_ macroName), params

 simpleMacro = (macroFn) ->
   (ctx, call) ->


arbitrary html in stepper view

arbitrary html in stepper view

--- lib/debugger/stepper.coffee
+++ lib/debugger/stepper.coffee
@@ -1,4 +1,6 @@
 StepperView = require './stepper-view'
+views = require '../util/views'
+{span} = views.tags

 module.exports =
 class Stepper
@@ -18,7 +20,7 @@
   setText: (@text) ->
     for view in @views
       view.clear()
-      view.appendChild text
+      view.appendChild views.render span 'stepper-label', text
       view.appendChild view.buttonGroup @buttons

   edsForFile: (file) ->


ControllerRunner tests fixed.

ControllerRunner tests fixed.

--- server/test/Controller/ControllerRunner.coffee
+++ server/test/Controller/ControllerRunner.coffee
@@ -20,7 +20,7 @@
                 before: ->
                     done()
             runner.run instance, 10000, ->
-                expect.fail()
+                expect().fail()

         it 'should call the after() callback if it is defined', (done) ->
             instance =
@@ -31,7 +31,7 @@
                 after: ->
                     done()
             runner.run instance, 10000, ->
-                expect.fail()
+                expect().fail()

         it 'should call the get() method if the instance has the method property defined as "get"', (done) ->
             instance =
@@ -40,7 +40,7 @@
                 get: ->
                     done()
             runner.run instance, 10000, ->
-                expect.fail()
+                expect().fail()

         it 'should call the callback passing a Timeout exception if the timeout has occured', (done) ->
             instance =
@@ -58,7 +58,7 @@
                 filters: []
                 method: 'get'
                 before: (callback) -> beforeCalled = true ; callback(true)
-                after: -> expect.fail()
+                after: -> expect().fail()
                 get: ->
                 afterTimeout: ->
                     expect(beforeCalled).to.be true
@@ -73,9 +73,8 @@
                 get: (callback) ->
                     callback {}
                 after: ->
-                    setTimeout ->
+                    setImmediate ->
                         throw afterError
-                    , 10
             runner.run instance, 10000, (e) ->
                 expect(e).to.be afterError
                 done()
@@ -85,9 +84,8 @@
             instance =
                 filters: []
                 before: ->
-                    setTimeout ->
+                    setImmediate ->
                         throw beforeError
-                    , 10
             runner.run instance, 10000, (e) ->
                 expect(e).to.be beforeError
                 done()
@@ -242,12 +240,12 @@
             bFilter =
                 responseHeaders: {}
                 before: ->
-                    expect.fail()
-            controller =
-                method: 'get'
-                filters: [aFilter, bFilter]
-                get: ->
-                    expect.fail()
+                    expect().fail()
+            controller =
+                method: 'get'
+                filters: [aFilter, bFilter]
+                get: ->
+                    expect().fail()

             runner.run controller, 10000, (error, response) ->
                 expect(error).not.to.be.ok()
@@ -262,12 +260,12 @@
                     throw name: 'MyError'
             bFilter =
                 before: ->
-                    expect.fail()
-            controller =
-                method: 'get'
-                filters: [aFilter, bFilter]
-                get: ->
-                    expect.fail()
+                    expect().fail()
+            controller =
+                method: 'get'
+                filters: [aFilter, bFilter]
+                get: ->
+                    expect().fail()

             runner.run controller, 10000, (error) ->
                 expect(error.name).to.be('MyError')
@@ -276,7 +274,7 @@
         it 'should stop the filter chain if an exception occurs in the after method of a filter', (done) ->
             aFilter =
                 after: ->
-                    expect.fail()
+                    expect().fail()
             bFilter =
                 after: ->
                     throw name: 'MyError'
@@ -300,14 +298,14 @@
                     callback()
                     done()
                 after: ->
-                    expect.fail()
+                    expect().fail()
             bFilter =
                 before: (callback) -> callback()
                 afterTimeout: (callback) ->
                     bTimeoutCalled = true
                     callback()
                 after: ->
-                    expect.fail()
+                    expect().fail()
             controller =
                 method: 'get'
                 filters: [aFilter, bFilter]
@@ -320,17 +318,16 @@
             aFilter =
                 afterTimeout: (callback) ->
                     callback()
-                    setTimeout ->
+                    setImmediate ->
                         done()
-                    , 30
                 before: ->
                     # The callback is not called, so the controller.get() will never be called
                     # Therefore controller.afterTimeout() should also not be called!
             controller =
-                afterTimeout: -> expect.fail()
+                afterTimeout: -> expect().fail()
                 method: 'get'
                 filters: [aFilter]
-                get: -> expect.fail()
+                get: -> expect().fail()

             runner.run controller, 100, (error) ->
                 expect(error.name).to.be 'Timeout'
@@ -339,18 +336,17 @@
             aFilter =
                 afterTimeout: (callback) ->
                     callback()
-                    setTimeout ->
+                    setImmediate ->
                         done()
-                    , 30
                 before: ->
                     # The callback is not called, so the B filter will never execute
                     # Therefore bFilter.afterTimeout() should also not be called!
             bFilter =
-                afterTimeout: -> expect.fail()
-            controller =
-                method: 'get'
-                filters: [aFilter, bFilter]
-                get: -> expect.fail()
+                afterTimeout: -> expect().fail()
+            controller =
+                method: 'get'
+                filters: [aFilter, bFilter]
+                get: -> expect().fail()

             runner.run controller, 100, (error) ->
                 expect(error.name).to.be 'Timeout'
@@ -365,23 +361,22 @@
                     expect(bAfterError).to.be true
                     callback()
                     done()
-                after: -> expect.fail()
-                afterTimeout: -> expect.fail()
+                after: -> expect().fail()
+                afterTimeout: -> expect().fail()
             bFilter =
                 before: (callback) -> callback()
                 afterError: (callback) ->
                     bAfterError = true
                     callback()
-                after: -> expect.fail()
-                afterTimeout: -> expect.fail()
-            controller =
-                method: 'get'
-                filters: [aFilter, bFilter]
-                get: ->
-                    setTimeout ->
+                after: -> expect().fail()
+                afterTimeout: -> expect().fail()
+            controller =
+                method: 'get'
+                filters: [aFilter, bFilter]
+                get: ->
+                    setImmediate ->
                         throw name: 'MyError'
-                    , 10
-                after: -> expect.fail()
+                after: -> expect().fail()

             runner.run controller, 10000, (error) ->
                 expect(error.name).to.be 'MyError'
@@ -390,39 +385,36 @@
             aFilter =
                 afterError: (callback) ->
                     callback()
-                    setTimeout ->
+                    setImmediate ->
+                        done()
+                before: ->
+                    setImmediate ->
+                        throw name: 'MyError'
+            controller =
+                afterError: -> expect().fail()
+                method: 'get'
+                filters: [aFilter]
+                get: -> expect().fail()
+
+            runner.run controller, 10000, (error) ->
+                expect(error.name).to.be 'MyError'
+
+        it 'should not call bFilter.afterError() if an exception occurs within the aFilter.before()', (done) ->
+            aFilter =
+                afterError: (callback) ->
+                    callback()
+                    setImmediate ->
                         done()
                     , 30
                 before: ->
-                    setTimeout ->
+                    setImmediate ->
                         throw name: 'MyError'
-                    , 10
-            controller =
-                afterError: -> expect.fail()
-                method: 'get'
-                filters: [aFilter]
-                get: -> expect.fail()
+            bFilter =
+                afterTimeout: -> expect().fail()
+            controller =
+                method: 'get'
+                filters: [aFilter, bFilter]
+                get: -> expect().fail()

             runner.run controller, 10000, (error) ->
                 expect(error.name).to.be 'MyError'
-
-        it 'should not call bFilter.afterError() if an exception occurs within the aFilter.before()', (done) ->
-            aFilter =
-                afterError: (callback) ->
-                    callback()
-                    setTimeout ->
-                        done()
-                    , 30
-                before: ->
-                    setTimeout ->
-                        throw name: 'MyError'
-                    , 10
-            bFilter =
-                afterTimeout: -> expect.fail()
-            controller =
-                method: 'get'
-                filters: [aFilter, bFilter]
-                get: -> expect.fail()
-
-            runner.run controller, 10000, (error) ->
-                expect(error.name).to.be 'MyError'


added mobile support

added mobile support

--- slider.coffee
+++ slider.coffee
@@ -22,7 +22,9 @@

 		removeClass: (e, c) -> e?.classList?.remove c

-		transform: (el, xform) -> el?.style?.transform = xform
+		transform: (el, xform) ->
+			style = el?.style
+			style.transform = style.WebkitTransform = style.msTransform = xform

 		clamp: (v, min, max) ->
 			v = min if min? and v < min


Always call getType callback

Always call getType callback

--- lib/ghc-modi-process.coffee
+++ lib/ghc-modi-process.coffee
@@ -41,7 +41,9 @@
             return acc unless myrange.containsRange(crange)
             return [myrange,type]),
             ''
-          callback range,type.replace(/\r/g,'\n'),crange if type
+          type='???' unless type
+          range=crange unless range
+          callback range,type.replace(/\r/g,'\n'),crange

   getInfo: (text,symbol,callback) ->
     Temp.open


use the new saveFile function that opens a "Save As"

dialog if the file has never been saved

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -87,7 +87,7 @@
       .subscribe successHandler, @displayErrors

   getTypeForWord: ({target}) =>
-    target.model.save()
+    @saveFile target.model
     word = @getWordUnderCursor target

     successHandler = ({responseType, msg}) =>
@@ -107,7 +107,7 @@

   doCaseSplit: ({target}) =>
     editor = target.model
-    editor.save()
+    @saveFile editor
     uri = editor.getURI()
     cursor = editor.getLastCursor()
     line = cursor.getBufferRow()
@@ -126,7 +126,7 @@

   doAddClause: ({target}) =>
     editor = target.model
-    editor.save()
+    @saveFile editor
     uri = editor.getURI()
     line = editor.getLastCursor().getBufferRow()
     word = @getWordUnderCursor target
@@ -149,7 +149,7 @@

   doMakeWith: ({target}) =>
     editor = target.model
-    editor.save()
+    @saveFile editor
     uri = editor.getURI()
     line = editor.getLastCursor().getBufferRow()
     editor.moveToBeginningOfLine()
@@ -174,7 +174,7 @@

   doMakeCase: ({target}) =>
     editor = target.model
-    editor.save()
+    @saveFile editor
     uri = editor.getURI()
     line = editor.getLastCursor().getBufferRow()
     word = @getWordUnderCursor target
@@ -212,7 +212,7 @@

   doProofSearch: ({target}) =>
     editor = target.model
-    editor.save()
+    @saveFile editor
     uri = editor.getURI()
     line = editor.getLastCursor().getBufferRow()
     word = @getWordUnderCursor target


Fixes #206

Fixes #206

--- lib/idris-controller.coffee
+++ lib/idris-controller.coffee
@@ -327,7 +327,7 @@
         .filter ({ responseType }) -> responseType == 'return'
         .flatMap => @model.makeWith line + 1, word
         .subscribe successHandler, @displayErrors
-    else
+    else
       @clearMessagePanel "Idris: Illegal position to make a with view"

   # construct a lemma from a hole
@@ -402,6 +402,7 @@

       editor.transact ->
         # Delete old line, insert the new case block
+        editor.moveToBeginningOfLine()
         editor.deleteLine()
         editor.insertText clause
         # And move the cursor to the beginning of


exit earlier during ko detection

exit earlier during ko detection

--- lib/javascripts/move.coffee
+++ lib/javascripts/move.coffee
@@ -80,9 +80,6 @@

   is_no_illegal_ko_move = (move, board, captures)->

-    last_move_and_current_move_captured_exactly_one = (captures, last_move) ->
-      captures.length == 1 && last_move.captures.length == 1
-
     isFirstOrSecondMove = (board)-> board.moves.length <= 1

     captures_of_move = (move, board)->
@@ -95,8 +92,9 @@
     last_move = get_last_move(board)
     return true if last_move.captures.length != 1
     captures = captures_of_move(stone, board)
+    return true if captures.length != 1
     not (is_same_move(last_move.captures[0], move) and
-      last_move_and_current_move_captured_exactly_one(captures, last_move))
+      is_same_move(captures[0], last_move))

   is_no_double_move(stone, board) and


findOrCreate update

findOrCreate update

--- lib/model_extensions.coffee
+++ lib/model_extensions.coffee
@@ -44,7 +44,7 @@
   model_type.findOrCreate = (data, callback) ->
     throw 'findOrCreate requires object data' if not _.isObject(data) or (data instanceof Backbone.Model) or (data instanceof Backbone.Collection)

-    query = {id: data.id, $one: true}
+    query = _.extend({$one: true}, data)
     model_type::sync('cursor', query).toModels (err, model) ->
       return callback(err) if err
       return callback(null, model) if model


fix goal context range

fix goal context range

--- lib/text-buffer/goal.coffee
+++ lib/text-buffer/goal.coffee
@@ -45,14 +45,16 @@
     setTextInRange: (text, range) -> @editor.setTextInBufferRange range, text

     # without boundaries and spaces
-    getContent:        -> @editor.getTextInRange(      new Range(@startMarker.bufferMarker.getEndPosition(), @endMarker.bufferMarker.getStartPosition())).replace(/^\s\s*/, '').replace(/\s\s*$/, '')
+    getContent:        -> @editor.getTextInRange(      new Range(@getInnerStart(), @getInnerEnd())).replace(/^\s\s*/, '').replace(/\s\s*$/, '')
     setContent: (text) -> @editor.setTextInBufferRange new Range(@startMarker.bufferMarker.getEndPosition(), @endMarker.bufferMarker.getStartPosition()), text

+    getInnerStart: -> @translate(@startMarker.bufferMarker.getStartPosition(), 2)
     getStart: -> @startMarker.bufferMarker.getStartPosition()
     setStart: (startLeft) ->
         startRight = @translate startLeft, 2
         @startMarker.bufferMarker.setRange new Range startLeft, startRight

+    getInnerEnd: -> @translate(@endMarker.bufferMarker.getEndPosition(), -2)
     getEnd: -> @endMarker.bufferMarker.getEndPosition()
     setEnd: (endRight) ->
         endLeft = @translate endRight, -2


Fix double-quoted strings tests

Fix double-quoted strings tests

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -60,18 +60,18 @@

       it "should mark all parts of the string with the same scope", ->
         for token in tokens
-          expect(token).toHaveScopes ["string.quoted.double.single-line.powershell"]
+          expect(token).toHaveScopes ["string.quoted.double.powershell"]

       it "should tokenize the opening double-quote", ->
         expect(tokens[0].value).toEqual "\""
-        expect(tokens[0]).toHaveScopes ["punctuation.definition.string.begin.powershell"]
+        expect(tokens[0]).toHaveScopes ["string.quoted.double.powershell"]

       it "should tokenize content of the string", ->
         expect(tokens[1].value).toEqual "Hi there! and welcome to 'string-making': 101."

       it "should tokenize the closing double-quote", ->
         expect(tokens[2].value).toEqual "\""
-        expect(tokens[2]).toHaveScopes ["punctuation.definition.string.end.powershell"]
+        expect(tokens[2]).toHaveScopes ["string.quoted.double.powershell"]

     describe "Empty string", ->
       tokens = null
@@ -81,26 +81,18 @@

       it "should mark all parts of the string with the same scope", ->
         for token in tokens
-          expect(token).toHaveScopes ["string.quoted.double.single-line.powershell"]
-
-      it "should tokenize the opening double-quote as punctuation", ->
-        expect(tokens[0].value).toEqual "\""
-        expect(tokens[0]).toHaveScopes ["punctuation.definition.string.begin.powershell"]
-
-      it "should tokenize the closing double-quote as empty string", ->
-        expect(tokens[1].value).toEqual "\""
-        expect(tokens[1]).toHaveScopes ["punctuation.definition.string.end.powershell", "meta.empty-string.double.powershell"]
+          expect(token).toHaveScopes ["string.quoted.double.powershell"]

     describe "Variables within a string", ->
       tokens = null
-      expectedDollarSignScopes = ["embedded.punctuation.variable.begin.powershell", "embedded.variable.other.powershell"]
+      expectedDollarSignScopes = ["keyword.other.powershell"]

       beforeEach ->
         {tokens} = grammar.tokenizeLine("\"Hi there $name `$bob\"")

       it "should mark all parts of the string with the same scope", ->
         for token in tokens
-          expect(token).toHaveScopes ["string.quoted.double.single-line.powershell"]
+          expect(token).toHaveScopes ["source.powershell", "string.quoted.double.powershell"]

       it "should tokenize content", ->
         expect(tokens[1].value).toEqual "Hi there "
@@ -111,15 +103,15 @@

       it "should tokenize variable names", ->
         expect(tokens[3].value).toEqual "name"
-        expect(tokens[3]).toHaveScopes ["embedded.variable.other.powershell"]
+        expect(tokens[3]).toHaveScopes ["source.powershell", "string.quoted.double.powershell", "variable.other.readwrite.powershell"]

       it "should not tokenize as a variable when leading $ has been escaped", ->
         expect(tokens[5].value).toEqual "`$"
-        expect(tokens[5]).toHaveScopes ["source.powershell", "string.quoted.double.single-line.powershell", "constant.character.escape.powershell"]
-        expect(tokens[5]).not.toHaveScopes ["embedded.variable.other.powershell"]
+        expect(tokens[5]).toHaveScopes ["source.powershell", "string.quoted.double.powershell", "constant.character.escape.powershell"]
+        expect(tokens[5]).not.toHaveScopes ["source.powershell", "embedded.variable.other.powershell"]

         expect(tokens[6].value).toEqual "bob"
-        expect(tokens[6]).not.toHaveScopes ["embedded.variable.other.powershell"]
+        expect(tokens[6]).not.toHaveScopes ["source.powershell", "embedded.variable.other.powershell"]

   describe "Keywords", ->
     describe "Block keywords", ->


Add CSS snippets to insert headers and @media queries


Use Courier as the default font for specs

Fixes #323

--- spec/wrap-guide-spec.coffee
+++ spec/wrap-guide-spec.coffee
@@ -31,7 +31,8 @@
     it "updates the wrap guide position", ->
       initial = wrapGuide.position().left
       expect(initial).toBeGreaterThan(0)
-      rootView.trigger('window:increase-font-size')
+      fontSize = config.get("editor.fontSize")
+      config.set("editor.fontSize", fontSize * 2)
       expect(wrapGuide.position().left).toBeGreaterThan(initial)
       expect(wrapGuide).toBeVisible()


added cmd-d for osx

added cmd-d for osx

--- keymaps/omnisharp-atom.cson
+++ keymaps/omnisharp-atom.cson
@@ -30,6 +30,9 @@
   'ctrl-f12': 'omnisharp-atom:go-to-implementation'
   'f1': 'omnisharp-atom:type-lookup'

+'.platform-darwin atom-text-editor:not([mini])[data-grammar~="cs"], atom-text-editor:not([mini])[data-grammar~="csx"]':
+  'cmd-d': 'omnisharp-atom:go-to-definition'
+
 '.autocomplete-active:not([mini])[data-grammar~="cs"], .autocomplete-active:not([mini])[data-grammar~="csx"]':
   'space': 'omnisharp-atom:intellisense-space'
   '.': 'omnisharp-atom:intellisense-dot'


Fix desc

Fix desc

--- components/MakeRange.coffee
+++ components/MakeRange.coffee
@@ -2,8 +2,8 @@

 exports.getComponent = ->
   c = new noflo.Component
-  description: 'Make an array of evenly-spaced numbers'
-  icon: 'folder-o'
+  c.description = 'Make an array of evenly-spaced numbers'
+  c.icon = 'folder-o'
   c.inPorts.add 'from',
     datatype: 'number'
   c.inPorts.add 'to',


Reload the page if the WS got disconnected (server reboot...)

--- static/web/coffee/app.coffee
+++ static/web/coffee/app.coffee
@@ -388,6 +388,7 @@
                     point.data = data.gamePoint

             $rootScope.socket.on 'user:update', (data) ->
+                console.log data
                 if data.energy then $rootScope.energy = data.energy
                 $rootScope.$apply()
 ]
@@ -556,7 +557,8 @@
     '$http'
     'notify'
     '$q'
-    ($rootScope, $state, $window, $http, notify, $q) ->
+    '$timeout'
+    ($rootScope, $state, $window, $http, notify, $q, $timeout) ->
         $rootScope.$state = $state
         $rootScope.$http = $http

@@ -600,6 +602,19 @@

         # Handle websockets
         $rootScope.socket = io wsUrl
+
+        fnTimeout = ->
+            console.log $rootScope.socket.connected
+            if !$rootScope.socket.connected
+                $timeout ->
+                    window.location.reload false
+                , 3000
+            else
+                $rootScope.wsCheck = $timeout fnTimeout, 1000
+
+        $rootScope.wsCheck = $timeout fnTimeout, 1000
+
+
         $rootScope.socket.on 'notification:send', (data) ->
             notify "Hello there "+data


Hotfix for POST.

Hotfix for POST.

--- tunemachine/routes/api.coffee
+++ tunemachine/routes/api.coffee
@@ -88,7 +88,7 @@
         s.save (err) ->
           if err?
             return models.err res, err
-          req.snapId = s.id
+          req.params.snapId = s.id
           get req, res

   # POST /api/playlist/:playlistId


add ? for travis

add ? for travis

--- tutor/src/components/book-content-mixin.cjsx
+++ tutor/src/components/book-content-mixin.cjsx
@@ -183,7 +183,7 @@

   if @naturalWidth > @naturalHeight or figure.parentNode.dataset.orient is 'horizontal'
     figure.classList.add('tutor-ui-horizontal-img')
-    if @naturalWidth > 450 and figure.parentNode.nodeName isnt 'FIGURE'
+    if @naturalWidth > 450 and figure.parentNode?.nodeName isnt 'FIGURE'
       figure.classList.add('full-width')
   else
     figure.classList.add('tutor-ui-vertical-img')


Calculate values with a map.

Calculate values with a map.

--- app/static/coffee/production-chart.coffee
+++ app/static/coffee/production-chart.coffee
@@ -1,26 +1,17 @@
 d3.csv "/data/ds140-bauxi-clean.csv", (data) ->
-
-  # Read CSV file: first row =>  labels
   reserve_scale = 0.01

-  values = new Array()
-
-  i = 0
-  while i <= data.length - 1
-    values[i] =
-      x: parseInt(data[i]["Year"])
-      y: parseFloat(data[i]["World production"]) or 0
-      z: parseFloat(data[i]["Estimated"]) or 0
-      zlog: parseFloat(data[i]["Log. Estimated"]) or 0
-      res: parseFloat(data[i]["Reserves"]) * reserve_scale or 0
-    i++
+  values = _.map data, (row) ->
+    x: parseInt row["Year"]
+    y: parseFloat(row["World production"]) or 0
+    z: parseFloat(row["Estimated"]) or 0
+    zlog: parseFloat(row["Log. Estimated"]) or 0
+    res: parseFloat(row["Reserves"]) * reserve_scale or 0

   maxval = _.max(_.map(values, _.compose(_.max, _.values)))

   maxval = (1 + Math.floor(maxval / 10)) * 10
   maxval = Math.pow(2, Math.ceil(Math.log(maxval) / Math.log(2)))
-
-  window.data = values

   color1 = "#ffaa00"
   color2 = "#770033"


Unsubscribe regardless of possible upstream /contact errors (#5894)

--- app/views/account/SubscriptionView.coffee
+++ app/views/account/SubscriptionView.coffee
@@ -180,34 +180,30 @@
     me.patch({headers: {'X-Change-Plan': 'true'}})

   unsubscribe: (message, render) ->
-    removeSub = =>
-      payPalInfo = me.get('payPal')
-      stripeInfo = _.clone(me.get('stripe'))
-      if payPalInfo?.billingAgreementID
-        api.users.cancelBillingAgreement({userID: me.id, billingAgreementID: payPalInfo?.billingAgreementID})
-        .then (response) =>
-          window.tracker?.trackEvent 'Unsubscribe End', message: message
-          document.location.reload()
-        .catch (jqxhr) =>
-          console.error('PayPal unsubscribe', jqxhr)
-      else if stripeInfo
-        delete stripeInfo.planID
-        me.set('stripe', stripeInfo)
-        me.once 'sync', ->
-          window.tracker?.trackEvent 'Unsubscribe End', message: message
-          document.location.reload()
-        me.patch({headers: {'X-Change-Plan': 'true'}})
-
-      else
-        console.error "Tried to unsubscribe without PayPal or Stripe user info."
-        @state = 'unknown_error'
-        @stateMessage = "You do not appear to be subscribed."
-        render()
+    payPalInfo = me.get('payPal')
+    stripeInfo = _.clone(me.get('stripe'))
+    if payPalInfo?.billingAgreementID
+      api.users.cancelBillingAgreement({userID: me.id, billingAgreementID: payPalInfo?.billingAgreementID})
+      .then (response) =>
+        window.tracker?.trackEvent 'Unsubscribe End', message: message
+        document.location.reload()
+      .catch (jqxhr) =>
+        console.error('PayPal unsubscribe', jqxhr)
+    else if stripeInfo
+      delete stripeInfo.planID
+      me.set('stripe', stripeInfo)
+      me.once 'sync', ->
+        window.tracker?.trackEvent 'Unsubscribe End', message: message
+        document.location.reload()
+      me.patch({headers: {'X-Change-Plan': 'true'}})
+
+    else
+      console.error "Tried to unsubscribe without PayPal or Stripe user info."
+      @state = 'unknown_error'
+      @stateMessage = "You do not appear to be subscribed."
+      render()
     if message
-      $.post '/contact', message: message, subject: 'Cancellation', (response) ->
-        removeSub()
-    else
-      removeSub()
+      $.post '/contact', message: message, subject: 'Cancellation'

   update: (render) ->
     stripeInfo = me.get('stripe')


Add buildable ogre types to DuelStatsView army power table

--- app/views/play/level/DuelStatsView.coffee
+++ app/views/play/level/DuelStatsView.coffee
@@ -85,6 +85,8 @@
       'arrow-tower': 100
       palisade: 10
       peasant: 50
+      thrower: 9
+      scout: 18
     powers = humans: 0, ogres: 0
     for thang in @options.thangs when thang.health > 0 and thang.exists
       powers[thang.team] += @costTable[thang.type] or 0 if powers[thang.team]?


* fix coffee script

* fix coffee script

--- src/ng-quick-date.coffee
+++ src/ng-quick-date.coffee
@@ -272,8 +272,8 @@
       if typeof(scope.dateFilter) == 'function' && !scope.dateFilter(date)
         return false
       ngModelCtrl.$setViewValue(date)
-      var tmpDate = $filter('date')(date, scope.dateFormat);
-+			date = parseDateString('' + tmpDate + ' ' + scope.inputTime);
+      tmpDate = $filter('date')(date, scope.dateFormat)
+      date = parseDateString('' + tmpDate + ' ' + scope.inputTime)
       if closeCalendar
         scope.toggleCalendar(false)
       true


removed un-used functions and comments

removed un-used functions and comments

--- src/organizations.coffee
+++ src/organizations.coffee
@@ -22,7 +22,6 @@
 # TODO - ideal commands
 # - createThin layer for parsing commands
 # - add (members|repos) to (team|organization)
-# - list (teams|members|repos) of (extra)
 # - move (member|members|repos|repo) (to|from) (team) to (team)
 # - remove (member|team) from (team|organization)

@@ -42,12 +41,6 @@
   return false unless (process.env.HUBOT_GITHUB_KEY and process.env.HUBOT_GITHUB_ORG and process.env.HUBOT_SLACK_ADMIN)
   true

-getOrgMembers = (msg, orgName) ->
-  ensureConfig msg.send
-  github.orgs.getMembers org: orgName, per_page: 100, (err, res) ->
-    msg.reply "There was an error getting the members for organization: #{orgName}" if err
-    msg.send "* <#{user.url}|#{user.login}>" for user in res unless err and res.length == 0
-

 getOrgMember = (msg, username, orgName) ->
   ensureConfig msg.send
@@ -56,19 +49,6 @@
     msg.send "#{username} is part of the organization: #{orgName}" unless err

-getOrgTeams = (msg, orgName) ->
-  ensureConfig msg.send
-  github.orgs.getTeams org: orgName, per_page: 100, (err, res) ->
-    msg.reply "There was an error getting the teams for organization: #{orgName}" if err
-    msg.send "* <https://github.com/org/#{orgName}/teams/#{team.name}|#{team.name}> - #{team.description}" for team in res unless err and res.length == 0
-
-
-
-getOrgRepos = (msg, orgName, repoType) ->
-  ensureConfig msg.send
-  github.repos.getFromOrg org: orgName, type: repoType, per_page: 100, (err, res) ->
-    msg.reply "There was an error getting all repos for organization: #{orgName}" if err
-    msg.send "*<#{repo.html_url}|#{repo.name}> - #{repo.description}" for repo in res unless err and res.length == 0

 # org = {
@@ -77,12 +57,6 @@

 #   summary: (msg) ->
 #     # get org summary
-
-#   list: {
-#     teams: (msg) ->
-#     members: (msg) ->
-#     repos: (msg) ->
-#   }

 #   # remove: {
 #   #   member: (msg, name, from) ->


scense to sense

scense to sense

--- dvl.coffee
+++ dvl.coffee
@@ -772,7 +772,7 @@
 ##
 dvl.apply = ({fn, args, out, name, invalid, allowNull}) ->
   fn = dvl.wrapConstIfNeeded(fn)
-  throw 'dvl.apply only makes scense with at least one argument' unless args?
+  throw 'dvl.apply only makes sense with at least one argument' unless args?
   args = [args] unless dvl.typeOf(args) is 'array'
   args = args.map(dvl.wrapConstIfNeeded)
   invalid = dvl.wrapConstIfNeeded(if invalid? then invalid else null)


JS: Remove leftover pagination event handling

--- evesrp/static/js/requestList.coffee
+++ evesrp/static/js/requestList.coffee
@@ -141,20 +141,6 @@
     false

-changePage = (ev) ->
-    $target = jQuery ev.target
-    # Update the page number
-    filters = filter.getFilters()
-    if ($target.attr 'id') == 'prev_page'
-        filters.page -= 1
-    else if ($target.attr 'id') == 'next_page'
-        filters.page += 1
-    else
-        filters.page = parseInt $target.contents()[0].data, 10
-    filter.updateURL()
-    false
-
-
 addQuickFilter = (ev) ->
     $cell = (jQuery ev.target).closest 'td'
     attribute = $cell.data 'attribute'
@@ -172,7 +158,6 @@

 setupEvents = () ->
     (jQuery 'th a.heading').on 'click', changeSort
-    (jQuery 'ul.pagination').on 'click', changePage
     (jQuery '.filterable a').on 'click', addQuickFilter
     $window = jQuery window
     $window.on 'popstate', getRequests


Limit tweet regex to be at the end of the word.

Without this, anytime you mention the word tweet in a hubot command, it
treats it as a thing to find a tweet for.

--- src/scripts/tweet.coffee
+++ src/scripts/tweet.coffee
@@ -33,7 +33,7 @@

   twit = undefined

-  robot.respond /(.+) tweet/i, (msg) ->
+  robot.respond /(.+) tweet(\s*)?$/i, (msg) ->
     unless auth.consumer_key
       msg.send "Please set the HUBOT_TWITTER_CONSUMER_KEY environment variable."
       return


Fix warnings to use deprecated call

Fix warnings to use deprecated call

--- lib/broadcast-target.coffee
+++ lib/broadcast-target.coffee
@@ -23,7 +23,7 @@
           @listener?()

       when ContentType.HighlightedCode
-        @editor.getBuffer().on 'contents-modified', =>
+        @editor.getBuffer().onDidStopChanging? =>
           @listener?()

         Highlights = require 'highlights'


Catch invalid module directory paths

Catch invalid module directory paths

--- lib/fortran-provider.coffee
+++ lib/fortran-provider.coffee
@@ -103,7 +103,14 @@
           for modDir in configOptions['mod_dirs']
             @modDirs.push(path.join(projDir, modDir))
     for modDir in @modDirs
-      files = fs.readdirSync(modDir)
+      try
+        files = fs.readdirSync(modDir)
+      catch
+        atom.notifications?.addWarning("Warning: During indexing specified module directory cannot be read", {
+          detail: "Directory '#{modDir}' will be skipped",
+          dismissable: true
+        })
+        continue
       for file in files
         if file.match(F90Regex) or file.match(F77Regex)
           filePath = path.join(modDir, file)


Apps: AppsListItemView: Override the render method to render  correctly when the JNewApp updated

--- client/Social/Apps/Views/AppsListItemView.coffee
+++ client/Social/Apps/Views/AppsListItemView.coffee
@@ -1,6 +1,6 @@
 class AppsListItemView extends KDListItemView

-  constructor:(options = {},data)->
+  constructor:(options = {}, data)->

     options.type = "appstore"

@@ -20,23 +20,24 @@
         KodingAppsController.runExternalApp @getData()
         KD.mixpanel "App run, click"

-  viewAppended:->
-    @setTemplate @pistachio()
-    @template.update()
+  # Override KDView::render since I'm updating all the manifest at once ~ GG
+  render:-> @template.update()
+
+  viewAppended: JView::viewAppended

   pistachio:->
     """
-

-      {{> @thumbnail}}
-

-

-
#{@getData().name}

-
{{#(manifest.author)}}

-

-
{{@utils.shortenText #(manifest.description)}}

+

+        {{> @thumbnail}}
+

+

+
#{@getData().name}

+
{{#(manifest.author)}}

+

+
{{@utils.shortenText #(manifest.description)}}

+

-

-

-      {{> @runButton}}
-

+

+        {{> @runButton}}
+

     """


Client: Don't focus PMs. It gets annoying.

--- client/app/js/views/private_messages_view.coffee
+++ client/app/js/views/private_messages_view.coffee
@@ -34,7 +34,6 @@
       @positionPopup($html, @numPopups)
       @addLogMessages($html, message.getLog())
       @numPopups += 1
-    $html.find('input').focus()
     $html

   closePopup: (message) =>


fix location filter bugs

fix location filter bugs

--- client/controllers/events/eventFiltration.coffee
+++ client/controllers/events/eventFiltration.coffee
@@ -43,18 +43,17 @@
       filters.status =
         $in: status

+    countryLevel = @countryLevel.get()
     selectedLocations = @selectedLocations.find().map (location) ->
       query = {}
-      if location.countryName
-        query['locations.countryName'] = location.countryName
-      if location.admin1Name
-        query['locations.admin1Name'] = location.admin1Name
-      if location.admin2Name
-        query['locations.admin2Name'] = location.admin2Name
+      for level in ['countryName', 'admin1Name', 'admin2Name']
+        query["locations.#{level}"] = location[level]
+        if countryLevel == level
+          break
       query

     if selectedLocations.length
-      filters.$or = selectedLocations
+      filters.$or = (filters.$or or []).concat selectedLocations

     filters = _.extend(filters, @properties.get())


Method to clear all workspace items

Method to clear all workspace items

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -260,3 +260,41 @@
     return workspaceView.layout.getSplitViewByName 'BaseSplit'

+  ###*
+   * Clear all split views/tabs and create new an `IDEView`
+   *
+   * @param {boolean=} recover  Recover opened panes if it is `yes`
+   * @param {boolean=} save
+   * @return {Array} panes
+  ###
+  clearWorkspace: (recover = no, save = no) ->
+
+    ## The `ideApp` is an `IDEAppController`s instane
+    ideApp          = @getDelegate()
+    panes           = []
+    baseSplitView   = @getBaseSplitView()
+    parentView      = baseSplitView.panels.last.getSubViews().first
+    splitViews      = parentView.getSubViews().first
+
+    if recover
+      ideApp.forEachSubViewInIDEViews_ (p) ->
+        paneView  = p.parent
+        tabView   = paneView.parent
+
+        { pane }  = tabView.removePane paneView, yes, yes
+        panes.push pane
+
+    ideView         = new IDEView
+    ideApp.ideViews = []  # Reset `ideViews`s array
+
+    splitViews.detach()
+
+    parentView.addSubView ideView
+
+    ideApp.registerIDEView ideView
+    ideApp.setActiveTabView ideView.tabView
+
+    ideApp.recalculateHandles()
+    ideApp.writeSnapshot()  if save
+
+    return panes


stacks: use defaults.description for readme content

--- client/stacks/lib/views/stacks/readmeview.coffee
+++ client/stacks/lib/views/stacks/readmeview.coffee
@@ -1,8 +1,8 @@
-kd                      = require 'kd'
-Encoder                 = require 'htmlencode'
-
-MarkdownEditorView      = require './editors/markdowneditorview'
-StackBaseEditorTabView  = require './stackbaseeditortabview'
+kd = require 'kd'
+Encoder = require 'htmlencode'
+MarkdownEditorView = require './editors/markdowneditorview'
+StackBaseEditorTabView = require './stackbaseeditortabview'
+defaults = require '../../defaults'

 module.exports = class ReadmeView extends StackBaseEditorTabView
@@ -14,19 +14,9 @@

     { stackTemplate } = @getOptions()

-    defaultContent = '''
-      ##### Readme text for this stack template
-
-      You can write down a readme text for new users.
-      This text will be shown when they want to use this stack.
-      You can use markdown with the readme content.
-
-
-    '''
-
     content = if stackTemplate?.description \
       then Encoder.htmlDecode stackTemplate?.description
-      else defaultContent
+      else defaults.description

     @editorView   = @addSubView new MarkdownEditorView
       content     : content


Length через Object.keys

Length через Object.keys

--- storage.coffee
+++ storage.coffee
@@ -49,6 +49,7 @@
         @get key, fn if _elements[key]?

     length: () ->
+        return Object.keys(_elements).length if Object.keys?
         i = 0
         for key of _elements
             i++ if _elements.hasOwnProperty key


FIX: componentReady helper

FIX: componentReady helper

--- frontend/javascripts/helpers.coffee
+++ frontend/javascripts/helpers.coffee
@@ -25,9 +25,9 @@
     if el.connected
       setTimeout -> deferred.resolve()
     else
-      el.on 'tao:connected.taoReady', (e) ->
+      $(el).on 'tao:connected.taoReady', (e) ->
         return unless e.target == el
-        el.off 'tao:connected.taoReady'
+        $(el).off 'tao:connected.taoReady'
         deferred.resolve()
     deferred.promise()


Add build/transfer function

Add build/transfer function

--- src/sketch/sketch.coffee
+++ src/sketch/sketch.coffee
@@ -119,6 +119,12 @@

   SKETCH_CONFIG   = "sketch.json"
   SKETCH_ENCODING = "utf8"
+  arrayEqual = (a1, a2) ->
+    return true if a1 == a2
+    return false unless a1? and a2?
+    return false if a1.length != a2.length
+    (return false if v != a2[i]) for v, i in a1
+    return true

   #--------------------------------------------------------------------------------
   # Public methods
@@ -148,15 +154,6 @@
       sketch = new Sketch()
       return sketch.save(dirFs)
     ).then(=>
-      # FIXME>>>
-      sketch.addItem(i = new SketchItem({path: "main.rb"}))
-      sketch.bootItem = "main.rb"
-      sketch.addItem(i = new SketchItem({path: "submodule1.rb"}))
-      sketch.addItem(i = new SketchItem({path: "submodule2.rb"}))
-    #   sketch.board = new Board()
-    #   return i.setup()
-    # ).then(=>
-      # <<<FIXME
       sketch._temporary = true
       return sketch
     ) # return AsyncFs.opentmpfs().then()...
@@ -181,7 +178,7 @@
       return sketch
     ).catch(=>
       return I18n.rejectPromise("Invalid_sketch")
-    )
+    ) # return dirFs.readFile().then()...

   ###*
   @method
@@ -200,16 +197,18 @@
       # Save all files in sketch
       (promise, item) =>
         return item.editor.save() if item.editor?
-        return unless newDirFs?
+        return promise unless newDirFs?
         # Relocate file
-        return oldDirFs.readFile(item.path).then((data) =>
+        return promise.then(=>
+          return oldDirFs.readFile(item.path)
+        ).then((data) =>
           return newDirFs.writeFile(item.path, data)
         )
       Promise.resolve()
     ).then(=>
       # Save sketch settings
       @_rubicVersion = App.version
-      return newDirFs.writeFile(SKETCH_CONFIG, JSON.stringify(this), SKETCH_ENCODING)
+      return @_dirFs.writeFile(SKETCH_CONFIG, JSON.stringify(this), SKETCH_ENCODING)
     ).then(=>
       # Successfully saved
       @_modified = false
@@ -223,6 +222,15 @@

   ###*
   @method
+    Generate skeleton code
+  @return {Promise}
+    Promise object
+  ###
+  generateSkeleton: ->
+    return
+
+  ###*
+  @method
     Get item in sketch
   @param {string} path
     Path of item
@@ -232,6 +240,29 @@
   getItem: (path) ->
     return item for item in @_items when item.path == path
     return null
+
+  ###*
+  @method
+    Add a new item
+  @param {SketchItem/string} item
+    Item or path to create
+  @param {ArrayBuffer/string} content
+    Content of new item
+  @param {string} [encoding="utf8"]
+    Encoding (for string content)
+  @return {Promise}
+    Promise object
+  @return {SketchItem} return.PromiseValue
+    Item instance created
+  ###
+  addNewItem: (item, content, encoding = "utf8") ->
+    item = new SketchItem({path: item}) if typeof(item) == "string"
+    return Promise.reject(Error("Already_exist")) unless @addItem(item)
+    options = {}
+    options.encoding = encoding if typeof(content) == "string"
+    return @_dirFs.writeFile(item.path, content, options).then(=>
+      return item # Last PromiseValue
+    )

   ###*
   @method
@@ -266,7 +297,7 @@
     @_items.splice(index, 1)
     @_modify()
     @dispatchEvent({type: "removeItem", item: itemRemoved})
-    return false
+    return true

   ###*
   @method
@@ -284,6 +315,83 @@
         @_items.splice(--index, 1) if exists == 0
     return

+  ###*
+  @method
+    Build sketch
+  @param {boolean} [force=false]
+    Force build all files
+  @param {function(string,number,Error)} [progress=null]
+    Hook function for show progress
+  @return {Promise}
+    Promise object
+  ###
+  build: (force = false, progress = null) ->
+    buildItems = (item for item in @_items when item.engine?)
+    done = 0
+    return buildItems.reduce(
+      (promise, item) =>
+        return promise.then(=>
+          engine = item.engine
+          progress?(item.path, (100 * done / buildItems.length))
+          return engine.build(this, item).catch((error) =>
+            progress?(item.path, null, error)
+            return Promise.reject(error)
+          )
+        ).then(=>
+          progress?(item.path, (100 * ++done / buildItems.length))
+          return
+        )
+      Promise.resolve()
+    ) # return buildItems.reduce()
+
+  ###*
+  @method
+    Transfer sketch (Rubic->Board)
+  @param {boolean} [force=false]
+    Force download all files
+  @param {function(string,number,Error)} [progress=null]
+    Hook function for show progress
+  @return {Promise}
+    Promise object
+  ###
+  transfer: (force = false, progress = null) ->
+    return Promise.reject(Error("No board")) unless @_board?
+    transferItems = (item for item in @_items when item.transfer)
+    boardFs = null
+    done = 0
+    writeData = null
+    return transferItems.reduce(
+      (promise, item) =>
+        return promise.then(=>
+          progress?(item.path, (100 * done / transferItems.length))
+          return @_dirFs.readFile(item.path).catch((error) =>
+            progress?(item.path, null, error)
+            return Promise.reject(error)
+          )
+        ).then((readData) =>
+          writeData = readData
+          return if force # Skip readback when force==true
+          return if item.alreadyTransfered
+          return boardFs.readFile(item.path).catch(=>
+            return  # Ignore errors on readFile()
+          )
+        ).then((compareData) =>
+          return if !force and item.alreadyTransfered
+          return if !force and compareData? and arrayEqual(
+            new Uint8Array(writeData)
+            new Uint8Array(compareData)
+          )
+          return boardFs.writeFile(item.path, data)
+        ).then(=>
+          progress?(item.path, (100 * ++done / transferItems.length))
+          return
+        )
+      @_board.requestFileSystem().then((fs) =>
+        boardFs = fs
+        return
+      )
+    ) # return transferItems.reduce()
+
   #--------------------------------------------------------------------------------
   # Protected methods
   #
@@ -295,12 +403,12 @@
   @param {Object} obj
     JSON object
   ###
-  constructor: (obj) ->
+  constructor: (obj = {}) ->
     super(obj)
-    @_rubicVersion = "#{obj?.rubicVersion || ""}"
+    @_rubicVersion = obj.rubicVersion?.toString()
     @_items = (SketchItem.parseJSON(item) for item in (obj?.items or []))
-    @_bootItem = "#{obj?.bootItem || ""}"
-    @_board = Board.parseJSON(obj.board) if obj?.board?
+    @_bootItem = obj.bootItem?.toString()
+    @_board = Board.parseJSON(obj.board) if obj.board?
     @_modify = (key, value) =>
       if key?
         return if @[key] == value


recreate transforms object

recreate transforms object

--- test/browserify-tests.coffee
+++ test/browserify-tests.coffee
@@ -1,4 +1,6 @@
-clearPreviousResults = -> delete Result[key] for key of Result
+clearPreviousResults = ->
+  delete Result[key] for key of Result
+  Result.transforms = {}

 defaultOptions = ->
   return options =


Don't run DB tests on Travis for now

Don't run DB tests on Travis for now

--- test/db.coffee
+++ test/db.coffee
@@ -3,6 +3,10 @@
 fs = require 'fs'
 redis = require 'redis'
 exec = require('child_process').exec
+
+# Skip the database tests on Travis CI
+# Todo: Make these work
+return if process.env.TRAVIS is 'true'

 describe 'Impromptu', ->
   it 'should exist', ->


Fix sorting of orders

Fix sorting of orders

(wrong copied function call)

--- app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee
+++ app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee
@@ -67,7 +67,7 @@
     return unless sort && sort.predicate != ""

     $scope.sorting = sort.getSortingExpr()
-    $scope.fetchProducts()
+    $scope.fetchResults()
   , true

   $scope.capturePayment = (order) ->


`CierreCajaNewRoute` now destroys a `CierreCaja` when transitioning out

Fixes a bug that duplicates cierres de caja

--- app/assets/javascripts/routes/cierres_caja/cierres_caja_new_route.js.coffee
+++ app/assets/javascripts/routes/cierres_caja/cierres_caja_new_route.js.coffee
@@ -24,4 +24,6 @@
       model = @get("controller.model")
       if model.get("isDirty")
         model.rollback()
+      if model.get("isNew")
+        model.deleteRecord()
       model.rollbackAssociations()


Activity: fix syncronization problem for like counts

--- client/Social/Activity/views/likecount.coffee
+++ client/Social/Activity/views/likecount.coffee
@@ -16,6 +16,8 @@

   syncCountWithData: ->
     { @actorsCount } = @getData().interactions.like
+
+    @setTemplate @pistachio()

   # latency compensation


groups only invisible on production

groups only invisible on production

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -312,16 +312,29 @@
     @$("#finder-holder").height @getHeight() - @$("#finder-header-holder").height() - bottomListHeight

   navItems =
-    id    : "navigation"
-    title : "navigation"
-    items : [
-      { title : "Activity" }
-      { title : "Topics" }
-      { title : "Members" }
-      { title : "Groups",  path : "GroupsFake" }
-      { title : "Develop", loggedIn : yes,  path : "StartTab" }
-      { title : "Apps" }
-    ]
+    # temp until groups are implemented
+    do ->
+      if location.hostname is "koding.com"
+        id    : "navigation"
+        title : "navigation"
+        items : [
+          { title : "Activity" }
+          { title : "Topics" }
+          { title : "Members" }
+          { title : "Develop", loggedIn : yes,  path : "StartTab" }
+          { title : "Apps" }
+        ]
+      else
+        id    : "navigation"
+        title : "navigation"
+        items : [
+          { title : "Activity" }
+          { title : "Topics" }
+          { title : "Members" }
+          { title : "Groups",  path : "GroupsFake" }
+          { title : "Develop", loggedIn : yes,  path : "StartTab" }
+          { title : "Apps" }
+        ]

   accNavItems =
     id    : "acc-navigation"


collaboration: controller remove isHostOnline

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -435,27 +435,6 @@
     callback collaborationData

-  isHostOnline: ->
-
-    host = @getCollaborationHost()
-
-    filtered = @participants
-      .asArray()
-      .filter (its) -> its.nickname is host
-
-    return no  unless filtered.length
-
-    { sessionId } = filtered.first
-
-    return no  unless sessionId
-
-    final = @rtm
-      .getCollaborators()
-      .filter (its) -> its.sessionId is sessionId
-
-    return final.length > 0
-
-
   startHeartbeat: ->

     interval = 1000 * 15


KDTokenizedInputView: find tokens in an element recursively

--- components/inputs/KDTokenizedInputView.coffee
+++ components/inputs/KDTokenizedInputView.coffee
@@ -44,17 +44,21 @@
     return  view.encodeValue() if key and view

   getTokens: ->
+    return  @findTokensInElement @getEditableElement()
+
+  findTokensInElement: (element) ->
     tokens = []
-
-    for node in @getEditableElement().childNodes
-      if node.nodeType is Node.ELEMENT_NODE
-        view = @getTokenView node.dataset.key
-        continue unless view
-        {type} = view.getOptions()
-        data   = view.getData()
-        tokens.push {type, data}
-
-    return tokens
+    for child in element.childNodes
+      switch child.nodeType
+        when Node.ELEMENT_NODE
+          if key = child.dataset?.key
+            view = @getTokenView key
+            {type} = view.getOptions()
+            data   = view.getData()
+            tokens.push {type, data}
+          else
+            tokens = tokens.concat @findTokensInElement child
+    return  tokens

   getTokenView: (key) ->
     return  @tokenViews[key]


Trigger or animated or finish, not both

Trigger or animated or finish, not both

--- components/DetectAnimatedGif.coffee
+++ components/DetectAnimatedGif.coffee
@@ -25,17 +25,24 @@
     if Buffer.isBuffer buffer
       out.send animated buffer
       do callback
+      return
     else if typeof buffer is 'string'
+      isAnimated = false
       fs.createReadStream buffer
         .pipe animated()
         .once 'animated', ->
+          isAnimated = true
           out.send true
           do callback
+          return
         .on 'finish', ->
-          out.send false
+          unless isAnimated
+            out.send false
           do callback
+          return
     else
       out.send false
       do callback
+      return

   c


SelectAll when the mini editor is focused

--- lib/search-in-buffer.coffee
+++ lib/search-in-buffer.coffee
@@ -1,6 +1,7 @@
 {View} = require 'space-pen'
 Editor = require 'editor'
 $ = require 'jquery'
+_ = require 'underscore'
 Point = require 'point'
 SearchModel = require './search-model'
 SearchResultsView = require './search-results-view'
@@ -59,13 +60,13 @@
       @previouslyFocusedElement = $(':focus')
       rootView.append(this)

-    @searchModel.showResults()
+    @miniEditor.selectAll()
     @miniEditor.focus()
+    _.nextTick => @searchModel.showResults()

   confirm: =>
     @search()
     @findNext()
-    #rootView.getActiveView().focus()

   showFind: =>
     @attach()


Use skinny arrow for updateMisspellings

Use skinny arrow for updateMisspellings

--- lib/spell-check-view.coffee
+++ lib/spell-check-view.coffee
@@ -55,7 +55,8 @@

     if @spellCheckCurrentGrammar()
       @buffer = @editorView.getEditor().getBuffer()
-      @subscribe @buffer, 'contents-modified', @updateMisspellings
+      @subscribe @buffer, 'contents-modified', =>
+        @updateMisspellings()
       @updateMisspellings()

   spellCheckCurrentGrammar: ->
@@ -72,7 +73,7 @@
       @views.push(view)
       @append(view)

-  updateMisspellings: =>
+  updateMisspellings: ->
     @constructor.startTask @buffer.getText(), (misspellings) =>
       @destroyViews()
       @addViews(misspellings)


fix test

fix test

--- test/scripts/auto-fav.coffee
+++ test/scripts/auto-fav.coffee
@@ -11,14 +11,13 @@
     # process.on 'uncaughtException'
     @sinon.stub process, 'on', -> null
     # config
-    @originalApiInterval = process.env.HUBOT_AUTO_FAV_API_INTERVAL
-    @originalKeywords = process.env.HUBOT_AUTO_FAV_KEYWORDS
-    @originalInterval = process.env.HUBOT_AUTO_FAV_INTERVAL
-    @originalRoom = process.env.HUBOT_AUTO_FAV_ROOM
-    process.env.HUBOT_AUTO_FAV_API_INTERVAL = 0
-    process.env.HUBOT_AUTO_FAV_KEYWORDS = '["#hitoridokusho"]'
-    process.env.HUBOT_AUTO_FAV_INTERVAL = 10
-    process.env.HUBOT_AUTO_FAV_ROOM = 'hitoridokusho'
+    @originalEnv = process.env
+    process.env =
+      HUBOT_AUTO_FAV_API_INTERVAL: 0
+      HUBOT_AUTO_FAV_KEYWORDS: '["#hitoridokusho"]'
+      HUBOT_AUTO_FAV_INTERVAL: 10
+      HUBOT_AUTO_FAV_ROOM: 'hitoridokusho'
+      HUBOT_AUTO_FAV_SHOW_DETAIL: null
     # twitter
     Twitter = require '../../src/twitter'
     @searchResult =
@@ -42,17 +41,14 @@
     @robot.run()

   afterEach (done) ->
-    process.env.HUBOT_AUTO_FAV_API_INTERVAL = @originalApiInterval
-    process.env.HUBOT_AUTO_FAV_KEYWORDS = @originalKeywords
-    process.env.HUBOT_AUTO_FAV_INTERVAL = @originalInterval
-    process.env.HUBOT_AUTO_FAV_ROOM = @originalRoom
+    process.env = @originalEnv
     @robot.brain.on 'close', =>
       @sinon.restore()
       done()
     @robot.shutdown()

   describe 'start', ->
-    it 'send "https://twitter.com/bouzuya/status/12345"', ->
+    it 'send "https://twitter.com/bouzuya/status/12345"', (done) ->
       setTimeout =>
         try
           assert @messageRoom.callCount is 1
@@ -61,4 +57,4 @@
           done()
         catch e
           done e
-      , 10
+      , 100


Added hashbang to URL.

Added hashbang to URL.

--- client/app/Applications/Activity.kdapplication/views/activityactions.coffee
+++ client/app/Applications/Activity.kdapplication/views/activityactions.coffee
@@ -54,9 +54,9 @@
       click           :(event)=>
         data = @getData()
         if data?.group? and data.group isnt "koding"
-          shareUrl = "#{KD.config.mainUri}/#{data.group}/Activity/#{data.slug}"
+          shareUrl = "#{KD.config.mainUri}/#!/#{data.group}/Activity/#{data.slug}"
         else
-          shareUrl      = "#{KD.config.mainUri}/Activity/#{data.slug}"
+          shareUrl      = "#{KD.config.mainUri}/#!/Activity/#{data.slug}"
         contextMenu   = new JContextMenu
           cssClass    : "activity-share-popup"
           type        : "activity-share"


activity feed: add docs; format fixes

activity feed: add docs; format fixes

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -124,10 +124,15 @@
     @lastTo   = cache.to
     @lastFrom = cache.from

+  # Refreshes activity feed.
+  #
+  # If no response is returned in 5 secs, we assume user has
+  # been disconnected so long, his/her channel subscription is
+  # dead, so we reconnect.
   refresh:->
     @populateActivity {},\
       KD.utils.getTimedOutCallback (err, items)->
-        log 'refresh'
+        log 'refreshing activity feed'
       , =>
         @isLoading = no
         @status.disconnect()
@@ -323,4 +328,3 @@
           if err then callback err
           else
             callback instances
-


Add important links there

Add important links there

--- client/app/MainApp/login/loginview.coffee
+++ client/app/MainApp/login/loginview.coffee
@@ -178,6 +178,8 @@

Already a member? {{> @backToLoginLink}}

Trouble logging in? {{> @goToRecoverLink}}

Didn't receive confirmation email? {{> @goToResendMailConfirmationLink}}

+
Terms of service

+
Privacy policy

     """


app: disable react

app: disable react

--- client/app/lib/util/isReactEnabled.coffee
+++ client/app/lib/util/isReactEnabled.coffee
@@ -3,7 +3,8 @@

 module.exports = ->

-  return yes
+  # to develop react features turn this to yes.
+  return no

   # not kd.singletons.mainController.isFeatureDisabled 'react-render'


ShowSaveDialog: cleanup the mess, do not save changes on this dialog

--- client/app/lib/util/showSaveDialog.coffee
+++ client/app/lib/util/showSaveDialog.coffee
@@ -8,7 +8,7 @@
 envDataProvider = require 'app/userenvironmentdataprovider'

-module.exports = (container, callback = kd.noop, options = {}) ->
+module.exports = showSaveDialog = (container, callback = kd.noop, options = {}) ->

   container.addSubView dialog = new KDDialogView
     cssClass      : kd.utils.curry 'save-as-dialog', options.cssClass
@@ -53,7 +53,7 @@
   dialog.show()
   input.setFocus()

-  finderController = kd.singletons['appManager'].get('Finder').create
+  finderController = kd.singletons.appManager.get('Finder').create
     addAppTitle       : no
     treeItemClass     : IDEFinderItem
     nodeIdPath        : 'path'
@@ -62,19 +62,10 @@
     contextMenu       : yes
     loadFilesOnInit   : yes
     machineToMount    : options.machine
+    saveChanges       : no

   finderController.reset()

   form.addSubView finderWrapper = new KDView { cssClass : 'save-as-dialog save-file-container' }, null
   finderWrapper.addSubView finderController.getView()
   finderWrapper.setHeight 200
-
-  # FIXME: rootpath should be taken from options.
-  # i don't want to do it for now because this file should be
-  # refactored from the first line and should be moved to somewhere else.
-  # for now we can live with it and assuming appManager.frontApp is IDE in
-  # this case is safe because this is save/save-as modal.
-  if machine = options.machine
-    if ideApp = envDataProvider.getIDEFromUId machine.uid
-      { rootPath } = ideApp.workspaceData
-      finderController.updateMachineRoot machine.uid, rootPath


* add some tests

* add some tests

--- test/test_file_loader.coffee
+++ test/test_file_loader.coffee
@@ -88,6 +88,57 @@

       fl_obj.getFileContent fixturesCoffee, res_fn1

+  describe 'getFileWithMeta() *async*', ->
+
+    it 'should get file from disk on empty cache', (done) ->
+      res_fn = (err, data) ->
+        expect(err).to.be.null
+        expect(data).to.not.be.null
+        expect(data).to.not.be.undefined
+        data.should.to.be.a 'object'
+        data.should.to.have.keys ['meta', 'digest', 'content']
+        #console.log data
+        done()
+      fl_obj.getFileWithMeta fixturesCoffee, res_fn
+
+    it 'should get file from cache', (done) ->
+      res_fn2 = (err, data) ->
+        expect(err).to.be.null
+        expect(data).to.not.be.null
+        expect(data).to.not.be.undefined
+        data.should.to.be.a 'object'
+        data.should.to.have.keys ['meta', 'digest', 'content']
+        #console.log data
+        done()
+
+      res_fn1 = (err, data) ->
+        expect(err).to.be.null
+        expect(data).to.not.be.null
+        expect(data).to.not.be.undefined
+        data.should.to.be.a 'object'
+        data.should.to.have.keys ['meta', 'digest', 'content']
+        #console.log data
+        fl_obj.getFileWithMeta fixturesCoffee, res_fn2
+
+      fl_obj.getFileWithMeta fixturesCoffee, res_fn1
+
+    it 'should use queue for some request in one time', (done) ->
+
+      cicle_numbers = 10000
+
+      all_done = (err, data) ->
+        expect(err).to.be.null
+        expect(data).to.not.be.null
+        expect(data).to.not.be.undefined
+        data.should.to.be.a 'object'
+        data.should.to.have.keys ['meta', 'digest', 'content']
+        #console.log data
+        done()
+
+      one_done = _.after cicle_numbers, all_done
+
+      _.times cicle_numbers, -> fl_obj.getFileWithMeta fixturesCoffee, one_done
+
   describe 'resetCaches()', ->

     it 'should drop cache and return null', ->


removed unused code from post.coffee.

removed unused code from post.coffee.

--- client/coffeescript/templates/post.coffee
+++ client/coffeescript/templates/post.coffee
@@ -24,10 +24,6 @@
       $('.new_post').waypoint( -> @click?() ).removeClass('new_post')
     )
     return ""
-
-  avatar: ->
-    hash = hex_md5("#{@_id}@example.com")
-    return "http://www.gravatar.com/avatar/#{hash}"

   content: ->


Tests: helpers: idehelpers: Added moveToElement in openContextMenu helper.

--- client/test/lib/helpers/idehelpers.coffee
+++ client/test/lib/helpers/idehelpers.coffee
@@ -23,6 +23,7 @@
     browser
       .waitForElementVisible  paneSelector + fileSelector, 20000
       .moveToElement          paneSelector + fileSelector, 60, 17
+      .moveToElement          paneSelector + fileSelector + ' span.options', 8, 8
       .waitForElementVisible  paneSelector + fileSelector + ' span.options', 20000
       .click                  paneSelector + fileSelector + ' span.options'
       .waitForElementVisible  '.kdlistview-contextmenu', 20000 # Assertion


missed file in previous commit

missed file in previous commit

--- coach/src/course/modify-registration.cjsx
+++ coach/src/course/modify-registration.cjsx
@@ -2,7 +2,6 @@
 _ = require 'underscore'

 EnrollmentCodeInput = require './enrollment-code-input'
-
 ConfirmJoin = require './confirm-join'
 User = require '../user/model'
 Course = require './model'


KiRouter converts hashbang urls for pushState only if path is known

--- lib/web/views/router.coffee
+++ lib/web/views/router.coffee
@@ -104,7 +104,7 @@
     initialUrl = window.location.pathname
     forceUrlUpdate = false
     if @pushStateSupport
-      if window.location.hash.substring(0, 2) == "#!"
+      if window.location.hash.substring(0, 2) == "#!" && @find(window.location.hash.substring(2))
         forceUrlUpdate = initialUrl = window.location.hash.substring(2)
     else
       if @hashBaseUrl && @hashBaseUrl != window.location.pathname && window.location.hash == "" && @find(window.location.pathname)


Change protocol name.

Change protocol name.

--- lib/websocket_server.coffee
+++ lib/websocket_server.coffee
@@ -30,7 +30,7 @@

   onRequest: (request) =>
     # todo - check request.origin maybe?
-    connection = request.accept("scene-server", request.origin)
+    connection = request.accept("scenevr", request.origin)

     observer = new Observer(connection, @reflector)
     @reflector.addObserver(observer)


Event loop should act more like real browser: - Don't suspend/resume timeout/interval - Ignore interval firing if previous has not run

--- lib/zombie/eventloop.coffee
+++ lib/zombie/eventloop.coffee
@@ -127,7 +127,6 @@
     if @listeners.length == 1
       setImmediate =>
         if @active
-          @active._eventQueue.resume()
           @run()

     return promise
@@ -143,11 +142,8 @@
   # switches to processing events from this window's queue.
   setActiveWindow: (window)->
     return if window == @active
-    if @active
-      @active._eventQueue.suspend()
     @active = window
     if @active
-      @active._eventQueue.resume()
       @run() # new window, new events

   # Call this method when you know an event is coming, but don't have the event
@@ -267,7 +263,7 @@

   # -- Events --
-
+
   # Add a function to the event queue, to be executed in order.
   enqueue: (fn)->
     if fn && @queue
@@ -359,16 +355,6 @@
     timer.stop() if timer
     return

-  # Used when window goes out of focus, prevents timers from firing
-  suspend: ->
-    for timer in @timers
-      timer.suspend() if timer
-
-  # Used when window goes back in focus, resumes timers
-  resume: ->
-    for timer in @timers
-      timer.resume() if timer
-
   # Returns the timestamp of the next timer event
   next: ->
     next = Infinity
@@ -395,11 +381,7 @@
   # handle  - Node.js timeout handle
   constructor: (@queue, @fn, @delay, @remove)->
     @delay = Math.max(@delay || 0, 0)
-    @resume()
-
-  # Resume (also start) this timer
-  resume: ->
-    return if @handle # already resumed
+    # When timeout fires, queue event for processing during a wait.
     fire = =>
       @queue.enqueue =>
         @queue.browser.emit("timeout", @fn, @delay)
@@ -408,14 +390,8 @@
     @handle = global.setTimeout(fire, @delay)
     @next = Date.now() + @delay

-  # Make sure timer doesn't fire until we're ready for it again
-  suspend: ->
-    global.clearTimeout(@handle)
-    @handle = null
-
   # clearTimeout
   stop: ->
-    @stopped = true
     global.clearTimeout(@handle)
     @remove()

@@ -433,23 +409,20 @@
   # handle  - Node.js interval handle
   constructor: (@queue, @fn, @interval, @remove)->
     @interval =  Math.max(@interval || 0)
-    @resume()
-
-  # Resume (also start) this timer
-  resume: ->
-    return if @handle # already resumed
+    # When interval fires, queue event for processing during a wait.
+    # Don't queue if already processing.
+    pendingEvent = false
     fire = =>
+      @next = Date.now() + @interval
+      if pendingEvent
+        return
+      pendingEvent = true
       @queue.enqueue =>
+        pendingEvent = false
         @queue.browser.emit("interval", @fn, @interval)
         @queue.window._evaluate(@fn)
-      @next = Date.now() + @interval
     @handle = global.setInterval(fire, @interval)
     @next = Date.now() + @interval
-
-  # Make sure timer doesn't fire until we're ready for it again
-  suspend: ->
-    global.clearInterval(@handle)
-    @handle = null

   # clearTimeout
   stop: ->


notify pr creation for specific repos

notify pr creation for specific repos

--- scripts/gitter-activity-notifier.coffee
+++ scripts/gitter-activity-notifier.coffee
@@ -10,6 +10,7 @@
 notificationRoom = process.env.GITTER_ACTIVITY_TARGET;
 disabledIssueActions = ['pinned', 'unpinned', 'locked', 'unlocked', 'milestoned', 'demilestoned'];
 validLabels = ['priority: low', 'priority: medium', 'priority: high', 'priority: urgent'];
+noCheck = ['muffin-crusaders/glitch', 'muffin-crusaders/hubot-scrumminator', 'muffin-crusaders/static-website-blob-browser', 'muffin-crusaders/hubot-azure-scripts', 'muffin-crusaders/angularjs-styleguide-snippets-es6', '/muffin-crusaders/hubot-gitter2'];

 module.exports = (robot) ->
     gitter = new Gitter(process.env.HUBOT_GITTER2_TOKEN)
@@ -99,7 +100,7 @@
                 }
                 if action == "opened"
                     glitchpy.checkPrAssignee(repo, prNum)
-                flag = false
+                flag = repo in noCheck
             else
                 flag = false


Delay a bit on closing the modal.

Delay a bit on closing the modal.

--- scripts/gh-book/auth.coffee
+++ scripts/gh-book/auth.coffee
@@ -243,7 +243,10 @@
             else
               alert('Fork failed')
           .always () =>
-            $modal.modal('hide')
+            # Leave it open for another two seconds, because sometimes
+            # the fork happens so quickly that people are unnerved by
+            # the flashing modal they couldn't read.
+            window.setTimeout((() -> $modal.modal('hide')), 2000)
         pollRepo()


Disable new user party celebration

Disable new user party celebration

--- scripts/newuserparty.coffee
+++ scripts/newuserparty.coffee
@@ -13,15 +13,15 @@
 # Author:
 #   PiXeL16

-messages  = [
-  "A new user hooray!! :tada::tada::smile::tada::tada:",
-  "A new user eeeehhh!! :confetti_ball::tada::smile::tada::confetti_ball:",
-  "Its a new user, yes!! :tada::tada::metal::tada::tada:",
-  "A new user appeared in the Wild  :tada::poke::hatching_chick::tada:",
-]
-
-module.exports = (robot) ->
-
-  robot.catchAll (msg) ->
-    if msg.message.match /new user/i
-        msg.send msg.random messages
+# messages  = [
+#   "A new user hooray!! :tada::tada::smile::tada::tada:",
+#   "A new user eeeehhh!! :confetti_ball::tada::smile::tada::confetti_ball:",
+#   "Its a new user, yes!! :tada::tada::metal::tada::tada:",
+#   "A new user appeared in the Wild  :tada::poke::hatching_chick::tada:",
+# ]
+#
+# module.exports = (robot) ->
+#
+#   robot.catchAll (msg) ->
+#     if msg.message.match /new user/i
+#         msg.send msg.random messages


Complete config sprite test

Complete config sprite test

--- src/test/test_config.coffee
+++ src/test/test_config.coffee
@@ -36,7 +36,13 @@
     t.end()

   test 'config sprite', (t) ->
+    t.ok turtles = world.find_child('turtles'), "can not find turtles"
+    t.ok current = turtles.find_child(), "require current turtle"
     t.ok sprites = world.find_child('sprites'), "can not find sprites"
+    t.ok sprite = sprites.call('NewFromTurtle', current)
+    console.log sprite
+    t.equal sprite.get('i'), 4.5, "sprite has position"
+    t.end()

   test 'config program', (t) ->


Add client-side validation to create TrafficSource

Add client side validation to the dialog box for creating a
TrafficSource so that it doesn't periodically attempt to create a
TrafficSource that doesn't have any contents. Will subsequently add
back-end validation to the same effect.

--- BaragonUI/app/models/Group.coffee
+++ BaragonUI/app/models/Group.coffee
@@ -76,6 +76,7 @@
                 vex.dialog.buttons.NO
             ]
             callback: (data) =>
+                return if data is false
                 @addSource(data).done callback

 module.exports = Group


Referral: move referral redeeming code from utils

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -476,97 +476,6 @@

     return img

-  redeemReferralPoint:(modal)->
-    {vmToResize, sizes} = modal.modal.modalTabs.forms.Redeem.inputs
-
-    data = {
-      vmName : vmToResize.getValue(),
-      size   : sizes.getValue(),
-      type   : "disk"
-    }
-
-    KD.remote.api.JReferral.redeem data, (err, refRes)->
-      return KD.showError errr if err
-      modal.modal.destroy()
-      KD.getSingleton("vmController").resizeDisk data.vm, (err, res)->
-        return KD.showError err if err
-        KD.getSingleton("vmController").emit "ReferralCountUpdated"
-        KD.notify_ """
-          #{refRes.addedSize} #{refRes.unit} extra #{refRes.type} is successfully added to your #{refRes.vm} VM.
-        """
-
-  showRedeemReferralPointModal:->
-    vmController = KD.getSingleton("vmController")
-    vmController.fetchVMs yes, (err, vms)=>
-      return KD.showError err if err
-      return KD.notify_ "You don't have any VMs. Please create one VM" if not vms or vms.length < 1
-
-      KD.remote.api.JReferral.fetchRedeemableReferrals { type: "disk" }, (err, referals)=>
-        return KD.showError err if err
-        return KD.notify_ "You dont have any referrals" if not referals or referals.length < 1
-
-        @modal = modal = new KDModalViewWithForms
-          title                   : "Redeem Your Referral Points"
-          cssClass                : "redeem-modal"
-          content                 : ""
-          overlay                 : yes
-          width                   : 500
-          height                  : "auto"
-          tabs                    :
-            forms                 :
-              Redeem               :
-                callback          : =>
-                  @modal.modalTabs.forms.Redeem.buttons.redeemButton.showLoader()
-                  KD.utils.redeemReferralPoint @
-                buttons           :
-                  redeemButton    :
-                    title         : "Redeem"
-                    style         : "modal-clean-gray"
-                    type          : "submit"
-                    loader        :
-                      color       : "#444444"
-                      diameter    : 12
-                    callback      : -> @hideLoader()
-                  cancel          :
-                    title         : "Cancel"
-                    style         : "modal-cancel"
-                    callback      : (event)-> modal.destroy()
-                fields            :
-                  vmToResize    :
-                    label         : "Select a WM to resize"
-                    cssClass      : "clearfix"
-                    itemClass     : KDSelectBox
-                    type          : "select"
-                    name          : "vmToResize"
-                    validate      :
-                      rules       :
-                        required  : yes
-                      messages    :
-                        required  : "You must select a VM!"
-                    selectOptions : (cb)->
-                      options = for vm in vms
-                        ( title : vm, value : vm)
-                      cb options
-                  sizes           :
-                    label         : "Select Size"
-                    cssClass      : "clearfix"
-                    itemClass     : KDSelectBox
-                    type          : "select"
-                    name          : "size"
-                    validate      :
-                      rules       :
-                        required  : yes
-                      messages    :
-                        required  : "You must select a size!"
-                    selectOptions : (cb)=>
-                      options = []
-                      previousTotal = 0
-                      referals.forEach (referal, i)->
-                        previousTotal += referal.amount
-                        options.push ( title : "#{previousTotal} #{referal.unit}" , value : previousTotal)
-                      cb options
-
-

   compileCoffeeOnClient: (coffeeCode, callback = noop) ->
     require ["//cdnjs.cloudflare.com/ajax/libs/coffee-script/1.6.3/coffee-script.min.js"], (coffeeCompiler) ->


utils: return no if no purpose

utils: return no if no purpose

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -951,6 +951,8 @@

   isChannelCollaborative: (channel) ->

+    return no  unless channel.purpose?
+
     prefix = KD.utils.getCollaborativeChannelPrefix()
     return channel.purpose.slice(0, prefix.length) is prefix