Restoring garbage collection.

Restoring garbage collection.

--- scope.coffee
+++ scope.coffee
@@ -43,9 +43,7 @@
   endLevel: ->
     vars = @variables
     for garbage in @garbage.pop() when @type(garbage) is 'var'
-      for v, i in vars when v.name is garbage.name
-        vars.splice(i, 1, {name: garbage.name, type: 'reuse'})
-        break
+      @setVar garbage, 'reuse'

   # Look up a variable name in lexical scope, and declare it if it does not
   # already exist.


fixed zoom in menu items

fixed zoom in menu items

--- client/init.coffee
+++ client/init.coffee
@@ -14,6 +14,14 @@
 prefs = require './prefs'

 $ ->
+  if D.nwjs
+    zM = 11 # zoom level magnitude limit
+    $ document
+      .on 'keydown', '*', 'ctrl+=', D.zoomIn    = -> prefs.zoom Math.min  zM, prefs.zoom() + 1; false
+      .on 'keydown', '*', 'ctrl+-', D.zoomOut   = -> prefs.zoom Math.max -zM, prefs.zoom() - 1; false
+      .on 'keydown', '*', 'ctrl+0', D.resetZoom = -> prefs.zoom 0; false
+      .on 'keydown', '*', 'ctrl+shift+=', D.zoomIn
+
   urlParams = {}
   for kv in (location + '').replace(/^[^\?]*($|\?)/, '').split '&'
     [_, k, v] = /^([^=]*)=?(.*)$/.exec kv; urlParams[unescape(k or '')] = unescape(v or '')
@@ -46,14 +54,6 @@
     else
       connect()

-  if D.nwjs
-    zM = 11 # zoom level magnitude limit
-    $ document
-      .on 'keydown', '*', 'ctrl+=', D.zoomIn    = -> prefs.zoom Math.min  zM, prefs.zoom() + 1; false
-      .on 'keydown', '*', 'ctrl+-', D.zoomOut   = -> prefs.zoom Math.max -zM, prefs.zoom() - 1; false
-      .on 'keydown', '*', 'ctrl+0', D.resetZoom = -> prefs.zoom 0; false
-      .on 'keydown', '*', 'ctrl+shift+=', D.zoomIn
-
   $ document
     .on 'keydown', '*', 'ctrl+0',       -> D.zoomIn();    false
     .on 'keydown', '*', 'shift+f1',     -> about(); false


bot.action() and fancy error handling

bot.action() and fancy error handling

- bot.action() and botProxy.action() perform an IRC action (equivalent
  to "/me does something" in your IRC client).
- Errors now mention which command or plugin:event triggered them.
- Errors are reported via PM (user to report to is hardcoded for now).

--- src/Bot.coffee
+++ src/Bot.coffee
@@ -58,12 +58,15 @@
   say: (context, message) =>
     @client.say context, message

+  action: (context, message) =>
+    @client.action context, message
+
   # Regular "public methods":

   load: (name, plugin) ->
     if typeof plugin is 'function'
       botProxy = {}
-      botProxy[f] = @[f] for f in ['meet', 'forget', 'say']
+      botProxy[f] = @[f] for f in ['meet', 'forget', 'say', 'action']
       botProxy.config = {}
       botProxy.config[key] = val for key, val of @config when typeof val is 'string'
       plugin = plugin botProxy
@@ -80,7 +83,7 @@
   runCommand: (command, message) ->
     d = domain.create()
     d.on 'error', (err) =>
-      @emit 'error', "[runCommand] " + err.toString()
+      @emit 'error', "[#{@config.commandPrefix}#{command}] " + err.toString()
     d.run =>
       cmd = @commands[command]
       if typeof cmd is 'function' then cmd message
@@ -143,11 +146,13 @@
     @runCommand command, message

   _handleEvent: (eventType, message) ->
+    currentPlugin = null
     d = domain.create()
     d.on 'error', (err) =>
-      @emit 'error', "[_handleEvent] " + err.toString()
+      @emit 'error', "[#{currentPlugin}:#{eventType}] " + err.toString()
     d.run =>
       for name, plugin of @plugins
+        currentPlugin = name
         if plugin.events? and plugin.events.hasOwnProperty eventType
           plugin.events[eventType] message


Tests that a[up-target] respects a X-Up-Location header that the server sends in case of a redirect

--- spec_app/spec/javascripts/up/link_spec.js.coffee
+++ spec_app/spec/javascripts/up/link_spec.js.coffee
@@ -180,12 +180,24 @@
         expect($('.target')).toHaveText('new text')
         expect(location.pathname).toEqual('/path')

+      it 'respects a X-Up-Location header that the server sends in case of a redirect', ->
+        affix('.target')
+        $link = affix('a[href="/path"][up-target=".target"]')
+        $link.click()
+        @respondWith
+          responseText: '
new text
'
+          responseHeaders: { 'X-Up-Location': '/other/path' }
+        expect($('.target')).toHaveText('new text')
+        expect(location.pathname).toEqual('/other/path')
+
       it 'does not add a history entry when an up-history attribute is set to "false"', ->
         oldPathname = location.pathname
         affix('.target')
         $link = affix('a[href="/path"][up-target=".target"][up-history="false"]')
         $link.click()
-        @respondWith('
new text
')
+        @respondWith
+          responseText: '
new text
'
+          responseHeaders: { 'X-Up-Location': '/other/path' }
         expect($('.target')).toHaveText('new text')
         expect(location.pathname).toEqual(oldPathname)


get main nodes

get main nodes

--- js/render.coffee
+++ js/render.coffee
@@ -19,7 +19,27 @@

      return top

+    getMainNodes: ->
+     f = @map.start
+     e = @map.N
+
+     nodes = []
+     for i in [f...e]
+      elem = @map.nodes[i].elem
+      main = false
+      while elem?
+       if elem is @elems.main
+        main = true
+        break
+       elem = elem.parentNode
+      nodes.push i if main
+
+     return nodes
+
     setPages: (H) ->
+     nodes = @getMainNodes()
+     console.log nodes
+
      page = 0
      for sidenote in @sidenotes
       elemSidenote = sidenote.elem


feat(Resource): improve error handling

feat(Resource): improve error handling

--- src/ORM.coffee
+++ src/ORM.coffee
@@ -466,12 +466,16 @@
         headers: req.headers
         params: req.params
         data: req.data
-      ).then (response) =>
+      ).then ((response) =>
         res = @_TransformResponse req, response
         for values in res.data
           collection.push @_MakeInstanceFromRemote(values)
         @_PostResponse res
         collection.$finalize()
+      ), (response) =>
+        res = @_TransformResponse req, response
+        @_PostResponse res
+        collection.$finalize(false)
       collection

     @Search: (field, value, opts={}) ->
@@ -495,13 +499,17 @@
         headers: req.headers
         params: req.params
         data: req.data
-      ).then (response) =>
+      ).then ((response) =>
         res = @_TransformResponse req, response
         for values in res.data
           collection.push @_MakeInstanceFromRemote(values)
         @_PostResponse res
         # @_MakeInstanceFromRemote(values) for values in response.data
         collection.$finalize()
+      ), (response) =>
+        res = @_TransformResponse req, response
+        @_PostResponse res
+        collection.$finalize( false)
       collection

     ###*
@@ -594,6 +602,7 @@
           complete:
             deferred: $q.defer()
             resolved: false
+      collection.$error = false
       collection.$promise = collection.$meta.async.complete.deferred.promise
       collection.$promiseDirect = collection.$meta.async.direct.deferred.promise
       collection.$getItemsPromises = ->
@@ -617,14 +626,22 @@
           if not $rootScope.$$phase
             $rootScope.$apply()
         collection
-      collection.$finalize = ->
-        collection.$_getPromiseForItems().then ->
+      collection.$finalize = (success=true) ->
+        items_success = success
+        collection.$_getPromiseForItems().then ( ->
           #collection.$meta.async.complete.deferred.resolve(collection)
-          collection.resolvePromise(collection.$meta.async.complete.deferred)
+          collection.resolvePromise(collection.$meta.async.complete.deferred, success)
           collection.$meta.async.complete.resolved = true
+        ), ->
+          items_success = false
+          #collection.$meta.async.complete.deferred.reject(collection)
+          collection.resolvePromise(collection.$meta.async.complete.deferred, items_success)
+          collection.$meta.async.complete.resolved = true
+          collection.$error = true
         #collection.$meta.async.direct.deferred.resolve(collection)
-        collection.resolvePromise(collection.$meta.async.direct.deferred)
+        collection.resolvePromise(collection.$meta.async.direct.deferred, success)
         collection.$meta.async.direct.resolved = true
+        collection.$error = (not (success and items_success))
         collection
       collection

@@ -782,9 +799,11 @@
         def = @_getField(name)
         if def.type is @constructor.Reference
           fetchReference(@, def, promises)
-      $q.all(promises).then =>
+      $q.all(promises).then ( =>
         #@$meta.async.m2o.deferred.resolve(@)
         @resolvePromise(@$meta.async.m2o.deferred)
+      ), =>
+        @resolvePromise(@$meta.async.m2o.deferred, false)
       @

     _fetchM2M: ->
@@ -808,9 +827,11 @@
         def = @_getField(name)
         if def.type is @constructor.ManyToMany
           fetchM2M(@, def, promises, collections)
-      $q.all(promises).then =>
+      $q.all(promises).then ( =>
         #@$meta.async.m2m.deferred.resolve(@)
         @resolvePromise(@$meta.async.m2m.deferred)
+      ) , =>
+        @resolvePromise(@$meta.async.m2m.deferred, false)
       for refs_collection in collections
         refs_collection.$finalize()
       @


Update debugging provisions.

Update debugging provisions.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -97,18 +97,18 @@
         self.node = node
         depth += 1
         fn = self["#{node.type}"]
-        self.onBeforeEnter?(node, depth, fn, res)
-        res = (fn.apply(self, [ node, parent ]) if fn)
-        self.onEnter?(node, depth, fn, res)
+        self.onBeforeEnter?(node, depth)
+        res = self["#{node.type}"]?(node, parent)
+        self.onEnter?(node, depth)
         res
       leave: (node, parent) ->
         self.controller = this
         self.node = node
         depth -= 1
         fn = self["#{node.type}Exit"]
-        self.onBeforeExit?(node, depth, fn, res)
+        self.onBeforeExit?(node, depth)
         res = (fn.apply(self, [ node, parent ]) if fn)
-        self.onExit?(node, depth, fn, res)
+        self.onExit?(node, depth)
         res
     root

@@ -1054,33 +1054,37 @@
 # ----------------------------------------------------------------------------

 ###
-# Debugging provisions
+# Debugging provisions.
+# Run `before -> js2coffee.debug()` in tests to print out some debug information.
 ###

 js2coffee.debug = ->
-  isDead = (ast) ->
+  TransformerBase::onBeforeEnter = (node, depth) ->
+    msg = "#{node.type}"
+    fn = @[msg]?
+    broken = isBroken(@ast) or ""
+    print depth, (if fn then "#{msg} *" else "#{msg}"), broken
+
+  TransformerBase::onBeforeExit = (node, depth, fn) ->
+    msg = "#{node.type}Exit"
+    fn = @[msg]?
+    print depth+1, (if fn then "#{msg} *" else "#{msg}"), broken
+
+  # Prints the current node.
+  print = (depth, nodeType, message="") ->
+    color = if (/\*$/.test(nodeType)) then 35 else 30
+    prefix = "\u001b[#{color}m#{nodeType}\u001b[0m"
+    indent = "\u001b[#{30 + (depth % 5)}m· \u001b[0m"
+
+    console.log \
+      Array(depth+1).join(indent) + prefix,
+      message
+
+  # Checks if a certain AST is broken.
+  isBroken = (ast) ->
     output = require('util').inspect(ast, depth: 1000)
     if ~output.indexOf("[Circular]")
