Fixing the percent complete.

Fixing the percent complete.

--- src/tegh.coffee
+++ src/tegh.coffee
@@ -236,7 +236,7 @@
       continue unless job.status == "printing"
       total += job.total_lines || 0
       current += job.current_line || 0
-    return status + "( #{((current / total) || 0).format(2)}% ) "
+    return status + "( #{((100*current / total) || 0).format(2)}% ) "

   _append: (s, prefix = "") ->
     stdout.write(prefix + s + "\n")


fix API updates

fix API updates


Make description optional

Make description optional

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -3,9 +3,7 @@
 	author: "Jorn van Dijk"
 	twitter: "jornvandijk"
 	title: "Android Tabs"
-	description: """
-		Here's a new Framer example. It's a little grid of photos, which you can scroll, click and pinch to zoom. Made to highlight some of our latest 		features: scroll and click separation, pinchable layers, event shortcuts and more.
-		"""
+	description: "Here's a new Framer example. It's a little grid of photos, which you can scroll, click and pinch to zoom. Made to highlight some of our latest features: scroll and click separation, pinchable layers, event shortcuts and more."

 # Setup
 context = new Framer.Context({name: "Sharing"})
@@ -39,7 +37,7 @@
 		@_renderToggleButtons()
 		@_renderCTA()
 		@_renderInfo()
-		@_renderDescription()
+		@_renderDescription() if @shareInfo.description
 		@_renderDownload()

 		# Evaluate content and set height accordingly
@@ -82,6 +80,9 @@
 			image: "images/logo-button.png"
 			y: Align.center(1)
 			x: Align.center
+
+		for l in [@close, @framerButton]
+			@_showPointer(l)

 	# Render CTA section
 	_renderCTA: ->
@@ -178,10 +179,11 @@
 			showAuthor(if @shareInfo.author then @shareInfo.author else "@#{@shareInfo.twitter}")

 			@_showPointer(@avatar)
-
+
+		# If there's no Twitter handle, but there is an author. Just show author.
 		if @shareInfo.author and !@shareInfo.twitter
 			showAuthor(@shareInfo.author)
-
+
 	_renderDescription: ->
 		@description = new ShareLayer
 			parent: @info
@@ -195,9 +197,12 @@
 		@description.height = descriptionTextSize.height

 	_renderDownload: ->
+
+		verticalPosition = if @description then @description.maxY else @credentials.maxY
+
 		@download = new ShareLayer
 			parent: @info
-			y: @description.maxY + 20
+			y: verticalPosition + 20
 			height: 33
 			borderRadius: 3
 			backgroundColor: "00AAFF"


Session: create a new session for user if they don’t have any clientId

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -104,6 +104,17 @@
   console.error err
   process.exit(1)

+
+# this is for creating session for incoming user if it doesnt have
+app.use (req, res, next) ->
+  {JSession} = koding.models
+  {clientId} = req.cookies
+  return next() if clientId
+  JSession.createSession (err, session, account)->
+    return next() if err or not session
+    res.cookie "clientId", session.clientId, {}
+    next()
+
 app.use (req, res, next) ->
   # add referral code into session if there is one
   addReferralCode req, res


Revert "phantomJS fiuxup attempt number two"

This reverts commit ea68584b8655dcc00ae0df6f4b79016d94a96a60.

--- bokehjs/src/coffee/base.coffee
+++ bokehjs/src/coffee/base.coffee
@@ -6,13 +6,6 @@

 # add some useful functions to underscore
 require("./core/util/underscore").patch()
-
-# fixup for PhantomJS deficiency
-#
-# see https://github.com/ariya/phantomjs/issues/10522#issuecomment-66449127
-Function.prototype.bind = Function.prototype.bind || (thisp) ->
-  fn = this
-  return () -> fn.apply(thisp, arguments)

 Config = {}
 url = window.location.href


fix typo

fix typo

--- bokehjs/src/coffee/main.coffee
+++ bokehjs/src/coffee/main.coffee
@@ -64,7 +64,7 @@
   Bokeh.DiamondCross     = glyph_factory.diamond_cross
   Bokeh.Image            = glyph_factory.image
   Bokeh.ImageRGBA        = glyph_factory.image_rgba
-  Bokeh.ImageURl         = glyph_factory.image_url
+  Bokeh.ImageURL         = glyph_factory.image_url
   Bokeh.InvertedTriangle = glyph_factory.inverted_triangle
   Bokeh.Line             = glyph_factory.line
   Bokeh.MultiLine        = glyph_factory.multi_line


send UTC timestamp

send UTC timestamp

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -369,7 +369,7 @@
 														if (date = pickerDiv.datepicker 'getDate')
 															#timeSectionElements.manual.setSelected true, true
 															log 'schedule', date
-															schedule @_specified date.getTime()
+															schedule @_specified date
 														return
 					initPicker = true
 				return
@@ -456,7 +456,8 @@
 		_delta: (offset) ->
 			"delta:#{offset}"

-		_specified: (time) ->
+		_specified: (d) ->
+			time = d.getTime() + d.getTimezoneOffset()*60*1000
 			"specified:#{time}"

 		generateTimeFn: (unit) ->
@@ -476,7 +477,7 @@
 						tomorrow.setMinutes 0
 						tomorrow.setSeconds 0
 						tomorrow.setMilliseconds 0
-						@_specified tomorrow.getTime()
+						@_specified tomorrow
 				when 'days'
 					(time) => @_delta (_1d * time)
 				when 'months'
@@ -484,7 +485,7 @@
 						now = new Date
 						other = new Date
 						other.setMonth (now.getMonth() + month)
-						@_specified other.getTime()
+						@_specified other

 		getMessageId: ->
 			id = /\/([0-9a-f]{16})/.exec window.location.hash


Command unit tests addons

Command unit tests addons

--- test/unit/command-test.coffee
+++ test/unit/command-test.coffee
@@ -1,5 +1,6 @@
 {assert} = require 'chai'
 sinon = require 'sinon'
+express = require 'express'

 proxyquire = require('proxyquire').noCallThru()

@@ -161,12 +162,14 @@
         assert.equal dc.dreddInstance, 'myDreddInstance'

-  describe 'takeoff', ->
+  describe 'takeoff without argv set', ->
     dc = null
     runDreddStub = null
+    exitCalled = null
     beforeEach ->
       dc = new DreddCommand({
         exit: ->
+          exitCalled = true
       })
       dc.dreddInstance = 'dreddInstance'
       runDreddStub = sinon.stub dc, 'runDredd', ->
@@ -175,6 +178,8 @@
       dc.runDredd.restore()

     describe 'with finished set to false', ->
+      it 'should not call exit callback', ->
+        assert.isNull exitCalled
       it 'does call runDredd just once with dreddInstance as first argument', ->
         runDreddStub.reset()
         dc.takeoff()
@@ -191,6 +196,62 @@
         assert.equal dc.runDredd.called, 0

+  describe 'takeoff with argv set to load regular blueprint', ->
+    dc = null
+    runDreddStub = null
+    returnGood = true
+
+    beforeEach (done) ->
+      app = express()
+
+      app.get '/machines', (req, res) ->
+        if returnGood
+          res.type('json').status(200).send [type: 'bulldozer', name: 'willy']
+        else
+          res.type('json').status(200).send [my: 'another', world: 'service']
+
+      dc = new DreddCommand({
+        custom:
+          argv: [
+            './test/fixtures/single-get.apib'
+            "http://localhost:#{PORT}"
+            '--path=./test/fixtures/single-get.apib'
+          ]
+        exit: (code) ->
+          exitStatus = code
+          server.close()
+      })
+      dc.warmUp()
+
+      server = app.listen PORT, () ->
+        dc.takeoff()
+
+      server.on 'close', done
+
+    describe 'with server returning good things', ->
+      before ->
+        returnGood = true
+
+      it 'returns exit code 0', ->
+        assert.equal exitStatus, 0
+
+      it 'propagates configuration options to Dredd class', ->
+        assert.equal dc.dreddInstance.configuration.options.path[0], "./test/fixtures/single-get.apib"
+        assert.equal dc.dreddInstance.configuration.server, "http://localhost:#{PORT}"
+
+    describe 'with server returning wrong things', ->
+
+      before ->
+        returnGood = false
+
+      it 'returns exit code 1', ->
+        assert.equal exitStatus, 1
+
+      it 'propagates configuration options to Dredd class', ->
+        assert.equal dc.dreddInstance.configuration.options.path[0], "./test/fixtures/single-get.apib"
+        assert.equal dc.dreddInstance.configuration.server, "http://localhost:#{PORT}"
+
+
   describe "when called w/ OR wo/ special arguments", () ->
     describe '--help', ->
       before (done) ->


remove null attributes; bind return self

--- src/view.coffee
+++ src/view.coffee
@@ -25,10 +25,11 @@
   # swap content for given element
   # @return null
   swap: (attributes...) ->
-    if attributes.length == 0
+    real_attributes = for a in attributes when a != null then a
+    if real_attributes.length == 0
       Sirius.Application.adapter.swap(@element, @_result)
     else
-      for attr in attributes
+      for attr in real_attributes
         Sirius.Application.adapter.set_attr(@element, attr, @_result)
     null

@@ -80,10 +81,10 @@
   #
   # @param [Any] - klass, another view\model\function
   # @param [Object] - hash with setting: [to, from]
-  bind: (klass, object_setting) ->
+  bind: (klass, object_setting = {}) ->
     `var c = function(m){console.log(m);};`
     current = @element
-    to = object_setting['to'] || null
+    to   = object_setting['to'] || null
     from = object_setting['from'] || null
     if klass && klass.constructor && klass.constructor.name
       if klass.constructor.name == "View"
@@ -97,5 +98,6 @@

         new Sirius.Observer(current, clb)

+    @

   bind2: () ->


Allow chaining in more places

Allow chaining in more places

--- src/view.coffee
+++ src/view.coffee
@@ -202,18 +202,27 @@

 	appendTo: (el) ->
 		@el.appendTo(el)
+		@

 	insertBefore: (el) ->
 		@el.insertBefore(el)
+		@

 	insertAfter: (el) ->
 		@el.insertAfter(el)
+		@
+
+	remove: ->
+		@el.remove()
+		@

 	addClass: (classes) ->
 		@el.addClass(classes)
+		@

 	removeClass: (classes) ->
 		@el.removeClass(classes)
+		@

 	enable: (recursive = true, updateClass = true)->
 		@enabled = true
@@ -235,11 +244,13 @@
 		@hidden = false
 		@enable(true, false)
 		@removeClass('hidden')
+		@

 	hide: ->
 		@hidden = true
 		@disable(true, false)
 		@addClass('hidden')
+		@

 	hideAction: -> @hide()


Fix XAPI double logins.

Fix XAPI double logins.

--- src/xapi.coffee
+++ src/xapi.coffee
@@ -59,6 +59,7 @@
           else
             args

+          $debug '%s: %s(%j)', @_readableHost, method, actualArgs
           @xmlrpc.methodCall method, actualArgs, callback

         # Returns the plain result if it does not have a valid XAPI format.
@@ -127,15 +128,20 @@
           throw error

   logIn: ->
+    # FIXME: Ugly hack.
+    return if @_logging
+    @_logging = true
+
     # Makes sure there is not session id left.
     delete @sessionId
-
-    $debug 'Logging in %s...', @_readableHost

     @sessionId = @call 'session.login_with_password', @username, @password

     $debug 'Logged in %s (session = %s)', @_readableHost, @sessionId

+    # FIXME: Ugly hack.
+    delete @_logging
+
 #=====================================================================

 module.exports = $XAPI


Update translation, sounds unnatural

Update translation, sounds unnatural

--- p/a-text-based-game/game/es.coffee
+++ p/a-text-based-game/game/es.coffee
@@ -15,7 +15,7 @@
     H.thenChain()
     .then -> H.out 'Encuentras un videojuego de texto.'
     .then -> H.out 'Es obvio que menos de 24 horas se han
-        dedicado a hacerlo.', 3
+        dedicado en hacerlo.', 3
     .then -> H.newLine 3
     .then -> H.choice({
         content: '¿Deseas jugar el juego?'
@@ -26,8 +26,8 @@
         }, {
             content: 'No'
             callback: ->
-                H.out "La tentación es demasiada, y terminas
-                    iniciando el juego."
+                H.out "No te parece interesante, pero la curiosidad
+                    te obliga a jugarlo."
         }]
     })
     .then -> H.out "El juego comienza con una pantalla que dice:", 3
@@ -68,7 +68,7 @@
     }
 G.$2_investigate = ->
     H.thenChain()
-    .then -> H.out 'Al ver por la habitación, te das cuenta que
+    .then -> H.out 'Al mirar por la habitación, te das cuenta de que
         es bastante amplia.'
     .then -> H.out 'Una de las paredes está hecha de vitral.', 3
     .then -> H.out 'Más allá del vitral, no hay más que un vacío.', 3
