Created uri object in downloadTrack

Created uri object in downloadTrack

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -24,3 +24,9 @@
 # Download the given file name from
 # the given url.
 downloadTrack = (name, url) ->
+  URI =
+    url: url
+    headers:
+      'Accept': '*/*'
+      'Referer': 'http://www.123savemp3.net'
+      'User-agent': 'Mozilla/5.0 (Macintosh)'


Allow overriding URL update for commands.

--- src/app.coffee
+++ src/app.coffee
@@ -599,7 +599,8 @@

         commandInterceptor = (comm, parameters) ->
             appControl[comm].apply(appControl, parameters)?.done? =>
-                router.navigateByCommand comm, parameters
+                unless parameters[0]?.navigate == false
+                    router.navigateByCommand comm, parameters

         makeInterceptor = (comm) ->
             if DEBUG_STATE


repeat stages are Off, One, All instead of Off, All, One

--- src/app.coffee
+++ src/app.coffee
@@ -230,14 +230,14 @@
   if not mpd.status.repeat
     mpd.changeStatus
       repeat: true
-      single: false
+      single: true
   else if mpd.status.repeat and not mpd.status.single
     mpd.changeStatus
-      repeat: true
-      single: true
+      repeat: false
+      single: false
   else
     mpd.changeStatus
-      repeat: false
+      repeat: true
       single: false

 setUpUi = ->


Added Windows support for including files in subdirectories.

--- src/snockets.coffee
+++ src/snockets.coffee
@@ -127,7 +127,7 @@
         depPath = relName + '.js'
         q.perform relName, depPath
       else
-        depName = path.join path.dirname(filePath), relName
+        depName = @joinPath path.dirname(filePath), relName
         @findMatchingFile depName, flags, (err, depPath) ->
           return callback err if err
           q.perform relName, depPath
@@ -138,7 +138,7 @@
         return callback err if err
         q.unwaitFor dirName
         for item in items
-          itemPath = path.join dirName, item
+          itemPath = @joinPath dirName, item
           continue if @absPath(itemPath) is @absPath(filePath)
           q.waitFor itemPath
           do (itemPath) =>
@@ -162,7 +162,7 @@
             require relPath for relPath in relPaths
           when 'require_tree'
             for relPath in relPaths
-              requireTree path.join path.dirname(filePath), relPath
+              requireTree @joinPath path.dirname(filePath), relPath

       q.finalize()

@@ -178,7 +178,7 @@
     @readdir path.dirname(@absPath filename), flags, (err, files) =>
       return callback err if err
       return if tryFiles (for file in files
-        path.join path.dirname(filename), file
+        @joinPath path.dirname(filename), file
       )
       callback new Error("File not found: '#{filename}'")

@@ -237,9 +237,14 @@
     if relPath.match EXPLICIT_PATH
       relPath
     else if @options.src.match EXPLICIT_PATH
-      path.join @options.src, relPath
-    else
-      path.join process.cwd(), @options.src, relPath
+      @joinPath @options.src, relPath
+    else
+      @joinPath process.cwd(), @options.src, relPath
+
+  # Replace backslashes with forward slashes for Windows compatability
+  joinPath: ->
+    slash = '/' # / on the same line as the regex breaks ST2 syntax highlight
+    path.join.apply(path, arguments).replace /\\/g, slash

 # ## Compilers


activity: add logged in user validation to new pm modal

--- client/activity/lib/components/createprivatechannelmodal/index.coffee
+++ client/activity/lib/components/createprivatechannelmodal/index.coffee
@@ -184,13 +184,22 @@
   validateParticipants: ->

     recipients = @prepareRecipients()
-
-    if recipients.length
-      @setState invalidParticipants: no
+    myNickname = whoami().profile.nickname
+
+    success = =>
+      @setState { invalidParticipants: no }
       return yes
+
+    fail = =>
+      @setState { invalidParticipants: yes }
+      return no
+
+    if recipients.length is 0
+      return fail()
+    else if recipients.length is 1 and recipients[0] is myNickname
+      return fail()
     else
-      @setState invalidParticipants: yes
-      return no
+      return success()

   createChannel: ->


Add shooting to benchmark to have better offset

since player will be shooting most of the time

--- src/lazerscripts/benchmark.coffee
+++ src/lazerscripts/benchmark.coffee
@@ -21,7 +21,9 @@
       @runScript(StartOfDawn, speed: 4)
       #@placeSquad Stalker,
         #drop: 'pool'
-      @wait 1500
+      @wait 1000
+      => Crafty('PlayerSpaceship').each(-> @shoot(true))
+      @wait 1000
       => Crafty('DebugInfo').capture('start')
       @setSpeed 300
       @wait 15000
@@ -34,6 +36,7 @@
       => Crafty('DebugInfo').capture('ship particles')
       @setScenery('BayStart')
       => Crafty('PlayerSpaceship').each(-> @addComponent('Invincible').invincibleDuration(15000))
+      => Crafty('PlayerSpaceship').each(-> @shoot(false))
       @enemies()
       => Crafty('DebugInfo').capture('swirlers')
       => Crafty('PlayerSpaceship').each(-> @shoot(true))


Name the packed dmg as name-version

Name the packed dmg as name-version

--- tasks/pack.coffee
+++ tasks/pack.coffee
@@ -78,7 +78,7 @@
     (callback) ->
       appdmg
         source: './build/resources/darwin/dmg.json'
-        target: './dist/' + manifest.productName + '.dmg'
+        target: './dist/' + manifest.name + '-' + manifest.version + '.dmg'
       .on 'finish', callback
       .on 'error', callback
   ], done


Remove Document wrapper in DOM representations

* `makeDOM` etc. returns `documentElement` instead of `Document` parent
* `makeDOM` separated into `makeDOM` (for just parsing) and
  `postprocessDOM` (for other work)

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -287,10 +287,9 @@
   possibly under the old name of `overflowBox`.
   Also remove them if present, so output is valid SVG.
   ###
-  box = xml.documentElement.getAttribute('boundingBox') or
-        xml.documentElement.getAttribute('overflowBox')
-  xml.documentElement.removeAttribute 'boundingBox'
-  xml.documentElement.removeAttribute 'overflowBox'
+  box = xml.getAttribute('boundingBox') or xml.getAttribute('overflowBox')
+  xml.removeAttribute 'boundingBox'
+  xml.removeAttribute 'overflowBox'
   if box.toLowerCase().trim() == 'none'
     [null, null, null, null]
   else
@@ -305,10 +304,10 @@
   ##   - line widths which extend bounding box
   recurse = (node) ->
     if node.nodeType != node.ELEMENT_NODE or
-        node.nodeName in ['defs', 'use']
+       node.nodeName in ['defs', 'use']
       return null
     # Ignore <symbol>s except the root <symbol> that we're bounding
-    if node.nodeName == 'symbol' and node != dom.documentElement
+    if node.nodeName == 'symbol' and node != dom
       return null
     switch node.tagName
       when 'rect', 'image'
@@ -356,7 +355,7 @@
         xMax = Math.max ...(viewBox[0]+viewBox[2] for viewBox in viewBoxes)
         yMax = Math.max ...(viewBox[1]+viewBox[3] for viewBox in viewBoxes)
         [xMin, yMin, xMax - xMin, yMax - yMin]
-  viewBox = recurse dom.documentElement
+  viewBox = recurse dom
   if not viewBox? or Infinity in viewBox or -Infinity in viewBox
     null
   else
@@ -686,7 +685,7 @@

   setId: (@id) ->
     ## Can be called before or after makeDOM, updating DOM in latter case.
-    @dom.documentElement.setAttribute 'id', @id if @dom?
+    @dom.setAttribute 'id', @id if @dom?
   defaultId: (base = 'id') ->
     ###
     Generate a "default" id (typically for use in def) using these rules:
@@ -695,7 +694,7 @@
     3. Fallback to use first argument `base`, which defaults to `"id"`.
     The returned id is not yet escaped; you should pass it to `escapeId`.
     ###
-    doc = @makeDOM().documentElement
+    doc = @makeDOM()
     doc.getAttribute('id') or doc.tagName or base
   makeDOM: ->
     return @dom if @dom?
@@ -724,19 +723,23 @@
           (if line < lines.length then '\n' + indent + lines[line] else '')
         console.error "SVG parse #{level} in #{@name}: #{msg}"
     .parseFromString svg, 'image/svg+xml'
+    .documentElement
     ## Remove from the symbol any top-level xmlns=SVGNS or xmlns:xlink,
+    @postprocessDOM()
+    @dom
+  postprocessDOM: ->
     ## in the original parsed content or possibly added above,
     ## to avoid conflict with these attributes in the top-level <svg>.
-    @dom.documentElement.removeAttribute 'xmlns'
+    @dom.removeAttribute 'xmlns'
     unless @getSetting 'useHref'
-      @dom.documentElement.removeAttribute 'xmlns:xlink'
+      @dom.removeAttribute 'xmlns:xlink'

     ## Wrap in <symbol> if appropriate,
     ## before we add width/height/etc. attributes.
     @wrap?()

     ## <image> processing (must come before width/height processing).
-    domRecurse @dom.documentElement, (node) =>
+    domRecurse @dom, (node) =>
       if node.nodeName == 'image'
         ###
         Fix image-rendering: if unspecified, or if specified as "optimizeSpeed"
@@ -801,14 +804,14 @@
         true

     ## Determine `viewBox`, `width`, and `height` attributes.
-    @viewBox = parseBox @dom.documentElement.getAttribute 'viewBox'
-    @width = parseDim @origWidth = @dom.documentElement.getAttribute 'width'
-    @height = parseDim @origHeight = @dom.documentElement.getAttribute 'height'
+    @viewBox = parseBox @dom.getAttribute 'viewBox'
+    @width = parseDim @origWidth = @dom.getAttribute 'width'
+    @height = parseDim @origHeight = @dom.getAttribute 'height'
     ## Check for default width/height specified by caller.
     if not @width? and @defaultWidth?
-      @dom.documentElement.setAttribute 'width', @width = @defaultWidth
+      @dom.setAttribute 'width', @width = @defaultWidth
     if not @height? and @defaultHeight?
-      @dom.documentElement.setAttribute 'height', @height = @defaultHeight
+      @dom.setAttribute 'height', @height = @defaultHeight
     ## Absent viewBox becomes 0 0 <width> <height> if latter are present
     ## (but only internal to SVG Tiler, DOM remains unchanged).
     if @width? and @height? and not @viewBox?
@@ -817,20 +820,20 @@
     ## (e.g. in `SVGSymbol`).
     if not @viewBox? and @autoViewBox
       if (@viewBox = svgBBox @dom)?
-        @dom.documentElement.setAttribute 'viewBox', @viewBox.join ' '
+        @dom.setAttribute 'viewBox', @viewBox.join ' '
     ## Absent width/height inherited from viewBox if latter is present,
     ## in `SVGSymbol` which sets `@autoWidthHeight`.
     ## Including the width/height in the <symbol> lets us skip it in <use>.
     if @viewBox? and @autoWidthHeight
       unless @width?
-        @dom.documentElement.setAttribute 'width', @width = @viewBox[2]
+        @dom.setAttribute 'width', @width = @viewBox[2]
       unless @height?
-        @dom.documentElement.setAttribute 'height', @height = @viewBox[3]
+        @dom.setAttribute 'height', @height = @viewBox[3]

     ## Overflow behavior
-    overflow = attributeOrStyle @dom.documentElement, 'overflow'
+    overflow = attributeOrStyle @dom, 'overflow'
     if not overflow? and @defaultOverflow?
-      @dom.documentElement.setAttribute 'overflow', overflow = @defaultOverflow
+      @dom.setAttribute 'overflow', overflow = @defaultOverflow
     @overflowVisible = (overflow? and /^\s*(visible|scroll)\b/.test overflow)

     ###
@@ -842,35 +845,34 @@
     ###
     if @overflowVisible
       if @width == 0
-        @dom.documentElement.setAttribute 'width', zeroSizeReplacement
+        @dom.setAttribute 'width', zeroSizeReplacement
       if @height == 0
