Use tile node as the reference for highlights width

--- spec/text-editor-component-spec.coffee
+++ spec/text-editor-component-spec.coffee
@@ -957,14 +957,15 @@
     it "renders 2 regions for 2-line selections", ->
       editor.setSelectedScreenRange([[1, 6], [2, 10]])
       nextAnimationFrame()
-      regions = componentNode.querySelectorAll('.selection .region')
+      tileNode = componentNode.querySelectorAll(".tile")[0]
+      regions = tileNode.querySelectorAll('.selection .region')
       expect(regions.length).toBe 2

       region1Rect = regions[0].getBoundingClientRect()
       expect(region1Rect.top).toBe 1 * lineHeightInPixels
       expect(region1Rect.height).toBe 1 * lineHeightInPixels
       expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
-      expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right

       region2Rect = regions[1].getBoundingClientRect()
       expect(region2Rect.top).toBe 2 * lineHeightInPixels
@@ -977,42 +978,44 @@
       nextAnimationFrame()

       # Tile 0
-      regions = componentNode.querySelectorAll(".tile")[0].querySelectorAll('.selection .region')
+      tileNode = componentNode.querySelectorAll(".tile")[0]
+      regions = tileNode.querySelectorAll('.selection .region')
       expect(regions.length).toBe(3)

       region1Rect = regions[0].getBoundingClientRect()
       expect(region1Rect.top).toBe 0
       expect(region1Rect.height).toBe 1 * lineHeightInPixels
       expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
-      expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right

       region2Rect = regions[1].getBoundingClientRect()
       expect(region2Rect.top).toBe 1 * lineHeightInPixels
       expect(region2Rect.height).toBe 1 * lineHeightInPixels
       expect(region2Rect.left).toBe scrollViewClientLeft + 0
-      expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right

       region3Rect = regions[2].getBoundingClientRect()
       expect(region3Rect.top).toBe 2 * lineHeightInPixels
       expect(region3Rect.height).toBe 1 * lineHeightInPixels
       expect(region3Rect.left).toBe scrollViewClientLeft + 0
-      expect(region3Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region3Rect.right).toBe tileNode.getBoundingClientRect().right

       # Tile 3
-      regions = componentNode.querySelectorAll(".tile")[1].querySelectorAll('.selection .region')
+      tileNode = componentNode.querySelectorAll(".tile")[1]
+      regions = tileNode.querySelectorAll('.selection .region')
       expect(regions.length).toBe(3)

       region1Rect = regions[0].getBoundingClientRect()
       expect(region1Rect.top).toBe 3 * lineHeightInPixels
       expect(region1Rect.height).toBe 1 * lineHeightInPixels
       expect(region1Rect.left).toBe scrollViewClientLeft + 0
-      expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region1Rect.right).toBe tileNode.getBoundingClientRect().right

       region2Rect = regions[1].getBoundingClientRect()
       expect(region2Rect.top).toBe 4 * lineHeightInPixels
       expect(region2Rect.height).toBe 1 * lineHeightInPixels
       expect(region2Rect.left).toBe scrollViewClientLeft + 0
-      expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right
+      expect(region2Rect.right).toBe tileNode.getBoundingClientRect().right

       region3Rect = regions[2].getBoundingClientRect()
       expect(region3Rect.top).toBe 5 * lineHeightInPixels


Add test for highlight region calculation error

--- spec/text-editor-presenter-spec.coffee
+++ spec/text-editor-presenter-spec.coffee
@@ -2028,6 +2028,27 @@

           expect(stateForHighlightInTile(presenter, highlight, 0)).toBeUndefined()