@@ -160,7 +160,7 @@
                 }]
             })
             .then -> H.newLine()
-            .then -> H.out 'Te pones de pié e intentas irte del salón.'
+            .then -> H.out 'Te pones de pié e intentas huir del salón.'
             .then -> H.out 'La puerta no abre.', 3
             .then -> H.out 'Es obvio que estás atrapado aquí.', 5
             .then -> H.out 'Volteas a ver donde te despertaste.', 3
@@ -168,7 +168,7 @@
             .then -> H.out 'Tu vista se hace más y más oscura.', 3
             .then -> H.out 'Intentas buscar algo por dónde escapar.', 3
             .then -> H.newLine(10)
-            .then -> H.out 'Cuando de repente,'
+            .then -> H.out 'De repente,'
             .then -> H.wait 3
             .then -> G.badend()
     else
@@ -215,7 +215,7 @@
     H.thenChain()
     .then -> H.newLine()
     .then -> H.out 'Al entrar por el pasillo, la oscuridad se
-        va acercando poco a poco.'
+        acerca poco a poco.'
     .then -> H.out 'Solo puedes ver 2 metros a tu alrededor.', 3
     .then -> H.out 'Al entrar por la habitación, la puerta
         se cierra por detrás de tí.', 3
@@ -239,9 +239,9 @@
 G.wa_room_investigate = ->
     H.thenChain()
     .then -> H.newLine()
-    .then -> H.out 'La habitación en la que te encuentras tiene varios
+    .then -> H.out 'Esta habitación tiene varios
         pupitres bien ordenados.'
-    .then -> H.out 'Te fijas en un papel que está en cima de
+    .then -> H.out 'Te fijas en un papel que está encima de
         uno de los pupitres.', 3
     .then -> H.newLine(3)
     .then -> H.choice({
@@ -315,7 +315,7 @@
     .then -> H.out 'Por desgracia, apenas puedes ver un
         metro alrededor de ti.', 3
     .then -> H.out 'La habitación está llena de mesas plegables muy bien
-        ordenadas, de cuales son acompañados por sillas y recipientes.', 3
+        ordenadas, de cuales son acompañados por sillas y botes de reciclaje.', 3
     .then -> H.out 'Nada ha sido tocado.', 3
     .then -> H.newLine(5)
     .then -> H.out 'Encuentras un papel mientras examinabas la habitación.'
@@ -373,7 +373,7 @@
     .then -> H.out 'Estás acostado en tu cama, ojos fijados al techo.', 3
     .then -> H.out 'Miras a tu celular.', 3
     .then -> H.newLine 3
-    .then -> H.out '«07:30, viernes, 30 de marzo», dice.'
+    .then -> H.out 'Muestra la hora: «07:30, viernes, 30 de marzo».'
     .then -> H.newLine 3
     .then -> H.out 'De mala gana, te arreglas y vas a la escuela.'
     .then -> H.newLine 10
@@ -383,7 +383,7 @@
         podrías echarle un vistazo a lo que hicieron los demás.', 3
     .then -> H.newLine 3
     .then -> H.out 'Paseas por el salón, mirando a los proyectos que
-        hizo todo mundo, y uno de ellos te llama la atención.'
+        hicieron todos, y uno de ellos te llama la atención.'
     .then -> G.state.wa = no; H.newLine(5)
     .then -> G.$1()
 G.no = ->


Clean up cscope.coffee

Clean up cscope.coffee

--- lib/cscope.coffee
+++ lib/cscope.coffee
@@ -1,50 +1,51 @@
 {BufferedProcess} = require 'atom'
 ResultSetModel = require './models/result-set-model'
 fs = require 'fs'
+path = require 'path'

 module.exports = CscopeCommands =
-  getSourceFiles: (path, exts) ->
+  getSourceFiles: (project, exts) ->
     args = ['.']
     for ext,index in exts.split(/\s+/)
       args.push '-o' if index > 0
       args.push '-name'
       args.push '*' + ext

-    return @runCommand 'find', args, {cwd: path}
+    return @runCommand 'find', args, {cwd: project}

-  generateCscopeDB: (path) ->
+  generateCscopeDB: (project) ->
     cscope_binary = atom.config.get('atom-cscope.cscopeBinaryLocation')
-    return @runCommand cscope_binary, ['-q', '-R', '-b', '-i', 'cscope.files'], {cwd: path}
+    return @runCommand cscope_binary, ['-qRbi', 'cscope.files'], {cwd: project}

-  writeToFile: (path, fileName, content) ->
-    filePath = path + '/' + fileName
+  writeToFile: (project, fileName, content) ->
+    filePath = path.join(project, fileName)
     return new Promise (resolve, reject) ->
       fs.writeFile filePath, content, (err) ->
         reject {success: false, info: err.toString()} if err
         resolve {success: true}

-  setupCscopeForPath: (path, exts, force) ->
-    cscopeExists = if force then Promise.reject force else @cscopeExists path
+  setupCscopeForPath: (project, exts, force) ->
+    cscopeExists = if force then Promise.reject force else @cscopeExists project
     cscopeExists.then (data) =>
       return Promise.resolve {success: true}
     .catch (data) =>
-      sourceFileGen = @getSourceFiles path, exts
+      sourceFileGen = @getSourceFiles project, exts
       writeCscopeFiles = sourceFileGen.then (data) =>
-        return @writeToFile path, 'cscope.files', data
+        return @writeToFile project, 'cscope.files', data
       dbGen = writeCscopeFiles.then (data) =>
-        return @generateCscopeDB path
+        return @generateCscopeDB project

       return Promise.all([sourceFileGen, writeCscopeFiles, dbGen])

-  setupCscope: (paths, exts, force = false) ->
+  setupCscope: (projects, exts, force = false) ->
     promises = []
-    for path in paths
-      promises.push @setupCscopeForPath path, exts, force
+    for project in projects
+      promises.push @setupCscopeForPath project, exts, force

     return Promise.all(promises)

-  cscopeExists: (path) ->
-    filePath = path + '/' + 'cscope.out'
+  cscopeExists: (project) ->
+    filePath = path.join(project, 'cscope.out')
     return new Promise (resolve, reject) ->
       fs.access filePath, fs.R_OK | fs.W_OK, (err) =>
         reject err if err
@@ -67,21 +68,21 @@

   runCscopeCommand: (num, keyword, cwd) ->
     cscope_binary = atom.config.get('atom-cscope.cscopeBinaryLocation')
-    if keyword.trim() == ''
+    if keyword.trim() is ''
       return Promise.resolve new ResultSetModel()
     else
       return new Promise (resolve, reject) =>
-        @runCommand cscope_binary, ['-d', '-L', '-' + num, keyword], {cwd: cwd}
+        @runCommand cscope_binary, ['-dL' + num, keyword], {cwd: cwd}
         .then (data) ->
           resolve new ResultSetModel(keyword, data)
         .catch (data) ->
           reject data

-  runCscopeCommands: (num, keyword, paths) ->
+  runCscopeCommands: (num, keyword, projects) ->
     promises = []
     resultSet = new ResultSetModel(keyword)
-    for path in paths
-      promises.push(@runCscopeCommand num, keyword, path)
+    for project in projects
+      promises.push(@runCscopeCommand num, keyword, project)

     motherSwear = new Promise (resolve, reject) =>
       Promise.all(promises)


FSItem: update helpers isHidden and getExtension

--- client/Finder/fs/fsitem.coffee
+++ client/Finder/fs/fsitem.coffee
@@ -192,7 +192,7 @@
     .finally =>
       @emit "fs.job.finished"

-  @isHidden: (name)-> return /^\./.test name
+  getExtension:-> FSHelper.getFileExtension @name

   ###
   INSTANCE METHODS
@@ -205,12 +205,10 @@
     super

     @vmController   = KD.getSingleton('vmController')
-
-  getExtension:-> FSItem.getFileExtension @name
+  isHidden:-> FSHelper.isHidden @name
+

   getPath: -> FSHelper.plainPath @path
-
-  isHidden:-> FSItem.isHidden @name

   exists:(callback=noop)->
     kite = @getKite()


remove old tools page

remove old tools page

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -40,16 +40,6 @@
         @titleView.showView titleView
         @appView.showView contentView
         window.tools.fetch()
-      error: (x,y,z) ->
-        console.warn 'ERRROR', x, y, z
-
-  tools: ->
-    window.tools.fetch
-      success: =>
-        titleView = new Cu.View.Title {text: 'My Tools'}
-        contentView = new Cu.View.ToolList {collection: window.tools}
-        @titleView.showView titleView
-        @appView.showView contentView
       error: (x,y,z) ->
         console.warn 'ERRROR', x, y, z


Delete LoDash lib

Delete LoDash lib

Rely on nothing except socket.io …

--- ioserver.coffee
+++ ioserver.coffee
@@ -14,7 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.

-_		= require 'lodash'
 Server	= require('socket.io')

 PORT 	= 8080
@@ -37,7 +36,8 @@
 			@service_list[name] = new service()

 			# list methods of object... it will be the list of io actions
-			@method_list[name] = _.functions(@service_list[name])
+			self.dumpMethods(name)
+			# @method_list[name] = self.dumpMethods(@service_list[name])
 		else
 			console.error "#[!] Service name MUST be longer than 2 characters"

@@ -96,6 +96,19 @@
 						if socket?
 							socket.emit method, data

+	# Thanks to Kri-ban
+	# http://stackoverflow.com/questions/7445726/how-to-list-methods-of-inherited-classes-in-coffeescript-or-javascript
+	# ___ ;)
+	_dumpMethods: (name) ->
+		@method_list[name] = []
+		s = @method_list[name].prototype
+		while k
+			names = Object.getOwnPropertyNames(k)
+			@method_list[name] = @method_list[name].concat(names)
+			k = Object.getPrototypeOf(k)
+			break if not Object.getPrototypeOf(k) # avoid Object properties
+		@method_list[name].unique().sort()
+

 	_findClientsSocket: ({service, room, cb}={}) ->
 		res = []


Build: spritesmith added

Build: spritesmith added

--- client/landing/gulpfile.coffee
+++ client/landing/gulpfile.coffee
@@ -14,6 +14,7 @@
 source     = require 'vinyl-source-stream'
 nodemon    = require 'gulp-nodemon'
 pistachio  = require 'gulp-kd-pistachio-compiler'
+spritesmith= require 'gulp.spritesmith'

 STYLES_PATH = ['./app/styl/**/*.styl']
 COFFEE_PATH = ['./app/coffee/**/*.coffee']


Build: made main gulp produce sites

Build: made main gulp produce sites

--- client/landing/gulpfile.coffee
+++ client/landing/gulpfile.coffee
@@ -2,6 +2,7 @@
 gulp    = require 'gulp'
 argv    = require('minimist') process.argv
 nodemon = require 'gulp-nodemon'
+shell   = require 'gulp-shell'
 req     = (module) -> require "./gulptasks/#{module}"

 # CONSTANTS
@@ -29,10 +30,14 @@

 gulp.task 'build', ->

-  folders = (folder for folder in fs.readdirSync('./') when fs.statSync(folder).isDirectory())
-  sites   = folders.filter (folder) -> folder.search(/^site\./) is 0
+  folders  = (folder for folder in fs.readdirSync('./') when fs.statSync(folder).isDirectory())
+  sites    = folders.filter (folder) -> folder.search(/^site\./) is 0
+  commands = ("gulp --gulpfile ./#{site}/gulpfile.coffee build --site=#{site}" for site in sites)

-  console.log sites
+  gulp.src ''
+    .pipe shell commands
+
+

 gulp.task 'default', ['build', 'serve'], ->


spa routing

spa routing

--- root/app.coffee
+++ root/app.coffee
@@ -16,3 +16,6 @@

   stylus:
     use: [axis(), autoprefixer(), rupture()]
+
+  server:
+    spa: true


hotfix: Export games tool

hotfix: Export games tool

--- client/tools/command_db.coffee
+++ client/tools/command_db.coffee
@@ -91,10 +91,19 @@
       console.log("Creating game version #{gameFile} done.")
       cb()

+getAllGameVersions = (baseUrl, gameId, cb) ->
+  request "#{baseUrl}/game-versions?gameId=#{gameId}", (error, response, body) ->
+    if error then return console.error(error)
+    if statusIsError(response.statusCode) then return console.error(body)
+
+    gameVersions = JSON.parse(body)
+    console.log("Found #{gameVersions.length} game versions.")
+    cb(gameVersions)
+

 # Define command-line arguments
 program
-  .option('-b, --baseUrl <url>', 'Base URL of RedMetrics server. Include protocol, host and port (ex. http://localhost:5000)')
+  .option('-b, --baseUrl <url>', 'Base URL of RedMetrics server. Include protocol, host and port (ex. http://localhost:5000). Defaults to http://api.redwire.io', 'http://redwire.io/api')
   .option('-u, --user <user>', 'User name (must be an admin)')
   .option('-p, --password <password>', 'Password')

@@ -107,8 +116,18 @@
         console.log("Success!")

 program
+  .command('exportGame <gameId> <outputFile>')
+  .description('Export all the versions of a game into a single JSON file')
+  .action (gameId, outputFile, options) ->
+    console.log("Exporting game #{gameId}...")
+    login program.baseUrl, program.user, program.password, ->
+      getAllGameVersions program.baseUrl, gameId, (gameVersions) ->
+        fs.writeFileSync(outputFile, JSON.stringify(gameVersions), { encoding: "utf8"})
+        console.log("Wrote to #{outputFile}")
+
+program
   .command('deleteAllGames')
-  .description("delete all")
+  .description("Delete all games")
   .action (options) ->
     console.log("Deleting all games")
     login program.baseUrl, program.user, program.password, ->


Add failing spec from #739

Add failing spec from #739

--- spec/buffer-search-spec.coffee
+++ spec/buffer-search-spec.coffee
@@ -432,6 +432,24 @@
       expect(currentResultListener.mostRecentCall.args[0].getBufferRange()).toEqual markers[2].getBufferRange()
       expect(currentResultListener.mostRecentCall.args[0].isDestroyed()).toBe false

+    it "replaces the marked text with the given string that contains escaped escape sequence", ->
+      markers = markersListener.mostRecentCall.args[0]
+      markersListener.reset()
+
+      model.replace(markers, "new-text\\\\n")
+
+      expect(editor.getText()).toBe """
+        -----------
+        new-text\\n bbb ccc
+        ddd new-text\\n bbb
+        ccc ddd new-text\\n
+        -----------
+        new-text\\n bbb ccc
+        ddd new-text\\n bbb
+        ccc ddd new-text\\n
+        -----------
+      """
+
   describe ".prototype.resultsMarkerLayerForTextEditor(editor)", ->
     it "creates or retrieves the results marker layer for the given editor", ->
       layer1 = model.resultsMarkerLayerForTextEditor(editor)


Temporarily limit galaxy gallery results to Artsy partners

--- lib/galaxy.coffee
+++ lib/galaxy.coffee
@@ -5,4 +5,6 @@
     Accept: 'application/vnd.galaxy-public+json'

   url: (kind) ->
-    "#{GALAXY_URL}/#{kind}?token=#{GALAXY_PUBLISHABLE_TOKEN}"
+    url = "#{GALAXY_URL}/#{kind}?token=#{GALAXY_PUBLISHABLE_TOKEN}"
+    url += '&artsy_only=true' if kind is 'galleries' # Temporarily limit gallery results
+    url


Add assert for unindented line

Add assert for unindented line

--- spec/language-mode-spec.coffee
+++ spec/language-mode-spec.coffee
@@ -35,6 +35,10 @@
         buffer.setText('\tvar i;')
         languageMode.toggleLineCommentsForBufferRows(0, 0)
         expect(buffer.lineForRow(0)).toBe "\t// var i;"
+
+        buffer.setText('var i;')
+        languageMode.toggleLineCommentsForBufferRows(0, 0)
+        expect(buffer.lineForRow(0)).toBe "// var i;"

     describe "fold suggestion", ->
       describe ".doesBufferRowStartFold(bufferRow)", ->


Fix specs

Fix specs

--- spec/linter-pylama-spec.coffee
+++ spec/linter-pylama-spec.coffee
@@ -63,38 +63,37 @@
         err5 = "E302 expected 2 blank lines, found 1 [pep8]"
         err6 = "D101 Missing docstring in public class [pep257]"

-        expect(messages[0].text).toBe(err0)
-        expect(messages[0].range).toEqual([[0,0], [0,1]])
-        expect(messages[0].type).toBe('Warning')
-        expect(messages[0].filePath).toMatch(badPath)
+        expect(messages[0].excerpt).toBe(err0)
+        expect(messages[0].location.position).toEqual([[0,0], [0,1]])
+        expect(messages[0].severity).toBe('warning')
+        expect(messages[0].location.file).toMatch(badPath)

-        expect(messages[1].text).toBe(err1)
-        expect(messages[1].range).toEqual([[0,0], [0,1]])
-        expect(messages[1].type).toBe('Error')
-        expect(messages[1].filePath).toMatch(badPath)
+        expect(messages[1].excerpt).toBe(err1)
+        expect(messages[1].location.position).toEqual([[0,0], [0,1]])
+        expect(messages[1].severity).toBe('error')
+        expect(messages[1].location.file).toMatch(badPath)

-        expect(messages[2].text).toBe(err2)
-        expect(messages[2].range).toEqual([[2,1], [2,4]])
-        expect(messages[2].type).toBe('Error')
-        expect(messages[2].filePath).toMatch(badPath)
+        expect(messages[2].excerpt).toBe(err2)
+        expect(messages[2].location.position).toEqual([[2,1], [2,4]])
+        expect(messages[2].severity).toBe('error')
+        expect(messages[2].location.file).toMatch(badPath)

-        expect(messages[3].text).toBe(err3)
-        expect(messages[3].range).toEqual([[2,2], [2,3]])
-        expect(messages[3].type).toBe('Error')
-        expect(messages[3].filePath).toMatch(badPath)
+        expect(messages[3].excerpt).toBe(err3)
+        expect(messages[3].location.position).toEqual([[2,2], [2,3]])
+        expect(messages[3].severity).toBe('error')
+        expect(messages[3].location.file).toMatch(badPath)

-        expect(messages[4].text).toBe(err4)
-        expect(messages[4].range).toEqual([[2,3], [2,3]])
-        expect(messages[4].type).toBe('Error')
-        expect(messages[4].filePath).toMatch(badPath)
+        expect(messages[4].excerpt).toBe(err4)
+        expect(messages[4].location.position).toEqual([[2,3], [2,3]])
+        expect(messages[4].severity).toBe('error')
+        expect(messages[4].location.file).toMatch(badPath)

-        expect(messages[5].text).toBe(err5)
-        expect(messages[5].range).toEqual([[4,0], [4,5]])
-        expect(messages[5].type).toBe('Error')
-        expect(messages[5].filePath).toMatch(badPath)
+        expect(messages[5].excerpt).toBe(err5)
+        expect(messages[5].location.position).toEqual([[4,0], [4,5]])
+        expect(messages[5].severity).toBe('error')
+        expect(messages[5].location.file).toMatch(badPath)

-        expect(messages[6].text).toBe(err6)
-        expect(messages[6].range).toEqual([[4,0], [4,5]])
-        expect(messages[6].type).toBe('Warning')
-        expect(messages[6].filePath).toMatch(badPath)
-
+        expect(messages[6].excerpt).toBe(err6)
+        expect(messages[6].location.position).toEqual([[4,0], [4,5]])
+        expect(messages[6].severity).toBe('warning')
+        expect(messages[6].location.file).toMatch(badPath)


`Iterator::clearData()` fixed

`Iterator::clearData()` fixed

--- iterator.coffee
+++ iterator.coffee
@@ -25,8 +25,6 @@
 		unit = new File.Unit self, name, @bodyNode
 		@unit = unit.id
 		@bodyNode.parent = undefined
-
-		@usedUnits = []

 	unit: ''
 	storage: null
@@ -83,7 +81,7 @@
 	clearData: ->
 		expect(@data).toBe.object()

-		while length = @usedUnit.length
+		while length = @usedUnits.length
 			@popItem length - 1

 		@
@@ -149,6 +147,7 @@

 		clone.storage = utils.cloneDeep @storage
 		clone.array = null
+		clone.usedUnits = []

 		clone.updateItem = @updateItem.bind clone
 		clone.insertItem = @insertItem.bind clone


Fix specs

Fix specs

--- spec/results-model-spec.coffee
+++ spec/results-model-spec.coffee
@@ -12,7 +12,7 @@

   beforeEach ->
     window.rootView = new RootView()
-    project.setPath(path.join(__dirname, '..', 'fixtures'))
+    project.setPath(path.join(__dirname, 'fixtures'))
     rootView.openSync('sample.js')
     rootView.attachToDom()


Replace the original theme manager with new theme manager

--- spec/theme-manager-spec.coffee
+++ spec/theme-manager-spec.coffee
@@ -6,15 +6,17 @@
 Package = require '../src/package'

 describe "ThemeManager", ->
-  themeManager = null
+  [originalThemes, themeManager] = []
   resourcePath = atom.getLoadSettings().resourcePath
   configDirPath = atom.getConfigDirPath()

   beforeEach ->
-    themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath})
+    originalThemes = atom.themes
+    atom.themes = themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath})

   afterEach ->
     themeManager.deactivateThemes()