-        @dom.documentElement.setAttribute 'height', zeroSizeReplacement
+        @dom.setAttribute 'height', zeroSizeReplacement
       if @viewBox? and (@viewBox[2] == 0 or @viewBox[3] == 0)
         @viewBox[2] = zeroSizeReplacement if @viewBox[2] == 0
         @viewBox[3] = zeroSizeReplacement if @viewBox[3] == 0
-        @dom.documentElement.setAttribute 'viewBox', @viewBox.join ' '
+        @dom.setAttribute 'viewBox', @viewBox.join ' '

     ## Special SVG Tiler attributes that get extracted from DOM
     @boundingBox = extractBoundingBox @dom
-    @zIndex = extractZIndex @dom.documentElement
-    #@isEmpty = @dom.documentElement.childNodes.length == 0 and
-    #  (@emptyWithId or not @dom.documentElement.hasAttribute 'id') and
-    #  emptyContainers.has @dom.documentElement.tagName
+    @zIndex = extractZIndex @dom
+    #@isEmpty = @dom.childNodes.length == 0 and
+    #  (@emptyWithId or not @dom.hasAttribute 'id') and
+    #  emptyContainers.has @dom.tagName
     recursiveEmpty = (node, allowId) ->
       return false unless emptyContainers.has node.tagName
       return false unless allowId or not node.hasAttribute 'id'
       for child in node.childNodes
         return false unless recursiveEmpty child
       true
-    @isEmpty = recursiveEmpty @dom.documentElement, @emptyWithId
-    @dom
+    @isEmpty = recursiveEmpty @dom, @emptyWithId
   useDOM: ->
     @makeDOM()
     ## Clone if content is static, to enable later re-use
     if @isStatic
-      @dom.documentElement.cloneNode true
+      @dom.cloneNode true
     else
-      @dom.documentElement
+      @dom

 class SVGWrapped extends SVGContent
   ###
@@ -881,33 +883,32 @@
   ###
   wrap: ->
     ## Wrap XML in <wrapper>.
-    symbol = @dom.createElementNS SVGNS, @wrapper
-    ## Force `id` to be first attribute (if it's already set).
-    symbol.setAttribute 'id', @id if @id?
+    symbol = @dom.ownerDocument.createElementNS SVGNS, @wrapper
+    ## Force `id` to be first attribute.
+    symbol.setAttribute 'id', @id or ''
     ## Avoid a layer of indirection for <symbol>/<svg> at top level
-    if @dom.documentElement.nodeName in ['symbol', 'svg'] and
-       not @dom.documentElement.nextSibling?
-      for attribute in @dom.documentElement.attributes
+    if @dom.nodeName in ['symbol', 'svg']
+      for attribute in @dom.attributes
         unless attribute.name in ['version', 'id'] or attribute.name.startsWith 'xmlns'
           symbol.setAttribute attribute.name, attribute.value
-      @dom.removeChild doc = @dom.documentElement
+      for child in (node for node in @dom.childNodes)
+        symbol.appendChild child
+      @dom.parentNode?.replaceChild @dom, symbol
     else
-      doc = @dom
       ## Allow top-level object to specify <symbol> data.
       ## `z-index` and `boundingBox` should already be extracted.
       ## `width` and `height` have another meaning in e.g. <rect>s,
       ## so just transfer for tags where they are meaningless.
       for attribute in ['viewBox', 'width', 'height', 'overflow']
         continue if attribute in ['width', 'height'] and
-                    doc.documentElement.tagName not in ['g']
-        if doc.documentElement.hasAttribute attribute
-          symbol.setAttribute attribute,
-            doc.documentElement.getAttribute attribute
-          doc.documentElement.removeAttribute attribute
-    for child in (node for node in doc.childNodes)
-      symbol.appendChild child
-    @dom.appendChild symbol
-    @dom
+                    @dom.tagName not in ['g']
+        if @dom.hasAttribute attribute
+          symbol.setAttribute attribute, @dom.getAttribute attribute
+          @dom.removeAttribute attribute
+      parent = @dom.parentNode
+      symbol.appendChild @dom
+      parent?.appendChild symbol
+    @dom = symbol

 #class SVGSVG extends SVGWrapped
 #  ###
@@ -925,15 +926,13 @@
   autoViewBox: true
   autoWidthHeight: true
   emptyWithId: true  # consider empty even if <symbol> has id attribute
-  makeDOM: ->
-    return @dom if @dom?
-
+  postprocessDOM: ->
     ## Special defaults for loading symbols in `SVGContent`'s `makeDOM`.
     @defaultWidth = @getSetting 'forceWidth'
     @defaultHeight = @getSetting 'forceHeight'
     @defaultOverflow = @getSetting 'overflowDefault'

-    ## `SVGTop` sets `@width` and `@height` according to
+    ## `SVGContent` sets `@width` and `@height` according to
     ## `width`/`height`/`viewBox` attributes or our defaults.
     super()

@@ -944,8 +943,8 @@

     ## Remove `width="auto"` and `height="auto"` attributes
     ## (or whatever `SVGTop` set them to).
-    @dom.documentElement.removeAttribute 'width' if @autoWidth
-    @dom.documentElement.removeAttribute 'height' if @autoHeight
+    @dom.removeAttribute 'width' if @autoWidth
+    @dom.removeAttribute 'height' if @autoHeight

     ## Warning for missing width/height.
     warnings = []
@@ -961,7 +960,7 @@
     ## Optionally extract <text> nodes for LaTeX output
     if @getSetting 'texText'
       @text = []
-      domRecurse @dom.documentElement, (node, parent) =>
+      domRecurse @dom, (node, parent) =>
         if node.nodeName == 'text'
           @text.push node
           node.parentNode.removeChild node
@@ -1737,9 +1736,9 @@
       @updateSize()
     @layers[content.zIndex] ?= []
     if prepend
-      @layers[content.zIndex].unshift dom.documentElement
+      @layers[content.zIndex].unshift dom
     else
-      @layers[content.zIndex].push dom.documentElement
+      @layers[content.zIndex].push dom
   expandBox: (box) ->
     if box[0]?
       @xMin = box[0] if not @xMin? or box[0] < @xMin
@@ -1767,18 +1766,18 @@
     and bounding box in `@xMin`, `@xMax`, `@yMin`, `@yMax`,
     `@width`, and `@height`.
     ###
-    @dom = domImplementation.createDocument SVGNS, 'svg'
-    svg = @dom.documentElement
-    svg.setAttribute 'xmlns:xlink', XLINKNS unless @getSetting 'useHref'
-    svg.setAttribute 'version', '1.1'
-    #svg.appendChild defs = @dom.createElementNS SVGNS, 'defs'
+    doc = domImplementation.createDocument SVGNS, 'svg'
+    @dom = doc.documentElement
+    @dom.setAttribute 'xmlns:xlink', XLINKNS unless @getSetting 'useHref'
+    @dom.setAttribute 'version', '1.1'
+    #@dom.appendChild defs = doc.createElementNS SVGNS, 'defs'

     ## Preprocess callbacks, which may change anything about the Render job
     @mappings.doPreprocess @

     ## <style> tags for CSS
     for style in @styles
-      svg.appendChild styleTag = @dom.createElementNS SVGNS, 'style'
+      @dom.appendChild styleTag = doc.createElementNS SVGNS, 'style'
       styleTag.textContent = style.css

     ## Render all tiles in the drawing.
@@ -1814,7 +1813,7 @@
                 symbol.setId '_empty'
               else
                 ## Include new non-empty <symbol> in SVG
