npm like dependency printer

npm like dependency printer

--- src/resolver.coffee
+++ src/resolver.coffee
@@ -98,6 +98,40 @@
   )(tree,m['app'])
   m

+
+getBranchSize = (branch) ->
+  i = 0
+  i++ for key of branch
+  i
+
+getReadableDep = (tree) ->
+  lines = []
+  ((branch, level, parentAry) ->
+    idx = 0
+    bSize = getBranchSize(branch)
+    for key,val of branch
+      hasChildren = getBranchSize(branch[key]) > 0
+      forkChar = if hasChildren then "┬" else "─"
+      isLast = ++idx is bSize
+      turnChar = if isLast then "└" else "├"
+
+      indents = []
+      if level > 1
+        for i in [1...level]
+          gapChar = if parentAry[i] then " " else "│"
+          indents.push gapChar+"  " #add one extra space per (horizontal) length of branch
+
+      if level <= 0
+        lines.push "#{key}" #root
+      else
+        lines.push "#{indents.join('')}#{turnChar}──#{forkChar} #{key}"
+
+      arguments.callee(branch[key], level+1, parentAry.concat(isLast)) #recurse into key's tree (NB: parentAry.length === level)
+    return
+  )(tree, 0, [])
+  lines.join('\n')
+
+
 module.exports = (o) ->
   tree = (new Resolver(o)).getTree()
   if o.targetTree
@@ -119,12 +153,14 @@
     deps :
       'A'  :
         name : 'A'
-        deps : {}
+        deps : { 'F':{name:'F',deps:{}},  'G':{name:'G',deps:{}}  ,  'H':{name:'H',deps:{'Z':{name:'Z', deps:{  'WWW':{name:'W',deps:{}}   }}}},  'underWWW':{name:'underWWW',deps:{}}   }
       'B'  :
         name : 'B'
         deps : {'C': {name:'C', deps: {'E':{name:'E',deps:{}}}  }, 'D' :{name:'D', deps: {}} }
-  console.log JSON.stringify sanitizeTree tree
+  #console.log JSON.stringify sanitizeTree tree

+  smallTree = sanitizeTree tree
+  console.log getReadableDep(smallTree)
   return

 ###


Add show prop (needed for updated RB)

Add show prop (needed for updated RB)

--- src/components/published-modal.cjsx
+++ src/components/published-modal.cjsx
@@ -29,6 +29,7 @@
     exercise = ExerciseStore.get(@props.exerciseId)

     <BS.Modal
+      show={@state.isShowing}
       enforceFocus={false}
       autoFocus={false}
       backdrop={false}


CodeInput: Fixed the check of the value passed to codeinput, before the fix we were stringify strings without parsing them as json first. See #64851

--- src/elements/Input/CodeInput.coffee
+++ src/elements/Input/CodeInput.coffee
@@ -31,7 +31,7 @@

 		value = @__data?[@_name]
 		if value
-			if CUI.util.isPlainObject(value)
+			if CUI.util.isString(value)
 				try # Workaround to format/indent
 					value = JSON.parse(value)


Code Input: Workaround for a bug that the 'ace' library has: Set the mode after initializing the input, so the worker is not loaded. See #64177

--- src/elements/Input/CodeInput.coffee
+++ src/elements/Input/CodeInput.coffee
@@ -1,9 +1,8 @@
-require('ace-builds')
+Ace = require('ace-builds/src-noconflict/ace')
 require('ace-builds/src-min-noconflict/mode-json')
 require('ace-builds/src-min-noconflict/mode-html')
 require('ace-builds/src-min-noconflict/mode-javascript')
 require('ace-builds/src-min-noconflict/mode-css')
-require('ace-builds/webpack-resolver')

 class CUI.CodeInput extends CUI.Input

@@ -24,12 +23,11 @@
 	render: ->
 		super()

-		ace = window.ace
-		@__aceEditor = ace.edit(@__input,
-			mode: "ace/mode/#{@_mode}",
-			selectionStyle: "text",
+		@__aceEditor = Ace.edit(@__input,
 			useWorker: false
+			selectionStyle: "text"
 		)
+		@__aceEditor.getSession().setMode("ace/mode/#{@_mode}");

 		value = @__data?[@_name]
 		if value


disable debug logging

disable debug logging

--- src/steroids.coffee
+++ src/steroids.coffee
@@ -18,7 +18,7 @@
   waitingForComponents: []

   debugMessages: []
-  debugEnabled: true
+  debugEnabled: false

   debug: (options={}) ->
     return unless steroids.debugEnabled


Fix #6.

Fix #6.

--- src/telegram.coffee
+++ src/telegram.coffee
@@ -20,8 +20,12 @@
         Q.fcall -> self.emit 'message', i.message
         .catch (err) -> console.error err

-      id = _.last(data.result)?.update_id || update_id
-      self.polling(id + 1)
+      maxId = _.last(data.result)?.update_id
+      if maxId != undefined
+        maxId += 1
+
+      id = maxId || update_id
+      self.polling(id)

   start: ->
     @getMe().then (data) =>


Call toJSON on arrays

Call toJSON on arrays

--- src/template.coffee
+++ src/template.coffee
@@ -19,6 +19,8 @@
     # transform class representations of models to JSON data
     # otherwise handlebars can't handle them
     for key, val of options
+      if val instanceof Array
+        options[key] = (a.toJSON() for a in val)
       if val?.toJSON
         options[key] = val.toJSON()


typo: mult-dot => multi-dot

typo: mult-dot => multi-dot

--- src/register.coffee
+++ src/register.coffee
@@ -14,7 +14,7 @@
   for ext in CoffeeScript.FILE_EXTENSIONS
     require.extensions[ext] = loadFile

-  # Patch Node's module loader to be able to handle mult-dot extensions.
+  # Patch Node's module loader to be able to handle multi-dot extensions.
   # This is a horrible thing that should not be required. Perhaps, one day,
   # when a truly benevolent dictator comes to rule over the Republik of Node,
   # it won't be.


remove unused code

remove unused code

--- src/reporter.coffee
+++ src/reporter.coffee
@@ -1,32 +1,3 @@
-###
-class Reporter
-
-    constructor: (@lines, @data) ->
-        @errorCount = 0
-        @errors =
-            'generic': [],
-            'store': []
-        @process()
-
-    process: ->
-        if @data['generic'] and @data['generic'] instanceof Array and @data['generic'].length > 0
-            for value, index in @data['generic']
-                msg = "Line #{value}: #{@lines[value-1]}"
-                @errors['generic'].push msg
-                @errorCount += 1
-
-
-        if @data['store']
-            for store, value of @data['store']
-                if value instanceof Array and value.length > 0
-                    for line in value
-                        msg = "#{line}: #{@lines[line-1]}"
-                        @errors['store'].push msg
-                        @errorCount += 1
-
-        return
-###
-
 class Reporter
     constructor: (@storesConf) ->
         @errors = {}


добавил флаг инициализации

добавил флаг инициализации

--- emt.coffee
+++ emt.coffee
@@ -41,8 +41,8 @@
           @rules.push new @Rules[ruleName]
             Rules:  @Rules
             Lib:    @Lib
-
-      @apply()
+      @inited = true
+      # @apply()
       @

   window.EMT = App


Tile class for rendered SVGSymbol

Tile class for rendered SVGSymbol

* Each `Tile` has `key`, `i`, `j`, `symbol`, `xMin`, `yMin`, `xMax`,
  `yMax`, `width`, `height`, `zIndex`.
* Replace `Render`'s `@symbols` (2D array of `SVGSymbol`s, with
  duplicates) and `@coords` (2D array of `{x, y}` objects for layout)
  with `@tiles` (2D array of `Tile`s).

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -1319,6 +1319,19 @@
   @title: "SVG file (convert to PDF/PNG without any tiling)"
   @skipRead: true  # svgink/Inkscape will do actual file reading