+    atom.themes = originalThemes

   describe "theme getters and setters", ->
     beforeEach ->
@@ -29,7 +31,7 @@
       themes = themeManager.getLoadedThemes()
       expect(themes.length).toBeGreaterThan(2)

-    fit 'getActiveThemes get all the active themes', ->
+    it 'getActiveThemes get all the active themes', ->
       waitsForPromise ->
         themeManager.activateThemes()

@@ -39,6 +41,7 @@

   describe "when the core.themes config value contains invalid entry", ->
     it "ignores theme", ->
+      spyOn(console, 'warn')
       atom.config.set 'core.themes', [
         'atom-light-ui'
         null
@@ -375,7 +378,7 @@

     it 'uses the default dark UI and syntax themes and logs a warning', ->
       activeThemeNames = themeManager.getActiveThemeNames()
-      expect(console.warn.callCount).toBe 2
+      # expect(console.warn.callCount).toBe 2
       expect(activeThemeNames.length).toBe(2)
       expect(activeThemeNames).toContain('atom-dark-ui')
       expect(activeThemeNames).toContain('atom-dark-syntax')


Stop calling `initialize` in `ViewRegistry` tests

--- spec/view-registry-spec.coffee
+++ spec/view-registry-spec.coffee
@@ -5,7 +5,6 @@

   beforeEach ->
     registry = new ViewRegistry
-    registry.initialize()

   afterEach ->
     registry.clearDocumentRequests()


Add prefix to mongoose logs.

Add prefix to mongoose logs.

--- src/beans/MongooseLogger.coffee
+++ src/beans/MongooseLogger.coffee
@@ -1,4 +1,4 @@
 util = require 'util'
 module.exports = (logger, depth = 2) ->
   (collection, method, query, doc, options) ->
-    logger.info 'mongoose', '"#{collection}.#{method}(#{util.inspect(query, depth: depth)}) - #{util.inspect(doc, depth: depth)} [#{options}]"
+    logger.info 'mongoose', "#{collection}.#{method}(#{util.inspect(query, depth: depth)}) - #{util.inspect(doc, depth: depth)} [#{options}]"


change to coffee existential operator

change to coffee existential operator

'a = a or b' -> 'a or a = b' -> 'a ?= b'

--- src/chaplin/models/model.coffee
+++ src/chaplin/models/model.coffee
@@ -42,7 +42,7 @@
         if value instanceof Model
           # Don’t change the original attribute, create a property
           # on the delegator which shadows the original attribute
-          delegator or delegator = utils.beget attributes
+          delegator ?= utils.beget attributes
           delegator[key] = if value is model or value in modelStack
             # Nullify circular references
             null
@@ -52,7 +52,7 @@
               value, value.getAttributes(), modelStack
             )
         else if value instanceof Backbone.Collection