-                svg.appendChild symbol.useDOM()
+                @dom.appendChild symbol.useDOM()
             new Tile {i, j, k, key, symbol,
               isEmpty: symbol.isEmpty
               zIndex: symbol.zIndex
@@ -1870,7 +1869,7 @@
           tile.yMax = y + tile.height
           unless tile.isEmpty
             @layers[tile.zIndex] ?= []
-            @layers[tile.zIndex].push use = @dom.createElementNS SVGNS, 'use'
+            @layers[tile.zIndex].push use = doc.createElementNS SVGNS, 'use'
             use.setAttribute @hrefAttr(), '#' + symbol.id
             use.setAttribute 'x', x
             use.setAttribute 'y', y
@@ -1934,7 +1933,7 @@
               @defs.push def
           node.setAttribute attr, node.getAttribute(attr).replace "##{id}",
             "##{newId}"
-    findGlobalDefs svg
+    findGlobalDefs @dom

     ## Render all <defs> so far and check for additional <defs> used by them.
     ## `for def in @defs` but allowing @defs to change in length
@@ -1948,22 +1947,22 @@
         findGlobalDefs dom
         {def, dom}
     ## Add <defs> to DOM if they're used or forced.
-    firstSymbol = svg.firstChild
+    firstSymbol = @dom.firstChild
     defsWrapper = null
     for {def, dom} in defDoms
       ## Omit unused <defs> unless forced.
       continue unless def.isForced or usedIds.has def.id
       ## Wrap in <defs> if needed.
       if skipDef.has dom.tagName
-        svg.insertBefore dom, firstSymbol
+        @dom.insertBefore dom, firstSymbol
       else
-        defsWrapper ?= @dom.createElementNS SVGNS, 'defs'
+        defsWrapper ?= doc.createElementNS SVGNS, 'defs'
         defsWrapper.appendChild dom
-    svg.insertBefore defsWrapper, svg.firstChild if defsWrapper?
+    @dom.insertBefore defsWrapper, @dom.firstChild if defsWrapper?

     ## Factor out duplicate inline <image>s into separate <symbol>s.
     inlineImages = new Map
-    domRecurse svg, (node) =>
+    domRecurse @dom, (node) =>
       return true unless node.nodeName == 'image'
       {href} = getHref node
       return true unless href?.startsWith 'data:'
@@ -1977,7 +1976,7 @@
       height = node.getAttribute 'data-height'
       node.removeAttribute 'data-height'
       # Transfer x/y/width/height to <use> element, for more re-usability.
-      node.parentNode.replaceChild (use = @dom.createElementNS SVGNS, 'use'),
+      node.parentNode.replaceChild (use = doc.createElementNS SVGNS, 'use'),
         node
       for attr in ['x', 'y', 'width', 'height']
         use.setAttribute attr, node.getAttribute attr if node.hasAttribute attr
@@ -1990,7 +1989,7 @@
       attributes = attributes.join ' '
       unless (id = inlineImages.get attributes)?
         inlineImages.set attributes, id = @id filename
-        svg.appendChild symbol = @dom.createElementNS SVGNS, 'symbol'
+        @dom.appendChild symbol = doc.createElementNS SVGNS, 'symbol'
         symbol.setAttribute 'id', id
         # If we don't have width/height set from data-width/height fields,
         # we take the first used width/height as the defining height.
@@ -2005,11 +2004,11 @@
     layerOrder = (layer for layer of @layers).sort (x, y) -> x-y
     for layer in layerOrder
       for node in @layers[layer]
-        svg.appendChild node
-    svg.setAttribute 'viewBox', "#{@xMin} #{@yMin} #{@width} #{@height}"
-    svg.setAttribute 'width', @width
-    svg.setAttribute 'height', @height
-    #svg.setAttribute 'preserveAspectRatio', 'xMinYMin meet'
+        @dom.appendChild node
+    @dom.setAttribute 'viewBox', "#{@xMin} #{@yMin} #{@width} #{@height}"
+    @dom.setAttribute 'width', @width
+    @dom.setAttribute 'height', @height
+    #@dom.setAttribute 'preserveAspectRatio', 'xMinYMin meet'
     @dom
   makeSVG: ->
     out = new XMLSerializer().serializeToString @makeDOM()
@@ -2301,7 +2300,7 @@
       filename = eltSettings.filename
       drawing = Input.recognize filename, elt.innerText, eltSettings
       if drawing instanceof Drawing
-        dom = drawing.renderDOM().documentElement
+        dom = drawing.renderDOM()
         if eltSettings.keepParent
           elt.innerHTML = ''
           elt.appendChild dom


If there are no listeners for the "disconnected" event, emit an "error" event instead of throwing an error. Per discussion with @alaingilbert.

--- src/bot.coffee
+++ src/bot.coffee
@@ -76,6 +76,15 @@
         console.log.apply(@, args)

+  disconnect: (err) ->
+    @_isAuthenticated = false
+    @_isConnected = false
+    if @listeners('disconnected').length > 0
+      @emit 'disconnected', err
+    else
+      @emit 'error', err
+
+
   connect: (roomId) ->
     if not /^[0-9a-f]{24}$/.test(roomId)
       throw new Error "Invalid roomId: cannot connect to '#{roomId}'"
@@ -116,15 +125,6 @@
       command : 'endsong'
       room : data.room
       success : true
-
-
-  disconnect: (err) ->
-    @_isAuthenticated = false
-    @_isConnected = false
-    if @listeners('disconnected').length
-      @emit 'disconnected', err
-    else
-      throw err

   onClose: ->


repaired tests

repaired tests

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -433,9 +433,9 @@
         results = add:['add'], close:['close']

         xml.on 'add', (par, el) ->
-            results.add.push el.name
+            results.add.push   el.name unless el is el.builder
         xml.on 'close', (el) ->
-            results.close.push el.name
+            results.close.push el.name unless el is el.builder

         root = xml.tag('root')
         c = root.tag('childA')
@@ -462,12 +462,10 @@

             æ.done()

-        xml.on 'add',   (par, tag) -> results.add.push tag.name
-        xml.on 'close', (tag) ->
-            results.close.push tag.name
-            #{name, closed, level} = tag
-            #console.log 'close',{name, closed, level, parent:tag.parent.name, builder:tag.builder.closed}
-            console.log 'close', tag.name
+        xml.on 'add', (par, el) ->
+            results.add.push   el.name unless el is el.builder
+        xml.on 'close', (el) ->
+            results.close.push el.name unless el is el.builder

         first = xml.tag('first')
         first.end()
@@ -491,9 +489,9 @@
         results = add:['add'], close:['close']

         xml.on 'add', (par, el) ->
-            results.add.push el.name
+            results.add.push   el.name unless el is el.builder
         xml.on 'close', (el) ->
-            results.close.push el.name
+            results.close.push el.name unless el is el.builder

         sub = new Builder
         root = xml.tag('root').add(sub).end()


sync call order test

sync call order test

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -299,3 +299,32 @@
         stream = createReadStream path.join(__dirname,"..","..","..","filename")
         stream.pipe(file)
         xml.end()
+
+    'sync call order': (æ) ->
+        xml = new Builder
+        xml.on 'end', æ.done
+        xml.on 'data', (tag) -> æ.equal results.shift(), tag
+        results = [
+            '<apple>'
+            '<wurm>'
+            '<seed/>'
+            '</wurm>'
+            '</apple>'
+        ]
+        counter = 0
+        apple = xml.$tag 'apple', ->
+            æ.equal counter, 0
+            counter++
+            wurm = @$tag 'wurm', ->
+                æ.equal counter, 1
+                counter++
+                @tag('seed').end()
+                æ.equal counter, 2
+            counter++
+            æ.equal counter, 3
+        counter++
+        æ.equal counter, 4
+        xml.end()
+        counter++
+        æ.equal counter, 5
+


don't show URLs for fav images

don't show URLs for fav images

--- scripts/favorite-images.coffee
+++ scripts/favorite-images.coffee
@@ -22,5 +22,5 @@
   robot.respond /favorite images/i, (msg) ->
     robot.brain.data.favorite_images or= {}
     imageNames = for name, image of robot.brain.data.favorite_images
-      "#{name} -> #{image.replace(/https?:\/\//,'')}"
+      "#{name}"
     msg.send imageNames.join('\n')


increase timeout just in case

increase timeout just in case

--- test/Model.coffee
+++ test/Model.coffee
@@ -12,7 +12,7 @@
 # AZURE_STORAGE_ACCESS_KEY
 #
 describe 'Model', ->
-    @timeout 15000
+    @timeout 20000

     before (done) ->
         @testTableName = 'user'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -251,6 +251,15 @@
   equals darkColor.g(), 24
   equals darkColor.b(), 12

+test "#grayscale", ->
+  color = Color(59, 200, 1)
+
+  gray = color.grayscale()
+
+  equals gray.r(), (0.3941176470588235 * 255).round()
+  equals gray.g(), (0.3941176470588235 * 255).round()
+  equals gray.b(), (0.39411764705882350 * 255).round()
+
 test "#saturate", ->
   color = Color(45, 26, 100)

@@ -273,8 +282,6 @@
   equals mixedColor2.g(), (5.5 + 81).round()
   equals mixedColor2.b(), (10 + 180).round()
   equals mixedColor2.a(), 0.1 + (0.5 * 0.9)
-
-#TODO: test hsl methods
-
+cd
 module()


JSON from input string c/s

JSON from input string c/s

--- src/web_client/app/scripts/websocket.coffee
+++ src/web_client/app/scripts/websocket.coffee
@@ -56,17 +56,15 @@

   $('#send_client').on 'click', () ->
       text = $('#text').val()
-      console.log(conn)
-      #log('c>: ' + text);
-      conn.send('c>'+text)
+      msg = JSON.stringify({'c':text})
+      conn.send(msg)
       $('#text').val('').focus()
       return false

   $('#send_server').on 'click', () ->
       text = $('#text').val()
-      console.log(conn)
-      #log('s>: ' + text);
-      conn.send('s>'+text)
+      msg = JSON.stringify({'s':text})
+      conn.send(msg)
       $('#text').val('').focus()
       return false


Test creating customer records

Test creating customer records

--- spec/skr/models/CustomerSpec.coffee
+++ spec/skr/models/CustomerSpec.coffee
@@ -3,3 +3,37 @@
     it "can be instantiated", ->
         model = new Skr.Models.Customer()
         expect(model).toEqual(jasmine.any(Skr.Models.Customer))
+
+
+    it "sends failure messages when code isn't set", (done)->
+        model = new Skr.Models.Customer()
+        Lanes.Testing.ModelSaver.perform(model, done).then (save)->
+            expect(save.error).toHaveBeenCalled()
+            expect(model.errors.code).toContain("blank")
+
+    it "saves when fields are set", (done)->
+        model = new Skr.Models.Customer(
+            code: "SPECTEST", name: "A Spec Test Customer"
+            billing_address:  { name: "Billing Address" }
+            shipping_address: { name: "Shipping Address" }
+        )
+        Lanes.Testing.ModelSaver.perform(model, done).then (save)->
+            expect(save.error).not.toHaveBeenCalled()
+            expect(model.errors).toBeNull()
+
+
+        # model.save().then( null, ->
+        #
+        #         done()
+        # )
+
+        # expect(success).not.toHaveBeenCalled()
+        # expect(failure).toHaveBeenCalled()
+        # console.log model.errors
+        #
+
+        #  ->
+        #     expect(false).toBeTrue
+        # ,  ->
+        #     assert_equal 1, model.errors.length
+        #     done()


Delay 5 seconds after autostart for network.

--- src/cli.coffee
+++ src/cli.coffee
@@ -33,8 +33,8 @@
       if ret then vals else vms

   autoStart: (headless) ->
-    addDsl @then (vms) => @where('!running')
-      .all((vm) -> vm.start headless).then -> vms
+    auto = (vm) -> vm.start(headless).then -> Q.delay(5000).then ->
+    addDsl @then (vms) => @where('!running').all(auto).then -> vms

   maybeAutoStart: (maybe, headless) -> if maybe then @autoStart headless else @


Make it work with update forms

Make it work with update forms

--- lib/client/autoform-file.coffee
+++ lib/client/autoform-file.coffee
@@ -14,7 +14,7 @@
   collection?.findOne(id)

 Template.afFileUpload.onCreated ->
-  @value = new ReactiveVar
+  @value = new ReactiveVar @data.value

 Template.afFileUpload.onRendered ->
   self = @


Show detailed time in mapping box

Show detailed time in mapping box

--- resources/assets/coffee/react/_components/beatmapset-mapping.coffee
+++ resources/assets/coffee/react/_components/beatmapset-mapping.coffee
@@ -16,7 +16,7 @@
 #    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
 ###

-{div, span, a, ol, li} = ReactDOMFactories
+{div, span, a, time} = ReactDOMFactories
 el = React.createElement

 bn = 'beatmapset-mapping'
@@ -41,19 +41,22 @@

       div null,
         osu.trans 'beatmapsets.show.details.submitted'
-        span
-          className: "#{bn}__date"
+        time
+          className: "#{bn}__date timeago"
+          datetime: beatmapset.submitted_date
           moment(beatmapset.submitted_date).format dateFormat

       if beatmapset.ranked > 0
         div null,
           osu.trans "beatmapsets.show.details.#{beatmapset.status}"
-          span
-            className: "#{bn}__date"
+          time
+            className: "#{bn}__date timeago"
+            datetime: beatmapset.submitted_date
             moment(beatmapset.ranked_date).format dateFormat
       else
         div null,
           osu.trans 'beatmapsets.show.details.updated'
-          span
-            className: "#{bn}__date"
+          time
+            className: "#{bn}__date timeago"
+            datetime: beatmapset.submitted_date
             moment(beatmapset.last_updated).format dateFormat


 Merge branch 'master.kodingRouterTempFix' into 'master'

Master.Koding Router Temp Fix

--- client/Main/kodingrouter.coffee
+++ client/Main/kodingrouter.coffee
@@ -281,8 +281,9 @@
       '/Landing/:page'         : noop
       '/R/:username'           : ({params:{username}})->
         KD.mixpanel "Visit referrer url, success", {username}
+
         # give a notification to tell that this is a referral link here - SY
-        handleRoot.call this
+        @handleRoute if KD.isLoggedIn() then "/Activity" else "/"

       '/:name?/Logout'         : ({params:{name}})-> requireLogin -> mainController.doLogout()


Dont use same variable in nested loops

Dont use same variable in nested loops

--- lib/deprecation-cop-view.coffee
+++ lib/deprecation-cop-view.coffee
@@ -288,11 +288,11 @@
                   @button class: 'btn check-for-update', 'Check for Update'
                   @button class: 'btn disable-package', 'data-package-name': packageName, 'Disable Package'

-            for sourcePath, deprecations of deprecationsByFile
+            for sourcePath, deprecationsInFile of deprecationsByFile
               @li class: 'list-item source-file', =>
-                @a class: 'source-url', href: path.join(deprecations[0].packagePath, sourcePath), sourcePath
+                @a class: 'source-url', href: path.join(deprecationsInFile[0].packagePath, sourcePath), sourcePath
                 @ul class: 'list', =>
-                  for deprecation in deprecations
+                  for deprecation in deprecationsInFile
                     @li class: 'list-item deprecation-detail', =>
                       @span class: 'text-warning icon icon-alert'
                       @div class: 'list-item deprecation-message', =>


Display if maxDeprecationCallCount is reached

--- lib/deprecation-cop-view.coffee
+++ lib/deprecation-cop-view.coffee
@@ -84,7 +84,10 @@
           @li class: 'deprecation list-nested-item collapsed', =>
             @div class: 'deprecation-info list-item', =>
               @span class: 'text-highlight', deprecation.getOriginName()
-              @span " (called #{_.pluralize(deprecation.getCallCount(), 'time')})"
+              if deprecation.getCallCount() >= Grim.maxDeprecationCallCount()
+                @span " (called more than #{deprecation.getCallCount()} times)"
+              else
+                @span " (called #{_.pluralize(deprecation.getCallCount(), 'time')})"

             @ul class: 'list', =>
               @li class: 'list-item', =>


Fix references to the RecurlyWrapper object.

--- services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
+++ services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
@@ -191,13 +191,13 @@
 		            </account>
 		          </subscription>
 		          """
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "subscriptions"
 			method : "POST"
 			body   : requestBody
 		}, (error, response, responseBody) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml responseBody, callback
+			RecurlyWrapper._parseSubscriptionXml responseBody, callback
 		)

 	createSubscription: (user, subscriptionDetails, recurly_token_id, callback)->
@@ -207,7 +207,7 @@
 		fn user, subscriptionDetails, recurly_token_id, callback

 	apiRequest : (options, callback) ->
-		options.url = @apiUrl + "/" + options.url
+		options.url = RecurlyWrapper.apiUrl + "/" + options.url
 		options.headers =
 			"Authorization" : "Basic " + new Buffer(Settings.apis.recurly.apiKey).toString("base64")
 			"Accept"        : "application/xml"
@@ -249,11 +249,11 @@

 	getSubscriptions: (accountId, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}/subscriptions"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)

@@ -266,11 +266,11 @@
 		else
 			url = "subscriptions/#{subscriptionId}"

-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: url
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml body, (error, recurlySubscription) =>
+			RecurlyWrapper._parseSubscriptionXml body, (error, recurlySubscription) =>
 				return callback(error) if error?
 				if options.includeAccount
 					if recurlySubscription.account? and recurlySubscription.account.url?
@@ -278,7 +278,7 @@
 					else
 						return callback "I don't understand the response from Recurly"

-					@getAccount accountId, (error, account) ->
+					RecurlyWrapper.getAccount accountId, (error, account) ->
 						return callback(error) if error?
 						recurlySubscription.account = account
 						callback null, recurlySubscription
@@ -296,9 +296,9 @@
 					per_page:200
 			if cursor?
 				opts.qs.cursor = cursor
-			@apiRequest opts, (error, response, body) =>
+			RecurlyWrapper.apiRequest opts, (error, response, body) =>
 				return callback(error) if error?
-				@_parseXml body, (err, data)->
+				RecurlyWrapper._parseXml body, (err, data)->
 					if err?
 						logger.err err:err, "could not get accoutns"
 						callback(err)
@@ -314,19 +314,19 @@

 	getAccount: (accountId, callback) ->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseAccountXml body, callback
+			RecurlyWrapper._parseAccountXml body, callback
 		)

 	getBillingInfo: (accountId, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "accounts/#{accountId}/billing_info"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)

@@ -338,13 +338,13 @@
 		            <timeframe>#{options.timeframe}</timeframe>
 		          </subscription>
 		          """
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "subscriptions/#{subscriptionId}"
 			method : "put"
 			body   : requestBody
 		}, (error, response, responseBody) =>
 			return callback(error) if error?
