export FlowComponent

export FlowComponent

--- app/builtins.coffee
+++ app/builtins.coffee
@@ -187,9 +187,9 @@
 FlowComponent = React.createClass
   displayName: 'FlowComponent'
   render: ->
-    React.DOM.div {style: {display: 'flex', flexWrap: 'wrap'}}, @props.children
-
-_.extend exports, {help_component, ExampleComponent, PromiseStatusComponent, ComponentAndError, PromiseResolvedComponent, ErrorComponent, ObjectComponent, ObjectBrowserComponent, ObservableComponent, PromiseComponent}
+    React.DOM.div {style: {display: 'flex', flexWrap: 'wrap'}, className: 'flex-layout'}, @props.children
+
+_.extend exports, {help_component, ExampleComponent, PromiseStatusComponent, ComponentAndError, PromiseResolvedComponent, ErrorComponent, ObjectComponent, ObjectBrowserComponent, ObservableComponent, PromiseComponent, FlowComponent}

 modules.export exports, 'builtins', ({doc, fn, cmd, component_fn, component_cmd}) ->
   component_cmd 'help', 'Shows this help', (ctx, o) ->


metric: added interstitial impression

metric: added interstitial impression

--- src/models/metric.coffee
+++ src/models/metric.coffee
@@ -146,6 +146,16 @@
     numerator:
       select: 'count(userId)'
       from: 'claimReward'
+      where: (day) ->
+        "time >= #{dayToMS day}ms AND time < #{dayToMS day + 1}ms"
+  }
+  {
+    name: 'interstitial impression'
+    format: '0'
+    isGroupSizeDependent: true
+    numerator:
+      select: 'count(userId)'
+      from: 'interstitialImpression'
       where: (day) ->
         "time >= #{dayToMS day}ms AND time < #{dayToMS day + 1}ms"
   }


add root route to express server

add root route to express server

--- node/app.coffee
+++ node/app.coffee
@@ -3,6 +3,9 @@
 app = express()

 app.use(express.bodyParser())
+
+app.get "/", (req, res, err) ->
+  res.send 200

 app.get "/problem", (req, res, err) ->
   res.send(501)


don't require markdown until needed

don't require markdown until needed

--- app/documentation.coffee
+++ app/documentation.coffee
@@ -130,8 +130,8 @@
   load_file: (name) ->
     # TODO run in node
     if process.browser
-      {images, content} = require "../lib/markdown-loader.coffee!../docs/#{name}.md"
       ->
+        {images, content} = require "../lib/markdown-loader.coffee!../docs/#{name}.md"
         Markdown.LeadMarkdownComponent
           value: content
           image_urls: images


Removed unused variable

Removed unused variable

--- app/js/core.coffee
+++ app/js/core.coffee
@@ -17,9 +17,6 @@
 # Create the Factlink container
 Factlink.el = $("#fl")

-# Create template wrapper
-Factlink.tmpl = {}
-
 # Add the stylesheet
 $style = $("<link>").attr
   type: "text/css"


Spec no longer relevant

Spec no longer relevant

--- test/models/user.coffee
+++ test/models/user.coffee
@@ -46,69 +46,3 @@
       it 'returns true if the collector level above 3', ->
         @user.set 'collector_level', 4
         @user.isCollector().should.be.true()
-
-  describe '#findOrCreate', ->
-    describe 'CurrentUser', ->
-      beforeEach ->
-        sinon.stub Backbone, 'sync'
-        sinon.stub(User, 'instantiate').returns new CurrentUser
-        @user = User.instantiate()
-
-      afterEach ->
-        User.instantiate.restore()
-        Backbone.sync.restore()
-
-      describe 'success', ->
-        it 'fetches the user; returns a promise; calls a success callback', (done) ->
-          promise = @user.findOrCreate success: (model) ->
-            model.id.should.equal 'current-user'
-            promise.isFulfilled().should.be.true()
-            done()
-          promise.isFulfilled().should.be.false()
-          Backbone.sync.args[0][1].url().should.containEql '/api/v1/me'
-          Backbone.sync.args[0][2].success fabricate 'user', id: 'current-user'
-
-      describe 'error', ->
-        it 'fetches the user; returns a promise; calls a success callback', (done) ->
-          promise = @user.findOrCreate error: ->
-            promise.isRejected().should.be.true()
-            done()
-          promise.isFulfilled().should.be.false()
-          Backbone.sync.args[0][1].url().should.containEql '/api/v1/me'
-          Backbone.sync.args[0][2].error()
-
-    describe 'LoggedOutUser', ->
-      beforeEach ->
-        sinon.stub Backbone, 'sync'
-        Backbone.__ANONYMOUS_SESSION_SYNC_WRAPPED__ = true
-        sinon.stub(User, 'instantiate').returns new LoggedOutUser
-        @user = User.instantiate()
-
-      afterEach ->
-        User.instantiate.restore()
-        Backbone.sync.restore()
-
-      describe 'existing session', ->
-        it 'tries to get the anonymous session', (done) ->
-          promise = @user.findOrCreate success: (collection) =>
-            @user.id.should.equal 'anonymous-session-id'
-            promise.isFulfilled().should.be.true()
-            done()
-          promise.isFulfilled().should.be.false()
-          Backbone.sync.args[0][2].url.should.containEql '/api/v1/me/anonymous_sessions'
-          Backbone.sync.args[0][2].success [id: 'anonymous-session-id']
-
-      describe 'no previous session', ->
-        it 'tries to get the session and if it comes up empty it creates one', (done) ->
-          promise = @user.findOrCreate success: =>
-            @user.id.should.equal 'fresh-anonymous-session-id'
-            promise.isFulfilled().should.be.true()
-            done()
-          promise.isFulfilled().should.be.false()
-          Backbone.sync.args[0][2].url.should.containEql '/api/v1/me/anonymous_sessions'
-          Backbone.sync.args[0][2].success [] # No existing session
-          # Creates one:
-          promise.isFulfilled().should.be.false()
-          Backbone.sync.args[1][0].should.equal 'create'
-          Backbone.sync.args[1][1].url().should.containEql '/api/v1/me/anonymous_session'
-          Backbone.sync.args[1][2].success id: 'fresh-anonymous-session-id'


annotate tutorial

annotate tutorial

--- games/tutorial.coffee
+++ games/tutorial.coffee
@@ -1,13 +1,21 @@
 exports.game = {
-  name: "Tutorial Game"
+  name: 'Tutorial Level'
   assume: 'baseline'
   comment: "The world's first NodeScript program"
-  author: {name: 'David Huffman', email: 'david@theswanfactory.com'}
-  license: 'Creative Commons'
+  author: {name: 'David Huffman', url: 'mailto:david@theswanfactory.com'}
+  license: {
+    name: 'Creative Commons Attribution 4.0 International'
+    url: 'http://creativecommons.org/licenses/by/4.0/'
+  }

   levels: [
     {
-      name: 'One of Everything'
+      name: 'A Complex Maze'
+      goal: {
+        clicks: 6
+        ticks: 20
+        bricks: 6
+      }
       sprites: [
         {kind: 'gate'}
         {kind: 'wall'}
@@ -64,11 +72,6 @@
         }

       ]
-      goal: {
-        clicks: 1
-        ticks: 4
-        bricks: 1
-      }
     }
   ]
 }


Oh, actually this test is working

Oh, actually this test is working

--- test/node/module.coffee
+++ test/node/module.coffee
@@ -1,7 +1,6 @@
 udefine = require '../../dist/udefine'
 {expect} = require 'chai'

-# TODO: This test is not working yet
 describe 'udefine commonjs dependencies', ->
   it 'multiple dependencies', (done) ->
     udefine.paths.commonjs.base = 'test/node'


limit calls to 15. fixes lessthan3/packages#3952

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -247,6 +247,7 @@
     LAST_SCRIPT_LOADED = 0
     SCRIPT_LOAD_TIME = 0
     SECONDS_ELAPSED = 0
+    RATE_LIMIT = 15
     DOMAIN = args[0]
     debug_mode = true if args[1] is 'debug'
     log 'DEBUG MODE' if debug_mode
@@ -376,7 +377,7 @@
           if debug_mode
             async.eachSeries domains, domain_iterator, -> done null, SITES
           else
