the beauty of coffeescript and d3

the beauty of coffeescript and d3

this uses more typographic features of coffeescript to make the source more pleasing to the eye:
- templated and multiline strings

additionally, uses the [UMD](https://github.com/umdjs/umd) pattern (found [here](https://gist.github.com/bcherny/6567138)) to service all the major module providers.

--- textures.coffee
+++ textures.coffee
@@ -1,18 +1,25 @@
-root = exports ? this
+

 # helper function
 rand = () ->
-  (Math.random().toString(36)+"00000000000000000")
-    .replace(/[^a-z]+/g, "")
-    .slice(0, 5)
-
-# global object
-root.textures = {
-
+  "#{Math.random().toString(36)}00000000000000000"
+    .replace /[^a-z]+/g, ""
+    .slice 0, 5
+
+umd = (factory) ->
+  if typeof exports is 'object'
+    module.exports = factory()
+  else if typeof define is 'function' and define.amd
+    define [], factory
+  else
+    @textures = factory()
+
+# global factory
+umd ->
+
   # circles ------------------------------------------------------------

-  circles : () ->
-
+  circles: () ->
     size = 20
     background = ""
     radius = 2
@@ -23,33 +30,37 @@
     id = rand()

     circles = () ->
-      g = this.append("defs")
-          .append("pattern")
-          .attr("id", id)
-          .attr("patternUnits", "userSpaceOnUse")
-          .attr("width", size)
-          .attr("height", size)
+      g = this.append "defs"
+          .append "pattern"
+          .attr
+            id: id
+            patternUnits: "userSpaceOnUse"
+            width: size
+            height: size
       if background
-        g.append("rect")
-            .attr("width", size)
-            .attr("height", size)
-            .attr("fill", background)
-      g.append("circle")
-          .attr("cx", size / 2)
-          .attr("cy", size / 2)
-          .attr("r", radius)
-          .attr("fill", fill)
-          .attr("stroke", stroke)
-          .attr("stroke-width", strokeWidth)
+        g.append "rect"
+          .attr
+            width: size
+            height: size
+            fill: background
+      g.append "circle"
+        .attr
+          cx: size / 2
+          cy: size / 2
+          r: radius
+          fill: fill
+          stroke: stroke
+          "stroke-width": strokeWidth
       if complement
         for corner in [ [ 0, 0 ], [ 0, size ], [ size, 0 ], [ size, size ] ]
-          g.append("circle")
-              .attr("cx", corner[0])
-              .attr("cy", corner[1])
-              .attr("r", radius)
-              .attr("fill", fill)
-              .attr("stroke", stroke)
-              .attr("stroke-width", strokeWidth)
+          g.append "circle"
+            .attr
+              cx: corner[0]
+              cy: corner[1]
+              r: radius
+              fill: fill
+              stroke: stroke
+              "stroke-width": strokeWidth

     circles.heavier = (_) ->
       if not arguments.length
@@ -115,14 +126,14 @@
         circles

     circles.url = () ->
-      "url(#" + circles.id() + ")"
+      "url(##{id})"

     circles

+
   # lines --------------------------------------------------------------
-
-  lines : () ->
-
+
+  lines: () ->
     size = 20
     strokeWidth = 2
     stroke = "#343434"
@@ -133,38 +144,60 @@

     path = (orientation) ->
       switch orientation
-        when "0/8" then do (s=size) -> "M "+s/2+", 0 l 0, "+s
-        when "vertical" then do (s=size) -> "M "+s/2+", 0 l 0, "+s
-        when "1/8" then do (s=size) -> "M "+s/4+",0 l "+s/2+","+s+" M "+-s/4+",0 l "+s/2+","+s+" M "+s*3/4+",0 l "+s/2+","+s
-        when "2/8" then do (s=size) -> "M 0,"+s+" l "+s+","+-s+" M "+-s/4+","+s/4+" l "+s/2+","+-s/2+" M "+3/4*s+","+5/4*s+" l "+s/2+","+-s/2
-        when "diagonal" then do (s=size) -> "M 0,"+s+" l "+s+","+-s+" M "+-s/4+","+s/4+" l "+s/2+","+-s/2+" M "+3/4*s+","+5/4*s+" l "+s/2+","+-s/2
-        when "3/8" then do (s=size) -> "M 0,"+3/4*s+" l "+s+","+-s/2+" M 0,"+s/4+" l "+s+","+-s/2+" M 0,"+s*5/4+" l "+s+","+-s/2
-        when "4/8" then do (s=size) -> "M 0,"+s/2+" l "+s+",0"
-        when "horizontal" then do (s=size) -> "M 0,"+s/2+" l "+s+",0"
-        when "5/8" then do (s=size) -> "M 0,"+-s/4+" l "+s+","+s/2+"M 0,"+s/4+" l "+s+","+s/2+"M 0,"+s*3/4+" l "+s+","+s/2
-        when "6/8" then do (s=size) -> "M 0,0 l "+s+","+s+" M "+-s/4+","+3/4*s+" l "+s/2+","+s/2+" M "+s*3/4+","+-s/4+" l "+s/2+","+s/2
-        when "7/8" then do (s=size) -> "M "+-s/4+",0 l "+s/2+","+s+" M "+s/4+",0 l "+s/2+","+s+" M "+s*3/4+",0 l "+s/2+","+s
-        else do (s=size) -> "M "+s/2+", 0 l 0, "+s
+        when "0/8" then do (s=size) ->
+          "M #{s/2}, 0 l 0, #{s}"
+        when "vertical" then do (s=size) ->
+          "M #{s/2}, 0 l 0, #{s}"
+        when "1/8" then do (s=size) ->
+          """M #{s/4},0 l #{s/2},#{s} M #{-s/4},0 l #{s/2},#{s}
+             M #{s*3/4},0 l #{s/2},#{s}"""
+        when "2/8" then do (s=size) ->
+          """M 0,#{s} l #{s},#{-s} M #{-s/4},#{s/4} l #{s/2},#{-s/2}
+             M #{3/4*s},#{5/4*s} l #{s/2},#{-s/2}"""
+        when "diagonal" then do (s=size) ->
+          """M 0,#{s} l #{s},#{-s} M #{-s/4},#{s/4} l #{s/2},#{-s/2}
+             M #{3/4*s},#{5/4*s} l #{s/2},#{-s/2}"""
+        when "3/8" then do (s=size) ->
+          """M 0,#{3/4*s} l #{s},#{-s/2} M 0,#{s/4} l #{s},#{-s/2}
+             M 0,#{s*5/4} l #{s},#{-s/2}"""
+        when "4/8" then do (s=size) ->
+          "M 0,#{s/2} l #{s},0"
+        when "horizontal" then do (s=size) ->
+          "M 0,#{s/2} l #{s},0"
+        when "5/8" then do (s=size) ->
+          """M 0,#{-s/4} l #{s},#{s/2}M 0,#{s/4} l #{s},#{s/2}
+             M 0,#{s*3/4} l #{s},#{s/2}"""
+        when "6/8" then do (s=size) ->
+          """M 0,0 l #{s},#{s} M #{-s/4},#{3/4*s} l #{s/2},#{s/2}
+             M #{s*3/4},#{-s/4} l #{s/2},#{s/2}"""
+        when "7/8" then do (s=size) ->
+          """M #{-s/4},0 l #{s/2},#{s} M #{s/4},0 l #{s/2},#{s}
+             M #{s*3/4},0 l #{s/2},#{s}"""
+        else do (s=size) ->
+          "M #{s/2}, 0 l 0, #{s}"

     lines = () ->
-      g = this.append("defs")
-          .append("pattern")
-          .attr("id", id)
-          .attr("patternUnits", "userSpaceOnUse")
-          .attr("width", size)
-          .attr("height", size)
+      g = this.append "defs"
+          .append "pattern"
+          .attr
+            id: id
+            patternUnits: "userSpaceOnUse"
+            width: size
+            height: size
       if background
-        g.append("rect")
-            .attr("width", size)
-            .attr("height", size)
-            .attr("fill", background)
+        g.append "rect"
+          .attr
+            width: size
+            height: size
+            fill: background
       for o in orientation
-        g.append("path")
-            .attr("d", path(o))
-            .attr("stroke-width", strokeWidth)
-            .attr("shape-rendering", shapeRendering)
-            .attr("stroke", stroke)
-            .attr("stroke-linecap", "square")
+        g.append "path"
+          .attr
+            d: path o
+            "stroke-width": strokeWidth
+            "shape-rendering": shapeRendering
+            stroke: stroke
+            "stroke-linecap": "square"

     lines.background = (_) ->
       background = _
@@ -226,14 +259,14 @@
         lines

     lines.url = () ->
-      "url(#" + lines.id() + ")"
-
+      "url(##{id})"
+
     lines

+
   # path ---------------------------------------------------------------
-
-  paths : () ->
-
+
+  paths: () ->
     size = 20
     height = 1
     width = 1
@@ -244,7 +277,7 @@
     shapeRendering = "auto"
     fill = "transparent"
     id = undefined
-
+
     # Contributions with custom paths are welcome,
     # for example have a look at the "hexagons" path below
     # (`width` and `height` are in units of `size`)
@@ -252,44 +285,57 @@
     svgPath = (_) ->
       switch _
         when "squares" then do (s=size) ->
-          "M "+s/4+" "+s/4+" l "+s/2+" 0 l 0 "+s/2+" l "+-s/2+" 0 Z"
+          "M #{s/4} #{s/4} l #{s/2} 0 l 0 #{s/2} l #{-s/2} 0 Z"
         when "nylon" then do (s=size) ->
-          "M 0 "+s/4+" l "+s/4+" 0 l 0 "+-s/4+" M "+s*3/4+" "+s+" l 0 "+-s/4+" l "+s/4+" 0 M "+s/4+" "+s/2+" l 0 "+s/4+" l "+s/4+" 0 M "+s/2+" "+s/4+" l "+s/4+" 0 l 0 "+s/4
+          """M 0 #{s/4} l #{s/4} 0 l 0 #{-s/4} M #{s*3/4} #{s} l 0 #{-s/4}
+             l #{s/4} 0 M #{s/4} #{s/2} l 0 #{s/4} l #{s/4} 0 M #{s/2} #{s/4}
+             l #{s/4} 0 l 0 #{s/4}"""
         when "waves" then do (s=size) ->
-          "M 0 "+s/2+" c "+s/8+" "+-s/4+" , "+s*3/8+" "+-s/4+" , "+s/2+" 0 c "+s/8+" "+s/4+" , "+s*3/8+" "+s/4+" , "+s/2+" 0 M "+-s/2+" "+s/2+" c "+s/8+" "+s/4+" , "+s*3/8+" "+s/4+" , "+s/2+" 0 M "+s+" "+s/2+" c "+s/8+" "+-s/4+" , "+s*3/8+" "+-s/4+" , "+s/2+" 0"
+          """M 0 #{s/2} c #{s/8} #{-s/4} , #{s*3/8} #{-s/4} , #{s/2} 0
+             c #{s/8} #{s/4} , #{s*3/8} #{s/4} , #{s/2} 0 M #{-s/2} #{s/2}
+             c #{s/8} #{s/4} , #{s*3/8} #{s/4} , #{s/2} 0 M #{s} #{s/2}
+             c #{s/8} #{-s/4} , #{s*3/8} #{-s/4} , #{s/2} 0"""
         when "woven" then do (s=size) ->
-          "M "+s/4+","+s/4+"l"+s/2+","+s/2+"M"+s*3/4+","+s/4+"l"+s/2+","+-s/2+"M"+s/4+","+s*3/4+"l"+-s/2+","+s/2+"M"+s*3/4+","+s*5/4+"l"+s/2+","+-s/2+"M"+-s/4+","+s/4+"l"+s/2+","+-s/2
+          """M #{s/4},#{s/4}l#{s/2},#{s/2}M#{s*3/4},#{s/4}l#{s/2},#{-s/2}
+             M#{s/4},#{s*3/4}l#{-s/2},#{s/2}M#{s*3/4},#{s*5/4}l#{s/2},#{-s/2}
+             M#{-s/4},#{s/4}l#{s/2},#{-s/2}"""
         when "crosses" then do (s=size) ->
-          "M "+s/4+","+s/4+"l"+s/2+","+s/2+"M"+s/4+","+s*3/4+"l"+s/2+","+-s/2
+          "M #{s/4},#{s/4}l#{s/2},#{s/2}M#{s/4},#{s*3/4}l#{s/2},#{-s/2}"
         when "caps" then do (s=size) ->
-          "M "+s/4+","+s*3/4+"l"+s/4+","+-s/2+"l"+s/4+","+s/2
+          "M #{s/4},#{s*3/4}l#{s/4},#{-s/2}l#{s/4},#{s/2}"
         when "hexagons" then do (s=size) ->
           width = 3
           height = Math.sqrt(3)
-          "M "+s+",0 l "+s+",0 l "+s/2+","+(s*Math.sqrt(3)/2)+" l "+(-s/2)+","+(s*Math.sqrt(3)/2)+" l "+(-s)+",0 l "+(-s/2)+","+(-s*Math.sqrt(3)/2)+" Z M 0,"+s*Math.sqrt(3)/2+" l "+s/2+",0 M "+(3*s)+","+s*Math.sqrt(3)/2+" l "+(-s/2)+",0"
-        else _(size)
+          """M #{s},0 l #{s},0 l #{s/2},#{(s*Math.sqrt(3)/2)}
+             l #{(-s/2)},#{(s*Math.sqrt(3)/2)} l #{(-s)},0
+             l #{(-s/2)},#{(-s*Math.sqrt(3)/2)} Z M 0,#{s*Math.sqrt(3)/2}
+             l #{s/2},0 M #{(3*s)},#{s*Math.sqrt(3)/2} l #{(-s/2)},0"""
+        else _ size

     paths = () ->
-      path = svgPath(d)
+      path = svgPath d
       id = rand()
-      g = this.append("defs")
-          .append("pattern")
-          .attr("id", id)
-          .attr("patternUnits", "userSpaceOnUse")
-          .attr("width", size * width)
-          .attr("height", size * height)
+      g = this.append "defs"
+        .append "pattern"
+        .attr
+          id: id
+          patternUnits: "userSpaceOnUse"
+          width: size * width
+          height: size * height
       if background
-        g.append("rect")
-            .attr("width", size * width)
-            .attr("height", size * height)
-            .attr("fill", background)
-      g.append("path")
-          .attr("d", path)
-          .attr("fill", fill)
-          .attr("stroke-width", strokeWidth)
-          .attr("shape-rendering", shapeRendering)
-          .attr("stroke", stroke)
-          .attr("stroke-linecap", "square")
+        g.append "rect"
+          .attr
+            width: size * width
+            height: size * height
+            fill: background
+      g.append "path"
+        .attr
+          d: path
+          fill: fill
+          "stroke-width": strokeWidth
+          "shape-rendering": shapeRendering
+          stroke: stroke
+          "stroke-linecap": "square"

     paths.background = (_) ->
       background = _
@@ -351,8 +397,6 @@
         paths

     paths.url = () ->
-      "url(#" + paths.id() + ")"
-
+      "url(##{id})"
+
     paths
-
-}


Updates Electron to v0.31.1

Updates Electron to v0.31.1

--- tasks/download.coffee
+++ tasks/download.coffee
@@ -26,7 +26,7 @@
       return done()

     electronDownloader
-      version: 'v0.29.0'
+      version: 'v0.31.1'
       cacheDir: './cache'
       outputDir: outputDir
       platform: platform


remove air redactor

remove air redactor

--- app/assets/javascripts/para/inputs/nested_many.coffee
+++ app/assets/javascripts/para/inputs/nested_many.coffee
@@ -29,9 +29,8 @@
     if @orderable
       @$fieldsList.sortable('reload')

-    $element.find('[data-redactor]').simpleFormRedactor()
-    $element.find('[data-air-redactor]').simpleFormAirRedactor()
-
+    if ($redactor = $element.find('[data-redactor]')).length
+      $redactor.simpleFormRedactor()

   openInsertedField: ($field) ->
     $target = $($field.attr('href'))


填空传递空字符串问题解决

填空传递空字符串问题解决

--- app/assets/javascripts/question_bank/questions.coffee
+++ app/assets/javascripts/question_bank/questions.coffee
@@ -103,4 +103,8 @@
     jQuery('.form-question-fill .answer:last').after(blank)

+  jQuery('.form-question-fill').on 'submit','form',->
+    if jQuery('.form-question-fill .answer:not(.hidden)').length > 0
+      jQuery('.form-question-fill .answer.hidden').remove()

+


Added Order.isValid() and isInvalid()

Added Order.isValid() and isInvalid()

--- app/assets/javascripts/sprangular/models/order.coffee
+++ app/assets/javascripts/sprangular/models/order.coffee
@@ -23,6 +23,15 @@
   isEmpty: ->
     @items.length == 0

+  isValid: ->
+    @billingAddress.validate()
+    @actualShippingAddress().validate()
+
+    @billingAddress.isValid() && @actualShippingAddress().isValid()
+
+  isInvalid: ->
+    !@isValid()
+
   totalQuantity: ->
     @items.reduce ((total, item) -> total + item.quantity), 0


Don't clear state after each test

Don't clear state after each test

--- spec/Helpers.coffee
+++ spec/Helpers.coffee
@@ -71,7 +71,7 @@
       y = null
       z = null
       p = null
-      beforeEach (done) ->
+      before (done) ->
         c = new noflo.Component
         c.inPorts.add 'x',
           required: true


Removed the 'restrict to serializable' feature. We don't need this after all, and when we do, we can use jQuery to get this effect.

--- app/lib/dom_text_mapper/dom_text_mapper.coffee
+++ app/lib/dom_text_mapper/dom_text_mapper.coffee
@@ -16,16 +16,9 @@

   constructor: ->
     @setRealRoot()
-    @restrictToSerializable false
     window.DomTextMapper.instances.push this

   # ===== Public methods =======
-
-  # Switch the library into "serializable-only" mode.
-  # If set to true, all public API calls will be restricted to return
-  # strictly serializable data structures.
-  # (References to DOM objects will be omitted.)
-  restrictToSerializable: (value = true) -> @restricted = value

   # Consider only the sub-tree beginning with the given node.
   #
@@ -86,11 +79,10 @@
   #   content: the text content of the node, as rendered by the browser
   #   length: the length of the next content
   scan: ->
-#    console.log "in getAllPaths"
     if @domStableSince @lastScanned
       # We have a valid paths structure!
 #      console.log "We have a valid DOM structure cache."
-      return if @restricted then @cleanPaths else @allPaths
+      return @allpaths

 #    console.log "No valid cache, will have to calculate getAllPaths."
     startTime = @timestamp()
@@ -112,15 +104,7 @@
     t2 = @timestamp()
     console.log "Phase II of data collecting " + (t2 - t1) + " ms."

-    if @restricted
-      @cleanPaths = {}
-      for path, info of @allPaths
-        cleanInfo = $.extend({}, info);
-        delete cleanInfo.node
-        @cleanPaths[path] = cleanInfo
-      @cleanPaths
-    else
-      @allPaths
+    @allPaths

   # Select the given path (for visual identification), and optionally scroll to it
   selectPath: (path, scroll = false) ->
@@ -199,10 +183,6 @@
   getRangeForPath: (path) ->
     result = @mappings[path]
     unless result? then throw new Error "Found no range for path '" + path + "'!"
-    if @restricted
-      result = $.extend {}, result;
-      result.pathInfo = $.extend {}, result.pathInfo
-      delete result.pathInfo.node
     result

   # Return the character range mappings for a given node in the DOM
@@ -216,24 +196,6 @@
     mappings = (for range in ranges
       mapping = @getMappingsForRange range.start, range.end
     )
-#    console.log "Raw mappings:"
-#    console.log mappings
-
-    if @restricted
-      mappings = (for mapping in mappings
-        cleanMapping = $.extend {}, mapping
-        delete cleanMapping.range
-        cleanMapping.nodes = (for node in cleanMapping.nodes
-          cleanNode = $.extend {}, node
-          cleanNode.element = $.extend {}, cleanNode.element
-          cleanNode.element.pathInfo = $.extend {}, cleanNode.element.pathInfo
-          delete cleanNode.element.pathInfo.node
-          cleanNode
-        )
-        cleanMapping
-      )
-#      console.log "Cleaned mappings:"
-#      console.log mappings

     mappings


Windows timing issues workaround

Windows timing issues workaround

--- spec/Network.coffee
+++ spec/Network.coffee
@@ -285,11 +285,12 @@
       it 'processes should be running before the stop call', ->
         chai.expect(n.processes.Repeat.component.stopped).to.equal false
       it 'should emit the end event', (done) ->
+        @timeout 5000
         # Ensure we have a connection open
-        n.stop()
         n.once 'end', (endTimes) ->
           chai.expect(endTimes).to.be.an 'object'
           done()
+        n.stop()
       it 'should have called the shutdown method of each process', ->
         chai.expect(n.processes.Repeat.component.stopped).to.equal true


Add test demonstrating broken forwardBrackets when scopes are used

--- spec/Scoping.coffee
+++ spec/Scoping.coffee
@@ -317,3 +317,34 @@
       in2.connect()
       in2.send 'foo'
       in2.disconnect()
+    it 'should forward scopes as expected', (done) ->
+      expected = [
+        'x < 1'
+        'x DATA 1onePc1:2twoPc2:PcMerge'
+        'x >'
+      ]
+      received = []
+      brackets = []
+
+      out.on 'ip', (ip) ->
+        switch ip.type
+          when 'openBracket'
+            received.push "#{ip.scope} < #{ip.data}"
+            brackets.push ip.data
+          when 'data'
+            received.push "#{ip.scope} DATA #{ip.data}"
+          when 'closeBracket'
+            received.push "#{ip.scope} >"
+            brackets.pop()
+            return if brackets.length
+            chai.expect(received).to.eql expected
+            done()
+
+      in2.post new noflo.IP 'data', 'two',
+        scope: 'x'
+      in1.post new noflo.IP 'openBracket', 1,
+        scope: 'x'
+      in1.post new noflo.IP 'data', 'one',
+        scope: 'x'
+      in1.post new noflo.IP 'closeBracket', 1,
+        scope: 'x'


Modal 'closed' event.

Modal 'closed' event.

--- app/scripts/components/BsModalComponent.coffee
+++ app/scripts/components/BsModalComponent.coffee
@@ -1,7 +1,7 @@
 ###
 Modal component.
 ###
-Bootstrap.BsModalComponent = Ember.Component.extend(
+Bootstrap.BsModalComponent = Ember.Component.extend(Ember.Evented,
     layoutName: 'components/bs-modal'
     classNames: ['modal']
     #classNameBindings: ['fade']
@@ -63,6 +63,7 @@

     close: (event) ->
         if @get('manual') then @destroy() else @hide()
+        @trigger 'closed'

     #Invoked automatically by ember when the view is destroyed, giving us a chance to perform cleanups
     willDestroyElement: ->


(fix) Load PDF after loading the solution in correction mode.

--- app/scripts/pages/correction/correction.coffee
+++ app/scripts/pages/correction/correction.coffee
@@ -31,7 +31,7 @@
       getSolution = api.get.nextSolution @exercise().id
         .then (solution) => @solution solution

-      Q.all([getSolution, @loadPdf()]).then => @initPages()
+      getSolution.then(=> @loadPdf()).then => @initPages()

     @loadExercise = => #load the exercise and pdf for the current solution
       if !@solution()
@@ -118,16 +118,20 @@
       .then (exercise) =>
         @exercise exercise
         @loadSolution()
-      .catch -> alert 'Could not load this solution.'
+      .catch (e) ->
+        console.error(e)
+        alert 'Could not load this solution.'
     else if @params.solutionId
       api.get.solution @params.solutionId
       .then (solution) =>
         @solution solution
         @loadExercise()
-      .catch -> alert 'Could not load this solution.'
+      .catch (e) ->
+        console.error(e)
+        alert 'Could not load this solution.'

   loadPdf: ->
-    PDFJS.getDocument(api.urlOf.pdf(@exercise()))
+    PDFJS.getDocument(api.urlOf.pdf(@solution().id))
     .then (pdf) =>
       Q.all [1..pdf.numPages].map (pageNo) ->
           pdf.getPage(pageNo).then (page) ->


set to canvas renderer for pixel perfect rendering in webkit

--- src/App.coffee
+++ src/App.coffee
@@ -14,11 +14,11 @@
 class App

 	constructor: () ->
-		@level = 3;
+		@level = 1;

 		@filter = []

-		@game = new Phaser.Game 160, 160, Phaser.AUTO, 'game', {
+		@game = new Phaser.Game 160, 160, Phaser.CANVAS, 'game', {
 			preload: @preload
 			create: @create
 			update: @update
@@ -62,6 +62,8 @@
 		# Enabled pixel-perfect scaling :D
 		Phaser.Canvas.setImageRenderingCrisp(@game.canvas)
 		PIXI.scaleModes.DEFAULT = PIXI.scaleModes.NEAREST;
+		@game.stage.smoothed = false
+		Phaser.Canvas.setSmoothingEnabled @game.context, false

 		#  create the map object
 		@map = new Map @game


Fixing whitespace

Fixing whitespace

--- src/CLI.coffee
+++ src/CLI.coffee
@@ -20,7 +20,7 @@
     # if the command has options
     if parts[2]
       # Split each option
-      argv = parts[2].split(/(?=-+)/);
+      argv = parts[2].trim().split(/(?=-+)/);
       # Split last args
       argv = argv.concat(argv.pop().split(" "));

@@ -39,8 +39,7 @@
     argv.unshift("main.js")
     argv.unshift("node")

-    # removing empty elements before assign
-    process.argv = (item for item in argv when item)
+    process.argv = argv

   executeCommand: ->


Add linesOfCode goal C++ middleware (#5740)

Co-authored-by: Tong Yuqiang <ad31f93c95f658d331507e44ff69be7e2b666bf9@gmail.com>

--- app/views/play/level/PlayLevelView.coco.coffee
+++ app/views/play/level/PlayLevelView.coco.coffee
@@ -390,6 +390,14 @@

   initGoalManager: ->
     options = {}
+
+    # Add two lines to handle `void main() {}` in C++ for the linesOfCode goal.
+    if ((@session?.get('codeLanguage') is 'cpp' or me.get('aceConfig')?.language is 'cpp') and Array.isArray(@level.get('goals')))
+      @level.get('goals').forEach((goal) =>
+        if goal?.linesOfCode?.humans and typeof goal.linesOfCode.humans == 'number'
+          goal.linesOfCode.humans += 2
+      )
+
     if @level.get('assessment') is 'cumulative'
       options.minGoalsToComplete = 1
     @goalManager = new GoalManager(@world, @level.get('goals'), @team, options)


fix this one too!

fix this one too!

--- apps/fair/components/browse/test/router.coffee
+++ apps/fair/components/browse/test/router.coffee
@@ -8,7 +8,7 @@

   beforeEach ->
     benv.setup =>
-      benv.expose { $: require('jquery') }
+      benv.expose { $: benv.require('jquery') }
       FilterRouter = benv.require resolve(__dirname, '../router.coffee')
       FilterRouter.__set__ 'FairBrowseView', @FairBrowseView = sinon.stub()
       sinon.stub Backbone.history, 'start'


Minor refactoring for building context

Minor refactoring for building context

--- src/client/widget.coffee
+++ src/client/widget.coffee
@@ -104,8 +104,13 @@
         onDataError = (datasourceName, err)->
           console.log "An error occurred with datasource #{datasourceName}", err
         @_renderCount ?= 0
-        @_renderCount++
-        ret = {}
+        ret =
+          options: @options
+          loggedIn: @loggedIn()
+          isAdmin: @sandbox.isAdmin
+          debug: @sandbox.config.debug
+          renderCount: ++@_renderCount
+
         dfd = @sandbox.data.deferred()
         datasourceErrors = {}
         try
@@ -122,16 +127,10 @@
               datasourceErrors[k] = err
               promiseDfd.resolve(handler err)
             promiseDfd
-
+          @data = {}
           widgetDeferred = @sandbox.data.when.apply(undefined, promises)
-          templateDeferred = @sandbox.template.load(@templates, @ref)
-          @data = {}
-          readyDfd = $.when(widgetDeferred, templateDeferred)
-          readyDfd.fail (err)=>
-            console.error("Error in Building Render Context", err.message, err)
-            @renderError.call(@, err.message, err)
-          readyDfd.done (data, tpls)=>
-            args = data
+          widgetDeferred.done ()=>
+            args = slice.call arguments
             _.map keys, (k,i)=>
               @data[k] = args[i]
               if _.isFunction args[i]?.toJSON
@@ -140,12 +139,15 @@
                 ret[k] = args[i][0]
               else
                 ret[k] = args[i]
-            ret.options     = @options
-            ret.loggedIn    = @loggedIn()
-            ret.isAdmin     = @sandbox.isAdmin
-            ret.debug       = @sandbox.config.debug
-            ret.renderCount = @_renderCount
+          templateDeferred = @sandbox.template.load(@templates, @ref)
+          templateDeferred.done (tpls)=>
             @_templates     = tpls
+          readyDfd = $.when(widgetDeferred, templateDeferred)
+          readyDfd.fail (err)=>
+            console.error("Error in Building Render Context", err.message, err)
+            @renderError.call(@, err.message, err)
+            dfd.reject(err)
+          readyDfd.done ()=>
             dfd.resolve(ret)

         catch e


unneeded ref

unneeded ref

--- resources/assets/lib/select-options.coffee
+++ resources/assets/lib/select-options.coffee
@@ -17,13 +17,12 @@
 ###

 import { a, i, div } from 'react-dom-factories'
-import { createRef, PureComponent } from 'react'
+import { PureComponent } from 'react'

 export class SelectOptions extends PureComponent
   constructor: (props) ->
     super props
     @bn = @props.bn ? 'select-options'
-    @ref = createRef()

     @state =
       showingSelector: false
@@ -47,7 +46,6 @@

     div
       className: classNames
-      ref: @ref
       div
         className: "#{@bn}__select"
         @renderItem


Revert "remove the conditional that creates a special case when mapping.query exists"

This reverts commit c0fee8b7193ea443f2f983cdd70106a3e7112aed.

--- src/api.coffee
+++ src/api.coffee
@@ -22,7 +22,21 @@
     if (name = schema.id?.split("#")[1])?
       if (mapping = @mappings[name])?
         constructor = mapping.constructor
-        data = new constructor context, data
+        _data = data
+        if mapping.query?
+          # Some resources require query parameters to instantiate.
+          # For these, we've stuck the query definition onto the
+          # constructor.  For these cases, we substitute a simple
+          # function for the property.
+          # In usage, this looks like:
+          #   user.repository(name: "patchboard").update(content, callback)
+          data = (params) ->
+            if _data.url?
+              params.url = _data.url
+            new constructor context, {url: mapping.generate_url(params)}
+
+        else
+          data = new constructor context, _data
     return @_decorate(context, schema, data) || data


index into asarPaths  instead of using pop

--- src/apm.coffee
+++ src/apm.coffee
@@ -55,7 +55,7 @@
           glob = require 'glob'
           pattern = "/Users/#{process.env.USERNAME}/AppData/Local/atom/app-+([0-9]).+([0-9]).+([0-9])/resources/app.asar"
           asarPaths = glob.sync(pattern, null) # [] | a sorted array of locations with the newest version being last
-          asarPath = asarPaths.pop()
+          asarPath = asarPaths[asarPaths.length - 1]
         return process.nextTick -> callback(asarPath)
       else
         return process.nextTick -> callback('')


Revert "Stop using asar archives"

Revert "Stop using asar archives"

--- src/apm.coffee
+++ src/apm.coffee
@@ -26,24 +26,28 @@

     apmFolder = path.resolve(__dirname, '..')
     appFolder = path.dirname(apmFolder)
-    if path.basename(apmFolder) is 'apm' and path.basename(appFolder) is 'app' and fs.existsSync(appFolder)
-      return process.nextTick -> callback(appFolder)
+    if path.basename(apmFolder) is 'apm' and path.basename(appFolder) is 'app'
+      asarPath = "#{appFolder}.asar"
+      if fs.existsSync(asarPath)
+        return process.nextTick -> callback(asarPath)

     apmFolder = path.resolve(__dirname, '..', '..', '..')
     appFolder = path.dirname(apmFolder)
-    if path.basename(apmFolder) is 'apm' and path.basename(appFolder) is 'app' and fs.existsSync(appFolder)
-      return process.nextTick -> callback(appFolder)
+    if path.basename(apmFolder) is 'apm' and path.basename(appFolder) is 'app'
+      asarPath = "#{appFolder}.asar"
+      if fs.existsSync(asarPath)
+        return process.nextTick -> callback(asarPath)

     switch process.platform
       when 'darwin'
         child_process.exec 'mdfind "kMDItemCFBundleIdentifier == \'com.github.atom\'"', (error, stdout='', stderr) ->
           [appLocation] = stdout.split('\n') unless error
           appLocation = '/Applications/Atom.app' unless appLocation
-          callback("#{appLocation}/Contents/Resources/app")
+          callback("#{appLocation}/Contents/Resources/app.asar")
       when 'linux'
-        appLocation = '/usr/local/share/atom/resources/app'
+        appLocation = '/usr/local/share/atom/resources/app.asar'
         unless fs.existsSync(appLocation)
-          appLocation = '/usr/share/atom/resources/app'
+          appLocation = '/usr/share/atom/resources/app.asar'
         process.nextTick -> callback(appLocation)

   getReposDirectory: ->


Preload service for radius filter units.

--- src/app.coffee
+++ src/app.coffee
@@ -341,6 +341,7 @@

             unitList = new models.UnitList [], pageSize: PAGE_SIZE
                 .setFilter 'only', 'name,location,root_services'
+                .setFilter 'include', 'services'
                 .setFilter 'lat', pos.get('location').coordinates[1]
                 .setFilter 'lon', pos.get('location').coordinates[0]
                 .setFilter 'distance', radius


Explain default locales

Explain default locales

--- www/js/globals.coffee
+++ www/js/globals.coffee
@@ -16,6 +16,9 @@
         "WebkitOverflowScrolling" in window.document.createElement("div").style
       )()
     HOUR: 3600
+    # These are the officially supported locales for around. If the user's UA
+    # returns a separate locale (or one that doesn't fallback to one like en-ca
+    # might fallback to en-us), the "default" locale (of en-US) will be used.
     LOCALES: [
       'en-GB'
       'en-US'


Added check on action click to see if action is enabled.

--- ribs.coffee
+++ ribs.coffee
@@ -708,7 +708,7 @@
         className: "action"

         events:
-            'click': 'activate'
+            'click': 'activateIfAllowed'
             'keypress': 'keypressed'

         render : ->
@@ -742,6 +742,11 @@
             @$el.toggleClass "disabled", not enabled
             idx = if enabled then 0 else -1
             @$(".button").prop "tabindex", idx
+
+        activateIfAllowed: (event) ->
+            unless @$el.is ".disabled"
+                @model.activate @getSelected(), @getListItem()
+            false

         activate: (event) ->
             @model.activate @getSelected(), @getListItem()


fixed label for advanced options on task type edit

--- app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
+++ app/assets/javascripts/admin/views/tasktype/task_type_create_view.coffee
@@ -64,7 +64,7 @@
                   Allow Branchpoints
                 </label>

-                <label class="col-sm-3" for="settings[advancedOptionsAllowed">
+                <label class="col-sm-3" for="advancedOptionsAllowed">
                   <input type="checkbox" id="advancedOptionsAllowed" name="settings[advancedOptionsAllowed]" <%- isChecked(settings.advancedOptionsAllowed) %>>
                   Advanced Tracing Options
                 </label>


Retrieve ticket to be able to show while ticket data in ticket info.

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -283,11 +283,11 @@
       placement: position
       title: ->
         ticket_id = $(@).data('id')
-        ticket = App.Ticket.find( ticket_id )
+        ticket = App.Ticket.retrieve( ticket_id )
         ticket.title
       content: ->
         ticket_id = $(@).data('id')
-        ticket = App.Ticket.find( ticket_id )
+        ticket = App.Ticket.retrieve( ticket_id )
         ticket.humanTime = ui.humanTime(ticket.created_at)
         # insert data
         App.view('ticket_info_small')(


Check for <math> and return early if nothing to do

--- src/components/html.cjsx
+++ src/components/html.cjsx
@@ -45,21 +45,22 @@
       link.setAttribute('target', '_blank') unless link.getAttribute('href')?[0] is '#'

     nodes = root.querySelectorAll('[data-math]:not(.math-rendered)') or []
+    hasMath = root.querySelector('math')

-    # Clone the array because the browser will mutable it
-    nodes = _.toArray(nodes)
+    # Return immediatly if no [data-math] or <math> elements are present
+    return unless _.any(nodes) or hasMath

-    _.each nodes, (node) ->
+    for node in nodes
       formula = node.getAttribute('data-math')
-
-      # Divs with data-math should be rendered as a block
+      # divs with data-math should be rendered as a block
       if node.tagName.toLowerCase() is 'div'
         node.textContent = "\u200c\u200c\u200c#{formula}\u200c\u200c\u200c"
       else
         node.textContent = "\u200b\u200b\u200b#{formula}\u200b\u200b\u200b"
+      window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, node])

-    # MathML should be rendered by MathJax (if available)
-    window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, root])
+    # render MathML with MathJax
+    window.MathJax?.Hub.Queue(['Typeset', MathJax.Hub, root]) if hasMath

     # Once MathJax finishes processing, mark all the nodes as
     # rended and then manually cleanup the MathJax message nodes to prevent


fixed delete route

fixed delete route

--- src/api.coffee
+++ src/api.coffee
@@ -106,7 +106,7 @@
   apiHelper TaskPlanActions, TaskPlanActions.save, TaskPlanActions.saved, 'PATCH', saveHelper

   apiHelper TaskPlanActions, TaskPlanActions.delete, TaskPlanActions.deleted, 'DELETE', (id) ->
-    url: "/api/courses/1/plans/#{id}"
+    url: "/api/plans/#{id}"

   apiHelper TaskPlanActions, TaskPlanActions.load , TaskPlanActions.loaded, 'GET', (id) ->
     url: "/api/plans/#{id}"


Use piwik to track executed commands.

Use piwik to track executed commands.

--- src/app.coffee
+++ src/app.coffee
@@ -501,6 +501,8 @@
                 throw e

         commandInterceptor = (comm, parameters) ->
+            if _paq?
+                _paq.push ['trackEvent', 'Command', comm]
             appControl[comm].apply(appControl, parameters)?.done? =>
                 unless parameters[0]?.navigate == false
                     router.navigateByCommand comm, parameters


Simplify console message on JSON parse error

--- src/app.coffee
+++ src/app.coffee
@@ -49,8 +49,7 @@
     try
       config = JSON.parse(fs.readFileSync(file))
     catch err
-      console.error new Error "Error parse JSON file"
-      console.error err
+      console.error "Error parsing JSON file #{file} : #{err.message}"
       process.exit 1

 getConfig file for file in CONFIG_FILES


remove unnecessary functions

remove unnecessary functions

--- src/app.coffee
+++ src/app.coffee
@@ -13,10 +13,6 @@

 noop = GLOBAL.noop = ->
     null
-
-exitProcess = GLOBAL.exitProcess = ->
-    logger.info '[DONE]'
-    process.exit 0

 #######################


Fix to account for slack adaptor API change

--- src/aws.coffee
+++ src/aws.coffee
@@ -108,15 +108,14 @@
             name = _(instance.Tags).findWhere(Key: 'Name')?.Value or "Unnamed"
             message += "\n `#{name}` (launched #{moment(instance.LaunchTime).fromNow()}) <#{url}|Console Link>"

-        robot.adapter.send
-          channel: room
-          text: message
+        robot.messageRoom room, message

   reportUntagged = (room, reportZero) ->
     reportZero = no if not reportZero?

     fetchInstances (data) ->
       if not data
+        robot.messageRoom room, "No instance data found"
         return

       untagged = _(data.Reservations).filter (reservation) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/animated.coffee
+++ src/animated.coffee
@@ -98,6 +98,7 @@
         I.height = sprite.height

     I.currentFrameIndex = I.activeAnimation.frames[(frames.indexOf(I.currentFrameIndex) + 1) % frames.length]
+    I.sprite = I.spriteLookup[I.currentFrameIndex]

   find = (name) ->
     result = null
@@ -107,10 +108,6 @@
       result = animation if animation.name.toLowerCase() == nameLower

     return result
-
-  draw: (canvas) ->
-    canvas.withTransform self.transform(), ->
-      I.spriteLookup[I.currentFrameIndex].draw(canvas, I.x, I.y)

   ###*
   Transitions to a new active animation. Will not transition if the new state


add host stats

add host stats

--- src/api/host.coffee
+++ src/api/host.coffee
@@ -3,6 +3,7 @@
 {parseXml} = require '../utils'
 $forEach = require 'lodash.foreach'
 $find = require 'lodash.find'
+$findIndex = require 'lodash.findindex'

 #=====================================================================

@@ -244,3 +245,85 @@
 }

 exports.installPatch = installPatch
+
+#---------------------------------------------------------------------
+
+
+stats = $coroutine ({host}) ->
+
+  xapi = @getXAPI host
+
+  [response, body] = $wait $request {
+    method: 'get'
+    rejectUnauthorized: false
+    url: 'https://'+host.address+'/host_rrd?session_id='+xapi.sessionId
+  }
+
+  if response.statusCode isnt 200
+    throw new Error('Cannot fetch the RRDs')
+
+  json = parseXml(body)
+  # Find index of needed objects for getting their values after
+  cpusIndexes = []
+  index = 0
+  while (pos = $findIndex(json.rrd.ds, 'name', 'cpu' + index++)) isnt -1
+    cpusIndexes.push(pos)
+  pifsIndexes = []
+  index = 0
+  while (pos = $findIndex(json.rrd.ds, 'name', 'pif_eth' + index + '_rx')) isnt -1
+    pifsIndexes.push(pos)
+    pifsIndexes.push($findIndex(json.rrd.ds, 'name', 'pif_eth' + (index++) + '_tx'))
+
+  memoryFreeIndex = $findIndex(json.rrd.ds, 'name': 'memory_free_kib')
+  memoryIndex = $findIndex(json.rrd.ds, 'name': 'memory_total_kib')
+  loadIndex = $findIndex(json.rrd.ds, 'name': 'loadavg')
+
+  memoryFree = []
+  memoryUsed = []
+  memory = []
+  load = []
+  cpus = []
+  pifs = []
+  date = [] #TODO
+  baseDate = json.rrd.lastupdate
+  dateStep = json.rrd.step
+  numStep = json.rrd.rra[0].database.row.length - 1
+
+  $forEach json.rrd.rra[0].database.row, (n, key) ->
+    memoryFree.push(Math.round(parseInt(n.v[memoryFreeIndex])))
+    memoryUsed.push(Math.round(parseInt(n.v[memoryIndex])-(n.v[memoryFreeIndex])))
+    memory.push(parseInt(n.v[memoryIndex]))
+    load.push(n.v[loadIndex])
+    date.push(baseDate - (dateStep * (numStep - key)))
+    # build the multi dimensional arrays
+    $forEach cpusIndexes, (value, key) ->
+      cpus[key] ?= []
+      cpus[key].push(n.v[value]*100)
+      return
+    $forEach pifsIndexes, (value, key) ->
+      pifs[key] ?= []
+      pifs[key].push(if n.v[value] == 'NaN' then null else n.v[value]) # * (if key % 2 then -1 else 1))
+      return
+    return
+
+
+  # the final object
+  return {
+    memoryFree: memoryFree
+    memoryUsed: memoryUsed
+    memory: memory
+    date: date
+    cpus: cpus
+    pifs: pifs
+    load: load
+  }
+
+stats.params = {
+  host: { type: 'string' }
+}
+
+stats.resolve = {
+  host: ['host', 'host']
+}
+
+exports.stats = stats;


Implement atom.confirm with atom-shell's dialog API.

Note that the dialog.showMessageBox is blocking, so we don't need to use
the '@presentModal'.

--- src/app/atom.coffee
+++ src/app/atom.coffee
@@ -3,6 +3,7 @@
 Package = require 'package'
 Theme = require 'theme'
 ipc = require 'ipc'
+remote = require 'remote'

 window.atom =
   exitWhenDone: window.location.params.exitWhenDone
@@ -157,17 +158,21 @@
     @sendMessageToBrowserProcess('restartRendererProcess')

   confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
-    wrapCallback = (callback) => => @dismissModal(callback)
-    @presentModal =>
-      args = [message, detailedMessage]
-      callbacks = []
-      while buttonLabelsAndCallbacks.length
-        do =>
-          buttonLabel = buttonLabelsAndCallbacks.shift()
-          buttonCallback = buttonLabelsAndCallbacks.shift()
-          args.push(buttonLabel)
-          callbacks.push(=> @dismissModal(buttonCallback))
-      @sendMessageToBrowserProcess('confirm', args, callbacks)
+    buttons = []
+    callbacks = []
+    while buttonLabelsAndCallbacks.length
+      do =>
+        buttons.push buttonLabelsAndCallbacks.shift()
+        callbacks.push buttonLabelsAndCallbacks.shift()
+
+    dialog = remote.require 'dialog'
+    chosen = dialog.showMessageBox remote.getCurrentWindow(),
+      type: dialog.MESSAGE_BOX_INFORMATION
+      message: message
+      detail: detailedMessage
+      buttons: buttons
+
+    callbacks[chosen]?()

   showSaveDialog: (callback) ->
     @presentModal =>


Use _.remove to remove windows from atom.windows array

--- src/atom/app.coffee
+++ src/atom/app.coffee
@@ -1,6 +1,7 @@
 Keymap = require 'keymap'
 $ = require 'jquery'
 _ = require 'underscore'
+require 'underscore-extensions'

 module.exports =
 class App
@@ -28,5 +29,4 @@
     @windows.push window

   windowClosed: (window) ->
-    index = @windows.indexOf(window)
-    @windows.splice(index, 1) if index >= 0
+    _.remove(@windows, window)


initialize split test

initialize split test

--- apps/artist/routes.coffee
+++ apps/artist/routes.coffee
@@ -22,7 +22,7 @@
   if (referrer = req.get 'Referrer')?
     medium = new ReferrerParser(referrer).medium

-  format = res.locals.sd.artist_page_format
+  format = res.locals.sd.ARTIST_PAGE_FORMAT

   Q.allSettled([
     artist.fetch(cache: true)
@@ -42,7 +42,7 @@
         res.locals.sd.STATUSES = statuses = statusesRequest.value
         res.locals.sd.MEDIUM = medium if medium?

-        res.render "#{version}/index",
+        res.render "#{format}/index",
           artist: artist
           carousel: carousel
           tab: tab


test child in anchor

test child in anchor

--- spec/javascripts/manuscript_manager_spec.js.coffee
+++ spec/javascripts/manuscript_manager_spec.js.coffee
@@ -28,12 +28,17 @@
           taskPath: "/papers/1/tasks/1"
         @component = Tahi.manuscriptManager.Card task: task

-      it "renders the Cards", ->
-        result = @component.render()
-        cardAnchor = result.props.children[0].props
-        expect(cardAnchor.className).toEqual "card"
-        expect(cardAnchor['data-card-name']).toEqual "upload-manuscript"
-        expect(cardAnchor['data-task-id']).toEqual 1
-        expect(cardAnchor['data-task-path']).toEqual "/papers/1/tasks/1"
+      context "the anchor tag", ->
+        beforeEach ->
+          result = @component.render()
+          @cardAnchor = result.props.children[0].props

+        it "rendering", ->
+          expect(@cardAnchor.className).toEqual "card"
+          expect(@cardAnchor['data-card-name']).toEqual "upload-manuscript"
+          expect(@cardAnchor['data-task-id']).toEqual 1
+          expect(@cardAnchor['data-task-path']).toEqual "/papers/1/tasks/1"

+        it "child glyphicon", ->
+          glyphOk = @cardAnchor.children[0]
+          expect(glyphOk.props.className.match("glyphicon glyphicon-ok")).toBeTruthy()


Handle user verification error

Handle user verification error

--- resources/assets/coffee/react/_components/friend-button.coffee
+++ resources/assets/coffee/react/_components/friend-button.coffee
@@ -63,7 +63,7 @@
         type: "DELETE"
         url: laroute.route 'friends.destroy', friend: @props.user_id
         success: @updateFriends
-        error: osu.ajaxError
+        error: osu.emitAjaxError(@button)
         complete: @requestDone
     else
       #friending
@@ -71,7 +71,7 @@
         type: "POST"
         url: laroute.route 'friends.store', target: @props.user_id
         success: @updateFriends
-        error: osu.ajaxError
+        error: osu.emitAjaxError(@button)
         complete: @requestDone

@@ -108,6 +108,7 @@
       onMouseEnter: @hover
       onMouseLeave: @unhover
       onClick: @clicked
+      ref: (el) => @button = el
       title: if @state.friend then osu.trans('friends.buttons.remove') else osu.trans('friends.buttons.add')
       disabled: @state.loading
       if @state.loading


Add option to disable source maps.

Add option to disable source maps.

--- src/cli.coffee
+++ src/cli.coffee
@@ -28,6 +28,7 @@
     -o, --output <file>          write bundle to file instead of stdout, {} may be used as a placeholder
     -p, --prelude <file>         file to use as prelude
         --no-prelude             exclude prelude from bundle
+        --no-source-map          disable source maps
         --prelude-only           only output prelude
     -s, --strict                 add "use strict" to each bundled module
         --strip-debug            strip `alert`, `console`, `debugger` statements
@@ -61,6 +62,7 @@
   minify:     false
   output:     []
   prelude:    null
+  sourceMap:  true
   strict:     false
   stripDebug: false
   watch:      false
@@ -96,6 +98,8 @@
       opts.prelude = args.shift()
     when '--no-prelude'
       opts.prelude = false
+    when '--no-source-map'
+      opts.sourceMap = false
     when '--prelude-only'
       opts.preludeOnly = true
     when '-s', '--strict'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -33,7 +33,7 @@
       yourObject.bind "anotherCustomEvent", ->
         doSomething()

-  @name bind
+  @name on
   @methodOf Bindable#
   @param {String} event The event to listen to.
   @param {Function} callback The function to be called when the specified event


enable uglifyify, don't mangle variables

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -81,8 +81,8 @@
 scripts = (isWatch) ->
   ->
     setup = (bundler) ->
-      #if isDeploy(nodeEnv)
-      #  bundler.transform(global: true, 'uglifyify')
+      if isDeploy(nodeEnv)
+        bundler.transform(global: true, mangle: false, 'uglifyify')
       bundler

     bundle = (bundler) ->


uprev to 0.2.5 for deploy with new tests

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -48,6 +48,7 @@
       extensions: [ '.coffee' ]
       standalone: NAME
     }
+    .transform 'coffeeify'
     .bundle()
       .on 'error', gutil.log
     .pipe source DIST+'.js'


Added new fields

Added new fields

--- snippets/php_fields.cson
+++ snippets/php_fields.cson
@@ -9,9 +9,63 @@
   'TextField':
     'prefix': 'textfield'
     'body': 'TextField::create(\'${1:name}\',\'${2:title}\')$0'
+  'ReadonlyField':
+    'prefix': 'readonlyfield'
+    'body': 'ReadonlyField::create(\'${1:name}\',\'${2:title}\')$0'
+  'NumericField':
+    'prefix': 'numericfield'
+    'body': 'NumericField::create(\'${1:name}\',\'${2:title}\')$0'
+  'NumericField_Readonly':
+    'prefix': 'numericfield_readonly'
+    'body': 'NumericField_Readonly::create(\'${1:name}\',\'${2:title}\')$0'
+  'CheckboxField':
+    'prefix': 'checkboxfield'
+    'body': 'CheckboxField::create(\'${1:name}\',\'${2:title}\')$0'
+  'CheckboxField_Readonly':
+    'prefix': 'checkboxfield_readonly'
+    'body': 'CheckboxField_Readonly::create(\'${1:name}\',\'${2:title}\')$0'
+  'NullableField':
+    'prefix': 'nullablefield'
+    'body': 'NullableField::create(\'${1:name}\',\'${2:title}\')$0'
   'TextareaField':
     'prefix': 'textareafield'
     'body': 'TextareaField::create(\'${1:name}\',\'${2:title}\')$0'
+  'DropdownField':
+    'prefix': 'dropdownfield'
+    'body': """
+    DropdownField::create(
+    \t\'${1:name}\',
+    \t\'${2:title}\',
+    \t\'${3:datalist}\'
+    )$0
+    """
+  'LookupField':
+    'prefix': 'lookupfield'
+    'body': """
+    LookupField::create(
+    \t\'${1:name}\',
+    \t\'${2:title}\',
+    \t\'${3:datalist}\'
+    )$0
+    """
+  'ListboxField':
+    'prefix': 'listboxfield'
+    'body': """
+    ListboxField::create(
+    \t\'${1:name}\',
+    \t\'${2:title}\',
+    \t\'${3:datalist}\'
+    )$0
+    """
+  'GroupedDropdownField':
+    'prefix': 'groupeddropdownfield'
+    'body': """
+    GroupedDropdownField::create(
+    \t\'${1:name}\',
+    \t\'${2:title}\',
+    \t\'${3:datalist}\'
+    )$0
+    """
   'OptionsetField':
     'prefix': 'optionsetfield'
     'body': """
@@ -21,15 +75,21 @@
     \t\'${3:datalist}\'
     )$0
     """
-  'DropdownField':
-    'prefix': 'dropdownfield'
+  'CheckboxSetField':
+    'prefix': 'checkboxsetfield'
     'body': """
-    DropdownField::create(
+    CheckboxSetField::create(
     \t\'${1:name}\',
     \t\'${2:title}\',
     \t\'${3:datalist}\'
     )$0
     """
+  'HeaderField':
+    'prefix': 'headerfield'
+    'body': 'HeaderField::create(\'${1:name}\',\'${2:title}\')$0'
+  'HiddenField':
+    'prefix': 'hiddenfield'
+    'body': 'HiddenField::create(\'${1:name}\',\'${2:title}\')$0'
   'GridField':
     'prefix': 'gridfield'
     'body': """


Implemented PPU clipping.

Implemented PPU clipping.

--- src/PPU.coffee
+++ src/PPU.coffee
@@ -90,12 +90,12 @@
         value

     setMask: (value) ->
-        @monochromeMode            =  value        & 0x01  # M[0]
-        @noSpriteClipping          = (value >>> 1) & 0x01  # M[1]
-        @noBackgroundClipping      = (value >>> 2) & 0x01  # M[2]
-        @backgroundVisible         = (value >>> 3) & 0x01  # M[3]
-        @spritesVisible            = (value >>> 4) & 0x01  # M[4]
-        @backgroundColorsIntensity = (value >>> 5)         # M[7-5]
+        @monochromeMode            =    value        & 0x01  #  M[0]
+        @spriteClipping            = !((value >>> 1) & 0x01) # !M[1]
+        @backgroundClipping        = !((value >>> 2) & 0x01) # !M[2]
+        @backgroundVisible         =   (value >>> 3) & 0x01  #  M[3]
+        @spritesVisible            =   (value >>> 4) & 0x01  #  M[4]
+        @backgroundColorsIntensity =   (value >>> 5)         #  M[7-5]

     ###########################################################
     # Status register
@@ -305,8 +305,8 @@
         @framePosition++ # Skip alpha because it was already set to 0xFF.

     renderFramePixel: ->
-        backgroundColor = if @backgroundVisible then @renderBackgroundPixel() else 0
-        spriteColor = if @spritesVisible then @renderSpritePixel() else 0
+        backgroundColor = @renderBackgroundPixel()
+        spriteColor = @renderSpritePixel()
         if (spriteColor & 0x03) and (backgroundColor & 0x03) # Both bagckground and sprite pixels are visible
             @spriteZeroHit |= @spriteZeroRendered
             if @spriteInFront then spriteColor else backgroundColor
@@ -386,6 +386,7 @@

     renderBackgroundPixel: ->
         @fetchPattern() if @fineXScroll is 0 # When coarse scroll X was incremented.
+        return 0 if not @backgroundVisible or @cycle < 9 and @backgroundClipping
         bitNumber = @fineXScroll ^ 0x07 # 7 - fineXScroll
         colorSelect1 =  (@patternLayer1Row >>> bitNumber) & 0x01
         colorSelect2 = ((@patternLayer2Row >>> bitNumber) & 0x01) << 1
@@ -417,9 +418,10 @@
     renderSpritePixel: ->
         # TODO rewrite and optimize this
         @fetchSprites() if @cycle is 1
+        return 0 if not @spritesVisible or @cycle < 9 and @spriteClipping
         @spriteZeroRendered = false
-        currentX = @cycle
-        currentY = @scanline
+        currentX = @cycle - 1
+        currentY = @scanline - 1
         heightMask = if @bigSprites then 0x0F else 0x07
         for address in @spriteAddresses
             innerX = currentX - @objectAttributeMemory[address + 3]
@@ -451,8 +453,8 @@
     fetchSprites: ->
         @spriteScalineOverflow = 0
         @spriteAddresses = []
-        bottomY = @scanline
-        topY = Math.max 0, @scanline - (if @bigSprites then 16 else 8)
+        bottomY = @scanline - 1
+        topY = Math.max 0, bottomY - (if @bigSprites then 16 else 8)
         for spriteY, address in @objectAttributeMemory by 4
             if topY < spriteY <= bottomY
                 @spriteAddresses.push address


Added request to download function

Added request to download function

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -14,3 +14,4 @@
 # a file from url to destination path.
 download = (uri, file, callback) ->
   winston.info('Download started.')
+  request.head uri, (error, response, content) ->


Cleanup open and etc..

Cleanup open and etc..

--- src/commands.coffee
+++ src/commands.coffee
@@ -34,16 +34,26 @@
         _.each pullRequests, (pr, i) =>
           console.log "#{i + 1}: [#{pr.head.repo.name} @#{pr.user.login}]\t#{pr.title}"

+        console.log ""
+
         promptly.choose "ping to which? ", [1..pullRequests.length], (err, value) =>
           @_postComment pullRequests[value - 1]

-  open: (number) =>
+  open: (index) =>
     @_list (pullRequests) =>
-      if @all
+      if @program.all
         for pr in pullRequests
           open pr.html_url
+      else if index?
+        open pullRequests[index - 1].html_url
       else
-        open pullRequests[number].html_url
+        _.each pullRequests, (pr, i) =>
+          console.log "#{i + 1}: [#{pr.head.repo.name} @#{pr.user.login}]\t#{pr.title}"
+
+        console.log ""
+
+        promptly.choose "browse which? ", [1..pullRequests.length], (err, value) =>
+          open pullRequests[value - 1].html_url

   _list: (fn) =>
     async.map @config.repos, (repo, callback) =>
@@ -53,10 +63,8 @@
         repo: r
       }, callback
     , (err, results) ->
-      a = _.flatten _.map results, (rows) ->
-        _.filter rows, (row) -> row.url
-
-      do (a) -> fn a
+      rows = _.flatten results
+      do (rows) -> fn rows

   _postComment: (pr) =>
     [user, repo] = pr.base.repo.full_name.split('/')


Support for embedded formats

Support for embedded formats

--- src/creative.coffee
+++ src/creative.coffee
@@ -4,7 +4,7 @@
   constructor: (@format, @creative, @placement) ->

   render: ->
-    InfeedlTemplates[@format].render(@_interpolations())
+    InfeedlTemplates[@format.replace("embedded_", "")].render(@_interpolations())

   click: (evt) ->
     if @creative.content.embedded


mark polling endpoint to not display errors

--- src/api.coffee
+++ src/api.coffee
@@ -287,7 +287,7 @@
     url: "/api/courses/#{courseId}/dashboard"

   apiHelper NotificationActions, NotificationActions.loadUpdates, NotificationActions.loadedUpdates, 'GET', ->
-    url: "/api/notifications"
+    displayError: false, url: "/api/notifications"

   CurrentUserActions.logout.addListener 'trigger', ->
     # Logging out programatically needs to be done via a form submission or follow redirects


Fix the stalk feature.

Fix the stalk feature.

--- src/bot.coffee
+++ src/bot.coffee
@@ -363,8 +363,9 @@
       self.directoryGraph (directoryGraphData) ->
         if not directoryGraphData.success
           return callback directoryGraphData
-        rooms = directoryGraphData.rooms
-        for room, users in rooms
+        for graphObj in directoryGraphData.rooms
+          room = graphObj[0]
+          users = graphObj[1]
           for user in users
             if user.userid == userId
               if allInfos


cool that looked like it fixed it

cool that looked like it fixed it

--- assets/js/teacher.coffee
+++ assets/js/teacher.coffee
@@ -28,7 +28,7 @@
         id_rgx = new RegExp(id, 'g') #make sure we don't add the same id twice
         nm_rgx = new RegExp(nick, 'g')
         if !_.any( _.map( $('.container').find('.student-box'), (el) -> (id_rgx.test $(el).text()) and (nm_rgx.test $(el).text()) ) )
-            if idx % 4 == 0
+            if idx % 2 == 0
                 current_row = $("

")
                 $("#student-container").append current_row
             if !current_row
@@ -37,7 +37,7 @@
             current_row.append $("

#{nick}

-                                       <textarea rows=10 class='span6' id='text-#{id}'></textarea>
+                                       <textarea rows=10 class='span10' id='text-#{id}'></textarea>

#{id}

                                     #{prog}


fix caching of suggestions

fix caching of suggestions

--- services/spelling/app/coffee/ASpell.coffee
+++ services/spelling/app/coffee/ASpell.coffee
@@ -13,8 +13,12 @@
 			suggestions = @getSuggestions(output)
 			results = []
 			for word, i in words
-				if suggestions[word]?
-					cache.set(language + ':' + word, suggestions[word])
+				key = language + ':' + word
+				cached = cache.get(key)
+				if cached?
+					results.push index: i, suggestions: cached
+				else if suggestions[word]?
+					cache.set(key, suggestions[word])
 					results.push index: i, suggestions: suggestions[word]
 			callback null, results

@@ -51,13 +55,13 @@

 		newWord = {}
 		for word in words
-			newWord[word] = true if !newWord[word] && !cache.get(language + ':' + word)?
+			newWord[word] = true if !newWord[word] && !cache.has(language + ':' + word)
 		words = Object.keys(newWord)

 		if words.length
 			WorkerPool.check(language, words, ASpell.ASPELL_TIMEOUT, callback)
 		else
-			callback null, []
+			callback null, ""

 module.exports = ASpell =
 	# The description of how to call aspell from another program can be found here:
@@ -69,4 +73,3 @@
 	ASPELL_TIMEOUT : 4000

 WorkerPool = new ASpellWorkerPool()
-


Load accessibility properties for radius filter.

--- src/app.coffee
+++ src/app.coffee
@@ -341,7 +341,7 @@

             unitList = new models.UnitList [], pageSize: PAGE_SIZE
                 .setFilter 'only', 'name,location,root_services'
-                .setFilter 'include', 'services'
+                .setFilter 'include', 'services,accessibility_properties'
                 .setFilter 'lat', pos.get('location').coordinates[1]
                 .setFilter 'lon', pos.get('location').coordinates[0]
                 .setFilter 'distance', radius


flesh out descriptive text for various layers. Progress #5

--- src/app.coffee
+++ src/app.coffee
@@ -24,7 +24,15 @@
       typeName: "azgs:earthfissures"
       active: false
       useD3: true
-      description: '
Layer Description

Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep. In Arizona, earth fissures form as a result of land subsidence driven by groundwater withdrawal. The Arizona Geological Survey has mapped and reported earth fissures throughout south-central and southeastern Arizona since the 1990s and published earth fissure study area maps in accordance with Arizona Revised Statute § 27-152.01(3) since 2007.  Mapping of fissures involves incorporating mapped earth fissures from previous studies, examining remotely sensed subsidence data and aerial imagery, and on-site field mapping using high-precision GPS receivers.

Between 2007 and 2012, AZGS identified 24 discreet areas throughout southern and central Arizona with concentrations of earth fissures totaling more than 150 miles of mapped fissures. These include uninterrupted open fissures and fissure gullies and discontinuous linear trends of potholes, abbreviated open cracks, elongated depressions, and other collapse features. An additional 180 miles of reported/unconfirmed earth fissures appear on AZGS maps. These features include fissures previously mapped by professional geologists appearing in published maps or figures which could not be confirmed through recent surface mapping by AZGS Geologists.

A reasonable effort was made to identify all earth fissures in the study area although some fissures may remain unmapped due to overprinting or disturbance by development, agricultural tilling, or lack of surface expression along the fissure at the time mapping was conducted. In addition, earth fissures are not static features. They may change in width, depth or length, and new fissures may appear after an area has been mapped. For these reasons the State of Arizona does not guarantee the maps are error free. Blank areas within study area boundaries have been investigated and no surface evidence of fissures was found as of the date of map publication.

For additional information on the origin and impacts of earth fissures and free PDF maps of earth fissure study areas, visit the Arizona Geological Survey’s Earth Fissure Center.
'
+      citation: "Arizona Geological Survey"
+      details: '
Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep. In Arizona, earth fissures form as a result of land subsidence driven by groundwater withdrawal. The Arizona Geological Survey has mapped and reported earth fissures throughout south-central and southeastern Arizona since the 1990s and published earth fissure study area maps in accordance with Arizona Revised Statute § 27-152.01(3) since 2007.  Mapping of fissures involves incorporating mapped earth fissures from previous studies, examining remotely sensed subsidence data and aerial imagery, and on-site field mapping using high-precision GPS receivers.

Between 2007 and 2012, AZGS identified 24 discreet areas throughout southern and central Arizona with concentrations of earth fissures totaling more than 150 miles of mapped fissures. These include uninterrupted open fissures and fissure gullies and discontinuous linear trends of potholes, abbreviated open cracks, elongated depressions, and other collapse features. An additional 180 miles of reported/unconfirmed earth fissures appear on AZGS maps. These features include fissures previously mapped by professional geologists appearing in published maps or figures which could not be confirmed through recent surface mapping by AZGS Geologists.

A reasonable effort was made to identify all earth fissures in the study area although some fissures may remain unmapped due to overprinting or disturbance by development, agricultural tilling, or lack of surface expression along the fissure at the time mapping was conducted. In addition, earth fissures are not static features. They may change in width, depth or length, and new fissures may appear after an area has been mapped. For these reasons the State of Arizona does not guarantee the maps are error free. Blank areas within study area boundaries have been investigated and no surface evidence of fissures was found as of the date of map publication.

For additional information on the origin and impacts of earth fissures and free PDF maps of earth fissure study areas, visit the Arizona Geological Survey’s Earth Fissure Center.
'
+      brief: 'Earth fissures are open surface fractures that may be as much as a mile in length, up to 15 ft wide, and 10s of feet deep.'
+      links: [
+          text: "AZGS Earth Fissure Center"
+          url: "http://www.azgs.az.gov/EFC.shtml"
+      ]
+      lastUpdate: "Summer, 2013"
+      description: '
Layer Description
'
       legend: new app.models.Legend [
           caption: "Continuous Earth Fissure"
           attribute: "fisstype"
@@ -64,7 +72,12 @@
       typeName: "azgs:activefaults"
       active: false
       useD3: true
-      description: '
Layer Description

This layer summarizes available data on active Arizona faults. If faults have been active during the past 2.5 million years (Quaternary period), then we consider that there is some chance they could generate large earthquakes. Generally, the more active the fault zone is, the more likely it is to generate earthquakes, and earthquakes are more likely to occur in regions with many Quaternary faults.

These data were originally compiled in 1998 as part of an effort coordinated by the USGS to compile data and map information on Quaternary faults throughout the world. The database has recently been revised with much more accurate fault mapping, incorporation of new data on fault activity, and inclusion of additional fault zones, primarily in northern Arizona. This database depends heavily on several previous compilations of data on Quaternary faults in Arizona. The first and most comprehensive of these is the state-wide compilation of neotectonic faults by Menges and Pearthree (1983), supplemented by a state-wide compilation of young faults by Euge and others (1992). More detailed regional geologic maps provided most of the data in northern Arizona and southeastern Arizona. The database includes information from all detailed investigations of fault zones in Arizona.

The data structure is set up to provide systematic information on each fault zone. Fault names are based primarily on published maps or reports. In cases where different names have been used for the fault, the alternative names are listed within the database. All of the faults are listed by name and number in the table on the following page. This table indicates where the data summary for each fault can be found, as well as the age of youngest activity and fault slip rate category. The individual fault data sheets include information on map and data sources, fault location, geologic setting of the fault, the geomorphic expression of the fault, recency of fault movement, fault slip rate(s), and fault zone length and orientation. Faults are grouped into slip rate categories of <0.02 mm/yr, < 0.2 mm/yr, and <1 mm/yr. Most faults in Arizona fall into the lowest slip-rate category, with a few faults in the higher categories. Reported lengths are for the whole fault zone, not cumulative length of each individual fault in the zone, and orientations are averages for the fault zone.

Citations:

Euge, K.M., Schell, B.A. and Lam, I.P., 1992, Development of seismic acceleration maps for Arizona: Arizona Department of Transportation Report No. AZ92-344, 327 p., 5 sheets, scale 1:1,000,000.

Menges, C.M. and Pearthree, P.A., 1983, Map of neotectonic (latest Pliocene - Quaternary) deformation in Arizona: Arizona Bureau of Geology and Mineral Technology Open-File Report 83-22, 48 p., map scale 1:500,000, 4 map sheets.
'
+      citation: "Arizona Geological Survey"
+      details: '
This layer summarizes available data on active Arizona faults. If faults have been active during the past 2.5 million years (Quaternary period), then we consider that there is some chance they could generate large earthquakes. Generally, the more active the fault zone is, the more likely it is to generate earthquakes, and earthquakes are more likely to occur in regions with many Quaternary faults.

These data were originally compiled in 1998 as part of an effort coordinated by the USGS to compile data and map information on Quaternary faults throughout the world. The database has recently been revised with much more accurate fault mapping, incorporation of new data on fault activity, and inclusion of additional fault zones, primarily in northern Arizona. This database depends heavily on several previous compilations of data on Quaternary faults in Arizona. The first and most comprehensive of these is the state-wide compilation of neotectonic faults by Menges and Pearthree (1983), supplemented by a state-wide compilation of young faults by Euge and others (1992). More detailed regional geologic maps provided most of the data in northern Arizona and southeastern Arizona. The database includes information from all detailed investigations of fault zones in Arizona.

The data structure is set up to provide systematic information on each fault zone. Fault names are based primarily on published maps or reports. In cases where different names have been used for the fault, the alternative names are listed within the database. All of the faults are listed by name and number in the table on the following page. This table indicates where the data summary for each fault can be found, as well as the age of youngest activity and fault slip rate category. The individual fault data sheets include information on map and data sources, fault location, geologic setting of the fault, the geomorphic expression of the fault, recency of fault movement, fault slip rate(s), and fault zone length and orientation. Faults are grouped into slip rate categories of <0.02 mm/yr, < 0.2 mm/yr, and <1 mm/yr. Most faults in Arizona fall into the lowest slip-rate category, with a few faults in the higher categories. Reported lengths are for the whole fault zone, not cumulative length of each individual fault in the zone, and orientations are averages for the fault zone.

Citations:

Euge, K.M., Schell, B.A. and Lam, I.P., 1992, Development of seismic acceleration maps for Arizona: Arizona Department of Transportation Report No. AZ92-344, 327 p., 5 sheets, scale 1:1,000,000.

Menges, C.M. and Pearthree, P.A., 1983, Map of neotectonic (latest Pliocene - Quaternary) deformation in Arizona: Arizona Bureau of Geology and Mineral Technology Open-File Report 83-22, 48 p., map scale 1:500,000, 4 map sheets.
'
+      brief: 'Faults that are known to have been active within the last 2.5 million years (Quaternary period), and thus have some chance that they could generate a large earthquake.'
+      links: []
+      lastUpdate: "Fall, 2012"
+      description: '
Layer Description
'
       legend: new app.models.Legend [
           caption: "Within 10,000 years"
           attribute: "symbol"
@@ -101,7 +114,15 @@
       serviceUrl: app.geoserverUrl
       serviceType: "WFS"
       typeName: "azgs:earthquakedata"
-      description: '
Layer Description

The earthquakes displayed are from the AZGS Earthquake Catalog, and are the minimum number of earthquakes that have occurred in historical times.  Older events are represented by estimated Modified Mercalli Intensity Scale (MMI) roman-numeral values which reflect the amount of shaking experienced by those who felt and reported the earthquake. For a full description of the MMI scale, please refer to: http://earthquake.usgs.gov/learn/topics/mercalli.php

The earthquake layer in the AZGS Hazard Viewer includes an epicentral location denoted in Latitude and Longitude, depth (in kilometers), date and time (UTC; -7:00 for MST). There are several magnitude scales employed to estimate the size (i.e., energy release) of an earthquake, such as Md - duration magnitude, ML - local magnitude, Mw - moment magnitude.  AZGS calculates duration magnitude, Md; however, the catalog includes all three types of magnitude scales depending on who located the event. For example, northern Arizona earthquakes located by the University of Utah are frequently assigned a local magnitude (Ml).  Sources of earthquake data reported in the AZGS Earthquake Catalog, include: AZGS (Arizona Geological Survey), USGS (United States Geological Survey), AEIC (Arizona Earthquake Information Center), UU (University of Utah), CI (California Integrated Seismic Network), and ASU (thesis work by Lockridge, Arizona State University). The earthquake layer does not include small magnitude events (< 2.0) because of the difficulty inherent in identifying and locating such events.
'
+      citation: "Arizona Geological Survey"
+      details: '
The earthquakes displayed are from the AZGS Earthquake Catalog, and are the minimum number of earthquakes that have occurred in historical times.  Older events are represented by estimated Modified Mercalli Intensity Scale (MMI) roman-numeral values which reflect the amount of shaking experienced by those who felt and reported the earthquake. For a full description of the MMI scale, please refer to: http://earthquake.usgs.gov/learn/topics/mercalli.php

The earthquake layer in the AZGS Hazard Viewer includes an epicentral location denoted in Latitude and Longitude, depth (in kilometers), date and time (UTC; -7:00 for MST). There are several magnitude scales employed to estimate the size (i.e., energy release) of an earthquake, such as Md - duration magnitude, ML - local magnitude, Mw - moment magnitude.  AZGS calculates duration magnitude, Md; however, the catalog includes all three types of magnitude scales depending on who located the event. For example, northern Arizona earthquakes located by the University of Utah are frequently assigned a local magnitude (Ml).  Sources of earthquake data reported in the AZGS Earthquake Catalog, include: AZGS (Arizona Geological Survey), USGS (United States Geological Survey), AEIC (Arizona Earthquake Information Center), UU (University of Utah), CI (California Integrated Seismic Network), and ASU (thesis work by Lockridge, Arizona State University). The earthquake layer does not include small magnitude events (< 2.0) because of the difficulty inherent in identifying and locating such events.
'
+      brief: 'The earthquakes displayed are from the AZGS Earthquake Catalog, and are the minimum number of earthquakes that have occurred in the historical period dating to about 1850.'
+      links: [
+          text: "Description of Modified Mercalli Intensity Scale"
+          url: "http://earthquake.usgs.gov/learn/topics/mercalli.php"
+      ]
+      lastUpdate: "Aug. 7, 2013"
+      description: '
Layer Description
'
       filterClause: "&filter=#{(new app.models.Filter([{calculated_magnitude: "[2.0,9.0]"}])).urlEncoded()}"
       active: false
       legend: new app.models.Legend [
@@ -234,7 +255,14 @@
       wfsUrl: "http://data.usgin.org/arizona/ows"
       typeName: "azgs:floods"
       active: false
-      description: '
Layer Description

This layer shows areas with potentially High and Medium Risk of flooding. Data are drawn from the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010. High flooding potential includes areas covered by all of the “A” Special Flood Hazard zones. These areas have a 1% annual chance of flooding, also known as the 100-year flood, and a 26% chance of flooding within the span of a 30-year mortgage. Medium flood potential includes  areas within the “Shaded X” zones. These are areas between the limits of the 100-year and 500-year flood zones.

The flood potential layer was created by JE Fuller/Hydrology and Geomorpholgy, Inc. for the Arizona Division of Emergency Management (ADEM), as part of a larger study for the 2010 State of Arizona Multi-Hazard Mitigation Plan (http://www.dem.azdema.gov/operations/mitigation/hazmitplan/hazmitplan.html). Data is current as of May 2010. These data are provided as guidance only. Local Flood Control Districts should be consulted for additional or more detailed information.
'
+      citation: "Arizona Division of Emergency Management"
+      details: '
This layer shows areas with potentially High and Medium Risk of flooding. Data are drawn from the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010. High flooding potential includes areas covered by all of the “A” Special Flood Hazard zones. These areas have a 1% annual chance of flooding, also known as the 100-year flood, and a 26% chance of flooding within the span of a 30-year mortgage. Medium flood potential includes  areas within the “Shaded X” zones. These are areas between the limits of the 100-year and 500-year flood zones.

The flood potential layer was created by JE Fuller/Hydrology and Geomorpholgy, Inc. for the Arizona Division of Emergency Management (ADEM), as part of a larger study for the 2010 State of Arizona Multi-Hazard Mitigation Plan. Data is current as of May 2010. These data are provided as guidance only. Local Flood Control Districts should be consulted for additional or more detailed information.
'
+      brief: 'Areas with High and Medium flooding potential as represented by the 100- and 500- flood zones determined by the Federal Emergency Management Agency (FEMA) digital flood insurance rate maps (DFIRM) database, dated May 2010.'
+      links: [
+          text: "2010 State of Arizona Multi-Hazard Mitigation Plan"
+          url: "http://www.dem.azdema.gov/operations/mitigation/hazmitplan/hazmitplan.html"
+      ]
+      lastUpdate: "May, 2010"
       legend: new app.models.Legend [
           caption: "High"
           imageTemplateId: "floodImage"
@@ -261,6 +289,7 @@
       details: '
The Fire Risk Index (FRI) layer, as shown here, depicts relative risks of areas susceptible to wildfires with 1 (dark green) representing the lowest risk and 9 (dark red) representing the highest risk. The data was developed through the West Wide Wildfire Risk Assessment (WWA) project to identify areas susceptible to wildfires in 17 western states and some U.S. affiliated Pacific Islands. The Oregon Department of Forestry (2013) implemented this project on behalf of the Council of Western State Foresters and the Western Forestry Leadership Coalition. The goal of the project was to provide a wildfire risk assessment appropriate for comparing areas at risks to wildfires across geographic regions, or within individual states, and to aid in mitigation of areas at risk, to identify the level of risks within communities and to communicate those risks to the public.

The FRI layer was created from the Fire Effect Index (FEI) and Fire Threat Index (FTI). FEI identifies areas that have important values at risk to wildfire and where wildland fires would be difficult and/or costly to suppress. FTI describes the likelihood of an acre burning and the expected final fire size based on conditions of fuels and potential fire behavior under different weather scenarios. FEI and FTI data were combined to create the FRI layer which describes relative probabilities of areas at risk to wildfires. The data used to develop FEI, FTI and FRI reflects conditions between 2008 and 2010, depending on the type of data (i.e. fuels, wildland development areas, and historic fire locations, etc.).

Disclaimer

The Oregon Department of Forestry implemented conducting this assessment on behalf of the Council of Western State Foresters with funding from the USDA Forest Service. Anyone utilizing this layer is asked to credit the Oregon Department of Forestry. Users must read and fully comprehend the metadata prior to data use. The spatial data to develop this layer were derived from a variety of sources. Care was taken in the creation of these themes, but they are provided "as is." The Oregon Department of Forestry, State of Oregon, WWA Project Partners, or any of the data providers cannot accept any responsibility for errors, omissions, or positional accuracy in the digital data or underlying records. There are no warranties, expressed or implied, including the warranty of merchantability or fitness for a particular purpose, accompanying any of these products.

The West Wide Risk Assessment was conducted to support strategic planning at regional, state, and landscape scale. WWA data is intended for planning purposes only and should not to be used for engineering or legal purposes. Further investigation by local and regional experts should be conducted to inform decisions regarding local applicability. It is the sole responsibility of the local user, using product metadata and local knowledge, to determine if and/or how WWA data can be used for particular areas of interest. It is the responsibility of the user to be familiar with the value, assumptions, and limitations of WWA products. Managers and planners must evaluate WWA data according to the scale and requirements specific to their needs. Please note that the WWA Published Results may not match other assessments conducted that use different data, technical methods, or scale of analysis. Having two assessments that do not match does not mean that either one of them is incorrect. The use of different data sources, often from different collection dates and with spatial accuracy and resolutions, combined with different modeling assumptions or definitions will result in different results and can have different interpretations and uses. The WWA results are not intended to replace local and state products as a decision-making tool. The WWA is meant to serve as a regional policy analysis tool that provides results comparable across geographic areas in the West.
',
       brief: 'Shows the relative risks of wildfire based on values at risk (i.e. development, infrastructure, etc.), the likelihood of an acre to burn, the expected final fire size based on fuels conditions and potential fire behavior and the difficulty or expense of suppression.'
       links: [{"text": "ADEM EIN Page", "url": ""}, {"text": "Be prepared pamphlet", "url": ""}]
+      lastUpdate: "Fall 2013"
       active: false
       legend: new app.models.Legend [
           caption: "Lowest"
@@ -312,16 +341,6 @@
         heading: "Relative Risk"
         filterable: false
 ]
-
-###,
-  new app.models.TileLayer
-    id: "femaFloods"
-    layerName: "FEMA Flood Hazards"
-    url: "http://atlas.resources.ca.gov/arcgis/rest/services/GeoScience/Flood_Risk_FEMA/MapServer/tile/{z}/{y}/{x}"
-    downloadUrlTemplate: "problematic"
-    description: '
Layer Description

Senate Bill 5 (SB 5), which was enacted in 2007, authorized the California Department of Water Resources (DWR) to develop the Best Available Maps (BAM) displaying 100- and 200-year floodplains for areas located within the Sacramento-San Joaquin (SAC-SJ) Valley watershed. SB 5 requires that these maps contain the best available information on flood hazards and be provided to cities and counties in the SAC-SJ Valley watershed. This effort was completed by DWR in 2008. DWR has expanded the BAM to cover all counties in the State and to include 500-year floodplains.. The 100-, 200-, and 500-year floodplains are displayed on this web viewer. The web viewer allows users to view a particular area and identify their potential flood hazards. The following is a comprehensive list of the floodplains that are displayed on the web viewer and may be updated periodically. * 100-Year Floodplains * Federal Emergency Management Agency (FEMA )Digital Flood Insurance Rate Map (DFIRM) * Effective * Preliminary * FEMA Q3 Effective Flood Data (for areas lacking Effective DFIRM coverage) * DWR Awareness Mapping * United States Army Corps of Engineers (USACE) Sacramento and San Joaquin River Basins Comprehensive Study * Regional/Special Studies * 200-Year Floodplains * USACE Sacramento and San Joaquin River Basins Comprehensive Study * 500-Year Floodplains (for web viewer) * FEMA DFIRM * Effective * Preliminary * FEMA Q3 Effective Flood Data (for areas lacking Effective DFIRM coverage) * USACE Sacramento and San Joaquin River Basins Comprehensive Study * Regional/Special Studies Disclaimer The BAM does not replace existing FEMA regulatory floodplains shown on Flood Insurance Rate Maps (FIRM). For more information on the FEMA regulatory floodplains, please contact FEMA directly. The BAM floodplains identify potential flood risks that may warrant further studies or analyses for land use decision making. The floodplains shown delineate areas with potential exposure to flooding for three different storm events: one with storm flows that have a 1% chance of being equaled or exceeded in any year (100-year), one with storm flows that have a 0.5% chance of being equaled or exceeded in any year (200-year), and one with storms flows that have a 0.2% chance of being equaled or exceeded in any year (500-year). These flows and resulting flooded area are based on the best available floodplain information and may not identify all areas subject to flooding.

Copyright © FEMA
'
-    legend: new app.models.Legend [], { heading: "TBD", filterable: false }
-###

 app.dataLayerCollection = new app.models.LayerCollection dataLayers

@@ -350,15 +369,6 @@

 app.baseLayerCollection = new app.models.LayerCollection baseLayers

-###
-  new app.models.NavToolModel
-    id: "addLayer"
-    toolName: "Add Layer"
-    modalName: "Add a Layer"
-    modalBody: "Not Implemented Yet"
-,
-###
-
 navTools = [
     new app.models.NavToolModel
       id: "print"


marionette region updated API

marionette region updated API

--- src/app.coffee
+++ src/app.coffee
@@ -533,7 +533,7 @@
         showButton = =>
             tourButtonView = new TourStartButton()
             app.getRegion('tourStart').show tourButtonView
-            @listenToOnce tourButtonView, 'close', => app.getRegion('tourStart').reset()
+            @listenToOnce tourButtonView, 'close', => app.getRegion('tourStart').empty()
         if p13n.get('skip_tour')
             showButton()
         @listenTo p13n, 'tour-skipped', =>


Clean up the scraper

Clean up the scraper

--- API/src/scraper.coffee
+++ API/src/scraper.coffee
@@ -9,16 +9,16 @@
 app = express()
 cache = new NodeCache()

+# Zollstock
 zollstock_urls = ["http://www.tierheim-koeln-zollstock.de/tiervermittlung/katzen.html",
               "http://www.tierheim-koeln-zollstock.de/tiervermittlung/hunde.html",
               "http://www.tierheim-koeln-zollstock.de/tiervermittlung/nagetiere.html"];
 zollstock_base_url = "http://www.tierheim-koeln-zollstock.de"

-# Mittelgrosse Hunde, Katzen und Kaninchen gehen aktuell nicht :(
+#Dellbrück
 dellbrueck_urls = ["http://presenter.comedius.de/design/bmt_koeln_standard_10001.php?f_mandant=bmt_koeln_d620d9faeeb43f717c893b5c818f1287&f_bereich=Vermittlung+kleine+Hunde+&f_funktion=Uebersicht",
               "http://presenter.comedius.de/design/bmt_koeln_standard_10001.php?f_mandant=bmt_koeln_d620d9faeeb43f717c893b5c818f1287&f_bereich=Vermittlung+gro%DFe+Hunde+&f_funktion=Uebersicht",
-              "http://presenter.comedius.de/design/bmt_koeln_standard_10001.php?f_mandant=bmt_koeln_d620d9faeeb43f717c893b5c818f1287&f_bereich=Vermittlung%2BMeerschweinchen&f_funktion=Uebersicht",
-              "http://presenter.comedius.de/design/bmt_koeln_standard_10001.php?f_mandant=bmt_koeln2_d620d9faeeb43f717c893b5c818f1287&f_bereich=Degus%2Bund%2BChinchillas&f_funktion=Uebersicht"];
+              "http://presenter.comedius.de/design/bmt_koeln_standard_10001.php?f_mandant=bmt_koeln_d620d9faeeb43f717c893b5c818f1287&f_bereich=Vermittlung+Katzen&f_funktion=Uebersicht"];
 dellbrueck_base_url = "http://presenter.comedius.de/design/bmt_koeln_standard_10001.php"
 dellbrueck_pic_url = "http://presenter.comedius.de/pic/bmt_koeln_d620d9faeeb43f717c893b5c818f1287"

@@ -64,11 +64,9 @@

 get_dellbrueck_tier = (url)->
     new Promise (f, r) ->
-        #console.log('\n' + url + '\n')
         request (uri: url, encoding: "ISO-8859-1"), (err, response, body) ->
             if err
                 r err
-          #  body = iconvlite.decode(body, "ISO-8859-1")
             $ = cheerio.load body
             content = $('p[style="font-family:Verdana;font-size:13px;font-style:normal;font-weight:normal;color:#756d58;vertical-align:top"]').first()
             name = content.find('b').first().text()
@@ -115,16 +113,12 @@
     request sub_url, (err, response, body) ->
         if err
             r err
-      #  console.log(sub_url)
         urls = []
         $ = cheerio.load body
         $('a[style="border-style:none;background-color:#ece9e2;vertical-align:top;font:normal 13px Verdana; color:#756d58"]').each ->
             elem = $(this)
             detail_url = dellbrueck_base_url + elem.attr('href')
-            #console.log(detail_url)
             urls.push detail_url
-
-        #console.log(urls.length)
         f urls

@@ -137,13 +131,10 @@
                     for sub_url in sub_urls
                       p.push get_dellbrueck_urls_for_page sub_url
                     Promise.all p
-                      .then (tier_urls) ->
-                        console.log(tier_urls.length)
-                        for tier_url in tier_urls
-                          console.log(tier_url)
-                          for animal in tier_url
-                            console.log(animal)
-                            urls.push animal
+                      .then (page_urls) ->
+                        for page_url in page_urls
+                          for animal_url in page_url
+                            urls.push animal_url
                   .then () ->
                     f urls

@@ -165,9 +156,9 @@
     new Promise (f, r) ->
           get_dellbrueck_urls tier_url
               .then (urls) ->
-                  console.log(urls.length)
                   p = []
                   for url in urls
+                      console.log(url)
                       p.push get_dellbrueck_tier url
                   Promise.all p
                     .then (values) ->
@@ -176,37 +167,32 @@
                         r err

 get_data = ->
-    new Promise (f ,r) ->
-        #values = cache.get('tiere')
-        #if not values
-            #Promise.all [get_tierschutzdata(), get_tierfreundedata()]
-            #Promise.all [ get_tierfreundedata()]
-            p = []
-            iconvlite.extendNodeEncodings()
-            for url in zollstock_urls
-                p.push get_zollstockdata url
-            for url in dellbrueck_urls
-                p.push get_dellbrueckdata url
-            Promise.all p
-                .then (list_of_values) ->
-                    values = _.union.apply null, list_of_values
-                    cache.set('tiere', values, 60*60*24)
-                    f values
-                .catch (err) ->
-                    r err
-        #else
-        #    f values
+    new Promise (f ,r) ->
+          p = []
+          iconvlite.extendNodeEncodings()
+          for url in zollstock_urls
+              p.push get_zollstockdata url
+          for url in dellbrueck_urls
+              p.push get_dellbrueckdata url
+          Promise.all p
+              .then (list_of_values) ->
+                  values = _.union.apply null, list_of_values
+                  cache.set('tiere', values, 60*60*24)
+                  f values
+              .catch (err) ->
+                  r err

 # get a pet that was not posted yet
 get_notPostedPet = ->
     new Promise (f, r) ->
-        filename = '/var/cache/petschemnitz/posted_pets.json'
+        filename = 'posted_pets.json'
         try
             if fs.existsSync(filename)
                 postedPets = JSON.parse fs.readFileSync filename, 'utf8'
             else
                 fs.writeFileSync filename, JSON.stringify []
                 postedPets = []
+                console.log('file does not exist')
         catch err
             r err
         get_data().then (pets) ->


updated atom config

updated atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -18,10 +18,12 @@
       "background-tips"
       "exception-reporting"
       "metrics"
+      "linter-rubocop"
+      "block-travel"
     ]
     themes: [
-      "unity-dark-ui"
-      "monokai"
+      "unity-ui"
+      "one-dark-syntax"
     ]
     projectHome: "/Users/steveagalloco/Projects"
     audioBeep: false
@@ -36,3 +38,5 @@
   "release-notes":
     viewedVersion: "0.89.0"
   linter: {}
+  "autocomplete-plus":
+    confirmCompletion: "enter"


Fix whitespace

Fix whitespace

--- Atom/config.cson
+++ Atom/config.cson
@@ -3,11 +3,9 @@
     userId: ...
   "welcome":
     showOnStartup: false
-  "core":
-    {}
+  "core": {}
   "editor":
-    invisibles:
-      {}
+    invisibles: {}
     fontSize: 16
     showIndentGuide: true
     scrollPastEnd: true


cjs dsl

cjs dsl

--- src/dsl.coffee
+++ src/dsl.coffee
@@ -1,112 +1,111 @@
-define (require) ->
-  _ = require 'underscore'
+_ = require 'underscore'

-  dsl_type = ->
-  dsl = type: dsl_type
+dsl_type = ->
+dsl = type: dsl_type

-  create_type = (n, parent) ->
-    t = (@values...) -> @type = n
-    t.prototype = new parent
-    dsl_type[n] = t
+create_type = (n, parent) ->
+  t = (@values...) -> @type = n
+  t.prototype = new parent
+  dsl_type[n] = t

-  # Create the types:
-  #  f: function invocation
-  #  q: raw string
-  #  b: boolean
-  #  n: number
-  #  s: string
-  #  i: identifier
-  create_type n, dsl.type for n in "pfqi"
+# Create the types:
+#  f: function invocation
+#  q: raw string
+#  b: boolean
+#  n: number
+#  s: string
+#  i: identifier
+create_type n, dsl.type for n in "pfqi"

-  dsl.type.f::to_target_string = ->
-    [name, args...] = @values
-    "#{name}(#{(a.to_target_string() for a in args).join ','})"
+dsl.type.f::to_target_string = ->
+  [name, args...] = @values
+  "#{name}(#{(a.to_target_string() for a in args).join ','})"

-  dsl.type.f::to_js_string = ->
-    [name, args...] = @values
-    "#{name}(#{(a.to_js_string() for a in args).join ','})"
+dsl.type.f::to_js_string = ->
+  [name, args...] = @values
+  "#{name}(#{(a.to_js_string() for a in args).join ','})"

-  dsl.type.q::to_target_string = ->
-    @values.join ','
+dsl.type.q::to_target_string = ->
+  @values.join ','

-  dsl.type.q::to_js_string = ->
-    "q(#{(@values.map JSON.stringify).join ', '})"
+dsl.type.q::to_js_string = ->
+  "q(#{(@values.map JSON.stringify).join ', '})"

-  # numbers, strings, and booleans use json serialization
-  dsl.type.p::to_js_string =
-  dsl.type.p::to_target_string = ->
-    JSON.stringify @values[0]
+# numbers, strings, and booleans use json serialization
+dsl.type.p::to_js_string =
+dsl.type.p::to_target_string = ->
+  JSON.stringify @values[0]

-  create_type n, dsl.type.p for n in "nsb"
+create_type n, dsl.type.p for n in "nsb"

-  dsl.type.TRUE = new dsl.type.b true
-  dsl.type.FALSE = new dsl.type.b false
+dsl.type.TRUE = new dsl.type.b true
+dsl.type.FALSE = new dsl.type.b false

-  # Graphite doesn't support escaped quotes in strings, so avoid including any if possible.
-  dsl.type.s::to_target_string = ->
-    s = @values[0]
-    if s.indexOf('"') >= 0 or s.indexOf("'") < 0
-      quoteChar = "'"
+# Graphite doesn't support escaped quotes in strings, so avoid including any if possible.
+dsl.type.s::to_target_string = ->
+  s = @values[0]
+  if s.indexOf('"') >= 0 or s.indexOf("'") < 0
+    quoteChar = "'"
+  else
+    quoteChar = '"'
+  quoteChar + s.replace(quoteChar, "\\#{quoteChar}") + quoteChar
+
+dsl.type.i::to_target_string = dsl.type.s::to_target_string
+dsl.type.i::to_js_string = ->
+  @values[0]
+
+process_arg = (arg) ->
+  return arg if arg instanceof dsl.type
+  if typeof arg is "number"
+    return new dsl.type.n arg
+  if _.isString arg
+    return new dsl.type.s arg
+  if _.isBoolean arg
+    return new dsl.type.b arg
+  throw new TypeError('illegal argument ' + arg)
+
+dsl_fn = (name) ->
+  result = (args...) ->
+    new dsl.type.f name, (process_arg arg for arg in args)...
+  result.type = 'i'
+  result.name = name
+  result.values = [name]
+  result.__proto__ = new dsl.type.i
+
+  result
+
+dsl.define_functions = (ns, names) ->
+  ns[name] = dsl_fn name for name in names
+  ns
+
+dsl.to_string = (node) ->
+  unless node instanceof dsl.type
+    throw new TypeError(node + " is not a dsl node")
+  node.to_target_string()
+
+dsl.to_target_string = (node) ->
+  if _.isString node
+    node
+  else
+    dsl.to_string node
+
+dsl.to_js_string = (node) ->
+  node.to_js_string()
+
+dsl.is_dsl_node = (x) ->
+  x instanceof dsl.type
+
+# TODO rename
+dsl.context_result_handler = (object) ->
+  if dsl.is_dsl_node object
+    lead_string = dsl.to_string object
+    if _.isFunction object
+      @text "#{lead_string} is a Graphite function"
+      @example "docs #{object.values[0]}"
     else
-      quoteChar = '"'
-    quoteChar + s.replace(quoteChar, "\\#{quoteChar}") + quoteChar
+      @text "What do you want to do with #{lead_string}?"
+      for f in ['data', 'graph', 'img', 'url']
+        @example "#{f} #{object.to_js_string()}"
+    true

-  dsl.type.i::to_target_string = dsl.type.s::to_target_string
-  dsl.type.i::to_js_string = ->
-    @values[0]
-
-  process_arg = (arg) ->
-    return arg if arg instanceof dsl.type
-    if typeof arg is "number"
-      return new dsl.type.n arg
-    if _.isString arg
-      return new dsl.type.s arg
-    if _.isBoolean arg
-      return new dsl.type.b arg
-    throw new TypeError('illegal argument ' + arg)
-
-  dsl_fn = (name) ->
-    result = (args...) ->
-      new dsl.type.f name, (process_arg arg for arg in args)...
-    result.type = 'i'
-    result.name = name
-    result.values = [name]
-    result.__proto__ = new dsl.type.i
-
-    result
-
-  dsl.define_functions = (ns, names) ->
-    ns[name] = dsl_fn name for name in names
-    ns
-
-  dsl.to_string = (node) ->
-    unless node instanceof dsl.type
-      throw new TypeError(node + " is not a dsl node")
-    node.to_target_string()
-
-  dsl.to_target_string = (node) ->
-    if _.isString node
-      node
-    else
-      dsl.to_string node
-
-  dsl.to_js_string = (node) ->
-    node.to_js_string()
-
-  dsl.is_dsl_node = (x) ->
-    x instanceof dsl.type
-
-  # TODO rename
-  dsl.context_result_handler = (object) ->
-    if dsl.is_dsl_node object
-      lead_string = dsl.to_string object
-      if _.isFunction object
-        @text "#{lead_string} is a Graphite function"
-        @example "docs #{object.values[0]}"
-      else
-        @text "What do you want to do with #{lead_string}?"
-        for f in ['data', 'graph', 'img', 'url']
-          @example "#{f} #{object.to_js_string()}"
-      true
-
-  dsl
+module.exports = dsl


Correct actions for teacher student store

--- src/api.coffee
+++ src/api.coffee
@@ -166,7 +166,8 @@
     LearningGuide.Teacher.actions.loaded, 'GET', (id) -> url: "/api/courses/#{id}/teacher_guide"

   apiHelper LearningGuide.TeacherStudent.actions, LearningGuide.TeacherStudent.actions.load,
-    LearningGuide.Teacher.actions.loaded, 'GET', (id, roleId) -> url: "/api/courses/#{id}/guide/role/#{roleId}"
+    LearningGuide.TeacherStudent.actions.loaded, 'GET', (id, {roleId}) ->
+      url: "/api/courses/#{id}/guide/role/#{roleId}"

   apiHelper PerformanceActions, PerformanceActions.load, PerformanceActions.loaded, 'GET', (id) ->
     url: "/api/courses/#{id}/performance"


Exit after joining

Exit after joining

--- src/app.coffee
+++ src/app.coffee
@@ -109,8 +109,12 @@
 			if !properties.connected
 				console.log('Trying to join wifi')
 				wifi.joinFavoriteAsync()
+				.then ->
+					console.log('Joined! Exiting.')
+					retryCallback()
 				.catch (err) ->
 					startServer(wifi)
+
 			else
 				console.log('Already connected')
 				retryCallback()


change default page

change default page

--- src/app.coffee
+++ src/app.coffee
@@ -32,7 +32,7 @@
   $urlRouterProvider
 ) ->
   $locationProvider.hashPrefix '!'
-  $urlRouterProvider.otherwise '/map'
+  $urlRouterProvider.otherwise '/theaters'

 .config ($compileProvider) ->
   $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|geo):/);


Callback error if no sensors are found with the given name.

--- src/hue.coffee
+++ src/hue.coffee
@@ -123,7 +123,8 @@
     debug 'checking buttons'
     @checkSensors (error, body) =>
       return callback error if error?
-      state = _.findWhere(_.values(body), name: sensorName).state
+      state = _.findWhere(_.values(body), name: sensorName)?.state
+      return callback(sensorName + ' not found') if not state?
       debug 'got state', state
       callback null, button: BUTTON_EVENTS[state.buttonevent], state: state