-      "[CIRCULAR]"
-    else
-      ""
-
-  print = (depth, type, etc) ->
-    color = if (/\*$/.test(type)) then 35 else 30
-    type = "\u001b[#{color}m#{type}\u001b[0m"
-    indent = "\u001b[#{30 + (depth % 5)}m·  \u001b[0m"
-
-    console.log \
-      Array(depth+1).join(indent) + type,
-      etc
-
-  TransformerBase::onBeforeEnter = (node, depth, fn) ->
-    msg = "#{node.type}"
-    print depth, (if fn then "#{msg} *" else "#{msg}"), isDead(@ast)
-
-  TransformerBase::onBeforeExit = (node, depth, fn) ->
-    msg = "#{node.type}Exit"
-    print depth, (if fn then "#{msg} *" else "#{msg}"), isDead(@ast)
+      "[Circular]"

 # ----------------------------------------------------------------------------

@@ -1090,3 +1094,5 @@

 js2coffee.Builder = Builder
 js2coffee.BuilderBase = BuilderBase
+
+# js2coffee.debug()


Fixed indexing/toc not updating

Fixed indexing/toc not updating

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -88,14 +88,19 @@
       saved = not err?
       updateTitle()

+setViewHtml = (html) ->
+  viewEl.innerHTML = html
+  updateIndex() if state.index
+  updateToc() if state.toc
+
 updateView = (force) ->
   switch
     when force or state.mode in ['read', 'write']
-      viewEl.innerHTML = marked editor.getValue()
+      setViewHtml marked editor.getValue()
     when state.mode is 'present'
       md = editor.getValue().split /\n\s*\n\s*(?:[-*]\s*){3,}\n/
       if state.slide < md.length
-        viewEl.innerHTML = marked md[state.slide || 0]
+        setViewHtml marked md[state.slide || 0]
       else
         state.slide = md.length - 1
     else
@@ -103,9 +108,7 @@
       md = editor.getValue().split '\n'
       md[cline] += ''
       md = md.join '\n'
-      viewEl.innerHTML = marked md
-      updateIndex() if state.index
-      updateToc() if state.toc
+      setViewHtml marked md
       scrollTop = viewWrapEl.scrollTop
       viewHeight = viewWrapEl.offsetHeight
       cursorSpan = document.getElementById 'cursor'


update data url

update data url

--- src/app.coffee
+++ src/app.coffee
@@ -16,7 +16,7 @@
   minZoom: 6
   maxZoom: 15