-			@_parseSubscriptionXml responseBody, callback
+			RecurlyWrapper._parseSubscriptionXml responseBody, callback
 		)

 	createFixedAmmountCoupon: (coupon_code, name, currencyCode, discount_in_cents, plan_code, callback)->
@@ -363,7 +363,7 @@
 			</coupon>
 		"""
 		logger.log coupon_code:coupon_code, requestBody:requestBody, "creating coupon"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "coupons"
 			method : "post"
 			body   : requestBody
@@ -375,16 +375,16 @@

 	lookupCoupon: (coupon_code, callback)->
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "coupons/#{coupon_code}"
 		}, (error, response, body) =>
 			return callback(error) if error?
-			@_parseXml body, callback
+			RecurlyWrapper._parseXml body, callback
 		)

 	cancelSubscription: (subscriptionId, callback) ->
 		logger.log subscriptionId:subscriptionId, "telling recurly to cancel subscription"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "subscriptions/#{subscriptionId}/cancel",
 			method: "put"
 		}, (error, response, body) ->
@@ -393,7 +393,7 @@

 	reactivateSubscription: (subscriptionId, callback) ->
 		logger.log subscriptionId:subscriptionId, "telling recurly to reactivating subscription"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url: "subscriptions/#{subscriptionId}/reactivate",
 			method: "put"
 		}, (error, response, body) ->
@@ -409,7 +409,7 @@
 			</redemption>
 		"""
 		logger.log account_code:account_code, coupon_code:coupon_code, requestBody:requestBody, "redeeming coupon for user"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "coupons/#{coupon_code}/redeem"
 			method : "post"
 			body   : requestBody
@@ -423,7 +423,7 @@
 		next_renewal_date = new Date()
 		next_renewal_date.setDate(next_renewal_date.getDate() + daysUntilExpire)
 		logger.log subscriptionId:subscriptionId, daysUntilExpire:daysUntilExpire, "Exending Free trial for user"