+        it "handles highlights that extend to the left of the visible area (regression)", ->
+          editor.setSelectedBufferRanges([
+            [[0, 2], [1, 4]],
+          ])
+
+          presenter = buildPresenter(explicitHeight: 20, scrollLeft: 0, tileSize: 2)
+          expectValues stateForSelectionInTile(presenter, 0, 0), {
+            regions: [
+              {top: 0 * 10, height: 10, left: 2 * 10, right: 0 * 10},
+              {top: 1 * 10, height: 10, left: 0 * 10, width: 4 * 10}
+            ]
+          }
+
+          presenter = buildPresenter(explicitHeight: 20, scrollLeft: 20, tileSize: 2)
+          expectValues stateForSelectionInTile(presenter, 0, 0), {
+            regions: [
+              {top: 0 * 10, height: 10, left: 2 * 10, right: 0 * 10},
+              {top: 1 * 10, height: 10, left: 0 * 10, width: 4 * 10}
+            ]
+          }
+
         it "updates when ::scrollTop changes", ->
           editor.setSelectedBufferRanges([
             [[6, 2], [6, 4]],


Added new files as level music (#707)

Added new files as level music (#707)

--- app/schemas/models/level.coffee
+++ app/schemas/models/level.coffee
@@ -222,6 +222,10 @@
         '/music/OzariaSpiritLands1',
         '/music/OzariaSpiritLands2',
         '/music/OzariaSpiritLands3',
+        '/music/forestAmbience01',
+        '/music/forestAmbience02',
+        '/music/forestAmbience03',
+        '/music/mountainWind01',
         '/music/music_level_1',
         '/music/music_level_2',
         '/music/music_level_3',


Add test for setPrefix

Add test for setPrefix

--- tests/LoggerFactoryTest.coffee
+++ tests/LoggerFactoryTest.coffee
@@ -3,11 +3,12 @@
   meteorSettings = Meteor.settings

   logAllLevels = (log)->
-    log.trace("I'm log.trace");
-    log.debug("I'm log.debug");
-    log.info("I'm log.info");
-    log.warn("I'm log.warn");
-    log.error("I'm log.error");
+    log.trace("1 I'm log.trace");
+    log.fine("2 I'm log.fine");
+    log.debug("3 I'm log.debug");
+    log.info("4 I'm log.info");
+    log.warn("5 I'm log.warn");
+    log.error("6 I'm log.error");

   beforeEach ->
     Meteor.settings = {}
@@ -18,6 +19,12 @@
   afterAll ->
     spies.restoreAll()
     Meteor.settings = meteorSettings
+
+  it 'setPrefix - should work', ->
+    log = loglevel.createLogger()
+    log.info ('Without prefix')
+    log.setPrefix('My prefix:')
+    log.info('With prefix')

   describe "_getNamespaceLoglevel", ->

@@ -140,11 +147,12 @@
       log = loglevel.createPackageLogger('loglevel:mypackage')
       expect(log.prefix).to.equal('loglevel:mypackage:')
       log.enableAll()
-      log.trace("I'm log.trace")
-      log.debug("I'm log.debug")
-      log.info("I'm log.info")
-      log.warn("I'm log.warn")
-      log.error("I'm log.error")
+      log.trace("1 I'm log.trace")
+      log.fine("2 I'm log.fine")
+      log.debug("3 I'm log.debug")
+      log.info("4 I'm log.info")
+      log.warn("5 I'm log.warn")
+      log.error("6 I'm log.error")

     it "should read level from Meteor.settings", ->
       Meteor.settings =
@@ -164,8 +172,9 @@
       log = loglevel.createAppLogger('myapp')
       expect(log.prefix).to.equal('myapp:')
       log.enableAll()
-      log.trace("I'm log.trace")
-      log.debug("I'm log.debug")
-      log.info("I'm log.info")
-      log.warn("I'm log.warn")
-      log.error("I'm log.error")
+      log.trace("1 I'm log.trace")
+      log.fine("2 I'm log.fine")
+      log.debug("3 I'm log.debug")
+      log.info("4 I'm log.info")
+      log.warn("5 I'm log.warn")
+      log.error("6 I'm log.error")


Hide terminal-plus from the status bar

Hide terminal-plus from the status bar

--- atom/atom/packages/terminal-plus/lib/status-bar.coffee
+++ atom/atom/packages/terminal-plus/lib/status-bar.coffee
@@ -101,7 +101,7 @@
     @subscriptions.add dispose: ->
       window.removeEventListener 'focus', handleFocus

-    @attach()
+    # @attach() # hide status bar

   registerContextMenu: ->
     @subscriptions.add atom.commands.add '.terminal-plus.status-bar',


Make home page trackable by sidebar

Make home page trackable by sidebar

--- app/views/home_page_view.coffee
+++ app/views/home_page_view.coffee
@@ -4,3 +4,4 @@
 module.exports = class HomePageView extends PageView
   template: template
   className: 'home-page'
+  id: 'home-page'


correct exception catching for BufferedProcess

--- lib/git.coffee
+++ lib/git.coffee
@@ -47,20 +47,19 @@
   cmd: (args, options={ env: process.env }) ->
     new Promise (resolve, reject) ->
       output = ''
-      try
-        new BufferedProcess
-          command: atom.config.get('git-plus.gitPath') ? 'git'
-          args: args
-          options: options
-          stdout: (data) -> output += data.toString()
-          stderr: (data) ->
-            output += data.toString()
-          exit: (code) ->
-            if code is 0
-              resolve output
-            else
-              reject output
-      catch
+      new BufferedProcess
+        command: atom.config.get('git-plus.gitPath') ? 'git'
+        args: args
+        options: options
+        stdout: (data) -> output += data.toString()
+        stderr: (data) ->
+          output += data.toString()
+        exit: (code) ->
+          if code is 0
+            resolve output
+          else
+            reject output
+      .onWillThrowError (errorObject) ->
         notifier.addError 'Git Plus is unable to locate the git command. Please ensure process.env.PATH can access git.'
         reject "Couldn't find git"


Default char value defined as coffee argument

--- lib/pad.coffee
+++ lib/pad.coffee
@@ -1,6 +1,5 @@

-module.exports = (string, size, char) ->
-    char ?= ' '
+module.exports = (string, size, char=' ') ->
     if typeof string is 'number'
         _size = size
         size = string


Update tests to pass against latest MC changes

Signed-off-by: Tim Perry <5ee0edb9e2229c0838f1959779f1949031de0123@resin.io>

--- tests/integration/setup.coffee
+++ tests/integration/setup.coffee
@@ -141,6 +141,7 @@
 						commit: 'old-release-commit'
 						status: 'success'
 						source: 'cloud'
+						composition: {}
 						start_timestamp: 1234
 			,
 				resin.pine.post
@@ -151,6 +152,7 @@
 						commit: 'new-release-commit'
 						status: 'success'
 						source: 'cloud'
+						composition: {}
 						start_timestamp: 54321
 			]
 		.spread (webService, dbService, oldRelease, newRelease) =>


adjust program bg size

adjust program bg size

--- graphics.coffee
+++ graphics.coffee
@@ -469,9 +469,9 @@
     if sprite.scale > 0
       @ctx.scale(sprite.scale, sprite.scale)
       if sprite.programmed
-        bs = @block * .45
+        bs = @block * 7/16
         @ctx.fillStyle = @instruction_colors[sprite.command]
-        @ctx.fillRect(-bs, -bs, bs*2, bs*2)
+        @ctx.fillRect(-bs-.5, -bs-.5, bs*2, bs*2)
       @ctx.strokeStyle = 'white'
       @renderArrow(sprite.dir.theta, @block)

@@ -524,9 +524,9 @@
         render: (sprite) =>
           if sprite.scale > 0
             @ctx.scale(sprite.scale, sprite.scale)
-            bs = @block * .45
+            bs = @block * 7/16
             @ctx.fillStyle = @instruction_colors[sprite.command]
-            @ctx.fillRect(-bs, -bs, bs*2, bs*2)
+            @ctx.fillRect(-bs-.5, -bs-.5, bs*2, bs*2)
         clock: @record_sprite_clock
         animations: []


implement hasActiveDescendant

implement hasActiveDescendant

--- gulp-nav.coffee
+++ gulp-nav.coffee
@@ -131,6 +131,11 @@
       enumerable: yes
       get: ->
         navInContext root.obj, context.concat(root.name), root
+    hasActiveDescendant:
+      enumerable: yes
+      get: ->
+        @children.some (child) ->
+          child.active or child.hasActiveDescendant

 # relative() and resolve() are just like the path functions, except trailing
 # slashes are significant since we're dealing with URLs


Change when RC changes

Change when RC changes

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -34,7 +34,7 @@

 # Watch and resync

-all_src = ['src/*', 'src/*/*']
+all_src = ['src/*', 'src/*/*', '../reactive-coffee/src/*']
 gulp.task 'watch', ['sync'], ->
   gulp.watch all_src, all_builds


inject proposal

inject proposal

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -55,11 +55,15 @@
     .pipe gulp.dest outapp

-# copy .html-files
-gulp.task 'html', ->
+buildHtml = (transform = gutil.noop()) ->
   gulp.src paths.html
+    .pipe transform
     .pipe changed outapp
     .pipe gulp.dest outapp
+
+
+# copy .html-files
+gulp.task 'html', -> buildHtml()

 # compile less
@@ -80,6 +84,9 @@
   reloader.script()
     .pipe gulp.dest outui

+  # inject scripts in html
+  #buildHtml reloader.inject()
+
   # watch rebuilt stuff
   gulp.watch "#{outui}/**/*", reloader.onChange


Fixed the output folder of the sourcemap

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -15,5 +15,5 @@
             .pipe(source "endev.full.js")
             .pipe(do buffer)
             .pipe(do sourcemaps.init)
-            .pipe(sourcemaps.write "./dist")
+            .pipe(sourcemaps.write "./")
             .pipe(gulp.dest "./dist")


Use path.join for compatibility

Use path.join for compatibility

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -88,7 +88,7 @@

 gulp.task 'server', ->
   require('http')
-    .createServer ecstatic root: __dirname + '/public'
+    .createServer ecstatic root: path.join(__dirname, '/public')
     .listen 9001

 gulp.task 'watch', ->


Fix build task

Fix build task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -38,8 +38,7 @@
   js       :
     main   : 'app/client/js/fink.js'
     tag    : 'app/client/tag/**/**'
-    vendor : ['node_modules/parse-uri/dist/parse-uri.js'
-              'node_modules/is-uri/dist/is-uri.js'
+    vendor : ['node_modules/fink-is-uri/dist/fink-is-uri.js'
               'bower_components/es6-promise/promise.min.js'
               'bower_components/fetch/fetch.js'
               'bower_components/clipboard/dist/clipboard.min.js']


docsの対応できず

docsの対応できず

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -25,13 +25,13 @@
 		.pipe rename 'baser.min.js'
 		.pipe gulp.dest './'

-gulp.task 'docs', ->
-	gulp.src 'src/baserJS.ts'
-		.pipe typedoc
-			target: 'ES5'
-			includeDeclarations: on
-			mode: 'file'
-			out: 'docs'
+# gulp.task 'docs', ->
+# 	gulp.src 'src/baserJS.ts'
+# 		.pipe typedoc
+# 			target: 'ES5'
+# 			includeDeclarations: on
+# 			mode: 'file'
+# 			out: 'docs'

 gulp.task 'watch', ->
 	gulp.watch 'src/**/*.ts', ['default']


Add tasks for testing

Add tasks for testing

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,17 +1,25 @@
 gulp = require 'gulp'
 coffee = require 'gulp-coffee'
-sketch = require './'
+sketch = require './coffee/'

 gulp.task 'default', ->
   gulp.src './coffee/index.coffee'
   .pipe coffee()
   .pipe gulp.dest './'

-gulp.task 'test1', ->
-  gulp.src './test/fixtures/symbol-font-14px.sketch'
+gulp.task 'test-flat', ->
+  gulp.src './test/fixtures/flat.sketch'
   .pipe sketch
-    export: 'artboards'
-    formats: 'svg'
+    export: 'slices'
+    formats: 'png'
     clean: true
-  .pipe gulp.dest './tmp/'
+  .pipe gulp.dest './tmp/test/flat'
+
+gulp.task 'test-subdir', ->
+  gulp.src './test/fixtures/subdir.sketch'
+  .pipe sketch
+    export: 'slices'
+    formats: 'png'
+    clean: true
+  .pipe gulp.dest './tmp/test/subdir'


axis data ticks now only transition if transitions are not turned off by the large cutoff

--- src/viz/types/helpers/graph/dataTicks.coffee
+++ src/viz/types/helpers/graph/dataTicks.coffee
@@ -1,53 +1,76 @@
 color   = require "../../../../core/fetch/color.coffee"
 legible = require "../../../../color/legible.coffee"
+print   = require "../../../../core/console/print.coffee"

 module.exports = (vars) ->

-  data = if vars.axes.stacked then [] else vars.data.viz
+  axes = vars.axes
+  data = if axes.stacked then [] else vars.data.viz

   style = (line, axis) ->
     line
       .attr "x1", (d) ->
-        if axis is "y" then -2 else d.d3plus.x - vars.axes.margin.left
+        if axis is "y" then -2 else d.d3plus.x - axes.margin.left
       .attr "x2", (d) ->
-        if axis is "y" then -8 else d.d3plus.x - vars.axes.margin.left
+        if axis is "y" then -8 else d.d3plus.x - axes.margin.left
       .attr "y1", (d) ->
-        if axis is "x" then vars.axes.height + 2 else d.d3plus.y - vars.axes.margin.top
+        if axis is "x" then axes.height + 2 else d.d3plus.y - axes.margin.top
       .attr "y2", (d) ->
-        if axis is "x" then vars.axes.height + 8 else d.d3plus.y - vars.axes.margin.top
+        if axis is "x" then axes.height + 8 else d.d3plus.y - axes.margin.top
       .style "stroke", (d) -> legible color vars, d
       .style "stroke-width", vars.data.stroke.width
       .attr "shape-rendering", vars.shape.rendering.value

-  ticks = vars.group.select("g#d3plus_graph_plane").selectAll "g.d3plus_data_tick"
+  print.time "creating axis tick groups" if vars.dev.value
+
+  ticks = vars.group.select("g#d3plus_graph_plane")
+    .selectAll "g.d3plus_data_tick"
     .data data, (d) ->
-      mod = if vars.axes.discrete then "_"+d.d3plus[vars.axes.discrete] else ""
+      mod = if axes.discrete then "_"+d.d3plus[axes.discrete] else ""
       "tick_" + d[vars.id.value] + "_" + d.d3plus.depth + mod

   ticks.enter().append "g"
     .attr "class", "d3plus_data_tick"
     .attr "opacity", 0

+  print.timeEnd "creating axis tick groups" if vars.dev.value
+
   for axis in ["x","y"]

-    axisData = if axis isnt vars.axes.discrete then data else []
+    print.time "creating " + axis + " ticks" if vars.dev.value
+
+    axisData = if axis isnt axes.discrete then data else []

     tick = ticks.selectAll "line.d3plus_data_"+axis
       .data axisData, (d) ->
-        mod = if vars.axes.discrete then "_"+d.d3plus[vars.axes.discrete] else ""
-        "tick_" + d[vars.id.value] + "_" + d.d3plus.depth + mod
+        "tick_" + d[vars.id.value] + "_" + d.d3plus.depth
+
+    print.timeEnd "creating " + axis + " ticks" if vars.dev.value
+
+    print.time "styling " + axis + " ticks" if vars.dev.value
+
+    if vars.draw.timing > 0
+      tick.transition().duration vars.draw.timing
+        .call style, axis
+    else
+      tick.call style, axis

     tick.enter().append("line")
       .attr "class","d3plus_data_"+axis
       .call style, axis

-    tick.transition().duration vars.draw.timing
-      .call style, axis
+    print.timeEnd "styling " + axis + " ticks" if vars.dev.value

-  ticks.transition().duration vars.draw.timing
-    .attr("opacity",1)
+  if vars.draw.timing > 0

-  ticks.exit().transition().duration vars.draw.timing
-    .attr("opacity",0).remove()
+    ticks.transition().duration vars.draw.timing
+      .attr("opacity",1)
+
+    ticks.exit().transition().duration vars.draw.timing
+      .attr("opacity",0).remove()
+
+  else
+    ticks.attr("opacity",1)
+    ticks.exit().remove()

   return


Show help if init is called with no args. Closes #282.

--- init.coffee
+++ init.coffee
@@ -16,10 +16,21 @@

 'use strict'

+config = require './config.js'
+utils = require './utils.js'
+async = require 'async'
+dashdash = require 'dashdash'
+
+anyDB = null
+needAnyDB = () ->
+	anyDB = require 'any-db' unless anyDB?
+
+
 checkError = (error, dontExit) ->
 	if error?
 		console.error error
 		unless dontExit then process.exit 1
+

 checkArgs = (passed, available) ->
 	unless passed in available
@@ -27,19 +38,11 @@
 		console.log "Available: #{available}"
 		process.exit 1

-config = require './config.js'
-utils = require './utils.js'
-async = require 'async'
-dashdash = require 'dashdash'
-
-anyDB = null
-needAnyDB = () ->
-	anyDB = require 'any-db' unless anyDB?
-

 help = (arg, callback) ->
 	console.log "\nUsage: node init.js <commands>\n\n#{parser.help(includeEnv: true).trimRight()}"
 	process.exit 2
+

 info = (arg, callback) ->
 	needAnyDB()
@@ -54,6 +57,7 @@
 			console.log "init.js with #{newest + 1} revisions on board."
 			console.log "Current revision is #{result} (#{status})."
 			process.exit 0
+

 createDatabase = (arg, callback) ->
 	needAnyDB()
@@ -170,6 +174,7 @@

 if opts._order.length is 0
 	opts.help = true
+	opts._order.push(key: 'help', value: true, from: 'argv')

 # Some other schema:
 #  'help', 'h', 'Show this text'


Syntax fix

Syntax fix

--- init.coffee
+++ init.coffee
@@ -225,7 +225,7 @@
 insertTestMonsters = ->
 	dbConnection = createAnyDBConnection(config.DATABASE_URL)

-	console.log('Inserting test prototypes...');
+	console.log('Inserting test prototypes...')

 	prototypes = [
 		[1, 'Гигантская улитка', 1, 1, 1, 1, 1, 1, 1, 1, 3]
@@ -249,7 +249,7 @@
 			i
 		)

-	console.log('Inserting monsters...');
+	console.log('Inserting monsters...')

 	monsters = [
 		[1, 6, 774449300, 1, 1, null, 16]
@@ -320,7 +320,7 @@
 			i
 		)

-	console.log('Done.');
+	console.log('Done.')

 sync(


cleanup tests

cleanup tests

--- src/test/test_world.coffee
+++ src/test/test_world.coffee
@@ -74,7 +74,6 @@
       [world.get("instance"), args['key']]
     world.put('callme', callme)
     result = world.call('callme', args)
-    console.log(result)
     t.equal result[0], "variable", "world parameter"
     t.equal result[1], "value", "args parameter"
     t.end()


Change rx label

Change rx label

--- src/test/test_world.coffee
+++ src/test/test_world.coffee
@@ -23,7 +23,7 @@
     t.equal world.get('x'), 3
     t.end()

-  test 'world contains rx', (t) ->
+  test 'world contains reactive extensions', (t) ->
     t.ok world.rx(), "rx()"
     t.ok world.T(), "T"
     t.ok world.SVG() ,"SVG"


Pass request.head through poi's proxy.

Pass request.head through poi's proxy.

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

 zerofill = (n) ->
   pad = "000"
@@ -94,7 +95,10 @@
   updateNotifyConfig: (ship_id) ->
     setConfig = (key, audio) ->
       config.set(key, audio)
-      request.head audio, (error, response, body) ->
+      request.head
+        url: audio
+        proxy: POI_PROXY
+      , (error, response, body) ->
         return if !error and response?.statusCode == 200
         config.set(key, null)


Minor update

Minor update

--- index.cjsx
+++ index.cjsx
@@ -88,7 +88,7 @@
           param: body.api_eParam
         console.log JSON.stringify info if process.env.DEBUG?
         try
-          yield request.postAsync "http://#{TEST_HOST}/enemy.action",
+          yield request.postAsync "http://#{KCWIKI_HOST}/enemy.action",
             form:
               data: JSON.stringify info
             headers:
@@ -125,7 +125,7 @@
                 kaihi: kaihi
           if data.length > 0
             try
-              yield request.postAsync "http://#{TEST_HOST}/attr.action",
+              yield request.postAsync "http://#{KCWIKI_HOST}/attr.action",
                 form:
                   # data: JSON.stringify info
                   data: JSON.stringify data
@@ -160,7 +160,7 @@
             __ships = {}
             console.log JSON.stringify info if process.env.DEBUG?
             try
-              yield request.postAsync "http://#{TEST_HOST}/initEquip.action",
+              yield request.postAsync "http://#{KCWIKI_HOST}/initEquip.action",
                 form:
                   # data: JSON.stringify info
                   ships: JSON.stringify _newShips
@@ -181,7 +181,7 @@
             map: _map
           console.log JSON.stringify info if process.env.DEBUG?
           try
-            yield request.postAsync "http://#{TEST_HOST}/path.action",
+            yield request.postAsync "http://#{KCWIKI_HOST}/path.action",
               form:
                 data: JSON.stringify info
               headers:
@@ -200,7 +200,7 @@
           ships: data
         console.log JSON.stringify info if process.env.DEBUG?
         try
-          yield request.postAsync "http://#{TEST_HOST}/initEquip.action",
+          yield request.postAsync "http://#{KCWIKI_HOST}/initEquip.action",
             form:
               # data: JSON.stringify info
               ships: JSON.stringify data
@@ -226,7 +226,7 @@
       tyku: tyku
       rank: rank
     try
-      yield request.postAsync "http://#{TEST_HOST}/tyku.action",
+      yield request.postAsync "http://#{KCWIKI_HOST}/tyku.action",
         form:
           data: JSON.stringify info
         headers:


change version

change version

--- index.cjsx
+++ index.cjsx
@@ -88,7 +88,7 @@
   author: 'Rui'
   link: 'https://github.com/ruiii'
   description: __ 'Senka calculator'
-  version: '1.1.0'
+  version: '1.2.0'
   reactClass: React.createClass
     getInitialState: ->
       data: Object.clone emptyData


fix dayInjure

fix dayInjure

--- index.cjsx
+++ index.cjsx
@@ -92,7 +92,7 @@
       max: 0
       injure: 0
       damage: 0
-      dayDamage: 0
+      dayInjure: 0
     @id = -1
     @lv = -1
     @cond = 0
@@ -172,11 +172,11 @@
       checkRepair ship
   damage

-saveDayDamage = (ship) ->
-  ship.hp.dayDamage = ship.hp.damage
-
-loadDayDamage = (ship)->
-  ship.hp.damage -= ship.hp.dayDamage
+saveDayInjure = (ship) ->
+  ship.hp.dayInjure = ship.hp.injure
+
+loadDayInjure = (ship)->
+  ship.hp.injure -= ship.hp.dayInjure

 getCellInfo = (eventId, eventKind, bossCell, CellNo) ->
   if bossCell is CellNo
@@ -455,14 +455,14 @@
           if path != '/kcsapi/api_req_battle_midnight/battle'
             getEnemyInfo enemyFleet, body, false
           for i in [0..5]
-            saveDayDamage mainFleet.ship[i]
-            saveDayDamage enemyFleet.ship[i]
-            saveDayDamage escortFleet.ship[i]
+            saveDayInjure mainFleet.ship[i]
+            saveDayInjure enemyFleet.ship[i]
+            saveDayInjure escortFleet.ship[i]
           result = simulateBattle mainFleet, enemyFleet, escortFleet, combinedFlag, body, planeCount
           for i in [0..5]
-            loadDayDamage mainFleet.ship[i]
-            loadDayDamage enemyFleet.ship[i]
-            loadDayDamage escortFleet.ship[i]
+            loadDayInjure mainFleet.ship[i]
+            loadDayInjure enemyFleet.ship[i]
+            loadDayInjure escortFleet.ship[i]
         # Practice battle
         when '/kcsapi/api_req_practice/battle', '/kcsapi/api_req_practice/midnight_battle'
           shouldRender = true
@@ -473,14 +473,14 @@
             getShipInfo mainFleet, postBody.api_deck_id - 1
             getEnemyInfo enemyFleet, body, true
           for i in [0..5]
-            saveDayDamage mainFleet.ship[i]
-            saveDayDamage enemyFleet.ship[i]
-            saveDayDamage escortFleet.ship[i]
+            saveDayInjure mainFleet.ship[i]
+            saveDayInjure enemyFleet.ship[i]
+            saveDayInjure escortFleet.ship[i]
           result = simulateBattle mainFleet, enemyFleet, escortFleet, combinedFlag, body, planeCount
           for i in [0..5]
-            loadDayDamage mainFleet.ship[i]
-            loadDayDamage enemyFleet.ship[i]
-            loadDayDamage escortFleet.ship[i]
+            loadDayInjure mainFleet.ship[i]
+            loadDayInjure enemyFleet.ship[i]
+            loadDayInjure escortFleet.ship[i]
         # Combined battle
         when '/kcsapi/api_req_combined_battle/airbattle', '/kcsapi/api_req_combined_battle/sp_midnight', '/kcsapi/api_req_combined_battle/battle', '/kcsapi/api_req_combined_battle/battle_water', '/kcsapi/api_req_combined_battle/midnight_battle', '/kcsapi/api_req_combined_battle/ld_airbattle'
           shouldRender = true
@@ -488,14 +488,14 @@
           if path != '/kcsapi/api_req_combined_battle/midnight_battle'
             getEnemyInfo enemyFleet, body, false
           for i in [0..5]
-            saveDayDamage mainFleet.ship[i]
-            saveDayDamage enemyFleet.ship[i]
-            saveDayDamage escortFleet.ship[i]
+            saveDayInjure mainFleet.ship[i]
+            saveDayInjure enemyFleet.ship[i]
+            saveDayInjure escortFleet.ship[i]
           result = simulateBattle mainFleet, enemyFleet, escortFleet, combinedFlag, body, planeCount
           for i in [0..5]
-            loadDayDamage mainFleet.ship[i]
-            loadDayDamage enemyFleet.ship[i]
-            loadDayDamage escortFleet.ship[i]
+            loadDayInjure mainFleet.ship[i]
+            loadDayInjure enemyFleet.ship[i]
+            loadDayInjure escortFleet.ship[i]
         # Battle Result
         when '/kcsapi/api_req_practice/battle_result', '/kcsapi/api_req_sortie/battleresult', '/kcsapi/api_req_combined_battle/battleresult'
           shouldRender = true


bugzilla/index: remove unwanted require

bugzilla/index: remove unwanted require

--- plugins/bugzilla/index.coffee
+++ plugins/bugzilla/index.coffee
@@ -1,5 +1,3 @@
-url = require 'url'
-
 NotificationPlugin = require '../../notification-plugin'
 bugzillaRPC = require './bugzillarpc'


Adjust pop-up window as a variable [issue]

Issue:

Failed to hide window

--- plugins/owned-list/index.cjsx
+++ plugins/owned-list/index.cjsx
@@ -2,6 +2,30 @@
 {Button} = ReactBootstrap
 remote = require 'remote'
 BrowserWindow = remote.require 'browser-window'
+
+shipInfoWindow = null
+
+initialShipInfoWindow = ->
+  shipInfoWindow = new BrowserWindow
+    #Use cconfig
+    x: 0
+    y: 0
+    width: 800
+    height: 600
+    show: false
+
+  shipInfoWindow.loadUrl "file://#{__dirname}/parts/info-panel/ships/index.html"
+  shipInfoWindow.openDevTools
+    detach: true
+  shipInfoWindow.onbeforeunload = ->
+    false
+    ###
+  shipInfoWindow.on 'close', (e) =>
+    console.log "It works!"
+    e.preventDefault()
+    shipInfoWindow.hide()
+    ###
+initialShipInfoWindow()

 module.exports =
   name: 'OwnedList'
@@ -9,37 +33,11 @@
   displayName: '详细信息'
   description: '提供已有舰娘和已有装备详细信息查看'
   reactClass: React.createClass
-    shipInfoWindow: new BrowserWindow
-      #Need test / use config?
-      x: 0
-      y: 0
-      width: 800
-      height: 600
-      show: false
-
-    initialShipInfoWindow: ->
-      @shipInfoWindow.loadUrl "file://#{__dirname}/parts/info-panel/ships/index.html"
-      ###
-      issue code: failed to hide the window
-      @shipInfoWindow.onbeforeunload = (e) ->
-        @shipInfoWindow.hide()
-        false
-
-      or
-
-      @shipInfoWindow.on 'close', () ->
-        @shipInfoWindow.preventDefault()
-        @shipInfoWindow.hide()
-      ###
-
+
     handleClickShipButton: ->
-      @shipInfoWindow.show()
-
-    handleTest: ->
-      @shipInfoWindow.hide()
+      shipInfoWindow.show()

     render: ->
-      @initialShipInfoWindow()
       <div>
         <Button bsStyle='info' bsSize='large' block onClick={@handleClickShipButton} >舰娘信息</Button>
         <Button bsStyle='info' bsSize='large' block onClick={@handleTest} >装备信息</Button>


Use destructuring assignment for expect.

--- spec/vfl.coffee
+++ spec/vfl.coffee
@@ -4,25 +4,28 @@
   chai = require 'chai' unless chai
   parser = require '../lib/vfl-compiler'

-parse = (source, expect) ->
+{expect} = chai
+
+
+parse = (source, expectation) ->
   result = null
   describe source, ->
     it 'should do something', ->
       result = parser.parse source
-      chai.expect(result).to.be.an 'array'
+      expect(result).to.be.an 'array'
     it 'should match expected', ->
-      chai.expect(result).to.eql expect
+      expect(result).to.eql expectation

 expectError = (source, message) ->
   describe source, ->
     it "should throw an error with message: #{message}", ->
       exercise = -> parser.parse source
-      chai.expect(exercise).to.throw Error, message
+      expect(exercise).to.throw Error, message

 describe 'VFL-to-CCSS Compiler', ->

   it 'should provide a parse method', ->
-    chai.expect(parser.parse).to.be.a 'function'
+    expect(parser.parse).to.be.a 'function'

   # Basics
   # --------------------------------------------------


Clone options parameter before modifying it.

--- src/atom.coffee
+++ src/atom.coffee
@@ -786,6 +786,8 @@
   showSaveDialogSync: (options={}) ->
     if _.isString(options)
       options = defaultPath: options
+    else
+      options = _.clone(options)
     currentWindow = @getCurrentWindow()
     dialog = remote.require('dialog')
     options.title ?= 'Save File'


(fix) Send status 204 after successful login and logout.

--- src/auth.coffee
+++ src/auth.coffee
@@ -19,7 +19,7 @@
         else
           req.session.id = req.body.id
           req.session.save (err) ->
-          res.status(202).json {}
+          res.status(204).end()

     # affects all app... requests after this one
     # the only accessable thing before logging in is the login form
@@ -32,4 +32,4 @@

     app.use "/api/logout", (req, res) ->
       req.session.destroy()
-      res.status(202).json {}
+      res.status(204).end()


get working when in untrusted frame (just use local methods)

--- src/index.coffee
+++ src/index.coffee
@@ -20,7 +20,6 @@
     timeout ?= null
     @handshakeTimeout ?= DEFAULT_HANDSHAKE_TIMEOUT_MS
     @isListening = false
-    @isLegacy = false # TODO: remove when native apps are updated
     # window?._portalIsInAppBrowser is set by native app. on iOS it isn't set
     # soon enough, so we rely on userAgent
     isInAppBrowser = window?._portalIsInAppBrowser or
@@ -143,13 +142,11 @@

     @clientValidation = @client.call 'ping', null, {timeout: @handshakeTimeout}
     .then (registeredMethods) =>
-      console.log 'got reg', registeredMethods
-      if registeredMethods is 'pong'
-        @isLegacy = true
-      else if @hasParent
+      if @hasParent
         @parentsRegisteredMethods = @parentsRegisteredMethods.concat(
           registeredMethods
         )
+    .catch -> null

     @swValidation = @ready.then =>
       @sw.call 'ping', null, {timeout: @handshakeTimeout}
@@ -184,7 +181,7 @@
         parentError = null
         @clientValidation
         .then =>
-          if not @isLegacy and @parentsRegisteredMethods.indexOf(method) is -1
+          if @parentsRegisteredMethods.indexOf(method) is -1
             return localMethod method, params
           else
             @client.call method, params


Better redirect URL

Better redirect URL

--- src/index.coffee
+++ src/index.coffee
@@ -46,7 +46,7 @@
       params =
         client_id: app.config.clientID
         client_secret: app.config.clientSecret
-        redirect_uri: app.makeURL '/invalid'
+        redirect_uri: app.makeURL '/authorized'
         grant_type: 'authorization_code'
         code: code

@@ -85,5 +85,4 @@
       req.session.user = user.id
       res.redirect "/home", 303

-
 module.exports = router


remove duplicate message

remove duplicate message

--- src/index.coffee
+++ src/index.coffee
@@ -4,8 +4,7 @@
   (err, req, res, next) ->
     unless err instanceof Error then err = new Error err
     status = err.statusCode or err.status
-    message = if status then err.message else null
-    Boom.wrap(err, status, message)
+    Boom.wrap(err, status)
     err.reformat()
     res.set err.output.headers
     res.status err.output.statusCode


Call `anymatch` only once per sort.

Call `anymatch` only once per sort.

```coffeescript
# anymatch will be called 5k times
[5k files array].sort (a, b) -> anysort a, b, criteria

# will be called 1 time, looks cooler
[5k files array].sort anysort criteria

```

--- src/index.coffee
+++ src/index.coffee
@@ -1,24 +1,31 @@
 anymatch = require 'anymatch'

+generateAnysort = (criteria = -> false) ->
+	matcher = anymatch.matcher criteria
+	(a, b) ->
+		indexOfA = matcher a, true
+		indexOfB = matcher b, true
+		[hasA, hasB] = [(indexOfA isnt -1), (indexOfB isnt -1)]
+		if hasA and not hasB
+			-1
+		else if not hasA and hasB
+			1
+		else if indexOfA isnt indexOfB
+			indexOfA - indexOfB
+		# when all else is equal, natural sort
+		else if a < b
+			-1
+		else if a > b
+			1
+		else
+			0
+
 # A/B comparison for use in an Array.sort callback
-anysort = (a, b, criteria = -> false) ->
-	matcher = anymatch.matcher criteria
-	indexOfA = matcher a, true
-	indexOfB = matcher b, true
-	[hasA, hasB] = [(indexOfA isnt -1), (indexOfB isnt -1)]
-	if hasA and not hasB
-		-1
-	else if not hasA and hasB
-		1
-	else if indexOfA isnt indexOfB
-		indexOfA - indexOfB
-	# when all else is equal, natural sort
-	else if a < b
-		-1
-	else if a > b
-		1
+anysort = (a, b, criteria) ->
+	if typeof a is 'function'
+		generateAnysort a
 	else
-		0
+		generateAnysort(criteria) a, b

 # expose anymatch methods
 anysort.match   = anymatch
@@ -30,7 +37,7 @@
 	matcher = anymatch.matcher criteria
 	matched = array.filter matcher
 	unmatched = array.filter (s) -> -1 is matched.indexOf s
-	matched = matched.sort (a, b) -> anysort a, b, criteria
+	matched = matched.sort anysort criteria
 	{matched, unmatched, sorted: matched.concat unmatched}

 # Does a full sort based on an array of criteria, plus the


* update comments

* update comments

--- src/index.coffee
+++ src/index.coffee
@@ -28,6 +28,7 @@
     if aFile.path? and aFile.dest?
       if isFunction(aFile.getContentSync)
         if !aFile.hasOwnProperty('_contents') && aFile.parent && !aFile.parent.isDirectory()
+          #  the inherited file resource object use the parent's contents if parent is a file(not folder) and  no contents on itself.
           vFile = aFile.parent
         else
           vFile = aFile


strict condition

strict condition

--- src/index.coffee
+++ src/index.coffee
@@ -63,7 +63,7 @@
           @close()
           return after false

-        @close() if @_closeAfterRequest
+        @close() if @_closeAfterRequest is true
         after if results and results.length is 1 then results[0] else false

   find: (table, params, options, after) ->
@@ -95,7 +95,7 @@
           @close()
           return after []

-        @close() if @_closeAfterRequest
+        @close() if @_closeAfterRequest is true
         after results

   count: (table, params, after) ->
@@ -113,7 +113,7 @@
           @close()
           return after false

-        @close() if @_closeAfterRequest
+        @close() if @_closeAfterRequest is true
         after parseInt results, 10

   save: (table, params, after = ->) ->
@@ -133,7 +133,7 @@
           @close()
           return after false

-        @close() if @_closeAfterRequest
+        @close() if @_closeAfterRequest is true
         after if params._id? then params else results[0]

   closeAfterRequest: (value) ->


* set encoding to undefined instead of null

--- src/index.coffee
+++ src/index.coffee
@@ -97,7 +97,7 @@
                 vEncoding ?= 'utf8'
                 @encoding = vEncoding
               else
-                @encoding = null
+                @encoding = undefined
               @_contents = result
               @skipSize = aOptions.skipSize if @skipSize isnt aOptions.skipSize
             done.call @, err, result
@@ -128,7 +128,7 @@
             vEncoding ?= 'utf8'
             @encoding = vEncoding
           else
-            @encoding = null
+            @encoding = undefined
           @_contents = @_loadContentSync(aOptions)
         else
           ### !pragma coverage-skip-next ###
@@ -205,7 +205,7 @@
             vEncoding ?= 'utf8'
             @encoding = vEncoding
           else
-            @encoding = null
+            @encoding = undefined
           @_contents = result
           @skipSize = aOptions.skipSize if @skipSize isnt aOptions.skipSize
         done(err, result)
@@ -226,7 +226,7 @@
           vEncoding ?= 'utf8'
           @encoding = vEncoding
         else
-          @encoding = null
+          @encoding = undefined
         @skipSize = aOptions.skipSize if @skipSize isnt aOptions.skipSize
         @_contents = result
     else


* working on index.coffee

* working on index.coffee

--- src/index.coffee
+++ src/index.coffee
@@ -1,23 +1,167 @@
-isInt           = require 'util-ex/lib/is/string/int'
-isFloat         = require 'util-ex/lib/is/string/float'
-isNumber        = require 'util-ex/lib/is/type/number'
+properties      = require 'property-manager/ability'
+createObject    = require 'inherits-ex/lib/createObject'
+isInheritedFrom = require 'inherits-ex/lib/isInheritedFrom'
+isObject        = require 'util-ex/lib/is/type/object'
+isFunction      = require 'util-ex/lib/is/type/function'
 isString        = require 'util-ex/lib/is/type/string'
-Attributes      = require 'abstract-type/lib/attributes'
-Type            = require 'abstract-type'
-register        = Type.register
-aliases         = Type.aliases
+isArray         = require 'util-ex/lib/is/type/array'
+isUndefined     = require 'util-ex/lib/is/type/undefined'
+defineProperty  = require 'util-ex/lib/defineProperty'
+Attributes      = require './attributes/'

-module.exports = class ClassType
-  register ClassType
-  aliases ClassType, 'class'
+objectToString  = Object::toString
+getObjectKeys   = Object.keys
+attributes      = new Attributes()

-  constructor: ->
+module.exports  = class ClassType
+  properties ClassType, name: 'advance'
+
+
+  $attributes: attributes
+
+  constructor: (aTypeName, aOptions)->
+    # create a new instance object if aOptions is not the original
+    # options of the type.
+    if not (this instanceof Type) and not (aTypeName instanceof Type)
+      if aTypeName
+        if isObject aTypeName
+          aOptions = aTypeName
+          aTypeName = attributes.getValue aOptions, 'name'
+        else if not isString aTypeName
+          aOptions = aTypeName
+          aTypeName = undefined
+      if not aTypeName
+        # arguments.callee is forbidden if strict mode enabled.
+        try vCaller = arguments.callee.caller
+        if vCaller and isInheritedFrom vCaller, Type
+          aTypeName = vCaller
+          vCaller = vCaller.caller
+          #get farest hierarchical registered class
+          while isInheritedFrom vCaller, aTypeName
+            aTypeName = vCaller
+            vCaller = vCaller.caller
+          aTypeName = Type.getNameFromClass(aTypeName) if aTypeName
+        return unless aTypeName
+      vTypeClass = Type.registeredClass aTypeName
+      if vTypeClass and aOptions and
+         not vTypeClass::$attributes.isDefaultObject(aOptions)
+        return createObject vTypeClass, aOptions
     return super

-  $attributes: Attributes
-    attributes:
-      type: 'Object'
+  initialize: (aOptions)->
+    defineProperty @, 'errors', null
+    @$attributes.initializeTo @ if @$attributes
+    @_initialize aOptions if @_initialize
+    @assign(aOptions) if aOptions?

-  #@toValue:-> #must be overrided this method!
+  finalize: (aOptions)->
+    @errors = null if @errors
+    @_finalize(aOptions) if @_finalize

-  _validate: (aValue, aOptions)->
+  oldAssign = @::assign
+  assign: (aOptions, aExclude)->
+    @errors = []
+    return oldAssign.call @, aOptions, aExclude
+
+  _validate: (aValue, aOptions)->true
+  error: (aMessage, aOptions)->
+    name = (aOptions && @$attributes.getValue(aOptions, 'name')) || String(@)
+    @errors.push name: name, message: aMessage
+    return
+  isRequired: (aValue, aOptions)->
+    aOptions = @ unless isObject aOptions
+    vRequired = @$attributes.getValue aOptions, 'required'
+    result = not vRequired or (vRequired is true and aValue?)
+  validateRequired: (aValue, aOptions)->
+    result = @isRequired aValue, aOptions
+    @error 'is required', aOptions unless result
+    result
+  validate: (aValue, raiseError, aOptions)->
+    @errors = []
+    if isObject raiseError
+      aOptions    = raiseError
+      raiseError  = aOptions.raiseError
+    aOptions = @mergeTo(aOptions, 'name')
+    aOptions.raiseError = true if raiseError
+    result = @validateRequired aValue, aOptions
+    result = @_validate(aValue, aOptions) if result and aValue?
+    if raiseError isnt false and not result
+      throw new TypeError('"'+aValue + '" is an invalid ' + @name)
+    result
+  isValid: (aValue) ->
+    @validate(aValue, false)
+
+  createValue: (aValue, aOptions)->
+    if aOptions and not @isSame(aOptions)
+      aOptions = @mergeTo(aOptions, 'name')
+      # TODO: seperate the cache-able ability
+      if isFunction Type.getCacheItem
+        # this Type Factory is cache-able.
+        aOptions.cached = true unless aOptions.cached?
+        vType = Factory.getCacheItem @Class, aOptions
+      else
+        vType = @createType aOptions
+    else
+      vType = @
+    createObject vType.ValueType, aValue, vType, aOptions
+  create: @::createValue
+  createType: (aOptions)->
+    delete aOptions.value if aOptions
+    result = createObject @Class, aOptions
+    result
+  cloneType: (aOptions)->
+    aOptions = @exportTo(aOptions, 'name', true, true)
+    aOptions.name = @name unless aOptions.name
+    @createType aOptions
+  clone: @::cloneType
+  # Get(create) a global Type class or create new Value from the json string.
+  # it will create a new type object if options is not the original type
+  # options.
+  @fromJson: (aString)->
+    #aString = JSON.parse aString
+    Type.from JSON.parse(aString)
+  # create a new Type instance  or create new Value from json string.
+  @createFromJson: (aString)->
+    Type.createFrom JSON.parse aString
+
+  # Get(create) a global Type class or create new Value from the parametric
+  # type object.
+  # it will create a new type object if options is not the original(default)
+  # type options.
+  @from: (aObject) ->
+    result = Type aObject
+    if aObject.value? and result
+      result = result.createValue aObject.value
+    result
+  @createFrom: (aObject)->
+    value   = aObject.value
+    result  = Type.create aObject.name, aObject
+    result  = result.createValue value if value? and result
+    result
+
+  toString: (aOptions)->
+    '[type '+ @name+']'
+
+  _inspect: (aOptions, aNameRequired = false)->
+    result = '"' + @name + '"'
+    vAttrs = @toJson(aOptions, aNameRequired).slice(1,-1)
+    result += ': ' + vAttrs if vAttrs
+    result
+  inspect: ->
+    '<type ' + @_inspect()+ '>'
+  toJson: (aOptions, aNameRequired)->
+    result = @toObject(aOptions, aNameRequired)
+    result = JSON.stringify result
+    result
+  _toObject:(aOptions, aNameRequired = true)->
+    vExclude = 'name' unless aNameRequired
+    result = @exportTo aOptions, vExclude, true, true
+    result
+  toObject: (aOptions, aNameRequired)->
+    if aOptions
+      if not aOptions.typeOnly and not isUndefined aOptions.value
+        value = aOptions.value
+      delete aOptions.typeOnly
+    result = @_toObject(aOptions, aNameRequired)
+    result.value = value unless isUndefined value
+    result


Return report & code map results instead of just report

--- src/jscpd.coffee
+++ src/jscpd.coffee
@@ -109,8 +109,10 @@

       logger.profile 'Generate report time:'
       logger.info 'Start report generation...\n'
-      result = report.generate codeMap
+      reportResult = report.generate codeMap
       logger.profile 'Generate report time:'
-      result
+      result =
+        report: reportResult,
+        result: codeMap

 module.exports = jscpd


s/lookahead/non-capturing

s/lookahead/non-capturing

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -565,7 +565,7 @@
 LEADING_SPACES  = /^\s+/
 TRAILING_SPACES = /\s+$/
 NO_NEWLINE      = /// ^
-  (?:                                   # lookahead
+  (?:                                   # non-capturing...
     [-+*&|/%=<>!.\\][<>=&|]* |          # symbol operators
     and | or | is(?:nt)? | n(?:ot|ew) | # word operators
     delete |typeof | instanceof


annotations to lexer.coffee

annotations to lexer.coffee

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -107,6 +107,7 @@
     poppedToken = undefined

     # This if handles "for own key, value of myObject", loop over keys and values of obj, ignoring proptotype
+    # If previous token's tag was 'FOR' and this is 'OWN', then make a token called 'OWN'
     if id is 'own' and @tag() is 'FOR'
       @token 'OWN', id
       return id.length
@@ -118,7 +119,7 @@
     # OR, it can be true if the previos token was NOT SPACED and it is "@". All that makes sense.
     forcedIdentifier = colon or
       (prev = last @tokens) and (prev[0] in ['.', '?.', '::', '?::'] or
-      not prev.spaced and prev[0] is '@')
+      not prev.spaced and prev[0] is '@') # if the previous token was "@" and there is no space between(?)
     tag = 'IDENTIFIER'

     # only do this if it is nOT forcedIdentifier. See if the identifier is in JS_KEYWORDS or COFFEE_KEYWORDS
@@ -127,7 +128,7 @@
       # if we're using WHEN, and the previous tag was LINE_BREAK, then this is a LEADING_WHEN token.
       if tag is 'WHEN' and @tag() in LINE_BREAK
         tag = 'LEADING_WHEN'
-      else if tag is 'FOR' # just make a note we've seen FOR.
+      else if tag is 'FOR' # just make a note we've seen FOR. in a CLASS VAR, so it outlives this stack frame.
         @seenFor = yes
       else if tag is 'UNLESS' # UNLESS is turned into an IF token
         tag = 'IF'


lexer: unrolled @extractNextToken/@match

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -43,26 +43,23 @@
     @outdebt = 0            # The under-outdentation at the current level.
     @indents = []           # The stack of all current indentation levels.
     @tokens  = []           # Stream of parsed tokens in the form ['TYPE', value, line]
+    # At every position, run through this list of attempted matches,
+    # short-circuiting if any of them succeed. Their order determines precedence:
+    # `@literalToken` is the fallback catch-all.
     while (@chunk = code[@i..])
-      @extractNextToken()
+      @identifierToken() or
+      @commentToken()    or
+      @whitespaceToken() or
+      @lineToken()       or
+      @heredocToken()    or
+      @stringToken()     or
+      @numberToken()     or
+      @regexToken()      or
+      @jsToken()         or
+      @literalToken()
     @closeIndentation()
     return @tokens if o.rewrite is off
     (new Rewriter).rewrite @tokens
-
-  # At every position, run through this list of attempted matches,
-  # short-circuiting if any of them succeed. Their order determines precedence:
-  # `@literalToken` is the fallback catch-all.
-  extractNextToken: ->
-    @identifierToken() or
-    @commentToken()    or
-    @whitespaceToken() or
-    @lineToken()       or
-    @heredocToken()    or
-    @stringToken()     or
-    @numberToken()     or
-    @regexToken()      or
-    @jsToken()         or
-    @literalToken()

   # Tokenizers
   # ----------
@@ -74,7 +71,8 @@
   # referenced as property names here, so you can still do `jQuery.is()` even
   # though `is` means `===` otherwise.
   identifierToken: ->
-    return false unless id = @match IDENTIFIER
+    return false unless match = IDENTIFIER.exec @chunk
+    id = match[0]
     @i += id.length
     if id is 'all' and @tag() is 'FOR'
       @token 'ALL', id
@@ -111,7 +109,8 @@
   # Matches numbers, including decimals, hex, and exponential notation.
   # Be careful not to interfere with ranges-in-progress.
   numberToken: ->
-    return false unless number = @match NUMBER
+    return false unless match = NUMBER.exec @chunk
+    number = match[0]
     return false if @tag() is '.' and number.charAt(0) is '.'
     @i += number.length
     @token 'NUMBER', number
@@ -122,8 +121,8 @@
   stringToken: ->
     switch @chunk.charAt 0
       when "'"
-        return false unless string = @match SIMPLESTR
-        @token 'STRING', string.replace MULTILINER, '\\\n'
+        return false unless match = SIMPLESTR.exec @chunk
+        @token 'STRING', (string = match[0]).replace MULTILINER, '\\\n'
       when '"'
         return false unless string = @balancedToken ['"', '"'], ['#{', '}']
         @interpolateString string.replace MULTILINER, '\\\n'
@@ -159,8 +158,8 @@

   # Matches JavaScript interpolated directly into the source via backticks.
   jsToken: ->
-    return false unless @chunk.charAt(0) is '`' and script = @match JSTOKEN
-    @token 'JS', script.slice 1, -1
+    return false unless @chunk.charAt(0) is '`' and match = JSTOKEN.exec @chunk
+    @token 'JS', (script = match[0]).slice 1, -1
     @i += script.length
     true

@@ -203,7 +202,8 @@
   # Keeps track of the level of indentation, because a single outdent token
   # can close multiple indents, so we need to know how far in we happen to be.
   lineToken: ->
-    return false unless indent = @match MULTI_DENT
+    return false unless match = MULTI_DENT.exec @chunk
+    indent = match[0]
     @line += count indent, '\n'
     @i    += indent.length
     prev = @prev 2
@@ -253,10 +253,10 @@
   # Matches and consumes non-meaningful whitespace. Tag the previous token
   # as being "spaced", because there are some cases where it makes a difference.
   whitespaceToken: ->
-    return false unless space = @match WHITESPACE
+    return false unless match = WHITESPACE.exec @chunk
     prev = @prev()
     prev.spaced = true if prev
-    @i += space.length
+    @i += match[0].length
     true

   # Generate a newline token. Consecutive newlines get merged together.
@@ -481,11 +481,6 @@
   prev: (index) ->
     @tokens[@tokens.length - (index or 1)]

-  # Attempt to match a string against the current chunk, returning the indexed
-  # match if successful, and `false` otherwise.
-  match: (regex, index) ->
-    if m = @chunk.match regex then m[index or 0] else false
-
   # Are we in the midst of an unfinished expression?
   unfinished: ->
     (prev  = @prev 2 ) and prev[0] isnt '.' and


preallocate return array and reuse ins function

--- src/gfin.coffee
+++ src/gfin.coffee
@@ -50,9 +50,10 @@
 expirations = (symbol, callback) -> front(symbol, (front) -> callback front.expirations)

 options = (symbol, exps, callback) ->
-	os = []
-	l = exps.length
-	(chain(symbol, m, (o) -> os.push(o); callback(os) unless --l)) for m in exps
+	os = new Array(l = exps.length)
+	ins = (o) -> os[--l] = o; callback(os) unless l
+	chain(symbol, m, ins) for m in exps
+	os

 chains = (symbol, callback) ->
             expirations(symbol, (exps) ->


Cleanup horrible function binding in gurk.coffee

--- src/gurk.coffee
+++ src/gurk.coffee
@@ -93,14 +93,14 @@

   # Game initialization/loading
   # -----------------------------------------------------------------------------
-  startSavedGame: =>
+  startSavedGame: ->
     @game = new Game()
     @game.loadGame(Device.loadGame())
     eburp.track 'Load Game'
     mapView = new MapView(this)
     @setView(mapView)

-  startNewGame : (game) =>
+  startNewGame : (game) ->
     eburp.track 'New Game', Progress: 'Finished'
     @game = game
     club = @game.createItem(Library.getItemTemplateByName("Crude Club"))
@@ -118,7 +118,7 @@
     @setView(mapView)

-  start: =>
+  start: ->
     @screenCanvas = document.getElementById("screenID");
     ctx = @screenCanvas.getContext("2d")
     ctx.webkitImageSmoothingEnabled = false
@@ -163,7 +163,7 @@

   # View utilities
   # -----------------------------------------------------------------------------
-  setView: (view) =>
+  setView: (view) ->
     @scene.removeView(@view) if @view
     console.log("Set View: " + view)
     @stack = new Array()
@@ -171,19 +171,19 @@
     @showView()

-  showView: () =>
+  showView: () ->
     #console.log("View: #{@view.name}")
     @view.doLayout()
     @view.setButtons(@buttonGrid)
     @view.draw()
     $('.game-container .eight-bit-panel').toggle(@view instanceof SplashView);

-  pushView: (view) =>
+  pushView: (view) ->
     @stack.unshift(@view)
     @view = view
     @showView()

-  popView: (result) =>
+  popView: (result) ->
     @scene.removeView @view
     parent = @stack.shift()
     if (parent != null)
@@ -192,13 +192,13 @@
         @view.processResult(result)
       @showView()

-  swapView: (result) =>
+  swapView: (result) ->
     @view = view
     if (result != null)
       @view.processResult(result)
     @showView()

-  popToTopView: (result) =>
+  popToTopView: (result) ->
     if (@stack.length > 0)
       @view = @stack.shift()
       while (@stack.length > 0)
@@ -207,10 +207,10 @@
         @view.processResult(result)
       @showView()

-  isCurrentView: (view) =>
+  isCurrentView: (view) ->
     return @view == view

-  getScreen: () =>
+  getScreen: () ->
     @screen

   windowKeyPress: (event) =>
@@ -246,42 +246,42 @@
     event.stopImmediatePropagation()
     return false

-  getSoundSetting : =>
+  getSoundSetting : ->
     Device.getSetting("sound", true)

-  setSoundSetting : (value) =>
+  setSoundSetting : (value) ->
     Device.setSetting("sound", value)

-  getMusicSetting : =>
+  getMusicSetting : ->
     Device.getSetting("music", true)

-  setMusicSetting : (value) =>
+  setMusicSetting : (value) ->
     Device.setSetting("music", value)
     if (value)
       @resumeMusic()
     else
       @stopMusic()

-  getCombatMusicSetting : =>
+  getCombatMusicSetting : ->
     Device.getSetting("combatMusic", true)

-  setCombatMusicSetting : (value) =>
+  setCombatMusicSetting : (value) ->
     Device.setSetting("combatMusic", value)

-  getFastSetting : =>
+  getFastSetting : ->
     Device.getSetting("fast", false)

-  setFastSetting : (value) =>
+  setFastSetting : (value) ->
     Device.setSetting("fast", value)

-  playSound : (sound) =>
+  playSound : (sound) ->
     return if not @getSoundSetting()
     sound = "/data/sounds/#{sound}"
-    eburp.resources.load sound, (resource) =>
+    eburp.resources.load sound, (resource) ->
       resource.data.volume = 0.25
       resource.data.play()

-  playMusic : (track,musicOn=@getMusicSetting()) =>
+  playMusic : (track,musicOn=@getMusicSetting()) ->
     @music = track
     return if not musicOn
     track = "/data/music/#{track}"
@@ -292,21 +292,21 @@
       resource.data.play()
     @

-  playCombatMusic : =>
+  playCombatMusic : ->
     @playMusic eburp.data.combatMusic, @getCombatMusicSetting()

-  stopMusic : =>
+  stopMusic : ->
     if @activeTrack and @activeTrack.isReady()
       @activeTrack.data.pause()
       @activeTrack.data.currentTime = 0

-  resumeMusic : =>
+  resumeMusic : ->
     if @activeTrack and @activeTrack.isReady()
       @activeTrack.data.play()
     else if @music
       @playMusic @music

-  phoneClick: (e, offsetX = 0, offsetY = 0) =>
+  phoneClick: (e, offsetX = 0, offsetY = 0) ->
     relMouse = (event) ->
       canoffset = $(event.currentTarget).offset();
       x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - Math.floor(canoffset.left);
@@ -317,17 +317,17 @@
     point.y -= offsetY
     @buttonGrid.clicked(point)

-  showSettings : () =>
+  showSettings : () ->
     @pushView(new SettingsView(this))

-  buttonPressed: (text) =>
+  buttonPressed: (text) ->
     @view?.command(text)

-  showAlert: (icon, title, text, result) =>
+  showAlert: (icon, title, text, result) ->
     alert = new AlertView(this, icon, title, text, result)
     @pushView(alert)

-  showConfirm: (icon, title, text, yesResult, noResult) =>
+  showConfirm: (icon, title, text, yesResult, noResult) ->
     confirm = new ConfirmView(this, icon, title, text, yesResult, noResult)
     @pushView(confirm)


Emits an event when the app has started

Emits an event when the app has started

--- src/hull.coffee
+++ src/hull.coffee
@@ -59,6 +59,7 @@
         throw err if !errcb

       initProcess.done ()->
+        hull.app.sandbox.emit('hull.started')
         cb(window.Hull) if cb

       return hull


Fix functions' callback with hardcoded 'null' as error parameter

--- src/model.coffee
+++ src/model.coffee
@@ -82,6 +82,7 @@
     # Returns null
     @save: (id, data, callback) ->
         @adapter.save id, data, (err, attributes) =>
+            return callback err if err?
             callback null, new this(attributes)

     # Public: change some attributes of the model (PATCH)
@@ -93,6 +94,7 @@
     # Returns null
     @updateAttributes: (id, data, callback) ->
         @adapter.updateAttributes id, data, (err, updated) =>
+            return callback err if err?
             callback null, new this(updated)


delayed import on demand

delayed import on demand

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -2070,6 +2070,7 @@
   @properties: []
   @rootFile: null
   @importedDeclarations: {}
+  @delayedEnabled: no

   constructor: (@searchPath, types={}) ->
     {@isAbsolute, @isRelative, @isResolve} = types
@@ -2110,6 +2111,7 @@
       Import.properties = []
       Import.rootFile = null
       Import.importedDeclarations = {}
+      Import.delayedEnabled = no
       throw ex

   compileNode: (o) ->
@@ -2125,6 +2127,7 @@

     unless Import.imported[filename] or o.importingFile is filename
       args.push new Literal "function(cl) { #{variable} = cl; }"
+      Import.delayedEnabled = yes

     word = if Import.rootFile isnt o.filename then "this" else "__imports"

@@ -2182,23 +2185,27 @@
       Import.properties = []
       Import.rootFile = null
       Import.importedDeclarations = {}
+      Import.delayedEnabled = no

   @assignImports: (o)  ->
     o = extend {}, o
     o.indent += TAB

-    properties =
-      list    : new Literal "{}"
-      delayed : new Literal "[]"
-
-      put : new Literal "function(path, code) { " +
-        "this.list[path] = code; " +
-      "}"
-      get : new Literal "function(path, delayed) { " +
+    properties = {}
+    properties.list    = new Literal "{}"
+    properties.delayed = new Literal "[]" if Import.delayedEnabled
+    properties.put = new Literal "function(path, code) { " +
+      "this.list[path] = code; " +
+    "}"
+    properties.get = switch Import.delayedEnabled
+      when yes then new Literal "function(path, delayed) { " +
         "if (delayed) " +
           "this.delayed.push([path, delayed]); " +
         "return this.list[path]; " +
       "}"
+      else new Literal "function(path) { " +
+        "return this.list[path]; " +
+      "}"

     thisLit = new Literal "this"

@@ -2210,11 +2217,12 @@
     for values in Import.properties
       code.body.push new Call   new Value(thisLit, [ new Access new Literal "put" ]), values

-    code.body.push new Literal "(function(a, b, c) { " +
-      "while (c = a.shift()) { " +
-        "c[1](b[c[0]]); " +
-      "} " +
-    "})(this.delayed, this.list)"
+    if Import.delayedEnabled
+      code.body.push new Literal "(function(a, b, c) { " +
+        "while (c = a.shift()) { " +
+          "c[1](b[c[0]]); " +
+        "} " +
+      "})(this.delayed, this.list)"

     code.body.push new Literal "this"
     code.body.makeReturn()


an interp mode so that no install of runtime is needed

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -2658,13 +2658,16 @@
         if window_mode
           window_val = new Value new Literal v
         InlineRuntime.generate(if window_val then window_val.copy() else null)
-      when "node", "browserify"
-        modname = "iced-runtime"
+      when "node", "browserify", "interp"
+        interp = (v is "interp")
+        modname = if interp then "iced-coffee-script/lib/iced" else "iced-runtime"
         accessname = iced.const.ns
         file = new Literal "'#{modname}'"
+        access = new Access new Literal accessname
         req = new Value new Literal "require"
         call = new Call req, [ file ]
         callv = new Value call
+        callv.add access if interp
         ns = new Value new Literal iced.const.ns
         new Assign ns, callv
       when "none" then null


done with merge of class Base

done with merge of class Base

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -7,6 +7,7 @@

 {Scope} = require './scope'
 {RESERVED, STRICT_PROSCRIBED} = require './lexer'
+iced = require './iced'

 # Import the helpers we plan to use.
 {compact, flatten, extend, merge, del, starts, ends, last, some,
@@ -21,6 +22,7 @@
 NO      = -> no
 THIS    = -> this
 NEGATE  = -> @negated = not @negated; this
+NULL    = -> new Value new Literal 'null'

 #### CodeFragment

@@ -54,6 +56,22 @@
 # scope, and indentation level.
 exports.Base = class Base

+  constructor: ->
+    @icedContinuationBlock = null
+
+    # iced AST node flags -- since we make several passes through the
+    # tree setting these bits, we'll actually just flip bits in the nodes,
+    # rather than setting function pointers to YES or NO.
+    @icedLoopFlag        = false
+    @icedNodeFlag        = false
+    @icedGotCpsSplitFlag = false
+    @icedCpsPivotFlag    = false
+    @icedHasAutocbFlag   = false
+    @icedFoundArguments  = false
+    @icedParentAwait     = null
+    @icedCallContinuationFlag = false
+
+  # Common logic for determining whether to wrap this node in a closure before
   compile: (o, lvl) ->
     fragmentsToText @compileToFragments o, lvl

@@ -68,7 +86,9 @@
     o.level  = lvl if lvl
     node     = @unfoldSoak(o) or this
     node.tab = o.indent
-    if o.level is LEVEL_TOP or not node.isStatement(o)
+    if node.icedHasContinuation() and not node.icedGotCpsSplitFlag
+      node.compileCps o
+    else if o.level is LEVEL_TOP or not node.isStatement(o)
       node.compileNode o
     else
       node.compileClosure o
@@ -79,6 +99,16 @@
     if jumpNode = @jumps()
       jumpNode.error 'cannot use a pure statement in an expression'
     o.sharedScope = yes
+    # Solution for an iced corner case:
+    #
+    # foo = (autocb) ->
+    #   x = (i for i in [0..10])
+    #   x
+    #
+    #  We don't want the autocb to fire in the evaluation of the list
+    #  comprehension on the RHS.
+    #
+    @icedClearAutocbFlags()
     Closure.wrap(this).compileNode o

   # If the code generation wishes to use the result of a complex expression
@@ -131,9 +161,24 @@
   # `toString` representation of the node, for inspecting the parse tree.
   # This is what `coffee --nodes` prints out.
   toString: (idt = '', name = @constructor.name) ->
+    extras = []
+    extras.push "A" if @icedNodeFlag
+    extras.push "L" if @icedLoopFlag
+    extras.push "P" if @icedCpsPivotFlag
+    extras.push "C" if @icedHasAutocbFlag
+    extras.push "D" if @icedParentAwait
+    extras.push "G" if @icedFoundArguments
+    if extras.length
+      extras = " (" + extras.join('') + ")"
+    tree = '\n' + idt + name
     tree = '\n' + idt + name
     tree += '?' if @soak
+    tree += extras
     @eachChild (node) -> tree += node.toString idt + TAB
+    if @icedContinuationBlock
+      idt += TAB
+      tree += '\n' + idt + "Continuation"
+      tree += @icedContinuationBlock.toString idt + TAB
     tree

   # Passes each child to a function, breaking when the function returns `false`.
@@ -157,6 +202,106 @@
     continue until node is node = node.unwrap()
     node

+  # Start iced additions...
+
+  # Don't try this at home with actual human kids.  Added for iced
+  # for slightly different tree traversal mechanics.
+  flattenChildren : ->
+    out = []
+    for attr in @children when @[attr]
+      for child in flatten [@[attr]]
+        out.push (child)
+    out
+
+  #
+  # AST Walking Routines for CPS Pivots, etc.
+  #
+  #  There are three passes:
+  #    1. Find await's and trace upward.
+  #    2. Find loops found in #1, and flood downward
+  #    3. Find break/continue found in #2, and trace upward
+  #
+
+  # icedWalkAst
+  #
+  #   Walk the AST looking for taming. Mark a node as with iced flags
+  #   if any of its children are iced, but don't cross scope boundary
+  #   when considering the children.
+  #
+  #   The paremeter `p` is the parent `await`.  All nodes beneath the
+  #   first `await` in a function scope should point to its highest
+  #   parent `await`.  This is so in the case of nested `await`s,
+  #   they're really pulled out and run in sequence as the level of the
+  #   topmost await.
+  #
+  #   The parameter `o` is a global object, passed through all without
+  #   copies, to push information up and down the AST. This parameter is
+  #   used with subfields:
+  #
+  #      o.foundAutocb    -- on if the parent function has an autocb
+  #      o.foundDefer     -- on if defer() was found anywhere in the AST
+  #      o.foundAwait     -- on if await... was found anywhere in the AST
+  #      o.foundAwaitFunc -- on if await found in this func
+  #      o.currFunc       -- the current func we're in
+  #      o.foundArguments -- on if we found reference to 'arguments'
+  #
+  icedWalkAst : (p, o) ->
+    @icedParentAwait = p
+    @icedHasAutocbFlag = o.foundAutocb
+    for child in @flattenChildren()
+      @icedNodeFlag = true if child.icedWalkAst p, o
+    @icedNodeFlag
+
+  # icedWalkAstLoops
+  #   Walk all loops that are marked as "iced" and mark their children
+  #   as being children in a iced loop. They'll need more translations
+  #   than other nodes. Eventually, "switch" statements might also be "loops"
+  icedWalkAstLoops : (flood) ->
+    flood = true if  @isLoop() and @icedNodeFlag
+    flood = false if @isLoop() and not @icedNodeFlag
+    @icedLoopFlag = flood
+    for child in @flattenChildren()
+      @icedLoopFlag = true if child.icedWalkAstLoops flood
+    @icedLoopFlag
+
+  # icedWalkCpsPivots
+  #   A node is marked as a "cpsPivot" of it is (a) a 'iced' node,
+  #   (b) a jump node in a iced while loop; or (c) an ancestor of (a) or (b).
+  icedWalkCpsPivots : ->
+    @icedCpsPivotFlag = true if @icedNodeFlag or (@icedLoopFlag and @icedIsJump())
+    for child in @flattenChildren()
+      @icedCpsPivotFlag = true if child.icedWalkCpsPivots()
+    @icedCpsPivotFlag
+
+  icedClearAutocbFlags : ->
+    @icedHasAutocbFlag = false
+    @traverseChildren false, (node) ->
+      node.icedHasAutocbFlag = false
+      true
+
+  # A generic iced AST rotation is just to push down to its children
+  icedCpsRotate: ->
+    for child in @flattenChildren()
+      child.icedCpsRotate()
+    this
+
+  icedIsCpsPivot            :     -> @icedCpsPivotFlag
+  icedNestContinuationBlock : (b) -> @icedContinuationBlock = b
+  icedHasContinuation       :     -> (!!@icedContinuationBlock)
+  icedCallContinuation      :     -> @icedCallContinuationFlag = true
+  icedWrapContinuation      :     NO
+  icedIsJump                :     NO
+
+  icedUnwrap: (e) ->
+    if e.icedHasContinuation() and @icedHasContinuation()
+      this
+    else
+      if @icedHasContinuation()
+        e.icedContinuationBlock = @icedContinuationBlock
+      e
+
+  # End iced additions...
+
   # Default implementations of the common node properties and methods. Nodes
   # will override these with custom logic, if needed.
   children: []
@@ -166,6 +311,7 @@
   isComplex       : YES
   isChainable     : NO
   isAssignable    : NO
+  isLoop          : NO

   unwrap     : THIS
   unfoldSoak : NO
@@ -1478,6 +1624,7 @@
   children: ['condition', 'guard', 'body']

   isStatement: YES
+  isLoop : YES

   makeReturn: (res) ->
     if res


nodes: fix indent

nodes: fix indent

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -123,9 +123,9 @@
         @visibleLines = parseInt value, 10
         # TODO: something
       when 'deleteConstraint', 'referenceTo', 'relationshipLabel', 'relationshipName', 'relationshipOrder', 'writeRequiresMasterRead'
-          ''
+        ''
       when 'summarizedField', 'summaryFilterItems', 'summaryForeignKey', 'summaryOperation', 'displayFormat'
-          ''
+        ''
       else throw new Error "unknown field #{field} on field object"

 class DeploymentStatus


added more test values

added more test values


Loader: In fetch_objects, add an option for fetch_textures (true by default).

--- engine/fetch_assets.coffee
+++ engine/fetch_assets.coffee
@@ -113,7 +113,7 @@
 # This returns a promise of all things necessary to display the object
 # (meshes, textures, materials)
 fetch_objects = (object_list, options={}) ->
-    {render} = options
+    {render, fetch_textures=true} = options
     if not object_list.length
         return Promise.resolve()

@@ -127,7 +127,7 @@
             for mat in ob.materials
                 # TODO: we'll assume here there's only one shader per material for now
                 shader = mat.last_shader
-                if not shader and mat.scene?
+                if not shader and mat.scene? and fetch_textures
                     # TODO: add this back when we can ensure
                     # it is drawn at least once, to force loading into the GPU
                     ###


fix bug

fix bug

--- api/services/file.coffee
+++ api/services/file.coffee
@@ -63,7 +63,7 @@
 			.catch Promise.reject

 	type: (inode) ->
-		inode.?contentType
+		inode?.contentType

 	isImg: (inode) ->
 		(/^image/i).test @type inode


Make the stream infinite scroll work within 200px of the bottom, and only trigger one load at a time.

--- app/coffee/stream.coffee
+++ app/coffee/stream.coffee
@@ -30,18 +30,23 @@
     )
     # infinite scrolling
     $(window).scroll =>
-      if ($(window).scrollTop() == $(document).height() - $(window).height())
+      if ($(window).scrollTop() > $(document).height() - $(window).height() - 200)
+        # $(window).scrollTop($(document).height() - $(window).height() - 300)
         @loadNextPage()
   clear: ->
     $('#noMoreEntries, .noEntrys, #nextPageLoading').hide()

   loadNextPage: ->
+    return if @currentlyLoading
+    @currentlyLoading = true
     @clear()
     $('#nextPageLoading').show()
     @page += 1
     @stream.load({ page: @page }).then (data)=>
       @clear()
+      @currentlyLoading = false
       if !data.html? || data.html == ""
+        @currentlyLoading = true # No more entries to load.
         $('#noMoreEntries').show()

       @$container.append(data.html)


Don't touch the sql if there is no view

Don't touch the sql if there is no view

--- src/quest.coffee
+++ src/quest.coffee
@@ -161,12 +161,14 @@
   #
   # Returns an {Object} with the results of the last query in the block of sql
   # or file passed. This object will have a `rows` property.
-  sql: (queries, view={}, cb) ->
+  sql: (queries, view, cb) ->
     if typeof(queries) != 'string'
       sqlPath = path.join @questDir, 'sql', queries.file
       console.log ">>".blue.bold, "#{sqlPath}".blue.bold
       queries = fs.readFileSync(sqlPath, encoding: 'utf-8')
-    queries = Mustache.render queries, view
+    if view
+      queries = Mustache.render queries, view
+    queries = queries
       .split ';'
       .map (s) -> s.trim()
       .filter (s) -> s


Queries should be AND, not OR.

Queries should be AND, not OR.

--- src/quote.coffee
+++ src/quote.coffee
@@ -38,8 +38,6 @@
       quotes = _.filter quotes, (quote) -> quote.length > 1
       callback(null)

-  rxEscape = (str) -> (str + '').replace /([.?*+^$[\]\\(){}|-])/g, "\\$1"
-
   robot.respond /quote(.*)/i, (msg) ->
     query = msg.match[1].trim().split /\s+/
     query = _.filter query, (part) -> part.length > 0
@@ -47,9 +45,8 @@
     potential = quotes

     if query.length > 0
-      escaped = (rxEscape(q) for q in query).join '|'
-      pattern = new RegExp(escaped, "gi")
-      potential = _.filter potential, (quote) -> pattern.test(quote)
+      potential = _.filter potential, (quote) ->
+        _.every query, (q) -> quote.indexOf(q) isnt -1

     if potential.length > 0
       chosen = _.random potential.length - 1


Fix support for channel/group join/leave, and add support for channel/group topic notifications

--- src/slack.coffee
+++ src/slack.coffee
@@ -63,13 +63,17 @@
     return if user.name == @robot.name

     # Test for enter/leave messages
-    if msg.type is 'channel_join' or 'group_join'
+    if msg.subtype is 'channel_join' or msg.subtype is 'group_join'
       @robot.logger.debug "#{user.name} has joined #{channel.name}"
       @receive new EnterMessage user

-    else if msg.type is 'channel_leave' or 'group_leave'
+    else if msg.subtype is 'channel_leave' or msg.subtype is 'group_leave'
       @robot.logger.debug "#{user.name} has left #{channel.name}"
       @receive new LeaveMessage user
+
+    else if msg.subtype is 'channel_topic' or msg.subtype is 'group_topic'
+      @robot.logger.debug "#{user.name} set the topic in #{channel.name} to #{msg.topic}"
+      @receive new TopicMessage user, msg.topic, msg.ts

     else
       # Build message text to respond to, including all attachments


fixes #330 again

fixes #330 again

--- chrome/content/zotero-better-bibtex/preferences.coffee
+++ chrome/content/zotero-better-bibtex/preferences.coffee
@@ -170,31 +170,22 @@

   refresh: ->
     exportlist = document.getElementById('better-bibtex-auto-exports')
-    for node in exportlist.children
-      try
-        exportlist.removeChild(node)
+    while exportlist.firstChild
+      exportlist.removeChild(exportlist.firstChild)

     tree = new BetterBibTeXAutoExport('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', exportlist, document)

     for ae in Zotero.DB.query("select * from betterbibtex.autoexport order by path")
       ae.status = 'running' if Zotero.BetterBibTeX.auto.running == ae.id
-      tree.treeitem({autoexport: "#{ae.id}", container: 'true', '': ->
+      tree.treeitem({autoexport: "#{ae.id}", '': ->
         @treerow(->
-          @treecell({editable: 'false', label: "#{BetterBibTeXAutoExportPref.exportName(ae.collection)} -> #{ae.path.replace(/^.*[\\\/]/, '')}"})
+          @treecell({editable: 'false', label: "#{BetterBibTeXAutoExportPref.exportType(ae.collection)}: #{BetterBibTeXAutoExportPref.exportName(ae.collection)}"})
           @treecell({editable: 'false', label: ae.status})
-        )
-        @treechildren(->
-          @treeitem(autoexport: "#{ae.id}", '': ->
-            @treerow(->
-              @treecell({editable: 'false', label: "#{BetterBibTeXAutoExportPref.exportType(ae.collection)}: #{BetterBibTeXAutoExportPref.exportName(ae.collection)}"})
-              @treecell({editable: 'false', label: ae.status})
-              @treecell({editable: 'false', label: ae.path})
-              @treecell({editable: 'false', label: Zotero.BetterBibTeX.translatorName(ae.translatorID)})
-              @treecell({editable: 'false', label: ae.exportCharset})
-              @treecell({editable: 'false', label: ae.useJournalAbbreviation})
-              @treecell({editable: 'false', label: ae.exportNotes})
-            )
-          )
+          @treecell({editable: 'false', label: ae.path})
+          @treecell({editable: 'false', label: Zotero.BetterBibTeX.translatorName(ae.translatorID)})
+          @treecell({editable: 'false', label: ae.exportCharset})
+          @treecell({editable: 'false', label: ae.useJournalAbbreviation})
+          @treecell({editable: 'false', label: ae.exportNotes})
         )
       })


Workspace: Emitting an event when chat pane closed.

--- client/app/CommonViews/workspace/panes/chatpane.coffee
+++ client/app/CommonViews/workspace/panes/chatpane.coffee
@@ -19,6 +19,7 @@
         @toggle.toggleClass "active"
         @unreadCount = 0
         @title.updatePartial "Chat"
+        @emit "WorkspaceChatClosed"  unless @isVisible()

     @title       = new KDCustomHTMLView
       tagName    : "span"


NavigationMachineItem: Removed commented code block.

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -74,21 +74,6 @@

       .on "public-#{@machine._id}", (event)=>
         @handleMachineEvent event
-
-      # These are updating machine data on this instance indivudally
-      # but since we have more data to update, I'm updating all machines
-      # for now.
-      #
-      # .on "revive-#{@machine._id}", (machine)=>
-      #   @machine = machine
-
-      #   @label.updatePartial @machine.label
-      #   @alias   = @machine.slug or @label
-      #   newPath  = groupifyLink "/IDE/#{@alias}/my-workspace"
-
-      #   @setAttributes
-      #     href   : newPath
-      #     title  : "Open IDE for #{@alias}"

   settingsEnabled: ->


Split secondary index query from shard distribution and assignment query for web UI table page. Reduce CPU impact on server if the page is open.

--- admin/static/coffee/tables/table.coffee
+++ admin/static/coffee/tables/table.coffee
@@ -15,7 +15,8 @@
             @distribution = null

             @guaranteed_timer = null
-            @failable_timer = null
+            @failable_index_timer = null
+            @failable_misc_timer = null

             # Initialize the model with mostly empty dummy data so we can render it right away
             @model = new Table {id: id}
@@ -34,7 +35,7 @@
             # we don't get from the system tables includes things like
             # table/shard counts, or secondary index status. Those are
             # obtained from table.info() and table.indexStatus() below
-            # in `failable_query` so if they fail we still get the
+            # in `failable_*_query` so if they fail we still get the
             # data available in the system tables.
             guaranteed_query =
                 r.do(
@@ -62,26 +63,39 @@
                             ).without('shards')
                         )
                     )
+
+            # This query only checks the status of secondary indexes.
+            # It can throw an exception and failif the primary
+            # replica is unavailable (which happens immediately after
+            # a reconfigure).
+            failable_index_query = r.do(
+                r.db(system_db).table('table_config').get(this_id)
+                (table_config) ->
+                    r.db(table_config("db"))
+                    .table(table_config("name"), {useOutdated: true})
+                    .indexStatus()
+                    .pluck('index', 'ready', 'blocks_processed', 'blocks_total')
+                    .merge( (index) -> {
+                        id: index("index")
+                        db: table_config("db")
+                        table: table_config("name")
+                    }) # add an id for backbone
+            )
+
+            # Query to load data distribution and shard assignments.
+            # We keep this separate from the failable_index_query because we don't want
+            # to run it as often.
             # This query can throw an exception and failif the primary
             # replica is unavailable (which happens immediately after
             # a reconfigure). Since this query makes use of
-            # table.info() and table.indexStatus(), it's separated
-            # from the guaranteed query above.
-            failable_query = r.do(
+            # table.info(), it's separated from the guaranteed query above.
+            failable_misc_query = r.do(
                 r.db(system_db).table('table_status').get(this_id),
                 r.db(system_db).table('table_config').get(this_id),
-                r.db(system_db).table('server_config').coerceTo('array'),
-                (table_status, table_config, server_config) ->
+                r.db(system_db).table('server_config')
+                    .map((x) -> [x('name'), x('id')]).coerceTo('ARRAY').coerceTo('OBJECT'),
+                (table_status, table_config, server_name_to_id) ->
                     table_status.merge({
-                        indexes: r.db(table_status("db"))
-                            .table(table_status("name"), {useOutdated: true})
-                            .indexStatus()
-                            .pluck('index', 'ready', 'blocks_processed', 'blocks_total')
-                            .merge( (index) -> {
-                                id: index("index")
-                                db: table_status("db")
-                                table: table_status("name")
-                            }) # add an id for backbone
                         distribution: r.db(table_status('db'))
                             .table(table_status('name'), {useOutdated: true})
                             .info()('doc_count_estimates')
@@ -100,29 +114,48 @@
                                     .table(table_status('name'), {useOutdated: true})
                                     .info()('doc_count_estimates')(position)
                                 primary:
-                                    id: server_config.filter({name: shard("primary_replica")}).nth(0)("id")
+                                    id: server_name_to_id(shard("primary_replica"))
                                     name: shard("primary_replica")
                                 replicas: shard("replicas")
                                     .filter( (replica) -> replica.ne(shard("primary_replica")))
                                     .map (name) ->
-                                        id: server_config.filter({name: name}).nth(0)("id")
+                                        id: server_name_to_id(name)
                                         name: name
                         ).coerceTo('array')
                     })
-            )
-            # This timer keeps track of the failable query, so we can
+            )
+
+            # This timer keeps track of the failable index query, so we can
             # cancel it when we navigate away from the table page.
-            @failable_timer = driver.run failable_query, 1000, (error, result) =>
+            @failable_index_timer = driver.run failable_index_query, 1000, (error, result) =>
                 if error?
                     console.log error.msg
                     return
                 @error = null
                 if @indexes?
-                    @indexes.set _.map(result.indexes, (index) -> new Index index)
+                    @indexes.set _.map(result, (index) -> new Index index)
                 else
-                    @indexes = new Indexes _.map result.indexes, (index) ->
+                    @indexes = new Indexes _.map result, (index) ->
                         new Index index
                 @table_view?.set_indexes @indexes
+
+                @model.set
+                    indexes: result
+                if !@table_view?
+                    @table_view = new TableView.TableMainView
+                        model: @model
+                        indexes: @indexes
+                        distribution: @distribution
+                        shards_assignments: @shards_assignments
+                    @render()
+
+            # This timer keeps track of the failable misc query, so we can
+            # cancel it when we navigate away from the table page.
+            @failable_misc_timer = driver.run failable_misc_query, 10000, (error, result) =>
+                if error?
+                    console.log error.msg
+                    return
+                @error = null
                 if @distribution?
                     @distribution.set _.map result.distribution, (shard) ->
                         new Shard shard
@@ -176,7 +209,6 @@
                         distribution: @distribution
                         shards_assignments: @shards_assignments
                     @render()
-

             # This timer keeps track of the guaranteed query, running
             # it every 5 seconds. We cancel it when navigating away
@@ -221,7 +253,8 @@
             @
         remove: =>
             driver.stop_timer @guaranteed_timer
-            driver.stop_timer @failable_timer
+            driver.stop_timer @failable_index_timer
+            driver.stop_timer @failable_misc_timer
             @table_view?.remove()
             super()


Check theodo.fr projects before sending to CTO

--- client/src/daily-report/services/report-builder.coffee
+++ client/src/daily-report/services/report-builder.coffee
@@ -29,14 +29,20 @@
     emails = (member.email for member in project.team when member.daily is 'to')
     _.filter emails

-  renderCc = (project, copyCto = false) ->
+  renderCc = (project) ->
     emails = (member.email for member in project.team when member.daily is 'cc')
-
-    # Send a copy to the CTO if sprint is RED and ending soon.
-    if copyCto
-      emails.push 'maximet@theodo.fr'
+    _.filter emails

-    _.filter emails
+  # Given the full list of recipients, return true if there is at least
+  # one theodo.fr email address.
+  # This function is used to send a copy of the daily mail to the CTO of Theodo.fr
+  # only if it is a theodo.fr project (Scrumble is used by other companies)
+  isTheodoFrSprint = (emailAdresses) ->
+    for email in emailAdresses
+      if email.endsWith("theodo.fr")
+        return true
+    return false
+

   # If gap < 0 and end of sprint is in less than 2 days, returns true
   # Used to send a copy to the CTO
@@ -51,7 +57,6 @@
       if sprint?.dates?.end
         if moment().diff(sprint.dates.end, 'days') < -2 # End of sprint is in more than 2 days
           return false
-
       return true

@@ -86,9 +91,16 @@
     dynamicFieldsPromise = dynamicFields.ready sprint, project

     dynamicFieldsPromise.then (builtDict) ->
+      emailsTo = renderTo project
+      emailsCc = renderCc project
+
+      if isTheodoFrSprint(emailsTo.concat emailsCc) and shouldAddCto sprint
+        emailsCc.push 'maximet@theodo.fr'
+
+
       prebuildMessage =
-        to: renderTo project
-        cc: renderCc project, shouldAddCto sprint
+        to: emailsTo
+        cc: emailsCc
         subject: dynamicFields.render sections.subject, builtDict
         body: dynamicFields.render htmlMessage, builtDict


remove step sound from entity class

remove step sound from entity class

--- app/models/Entity.coffee
+++ app/models/Entity.coffee
@@ -18,11 +18,6 @@
   position:
     x: 0
     y: 0
-
-  ###
-  @property [String] The entity's StepSound
-  ###
-  #stepSound: 'defaultStep'

   ###
   @property [Integer]
@@ -65,11 +60,6 @@
       @viewDirection = vd
     else
       @animationStep++
-      # this prevents Player sounding like a mashinegun
-      # it's an ugly hack an should be done better later
-      if (@animationStep % 3) == 0
-        foo = bar
-        #mediator.publish 'play', @stepSound, 0.5

   ###
   Is called if the Player stands in front of this Entity and want's to interact with it.


ReferenceError: path is not defined     at Object.exports.parseArgs (/home/xxx/ShadowSPDY/lib/shadowspdy/utils.js:144:20)

--- src/utils.coffee
+++ src/utils.coffee
@@ -23,6 +23,7 @@
 fs = require 'fs'
 util = require 'util'
 argparse = require 'argparse'
+path = require 'path'

 utils = exports


Sort.

Sort.

--- src/utils.coffee
+++ src/utils.coffee
@@ -57,10 +57,10 @@
       indent:
         style: '  '
         base: 0
-      quotes: 'auto'
+      compact: no
       escapeless: yes
       parentheses: no
-      compact: no
+      quotes: 'auto'
       semicolons: no

   # No source map


Added string formatting utilities for time module and moved getNonOcuppyingLength to utils

--- src/utils.coffee
+++ src/utils.coffee
@@ -5,13 +5,14 @@
 { execSync } = require 'child_process'
 isExe = require 'is-executable'
 readLineSync = require 'readline-sync'
+{ stripColor } = require 'chalk'

 logger = require './logger'
 styler = require './styler'

-module.exports = @
+shelljs.config.silent = yes

-shelljs.config.silent = yes
+DFL_DECIMAL_PLACES = 3

 normalizeError = (err) -> err.toString().replace("Error: ", "")[...-1]

@@ -149,7 +150,23 @@

     answer in ['y', 'yes', 'yep', 'ye', 'sure', 'si', 'course', 'of course']

+@prettyDecimal = (value, { decimal_places = DFL_DECIMAL_PLACES } = {}) ->
+    rounder = Math.pow 10, decimal_places
+    Math.round(value*rounder)/rounder

+@getNonOcuppyingLength = (str) -> str.length - stripColor(str).length
+
+COMPARTMENT_SEPARATOR = " | "
+
+@compartimentedString = (values...) =>
+    string = ""
+    for { value, space, padKind = "End" }, i in values
+        string += COMPARTMENT_SEPARATOR if i > 0 and values[i-1].value.length > 0
+        padding = space + @getNonOcuppyingLength(value)
+        padding += COMPARTMENT_SEPARATOR.length if value.length is 0
+        string += _['pad' + padKind](value, padding)
+
+    string

 # Testing Code
 ###


Update RangesTracker

Update RangesTracker


:potable_water::green_heart: Updated at https://danielx.net/editor/

--- pixie.cson
+++ pixie.cson
@@ -14,7 +14,7 @@
   postmaster: "distri/postmaster:v0.2.3"
   runtime: "distri/runtime:v0.3.0"
   size: "distri/size:master"
-  "touch-canvas": "distri/touch-canvas:v0.4.1"
+  "touch-canvas": "distri/touch-canvas:v0.4.2"
   "undo": "distri/undo:v0.2.0"
   util: "distri/util:v0.1.0"
 width: 1280


Markdown quoting on mobile

Markdown quoting on mobile

--- app/assets/javascripts/mobile.js.coffee
+++ app/assets/javascripts/mobile.js.coffee
@@ -6,6 +6,7 @@
 #= require backbone_datalink

 #= require vendor/jquery.timeago
+#= require vendor/to-markdown

 #= require backbone/sugar
 #= require sugar
@@ -67,10 +68,14 @@
 window.quotePost = (postId) ->
   postDiv = "#post-" + postId
   if $(postDiv).length > 0
+    wrapInBlockquote = (string) ->
+      ("> " + line for line in string.split("\n")).join("\n")
+
     permalink = jQuery(postDiv + " .post_info .permalink a").get()[0].href.replace(/^https?:\/\/([\w\d\.:\-]*)/, "")
     username = jQuery(postDiv + " .post_info .username a").text()
-    content = jQuery(postDiv + " .body").html().replace(/^[\s]*/, "").replace(/[\s]*$/, "").replace(/
/g, "\n")
-    quotedPost = "
Posted by " + username + ":" + content + "
"
+    content = jQuery(postDiv + " .body").html().replace(/^[\s]*/, "").replace(/[\s]*$/, "")
+    cite = "Posted by [#{username}](#{permalink}):"
+    quotedPost = wrapInBlockquote("#{cite}\n\n#{toMarkdown content}")
     window.addToReply quotedPost

 parsePost = (body) ->


Fix bibtex generator on back button

Fix bibtex generator on back button

--- app/assets/javascripts/papers.js.coffee
+++ app/assets/javascripts/papers.js.coffee
@@ -64,10 +64,17 @@
         $(window).one 'click', remove

 $(document).on "ready", ->
+  console.log 'ready'
   setupBibtex()
   setupPaperPage()

 $(document).on "page:load", ->
+  $('.bibtex a').removeClass('zeroclipboard-js-hover')
+  ZeroClipboard.destroy()
+  setupBibtex()
+  setupPaperPage()
+
+$(document).on 'page:restore', ->
   $('.bibtex a').removeClass('zeroclipboard-js-hover')
   ZeroClipboard.destroy()
   setupBibtex()


(limit error feedback to 3) : The registration box should be fixed height:  [#77307148]

When a large form has many error fields, the form becomes very tall.  To address this, only show
a maximum of 3 form validation errors at once....

--- app/assets/javascripts/signup.js.coffee
+++ app/assets/javascripts/signup.js.coffee
@@ -84,10 +84,10 @@
       delete data.school_name unless @unknownSchool
       JSON.stringify(data)

-    showErrors: (errors) ->
+    showErrors: (errors, maxToShow=3) ->
       @clearErrors()
       @processTeacherErrors(errors) if @isTeacher()
-
+      errorCount = 0
       dispError = ($el, msg) =>
         $el.addClass 'error-field'
         $el.prepend "
#{msg}
"
@@ -103,7 +103,9 @@
           $f = @field(fieldName).eq(elIndex)
         else
           $f = @field(fieldName)
-        dispError $f.parent(), error
+        if (errorCount < maxToShow)
+          errorCount = errorCount + 1
+          dispError $f.parent(), error

       if errors['unknown-error']
         dispError @el('.unknown-error'), errors['unknown-error']


canceling existing clear_notice_and_alert setTimeout

--- app/assets/javascripts/stores.js.coffee
+++ app/assets/javascripts/stores.js.coffee
@@ -132,10 +132,12 @@
                     type: 'post'
                     success: (response) =>
                         if response.status isnt 'error'
-                            clear_alert_and_notice()
+                            if clear_alert_and_notice_timeout?
+                                clearTimeout(clear_alert_and_notice_timeout)
+                                clear_alert_and_notice()
                             update_notice("'#{response.product.name}' has been #{verb} successfully.")
                             update_alert(response.alert) if response.alert?
-                            setTimeout ->
+                            window.clear_alert_and_notice_timeout = setTimeout ->
                                 clear_alert_and_notice()
                             , 10000
                             reset_product_form()


Remove unused variable

Remove unused variable

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -89,7 +89,6 @@
 		@background.clip = true
 		@background.backgroundColor = "transparent"
 		@background.classList.add("DeviceBackground")
-		@_previousBackgroundColor = @background.backgroundColor

 		@hands    = new Layer name: "hands"
 		@handsImageLayer = new Layer parent: @hands, name: "handsImage"
@@ -431,7 +430,6 @@
 		set: (hideBezel) ->
 			return if not Utils.isFramerStudio()
 			@_hideBezel = hideBezel
-
 			@_update()

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


Fix custom device with local images

Fix custom device with local images

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -314,7 +314,7 @@
 			return name

 		# If this device is added by the user we use the name as it is
-		if @_deviceType not in BuiltInDevices
+		if @_deviceType not in BuiltInDevices or @_deviceType is "custom"
 			return name

 		# We want to get these image from our public resources server


Make paying for stripe work

Make paying for stripe work

--- js/inner.coffee
+++ js/inner.coffee
@@ -806,13 +806,11 @@
       type: if stripeInfo then "POST" else "PUT"
       data: JSON.stringify
         token: stripeInfo
-        orgs: @organizationMatrix()
-        team_plans: @userMatrix()
+        plan: @chosenPlan().id
+
       success: () =>
-        @payer VM.current_user().login
         @cardInfo(stripeInfo.card) if stripeInfo?
         @oldTotal(@total())
-
         SammyApp.setLocation "/account/plans"
     )
     false


Monkeypatch ko.observableArray until the pull request is accepted.

--- js/inner.coffee
+++ js/inner.coffee
@@ -28,6 +28,11 @@
   else
     notifyError xhr.responseText or xhr.statusText, null, null, true
 )
+
+ko.observableArray["fn"].setIndex = (index, newItem) ->
+  @valueWillMutate()
+  @()[index] = newItem
+  @valueHasMutated()

 $(document).ajaxSend((ev, xhr, options) ->


get rid of collection related behaviors till ready

we have not turned on any collection related stuff
in the UI so these did nothing but cause us to download
more code to the browser.

Change-Id: I03eee5cf762fac4e3c617504ad422e717034a525
Reviewed-on: https://gerrit.instructure.com/13938
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Sterling Cobb <7a57d889ea7f266299434459898a90c7f6a1ca4d@instructure.com>

--- app/coffeescripts/bundles/common.coffee
+++ app/coffeescripts/bundles/common.coffee
@@ -19,9 +19,10 @@
   'compiled/behaviors/ujsLinks'
   'compiled/behaviors/admin-links'
   'compiled/behaviors/elementToggler'
-  'compiled/behaviors/upvote-item'
-  'compiled/behaviors/repin-item'
-  'compiled/behaviors/follow'
+  # uncomment these to turn on collection pinning and voting
+  # 'compiled/behaviors/upvote-item'
+  # 'compiled/behaviors/repin-item'
+  # 'compiled/behaviors/follow'
   'compiled/behaviors/tooltip'

   # other stuff several bundles use


add skip list for fields to skip serialize

--- src/main.coffee
+++ src/main.coffee
@@ -93,9 +93,15 @@
             console.log "RmiService listening on #{@port}"
         )

-    _isPrivate : (name)->
+    _isPrivate : (name, skipList)->
         if name.indexOf(@_privatePrefix) is 0
             return true
+
+        if skipList?
+            for exclude in skipList
+                if name is exclude
+                    return true
+
         for exclude in @_excludeMethods
             if name is exclude
                 return true
@@ -198,7 +204,7 @@
             # serialize object
             objDesc = @__newRemoteObjectDesc(id, @host, @port)
             for k, v of obj
-                if @_isPrivate(k)
+                if @_isPrivate(k, obj.__r_skip)
                     continue
                 # to minimize size, local function will only take up an id field
                 if typeof v is 'function' and not v.__r_type?