-app.geoserverUrl = "http://10.208.3.125:8080/geoserver"
+app.geoserverUrl = "http://data.azgs.az.gov/arizona-hazards/"

 # Setup data layers
 dataLayers = [


hide seeker bar when not in use

hide seeker bar when not in use

--- src/app.coffee
+++ src/app.coffee
@@ -107,7 +107,10 @@
     return if userIsSeeking
     if context.status?.time? and track_start_date? and context.status.current_item? and context.status.state == "play"
       diff_sec = (new Date() - track_start_date) / 1000
+      $("#track-slider").show()
       $("#track-slider").slider("option", "value", diff_sec / context.status.time)
+    else
+      $("#track-slider").hide()

   # debug text box
   $("#line").keydown (event) ->


Use shell to open hotspot

Use shell to open hotspot

--- src/app.coffee
+++ src/app.coffee
@@ -84,7 +84,7 @@
 	loadModuleForAP(wifi)
 	.then ->
 		console.log("Opening hotspot")
-		wifi.openHotspotAsync(ssid, passphrase)
+		execAsync("connmanctl tether wifi on #{ssid} #{passphrase}")
 	.catch (err) ->
 		console.log(err)


Handle case where service is removed before units are added.

--- src/app.coffee
+++ src/app.coffee
@@ -356,6 +356,8 @@
         removeService: (serviceId) ->
             service = @services.get serviceId
             @services.remove service
+            unless service.get('units')?
+                return
             otherServices = @services.filter (s) => s != service
             unitsToRemove = service.get('units').reject (unit) =>
                 @selectedUnits.get(unit)? or


Better URL maker

Better URL maker

--- src/app.coffee
+++ src/app.coffee
@@ -24,13 +24,18 @@
   app.config = config

   app.makeURL = (relative, search) ->
+
     props =
-      protocol: if app.config.key then 'https' else 'http'
-      hostname: app.config.hostname
+      protocol: if @config.key then 'https' else 'http'
+      hostname: @config.hostname
       pathname: relative

-    if (app.config.key and app.config.port != 443) or (!app.config.key and app.config.port != 80)
-      props.port = app.config.port
+    if @config.key?
+      if @config.port != 443
+        props.port = @config.port
+    else
+      if @config.port != 80
+        props.port = @config.port

     if search
       props.search = search


Fix permission for sending mails

Fix permission for sending mails

--- src/app.coffee
+++ src/app.coffee
@@ -30,7 +30,7 @@
         'description': 'Useful to synchronize your folders'
     'Binary':
         'description': 'Useful to synchronize the content of your files'
-    'send email from user':
+    'send mail from user':
         'description': 'Useful to send issues by mail to the cozy team'


fix(tag-input): Fix issue with tag input using private variables

--- modules/tag-input/main/index.coffee
+++ modules/tag-input/main/index.coffee
@@ -61,7 +61,7 @@
       acData = createFilteredData filterFn, @options.autocompleteData

       @_.autocomplete = new hx.AutoComplete(@input.node(), acData, @options.autocompleteOptions)
-      inputMap = @_.autocomplete.options.inputMap || hx.identity
+      inputMap = @_.autocomplete.options?.inputMap || hx.identity
       @_.autocomplete.on 'change', 'hx.taginput', (value) =>  # add the item to the tag list on first enter/tab
         @add inputMap(value)
         setTimeout (=> @_.autocomplete.show()), 0


fixes #1537: ignore hidden files/directories in watched directories

--- command.coffee
+++ command.coffee
@@ -18,6 +18,8 @@

 printLine = (line) -> process.stdout.write line + '\n'
 printWarn = (line) -> process.stderr.write line + '\n'
+
+hidden = (file) -> /^\.|~$/.test file

 # The help banner that is printed when `coffee` is called without arguments.
 BANNER = '''
@@ -95,11 +97,11 @@
       fs.readdir source, (err, files) ->
         throw err if err and err.code isnt 'ENOENT'
         return if err?.code is 'ENOENT'
-        files = files.map (file) -> path.join source, file
         index = sources.indexOf source
-        sources[index..index] = files
+        sources[index..index] = (path.join source, file for file in files)
         sourceCode[index..index] = files.map -> null
-        compilePath file, no, base for file in files
+        for file in files when not hidden file
+          compilePath (path.join source, file), no, base
     else if topLevel or path.extname(source) is '.coffee'
       watch source, base if opts.watch
       fs.readFile source, (err, code) ->
@@ -220,8 +222,8 @@
             throw err unless err.code is 'ENOENT'
             watcher.close()
             return unwatchDir source, base
-          files = files.map (file) -> path.join source, file
-          for file in files when not notSources[file]
+          for file in files when not hidden(file) and not notSources[file]
+            file = path.join source, file
             continue if sources.some (s) -> s.indexOf(file) >= 0
             sources.push file
             sourceCode.push null


Update grammar

Update grammar


Propagates grunt flags to cover workflow

--- grunt/workflow.coffee
+++ grunt/workflow.coffee
@@ -18,7 +18,9 @@
   ]

   grunt.registerTask 'cover', ->
-    require('child_process').spawnSync 'grunt', ['test'], stdio: 'inherit'
+    options = ['test'].concat grunt.option.flags()
+    require('child_process').spawnSync 'grunt', options, stdio: 'inherit'
+
     grunt.task.run [
       'makeReport'
       'coverage'


Fix: Load array extensions

Fix: Load array extensions

--- src/galao.coffee
+++ src/galao.coffee
@@ -1,2 +1,3 @@
+require './galao/array'
 require './galao/number'
 require './galao/string'


Fix and close #39

Fix and close #39

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -157,7 +157,7 @@
       fileName: originFileName
       command: @pylamaPath
       args: args
-      options: {cwd: curDir, env: env, stream: 'stdout', ignoreExitCode: true}
+      options: {env: env, stream: 'stdout', ignoreExitCode: true}

   lintFile: (lintInfo, textEditor) ->


we don't need to send the response object to sendResponse

--- src/giphy.coffee
+++ src/giphy.coffee
@@ -117,7 +117,7 @@
         if err
           @error state.msg, err
         else
-          @sendResponse state, res
+          @sendResponse state
     else
       @getRandomUri state

@@ -138,7 +138,7 @@
       when Giphy.HelpName then @getHelp state
       else @error state.msg, "Unrecognized Endpoint: #{state.endpoint}"

-  sendResponse: (state, res) ->
+  sendResponse: (state) ->
     @log "sendResponse:", state
     if state.uri
       @sendMessage state.msg, state.uri


assets missing

assets missing

--- vendor/assets/javascripts/active_admin.js.coffee
+++ vendor/assets/javascripts/active_admin.js.coffee
@@ -1,4 +1,6 @@
 #= require ace/ace
+#= require ace/theme-textmate
+#= require ace/mode-html
 #= require active_admin/base
 #= require_self
 #= require pages_editor


null check

null check

--- lib/relations/one.coffee
+++ lib/relations/one.coffee
@@ -76,7 +76,7 @@

         # already set, merge
         previous_related_model = model.get(@key)
-        if previous_related_model and (previous_related_model.id is json.id)
+        if previous_related_model and (previous_related_model.id is json?.id)
           Utils.updateModel(previous_related_model, json)
         else
           related_model = if json then Utils.updateOrNew(json, @reverse_model_type) else null


add win32 check for shelljs

add win32 check for shelljs

--- gist-cloner.coffee
+++ gist-cloner.coffee
@@ -5,6 +5,7 @@
 request = require 'request'
 path = require 'path'
 shell = require 'shelljs'
+os = require 'os'

 # we will log our progress in ES
 elasticsearch = require('elasticsearch')
@@ -14,6 +15,9 @@

 base = __dirname + "/data/gists-clones/"
 timeouts = []
+
+winshellCheck = (cmd) ->
+    return if os.platform() == 'win32' then cmd.replace(';','&') else cmd

 done = (err, pruned) ->
   console.log "done writing files"
@@ -69,6 +73,7 @@
       console.log "already got", gist.id
       # we want to be able to pull recently modified gists
       cmd = "cd #{userfolder}/#{gist.id}; git pull origin master"
+      cmd = winshellCheck(cmd)
       shell.exec cmd, (code, huh, message) ->
         if code or message
           console.log gist.id, user, code, message
@@ -82,7 +87,7 @@
         cmd = "cd #{userfolder};git clone https://#{token}@gist.github.com/#{gist.id}"
       else
         cmd = "cd #{userfolder};git clone git@gist.github.com:#{gist.id}"
-
+      cmd = winshellCheck(cmd)
       shell.exec cmd, (code, huh, message) ->
         if code or message
           console.log gist.id, user, code, message


Don't app.init when document.body is null

--- assets/javascripts/application.js.coffee
+++ assets/javascripts/application.js.coffee
@@ -20,6 +20,10 @@

 init = ->
   document.removeEventListener 'DOMContentLoaded', init, false
-  app.init()
+
+  if document.body
+    app.init()
+  else
+    setTimeout(init, 42)

 document.addEventListener 'DOMContentLoaded', init, false


Allow user to specify custom logging by setting the debug property to aa function.

--- src/bot.coffee
+++ src/bot.coffee
@@ -62,12 +62,15 @@
     @connect(@roomId ? randomHash)

-  log: (message) ->
-    if @debug
+  log: ->
+    args = Array.prototype.slice.call(arguments)
+    if typeof @debug == 'function'
+      @debug.apply(@, args)
+    else if @debug
       if @stdout == 'stderr'
-        console.error message
+        console.error.apply(@, args)
       else
-        console.log message
+        console.log.apply(@, args)

   connect: (roomId) ->


Lispworks doesn't support '--load' command line arg

Although LW doesn’t have a canonical console application name, LW
documentation suggests ``lw-console``

--- lib/swank-starter.coffee
+++ lib/swank-starter.coffee
@@ -15,7 +15,10 @@
       return false
     command = @lisp
     args = []
-    args.push '--load' unless command.match(/clisp/)  # CLISP does not have a --load option
+    if not command.match(/clisp|lw/)
+      args.push '--load'
+    else
+      args.push '-load' if command.match(/lw/)
     args.push @swank_script
     @process = new BufferedProcess({
       command: command,


Fixing a typo in requirements

Fixing a typo in requirements

The module should be required as "ractive" instead of "Ractive".

--- src/index.coffee
+++ src/index.coffee
@@ -1,4 +1,4 @@
-Ractive = require 'Ractive'
+Ractive = require 'ractive'
 umd = require 'umd-wrapper'
 sysPath = require 'path'


export Sphere

export Sphere

--- src/index.coffee
+++ src/index.coffee
@@ -1,6 +1,7 @@
 module.exports =
   Node: require './core/node'
   Edge: require './core/edge'
+  Sphere: require './core/sphere'
   adaptor:
     Ipfs: require './adaptor/ipfs'
     Json: require './adaptor/json'


Added strict mode

Added strict mode

--- src/index.coffee
+++ src/index.coffee
@@ -1,3 +1,4 @@
+"use strict"

 normat = (opts) ->
   fn = (value) ->


test events

test events

--- src/index.coffee
+++ src/index.coffee
@@ -35,6 +35,13 @@
     console.log 'on before destroy'
   destroyed: ->
     console.log 'on destroyed'
+  events:
+    'hook:created': ->
+      console.log 'on created(event)'
+    ping: (message) ->
+      console.log 'PONG' + message
+
+app.$emit 'ping', '!'

 tick = ->
   app.decriment()


test vue options

test vue options

--- src/index.coffee
+++ src/index.coffee
@@ -1,7 +1,11 @@
 Vue = require 'vue'

+Vue.config.debug = true
+Vue.config.strict = true
+Vue.config.interpolate = false
+
 app = new Vue
-  data:
+  data: ->
     count: 3
     title: 'color-vue!'
   # TODO: props:
@@ -16,6 +20,7 @@

+      {{message}}

   '''
   replace: true # default: true


Now correctly propagating configurations.

--- src/index.coffee
+++ src/index.coffee
@@ -34,11 +34,11 @@
           deferred.reject error
   deferred.promise

-mysqlConnect = (config) ->
+mysqlConnect = (mysqlConfig, config) ->
   deferred = Q.defer()
   console.log "connecting to MySQL..."
   connectWatch = durations.stopwatch().start()
-  connection = mysql.mysql.createConnection config
+  connection = mysql.mysql.createConnection mysqlConfig
   connection.connect (error) ->
     if error
       console.log "Error connecting to MySQL:", error, "\nStack:\n", error.stack
@@ -55,7 +55,7 @@
   deferred.promise

 # Connect to the database at the specified URI
-pgConnect = (uri) ->
+pgConnect = (uri, config) ->
   deferred = Q.defer()
   console.log "connecting to PostgreSQL..."
   connectWatch = durations.stopwatch().start()
@@ -103,7 +103,7 @@
     database: config.database
   console.log "Connecting to database:\n'#{JSON.stringify(myCfg)}'"

-  mysqlConnect myCfg
+  mysqlConnect myCfg, config
   .then (context) ->
     context.watch = durations.stopwatch().start()
     console.log "Ready to query MySQL."
@@ -120,7 +120,7 @@
   uri = "postgres://#{config.user}:#{config.password}@#{config.host}:#{config.port}/#{config.database}"
   console.log "Connecting to URI '#{uri}'"

-  pgConnect uri
+  pgConnect uri, config
   .then (context) ->
     context.watch = durations.stopwatch().start()
     console.log "Ready to query PostgreSQL."


Removed logging.

Removed logging.

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -76,7 +76,7 @@
         # set default value
         else
           if 'default' of attribute
-            @log 'default', attribute.default
+#            @log 'default', attribute.default
             attribute.value = attribute.default
           else
             attribute.value = ''


fix api settings

fix api settings

--- src/index.coffee
+++ src/index.coffee
@@ -200,9 +200,11 @@
     @data.set 'payment', checkoutPayment
     store.remove 'checkout-payment'

-  @client = new Api
-    key:      opts.key
-    endpoint: opts.endpoint
+  settings = {}
+  settings.key = opts?.key
+  settings.endpoint = opts?.endpoint
+
+  @client = new Api settings

   @cart = new Cart @client, @data, opts.cartOptions


#2 fix mouse control bug

#2 fix mouse control bug

--- app/assets/javascripts/oxalis/controller/volumetracing_controller.coffee
+++ app/assets/javascripts/oxalis/controller/volumetracing_controller.coffee
@@ -11,7 +11,7 @@

     @mouseControls =

-      leftDownMove : (delta, pos, event) =>
+      leftDownMove : (delta, pos, plane, event) =>

         if event.ctrlKey
           @move [


Proper scoping of name and props in HasProps.define()

--- bokehjs/src/coffee/core/has_props.coffee
+++ bokehjs/src/coffee/core/has_props.coffee
@@ -21,24 +21,25 @@
       object = name_or_object

     for name, prop of object
-      if this.prototype.props[name]?
-        throw new Error("attempted to redefine property '#{this.name}.#{name}'")
-
-      if this.prototype[name]?
-        # XXX: should be an error, but Backbone.Model.url must be removed first
-        console.log("attempted to redefine attribute '#{this.name}.#{name}'")
-
-      Object.defineProperty(this.prototype, name, {
-        get: ()      -> this.get(name)
-        set: (value) -> this.set(name, value)
-      }, {
-        configurable: false
-        enumerable: true
-      })
-
-      props = _.clone(this.prototype.props)
-      props[name] = prop
-      this.prototype.props = props
+      do (name, prop) =>
+        if this.prototype.props[name]?
+          throw new Error("attempted to redefine property '#{this.name}.#{name}'")
+
+        if this.prototype[name]?
+          # XXX: should be an error, but Backbone.Model.url must be removed first
+          console.log("attempted to redefine attribute '#{this.name}.#{name}'")
+
+        Object.defineProperty(this.prototype, name, {
+          get: ()      -> this.get(name)
+          set: (value) -> this.set(name, value)
+        }, {
+          configurable: false
+          enumerable: true
+        })
+
+        props = _.clone(this.prototype.props)
+        props[name] = prop
+        this.prototype.props = props

   @mixin: (names...) ->
     @define(property_mixins.create(names))


Add missing patterns to ".Bd -literal" blocks

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -1314,7 +1314,8 @@
 				beginCaptures:
 					0: patterns: [include: "#main"]
 				patterns: [include: "source.css"]
-			}]
+
+			}, include: "#main"]
 		},{

 			# Unparsed macros: passing callable macros as arguments won't invoke them


Fix missing underlines in .cu requests

Fix missing underlines in .cu requests

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -239,10 +239,10 @@
 			# Generic requests without formatting
 			name: "meta.function.request.$2.roff"
 			begin: "(?x) ^([.'])[ \t]*
-				(ab|ad|af|bd|bp|br|c2|cc|ce|cf|ch|cs|cu|da|di|dt|ec|em|eo
-				|ev|ex|fc|fi|fl|fp|ft|hc|hw|hy|in|it|lc|lg|lf|ll|ls|lt|mc
-				|mk|na|ne|nf|nh|nm|nn|ns|nx|os|pc|pi|pl|pm|pn|po|ps|rd|rm
-				|rn|rr|rs|rt|so|sp|ss|sv|sy|ta|tc|ti|tm|tr|uf|vs|wh)
+				(ab|ad|af|bd|bp|br|c2|cc|ce|cf|ch|cs|da|di|dt|ec|em|eo|ev
+				|ex|fc|fi|fl|fp|ft|hc|hw|hy|in|it|lc|lg|lf|ll|ls|lt|mc|mk
+				|na|ne|nf|nh|nm|nn|ns|nx|os|pc|pi|pl|pm|pn|po|ps|rd|rm|rn
+				|rr|rs|rt|so|sp|ss|sv|sy|ta|tc|ti|tm|tr|uf|vs|wh)
 				(?=\\s|\\d+\\s*$|\\\\[\"#])"
 			end: "(?<!\\\\)(?=\n)|(?=\\\\\")"
 			beginCaptures:
@@ -554,9 +554,9 @@
 	underlines:
 		patterns: [{

-			# .ul 0: Empty request/noop
-			name: "meta.request.ul.roff"
-			match: "^([.'])[ \t]*(ul)\\s*(0+)(?:(?!\\\\\")[\\D])*(?=\\s|$)(.*)$"
+			# .ul/.cu 0: Empty request/noop
+			name: "meta.request.$2.roff"
+			match: "^([.'])[ \t]*(ul|cu)\\s*(0+)(?:(?!\\\\\")[\\D])*(?=\\s|$)(.*)$"
 			captures:
 				1: {name: "punctuation.definition.function.request.roff"}
 				2: {name: "entity.function.name.roff"}
@@ -566,8 +566,8 @@
 		}, {

 			# Underline following line
-			name: "meta.request.ul.roff"
-			begin: "^([.'])[ \t]*(ul)(.*?)$\\n"
+			name: "meta.request.$2.roff"
+			begin: "^([.'])[ \t]*(ul|cu)(.*?)$\\n"
 			end:   "(?<!\\\\)$"
 			beginCaptures:
 				1: {name: "punctuation.definition.function.request.roff"}


only print out the analytics every second

--- src/tasks/watch-analytics-ftdi.coffee
+++ src/tasks/watch-analytics-ftdi.coffee
@@ -20,7 +20,7 @@
                                    #{analytics.angularPosition.z}),
                  errors: #{analytics.errors}"

-  setInterval printResults, 500
+  setInterval printResults, 1000

   concatenate = (bufferA, bufferB) ->


Fix wording about cf account

Fix wording about cf account

--- client/components/EditUser.coffee
+++ client/components/EditUser.coffee
@@ -223,7 +223,7 @@
                             />

-                        Логин на codeforces:
+                        Хендл (никнейм) на codeforces:
                             <Input
                                 type = "text"
                                 name = "newLogin"
@@ -260,7 +260,7 @@
                         Вы можете указать данные того же аккаунта, что и выше, или можете зарегистрировать
                         отдельный аккаунт только для отправки решений с алгопрога, если не хотите указывать пароль
                         от вашего основного аккаунта.

-                        Логин на codeforces для отправки решений:
+                        Хендл (никнейм) на codeforces для отправки решений:
                             <Input
                                 type = "text"
                                 name = "codeforcesUsername"
@@ -290,4 +290,4 @@
     urls: (props) ->
         registeredUser: "registeredUser/#{props.user._id}"

-export default ConnectedComponent(EditingUser, options)+export default ConnectedComponent(EditingUser, options)


Change link from pay to payment in unpaid warning

--- client/components/TopPanel.coffee
+++ client/components/TopPanel.coffee
@@ -67,12 +67,12 @@

Ваши занятия оплачены только до {moment(props.myUser.paidTill).format("DD.MM.YYYY")}.

                     {if props.blocked

-                            Оплата просрочена более чем на 3 дня. Ваш аккаунт заблокирован до <Link to="/pay">полной оплаты</Link>.
+                            Оплата просрочена более чем на 3 дня. Ваш аккаунт заблокирован до <Link to="/payment">полной оплаты</Link>.

                     else

                             Вы можете пока решать задачи, но
-                            {" "}<Link to="/pay">продлите оплату</Link> в ближайшее время.
+                            {" "}<Link to="/payment">продлите оплату</Link> в ближайшее время.

                     }

Если вы на самом деле оплачивали занятия, или занятия для вас должны быть бесплатными,


be more robust on albums that are missing thumbnail

--- client/js/views/album_view.coffee
+++ client/js/views/album_view.coffee
@@ -7,15 +7,15 @@
     @$el.render @model.toJSON(),
       subalbums:
         thumb:
-          src: -> @thumbnail.src
-          width: -> @thumbnail.width
+          src: -> @thumbnail?.src
+          width: -> @thumbnail?.width
           alt: -> @title
         link:
           href: -> @path
       pictures:
         thumb:
-          src: -> @thumbnail.src
-          width: -> @thumbnail.width
+          src: -> @thumbnail?.src
+          width: -> @thumbnail?.width
           alt: -> @title
         link:
           href: -> @path


added kite name into database schema

added kite name into database schema

--- workers/social/lib/social/models/kiteapps.coffee
+++ workers/social/lib/social/models/kiteapps.coffee
@@ -24,12 +24,15 @@
           'create', 'get', 'inc'
         ]
     schema          :
-      appKey      :
+      username      :
         type        : String
         required    : yes
       methodName    :
         type        : String
         required    : yes
+      kiteName      :
+        type        : String
+        required    : yes
       count         :
         type        : Number
         required    : no
@@ -47,7 +50,6 @@

     @one {
       appKey     : data.appKey
-      methodName : data.methodName
     }, (err, appData)=>
       if err
         callback err


WCMS-275 p tags will now be favored over line breaks See: https://github.com/biola/profile-publisher/pull/93


Don't try to use email for automatic sign in if email is an optional field, DUH. [Fixes #49]

--- client/views/signUp/signUp.coffee
+++ client/views/signUp/signUp.coffee
@@ -122,7 +122,6 @@
           #login on client
           if  _.contains([
             'USERNAME_AND_EMAIL',
-            'USERNAME_AND_OPTIONAL_EMAIL',
             'EMAIL_ONLY'], AccountsEntry.settings.passwordSignupFields)
             Meteor.loginWithPassword(email, password)
           else


remove placeholder spec

remove placeholder spec

--- test/components/exercise-preview/index.spec.coffee
+++ test/components/exercise-preview/index.spec.coffee
@@ -64,12 +64,6 @@
       expect(actions.include.handler).to.have.been.called
       expect(onSelect.callCount).to.equal(1)

-  it 'renders placeholders', ->
-    _.extend(@props, isInteractive: false)
-    expect(@props.exercise.preview).to.exist
-    Testing.renderComponent( ExercisePreview, props: @props ).then ({dom}) ->
-      expect(dom.querySelector('svg.placeholder.video')).to.exist
-
   it 'renders context if given', ->
     Testing.renderComponent( ExercisePreview, props: @props ).then ({dom}) ->
       expect(dom.querySelector('div.context')).to.exist


Define Meteor.user

Define Meteor.user

--- packages/core/documents/user.coffee
+++ packages/core/documents/user.coffee
@@ -9,3 +9,19 @@

 getReference: ->
   _.pick @, _.keys @constructor.REFERENCE_FIELDS()
+
+Meteor.user = (userId, fields) ->
+  if not fields and _.isObject userId
+    fields = userId
+    userId = null
+
+  # Meteor.userId is reactive
+  userId ?= Meteor.userId()
+  fields ?= {}
+
+  return null unless userId
+
+  User.documents.findOne
+    _id: userId
+  ,
+    fields: fields


KDView: restore the ability of KDView to use options.pistachio

There was a regression that was breaking a few views.

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -814,6 +814,10 @@
 # #

   viewAppended:->
+    { pistachio } = @getOptions()
+    if pistachio and not @template?
+      @setTemplate pistachio
+      @template.update()

   childAppended:(child)->
     # bubbling childAppended event


chris wip last commit friday

chris wip last commit friday

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -56,7 +56,10 @@
     o.resizable   or= null      # TBDL
     super o,data

-    data?.on? 'update', => @render()
+    data?.on? 'update', =>
+      data
+      debugger if window.paws
+      @render()

     @setInstanceVariables options
     @defaultInit options,data
@@ -120,8 +123,8 @@

   setParent:(parent)->
     if @parent?
+      console.log "view:", @, "parent:", @parent
       error 'View already has a parent'
-      console.log "view:", @, "parent:", @parent
     else
       if defineProperty
         defineProperty @, 'parent', value : parent, configurable : yes


Quick way of fixing clashing ids by not including them in injected context.

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -5181,8 +5181,8 @@
       throw new Error "Macro #{name} already defined"
     else
       addToMap topScope.macros, name, macro
-  for name, definition of values definitions when shouldImport name
-    addToMap topScope, name, definition
+  for name, {type, arity, isClass, virtual} of values definitions when shouldImport name
+    addToMap topScope, name, {type, arity, isClass, virtual}
   topScope.typeNames = concatMaps topScope.typeNames, typeNames
   topScope.classes = concatMaps topScope.classes, classes
   ctx.scopeIndex += compiledModule.savedScopes.length


Fix type resolution for culprits

Fix type resolution for culprits

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1230,11 +1230,11 @@
           unify ctx, canonicalType.type, type.type
           # log "done unifying one"
         # have to promote constraints from just compiled dependencies
-        depConstraints = concat (for dep in binding.deps when cononicalType = ctx.actualType dep.name
+        depConstraints = concat (for dep in binding.deps or [] when cononicalType = ctx.actualType dep.name
           constraintsFromCanonicalType ctx, cononicalType, dep.type)
         # log binding.constraints, depConstraints
         inferType ctx, name, canonicalType,
-          (join binding.constraints, depConstraints), binding.polymorphic
+          (join binding.constraints or [], depConstraints), binding.polymorphic
         # unifiedType = (substitute ctx.substitution, canonicalType)
         # ctx.assignType name,
         #   quantifyAll (addConstraints unifiedType, definitionConstraints)


Use log helpers in Login command

Use log helpers in Login command

--- src/login.coffee
+++ src/login.coffee
@@ -70,5 +70,5 @@

     process.stdout.write('Saving token to Keychain ')
     auth.saveToken(token)
-    process.stdout.write '\u2713\n'.green
+    @logSuccess()
     Q(token)


fix a typo in the Function#bind shim; add console.time and console.timeEnd to our console shim.

--- core/KD.coffee
+++ core/KD.coffee
@@ -5,7 +5,7 @@
     if 1 < arguments.length
       args = slice.call arguments, 1
       return -> func.apply context, if arguments.length then args.concat slice.call arguments else args
-    -> if arguments.z then func.apply context, arguments else func.call context
+    -> if arguments.length then func.apply context, arguments else func.call context

 Function::swiss = (parent, names...)->
   for name in names
@@ -48,10 +48,10 @@
     @remote.api.JAccount.impersonate username, (err)->
       if err then new KDNotificationView title: err.message
       else location.reload()
-#
-#  testKDML:->
-#    {KDMLParser} = Bongo.KDML
-#    kdml = new KDMLParser @classes
+
+  # testKDML:->
+  #   {KDMLParser} = Bongo.KDML
+  #   kdml = new KDMLParser @classes

   debugStates     : {}
   instances       : {}
@@ -193,7 +193,7 @@
   enableLogs:do->
     oldConsole = window.console
     window.console = {}
-    console[method] = noop  for method in ['log','warn','error','trace']
+    console[method] = noop  for method in ['log','warn','error','trace','time','timeEnd']

     enableLogs =->
       window.console = oldConsole


Log error stack

Log error stack

--- src/packages/archive-view/lib/file-view.coffee
+++ src/packages/archive-view/lib/file-view.coffee
@@ -22,11 +22,11 @@
         else
           temp.mkdir 'atom-', (error, tempDirPath) ->
             if error?
-              console.error("Error creating temp directory: #{tempDirPath}")
+              console.error("Error creating temp directory: #{tempDirPath}", error.stack ? error)
             else
               tempFilePath = path.join(tempDirPath, path.basename(archivePath), entry.getName())
               fsUtils.writeAsync tempFilePath, contents, (error) ->
                 if error?
-                  console.error("Error writing to #{tempFilePath}")
+                  console.error("Error writing to #{tempFilePath}", error.stack ? error)
                 else
                   rootView.open(tempFilePath)


Fix typo

Fix typo

--- src/scripts/helpers/backbone/views/base.coffee
+++ src/scripts/helpers/backbone/views/base.coffee
@@ -31,7 +31,7 @@
       @views = null

     close: () ->
-      empty()
+      @empty()
       delete @[key] for key of @

   class Regions


Removed vestigial code

Removed vestigial code

--- src/scripts/modules/media/header/header.coffee
+++ src/scripts/modules/media/header/header.coffee
@@ -67,7 +67,6 @@
       'click .summary': 'toggleSummary'
       'click .derive .btn': 'derivePage'
       'click .edit .btn' : 'editPage'
-      'submit .info .btn': 'getBook'

     initialize: () ->
       super()
@@ -105,10 +104,6 @@
           router.navigate(href, {trigger: false, analytics: true})
       @model.editOrDeriveContent(options, data)

-    getBook: (event) ->
-      console.debug("Getting book", event)
-
-
     derivePage: () ->
       options =
         success: (model) =>


Fix/ keep minimal in new version url (#1406)

* Modified latest.coffee so that url will maintain the minimal parameter

* Modified the code to be more succinct

* changed latest.coffee to maintain minimal view when going to updated version from minimal view.

* Fixed a whitespace error.

* Added a space for consistency.

--- src/scripts/modules/media/latest/latest.coffee
+++ src/scripts/modules/media/latest/latest.coffee
@@ -8,7 +8,10 @@
   return class LatestView extends BaseView
     template: template
     templateHelpers:
-      url: () -> linksHelper.getModelPath(@model)
+      url: () ->
+        path = linksHelper.getModelPath(@model)
+        queryString = linksHelper.serializeQuery(location.search)
+        if queryString.minimal then return path + '?minimal=true' else return path

     initialize: () ->
       super()


don’t change current conversation

don’t change current conversation

--- src/model.coffee
+++ src/model.coffee
@@ -25,7 +25,6 @@
   messageAdd: (event) =>
     id = event.conversation_id.id
     if not @conversationCurrent then @conversationCurrent = id
-    @conversationCurrent = id
     @messagesByConversationId[id] = @messagesByConversationId[id] || []
     @messagesByConversationId[id].push event


Allow hiding + reseting a dialog using the escape key

--- src/trix/controllers/toolbar_controller.coffee
+++ src/trix/controllers/toolbar_controller.coffee
@@ -52,6 +52,9 @@
       attribute = element.getAttribute("name")
       dialog = @getDialogForAttributeName(attribute)
       @setAttribute(dialog)
+    if event.keyCode is 27 # Escape key
+      event.preventDefault()
+      @hideDialog()

   # Action buttons


mmd: make analyzenode a method on node

mmd: make analyzenode a method on node

--- src/mmd.coffee
+++ src/mmd.coffee
@@ -23,17 +23,17 @@
       @children[name] = new NodeType node
     @children[name]

+  analyze: (node) ->
+    children = node.getchildren()
+
+    if children.length is 0
+      @addValue node.text
+    else
+      @getChild(child).analyze child for child in children
+
 analyze = (et) ->
   root = new NodeType et
-  analyzeNode root, et
+  root.analyze et
   root

-analyzeNode = (this_type, node) ->
-  children = node.getchildren()
-
-  if children.length is 0
-    this_type.addValue node.text
-  else
-    analyzeNode this_type.getChild(child), child for child in children
-
 module.exports = analyze


Update countUp.coffee

Update countUp.coffee

fixed typo in variable name

--- countUp.coffee
+++ countUp.coffee
@@ -85,7 +85,7 @@
     progress = timestamp - @startTime

     # to ease or not to ease is the question
-    if @options.Easing
+    if @options.useEasing
       if @countDown
         i = @easeOutExpo progress, 0, @startVal - @endVal, @duration
         @frameVal = @startVal - i


Better test for scrollability.

Better test for scrollability.

Testing the `scrollHeight` is cheaper than testing scrollability.  There
are a lot of non-scrollabile divs, so it makes sense to use this cheaper
test as a filter.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -262,7 +262,7 @@
             reason = "Frame."
       when "div", "ol", "ul"
         isClickable ||=
-          if Scroller.isScrollableElement element
+          if element.clientHeight < element.scrollHeight and Scroller.isScrollableElement element
             reason = "Scroll."

     # An element with a class name containing the text "button" might be clickable.  However, real clickables


improve archve doc acceptence tests

improve archve doc acceptence tests

--- test/acceptance/coffee/ArchiveDocsTests.coffee
+++ test/acceptance/coffee/ArchiveDocsTests.coffee
@@ -7,146 +7,145 @@

 DocstoreClient = require "./helpers/DocstoreClient"

-if Settings.filestore?.backend == "s3"
+
+describe "Archiving all docs", ->
+	beforeEach (done) ->
+		@callback = sinon.stub()
+		@project_id = ObjectId()
+		@docs = [{
+			_id: ObjectId()
+			lines: ["one", "two", "three"]
+			rev: 2
+		}, {
+			_id: ObjectId()
+			lines: ["aaa", "bbb", "ccc"]
+			rev: 4
+		}, {
+			_id: ObjectId()
+			lines: ["111", "222", "333"]
+			rev: 6
+		}]
+		jobs = for doc in @docs
+			do (doc) =>
+				(callback) =>
+					DocstoreClient.createDoc @project_id, doc._id, doc.lines, (err)=>
+						doc.lines[0] = doc.lines[0]+" added"
+						DocstoreClient.updateDoc @project_id, doc._id, doc.lines, callback
+		async.series jobs, done
+
+	afterEach (done) ->
+		db.docs.remove({project_id: @project_id}, done)
+

 	describe "Archiving all docs", ->
 		beforeEach (done) ->
-			@callback = sinon.stub()
-			@project_id = ObjectId()
-			@docs = [{
-				_id: ObjectId()
-				lines: ["one", "two", "three"]
-				rev: 2
-			}, {
-				_id: ObjectId()
-				lines: ["aaa", "bbb", "ccc"]
-				rev: 4
-			}, {
-				_id: ObjectId()
-				lines: ["111", "222", "333"]
-				rev: 6
-			}]
-			jobs = for doc in @docs
-				do (doc) =>
-					(callback) =>
-						DocstoreClient.createDoc @project_id, doc._id, doc.lines, (err)=>
-							doc.lines[0] = doc.lines[0]+" added"
-							DocstoreClient.updateDoc @project_id, doc._id, doc.lines, callback
-			async.series jobs, done

-		afterEach (done) ->
-			db.docs.remove({project_id: @project_id}, done)
+			DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
+				done()

+		it "should archive all the docs", (done) ->
+			@res.statusCode.should.equal 204
+			done()

-		describe "Archiving all docs", ->
-			beforeEach (done) ->
+		it "should set inS3 and unset lines in each doc", (done) ->

+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						db.docs.findOne _id: archiveDoc._id, (error, doc) =>
+							should.not.exist doc.lines
+							doc.inS3.should.equal true
+							callback()
+			async.series jobs, done
+
+		it "should be able get the same docs back", (done) ->
+
+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
+							doc.toString().should.equal archiveDoc.lines.toString()
+							callback()
+			async.series jobs, done
+
+	describe "Arching all docs twice", ->
+		beforeEach (done) ->
+			DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
+				@res.statusCode.should.equal 204
 				DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
+					@res.statusCode.should.equal 204
 					done()

-			it "should archive all the docs", (done) ->
-				@res.statusCode.should.equal 204
-				done()
+		it "should archive all the docs", (done) ->
+			@res.statusCode.should.equal 204
+			done()

-			it "should set inS3 and unset lines in each doc", (done) ->
+		it "should set inS3 and unset lines in each doc", (done) ->

-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							db.docs.findOne _id: archiveDoc._id, (error, doc) =>
-								should.not.exist doc.lines
-								doc.inS3.should.equal true
-								callback()
-				async.series jobs, done
+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						db.docs.findOne _id: archiveDoc._id, (error, doc) =>
+							should.not.exist doc.lines
+							doc.inS3.should.equal true
+							callback()
+			async.series jobs, done

-			it "should be able get the same docs back", (done) ->
+		it "should be able get the same docs back", (done) ->

-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
-								doc.toString().should.equal archiveDoc.lines.toString()
-								callback()
-				async.series jobs, done
-
-		describe "Arching all docs twice", ->
-			beforeEach (done) ->
-				DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
-					@res.statusCode.should.equal 204
-					DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
-						@res.statusCode.should.equal 204
-						done()
-
-			it "should archive all the docs", (done) ->
-				@res.statusCode.should.equal 204
-				done()
-
-			it "should set inS3 and unset lines in each doc", (done) ->
-
-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							db.docs.findOne _id: archiveDoc._id, (error, doc) =>
-								should.not.exist doc.lines
-								doc.inS3.should.equal true
-								callback()
-				async.series jobs, done
-
-			it "should be able get the same docs back", (done) ->
-
-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
-								doc.toString().should.equal archiveDoc.lines.toString()
-								callback()
-				async.series jobs, done
+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
+							doc.toString().should.equal archiveDoc.lines.toString()
+							callback()
+			async.series jobs, done

-		describe "archiving massive document", (done)->
-			beforeEach (done)->
-				@timeout 1000 * 30
-				quarterMegInBytes = 250000
-				lines = require("crypto").randomBytes(quarterMegInBytes).toString("hex")
-				console.log @project_id, @docs[1]._id, "helllllo"
-				@docs[1].lines = [lines,lines,lines,lines]
-				DocstoreClient.updateDoc @project_id, @docs[1]._id, @docs[1].lines, =>
-					DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
-						done()
-
+	describe "archiving massive document", (done)->
+		beforeEach (done)->
+			@timeout 1000 * 30
+			quarterMegInBytes = 250000
+			lines = require("crypto").randomBytes(quarterMegInBytes).toString("hex")
+			console.log @project_id, @docs[1]._id, "helllllo"
+			@docs[1].lines = [lines,lines,lines,lines]
+			DocstoreClient.updateDoc @project_id, @docs[1]._id, @docs[1].lines, =>
+				DocstoreClient.archiveAllDoc @project_id, (error, @res) =>
+					done()
+

-			it "should archive all the docs", (done) ->
-				@res.statusCode.should.equal 204
-				done()
+		it "should archive all the docs", (done) ->
+			@res.statusCode.should.equal 204
+			done()

-			it "should set inS3 and unset lines in each doc", (done) ->
-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							db.docs.findOne _id: archiveDoc._id, (error, doc) =>
-								should.not.exist doc.lines
-								doc.inS3.should.equal true
-								callback()
-				async.series jobs, done
+		it "should set inS3 and unset lines in each doc", (done) ->
+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						db.docs.findOne _id: archiveDoc._id, (error, doc) =>
+							should.not.exist doc.lines
+							doc.inS3.should.equal true
+							callback()
+			async.series jobs, done

-			it "should be able get the same docs back", (done) ->
+		it "should be able get the same docs back", (done) ->

-				jobs = for archiveDoc in @docs
-					do (archiveDoc) =>
-						(callback) =>
-							DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
-								doc.toString().should.equal archiveDoc.lines.toString()
-								callback()
-				async.series jobs, done
+			jobs = for archiveDoc in @docs
+				do (archiveDoc) =>
+					(callback) =>
+						DocstoreClient.getS3Doc @project_id, archiveDoc._id, (error, res, doc) =>
+							doc.toString().should.equal archiveDoc.lines.toString()
+							callback()
+			async.series jobs, done

-		describe "Unarchiving all docs", ->
+	describe "Unarchiving all docs", ->

-			it "should unarchive all the docs", (done) ->
-				DocstoreClient.archiveAllDoc @project_id, (error, res) =>
-					DocstoreClient.getAllDocs @project_id, (error, res, docs) =>
-						throw error if error?
-						docs.length.should.equal @docs.length
-						for doc, i in docs
-							doc.lines.should.deep.equal @docs[i].lines
-						done()
+		it "should unarchive all the docs", (done) ->
+			DocstoreClient.archiveAllDoc @project_id, (error, res) =>
+				DocstoreClient.getAllDocs @project_id, (error, res, docs) =>
+					throw error if error?
+					docs.length.should.equal @docs.length
+					for doc, i in docs
+						doc.lines.should.deep.equal @docs[i].lines
+					done()


Store emitted worker code in the root node.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -246,6 +246,13 @@
   # It would be better not to generate them in the first place, but for now,
   # clean up obvious double-parentheses.
   compileRoot: (o) ->
+    # CS399
+    console.log "root options received: #{JSON.stringify o}"
+    o.root    = this
+    @moduleNames = {}
+    @moduleCode = {}
+    modulePrelude = ""
+
     o.indent  = if o.bare then '' else TAB
     o.scope   = new Scope null, this, null
     o.level   = LEVEL_TOP
@@ -260,8 +267,18 @@
       prelude = "#{@compileNode merge(o, indent: '')}\n" if preludeExps.length
       @expressions = rest
     code = @compileWithDeclarations o
-    return code if o.bare
-    "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+
+    # CS399
+    # emit module prelude
+    modulePrelude += "# CS399 module prelude\n\n"
+    console.log "emitted module prelude, module codes: #{JSON.stringify @moduleCode}"
+
+    # CS399
+    # return code if o.bare
+    # "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+    return "#{modulePrelude}code" if o.bare
+    "#{modulePrelude}#{prelude}(function() {\n#{code}\n}).call(this);\n"
+

   # Compile the expressions body for the contents of a function, with
   # declarations of all inner variables pushed up to the top.
@@ -297,6 +314,26 @@
   @wrap: (nodes) ->
     return nodes[0] if nodes.length is 1 and nodes[0] instanceof Block
     new Block nodes
+
+  # CS399
+  declareModule: (name) ->
+    console.log "declared module: #{name}"
+    if @moduleNames[name]
+      return no
+    @moduleNames[name] = yes
+    yes
+
+  # CS399
+  assignModuleName: ->
+    console.log "assigned module name"
+    "someName"
+
+  # CS399
+  storeModuleCode: (name, code) ->
+    console.log "storing module \"#{name}\", length: #{code.length}"
+    @moduleCode[name] = code
+
+

 #### Literal

@@ -1915,10 +1952,11 @@
 #### CS399 extensions: ExternalModule
 exports.ExternalModule = class ExternalModule extends Base
   constructor: (@name, @code) ->
-
-    # assign name if there isn't one
-    if !@name
-      @name = "\"unnamed-module.js\""
+    # capture the actual name
+    if @name
+      capture = /^\"(.+)\"$/g
+      match = capture.exec(@name)
+      @name = match[1]

     # add .js extension if there isn't one

@@ -1934,12 +1972,29 @@
   compileNode: (o) ->
     console.log "Compiling module name: #{@name}"

-    o.indent  += TAB
-    actualCode = @code.compileRoot { bare: yes }
+    # assign name if there isn't one
+    if @name
+      check = o.root.declareModule @name
+      if !check
+        # show error!
+        console.log "error! duplicate module name: #{@name}"
+    else
+      @name = o.root.assignModuleName()
+
+    # create a new scope (hence overwriting the present, inherited scope)
+    o.scope = new Scope null, this, null
+    o.level = LEVEL_TOP
+    o.indent = ''
+    # o.bare = yes
+
+    # o.indent  += TAB
+    actualCode = @code.compile o

     console.log "using HTML5 worker" if o.worker
     console.log "using node.js process" if o.nodejsprocess
     console.log "emitting actual code:\n###################\n#{actualCode}\n###################\n"
+
+    o.root.storeModuleCode @name, actualCode

     # attach this.onmessage and stuff


starting port of nodes.rb to coffee-script.. Node only, so far

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -1,4 +1,106 @@
+# The abstract base class for all CoffeeScript nodes.
+# All nodes are implement a "compile_node" method, which performs the
+# code generation for that node. To compile a node, call the "compile"
+# method, which wraps "compile_node" in some extra smarts, to know when the
+# generated code should be wrapped up in a closure. An options hash is passed
+# and cloned throughout, containing messages from higher in the AST,
+# information about the current scope, and indentation level.
 exports.Node: -> this.values: arguments
+
+exports.Node::TAB: '  '
+
+# Tag this node as a statement, meaning that it can't be used directly as
+# the result of an expression.
+exports.Node::mark_as_statement: ->
+  this.is_statement: -> true
+
+# Tag this node as a statement that cannot be transformed into an expression.
+# (break, continue, etc.) It doesn't make sense to try to transform it.
+exports.Node::mark_as_statement_only: ->
+  this.mark_as_statement()
+  this.is_statement_only: -> true
+
+# This node needs to know if it's being compiled as a top-level statement,
+# in order to compile without special expression conversion.
+exports.Node::mark_as_top_sensitive: ->
+  this.is_top_sensitive: -> true
+
+flatten: (aggList, newList) ->
+  for item in newList
+    aggList.push(item)
+  aggList
+
+compact: (input) ->
+  compected: []
+  for item in input
+    if item?
+      compacted.push(item)
+
+# Provide a quick implementation of a children method.
+exports.Node::children: (attributes) ->
+  # TODO -- are these optimal impls of flatten and compact
+  # .. do better ones exist in a stdlib?
+  agg = []
+  for item in attributes
+    agg: flatten agg, item
+  compacted: compact agg
+  this.children: ->
+    compacted
+
+exports.Node::write: (code) ->
+  # hm..
+  # TODO -- should print to STDOUT in "VERBOSE" how to
+  # go about this.. ? jsonify 'this'?
+  code
+
+# This is extremely important -- we convert JS statements into expressions
+# by wrapping them in a closure, only if it's possible, and we're not at
+# the top level of a block (which would be unnecessary), and we haven't
+# already been asked to return the result.
+exports.Node::compile: (o) ->
+  # TODO -- need JS dup/clone
+  opts: if not o? then {} else o
+  this.options: opts
+  this.indent: opts.indent
+  top: this.options.top
+  if not this.is_top_sentitive()
+    this.options.top: undefined
+  closure: this.is_statement() and not this.is_statement_only() and not top and typeof(this) == "CommentNode"
+  closure &&= not this.do_i_contain (n) -> n.is_statement_only()
+  if closure then this.compile_closure(this.options) else compile_node(this.options)
+
+# Statements converted into expressions share scope with their parent
+# closure, to preserve JavaScript-style lexical scope.
+exports.Node::compile_closure: (o) ->
+  opts: if not o? then {} else o
+  this.indent: opts.indent
+  opts.shared_scope: o.scope
+  exports.ClosureNode.wrap(this).compile(opts)
+
+# Quick short method for the current indentation level, plus tabbing in.
+exports.Node::idt: (tLvl) ->
+  tabs: if tLvl? then tLvl else 0
+  tabAmt: ''
+  for x in [0...tabs]
+    tabAmt: tabAmt + this.TAB
+  this.indent + tabAmt
+
+exports.Node::is_a_node: ->
+  true
+
+#Does this node, or any of it's children, contain a node of a certain kind?
+exports.Node::do_i_contain: (block) ->
+  for node in this.children
+    return true if block(node)
+    return true if node.is_a_node() and node.do_i_contain(block)
+  false
+
+# Default implementations of the common node methods.
+exports.Node::unwrap: -> this
+exports.Node::children: []
+exports.Node::is_a_statement: -> false
+exports.Node::is_a_statement_only: -> false
+exports.Node::is_top_sensitive: -> false

 exports.Expressions : exports.Node
 exports.LiteralNode : exports.Node


use clone instead of JSON.parse(JSON.stringify()) in headers results converter test

--- test/unit/headers-result-converter-test.coffee
+++ test/unit/headers-result-converter-test.coffee
@@ -1,4 +1,5 @@
 {assert} = require 'chai'
+clone    = require 'clone'

 HeadersResultConverter = require '../../src/headers-result-converter'
 arrayFixtures = require '../fixtures/gavel2html-legacy'
@@ -32,9 +33,10 @@
             dataReal: arrayFixtures.testsHeaders.headersRealFailValueCase.dataReal
             dataExpected: arrayFixtures.testsHeaders.headersRealFailValueCase.dataExpected
             gavelResult: arrayFixtures.testsHeaders.headersRealFailValueCase.gavelResult
+            usePointers: false
           }
           result = jsonResultConverter.getLinesFromAmandaResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput

     describe 'getLinesFromResults', () ->
@@ -44,9 +46,10 @@
             dataReal: pointerFixtures.testsHeaders.headersRealFailValueCase.dataReal
             dataExpected: pointerFixtures.testsHeaders.headersRealFailValueCase.dataExpected
             gavelResult: pointerFixtures.testsHeaders.headersRealFailValueCase.gavelResult
+            usePointers: true
           }
           result = jsonResultConverter.getLinesFromResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput

   describe 'regression test for migration from amandaResults to JSON pointer based results in headers on value change', () ->
@@ -76,9 +79,10 @@
             dataReal: arrayFixtures.testsHeaders.headersRealFailChanged.dataReal
             dataExpected: arrayFixtures.testsHeaders.headersRealFailChanged.dataExpected
             gavelResult: arrayFixtures.testsHeaders.headersRealFailChanged.gavelResult
+            usePointers: false
           }
           result = jsonResultConverter.getLinesFromAmandaResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput

     describe 'getLinesFromResults', () ->
@@ -88,9 +92,10 @@
             dataReal: pointerFixtures.testsHeaders.headersRealFailChanged.dataReal
             dataExpected: pointerFixtures.testsHeaders.headersRealFailChanged.dataExpected
             gavelResult: pointerFixtures.testsHeaders.headersRealFailChanged.gavelResult
+            usePointers: true
           }
           result = jsonResultConverter.getLinesFromResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput

   describe 'regression test for migration from amandaResults to JSON pointer based results in headers in key missing', () ->
@@ -120,9 +125,10 @@
             dataReal: arrayFixtures.testsHeaders.headersRealFailMiss.dataReal
             dataExpected: arrayFixtures.testsHeaders.headersRealFailMiss.dataExpected
             gavelResult: arrayFixtures.testsHeaders.headersRealFailMiss.gavelResult
+            usePointers: false
           }
           result = jsonResultConverter.getLinesFromAmandaResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput

     describe 'getLinesFromResults', () ->
@@ -132,8 +138,9 @@
             dataReal: pointerFixtures.testsHeaders.headersRealFailMiss.dataReal
             dataExpected: pointerFixtures.testsHeaders.headersRealFailMiss.dataExpected
             gavelResult: pointerFixtures.testsHeaders.headersRealFailMiss.gavelResult
+            usePointers: true
           }
           result = jsonResultConverter.getLinesFromResults()