-		@apiRequest({
+		RecurlyWrapper.apiRequest({
 			url    : "/subscriptions/#{subscriptionId}/postpone?next_renewal_date=#{next_renewal_date}&bulk=false"
 			method : "put"
 		}, (error, response, responseBody) =>
@@ -433,7 +433,7 @@
 		)

 	_parseSubscriptionXml: (xml, callback) ->
-		@_parseXml xml, (error, data) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
 			return callback(error) if error?
 			if data? and data.subscription?
 				recurlySubscription = data.subscription
@@ -442,7 +442,7 @@
 			callback null, recurlySubscription

 	_parseAccountXml: (xml, callback) ->
-		@_parseXml xml, (error, data) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
 			return callback(error) if error?
 			if data? and data.account?
 				account = data.account
@@ -451,7 +451,7 @@
 			callback null, account

 	_parseBillingInfoXml: (xml, callback) ->
-		@_parseXml xml, (error, data) ->
+		RecurlyWrapper._parseXml xml, (error, data) ->
 			return callback(error) if error?
 			if data? and data.account?
 				billingInfo = data.billing_info


rename isAutoCompile and isBackgroundAutoCompile

changed to isAutoCompileOnLoad and isAutoCompileOnChange

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -68,7 +68,7 @@
 		$scope.$on "project:joined", () ->
 			return if !autoCompile
 			autoCompile = false
-			$scope.recompile(isAutoCompile: true)
+			$scope.recompile(isAutoCompileOnLoad: true)
 			$scope.hasPremiumCompile = $scope.project.features.compileGroup == "priority"

 		$scope.$on "pdf:error:display", () ->
@@ -86,7 +86,7 @@

 			if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT
 				if (!ide.$scope.hasLintingError)
-					$scope.recompile(isBackgroundAutoCompile: true)
+					$scope.recompile(isAutoCompileOnChange: true)
 			else
 				# Extend remainder of timeout
 				autoCompileTimeout = setTimeout () ->
@@ -127,7 +127,7 @@
 		sendCompileRequest = (options = {}) ->
 			url = "/project/#{$scope.project_id}/compile"
 			params = {}
-			if options.isAutoCompile or options.isBackgroundAutoCompile
+			if options.isAutoCompileOnLoad or options.isAutoCompileOnChange
 				params["auto_compile"]=true
 			# if the previous run was a check, clear the error logs
 			$scope.pdf.logEntries = [] if $scope.check
@@ -207,7 +207,7 @@
 				$scope.shouldShowLogs = true
 				fetchLogs(fileByPath)
 			else if response.status == "autocompile-backoff"
-				if $scope.pdf.isAutoCompile # initial autocompile
+				if $scope.pdf.isAutoCompileOnLoad # initial autocompile
 					$scope.pdf.view = 'uncompiled'
 				else # background autocompile from typing
 					$scope.pdf.view = 'errors'
@@ -422,7 +422,7 @@
 			event_tracking.sendMBSampled "editor-recompile-sampled", options

 			$scope.pdf.compiling = true
-			$scope.pdf.isAutoCompile = options?.isAutoCompile # initial autocompile
+			$scope.pdf.isAutoCompileOnLoad = options?.isAutoCompileOnLoad # initial autocompile

 			if options?.force
 				# for forced compile, turn off validation check and ignore errors


Added ADC and AND instructions.

Added ADC and AND instructions.

--- src/CPU.coffee
+++ src/CPU.coffee
@@ -15,7 +15,7 @@

 Instruction =
     ADC: 1
-    # TODO add all instructions
+    AND: 2

 Interrupt =
     IRQ:   1
@@ -63,8 +63,9 @@
             (base + @registerX) & 0xFFFF

         @registerAddressingMode AddressingMode.Relative, ->
-            offset = @read @programCounter + 1
-            (@programCounter + @toSigned offset) & 0xFFFF
+            offset = @toSigned @read @programCounter + 1
+            @checkPageCrossed @programCounter, offset
+            (@programCounter + offset) & 0xFFFF

         @registerAddressingMode AddressingMode.Indirect, ->
             @read16 @read16 @programCounter + 1
@@ -83,17 +84,57 @@

     initInstructionsTable: ->
         @instructionsTable = []
+
         @registerInstruction Instruction.ADC, (address) ->
-            # TODO instruction code
-        # TODO add all instructions
+            addition = @read address
+            result = @accumulator + addition + @carryFlag
+            @computeCarryFlag result
+            @computeZeroFlag result
+            @computeOverflowFlag @accumulator @addition @result
+            @computeNegativeFlag result
+            @accumulator = result & 0xFF
+
+        @registerInstruction Instruction.AND, (address) ->
+            @accumulator = @accumulator & @read address
+            @computeZeroFlag @accumulator
+            @computeNegativeFlag @accumulator
+
+    computeCarryFlag: (result) ->
+        @carryFlag = if result > 0xFF then 1 else 0
+
+    computeZeroFlag: (result) ->
+        @zeroFlag = if (result & 0xFF) != 0 then 1 else 0
+
+    computeOverflowFlag: (operand1, operand2, result) ->
+        @overflowFlag = if (operand1 ^ result) & (operand2 ^ result) & 0x80 != 0 then 1 else 0
+
+    computeNegativeFlag: (result) ->
+        @negativeFlag = (result >> 7) & 1

     registerInstruction: (instruction, execution) ->
         @instructionsTable[instruction] = execution

     initOperationsTable: ->
         @operationsTable = []
-        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage, 2, 2
-        # TODO add all operations
+
+        @registerOperation 0x69, Instruction.ADC, AddressingMode.Immediate,        2, 2
+        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage,         2, 3
+        @registerOperation 0x75, Instruction.ADC, AddressingMode.IndexedXZeroPage, 2, 4
+        @registerOperation 0x6D, Instruction.ADC, AddressingMode.Absolute,         3, 4
+        @registerOperation 0x7D, Instruction.ADC, AddressingMode.IndexedXAbsolute, 3, 4
+        @registerOperation 0x79, Instruction.ADC, AddressingMode.IndexedYAbsolute, 3, 4
+        @registerOperation 0x61, Instruction.ADC, AddressingMode.IndexedXIndirect, 2, 6
+        @registerOperation 0x71, Instruction.ADC, AddressingMode.IndirectIndexedY, 2, 5
+
+        @registerOperation 0x29, Instruction.AND, AddressingMode.Immediate,        2, 2
+        @registerOperation 0x25, Instruction.AND, AddressingMode.ZeroPage,         2, 3
+        @registerOperation 0x35, Instruction.AND, AddressingMode.IndexedXZeroPage, 2, 4
+        @registerOperation 0x2D, Instruction.AND, AddressingMode.Absolute,         3, 4
+        @registerOperation 0x3D, Instruction.AND, AddressingMode.IndexedXAbsolute, 3, 4
+        @registerOperation 0x39, Instruction.AND, AddressingMode.IndexedYAbsolute, 3, 4
+        @registerOperation 0x21, Instruction.AND, AddressingMode.IndexedXIndirect, 2, 6
+        @registerOperation 0x31, Instruction.AND, AddressingMode.IndirectIndexedY, 2, 5
+

     registerOperation: (operationCode, instruction, addressingMode, size, cycles) ->
         @operationsTable[operationCode] =
@@ -116,16 +157,17 @@
         @registerY = 0       # 8-bit

     resetFlags: ->
-        @carryFlag = 0
-        @zeroFlag = 0
-        @interruptDisable = 1
-        @decimalMode = 0
-        @breakCommand = 1
-        @overflowFlag = 0
-        @negativeFlag = 0
+        @carryFlag = 0        # bit 0
+        @zeroFlag = 0         # bit 1
+        @interruptDisable = 1 # bit 2
+        @decimalMode = 0      # bit 3
+        @breakCommand = 1     # bit 4
+        @overflowFlag = 0     # bit 6
+        @negativeFlag = 0     # bit 7

     resetVariables: ->
         @pageCrossed = false
+        @branchTaken = false
         @requestedInterrupt = null

     resetMemory: ->
@@ -142,10 +184,14 @@
         @checkInterrupt()
         operation = @readOperation
         address = @computeAddress operation.addressingMode
+        @executeInstruction operation.instruction address
+        @programCounter += 1 if not @branchTaken
         cycles = operation.cycles
         cycles += 1 if @pageCrossed
+        cycles += 1 if @branchTaken
         @pageCrossed = false
-        @programCounter += cycles
+        @branchTaken = false
+        cycles

     checkInterrupt: ->
         if requestedInterrupt? and not @interruptDisable


welcome: updated cta link

welcome: updated cta link

--- client/welcome/lib/boxes.coffee
+++ client/welcome/lib/boxes.coffee
@@ -20,7 +20,7 @@

       Skip this
-      START
+      START

     """


import assert instead of expect

import assert instead of expect

--- test/index.coffee
+++ test/index.coffee
@@ -1,5 +1,5 @@
 main   = require('..');
-assert = require('chai').expect
+assert = require('chai').assert

 describe 'testing', ->


naming consistency in tests

naming consistency in tests

--- test/index.coffee
+++ test/index.coffee
@@ -7,16 +7,17 @@
     assert.equal typeof OrderedMap, "function"

   it "can could be called with `new` I guess, but don't expect `instanceof` coherence", ->
-    ordered_map = new OrderedMap()
-    assert.equal ordered_map instanceof OrderedMap, false
+    instance = new OrderedMap()
+    assert.equal instance instanceof OrderedMap, false

   it "can be called without `new`, and without exposing globals, because JS", ->
     num_globals = Object.keys(global).length
-    ordered_map = OrderedMap()
+    instance = OrderedMap()
     assert.equal num_globals, Object.keys(global).length

 describe "Methods", ->
   instance = undefined
+
   beforeEach ->
     instance = OrderedMap()

@@ -25,23 +26,23 @@
       assert.equal typeof instance.push, "function"

     it "adds a key-value pair to the tail", ->
-      instance.push "ABC", "DEF"
-      assert.equal instance.size(), 1
-      assert.equal instance.tail().key, "ABC"
-      assert.equal instance.tail().value, "DEF"
-      instance.push "GHI", "JKL"
-      assert.equal instance.size(), 2
-      assert.equal instance.tail().key, "GHI"
-      assert.equal instance.tail().value, "JKL"
+      instance.push 'abc', 'def'
+      assert.equal instance.size(), 1
+      assert.equal instance.tail().key, 'abc'
+      assert.equal instance.tail().value, 'def'
+      instance.push 'ghi', 'jkl'
+      assert.equal instance.size(), 2
+      assert.equal instance.tail().key, 'ghi'
+      assert.equal instance.tail().value, 'jkl'

     it "wires up .next properly", ->
-      instance.push "ABC", "DEF"
+      instance.push 'abc', 'def'
       prior_tail = instance.tail()
-      instance.push "GHI", "JKL"
+      instance.push 'ghi', 'jkl'
       assert.equal instance.tail().prev, prior_tail

     it "adds the pair at the head if no head existed previously", ->
-      instance.push "ABC", "DEF"
+      instance.push 'abc', 'def'
       assert.equal instance.head(), instance.tail()

   describe "#pop", ->


test: clearer pair sorting

test: clearer pair sorting

--- test/index.coffee
+++ test/index.coffee
@@ -101,11 +101,12 @@
   ]
 ]

+# For consistency, sort pairs by the values of the keys of the pairs, in alphabetical order
 sort_pairs = (pairs) ->
   _(pairs).sortBy (pair) ->
-    [left, right] = pair
-    [left_keys, right_keys] = _(pair).map (el = {}) -> _.keys el # el can be null
-    (left[key] for key in left_keys.sort()).concat(right[key] for key in right_keys.sort()).join ''
+    _(pair).chain()
+      .map((el={}) -> (el[key] for key in _(el).keys().sort()))
+      .flatten().value().join ''

 describe 'joins', ->
   _(configs).each (config, i) ->


Remove quick-test-or-trial, it's not used.

--- js/ab_test_definitions.coffee
+++ js/ab_test_definitions.coffee
@@ -7,7 +7,6 @@

 exports.ab_test_definitions =
   options:
-    quick_setup_or_trial: ["14-day free trial.", "Run your first test with 3 clicks."]
     github_warning_modal: [true, false]
     home_red_buttons: [true, false]
     home_text_hero: [true, false]


Started rendering code.

Started rendering code.

--- js/binding/src/binding.coffee
+++ js/binding/src/binding.coffee
@@ -16,7 +16,7 @@
 				child.merge(dom.children())
 			null
 		rest = doms.filter(":not([id])")
-		#[id]s removed, replace kids
+		#[id]s handled, replace kids
 		if rest.length
 			this.children(":not([id])").remove()
 			this.append(rest)
@@ -24,9 +24,6 @@
 ).call(this, jQuery))

 (((_, $, JEFRi)->
-	# A deferred to handle Binding's ready state.
-	ready = $.Deferred()
-
 	# Global Binding settings.
 	settings = {
 		paths : {
@@ -36,7 +33,11 @@
 	}

 	# Detached DOM node to hold templates.
-	template = $("

");
+	template = $()
+	_clear = () ->
+		template = $("

");
+		template.clear = _clear
+		true

 	# Add a new root block to our templates.
 	mergeTemplate = (html) ->
@@ -47,11 +48,12 @@
 		templates = if _.isArray(templates) then templates else Array.prototype.slice.call(arguments)
 		# Load each template
 		templates[i] = $.get(T) for T, i in templates
-		$.when.apply(null, templates).done () ->
+		_.when.apply(null, templates).done(() ->
 			# When behaves differently if there are 1 or 2+ args.
 			args = if templates.length is 1 then [arguments] else arguments
 			mergeTemplate tmpl[0] for tmpl in args
 			ready.resolve()
+		).promise()

 	# Finders to coalesce different templates into a single hierarchical system
 	find = (path) ->
@@ -102,19 +104,38 @@
 			_view.clone()
 	})

+	# The renderer returns the built and bound DOM for a JEFRi renderable thing.
+	render = (thing) ->
+		if JEFRi.isEntity(thing)
+			render.entity(thing)
+		else
+			render.page(thing)
+
+	_.extend(render, {
+		page: (page) ->
+			find(".._page")
+
+		entity: (entity) ->
+			find(".." + entity._type())
+
+		property: (type, name, property) ->
+			find(".." + type + "." + name)
+	})
+
 	init = (options) ->
-		$.extend(true, settings, options)
-		loadTemplates(settings.templates).done(->ready.resolve())
+		_clear()
+		_.extend(true, settings, options)
+		loadTemplates(settings.templates).done().promise()

 	JEFRi.Binding = {
-		ready: ready.promise()
 		init: init
 		templates: () -> template
 		loadTemplates: loadTemplates
 		settings: settings
 		find: find
+		render: render
 	}

 	return

-).call(this, _, jQuery, JEFRi))
+).call(this, _, jQuery || null, JEFRi))


LocalStore enhancements and features.

LocalStore enhancements and features.

--- js/core/src/LocalStore.coffee
+++ js/core/src/LocalStore.coffee
@@ -1,3 +1,9 @@
+#     JEFRi LocalStore.coffee 0.1.0
+#     (c) 2011-2012 David Souther
+#     JEFRi is freely distributable under the MIT license.
+#     For full details and documentation:
+#     http://jefri.org
+
 ( ->
 	root = @

@@ -7,30 +13,136 @@
 		constructor: (options) ->
 			@settings = { version: "1.0", size: Math.pow(2, 16) }
 			_.extend(@settings, options)
+			if not @settings.runtime
+				throw {message: "LocalStore instantiated without runtime to reference."}

+		# ### execute*(type, transaction)*
+		# Run the transaction.
 		execute: (type, transaction) ->
 			transactionEvent = JSON.parse(transaction.toString())
 			_(@).trigger('sending', type, 'localStorage:', transactionEvent, @)
 			if (type == "persist")
-				persist(transaction)
+				@persist(transaction)
 			else if (type == "get")
-				get(transaction)
-			_.Deferred().resolve(transactionEvent);
+				@get(transaction)
+			_.Deferred().resolve(transaction);

-		persist = (transaction) ->
-			transaction.entities = (_save(entity) for entity in transaction.entities)
+		# ### persist*(transction)*
+		# Treat the transaction as a persistence call. Save the data.
+		persist: (transaction) ->
+			transaction.entities = (@_save(entity) for entity in transaction.entities)

-		_save = (entity) ->
-			entity = _.extend({}, entity, _find(entity))
-			localStorage[_key(entity)] = entity;
+		# ### _save*(entity)*
+		# Save the data in the browser's localStorage.
+		_save: (entity) ->
+			# Merge the new data over the old data.
+			entity = _.extend({}, @_find(entity), entity)
+			# Store the JSON of the entity.
+			localStorage[_key(entity)] = JSON.stringify(entity)
+			# Register the entity with the type map.
+			_type(entity._type(), entity.id());
+			# Return the bare encoded object.
+			entity._encode()

-		get = (transaction) ->
-			entities = (_find(entity) for entity in transaction.entities)
+		# ### get*(transaction)*
+		# Treat the transaction as a lookup. Find all data matching the specs.
+		get: (transaction) ->
+			# Let _lookup handle the actual lookups. Each spec is an `or` op, so flatten then remove duplicates.
+			transaction.entities = _.uniq(
+				# The lookup
+				_.flatten((@_lookup(entity) for entity in transaction.entities)),
+				false,
+				# Uniq based on type.id
+				(e) => e._type + '.' + e[@settings.runtime.definition(e._type).key]
+			)
+			# Put the entities back in the runtime
+			@settings.runtime.expand(transaction, "gotten")
+			# Fire events
+			_.trigger(transaction, 'gotten')
+			transaction

-		_find = (entity) ->
-			localStorage[_key(entity)] || {}
+		# ### _find*(entity)*
+		# Return an entity directly, or pass a spec to _lookup.
+		_find: (entity) ->
+			if _.isEntity(entity)
+				JSON.parse(localStorage[_key(entity)] || "{}")
+			else
+				@_lookup(entity)

-		_key = (entity) -> entity.id(true)
+		# ### _lookup*(spec)*
+		# Given a transaction spec, pull all entities (including relationships) that match.
+		# See JEFRi Core documentation 5.1.1 Gory Get Details for rules.
+		_lookup: (spec) ->
+			# Need the key, properties, and relationships details
+			def = @settings.runtime.definition(spec._type)
+			# Get everything for this type
+			results = (JSON.parse(localStorage[spec._type + "." + id]) for id in _.keys(_type(spec._type)))
+
+			# Start immediately with the key to pear down results quickly. Rule 1.
+			if key of spec
+				results = [results[spec[key]]]
+
+			# Filter based on property specifications
+			_.each def.properties, (property, name) =>
+				if name of spec and name isnt key
+					results = _(results).filter(_seive(name, property, spec[name]))
+
+
+			results
+
+		_type = (type, id=null) ->
+			list = JSON.parse(localStorage[type] || "{}");
+			if id
+				list[id] = ""
+				localStorage[type] = JSON.stringify(list)
+			list
+
+		_key = (entity) ->
+			entity._type() + "." + entity.id()
+
+		_seive = (name, property, spec) ->
+			# Normalize rules 2 and 3 to operator array
+			if _.isNumber(spec)
+				if spec % 1 is 0
+					spec = ['=', spec]
+				else
+					spec = [spec, 8]
+
+			# Rule 4, string behaves as SQL "Like"
+			if _.isString(spec)
+				spec = ['REGEX', '.*' + spec + '.*']
+
+			# Spec should be an array by now, if it isn't, there's a problem.
+			if not _.isArray(spec)
+				throw { message: "Specification is invalid.", name: name, property: property, spec: spec}
+
+			# Rule 3, only floats are allowed in operator position
+			if _.isNumber(spec[0])
+				return (entity) ->
+					Math.abs(entity[name] - spec[0]) < Math.pow(2, -spec[1])
+
+			# Rule 8, AND specs.
+			if _.isArray(spec[0])
+				spec[i] = _seive(name, property, spec[i]) for s, i in spec
+				return (entity) ->
+					for filter in spec
+						if not filter(entity)
+							return false
+					return true
+
+			# Rule 6, several valid operators.
+			switch spec[0]
+				when "=" then return (entity) -> entity[name] == spec[1]
+				when "<=" then return (entity) -> entity[name] <= spec[1]
+				when ">=" then return (entity) -> entity[name] >= spec[1]
+				when "<" then return (entity) -> entity[name] < spec[1]
+				when ">" then return (entity) -> entity[name] > spec[1]
+				when "REGEX" then return (entity) -> ("" + entity[name]).match(spec[1])
+				# Rule 7, IN list
+				else return (entity) ->
+					while field = spec.shift
+						if entity[name] is field
+							return true

 	root.JEFRi.LocalStore = LocalStore
 )();


resurrection.activity: remove back link from status update content display

--- client/Social/Activity/ContentDisplays/StatusUpdate.coffee
+++ client/Social/Activity/ContentDisplays/StatusUpdate.coffee
@@ -18,6 +18,5 @@

   pistachio:->
     """
-
{{> @back}}

     {{> @activityItem}}
     """


managedkitechecker: Fixed plan allowances key

--- client/app/lib/providers/managed/managedkitechecker.coffee
+++ client/app/lib/providers/managed/managedkitechecker.coffee
@@ -265,7 +265,7 @@

       { plan, plans, usage } = planCombo

-      hasAllowances = usage.total < plans[plan].total
+      hasAllowances = usage.total < plans[plan].managed
       callback hasAllowances


Wait for the reporter to be marked as finished, so we get a proper result in a node now.

--- js/tests/circle-runner.coffee
+++ js/tests/circle-runner.coffee
@@ -1,13 +1,16 @@
 process = () =>
-  results = @JasmineReporter.results()
-  json = JSON.stringify(results)
+  if @JasmineReporter.finished
+    results = @JasmineReporter.results()
+    json = JSON.stringify(results)

-  # attach it to the DOM, so the it can be retrieved by webdriver
-  element = @document.createElement("div")
-  text = @document.createTextNode(json)
-  element.appendChild text
-  element.id = "jasmine-results"
-  @document.body.appendChild element
+    # attach it to the DOM, so the it can be retrieved by webdriver
+    element = @document.createElement("div")
+    text = @document.createTextNode(json)
+    element.appendChild text
+    element.id = "jasmine-results"
+    @document.body.appendChild element
+  else
+    setTimeout process, 100

 try
   @jasmine.getEnv().execute()


fixed indentation errors #795

fixed indentation errors #795

--- app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
+++ app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
@@ -12,70 +12,70 @@
   template : _.template("""

-

-

-
<%- getTitle() %> TaskType

-

+

+

+
<%- getTitle() %> TaskType

+

-	  <form method="POST" class="form-horizontal">
+          <form method="POST" class="form-horizontal">

-	      Summary
+              Summary

-	      <input type="text" id="summary" name="summary" value="<%- summary %>" class="form-control"
+              <input type="text" id="summary" name="summary" value="<%- summary %>" class="form-control"
                  required pattern=".{3,50}" title="Please use at least 3 characters.">

-	      Team
+              Team

-	      Description
+              Description

-	      <textarea id="description" name="description" class="form-control" value="<%- description %>"></textarea>
-

-

-
-

-	      Allowed Modes
-

-		<select multiple="multiple" name="settings[allowedModes[]]" class="form-control">
-		<% ["flight", "orthogonal", "oblique"].forEach(function(mode) { %>
-		  <option value="<%-mode %>" <%- isSelected(_.contains(settings.allowedModes, mode)) %>> <%- mode %> </option>
-		<% }) %>
-		</select>
-

-

-
-

-	      Settings
-

-
-

-		  <input type="checkbox" id="somaClickingAllowed" name="settings[somaClickingAllowed"] <%- isChecked(settings.somaClickingAllowed) %>>
-

-		Allow Soma clicking
-
-

-		  <input type="checkbox" id="branchPointsAllowed" name="settings[branchPointsAllowed"] <%- isChecked(settings.branchPointsAllowed) %>>
-