-          delegator or delegator = utils.beget attributes
+          delegator ?= utils.beget attributes
           delegator[key] = for item in value.models
             serializeAttributes(
               item, item.getAttributes(), modelStack


Bug fix

Bug fix

--- site/app.coffee
+++ site/app.coffee
@@ -318,7 +318,10 @@
       ind = 'PM'
       if currentdate.getHours() < 12
         ind = 'AM'
-      @dataTimeline.html("As of "+WEEKDAYS[currentdate.getDay()]+", "+MONTHS[currentdate.getMonth()]+" "+currentdate.getDate()+", "+currentdate.getFullYear()+", "+currentdate.getHours()+":"+currentdate.getMinutes()+ind+" EDT (epi-week "+currentdate.getWeek()+")");
+      mins = currentdate.getMinutes()
+      if mins < 10
+        mins = "0" + mins
+      @dataTimeline.html("As of "+WEEKDAYS[currentdate.getDay()]+", "+MONTHS[currentdate.getMonth()]+" "+currentdate.getDate()+", "+currentdate.getFullYear()+", "+currentdate.getHours()+":"+mins+ind+" EDT (epi-week "+currentdate.getWeek()+")");
       callback = (epidata) =>
         @colors = {}
         @mapData = {}


Loader's extend function deals with function returning metadata.

--- lib/loader.coffee
+++ lib/loader.coffee
@@ -28,6 +28,11 @@

 extend= (base,dominant)->
   return dominant unless base
+  # A meta file module may be a function or an object.  If its a function,
+  # it will be invoked with the parent metadata as an argument and should return
+  # a metadata object.  The meta file can thus create metadata that is
+  # affected by the values in the parent metadata
+  dominant= dominant(base) if typeof dominant == "function"
   r= _.clone(base)
   for key, value of dominant
     unless value?
@@ -59,13 +64,7 @@
   descend(name)

 load= (name,superMeta,cachename)->
-    content = require(name)
-    # A meta file module may be a function or an object.  If its a function,
-    # it will be invoked with the parent metadata as an argument and should return
-    # a metadata object.  The meta file can thus create metadata that is
-    # affected by the values in the parent metadata
-    content= content(superMeta) if typeof content == "function"
-    extend(superMeta, content)
+  extend(superMeta, require(name))

 loadTree= (dirname,superMeta) ->
   modules= []


Fix https://github.com/yudachi/poi/issues/91

--- views/components/ship/index.cjsx
+++ views/components/ship/index.cjsx
@@ -238,11 +238,11 @@
         messages: messages
         countdown: countdown
     updateCountdown: ->
-      {countdown} = @state
+      {countdown, states} = @state
       for i in [0..3]
         if countdown[i] > 0
           countdown[i] -= 1
-          if countdown[i] is 0
+          if countdown[i] is 0 and states[i] < 4
             notify "#{@state.names[i]} 疲劳回复完成", {icon: join(ROOT, 'assets', 'img', 'operation', 'sortie.png')}
       @setState
         countdown: countdown


Remove unused method

Remove unused method

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -71,10 +71,6 @@

   replaceDocument: edit "replaceDocument", (document) ->
     @blockList = document.blockList.copy()
-
-  insertPlaceholderBlockAtLocationRange: edit "insertPlaceholderBlockAtLocationRange", (locationRange) ->
-    document = new Trix.Document [Trix.Block.createPlaceholder()]
-    @insertDocumentAtLocationRange(document, locationRange)

   insertTextAtLocationRange: edit "insertTextAtLocationRange", (text, locationRange) ->
     @removeTextAtLocationRange(locationRange)


Add route for article

Add route for article

--- js/outer.coffee
+++ js/outer.coffee
@@ -61,7 +61,10 @@
   @get "/", (context) -> home.display()
   @get "/about.*", (context) -> about.display()
   @get "/privacy.*", (context) -> privacy.display()
-  @get "/docs.*", (context) -> docs.display()
+  @get "/docs", (context) -> docs.display()
+
+  # Documentation
+  @get "/docs/article.*", -> article.display()
 )

 # Global polyfills


fixes typo in outer.js

fixes typo in outer.js

--- js/outer.coffee
+++ js/outer.coffee
@@ -196,5 +196,6 @@
 $ ->
   circle.run window.location.pathname.replace(/\/$/, '')
   setTimeout(  # give KM a short window to run
-    -> window.too_late_for_kissmetrics = true
+    ->
+      window.too_late_for_kissmetrics = true
     , 20)


Ignore colon expressions if not among regex, title, body. Fix #224

--- lib/search.coffee
+++ lib/search.coffee
@@ -35,7 +35,7 @@
   while (token = tokenRe.exec search)?
     continue if token[1]  ## ignore whitespace tokens
     ## Check for negation and/or leading commands followed by colon
-    colon = /^-?(?:[a-zA-Z]+:)*/.exec token[0]
+    colon = /^-?(?:(?:regex|title|body):)*/.exec token[0]
     colon = colon[0]
     ## Remove quotes (which are just used for avoiding space parsing).
     if token[4]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/function_extensions.coffee
+++ test/function_extensions.coffee
@@ -23,7 +23,7 @@

 test "#once", ->
   onceScore = ->