-          result = JSON.parse JSON.stringify result
+          result = clone result, false
           assert.deepEqual result, expectedOutput


Documentation

Documentation

--- src/point.coffee
+++ src/point.coffee
@@ -362,7 +362,7 @@

        point = Point(3.4, 5.8)
-       point.floor()
+       point.floor$()

        point.x
     => 3
@@ -371,9 +371,9 @@
     => 5

-    @name floor
-    @methodOf Point#
-    @returns {Point} A new point, with x and y values each floored to the largest previous integer.
+    @name floor$
+    @methodOf Point#
+    @returns {Point} A modified point, with x and y values each floored to the largest previous integer.
     ###
     floor$: ->
       @x = @x.floor()
@@ -387,8 +387,7 @@
     @methodOf Point#

     @param {Point} other The point to check for equality.
-    @returns true if the other point has the same x, y coordinates, false otherwise.
-    @type Boolean
+    @returns {Boolean} true if the other point has the same x, y coordinates, false otherwise.
     ###
     equal: (other) ->
       @x == other.x && @y == other.y


Switch HOSTNAME to BIND_ADDRESS as discussed.

--- src/robot.coffee
+++ src/robot.coffee
@@ -244,7 +244,7 @@
     app.use express.static stat if stat

     try
-      @server = app.listen(process.env.PORT || 8080, process.env.HOSTNAME || '0.0.0.0')
+      @server = app.listen(process.env.PORT || 8080, process.env.BIND_ADDRESS || '0.0.0.0')
       @router = app
     catch err
       @logger.error "Error trying to start HTTP server: #{err}\n#{err.stack}"