-            async.each domains, domain_iterator, -> done null, SITES
+            async.eachLimit domains, RATE_LIMIT, domain_iterator, -> done null, SITES

         async.waterfall [
           (next) -> getDomains next
@@ -439,7 +440,7 @@
         if debug_mode
           async.eachSeries sites, site_iterator, -> done null, SCRIPTS
         else
-          async.each sites, site_iterator, -> done null, []
+          async.eachLimit sites, RATE_LIMIT, site_iterator, -> done null, []

       # create URLs for package/main.js
@@ -488,7 +489,7 @@
         if debug_mode
           async.eachSeries scripts, script_iterator, done
         else
-          async.each scripts, script_iterator, done
+          async.eachLimit scripts, RATE_LIMIT, script_iterator, done

       # format errors to github markup, copy to clipboard
       compileErrors = (next) ->


編輯提示字

編輯提示字

居然之前一直沒有改

--- javascript/bus.coffee
+++ javascript/bus.coffee
@@ -71,7 +71,7 @@
         in_hours = (minutes - now_minutes) // 60
         in_minutes = (minutes - now_minutes) % 60
         message = switch
-          when info is "NA" then "本部末班車已過"
+          when info is "NA" then "末班車已過"
           when in_hours is 0 then "#{info}
【剩下 #{in_minutes} 分鐘】"
           else "#{info}
【剩下 #{in_hours} 小時 #{in_minutes} 分鐘】"
         document.getElementById(campus).innerHTML = message


fix markdown rendering

fix markdown rendering

--- app/markdown.coffee
+++ app/markdown.coffee
@@ -8,7 +8,7 @@

 fix_marked_renderer_href = (fn, base_href) ->
   (href, args...) ->
-    fn URI(href).absoluteTo(base_href).toString(), args...
+    fn.call this, URI(href).absoluteTo(base_href).toString(), args...

 UserHtmlComponent = React.createClass
   displayName: 'UserHtmlComponent'


builder: do not expose kd.js in watch mode (breaks watchify)

--- client/builder/lib/index.coffee
+++ client/builder/lib/index.coffee
@@ -276,7 +276,10 @@
     else
       b = bant opts_

-    b.require require.resolve('kd.js'), expose: 'kd'
+    if not opts.watchJs
+      # xxx: breaks watchify in watch mode, dedupe fuckup
+      b.require require.resolve('kd.js'), expose: 'kd'
+
     b.use manifests

     # force browser-pack to expose require


IDE: remove router handler for koding sidebar logo

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -131,9 +131,6 @@
       if routeInfo.path.indexOf('/IDE') is -1
         if mainView.isSidebarCollapsed
           mainView.toggleSidebar()
-      else
-        if @isKodingSidebarCollapsed and not mainView.isSidebarCollapsed
-          mainView.toggleSidebar()

   setActiveTabView: (tabView) ->
     @activeTabView = tabView


IDE: Fixed resize issue when sidebar collapsed.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -215,6 +215,7 @@
       floatedPanel._lastSize = desiredSize
       @getView().setClass 'sidebar-collapsed'
       @isSidebarCollapsed = yes
+      KD.getSingleton("windowController").notifyWindowResizeListeners()

     splitView.setFloatingPanel 0, 39
     tabView.showPaneByName 'Dummy'


assign key to notebook io cells

assign key to notebook io cells

--- app/notebook.coffee
+++ app/notebook.coffee
@@ -70,11 +70,11 @@
     ios = []
     _.each @state.value, (cell) ->
       if cell.type == 'input'
-        props = input_cell: cell
+        props = input_cell: cell, key: cell.key
         ios.push props
       else
         if !props? or props.input_cell.output_cell != cell
-          ios.push output_cell: cell
+          ios.push output_cell: cell, key: cell.key
         else
           props.output_cell = cell
         props = null


Provides a test helper for simple generators

--- test/test_helper.coffee
+++ test/test_helper.coffee
@@ -4,13 +4,15 @@
 Neat.require 'core'

 {run} = require '../lib/utils/commands'
-{resolve, existsSync:eS} = require 'path'
+{resolve} = require 'path'
 {rmSync:rm, ensurePath, touch} = Neat.require 'utils/files'
 {print} = require 'util'

 global.TEST_ROOT = resolve '.'
 global.FIXTURES_ROOT = '/tmp'
 global.NEAT_BIN = resolve __dirname, '../bin/neat'
+
+eS = fs.existsSync

 options = {}
   # stderr: (data)-> print data
@@ -130,3 +132,58 @@
           callback?()

   withProject name, desc, block, opts
+
+global.testSimpleGenerator= (name, dir, ext) ->
+  describe 'when outside a project', ->
+    beforeEach -> process.chdir FIXTURES_ROOT
+
+    describe "running `neat generate #{name} 'foo'`", ->
+      it "should return a status of 1 and don't generate anything", ->
+        ended = false
+        runs ->
+          args = [NEAT_BIN, 'generate', name, 'foo']
+          run 'node', args, options, (status) ->
+            expect(status).toBe(1)
+            ended = true
+
+        waitsFor progress(-> ended), 'Timed out', 10000
+
+  withProject 'foo', ->
+    describe "running `neat generate #{name}`", ->
+      it "should return a status of 1 and don't generate anything", ->
+        ended = false
+        runs ->
+          run 'node', [NEAT_BIN, 'generate', name], options, (status) ->
+            expect(status).toBe(1)
+            ended = true
+
+        waitsFor progress(-> ended), 'Timed out', 10000
+
+    describe "running `neat generate #{name} foo`", ->
+      it "should generate a new #{name} foo in the project", (done) ->
+        run 'node', [NEAT_BIN, 'generate', name, 'foo'], (status) ->
+          expect(inProject "#{dir}/foo#{ext}").toExist()
+
+          done()
+
+    describe "running `neat generate #{name} bar/foo`", ->
+      it "should generate a new #{name} foo in the project", (done) ->
+        run 'node', [NEAT_BIN, 'generate', name, 'bar/foo'], (status) ->
+          expect(inProject "#{dir}/bar/foo#{ext}")
+            .toExist()
+
+          done()
+
+    describe "with a file already existing at the same path", ->
+      it "should return a status of 1 and don't generate anything", ->
+        ended = false
+        runs ->
+          withSourceFile "#{dir}/foo#{ext}", 'original_content', ->
+            args = [NEAT_BIN, 'generate', name, 'foo']
+            run 'node', args, options, (status) ->
+              expect(status).toBe(1)
+              expect("#{dir}/foo#{ext}").toExist()
+              expect("#{dir}/foo#{ext}").toContain('original_content')
+              ended = true
+
+          waitsFor progress(-> ended), 'Timed out', 10000


Minor update

Minor update

--- app/pages/home.cjsx
+++ app/pages/home.cjsx
@@ -16,13 +16,6 @@
       title: 'People-Powered Science'
       tagline: 'The Zooniverse is a platform for citizen science and an opportunity for anyone to contribute to science.'
       button: 'Get started!'
-    featuredStats:
-      project: 'Project'
-      subjects: 'subjects'
-      classifications: 'classifications'
-    try:
-      title: 'Want to make your own project?'
-      button: 'Create an account'

 module.exports = React.createClass
   displayName: 'HomePage'


Add helper to run tests that trigger several different stanzas

--- test/test_server.coffee
+++ test/test_server.coffee
@@ -235,6 +235,29 @@

         @emit "stanza", stanza.root()

+    # Run an asynchronous test that triggers several stanzas.
+    # @param [ltx.Element] stanza Stanza to send to the buddycloud server
+    # @param [callback] cb_done Function to call when all the tests are over or
+    #   when an exception has been caught
+    # @param [Object] events A mapping of event names to check functions
+    doTests: (stanza, cb_done, events) ->
+        eventsLeft = 0
+        cb_partial = ->
+            eventsLeft -= 1
+            if eventsLeft == 0
+                cb_done()
+
+        for event, cb_check of events
+            eventsLeft += 1
+            @once event, (data) ->
+                try
+                    cb_check(data)
+                    cb_partial()
+                catch e
+                    cb_done(e)
+
+        @emit "stanza", stanza.root()
+
     # Used by the buddycloud server to send XML stanzas to the XMPP server.
     #
     # This parses and route these stanzas. Don't use this in tests! If you want


bongo: hush a meaningless log message

bongo: hush a meaningless log message

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -284,8 +284,7 @@
                       arguments : [api]
                       callbacks : {}
                     @respondToClient message.routingKey, response
-              else if routingKey is 'auth.leave'
-                console.log 'got an auth.leave message'
+              else if routingKey is 'auth.leave' then # ignore
               else
                 @handleRequest routingKey,
                   if message.data? then "#{message.data}"


Minor refactor

Minor refactor

Page numbering again

--- src/scripts/modules/media/tabs/contents/contents.coffee
+++ src/scripts/modules/media/tabs/contents/contents.coffee
@@ -8,7 +8,6 @@
   cumulativeChapters = []
   numberChapters = (toc, depth=0) ->
     sectionNumber = 0
-    skippedIntro = false
     for item in toc
       isSection = not item.get('contents')?
       isCcap = (item.get('book')?.get('printStyle') ? '').match(/^ccap-/)?
@@ -20,12 +19,10 @@
           chapterNumber = cumulativeChapters.slice(0,depth).join('.')
           sectionNumber = cumulativeChapters[depth] ? 0
         else if not atTopLevel
-          if sectionNumber > 0 or skippedIntro
-            sectionNumber += 1
+          if sectionNumber > 0
             cumulativeChapters[depth] = sectionNumber
             item.set('chapter', "#{chapterNumber}.#{sectionNumber}")
-          else
-            skippedIntro = true
+          sectionNumber += 1
       else
         if cumulativeChapters[depth]?
           cumulativeChapters[depth] += 1


Capitalize search filter limit names

Capitalize search filter limit names

--- src/scripts/modules/search/results/filter/filter.coffee
+++ src/scripts/modules/search/results/filter/filter.coffee
@@ -2,6 +2,17 @@
   BaseView = require('cs!helpers/backbone/views/base')
   template = require('hbs!./filter-template')
   require('less!./filter')
+
+  FILTER_NAMES = {
+    "author": "Author"
+    "keyword": "Keyword"
+    "mediaType": "Type"
+    "pubYear": "Publication Date"
+    "subject": "Subject"
+  }
+
+  capitalize: (str) ->
+    return (str.split(' ').map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' '

   return class SearchResultsFilterView extends BaseView
     template: template
@@ -12,9 +23,8 @@
       _.each limits, (limit) ->
         _.each _.keys(limit), (key) ->
           if key isnt 'count'
+            filterName = FILTER_NAMES[key] or capitalize(key)
             name = limit[key]
-            filterName = key
-            if filterName is 'mediaType' then filterName = 'type'
             if name is 'application/vnd.org.cnx.collection' then name = 'book'
             else if name is 'application/vnd.org.cnx.module' then name = 'page'
             filters[filterName] = filters[filterName] or {}


Add failed and pause functions to Task.

Add failed and pause functions to Task.

--- src/models/task.coffee
+++ src/models/task.coffee
@@ -76,13 +76,17 @@
   @lastExecution = Date.now()
   @status = 'IDLE'
   @nextExecution = new Date(@lastExecution.getTime() + @scheduler.duration)
-  yield @save
+  yield @save()

 TaskSchema.methods.failed = (err) ->
+  @status = 'ERROR'
+  yield @save()

 TaskSchema.methods.pause = () ->
+  @status = 'PAUSED'
+  yield @save()

 module.exports = Task = mongoose.model 'Task', TaskSchema


We now handle and recover from WebGL context loss

--- app/WebGLCanvas.coffee
+++ app/WebGLCanvas.coffee
@@ -54,12 +54,6 @@
     unless browserSupportsRequiredFeatures()
       throw new Error "This browser does not support WebGL"

-    @_contextRequiresSetup = true
-    @_fragmentShaderIsDirty = false
-    @_vertexShaderIsDirty = false
-    @_geometryIsDirty = false
-    @_renderIsDirty = false
-
     # The number of vertices drawn
     @vertexCount = 4

@@ -71,6 +65,10 @@

     @canvas.addEventListener "webglcontextcreationerror", (event) =>
       @trace.error event.statusMessage
+
+    @canvas.addEventListener "webglcontextlost", => @_handleContextLost()
+    @canvas.addEventListener "webglcontextrestored", => @_handleContextRestored()
+

     @_createContext()

@@ -92,9 +90,13 @@

     @_frameScheduled = false

+    if @_contextLost
+      return
+
     if @_contextRequiresSetup
       @_setupContext()
       @_contextRequiresSetup = false
+      @_vertexShaderIsDirty = @_fragmentShaderIsDirty = @_geometryIsDirty = true

     if @_geometryIsDirty
       @_updateGeometry()
@@ -135,6 +137,7 @@

     @debugContext = WebGLDebugUtils.makeDebugContext @nativeContext, null, throwErrorOnUndefinedArgument, null

+    @_contextRequiresSetup = true

     @gl = if @_debugMode then @debugContext else @nativeContext

@@ -225,6 +228,16 @@
     gl.drawArrays @_drawingMode, 0, @vertexCount

+  _handleContextLost: (e) ->
+    @trace.log "WebGL context lost, suspending all GL calls"
+    @_contextLost = true
+    (e || window.event).preventDefault()
+
+  _handleContextRestored: (e) ->
+    @trace.log "WebGL context restored, resuming rendering"
+    @_contextLost = false
+    @_contextRequiresSetup = true
+    @_scheduleFrame()


fixes extend; adds comments

fixes extend; adds comments

--- src/net-cluster.coffee
+++ src/net-cluster.coffee
@@ -4,10 +4,14 @@
 nop = require "nop"
 TCP = process.binding('tcp_wrap').TCP

+##
+# utility function
 extend = (obj, mixin) ->
-  obj[name] = method for name, method in mixin
+  obj[name] = method for name, method of mixin
   obj

+##
+# functions from node's net module
 toNumber = (x) ->
   x = Number(x)
   if x >= 0
@@ -22,6 +26,8 @@
   e.syscall = syscall
   return e

+##
+# new server class extending node's net.Server
 class Server extends net.Server
   _netClusterListen: (address, port, addressType) ->
     handle = new TCP()
@@ -95,7 +101,11 @@
       return @emit 'error', err if err
       @_netClusterListen.apply(@, portArgs)

-extend module.exports, net
+##
+# provide everything that the net module does
+extend(module.exports, net)
+
+# but override the createServer function to use our server
 module.exports.createServer = ->
   new Server(arguments[0], arguments[1])


AppController: Method rename. isMachineNotRunning -> isMachineRunning.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -269,12 +269,9 @@
     filesPane.emit 'MachineUnmountRequested', machineData

-  isMachineNotRunning: ->
-
-    states    = [ 'Stopped', 'NotInitialized', 'Terminated', 'Starting', 'Building' ]
-    { state } = @mountedMachine.status
-
-    return states.indexOf(state) > -1
+  isMachineRunning: ->
+
+    return @mountedMachine.status.state is Machine.State.Running

   createInitialView: ->
@@ -288,7 +285,7 @@
         for ideView in @ideViews
           ideView.mountedMachine = @mountedMachine

-        if @isMachineNotRunning()
+        unless @isMachineRunning()
           nickname     = KD.nick()
           machineLabel = machine.slug or machine.label
           splashs      = IDE.splashMarkups
@@ -589,7 +586,7 @@

   writeSnapshot: ->

-    return  if @isMachineNotRunning()
+    return  unless @isMachineRunning()

     name  = @getWorkspaceSnapshotName()
     value = @getWorkspaceSnapshot()
@@ -839,9 +836,7 @@
       @fakeFinderView?.destroy()
       @fakeViewsDestroyed = yes

-    if snapshot
-      @resurrectLocalSnapshot snapshot
-
+    if snapshot then @resurrectLocalSnapshot snapshot
     else
       @ideViews.first.createEditor()
       @ideViews.last.createNewTerminal { machine }
@@ -850,8 +845,8 @@

   resurrectLocalSnapshot: (snapshot) ->

-    for key, value of snapshot
-      @createPaneFromChange value, yes  if value
+    for key, value of snapshot when value
+      @createPaneFromChange value, yes

   toggleFullscreenIDEView: ->


ampersands

ampersands

--- apps/order/routes.coffee
+++ apps/order/routes.coffee
@@ -9,7 +9,7 @@
       res.redirect '/'

 @resume = (req, res) ->
-  unless ((token = req.query.token) && (orderId = req.params.id)) and req.user
+  unless ((token = req.query.token) && (orderId = req.params.id)) && req.user
     return res.redirect '/'
   req.user.resumeOrder orderId, token,
     success: ->


Add toggle on/off for time-tracking and display of time-tracking.

--- plugins/time-tracking/time-tracking.coffee
+++ plugins/time-tracking/time-tracking.coffee
@@ -27,15 +27,41 @@
       @logger = @api.logger
       @logger.info "Loading time tracking"
       @api.cursor.on 'rowChange', (@onRowChange.bind @)
-      @onRowChange undefined, @api.cursor.row # Initial setup
+      @onRowChange null, @api.cursor.row # Initial setup
       @api.view.on 'renderLine', (@onRenderLine.bind @)
       @api.view.data.on 'afterDescendantRemoved', (@onDescendantRemoved.bind @)
       @api.view.data.on 'afterDescendantAdded', (@onDescendantAdded.bind @)
       @rowChanges = []
       @currentRow = null
-      @displayTime = true
       @api.view.on 'exit', () =>
         @onRowChange @currentRow, null
+      CMD_TOGGLE = @api.registerCommand {
+        name: 'TOGGLE'
+        default_hotkeys:
+          normal_like: ['Z']
+      }
+      CMD_TOGGLE_DISPLAY = @api.registerCommand {
+        name: 'TOGGLE_DISPLAY'
+        default_hotkeys:
+          normal_like: ['d']
+      }
+      CMD_TOGGLE_LOGGING = @api.registerCommand {
+        name: 'TOGGLE_LOGGING'
+        default_hotkeys:
+          normal_like: ['l']
+      }
+      @api.registerAction [@api.modes.NORMAL], CMD_TOGGLE, {
+        description: 'Toggle a setting',
+      }, {}
+      @api.registerAction [@api.modes.NORMAL], [CMD_TOGGLE, CMD_TOGGLE_DISPLAY], {
+        description: 'Toggle whether time spent on each row is displayed',
+      }, () =>
+        do @toggleDisplay
+      @api.registerAction [@api.modes.NORMAL], [CMD_TOGGLE, CMD_TOGGLE_LOGGING], {
+        description: 'Toggle whether time is being logged',
+      }, () =>
+        do @toggleLogging
+

     getRowData: (id, keytype) ->
       key = "#{id}:#{keytype}"
@@ -45,6 +71,25 @@
       @api.setData key, value
     transformRowData: (id, keytype, transform) ->
       @setRowData id, keytype, (transform (@getRowData id, keytype))
+
+    DEFAULT_LOGGING = true
+    DEFAULT_DISPLAY = true
+    isLogging: () ->
+      (@api.getData "isLogging") ? DEFAULT_LOGGING
+    shouldDisplayTime: () ->
+      (@api.getData "display") ? DEFAULT_DISPLAY
+    toggleLogging: () ->
+      @logger.info "Turning logging #{if (do @isLogging) then "off" else "on"}"
+      if do @isLogging
+        @onRowChange @api.cursor.row, null # Final close
+      else
+        @onRowChange null, @api.cursor.row # Initial setup
+      @api.setData "isLogging", not (do @isLogging)
+      do @api.view.render
+    toggleDisplay: () ->
+      @logger.info "Turning display #{if (do @shouldDisplayTime) then "off" else "on"}"
+      @api.setData "display", not (do @shouldDisplayTime)
+      do @api.view.render

     _date: (timestamp) ->
       "#{timestamp.getFullYear()}-#{timestamp.getMonth()}-#{timestamp.getDate()}"
@@ -66,6 +111,8 @@

     onRowChange: (from, to) ->
       @logger.debug "Switching from row #{from?.id} to row #{to?.id}"
+      if not do @isLogging
+        return
       time = new Date()
       if @currentRow and @currentRow.id != to?.id
         @onRowPeriod { start: @currentRow.time, stop: time, id: @currentRow.id, row: from }
@@ -163,7 +210,7 @@
         "#{seconds}s"

     onRenderLine: (row, renderArray, options) ->
-      if @displayTime
+      if do @shouldDisplayTime
         time = @rowTime row
         if time > 1000
           @logger.info "Rendering time for row #{row.id} as #{time}"


Remove related posts when topic is deleted

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -190,6 +190,7 @@
   # 7. saveTrackedItem needs to load trackedItemStatuses when new
   # 8. deleteTrackedItem needs to remove trackedItemStatuses
   # 9. deleteTeam needs to remove all related data except plan and sport
+  # 10. deleteForumTopic needs to delete all related posts

   # Load related records when a member is created
   wrapSave sdk, 'saveMember', (member) ->
@@ -358,7 +359,7 @@
           teamId = member.teamId
           bulkLoadTypes = "memberStatistic,statisticDatum,statisticAggregate"
           sdk.bulkLoad(teamId, bulkLoadTypes)
-
+
         ).then -> result
       ).fail((err) ->
         linking.linkItems toRemove, lookup
@@ -489,6 +490,17 @@
         result
       ).callback callback