-		Allow Branchpoints
-
-

-		  <input type="checkbox" id="advancedOptionsAllowed" name="settings[advancedOptionsAllowed]" <%- isChecked(settings.advancedOptionsAllowed) %>>
-

-		Advanced Tracing Options
+              <textarea id="description" name="description" class="form-control" value="<%- description %>"></textarea>

-	      Preferred Mode
-

+              Allowed Modes
+

+                <select multiple="multiple" name="settings[allowedModes[]]" class="form-control">
+                <% ["flight", "orthogonal", "oblique"].forEach(function(mode) { %>
+                  <option value="<%-mode %>" <%- isSelected(_.contains(settings.allowedModes, mode)) %>> <%- mode %> </option>
+                <% }) %>
+                </select>
+

+

+
+

+              Settings
+

+
+

+                  <input type="checkbox" id="somaClickingAllowed" name="settings[somaClickingAllowed"] <%- isChecked(settings.somaClickingAllowed) %>>
+

+                Allow Soma clicking
+
+

+                  <input type="checkbox" id="branchPointsAllowed" name="settings[branchPointsAllowed"] <%- isChecked(settings.branchPointsAllowed) %>>
+

+                Allow Branchpoints
+
+

+                  <input type="checkbox" id="advancedOptionsAllowed" name="settings[advancedOptionsAllowed]" <%- isChecked(settings.advancedOptionsAllowed) %>>
+

+                Advanced Tracing Options
+

+

+
+

+              Preferred Mode
+

                 <select id="preferredMode" name="preferredMode" class="form-control">
-		<option>Any</option>
+                <option>Any</option>
                   <option value="orthogonal">Orthogonal</option>
                   <option value="oblique">Oblique</option>
                   <option value="flight">Flight</option>
@@ -84,44 +84,44 @@

-	      Expected Time (min)
-

+              Expected Time (min)
+

-		<input type="number" id="expectedTime_minTime" name="expectedTime[min]"
-		  value="<%- expectedTime.min %>" min="1" input-append="minutes" class="form-control" required>
+                <input type="number" id="expectedTime_minTime" name="expectedTime[min]"
+                  value="<%- expectedTime.min %>" min="1" input-append="minutes" class="form-control" required>
                   minutes

-	      Expected Time (max)
-

+              Expected Time (max)
+

-		<input type="number" id="expectedTime_maxTime" name="expectedTime[max]"
-		  value="<%- expectedTime.max %>" min="1" input-append="minutes" class="form-control" required>
+                <input type="number" id="expectedTime_maxTime" name="expectedTime[max]"
+                  value="<%- expectedTime.max %>" min="1" input-append="minutes" class="form-control" required>
                   minutes

-	      Time limit
-

+              Time limit
+

-		<input type="number" id="expectedTime_maxHard" name="expectedTime[maxHard]"
-		  value="<%- expectedTime.maxHard %>" min="1" input-append="minutes" class="form-control" required>
+                <input type="number" id="expectedTime_maxHard" name="expectedTime[maxHard]"
+                  value="<%- expectedTime.maxHard %>" min="1" input-append="minutes" class="form-control" required>
                   minutes

-

-	      <button type="submit" class="form-control btn btn-primary"><%- getTitle() %></button>
+

+              <button type="submit" class="form-control btn btn-primary"><%- getTitle() %></button>

-	  </form>
+          </form>

@@ -174,7 +174,7 @@
       collection : new TeamCollection()
       childViewOptions :
         modelValue: -> return "#{@model.get("name")}"
-	defaultItem : {name : @model.get("team")}
+        defaultItem : {name : @model.get("team")}
       data : "amIAnAdmin=true"
       name : "team"
       required : true


Resolve plant selector values

Resolve plant selector values

--- app/assets/javascripts/utils/unroll/plant-land-parcel-selector.js.coffee
+++ app/assets/javascripts/utils/unroll/plant-land-parcel-selector.js.coffee
@@ -11,15 +11,20 @@
       PLANT_VALUE: 'plant'
       LAND_PARCEL_VALUE: 'land_parcel'

+      PLANT_UNROLL_VALUE: 'is+plant+and+has+indicator+shape'
+      LAND_PARCEL_UNROLL_VALUE: 'is+land_parcel+and+has+indicator+shape'
+
       constructor: ->

       changeUnrollUrl: (event, unrollElement, supportId = null, activityProduction = null) ->
         selectedValue = $(event.target).val()
+        unrollExpression = @PLANT_UNROLL_VALUE
+        unrollExpression = @LAND_PARCEL_UNROLL_VALUE if selectedValue == @LAND_PARCEL_VALUE

         unrollRegex = /(unroll\?.*scope.*of_expression[^=]*)=([^&]*)(&?.*)/
         unrollPath = $(unrollElement).attr('data-selector')
-        unrollPath = unrollPath.replace(unrollRegex, "$1=is+#{ selectedValue }$3")
+        unrollPath = unrollPath.replace(unrollRegex, "$1=#{ unrollExpression }$3")

         ofActivityProduction = unrollPath.includes(@ACTIVITY_PRODUCTION_PARAM_NAME)
         withId = unrollPath.includes(@WITH_ID_PARAM_NAME)


Allow non-numeric branchId

Allow non-numeric branchId

Branch Ids are the name of the branch, which is typically not an id.
Change the parseInt function to be encodeURI.

--- src/Models/ProjectRepository.coffee
+++ src/Models/ProjectRepository.coffee
@@ -9,7 +9,7 @@

   showBranch: (projectId, branchId, fn = null) =>
     @debug "Projects::branch()"
-    @get "projects/#{parseInt projectId}/repository/branches/#{parseInt branchId}", (data) => fn data if fn
+    @get "projects/#{parseInt projectId}/repository/branches/#{encodeURI branchId}", (data) => fn data if fn

   ## TODO:
   # - Protect and


- Remove dangling log

- Remove dangling log

--- client/landing/app/CommonViews/comments/commentlistviewcontroller.coffee
+++ client/landing/app/CommonViews/comments/commentlistviewcontroller.coffee
@@ -30,7 +30,6 @@

     listView.on 'ItemWasAdded', (view, index)=>
       view.on 'CommentIsDeleted', ->
-        log "I JUST EMMITED!!"
         listView.emit "CommentIsDeleted"

     listView.on "AllCommentsLinkWasClicked", (commentHeader)=>


chore: exclude from test coverage

chore: exclude from test coverage

--- it/pingpong.coffee
+++ it/pingpong.coffee
@@ -13,6 +13,8 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 #
+
+### !pragma coverage-skip-block ###

 Log = require('log')
 logger = new Log 'info'


Simplified subscriptions in the main module

--- lib/levels-debugger-ruby.coffee
+++ lib/levels-debugger-ruby.coffee
@@ -16,17 +16,18 @@

     @subscriptions = new CompositeDisposable
     @subscriptions.add atom.workspace.addOpener (uri) => @handleOpener uri
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:toggle-debugger-view': => @toggle()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:toggle-breakpoint': => @debuggerPresenter.toggleBreakpoint()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:remove-all-breakpoints': => @debuggerPresenter.removeAllBreakpoints()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:enable-disable-all-breakpoints': => @debuggerPresenter.enableDisableAllBreakpoints()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:start-debugging': => @debuggerPresenter.startDebugging()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:stop-debugging': => @debuggerPresenter.stopDebugging()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:step': => @debuggerPresenter.step()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:step-over': => @debuggerPresenter.stepOver()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:run-to-end-of-method': => @debuggerPresenter.runToEndOfMethod()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:run-to-next-breakpoint': => @debuggerPresenter.runToNextBreakpoint()
-    @subscriptions.add atom.commands.add 'atom-workspace', 'levels-debugger-ruby:stop-replay': => @debuggerPresenter.stopReplay()
+    @subscriptions.add atom.commands.add 'atom-workspace',
+      'levels-debugger-ruby:toggle-debugger-view': => @toggle()
+      'levels-debugger-ruby:toggle-breakpoint': => @debuggerPresenter.toggleBreakpoint()
+      'levels-debugger-ruby:remove-all-breakpoints': => @debuggerPresenter.removeAllBreakpoints()
+      'levels-debugger-ruby:enable-disable-all-breakpoints': => @debuggerPresenter.enableDisableAllBreakpoints()
+      'levels-debugger-ruby:start-debugging': => @debuggerPresenter.startDebugging()
+      'levels-debugger-ruby:stop-debugging': => @debuggerPresenter.stopDebugging()
+      'levels-debugger-ruby:step': => @debuggerPresenter.step()
+      'levels-debugger-ruby:step-over': => @debuggerPresenter.stepOver()
+      'levels-debugger-ruby:run-to-end-of-method': => @debuggerPresenter.runToEndOfMethod()
+      'levels-debugger-ruby:run-to-next-breakpoint': => @debuggerPresenter.runToNextBreakpoint()
+      'levels-debugger-ruby:stop-replay': => @debuggerPresenter.stopReplay()

     return