Add an explicit undefined return

Add an explicit undefined return

--- src/robot.coffee
+++ src/robot.coffee
@@ -310,6 +310,7 @@
         else
           process.nextTick done if done?
     )
+    return undefined

   # Public: Loads a file in path.


Refactor for clarity

Refactor for clarity

--- src/robot.coffee
+++ src/robot.coffee
@@ -261,15 +261,15 @@
         break unless line[0] is '#' or line.substr(0, 2) is '//'

         # remove leading '# '
-        cleanedLine = line[2..line.length].replace("\n", "")
+        cleanedLine = line[2..line.length].replace("\n", "").trim()
         @logger.debug "parseHelp(#{scriptName}): read #{cleanedLine}"

         continue if cleanedLine.length is 0
-        continue if cleanedLine.trim().toLowerCase() is 'none'
-
-        # sections shouldn't have any leading whitespace
-        if cleanedLine.trim().toLowerCase().replace(':', '') in HUBOT_DOCUMENTATION_SECTIONS
-          currentSection = cleanedLine.replace(':', '').toLowerCase()
+        continue if cleanedLine.toLowerCase() is 'none'
+
+        nextSection = cleanedLine.toLowerCase().replace(':', '')
+        if nextSection in HUBOT_DOCUMENTATION_SECTIONS
+          currentSection = nextSection
           scriptDocumentation[currentSection] = []
           @logger.debug "parseHelp(#{scriptName}): adding #{currentSection} section"
         # lines in a section _do_ have leading whitespace


