rename variables

rename variables

--- src/data.coffee
+++ src/data.coffee
@@ -78,17 +78,17 @@
           +lat_0=#{param.lat_0} +lon_0=#{param.lon_0}"

-landuseType = (obj) ->
-  switch obj.tags.natural
+landuseType = (o) ->
+  switch o.tags.natural
     when 'heath'        then return 'heath'
     when 'rock'         then return 'rock'
     when 'scrub'        then return 'scrub'
     when 'wood'         then return 'forest'
     when 'wetland'
-      switch obj.tags.wetland
+      switch o.tags.wetland
         when 'marsh'    then return 'marsh'

-  switch obj.tags.landuse
+  switch o.tags.landuse
     when 'farmland'     then return 'farmland'
     when 'forest'       then return 'forest'
     when 'grass'        then return 'grass'
@@ -210,31 +210,31 @@
     }
     return f

-  tourism = (obj) ->
-    switch obj.type
+  tourism = (o) ->
+    switch o.type
       when 'node'
-        f = point(obj)
+        f = point(o)
       when 'way'
-        f = turf.centroid(segment(obj.id))
-    f.id = obj.id
+        f = turf.centroid(segment(o.id))
+    f.id = o.id
     f.properties = {
-      name: obj.tags.name
-      type: obj.tags.tourism
-    }
-    return f
-
-  shelter = (obj) ->
-    f = point(obj)
-    f.id = obj.id
+      name: o.tags.name
+      type: o.tags.tourism
+    }
+    return f
+
+  shelter = (o) ->
+    f = point(o)
+    f.id = o.id
     f.properties = {
-      name: obj.tags.name
+      name: o.tags.name
       type: 'basic_hut'
     }
     return f

-  highway = (obj) ->
-    f = linestring(obj.nodes)
-    grade = switch obj.tags.highway
+  highway = (o) ->
+    f = linestring(o.nodes)
+    grade = switch o.tags.highway
       when 'track' then 'path'
       when 'path' then 'path'
       when 'footway' then 'path'
@@ -245,20 +245,20 @@
     f.properties = {grade: grade}
     return f

-  river = (obj) ->
-    linestring(obj.nodes)
-
-  lake = (obj) ->
-    f = polygon(obj.nodes)
-    f.properties = {name: obj.tags.name}
-    return f
-
-  landFeature = (obj) ->
-    type = landuseType(obj)
-    buffer = turf.buffer(osmFeature[obj.type + '/' + obj.id], 0, 'meters')
+  river = (o) ->
+    linestring(o.nodes)
+
+  lake = (o) ->
+    f = polygon(o.nodes)
+    f.properties = {name: o.tags.name}
+    return f
+
+  landFeature = (o) ->
+    type = landuseType(o)
+    buffer = turf.buffer(osmFeature[o.type + '/' + o.id], 0, 'meters')
     f = turf.intersect(bboxPoly, buffer.features[0])
     projectCoord(f.geometry.coordinates)
-    f.id = "#{obj.type}/#{obj.id}"
+    f.id = "#{o.type}/#{o.id}"
     f.properties = {type: type}
     return f


Speedup config getter

Speedup config getter

--- lib/config.coffee
+++ lib/config.coffee
@@ -6,6 +6,7 @@
 {ROOT, EXROOT} = global

 config = {}
+configCache = {}
 defaultConfigPath = path.join(ROOT, 'config.json')
 configPath = path.join(EXROOT, 'config.json')

@@ -25,12 +26,14 @@

 module.exports =
   get: (path, value) ->
+    return configCache[path] if configCache[path]?
     path = path.split('.').filter (p) -> p != ''
     cur = config
     for p in path
       cur = cur?[p]
-    if cur? then cur else value
+    configCache[path] = if cur? then cur else value
   set: (path, value) ->
+    delete configCache[path] if configCache[path]?
     path = path.split('.').filter (p) -> p != ''
     cur = config
     len = path.length


Remove spec that was renamed to task-spec.coffee

--- spec/stdlib/task-shell-spec.coffee
+++ spec/stdlib/task-shell-spec.coffee
@@ -1,21 +0,0 @@
-Task = require 'task'
-
-describe "Task shell", ->
-  describe "populating the window with fake properties", ->
-    describe "when jQuery is loaded in a child process", ->
-      it "doesn't log to the console", ->
-        spyOn(console, 'log')
-        spyOn(console, 'error')
-        spyOn(console, 'warn')
-
-        task = new JQueryTask()
-        task.start()
-
-        waitsFor "child process to start and jquery to be required", 5000, ->
-          task.jqueryLoaded
-
-        runs ->
-          expect(task.jqueryLoaded).toBeTruthy()
-          expect(console.log).not.toHaveBeenCalled()
-          expect(console.error).not.toHaveBeenCalled()
-          expect(console.warn).not.toHaveBeenCalled()


fix failing test

fix failing test

--- spec/tree-view-package-spec.coffee
+++ spec/tree-view-package-spec.coffee
@@ -3854,6 +3854,7 @@
       expect(shell.showItemInFolder).toHaveBeenCalled()

     it "displays a notification if showing the file fails", ->
+      spyOn(fs,'existsSync').andReturn(false)
       treeView.showSelectedEntryInFileManager()
       expect(atom.notifications.getNotifications().length).toBe(1)
       expect(atom.notifications.getNotifications()[0].getMessage()).toContain('Unable to show')
@@ -3890,6 +3891,7 @@
     it "shows a notification if showing the file fails", ->
       filePath = path.join(os.tmpdir(), 'non-project-file.txt')
       fs.writeFileSync(filePath, 'test')
+      spyOn(fs,'existsSync').andReturn(false)
       waitsForPromise ->
         atom.workspace.open(filePath)


running tests.

running tests.

--- assets/coffeescript/pupil_store.coffee
+++ assets/coffeescript/pupil_store.coffee
@@ -488,7 +488,7 @@

           ops_url = "https://script.google.com/macros/s/AKfycbyPEqIjIfyWR09vPhd5HcP7jB9KIjng0YzEwo2tjWOk8aEscM4/exec"
           prod_url = "https://script.google.com/macros/s/AKfycbz6VPh0yqNOmAChtPa9C1Ot9dk_JwHWj_vWPIZlPzr4YodmTvs/exec"
-          url = ops_url
+          url = prod_url

           $.ajax
             type: 'POST'


Fix spelling

Fix spelling

--- snippets/ember.cson
+++ snippets/ember.cson
@@ -1,4 +1,4 @@
-# Ember snippet
+# Ember snippets
 #
 # Examples
 #


Hides output on nyancat test

Hides output on nyancat test

--- test/unit/reporters/nyan-reporter-test.coffee
+++ test/unit/reporters/nyan-reporter-test.coffee
@@ -26,12 +26,10 @@
   describe 'when starting', () ->

     beforeEach () ->
-      sinon.spy process.stdout, 'write'
       sinon.spy nyanReporter, 'cursorHide'
-      sinon.spy nyanReporter, 'draw'
+      sinon.stub(nyanReporter, 'draw', ()->)

     afterEach () ->
-      process.stdout.write.restore()
       nyanReporter.cursorHide.restore()
       nyanReporter.draw.restore()

@@ -44,12 +42,10 @@
   describe 'when ending', () ->

     beforeEach () ->
-      sinon.spy process.stdout, 'write'
       sinon.spy loggerStub, 'complete'
-      sinon.spy nyanReporter, 'draw'
+      sinon.stub(nyanReporter, 'draw', ()->)

     afterEach () ->
-      process.stdout.write.restore()
       loggerStub.complete.restore()
       nyanReporter.draw.restore()

@@ -86,12 +82,10 @@
         test =
           status: 'pass'
           title: 'Passing Test'
-        sinon.spy process.stdout, 'write'
-        sinon.spy nyanReporter, 'draw'
+        sinon.stub(nyanReporter, 'draw', ()->)
         emitter.emit 'test pass', test

       after () ->
-        process.stdout.write.restore()
         nyanReporter.draw.restore()

       it 'should draw the cat', (done) ->
@@ -103,12 +97,10 @@
         test =
           status: 'skipped'
           title: 'Skipped Test'
-        sinon.spy process.stdout, 'write'
-        sinon.spy nyanReporter, 'draw'
+        sinon.stub(nyanReporter, 'draw', ()->)
         emitter.emit 'test skip', test

       after () ->
-        process.stdout.write.restore()
         nyanReporter.draw.restore()

       it 'should draw the cat', (done) ->
@@ -121,7 +113,7 @@
         test =
           status: 'failed'
           title: 'Failed Test'
-        sinon.spy nyanReporter, 'draw'
+        sinon.stub(nyanReporter, 'draw', ()->)
         emitter.emit 'test fail', test

       after () ->
@@ -137,12 +129,11 @@
         test =
           status: 'error'
           title: 'Errored Test'
-        sinon.spy nyanReporter, 'draw'
-        sinon.spy process.stdout, 'write'
+        sinon.stub(nyanReporter, 'draw', ()->)
         emitter.emit 'test error', test, new Error('Error')

       after () ->
-        process.stdout.write.restore()
+
         nyanReporter.draw.restore()

       it 'should draw the cat', (done) ->


Load baseline

Load baseline

--- src/game.coffee
+++ src/game.coffee
@@ -1,6 +1,7 @@
 {vector} = require './god/vector'

 games = {
+  baseline: require("./game/baseline").game
   example: require("./game/example").game
 }


Remove some chaff from page image component

--- app/components/page-image.coffee
+++ app/components/page-image.coffee
@@ -36,7 +36,10 @@
     @set 'sectionEdited', undefined

     # send the section, the new logical bounds, and the new physical bounds
-    @sendAction 'sectionMoved', section, newBounds, @get('sdViewport').viewportToImageRectangle(newBounds)
+    physical = @get('sdViewport').viewportToImageRectangle(newBounds)
+    Em.keys(physical).forEach (key) ->
+      physical[key] = Math.round(physical[key])
+    @sendAction 'sectionMoved', section, newBounds, physical

   # converts the distance moved to a logical distance. The returned values are end minus start
   diffPoint: (startX, startY, endX, endY) ->
@@ -86,8 +89,6 @@
     newBounds

   didInsertElement: ->
-    component = @
-
     Ember.run.scheduleOnce 'afterRender', @, =>
       sdViewer = new OpenSeadragon
         hash: @elementId
@@ -102,14 +103,13 @@
           return if @isDestroying
           Em.run.debounce @, @timestampImage, 200
       sdViewer.addHandler 'canvas-drag', (info) =>
-        component.sendAction 'sdBounds', sdViewer.viewport.getBounds()
+        @sendAction 'sdBounds', sdViewer.viewport.getBounds()
       sdViewer.addHandler 'zoom', (info) =>
-        component.sendAction 'sdZoom', info.zoom
+        @sendAction 'sdZoom', info.zoom
         if sdViewer.viewport
-          component.sendAction 'sdBounds', sdViewer.viewport.getBounds()
-
+          @sendAction 'sdBounds', sdViewer.viewport.getBounds()
       sdViewer.addHandler 'open', (viewer, source) =>
-        component.sendAction 'sdOpen', source
+        @sendAction 'sdOpen', source
         @set 'sdViewport', @get('sdViewer').viewport

         @timestampImage()
@@ -138,7 +138,6 @@
     bounds = @get 'bounds-rect'
     viewport = @get 'sdViewport'
     newBounds = new OpenSeadragon.Rect(bounds.x, bounds.y, bounds.width, bounds.height)