Added NormalizedRange#textNodes() to return an Array of all textNodes in the range

--- src/range.coffee
+++ src/range.coffee
@@ -130,6 +130,13 @@
       endOffset: end[1]
     })

+  textNodes: ->
+    textNodes = $(this.commonAncestor).textNodes()
+    [start, end] = [textNodes.index(this.start), textNodes.index(this.end)]
+
+    # Return the textNodes that fall between the start and end indexes.
+    $.makeArray textNodes[start..end]
+
 class Range.SerializedRange

   constructor: (obj) ->


Fix deprecated method

Fix deprecated method

--- src/Hello.cjsx
+++ src/Hello.cjsx
@@ -5,4 +5,4 @@

Hello

 )

-React.renderComponent(<Hello />, document.getElementById('component'))
+React.render(<Hello />, document.getElementById('component'))


Fixed tests for cache

Fixed tests for cache

--- test/suite.coffee
+++ test/suite.coffee
@@ -5,7 +5,7 @@
 ModelTypeID.strict = false

 option_sets = require('./option_sets')
-option_sets = option_sets.slice(0, 5)
+# option_sets = option_sets.slice(0, 5)

 queue = new Queue(1)
 for options in option_sets


fix jupyter export "source is required" bug.

--- src/smc-webapp/jupyter/export-to-ipynb.coffee
+++ src/smc-webapp/jupyter/export-to-ipynb.coffee
@@ -37,7 +37,7 @@
     metadata = {}
     obj =
         cell_type : cell.get('cell_type') ? 'code'
-        source    : diff_friendly(cell.get('input'))
+        source    : diff_friendly(cell.get('input') ? '')
         metadata  : metadata

     # Handle any extra metadata (mostly user defined) that we don't handle in a special


Remove left over debug code

Remove left over debug code

--- src/robot.coffee
+++ src/robot.coffee
@@ -150,13 +150,6 @@
         result = @brain.data.users[k]
     result

-  # Public: Update a User objects name property.
-  updateUserName: (id, name) ->
-    user = @userForId id
-    unless user.name is name
-      user.name = name
-    user
-
   # Public: Run Hubot using the loaded adapter.
   #
   # Returns nothing.


removed player speed modifications

removed player speed modifications

--- src/scene.coffee
+++ src/scene.coffee
@@ -46,7 +46,6 @@

   resetGame: ->
     @score.textContent = "0"
-    @player.reset()
     @addEnemy()

 class Controllable extends CAAT.Actor
@@ -100,15 +99,11 @@
     @setSize(30,30)
     @setFillStyle('rgb(0,0,0)')

-  boost: -> @maxSpeed += 0.1
-  reset: -> @maxSpeed = 3
-
   act: ->
     super()
     @scene.childrenList.forEach( (element) =>
       if (element != this) && (@scene.collides(this, element))
         element.caught()
-        @boost()
         @scene.incrementScore()
         @scene.addEnemy()
     )


Fix tests' require path

Fix tests' require path

--- test/tests.coffee
+++ test/tests.coffee
@@ -1,5 +1,5 @@
 assert = require 'assert'
-{parseTerm, reduceTerm, termTreeStr} = require '../lib/lambda'
+{parseTerm, reduceTerm, termTreeStr} = require '../src/lambda'

 shouldParse = (expr, expected) ->
   assert.strictEqual (parseTerm expr), expected


Add HUBOT_LIVE check back into test framework

--- test/tests.coffee
+++ test/tests.coffee
@@ -54,8 +54,9 @@
     user = new Robot.User 1, 'helper'
     super new Robot.TextMessage user, text

-class Helper.Response extends Robot.Response
-  # This changes ever HTTP request to hit the danger server above
-  http: (url) ->
-    super(url).host('127.0.0.1').port(9001)
+if not process.env.HUBOT_LIVE
+  class Helper.Response extends Robot.Response
+    # This changes ever HTTP request to hit the danger server above
+    http: (url) ->
+      super(url).host('127.0.0.1').port(9001)


Added test for Utils.isLocalAssetUrl

Added test for Utils.isLocalAssetUrl

We need to make sure this doesn't respond with true so we should make sure we check it.

--- test/tests/UtilsTest.coffee
+++ test/tests/UtilsTest.coffee
@@ -431,9 +431,11 @@

 	describe "isLocalAssetUrl", ->
 		it "should work", ->
+			dataUrl = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
 			Utils.isLocalAssetUrl("Desktop/index.html", "http://localhost/index.html").should.equal(true)
 			Utils.isLocalAssetUrl("/Desktop/index.html", "http://localhost/index.html").should.equal(true)
 			Utils.isLocalAssetUrl("Desktop/index.html", "http://127.0.0.1/index.html").should.equal(true)
 			Utils.isLocalAssetUrl("Desktop/index.html", "http://apple.com/index.html").should.equal(false)
 			Utils.isLocalAssetUrl("file:///Desktop/index.html", "http://apple.com/index.html").should.equal(true)
 			Utils.isLocalAssetUrl("http://apple.com/index.html", "http://127.0.0.1/index.html").should.equal(false)
+			Utils.isLocalAssetUrl("Desktop/index.html", dataUrl).should.equal(false)


only make ./install an executable

only make ./install an executable

--- src/wrap.coffee
+++ src/wrap.coffee
@@ -42,6 +42,7 @@
                 gname:'nogroup'
                 uid: 1000
                 gid: 1000
+                mode: 0o0666
         @tarball.on('error', @emit.bind(this, 'error'))
         @tarball.setMaxListeners(0)

@@ -94,7 +95,7 @@
             src.push("# go back where we came from")
             src.push("cd $cwd")
             src.push("echo 'done.'")
-            entry = @append("install", src.join("\n"))
+            entry = @append("install", src.join("\n"), mode:0o0777)
             entry.name = "main"
         if @working
             @ending = yes
@@ -140,6 +141,7 @@
                 opts.size = new Buffer(content).length
             @emit('error', "no size for #{file}") unless opts.size?
         stream.props = size:opts.size
+        stream.props.mode = opts.mode if opts.mode?
         @_push (done) ->
             return done() if @skip[entry.name]
             @emit('append', stream, entry)


dont print anymore when evaluating expressions

--- src/wrap.coffee
+++ src/wrap.coffee
@@ -19,19 +19,19 @@
         dir = dn
         dn = path.dirname(dir)
     return dir
-eval_ = (expressions, ctx) ->
-    res = []
-    console.error("Expressions in require() statements in #{ctx.__filename}")
+evalExpressions = (expressions, ctx) ->
+    [res, haserr] = [[], no]
+    output = ["Expressions in require() statements in #{ctx.__filename}"]
     for ex in expressions
         # lets try to eval the exp before we completely give up
         try # veryCrude™
             ev = eval("with(ctx){#{ex}}", ctx)
-            if typeof ev is 'string'
-                res.push(ev)
-            else
-                console.error("    require(#{ex})  #{JSON.stringify ev}")
+            res.push(ev) if typeof ev is 'string'
+            output.push("    `#{ex}` → #{JSON.stringify ev}")
         catch err
-            console.error("    require(#{ex})  #{err?.message or ""}")
+            output.push("    require(#{ex})  #{err?.message or ""}")
+            haserr = yes
+    @emit(haserr and 'error' or 'warn', output.join("\n"))
     return res

@@ -208,7 +208,7 @@
             process.nextTick( => @emit('syntaxError', err))
             return this
         if required.expressions.length
-            exps = eval_ required.expressions,
+            exps = evalExpressions.call this, required.expressions,
                 process:process
                 __dirname:dirname
                 __filename:file
@@ -289,7 +289,7 @@
             process.nextTick( => @emit('syntaxError', err))
             return this
         if required.expressions.length
-            exps = eval_ required.expressions,
+            exps = evalExpressions.call this, required.expressions,
                 process:process
                 __dirname:dirname
                 __filename:opts.file


Some class update fix

Some class update fix

--- app/assets/javascripts/channels/cluster.coffee
+++ app/assets/javascripts/channels/cluster.coffee
@@ -13,7 +13,7 @@
     else
       $(data.uuid).html("<strong>#{data.state}</strong>")

-    if date.state.downcase.include? 'ready'
+    if data.state.downcase.include? 'ready'
       $(data.uuid).attr("class", "text-success")
-    if date.state.downcase.include? 'fail'
+    if data.state.downcase.include? 'fail'
       $(data.uuid).attr("class", "text-danger")


Styled box color depending on who's leading

--- app/assets/javascripts/components/users.coffee
+++ app/assets/javascripts/components/users.coffee
@@ -12,6 +12,12 @@
       "Good job! Both are at the their target weights."
     else
       "Both are equally close to their target weight."
+  diffType: (jias, chloes, jiasTarget, chloesTarget, uId) ->
+    if (Math.abs(jias - jiasTarget) > Math.abs(chloes - chloesTarget) && uId == 1) ||
+       (Math.abs(jias - jiasTarget) < Math.abs(chloes - chloesTarget) && uId == 2)
+      "danger"
+    else
+      "success"
   render: ->
     React.DOM.div
       className: 'users'
@@ -20,9 +26,9 @@
         'Weight Monitor'
       React.DOM.div
         className: 'row'
-      React.createElement AmountBox, type: 'success', content: @state.users[0].weights[0].entry + "kg" + " (" + (@state.users[0].weights[0].entry - @state.users[0].targetWeight).toFixed(1)+ "kg off target)", text: "#{@state.users[0].name} (target: #{@state.users[0].targetWeight}kg)"
+      React.createElement AmountBox, type: @diffType(@state.users[0].weights[0].entry, @state.users[1].weights[0].entry, @state.users[0].targetWeight, @state.users[1].targetWeight, 1), content: @state.users[0].weights[0].entry + "kg" + " (" + (@state.users[0].weights[0].entry - @state.users[0].targetWeight).toFixed(1)+ "kg off target)", text: "#{@state.users[0].name} (target: #{@state.users[0].targetWeight}kg)"
       React.createElement AmountBox, type: 'info', content: @diff(@state.users[0].weights[0].entry, @state.users[1].weights[0].entry, @state.users[0].targetWeight, @state.users[1].targetWeight), text: 'Leader'
-      React.createElement AmountBox, type: 'danger', content: @state.users[1].weights[0].entry + "kg" + " (" + (@state.users[1].weights[0].entry - @state.users[1].targetWeight).toFixed(1)+ "kg off target)", text: "#{@state.users[1].name} (target: #{@state.users[1].targetWeight}kg)"
+      React.createElement AmountBox, type: @diffType(@state.users[0].weights[0].entry, @state.users[1].weights[0].entry, @state.users[0].targetWeight, @state.users[1].targetWeight, 2), content: @state.users[1].weights[0].entry + "kg" + " (" + (@state.users[1].weights[0].entry - @state.users[1].targetWeight).toFixed(1)+ "kg off target)", text: "#{@state.users[1].name} (target: #{@state.users[1].targetWeight}kg)"
       React.DOM.hr null
       React.DOM.table
         className: 'table table-bordered'


OHM-419: Fix alert tests

OHM-419: Fix alert tests

--- test/unit/alertsTest.coffee
+++ test/unit/alertsTest.coffee
@@ -101,90 +101,90 @@
   # 0
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa0'
-    route: 'primary'
     event: 'end'
     status: 404
+    type: 'channel'

   # 1
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa1'
-    route: 'route'
     event: 'end'
     status: 404
+    type: 'channel'

   # 2
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa2'
-    route: 'primary'
     event: 'end'
     status: 400
+    type: 'channel'

   # 3
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa3'
-    route: 'primary'
     event: 'end'
     status: 500
+    type: 'channel'

   # 4
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa4'
-    route: 'primary'
     event: 'end'
     status: 500
+    type: 'channel'

   # 5
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa5'
-    route: 'primary'
     event: 'end'
     status: 500
+    type: 'channel'

   # 6
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa6'
-    route: 'primary'
     event: 'end'
     status: 404
+    type: 'channel'

   # 7
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa7'
-    route: 'primary'
     event: 'end'
     status: 404
+    type: 'channel'

   # 8
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa8'
-    route: 'primary'
     event: 'end'
     status: 500
     autoRetryAttempt: 2