-    ( ->
+    (  ->
     ).once()

 test "#returning", ->


Add version info when checking update

Add version info when checking update

--- lib/update.coffee
+++ lib/update.coffee
@@ -1,14 +1,9 @@
 Promise = require 'bluebird'
 async = Promise.coroutine
-path = Promise.promisifyAll require 'path-extra'
-fs = Promise.promisifyAll require 'fs-extra'
 request = Promise.promisifyAll require 'request'
 requestAsync = Promise.promisify request
-{fork} = require 'child_process'
-AdmZip = require 'adm-zip'
-app = require 'app'

-{SERVER_HOSTNAME, ROOT} = global
+{SERVER_HOSTNAME, POI_VERSION, ROOT} = global
 {log, warn, error} = require './utils'

 module.exports =
@@ -17,6 +12,8 @@
       [response, body] = yield requestAsync "http://#{SERVER_HOSTNAME}/update/latest.json",
         method: 'GET'
         json: true
+        headers:
+          'User-Agent': "poi v#{POI_VERSION}"
       if response.statusCode == 200
         callback body
       else


Remove defunct variables.

Remove defunct variables.

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -65,7 +65,7 @@
     @yield()

   yield: ->
-    _.tap yield(), => Worker.current =this
+    _.tap yield(), => Worker.current = this

   # This is a bit of syntactic sugar. It's the equivalent of:
   #
@@ -117,16 +117,9 @@

   # Reset information about this run, including:
   #
-  # * `@stage`: how many calls to `@for_reals` we've seen
-  # * `@deps`: a list of new dependencies
   # * `@emitted`: whether `@emit` has been called.
-  # * `@search`: the key search currently requested
   clear: ->
-    @stage = 0
-    @key_index = 0
-    @deps = []
     @emitted = false
-    @search = null
     Worker.clear_callback?.apply this

   # Mark that a fatal exception occurred
@@ -151,46 +144,6 @@
     @queue.finish @key
     @emit @key, (result ? null) unless @emitted

-<<<<<<< HEAD
-  # Compare the provided values against our current dependencies.
-  # Missing dependencies are returned in an array.
-  check_values: (arr) ->
-    bad = []
-    for dep, i in @deps
-      try
-        @cache[dep] = JSON.parse arr[i]
-      catch e
-        throw new Error "Cannot parse JSON for #{dep}: #{arr[i]}"
-      bad.push dep unless @cache[dep]?
-    bad
-
-  # The first step in resolving dependencies from a `@for_reals` is
-  # to record that there's one more of them to get through, then to check
-  # the dependencies.
-  resolve: ->
-    if @search
-      @db.keys @search, (e, keys) =>
-        @saved_keys[@search] = keys
-        @run()
-    else
-      @last_stage++
-      @get_deps()
-
-  # Ask redis to provide values for our dependencies. If any are missing,
-  # send a request to the dispatcher; otherwise, resume trying to run the
-  # main function.
-  get_deps: (force = false) ->
-    throw "No dependencies to get: #{@key}" unless @deps.length
-    @db.mget @deps, (err, arr) =>
-      return @error err if err
-      bad = @check_values arr
-      if bad.length && !force
-        @request_missing bad
-      else
-        @run()
-
-=======
->>>>>>> Testing fibers
   # Ask the dispatcher to providethe given keys by publishing on the
   # `requests` channel. Then block-wait to be signalled by a response
   # on a resume key. Once we get that response, try again to fetch the


Abort!

Abort!

--- ext/user_heartbeat.coffee
+++ ext/user_heartbeat.coffee
@@ -8,8 +8,7 @@
   if clientId && userId && message.channel.match(/meta\/connect/ig)
     console.log "User ID: #{userId}"
     startHeartbeat(userId,clientId)
-
-  if clientId && message.channel.match(/meta\/subscribe/ig)
+  else if clientId && message.channel.match(/meta\/subscribe/ig)
     privateChannelMatch = /^\/users\/(.{24})\/private$/ig.exec(message.subscription)
     if privateChannelMatch
       userId = privateChannelMatch[1]


Fix blobs to not to merge faster if there are many blobs

--- src/Blob.coffee
+++ src/Blob.coffee
@@ -18,13 +18,13 @@
   [otherPos, otherBlob] = [other.c('pos'), other.c('blob')]
   # Run away from the other
   direction = Math.atan2 entPos.y - otherPos.y, entPos.x - otherPos.x
-  vel = 6
+  vel = 30
   velX = vel * Math.cos direction
   velY = vel * Math.sin direction
-  entBlob.velX += velX
-  entBlob.velY += velY
-  otherBlob.velX -= velX
-  otherBlob.velY -= velY
+  entBlob.velX += (velX-entBlob.velX)/10
+  entBlob.velY += (velY-entBlob.velY)/10
+  otherBlob.velX += (-velX-otherBlob.velX)/10
+  otherBlob.velY += (-velY-otherBlob.velY)/10

 BlobSystem =
   priority: 1500
@@ -32,6 +32,10 @@
     @engine = engine
     @entities = engine.e 'blob', 'pos'
   update: (delta) ->
+    for group in groups
+      group -= delta
+      if group < 0
+        group = null
     # Create QuadTree, TODO should not use hard-coding
     quad = new QuadTree -1000, -1000, 1000*2, 1000*2,
       maxchildren: 4
@@ -56,10 +60,6 @@
       if entBlob.invincible
         entBlob.invincible -= delta
         entBlob.invincible = null if entBlob.invincible < 0
-      if groups[entBlob.group]
-        groups[entBlob.group] -= delta
-        if groups[entBlob.group] < 0
-          groups[entBlob.group] = null
       entObj =
         x: entPos.x - entPos.radius
         y: entPos.y - entPos.radius


score is now updated, topic is seen when set but may not be ideal

--- src/Game.coffee
+++ src/Game.coffee
@@ -18,9 +18,18 @@
       evt.preventDefault()
       @sendWord $('#word').val()

+    @socket = @setupSocket()
+    @socket.on 'new topic', (data) =>
+      @topic = data
+      $('#info').fadeOut 400, =>
+          $('#info').html('Topic set to: ' + @topic).fadeIn(500)
+      $('#wordListInfo').hide()
+      $('#wordListInfo').html('Your words will appear here:')
+      $('#wordListInfo').fadeIn(500)
+
+
   joinGame: (playerName) ->
     @player = new Player(@viz.two, playerName, "#00FF00")
-    @socket = @setupSocket()
     $('#info').fadeOut 400, ->
       $('#info').html(' Hi '+playerName+', important information will appear here.').fadeIn(500)
     $( "#nameForm" ).fadeOut 400, ->
@@ -44,21 +53,13 @@
       #TODO state winners
       $('#word').prop('disabled', true)

-    @socket.on 'new topic', (data) =>
-      @topic = data
-      $('#info').fadeOut 400, =>
-          $('#info').html('Topic set to: ' + @topic).fadeIn(500)
-      $('#wordListInfo').hide()
-      $('#wordListInfo').html('Your words will appear here:')
-      $('#wordListInfo').fadeIn(500)
-
     @socket.on 'snap', (data) =>
       score = data.d_score
       @player.addPoints score
       @player.snap(@players[data.player])
       $('#wordList').html()
       $('#wordList').append(''+data.word+' ('+data.player+')
')
-      $('#score').html(score)
+      $('#score').html(@player.snaps)

   sendWord: (word) ->
     @socket.emit 'new word', word
@@ -67,11 +68,12 @@
     $("#wordList").animate({ scrollTop: $("#wordList").prop("scrollHeight")}, 100)

   setupSocket: ->
-    if window.location.search.indexOf("local") != -1
-        return io.connect('http://localhost:8080', {secure: false})
-    else if window.location.search.indexOf("dev") != -1
-        return io.connect('https://snap-backend-dev.herokuapp.com:443', {secure: true})
-    else
-        return io.connect('https://snapgame.herokuapp.com:443', {secure: true})
+    return io.connect('http://localhost:8080', {secure: false})
+  #if window.location.search.indexOf("local") != -1
+  #      return io.connect('http://localhost:8080', {secure: false})
+  #  else if window.location.search.indexOf("dev") != -1
+  #      return io.connect('https://snap-backend-dev.herokuapp.com:443', {secure: true})
+  #  else
+  #      return io.connect('https://snapgame.herokuapp.com:443', {secure: true})

 module.exports = Game


introducing new raw, command escaping

introducing new raw, command escaping

--- src/MKTS.coffee
+++ src/MKTS.coffee
@@ -994,6 +994,8 @@
     .pipe @$_flatten_tokens                 state
     .pipe @$_reinject_html_blocks           state
     .pipe @$_rewrite_markdownit_tokens      state
+    .pipe @$XXX_expand_commands             state
+    .pipe @$XXX_unescape_raw_spans          state
     # .pipe @$_replace_text                   state, @_unescape_command_fences_A
     # .pipe @$_preprocess_commands            state
     # .pipe @$_replace_text                   state, @_unescape_command_fences_B
@@ -1006,7 +1008,7 @@
     ### TAINT what to do with useful data appearing environment? ###
     ### TAINT environment becomes important for footnotes ###
     environment = {}
-    md_source   = MKTS.XXX_escape_raw_spans md_source
+    md_source   = @XXX_escape_raw_spans md_source
     tokens      = md_parser.parse md_source, environment
     # @set_meta R, 'environment', environment
     confluence.write token for token in tokens


fix view so new content is editable

fix view so new content is editable

and removed explicit use of internal model.attributes

--- scripts/views/workspace/content/aloha-edit.coffee
+++ scripts/views/workspace/content/aloha-edit.coffee
@@ -17,10 +17,10 @@
       return {isLoaded: @isLoaded}

     initialize: () ->
-      @isLoaded = false
+      @isLoaded = @model.isNew()

       # images only have to be loaded the first time
-      if @model.attributes.body?.length
+      if @model.get(@modelKey)?.length
         @imagesLoaded = (new $.Deferred()).resolve()
       else
         @imagesLoaded = new $.Deferred()
@@ -34,14 +34,14 @@
         @imagesLoaded.resolve()

       @listenTo @model, "change:#{@modelKey}", =>
-        @contentLoaded.resolve() if @model.attributes.body.length
+        @contentLoaded.resolve() if @model.get(@modelKey)?.length

       # if content is already present change will never fire
       # so check that and conditionally finish the content loading as well
       @modelLoaded.done =>
-        @contentLoaded.resolve() if @model.attributes.body.length
-
-      # this is the trigger for actually showing content and enabling editing
+        @contentLoaded.resolve() if @model.get(@modelKey)?.length
+
+      # this is the trigger for actually showing content and enabling editing
       $.when(@imagesLoaded, @modelLoaded, @contentLoaded, @initalRender).done =>
         @isLoaded = true
         @render()
@@ -53,31 +53,31 @@

     onRender: () ->
       # update model after the user has stopped making changes
-
+
       if @isLoaded
         updateModel = =>
           alohaId = @$el.attr('id')
           alohaEditable = Aloha.getEditableById(alohaId)
-
+
           if alohaEditable
             editableBody = alohaEditable.getContents()
             editableBody = editableBody.trim() # Trim for idempotence
             # Change the contents but do not update the Aloha editable area
             @model.set(@modelKey, editableBody, {internalAlohaUpdate: true})
-
+
         @saveInterval = setInterval(updateModel, AUTOSAVE_INTERVAL) if not @saveInterval
-
+
         # Once Aloha has finished loading enable
         @$el.addClass('disabled')
-
+
         Aloha.ready =>
           @$el.addClass('aloha-root-editable')
           @$el.mahalo?()
           @$el.aloha()
-
+
           # Wait until Aloha is started before loading MathJax.
           MathJax?.Hub.Configured()
-
+
           # reenable everything
           @$el.removeClass('disabled')


FIX: Killed the args parameter for now

FIX: Killed the args parameter for now

--- src/Moon.coffee
+++ src/Moon.coffee
@@ -1,9 +1,9 @@
 do (window, document) ->
     # Main object of MoonJS
-    Moon = (target, args) ->
+    Moon = (target) ->
         # this function acts like a 'enhanced' init
         if window == this
-            return new Moon(target, args)
+            return new Moon(target)

         # Moon collection
         this._collection = Moon.fn._getMoonCollection(target)


Better constantize

Better constantize

--- test/core_object.coffee
+++ test/core_object.coffee
@@ -66,5 +66,8 @@

     test2: "cool"

-
+  o = Core
+    test: "my_val"
+
+  o
 module undefined


Bind LinkHints, Vomnibar and Marks commands appropriately

--- content_scripts/mode_normal.coffee
+++ content_scripts/mode_normal.coffee
@@ -179,28 +179,28 @@

 if LinkHints?
   extend NormalModeCommands,
-    "LinkHints.activateMode": LinkHints.activateMode
-    "LinkHints.activateModeToOpenInNewTab": LinkHints.activateModeToOpenInNewTab
-    "LinkHints.activateModeToOpenInNewForegroundTab": LinkHints.activateModeToOpenInNewForegroundTab
-    "LinkHints.activateModeWithQueue": LinkHints.activateModeWithQueue
-    "LinkHints.activateModeToOpenIncognito": LinkHints.activateModeToOpenIncognito
-    "LinkHints.activateModeToDownloadLink": LinkHints.activateModeToDownloadLink
-    "LinkHints.activateModeToCopyLinkUrl": LinkHints.activateModeToCopyLinkUrl
+    "LinkHints.activateMode": LinkHints.activateMode.bind LinkHints
+    "LinkHints.activateModeToOpenInNewTab": LinkHints.activateModeToOpenInNewTab.bind LinkHints
+    "LinkHints.activateModeToOpenInNewForegroundTab": LinkHints.activateModeToOpenInNewForegroundTab.bind LinkHints
+    "LinkHints.activateModeWithQueue": LinkHints.activateModeWithQueue.bind LinkHints
+    "LinkHints.activateModeToOpenIncognito": LinkHints.activateModeToOpenIncognito.bind LinkHints
+    "LinkHints.activateModeToDownloadLink": LinkHints.activateModeToDownloadLink.bind LinkHints
+    "LinkHints.activateModeToCopyLinkUrl": LinkHints.activateModeToCopyLinkUrl.bind LinkHints

 if Vomnibar?
   extend NormalModeCommands,
-    "Vomnibar.activate": Vomnibar.activate
-    "Vomnibar.activateInNewTab": Vomnibar.activateInNewTab
-    "Vomnibar.activateTabSelection": Vomnibar.activateTabSelection
-    "Vomnibar.activateBookmarks": Vomnibar.activateBookmarks
-    "Vomnibar.activateBookmarksInNewTab": Vomnibar.activateBookmarksInNewTab
-    "Vomnibar.activateEditUrl": Vomnibar.activateEditUrl
-    "Vomnibar.activateEditUrlInNewTab": Vomnibar.activateEditUrlInNewTab
+    "Vomnibar.activate": Vomnibar.activate.bind Vomnibar
+    "Vomnibar.activateInNewTab": Vomnibar.activateInNewTab.bind Vomnibar
+    "Vomnibar.activateTabSelection": Vomnibar.activateTabSelection.bind Vomnibar
+    "Vomnibar.activateBookmarks": Vomnibar.activateBookmarks.bind Vomnibar
+    "Vomnibar.activateBookmarksInNewTab": Vomnibar.activateBookmarksInNewTab.bind Vomnibar
+    "Vomnibar.activateEditUrl": Vomnibar.activateEditUrl.bind Vomnibar
+    "Vomnibar.activateEditUrlInNewTab": Vomnibar.activateEditUrlInNewTab.bind Vomnibar

 if Marks?
   extend NormalModeCommands,
-    "Marks.activateCreateMode": Marks.activateCreateMode
-    "Marks.activateGotoMode": Marks.activateGotoMode
+    "Marks.activateCreateMode": Marks.activateCreateMode.bind Marks
+    "Marks.activateGotoMode": Marks.activateGotoMode.bind Marks

 # The types in <input type="..."> that we consider for focusInput command. Right now this is recalculated in
 # each content script. Alternatively we could calculate it once in the background page and use a request to


Fix starting mode when loading user profile page

Splitting empty string results in single element array of empty string

--- resources/assets/coffee/_classes/profile-page-hash.coffee
+++ resources/assets/coffee/_classes/profile-page-hash.coffee
@@ -22,7 +22,9 @@

   @parse: (hash) =>
     hash = hash.slice 1
-    if @noMode(hash)
+    if hash.length == 0
+      {}
+    else if @noMode(hash)
       page: hash
     else
       split = hash.split '/'


Allow custom fields in message objects.

Allow custom fields in message objects.

--- lib/chat.coffee
+++ lib/chat.coffee
@@ -95,8 +95,7 @@
 checkMessage = (msg) ->
   passed = check.object msg
   unless passed then return false
-  passed = check.map msg, { textMessage : check.string }
-  if passed then return Object.keys(msg).length == 1
+  return check.map msg, { textMessage : check.string }

 # @private
 # @nodoc
@@ -855,15 +854,15 @@
       return cb error
     @chatState.getOnlineUser toUserName, withEH cb, (toUser) =>
       @chatState.getOnlineUser @username, withEH cb, (fromUser) =>
-        msg = processMessage @username, msg
-        toUser.message @username, msg, withEH cb, =>
+        pmsg = processMessage @username, msg
+        toUser.message @username, pmsg, withEH cb, =>
           fromUser.userState.socketsGetAll withEH cb, (sockets) =>
             for sid in sockets
               if sid != id
-                @send sid, 'directMessageEcho', toUserName, msg
+                @send sid, 'directMessageEcho', toUserName, pmsg
             toUser.userState.socketsGetAll withEH cb, (sockets) =>
               for sid in sockets
-                @send sid, 'directMessage', @username, msg
+                @send sid, 'directMessage', @username, pmsg
               cb null, msg

   # @private
@@ -995,9 +994,9 @@
   # @nodoc
   roomMessage : (roomName, msg, cb) ->
     @withRoom roomName, withEH cb, (room) =>
-      msg = processMessage @username, msg
-      room.message @username, msg, withEH cb, =>
-        @send roomName, 'roomMessage', roomName, @username, msg
+      pmsg = processMessage @username, msg
+      room.message @username, pmsg, withEH cb, =>
+        @send roomName, 'roomMessage', roomName, @username, pmsg
         cb null, msg

   # @private


Correct login page test

Correct login page test

--- server/test/integration/sessions.coffee
+++ server/test/integration/sessions.coffee
@@ -16,7 +16,7 @@
     assert.equal res.statusCode, 200

     assert.match body, new RegExp(".*form.*")
-    assert.match body, new RegExp(".*Email.*")
+    assert.match body, new RegExp(".*Username.*")
     assert.match body, new RegExp(".*Password.*")

     done()


Require $$ via space-pen-extensions

Require $$ via space-pen-extensions

--- src/atom.coffee
+++ src/atom.coffee
@@ -5,13 +5,12 @@
 Emitter::subscriptionCount = (args...) -> @getSubscriptionCount(args...)

 fsUtils = require './fs-utils'
-{$} = require './space-pen-extensions'
+{$, $$} = require './space-pen-extensions'
 _ = require 'underscore-plus'
 Package = require './package'
 ipc = require 'ipc'
 remote = require 'remote'
 shell = require 'shell'
-{$$} = require 'space-pen'
 crypto = require 'crypto'
 path = require 'path'
 os = require 'os'


Don't extend Model in 1.0 mode

Don't extend Model in 1.0 mode

--- src/atom.coffee
+++ src/atom.coffee
@@ -16,11 +16,13 @@
 WindowEventHandler = require './window-event-handler'
 StylesElement = require './styles-element'

+SuperClass = if includeDeprecations then Model else Object
+
 # Essential: Atom global for dealing with packages, themes, menus, and the window.
 #
 # An instance of this class is always available as the `atom` global.
 module.exports =
-class Atom extends Model
+class Atom extends SuperClass
   @version: 1  # Increment this when the serialization format changes

   # Load or create the Atom environment in the given mode.
@@ -230,7 +232,7 @@
         @openDevTools()
         @executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')

-      @emit 'uncaught-error', arguments...
+      @emit 'uncaught-error', arguments... if includeDeprecations
       @emitter.emit 'did-throw-error', {message, url, line, column, originalError}

     @disposables?.dispose()


Troubleshoot: checkVm is added for checking user's vm status

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -69,11 +69,19 @@

   registerItem : (name, cb) ->
     @items[name] = new HealthChecker {}, cb
+    @registerItem "vm", timeout: 10000, speedCheck: no, checkVm

   getItems: ->
     @items

+
+  checkVm = (callback) ->
+    {vmController} = KD.singletons
+    # probably we will need to check all vms
+    vmController.once "vm.state.info", ({alias, state}) =>
+      @status = "fail"  if state.state isnt "RUNNING"
+      callback null

   getFailureFeedback: ->
     result = ""


sidebar: remove settings icon vm item view

--- client/Social/Activity/sidebar/vmitem.coffee
+++ client/Social/Activity/sidebar/vmitem.coffee
@@ -5,22 +5,14 @@
   constructor: (options = {}, data) ->

     options.attributes = {}
+
     super

     @machine = new Machine {machine: data}
-
-    position =
-      top    : 0
-      left   : 0
-
-    @settings = new KDButtonView
-      partial : 'Settings'
-      callback: => new MachineSettingsPopup {position}, @machine

   pistachio: ->
     """
       #{@machine.slug}
-      {{> @settings }}
     """


Cosmetic changes

Cosmetic changes

--- client/test/lib/teams/teamslikeunlike.coffee
+++ client/test/lib/teams/teamslikeunlike.coffee
@@ -49,11 +49,11 @@
       .waitForElementVisible  menuButton,   20000
       .click                  menuButton
       .waitForElementVisible  editButton,   20000
-      .click  				        editButton
+      .click  		            editButton
       .waitForElementVisible  chatInput,    20000
       .clearValue             chatInput
       .setValue               chatInput,    editedmessage
-      .setValue				        chatInput,    browser.Keys.ENTER
+      .setValue		            chatInput,    browser.Keys.ENTER
       .waitForElementVisible  editedText,   20000
       .assert.containsText    editedText,   editedmessage
       .end()


prepopulate hint value

prepopulate hint value

--- jsapp/xlform/src/view.rowDetail.coffee
+++ jsapp/xlform/src/view.rowDetail.coffee
@@ -169,6 +169,7 @@
     html: -> false
     insertInDOM: (rowView) ->
       hintEl = rowView.$hint
+      hintEl.value = @model.get("value")
       return @
     afterRender: ->
       @listenForInputChange({


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -38,7 +38,7 @@

   p = o.sendOrApply "position"

-  equals p.x
+  equals p.x, o.I.x

 test "[event] create", 2, ->
   GameObject


Remove an old upgrade codepath for version 1.21.

We're on 1.37 now so I think we're in the clear.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -588,25 +588,12 @@
 init = ->
   Commands.clearKeyMappingsAndSetDefaults()

-  if (Settings.has("keyMappings"))
+  if Settings.has("keyMappings")
     Commands.parseCustomKeyMappings(Settings.get("keyMappings"))
-
-  # In version 1.22, we changed the mapping for "d" and "u" to be scroll page down/up instead of close
-  # and restore tab. For existing users, we want to preserve existing behavior for them by adding some
-  # custom key mappings on their behalf.
-  if (Settings.get("previousVersion") == "1.21")
-    customKeyMappings = Settings.get("keyMappings") || ""
-    if ((Commands.keyToCommandRegistry["d"] || {}).command == "scrollPageDown")
-      customKeyMappings += "\nmap d removeTab"
-    if ((Commands.keyToCommandRegistry["u"] || {}).command == "scrollPageUp")
-      customKeyMappings += "\nmap u restoreTab"
-    if (customKeyMappings != "")
-      Settings.set("keyMappings", customKeyMappings)
-      Commands.parseCustomKeyMappings(customKeyMappings)

   populateValidFirstKeys()
   populateSingleKeyCommands()
-  if (shouldShowUpgradeMessage())
+  if shouldShowUpgradeMessage()
     sendRequestToAllTabs({ name: "showUpgradeNotification", version: currentVersion })

   # Ensure that openTabs is populated when Vimium is installed.
@@ -621,7 +608,5 @@

 init()

-#
 # Convenience function for development use.
-#
 runTests = -> open(chrome.extension.getURL('test_harnesses/automated.html'))


Fixes #1838

Fixes #1838


belts and braces, but I feel this should not really be here?

--- client/controllers/logoutController.coffee
+++ client/controllers/logoutController.coffee
@@ -7,5 +7,6 @@
   .success ->
     $rootScope.me = {}
     $rootScope.loggedIn = false
-
+    $rootScope.isAdmin = false
+    $rootScope.isRestricted = false
 ]


Remove grunt

Remove grunt

--- services/notifications/Gruntfile.coffee
+++ services/notifications/Gruntfile.coffee
@@ -1,54 +0,0 @@
-module.exports = (grunt) ->
-	grunt.initConfig
-		coffee:
-			app_src:
-				expand: true,
-				cwd: "app/coffee"
-				src: ['**/*.coffee'],
-				dest: 'app/js/',
-				ext: '.js'
-
-			app:
-				src: "app.coffee"
-				dest: "app.js"
-
-			unit_tests:
-				expand: true
-				cwd:  "test/unit/coffee"
-				src: ["**/*.coffee"]
-				dest: "test/unit/js/"
-				ext:  ".js"
-
-		clean:
-			app: ["app/js/"]
-			unit_tests: ["test/unit/js"]
-
-		execute:
-			app:
-				src: "app.js"
-
-		mochaTest:
-			unit:
-				options:
-					reporter: grunt.option('reporter') or 'spec'
-					grep: grunt.option("grep")
-
-				src: ["test/unit/js/**/*.js"]
-
-	grunt.loadNpmTasks 'grunt-contrib-coffee'
-	grunt.loadNpmTasks 'grunt-contrib-clean'
-	grunt.loadNpmTasks 'grunt-mocha-test'
-	grunt.loadNpmTasks 'grunt-execute'
-	grunt.loadNpmTasks 'grunt-bunyan'
-
-	grunt.registerTask 'compile:app', ['clean:app', 'coffee:app', 'coffee:app_src']
-	grunt.registerTask 'run',         ['compile:app', 'bunyan', 'execute']
-
-	grunt.registerTask 'compile:unit_tests', ['clean:unit_tests', 'coffee:unit_tests']
-	grunt.registerTask 'test:unit',          ['compile:app', 'compile:unit_tests', 'mochaTest:unit']
-
-	grunt.registerTask 'install', 'compile:app'
-
-	grunt.registerTask 'default', ['run']
-
-


add error handler to each stage of download pipeline

--- services/track-changes/app/coffee/MongoAWS.coffee
+++ services/track-changes/app/coffee/MongoAWS.coffee
@@ -41,7 +41,11 @@
 			.on 'finish', () ->
 				return callback(null)

-	unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) ->
+	unArchiveDocHistory: (project_id, doc_id, _callback = (error) ->) ->
+
+		callback = (args...) ->
+			_callback(args...)
+			_callback = () ->

 		AWS.config.update {
 			accessKeyId: settings.filestore.s3.key
@@ -61,6 +65,8 @@
 		download
 			.on 'open', (obj) ->
 				return 1
+			.on 'error', (err) ->
+				callback(err)
 			.pipe lineStream
 			.on 'data', (line) ->
 				if line.length > 2


removed alert

removed alert

--- src/Acme/AssetBundle/Resources/javascripts/grading.coffee
+++ src/Acme/AssetBundle/Resources/javascripts/grading.coffee
@@ -8,4 +8,4 @@

 eldest = if 24 > 21 then "Liz" else "Ike"

-alert(grade({"excellentWork":true}));
+grade({"excellentWork":true});


Fix the last bug. Need to refactor later to add listener/callback for cleaner code

--- admin/static/coffee/namespaces/namespace.coffee
+++ admin/static/coffee/namespaces/namespace.coffee
@@ -86,7 +86,7 @@
         rebalance_shards: (event) =>
             event.preventDefault()
             confirmation_modal = new UIComponents.ConfirmationDialogModal
-            confirmation_modal.render("Are you sure you want to rebalance the shards for the namespace "+@model.get('name')+"? This operation might take some time",
+            confirmation_modal.render("Are you sure you want to rebalance the shards for the namespace "+@model.get('name')+"? This operation might take some time.",
                 "",
                 {},
                 undefined)
@@ -294,17 +294,22 @@

                     data_pie = [data_total-data_in_memory, data_in_memory]

-                    # Remove transition for the time being. We have to use transition with opacity only the first time
-                    # For update, we should just move/extend pieces, too much work for now
-                    #@.$('.loading_text-svg').fadeOut '600', -> $(@).remove()
-                    @.$('.loading_text-pie_chart').css 'display', 'none'
-
-                    arc = d3.svg.arc().innerRadius(0).outerRadius(r)
-                    svg = d3.select('.pie_chart-data_in_memory').attr('width', width).attr('height', height).append('svg:g').attr('transform', 'translate('+width/2+', '+height/2+')')
-                    arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc)
-
-                    # No transition for now
-                    #arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc).style('opacity', 0).transition().duration(600).style('opacity', 1)
+
+                    if $('.pie_chart-data_in_memory').length is 1 #Check dom tree ready for d3. TODO: Replace this hack.
+                        # Remove transition for the time being. We have to use transition with opacity only the first time
+                        # For update, we should just move/extend pieces, too much work for now
+                        #@.$('.loading_text-svg').fadeOut '600', -> $(@).remove()
+                        @.$('.loading_text-pie_chart').css 'display', 'none'
+
+                        arc = d3.svg.arc().innerRadius(0).outerRadius(r)
+                        svg = d3.select('.pie_chart-data_in_memory').attr('width', width).attr('height', height).append('svg:g').attr('transform', 'translate('+width/2+', '+height/2+')')
+                        arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc)
+
+                        # No transition for now
+                        #arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc).style('opacity', 0).transition().duration(600).style('opacity', 1)
+                    else
+                        setTimeout(@render_data_memory, 1000)
+
             return @ # Just to make sure that d3 doesn't return false

         render_data_repartition: (force_render) =>
@@ -337,15 +342,21 @@
                 if not @json_repartition[key]? or @json_repartition[key] != json[key]
                     need_update = true
                     break
-
-            if need_update or force_render is true or @.$('.data-repartition-diagram').children().length is 1
-                @json_repartition = json
+
+            if need_update or force_render is true or @.$('.data_repartition-diagram').children().length is 1
+
                 @.$('.data_repartition-container').html @data_repartition_template @json_repartition
+
+                if $('.data_repartition-diagram').length>0 #We need the dom tree to be fully available for d3
+                    #TODO Replace this hack with a proper callback/listener
+                    @.$('.loading_text-diagram').css 'display', 'none'
+                    @json_repartition = json
+                else
+                    setTimeout(@render_data_repartition, 1000)
+

                 # Draw histogram
                 if json.max_keys? and not _.isNaN json.max_keys and shards.length isnt 0
-
-                    @.$('.loading_text-diagram').css 'display', 'none'

                     if json.numerous_shards? and json.numerous_shards
                         svg_width = 700
@@ -374,6 +385,7 @@
                         .attr('width', width)
                         .attr( 'height', (d) -> return y(d.num_keys))
                         .attr( 'title', (d) -> return 'Shard:'+d.boundaries+'<br />'+d.num_keys+' keys')
+

                     arrow_width = 4
                     arrow_length = 7


using listSync to list draft files

using listSync to list draft files

--- lib/hexo.coffee
+++ lib/hexo.coffee
@@ -28,16 +28,12 @@
       unless hexoPath
         return @display projectPathError

-      fs.list path.join(hexoPath, '/source/_drafts'), (err, paths) =>
-        if err
-          return @display message: err.message, className: 'stderr'
+      paths = fs.listSync path.join(hexoPath, '/source/_drafts'), ['md', 'markdown']
+      unless paths and paths.length
+        return @display message: 'There is no draft.', className: 'warning'

-        paths = fs.filterExtensions paths, ['md', 'markdown']
-        unless paths.length
-          return @display message: 'There is no draft.', className: 'stdout'
-
-        @createDraftPublishView().setItems paths
-        @draftPublishView.toggle()
+      @createDraftPublishView().setItems paths
+      @draftPublishView.toggle()

     atom.workspaceView.on 'hexo:exec', (event, {cmd, extraArgs} = {}) =>
       @executeCommand cmd, extraArgs


vimeoShellSpec: dry up code with wider beforeEach

also, clean up memory and DOM with afterEach. in 'should look good'
test, stub afterEach destruction to leave playerView rendered in DOM

--- coffee/test/shells/vimeo.shell.spec.coffee
+++ coffee/test/shells/vimeo.shell.spec.coffee
@@ -140,18 +140,26 @@
     # tests pass when run with `grunt server`, they fail when run with
     # `grunt test`
     xdescribe 'VimeoShell.PlayerView', ->
+      pv = undefined
+      $hiddenPlayer = undefined
+
+      beforeEach ->
+        pv = new PlayerView viewOptions()
+
+        # setup DOM
+        acorn.util.appendCss()
+        $hiddenPlayer = $('<div>').addClass('acorn-player hidden').width(600)
+            .height(400).appendTo('body')
+
+      afterEach ->
+        pv.destroy()
+        $hiddenPlayer.remove()

       it 'should load and ready a vimeo player on render', ->
-        pv = new PlayerView viewOptions()
         setPlayerListener = false
         playerReady = false
         pvReady = false
         pv.on 'PlayerView:Ready', -> pvReady = true
-
-        # setup DOM
-        acorn.util.appendCss()
-        $hiddenPlayer = $('<div>').addClass('acorn-player hidden').width(600)
-            .height(400).appendTo('body')

         # must be appended to DOM in order to load properly
         runs -> $hiddenPlayer.append pv.render().el
@@ -177,8 +185,6 @@
         ), 'player ready', 10000

       it 'should load a vimeo player on render that loads video when played', ->
-        pv = new PlayerView viewOptions()
-
         loading = false
         # set 'loadProgress' listener and start playing on pv ready
         pv.on 'PlayerView:Ready', ->
@@ -188,11 +194,6 @@

           pv.player.api 'play'

-        # setup DOM
-        acorn.util.appendCss()
-        $hiddenPlayer = $('<div>').addClass('acorn-player hidden').width(600)
-            .height(400).appendTo('body')
-
         # must be appended to DOM in order to load properly
         runs -> $hiddenPlayer.append pv.render().el

@@ -200,35 +201,25 @@
         waitsFor (-> loading), 'video loading', 10000

       it 'should announce state changes', ->
-        pv = new PlayerView viewOptions()
-
         # start playing on pv ready
         pv.on 'PlayerView:Ready', -> pv.player.api 'play'

         stateChanged = false
         pv.on 'PlayerView:StateChange', -> stateChanged = true
-
-        # load player view
-        acorn.util.appendCss()
-        $hiddenPlayer = $('<div>').addClass('acorn-player hidden').width(600)
-            .height(400).appendTo('body')
         runs -> $hiddenPlayer.append pv.render().el

         waitsFor (-> stateChanged), 'state change event', 10000

       describe 'video player view api', ->
-        pv = undefined
-        $hiddenPlayer = undefined
-
-        # froogaloop 'paused' query does not work, so track paused status
-        # manually. for details on bug, see:
-        #   https://github.com/vimeo/player-api/issues/31
-        #   http://stackoverflow.com/questions/14119494/
-        paused = true
+        paused = undefined

         beforeEach ->
-          pv = new PlayerView viewOptions()
+          # froogaloop 'paused' query does not work, so track paused status
+          # manually. for details on bug, see:
+          #   https://github.com/vimeo/player-api/issues/31
+          #   http://stackoverflow.com/questions/14119494/
+          paused = true

           # set listeners and start playing on pv ready
           pv.on 'PlayerView:Ready', ->
@@ -237,21 +228,11 @@

             pv.player.api 'play'

-          # setup DOM
-          acorn.util.appendCss()
-          $hiddenPlayer = $('<div>').addClass('acorn-player hidden').width(600)
-              .height(400).appendTo('body')
-
           # must be appended to DOM in order to load properly
           runs -> $hiddenPlayer.append pv.render().el

           # wait for playing
           waitsFor (-> not paused), 'video to play', 10000
-
-        afterEach ->
-          pv.destroy()
-          $hiddenPlayer.remove()
-          paused = true

         it 'should play', ->
           # pause video with vimeo API, don't expect PLAYING state
@@ -385,12 +366,8 @@

       it 'should look good', ->
-        # setup DOM
-        acorn.util.appendCss()
-        $player = $('<div>').addClass('acorn-player').width(600).height(400)
-            .appendTo('body')
-
-        pv = new PlayerView viewOptions()
+        $player = $hiddenPlayer.removeClass 'hidden'
+
         pvReady = false
         pv.on 'PlayerView:Ready', -> pvReady = true

@@ -399,3 +376,9 @@
           $player.append pv.render().el
           pv.$el.find('iframe').width(600).height(371)
         waitsFor (-> pvReady), 'player view ready', 10000
+
+        runs ->
+          # stub afterEach pv.destroy() and $hiddenPlayer.remove() calls so that
+          # playerView remains in DOM
+          pv = destroy: ->
+          $hiddenPlayer = remove: ->


Just style

Just style

--- app/coffee/modules/schema.coffee
+++ app/coffee/modules/schema.coffee
@@ -20,7 +20,11 @@

 	User = access.wrapModel schema.define 'User',

-		name: type: String, length: 255, default: 'Anonymous', index: true
+		name:
+			type: String
+			default: 'Anonymous'
+			length: 255
+			index: true

 	User::hasPermission = (perm) -> true


Add support for tunein.com

Add support for tunein.com

--- src/background/mappings.coffee
+++ src/background/mappings.coffee
@@ -62,4 +62,5 @@
   'pony.fm': 'PonyFmController.js'
   'ponyvillelive': 'PonyvilleLiveController.js'
   'themusicninja': 'TheMusicNinjaController.js'
-  'one.npr': 'NPROneController.js'+  'tunein': 'TuneinController.js',
+  'one.npr': 'NPROneController.js'


Double quoting

Double quoting

--- test/test-airbud.coffee
+++ test/test-airbud.coffee
@@ -13,7 +13,7 @@
         url: "file://#{fixtureDir}/root.json"
       Airbud.fetch opts, (err, data, info) ->
         expect(err).to.be.null
-        info.should.have.property('attempts').that.equals 1
+        info.should.have.property("attempts").that.equals 1
         done()

     it "should fail on not having a desired key in body", (done) ->
@@ -33,9 +33,9 @@
         retries   : 1

       Airbud.fetch opts, (err, data, info) ->
-        info.should.have.property('attempts').that.equals 2
-        info.should.have.property('totalDuration').that.is.within opts.minTimeout, opts.maxTimeout
-        err.should.have.property('message').that.match /Error while opening/
+        info.should.have.property("attempts").that.equals 2
+        info.should.have.property("totalDuration").that.is.within opts.minTimeout, opts.maxTimeout
+        err.should.have.property("message").that.match /Error while opening/
         done()

     it "should retry and fail on long execution", (done) ->
@@ -46,8 +46,8 @@
         retries  : 1

       Airbud.fetch opts, (err, data, info) ->
-        err.should.have.property('message').that.match /Operation timeout of \d+ms reached/
-        info.should.have.property('attempts').that.equals 2
-        info.should.have.property('totalDuration').that.is.within 97, 103
+        err.should.have.property("message").that.match /Operation timeout of \d+ms reached/
+        info.should.have.property("attempts").that.equals 2
+        info.should.have.property("totalDuration").that.is.within 97, 103
         #         should be 100. but allow for timer inaccuracy --^
         done()


update inputFirst assertion text

update inputFirst assertion text

--- test/test-helper.coffee
+++ test/test-helper.coffee
@@ -34,7 +34,7 @@
 Browser.Assert.prototype.inputFirst = (selector, expected, message) ->
   expected = null if arguments.length == 1
   element = @browser.query(selector)
-  assert element, "Expected selector '#{selector}' to return one or more elements"
+  assert element, "Expected selector '#{selector}' to return an element"

   value = element.value.replace(/(\r\n|\n|\r)/gm,"")
   assertMatch value, expected, message


more updates!

more updates!

--- app/assets/javascripts/inspector_address.coffee
+++ app/assets/javascripts/inspector_address.coffee
@@ -230,6 +230,7 @@
         intro: "Piece of cake, right? Like this one - mark it 20...  "
         position: "right"
         polygon_index: 0
+        ixactive: true
       }
       {
         element: "#submit-button"
@@ -240,8 +241,9 @@
       {
         element: "#submit-button"
         intro: "What if there is no number at all? Skip it and move on. "
-        position: "right"
+        position: "top"
         polygon_index: 4
+        ixactive: true
       }
       {
         element: "#map-highlight"
@@ -258,13 +260,13 @@
       }
       {
         element: "#map-highlight"
-        intro: "It can be easy to be fooled by those olde tyme numbers. 47 or 17? If you can't tell, check th neighbors. This one's near the 20s, so it must be ye olde 17. Click. Type. Proceed. "
+        intro: "It can be easy to be fooled by those olde tyme numbers. 47 or 17? If you can't tell, check the neighbors. This one's near the 20s, so it must be ye olde 17. Click. Type. Proceed. "
         position: "right"
         polygon_index: 1
       }
       {
         element: "#submit-button"
-        intro: "Hit next to save it and move on.

 Okay, you probably get the concept of hitting \"Next\" at this point - so we're just going to roll through some examples lighting round style and trust that when the time comes for the real thing - you'll know what to do!"
+        intro: "Hit next to save it and move on.

 Okay, you probably get the concept of hitting \"Next\" at this point - so we're just going to roll through some examples lightning round style and trust that when the time comes for the real thing - you'll know what to do!"
         position: "top"
         polygon_index: 1
         ixactive: true
@@ -295,20 +297,20 @@
       }
       {
         element: "#map-highlight"
-        intro: "Watch out: sometimes you'll find a number  ON  the polygon. That's a floor count - not an address. "
+        intro: "Watch out: sometimes you'll find a number  ON  the polygon. That's a floor count - not an address. This building doesn't seem to have one."
         position: "right"
         polygon_index: 11
       }
       {
         element: "#submit-button"
-        intro: "Skip it like your weird cousin's wedding!"
+        intro: "So skip it like your weird cousin's wedding!"
         position: "top"
         polygon_index: 11
         ixactive: true
       }
       {
         element: "#map-highlight"
-        intro: "Be careful of reading the numbers upside down! This is a 60, not an 09. Always check the neighbors if you're not sure."
+        intro: "Be careful of reading the numbers upside down! This is 60, not 09. Always check the neighbors if you're not sure."
         position: "right"
         polygon_index: 12
       }


Add "precision=auto" option to Metric::autoscale_value.

This is now the default option when no explicit argument is given, and
prevents the loss of precision on numbers on chart axis.

Closes #1026.

--- app/assets/javascripts/lib/models/metric.coffee
+++ app/assets/javascripts/lib/models/metric.coffee
@@ -38,15 +38,27 @@
   # autoscale_value(20, '%') => 20%
   # autoscale_value(1234)    => 1234
   #
-  autoscale_value: (x, unit, precision = 0) ->
+  autoscale_value: (x, unit, precision = 'auto') ->
     if x == 0
       pow = 0
       value = 0
+      precision = 0
     else
       pow = @power_of_thousand(x)
       # in future we should downscale, too
       pow = 0 if pow < 0
       value = x / Math.pow(1000, pow)
+
+      if precision is 'auto'
+        # Automatically determine how many significant decimal places are
+        # present in the number.
+        #
+        # .split('.')[1] => the decimal places
+        # .split(/0/)[0] => decimal places minus trailing zeros
+        #
+        str_value  = "#{ value }"
+        precision = str_value.split('.', 2)[1]?.split(/0/, 1)[0].length || 0
+
       value = @round_number(value, precision)

     switch unit


removing duplicate key

removing duplicate key

--- src/coffee/controllers/player/player-overview-ctrl.coffee
+++ src/coffee/controllers/player/player-overview-ctrl.coffee
@@ -34,7 +34,6 @@
       'party':            'fa-users'
       'exp':              'fa-support'
       'gold':             'icon-money'
-      'guild':            'fa-network'
       'combat':           'fa-newspaper-o faa-pulse animated'
       'event':            'fa-gift faa-shake animated'
       'pet':              'fa-paw'
@@ -139,4 +138,4 @@

     $scope.initialize()

-]+]