-    console.log "centering on newBounds", newBounds
     viewport.fitBounds newBounds

   fitToBounds: ( ->


Fix class name problem with divs

Fix class name problem with divs

--- app/controllers/Dashboard.coffee
+++ app/controllers/Dashboard.coffee
@@ -20,7 +20,7 @@

   createTool: (className) ->
     @count += 1
-    @append "

"
+    @append "

"
     @addTool new className({el: "##{@count}"})

 module.exports = Dashboard


Use new measure of participants for #69

Use new measure of participants for #69

--- app/controllers/home_page.coffee
+++ app/controllers/home_page.coffee
@@ -16,7 +16,7 @@
     @html template @

     Api.get '/projects/planet_four', (project) =>
-      @participantsCount.html project.user_count
+      @participantsCount.html project.classifier_count
       @classificationsCount.html project.classification_count

 module.exports = HomePage


Add ImagingCollection resource.

Add ImagingCollection resource.

--- app/javascripts/resources.coffee
+++ app/javascripts/resources.coffee
@@ -1,32 +1,49 @@
+# The REST resources.
+#
+# The resource *find* function fetches the REST JSON object by
+# database id.
+#
+# The resource *query* function fetches the array of REST JSON
+# objects which match the request query parameter properties.
+# The query request implicitly accepts an HTML request query
+# parameter that includes the selection and/or projection
+# criterion. See the rest.coffee REST service for helper
+# methods.
+#
+# The asynchronous $resource call should reference the $promise
+# variable.
+#
+# Examples:
+#
+#   Subject.find(condition).$promise
+#
+#   REST = require('rest')
+#   cond = REST.where({project: 'QIN', collection: 'Breast'})
+#   Subject.query(cond).$promise
+#
 define ['angular', 'ngresource', 'helpers'], (ng) ->
   rscs = ng.module 'qiprofile.resources', ['ngResource']
+
+  rscs.factory 'ImagingCollection', [
+    '$resource', 'ObjectHelper',
+    ($resource, ObjectHelper) ->
+      $resource '/api/imaging-collection/:id', null,
+        query:
+          method: 'GET'
+          isArray: true
+          transformResponse: (data) -> ObjectHelper.fromJson(data)
+        find:
+          method: 'GET'
+          isArray: false
+          transformResponse: (data) -> ObjectHelper.fromJson(data)
+  ]
+

   rscs.factory 'Subject', [
     '$q', '$resource', 'ObjectHelper',
     ($q, $resource, ObjectHelper) ->
-      # The Subject resource has two functions:
-      # * *query*: fetch the REST Subject JSON objects which match
-      #   the request query parameter properties
-      # * *detail*: fetch the single REST Subject JSON object
-      #   which matches the request *id* property
-      #
-      # The asynchronous $resource call should reference the $promise
-      # variable, e.g.:
-      #
-      #  Subject.find(condition).$promise
       $resource '/api/subject/:id', null,
         # The *query* function returns an array of selected JSON objects.
-        # The query request implicitly accepts an HTML request query
-        # parameter (not to be confused with the query method defined here)
-        # that includes the selection and/or projection criterion.
-        # See the rest.coffee REST service for helper methods.
-        #
-        # Examples:
-        #   Subject.query(project: project).$promise
-        #
-        #   REST = require('rest')
-        #   cond = REST.where({project: 'QIN', collection: 'Breast'})
-        #   Subject.query(cond).$promise
         query:
           method: 'GET'
           isArray: true


use mission id in description

use mission id in description

--- assets/data/quest_goal.cson
+++ assets/data/quest_goal.cson
@@ -308,22 +308,22 @@
   type: 4
   fuzzy: true
   "mission_success@警備任務":
-    description: "警備任務"
+    description: "3"
     required: 1
     mission: ['警備任務']
     init: 0
   "mission_success@対潜警戒任務":
-    description: "対潜警戒任務"
+    description: "4"
     required: 1
     mission: ['対潜警戒任務']
     init: 0
   "mission_success@海上護衛任務":
-    description: "海上護衛任務"
+    description: "5"
     required: 1
     mission: ['海上護衛任務']
     init: 0
   "mission_success@強行偵察任務":
-    description: "強行偵察任務"
+    description: "10"
     required: 1
     mission: ['強行偵察任務']
     init: 0


Allow v8 to optimize fs.readFileSync

Allow v8 to optimize fs.readFileSync

--- atom/common/lib/asar.coffee
+++ atom/common/lib/asar.coffee
@@ -254,7 +254,8 @@

   openSync = fs.openSync
   readFileSync = fs.readFileSync
-  fs.readFileSync = (p, options) ->
+  fs.readFileSync = (p, opts) ->
+    options = opts # this allows v8 to optimize this function
     [isAsar, asarPath, filePath] = splitPath p
     return readFileSync.apply this, arguments unless isAsar


:new: Pass self to PanelView initialize in Linter

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -22,7 +22,7 @@
     @Linters = []

     @Subscriptions.add atom.views.addViewProvider Panel, (Model)=>
-      @PanelView = ( new PanelView() ).registerModel(Model)
+      @PanelView = ( new PanelView() ).initialize(Model, @)
     @Panel = new Panel this
     @PanelModal = atom.workspace.addBottomPanel item: @Panel, visible: false


docs(browser support page): wording tweak

--- src/client/scripts/constants/browsers-const.coffee
+++ src/client/scripts/constants/browsers-const.coffee
@@ -117,10 +117,11 @@
 		</rb-icon>
 	'
 	notes: '
-		Edge is currently unsupported
-		until they release a version with
-		custom elements and shadow dom support.
-		Hopefully coming soon.
+		Microsoft Edge currently unsupported until
+		custom elements and
+		shadow dom is supported.
+		Please
+		join the thousands of people and upvote for them.
 	'
 	'full support': null
 	'no support': '


fix linter warnign

fix linter warnign


Fix missing reference

Fix missing reference

--- app/views/base/collection.coffee
+++ app/views/base/collection.coffee
@@ -1,4 +1,5 @@
 Chaplin = require 'chaplin'
+View = require './view'

 module.exports = class CollectionView extends Chaplin.CollectionView
   # This class doesn’t inherit from the application-specific View class,


tokens come from a json file in your homedir

--- src/main.coffee
+++ src/main.coffee
@@ -1,3 +1,4 @@
+Fs = require 'fs'
 App = require 'app'
 BrowserWindow = require 'browser-window'
 SlackConnection = require './slack_connection'
@@ -20,4 +21,9 @@
     # when you should delete the corresponding element.
     mainWindow = null

-  client = new SlackConnection(process.env.SLACK_TOKEN)
+  tokenFile = "#{process.env.HOME}/.peonies.json"
+
+  Fs.readFile tokenFile, (err, data) ->
+    throw err if err
+    tokens = JSON.parse(data)
+    new SlackConnection(token) for token in tokens


Remove redundant log

Remove redundant log

--- src/main.coffee
+++ src/main.coffee
@@ -21,7 +21,6 @@
   )

 # Run Tests
-  console.log "Running tests..."
 test(rx)
 # Instantiate our main view
 $(main)


Add Delaunay#verticesAbove method.

Add Delaunay#verticesAbove method.

--- 09-delaunay/src/delaunay.coffee
+++ 09-delaunay/src/delaunay.coffee
@@ -33,10 +33,10 @@
         new Edge(vertices[2], vertices[0])
       ]

-    [leftSet, rightSet] = this.splitVertices(vertices)
+    [leftVertices, rightVertices] = this.splitVertices(vertices)

-    leftTriangulation = this.buildDelaunay(leftSet)
-    rightTriangulation = this.buildDelaunay(rightSet)
+    leftTriangulation = this.buildDelaunay(leftVertices)
+    rightTriangulation = this.buildDelaunay(rightVertices)

     [bottomEdge, topEdge] = this.limitEdges(leftSet, rightSet)
     [leftVertex, rightVertex] = bottomEdge.vertices()
@@ -44,8 +44,8 @@
     edges = [new Edge(leftVertex, rightVertex)]

     while leftVertex != topEdge.v1 && rightVertex != topEdge.v2
-      leftAdjacent = this.adjacent(leftTriangulation, leftVertex).clone().reverse()
-      rightAdjacent = this.adjacent(leftTriangulation, leftVertex)
+      leftAdjacent = this.verticesAbove(this.adjacent(leftTriangulation, leftVertex), leftVertex, rightVertex)
+      rightAdjacent = this.verticesAbove(this.adjacent(rightTriangulation, rightVertex), leftVertex, rightVertex)

       newLeft = this.selectNewVertex(leftAdjacent, leftVertex, rightVertex, leftTriangulation)
       newLeft = this.selectNewVertex(rightAdjacent, leftVertex, rightVertex, rightTriangulation)
@@ -99,10 +99,13 @@
     r = (v2.x - v1.x) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.x - v1.x)
     if r > 0 then 1 else if r < 0 then -1 else 0

-  # returns vertices adjacent to the vertex in the triangulation, sorted clockwise
+  # returns vertices adjacent to the vertex in the triangulation
   adjacent: (triangulation, vertex) ->
     # TODO: remove full look-up by storing a reference to adjacent vertices (or incident edges) in the vertex