+  # Remove related posts when a topic is deleted
+  wrapMethod sdk, 'deleteForumTopic', (deleteForumTopic) ->
+    (topic, callback) ->
+      toRemove = []
+      toRemove.push topic.forumPosts...
+
+      linking.unlinkItems toRemove, lookup
+      deleteForumTopic.call(this, topic, callback).fail((err) ->
+        linking.linkItems toRemove, lookup
+        err
+      ).callback callback

 revertSDK = (sdk) ->


make logging in from /home work

make logging in from /home work

--- js/libs/github.coffee
+++ js/libs/github.coffee
@@ -1,5 +1,5 @@
 checkOuterPages = (url) =>
-  outerPages = ["docs", "about", "privacy", "pricing"]
+  outerPages = ["docs", "about", "privacy", "pricing", "home"]
   for page in outerPages
     if (url.match "^/#{page}.*")
       return "/"


Tasks must return true when done

Tasks must return true when done

--- app/core/Entity.coffee
+++ app/core/Entity.coffee
@@ -279,11 +279,13 @@
   blockInput: () =>
     @tasks.push ->
       require('mediator').blockInput = true
+      return true

   unblockInput: () =>
     @tasks.push ->
       require('mediator').blockInput = false
+      return true

   getActualMoveDistance: (distance) =>


Standardize child views

Standardize child views

--- src/trix/views/object_view.coffee
+++ src/trix/views/object_view.coffee
@@ -19,8 +19,7 @@

   findOrCreateCachedChildView: (viewClass, object, options) ->
     if view = @getCachedViewForObject(object)
-      view.parentView = this
-      @childViews.push(view)
+      @recordChildView(view)
     else
       view = @createChildView(arguments...)
       @cacheViewForObject(view, object)
@@ -32,6 +31,9 @@
       viewClass = Trix.ObjectGroupView

     view = new viewClass object, options
+    @recordChildView(view)
+
+  recordChildView: (view) ->
     view.parentView = this
     view.rootView = @rootView
     @childViews.push(view)


Fix crash when registering subscriber

Fix crash when registering subscriber

--- lib/subscriber.coffee
+++ lib/subscriber.coffee
@@ -123,9 +123,9 @@
             cb(@info)
         else
             @redis.hgetall @key, (err, @info) =>
-                if info?.updated? # subscriber exists
+                if @info?.updated? # subscriber exists
                     # transform numeric value to number type
-                    for own key, value of info
+                    for own key, value of @info
                         num = parseInt(value)
                         @info[key] = if num + '' is value then num else value
                     cb(@info)


Rename mapper to context in js/rdfa/parser

--- js/rdfa/parser.coffee
+++ js/rdfa/parser.coffee
@@ -164,7 +164,7 @@
   class State
     constructor: (@base, @profile, resolveURI) ->
       @resolveURI = resolveURI
-      @mapper = new Mapper(null, contexts[@profile])
+      @context = new Context(null, contexts[@profile])
       @lang = null

       @lists = {}
@@ -175,31 +175,31 @@
     createSubState: (base, lang, vocab, prefixes) ->
       subState = inherit(this)
       subState.lang = if lang? then lang else @lang
-      subState.mapper = @mapper.createSubMap(vocab, prefixes)
+      subState.context = @context.createSubContext(vocab, prefixes)
       return subState

     expandTermOrCurieOrIRI: (expr) ->
-      @mapper.expandTermOrCurieOrIRI(expr)
+      @context.expandTermOrCurieOrIRI(expr)

     expandCurieOrIRI: (expr) ->
-      @mapper.expandCurieOrIRI(expr)
+      @context.expandCurieOrIRI(expr)

     expandAndResolve: (curieOrIri) ->
       @resolveURI(@expandCurieOrIRI(curieOrIri))

-  class Mapper
-    constructor: (@vocab=null, @map={}) ->
-
-    createSubMap: (vocab, prefixes) ->
+  class Context
+    constructor: (@vocab=null, @prefixes={}) ->
+
+    createSubContext: (vocab, prefixes) ->
       vocab ?= @vocab
-      subMap = inherit(@map)
+      subMap = inherit(@prefixes)
       for pfx, iri of prefixes
         subMap[pfx] = iri
-      return new Mapper(vocab, subMap)
+      return new Context(vocab, subMap)

     expandTermOrCurieOrIRI: (expr) ->
-      iri = @map[expr]
+      iri = @prefixes[expr]
       if iri
         return iri
       else if expr.indexOf(":") is -1
@@ -220,7 +220,7 @@
         return expr
       if term.slice(0, 2) is "//"
         return expr
-      ns = @map[pfx]
+      ns = @prefixes[pfx]
       if ns
         return ns + term
       return expr
@@ -231,12 +231,10 @@
   # attributes of an element. Use this to produce triples.
   class Description
     constructor: (el, parentState) ->
-      #@data =
       data = new ElementData(el, parentState)
       @usesVocab = data.getVocab()
       @state = data.state
-      @tagName = data.tagName
-      #@mapper = data.mapper
+      #@context = data.context
       @about = data.getAbout()
       @resource = data.getResource()
       @types = data.getTypes()
@@ -471,7 +469,7 @@
   exports.extract = extract
   exports.Description = Description
   exports.State = State
-  exports.Mapper = Mapper
+  exports.Context = Context
   exports.ElementData = ElementData
   exports.contexts = contexts


Save temporary uuid mailbox

Save temporary uuid mailbox

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -393,15 +393,15 @@
                 success(error, message) if _.isFunction success
                 return

+            # Initialize @state
+            # Must stay silent (do not use setState)
             unless @state.id
                 MessageActionCreator.setCurrent message.id
-
-                # Initialize @state
-                # Must stay silent (do not use setState)
-                _keys = _.without _.keys(message), 'attachments', 'html', 'text'
-                _.each _keys, (key) =>
-                    if _.isUndefined @state[key]
-                        @state[key] = message[key]
+                @state.id = message.id
+                @state.conversationID = message.conversationID
+
+            # Update State tmp ID
+            @state.mailboxIDs = message.mailboxIDs

             @props.lastUpdate = @state.date
             @props.isSaving = false


Fix React error message when canceling draft

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -253,13 +253,15 @@
         if @state.isDraft and
            @state.id?
             if not window.confirm(t 'compose confirm keep draft')
-                MessageActionCreator.delete @state.id, (error) ->
-                    if error?
-                        LayoutActionCreator.alertError \
-                            "#{t("message action delete ko")} #{error}"
-                    else
-                        LayoutActionCreator.notify t('compose draft deleted'),
-                            autoclose: true
+                window.setTimeout =>
+                    MessageActionCreator.delete @state.id, (error) ->
+                        if error?
+                            LayoutActionCreator.alertError \
+                                "#{t("message action delete ko")} #{error}"
+                        else
+                            LayoutActionCreator.notify t('compose draft deleted'),
+                                autoclose: true
+                , 0
             else
                 if @state.originalConversationID?
                     # save one last time the draft, adding the conversationID


Fix broken scroll on firefox

Fix broken scroll on firefox