extracted parse_env method

extracted parse_env method

--- app/assets/javascripts/models/command.js.coffee
+++ app/assets/javascripts/models/command.js.coffee
@@ -29,24 +29,7 @@
       return need

     run: (hosts, args, stdin, env, successCb) ->
-
-      if _.isString(env)
-        # convert env string into an object hash of key/value pairs
-        # expects env to have one pair on each line, separated by '=',
-        # e.g., "foo=bar\nbaz=boo"
-        e = {}
-        env.split(/\n/).forEach (line) ->
-          line = line.trim()
-          i = line.indexOf('=')
-          if i < 0
-            return
-          k = line.substr(0, i).trim()
-          v = line.substr(i+1).trim()
-          if v[0] == '"' || v[0] == "'"
-            v = JSON.parse(v)
-          e[k] = v
-        env = e
-
+      env = @constructor.parse_env(env)
       @ajax "post",
         url: @url() + "/run"
         data: {hosts: hosts, args: args, stdin: stdin, env: env}
@@ -54,6 +37,27 @@
           successCb(data)
         error: (xhr, status, err) =>
           @log "error: ", err
+
+    # Convert env string into an object hash of key/value pairs.
+    # Expects env to have one pair on each line, separated by '=',
+    # e.g., "foo=bar\nbaz=boo"
+    #
+    # @param [String] env
+    # @return [Object]
+    @parse_env: (env) ->
+      return env if ! _.isString(env)
+      e = {}
+      env.split(/\n/).forEach (line) ->
+        line = line.trim()
+        i = line.indexOf('=')
+        if i < 0
+          return
+        k = line.substr(0, i).trim()
+        v = line.substr(i+1).trim()
+        if v[0] == '"' || v[0] == "'"
+          v = JSON.parse(v)
+        e[k] = v
+      return e

     # Retrieve help text for this command
     help_html: ->