Fixing undefined access error.

Fixing undefined access error.

--- src/robot.coffee
+++ src/robot.coffee
@@ -277,7 +277,7 @@
       user = new User id, options
       @brain.data.users[id] = user

-    if options.room and (!user.room or user.room isnt options.room)
+    if options and options.room and (!user.room or user.room isnt options.room)
       user = new User id, options
       @brain.data.users[id] = user


Fix issue with orphaned references Conditions must be updated in place, while branches may be added and removed freely.

--- cilantro/static/cilantro/scripts/coffeescript/serrano/datacontext.coffee
+++ cilantro/static/cilantro/scripts/coffeescript/serrano/datacontext.coffee
@@ -13,17 +13,18 @@
         attrs.composite is true and attrs.id

+    # Represents a single node within a DataContext tree. Non-branch nodes
+    # must be carefully handled and not removed from the tree unless explicitly
+    # removed
     class DataContextNode extends Backbone.Model
         validate: (attrs) ->
             # Some kind of node
-            if isBranch(attrs) or isCondition(attrs) or isComposite attrs
+            if isBranch(attrs) or isCondition(attrs) or isComposite(attrs)
                 return
-
             # Check for all undefined values
             for key, value of attrs
                 if value isnt undefined
                     return 'Unknown node type'
-
             return

         toJSON: ->
@@ -35,54 +36,56 @@
                 json = super
             return json

+        isRoot: -> not @parent?
+
+        isEmpty: -> _.isEmpty @attributes
+
+        isBranch: -> isBranch @attributes
+
+        isCondition: -> isCondition @attributes
+
+        isComposite: -> isComposite @attributes
+
         # Retrieve this node's siblings
         siblings: ->
-            if @root then false else _.without @parent.get('children'), @
-
-        # Proxy methods
-        isEmpty: ->
-            _.isEmpty @attributes
-
-        isBranch: ->
-            isBranch @attributes
-
-        isCondition: ->
-            isCondition @attributes
-
-        isComposite: ->
-            isComposite @attributes
+            if @isRoot() then false else _.without @parent.get('children'), @

         # Creates a new branch and adds this node along with one or more other
         # nodes to the branch
         promote: (nodes...) ->
-            if _.isString nodes[0]
-                type = nodes[0]
-                if type isnt 'and' and type isnt 'or'
-                    throw new Error 'Type must be "and" or "or"'
-                nodes.splice 0, 1
-            else
+            if nodes.length is 0
+                throw new Error 'At least one node must be supplied'
+
+            if @isRoot()
                 type = 'and'
-
-            if nodes.length is 0
-                throw new Error 'At least one other node must be supplied'
-
-            children = _.map [@toJSON(), nodes...], (attrs) =>
+            # Alternate operators at each level. If the parent and child
+            # branches have the same operator, the child can be demoted.
+            else
+                type = if @parent.get('type') is 'and' then 'or' else 'and'
+
+            # Pass this node's attributes in the map parser to convert into
+            # a newly referenced node.
+            children = _.map [@attributes, nodes...], (attrs) =>
                 DataContextNode.parseAttrs attrs, @

+            # Clear the existing attributes and set the new ones
             @clear slient: true
             @set type: type, children: children
             return @

         # Takes a node and removes it from the current branch and adds it
-        # to the branch's parent.
+        # to the branch's parent. If the branch has only one node left, it
+        # too gets demoted and the branch is destroyed.
         demote: ->
-            # If this is the root node or it's parent is, this node cannot
-            # be promoted
-            if @root then return false
-
-            # If the parent is root and this is the only child node, can
-            # it be demoted.
-            if @parent.root
+            # Nothing to do if this is the root
+            if @isRoot() then return false
+
+            # If the parent is the root and this is the only child node, can
+            # it be demoted. This is a special case when the root is a branch
+            # and one of the children has been removed. Since this will cause
+            # the other sibling to be demoted, this will ensure the single
+            # condition becomes the root.
+            if @parent.isRoot()
                 if @siblings().length is 0
                     @parent.clear silent: true
                     @parent.set @attributes
@@ -90,33 +93,33 @@
                 return false

             # Check the state of the siblings. If only one sibling is left,