--- coffee/image_list_widget.coffee
+++ coffee/image_list_widget.coffee
@@ -14,7 +14,7 @@

         @binder.bindAction(
             Rx.Observable
-                .fromEvent($(window), 'scroll')
+                .fromEvent($(window), 'scroll mousewheel')
                 .map(() => Unit.create())
                 .startWith(Unit.create()),
             () => @reveal()
@@ -23,7 +23,7 @@
     onDestroy: () ->
         @binder.detach()

-    getScroll: () -> $('body').scrollTop()
+    getScroll: () -> document.documentElement.scrollTop || document.body.scrollTop

     isVisible: (e) ->
         extra = @height * .5


#57 - Wrapping up the close all module

#57 - Wrapping up the close all module

--- coffee/notific8-closeall.coffee
+++ coffee/notific8-closeall.coffee
@@ -79,19 +79,7 @@
     { namespace } = data.settings
     container.querySelector(".#{namespace}-closeall-button")

-  notific8(
-    'registerModule'
-    'closeall'
-    'insideContainer'
-    {
-      closeAll: false
-      closeAllText: 'Close All'
-      closeAllTheme: 'legacy'
-      closeAllColor: 'teal'
-    }
-    closeAllCallback
-  )
-
+  # configure the onCreate event handler
   notific8(
     'configure'
     onCreate: (notification, data) ->
@@ -101,6 +89,7 @@
         closeButton.style.display = 'block'
   )

+  # configure the onClose event handler
   notific8(
     'configure'
     onClose: (notification, data) ->
@@ -110,6 +99,7 @@
         closeButton.style.display = 'none'
   )

+  # configure the onContainerCreate event handler
   notific8(
     'configure'
     onContainerCreate: (container, options) ->
@@ -135,11 +125,24 @@

         notificationClass = ".#{namespace}-notification.open"
         notifications = container.querySelectorAll(notificationClass)
+
         for notification in notifications
-          console.log 'close all is on ', notification.id
-          setTimeout ->
-            notification.querySelector(".#{namespace}-close").click()
-          # , 5
-          , 100
+          ((n, namespace) ->
+            n.querySelector(".#{namespace}-close").click()
+          )(notification, namespace)
         return
   )
+
+  # register the closeAll module
+  notific8(
+    'registerModule'
+    'closeall'
+    'insideContainer'
+    {
+      closeAll: false
+      closeAllText: 'Close All'
+      closeAllTheme: 'legacy'
+      closeAllColor: 'teal'
+    }
+    closeAllCallback
+  )


fix weird Backbone/YouTube bug on Firefox

--- client/code/view/table-xtract.coffee
+++ client/code/view/table-xtract.coffee
@@ -19,12 +19,6 @@
     @

   setUpYouTube: ->
-    tag = document.createElement('script')
-
-    tag.src = "https://www.youtube.com/iframe_api"
-    firstScriptTag = document.getElementsByTagName('script')[0]
-    firstScriptTag.parentNode.insertBefore tag, firstScriptTag
-
     window.onYouTubeIframeAPIReady = ->
       window.player = new YT.Player 'youtube-video',
         height: '390'
@@ -40,3 +34,14 @@
         _gaq.push ['_trackEvent', 'table-xtract', 'video-pause']
       else if event.data is YT.PlayerState.ENDED
         _gaq.push ['_trackEvent', 'table-xtract', 'video-finished']
+
+    if YT?
+      # YouTube plugin has already been loaded on a previous page load,
+      # but we inexplicably need to wait a bit before using it
+      setTimeout window.onYouTubeIframeAPIReady, 1000
+    else
+      # No YouTube plugin yet, so insert it into the head
+      tag = document.createElement('script')
+      tag.src = "https://www.youtube.com/iframe_api"
+      firstScriptTag = document.getElementsByTagName('script')[0]
+      firstScriptTag.parentNode.insertBefore tag, firstScriptTag


actually include the mixin that's being used

--- tutor/src/components/reference-book/reference-book.cjsx
+++ tutor/src/components/reference-book/reference-book.cjsx
@@ -7,7 +7,7 @@

 NavBar = require './navbar'
 Menu = require './slide-out-menu'
-{ResizeListenerMixin} = require 'shared'
+{ResizeListenerMixin, ChapterSectionMixin} = require 'shared'
 PageShell = require './page-shell'
 ReferenceViewPageNavigation = require './page-navigation'


Fixing external constructors / order of execution for once and for all ... knock on wood.

--- nodes.coffee
+++ nodes.coffee
@@ -816,11 +816,11 @@
     if @boundFuncs.length
       for bvar in @boundFuncs
         bname = bvar.compile o
-        @ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this);"
+        @ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this)"

   # Merge the properties from a top-level object as prototypal properties
   # on the class.
-  addProperties: (node, name) ->
+  addProperties: (node, name, o) ->
     props = node.base.properties.slice 0
     exprs = while assign = props.shift()
       if assign instanceof Assign
@@ -835,8 +835,8 @@
           if func instanceof Code
             assign = @ctor = func
           else
-            assign = null
-            @ctor = new Assign new Value(new Literal name), func
+            @externalCtor = o.scope.freeVariable 'class'
+            assign = new Assign new Literal(@externalCtor), func
         else
           unless assign.variable.this
             assign.variable = new Value(new Literal(name), [new Access(base, 'proto')])
@@ -847,13 +847,13 @@
     compact exprs

   # Walk the body of the class, looking for prototype properties to be converted.
-  walkBody: (name) ->
+  walkBody: (name, o) ->
     @traverseChildren false, (child) =>
       return false if child instanceof Class
       if child instanceof Block
         for node, i in exps = child.expressions
           if node instanceof Value and node.isObject(true)
-            exps[i] = @addProperties node, name
+            exps[i] = @addProperties node, name, o
         child.expressions = exps = flatten exps

   # Make sure that a constructor is defined for the class, and properly
@@ -861,7 +861,8 @@
   ensureConstructor: (name) ->
     if not @ctor
       @ctor = new Code
-      @ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent
+      @ctor.body.push new Literal "#{name}.__super__.constructor.apply(this, arguments)" if @parent
+      @ctor.body.push new Literal "return #{@externalCtor}.apply(this, arguments)" if @externalCtor
       @body.expressions.unshift @ctor
     @ctor.ctor     = @ctor.name = name
     @ctor.klass    = null
@@ -876,7 +877,7 @@
     lname = new Literal name

     @setContext name
-    @walkBody name
+    @walkBody name, o
     @ensureConstructor name
     @body.expressions.unshift new Extends lname, @parent if @parent
     @body.expressions.unshift @ctor unless @ctor instanceof Code


Function soaks should preserve `this`. Fixes issue #692.

--- nodes.coffee
+++ nodes.coffee
@@ -452,7 +452,13 @@
     o.chainRoot = this unless o.chainRoot
     op = @tags.operation
     if @exist
-      [@first, @meth] = @variable.compileReference o, precompile: yes
+      if @variable instanceof ValueNode and @variable.properties[@variable.properties.length - 1] instanceof AccessorNode
+        methodAccessor = @variable.properties.pop()
+        [@first, @meth] = @variable.compileReference o
+        @first = new ValueNode(@first, [methodAccessor]).compile o
+        @meth = new ValueNode(@meth, [methodAccessor]).compile o
+      else
+        [@first, @meth] = @variable.compileReference o, precompile: yes
       @first = "(typeof #{@first} === \"function\" ? "
       @last  = " : undefined)"
     else if @variable then @meth = @variable.compile o


fixed a bug with pasting scrolling the page oddly

--- vendor/assets/javascripts/mercury/page_editor.js.coffee
+++ vendor/assets/javascripts/mercury/page_editor.js.coffee
@@ -87,7 +87,7 @@

   finalizeInterface: ->
-    @santizerElement = jQuery('<div>', {id: 'mercury_sanitizer', contenteditable: 'true', style: 'position:fixed;width:100px;height:100px;top:-100px;left:-100px;opacity:0'})
+    @santizerElement = jQuery('<div>', {id: 'mercury_sanitizer', contenteditable: 'true', style: 'position:fixed;width:100px;height:100px;top:0;left:-100px;opacity:0;overflow:hidden'})
     @santizerElement.appendTo(@options.appendTo ? @document.find('body'))

     @snippetToolbar = new Mercury.SnippetToolbar(@document)


Compile to an array of CodeFragments instead of to a giant string.

--- nodes.coffee
+++ nodes.coffee
@@ -2,6 +2,8 @@
 # nodes are created as the result of actions in the [grammar](grammar.html),
 # but some are created by other nodes as a method of code generation. To convert
 # the syntax tree into a string of JavaScript code, call `compile()` on the root.
+
+Error.stackTraceLimit = Infinity

 {Scope} = require './scope'
 {RESERVED, STRICT_PROSCRIBED} = require './lexer'
@@ -18,6 +20,49 @@
 NO      = -> no
 THIS    = -> this
 NEGATE  = -> @negated = not @negated; this
+
+# If PARANOID is true, then certain extra internal consistency checks are done during the compile.
+# This is used for checks that would slow down the compiler.  This is really only useful if you're
+# doing compiler work.
+PARANOID = false
+
+#### CodeFragment
+
+# The various nodes defined below all compile to a collection of **CodeFragment** objects.
+# A CodeFragments is a block of generated code, and the location in the source file where the code
+# came from.  CodeFragments can be assembled together into working code just by catting together
+# all the CodeFragments' `code` snippets, in order.
+exports.CodeFragment = class CodeFragment
+  constructor: (parent, @code) ->
+    @locationData = parent?.locationData
+    @type = parent?.constructor?.name or 'unknown'
+
+  toString: () ->
+    "#{@code}#{[if @locationData then ": " + locationDataToString(@locationData)]}"
+
+# Convert an array of CodeFragments into a string.
+fragmentsToText = (fragments) ->
+  checkFragments fragments
+  (fragment.code for fragment in fragments).join('')
+
+# Validates that `fragments` is an array of CodeFragment objects.
+# If PARANOID is false, then this does nothing.
+checkFragments = (fragments, node=null) ->
+  if !PARANOID then return fragments
+
+  nodeName = if node then " from #{node.constructor.name}" else ""
+  if !fragments
+    throw new Error("Fragments is null#{nodeName}: #{fragments}\n" )
+  if fragments instanceof CodeFragment
+    throw new Error("Expected array of fragments but found fragment#{nodeName}: #{fragments}\n" )
+  for fragment, i in fragments
+    if !(fragment instanceof CodeFragment)
+      # This is not a fragment.
+      inspected = (require 'util').inspect fragments
+      nodeStr = if node then "node: #{node.toString()}\n" else ""
+      throw new Error("Expected fragment: #{i} of #{fragments.length}#{nodeName}: #{fragment}\nFragments: #{inspected}\n#{nodeStr}")
+
+  return fragments

 #### Base

@@ -32,21 +77,26 @@
 # scope, and indentation level.
 exports.Base = class Base

+  compile: (o, lvl) ->
+    fragmentsToText @compileToFragments o, lvl
+
   # Common logic for determining whether to wrap this node in a closure before
   # compiling it, or to compile directly. We need to wrap if this node is a
   # *statement*, and it's not a *pureStatement*, 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 (because statements know how to
   # return results).
-  compile: (o, lvl) ->
+  compileToFragments: (o, lvl) ->
     o        = extend {}, o
     o.level  = lvl if lvl
     node     = @unfoldSoak(o) or this
     node.tab = o.indent
     if o.level is LEVEL_TOP or not node.isStatement(o)
-      node.compileNode o
-    else
-      node.compileClosure o
+      fragments = node.compileNode o
+    else
+      fragments = node.compileClosure o
+    checkFragments fragments, node
+    fragments

   # Statements converted into expressions via closure-wrapping share a scope
   # object with their parent closure, to preserve the expected lexical scope.
@@ -59,21 +109,21 @@
   # If the code generation wishes to use the result of a complex expression
   # in multiple places, ensure that the expression is only ever evaluated once,
   # by assigning it to a temporary variable. Pass a level to precompile.
+  #
+  # If `level` is passed, then returns `[val, ref]`, where `val` is the compiled value, and `ref`
+  # is the compiled reference.  If `level` is not passed, this returns `[val, ref]` where
+  # the two values are raw nodes which have not been compiled.
   cache: (o, level, reused) ->
     unless @isComplex()
-      ref = if level then @compile o, level else this
+      ref = if level then @compileToFragments o, level else this
       [ref, ref]
     else
       ref = new Literal reused or o.scope.freeVariable 'ref'
       sub = new Assign ref, this
-      if level then [sub.compile(o, level), ref.value] else [sub, ref]
-
-  # Compile to a source/variable pair suitable for looping.
-  compileLoopReference: (o, name) ->
-    src = tmp = @compile o, LEVEL_LIST
-    unless -Infinity < +src < Infinity or IDENTIFIER.test(src) and o.scope.check(src, yes)
-      src = "#{ tmp = o.scope.freeVariable name } = #{src}"
-    [src, tmp]
+      if level then [sub.compileToFragments(o, level), [@makeCode(ref.value)]] else [sub, ref]
+
+  cacheToCodeFragments: (cacheValues) ->
+    [fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])]

   # Construct a node that returns the current node's result.
   # Note that this is overridden for smarter behavior for
@@ -163,6 +213,23 @@
     @eachChild (child) ->
       child.updateLocationDataIfMissing locationData

+  makeCode: (code) ->
+    new CodeFragment this, code
+
+  wrapInBraces: (fragments) ->
+    [].concat @makeCode('('), fragments, @makeCode(')')
+
+  # `fragmentsList` is an array of arrays of fragments.  Each array in fragmentsList will be
+  # concatonated together, with `joinStr` added in between each, to produce a final flat array
+  # of fragments.
+  joinFragmentArrays: (fragmentsList, joinStr) ->
+    answer = []
+    for fragments,i in fragmentsList
+      checkFragments fragments, @
+      if i then answer.push @makeCode joinStr
+      answer = answer.concat fragments
+    answer
+
 #### Block

 # The block is the list of expressions that forms the body of an
@@ -219,7 +286,7 @@
     this

   # A **Block** is the only node that can serve as the root.
-  compile: (o = {}, level) ->
+  compileToFragments: (o = {}, level) ->
     if o.scope then super o, level else @compileRoot o

   # Compile all expressions within the **Block** body. If we need to
@@ -228,30 +295,36 @@
   compileNode: (o) ->
     @tab  = o.indent
     top   = o.level is LEVEL_TOP