Fix watch to publish updates automatically

--- grunt/config/watch.coffee
+++ grunt/config/watch.coffee
@@ -14,22 +14,22 @@
         '<%= src %>/*.coffee'
         '<%= src %>/views/*.coffee'
       ]
-      tasks: ['copy:app']
+      tasks: ['copy:app', 'publish']
     coffee2css:
       files: [
         'grunt/tasks/color2css.coffee'
         '<%= src %>/color.coffee'
       ]
-      tasks: 'coffee2css'
+      tasks: ['coffee2css', 'publish']
     less:
       files: ['<%= styles %>/**/*.less']
-      tasks: ['less']
+      tasks: ['less', 'publish']
     i18n:
       files: ['<%= locales %>/*.yaml']
-      tasks: ['i18next-yaml']
+      tasks: ['i18next-yaml', 'publish']
     jade:
       files: ['<%= views %>/**/*.jade']
-      tasks: ['jade']
+      tasks: ['jade', 'publish']
     livereload:
       options:
         livereload: true


Add close method

Add close method

--- src/ievm.coffee
+++ src/ievm.coffee
@@ -55,7 +55,7 @@

   # ## Class Methods

-  # Run ievms shell script with a given environment. A debug function may be
+  # Run ievms shell script with a given environment. A debug function may be
   # passed (like `console.log`) which will be called for each line of ievms
   # output.
   @ievms: (env, dbg=debug) ->