-            # promote the parent instead
-            node = @parent.remove @
-            grand = @parent.parent
-            grand.attributes.children.push node
-            return @
-
+            # demote the parent instead
+            @parent.parent.get('children').push @remove()
+            return @
+
+        # Add one or more nodes to this branch.
         add: (nodes...) ->
             if not @isBranch()
                 throw new Error 'Node is not a branch. Use "promote" to convert it into one'
-            @attributes.children.push.apply @attributes.children, _.map nodes, (attrs) =>
+            (children = @get('children')).push.apply children, _.map nodes, (attrs) =>
                 DataContextNode.parseAttrs attrs, @
             return @

-        # Removes itself from it's parent. If a `node` is passed, this
-        # implies this node is a branch. When only one sibling remains in the
+        # Removes itself from it's parent. When only one sibling remains in the
         # respective branch, the sibling gets demoted.
         remove: ->
-            if @root then return false
-            children = @parent.attributes.children
-
-            # Ensure the node exists, then splice it directly out of
-            # the attributes. This bypasses validation since this operation
-            # is recursive.
-            if (idx = children.indexOf @) >= 0
-                children.splice(idx, 1)[0]
-                if children.length is 1
-                    children[0].demote()
+            # If this is the root, simply clear the attributes
+            if @isRoot()
+                @clear()
+            else
+                children = @parent.get 'children'
+                # Ensure the node exists, then splice it directly out of
+                # the attributes. This bypasses validation since this operation
+                # is recursive.
+                if (idx = children.indexOf @) >= 0
+                    children.splice(idx, 1)[0]
+                    if children.length is 1
+                        children[0].demote()
             return @

@@ -129,7 +132,8 @@
         # Branch
         else if isBranch attrs
             node = new DataContextNode type: attrs.type
-            children = _.map attrs.children, (_attrs) -> DataContextNode.parseAttrs _attrs, node, callback
+            children = _.map attrs.children, (_attrs) ->
+                DataContextNode.parseAttrs _attrs, node, callback
             node.set children: children
         # Condition
         else if isCondition attrs
@@ -142,12 +146,18 @@

         if parent
             node.parent = parent
-            delete node.root
-        else
-            node.root = true

         callback?(node)
         return node
+
+
+    DataContextNode.updateAttrs = (node, attrs) ->
+        if node.isBranch()
+            children = node.get('children')
+            for _attrs, i in attrs.children
+                DataContextNode.updateAttrs children[i], _attrs
+        else
+            node.set attrs

     class DataContext extends Backbone.Model
@@ -157,20 +167,23 @@
         url: ->
             if @isNew() then super else @get 'url'

-        parse: (response) =>
-            if response
-                @nodes = {}
-                @node = DataContextNode.parseAttrs response.json, null, @cacheNode
-
-            return response
+        parse: (resp) =>
+            if resp
+                if not @node
+                    @nodes = {}
+                    @node = DataContextNode.parseAttrs resp.json, null, @_cacheNode
+                else
+                    DataContextNode.updateAttrs @node, resp.json
+            return resp

         toJSON: ->
             attrs = super
-            if @node
-                attrs.json = @node.toJSON()
+            if @node then attrs.json = @node.toJSON()
             return attrs

-        # Proxy methods
+        isRoot: ->
+            @node.isRoot()
+
         isEmpty: ->
             @node.isEmpty()

@@ -183,7 +196,10 @@
         isComposite: ->
             @node.isComposite()

-        cacheNode: (node) =>
+        # Each condition or composite node has a reference stored in
+        # a array grouped by `id` to enable manipulating nodes in flat
+        # means.
+        _cacheNode: (node) =>
             if node.id
                 if not (cache = @nodes[node.id])
                     cache = @nodes[node.id] = []
@@ -191,34 +207,45 @@
                 if cache.indexOf(node) is -1
                     cache.push node

+        _deferenceNode: (node) =>
+            if (cache = @nodes[node.id]) and (idx = cache.indexOf(node)) >= 0
+                cache.splice(idx, 1)
+
+        # Returns an array of nodes for the provided `id`.
+        getNodes: (id) ->
+            @nodes[id] or []
+
+        # Promotes the given `node` to a branch
         promote: (node, nodes...) ->
             if node is null
                 @node.promote nodes...
             else
                 node.promote nodes...
-            @set 'json', @node.toJSON()
+            @save()

         demote: (node) ->
             node.demote()
-            @set 'json', @node.toJSON()
+            @save()

         add: (node, nodes...) ->
-            if node is null
-                node = @node
-
-            if node.isEmpty()
-                node.set nodes[0].attributes or nodes[0]
-            else
-                node.add(nodes...)
-                @cacheNode node for node in node.get 'children'
-            @set 'json', @node.toJSON()
+            # Attempt to add to the root node. If the root is empty, so
+            # replace it with this node
+            if not node
+                if @node.isEmpty()
+                    @node = nodes[0]
+                    @_cacheNode @node
+                else
+                    node = @node
+            if node
+                node.add nodes...
+                @_cacheNode node for node in node.get 'children'
+            @save()

         remove: (node) ->
+            node = node or @node
             node.remove()
-            # Dereference it
-            if (cache = @nodes[node.id]) and (idx = cache.indexOf(node)) >= 0
-                cache.splice(idx, 1)
-            @set 'json', @node.toJSON()
+            if not node.isRoot() then @_deferenceNode node
+            @save()

     class DataContexts extends Backbone.Collection


Apps: Added tooltip for experimental badge.

--- client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -108,6 +108,8 @@
       @experimentalView = new KDCustomHTMLView
         cssClass   : "top-badge orange"
         partial    : "Experimental"
+        tooltip    :
+          title    : "This is an experimental app, click for help."
         click      : (e) =>
           e.stopPropagation()
           new KDModalView


sitewide: minor

sitewide: minor

--- client/landing/app/Applications/Demos.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Demos.kdapplication/AppController.coffee
@@ -15,25 +15,23 @@

   loadView:(mainView)->

-    mainView.addSubView input = new KDInputView
-      name      : "lol"
-      validate  :
-        rules   :
-          required : yes
-        events  :
-          required : 'click'
-      click     : -> log 'zozo'
-
-    input.on 'click', -> log 'zoma'
-
-    mainView.addSubView button = new KDButtonView
-      title    : 'reset validation'
-      callback : ->
-        input.setValidation
-          rules   :
-            required : yes
-          events  :
-            required : 'blur'
+    mainView.addSubView a = new KDView
+      partial    : ""
+      tooltip    :
+        title    : "zulaku bereke"
+        selector : 'span'

-  parentDidResize:-> log "geldi mi >>>>>>>", arguments
+    a.$().css
+      display          : "block"
+      width            : "50%"
+      height           : "50%"
+      backgroundColor  : "pink"
+      margin           : "100px auto"
+
+    a.$('span').css
+      display          : "block"
+      width            : "50%"
+      height           : "50%"
+      backgroundColor  : "blue"
+      margin           : "100px auto"


activity: implement name remapping for ipython

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2494,11 +2494,20 @@
 normalize_path = (path) ->
     # Rules:
     # kdkd/tmp/.test.sagews.sage-chat --> kdkd/tmp/test.sagews, comment "chat"
+    # foo/bar/.2014-11-01-175408.ipynb.syncdoc --> foo/bar/2014-11-01-175408.ipynb
     ext = misc.filename_extension(path)
     comment = undefined
     if ext == "sage-chat"
         comment = 'chat'
         path = path.slice(0, path.length-'.sage-chat'.length)
+        {head, tail} = misc.path_split(path)
+        tail = tail.slice(1) # get rid of .
+        if head
+            path = head + '/' + tail
+        else
+            path = tail
+    else if ext == 'syncdoc'   # for IPython, and possibly other things later
+        path = path.slice(0, path.length-'.syncdoc'.length)
         {head, tail} = misc.path_split(path)
         tail = tail.slice(1) # get rid of .
         if head


Debug

Debug

--- lib/ghc-modi-process-base.coffee
+++ lib/ghc-modi-process-base.coffee
@@ -1,5 +1,6 @@
 {BufferedProcess,Emitter,CompositeDisposable} = require('atom')
 CP = require('child_process')
+{debug} = require './util'

 module.exports =
 class GhcModiProcessBase
@@ -13,17 +14,24 @@
   spawnProcess: (rootDir,options)=>
     return unless @processMap?
     return unless atom.config.get('haskell-ghc-mod.enableGhcModi')
-    timer = setTimeout (=> @killProcessForDir rootDir), 60*60*1000
+    timer = setTimeout (=>
+      debug "Killing ghc-modi for #{rootDir.getPath()} due to inactivity"
+      @killProcessForDir rootDir), 60*60*1000
     proc = @processMap.get(rootDir)
+    debug "Checking for ghc-modi in #{rootDir.getPath()}"
     if proc?
+      debug "Found running ghc-modi instance for #{rootDir.getPath()}"
       clearTimeout proc.timer
       proc.timer = timer
       return proc.process
+    debug "Spawning new ghc-modi instance for #{rootDir.getPath()} with
+          #{"options.#{k} = #{v}" for k,v of options}"
     modiPath = atom.config.get('haskell-ghc-mod.ghcModiPath')
     proc = CP.spawn(modiPath,[],options)
     proc.on 'stderr', (data) ->
       console.error 'Ghc-modi says:',data
     proc.on 'exit', (code) =>
+      debug "ghc-modi for #{rootDir.getPath()} ended with #{code}"
       @processMap.delete(rootDir)
       @spawnProcess(rootDir,options) if code!=0
     @processMap.set rootDir,
@@ -41,6 +49,8 @@
       cmd = [command].concat args
     if text?
       cmd = ['--file-map',uri].concat cmd
+    debug "running #{modPath} #{cmd} with
+          #{"options.#{k} = #{v}" for k,v of options}"
     process=new BufferedProcess
       command: modPath
       args: cmd
@@ -50,6 +60,7 @@
       stderr: (data) ->
         err=err.concat(data.split('\n'))
       exit: (code) ->
+        debug "#{modPath} ended with code #{code}"
         if code!=0
           atom.notifications.addError "Haskell-ghc-mod: #{modPath}
               #{args.join ' '} failed with error code #{code}",
@@ -61,13 +72,17 @@
           callback result.slice(0,-1).map (line)->
             line.replace /\0/g,'\n'
     if text?
+      debug "sending stdin text to #{modPath}"
       process.process.stdin.write "#{text}\x04\n"

   runModiCmd: ({dir,options,command,text,uri,args,callback}) =>
+    debug "Trying to run ghc-modi in #{dir.getPath()}"
     process=@spawnProcess(dir,options)
     unless process
+      debug "Failed. Falling back to ghc-mod"
       return @runModCmd {options,command,text,uri,args,callback}
     parseData = (data)->
+      debug "Got response from ghc-modi:\n#{data}"
       lines = "#{data}".split("\n")
       result = lines[lines.length-2]
       unless result.match(/^OK/)
@@ -82,11 +97,14 @@
       callback lines.map (line)->
         line.replace /\0/g,'\n'
     if text?
+      debug "Loading file text for ghc-modi"
       process.stdin.write "load #{uri}\n#{text}\x04\n"
       process.stdout.once 'data', (data)->
         if "#{data}" isnt 'OK\n'
+          debug "Failed to load file text for ghc-modi"
           callback []
           return
+        debug "Successfully loaded file text for ghc-modi"
         process.stdout.once 'data', parseData
     else
       process.stdout.once 'data', parseData
@@ -95,18 +113,22 @@
       cmd = [command, uri].concat args
     else
       cmd = [command].concat args
+    debug "Running ghc-modi command #{cmd}"
     process.stdin.write cmd.join(' ').replace(/\r|\r?\n/g,' ') + '\n'

     if text?
+      debug "Unloading file text from ghc-modi"
       process.stdin.write "unload #{uri}\n"

   killProcess: =>
     return unless @processMap?
+    debug "Killing all ghc-modi processes"
     atom.project.getDirectories().forEach (dir) =>
       @killProcessForDir dir

   killProcessForDir: (dir) =>
     return unless @processMap?
+    debug "Killing ghc-modi process for #{dir.getPath()}"
     clearTimeout @processMap.get(dir)?.timer
     @processMap.get(dir)?.process.stdin?.end?()
     @processMap.get(dir)?.process.kill?()