-    codes = []
-    for node in @expressions
+    compiledNodes = []
+
+    for node, index in @expressions
+
       node = node.unwrapAll()
       node = (node.unfoldSoak(o) or node)
       if node instanceof Block
         # This is a nested block.  We don't do anything special here like enclose
         # it in a new scope; we just compile the statements in this block along with
         # our own
-        codes.push node.compileNode o
+        compiledNodes.push node.compileNode o
       else if top
         node.front = true
-        code = node.compile o
+        fragments = node.compileToFragments o
         unless node.isStatement o
-          code = "#{@tab}#{code};"
-        codes.push code
+          fragments.unshift @makeCode "#{@tab}"
+          fragments.push @makeCode ";"
+        compiledNodes.push fragments
       else
-        codes.push node.compile o, LEVEL_LIST
+        compiledNodes.push node.compileToFragments o, LEVEL_LIST
     if top
       if @spaced
-        return "\n#{codes.join '\n\n'}\n"
+        return [].concat @makeCode("\n"), @joinFragmentArrays(compiledNodes, '\n\n'), @makeCode("\n")
       else
-        return codes.join '\n'
-    code = codes.join(', ') or 'void 0'
-    if codes.length > 1 and o.level >= LEVEL_LIST then "(#{code})" else code
+        return @joinFragmentArrays(compiledNodes, '\n')
+    if compiledNodes.length
+      answer = @joinFragmentArrays(compiledNodes, ', ')
+    else
+      answer = [@makeCode "void 0"]
+    if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInBraces answer else answer

   # If we happen to be the top-level **Block**, wrap everything in
   # a safety closure, unless requested not to.
@@ -262,31 +335,34 @@
     o.scope   = new Scope null, this, null
     o.level   = LEVEL_TOP
     @spaced   = yes
-    prelude   = ""
+    prelude   = []
     unless o.bare
       preludeExps = for exp, i in @expressions
         break unless exp.unwrap() instanceof Comment
         exp
       rest = @expressions[preludeExps.length...]
       @expressions = preludeExps
-      prelude = "#{@compileNode merge(o, indent: '')}\n" if preludeExps.length
+      if preludeExps.length
+        prelude = @compileNode merge(o, indent: '')
+        prelude.push @makeCode "\n"
       @expressions = rest
-    code = @compileWithDeclarations o
-    return code if o.bare
-    "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+    fragments = @compileWithDeclarations o
+    return fragments if o.bare
+    [].concat prelude, @makeCode("(function() {\n"), fragments, @makeCode("\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.
   compileWithDeclarations: (o) ->
-    code = post = ''
+    fragments = []
+    post = []
     for exp, i in @expressions
       exp = exp.unwrap()
       break unless exp instanceof Comment or exp instanceof Literal
     o = merge(o, level: LEVEL_TOP)
     if i
       rest = @expressions.splice i, 9e9
-      [spaced, @spaced] = [@spaced, no]
-      [code  , @spaced] = [(@compileNode o), spaced]
+      [spaced,    @spaced] = [@spaced, no]
+      [fragments, @spaced] = [(@compileNode o), spaced]
       @expressions = rest
     post = @compileNode o
     {scope} = o
@@ -294,15 +370,15 @@
       declars = o.scope.hasDeclarations()
       assigns = scope.hasAssignments
       if declars or assigns
-        code += '\n' if i
-        code += "#{@tab}var "
+        fragments.push @makeCode '\n' if i
+        fragments.push @makeCode "#{@tab}var "
         if declars
-          code += scope.declaredVariables().join ', '
+          fragments.push @makeCode(scope.declaredVariables().join ', ')
         if assigns
-          code += ",\n#{@tab + TAB}" if declars
-          code += scope.assignedVariables().join ",\n#{@tab + TAB}"
-        code += ';\n'
-    code + post
+          fragments.push @makeCode ",\n#{@tab + TAB}" if declars
+          fragments.push @makeCode (scope.assignedVariables().join ",\n#{@tab + TAB}")
+        fragments.push @makeCode ';\n'
+    fragments.concat post

   # Wrap up the given nodes as a **Block**, unless it already happens
   # to be one.
@@ -343,7 +419,8 @@
       "\"#{@value}\""
     else
       @value
-    if @isStatement() then "#{@tab}#{code};" else code
+    answer = if @isStatement() then "#{@tab}#{code};" else code
+    [@makeCode answer]

   toString: ->
     ' "' + @value + '"'
@@ -352,17 +429,17 @@
   isAssignable: NO
   isComplex: NO
   compileNode: (o) ->
-    if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
+    [@makeCode if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0']

 class exports.Null extends Base
   isAssignable: NO
   isComplex: NO
-  compileNode: -> "null"
+  compileNode: -> [@makeCode "null"]

 class exports.Bool extends Base
   isAssignable: NO
   isComplex: NO
-  compileNode: -> @val
+  compileNode: -> [@makeCode @val]
   constructor: (@val) ->

 #### Return
@@ -379,12 +456,19 @@
   makeReturn:      THIS
   jumps:           THIS

-  compile: (o, level) ->
+  compileToFragments: (o, level) ->
     expr = @expression?.makeReturn()
-    if expr and expr not instanceof Return then expr.compile o, level else super o, level
+    if expr and expr not instanceof Return then expr.compileToFragments o, level else super o, level

   compileNode: (o) ->
-    @tab + "return#{[" #{@expression.compile o, LEVEL_PAREN}" if @expression]};"
+    answer = []
+    # TODO: If we call expression.compile() here twice, we'll sometimes get back different results!
+    answer.push @makeCode(@tab + "return#{[" " if @expression]}")
+    if @expression
+      answer = answer.concat @expression.compileToFragments(o, LEVEL_PAREN)
+    answer.push @makeCode ";"
+    return answer
+

 #### Value

@@ -463,10 +547,12 @@
   compileNode: (o) ->
     @base.front = @front
     props = @properties
-    code  = @base.compile o, if props.length then LEVEL_ACCESS else null
-    code  = "#{code}." if (@base instanceof Parens or props.length) and SIMPLENUM.test code
-    code += prop.compile o for prop in props
-    code
+    fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS else null)
+    if (@base instanceof Parens or props.length) and SIMPLENUM.test fragmentsToText fragments
+      fragments.push @makeCode '.'
+    for prop in props
+      fragments.push (prop.compileToFragments o)...
+    fragments

   # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
   unfoldSoak: (o) ->
@@ -500,7 +586,7 @@
   compileNode: (o, level) ->
     code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/\n"
     code = o.indent + code if (level or o.level) is LEVEL_TOP
-    code
+    [@makeCode code]

 #### Call

@@ -596,49 +682,68 @@
   # Compile a vanilla function call.
   compileNode: (o) ->
     @variable?.front = @front
-    if code = Splat.compileSplattedArray o, @args, true
-      return @compileSplat o, code
+    compiledArray = Splat.compileSplattedArray o, @args, true
+    if compiledArray.length
+      return @compileSplat o, compiledArray
     args = @filterImplicitObjects @args
-    args = (arg.compile o, LEVEL_LIST for arg in args).join ', '
+    compiledArgs = []
+    for arg, argIndex in args
+      if argIndex then compiledArgs.push @makeCode ", "
+      compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
+
+    fragments = []
     if @isSuper
-      @superReference(o) + ".call(#{@superThis(o)}#{ args and ', ' + args })"
-    else
-      (if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
-
-  # `super()` is converted into a call against the superclass's implementation
-  # of the current function.
-  # TODO: This looks like it is never called?
-  compileSuper: (args, o) ->
-    "#{@superReference(o)}.call(#{@superThis(o)}#{ if args.length then ', ' else '' }#{args})"
+      preface = @superReference(o) + ".call(#{@superThis(o)}"
+      if compiledArgs.length then preface += ", "
+      fragments.push @makeCode preface
+    else
+      if @isNew then fragments.push @makeCode 'new '
+      fragments.push (@variable.compileToFragments(o, LEVEL_ACCESS))...
+      fragments.push @makeCode "("
+    fragments.push compiledArgs...
+    fragments.push @makeCode ")"
+    fragments

   # If you call a function with a splat, it's converted into a JavaScript
   # `.apply()` call to allow an array of arguments to be passed.
   # If it's a constructor, then things get real tricky. We have to inject an
   # inner constructor in order to be able to pass the varargs.
+  #
+  # splatArgs is an array of CodeFragments to put into the 'apply'.
   compileSplat: (o, splatArgs) ->
-    return "#{ @superReference o }.apply(#{@superThis(o)}, #{splatArgs})" if @isSuper
+    if @isSuper
+      return [].concat @makeCode("#{ @superReference o }.apply(#{@superThis(o)}, "),
+        splatArgs, @makeCode(")")
+
     if @isNew
       idt = @tab + TAB
-      return """
+      return [].concat @makeCode("""
         (function(func, args, ctor) {
         #{idt}ctor.prototype = func.prototype;
         #{idt}var child = new ctor, result = func.apply(child, args);
         #{idt}return Object(result) === result ? result : child;
-        #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function(){})
-      """
+        #{@tab}})("""),
+        (@variable.compileToFragments o, LEVEL_LIST),
+        @makeCode(", "), splatArgs, @makeCode(", function(){})")
+
+    answer = []
     base = Value.wrap @variable
     if (name = base.properties.pop()) and base.isComplex()
       ref = o.scope.freeVariable 'ref'
-      fun = "(#{ref} = #{ base.compile o, LEVEL_LIST })#{ name.compile o }"
-    else
-      fun = base.compile o, LEVEL_ACCESS
-      fun = "(#{fun})" if SIMPLENUM.test fun
+      answer = answer.concat @makeCode("(#{ref} = "),
+        (base.compileToFragments o, LEVEL_LIST),
+        @makeCode(")"),
+        name.compileToFragments(o)
+    else
+      fun = base.compileToFragments o, LEVEL_ACCESS
+      fun = @wrapInBraces fun if SIMPLENUM.test fragmentsToText fun
       if name
-        ref = fun
-        fun += name.compile o
+        ref = fragmentsToText fun
+        fun.push (name.compileToFragments o)...
       else
         ref = 'null'
-    "#{fun}.apply(#{ref}, #{splatArgs})"
+      answer = answer.concat fun
+    answer = answer.concat @makeCode(".apply(#{ref}, "), splatArgs, @makeCode(")")

 #### Extends

@@ -651,8 +756,8 @@
   children: ['child', 'parent']

   # Hooks one constructor into another's prototype chain.
-  compile: (o) ->
-    new Call(Value.wrap(new Literal utility 'extends'), [@child, @parent]).compile o
+  compileToFragments: (o) ->
+    new Call(Value.wrap(new Literal utility 'extends'), [@child, @parent]).compileToFragments o

 #### Access

@@ -665,9 +770,14 @@

   children: ['name']

-  compile: (o) ->
-    name = @name.compile o
-    if IDENTIFIER.test name then ".#{name}" else "[#{name}]"
+  compileToFragments: (o) ->
+    name = @name.compileToFragments o
+    if IDENTIFIER.test fragmentsToText name
+      name.unshift @makeCode "."
+    else
+      name.unshift @makeCode "["
+      name.push @makeCode "]"
+    name

   isComplex: NO

@@ -679,8 +789,8 @@

   children: ['index']

-  compile: (o) ->
-    "[#{ @index.compile o, LEVEL_PAREN }]"
+  compileToFragments: (o) ->
+    [].concat @makeCode("["), (@index.compileToFragments o, LEVEL_PAREN), @makeCode("]")

   isComplex: ->
     @index.isComplex()
@@ -698,13 +808,15 @@
     @exclusive = tag is 'exclusive'
     @equals = if @exclusive then '' else '='

+
+
   # Compiles the range's source variables -- where it starts and where it ends.
   # But only if they need to be cached to avoid double evaluation.
   compileVariables: (o) ->
     o = merge o, top: true
-    [@fromC, @fromVar]  =  @from.cache o, LEVEL_LIST
-    [@toC, @toVar]      =  @to.cache o, LEVEL_LIST
-    [@step, @stepVar]   =  step.cache o, LEVEL_LIST if step = del o, 'step'
+    [@fromC, @fromVar]  =  @cacheToCodeFragments @from.cache o, LEVEL_LIST
+    [@toC, @toVar]      =  @cacheToCodeFragments @to.cache o, LEVEL_LIST
+    [@step, @stepVar]   =  @cacheToCodeFragments step.cache o, LEVEL_LIST if step = del o, 'step'
     [@fromNum, @toNum]  = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
     @stepNum            = @stepVar.match(SIMPLENUM) if @stepVar

@@ -752,7 +864,7 @@
     stepPart = "#{idxName} = #{stepPart}" if namedIndex

     # The final loop body.
-    "#{varPart}; #{condPart}; #{stepPart}"
+    [@makeCode "#{varPart}; #{condPart}; #{stepPart}"]

   # When used as a value, expand the range into the equivalent array.
@@ -760,14 +872,14 @@
     if @fromNum and @toNum and Math.abs(@fromNum - @toNum) <= 20
       range = [+@fromNum..+@toNum]
       range.pop() if @exclusive
-      return "[#{ range.join(', ') }]"
+      return [@makeCode "[#{ range.join(', ') }]"]
     idt    = @tab + TAB
     i      = o.scope.freeVariable 'i'
     result = o.scope.freeVariable 'results'
     pre    = "\n#{idt}#{result} = [];"
     if @fromNum and @toNum
       o.index = i
-      body    = @compileNode o
+      body    = fragmentsToText @compileNode o
     else
       vars    = "#{i} = #{@fromC}" + if @toC isnt @toVar then ", #{@toC}" else ''
       cond    = "#{@fromVar} <= #{@toVar}"
@@ -775,7 +887,7 @@
     post   = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
     hasArgs = (node) -> node?.contains (n) -> n instanceof Literal and n.value is 'arguments' and not n.asKey
     args   = ', arguments' if hasArgs(@from) or hasArgs(@to)
-    "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this#{args ? ''})"
+    [@makeCode "(function() {#{pre}\n#{idt}for (#{body})#{post}}).apply(this#{args ? ''})"]

 #### Slice

@@ -794,17 +906,20 @@
   # `9e9` should be safe because `9e9` > `2**32`, the max array length.
   compileNode: (o) ->
     {to, from} = @range
-    fromStr    = from and from.compile(o, LEVEL_PAREN) or '0'
-    compiled   = to and to.compile o, LEVEL_PAREN
-    if to and not (not @range.exclusive and +compiled is -1)
-      toStr = ', ' + if @range.exclusive
-        compiled
-      else if SIMPLENUM.test compiled
-        "#{+compiled + 1}"
-      else
-        compiled = to.compile o, LEVEL_ACCESS
-        "+#{compiled} + 1 || 9e9"
-    ".slice(#{ fromStr }#{ toStr or '' })"
+    fromCompiled = from and from.compileToFragments(o, LEVEL_PAREN) or [@makeCode '0']
+    # TODO: jwalton - move this into the 'if'?
+    if to
+      compiled     = to.compileToFragments o, LEVEL_PAREN
+      compiledText = fragmentsToText compiled
+      if not (not @range.exclusive and +compiledText is -1)
+        toStr = ', ' + if @range.exclusive
+          compiledText
+        else if SIMPLENUM.test compiledText
+          "#{+compiledText + 1}"
+        else
+          compiled = to.compileToFragments o, LEVEL_ACCESS
+          "+#{fragmentsToText compiled} + 1 || 9e9"
+    [@makeCode ".slice(#{ fragmentsToText fromCompiled }#{ toStr or '' })"]

 #### Obj

@@ -817,13 +932,14 @@

   compileNode: (o) ->
     props = @properties
-    return (if @front then '({})' else '{}') unless props.length
+    return [@makeCode(if @front then '({})' else '{}')] unless props.length
     if @generated
       for node in props when node instanceof Value
         throw new Error 'cannot have an implicit value in an implicit object'
     idt         = o.indent += TAB
     lastNoncom  = @lastNonComment @properties
-    props = for prop, i in props
+    answer = []
+    for prop, i in props
       join = if i is props.length - 1
         ''
       else if prop is lastNoncom or prop instanceof Comment
@@ -837,10 +953,12 @@
         if prop not instanceof Assign
           prop = new Assign prop, prop, 'object'
         (prop.variable.base or prop.variable).asKey = yes
-      indent + prop.compile(o, LEVEL_TOP) + join
-    props = props.join ''
-    obj   = "{#{ props and '\n' + props + '\n' + @tab }}"
-    if @front then "(#{obj})" else obj
+      if indent then answer.push @makeCode indent
+      answer.push prop.compileToFragments(o, LEVEL_TOP)...
+      if join then answer.push @makeCode join
+    answer.unshift @makeCode "{#{ props.length and '\n' }"
+    answer.push @makeCode "#{ props.length and '\n' + @tab }}"
+    if @front then @wrapInBraces answer else answer

   assigns: (name) ->
     for prop in @properties when prop.assigns name then return yes
@@ -858,15 +976,25 @@
   filterImplicitObjects: Call::filterImplicitObjects

   compileNode: (o) ->
-    return '[]' unless @objects.length
+    return [@makeCode '[]'] unless @objects.length
     o.indent += TAB
     objs = @filterImplicitObjects @objects
-    return code if code = Splat.compileSplattedArray o, objs
-    code = (obj.compile o, LEVEL_LIST for obj in objs).join ', '
-    if code.indexOf('\n') >= 0
-      "[\n#{o.indent}#{code}\n#{@tab}]"
-    else
-      "[#{code}]"
+    answer = Splat.compileSplattedArray o, objs
+    return answer if answer.length
+
+    answer = []
+    compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in objs)
+    for fragments, index in compiledObjs
+      if index
+        answer.push @makeCode ", "
+      answer.push fragments...
+    if (fragmentsToText answer).indexOf('\n') >= 0
+      answer.unshift @makeCode "[\n#{o.indent}"
+      answer.push @makeCode "\n#{@tab}]"
+    else
+      answer.unshift @makeCode "["
+      answer.push @makeCode "]"
+    answer

   assigns: (name) ->
     for obj in @objects when obj.assigns name then return yes
@@ -1015,7 +1143,7 @@

     klass = new Parens call, yes
     klass = new Assign @variable, klass if @variable
-    klass.compile o
+    klass.compileToFragments o

 #### Assign

@@ -1049,7 +1177,8 @@
       return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
       return @compileSplice       o if @variable.isSplice()
       return @compileConditional  o if @context in ['||=', '&&=', '?=']
-    name = @variable.compile o, LEVEL_LIST
+    compiledName = @variable.compileToFragments o, LEVEL_LIST
+    name = fragmentsToText compiledName
     unless @context
       unless (varBase = @variable.unwrapAll()).isAssignable()
         throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
@@ -1061,10 +1190,10 @@
     if @value instanceof Code and match = METHOD_DEF.exec name
       @value.klass = match[1] if match[1]
       @value.name  = match[2] ? match[3] ? match[4] ? match[5]
-    val = @value.compile o, LEVEL_LIST
-    return "#{name}: #{val}" if @context is 'object'
-    val = name + " #{ @context or '=' } " + val
-    if o.level <= LEVEL_LIST then val else "(#{val})"
+    val = @value.compileToFragments o, LEVEL_LIST
+    return (compiledName.concat @makeCode(": "), val) if @context is 'object'
+    answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
+    if o.level <= LEVEL_LIST then answer else @wrapInBraces answer

   # Brief implementation of recursive pattern matching, when assigning array or
   # object literals to a value. Peeks at their properties to assign inner names.
@@ -1075,8 +1204,8 @@
     {value}   = this
     {objects} = @variable.base
     unless olen = objects.length
-      code = value.compile o
-      return if o.level >= LEVEL_OP then "(#{code})" else code
+      code = value.compileToFragments o
+      return if o.level >= LEVEL_OP then @wrapInBraces code else code
     isObject = @variable.isObject()
     if top and olen is 1 and (obj = objects[0]) not instanceof Splat
       # Unroll simplest cases: `{v} = x` -> `v = x.v`
@@ -1092,13 +1221,16 @@
       value.properties.push new (if acc then Access else Index) idx
       if obj.unwrap().value in RESERVED
         throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{value.compile o}"
-      return new Assign(obj, value, null, param: @param).compile o, LEVEL_TOP
-    vvar    = value.compile o, LEVEL_LIST
+      return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
+    vvar    = value.compileToFragments o, LEVEL_LIST
+    vvarText = fragmentsToText vvar
     assigns = []
     splat   = false
-    if not IDENTIFIER.test(vvar) or @variable.assigns(vvar)
-      assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{vvar}"
-      vvar = ref
+    # Make vvar into a simple variable if it isn't already.
+    if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
+      assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
+      vvar = [@makeCode ref]
+      vvarText = ref
     for obj, i in objects
       # A regular array pattern-match.
       idx = i
@@ -1115,10 +1247,10 @@
       if not splat and obj instanceof Splat
         name = obj.name.unwrap().value
         obj = obj.unwrap()
-        val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
+        val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}"
         if rest = olen - i - 1
           ivar = o.scope.freeVariable 'i'
-          val += ", #{ivar} = #{vvar}.length - #{rest}) : (#{ivar} = #{i}, [])"
+          val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])"
         else
           val += ") : []"
         val   = new Literal val