@@ -177,6 +177,10 @@
     @debug "open: #{url}"
     @exec 'cmd.exe', '/c', 'start',
       'C:\\Program Files\\Internet Explorer\\iexplore.exe', url
+
+  close: -> @ensureRunning().then =>
+    @debug 'close'
+    @exec 'taskkill.exe', '/f', '/im', 'iexplore.exe'

   rearm: (delay=30000) -> @ensureNotMissing().then => @ensureRunning().then =>
     @debug 'rearm'


Refactor test_helper

Refactor test_helper

--- test/test_helper.coffee
+++ test/test_helper.coffee
@@ -1,5 +1,4 @@
 fs = require 'fs'
-encoding = require 'encoding'
 chai = require 'chai'
 chai.use require('sinon-chai')

@@ -14,7 +13,7 @@

 exports.getFixture = (name, encoding) =>
   return fixtureCache[name] if fixtureCache[name]
-  result = fs.readFileSync "./test/fixtures/#{name}", encoding
+  result = fs.readFileSync "./test/fixtures/#{name}"
   fixtureCache[name] = result
   result


fix_bug:图片显示超过边框

fix_bug:图片显示超过边框

不影响上传图片大小

--- app/assets/javascripts/simditor/simditor.coffee
+++ app/assets/javascripts/simditor/simditor.coffee
@@ -2970,6 +2970,7 @@
       else

         $img.attr 'src', result.file_path
+        $img.css 'width', '100%'

       @popover.srcEl.prop('disabled', false)

@@ -3660,4 +3661,4 @@
   command: ->
     @editor.undoManager.undo()

-Simditor.Toolbar.addButton UndoButton+Simditor.Toolbar.addButton UndoButton


Use jQuery.noConflict deep

Use jQuery.noConflict deep

--- src/init.coffee
+++ src/init.coffee
@@ -5,5 +5,5 @@
     placement = new Infeedl.Placement(Infeedl.$(this).attr("data-infeedl-placement"), this)
     placement.fetch()

-Infeedl.$ = jQuery.noConflict()
+Infeedl.$ = jQuery.noConflict(true)
 @Infeedl.init()


Less verbose webpack

Less verbose webpack

--- gulp/tasks/webpack.coffee
+++ gulp/tasks/webpack.coffee
@@ -18,7 +18,7 @@
 # Development Webpack Task
 gulp.task "webpack-dev", (cb) ->
   config = require("../../config/webpack/webpack.config.dev.js")
-  new WebpackDevServer(webpack(config) ,{}).listen(8080, "localhost", (err)->
+  new WebpackDevServer(webpack(config) , { stats: 'errors-only' }).listen(8080, "localhost", (err)->
     throw new plugins.util.PluginError("webpack-dev-server", err) if err
     plugins.util.log "[webpack-dev-server] Running"
   )


Don't return array from deactivate

Don't return array from deactivate

--- lib/main.coffee
+++ lib/main.coffee
@@ -39,6 +39,7 @@
   deactivate: ->
     @paneSubscription.dispose()
     tabBarView.remove() for tabBarView in @tabBarViews
+    return

   serialize: ->
     for tabBarView in @tabBarViews


:bug: Guard against undefined path

:bug: Guard against undefined path

--- lib/main.coffee
+++ lib/main.coffee
@@ -51,7 +51,7 @@
     @treeView

   shouldAttach: ->
-    projectPath = atom.project.getPaths()[0]
+    projectPath = atom.project.getPaths()[0] ? ''
     if atom.workspace.getActivePaneItem()
       false
     else if path.basename(projectPath) is '.git'


Add timestamp to filename

Add timestamp to filename

--- lib/main.coffee
+++ lib/main.coffee
@@ -173,8 +173,10 @@
 					else
 						@consolePanel.raw fs.readFileSync(path.join(outputDir, 'memory-use.log')).toString()
 						# Rename binary based on platform
-						fs.moveSync path.join(outputDir, 'firmware.bin'),
-							path.join(projectDir, @profileManager.currentTargetPlatformName.toLowerCase() + '_firmware.bin')
+						outputFile = path.join(projectDir,
+							@profileManager.currentTargetPlatformName.toLowerCase() +
+							'_firmware_' + (new Date()).getTime() + '.bin')
+						fs.moveSync path.join(outputDir, 'firmware.bin'), outputFile
 				, (error) =>
 					compileErrorHandler error


cleanup

cleanup

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,20 +1,20 @@
+fs = require 'fs-plus'
+CSON = require 'season'
 {CompositeDisposable} = require 'atom'
-fs = require 'fs-plus'
 settings = require './settings'
-CSON = null

 configTemplate = """
-# groups:
-#   atom: [
-#     "~/github/atom"
-#     "~/github/text-buffer"
-#     "~/github/atom-keymap"
-#   ]
-#   sample: [
-#     "~/dir/hello-project"
-#     "~/dir/world-project"
-#   ]
-"""
+  # groups:
+  #   atom: [
+  #     "~/github/atom"
+  #     "~/github/text-buffer"
+  #     "~/github/atom-keymap"
+  #   ]
+  #   sample: [
+  #     "~/dir/hello-project"
+  #     "~/dir/world-project"
+  #   ]
+  """

 module.exports =
   config: settings.config
@@ -26,7 +26,7 @@
     @subscriptions.add atom.commands.add 'atom-workspace',
       'project-folder:add': => @view.start('add')
       'project-folder:remove': => @view.start('remove')
-      'project-folder:open-config': => @openUserConfig()
+      'project-folder:open-config': => @openConfig()

   deactivate: ->
     @subscriptions.dispose()
@@ -34,12 +34,12 @@
     {@subscriptions, @view} = {}

   readConfig: ->
+    config = {}
+
     filePath = @getUserConfigPath()
-    config = {}
     return config unless fs.existsSync(filePath)

     try
-      CSON ?= require 'season'
       config = CSON.readFileSync(filePath) or {}
     catch error
       atom.notifications.addError('[project-folder] config file has error', detail: error.message)
@@ -52,7 +52,7 @@
     if groups = @readConfig().groups
       @view.setGroups(groups)

-  openUserConfig: ->
+  openConfig: ->
     filePath = @getUserConfigPath()

     atom.workspace.open(filePath, searchAllPanes: true).then (editor) =>


:bug: Fix a typo

:bug: Fix a typo

--- lib/main.coffee
+++ lib/main.coffee
@@ -18,8 +18,8 @@
     legacy = require('./legacy.coffee')
     for atomPackage in atom.packages.getLoadedPackages()
       if atomPackage.metadata['linter-package'] is true
-        implemention = atomPackage.metadata['linter-implementation'] ? atomPackage.name
-        linter = legacy(require "#{atomPackage.path}/lib/#{implemention}")
+        implementation = atomPackage.metadata['linter-implementation'] ? atomPackage.name
+        linter = legacy(require "#{atomPackage.path}/lib/#{implementation}")
         @consumeLinter(linter)
       if atomPackage.metadata.providedServices?['linter-plus']?.versions['0.1.0']
         atom.notifications.addWarning("#{atomPackage.name} still provides


Remove TODO

Remove TODO

--- lib/menu.coffee
+++ lib/menu.coffee
@@ -17,8 +17,6 @@
   if item.type is 'link'
     item.path = item.use
   else if item.type is 'template'
-    # TODO: Check if collection with this name already exists
-
     item.path = "/admin/#{item.use}"

   return item


Fixed #2729

Fixed #2729

--- app/views/editor/article/ArticleEditView.coffee
+++ app/views/editor/article/ArticleEditView.coffee
@@ -54,8 +54,12 @@
     return unless @treema and @preview
     m = marked(@treema.data.body)
     b = $(@preview.document.body)
-    b.find('#insert').html(m)
-    b.find('#title').text(@treema.data.name)
+    onLoadHandler = =>
+      if b.find('#insert').length == 1
+        b.find('#insert').html(m)
+        b.find('#title').text(@treema.data.name)
+        clearInterval(id)
+    id = setInterval(onLoadHandler, 100)

   getRenderData: (context={}) ->
     context = super(context)
@@ -71,7 +75,8 @@
     @patchesView.load()

   openPreview: ->
-    @preview = window.open('/editor/article/preview', 'preview', 'height=800,width=600')
+    if not @preview or @preview.closed
+      @preview = window.open('/editor/article/preview', 'preview', 'height=800,width=600')
     @preview.focus() if window.focus
     @preview.onload = => @pushChangesToPreview()
     return false


Always give fingerprtint per m-line

Always give fingerprtint per m-line

--- src/echo.coffee
+++ src/echo.coffee
@@ -69,6 +69,7 @@
         credentials = stream.nice.getLocalCredentials()
         insert_line 'a=ice-ufrag:' + credentials.ufrag
         insert_line 'a=ice-pwd:' + credentials.pwd
+        insert_line 'a=fingerprint:' + stream.dtls_srtp.fingerprint()
         delete stream.needs_ice_cred

       if m = line.match(/m=(\w+) \S+ \S+ (.*)/)
@@ -152,8 +153,8 @@
         remove_line()

       else if m = line.match(/a=fingerprint:/)
-        # replace fingerprint of the peer with our own
-        lines[i] = 'a=fingerprint:' + stream.dtls_srtp.fingerprint()
+        # remove fingerprint, we add our own above
+        remove_line()

       i++


fixed typo

fixed typo

--- src/components/life_app.coffee
+++ src/components/life_app.coffee
@@ -116,7 +116,7 @@
         new_state.in_edit = false
         new_state.temp_event = null

-        @setState new_statetest
+        @setState new_state

   cancelHandler: (e) ->
     # If the event was a temp event, just delete it


Attempt to handle bogus IDB NotFoundError

--- assets/javascripts/app/db.coffee
+++ assets/javascripts/app/db.coffee
@@ -71,7 +71,7 @@
       try db.deleteObjectStore(name)
     return

-  store: (doc, data, onSuccess, onError) ->
+  store: (doc, data, onSuccess, onError, _retry = true) ->
     @db (db) =>
       unless db
         onError()
@@ -82,9 +82,13 @@
         @cachedDocs?[doc.slug] = doc.mtime
         onSuccess()
         return
-      txn.onerror = (event) ->
-        event.preventDefault()
-        onError(event)
+      txn.onerror = (event) =>
+        event.preventDefault()
+        if txn.error.name is 'NotFoundError' and _retry
+          @migrate()
+          @store(doc, data, onSuccess, onError, false)
+        else
+          onError(event)
         return

       store = txn.objectStore(doc.slug)
@@ -96,7 +100,7 @@
       return
     return

-  unstore: (doc, onSuccess, onError) ->
+  unstore: (doc, onSuccess, onError, _retry = true) ->
     @db (db) =>
       unless db
         onError()
@@ -109,7 +113,11 @@
         return
       txn.onerror = (event) ->
         event.preventDefault()
-        onError(event)
+        if txn.error.name is 'NotFoundError' and _retry
+          @migrate()
+          @unstore(doc, onSuccess, onError, false)
+        else
+          onError(event)
         return

       store = txn.objectStore(doc.slug)


Fix adding rows in non-default ss views

Fix adding rows in non-default ss views

Going back to old Foot way really

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -117,9 +117,8 @@

         Groups = []     # [] Array over categories :: {category: Category, metrics[]: Array of Metric}
         Cols = []       # [] Array over metrics :: {category: Category, metric: Metric} (flattened version of Groups)
-        Rows = []       # [i] :: Row
+        Rows = []       # [i] :: Row (-1 => foot)
         Order = []      # Permutation Array of Row in display order
-        Foot = undefined # DOM Element tr
         Expanded = undefined # Info

@@ -414,6 +413,7 @@
         populate = ->
           bySlot = Key == pseudoCategory.slot
           populateCols(bySlot)
+          foot = Rows[-1]
           Rows = []
           if bySlot
             populateSlots()
@@ -423,6 +423,7 @@
             Order = if Rows.length then [0..Rows.length-1] else []
           $(TBody).empty()
           Expanded = undefined
+          Rows[-1] = foot if foot
           generate()
           return

@@ -564,16 +565,15 @@
           return

         generateFoot = ->
-          info = new Info(-1)
-          if Foot
-            $(Foot).empty()
-          else
-            Foot = TFoot.appendChild(document.createElement('tr'))
-            Foot.id = ID + '_add'
-          info.tr = Foot
+          if row = Rows[-1]
+            $(Rows[-1].tr).empty()
+          else
+            row = new Row(-1)
+            row.tr = TFoot.appendChild(document.createElement('tr'))
+            row.tr.id = ID + '_add'
+          info = new Info(row.i)
           info.cols = Groups[0]
           info.category = Key
-          info.slot = undefined
           td = info.tr.appendChild(document.createElement('td'))
           td.setAttribute("colspan", info.cols.metrics.length)
           td.className = 'null'