+    type: 'channel'

   # 9
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa9'
-    route: 'primary'
     event: 'end'
     status: 500
     autoRetryAttempt: 3
+    type: 'channel'

   # 10 - channel event for 9
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa9'
-    route: 'channel'
     event: 'end'
     status: 500
     autoRetryAttempt: 3
+    type: 'channel'

   # 11
   new Event
     transactionID: 'aaa908908bbb98cc1daaaaa9'
-    route: 'primary'
     event: 'end'
     status: 200
     autoRetryAttempt: 3
+    type: 'channel'
 ]

 dateFrom = new Date()


Added CO2 emissions biomass to chart types

--- app/assets/javascripts/lib/models/chart.coffee
+++ app/assets/javascripts/lib/models/chart.coffee
@@ -62,6 +62,8 @@
       when 'block'                  then BlockChartView
       when 'co2_emissions'
         if d3_support then D3.co2_emissions.View else CO2EmissionsChartView
+      when 'co2_emissions_biomass'
+        if d3_support then D3.co2_emissions_biomass.View else CO2EmissionsChartView
       when 'html_table'             then @table_view_factory()
       when 'scatter'
         if d3_support then D3.scatter.View else ScatterChartView


Set debug false in webshims

Set debug false in webshims

--- app/assets/javascripts/service_versions.coffee
+++ app/assets/javascripts/service_versions.coffee
@@ -16,6 +16,7 @@
     editors[location].setValue("{}")
   setConsoleBtnOptions('#btns-service-console li a:first')
   convertAllFormsToJSON()
+  webshims.setOptions({debug: false})
   webshims.setOptions('forms-ext', {types: 'datetime-local time date number month range'})
   webshims.polyfill('forms forms-ext')


_runner rendering latex table

_runner rendering latex table

--- queries/_runner.coffee
+++ queries/_runner.coffee
@@ -1,18 +1,72 @@
 walk = require 'walk'
 async = require 'async'
+_ = require "underscore"

 queryDirs = ['combined', 'individual']

+longtableHeadText = "-- continued from previous page"
+longtableFootText = "Continued on next page"
+
+objectToTable = (opts) ->
+  data = opts.data
+  columns = opts.columns
+
+  aligns = _.pluck columns, "align"
+  columnAligns = "| " + aligns.join(" | ") + " |"
+
+  preamble = "\\begin{center}\\begin{longtable}{ #{columnAligns} }\\hline"
+
+  for column, i in columns
+    align = if i is 0 then "|c|" else "c|"
+    preamble += "\\multicolumn{1}{#{align}}{\\textbf{#{column.title}}}"
+    preamble += " & " unless i is columns.length - 1
+
+  preamble += "\\\\ \\hline \\endfirsthead"
+
+  longtable = "
+    \\multicolumn{#{columns.length}}{c}%
+    {{\\bfseries \\tablename\ \\thetable{} #{longtableHeadText}}} \\\\
+    \\hline "
+
+  for column, i in columns
+    align = if i is 0 then "|c|" else "c|"
+    longtable += "\\multicolumn{1}{#{align}}{\\textbf{#{column.title}}}"
+    longtable += " & " unless i is columns.length - 1
+
+  longtable += "
+    \\\\ \\hline \\endhead
+    \\hline \\multicolumn{#{columns.length}}{|r|}{{#{longtableFootText}}} \\\\ \\hline
+    \\endfoot \\hline \\hline \\endlastfoot"
+
+  body = "\\hline"
+  console.log "data: ", data
+  for row in data
+    console.log "row: ", row
+    for column, j in columns
+      console.log "j: ", j
+      console.log "column: ", column
+      body += row[column.property]
+      body += if j is columns.length - 1 then " \\" else " & "
+    body += "\\hline"
+
+  footer = "\\end{longtable} \\end{center}"
+
+  fullTable = preamble + longtable + body + footer
+  return fullTable
+
 async.forEachSeries queryDirs, (dir, forEachNext) ->
-  console.log "query dir: #{dir}"
   walker = walk.walk "#{__dirname}/#{dir}"

   walker.on "file", (root, stats, next) ->
     source = "#{root}/#{stats.name}"
-    console.log "source: ", source
     query = require source
     query.run (err, rows) ->
+      latexTable = objectToTable
+        data: rows
+        columns: query.columns
+
       console.log "rows: ", rows
+      console.log "latexTable: ", latexTable
       next()

   walker.on "end", ->


move clear button

move clear button

--- lib/python-debugger-view.coffee
+++ lib/python-debugger-view.coffee
@@ -40,8 +40,8 @@
         @span "run"
       @button outlet: "stopBtn", click: "stopApp", class: "btn", =>
         @span "stop"
-      @button outlet: "clearBtn", click: "clearOutput", class: "btn", =>
-        @span "clear"
+      @button class: "btn", =>
+        @span "        "
       @button outlet: "stepOverBtn", click: "stepOverBtnPressed", class: "btn", =>
         @span "next"
       @button outlet: "stepInBtn", click: "stepInBtnPressed", class: "btn", =>
@@ -50,6 +50,8 @@
         @span "continue"
       @button outlet: "returnBtn", click: "returnBtnPressed", class: "btn", =>
         @span "return"
+      @button outlet: "clearBtn", click: "clearOutput", class: "btn", =>
+        @span "clear"
       @div class: "panel-body", outlet: "outputContainer", =>
         @pre class: "command-output", outlet: "output"


add new test case

add new test case

--- lib/smart-daemon-control.coffee
+++ lib/smart-daemon-control.coffee
@@ -79,3 +79,4 @@
   # atom call: save current daemonItems state
   serialize: ->
     @daemonItemCollection.serialize()
+    # return undefined -> to test reset state


Update photos.coffee

Update photos.coffee

--- coffee/models/photos.coffee
+++ coffee/models/photos.coffee
@@ -5,11 +5,10 @@
   'use strict'

   class Photos extends Model
-    urlRoot:"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=100fdf7552196490a96bea6ae20d6096&tags=cat&format=json&nojsoncallback=1&api_sig=1ccc53d1cbea10e25098ad252502990c"
+    urlRoot:"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=7d84a9ffdf7d39e9f86da709ae558cbf&tags=cat&format=json&nojsoncallback=1&api_sig=d30962aea5ca5d819cb7eda96f21f7a3"

-    defaults:
-      message: 'ey!'
+    #parse:(response)->
+      #console.log "entra aqui"
+      #console.log response
+      #return response.photos.photo

-    parse:(response)->
-      return response.photo
-


util.test: test that a view calls super

util.test: test that a view calls super

--- coffee/src/util/test.coffee
+++ coffee/src/util/test.coffee
@@ -139,6 +139,13 @@
     it 'should have render return @', ->
       view = new View(options)
       expect(view.render()).toBe view
+
+    it 'should be rendering after render (i.e. should call super)', ->
+      view = new View(options)
+      expect(view.rendering).toBe false
+
+      view.render()
+      expect(view.rendering).toBe true

     tests?()


debug flag was set incorrectly

debug flag was set incorrectly

--- lib/web/views/repository.coffee
+++ lib/web/views/repository.coffee
@@ -25,7 +25,7 @@
   router.add("/repository", (params) -> show_components( ))
   router.fallbackRoute = (url) -> alert("Unknown route: " + url);
   router.hashBaseUrl="/repository"
-  router.pushStateSupport = false
+#  router.pushStateSupport = false
 #  router.debug = true
   router.initPushState()
   window.router = router


Remove debug code

Remove debug code

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -88,7 +88,6 @@
       return response

     initialize: (options = {}) ->
-      window.x = @
       @set('toc', [])
       @fetch
         success: () => @load(options.page)


code cleanup

code cleanup

--- src/scripts/slick-lightbox.coffee
+++ src/scripts/slick-lightbox.coffee
@@ -40,18 +40,23 @@

   createModalItems: ->
     ### Creates individual slides to be used with slick. If `options.images` array is specified, it uses it's contents, otherwise loops through elements' `options.itemSelector`. ###
-    # lazyPlaceholder = @options.lazyPlaceholder ||
-    itemTemplate = (source, lazy, lazyPlaceholder) ->
-      lazyPlaceholder = lazyPlaceholder || 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
+    lazyPlaceholder = @options.lazyPlaceholder || 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
+
+    itemTemplate = (source, lazy) ->
       if lazy == true
         imgSourceParams = """ data-lazy="#{ source }" src="#{ lazyPlaceholder }" """
       else
         imgSourceParams = """ src="#{ source }" """
-      """

"""
+      """
+

+

+
+

+
"""

     if @options.images
       links = $.map @options.images, (img) ->
-        itemTemplate(img, @options.lazy, @options.lazyPlaceholder)
+        itemTemplate(img, @options.lazy)

     else
       $items = @filterOutSlickClones @$element.find(@options.itemSelector)
@@ -63,7 +68,7 @@
           length: length
         caption = @getElementCaption(el, info)
         src = @getElementSrc(el)
-        itemTemplate(src, @options.lazy, @options.lazyPlaceholder)
+        itemTemplate(src, @options.lazy)

       links = $.map $items, createItem
     links
@@ -88,8 +93,9 @@
   initSlick: (index) ->
     ### Runs slick by default, using `options.slick` if provided. If `options.slick` is a function, it gets fired instead of us initializing slick. Merges in initialSlide option. ###
     # We need to start with the `index`-th item.
-    additional =
-      initialSlide: index
+    additional = { initialSlide: index }
+    additional.lazyLoad = 'ondemand' if @options.lazy
+
     if @options.slick?
       if typeof @options.slick is 'function'
         # TODO: support element's index
@@ -256,6 +262,7 @@
     closeButton: """<button type="button" class="slick-lightbox-close"></button>"""
   shouldOpen: null
   imageMaxHeight: 0.9
+  lazy: false

 # jQuery methods
 $.fn.slickLightbox = (options) ->


show an error if not collaborator

show an error if not collaborator

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -1994,14 +1994,24 @@
                 style   = {error_style}
                 onClose = {=>@props.actions.setState(error:'')} />

-    render_not_public_error: ->
-        if @props.redux.getStore('account').is_logged_in()
-            <ErrorDisplay title="Directory is not public" error={"You are trying to access a non public project that you are not a collaborator on. You need to ask a collaborator of the project to add you."} />
-        else
-

-                <ErrorDisplay title="Directory is not public" error={"You are not logged in. If you are collaborator on this project you need to log in first. This project is not public."} />
-                <AccountPage />
-

+    render_not_collaborator_error: ->
+        public_view = @props.get_my_group(@props.project_id) == 'public'
+        if public_view
+            if @props.redux.getStore('account').is_logged_in()
+                <ErrorDisplay title="Showing only public files" error={"You are trying to access a project that you are not a collaborator on. To view non-public files or edit files in this project you need to ask a collaborator of the project to add you."} />
+            else
+

+                    <ErrorDisplay title="Showing only public files" error={"You are not logged in. To view non-public files or edit files in this project you need to ask a collaborator of the project to add you."} />
+                    <AccountPage />
+

+        else
+            if @props.redux.getStore('account').is_logged_in()
+                <ErrorDisplay title="Directory is not public" error={"You are trying to access a non public project that you are not a collaborator on. You need to ask a collaborator of the project to add you."} />
+            else
+

+                    <ErrorDisplay title="Directory is not public" error={"You are not logged in. If you are collaborator on this project you need to log in first. This project is not public."} />
+                    <AccountPage />
+

     render_file_listing: (listing, file_map, error, project_state, public_view) ->
         if project_state? and project_state not in ['running', 'saving']
@@ -2012,7 +2022,7 @@
             quotas = @props.get_total_project_quotas(@props.project_id)
             switch error
                 when '"not_public"'
-                    e = @render_not_public_error()
+                    e = @render_not_collaborator_error()
                 when 'no_dir'
                     e = <ErrorDisplay title="No such directory" error={"The path #{@props.current_path} does not exist."} />
                 when 'not_a_dir'
@@ -2153,4 +2163,5 @@
             {@render_paging_buttons(Math.ceil(listing.length / file_listing_page_size)) if listing?}
             {@render_file_listing(visible_listing, file_map, error, project_state, public_view)}
             {@render_paging_buttons(Math.ceil(listing.length / file_listing_page_size)) if listing?}
+
{@render_not_collaborator_error() if public_view}