@@ -1126,7 +1258,7 @@
       else
         name = obj.unwrap().value
         if obj instanceof Splat
-          obj = obj.name.compile o
+          obj = obj.name.compileToFragments o
           throw new SyntaxError \
             "multiple splats are disallowed in an assignment: #{obj}..."
         if typeof idx is 'number'
@@ -1134,13 +1266,13 @@
           acc = no
         else
           acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
-        val = Value.wrap new Literal(vvar), [new (if acc then Access else Index) idx]
+        val = Value.wrap new Literal(vvarText), [new (if acc then Access else Index) idx]
       if name? and name in RESERVED
         throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{val.compile o}"
-      assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compile o, LEVEL_LIST
+      assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
     assigns.push vvar unless top or @subpattern
-    code = assigns.join ', '
-    if o.level < LEVEL_LIST then code else "(#{code})"
+    fragments = @joinFragmentArrays assigns, ', '
+    if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments

   # When compiling a conditional assignment, take care to ensure that the
   # operands are only evaluated once, even though we have to reference them
@@ -1152,14 +1284,17 @@
            left.base.value != "this" and not o.scope.check left.base.value
       throw new Error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been defined."
     if "?" in @context then o.isExistentialEquals = true
-    Op.create(@context[...-1], left, new Assign(right, @value, '=') ).compile o
+    Op.create(@context[...-1], left, new Assign(right, @value, '=') ).compileToFragments o

   # Compile the assignment from an array splice literal, using JavaScript's
   # `Array#splice` method.
   compileSplice: (o) ->
     {range: {from, to, exclusive}} = @variable.properties.pop()
     name = @variable.compile o
-    [fromDecl, fromRef] = from?.cache(o, LEVEL_OP) or ['0', '0']
+    if from
+      [fromDecl, fromRef] = @cacheToCodeFragments from.cache o, LEVEL_OP
+    else
+      fromDecl = fromRef = '0'
     if to
       if from?.isSimpleNumber() and to.isSimpleNumber()
         to = +to.compile(o) - +fromRef
@@ -1170,8 +1305,8 @@
     else
       to = "9e9"
     [valDef, valRef] = @value.cache o, LEVEL_LIST
-    code = "[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat(#{valDef})), #{valRef}"
-    if o.level > LEVEL_TOP then "(#{code})" else code
+    answer = [].concat @makeCode("[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat("), valDef, @makeCode(")), "), valRef
+    if o.level > LEVEL_TOP then @wrapInBraces answer else answer

 #### Code

@@ -1228,7 +1363,9 @@
     wasEmpty = @body.isEmpty()
     exprs.unshift splats if splats
     @body.expressions.unshift exprs... if exprs.length
-    o.scope.parameter params[i] = p.compile o for p, i in params
+    for p, i in params
+      params[i] = p.compileToFragments o
+      o.scope.parameter fragmentsToText params[i]
     uniqs = []
     for name in @paramNames()
       throw SyntaxError "multiple parameters named '#{name}'" if name in uniqs
@@ -1242,11 +1379,17 @@
     idt   = o.indent
     code  = 'function'
     code  += ' ' + @name if @ctor
-    code  += '(' + params.join(', ') + ') {'
-    code  += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
-    code  += '}'
-    return @tab + code if @ctor
-    if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
+    code  += '('
+    answer = [@makeCode(code)]
+    for p, i in params
+      if i then answer.push @makeCode ", "
+      answer.push p...
+    answer.push @makeCode ') {'
+    answer = answer.concat(@makeCode("\n"), @body.compileWithDeclarations(o), @makeCode("\n#{@tab}")) unless @body.isEmpty()
+    answer.push @makeCode '}'
+
+    return [@makeCode(@tab), answer...] if @ctor
+    if @front or (o.level >= LEVEL_ACCESS) then @wrapInBraces answer else answer

   # A list of parameter names, excluding those generated by the compiler.
   paramNames: ->
@@ -1271,8 +1414,8 @@

   children: ['name', 'value']

-  compile: (o) ->
-    @name.compile o, LEVEL_LIST
+  compileToFragments: (o) ->
+    @name.compileToFragments o, LEVEL_LIST

   asReference: (o) ->
     return @reference if @reference