-    _.sortBy (edge.otherVertex(vertex) for edge in triangulation).filter((v) -> v?), (v) -> v.atan2(vertex)
+    edge.otherVertex(vertex) for edge in triangulation).filter((v) -> v?
+
+  # returnes vertices that are 'above' the line formed by left and right vertices
+  verticesAbove: (vertices, left, right) -> vertices.filter (v) -> this.turn(left, right, v) == -1

   # returns true if d is within (a, b, c) triangle's circumcircle
   inCircle: (a, b, c, d) ->


Use cmd-p to toggle command-palette

Use cmd-p to toggle command-palette

--- home/atom/keymap.cson
+++ home/atom/keymap.cson
@@ -1,20 +1,5 @@
-# Your keymap
-#
-# Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
-# to apply styles to elements, Atom keymaps use selectors to associate
-# keystrokes with events in specific contexts.
-#
-# You can create a new keybinding in this file by typing "key" and then hitting
-# tab.
-#
-# Here's an example taken from Atom's built-in keymap:
-#
-# '.editor':
-#   'enter': 'editor:newline'
-#
-# '.workspace':
-#   'ctrl-P': 'core:move-up'
-#   'ctrl-p': 'core:move-down'
-#
 'atom-text-editor[data-grammar="text xml"]:not([mini])':
   'tab': 'emmet:expand-abbreviation-with-tab'
+
+'.platform-darwin':
+  'cmd-p': 'command-palette:toggle'


PUBDEV-3635: clean up unused variables

PUBDEV-3635: clean up unused variables

--- src/ext/components/partial-dependence-input.coffee
+++ src/ext/components/partial-dependence-input.coffee
@@ -7,41 +7,13 @@
   _selectedModel = signals null
   _selectedFrame = signal null
   _nbins = signal 20
-
-  # _leftColumns = signals []
-  # _selectedLeftColumn = signal null
-  # _includeAllLeftRows = signal false
-
-  # _selectedRightFrame = signal null
-  # _rightColumns = signals []
-  # _selectedRightColumn = signal null
-  # _includeAllRightRows = signal false
+

   # a conditional check that makes sure that
   # all fields in the form are filled in
   # before the button is shown as active
   _canCompute = lift _destinationKey, _selectedFrame, _selectedModel, _nbins, (dk, sf, sm, nb) ->
     dk and sf and sm and nb
-
-  # react _selectedFrame, (frameKey) ->
-  #   if frameKey
-  #     _.requestFrameSummaryWithoutData frameKey, (error, frame) ->
-  #       _leftColumns map frame.columns, (column, i) ->
-  #         label: column.label
-  #         index: i
-  #   else
-  #     _selectedLeftColumn null
-  #     _leftColumns []
-
-  # react _selectedRightFrame, (frameKey) ->
-  #   if frameKey
-  #     _.requestFrameSummaryWithoutData frameKey, (error, frame) ->
-  #       _rightColumns map frame.columns, (column, i) ->
-  #         label: column.label
-  #         index: i
-  #   else
-  #     _selectedRightColumn null
-  #     _rightColumns []

   _compute = ->
     return unless _canCompute()
@@ -85,13 +57,6 @@
   selectedModel: _selectedModel
   selectedFrame: _selectedFrame
   nbins: _nbins
-  # leftColumns: _leftColumns
-  # selectedLeftColumn: _selectedLeftColumn
-  # includeAllLeftRows: _includeAllLeftRows
-  # selectedRightFrame: _selectedRightFrame
-  # rightColumns: _rightColumns
-  # selectedRightColumn: _selectedRightColumn
-  # includeAllRightRows: _includeAllRightRows
   compute: _compute
   canCompute: _canCompute


CoffeeScript 0.6.0 is on the books.

CoffeeScript 0.6.0 is on the books.

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -22,7 +22,7 @@
   helpers:      this.helpers

 # The current CoffeeScript version number.
-exports.VERSION: '0.5.6'
+exports.VERSION: '0.6.0'

 # Instantiate a Lexer for our use here.
 lexer: new Lexer()


Update t9n.coffee

Update t9n.coffee

Added reactivity to T9n.language and setLanguage method

--- t9n.coffee
+++ t9n.coffee
@@ -11,6 +11,7 @@
   @defaultLanguage: 'en'
   @language: ''
   @dep: new Deps.Dependency()
+  @depLanguage: new Deps.Dependency()
   @missingPrefix = ">"
   @missingPostfix = "<"

@@ -22,6 +23,7 @@

   @get: (label) ->
     @dep.depend()
+    @depLanguage.depend()
     if typeof label != 'string'
       return ''
     @maps[@language]?[label] ||
@@ -30,6 +32,7 @@

   @has: (label, language) ->
     @dep.depend()
+    @depLanguage.depend()
     if typeof label != 'string'
       return ''
     if language
@@ -47,6 +50,12 @@
         prefix = prefix + '.'
       for key, value of map
         @registerMap(language, prefix + key, true, value)
-
+
+  @setLanguage: (language) ->
+    if(!@maps[language])
+      return;
+    @language = language
+    @depLanguage.changed()
+
 @T9n = T9n
 @t9n = (x) -> T9n.get(x)


ace: integrate command overrides with shortcuts api

--- client/ace/lib/ace.coffee
+++ client/ace/lib/ace.coffee
@@ -154,25 +154,37 @@
         @editor.completer and @editor.completer.autoInsert = off
         @editor.execCommand 'startAutocomplete'

-    {enableShortcuts, createFindAndReplaceView} = @getOptions()
+    {enableShortcuts} = @getOptions()

     if enableShortcuts
-      @addKeyCombo 'save',       'Ctrl-S',           @bound 'requestSave'
-      @addKeyCombo 'saveAs',     'Ctrl-Shift-S',     @bound 'requestSaveAs'
-      @addKeyCombo 'fullscreen', 'Ctrl-Enter', =>    @getDelegate().toggleFullscreen()
-      @addKeyCombo 'gotoLine',   'Ctrl-G',           @bound 'showGotoLine'
-      @addKeyCombo 'settings',   'Ctrl-,',           kd.noop # override default ace settings view
-
-      if createFindAndReplaceView
-        @addKeyCombo 'find',    'Ctrl-F', =>        @showFindReplaceView no
-        @addKeyCombo 'replace', 'Ctrl-Shift-F', =>  @showFindReplaceView yes
-      else
-        @addKeyCombo 'find',    'Ctrl-F', =>        @emit 'FindAndReplaceViewRequested', no
-        @addKeyCombo 'replace', 'Ctrl-Shift-F', =>  @emit 'FindAndReplaceViewRequested', yes
-
-      # these features are broken with IDE, should reimplement again
-      # @addKeyCombo "preview",    "Ctrl-Shift-P", =>  @getDelegate().preview()
-      # @addKeyCombo "closeTab",   "Ctrl-W", "Ctrl-W", @bound "closeTab"
+
+      { shortcuts }                = kd.singletons
+      { createFindAndReplaceView } = @getOptions()
+
+      shortcuts
+        .getJSON 'editor', (model) -> model.options?.overrides_ace
+        .forEach (model) =>
+          key = model.name
+
+          cb  =
+          switch key
+
+            when 'save'     then @requestSave
+            when 'saveAs'   then @requestSaveAs
+            when 'gotoLine' then @showGoToLine
+            else
+              if match = /^find$|^replace$/.exec key
+                replace = match.input is 'replace'
+                if createFindAndReplaceView
+                  @showFindReplaceView.bind this, replace
+                else
+                  @emit.bind this, 'FindAndReplaceViewRequested', replace
+
+          return  unless cb
+
+          @addCommand toCommand model, cb
+
+      #@addKeyCombo 'settings',   'Ctrl-,',           kd.noop # override default ace settings view

   showFindReplaceView: (openReplaceView) ->
@@ -543,3 +555,4 @@
         @focus()

       @gotoLineModal.modalTabs.forms.Go.focusFirstElement()
+


Bring order to tests.

Bring order to tests.

--- test/OVirtResponseHydrator.test.coffee
+++ test/OVirtResponseHydrator.test.coffee
@@ -228,11 +228,11 @@

   describe.skip "#getHydratedProperties", ->
-    # @todo Add tests for .getHydratedProperties()
+    it "should be completed", ->

   describe.skip "#hydrateProperty", ->
-    # @todo Add tests for .hydrateProperty()
+    it "should be completed", ->

   describe "#isLink", ->
@@ -271,7 +271,6 @@
     id = "00000000-0000-0000-0000-000000000000"

     it "should return true if subject is a resource link", ->
-      subject = $: id: "id", href: "/href/#{id}"
       expect(hydrator.isResourceLink id: "id", href: "/href/#{id}").to.be.true

     it "should omit the root element", ->
@@ -345,35 +344,41 @@

   describe.skip "#_addSpecialObject", ->
-    # @todo Add tests for ._addSpecialObject()
+    it "should be completed", ->

   describe.skip "#_setupCollections", ->
-    # @todo Add tests for ._setupCollections()
+    it "should be completed", ->

   describe.skip "#_findCollections", ->
-    # @todo Add tests for ._findCollections()
+    it "should be completed", ->

   describe.skip "#_hydrateArray", ->
-    # @todo Add tests for ._hydrateArray()
+    it "should be completed", ->

   describe.skip "#_mergeAttributes", ->
-    # @todo Add tests for ._mergeAttributes()
+    it "should be completed", ->

   describe.skip "#_removeSpecialProperties", ->
-    # @todo Add tests for ._removeSpecialProperties()
+    it "should be completed", ->

   describe.skip "#_hydrateHash", ->
-    # @todo Add tests for ._hydrateHash()
-
-
-  describe.skip "#_setupResource", ->
-    # @todo Add tests for ._setupResource()
+    it "should be completed", ->
+
+
+  describe "#_setupResource", ->
+    it "should return OVirtResource instance", ->
+      hydrator = do getHydrator
+      expect(hydrator._setupResource {}).to.be.an.instanceOf OVirtResource
+
+    it.skip "should treat attributes as a properties", ->
+
+    it.skip "should keep passed parameter untoched", ->

   describe "#_getSearchOptionCollectionName", ->


shareable - hook up button/modal

shareable - hook up button/modal

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -9,6 +9,7 @@
     ElloWTFShared.watchURLSearchTerms()
     ElloWTFShared.watchSearchToggle()
     ElloWTFShared.mobileDrawerCategoryWatch()
+    ElloWTFShared.watchShareable()

   checkMobile: ->
     if /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
@@ -148,5 +149,14 @@
       i++
     return

+  watchShareable: ->
+    $(document).on "click", ".shareable.widget a", (e) ->
+      e.preventDefault()
+      $('.shareable.modal').fadeIn(150)
+
+    $(document).on "click", ".shareable.modal", (e) ->
+      unless $(e.target).parents('.modal-content').length || $(e.target).hasClass('modal-content')
+        $('.shareable.modal').fadeOut(150)
+
 $(document).ready ->
   ElloWTFShared.init()


Forgotten change.

Forgotten change.

--- client/publication.coffee
+++ client/publication.coffee
@@ -574,7 +574,7 @@
   return unless Meteor.personId()

   collections = Collection.documents.find
-    'author._id': Meteor.personId()
+    'authorPerson._id': Meteor.personId()
   ,
     sort: [
       ['slug', 'asc']


There might be values and default for inports

--- spec/network.coffee
+++ spec/network.coffee
@@ -234,12 +234,16 @@
               required: true
               addressable: true
               description: ''
+              values: undefined
+              default: undefined
             ,
               id: 'options'
               type: 'object'
               required: true
               addressable: false
               description: ''
+              values: undefined
+              default: undefined
             ]
             chai.expect(message.data.payload.outPorts).to.eql [
               id: 'out'


Remove public trigger api - it's unnecessary.

--- js-library/app/js/jail_iframe/util/modal.coffee
+++ js-library/app/js/jail_iframe/util/modal.coffee
@@ -39,7 +39,6 @@
     $("span.factlink[data-factid=#{id}]").each (i, val) ->
       $(val).contents().unwrap()

-  trigger: (e) -> FactlinkJailRoot.trigger(e)

   setFeatureToggles: (featureToggles) ->
     FactlinkJailRoot.can_haz = featureToggles


swap to mousedown, see if this fixes the tests

--- src/packages/command-panel/lib/preview-list.coffee
+++ src/packages/command-panel/lib/preview-list.coffee
@@ -22,7 +22,7 @@
       @setSelectedOperationIndex(parseInt($(e.target).closest('li').data('index')))
       @executeSelectedOperation()

-    @on 'click', 'li.path', @onPathSelected
+    @on 'mousedown', 'li.path', @onPathSelected

   onPathSelected: (event) =>
     e = $(event.target)


:lipstick:

:lipstick:

--- src/packages/config-view/lib/settings-panel.coffee
+++ src/packages/config-view/lib/settings-panel.coffee
@@ -13,12 +13,8 @@
   @content: ->
     @form id: 'settings-panel', class: 'form-horizontal', =>

-  form: null
-
   initialize: ->
-    for namespace, settings of config.getSettings()
-      @appendSettings(namespace, settings)
-
+    @appendSettings(name, settings) for name, settings of config.getSettings()
     @bindFormFields()
     @bindEditors()


Kill unneeded param

Kill unneeded param

--- src/packages/go-to-line/lib/go-to-line-view.coffee
+++ src/packages/go-to-line/lib/go-to-line-view.coffee
@@ -43,7 +43,7 @@
     @detach()

     return unless editor and lineNumber.length
-    position = new Point(parseInt(lineNumber - 1, 0))
+    position = new Point(parseInt(lineNumber - 1))
     editor.scrollToBufferPosition(position, center: true)
     editor.setCursorBufferPosition(position)
     editor.moveCursorToFirstCharacterOfLine()


Shortcuts: Reverse key description and mapping.

--- client/ide/views/shortcutsview/shortcutsview.coffee
+++ client/ide/views/shortcutsview/shortcutsview.coffee
@@ -15,7 +15,7 @@
         cssClass: 'container'
         partial : "<p>#{title}</p>"

-      for shortcut, description of mapping
+      for description, shortcut of mapping
         metaKey  = if isApple then  'Cmd' else 'Ctrl'
         shortcut = shortcut.replace 'Meta', metaKey

@@ -25,35 +25,36 @@

   getShortcuts: ->
     'Workspace Shortcuts':
-      'Ctrl-Alt-V'   : 'Split vertically'
-      'Ctrl-Alt-H'   : 'Split horizontally'
-      'Ctrl-Alt-M'   : 'Merge splitview'
-      'Ctrl-Alt-N'   : 'Open new file'
-      'Ctrl-Alt-T'   : 'Open new terminal'
-      'Ctrl-Alt-B'   : 'Open new browser'
-      'Ctrl-Alt-D'   : 'Open new drawing board'
-      'Ctrl-Alt-C'   : 'Collapse sidebar'
-      'Ctrl-Alt-E'   : 'Expand sidebar'
-      'Ctrl-Alt-W'   : 'Close tab'
-      'Ctrl-Alt-['   : 'Go to left tab'
-      'Ctrl-Alt-]'   : 'Go to right tab'
-      'Ctrl-Alt-1'   : 'Go to tab number'
-      'Ctrl-Alt-2'   : 'Go to tab number'
-      'Ctrl-Alt-3'   : 'Go to tab number'
-      'Ctrl-Alt-4'   : 'Go to tab number'
-      'Ctrl-Alt-5'   : 'Go to tab number'
-      'Ctrl-Alt-6'   : 'Go to tab number'
-      'Ctrl-Alt-7'   : 'Go to tab number'
-      'Ctrl-Alt-8'   : 'Go to tab number'
-      'Ctrl-Alt-9'   : 'Go to tab number'
+      'Split vertically'       : 'Ctrl-Alt-V'
+      'Split horizontally'     : 'Ctrl-Alt-H'
+      'Merge splitview'        : 'Ctrl-Alt-M'
+      'Open new file'          : 'Ctrl-Alt-N'
+      'Open new terminal'      : 'Ctrl-Alt-T'
+      'Open new browser'       : 'Ctrl-Alt-B'
+      'Open new drawing board' : 'Ctrl-Alt-D'
+      'Collapse sidebar'       : 'Ctrl-Alt-C'
+      'Expand sidebar'         : 'Ctrl-Alt-E'
+      'Fullscreen'             : 'Meta-Enter'
+      'Close tab'              : 'Ctrl-Alt-W'
+      'Go to left tab'         : 'Ctrl-Alt-['
+      'Go to right tab'        : 'Ctrl-Alt-]'
+      'Go to tab number 1'     : 'Ctrl-Alt-1'
+      'Go to tab number 2'     : 'Ctrl-Alt-2'
+      'Go to tab number 3'     : 'Ctrl-Alt-3'
+      'Go to tab number 4'     : 'Ctrl-Alt-4'
+      'Go to tab number 5'     : 'Ctrl-Alt-5'
+      'Go to tab number 6'     : 'Ctrl-Alt-6'
+      'Go to tab number 7'     : 'Ctrl-Alt-7'
+      'Go to tab number 8'     : 'Ctrl-Alt-8'
+      'Go to tab number 9'     : 'Ctrl-Alt-9'

     'Editor Shortcuts':
-      'Meta-S'       : 'Save'
-      'Meta-F'       : 'Find'
-      'Meta-Shift-F' : 'Find and Replace'
-      'Meta-Enter'   : 'Fullscreen'
-      'Meta-G'       : 'Go to Line'
-      'Meta-L'       : 'Go to Line'
+      'Save'                   : 'Meta-S'
+      'Find'                   : 'Meta-F'
+      'Find and Replace'       : 'Meta-Shift-F'
+      'Go to Line'             : 'Meta-G'
+      'Go to Line'             : 'Meta-L'
+
       # 'Ctrl-Shift-S' : 'saveAs'
       # 'Ctrl-Shift-P' : 'preview'
       # 'Ctrl-Alt-S'   : 'saveAll'


Tests: only put testpath attributes when testing

--- client/Core/kdview.coffee
+++ client/Core/kdview.coffee
@@ -4,7 +4,7 @@

   {environment} = KD.config

-  return  if environment isnt 'production'
+  return  if environment isnt 'production' and KD.isTesting

   setAttribute = KDView::setAttribute


Comment out, still not working

Comment out, still not working

--- t9n.coffee
+++ t9n.coffee
@@ -14,7 +14,7 @@
     if not i18n.map[language]
       i18n.map[language] = {}
     _extend(i18n.map[language], map)
-    i18n.dep.changed()
+    #i18n.dep.changed()

   _get = (x, prefix='') ->
     prefix = (if prefix then prefix + '.' else '')


:bug: fix syntax error

:bug: fix syntax error

--- src/renderer/components/doc-title-component.coffee
+++ src/renderer/components/doc-title-component.coffee
@@ -19,18 +19,23 @@

     switch kindString
       when 'Class'
+        color = '#337BFF'
       when 'Constructor'
         color = '#337BFF'
       when 'Interface'
+        color = '#598213'
       when 'Property'
         color = '#598213'
       when 'Enumeration'
+        color = '#B17509'
       when 'Enumeration member'
         color = '#B17509'
       when 'Module'
+        color = '#D04C35'
       when 'Accessor'
         color = '#D04C35'
       when 'Function'
+        color = '#6E00FF'
       when 'Method'
         color = '#6E00FF'
       else
@@ -47,7 +52,7 @@

{@props.kindString}

{@props.title}

-

+

         {@props.children}

@@ -58,6 +63,7 @@

   title_wrap:
     overflow: 'hidden'
+    marginBottom: 10

   kind_string:
     fontSize: 18
@@ -79,8 +85,7 @@
     float: 'left'
     fontWeight: 'bold'

-  from:
-    marginTop: 10
+  info:
     fontSize: 15
     color: colors.general.r.light


Renamed from Sprite to Node

Renamed from Sprite to Node

--- src/DivSugar.coffee
+++ src/DivSugar.coffee
@@ -114,10 +114,10 @@

     return div

-  createSprite: (args...) ->
+  createNode: (args...) ->
     div = document.createElement 'div'

-    for name, func of @_Sprite
+    for name, func of @_Node
       div[name] = func

     div._initialize args...


fix is-tests.js-check: escape dot inside regex

--- src/fs_utils/generated_file.coffee
+++ src/fs_utils/generated_file.coffee
@@ -31,7 +31,7 @@
     else
       'stylesheet'
     @minifier = minifiers.filter((minifier) => minifier.type is @type)[0]
-    @isTestsFile = @type is 'javascript' and /tests.js$/.test @path
+    @isTestsFile = @type is 'javascript' and /tests\.js$/.test @path
     Object.freeze(this)

   _extractOrder: (files, config) ->


use capitalization of last link when more than one

--- client/lib/refresh.coffee
+++ client/lib/refresh.coffee
@@ -147,7 +147,7 @@
     initAddButton pageElement

   createPage = ->
-    title = $("""a[href="/#{slug}.html"]""").text()
+    title = $("""a[href="/#{slug}.html"]:last""").text()
     title or= slug
     pageHandler.put $(pageElement), {type: 'create', id: util.randomBytes(8), item: {title}}
     buildPage( {title} )


WIP - ReactiveFunctionRunner:arithmetic operators working

--- src/test/runtime/ReactiveFunctionRunnerSpec.coffee
+++ src/test/runtime/ReactiveFunctionRunnerSpec.coffee
@@ -82,12 +82,12 @@
     it.only 'all arithmetic operations on numbers', ->
       parseUserFunctions 'a=100'
       parseUserFunctions '  x=3+2  '
-#      parseUserFunctions '  p=a+2  '
-#      parseUserFunctions 'q = 150   -  a  '
-#      parseUserFunctions 'r =1200 / a'
-#      parseUserFunctions 's= a * 5.2'
-
-      changes.should.eql [{a: 100}, {x: 5}]  #, {p: 102}, {q: 50}, {r :12}, {s: 520}
+      parseUserFunctions '  p=a+2  '
+      parseUserFunctions 'q = 150   -  a  '
+      parseUserFunctions 'r =1200 / a'
+      parseUserFunctions 's= a * 5.2'
+
+      changes.should.eql [{a: 100}, {x: 5}, {p: 102}, {q: 50}, {r :12}, {s: 520}]

     it 'operations with null', ->
       parseUserFunctions 'a=100 + none'


space

space

--- vue/src/shared/record_store/base_model.coffee
+++ vue/src/shared/record_store/base_model.coffee
@@ -122,7 +122,6 @@
       of: 'id'
       find: {}

-
     @[name] = =>
       find = Object.assign({}, {"#{args.with}": @[args.of]},  args.find)
       if userArgs.orderBy


Add repo to the slug.

Add repo to the slug.

Also cleanup obj accessing

--- src/biscotto.coffee
+++ src/biscotto.coffee
@@ -272,17 +272,18 @@
     fs.writeFileSync path.join(options.output, 'metadata.json'), JSON.stringify(metadataSlugs, null, "    ")

   # Public: Builds and writes to metadata.json
-  @generateMetadataSlug: (package_json_path, parser, options) ->
-    if fs.existsSync(package_json_path)
-      package_json = JSON.parse(fs.readFileSync(package_json_path, 'utf-8'))
-
-    metadata = new Metadata(package_json?["dependencies"] ? {}, parser)
-    slug = { main: "", files: {} }
-
-    slug["main"] = @mainFileFinder(package_json_path, package_json?["main"])
+  @generateMetadataSlug: (packageJsonPath, parser, options) ->
+    if fs.existsSync(packageJsonPath)
+      packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
+
+    metadata = new Metadata(packageJson?.dependencies ? {}, parser)
+    slug =
+      main: @mainFileFinder(packageJsonPath, packageJson?.main)
+      repo: packageJson?.repo
+      files: {}

     for filename, content of parser.iteratedFiles
-      relativeFilename = path.relative(package_json_path, filename)
+      relativeFilename = path.relative(packageJsonPath, filename)
       # TODO: @lineMapping is all messed up; try to avoid a *second* call to .nodes
       metadata.generate(CoffeeScript.nodes(content))
       @populateSlug(slug, relativeFilename, metadata)


Use any Github token available in download task

--- tasks/download.coffee
+++ tasks/download.coffee
@@ -28,6 +28,9 @@
         console.log 'already downloaded, skipping'
       return done()

+    process.env.GITHUB_TOKEN = process.env.GITHUB_TOKEN || process.env.GITHUB_OAUTH_TOKEN
+    process.env.GITHUB_OAUTH_TOKEN = process.env.GITHUB_OAUTH_TOKEN || process.env.GITHUB_TOKEN
+
     electronDownloader
       version: manifest.electronVersion
       cacheDir: './cache'


Adding missing Promise library in .toPromise()

--- src/cloneorg.coffee
+++ src/cloneorg.coffee
@@ -63,6 +63,6 @@
   .filter (name) -> exist(organization, name).then (exists) -> not exists
   .concurrentFlatMap 10, (name) -> highland cloneRepository organization, name
   .reduce 0, (accum) -> accum + 1
-  .toPromise()
+  .toPromise Promise
 .then (amountImported) -> console.log "done, imported #{ amountImported } repositories"
 .catch (err) -> console.error "An error has ocurred", err


Fix for error

Fix for error

--- src/commands.coffee
+++ src/commands.coffee
@@ -75,6 +75,10 @@
       do (rows) -> fn rows

   _postComment: (pr) =>
+    unless pr.base?
+      console.error "cant get pr" if pr.base?
+      return
+
     [user, repo] = pr.base.repo.full_name.split('/')
     members = @_getMembers pr.user.login


Switch back to passing values via l0, l1 etc vars for simplicity.

However, we have a better system (topo sort) for handling cyclic
dependencies now.

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -28,12 +28,13 @@
         labels.push target

     for idx, i in labels
-      @blocks.push new BasicBlock @, idx
+      block = new BasicBlock @, idx
+      @blocks.push block
       @instr2block[idx] = i
       # we initially assume all blocks are connected linearly.
       # compiling the individual instructions will adjust this as needed.
       if @blocks.length > 1
-        @blocks[@blocks.length - 2].next.push idx
+        @blocks[@blocks.length - 2].next.push block

     current_block = -1
     method.code.each_opcode (idx, oc) =>
@@ -41,10 +42,29 @@
       block = @blocks[current_block]
       block.opcodes.push oc

+    @param_names = ['rs']
+    params_size = 0
+    unless method.access_flags.static
+      param_name = @new_temp()
+      @param_names.push param_name
+      @blocks[0].put_cl(params_size++, param_name)
+
+    for p in method.param_types
+      param_name = @new_temp()
+      @param_names.push param_name
+      if p.toString() in ['D','J']
+        @blocks[0].put_cl2(params_size, param_name)
+        params_size += 2
+      else
+        @blocks[0].put_cl(params_size++, param_name)
+
   get_block_from_instr: (idx) -> @blocks[@instr2block[idx]]

-  new_temp: -> "$#{@temp_count++}"
-  get_all_temps: -> ("$#{i}" for i in [0...@temp_count])
+  new_temp: -> new Temp @temp_count++
+  get_all_temps: -> (new Temp i for i in [0...@temp_count])
+
+  compile: ->
+    @blocks[0].compile()

 class BasicBlock
   constructor: (@block_chain, @start_idx) ->
@@ -52,6 +72,7 @@
     @stack = []
     @locals = []
     @next = []
+    @prev = []
     @stmts = []
     @visited = false

@@ -68,26 +89,58 @@
   add_stmt: (stmt) -> @stmts.push stmt
   new_temp: -> @block_chain.new_temp()

+  entryFromVar: (v) ->
+    if v instanceof StackVar
+      expr = @stack[v.id]
+    else
+      expr = @locals[v.id]
+
+  forEachVar: (cb) ->
+    for s, i in @stack when s?
+      new_s = new StackVar i
+      cb new_s
+
+    for l, i in @locals when l?
+      new_l = new LocalVar i
+      cb new_l
+
   compile_epilogue: ->
     # copy our stack / local values into appropriately-named vars so that they
     # can be accessed from other blocks
     rv = []
-    for blk_id in @next
-      block = @block_chain.get_block_from_instr blk_id
-      for s, i in @stack when s?
-        unless /^\$\d+$/.test s
-          temp = @new_temp()
-          rv.push new Move temp, s
-          @stack[i] = temp
-        rv.push new Move block.in_stack[i], @stack[i] if block.in_stack[i]?
-
-      for l, i in @locals when l?
-        unless /^\$\d+$/.test l
-          temp = @new_temp()
-          rv.push new Move temp, l
-          @locals[i] = temp
-        rv.push new Move block.in_locals[i], @locals[i] if block.in_locals[i]?
-    rv
+
+    postordered = []
+    visited = {}
+    clobbered = {}
+    replacements = {}
+    traverse = (v) =>
+      return if visited[v]
+      visited[v] = true
+
+      expr = @entryFromVar v
+
+      unless expr instanceof Primitive and v.equals expr
+        clobbered[v] = true
+        replacer = (d) =>
+          return d unless (d instanceof StackVar or d instanceof LocalVar) and not d.equals v
+          if clobbered[d]
+            unless replacements[d]
+              replacements[d] = @new_temp()
+              rv.push new Move replacements[d], d
+            return replacements[d]
+          traverse d
+          d
+
+        if expr instanceof Expr
+          expr.replace replacer
+        else
+          expr = replacer expr
+
+        postordered.push new Move v, expr
+
+    @forEachVar traverse
+
+    rv.concat postordered.reverse()

   compile: (prev_stack, prev_locals) ->
     return if @visited
@@ -97,14 +150,11 @@
     # the first block has its local vars set from the function params
     unless @start_idx == 0
       @stack =
-        for s in prev_stack
-          if s? then @new_temp() else null
+        for s,i in prev_stack
+          if s? then new StackVar i else null
       @locals =
-        for l in prev_locals
-          if l? then @new_temp() else null
-      # dup the stack / locals so the next pass can retrieve our input variables
-      @in_stack = @stack[..]
-      @in_locals = @locals[..]
+        for l,i in prev_locals
+          if l? then new LocalVar i else null

     instr_idx = @start_idx
     for op in @opcodes
@@ -119,8 +169,7 @@
     unless op.offset? or (op.name.indexOf 'return') != -1
       @add_stmt => @compile_epilogue()

-    for idx in @next
-      next_block = @block_chain.get_block_from_instr idx
+    for next_block in @next
       # java bytecode verification ensures that the stack height and stack /
       # local table types match up across blocks
       next_block.compile @stack, @locals
@@ -151,18 +200,51 @@

   eval: (b) ->
     temp = b.new_temp()
-    b.add_stmt "$0 = #{@}", temp
+    b.add_stmt new Expr "$0 = #{@}", temp
     new Primitive b

   toString: -> @fragments.join ''

-class Primitive extends Expr
+  replace: (cb) ->
+    for frag, i in @fragments
+      if frag instanceof Temp
+        @fragments[i] = cb frag
+      else if frag instanceof Expr
+        frag.replace cb
+
+  get_dependencies: ->
+    temps = []
+    for frag in @fragments
+      if frag instanceof Expr
+        temps.concat frag.get_dependencies()
+      else if frag instanceof Temp
+        temps.push frag
+    temps
+
+
+class Primitive

   constructor: (@str) ->

   eval: -> @

   toString: -> @str
+
+  equals: (p) -> p.toString() == @toString()
+
+class Temp extends Primitive
+
+  constructor: (@id) ->
+
+  toString: -> "$#{@id}"
+
+class StackVar extends Temp
+
+  toString: -> "s#{@id}"
+
+class LocalVar extends Temp
+
+  toString: -> "l#{@id}"

 class Move

@@ -204,15 +286,15 @@
       b.push val
   ArrayLoadOpcode: (b) ->
     temp = b.new_temp()
-    b.add_stmt """
-    var idx = #{b.pop()};
-    var obj = rs.check_null(#{b.pop()});
+    b.add_stmt new Expr """
+    var idx = $0;
+    var obj = rs.check_null($1);
     var array = obj.array;
     if (!(0 <= idx && idx < array.length))
       java_throw(rs, 'java/lang/ArrayIndexOutOfBoundsException',
         idx + " not in length " + array.length + " array of type " + obj.type.toClassString());
-    #{temp} = array[idx]
-    """
+    $2 = array[idx]
+    """, b.pop(), b.pop(), temp
     if @name.match /[ld]aload/ then b.push2 temp else b.push temp
   UnaryBranchOpcode: (b, idx) ->
     cmpCode = @name[2..]
@@ -224,17 +306,14 @@
           "!== null"
         else
           "#{cmpMap[cmpCode]} 0"
-    b.next.push @offset + idx
-    v = b.pop()
+    b.next.push b.block_chain.get_block_from_instr @offset + idx
     b.add_stmt -> b.compile_epilogue()
-    b.add_stmt "if (#{v} #{cond}) { label = #{@offset + idx}; continue }"
+    b.add_stmt new Expr "if ($0 #{cond}) { label = #{@offset + idx}; continue }", b.pop()
   BinaryBranchOpcode: (b, idx) ->
     cmpCode = @name[7..]
-    b.next.push @offset + idx
-    v2 = b.pop()
-    v1 = b.pop()
+    b.next.push b.block_chain.get_block_from_instr @offset + idx
     b.add_stmt -> b.compile_epilogue()
-    b.add_stmt "if (#{v1} #{cmpMap[cmpCode]} #{v2}) { label = #{@offset + idx}; continue }"
+    b.add_stmt new Expr "if ($1 #{cmpMap[cmpCode]} $0) { label = #{@offset + idx}; continue }",b.pop(),b.pop()
   InvokeOpcode: (b, idx) ->
     method = new Method # kludge
     method.access_flags = { static: @name == 'invokestatic' }
@@ -264,10 +343,10 @@
       temp = b.new_temp()

       if method.return_type.toString() in ['D', 'J']
-        b.add_stmt new Move "#{temp} = rs.pop2()"
+        b.add_stmt new Move temp, "rs.pop2()"
         b.push2 temp
       else
-        b.add_stmt "#{temp} = rs.pop()"
+        b.add_stmt new Move temp, "rs.pop()"
         b.push temp

 compile_obj_handlers = {
@@ -286,15 +365,14 @@
   fconst_2: { compile: (b) -> b.push new Primitive "2"; }
   dconst_0: { compile: (b) -> b.push2 new Primitive "0"; }
   dconst_1: { compile: (b) -> b.push2 new Primitive "1"; }
-  # the *astore commands don't work yet...
-  iastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  lastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
-  fastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  dastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
-  aastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  bastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  castore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  sastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  iastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  lastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  fastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  dastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  aastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  bastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  castore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  sastore: {compile: (b) -> b.add_stmt new Expr "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
   pop: {compile: (b) -> b.pop()}
   pop2: {compile: (b) -> b.pop2()}
   # TODO: avoid duplicating non-primitive expressions so as to save on computation
@@ -356,28 +434,30 @@

   arraylength: { compile: (b) ->
     t = b.new_temp()
-    b.add_stmt "#{t} = rs.check_null(#{b.pop()}).array.length"
+    b.add_stmt new Move t, new Expr "rs.check_null($0).array.length", b.pop()
     b.push t
   }

   getstatic: { compile: (b) ->
-    temp = b.new_temp()
-    b.add_stmt "#{temp} = rs.static_get(#{JSON.stringify @field_spec})"
-    if @field_spec.type in ['J','D'] then b.push2 temp else b.push temp }
+    t = b.new_temp()
+    b.add_stmt new Move t, "rs.static_get(#{JSON.stringify @field_spec})"
+    if @field_spec.type in ['J','D'] then b.push2 t else b.push t
+  }

   'new': { compile: (b) ->
-    temp = b.new_temp()
-    b.add_stmt "#{temp} = rs.init_object(#{JSON.stringify @class})"
-    b.push temp }
+    t = b.new_temp()
+    b.add_stmt new Move t, "rs.init_object(#{JSON.stringify @class})"
+    b.push t
+  }

   goto: { compile: (b, idx) ->
-    b.next = [@offset + idx]
+    b.next = [b.block_chain.get_block_from_instr @offset + idx]
     b.add_stmt -> b.compile_epilogue()
     b.add_stmt "label = #{@offset + idx}; continue"
   }

   goto_w: { compile: (b, idx) ->
-    b.next = [@offset + idx]
+    b.next = [b.block_chain.get_block_from_instr @offset + idx]
     b.add_stmt -> b.compile_epilogue()
     b.add_stmt "label = #{@offset + idx}; continue"
   }
@@ -395,28 +475,12 @@

         block_chain = new BlockChain m

-        param_names = ['rs']
-        params_size = 0
-        unless m.access_flags.static
-          param_name = block_chain.new_temp()
-          param_names.push param_name
-          block_chain.blocks[0].put_cl(params_size++, param_name)
-
-        for p in m.param_types
-          param_name = block_chain.new_temp()
-          param_names.push param_name
-          if p.toString() in ['D','J']
-            block_chain.blocks[0].put_cl2(params_size, param_name)
-            params_size += 2
-          else
-            block_chain.blocks[0].put_cl(params_size++, param_name)
-
-        block_chain.blocks[0].compile()
+        block_chain.compile()

         temps = block_chain.get_all_temps()

         """
-        #{name}: function(#{param_names.join ", "}) {
+        #{name}: function(#{block_chain.param_names.join ", "}) {
           var label = 0;
           #{if temps.length > 0 then "var #{temps.join ", "};" else ""}
           while (true) {


Fix compilation of deferring to wait for full typing if not accessed from subscope

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -1676,7 +1676,7 @@
       prevSize = ctx.deferred().length
       [expression, dependencyName, useScope, lhs, rhs] = deferred = ctx.deferred().shift()
       if useScope isnt ctx.currentScopeIndex() and (ctx.isDeclared dependencyName) or
-          (ctx.isTyped dependencyName)
+          (ctx.isFinallyDeclaredCurrentlyTyped dependencyName)
         compiledPairs.push definitionPairCompile ctx, lhs, rhs
         deferredCount = 0
       else
@@ -3211,8 +3211,8 @@
       type = mapOrigin (freshInstance ctx, contextType), atom
       nameTranslate ctx, atom, symbol, type
     # In sub-scope (function) only defer compilation for declarations in current scope
-    else if ((not ctx.isCurrentlyDeclared symbol) and (ctx.isDeclared symbol)) or
-        contextType instanceof TempType
+    else if (b = ((not ctx.isCurrentlyDeclared symbol) and (ctx.isDeclared symbol)))# or
+        #contextType instanceof TempType
       # Typing deferred, use an impricise type var
       type = toConstrained ctx.freshTypeVariable star
       ctx.addToDeferredNames


Support for import-all exported

Support for import-all exported

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -212,8 +212,8 @@
     @importedModules = newSet()
     @_requested = newMap()

-  req: (moduleName, names) ->
-    addToMap @_requested, moduleName, names
+  req: (moduleName, importSetup) ->
+    addToMap @_requested, moduleName, importSetup
     if not @isModuleLoaded moduleName
       @_requesting = yes

@@ -2445,16 +2445,22 @@
   if (not isTuple reqTuple) or _empty reqs
     return malformed ctx, reqTuple, 'req requires a tuple of names to be required'
   map (syntaxNewName ctx, 'definition name to be imported required'), reqs
+  realReqs = filter (__ _not, isSplat), reqs
+  hasSplat = realReqs.length < reqs.length
   [moduleNameAtom] = _validArguments call
   if not moduleNameAtom or not isName moduleNameAtom
     return malformed ctx, call, 'req requires a module name to require from'
   moduleNameAtom.label = 'module'
   declaredModuleName = moduleNameAtom.symbol
-  requiredNames = (arrayToSet (filter _is, (map _symbol, reqs)))
+  requiredNames = (arrayToSet (filter _is, (map _symbol, realReqs)))
+  naming = arrayToMap (for name of values requiredNames
+    [name, name])
   moduleName = (resolveModuleName ctx, declaredModuleName)
-  ctx.req moduleName, requiredNames
+  ctx.req moduleName,
+    importAll: hasSplat
+    naming: naming
   if ctx.isModuleLoaded moduleName
-    for arg in reqs
+    for arg in realReqs
       if (isName arg)
         name = arg.symbol
         if ctx.isFinallyTyped name, ctx.currentScopeIndex()
@@ -2472,7 +2478,7 @@
           declareImported ctx, name
       else
         malformed ctx, arg, 'Name required'
-  irImport moduleName, (setToArray requiredNames), moduleNameAtom
+  irImport moduleName, naming, moduleNameAtom

 resolveModuleName = (ctx, declaredModuleName) ->
   modulePathToName (resolveModulePathFrom ctx.typedModulePath.names,
@@ -3630,10 +3636,10 @@
 #   // [a] (req ./Some/Submodule)              => I need to know the type of the module in the path
 #   var _temp = require('./Some').Submodule;
 #
-irImport = (moduleName, names, moduleNameAtom) ->
-  {ir: irImportTranslate, moduleName, names, moduleNameAtom}
-
-irImportTranslate = (ctx, {moduleName, names, moduleNameAtom}) ->
+irImport = (moduleName, naming, moduleNameAtom) ->
+  {ir: irImportTranslate, moduleName, naming, moduleNameAtom}
+
+irImportTranslate = (ctx, {moduleName, naming, moduleNameAtom}) ->
   # TODO: pass this info in through context instead of a direct call
   importedTypedModulePath = moduleNameToTypedModulePath moduleName, ctx.typedModulePath
   if not importedTypedModulePath
@@ -3656,9 +3662,8 @@
   submoduleLookup = reduce jsAccess, parts
   temp = ctx.newJsVariable()
   jsStatementList (join [jsVarDeclaration temp, submoduleLookup],
-      for name in names
-        validName = (validIdentifier name)
-        jsVarDeclaration validName, (jsAccess temp, validName))
+      for oldName, newName of values naming
+        jsVarDeclaration (validIdentifier newName), (jsAccess temp, oldName))

 isTypeAnnotation = (expression) ->
   (isCall expression) and (':' is _symbol _operator expression)
@@ -6018,13 +6023,13 @@
   modules: setToArray modules

 moduleDependencies = (moduleName) ->
-  concatSets (requiresFor moduleName), (newSetWith moduleName)
+  concatSets (requiresFor moduleName), (newMapWith moduleName, importAll: yes)
+
+reverseModuleDependencies = (moduleName) ->
+  concatSets (newMapWith moduleName, importAll: yes), (requiresFor moduleName)

 runtimeDependencies = (moduleName) ->
   concatSets (collectRequiresFor moduleName), (newSetWith moduleName)
-
-reverseModuleDependencies = (moduleName) ->
-  concatSets (newSetWith moduleName), (requiresFor moduleName)

 modulePathToName = (path) ->
   path.join '/'
@@ -6061,9 +6066,9 @@
     js

 allInjected = (required, injected) ->
-  for name, names of values required
+  for name of values required
     injectedNames = lookupInMap injected, name
-    if not injectedNames# or
+    if not injectedNames# or  # names = {naming} = required[name]
         #names.size isnt injectedNames.size or
         #(intersectSets [names, injectedNames]).size isnt names.size
       return no
@@ -6080,21 +6085,22 @@

 injectedContext = (modulesToInject, shouldDeclare) ->
   ctx = new Context
-  for moduleName, names of values modulesToInject when compiled = lookupCompiledModule moduleName
-    injectContext ctx, shouldDeclare, compiled.declared, moduleName, names
+  for moduleName, {naming, importAll} of values modulesToInject when compiled = lookupCompiledModule moduleName
+    injectContext ctx, shouldDeclare, compiled.declared, moduleName, naming or newMap(), importAll
   ctx

-injectContext = (ctx, shouldDeclare, compiledModule, moduleName, names) ->
+injectContext = (ctx, shouldDeclare, compiledModule, moduleName, naming, importAll) ->
   {definitions, typeNames, classes, macros} = compiledModule
   topScope = ctx._scope()
-  shouldImport = (name) -> not names.size or inSet names, name
-  for name, macro of values macros when shouldImport name
-    if ctx.isMacroDeclared name
-      throw new Error "Macro #{name} already defined"
-    else
-      addToMap topScope.macros, name, macro
-  for name, {type, arity, docs, source, isClass, virtual, final, exported} of values definitions when shouldImport name
-    addToMap topScope, name, {
+  nameAfterImport = (name) -> importAll and name or lookupInMap naming, name
+  for name, macro of values macros when newName = nameAfterImport name
+    if ctx.isMacroDeclared newName
+      throw new Error "Macro #{newName} already defined"
+    else
+      addToMap topScope.macros, newName, macro
+  for name, definition of values definitions when newName = nameAfterImport name
+    {type, arity, docs, source, isClass, virtual, final, exported} = definition
+    addToMap topScope, newName, {
       arity, docs, source, isClass, virtual, final,
       type: (type if shouldDeclare)
       tempType: type


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -23,7 +23,7 @@

 player.I.update()

-player.I.shoo
+player.I.shootTimer
 </pre></code>

 @name Cooldown


Corrected reference in push WebSocket rejection error assembly.

--- src/api.coffee
+++ src/api.coffee
@@ -175,7 +175,7 @@

             logger.error "Failed to connect to push WebSocket for namespace '#{@namespace}' channel #{jsonify(@attributes)}"

-            reject new errors.ApiError("Server rejected the push WebSocket", undefined, res.statusCode, json)
+            reject new errors.ApiError("Server rejected the push WebSocket", error)

         catch error
           logger.error "Error creating WebSocket for namespace '#{@namespace}' channel #{jsonify(@attributes)}"


draw triggering events

draw triggering events

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -3,6 +3,8 @@
 object.

 Binds a default draw listener to draw a rectangle or a sprite, if one exists.
+
+Binds a step listener to update the transform of the object.

 Autoloads the sprite specified in I.spriteName, if any.

@@ -25,9 +27,14 @@

   self.bind 'step', ->
     center = self.center()
-    I.transform = Matrix.translation(center.x, center.y)
-      .concat(Matrix.rotation(I.rotation))
-      .concat(Matrix.translation(-I.width/2, -I.height/2))
+
+    if I.rotation
+      I.transform = Matrix.translation(center.x, center.y)
+        .concat(Matrix.rotation(I.rotation)) if I.rotation
+        .concat(Matrix.translation(-I.width/2, -I.height/2))
+    else
+      # Assumes I.x and I.y are top-left
+      I.transform = Matrix.translation(I.x, I.y)

   self.bind 'draw', (canvas) ->
     if I.sprite
@@ -36,3 +43,5 @@
       canvas.fillColor(I.color)
       canvas.fillRect(0, 0, I.width, I.height)

+  return {}
+


option to prevent wired redirects, when drops on the outside of dropzone happen, added

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -150,6 +150,8 @@
     # If true, the dropzone will present a file selector when clicked.
     clickable: yes

+    preventDropOutside: no
+
     # Whether hidden files in directories should be ignored.
     ignoreHiddenFiles: yes

@@ -553,7 +555,16 @@
       else
         @clickableElements = Dropzone.getElements @options.clickable, "clickable"

-
+    if @options.preventDropOutside
+      disableDrop = (e) ->
+        e.stopPropagation()
+        e.preventDefault()
+        false
+      document.addEventListener('drop', disableDrop, false)
+      document.addEventListener('dragenter', disableDrop, false)
+      document.addEventListener('dragover', disableDrop, false)
+      document.addEventListener('dragleave', disableDrop, false)
+
     @init()


Support sending messages to threads

Support sending messages to threads

--- src/flowdock.coffee
+++ src/flowdock.coffee
@@ -80,6 +80,16 @@
       tags: tags || []
     @send "/messages", data, callback

+  # Send a thread message to Flowdock
+  threadMessage: (flowId, threadId, message, tags, callback) ->
+    data =
+      flow: flowId
+      thread_id: threadId
+      event: 'message'
+      content: message
+      tags: tags || []
+    @send "/messages", data, callback
+
   # Send a chat comment to Flowdock
   comment: (flowId, parentId, comment, tags, callback) ->
     data =


react for graphite components

react for graphite components

--- src/graphite.coffee
+++ src/graphite.coffee
@@ -12,8 +12,9 @@
   http = require 'http'
   docs = require 'graphite_docs'
   parser = require 'graphite_parser'
-
-  graphite = modules.create 'graphite', ({fn, cmd, settings}) ->
+  builtins = require 'builtins'
+
+  graphite = modules.create 'graphite', ({fn, component_fn, cmd, settings}) ->
     args_to_params = (context, args) ->
       graphite.args_to_params {args, default_options: context.options()}

@@ -24,6 +25,38 @@
         unless _.isString t
           throw new TypeError "#{t} is not a string"
       @value new dsl.type.q targets.map(String)...
+
+    DocsIndex = React.createClass
+      render: ->
+        names = (name for name of @props.docs)
+        names.sort()
+        React.DOM.div {},
+          _.map names, (name) =>
+            signature = @props.docs[name].signature
+            name = JSON.stringify name if @props.quote
+            value = "docs #{name}"
+            value += " # #{signature}" if signature?
+            builtins.ExampleComponent ctx: @props.ctx, value: value, run: true
+
+    FunctionDocsComponent = React.createClass
+      render: ->
+        React.DOM.div {}, [
+          React.DOM.div dangerouslySetInnerHTML: __html: @props.docs.docs
+          _.map @props.docs.examples, (example) =>
+            builtins.ExampleComponent ctx: @props.ctx, value: "#{default_target_command} #{JSON.stringify example}", run: false
+        ]
+
+    ParameterDocsComponent = React.createClass
+      render: -> React.DOM.div()
+      componentDidMount: (node) ->
+        ctx = @props.ctx
+        # TODO
+        $docs = $(node).append @props.docs
+        $docs.find('a').on 'click', (e) ->
+          e.preventDefault()
+          href = $(this).attr 'href'
+          if href[0] is '#'
+            ctx.run "docs '#{decodeURI href[1..]}'"

     cmd 'docs', 'Shows the documentation for a graphite function or parameter', (name) ->
       if name?
@@ -31,53 +64,36 @@
         name = name._lead_context_fn?.name if name._lead_op?
         function_docs = docs.function_docs[name]
         if function_docs?
-          @div function_docs.docs
-          for example in function_docs.examples
-            @example "#{default_target_command} #{JSON.stringify example}", run: false
+          @add_component FunctionDocsComponent ctx: @, docs: function_docs
         name = docs.parameter_doc_ids[name] ? name
         parameter_docs = docs.parameter_docs[name]
         if parameter_docs?
-          $docs = $(parameter_docs)
-          context = @
-          $docs.find('a').on 'click', (e) ->
-            e.preventDefault()
-            href = $(this).attr 'href'
-            if href[0] is '#'
-              context.run "docs '#{decodeURI href[1..]}'"
-          @div $docs
+          @add_component ParameterDocsComponent ctx: @, docs: parameter_docs
         unless function_docs? or parameter_docs?
           @text 'Documentation not found'
       else
-        @html '
Functions
'
-        names = (name for name of docs.function_docs)
-        names.sort()
-        for name in names
-          item = docs.function_docs[name]
-          @example "docs #{name}  # #{item.signature}"
-
-        @html '
Parameters
'
-        names = (name for name of docs.parameter_docs)
-        names.sort()
-        for name in names
-          @example "docs '#{name}'"
+        @add_component React.DOM.div {}, [
+          React.DOM.h3 {}, 'Functions'
+          DocsIndex ctx: @, docs: docs.function_docs
+          React.DOM.h3 {}, 'Parameters'
+          DocsIndex ctx: @, docs: docs.parameter_docs, quote: true
+        ]

     fn 'params', 'Generates the parameters for a Graphite render call', (args...) ->
       result = args_to_params @, args
       @value result

-    fn 'url', 'Generates a URL for a Graphite image', (args...) ->
+    component_fn 'url', 'Generates a URL for a Graphite image', (args...) ->
       params = args_to_params @, args
       url = graphite.render_url params
-      $a = $ ""
-      $a.text url
-      $pre = $ '
'
-      $pre.append $a
-      @add_rendered $pre
+      React.DOM.pre {}, React.DOM.a {href: url, target: 'blank'}, url

     fn 'img', 'Renders a Graphite graph image', (args...) ->
       params = args_to_params @, args
       url = graphite.render_url params
       @async ->
+        # TODO move this to react once it supports onload and onerror
+        # https://github.com/facebook/react/pull/774
         $img = $ ""
         @div $img
         deferred = Q.defer()


Truncate big posts [#36]

Truncate big posts [#36]

--- coffee/models/post.coffee
+++ coffee/models/post.coffee
@@ -10,6 +10,8 @@

     destroy: ->
       @apiCall "delete", message: @get "id"
+
+    SHORT_POST_LENGTH: 500

     getAttributes: ->
       # Because we could render template before the date has been
@@ -29,9 +31,20 @@

       canDelete = utils.getUser() == @get "user"

-      formattedText = formatters.format @get("text"), @get("format")
+      text = @get "text"
+      # Skip truncate for single post
+      if this instanceof Post
+        # XXX: Of course it is much better to shrink post based on
+        # it's rendered size but jquery.dotdotdot is so horrible slow.
+        # (About 38ms for single middle-sized div. What the fuck?)
+        if text.length > @SHORT_POST_LENGTH
+          text = text[...@SHORT_POST_LENGTH]
+          text += " […](/p/#{@get 'id'} \"Читать дальше\")"
+      formattedText = formatters.format text, @get("format")

-      _({recommendationsTitle,
-         recommended,
-         canDelete,
-         formattedText}).extend @attributes
+      _({
+          recommendationsTitle
+          recommended
+          canDelete
+          formattedText
+        }).extend @attributes


try.coffee tweaks

try.coffee tweaks

--- try.coffee
+++ try.coffee
@@ -2,21 +2,27 @@

 wm = new WM()

-work = (x) ->
-	new Promise (resolve, reject) ->
-		setTimeout ->
-			resolve "Resolve #{x}"
-			reject = -> undefined
-		, Math.random() * 100
+work = -> Promise.resolve('hi mom')

-		setTimeout ->
-			reject "Reject #{x}"
-			resolve = -> undefined
-		, Math.random() * 100

-ps = [1..1000].map (i) ->
-	wm.send(work, i)
-	.then (result) ->
-		console.log result
-	.catch (error) ->
-		console.log error
+recvd = 0
+
+printer = ->
+	if ++recvd % 10000 is 0
+		console.log recvd
+
+start = new Date()
+
+ps = [1..100000].map ->
+	p = wm.send work
+	p.then printer
+	return p
+
+
+Promise.all(ps).then ->
+	end = new Date()
+
+	console.log "#{ps.length} ops"
+	console.log "#{end - start} ms"
+	console.log "#{1000 * ps.length / (end - start)} ops/s"
+	process.exit()


Style.

Style.

--- client/publication.coffee
+++ client/publication.coffee
@@ -633,8 +633,10 @@
   @_rendered = false

 Template.publicationAnnotationsItem.rendered = ->
-  # Run for the first time only
+  # Run for the first time only and if not a local annotation
   return if @_rendered or @data.local
+  @_rendered = true
+
   $saved = $(@findAll '.saved')

   fadeOutSavedNotification = _.debounce ->
@@ -648,15 +650,17 @@
       $set:
         body: $(e.target).text()
         local: false
-    , (error) ->
-      throw error if error
-
-      $saved.addClass('display')
-      fadeOutSavedNotification()
-
-    return
-
-  @_rendered = true
+    ,
+      (error) ->
+        throw error if error
+
+        $saved.addClass('display')
+        fadeOutSavedNotification()
+
+    return # Make sure CoffeeScript does not return anything
+
+Template.publicationAnnotationsItem.destroyed = ->
+  @_rendered = false

 Template.publicationAnnotationsItem.canEdit = Template.highlightsControl.canEdit


There is only one local annotation.

There is only one local annotation.

--- client/publication.coffee
+++ client/publication.coffee
@@ -1112,13 +1112,9 @@
     return if $(e.target).hasClass('ui-icon-closethick') > 0

     # Find the current local annotation
-    localAnnotations = LocalAnnotation.documents.find
+    localAnnotation = LocalAnnotation.documents.findOne
       local:
         $exists: true
-    .fetch()
-
-    assert localAnnotations.length > 0
-    localAnnotation = localAnnotations[0]

     # We do nothing if editor is already collapsed
     return unless localAnnotation.editing


fixing vote regex

fixing vote regex

--- src/poll.coffee
+++ src/poll.coffee
@@ -53,25 +53,24 @@
             msg.send error
             @robot.logger.error error

-    robot.respond /vote\s+(\d+,?)/i, (msg) ->
+    robot.respond /vote\s+(\d+,?)+/i, (msg) ->
         try
             votes = msg.match[1]
             user = msg.envelope.user['name']
             room = msg.message.room
             options = @robot.brain.data.poll[room].options
-            @robot.logger.info "Adding new vote for #{@robot.brain.data.poll[room].topic} with #{Object.keys(options).length} options"
-
-            #check if the user already voted
-            @robot.logger.info "Adding a vote for #{vote} for #{user}"
+            @robot.logger.info "Adding new vote for #{votes} on #{@robot.brain.data.poll[room].topic} with #{Object.keys(options).length} options"
+
             for index, option of options
                 @robot.logger.info "Checking #{index}: #{option.text}"
                 if option.users != undefined
                     @robot.logger.info "Some have voted for this one"
                     option.users = option.users.filter (currentUser) -> currentUser isnt user

-            @robot.logger.info "Finishes removing user"
-            votes = votes.split(",")
+            @robot.logger.info "Finished removing user"
+            votes = votes.split ","
             for vote in votes
+                @robot.logger.info "running for vote: #{vote}"
                 if vote >= Object.keys(options).length
                     @robot.logger.info "Invalid vote. #{vote} >= #{Object.keys(options).length}"
                     msg.reply "Please vote for a valid option"


don't overwrite the old window.onload

don't overwrite the old window.onload

--- js/setup-frontend.coffee
+++ js/setup-frontend.coffee
@@ -2,4 +2,9 @@
 stefon doesn't like comments at the end of the previous file
 ###

-window.onload = frontend.core.setup_BANG_
+old_onload = document.onload
+window.onload = () ->
+  console.log('document onload')
+  frontend.core.setup_BANG_()
+  if old_onload?
+    old_onload()


Fix: parallel entry-accounts bug

Fix: parallel entry-accounts bug

--- client/shared/deps.coffee
+++ client/shared/deps.coffee
@@ -12,4 +12,7 @@
 			Languages[string]

 	if Meteor.userId() and not _.isNull(Router.current()) and Router.current().route.name == 'entrySignIn'
+		Router.go 'dashboard'
+
+	if Meteor.userId() and not _.isNull(Router.current()) and Router.current().route.name == 'entrySignUp'
 		Router.go 'dashboard'


Use _.sortBy WTF?

Use _.sortBy WTF?

--- client/view/agenda.coffee
+++ client/view/agenda.coffee
@@ -1,9 +1,9 @@
 mid = new Date('02/25/14 00:00:00')
 Template.agenda.day1 = ->
-  @items.filter((i) -> i.time < mid).sort((a, b) -> a.time.getTime() > b.time.getTime())
+  _.sortBy(@items.filter((i) -> i.time < mid), (i) -> i.time)

 Template.agenda.day2 = ->
-  @items.filter((i) -> i.time > mid).sort((a, b) -> a.time.getTime() > b.time.getTime())
+  _.sortBy(@items.filter((i) -> i.time > mid), (i) -> i.time)

 Template.agenda.canSee = ->
   u = User.current()


Success -> done

Success -> done

--- SingularityUI/app/controllers/TaskDetail.coffee
+++ SingularityUI/app/controllers/TaskDetail.coffee
@@ -146,7 +146,7 @@
               deployId: deployId
               requestId: requestId
             deployPromise = deployInfo.fetch()
-            deployPromise.success =>
+            deployPromise.done =>
                 avg = deployInfo.get('deployStatistics')?.averageRuntimeMillis
                 current =  new Date().getTime() - task.get('task').taskId.startedAt
                 threshold = window.config.warnIfScheduledJobIsRunningPastNextRunPct / 100


errata form expects 'cc' as project

errata form expects 'cc' as project

--- coach/src/task/index.cjsx
+++ coach/src/task/index.cjsx
@@ -31,7 +31,7 @@

   getChildContext: ->
     bookUUID: @props.collectionUUID
-    oxProject: 'coach'
+    oxProject: 'cc'

   getInitialState: ->
     {item} = @props


fixes #85

fixes #85

--- config/application.coffee
+++ config/application.coffee
@@ -220,3 +220,7 @@
     lint:
       files: "<%= files.js.app %>"
       tasks: ["jshint"]
+
+    webfonts:
+      files: "<%= files.webfonts.vendor %>"
+      tasks: ["webfonts:dev"]


feat(cli): add `t`, `tab`, default '\t' to the arguments

--- src/cli.coffee
+++ src/cli.coffee
@@ -10,6 +10,7 @@
                 .describe('s', 'Scope name of the grammar to use').alias('s', 'scope').string('s')
                 .describe('v', 'Output the version').alias('v', 'version').boolean('v')
                 .describe('f', 'File path to use for grammar detection when reading from stdin').alias('f', 'file-path').string('f')
+                .describe('t', 'Tab for JSON output (a string or the number of spaces to use)').alias('t', 'tab').default('t', '\t')
   optimist.usage """
     Usage: textlex [options] [file]

@@ -43,9 +44,9 @@

     tokens = new Textlex().lexSync({filePath, scopeName: cli.argv.scope})
     if outputPath
-      fs.writeFileSync(outputPath, JSON.stringify tokens, null, 2)
+      fs.writeFileSync(outputPath, JSON.stringify tokens, null, cli.argv.t)
     else
-      console.log(JSON.stringify tokens, null, 2)
+      console.log(JSON.stringify tokens, null, cli.argv.t)
   else
     filePath = cli.argv.f
     process.stdin.resume()
@@ -55,6 +56,6 @@
     process.stdin.on 'end', ->
       tokens = new Textlex().lexSync({filePath, fileContents, scopeName: cli.argv.scope})
       if outputPath
-        fs.writeFileSync(outputPath, JSON.stringify tokens, null, 2)
+        fs.writeFileSync(outputPath, JSON.stringify tokens, null, cli.argv.t)
       else
-        console.log(JSON.stringify tokens, null, 2)
+        console.log(JSON.stringify tokens, null, cli.argv.t)


bin supports test

bin supports test

--- src/cli.coffee
+++ src/cli.coffee
@@ -5,7 +5,7 @@
     log: config.log or (message) ->
       console.log message
     error: config.error or (err) ->
-      console.error err.printStack and err or err.message
+      console.error err.printStack is false and err.message or err.stack
       process.exit 1

   # opts: ext, path, template, mongo
@@ -54,7 +54,11 @@
           return migrate.error(err) if err?
           process.exit 0

-      #when 'test'
+      when 'test'
+        die('must provide migration name with --name') unless argv.name
+        migrate.test argv.name, (err) ->
+          return migrate.error(err) if err?
+          process.exit 0

   catch err
     migrate.error err


Display repo

Display repo

--- tourney.coffee
+++ tourney.coffee
@@ -75,11 +75,11 @@
     p2.score += result[1]

     if (result[0] == 0.5)
-      say("results", "Tie!")
+      say("results", "#{p1.name} tied with #{p2.name}")
     else if (result[0] == 1)
       say("results", "#{p1.name} vanquished #{p2.name}")
     else if (result[1] == 1)
-      say("results", "#{p2.name} vanquished #{p1.name}")
+      say("results", "#{p1.name} was vanquished by #{p2.name}")
     else
       say("results", "nobody wins!")
     callback()
@@ -88,10 +88,11 @@
 Models.Team.findAll()
 .then((teams) ->
   Q.all(_.map(teams, (team) ->
-    say('init', "#{team.name} -> #{team.repo}")
+    say('init', "#{team.name} -> #{team.gh_uname}/#{team.gh_repo}")
     PARTICIPANTS[team.id] =
       name: team.name
       repo: team.gh_uname + "/" + team.gh_repo
+      url: "https://github.com/#{team.gh_uname}/#{team.gh_repo}"

     def = Q.defer()

@@ -219,14 +220,14 @@
   winners = _.chain(PARTICIPANTS)
              .each((p) -> p.proc.kill('SIGKILL') )
              .sortBy((p) -> -p.score)
-             .map((p) -> [p.name, p.score])
+             .map((p) -> [p.name, p.score, p.url || p.file])
              .value()
   console.log()
   console.log()

   console.log("Results:")
   _.each(winners, (w) ->
-    console.log("#{w[0]} won #{w[1]} matches")
+    console.log("#{w[0]} won #{w[1]} matches (#{w[2]})")
   )

   process.exit(0)


setup verbose mode for some console.log statements

--- src/hem.coffee
+++ src/hem.coffee
@@ -48,7 +48,7 @@
   compilers: compilers

   # TODO: include way to handle older slug.json files?? backwards compatible??
-  slug:   argv.slug or './slug.json'
+  slug: argv.slug or './slug.json'

   options:
     server:
@@ -79,7 +79,7 @@
         console.log "ERROR: Unable to determine url mapping for package: #{pkg.name}"
         process.exit(1)
       # set route
-      console.log "Map package #{pkg.name} to #{pkg.url}"
+      console.log "Map package '#{pkg.name}' to #{pkg.url}" if argv.v
       app.use(pkg.url, pkg.middleware)

     # setup static routes
@@ -89,7 +89,7 @@
       if (typeof value is 'string')
         # make sure path exists
         if fs.existsSync(value)
-          console.log "Map directory #{value} to #{url}"
+          console.log "Map directory '#{value}' to #{url}" if argv.v
           app.use(url, connect.static(value))
         else
           console.log "ERROR: The folder #{value} does not exist."
@@ -102,7 +102,7 @@
     # start server
     http.createServer(app).listen(@options.server.port, @options.server.host)

-    # start addition process if any
+    # spawn addition process if any
     if @options.server.spawn
         spawn = @options.server.spawn
         console.log "Spawning process: #{spawn.command} #{spawn.args}"


Renamed the missed 'ex'

Renamed the missed 'ex'

--- views/app.cjsx
+++ views/app.cjsx
@@ -1,5 +1,5 @@
 # Break on first line (Please keep this line at the top of this file)
-debugger if dbg.ex.brk?.isEnabled()
+debugger if dbg.h.brk?.isEnabled()

 {ROOT, EXROOT, _, $, $$, React, ReactDOM} = window
 {config, toggleModal} = window


Append ".json" to url

Append ".json" to url

--- client/component/Grid.coffee
+++ client/component/Grid.coffee
@@ -160,7 +160,7 @@
             oScroller: { loadingIndicator: true }
 #            columnDefs: @column_definitions
             ajax:
-                url: @record_query.url
+                url: @record_query.url + ".json"
                 data: (d)=>@buildData(d)
                 dataSrc: (d)->
                     d.recordsFiltered = d.recordsTotal = d.total


Removed new keyword

Removed new keyword

--- core/app/backbone/views/base_fact_wheel_view.coffee
+++ core/app/backbone/views/base_fact_wheel_view.coffee
@@ -58,7 +58,7 @@
     @$canvasEl.addClass 'fact-wheel-responding-to-mouse' if @options.respondsToMouse

     @$('.raphael_container').html(@$canvasEl)
-    @canvas = new Raphael @$canvasEl[0], @boxSize(), @boxSize()
+    @canvas = Raphael @$canvasEl[0], @boxSize(), @boxSize()
     @bindCustomRaphaelAttributes()

   boxSize: -> @options.radius * 2 + @maxStrokeWidth()


Social: inject channel operations into singleton.channel

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -1,23 +1,23 @@
 class SocialApiController extends KDController
   constructor: (options = {}, data) ->
     super options, data
-    @groupsController = KD.getSingleton "groupsController"

-  getCurrentGroup: (callback)->
-    @groupsController.ready =>
-      callback @groupsController.getCurrentGroup()
+  getCurrentGroup = (callback)->
+    groupsController = KD.getSingleton "groupsController"
+    groupsController.ready =>
+      callback  KD.getSingleton("groupsController").getCurrentGroup()

-  fetchChannelActivity:(options, callback)->
+  fetchChannelActivity = (options, callback)->
     return callback {message: "Channel id is not set for request"} unless options.id
-    @getCurrentGroup (group)->
+    getCurrentGroup (group)->
       options.groupName = group.slug
       {SocialChannel} = KD.remote.api
       SocialChannel.fetchActivity options, (err, result)->
         return callback err if err
         return callback null, mapActivities result

-  fetchGroupActivity:(options, callback)->
-    @getCurrentGroup (group)->
+  fetchGroupActivity = (options, callback)->
+    getCurrentGroup (group)->
       return callback {message: "Group doesnt have socialApiChannelId"} unless group.socialApiChannelId
       options.id        = group.socialApiChannelId
       options.groupName = group.slug
@@ -26,13 +26,18 @@
         return callback err if err
         return callback null, mapActivities result

-  fetchChannels:(options, callback)->
-    @getCurrentGroup (group)->
+  fetchChannels = (options, callback)->
+    getCurrentGroup (group)->
       options.groupName = group.slug
       {SocialChannel} = KD.remote.api
       SocialChannel.fetchChannels options, (err, result)->
         return callback err if err
         return callback null, mapChannel result
+
+  channel:
+    list: fetchChannels
+    fetchActivity: fetchChannelActivity
+    fetchGroupActivity: fetchGroupActivity

   messageApiFunc = (name, rest..., callback)->
     KD.remote.api.SocialMessage[name] rest..., (err, res)->
@@ -83,7 +88,7 @@

     return m

-  mapChannel:(channels)->
+  mapChannel = (channels)->
     revivedChannels = []
     {SocialChannel} = KD.remote.api
     for channel in channels


socialapi: subscribe to pubnub channel when it is enabled

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -269,24 +269,30 @@
         socialapi.cacheItem socialApiChannel
         socialapi.openedChannels[channelName] = {} # placeholder to avoid duplicate registration

+        {name, typeConstant} = socialApiChannel
+
         subscriptionData =
           serviceType: 'socialapi'
           group      : group.slug
-          channelType: socialApiChannel.typeConstant
-          channelName: socialApiChannel.name
+          channelType: typeConstant
+          channelName: name
           isExclusive: yes
           connectDirectly: yes

-        # do not use callbacks while subscribing, KD.remote.subscribe already
-        # returns the required channel object. Use it. Callbacks are called
-        # twice in the subscribe function
-        brokerChannel = KD.remote.subscribe channelName, subscriptionData
+        if KD.isPubnubEnabled()
+          KD.singletons.realtime.subscribe {channelName: name, typeConstant, group: group.slug}
+          realtimeChannel = KD.singletons.realtime
+        else
+          # do not use callbacks while subscribing, KD.remote.subscribe already
+          # returns the required channel object. Use it. Callbacks are called
+          # twice in the subscribe function
+          realtimeChannel = KD.remote.subscribe channelName, subscriptionData

         # add opened channel to the openedChannels list, for later use
-        socialapi.openedChannels[channelName] = {delegate: brokerChannel, channel: socialApiChannel}
+        socialapi.openedChannels[channelName] = {delegate: realtimeChannel, channel: socialApiChannel}

         # start forwarding private channel evetns to the original social channel
-        forwardMessageEvents brokerChannel, socialApiChannel, getMessageEvents()
+        forwardMessageEvents realtimeChannel, socialApiChannel, getMessageEvents()

         # notify listener
         socialapi.emit "ChannelRegistered-#{channelName}", socialApiChannel


SyncLocalController: getter method for openedFiles array

--- client/Main/synclocalcontroller.coffee
+++ client/Main/synclocalcontroller.coffee
@@ -125,3 +125,25 @@
       @openedFiles.push fileName
       @storage.setValue "openedFiles", @openedFiles

+  removeFromOpenedFiles: (file)->
+    fileName = "[#{file.vmName}]#{file.path}"
+    log fileName
+    index = @openedFiles.indexOf fileName
+    unless index == -1
+      @openedFiles.splice index, 1
+      @storage.setValue "openedFiles", @openedFiles
+
+
+  getRecentOpenedFiles: ->
+    @openedFiles
+
+
+  saveEditorHistory: ->
+    log "NOT IMPLEMENTED YET"
+
+
+
+
+
+
+


Config: Google: attributeMapping for generic meta data mapping

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -110,6 +110,10 @@
     description            : 'Google compute engine'
     instanceTypes          : require './instance-types/gce'
     advancedFields         : []
+    attributeMapping       :
+      image                : 'disk.0.image' # getting the first disk image ~ GG
+      instance_type        : 'machine_type'
+      region               : 'zone'
     credentialFields       :
       project              :
         label              : 'Project ID'


Auto add name before message which sent from query window

--- src/irc.coffee
+++ src/irc.coffee
@@ -71,7 +71,7 @@

     bot.addListener 'message', (from, to, message) ->
       console.log "From #{from} to #{to}: #{message}"
-
+
       user = self.userForName from
       unless user?
         id = (new Date().getTime() / 1000).toString().replace('.','')
@@ -83,6 +83,8 @@
         console.log "#{to} <#{from}> #{message}"
       else
         user.room = null
+        unless message.indexOf(to) == 0
+          message = "#{to}: #{message}"
         console.log "msg <#{from}> #{message}"

       self.receive new Robot.TextMessage(user, message)


`job` utility now reads command line.

`job` utility now reads command line.

 * The `job` utiltiy now read the command line, instead of reading a full
   command from stdin.

--- src/job.coffee
+++ src/job.coffee
@@ -26,35 +26,18 @@

 hostname = argv.shift()

-stdin = process.openStdin()
-stdin.setEncoding "utf8"
 input = []
-stdin.on "data", (chunk) -> input.push chunk
-stdin.on "end", ->
+enqueue = ->
   db.createDatabase syslog, (database) ->
-    commands = []
-    input = input.join ""
-    for line in input.split /\n/
-      continue if /^\s*$/.test(line)
-      abend = (msg, e) ->
-        dump =
-          line: line
-          stdin: commands.substring(0, 256)
-        dump.e = e.message if e
-        syslog.send "err", "ERROR: #{msg}", dump
-        process.exit 1
-      try
-        command = JSON.parse line
-      catch e
-        abend "Invalid JSON line.", e
-      if not command.join
-        abend "Command is not an array."
-      if command.length > 3
-        abend "Command array is too long."
-      if command.length == 0
-        abend "Command array is empty."
-      if command.length != 1 and not command[1].join
-        abend "Command arguments must be contained in array."
-      syslog.send "info", "Queued command #{command[0]}.", { command }
-      commands.push command
-    database.enqueue hostname, commands
+    command = [ argv.shift(), argv ]
+    command.push(input.join("")) if input.length
+    database.enqueue hostname, [ command ]
+if argv[argv.length - 1] is "-"
+  argv.pop()
+  stdin = process.openStdin()
+  stdin.setEncoding "utf8"
+  stdin.on "data", (chunk) -> input.push chunk
+  stdin.on "end", ->
+    enqueue()
+else
+  enqueue()


Fixed package version and get value from package.json

--- src/ubs.coffee
+++ src/ubs.coffee
@@ -178,15 +178,17 @@
   [ tasklist, settings ]

 showVersion = ->
-  console.log "ubs version #{Config.version()}"
+  console.log "ubs version #{VERSION}"
   0

 showHelp = ->
   console.log HELP
   0

+exports.VERSION = VERSION = JSON.parse(fs.readFileSync('package.json', 'utf8')).version
+
 HELP = """
-ubs #{Config.version()}
+ubs #{VERSION}
 usage: ubs [options] [task-setting]* [task-name]*
 general options are listed below. task-settings are all of the form
 "<name>=<value>".


Prevent scroll jump when keyword input box is in focus

--- resources/assets/lib/beatmaps/search-panel.coffee
+++ resources/assets/lib/beatmaps/search-panel.coffee
@@ -225,10 +225,12 @@

   setHeaderPinned: (_event, pinned) =>
+    focusOptions = preventScroll: true
+
     if pinned && document.activeElement == @inputRef.current
-      @pinnedInputRef.current.focus()
+      @pinnedInputRef.current.focus(focusOptions)
     else if !pinned && document.activeElement == @pinnedInputRef.current
-      @inputRef.current.focus()
+      @inputRef.current.focus(focusOptions)

   mountPortal: (portal, root) ->


[GH-23] initial work on multi-node container definitions in clusters

--- src/decking.coffee
+++ src/decking.coffee
@@ -47,14 +47,33 @@
       throw new Error "No clusters defined!"

     for name, details of config.clusters
+      # convert shorthand of list of containers
+      if Array.isArray details
+        details = config.clusters[name] =
+          containers: details
+
       # explicit group; check it exists
       if details.group and not config.groups[details.group]
         err = "Cluster #{name} references invalid group #{details.group}"
         throw new Error err

-      # no group, but does the key match one?
+      # no group, but does the key match one? If so use it
       if not details.group and config.groups[name]
         details.group = name
+
+      # right, check out each container in the cluster
+      for container, index in details.containers
+        if typeof container is "string"
+          container = details.containers[index] =
+            name: container
+            count: 1
+
+        containerLookup = config.containers[container.name]
+
+        if not containerLookup
+          throw new Error("Container #{container.name} does not exist")
+
+        container.object = containerLookup

     return config

@@ -235,10 +254,8 @@

         callback null

-    # true, we don't care about the order of a cluster,
-    # but we *do* care about implicit containers, so we have to run this
-    # for now. Should split the methods out
-    resolveOrder @config, cluster, (list) -> async.eachLimit list, 5, iterator, done
+    resolveOrder @config, cluster, (list) ->
+      async.eachSeries list, iterator, done

   create: (cluster, done) ->
     # create a container based on metadata
@@ -396,30 +413,20 @@
   if cluster.group
     # right! specifying a group modifier. let's pump it up...
     groupName = cluster.group
-    containers = cluster.containers
     group = config.groups[groupName]
-  else
-    if cluster.containers
-      # longhand, explicit container list
-      containers = cluster.containers
-    else
-      # shorthand, just an array of container names
-      containers = cluster

   containerDetails = {}

   # map container names to actual container definitions
-  for containerName in containers
-    container = config.containers[containerName]
-
-    if not container
-      throw new Error("Container #{containerName} does not exist")
-
-    containerDetails[containerName] = container
+  for container in cluster.containers
+    containerDetails[container.name] = container.object
+
+    # bit of a fudge; tack on how many instances to spawn
+    containerDetails[container.name].count = container.count

     # dependencies might not be listed in the cluster but still
     # need to be resolved
-    for dependency in container.dependencies
+    for dependency in container.object.dependencies
       if not containerDetails[dependency]
         containerDetails[dependency] = config.containers[dependency]

@@ -446,7 +453,10 @@
           container[key] = value

     # just used for formatting so we pad the container names equally
-    maxNameLength = container.name.length if container.name.length > maxNameLength
+    length = container.name.length
+    # this is a multi-node definition so we'll suffix it .(n) in a minute
+    length += container.count.toString().length if container.count > 1
+    maxNameLength = length if length > maxNameLength

   # resolve dependency order
   depTree = new DepTree
@@ -455,7 +465,18 @@

   list = (containerDetails[item] for item in depTree.resolve())

-  callback list
+  # nearly there, we've got a flattened list, but we need to make sure we have
+  # the correct number of nodes for each container
+  final = []
+  for originalContainer in list
+    for i in [1..originalContainer.count]
+      # dirty clone!
+      container = JSON.parse JSON.stringify originalContainer
+      container.index = i
+      container.name += ".#{i}" if container.count > 1
+      final.push container
+
+  callback final

 validateContainerPresence = (list, done) ->
   iterator = (details, callback) ->


remove unsupported bzip compression from tarballs

--- src/decking.coffee
+++ src/decking.coffee
@@ -336,10 +336,11 @@
     options =
       t: image

-    tarball = "/tmp/decking-#{uuid.v4()}.tar.bz"
+    tarball = "/tmp/decking-#{uuid.v4()}.tar"
     log "Creating tarball to upload context..."

-    child_process.exec "tar -cjf #{tarball} ./", ->
+    # @TODO allow user to specifiy --exclude params to avoid unnecessarily huge tarballs
+    child_process.exec "tar -cf #{tarball} ./", ->

       fs.unlink "./Dockerfile", (err) -> log "[WARN] Could not remove Dockerfile" if err


Fix losing

Fix losing

--- coffee/models/game.coffee
+++ coffee/models/game.coffee
@@ -39,9 +39,10 @@
       pos.x += dx

   placePiece: (piece) ->
-    if piece.position.y < 0
+    if piece.position.y <= 0
       Tetrus.Flash.message("Game Over")
       Tetrus.get('controllers.game').disconnect()
+
     for x in [0...piece.width] by 1
       for y in [0...piece.height] by 1
         if piece.storage[(x + y * piece.width) * 4 + 3] > 0