@@ -114,6 +136,7 @@

   destroy: =>
     return unless @processMap?
+    debug "GhcModiProcessBase destroying"
     @killProcess()
     @emitter.emit 'did-destroy'
     @emitter = null


team: creation: create helper

team: creation: create helper

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -444,3 +444,31 @@
     return {}

+  createTeam: ->
+
+    teamData = KD.utils.getTeamData()
+    formData = {}
+
+    for key, value of teamData
+      for k, v of value
+        if k.search('invitee') >= 0
+          formData['invitees'] ?= v
+          formData['invitees'] += ",#{v}"
+        else
+          formData[k] = v
+
+    # manually add legacy fields - SY
+    formData.agree           = 'on'
+    formData.passwordConfirm = formData.password
+    formData.redirect        = "#{location.protocol}//#{formData.slug}.#{location.host}?username=#{formData.username}"
+
+    $.ajax
+      url       : "/-/teams/create"
+      data      : formData
+      type      : 'POST'
+      xhrFields : withCredentials : yes
+      success   : ->
+        localStorage.team = null
+        location.href = formData.redirect
+      error     : ({responseText}) =>
+        new KDNotificationView title : responseText


Fix debouncing of markdown rendering

Fix debouncing of markdown rendering

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -102,8 +102,9 @@
     null

   handleEvents: ->
-    @disposables.add atom.grammars.onDidAddGrammar => _.debounce((=> @renderMarkdown()), 250)
-    @disposables.add atom.grammars.onDidUpdateGrammar _.debounce((=> @renderMarkdown()), 250)
+    lazyRenderMarkdown = _.debounce((=> @renderMarkdown()), 250)
+    @disposables.add atom.grammars.onDidAddGrammar -> lazyRenderMarkdown()
+    @disposables.add atom.grammars.onDidUpdateGrammar -> lazyRenderMarkdown()

     atom.commands.add @element,
       'core:save-as': (event) =>


Sort requires

Sort requires

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -1,5 +1,5 @@
+path = require 'path'
 {_, $, $$$, EditorView, ScrollView} = require 'atom'
-path = require 'path'
 roaster = require 'roaster'
 {extensionForFenceName} = require './extension-helper'


JInvitation: use KodingError when there is error

--- workers/social/lib/social/models/invitation.coffee
+++ workers/social/lib/social/models/invitation.coffee
@@ -109,13 +109,16 @@
       { connection: { delegate:account } } = client
       account.fetchMyPermissions client, (err, permissionAndRoles) ->

+        return callback new KodingError 'Couldn\'t fetch user permissions'  if err
+
         { _id } = account
         { roles } = permissionAndRoles

         hasPermisson = 'admin' in roles or 'owner' in roles
         { inviterId, email, groupName } = invite

-        return callback 'noPermission'  if not hasPermisson and _id.toString() isnt inviterId
+        if not hasPermisson and _id.toString() isnt inviterId
+          return callback new KodingError 'You don\'t have permission'

         JInvitation.remove { email, groupName }, (err) ->
           return callback err


fix bug: wrong use of translation service in webhook destroy

--- vue/src/shared/services/webhook_service.coffee
+++ vue/src/shared/services/webhook_service.coffee
@@ -2,6 +2,7 @@
 import AppConfig      from '@/shared/services/app_config'
 import openModal      from '@/shared/helpers/open_modal'
 import EventBus from '@/shared/services/event_bus'
+import I18n from '@/i18n'

 export default new class WebhookService
   addAction: (group) ->
@@ -45,5 +46,5 @@
               submit: webhook.destroy
               text:
                 title: 'webhook.remove'
-                raw_helptext: @$t('webhook.confirm_remove', {name: webhook.name})
+                raw_helptext: I18n.t('webhook.confirm_remove', {name: webhook.name})
                 flash: 'webhook.removed'


support multiple args to resolve

support multiple args to resolve

--- lib/index.coffee
+++ lib/index.coffee
@@ -365,8 +365,11 @@
     else error: 'to(path) requires either a string or array of strings'

-  resolve: (path) ->
-    path = corepath.resolve @path, path
+  resolve: ->
+    paths = new Array arguments.length + 1
+    paths[i + 1] = arguments[i] for i in [0 ... arguments.length]
+    paths[0] = @path
+    path = corepath.resolve.apply null, paths
     if path is @path then this else new Path path


JDomain::create : use the new argument types

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -290,8 +290,6 @@
           resolveDomain domainData, (err)->
             return callback err  if err

-            createDomain domainData, delegate, group.slug, callback
-
   @createDomains = ({account, domains, hostnameAlias, group})->

     updateRelationship = (domainObj)->
@@ -306,6 +304,10 @@
           options = {data:{group}}
           account.addDomain domainObj, options, (err)->
             console.log err  if err?
+            createDomain {
+              domainData, account: delegate, group: group.slug, stack
+            }, callback
+

     domains.forEach (domain) ->
       domainObj = new JDomain {


Replace upperCase with shift-lowerCase

Replace upperCase with shift-lowerCase

--- keymaps/linux.cson
+++ keymaps/linux.cson
@@ -19,14 +19,14 @@

   # Sublime Parity
   'ctrl-,': 'application:show-settings'
-  'ctrl-N': 'application:new-window'
-  'ctrl-W': 'window:close'
+  'ctrl-shift-n': 'application:new-window'
+  'ctrl-shift-w': 'window:close'
   'ctrl-o': 'application:open-file'
   'ctrl-q': 'application:quit'
-  'ctrl-T': 'pane:reopen-closed-item'
+  'ctrl-shift-t': 'pane:reopen-closed-item'
   'ctrl-n': 'application:new-file'
   'ctrl-s': 'core:save'
-  'ctrl-S': 'core:save-as'
+  'ctrl-shift-s': 'core:save-as'
   'ctrl-f4': 'core:close'
   'ctrl-w': 'core:close'
   'ctrl-z': 'core:undo'
@@ -128,7 +128,7 @@
   'ctrl-down': 'editor:move-line-down'
   'ctrl-/': 'editor:toggle-line-comments'
   'ctrl-j': 'editor:join-lines'
-  'ctrl-D': 'editor:duplicate-lines'
+  'ctrl-shift-d': 'editor:duplicate-lines'
   'alt-shift-up': 'editor:add-selection-above'
   'alt-shift-down': 'editor:add-selection-below'

@@ -151,10 +151,10 @@
   'ctrl-alt-=': 'pane:increase-size'
   'ctrl-alt--': 'pane:decrease-size'

-# allow standard input fields to work correctly
+# Allow standard input fields to work correctly
 'body .native-key-bindings':
   'ctrl-z': 'native!'
-  'ctrl-Z': 'native!'
+  'ctrl-shift-z': 'native!'
   'ctrl-x': 'native!'
   'ctrl-c': 'native!'
   'ctrl-v': 'native!'


Add highlighting for various "noremenu" commands

--- grammars/viml.cson
+++ grammars/viml.cson
@@ -531,9 +531,9 @@
 	extraVimFunc:
 		name: "support.function.viml"
 		match: """(?x)\\b
-			(execute|Plugin|autocmd|[cinvo](?:un|nore)?map|(?:range)?go(?:to)?|(?:count)?(?:pop?|tag?|tn(?:ext)?|tp(?:revious)?|tr(?:ewind)?)
+			(execute|Plugin|autocmd|[cinvo]?(?:un|nore)?(?:map|menu)|(?:range)?go(?:to)?|(?:count)?(?:pop?|tag?|tn(?:ext)?|tp(?:revious)?|tr(?:ewind)?)
 			|(?:range)?(?:s(?:ubstitute)?|ret(?:ab)?|g(?:lobal)?)|unm(?:ap)?|map_l|mapc(?:lear)?|N?buffer|N?bnext|N?bNext|N?bprevious|N?bmod
-			|ab(?:breviate)?|norea(?:bbrev)?|[ic](?:un|nore)?ab|split_f|unmenu|rangefold|[ic](?:un|nore)?ab|[ic]abbrev|edit_f|next_f
+			|ab(?:breviate)?|norea(?:bbrev)?|[ic](?:un|nore)?ab|split_f|rangefold|[ic](?:un|nore)?ab|[ic]abbrev|edit_f|next_f
 			|(?:range)?(?:w(?:rite)?|up(?:date)?)|sar)
 		\\b"""


Increase static file max age

Increase static file max age

--- server/app.coffee
+++ server/app.coffee
@@ -30,7 +30,7 @@
 app.use favicon client_build + '/images/favicon.ico'
 app.use compression()
 app.use methodOverride()
-app.use express.static client_build
+app.use express.static(client_build, maxAge: 86400000)

 app.listen app.get('port'), ->
   console.log 'Express server listening on port ' + app.get('port')


Deal with narrow image on Resize

Deal with narrow image on Resize


fixing lint error

fixing lint error

--- src/components/cc-dashboard/index.cjsx
+++ src/components/cc-dashboard/index.cjsx
@@ -65,7 +65,7 @@
         <DashboardSectionProgress section={@props.section} />
       </BS.Col>
       <BS.Col xs={2}>
-        <DashboardSectionPerformance section={@props.section} />
+        <DashboardSectionPerformance section={@props.section} />
       </BS.Col>
       <BS.Col xs={2}>
       </BS.Col>


Input: resize made stateful and emits an event once height changes

--- src/components/inputs/inputview.coffee
+++ src/components/inputs/inputview.coffee
@@ -461,25 +461,36 @@
     @on 'input', (event) => KD.utils.defer => @resize event

-  resize: (event) ->
-
-    return  unless @_clone
-
-    @_clone.appendTo 'body' unless document.body.contains @_clone[0]
-    val = @getElement().value.replace(/\n/g,'\n ')
-    safeValue = Encoder.XSSEncode val
-    @_clone.html safeValue
-
-    height = @_clone.height()
-    if @$().css("boxSizing") is "border-box"
-      padding = parseInt(@_clone.css('paddingTop'),     10) + parseInt(@_clone.css('paddingBottom'),     10)
-      border  = parseInt(@_clone.css('borderTopWidth'), 10) + parseInt(@_clone.css('borderBottomWidth'), 10)
-      height  = height + border + padding
-
-    @setHeight \
-      if @initialHeight
-      then Math.max @initialHeight, height
-      else height
+  resize: do ->
+
+    previousHeight = null
+
+    (event) ->
+
+      return  unless @_clone
+
+      @_clone.appendTo 'body' unless document.body.contains @_clone[0]
+      val = @getElement().value.replace(/\n/g,'\n ')
+      safeValue = Encoder.XSSEncode val
+      @_clone.html safeValue
+
+      height = @_clone.height()
+
+      getValue = (rule) => parseInt @_clone.css(rule), 10
+
+      if @$().css('boxSizing') is 'border-box'
+        padding = getValue('paddingTop')     + getValue('paddingBottom')
+        border  = getValue('borderTopWidth') + getValue('borderBottomWidth')
+        height  = height + border + padding
+
+      newHeight = if @initialHeight then Math.max @initialHeight, height else height
+
+      if previousHeight isnt newHeight
+        @setHeight newHeight
+        @emit 'InputHeightChanged'
+
+      previousHeight = newHeight
+

   enableTabKey:-> @inputTabKeyEnabled = yes


:bug: Fix getRow using hard-coded index

:bug: Fix getRow using hard-coded index

--- lib/table.coffee
+++ lib/table.coffee
@@ -84,7 +84,7 @@

   getRows: -> @rows

-  getRow: (index) -> @rows[0]
+  getRow: (index) -> @rows[index]

   getRowsCount: -> @rows.length


less debugging

less debugging

--- lib/token.coffee
+++ lib/token.coffee
@@ -48,7 +48,6 @@
     Pudding.setWeb3 web3
     TokenContract.load Pudding
     tokenContract = TokenContract.at contractAddress
-    d { tokenContract }
     sender = config.rpc.from or throw new Error(
       "please set `rpc.from` property: #{envDir}/config.json")


indent error

indent error

--- lib/Context.coffee
+++ lib/Context.coffee
@@ -115,8 +115,8 @@
                             else
                                 done null, ctx

-                        else
-                            done null, ctx
+                    else
+                        done null, ctx

             else
                 done new Error 'Unknown Context-Type: '+config.type