@@ -1342,8 +1485,8 @@
   assigns: (name) ->
     @name.assigns name

-  compile: (o) ->
-    if @index? then @compileParam o else @name.compile o
+  compileToFragments: (o) ->
+    if @index? then @compileParam o else @name.compileToFragments o

   unwrap: -> @name

@@ -1352,20 +1495,26 @@
   @compileSplattedArray: (o, list, apply) ->
     index = -1
     continue while (node = list[++index]) and node not instanceof Splat
-    return '' if index >= list.length
+    return [] if index >= list.length
     if list.length is 1
-      code = list[0].compile o, LEVEL_LIST
-      return code if apply
-      return "#{ utility 'slice' }.call(#{code})"
+      node = list[0]
+      fragments = node.compileToFragments o, LEVEL_LIST
+      return fragments if apply
+      return [].concat node.makeCode("#{ utility 'slice' }.call("), fragments, node.makeCode(")")
     args = list[index..]
     for node, i in args
-      code = node.compile o, LEVEL_LIST
+      compiledNode = node.compileToFragments o, LEVEL_LIST
       args[i] = if node instanceof Splat
-      then "#{ utility 'slice' }.call(#{code})"
-      else "[#{code}]"
-    return args[0] + ".concat(#{ args[1..].join ', ' })" if index is 0
-    base = (node.compile o, LEVEL_LIST for node in list[...index])
-    "[#{ base.join ', ' }].concat(#{ args.join ', ' })"
+      then [].concat node.makeCode("#{ utility 'slice' }.call("), compiledNode, node.makeCode(")")
+      else [].concat node.makeCode("["), compiledNode, node.makeCode("]")
+    if index is 0
+      node = list[0]
+      concatPart = (node.joinFragmentArrays args[1..], ', ')
+      return args[0].concat node.makeCode(".concat("), concatPart, node.makeCode(")")
+    base = (node.compileToFragments o, LEVEL_LIST for node in list[...index])
+    base = list[0].joinFragmentArrays base, ', '
+    concatPart = list[index].joinFragmentArrays args, ', '
+    [].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")

 #### While

@@ -1416,11 +1565,12 @@
           body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
         else
           body = Block.wrap [new If @guard, body] if @guard
-      body = "\n#{ body.compile o, LEVEL_TOP }\n#{@tab}"
-    code = set + @tab + "while (#{ @condition.compile o, LEVEL_PAREN }) {#{body}}"
+      body = [].concat @makeCode("\n"), (body.compileToFragments o, LEVEL_TOP), @makeCode("\n#{@tab}")
+    answer = [].concat @makeCode(set + @tab + "while ("), @condition.compileToFragments(o, LEVEL_PAREN),
+      @makeCode(") {"), body, @makeCode("}")
     if @returns
-      code += "\n#{@tab}return #{rvar};"
-    code
+      answer.push @makeCode "\n#{@tab}return #{rvar};"
+    answer

 #### Op

@@ -1525,9 +1675,9 @@
     return @compileUnary     o if @isUnary()
     return @compileChain     o if isChain
     return @compileExistence o if @operator is '?'
-    code = @first.compile(o, LEVEL_OP) + ' ' + @operator + ' ' +
-           @second.compile(o, LEVEL_OP)
-    if o.level <= LEVEL_OP then code else "(#{code})"
+    answer = [].concat @first.compileToFragments(o, LEVEL_OP), @makeCode(' ' + @operator + ' '),
+            @second.compileToFragments(o, LEVEL_OP)
+    if o.level <= LEVEL_OP then answer else @wrapInBraces answer

   # Mimic Python's chained comparisons when multiple comparison operators are
   # used sequentially. For example:
@@ -1536,9 +1686,10 @@
   #     true
   compileChain: (o) ->
     [@first.second, shared] = @first.second.cache o
-    fst = @first.compile o, LEVEL_OP
-    code = "#{fst} #{if @invert then '&&' else '||'} #{ shared.compile o } #{@operator} #{ @second.compile o, LEVEL_OP }"
-    "(#{code})"
+    fst = @first.compileToFragments o, LEVEL_OP
+    fragments = fst.concat @makeCode(" #{if @invert then '&&' else '||'} "),
+      (shared.compileToFragments o), @makeCode(" #{@operator} "), (@second.compileToFragments o, LEVEL_OP)
+    @wrapInBraces fragments

   compileExistence: (o) ->
     if @first.isComplex()
@@ -1547,24 +1698,26 @@
     else
       fst = @first
       ref = fst
-    new If(new Existence(fst), ref, type: 'if').addElse(@second).compile o
+    new If(new Existence(fst), ref, type: 'if').addElse(@second).compileToFragments o

   # Compile a unary **Op**.
   compileUnary: (o) ->
-    parts = [op = @operator]
+    parts = []
+    op = @operator
+    parts.push [@makeCode op]
     if op is '!' and @first instanceof Existence
       @first.negated = not @first.negated
-      return @first.compile o
+      return @first.compileToFragments o
     if o.level >= LEVEL_ACCESS
-      return (new Parens this).compile o
+      return (new Parens this).compileToFragments o
     plusMinus = op in ['+', '-']
-    parts.push ' ' if op in ['new', 'typeof', 'delete'] or
+    parts.push [@makeCode(' ')] if op in ['new', 'typeof', 'delete'] or
                       plusMinus and @first instanceof Op and @first.operator is op
     if (plusMinus && @first instanceof Op) or (op is 'new' and @first.isStatement o)
       @first = new Parens @first
-    parts.push @first.compile o, LEVEL_OP
+    parts.push @first.compileToFragments o, LEVEL_OP
     parts.reverse() if @flip
-    parts.join ''
+    @joinFragmentArrays parts, ''

   toString: (idt) ->
     super idt, @constructor.name + ' ' + @operator
@@ -1587,21 +1740,22 @@
     @compileLoopTest o

   compileOrTest: (o) ->
-    return "#{!!@negated}" if @array.base.objects.length is 0
+    return [@makeCode("#{!!@negated}")] if @array.base.objects.length is 0
     [sub, ref] = @object.cache o, LEVEL_OP
     [cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
-    tests = for item, i in @array.base.objects
-      (if i then ref else sub) + cmp + item.compile o, LEVEL_ACCESS
-    tests = tests.join cnj
-    if o.level < LEVEL_OP then tests else "(#{tests})"
+    tests = []
+    for item, i in @array.base.objects
+      if i then tests.push @makeCode cnj
+      tests = tests.concat (if i then ref else sub), @makeCode(cmp), item.compileToFragments(o, LEVEL_ACCESS)
+    if o.level < LEVEL_OP then tests else @wrapInBraces tests

   compileLoopTest: (o) ->
     [sub, ref] = @object.cache o, LEVEL_LIST
-    code = utility('indexOf') + ".call(#{ @array.compile o, LEVEL_LIST }, #{ref}) " +
-           if @negated then '< 0' else '>= 0'
-    return code if sub is ref
-    code = sub + ', ' + code
-    if o.level < LEVEL_LIST then code else "(#{code})"
+    fragments = [].concat @makeCode(utility('indexOf') + ".call("), @array.compileToFragments(o, LEVEL_LIST),
+      @makeCode(", "), ref, @makeCode(") " + if @negated then '< 0' else '>= 0')
+    return fragments if (fragmentsToText sub) is (fragmentsToText ref)
+    fragments = sub.concat @makeCode(', '), fragments
+    if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments

   toString: (idt) ->
     super idt, @constructor.name + if @negated then '!' else ''
@@ -1627,7 +1781,7 @@
   # is optional, the *catch* is not.
   compileNode: (o) ->
     o.indent  += TAB
-    tryPart   = @attempt.compile o, LEVEL_TOP
+    tryPart   = @attempt.compileToFragments o, LEVEL_TOP

     catchPart = if @recovery
       if @error.isObject?()
@@ -1637,15 +1791,18 @@
       if @error.value in STRICT_PROSCRIBED
         throw SyntaxError "catch variable may not be \"#{@error.value}\""
       o.scope.add @error.value, 'param' unless o.scope.check @error.value
-      " catch (#{ @error.compile o }) {\n#{ @recovery.compile o, LEVEL_TOP }\n#{@tab}}"
+      [].concat @makeCode(" catch ("), @error.compileToFragments(o), @makeCode(") {\n"),
+        @recovery.compileToFragments(o, LEVEL_TOP), @makeCode("\n#{@tab}}")
     else unless @ensure or @recovery
-      ' catch (_error) {}'
-
-    ensurePart = if @ensure then " finally {\n#{ @ensure.compile o, LEVEL_TOP }\n#{@tab}}" else ''
-
-    """#{@tab}try {
-    #{tryPart}
-    #{@tab}}#{ catchPart or '' }#{ensurePart}"""
+      [@makeCode(' catch (_error) {}')]
+    else
+      []
+
+    ensurePart = if @ensure then ([].concat @makeCode(" finally {\n"), (@ensure.compileToFragments o, LEVEL_TOP), @makeCode("\n#{@tab}}")) else []
+
+    [].concat @makeCode("#{@tab}try {\n"),
+      tryPart,
+      @makeCode("\n#{@tab}}"), catchPart, ensurePart

 #### Throw

@@ -1662,7 +1819,7 @@
   makeReturn: THIS

   compileNode: (o) ->
-    @tab + "throw #{ @expression.compile o };"
+    [].concat @makeCode(@tab + "throw "), (@expression.compileToFragments o), @makeCode(";")

 #### Existence

@@ -1685,7 +1842,7 @@
     else
       # do not use strict equality here; it will break existing code
       code = "#{code} #{if @negated then '==' else '!='} null"
-    if o.level <= LEVEL_COND then code else "(#{code})"
+    [@makeCode(if o.level <= LEVEL_COND then code else "(#{code})")]

 #### Parens

@@ -1706,11 +1863,11 @@
     expr = @body.unwrap()
     if expr instanceof Value and expr.isAtomic()
       expr.front = @front
-      return expr.compile o
-    code = expr.compile o, LEVEL_PAREN
+      return expr.compileToFragments o
+    fragments = expr.compileToFragments o, LEVEL_PAREN
     bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
       (expr instanceof For and expr.returns))
-    if bare then code else "(#{code})"
+    if bare then fragments else @wrapInBraces fragments

 #### For

@@ -1747,8 +1904,8 @@
     @returns  = no if lastJumps and lastJumps instanceof Return
     source    = if @range then @source.base else @source
     scope     = o.scope
-    name      = @name  and @name.compile o, LEVEL_LIST
-    index     = @index and @index.compile o, LEVEL_LIST
+    name      = @name  and (@name.compile o, LEVEL_LIST)
+    index     = @index and (@index.compile o, LEVEL_LIST)
     scope.find(name)  if name and not @pattern
     scope.find(index) if index
     rvar      = scope.freeVariable 'results' if @returns
@@ -1756,7 +1913,7 @@
     kvar      = (@range and name) or index or ivar
     kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
     if @step and not @range