+class Tile
+  ###
+  `Tile` represents a rendered tile, which consists of
+  * the input `key` (usually a `String`) and coordinates (`i` and `j`);
+  * an `SVGSymbol` (`symbol`); and
+  * a layout (`xMin`, `yMin`, `xMax`, `yMax`, `width`, `height`).
+  We also store `zIndex` (from the symbol).
+  Note that typically several `Tile`s use the same `SVGSymbol`
+  (assuming some re-use of tiles, e.g., repeated keys).
+  ###
+  constructor: (opts) ->
+    @[key] = value for key, value of opts if opts?
+
 ## Do not need to wrap the following elements in <defs>.
 skipDef = new Set [
   'clipPath'
@@ -1381,10 +1394,9 @@
   makeDOM: -> runWithRender @, => runWithContext (new Context @), =>
     ###
     Main rendering engine, returning an xmldom object for the whole document.
-    Also saves the table of `SVGSymbol`s in `@symbols`, the corresponding
-    coordinates in `@coords`, bounding box in `@xMin`, `@xMax`, `@yMin`,
-    `@yMax`, `@width`, and `@height`, and text content in `@text`
-    for use by `makeTeX`.
+    Also saves the table of `Tile`s (symbols with layout geometry) in `@tiles`,
+    and bounding box in `@xMin`, `@xMax`, `@yMin`, `@yMax`,
+    `@width`, and `@height`.
     ###
     @dom = domImplementation.createDocument SVGNS, 'svg'
     svg = @dom.documentElement
@@ -1401,7 +1413,7 @@
     missing = new Set
     @cache = new Map
     ids = new Set
-    @symbols =
+    @tiles =
       for row, i in @drawing.keys
         for key, j in row
           currentContext.move j, i
@@ -1411,7 +1423,7 @@
             symbol = unrecognizedSymbol
           ## Check cache for this symbol
           if (found = @cacheLookup symbol)?
-            found
+            symbol = found
           else
             ## Include new <symbol> in SVG
             symbol.setId @id key unless symbol.id?  # unrecognizedSymbol has id
@@ -1420,58 +1432,70 @@
               console.warn "Multiple symbols with id '#{symbol.id}': This shouldn't happen, and SVG likely won't load correctly."
             else
               ids.add symbol.id
-            symbol
+          new Tile {i, j, key, symbol,
+            zIndex: symbol.zIndex
+          }
     currentContext = null
     missing = ("'#{key}'" for key from missing)
     if missing.length
       console.warn "Failed to recognize tiles:", missing.join ', '

-    ## Lay out the symbols in the drawing via SVG <use>.
+    ## Lay out the tiles in the drawing via SVG <use>.
     @xMin = @yMin = @xMax = @yMax = 0
     @layers = {}
     y = 0
     colWidths = {}
     @coords = []
-    for row, i in @symbols
-      @coords.push coordsRow = []
+    for row, i in @tiles
       rowHeight = 0
-      for tile in row when not tile.autoHeight
-        if tile.height > rowHeight
-          rowHeight = tile.height
+      for tile in row when not tile.symbol.autoHeight
+        if tile.symbol.height > rowHeight
+          rowHeight = tile.symbol.height
       x = 0
       for tile, j in row
-        coordsRow.push {x, y}
-        continue unless tile?
+        {symbol} = tile
+        tile.xMin = x
+        tile.yMin = y
+        unless symbol?
+          tile.width = tile.height = 0
+          tile.xMax = x
+          tile.yMax = y
+          continue
         @layers[tile.zIndex] ?= []
         @layers[tile.zIndex].push use = @dom.createElementNS SVGNS, 'use'
-        use.setAttribute @hrefAttr(), '#' + tile.id
+        use.setAttribute @hrefAttr(), '#' + symbol.id
         use.setAttribute 'x', x
         use.setAttribute 'y', y
         scaleX = scaleY = 1
-        if tile.autoWidth
+        if symbol.autoWidth
           colWidths[j] ?= Math.max 0, ...(
-            for row2 in @symbols when row2[j]? and not row2[j].autoWidth
-              row2[j].width
+            for row2 in @tiles when row2[j]? and not row2[j].symbol.autoWidth
+              row2[j].symbol.width
           )
-          scaleX = colWidths[j] / tile.width unless tile.width == 0
-          scaleY = scaleX unless tile.autoHeight
-        if tile.autoHeight
-          scaleY = rowHeight / tile.height unless tile.height == 0
-          scaleX = scaleY unless tile.autoWidth
-        ## Scaling of tile is relative to viewBox, so use that to define
-        ## width and height attributes:
+          scaleX = colWidths[j] / symbol.width unless symbol.width == 0
+          scaleY = scaleX unless symbol.autoHeight
+        if symbol.autoHeight
+          scaleY = rowHeight / symbol.height unless symbol.height == 0
+          scaleX = scaleY unless symbol.autoWidth
+        tile.width = symbol.width * scaleX
+        tile.height = symbol.height * scaleY
+        tile.xMax = x + tile.width
+        tile.yMax = y + tile.height
+        ## Scaling of tile is relative to viewBox (which may differ from
+        ## width and height, e.g. when width is actually zero but viewBox
+        ## grows), so use viewBox to define width and height attributes:
         use.setAttribute 'width',
-          (tile.viewBox?[2] ? tile.width) * scaleX
+          (symbol.viewBox?[2] ? symbol.width) * scaleX
         use.setAttribute 'height',
-          (tile.viewBox?[3] ? tile.height) * scaleY
-        if tile.overflowBox?
-          dx = (tile.overflowBox[0] - tile.viewBox[0]) * scaleX
-          dy = (tile.overflowBox[1] - tile.viewBox[1]) * scaleY
+          (symbol.viewBox?[3] ? symbol.height) * scaleY
+        if symbol.overflowBox?
+          dx = (symbol.overflowBox[0] - symbol.viewBox[0]) * scaleX
+          dy = (symbol.overflowBox[1] - symbol.viewBox[1]) * scaleY
           @xMin = Math.min @xMin, x + dx
           @yMin = Math.min @yMin, y + dy
-          @xMax = Math.max @xMax, x + dx + tile.overflowBox[2] * scaleX
-          @yMax = Math.max @yMax, y + dy + tile.overflowBox[3] * scaleY
-        x += tile.width * scaleX
+          @xMax = Math.max @xMax, x + dx + symbol.overflowBox[2] * scaleX
+          @yMax = Math.max @yMax, y + dy + symbol.overflowBox[3] * scaleY
+        x = tile.xMax
         @xMax = Math.max @xMax, x
       y += rowHeight
       @yMax = Math.max @yMax, y
@@ -1612,7 +1636,7 @@

 ''' + out
   makeTeX: (filename, relativeDir) ->
-    @makeDOM() unless @symbols?
+    @makeDOM() unless @tiles?
     filename = path.parse filename
     basename = filename.base[...-filename.ext.length]
     if relativeDir
@@ -1675,10 +1699,10 @@
         \\begin{picture}(#{@width},#{@height})%
           \\put(0,0){\\includegraphics[width=#{@width}\\unitlength]{\\currfiledir #{relativeDir ? ''}#{basename}}}%
     """]
-    for row, i in @symbols
-      for symbol, j in row
-        {x, y} = @coords[i][j]
-        for text in symbol.text
+    for row, i in @tiles
+      for tile, j in row
+        {xMin, yMin} = tile
+        for text in tile.symbol.text
           tx = parseNum(text.getAttribute('x')) ? 0
           ty = parseNum(text.getAttribute('y')) ? 0
           content = (
@@ -1693,7 +1717,7 @@
           else #if /^start\b/.test anchor  # default
             wrap = '\\llap{'
           # "@height -" is to flip between y down (SVG) and y up (picture)
-          lines.push "    \\put(#{x+tx},#{@height - (y+ty)}){\\color{#{attributeOrStyle(text, 'fill') or 'black'}}#{wrap}#{content}#{wrap and '}'}}%"
+          lines.push "    \\put(#{xMin+tx},#{@height - (yMin+ty)}){\\color{#{attributeOrStyle(text, 'fill') or 'black'}}#{wrap}#{content}#{wrap and '}'}}%"
     lines.push """
         \\end{picture}%
       \\endgroup


Avoid implicit `export default` when explicit CommonJS export

If `exports.default` or `module.exports` are present in the source file,
skip adding implicit `export default`.

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -24,16 +24,39 @@
 ## Register `require` hooks of Babel and CoffeeScript,
 ## so that imported/required modules are similarly processed.
 unless window?
-  ## Babel plugin to add implicit `export default` to last line of program,
-  ## to simulate the effect of `eval` but in a module context.
+  ###
+  Babel plugin to add implicit `export default` to last line of program,
+  to simulate the effect of `eval` but in a module context.
+  Only added if there isn't already an `export default` or `exports.default`
+  in the code, and when the last line is an object or function expression
+  (with the idea that it wouldn't do much by itself).
+  ###
   implicitFinalExportDefault = ({types}) ->
     visitor:
       Program: (path) ->
         body = path.get 'body'
         return unless body.length  # empty program
-        for part in body
-          if types.isExportDefaultDeclaration part
-            return  # already an export default, so don't add one
+        ## Check for existing `export default` or `exports.default` or
+        ## `exports['default']`, in which case definitely don't add one.
+        exportedDefault = false
+        path.traverse(
+          ExportDefaultDeclaration: (path) ->
+            exportedDefault = true
+          MemberExpression: (path) ->
+            {node} = path
+            check = (key, value) ->
+              types.isIdentifier(node.object) and
+              node.object.name == key and (
+                (types.isIdentifier(node.property) and
+                 node.property.name == value) or
+                (types.isStringLiteral(node.property) and
+                 node.property.value == value)
+              )
+            exportedDefault or= check('exports', 'default') or
+                                check('module', 'exports')
+            return
+        )
+        return if exportedDefault
         last = body[body.length-1]
         lastNode = last.node
         if types.isExpressionStatement(last) and (
@@ -47,7 +70,7 @@
           exportLast.innerComments = lastNode.innerComments
           exportLast.trailingComments = lastNode.trailingComments
           last.replaceWith exportLast
-        undefined
+        return

   babelConfig =
     plugins: [


Fix rendering so image is same size as terminal

--- src/terminal.coffee
+++ src/terminal.coffee
@@ -5,23 +5,23 @@
 module.exports = class Terminal

   constructor: (config) ->
-    @schema          = "/apps/gnome-terminal/profiles/#{config.profile}/"
-    @pixelsPerRow    = config.pixelsPerRow
-    @pixelsPerColumn = config.pixelsPerColumn
+    @schema      = "/apps/gnome-terminal/profiles/#{config.profile}/"
+    @rowHeight   = config.rowHeight
+    @columnWidth = config.columnWidth
     @_forbidScrolling() unless config.allowScroll

   replaceBackground: (fileName) ->
     @_setProperty 'background_image', fileName, 'string'

-  getDimensions: -> new Promise (resolve) ->
-    spawn('resize').stdout.on 'data', (data) ->
+  getDimensions: -> new Promise (resolve) =>
+    spawn('resize').stdout.on 'data', (data) =>
       data    = String data
       lines   = data.split '\n'
       columns = +lines[0].match(/^COLUMNS=([0-9]+);$/)[1]
       rows    = +lines[1].match(/^LINES=([0-9]+);$/)[1]
       resolve
-        width:  columns * @pixelsPerColumn
-        height: rows * @pixelsPerRow
+        width:  columns * @columnWidth
+        height: rows * @rowHeight

   _forbidScrolling: ->
     @_setProperty 'scroll_background', 'false'


remove/move tags

remove/move tags

--- src/timeline.coffee
+++ src/timeline.coffee
@@ -18,6 +18,12 @@
                                 zoomMax: 10 * 60 * 1000
                                 zoomMin: 1000
                                 showMajorLabels: false
+                                editable:
+                                        add: false
+                                        updateTime: true
+                                        updateGroup: true
+                                        remove: true
+                                selectable: true
                         node = $('<div>')
                         timeline = new vis.Timeline(node[0], @props.tags.tags, opts)
                         timeline.on 'rangechanged', @_onDragEnd


Fix up the triangle in the tool tips

Fix up the triangle in the tool tips

 - seems a bit of a mess; basically trial-and-error because I don't know what I'm doing

--- src/tooltips.coffee
+++ src/tooltips.coffee
@@ -39,7 +39,7 @@
     tridiv.style("color", fill) if fill?
     tridiv.style("font-size", "#{fontsize}px") if fontsize?

-    tripad = tridiv.style("font-size").replace("px", "")/4.0
+    tripad = tridiv.style("font-size").replace("px", "")*1.0

     objects.on("mouseover.#{tipclass}", (d,i) ->
           mouseX = d3.event.pageX*1.0
@@ -55,24 +55,27 @@
               posX = mouseX + pad
               posY = mouseY - tipbox_height/2.0
               triChar = '\u25C0'
-              shiftX = -tipbox_height/2-tripad
-              shiftY = tripad
+              shiftX = -tripad
+              shiftY = tripad/4
           else if direction == "west"
               posX = mouseX - tipbox_width*1.0 - pad
               posY = mouseY - tipbox_height/2.0
               triChar = '\u25B6'
-              shiftX = +tipbox_height/2.0-tripad
-              shiftY = tripad
+              shiftX = -tripad + tipbox_width
+              shiftY = tripad/4
           else if direction == "north"
               posX = mouseX - tipbox_width/2.0
               posY = mouseY - tipbox_height - pad
               triChar = '\u25BC'
-              shiftX = tipbox_width/2.0
+              shiftX = tipbox_width/2.0-tripad
+              shiftY = pad+tripad
           else if direction == "south"
               posX = mouseX - tipbox_width/2.0
               posY = mouseY + pad
               triChar = '\u25B2'
-              shiftX = tipbox_width/2.0
+              shiftX = tipbox_width/2.0-tripad
+              shiftY = -pad+tripad
+

           tipdiv.style("left", "#{posX}px")
@@ -86,6 +89,8 @@

           tridiv.style("left", "#{triX}px")
                 .style("top",  "#{triY}px")
+                .style("width", tipbox_width)
+                .style("height", tipbox_height)
                 .transition()
                 .duration(in_duration/1.2)
                 .style("opacity", 1.0))


WIP: SheetRunner: Fix value changes for undefined functions

--- src/main/runtime/SheetRunner.coffee
+++ src/main/runtime/SheetRunner.coffee
@@ -86,14 +86,10 @@
     @userFunctionImpls[name] = functionImpl
     @sheet[name] = functionImpl.theFunction

-    initValue = @_sheetValue name
-    subj = @userFunctionSubjects[name] or (@userFunctionSubjects[name] = new Rx.BehaviorSubject(initValue))
+    subj = @userFunctionSubjects[name] or (@userFunctionSubjects[name] = @_newUserFunctionSubject(name, @_sheetValue name))
     #    subj.sourceSub?.dispose()
     #    source = @_userFunctionStream funcDef, functionImpl.theFunction, functionImpl.functionNames
     #    subj.sourceSub = source.subscribe subj
-    if not subj.valueChangesSub
-      subj.valueChangesSub = subj.distinctUntilChanged().subscribe (value) =>
-        @valueChanges.onNext [name, value]

     @_recalculate()
 #    if not subj.observeStream then subj.observeStream = subj.observeOn Rx.Scheduler.timeout
@@ -165,6 +161,12 @@

   #  private functions

+  _newUserFunctionSubject: (name, initialValue) ->
+    subj = new Rx.BehaviorSubject(initialValue)
+    subj.valueChangesSub = subj.distinctUntilChanged().subscribe (value) =>
+      @valueChanges.onNext [name, value]
+    subj
+
   _recalculate: ->
     for name, subj of @userFunctionSubjects
       subj.onNext @_sheetValue name
@@ -174,12 +176,6 @@

   _userFunctionSubject: (name) -> @userFunctionSubjects[name]
   _unknownUserFunctionSubject: (name) -> (@userFunctionSubjects[name] = @_newUserFunctionSubject(name, new CalculationError(name, "Unknown name")))
-
-  _newUserFunctionSubject: (name, initialValue) ->
-    subj = new Rx.BehaviorSubject(initialValue)
-    subj.valueChangesSub = subj.subscribe (value) =>
-      @valueChanges.onNext [name, value]
-    subj

   _userFunctionStream: (func, theFunction, functionNames) ->
     if _.includes(functionNames, func.name) then return new Rx.BehaviorSubject( new CalculationError func.name, 'Formula uses itself')


Adding notification for inventory release

--- src/scripts/jenkins-notifier.coffee
+++ src/scripts/jenkins-notifier.coffee
@@ -73,6 +73,12 @@
             console.log "JEEEEEEEEEEEEEENKIIIIIIIIINS"
             console.log res.statusCode
           robot.send envelope, "I hope you know what you're doing..."
+        if data.name == 'JAVA-InventoryService (Prod)'
+          console.log "LEEEEEEEEEEEROOOOOOOOOOOOOY"
+          http.get 'http://wall:5051/shipit-inventory', (res) ->
+            console.log "JEEEEEEEEEEEEEENKIIIIIIIIINS"
+            console.log res.statusCode
+          robot.send envelope, "I hope you know what you're doing..."

     catch error
       console.log "jenkins-notify error: #{error}. Data: #{req.body}"


Correctly search for only folders if search is trailed by '/'

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -783,8 +783,11 @@
     _match : (words, s, is_dir) =>
         s = s.toLowerCase()
         for t in words
-            if t == '/'
+            console.log(t[t.length - 1])
+            if t[t.length - 1] == '/'
                 if not is_dir
+                    return false
+                else if s.indexOf(t.slice(0, -1)) == -1
                     return false
             else if s.indexOf(t) == -1
                 return false


make file readable!

make file readable!


mp4: Fix incorrect parsing of NAL units

mp4: Fix incorrect parsing of NAL units

--- mp4.coffee
+++ mp4.coffee
@@ -212,18 +212,22 @@

   parseH264Sample: (buf) ->
     # The format is defined in ISO 14496-15 5.2.3
-
-    # Why do we need a length header here even we
-    # already have the same data in stsz box???
+    # <length><NAL unit> <length><NAL unit> ...
+
     avcCBox = @videoTrakBox.find 'avcC'
     lengthSize = avcCBox.lengthSizeMinusOne + 1
     bits = new Bits buf
-    length = bits.read_bits lengthSize * 8
-    nalUnitType = bits.get_byte_at(0) & 0x1f
-    bits.skip_bytes length
+
+    nalUnits = []
+
+    while bits.has_more_data()
+      length = bits.read_bits lengthSize * 8
+      nalUnits.push bits.read_bytes(length)

     if bits.get_remaining_bits() isnt 0
-      throw new Error "remaining bits is not zero: #{bits.get_remaining_bits()}"
+      throw new Error "number of remaining bits is not zero: #{bits.get_remaining_bits()}"
+
+    return nalUnits

   readChunk: (chunkNumber, sampleNumber, trakBox) ->
     sttsBox = trakBox.find 'stts'
@@ -255,13 +259,26 @@

     samples = @readChunk nextChunkNumber, nextSampleNumber, @videoTrakBox

-    for sample in samples
-#      @parseH264Sample sample.data
-      # Remove length header
-      sample.data = sample.data[4..]
+    numSamples = samples.length
+    index = 0
+    # The size of samples array may be altered in-place
+    while index < samples.length
+      nalUnits = @parseH264Sample samples[index].data
+      if nalUnits.length >= 1
+        pts = samples[index].pts
+        time = samples[index].time
+
+        samples[index].data = nalUnits[0]
+        for nalUnit in nalUnits[1..]
+          index++
+          samples[index...index] =
+            pts: pts
+            time: time
+            data: nalUnit
+      index++

     @consumedVideoChunks++
-    @consumedVideoSamples += samples.length
+    @consumedVideoSamples += numSamples
     @bufferedVideoTime = samples[samples.length-1].time
     @bufferedVideoSamples = @bufferedVideoSamples.concat samples


Some comments. Found spot to add number check.

--- pca-visualize.coffee
+++ pca-visualize.coffee
@@ -36,6 +36,7 @@
 		# required, otherwise an error is returned.
 		@app.get "/pca", ( req, res ) =>

+			# Force attributes to have been specified.
 			if not req.query["attributes"]?
 				return @_error_out res, "Required field 'attributes' not specified."

@@ -126,7 +127,8 @@
 		if typeof @data is "function"
 			return @data filter, attributes, cb

-		# At this point we know we're dealing with a finite set of data.
+		# TODO add logic here regarding checking the type of
+		# the attributes that have been specified. Force 'number'.

 		# If both filter and attributes are null,
 		# return a list of attributes.


Fix unit tests

Fix unit tests

--- services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee
+++ services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee
@@ -296,6 +296,7 @@
 			@email = "Jane.Doe@example.com"
 			@priveleges = "readOnly"
 			@addedUser = { _id: "added-user" }
+			@ProjectEditorHandler.buildUserModelView = sinon.stub().returns(@addedUser)
 			@CollaboratorsHandler.addUserToProject = sinon.stub().callsArgWith(3, null, @addedUser)
 			@EditorRealTimeController.emitToRoom = sinon.stub()
 			@callback = sinon.stub()
@@ -314,9 +315,9 @@
 					@EditorRealTimeController.emitToRoom.calledWith(@project_id, "userAddedToProject", @addedUser).should.equal true
 					done()

-			it "should return true to the callback", (done)->
+			it "should return the user to the callback", (done)->
 				@EditorController.addUserToProject @project_id, @email, @priveleges, (err, result)=>
-					result.should.equal true
+					result.should.equal @addedUser
 					done()


embedly: cache status update results in memory

--- workers/social/lib/social/models/messages/newstatusupdate/index.coffee
+++ workers/social/lib/social/models/messages/newstatusupdate/index.coffee
@@ -122,7 +122,12 @@

   @getActivityType =-> require './statusactivity'

+  cachedEmbedlyResult = {}
+
   @fetchDataFromEmbedly = (urls, options, callback)->
+
+    if result = cachedEmbedlyResult[urls]
+      return callback null, result

     urls = [urls]  unless Array.isArray urls

@@ -136,7 +141,11 @@
       , options

       options.urls = urls
-      api.extract options, callback
+      api.extract options, (err, result)->
+        return callback err, result  if err
+
+        cachedEmbedlyResult[urls] = result
+        callback err, result

   @create$ = secure (client, data, callback)->
     @create client, data, callback


improved timing

improved timing

--- src/dt-jquery.coffee
+++ src/dt-jquery.coffee
@@ -22,8 +22,8 @@
         @fn = @fn()
         @builder = @template.xml ? @template
         # defaults
-        opts.timeoutexecution ?= '20ms'
-        opts.execution        ?= '4ms'
+        opts.timeoutexecution ?= '32ms'
+        opts.execution        ?= '8ms' # half of 16ms (60 FPS), the other half is for the browser
         opts.timeout          ?= '120ms'
         opts.toggle           ?= on
         @$ ?= opts.jquery ? opts.$ ? window?.$


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -1,4 +1,32 @@
+###*
+The Expirable module deactives a GameObject after a specified duration.
+If a duration is specified the object will update that many times. If -1 is
+specified the object will have an unlimited duration.

+

+enemy = GameObject
+  x: 50
+  y: 30
+  duration: 5
+
+enemy.include(Durable)
+
+enemy.I.active
+# => true
+
+5.times ->
+  enemy.update()
+
+enemy.I.active
+# => false
+

+
+@name Durable
+@module
+@constructor
+@param {Object} I Instance variables
+@param {Core} self Reference to including object
+###
 Expirable = (I, self) ->
   Object.reverseMerge I,
     duration: -1


social-worker: eliminate useless logs

social-worker: eliminate useless logs

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -102,9 +102,7 @@
         callback {sessionToken, context, connection:delegate:account}
       else
         console.log "this is not a proper account".red
-        console.log ''+account.constructor.pid
-        console.log ''+JAccount.pid
-        console.log JAccount is account.constructor
+        console.log "constructor is JAccount", JAccount is account.constructor
         # koding.emit 'error', message: 'this is not a proper account'
 }


warn to info because it isn't a problem

warn to info because it isn't a problem

--- js/src/crypto/KeypairSerializer.coffee
+++ js/src/crypto/KeypairSerializer.coffee
@@ -29,7 +29,7 @@

     @hydrate : (serialized) ->
       if !serialized || isUndefined(serialized)
-        log.warn('keypair not initialized')
+        log.info('keypair not initialized')
         return undefined

       privateKeyBuffer = Forge.util.createBuffer(atob(serialized), 'raw')


BF-119: Testing on server

BF-119: Testing on server

--- app/assets/javascripts/model/binary/pullqueue.coffee
+++ app/assets/javascripts/model/binary/pullqueue.coffee
@@ -73,6 +73,7 @@

     pullLoadingCount = @pullLoadingCount

+    console.log "requesting: ", [bucket_x, bucket_y, bucket_z]
     Cube.setBucketByAddress3(bucket_x, bucket_y, bucket_z, zoomStep, Cube.LOADING_PLACEHOLDER_OBJECT)
     pullLoadingCount++


Fix duplication of CKEditor when used with nested form

--- app/assets/javascripts/rails_admin/ra.widgets.coffee
+++ app/assets/javascripts/rails_admin/ra.widgets.coffee
@@ -130,5 +130,5 @@
       if not window.CKEDITOR
         $(window.document).append('<script src="' + options['jspath'] + '"><\/script>')
       if instance = window.CKEDITOR.instances[this.id]
-        window.CKEDITOR.remove(instance)
+        instance.destroy(true)
       window.CKEDITOR.replace(this, options['options'])


Use .empty()

Use .empty()

--- app/assets/javascripts/search_autocomplete.js.coffee
+++ app/assets/javascripts/search_autocomplete.js.coffee
@@ -176,7 +176,7 @@

     if @originalState._location is ''
-      @locationBadgeEl.html('')
+      @locationBadgeEl.empty()
     else
       @addLocationBadge(
         value: @originalState._location


remove any existing modal before opening the cart one

--- app/assets/javascripts/stall/add-to-cart-form.coffee
+++ app/assets/javascripts/stall/add-to-cart-form.coffee
@@ -53,6 +53,7 @@
     @setLoading(false)

   onSuccess: (e, resp) ->
+    @removeExistingModal()
     @$modal = $(resp).appendTo('body').modal()
     @updateWidget()

@@ -62,8 +63,16 @@
     else if ($counter = $('[data-cart-quantity-counter]')).length
       $counter.text(@$modal.data('cart-total-quantity'))

+  # Hide current modal and remove it from DOM when it's hidden
+  #
+  removeExistingModal: ->
+    return unless ($modal = $('.modal')).length
+    $modal.one('hidden.bs.modal', -> $modal.remove())
+    $modal.modal('hide')
+
   # Displays errors in a tooltip on the form submit button, listing different
   # errors and disabling the submit button
+  #
   refreshErrorsDisplay: (options = {}) ->
     @clearErrorMessages()
     @displayErrorMessages(options) if @errors.length


fixed samedate issue

fixed samedate issue

--- app/assets/javascripts/views/provider_view.js.coffee
+++ app/assets/javascripts/views/provider_view.js.coffee
@@ -23,7 +23,7 @@
   setEffectiveDate: (e) ->
     effectiveStartDate = $(".effective-date-picker.start").val()
     effectiveEndDate = $(".effective-date-picker.end").val()
-    if Date.parse(effectiveEndDate) < Date.parse(effectiveStartDate)
+    if Date.parse(effectiveEndDate) <= Date.parse(effectiveStartDate)
       return alert("That date range is invalid. Please check and try again.")
     if confirm("Caution! Changing the reporting period may initially cause a significant delay. Do you wish to continue?")
       user = PopHealth.currentUser.get 'username'


:bug: uncovered after reverting to Motion.CurrentSelection.

--- lib/operators.coffee
+++ lib/operators.coffee
@@ -133,7 +133,7 @@
   linewiseAlias: true
   execute: ->
     if @target.isLinewise?()
-      points = @editor.getCursorBufferPositions()
+      points = (s.getBufferRange().start for s in @editor.getSelections())
     if _.any @target.select()
       @setTextToRegister @editor.getSelectedText()
       for selection in @editor.getSelections()


update patterns based on atom/language-todo

--- lib/show-todo.coffee
+++ lib/show-todo.coffee
@@ -9,15 +9,23 @@
     # title, regex, title, regex...
     findTheseRegexes:
       type: 'array'
-      default: [
+      default: [ # based on atom/language-todo
         'FIXMEs'
-        '/FIXME:?(.+$)/g'
+        '/\\b@?FIXME:?(.+$)/g'
         'TODOs'
-        '/TODO:?(.+$)/g'
+        '/\\b@?TODO:?(.+$)/g'
         'CHANGEDs'
-        '/CHANGED:?(.+$)/g'
+        '/\\b@?CHANGED:?(.+$)/g'
         'XXXs'
-        '/XXX:?(.+$)/g'
+        '/\\b@?XXX:?(.+$)/g'
+        'IDEAs'
+        '/\\b@?IDEA:?(.+$)/g'
+        'HACKs'
+        '/\\b@?HACK:?(.+$)/g'
+        'NOTEs'
+        '/\\b@?NOTE:?(.+$)/g'
+        'REVIEWs'
+        '/\\b@?REVIEW:?(.+$)/g'
       ]
       items:
         type: 'string'


Guard against no selected entry when collapsing

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -232,8 +232,7 @@
     selectedEntry.view().expand() if selectedEntry instanceof DirectoryView

   collapseDirectory: ->
-    selectedEntry = @selectedEntry()
-    if directory = selectedEntry.closest('.expanded.directory').view()
+    if directory = @selectedEntry()?.closest('.expanded.directory').view()
       directory.collapse()
       @selectEntry(directory)


Use atom-shell's "shell.moveToTrash()" to replace "$native.moveToTrash()"

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -5,6 +5,7 @@
 FileView = require './file-view'
 Dialog = require './dialog'
 fsUtils = require 'fs-utils'
+shell = require 'shell'
 $ = require 'jquery'
 _ = require 'underscore'

@@ -250,7 +251,7 @@
     atom.confirm(
       "Are you sure you would like to delete the selected #{entryType}?",
       "You are deleting #{entry.getPath()}",
-      "Move to Trash", (=> $native.moveToTrash(entry.getPath())),
+      "Move to Trash", (=> shell.moveToTrash(entry.getPath())),
       "Cancel", null
       "Delete", (=> fsUtils.remove(entry.getPath()))
     )


:shirt:

:shirt:

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -226,7 +226,7 @@

   openAfterPromise: (uri, options) ->
     if promise = @currentlyOpening.get(uri)
-      promise.then => atom.workspace.open(uri, options)
+      promise.then -> atom.workspace.open(uri, options)
     else
       atom.workspace.open(uri, options)

@@ -507,7 +507,7 @@

       handleError(errorMessage) if failed

-    showProcess = new BufferedProcess({command, args, options:{shell:false}, stderr, exit})
+    showProcess = new BufferedProcess({command, args, options: {shell: false}, stderr, exit})
     showProcess.onWillThrowError ({error, handle}) ->
       handle()
       handleError(error?.message)


:lipstick: Sort requires and remove extra spaces

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -1,10 +1,10 @@
 path = require 'path'
 shell = require 'shell'
-showInFinder = require 'mac-open'

 _ = require 'underscore-plus'
 {$, ScrollView} = require 'atom'
 fs = require 'fs-plus'
+showInFinder = require 'mac-open'

 AddDialog = null  # Defer requiring until actually needed
 MoveDialog = null # Defer requiring until actually needed
@@ -269,7 +269,7 @@
     entry = @selectedEntry()
     return unless entry
     entryType = if entry instanceof DirectoryView then 'directory' else 'file'
-    showInFinder entry.getPath(), { R: true }, (error) ->
+    showInFinder entry.getPath(), {R: true}, (error) ->
       if error?
         atom.confirm
           message: "Opening #{entryType} in Finder failed"


websocket magic

websocket magic

--- ops.coffee
+++ ops.coffee
@@ -233,6 +233,16 @@
   @$result.append a
   @success()

+fn 'websocket', 'Runs commands from a web socket', (url) ->
+  ws = new WebSocket url
+  ws.onopen = => @cli.text 'Connected'
+  ws.onclose = =>
+    @cli.text 'Closed. Reconnect:'
+    @cli.example "websocket #{JSON.stringify url}"
+  ws.onmessage = (e) => @run e.data
+  ws.onerror = => @cli.text 'Error'
+  @success()
+
 fn 'q', 'Escapes a Graphite metric query', (targets...) ->
   for t in targets
     unless $.type(t) is 'string'


Autocomplete uses new findRoot

Autocomplete uses new findRoot

--- lib/autocomplete/clang-provider.coffee
+++ lib/autocomplete/clang-provider.coffee
@@ -7,6 +7,7 @@
 {existsSync} = require 'fs'
 ClangFlags = require 'clang-flags'
 config = require '../config'
+utils = require '../utils'

 module.exports =
 class ClangProvider
@@ -38,15 +39,15 @@
     command = 'clang'
     args = @buildClangArgs(editor, row, column, language)
     options =
-      cwd: atom.project.getPaths().filter((project) -> ~atom.workspace.getActiveTextEditor().getPath().indexOf project)?[0]
+      cwd: utils.getRoot atom.workspace.getActiveTextEditor().getPath()
       input: editor.getText()
     new Promise (resolve) =>
       allOutput = []
-      stdout = (output) => allOutput.push(output)
-      stderr = (output) => console.log output
+      stdout = (output) -> allOutput.push output
+      stderr = (output) -> console.log output
       exit = (code) => resolve(@handleCompletionResult(allOutput.join('\n'), code, prefix))
       bufferedProcess = new BufferedProcess({command, args, options, stdout, stderr, exit})
-      bufferedProcess.process.stdin.setEncoding = 'utf-8';
+      bufferedProcess.process.stdin.setEncoding = 'utf-8'
       bufferedProcess.process.stdin.write(editor.getText())
       bufferedProcess.process.stdin.end()

@@ -93,9 +94,9 @@
     outputLines = result.match(completionsRe)

     if outputLines?
-        return (@convertCompletionLine(line, prefix) for line in outputLines)
+      return (@convertCompletionLine(line, prefix) for line in outputLines)
     else
-        return []
+      return []

   buildClangArgs: (editor, row, column, language) ->
     settings = config.settings()


Fix syntax error

Fix syntax error

--- modules/giphy.coffee
+++ modules/giphy.coffee
@@ -10,7 +10,7 @@

     @registerCommand 'giphy', { aliases: ['gif'], allowDM: true }, (msg, q)=>
       try
-        { data } = await Giphy.get('search', { qs: { q, api_key: 'dc6zaTOxFJmzC' })
+        { data } = await Giphy.get('search', { qs: { q, api_key: 'dc6zaTOxFJmzC' } })
       return msg.reply 'No results' if not data.length
       msg.reply @chance.pickone(data).url
 }


Makes publish:bintray task override files

--- tasks/publish.coffee
+++ tasks/publish.coffee
@@ -52,6 +52,7 @@
           'X-Bintray-Package': manifest.name
           'X-Bintray-Version': manifest.version
           'X-Bintray-Publish': 1
+          'X-Bintray-Override': 1
           'X-Bintray-Debian-Distribution': 'stable'
           'X-Bintray-Debian-Component': 'main'
           'X-Bintray-Debian-Architecture': archType


FIX: typo in _cleanNodeStyles

FIX: typo in _cleanNodeStyles

--- src/formatter.coffee
+++ src/formatter.coffee
@@ -178,7 +178,7 @@
       style = $.trim style
       pair = style.split(':')
       continue unless pair.length = 2
-      styles[$.trim(pair[0])] = $trim(pair[1]) if pair[0] in allowedStyles
+      styles[$.trim(pair[0])] = $.trim(pair[1]) if pair[0] in allowedStyles

     $node.css styles if Object.keys(styles).length > 0
     $node


fixed pkg path

fixed pkg path

Former-commit-id: 5c57b45989f93808b82c6e4d25cdc8f64ee8a323 [formerly b19d87cb9877b10821d047dcfe0fb75ca8a8e013]
Former-commit-id: 4a5a8aaa2db94af5ee8a29911b38a283e7c1d517

--- src/g/package.coffee
+++ src/g/package.coffee
@@ -32,6 +32,6 @@
     if @g.config.get("debug")
       url = @development[pkg]
     else
-      url = "http://wzrd.in/bundle/#{pkg}/latest"
+      url = "http://wzrd.in/bundle/#{pkg}@latest"

     url


fetch from serialization cache

fetch from serialization cache

--- chrome/content/zotero-better-bibtex/BetterBibTeXPatternFormatter.coffee
+++ chrome/content/zotero-better-bibtex/BetterBibTeXPatternFormatter.coffee
@@ -15,7 +15,7 @@

     return {} if @item.itemType in ['attachment', 'note']

-    if item.date
+    if @item.date
       dates = @item.date.split('/')
       dates = @item.date.split('_') unless dates.length == 2
       @date = if dates.length == 2 then dates[0] else @item.date


Teamwork: Removed the sleep, used kdwrap instead of it.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -203,19 +203,19 @@
   doCurlRequest: (path, callback = noop) ->
     vmController = KD.getSingleton "vmController"
     vmController.run
-        withArgs: "sleep 2 ; curl -kLs #{path}"
-        vmName  : vmController.defaultVmName
-      , (err, contents) =>
-        extension = FSItem.getFileExtension path
-        error     = null
-
-        switch extension
-          when "json"
-            try
-              manifest = JSON.parse contents
-            catch err
-              error    = "Manifest file is broken for #{path}"
-
-            callback error, manifest
-          when "md"
-            callback errorMessage, KD.utils.applyMarkdown error, contents
+      withArgs: "kdwrap curl -kLs https://raw.github.com/koding/Teamwork/master/Playgrounds/manifest-dev.json"
+      vmName  : vmController.defaultVmName
+    , (err, contents) =>
+      extension = FSItem.getFileExtension path
+      error     = null
+
+      switch extension
+        when "json"
+          try
+            manifest = JSON.parse contents
+          catch err
+            error    = "Manifest file is broken for #{path}"
+
+          callback error, manifest
+        when "md"
+          callback errorMessage, KD.utils.applyMarkdown error, contents


Teamwork: Updating Teamwork Firebase instance name.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -10,7 +10,7 @@
       joinModalTitle      : "Join a coding session"
       joinModalContent    : "
Paste the session key that you received and start coding together.
"
       shareSessionKeyInfo : "
This is your session key, you can share this key with your friends to work together.
"
-      firebaseInstance    : "teamwork-local"
+      firebaseInstance    : "kd-prod-1"
       sessionKey          : @getOptions().sessionKey
       enableChat          : yes
       panels              : [


Fix for Firefox 26.0 having undefined window.location.username

--- src/ki-router.coffee
+++ src/ki-router.coffee
@@ -141,7 +141,10 @@

   targetHostSame: (aTag) =>
     l = window.location
-    aTag.host == l.host && aTag.protocol == l.protocol && aTag.username == l.username && aTag.password == aTag.password
+    locationUserName = l.username
+    if !locationUserName
+      locationUserName = ""
+    aTag.host == l.host && aTag.protocol == l.protocol && aTag.username == locationUserName && aTag.password == aTag.password

   attachLocationChangeListener: =>
     if @pushStateSupport


Use xhr instead of request.head

Use xhr instead of request.head

--- index.cjsx
+++ index.cjsx
@@ -38,7 +38,6 @@
   '203.104.209.55',
   '203.104.209.102'
 ]
-POI_PROXY = "http://127.0.0.1:#{config.get('poi.port', 12450)}/"

 zerofill = (n) ->
   pad = "000"
@@ -95,12 +94,11 @@
   updateNotifyConfig: (ship_id) ->
     setConfig = (key, audio) ->
       config.set(key, audio)
-      request.get
-        url: audio
-        proxy: POI_PROXY
-      , (error, response, body) ->
-        return if !error and response?.statusCode == 200
-        config.set(key, null)
+      xhr = new XMLHttpRequest()
+      xhr.open("GET", audio)
+      xhr.onabort = xhr.onerror = xhr.onload = (e) ->
+        config.set(key, null) if @status != 200
+      xhr.send()

     return unless ship_id > 0
     admiral_id = parseInt(window._nickNameId) || 0


Fixed NPE in HTTPS

Fixed NPE in HTTPS

--- src/lib/https.coffee
+++ src/lib/https.coffee
@@ -63,7 +63,7 @@

             @server = net.createServer (c) =>
                 libHTTPS.getClientHello c, (err, host, buf) =>
-                    @log.debug err, host, buf.length
+                    @log.debug err, host, buf?.length
                     if err?
                         # Connection is neither a TLS stream nor an HTTPS stream containing an SNI
                         @log.debug gLineInfo "TCP handling: "+err.message


added additional comments

added additional comments

--- src/lib/utils.coffee
+++ src/lib/utils.coffee
@@ -43,6 +43,7 @@
 # Selector containing the available internationalization attributes/handlers
 # which is used by `i18nProcess` to query all elements.
 i18nSelector   = "[#{i18nAttributes.join '],['}]"
+# Mapping of all timers currently being managed by `utils`.
 timings        = {}

 # Private functions


Disable classroom member role switching

Disable classroom member role switching

--- picoCTF-web/web/coffee/classroom-management.coffee
+++ picoCTF-web/web/coffee/classroom-management.coffee
@@ -20,12 +20,12 @@
       @props.refresh()
     ).bind this

-  switchUserRole: (tid, role) ->
-    apiCall "POST", "http://localhost:5000/api/v1/group/teacher/role_switch", {gid: @props.gid, tid: tid, role: role}
-    .done ((resp) ->
-      apiNotify resp
-      @props.refresh()
-    ).bind this
+  # switchUserRole: (tid, role) ->
+  #   apiCall "POST", "http://localhost:5000/api/v1/group/teacher/role_switch", {gid: @props.gid, tid: tid, role: role}
+  #   .done ((resp) ->
+  #     apiNotify resp
+  #     @props.refresh()
+  #   ).bind this

   render: ->
     if @props.teacher
@@ -41,10 +41,10 @@

User

       </Button>

-    if @props.teacher
-      switchUser = <Button onClick={@switchUserRole.bind(null, @props.tid, "member")}>Make Member</Button>
-    else
-      switchUser = <Button onClick={@switchUserRole.bind(null, @props.tid, "teacher")}>Make Teacher</Button>
+    # if @props.teacher
+    #   switchUser = <Button onClick={@switchUserRole.bind(null, @props.tid, "member")}>Make Member</Button>
+    # else
+    #   switchUser = <Button onClick={@switchUserRole.bind(null, @props.tid, "teacher")}>Make Teacher</Button>

     <ListGroupItem>
       <Row className="row">


Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -126,8 +126,8 @@
     level_difficulty: "Dificuldade: "
     play_as: "Jogar Como"
     spectate: "Espectar"
-#    players: "players"
-#    hours_played: "hours played"
+    players: "jogadores"
+    hours_played: "horas jogadas"

   contact:
     contact_us: "Contacte o CodeCombat"
@@ -513,7 +513,7 @@
     toggle_grid: "Ativar/desativar a sobreposição da grelha."
     toggle_pathfinding: "Ativar/desativar a sobreposição do encontrador de caminho."
     beautify: "Embelezar o código ao estandardizar a formatação."
-#    maximize_editor: "Maximize/minimize code editor."
+    maximize_editor: "Maximizar/minimizar o editor de código."
     move_wizard: "Mover o seu Feiticeiro pelo nível."

   admin:
@@ -910,7 +910,7 @@
     unknown: "Erro desconhecido."

   resources:
-#    sessions: "Sessions"
+    sessions: "Sessões"
     your_sessions: "As Suas Sessões"
     level: "Nível"
     social_network_apis: "APIs das Redes Sociais"
@@ -926,7 +926,7 @@
     patched_model: "Documento Fonte"
     model: "Modelo"
     system: "Sistema"
-#    systems: "Systems"
+    systems: "Sistemas"
     component: "Componente"
     components: "Componentes"
     thang: "Thang"
@@ -941,16 +941,16 @@
     source_document: "Documento Fonte"
     document: "Documento"
     sprite_sheet: "Folha de Sprite"
-#    employers: "Employers"
-#    candidates: "Candidates"
+    employers: "Empregadores"
+    candidates: "Candidatos"
     candidate_sessions: "Sessões de Candidatos"
     user_remark: "Observação do Utilizador"
-#    user_remarks: "User Remarks"
+    user_remarks: "Observações de Utilizador"
     versions: "Versões"
     items: "Itens"
-#    wizard: "Wizard"
-#    achievement: "Achievement"
-#    clas: "CLAs"
+    wizard: "Feiticeiro"
+    achievement: "Conquista"
+    clas: "CLAs"
 #    play_counts: "Play Counts"

   delta:


Site - Only check badges if logged in.

Site - Only check badges if logged in.

--- app/models/Badge.coffee
+++ app/models/Badge.coffee
@@ -62,7 +62,7 @@
   slug: => @name.replace /\s/g, "_"

   checkAward: =>
-    if @condition.func({user: User.current, archive: @archive()}) then @award()
+    if User.current and @condition.func({user: User.current, archive: @archive()}) then @award()

   award: =>
     if User.current?


Adjust default scores to current content

--- app/scripts/game.coffee
+++ app/scripts/game.coffee
@@ -53,6 +53,10 @@
         secondary: 2
         super: 4
         pause: 9
+        up: 12
+        down: 13
+        left: 14
+        right: 15

     Crafty.e('GamepadControls, PlayerAssignable')
       .controls
@@ -60,6 +64,10 @@
         fire: 0
         secondary: 2
         super: 4
+        up: 12
+        down: 13
+        left: 14
+        right: 15

     # Simply start splashscreen
     #Crafty.enterScene('Game', script: 'Lunch')
@@ -73,16 +81,16 @@

     defInit = 'SPD'
     list = [
-      { initials: defInit, score: 200000 }
-      { initials: defInit, score: 100000 }
-      { initials: defInit, score: 80000 }
-      { initials: defInit, score: 60000 }
-      { initials: defInit, score: 40000 }
-      { initials: defInit, score: 20000 }
-      { initials: defInit, score: 10000 }
+      { initials: defInit, score: 6000 }
       { initials: defInit, score: 5000 }
+      { initials: defInit, score: 4000 }
+      { initials: defInit, score: 3000 }
       { initials: defInit, score: 2000 }
+      { initials: defInit, score: 1500 }
       { initials: defInit, score: 1000 }
+      { initials: defInit, score: 500 }
+      { initials: defInit, score: 200 }
+      { initials: defInit, score: 100 }
     ].concat @loadedList
     _.sortBy(list, 'score').reverse()


fix for logos

fix for logos

--- app/scripts/main.coffee
+++ app/scripts/main.coffee
@@ -41,11 +41,15 @@
         lemgramResults?.abort()
         statsResults?.abort()

-$(window).resize (event) ->
+toggleLogos = () ->
     if $(window).width() > 1050
         $(".logos").show()
     else
         $(".logos").hide()
+toggleLogos()
+
+$(window).resize (event) ->
+    toggleLogos()

 $.when(loc_dfd, deferred_domReady).then ((loc_data) ->
     c.log "preloading done, t = ", $.now() - t


Properly catch home error

Properly catch home error

--- apps/home/routes.coffee
+++ apps/home/routes.coffee
@@ -19,7 +19,7 @@
       res.render 'page',
         heroUnits: heroUnits.models
         eoy_2016: eoyData
-    .catch (thing, err) ->
+    .catch (err) ->
       res.render 'page', heroUnits: []

 module.exports.featuredArtworks = (req, res, next) ->


Cache homepage demo blocks

Cache homepage demo blocks

--- apps/home/routes.coffee
+++ apps/home/routes.coffee
@@ -2,27 +2,31 @@
 { API_URL } = require('sharify').data
 { DEMO_USER_AUTH_TOKEN } = process.env
 GQL = require '../../lib/graphql'
+cached = require '../../lib/cached'

 @index = (_req, res, next) ->
-  send =
-    token: DEMO_USER_AUTH_TOKEN
-    query: """
-      {
-        channel(id: "are-na-connect-everything") {
-          blocks(per: 100) {
-            ... blockThumb
+  # Caches for 24 hours
+  cached 'homepage:demo-blocks', 86400, ->
+    send =
+      token: DEMO_USER_AUTH_TOKEN
+      query: """
+        {
+          channel(id: "are-na-connect-everything") {
+            blocks(per: 100) {
+              ... blockThumb
+            }
           }
         }
-      }

-      #{require '../../components/block_v2/queries/block'}
-    """
+        #{require '../../components/block_v2/queries/block'}
+      """

-  GQL send
-    .then ({ channel: { blocks }}) ->
-      res.locals.sd.DEMO_BLOCKS = demoBlocks = shuffle blocks
+    GQL send

-      res.render 'index',
-        demoBlocks: demoBlocks
+  .then ({ channel: { blocks }}) ->
+    res.locals.sd.DEMO_BLOCKS = demoBlocks = shuffle blocks

-    .catch next
+    res.render 'index',
+      demoBlocks: demoBlocks
+
+  .catch next


About link fix

About link fix

--- assets/js/arcade.coffee
+++ assets/js/arcade.coffee
@@ -54,6 +54,8 @@
   $(".about-link").click ->
     $(".about-view").removeClass("off")
     $(".game-menu").addClass("off")
+    $(".game-frame").hide()
+    $(".game-view").hide()

   $(".about-back").click ->
     $(".about-view").addClass("off")


Creates scope tree

Creates scope tree

--- assets/js/tracer.coffee
+++ assets/js/tracer.coffee
@@ -1,9 +1,59 @@
+class Tree
+
+	constructor: (@_parent, @_root, children) ->
+		@_children = []
+		@add child for child in children if children instanceof Array
+
+	parent: (parent) =>
+		if parent is undefined
+			@_parent
+		else
+			@_parent = parent
+			@
+
+	root: (root) =>
+		if root is undefined
+			@_root
+		else
+			@_root = root
+			@
+
+	add: (child) ->
+		@_children.push if child instanceof Tree then child.parent @ else new Tree @, child
+		@
+
+	toString: (ind) ->
+		ind ?= 0
+		str = ''
+		str += (' ' for i in [0...(4*ind)]).join ''
+		str += '('
+		str += @_root
+		if @_children.length
+			str += ', ['
+			str += '\n'
+			str += child.toString(ind + 1) + ',\n' for child in @_children
+			str += (' ' for i in [0...(4*ind)]).join ''
+			str += ']'
+		str += ')'
+		return str
+
+	toObject: ->
+		return {
+			root: @_root,
+			children: (child.toObject() for child in @_children)
+		}
+
+	toJSON: ->
+		return JSON.stringify @toObject(), null, 2
+
+
 editor = ace.edit 'code'
 editor.setTheme 'ace/theme/monokai'
 editor.getSession().setMode 'ace/mode/javascript'
 editor.setShowPrintMargin false
 editor.getSession().on 'change', (event) ->
-	display parse editor.getValue()
+	try
+		display treeify(parse editor.getValue()).toString()

 output = ace.edit 'output'
 output.setTheme 'ace/theme/monokai'
@@ -27,3 +77,96 @@
 		data = { 'JSON error': err }
 	output.setValue(data)
 	output.clearSelection()
+
+
+Syntax =
+	ArrayExpression: ["elements"]
+	AssignmentExpression: ["left", "right"]
+	BinaryExpression: ["left", "right"]
+	BlockStatement: ["body"]
+	BreakStatement: ["label"]
+	CallExpression: ["callee", "arguments"]
+	CatchClause: ["param", "guard", "body"]
+	ConditionalExpression: ["test", "alternate", "consequent"]
+	ContinueStatement: ["label"]
+	DebuggerStatement: []
+	DoWhileStatement: ["init", "test", "update", "body"]
+	EmptyStatement: []
+	ExpressionStatement: ["expression"]
+	ForInStatement: ["left", "right", "body"]
+	ForStatement: ['init', 'test', 'update', "body"]
+	FunctionDeclaration: ["id", "params", "defaults", "rest", "body"]
+	FunctionExpression: ["id", "params", "defaults", "rest", "body"]
+	Identifier: [] # Special
+	IfStatement: ["test", "consequent", "alternate"]
+	LabeledStatement: ["label", "body"]
+	Literal: []
+	LogicalExpression: ["left", "right"]
+	MemberExpression: ["object", "property"]
+	NewExpression: ["callee", "arguments"]
+	ObjectExpression: ["properties"] # Special
+	Pattern: []
+	Program: ["body"]
+	#Property: []
+	ReturnStatement: ["argument"]
+	SequenceExpression: ["expressions"]
+	SwitchCase: ["test", "consequent"]
+	SwitchStatement: ["discriminant", "cases"]
+	ThisExpression: []
+	ThrowStatement: ["argument"]
+	TryStatement: ["block", "handler", "guardedHandlers", "finalizer"]
+	UnaryExpression: ["argument"]
+	UpdateExpression: ["argument"]
+	VariableDeclaration: ["declarations"]
+	VariableDeclarator: ["init"] # Special
+	WhileStatement: ["test", "body"]
+	WithStatement: ["object", "body"]
+	#'ArrayPattern': ['elements'], # Harmony
+	#'ComprehensionBlock': ['left', 'right'], # Harmony
+	#'ComprehensionExpression': ['blocks', 'filter'], # Harmony
+	#'ForOfStatement': ['left', 'right', 'body'], # Harmony, Special
+	#'GeneratorExpression': ['blocks', 'filter'], # Harmony
+	#'LetExpression': ['head', 'body'], # Harmony, Special
+	#'LetStatement': ['head', 'body'], # Harmony
+	#'ObjectPattern': ['properties'], # Harmony, Special
+	#'YieldExpression': ['argument'] # Harmony
+
+
+treeify = (block, scope, identifiers) ->
+	scope = new Tree(null, []) unless scope?
+
+	if not block then return scope
+
+	if block instanceof Array
+		treeify(el, scope, identifiers) for el in block
+		return scope
+
+	switch block.type
+		when 'Identifier'
+			if identifiers
+				if -1 == scope.root().indexOf block.name
+					scope.root().push block.name
+				else
+					#console.log 'Variable %s already defined', block.name
+		when 'FunctionDeclaration', 'FunctionExpression'
+			local = new Tree(scope, [])
+			treeify block.id, scope
+			treeify block.params, local, true
+			treeify block.defaults, local
+			treeify block.rest, local, true
+			treeify block.body, local
+			scope.add local
+		when 'ObjectExpression'
+			for prop in block.properties
+				treeify prop.value, scope
+		when 'VariableDeclarator'
+			treeify block.id, scope, true
+			treeify block.init, scope
+		else
+			if Syntax.hasOwnProperty block.type
+				for prop in Syntax[block.type]
+					treeify block[prop], scope
+			else
+				console.log 'No handler for type #{block.type}'
+
+	scope


Removing _tasks.coffe file

Removing _tasks.coffe file

--- app/assets/javascripts/_tasks.coffee
+++ app/assets/javascripts/_tasks.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/


Further validate color inputs by removing '-' (#238)

--- app/assets/javascripts/brands.coffee
+++ app/assets/javascripts/brands.coffee
@@ -15,5 +15,5 @@
     ## Validation(mask)
     $('.form-group__field--color').mask 'ASSSSSS', 'translation':
       A: pattern: /['#']/
-      S: pattern: /[A-Za-z-0-9]/
+      S: pattern: /[A-Za-z0-9]/


That's a weird javascript

That's a weird javascript

--- app/assets/javascripts/cookie.coffee
+++ app/assets/javascripts/cookie.coffee
@@ -11,8 +11,8 @@
       .replace /(%[0-9A-Z]{2})+/g, decodeURIComponent

-Cookies.defaults['path'] = PREFIX
-Cookies.defaults['expires'] = 365
+Cookies.defaults.path = PREFIX
+Cookies.defaults.expires = 365

 window.Cookie =
   put: (name, value, days) ->


Also try calling preventDefault, since last didn't work

--- app/assets/volume/spreadsheet.coffee
+++ app/assets/volume/spreadsheet.coffee
@@ -1141,6 +1141,7 @@
         editScope.unedit = (event) ->
           doneEdit(event, unedit(event))
           event.stopPropagation()
+          event.preventDefault()
           false

         editSelect = (event) ->


skip cache directory error when empty

skip cache directory error when empty

--- app/coffee/OutputCacheManager.coffee
+++ app/coffee/OutputCacheManager.coffee
@@ -83,6 +83,7 @@
 		# look in compileDir for build dirs and delete if > N or age of mod time > T
 		fs.readdir cacheRoot, (err, results) ->
 			if err?
+				return callback(null) if err.code == 'ENOENT'	# cache directory is empty
 				logger.error err: err, project_id: cacheRoot, "error clearing cache"
 				return callback(err)


Fix error preventing level load when scripts are misconfigured

--- app/lib/scripts/ScriptManager.coffee
+++ app/lib/scripts/ScriptManager.coffee
@@ -148,9 +148,10 @@
       for note in script.noteChain or []
         if note.surface?.focus?
           surfaceModule = _.find note.modules or [], (module) -> module.surfaceCameraNote
-          cameraNote = surfaceModule.surfaceCameraNote true
-          @publishNote cameraNote
-          return
+          if surfaceModule
+            cameraNote = surfaceModule.surfaceCameraNote true
+            @publishNote cameraNote
+            return

   destroy: ->
     @onEndAll()


allow for passing test api endpoint with custom port

--- src/sailthru.coffee
+++ src/sailthru.coffee
@@ -35,8 +35,8 @@
             binary_data_params = []
         parse_uri = url.parse uri
         options =
-            host: parse_uri.host
-            port: if parse_uri.protocol is 'https:' then 443 else 80
+            host: parse_uri.hostname
+            port: parse_uri.port !== undefined ? parse_uri.port : (parse_uri.protocol === 'https:' ? 443 : 80)
             path: parse_uri.pathname
             method: method
             query: data


Show recents first in talk image selector

- Closes #1264

--- app/talk/comment-image-selector.cjsx
+++ app/talk/comment-image-selector.cjsx
@@ -15,7 +15,7 @@
     @setRecents()

   setRecents: ->
-    @props.user.get('recents')
+    @props.user.get('recents', {sort: '-created_at'})
       .then (subjects) => @setState {subjects}

   setQuery: (id) ->


now you can actually subsitute values into uncertainties

--- coffeequate/src/terminals.coffee
+++ coffeequate/src/terminals.coffee
@@ -401,8 +401,8 @@
 			[@label]

 		sub: (substitutions, uncertaintySubstitutions) ->
-			if @label of substitutions
-				substitute = substitutions[@label]
+			if @label of uncertaintySubstitutions
+				substitute = uncertaintySubstitutions[@label]
 				if substitute.copy?
 					return substitute.copy()
 				else


config.sl-production: update kontrold host configurations

--- config/main.sl-production.coffee
+++ config/main.sl-production.coffee
@@ -295,7 +295,8 @@
       apiHost     : "68.68.97.179"
       apiPort     : 80
       port        : 8080
-      switchHost  : "koding.com"
+      kodingHost  : "koding.com"
+      socialHost  : "social.koding.com"
     api           :
       port        : 80
       url         : "http://kontrol0.sj.koding.com"


update itempane

update itempane

--- content/itemPane/itemPane.coffee
+++ content/itemPane/itemPane.coffee
@@ -14,13 +14,13 @@
         return Zotero.Promise.coroutine((item, mode, index) ->
           yield original.apply(@, arguments)

-          if index == 0 # details pane
-            itemPane.addCitekeyRow()
-            display = itemPane.global.document.getElementById(id)
-            citekey = KeyManager.get(item.id)
-            debug('ItemPane: displaying citekey', display, citekey)
-            display.value = citekey.citekey
-            display.classList[if citekey.pinned then 'remove' else 'add']('citekey-dynamic')
+          itemPane.addCitekeyRow(item.id) if index == 0 # details pane
+
+          observer = new MutationObserver((mutations) ->
+            itemPane.addCitekeyRow(item.id)
+            return
+          )
+          observer.observe(itemPane.global.document.getElementById('dynamic-fields'), {childList: true})

           return
         )
@@ -28,31 +28,31 @@

     @addCitekeyRow()

-    observer = new MutationObserver((mutations) =>
-      for mutation in mutations
-        @addCitekeyRow() if mutation.target.childNodes.length == 1
-      return
-    )
-    observer.observe(@global.document.getElementById('dynamic-fields'), {childList: true})
-
-  addCitekeyRow: ->
+  addCitekeyRow: (itemID) ->
     if @global.document.getElementById(id)
       debug('ItemPane: citekey row already present')
       return

-    template = @global.document.getElementById(id + '-template')
-    row = template.cloneNode(true)
-    row.setAttribute('id', id + '-row')
-    row.setAttribute('hidden', false)
-    row.getElementsByClassName('better-bibtex-citekey-display')[0].setAttribute('id', id)
+    if !(display = @global.document.getElementById(id))
+      template = @global.document.getElementById(id + '-template')
+      row = template.cloneNode(true)
+      row.setAttribute('id', id + '-row')
+      row.setAttribute('hidden', false)
+      display = row.getElementsByClassName('better-bibtex-citekey-display')[0]
+      display.setAttribute('id', id)

-    fields = @global.document.getElementById('dynamic-fields')
-    if fields.childNodes.length > 1
-      fields.insertBefore(row, fields.childNodes[1])
-    else
-      fields.appendChild(row)
+      fields = @global.document.getElementById('dynamic-fields')
+      if fields.childNodes.length > 1
+        fields.insertBefore(row, fields.childNodes[1])
+      else
+        fields.appendChild(row)

-    debug('ItemPane: citekey row added')
+      debug('ItemPane: citekey row added')
+
+    if itemID?
+      citekey = KeyManager.get(itemID)
+      display.value = citekey.citekey
+      display.classList[if citekey.pinned then 'remove' else 'add']('citekey-dynamic')

     return


Removed unnecessary check

Removed unnecessary check

--- core/app/backbone/views/popovers/statistics_popover_content_view.coffee
+++ core/app/backbone/views/popovers/statistics_popover_content_view.coffee
@@ -12,4 +12,4 @@

   onRender: ->
     @buttonRegion.show @options.buttonView
-    @statisticsRegion.show @options.statisticsView if @options.statisticsView?
+    @statisticsRegion.show @options.statisticsView


Slight update to OpenSeadragon configuration

--- apps/artwork/client/deep_zoom.coffee
+++ apps/artwork/client/deep_zoom.coffee
@@ -48,7 +48,7 @@
         maxZoomPixelRatio: 1.0
         minZoomImageRatio: 0.9
         zoomPerClick: @zoomPerClick
-        zoomPerScroll: 1.1
+        zoomPerScroll: 1.25
         constrainDuringPan: true
         visibilityRatio: 1
         tileSources: @image.deepZoomJson()


fix apply button on auction page

fix apply button on auction page

--- apps/partnerships/client/view.coffee
+++ apps/partnerships/client/view.coffee
@@ -20,11 +20,13 @@
     @setupLiaisonsFading()

   intercept: (e) ->
-    if $(e.currentTarget).attr('href') != '/apply/institution' #Apply button on institution page must load a new page
-      e.preventDefault()
-      Backbone.history.navigate $(e.currentTarget).attr('href'), trigger: true
-      if $(e.currentTarget).hasClass('partnerships-nav-apply-link')
-        mediator.once 'scrolled:position', => $('#apply-name-field').focus()
+    href = $(e.currentTarget).attr('href')
+    if href isnt '/apply/institution' and
+      href isnt '/apply/auction'
+        e.preventDefault()
+        Backbone.history.navigate $(e.currentTarget).attr('href'), trigger: true
+        if $(e.currentTarget).hasClass('partnerships-nav-apply-link')
+          mediator.once 'scrolled:position', => $('#apply-name-field').focus()

   cacheSelectors: ->
     @$nav = @$ '.partnerships-section-nav'


Make Doc#text the same as Entry#text

Make Doc#text the same as Entry#text

--- assets/javascripts/models/doc.coffee
+++ assets/javascripts/models/doc.coffee
@@ -4,7 +4,7 @@
   constructor: ->
     super
     @reset @
-    @text = @name.toLowerCase()
+    @text = @toEntry().text

   reset: (data) ->
     @resetEntries data.entries
@@ -35,7 +35,7 @@
     "#{app.indexHost()}/#{@index_path}?#{@mtime}"

   toEntry: ->
-    new app.models.Entry
+    @entry ||= new app.models.Entry
       doc: @
       name: @name
       path: 'index'


[FEAT] 更新了部分参数

[FEAT] 更新了部分参数

* 增加了 initialNumber 参数用于设置初始化数量
* 增加了 minNumber 参数用户设置最少子 View 个数
* 重命名了 limit 为 MaxNumber，用于限制最大数量

--- backbone/clonable_view.module.coffee
+++ backbone/clonable_view.module.coffee
@@ -1,6 +1,6 @@
 module.exports = class ClonableView extends Backbone.View

-  template: JST["clonable"]
+  template: JST["./clonable"]
   className: 'clonable'
   subviewContainer: '.subview-container'
   extraButtons: '.extra-buttons'
@@ -20,16 +20,28 @@
       options['subviewOptions'] = {}
     if not options['allowBlank']?
       options['allowBlank'] = false
-    if options['limit']?
-      options['limit'] = parseInt options['limit']
+    if options['maxNumber']?
+      options['maxNumber'] = parseInt options['maxNumber']
+    if not options['minNumber']?
+      options['minNumber'] = 1
+    else
+      options['minNumber'] = parseInt options['minNumber']
+    if not options['initialNumber']?
+      options['initialNumber'] = 1
     @options = options

   render: =>
     @$el.html(@template())
-
+    index = 0
+    if @options['minNumber'] > @options['initialNumber']
+      number = @options['minNumber']
+    else
+      number = @options['initialNumber']
     # Initial the first subview
     if not @options['allowBlank']
-      @add()
+      while index < number
+        @add()
+        index += 1
     @redrawExtraButtons()
     @

@@ -42,11 +54,14 @@
     if childrenLength == 1 and @options.allowBlank
       @$('.extra-buttons .btn-primary').prop('disabled', false);
       conditionAvailabel = true
-    if @options.allowBlank and @$el.find(@subviewContainer).children().length != 0
+    if @options['allowBlank'] and @$el.find(@subviewContainer).children().length != 0
       @$('.extra-buttons .btn-danger').prop('disabled', false)
       conditionAvailabel = true
-    if @options.limit and childrenLength == @options.limit
+    if @options['maxNumber'] and childrenLength == @options.maxNumber
       @$('.extra-buttons .btn-primary').prop('disabled', true)
+      conditionAvailabel = true
+    if childrenLength == @options['minNumber']
+      @$('.extra-buttons .btn-danger').prop('disabled', true)
       conditionAvailabel = true
     if conditionAvailabel
       return
@@ -78,7 +93,8 @@
     if evt.target
       $el = $(evt.target)
     else
-      $el = @$('.extra-buttons:last .btn-danger') # 假设最后一个删除按钮
+      # Assume the latest delete button
+      $el = @$('.extra-buttons:last .btn-danger')
     @undelegateEvents()
     if @options.allowBlank and @$el.children().length == 1
       el = $el.parents('.control-group').find(@subviewContainer).children()
@@ -91,6 +107,10 @@
     @delegateEvents()

   clear: =>
+    # Limit to only one child.
+    child = $(@$('.control-group')[0]).clone()
+    @$el.html(child)
+    # Empty the child.
     @$('.control-group').find(@subviewContainer).empty()

   setOptions: (options) =>


test around help text

test around help text

--- test/bin_spec.coffee
+++ test/bin_spec.coffee
@@ -2,9 +2,9 @@
 pkg = require("../package.json")

 describe "bin/cypress", ->
-  it "is executable", (done) ->
-    cp.exec "bin/cypress --version", (err, stdout, stderr) ->
-      expect(stdout).to.eq(pkg.version + "\n")
+  it "displays help text", (done) ->
+    cp.exec "bin/cypress", (err, stdout, stderr) ->
+      expect(stdout).to.include("-v, --version  output the version of the cli and desktop app")
       done()

   it "is set in package.json", ->


unbind test and fix

unbind test and fix

--- test/bindable.coffee
+++ test/bindable.coffee
@@ -4,7 +4,7 @@
   o = $.extend({}, Bindable())

   o.bind("test", -> ok true)
-
+
   o.trigger("test")

 test "Multiple bindings", 2, ->
@@ -12,20 +12,34 @@

   o.bind("test", -> ok true)
   o.bind("test", -> ok true)
-
+
   o.trigger("test")
-
+
 test "#trigger arguments", ->
   o = $.extend({}, Bindable())
-
+
   param1 = "the message"
   param2 = 3

   o.bind "test", (p1, p2) ->
     equal(p1, param1)
     equal(p2, param2)
-
+
   o.trigger "test", param1, param2
+
+test "#unbind", ->
+  o = $.extend({}, Bindable())
+
+  callback = ->
+    ok false
+
+  o.bind "test", callback
+  o.unbind "test", callback
+  o.trigger "test"
+
+  o.bind "test", callback
+  o.unbind "test"
+  o.trigger "test"

 module()


half unit test

half unit test

--- test/function.coffee
+++ test/function.coffee
@@ -1,4 +1,5 @@
 humanizing = require '..'
+humanizer = humanizing.humanizer
 assert = require 'assert'
 ms = require 'ms'

@@ -53,3 +54,12 @@
     assert.equal result, '2.25 minutes'
     result = humanizing time, units: ['hour']
     assert.equal result, '0.0375 hours'
+
+  it 'makes no decimals of the smallest unit when halfUnit is false', ->
+    time = ms('2m') + ms('30s')
+    result = humanizer units: ['minute', 'second'], halfUnit: false
+    assert.equal result(time), '2 minutes, 30 seconds'
+
+    time = ms('1h') + ms('30m')
+    result = humanizing time, units: ['hour', 'minute'], halfUnit: false
+    assert.equal result, '1 hour, 30 minutes'


isEditor renamed to publishEditorTo

isEditor renamed to publishEditorTo

--- both/collections.coffee
+++ both/collections.coffee
@@ -6,7 +6,7 @@
 Meteorstrap.defaultTheme = -> Themes.findOne({default:true})?._id

 if Meteor.isServer
-  Meteorstrap.isEditor = -> true
+  Meteorstrap.publishEditorTo = -> true

   adminFields =
     name: 1
@@ -35,7 +35,7 @@

     # publish the editor info
     Meteor.publish 'MeteorstrapEditor', ->
-      if Meteorstrap.isEditor.apply @
+      if Meteorstrap.publishEditorTo.apply @
         Themes.find {}, {fields:adminFields}
       else
         return null


Change frontend's run_tests call to match.

--- browser/frontend.coffee
+++ browser/frontend.coffee
@@ -219,9 +219,9 @@
   test: (args) ->
     return "Usage: test all|[class(es) to test]" unless args[0]?
     if args[0] == 'all'
-      testing.run_tests [], stdout, -> controller.reprompt()
+      testing.run_tests [], stdout, false, -> controller.reprompt()
     else
-      testing.run_tests args, stdout, -> controller.reprompt()
+      testing.run_tests args, stdout, false, -> controller.reprompt()
     return null
   javap: (args) ->
     return "Usage: javap class" unless args[0]?


Register a parsed_geojson property

Register a parsed_geojson property

--- bokehjs/src/coffee/source/geojson_data_source.coffee
+++ bokehjs/src/coffee/source/geojson_data_source.coffee
@@ -5,9 +5,12 @@
 class GeoJSONDataSource extends ColumnDataSource.Model

   initialize: (options) ->
-    @set('geojson', JSON.parse(options.geojson))
+    super(options)
+    @register_property('parsed_geojson',
+        () -> JSON.parse(@get('geojson'))
+      , true)
+    @add_dependencies('parsed_geojson', this, ['geojson'])
     @set('data', @geojson_to_column_data())
-    super(options)

   _get_new_list_array: (length) ->
     array = new Array(length)
@@ -20,7 +23,7 @@
     return nan_array

   geojson_to_column_data: () ->
-    geojson = @get('geojson')
+    geojson = @get('parsed_geojson')

     if geojson.type not in ['GeometryCollection', 'FeatureCollection']
       throw new Error('Bokeh only supports type GeometryCollection and FeatureCollection at top level')


Update run.coffee

Update run.coffee

--- src/tasks/run.coffee
+++ src/tasks/run.coffee
@@ -8,7 +8,7 @@
   # @param [String] device One of `$ adb devices` or "emulator"
   # @param [Function] fn Optional callback to run when the child process terminates.
   local = (platform, device, fn) ->
-    cmd = "phonegap run #{platform} #{helpers.setVerbosity()}"
+    cmd = "cordova run #{platform} #{helpers.setVerbosity()}"
     if device
       if device == 'emulator'
         cmd += ' --emulator'
@@ -23,7 +23,7 @@
   # @param [String] device Ignored
   # @param [Function] fn Optional callback to run when the child process terminates.
   remote = (platform, device, fn) ->
-    cmd = "phonegap remote run #{platform} #{helpers.setVerbosity()}"
+    cmd = "cordova remote run #{platform} #{helpers.setVerbosity()}"
     helpers.exec cmd, fn

   run: (platform, device, fn) ->


Parse command args as options

Parse command args as options

--- src/uninstall.coffee
+++ src/uninstall.coffee
@@ -23,8 +23,11 @@
   showHelp: (argv) -> @parseOptions(argv).showHelp()

   run: (options) ->
-    if options.commandArgs.length is 0
-      options.callback("Must specify a package name to uninstall")
+    {callback} = options
+    options = @parseOptions(options.commandArgs)
+
+    if options.argv._.length is 0
+      callback("Must specify a package name to uninstall")
       return

     packagesDirectory = path.join(config.getAtomDirectory(), 'packages')
@@ -33,7 +36,7 @@
       process.stdout.write "Uninstalling #{packageName} "
       unless _.contains(packages, packageName)
         process.stdout.write '\u2717\n'.red
-        options.callback("#{packageName} does not exist in #{packagesDirectory}")
+        callback("#{packageName} does not exist in #{packagesDirectory}")
         return

       try
@@ -42,7 +45,7 @@
         process.stdout.write '\u2713\n'.green
       catch error
         process.stdout.write '\u2717\n'.red
-        options.callback("Failed to delete #{packageName}: #{error.message}")
+        callback("Failed to delete #{packageName}: #{error.message}")
         return

-    options.callback()
+    callback()


GameObject.Effect

GameObject.Effect

--- test/tweening.coffee
+++ test/tweening.coffee
@@ -11,7 +11,7 @@
     o.update(1)
     o.trigger "afterUpdate", 1

-    equals o.I.x, Math.min(i + 1
+    equals o.I.x, Math.min(i + 1, 10)

 # Clear out the module
 module()


Fix tests.

Fix tests.

--- test/server_spec.coffee
+++ test/server_spec.coffee
@@ -47,11 +47,6 @@
       server.queuePlayer("derp", generateTeam()).should.be.empty
       server.queues[gen.DEFAULT_GENERATION].size().should.equal(1)

-    it "does not queue null players", ->
-      server = new BattleServer()
-      server.queuePlayer(null, generateTeam()).should.be.empty
-      server.queues[gen.DEFAULT_GENERATION].size().should.equal(0)
-
     it "does not queue players already queued", ->
       server = new BattleServer()
       server.queuePlayer("derp", generateTeam()).should.be.empty
@@ -653,7 +648,7 @@
       server = new BattleServer()
       for i in [1..4]
         server.join(new User("user#{i}"))
-        server.queuePlayer("user#{i}", [ Factory("Magikarp") ])
+        server.queuePlayer("user#{i}", generateTeam())
       server.beginBattles (err, battleIds) ->
         battleIds.length.should.equal(2)
         done()


added jinvitation check to createteam.test file

--- servers/lib/server/handlers/createteam.test.coffee
+++ servers/lib/server/handlers/createteam.test.coffee
@@ -22,6 +22,7 @@
 JGroup                              = null
 JAccount                            = null
 JSession                            = null
+JInvitation                         = null
 JDomainAlias                        = null

@@ -37,6 +38,7 @@
       JGroup
       JAccount
       JSession
+      JInvitation
       JDomainAlias } = koding.models

     done()
@@ -57,7 +59,10 @@
   # creating team with an unregistered user
   it 'should handle team creation correctly when valid data provided', (done) ->

-    createTeamRequestParams = generateCreateTeamRequestParams()
+    inviteeEmail            = generateRandomEmail()
+    createTeamRequestParams = generateCreateTeamRequestParams
+      body        :
+        invitees  : inviteeEmail

     { slug
       email
@@ -124,6 +129,19 @@
           queue.next()

       ->
+        # expecting invitation to be created with correct data
+        params = { email : inviteeEmail }
+
+        JInvitation.one params, (err, invitation) ->
+          expect(err)                   .to.not.exist
+          expect(invitation)            .to.exist
+          expect(invitation.code)       .to.exist
+          expect(invitation.email)      .to.be.equal inviteeEmail
+          expect(invitation.status)     .to.be.equal 'pending'
+          expect(invitation.groupName)  .to.be.equal slug
+          queue.next()
+
+      ->
         # expecting session to be created
         params = { username }

@@ -131,6 +149,106 @@
           expect(err)               .to.not.exist
           expect(session)           .to.exist
           expect(session.username)  .to.be.equal username
+          queue.next()
+
+      -> done()
+
+    ]
+
+    daisy queue
+
+
+  it 'should save relationships correctly', (done) ->
+
+    slug                    = generateRandomString()
+    userId                  = ''
+    groupId                 = ''
+    username                = generateRandomString()
+    accountId               = ''
+    createTeamRequestParams = generateCreateTeamRequestParams
+      body        :
+        username  : username
+        slug      : slug
+
+    queue = [
+
+      ->
+        # expecting HTTP 200 status code
+        request.post createTeamRequestParams, (err, res, body) ->
+          expect(err)             .to.not.exist
+          expect(res.statusCode)  .to.be.equal 200
+          queue.next()
+
+      ->
+        # expecting user to be saved and holding userId
+        params = { username }
+
+        JUser.one params, (err, user) ->
+          expect(err)   .to.not.exist
+          expect(user)  .to.exist
+          userId = user._id
+          queue.next()
+
+      ->
+        # expecting account to be saved and holding accountId
+        params = { 'profile.nickname' : username }
+
+        JAccount.one params, (err, account) ->
+          expect(err)     .to.not.exist
+          expect(account) .to.exist
+          accountId = account._id
+          queue.next()
+
+      ->
+        # expecting group to be saved and holding groupId
+        params = { slug }
+
+        JGroup.one params, (err, group) ->
+          expect(err)     .to.not.exist
+          expect(group)   .to.exist
+          groupId = group._id
+          queue.next()
+
+      ->
+        # expecting owner account of the group to be saved
+        params =
+          $and : [
+            { as       : 'owner' }
+            { sourceId : groupId }
+            { targetId : accountId }
+          ]
+
+        Relationship.one params, (err, relationship) ->
+          expect(err)          .to.not.exist
+          expect(relationship) .to.exist
+          queue.next()
+
+      ->
+        # expecting account also to be saved as a member
+        params =
+          $and : [
+            { as       : 'member' }
+            { sourceId : groupId }
+            { targetId : accountId }
+          ]
+
+        Relationship.one params, (err, relationship) ->
+          expect(err)          .to.not.exist
+          expect(relationship) .to.exist
+          queue.next()
+
+      ->
+        # expecting user to be saved as the owner of the account
+        params =
+          $and : [
+            { as       : 'owner' }
+            { sourceId : userId }
+            { targetId : accountId }
+          ]
+
+        Relationship.one params, (err, relationship) ->
+          expect(err)          .to.not.exist
+          expect(relationship) .to.exist
           queue.next()

       -> done()


Server: add createUser helper function

Server: add createUser helper function

--- servers/testhelper/handler/createuserhelper.coffee
+++ servers/testhelper/handler/createuserhelper.coffee
@@ -1,4 +1,6 @@
-{ generateUrl
+{ expect
+  request
+  generateUrl
   deepObjectExtend
   generateRandomEmail
   generateRandomString
@@ -33,7 +35,24 @@
   return requestParams

+createUser = (apiToken, opts, callback) ->
+
+  [opts, callback] = [callback, opts]  unless callback
+  opts ?= {}
+
+  createUserRequestBody   = generateCreateUserRequestBody opts
+  createUserRequestParams = generateCreateUserRequestParams
+    body    : createUserRequestBody
+    headers : { Authorization : "Bearer #{apiToken}" }
+
+  request.post createUserRequestParams, (err, res, body) ->
+    expect(err).to.not.exist
+    expect(res.statusCode).to.be.equal 200
+    callback createUserRequestBody
+
+
 module.exports = {
+  createUser
   generateCreateUserRequestParams
 }


Added check which prevents useless settings updates

--- src/client/app/controllers/tabs/settings-site.iced
+++ src/client/app/controllers/tabs/settings-site.iced
@@ -30,10 +30,20 @@
 			.then (result) =>
 				@$.form.vm = result.data
 				@$.$watch "form.vm", _.debounce(@_putChanges, 2000), true
+			.catch (result) =>
+				@NotificationService.error
+					title: "Failed to fetch application data"
+					message: result.data.message
+					timeout: 20

-	_putChanges: (data) ->
+	_putChanges: (data, oldOne) ->
+		return if _(data).isEqual oldOne
+
 		@SettingsService.putApplication(data)
 			.then =>
 				@NotificationService.success message: "Settings updated!", timeout: 2
-			.catch =>
-				console.log "failed to update data"
+			.catch (result) =>
+				@NotificationService.error
+					title: "Failed to update settings"
+					message: result.data.message
+					timeout: 10


Added real app-id

Added real app-id

--- src/diary/site/static/coffee/diary.facebook.coffee
+++ src/diary/site/static/coffee/diary.facebook.coffee
@@ -12,7 +12,7 @@

   $window.fbAsyncInit = ->
     FB.init
-      "appId": "169415933126285"
+      "appId": "169415933126285" #240331372798385
       cookie: true
       xfbml: true
       version: "v2.1"


Update

Update

I have a question about the vega_character, is that a name of a character or a kind of character?

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -170,7 +170,7 @@
     legal: "Legal"
     privacy: "Privacidad"
     about: "Acerca"
-#    impact: "Impact"
+    impact: "Impacto"
     contact: "Contacto"
     twitter_follow: "Seguir"
     my_classrooms: "Mis clases"
@@ -199,7 +199,7 @@
   modal:
     close: "Cerrar"
     okay: "OK"
-#    cancel: "Cancel"
+    cancel: "Cancelar"

   not_found:
     page_not_found: "Página no encontrada"
@@ -213,12 +213,12 @@
     subscribe_as_diplomat: "Suscríbete como un Diplomático"

   play:
-#    title: "Play CodeCombat Levels - Learn Python, JavaScript, and HTML"
-#    meta_description: "Learn programming with a coding game for beginners. Learn Python or JavaScript as you solve mazes, make your own games, and level up. Challenge your friends in multiplayer arena levels!"
-#    level_title: "__level__ - Learn to Code in Python, JavaScript, HTML"
-#    video_title: "__video__ | Video Level"
-#    game_development_title: "__level__ | Game Development"
-#    web_development_title: "__level__ | Web Development"
+    title: "Juega los Niveles de - Aprende Python, JavaScript, y HTML"
+    meta_description: "Aprende a programar con un juego de programacion para principiantes. Aprende Python o JavaScript mientras resuelves laberintos, creas tus propios juegos, y subes de nivel. Reta a tus amigos en los niveles de arenas multijugador!"
+    level_title: "__nivel__ - Aprende a programar en Python, JavaScript, HTML"
+    video_title: "__video__ | Video de Nivel"
+    game_development_title: "__nivel__ | Desarollo de Juegos"
+    web_development_title: "_nivel__ | Desarrollo de Paginas Web"
     anon_signup_title_1: "CodeCombat tiene un"
     anon_signup_title_2: "¡Versión de aula!"
     anon_signup_enter_code: "Ingresa el código de la clase:"
@@ -433,8 +433,8 @@
     books: "Libros"

   common:
-#    default_title: "CodeCombat - Coding games to learn Python and JavaScript"
-#    default_meta_description: "Learn typed code through a programming game. Learn Python, JavaScript, and HTML as you solve puzzles and learn to make your own coding games and websites."
+    default_title: "CodeCombat - Juegos de programacion para aprender Python y JavaScript"
+    default_meta_description: "Aprende codigos a traves de un juego de programacion. Aprende Python, JavaScript, y HTML mientras resuelves laberintos y aprendes a programar tus propios juegos y sitios web."
     back: "Atrás" # When used as an action verb, like "Navigate backward"
     coming_soon: "¡Próximamente!"
     continue: "Continuar" # When used as an action verb, like "Continue forward"
@@ -466,9 +466,9 @@
     submit_changes: "Enviar cambios"
     save_changes: "Guardar cambios"
     required_field: "Requisito necesario" # {change}
-#    submit: "Submit"
-#    replay: "Replay"
-#    complete: "Complete"
+    submit: "Enviar"
+    replay: "Repetir"
+    complete: "Completar"

   general:
     and: "y"
@@ -601,8 +601,8 @@
     reload_title: "¿Recargar Todo el Código?"
     reload_really: "¿Estás seguro de que quieres empezar este nivel desde el principio?"
     reload_confirm: "Recargar Todo"
-#    restart_really: "Are you sure you want to restart the level? You'll loose all the code you've written."
-#    restart_confirm: "Yes, Restart"
+    restart_really: "¿Quieres reiniciar este nivel? Perderas todo el codigo que has escrito."
+    restart_confirm: "Sí, Reiniciar"
     test_level: "Probar Nivel"
     victory: "Victoria"
     victory_title_prefix: "¡"
@@ -627,7 +627,7 @@
     tome_cast_button_run: "Ejecutar"
     tome_cast_button_running: "Ejecutando"
     tome_cast_button_ran: "Ejecutado"
-#    tome_cast_button_update: "Update"
+    tome_cast_button_update: "Actualizar"
     tome_submit_button: "Enviar"
     tome_reload_method: "Recargar código original para este método" # {change}
     tome_available_spells: "Hechizos Disponibles"
@@ -730,12 +730,12 @@
     learn_new_concepts: "Aprende nuevos conceptos"
     watch_a_video: "Mira un video de __concept_name__"
     concept_unlocked: "Concepto desbloqueado"
-#    use_at_least_one_concept: "Use at least one concept: "
-#    command_bank: "Command Bank"
-#    learning_goals: "Learning Goals"
-#    start: "Start"
+    use_at_least_one_concept: "Usa al menos un concepto: "
+    command_bank: "Banco de Comandos"
+    learning_goals: "Metas de Aprendizaje"
+    start: "Iniciar"
 #    vega_character: "Vega Character"
-#    click_to_continue: "Click to Continue"
+    click_to_continue: "Click para Continuar"

   apis:
     methods: "Métodos"


Fixed unthrown errors (release 0.1.1)

Fixed unthrown errors (release 0.1.1)

--- Smooth.coffee
+++ Smooth.coffee
@@ -1,5 +1,5 @@
 ###
-Smooth.js version 0.1
+Smooth.js version 0.1.1

 Turn arrays into smooth functions.

@@ -72,6 +72,7 @@
 			else
 				err = new Error
 				err.message = "The clipping mode #{config.clip} is invalid."
+				throw err

     # Get input array value at i, applying the clipping method
 	getClippedInput: (i) ->
@@ -147,11 +148,13 @@
 		else
 			err = new Error
 			err.message = "The interpolation method #{config.method} is invalid."
+			throw err

 	#Make sure there's at least one element in the input array
 	if not arr.length
 		err = new Error
 		err.message = 'Array must have at least one element.'
+		throw err

 	#See what type of data we're dealing with
 	dataType = Object.prototype.toString.call arr[0]
@@ -167,6 +170,7 @@
 		else
 			err = new Error
 			err.message = 'Invalid element type: #{dataType}'
+			throw err


Atom: add local-history and c++ support

Atom: add local-history and c++ support

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -14,7 +14,6 @@
       "language-css"
       "language-csharp"
       "language-clojure"
-      "language-c"
       "language-javascript"
       "language-less"
       "language-make"
@@ -60,12 +59,13 @@
   "vim-mode": {}
   welcome:
     showOnStartup: false
-".gfm.source":
+  "local-history": {}
+".source.toml":
   editor:
     preferredLineLength: 120
 ".plain.text":
   editor:
     preferredLineLength: 120
-".source.toml":
+".gfm.source":
   editor:
     preferredLineLength: 120


Atom add more keymappings

Atom add more keymappings

--- atom.symlink/keymap.cson
+++ atom.symlink/keymap.cson
@@ -75,8 +75,9 @@
   'ctrl-shift-delete': 'editor:delete-to-end-of-line'
   'ctrl-shift-M': 'markdown-preview:toggle'
   'ctrl-shift-P': 'command-palette:toggle'
-  'cmd-z': 'core:undo'
-  'cmd-y': 'core:redo'
+  'ctrl-z': 'core:undo'
+  'ctrl-y': 'core:redo'
+  'ctrl-shift-f': 'project-find:show'

 '.platform-darwin .find-and-replace':
   'cmd-enter': 'find-and-replace:find-all'


80 chars limit

80 chars limit

--- tests/helpers.coffee
+++ tests/helpers.coffee
@@ -1,5 +1,6 @@

-# See cozy-fixtures documentation for testing on https://github.com/jsilvestre/cozy-fixtures#automatic-tests
+# See cozy-fixtures documentation for testing on
+# https://github.com/jsilvestre/cozy-fixtures#automatic-tests
 fixtures = require 'cozy-fixtures'

 helpers = {}
@@ -27,4 +28,4 @@
 helpers.cleanDBWithRequests = (done) ->
     fixtures.resetDatabase removeAllRequests: true, callback: done

-module.exports = helpers+module.exports = helpers


correction to service config parsing

correction to service config parsing

--- src/scripts/githooks/coreos_restart/service.coffee
+++ src/scripts/githooks/coreos_restart/service.coffee
@@ -25,7 +25,7 @@
     for name in names
       services[name] =
         # Merge Service config upwards with Application-level config.
-        config: merge app, (yield pull_configuration {name, path: join path, name}), {service: name}
+        config: merge app, (yield pull_configuration {name, path: app.launch}), {service: name}

     return services


Add login message

Add login message

--- www/app/main/main.controller.coffee
+++ www/app/main/main.controller.coffee
@@ -24,7 +24,7 @@
     closeLogin: ->
       $scope.loginModal.hide()
     doLogin: (accessToken) ->
-      $ionicLoading.show()
+      $ionicLoading.show(template: '登录中...')
       Restangular
         .all('accessToken')
         .post(accesstoken: accessToken)
@@ -32,8 +32,9 @@
           storage.set 'user', user
           storage.set 'accessToken', accessToken
           console.log 'login success', user
+          $scope.closeLogin()
+          $ionicLoading.show(template: '登录成功，欢迎您: ' + user?.loginname, duration: 1000)
         .catch (error) ->
           console.log 'login error', error
-        .finally ->
-          $ionicLoading.hide()
+          $ionicLoading.show(template: '登录失败: ' + error?.data?.error_msg, duration: 1000)


__all__ value is skipped closes #376

__all__ value is skipped closes #376

--- wzk/ui/ac/NativeDataProvider.coffee
+++ wzk/ui/ac/NativeDataProvider.coffee
@@ -18,10 +18,10 @@
     for el in select.children
       if el.nodeName is 'OPTGROUP'
         optgroupModels = @loadOptionsOfOptgroup el
-        models = models.concat optgroupModels if optgroupModels?
+        models = models.concat(optgroupModels) if optgroupModels?
       else
         model = @buildModel(el)
-        models.push model if model?
+        models.push(model) if model?
     callback(models)

   ###*
@@ -47,12 +47,13 @@
     @return {wzk.resource.Model}
   ###
   buildModel: (option, group = undefined) ->
-    id = option.getAttribute 'value'
-    return null if not id? or id is ''
+    id = option.getAttribute('value')
+    return null if not id? or id in ['', '__all__']

-    data =
-      _obj_name: @dom.getTextContent option
+    data = {
+      _obj_name: @dom.getTextContent(option)
       id: id
+    }

     if group?
       data['group'] = group


Repassing error on app.start to test callback

--- test/appTest.coffee
+++ test/appTest.coffee
@@ -5,8 +5,7 @@

 # app is not 'started' until its resolver is ready.
 before (done) ->
-  app.start ->
-    done()
+  app.start done

 describe "GET /", ->


decouple decrypt tests

decouple decrypt tests

--- test/encrypt.coffee
+++ test/encrypt.coffee
@@ -528,7 +528,7 @@

 describe 'document.decrypt()', ->
   simpleTestDoc6 = null
-  before (done) ->
+  beforeEach (done) ->
     simpleTestDoc6 = new BasicEncryptedModel
       text: 'Unencrypted text'
       bool: true
@@ -573,23 +573,25 @@
   it 'should return an unencrypted version even if document already decrypted', (done) ->
     simpleTestDoc6.decrypt (err) ->
       assert.equal err, null
-      assert.propertyVal simpleTestDoc6, 'text', 'Unencrypted text'
-      assert.propertyVal simpleTestDoc6, 'bool', true
-      assert.propertyVal simpleTestDoc6, 'num', 42
-      assert.property simpleTestDoc6, 'date'
-      assert.equal simpleTestDoc6.date.toString(), new Date("2014-05-19T16:39:07.536Z").toString()
-      assert.propertyVal simpleTestDoc6, 'id2', mongoose.Schema.Types.ObjectId '5303e65d34e1e80d7a7ce212'
-      assert.lengthOf simpleTestDoc6.arr, 2
-      assert.equal simpleTestDoc6.arr[0], 'alpha'
-      assert.equal simpleTestDoc6.arr[1], 'bravo'
-      assert.property simpleTestDoc6, 'mix'
-      assert.deepEqual simpleTestDoc6.mix, { str: 'A string', bool: false }
-      assert.property simpleTestDoc6, 'buf'
-      assert.equal simpleTestDoc6.buf.toString(), 'abcdefg'
-      assert.propertyVal simpleTestDoc6, 'idx', 'Indexed'
-      assert.property simpleTestDoc6, '_id'
-      assert.notProperty simpleTestDoc6, '_ct'
-      done()
+      simpleTestDoc6.decrypt (err) ->
+        assert.equal err, null
+        assert.propertyVal simpleTestDoc6, 'text', 'Unencrypted text'
+        assert.propertyVal simpleTestDoc6, 'bool', true
+        assert.propertyVal simpleTestDoc6, 'num', 42
+        assert.property simpleTestDoc6, 'date'
+        assert.equal simpleTestDoc6.date.toString(), new Date("2014-05-19T16:39:07.536Z").toString()
+        assert.propertyVal simpleTestDoc6, 'id2', mongoose.Schema.Types.ObjectId '5303e65d34e1e80d7a7ce212'
+        assert.lengthOf simpleTestDoc6.arr, 2
+        assert.equal simpleTestDoc6.arr[0], 'alpha'
+        assert.equal simpleTestDoc6.arr[1], 'bravo'
+        assert.property simpleTestDoc6, 'mix'
+        assert.deepEqual simpleTestDoc6.mix, { str: 'A string', bool: false }
+        assert.property simpleTestDoc6, 'buf'
+        assert.equal simpleTestDoc6.buf.toString(), 'abcdefg'
+        assert.propertyVal simpleTestDoc6, 'idx', 'Indexed'
+        assert.property simpleTestDoc6, '_id'
+        assert.notProperty simpleTestDoc6, '_ct'
+        done()

 describe 'document.decryptSync()', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -13,8 +13,7 @@

   obj.include Metered

-  obj.meter
-    name: 'health'
+  obj.meter 'health'

   equals obj.I.maxHealth, 150