-      [step, stepVar] = @step.cache o, LEVEL_LIST
+      [step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST
       stepNum = stepVar.match SIMPLENUM
     name      = ivar if @pattern
     varPart   = ''
@@ -1764,7 +1921,7 @@
     defPart   = ''
     idt1      = @tab + TAB
     if @range
-      forPart = source.compile merge(o, {index: ivar, name, @step})
+      forPartFragments = source.compileToFragments merge(o, {index: ivar, name, @step})
     else
       svar    = @source.compile o, LEVEL_LIST
       if (name or @own) and not IDENTIFIER.test svar
@@ -1790,7 +1947,7 @@
           increment = "#{ivar} += #{stepVar}"
         else
           increment = "#{if kvar isnt ivar then "++#{ivar}" else "#{ivar}++"}"
-        forPart  = "#{declare}; #{compare}; #{kvarAssign}#{increment}"
+        forPartFragments  = [@makeCode("#{declare}; #{compare}; #{kvarAssign}#{increment}")]
     if @returns
       resultPart   = "#{@tab}#{rvar} = [];\n"
       returnResult = "\n#{@tab}return #{rvar};"
@@ -1802,19 +1959,20 @@
         body = Block.wrap [new If @guard, body] if @guard
     if @pattern
       body.expressions.unshift new Assign @name, new Literal "#{svar}[#{kvar}]"
-    defPart     += @pluckDirectCall o, body
-    varPart     = "\n#{idt1}#{namePart};" if namePart
+    defPartFragments = [].concat @makeCode(defPart), @pluckDirectCall(o, body)
+    varPart = "\n#{idt1}#{namePart};" if namePart
     if @object
-      forPart   = "#{kvar} in #{svar}"
+      forPartFragments   = [@makeCode("#{kvar} in #{svar}")]
       guardPart = "\n#{idt1}if (!#{utility 'hasProp'}.call(#{svar}, #{kvar})) continue;" if @own
-    body        = body.compile merge(o, indent: idt1), LEVEL_TOP
-    body        = '\n' + body + '\n' if body
-    """
-    #{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}#{varPart}#{body}#{@tab}}#{returnResult or ''}
-    """
+    bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
+    if bodyFragments and (bodyFragments.length > 0)
+      bodyFragments = [].concat @makeCode("\n"), bodyFragments, @makeCode("\n")
+    [].concat defPartFragments, @makeCode("#{resultPart or ''}#{@tab}for ("),
+      forPartFragments, @makeCode(") {#{guardPart}#{varPart}"), bodyFragments,
+      @makeCode("#{@tab}}#{returnResult or ''}")

   pluckDirectCall: (o, body) ->
-    defs = ''
+    defs = []
     for expr, idx in body.expressions
       expr = expr.unwrapAll()
       continue unless expr instanceof Call
@@ -1830,7 +1988,7 @@
       if val.base
         [val.base, base] = [base, val]
       body.expressions[idx] = new Call base, expr.args
-      defs += @tab + new Assign(ref, fn).compile(o, LEVEL_TOP) + ';\n'
+      defs = defs.concat @makeCode(@tab), (new Assign(ref, fn).compileToFragments(o, LEVEL_TOP)), @makeCode(';\n')
     defs

 #### Switch
@@ -1857,18 +2015,22 @@
   compileNode: (o) ->
     idt1 = o.indent + TAB
     idt2 = o.indent = idt1 + TAB
-    code = @tab + "switch (#{ @subject?.compile(o, LEVEL_PAREN) or false }) {\n"
+    fragments = [].concat @makeCode(@tab + "switch ("),
+      (if @subject then @subject.compileToFragments(o, LEVEL_PAREN) else @makeCode("false")),
+      @makeCode(") {\n")
     for [conditions, block], i in @cases
       for cond in flatten [conditions]
         cond  = cond.invert() unless @subject
-        code += idt1 + "case #{ cond.compile o, LEVEL_PAREN }:\n"
-      code += body + '\n' if body = block.compile o, LEVEL_TOP
+        fragments = fragments.concat @makeCode(idt1 + "case "), cond.compileToFragments(o, LEVEL_PAREN), @makeCode(":\n")
+      fragments = fragments.concat body, @makeCode('\n') if (body = block.compileToFragments o, LEVEL_TOP).length > 0
       break if i is @cases.length - 1 and not @otherwise
       expr = @lastNonComment block.expressions
       continue if expr instanceof Return or (expr instanceof Literal and expr.jumps() and expr.value isnt 'debugger')
-      code += idt2 + 'break;\n'
-    code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL_TOP }\n" if @otherwise and @otherwise.expressions.length
-    code +  @tab + '}'
+      fragments.push cond.makeCode(idt2 + 'break;\n')
+    if @otherwise and @otherwise.expressions.length
+      fragments.push @makeCode(idt1 + "default:\n"), (@otherwise.compileToFragments o, LEVEL_TOP)..., @makeCode("\n")
+    fragments.push @makeCode @tab + '}'
+    fragments

 #### If

@@ -1925,28 +2087,30 @@
     exeq     = del o, 'isExistentialEquals'

     if exeq
-      return new If(@condition.invert(), @elseBodyNode(), type: 'if').compile o
-
-    cond     = @condition.compile o, LEVEL_PAREN
+      return new If(@condition.invert(), @elseBodyNode(), type: 'if').compileToFragments o
+
+    cond     = @condition.compileToFragments o, LEVEL_PAREN
     o.indent += TAB
     body     = @ensureBlock(@body)
-    ifPart   = "if (#{cond}) {\n#{body.compile(o)}\n#{@tab}}"
-    ifPart   = @tab + ifPart unless child
+    ifPart   = [].concat @makeCode("if ("), cond, @makeCode(") {\n"), body.compileToFragments(o), @makeCode("\n#{@tab}}")
+    ifPart.unshift @makeCode @tab unless child
     return ifPart unless @elseBody
-    ifPart + ' else ' + if @isChain
+    answer = ifPart.concat @makeCode(' else ')
+    if @isChain
       o.indent = @tab
       o.chainChild = yes
-      @elseBody.unwrap().compile o, LEVEL_TOP
-    else
-      "{\n#{ @elseBody.compile o, LEVEL_TOP }\n#{@tab}}"
+      answer = answer.concat @elseBody.unwrap().compileToFragments o, LEVEL_TOP
+    else
+      answer = answer.concat @makeCode("{\n"), @elseBody.compileToFragments(o, LEVEL_TOP), @makeCode("\n#{@tab}}")
+    answer

   # Compile the `If` as a conditional operator.
   compileExpression: (o) ->
-    cond = @condition.compile o, LEVEL_COND
-    body = @bodyNode().compile o, LEVEL_LIST
-    alt  = if @elseBodyNode() then @elseBodyNode().compile(o, LEVEL_LIST) else 'void 0'
-    code = "#{cond} ? #{body} : #{alt}"
-    if o.level >= LEVEL_COND then "(#{code})" else code
+    cond = @condition.compileToFragments o, LEVEL_COND
+    body = @bodyNode().compileToFragments o, LEVEL_LIST
+    alt  = if @elseBodyNode() then @elseBodyNode().compileToFragments(o, LEVEL_LIST) else [@makeCode('void 0')]
+    fragments = cond.concat @makeCode(" ? "), body, @makeCode(" : "), alt
+    if o.level >= LEVEL_COND then @wrapInBraces fragments else fragments

   unfoldSoak: ->
     @soak and this


simplifying generated output for common-case splices.

--- nodes.coffee
+++ nodes.coffee
@@ -944,13 +944,17 @@
   # `Array#splice` method.
   compileSplice: (o) ->
     {range} = @variable.properties.pop()
-    name  = @variable.compile o
-    plus  = if range.exclusive then '' else ' + 1'
-    from  = if range.from then range.from.compile(o) else '0'
-    to    = if range.to then range.to.compile(o) + ' - ' + from + plus else "#{name}.length"
-    ref   = o.scope.freeVariable 'ref'
-    val   = @value.compile(o)
-    "([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
+    name    = @variable.compile o
+    plus    = if range.exclusive then '' else ' + 1'
+    from    = if range.from then range.from.compile(o) else '0'
+    to      = "#{name}.length" unless range.to
+    unless to
+      if range.from and range.from.isSimpleNumber() and range.to.isSimpleNumber()
+        to = (+range.to.compile(o)) - +from + +plus
+      else
+        to = range.to.compile(o) + ' - ' + from + plus
+    val = @value.compile(o)
+    "[].splice.apply(#{name}, [#{from}, #{to}].concat(#{val}))"

 #### Code


Improving double-parentheses suppression. Issue #587

--- nodes.coffee
+++ nodes.coffee
@@ -354,6 +354,7 @@
     op          = del o, 'operation'
     props       = if only then @properties[0...@properties.length - 1] else @properties
     o.chainRoot or= this
+    @base.parenthetical = yes if @parenthetical and not props.length
     baseline    = @base.compile o
     baseline    = "(#{baseline})" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
     complete    = @last = baseline
@@ -434,11 +435,15 @@
     o.chainRoot = this unless o.chainRoot
     for arg in @args when arg instanceof SplatNode
       compilation = @compileSplat(o)
-    unless compilation
-      args = (arg.compile(o) for arg in @args).join(', ')
-      compilation = if @isSuper then @compileSuper(args, o)
-      else "#{@prefix()}#{@variable.compile(o)}(#{args})"
-    if o.operation and @wrapped then "(#{compilation})" else compilation
+    if not compilation
+      args = for arg in @args
+        arg.parenthetical = true
+        arg.compile o
+      compilation = if @isSuper
+        @compileSuper(args.join(', '), o)
+      else
+        "#{@prefix()}#{@variable.compile(o)}(#{ args.join(', ') })"
+    compilation

   # `super()` is converted into a call against the superclass's implementation
   # of the current function.
@@ -790,7 +795,7 @@
     o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
     val = "#{name} = #{val}"
     return "#{@tab}#{val};" if stmt
-    if top then val else "(#{val})"
+    if top or @parenthetical then val else "(#{val})"

   # Brief implementation of recursive pattern matching, when assigning array or
   # object literals to a value. Peeks at their properties to assign inner names.
@@ -1214,7 +1219,8 @@
   constructor: (@expression) ->

   compileNode: (o) ->
-    ExistenceNode.compileTest(o, @expression)[0]
+    test = ExistenceNode.compileTest(o, @expression)[0]
+    if @parenthetical then test.substring(1, test.length - 1) else test

   # The meat of the **ExistenceNode** is in this static `compileTest` method
   # because other nodes like to check the existence of their variables as well.
@@ -1248,12 +1254,11 @@

   compileNode: (o) ->
     top  = del o, 'top'
+    @expression.parenthetical = true
     code = @expression.compile(o)
-    if @isStatement(o)
-      return (if top then @tab + code + ';' else code)
-    l    = code.length
-    code = code.substr(o, l-1) if code.substr(l-1, 1) is ';'
-    if @expression instanceof AssignNode then code else "(#{code})"
+    if @parenthetical or @isStatement o
+      return if top then @tab + code + ';' else code
+    "(#{code})"

 #### ForNode


Update config

Update config

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -16,8 +16,8 @@
 			url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016"
 		web:
 			url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}"
-			user: "sharelatex"
-			pass: "password"
+			user: process.env['WEB_API_USER'] or "sharelatex"
+			pass: process.env['WEB_API_PASSWORD'] or "password"
 	redis:
 		lock:
 			host: process.env["REDIS_HOST"] or "localhost"


nothing much

nothing much

--- nodes.coffee
+++ nodes.coffee
@@ -1209,9 +1209,9 @@
       @switcher: variable
     @condition: if @multiple
       for cond, i in @condition
-        new OpNode('is', (if i is 0 then assigner else @switcher), cond)
+        new OpNode('==', (if i is 0 then assigner else @switcher), cond)
     else
-      new OpNode('is', assigner, @condition)
+      new OpNode('==', assigner, @condition)
     @else_body.rewrite_condition(@switcher) if @is_chain()
     this


Move initializer connectio to a function

--- noflo.coffee
+++ noflo.coffee
@@ -65,6 +65,15 @@

     return socket

+connectInitializer = (initializer, processes) ->
+    socket = internalSocket.createSocket()
+    to = getProcess initializer.to.node, processes
+    connectPort socket, to, initializer.to.port, false
+    socket.connect()
+    socket.send initializer.from.data
+    socket.disconnect()
+    return socket
+
 exports.createNetwork = (graph) ->
     sockets = []
     processes = []
@@ -76,12 +85,6 @@
         sockets.push connectProcess edge, processes

     for initializer in graph.initializers
-        socket = internalSocket.createSocket()
-        sockets.push socket
-        to = getProcess initializer.to.node, processes
-        connectPort socket, to, initializer.to.port, false
-        socket.connect()
-        socket.send initializer.from.data
-        socket.disconnect()
+        sockets.push connectInitializer initializer, processes

 exports.graph = graph


Move (temporarily) to ctrl-cmd-o since cmd-O seems unaccessible

--- keymaps/textmate.cson
+++ keymaps/textmate.cson
@@ -22,7 +22,7 @@
   'cmd-L': 'editor:select-line'

 'body.platform-darwin .workspace':
-  'cmd-O': 'textmate:open-favs'
+  'ctrl-cmd-o': 'textmate:open-favs'

 'body.platform-darwin .tree-view':
   'cmd-down': 'tree-view:open-selected-entry'


fix engine screen size methods calls

fix engine screen size methods calls

--- src/render/Engine.coffee
+++ src/render/Engine.coffee
@@ -9,8 +9,8 @@
             @view.style.position = "absolute"
             @view.style.top = "0px"
             @view.style.left = "0px"
-            @view.width = FF.Util.screenWidth()
-            @view.height = FF.Util.screenHeight()
+            @view.width = Rotten.Utils.screenWidth()
+            @view.height = Rotten.Utils.screenHeight()
             document.body.appendChild @view
         else
             @view = view


adding new slug for obj clone

adding new slug for obj clone

--- lib/lt3.coffee
+++ lib/lt3.coffee
@@ -880,6 +880,7 @@
     throw "need site_id" unless args[1]
     obj_id = args[0]
     site_id = args[1]
+    new_obj_slug=args[2]
     getDB (db) =>
       #get objects of old site
       db.get('objects').findOne {_id:obj_id}, (err, obj) =>
@@ -887,6 +888,7 @@
         #wipe data on old objects
         data=obj.val()
         delete data._id
+        data.slug=new_obj_slug
         data.site_id=site_id
         #insert them as new objects with site
         db.get('objects').insert data , (err , obj) =>