send-message-2-as positive and negative cases.

--- test/integration/send-message-2-as-spec.coffee
+++ test/integration/send-message-2-as-spec.coffee
@@ -62,8 +62,16 @@

     @devices.insert @imposterDevice, done

+  beforeEach 'create bananas device', (done) ->
+    @imposterDevice =
+      uuid: 'bananas-uuid'
+      token: bcrypt.hashSync 'leak', 8
+      type: 'device:bananas'
+
+    @devices.insert @imposterDevice, done
+
   context 'When sending a message as another device', ->
-    context "sender-uuid receiving it's sent messages", ->
+    context "receiver-uuid receiving it's received messages", ->
       beforeEach 'create message received subscription', (done) ->
         subscription =
           type: 'message.received'
@@ -96,5 +104,126 @@

           @dispatcher.generateJobs job, (error, @generatedJobs) => doneTwice()

+      it 'should deliver the received message to the receiver', ->
+        expect(@message).to.exist
+    context "sender-uuid receiving it's sent messages", ->
+      beforeEach 'create message received subscription', (done) ->
+        subscription =
+          type: 'message.received'
+          emitterUuid: 'sender-uuid'
+          subscriberUuid: 'sender-uuid'
+
+        @subscriptions.insert subscription, done
+
+      beforeEach 'create message sent subscription', (done) ->
+        subscription =
+          type: 'message.sent'
+          emitterUuid: 'sender-uuid'
+          subscriberUuid: 'sender-uuid'
+
+        @subscriptions.insert subscription, done
+
+      beforeEach (done) ->
+        doneTwice = _.after 2, done
+        job =
+          metadata:
+            auth:
+              uuid: 'imposter-uuid'
+              token: 'leak'
+              as: 'sender-uuid'
+            fromUuid: 'sender-uuid'
+            jobType: 'SendMessage2'
+          data:
+            devices: ['receiver-uuid'], payload: 'boo'
+
+        client = new RedisNS 'messages', redis.createClient(@redisUri)
+        @hydrant = new HydrantManager {client, @uuidAliasResolver}
+        @hydrant.connect uuid: 'sender-uuid', (error) =>
+          return done(error) if error?
+
+          @hydrant.once 'message', (@message) =>
+            @hydrant.close()
+            doneTwice()
+
+          @dispatcher.generateJobs job, (error, @generatedJobs) => doneTwice()
+
       it 'should deliver the sent message to the sender', ->
         expect(@message).to.exist
+
+  context 'When sending a message as another device, and not in the as whitelist', ->
+    context "receiver-uuid receiving it's received messages", ->
+      beforeEach 'create message received subscription', (done) ->
+        subscription =
+          type: 'message.received'
+          emitterUuid: 'receiver-uuid'
+          subscriberUuid: 'receiver-uuid'
+
+        @subscriptions.insert subscription, done
+
+      beforeEach (done) ->
+        job =
+          metadata:
+            auth:
+              uuid: 'bananas-uuid'
+              token: 'leak'
+              as: 'sender-uuid'
+            fromUuid: 'sender-uuid'
+            jobType: 'SendMessage2'
+          data:
+            devices: ['receiver-uuid'], payload: 'boo'
+
+        client = new RedisNS 'messages', redis.createClient(@redisUri)
+        @hydrant = new HydrantManager {client, @uuidAliasResolver}
+        @hydrant.connect uuid: 'receiver-uuid', (error) =>
+          return done(error) if error?
+
+          @hydrant.once 'message', (@message) =>
+            @hydrant.close()
+
+          @dispatcher.generateJobs job, (error, @generatedJobs) =>
+            setTimeout done, 2000
+
+      it 'should not deliver the received message to the receiver', ->
+        expect(@message).not.to.exist
+    context "sender-uuid receiving it's sent messages", ->
+      beforeEach 'create message received subscription', (done) ->
+        subscription =
+          type: 'message.received'
+          emitterUuid: 'sender-uuid'
+          subscriberUuid: 'sender-uuid'
+
+        @subscriptions.insert subscription, done
+
+      beforeEach 'create message sent subscription', (done) ->
+        subscription =
+          type: 'message.sent'
+          emitterUuid: 'sender-uuid'
+          subscriberUuid: 'sender-uuid'
+
+        @subscriptions.insert subscription, done
+
+      beforeEach (done) ->
+        job =
+          metadata:
+            auth:
+              uuid: 'bananas-uuid'
+              token: 'leak'
+              as: 'sender-uuid'
+            fromUuid: 'sender-uuid'
+            jobType: 'SendMessage2'
+          data:
+            devices: ['receiver-uuid'], payload: 'boo'
+
+        client = new RedisNS 'messages', redis.createClient(@redisUri)
+        @hydrant = new HydrantManager {client, @uuidAliasResolver}
+        @hydrant.connect uuid: 'sender-uuid', (error) =>
+          return done(error) if error?
+
+          @hydrant.once 'message', (@message) =>
+            @hydrant.close()
+
+          @dispatcher.generateJobs job, (error, @generatedJobs) =>
+            setTimeout done, 2000
+
+      it 'should deliver the sent message to the sender', ->
+        expect(@message).not.to.exist


use non-capturing groups

use non-capturing groups

--- src/scripts/dribbble.coffee
+++ src/scripts/dribbble.coffee
@@ -25,9 +25,9 @@
       msg.send '"' + data.title + '"' + " by " + data.player.name

 module.exports = (robot) ->
-  robot.respond /(depress|dribbble|inspire)( me)? (.*)/i, (msg) ->
-    query = msg.match[3]
-    query ||= 'popular'
+  robot.respond /(?:depress|dribbble|inspire)(?: me)?(.*)/i, (msg) ->
+    query = msg.match[1] || 'popular'
+    query = query.trim()
     msg.http("http://api.dribbble.com/shots/#{query}")
       .get() (err, res, body) ->
         data = JSON.parse(body)


testing

testing

--- src/scripts/pg-brain.coffee
+++ src/scripts/pg-brain.coffee
@@ -27,6 +27,7 @@

 # sets up hooks to persist the brain into postgres.
 module.exports = (robot) ->
+  console.log 'wat'

   database_url = process.env.DATABASE_URL

@@ -39,6 +40,7 @@

   query = client.query("SELECT brain FROM hubot LIMIT 1")
   query.on 'row', (row) ->
+    console.log row, row.brain
     robot.brain.mergeData row.brain
     robot.logger.debug "pg-brain loaded."


fix tc list builds reporting

fix tc list builds reporting

--- src/scripts/teamcity.coffee
+++ src/scripts/teamcity.coffee
@@ -43,7 +43,6 @@

   getBuildType = (msg, type, callback) ->
     url = "#{base_url}/httpAuth/app/rest/buildTypes/#{type}"
-    console.log "sending request to #{url}"
     msg.http(url)
       .headers(getAuthHeader())
       .get() (err, res, body) ->
@@ -96,7 +95,7 @@
       .query(locator: ["count:#{amount}","running:any"].join(","))
       .get() (err, res, body) ->
         err = body unless res.statusCode == 200
-        builds = JSON.parse(body).build.splice(amount) unless err
+        builds = JSON.parse(body).build unless err
         callback err, msg, builds

   mapNameToIdForBuildType = (msg, project, name, callback) ->
@@ -254,9 +253,8 @@
                 configuration = buildTypeMatches[1]
                 project = null

-
         getBuilds msg, project, configuration, amount, (err, msg, builds) ->
-          if not builds
+          if not builds || builds.length == 0
             msg.send "Could not find builds for #{option}"
             return
           createAndPublishBuildMap(builds, msg)


Improved hot loading for replacing existing instances

--- src/server/bootstrap.coffee
+++ src/server/bootstrap.coffee
@@ -111,13 +111,48 @@

     cb()

+  # This helps to set up hot loading of changed files
+  #
+  # It works by deleting the cached require entry for the file and then
+  # requiring it again.
+  #
+  # Afterwards we change the prototype of the old class, so that existing
+  # instances get changed, too
   setupHotloadingInFolder: (folder, callback) =>
     gaze ["#{folder}/**/*.coffee", "#{folder}/**/*.js"], (err, watcher) =>
       watcher.on "changed", (file) =>
         console.log "File changed!", file

+        # save current global App state in temporary variable
+        oldApp = global.App
+        global.App = {}
+
+        # reload file
         delete require.cache[require.resolve(file)]
         require file
+
+        # detect which classes where changed. By requiring the file, it gets
+        # a new entry in App and we can find out which class was changed
+        changedClasses =  _.keys global.App
+
+        # Iterate over all changed classes and detect if a method was deleted.
+        for newClassName in changedClasses
+          oldMethods = _.keys oldApp[newClassName].prototype
+          newMethods = _.keys global.App[newClassName].prototype
+
+          # If this is the case, delete the method from the existing instances
+          for currentMethod in oldMethods when currentMethod not in newMethods
+            console.log "Remove method #{methodName}"
+            delete oldApp[newClassName].prototype[currentMethod]
+
+          # Replace every old prototype method with the new version
+          for methodName, method of global.App[newClassName].prototype
+            console.log "Replace method #{methodName}"
+            oldApp[newClassName].prototype[methodName] = method
+
+          # TODO: Not working with fat arrow functions yet
+
+        global.App = oldApp

         callback null, file if callback


使用findone重写findById

使用findone重写findById

--- core/model/Model.coffee
+++ core/model/Model.coffee
@@ -105,10 +105,4 @@

   # id为string
   @findById: (id, opts = {}, callback) ->
-    if _.isFunction opts
-      callback = opts
-      opts = {}
-    @collection().findOne {_id: id}, (err, doc) =>
-      throw err if err
-      result = @create doc
-      callback err, result
+    @findOne {_id: id},opts,callback


set autoMark to false /cc @lstoll

set autoMark to false /cc @lstoll

--- src/slack_connection.coffee
+++ src/slack_connection.coffee
@@ -3,7 +3,7 @@

 class SlackConnection extends EventEmitter
   constructor: (@token, @document) ->
-    @client = new SlackClient @token, true, true
+    @client = new SlackClient @token, true, false

     @client.on 'open', @.open
     @client.on 'close', @.close


Add empty values to add payment FormControl

--- src/smc-webapp/billing.cjsx
+++ src/smc-webapp/billing.cjsx
@@ -180,10 +180,14 @@
         on_close : rtypes.func.isRequired  # called when this should be closed

     getInitialState : ->
-        new_payment_info : {name : @props.redux.getStore('account').get_fullname()}
-        submitting       : false
-        error            : ''
-        cvc_help         : false
+        new_payment_info :
+            name            : @props.redux.getStore('account').get_fullname()
+            number          : ""
+            address_state   : ""
+            address_country : ""
+        submitting : false
+        error      : ''
+        cvc_help   : false

     submit_payment_method : ->
         @setState(error: false, submitting:true)
@@ -272,7 +276,7 @@
             return true

         x = info[name]
-        if not x?
+        if not x? or x == ""
             return
         switch name
             when 'number'


Update options for typescript compiler

Update options for typescript compiler

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,6 +6,8 @@
  This software is released under the MIT License.
  http://opensource.org/licenses/mit-license.php
 ###
+
+tsconfig = require './tsconfig'

 module.exports = (grunt) ->

@@ -36,9 +38,9 @@

     typescript:
       base:
-        src: ["<%= dir.src %>/ts/*.ts"]
+        src: tsconfig.files
         dest: "<%= dir.dest %>/js/"
-        options: require('./tsconfig.json').compilerOptions
+        options: tsconfig.compilerOptions

     copy:
       html:


Create a new connection to mongo

Create a new connection to mongo

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -173,7 +173,7 @@
         files: ['public/**/*']

   grunt.registerTask 'checkDatabase', (next, stuff...)->
-    connection = mongoose.connect config.db, (err) ->
+    connection = mongoose.createConnection config.db, (err) ->
       if err
         throw "\nBuckets could not connect to MongoDB :/\n".magenta + "See the " + 'README.md'.bold + " for more info on installing MongoDB and check your settings at " + 'server/config.coffee'.bold + "."
         exit


disable sauce for pull requests

disable sauce for pull requests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -310,8 +310,15 @@
       # "csslint",
       "qunit:develop"
   ]
+
   grunt.registerTask "sauce", ["connect:sauce", "saucelabs-qunit"]
-  grunt.registerTask "travis", ["test", "sauce"]
+  if parseInt(process.env.TRAVIS_PULL_REQUEST, 10) > 0
+      # saucelab keys do not work on forks
+      # http://support.saucelabs.com/entries/25614798
+      grunt.registerTask "travis", ["test"]
+  else
+      grunt.registerTask "travis", ["test", "sauce"]
+
   grunt.registerTask "default", ["test"]
   grunt.registerTask "build", [
       "exec:tabfix"


Temp fix for karma testing.

Temp fix for karma testing.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -192,7 +192,7 @@
       # Changes to app code should be validated and re-copied to the `build`, triggering `watch:build`
       unit:
         files:      '<%= CLIENT_DIR + JS_FILES %>'
-        tasks:      [ 'copy:js', 'jshint', 'karma:background:run' ]
+        tasks:      [ 'copy:js', 'jshint']

   # Dependencies


Clean tmp-deploy folder

Clean tmp-deploy folder

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,7 +11,7 @@

 		# Tasks
 		clean:
-			main: ['build', 'build-raw']
+			main: ['build', 'build-raw', 'tmp-deploy']

 		copy:
 			main:


Add grunt work task alias

Add grunt work task alias

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -79,3 +79,8 @@
     "coffee"
     "concat"
   ]
+
+  grunt.registerTask "work", [
+    "default"
+    "watch"
+  ]


Disable browserify:tests from compile task for now.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -41,7 +41,7 @@

   grunt.registerTask 'compile', [
     'build',
-    'browserify',
+    'browserify:main',
     'uglify'
   ]


Add core:confirm and core:cancel keybindings

--- keymaps/spell-check.cson
+++ keymaps/spell-check.cson
@@ -1,5 +1,15 @@
 '.platform-darwin .editor':
   'cmd-:': 'spell-check:correct-misspelling'

+'.platform-darwin .corrections .editor':
+  'cmd-:': 'core:cancel'
+
 '.platform-win32 .editor':
   'ctrl-:': 'spell-check:correct-misspelling'
+
+'.platform-win32 .corrections .editor':
+  'ctrl-:': 'core:cancel'
+
+'.corrections .mini.editor input':
+  'enter': 'core:confirm'
+  'tab': 'core:confirm'


Extra blank line to make each username/link pair a paragraph.

--- server/code/lib/email.coffee
+++ server/code/lib/email.coffee
@@ -66,7 +66,7 @@
                    Username: #{user.shortName}
                    Reset link: https://scraperwiki.com/set-password/#{user.token}
                    """)
-    urlList = urlList.join "\n"
+    urlList = urlList.join "\n\n"
     mailOptions =
       from: 'hello@scraperwiki.com'
       to: userList[0].email[0]


add signature field to account model

add signature field to account model

--- server/models/account.coffee
+++ server/models/account.coffee
@@ -32,6 +32,7 @@
         allMailbox: String          # \All Maibox id
         favorites: [String]         # [String] Maibox id of displayed boxes
         patchIgnored: Boolean       # has patchIgnored been applied ?
+        signature: String            # Signature to add at the end of messages

     # Public: find an account by id
     # cozydb's find can return no error and no account (if id isnt an account)


Add spec for busting the cache on window focus

--- src/extensions/fuzzy-finder/spec/fuzzy-finder-spec.coffee
+++ src/extensions/fuzzy-finder/spec/fuzzy-finder-spec.coffee
@@ -228,3 +228,23 @@

       runs ->
         expect(rootView.project.getFilePaths).toHaveBeenCalled()
+
+    it "busts the cache when the window gains focus", ->
+      spyOn(rootView.project, "getFilePaths").andCallThrough()
+      rootView.trigger 'fuzzy-finder:toggle-file-finder'
+
+      waitsFor ->
+        finder.list.children('li').length > 0
+
+      runs ->
+        expect(rootView.project.getFilePaths).toHaveBeenCalled()
+        rootView.project.getFilePaths.reset()
+        $(window).trigger 'focus'
+        rootView.trigger 'fuzzy-finder:toggle-file-finder'
+        rootView.trigger 'fuzzy-finder:toggle-file-finder'
+
+      waitsFor ->
+        finder.list.children('li').length > 0
+
+      runs ->
+        expect(rootView.project.getFilePaths).toHaveBeenCalled()


Improve logs and add options to enable logs

--- konecty:methods-middleware.coffee
+++ konecty:methods-middleware.coffee
@@ -4,6 +4,8 @@
 BeforeMethods = {}
 AfterMethods = {}

+# Meteor.registerLogs = true
+# Meteor.registerVerboseLogs = true

 formatJsonWithPrefixAndColor = (value, prefix, color) ->
 	value = JSON.stringify value, null, '  '
@@ -18,7 +20,14 @@

 logBeforeExecution = (methodName, scope, args) ->
-	console.log "#{methodName} [will call]".cyan
+	if Meteor.registerLogs isnt true
+		return
+
+	console.log "#{methodName}".cyan
+
+	if Meteor.registerVerboseLogs isnt true
+		return
+
 	if scope?
 		console.log '  > SCOPE'.cyan
 		console.log formatJsonWithPrefixAndColor removeInternalProperties(scope), '  | ', 'cyan'
@@ -27,16 +36,23 @@
 		console.log formatJsonWithPrefixAndColor args, '  | ', 'cyan'

 logAfterExecution = (methodName, scope, args, result) ->
-	console.log "#{methodName} [called]".magenta
+	if Meteor.registerLogs isnt true
+		return
+
+	console.log "#{methodName} [done]".cyan
+
+	if Meteor.registerVerboseLogs isnt true
+		return
+
 	if scope?
-		console.log '  > SCOPE'.magenta
-		console.log formatJsonWithPrefixAndColor removeInternalProperties(scope), '  | ', 'magenta'
+		console.log '  > SCOPE'.cyan
+		console.log formatJsonWithPrefixAndColor removeInternalProperties(scope), '  | ', 'cyan'
 	if args?
-		console.log '  > ARGUMENTS'.magenta
-		console.log formatJsonWithPrefixAndColor args, '  | ', 'magenta'
+		console.log '  > ARGUMENTS'.cyan
+		console.log formatJsonWithPrefixAndColor args, '  | ', 'cyan'
 	if result?
-		console.log '  > RESULT'.magenta
-		console.log formatJsonWithPrefixAndColor args, '  | ', 'magenta'
+		console.log '  > RESULT'.cyan
+		console.log formatJsonWithPrefixAndColor args, '  | ', 'cyan'

 processResult = (result) ->
 	if _.isObject(result) and _.isFunction(result.fetch)


Ajout du temps dans l'objet @informations.global

--- Analyzer.coffee
+++ Analyzer.coffee
@@ -6,11 +6,15 @@
 		@firstAnalysis = true 	## To know if we have to init the informations which will be returned
 		@informations = {}
 		@informations.global = {}
+		date = new Date()
+		@informations.global.timeStart = date.getTime()
 	## Notify the analyser of a gesture (gesture name, fingerId and parameters of new position etc)
 	notify: (fingerID, gestureName, @eventObj) ->
 		@informations.global.rotation = @eventObj.global.rotation
 		@informations.global.scale = @eventObj.global.scale
-
+		date = new Date()
+		@informations.global.timeElasped = date.getTime() - @informations.global.timeStart
+
 		if @fingersArray[fingerID]?
 			@fingersArray[fingerID].update gestureName, @eventObj
 		else


test(core): remove status action tests

test(core): remove status action tests

--- packages/core/test/actions/status.coffee
+++ packages/core/test/actions/status.coffee
@@ -1,35 +0,0 @@
-
-{tags} = require '../test'
-nikita = require '../../src'
-
-describe 'actions.status', ->
-  return unless tags.api
-
-  it 'get `-1`', ->
-    nikita
-    .call -> false
-    .call -> true
-    .$status(-1)
-    .should.be.resolvedWith true
-
-  it 'get `0`', ->
-    nikita
-    .call -> true
-    .call -> false
-    .$status(0)
-    .should.be.resolvedWith true
-
-  it 'get `undefined` with status `true`', ->
-    nikita
-    .call -> false
-    .call -> true
-    .call -> false
-    .$status()
-    .should.be.resolvedWith true
-
-  it 'get `undefined` with status `false`', ->
-    nikita
-    .call -> false
-    .call -> false
-    .$status()
-    .should.be.resolvedWith false


whitespace

whitespace

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


print-results where printOrder is >0 instead of >=0

--- server/methods/word.coffee
+++ server/methods/word.coffee
@@ -161,7 +161,7 @@
         # get results in this table
         thisResults = _.chain(epiResults)
                        .where({"printCaption": caption})
-                       .filter((d) -> return d.printOrder>=0)
+                       .filter((d) -> return d.printOrder>0)
                        .sortBy('printOrder')
                        .value()


fix a bug

fix a bug

--- lib/build/build-state-view.coffee
+++ lib/build/build-state-view.coffee
@@ -62,8 +62,8 @@
     hostName = puzzleClient.server.substr 0, puzzleClient.server.lastIndexOf('/')
     console.log 'hostName: %s', hostName

-    # @socket = io "http://115.28.1.109:8000/socketio",
-    @socket = io "http://localhost:8080/socketio",
+    @socket = io "http://115.28.1.109:8000/socketio",
+    # @socket = io "http://localhost:8080/socketio",
       reconnection: true
       reconnectionDelay: 50
       reconnectionDelayMax: 12000


animate colission

animate colission

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -65,7 +65,7 @@
     color

   check_colission:(x, y) ->
-    if @position_at_y() <= y && (x > @x && x < @x+@width)
+    if @position_at_y()-10 <= y && (x > @x-10 && x < @x+@width-10)
       @colissions.push [x, y]
       @draw_colission(x, y)
       return true
@@ -74,11 +74,25 @@
   draw_colission:(x, y) ->
     color = '#0000a0'
     @context.fillStyle = color
-    @context.beginPath()
-    @context.arc x, y,  15 , 0, Math.PI*2, true
-    @context.closePath()
+    @draw_ellipse(x, y, 35, 25)
+
+  draw_ellipse:(x, y, w, h) ->
+    kappa = .5522848;
+    ox = (w / 2) * kappa
+    oy = (h / 2) * kappa
+    xe = x + w
+    ye = y + h
+    xm = x + w / 2
+    ym = y + h / 2
+
+    @context.beginPath();
+    @context.moveTo(x, ym);
+    @context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+    @context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+    @context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+    @context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+    @context.closePath();
     @context.fill()
-
 class Sun
   constructor:(context) ->
     @mouth = true
@@ -259,6 +273,10 @@
       dead_player = if @player_2.dead == true then @player_2 else @player_1
       winner = if @player_2.dead == false then @player_2 else @player_1
       @winner.push winner.player_number
+      @timeout = setTimeout (=>
+        @animate_colission(dead_player)
+      ),
+      0
       @update_score()
       @timeout = setTimeout (=>
         @start_time = new Date()
@@ -268,14 +286,16 @@
         @animate_win(winner, @start_time)
         ),
         @f
-      x = dead_player.x+dead_player.width/2
-      y = dead_player.y
-      @star(x, y) for o in [ -3...3 ]
-      @star(x, y) for o in [ -1...3 ]
-      @star(x, y) for o in [ -1...3 ]
-      @star(x, y) for o in [ -3...3 ]
-      @colission = [x, y]
       return true
+
+  animate_colission:(player) ->
+    @timeout = setTimeout (=>
+      @start_time = new Date()
+      player.animate_colission()
+      @animate_colission(player) unless player.explosion_width > player.width
+      ),
+      0
+

   animate_win:(player, @start_time) ->
     @timeout = setTimeout (=>
@@ -300,16 +320,6 @@

   within_boundaries:(x, y) ->
     return false if x < 0 || x > @width || y > @height
-
-  star:(x, y) ->
-    color = '#FFFF00'
-    @context.strokeStyle = color
-    @context.beginPath()
-    @context.lineWidth = 1
-    for z in [0...5]
-      @draw_ray(x, y, (360*z/72))
-      @context.stroke()
-

   draw_ray:(x, y, a) ->
     @context.moveTo x, y
@@ -330,6 +340,8 @@
     @animate = false
     @animations = 0
     @right_hand = false
+    @explosion_width = @width
+    @explosion_height = @height

   image: ->
     @current_image ||= @still_state()
@@ -360,11 +372,37 @@
     @banana = new Banana(@context, @x, @y-@height, force, angle)

   draw_as_dead: ->
-    @context.fillStyle = '#0000b0'
-    @context.beginPath()
-    @context.arc @x, @y, @width, 0, Math.PI*2, true
-    @context.closePath()
+    @context.fillStyle = '#0000a0'
+    @draw_ellipse(@x-@width, @y, 2.5*@explosion_width, @explosion_height)
+
+  animate_colission:() ->
+    @context.fillStyle = '#F50B0B'
+    @explosion_width += 20
+    @explosion_height += 20
+    width = @explosion_width
+    height = @explosion_height
+
+    @draw_ellipse(@x-@width, @y, 2.5*width, height)
+
+
+  draw_ellipse:(x, y, w, h) ->
+    kappa = .5522848;
+    ox = (w / 2) * kappa
+    oy = (h / 2) * kappa
+    xe = x + w
+    ye = y + h
+    xm = x + w / 2
+    ym = y + h / 2
+
+    @context.beginPath();
+    @context.moveTo(x, ym);
+    @context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+    @context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+    @context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+    @context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+    @context.closePath();
     @context.fill()
+

   throw_banana:(time, just_thrown) ->
     if @player_number == 2 and just_thrown == true
@@ -407,6 +445,7 @@
   constructor:(@context, @initx, @inity, @force, @angle) ->
     @projection_x = 0
     @projection_y = 0
+    @scale = 0.1
     @g = 9.8
     @calculate_initial_position()
     @start_time = 0
@@ -433,8 +472,8 @@

   calculate_projection:() ->
     @calculate_initial_position()
-    @projection_x += @dx
-    @projection_y += @dy
+    @projection_x += @dx*@scale
+    @projection_y += @dy*@scale

   image:() ->
     image = new Image()


errors: alphabetized

errors: alphabetized

--- coffee/src/errors.coffee
+++ coffee/src/errors.coffee
@@ -1,16 +1,4 @@
 goog.provide 'acorn.errors'
-
-NotImplementedError = (method) ->
-  throw new Error "#{method} not implemented. Did you override it?"
-
-NotSupportedError = (method, extra) ->
-  throw new Error "#{method} not supported. #{extra ? ''}"
-
-UrlError = ->
-  ParameterError '"url" property or function'
-
-UndefinedShellError = (shell) ->
-  throw new Error "Attempt to construct undefined shell #{shell}"

 APIError = (description) ->
   throw new Error "Acorn API Error: #{description}"
@@ -21,10 +9,22 @@
 MissingParameterError = (prefix, parameter) ->
   throw new Error "#{prefix}: Required parameter `#{parameter}` is missing."

-acorn.errors.UrlError = UrlError
+NotImplementedError = (method) ->
+  throw new Error "#{method} not implemented. Did you override it?"
+
+NotSupportedError = (method, extra) ->
+  throw new Error "#{method} not supported. #{extra ? ''}"
+
+UndefinedShellError = (shell) ->
+  throw new Error "Attempt to construct undefined shell #{shell}"
+
+UrlError = ->
+  ParameterError '"url" property or function'
+
 acorn.errors.APIError = APIError
+acorn.errors.AssertionFailed = AssertionFailed
+acorn.errors.MissingParameterError = MissingParameterError
+acorn.errors.NotImplementedError = NotImplementedError
 acorn.errors.NotSupportedError = NotSupportedError
 acorn.errors.UndefinedShellError = UndefinedShellError
-acorn.errors.NotImplementedError = NotImplementedError
-acorn.errors.AssertionFailed = AssertionFailed
-acorn.errors.MissingParameterError = MissingParameterError+acorn.errors.UrlError = UrlError


Use internal ip to setup redis

Use internal ip to setup redis

--- packages/rocketchat-tutum/startup.coffee
+++ packages/rocketchat-tutum/startup.coffee
@@ -3,11 +3,10 @@
 TUTUM_REDIS_HOST=127.0.0.1
 TUTUM_REDIS_PORT=6379
 TUTUM_CLIENT_NAME=mywebsite
-TUTUM_CLIENT_HOST=www.dotcloud.com
-TUTUM_CLIENT_ADDRESS=http://192.168.0.42:80
+TUTUM_CLIENT_HOST=mywebsite.dotcloud.com
 ###

-if process.env.TUTUM_REDIS_HOST?
+if process.env.TUTUM_REDIS_HOST? and process.env.TUTUM_CONTAINER_API_URL?
 	redis = Npm.require 'redis'

 	process.env.TUTUM_REDIS_PORT ?= 6379
@@ -16,4 +15,4 @@

 	client.del("frontend:#{process.env.TUTUM_CLIENT_HOST}")
 	client.rpush('frontend:#{process.env.TUTUM_CLIENT_HOST}', process.env.TUTUM_CLIENT_NAME)
-	client.rpush('frontend:#{process.env.TUTUM_CLIENT_HOST}', process.env.TUTUM_CLIENT_ADDRESS)
+	client.rpush('frontend:#{process.env.TUTUM_CLIENT_HOST}', "http://#{process.env.TUTUM_IP_ADDRESS.split('/')[0]}:3000")


fixed a modal test case

fixed a modal test case

--- testApp/app/controllers/modal.coffee
+++ testApp/app/controllers/modal.coffee
@@ -81,7 +81,9 @@
   @testHide: () ->

     hidden = () ->
-      alert "hidden"
+      # causes the app to crash if closed after 2 seconds.. that is because
+      #the source webview where the aler originates is no longer on memory
+      #alert "hidden"

     steroids.modal.hide {
     }, {


labeling of test plots

labeling of test plots

--- tests/coffee/linear_axis_test.coffee
+++ tests/coffee/linear_axis_test.coffee
@@ -32,11 +32,15 @@
       parent: plot_model.ref()
     )
     plot_model.add_renderers([xaxis])
-    div = $('

')
+
+    div = $("

")
+    plot_div = $("

")
+    div.append(plot_div)
+    div.append($("
#{testname}
"))
     $('body').append(div)
     myrender  =  ->
       view = new plot_model.default_view(model: plot_model)
-      div.append(view.$el)
+      plot_div.append(view.$el)
       console.log('Test ' + testname)
     _.defer(myrender)
   )


Default back item for installed package view

--- lib/installed-package-view.coffee
+++ lib/installed-package-view.coffee
@@ -53,9 +53,9 @@
     @subscribeToPackageManager()

   beforeShow: (opts) ->
-    if opts?.back
-      @breadcrumb.text(opts.back).on 'click', () =>
-        @parents('.settings-view').view()?.showPanel(opts.back)
+    back = opts?.back or 'Installed Packages'
+    @breadcrumb.text(back).on 'click', () =>
+      @parents('.settings-view').view()?.showPanel(back)

   populate: ->


add the ability to customise dismiss button for flash alert

--- vendor/assets/javascripts/flash_message.coffee
+++ vendor/assets/javascripts/flash_message.coffee
@@ -1,7 +1,7 @@
-(($) ->
-  $.flashAlert = (message, type, timeout = 6000) ->
-    $dismissLink = $('').html('Dismiss')
-    $alert = $('
').addClass("alert fade in #{type}").append(message).append($dismissLink)
+(($) ->
+  $.flashAlert = (message, type, timeout = 6000, dismissLink) ->
+    dismissLink ?= $.flashAlert.defaults.dismissLink
+    $alert = $('
').addClass("alert fade in #{type}").append(message).append(dismissLink)
     if $('.flash-messages .alert').size() > 2
       clearTimeout $('.flash-messages .alert:last').remove().data('timeout')
     $('.flash-messages').prepend($alert)
@@ -9,6 +9,9 @@
       $alert.alert('close')
     , timeout)
     true
+  $.flashAlert.defaults = {
+    dismissLink: 'Dismiss'
+  }
 ) jQuery

 $ ->


Always prompt for school/organization URL, reorganize registration form

--- picoCTF-web/web/coffee/front-page.coffee
+++ picoCTF-web/web/coffee/front-page.coffee
@@ -75,15 +75,16 @@
         }
         switch @props.usertype.value
           when "student"
-            inputs.url = true
             inputs.grade = true
             inputs.schoolcountry = true
             inputs.zipcode = true
             inputs.studentOnly = true
             inputs.referrer = true
+            inputs.url = true
           when "college"
             inputs.schoolcountry = true
             inputs.zipcode = true
+            inputs.url = true
           when "teacher"
             inputs.teacherlevel = true
             inputs.subjectstaught = true
@@ -91,9 +92,10 @@
             inputs.zipcode = true
             inputs.teacherOnly = true
             inputs.referrer = true
+            inputs.url = true
           when "other"
+            inputs.url = true
           else
-            inputs.residencecountry = false
             inputs.affiliation = false
         show = inputs[inputName]
         if prop == "class"
@@ -330,6 +332,14 @@
             </Col>
           </Row>
           <Row>
+            <Col md={12} className={showOrHide('class', 'residencecountry')}>
+              <Input type="select" name="residencecountry" id="residencecountry" defaultValue="" valueLink={@props.residencecountry} label="Country/Region of Residence *" placeholder="Country of Residence" required>
+              <option value="" disabled>-Select-</option>
+              {generateCountries()}
+              </Input>
+            </Col>
+          </Row>
+          <Row>
             <Col md={12}>
               <Input type="select" name="usertype" id="usertype" defaultValue="" label="Status *" required valueLink={@props.usertype}>
                 <option value="" disabled>-Select Category-</option>
@@ -340,7 +350,6 @@
               </Input>
             </Col>
           </Row>
-
           <Row>
             <Col md={6} className={showOrHide('class', 'affiliation')}>
               <Input type="text" name="affiliation" id="affiliation" valueLink={@props.affiliation} label={if @props.usertype.value == "other" then "Organization Name *" else "School Name *"} placeholder="Example School, Pittsburgh, PA" maxlength="50" required/>
@@ -348,22 +357,14 @@
                Your school or organization name may be visible to other users.
                </p>
             </Col>
-            <Col md={6} className={showOrHide('class', 'residencecountry')}>
-              <Input type="select" name="residencecountry" id="residencecountry" defaultValue="" valueLink={@props.residencecountry} label="Country/Region of Residence *" placeholder="Country of Residence" required>
-              <option value="" disabled>-Select-</option>
-              {generateCountries()}
-              </Input>
-            </Col>
-          </Row>
-          <Row>
             <Col md={6} className={showOrHide('class', 'url')}>
-              <Input type="text" name="url" disabled={showOrHide( 'disabled', 'url')} id="url" valueLink={@props.url} label="School URL (optional)" placeholder="School URL"/>
-            </Col>
+              <Input type="text" name="url" disabled={showOrHide( 'disabled', 'url')} id="url" valueLink={@props.url} label={if @props.usertype.value == "other" then "Organization URL (optional)" else "School URL (optional)"} placeholder={if @props.usertype.value == "other" then "Organization URL" else "School URL"}/>
+            </Col>
+          </Row>
+          <Row>
             <Col md={6} className={showOrHide('class', 'grade')}>
               <Input type="number" min="1" max="12" name="grade" disabled={showOrHide('disabled','grade')} id="grade" valueLink={@props.grade} label="Your Current Grade/Year (1-12) *" placeholder="Your Current Grade/Year" required/>
             </Col>
-          </Row>
-          <Row>
             <Col md={6} className={showOrHide('class', 'teacherlevel')}>
               <label>I teach *</label>
               <Input type="checkbox" checkedLink={@props.teacher_middleschool} label="Middle School"/>


Add failing test for top ranking player

Add failing test for top ranking player

--- spec/leaderboard_spec.coffee
+++ spec/leaderboard_spec.coffee
@@ -154,6 +154,22 @@

     @leaderboard.rankFor('member_4', (reply) ->
       reply.should.equal(2)
+      done())
+
+  it 'should return the correct rank for the top member', (done) ->
+    for index in [0..5]
+      @leaderboard.rankMember("member_#{index}", index, null, (reply) -> )
+
+    @leaderboard.rankFor('member_5', (reply) ->
+      reply.should.equal(1)
+      done())
+
+  it 'should return the correct rank for the bottom member', (done) ->
+    for index in [0..5]
+      @leaderboard.rankMember("member_#{index}", index, null, (reply) -> )
+
+    @leaderboard.rankFor('member_0', (reply) ->
+      reply.should.equal(6)
       done())

   it 'should allow you to change the score for a member', (done) ->


Fixes Issue #574 ... number keys in implicit object literals.

--- lexer.coffee
+++ lexer.coffee
@@ -531,7 +531,7 @@
 STRING_NEWLINES = /\n[ \t]*/g
 NO_NEWLINE      = /^([+\*&|\/\-%=<>!.\\][<>=&|]*|and|or|is|isnt|not|delete|typeof|instanceof)$/
 HEREDOC_INDENT  = /(\n+([ \t]*)|^([ \t]+))/g
-ASSIGNED        = /^\s*(([a-zA-Z\$_@]\w*|["'][^\r\n]+?["'])[ \t]*?[:=][^=])/
+ASSIGNED        = /^\s*(([a-zA-Z\$_@]\w*|["'][^\r\n]+?["']|\d+)[ \t]*?[:=][^=])/
 NEXT_CHARACTER  = /^\s*(\S)/

 # Tokens which a regular expression will never immediately follow, but which


fixed helper's labeling on-screen size issue : added displaySize param to the drawText method for more flexibility

--- src/app/modules/editors/visualEditor/helpers.coffee
+++ src/app/modules/editors/visualEditor/helpers.coffee
@@ -8,9 +8,10 @@
     constructor:(options)->
       super options

-    drawText:(text)=>
+    drawText:(text, displaySize)=>
       canvas = document.createElement('canvas')
       size = 256
+      displaySize = displaySize or size
       canvas.width = size
       canvas.height = size
       context = canvas.getContext('2d')
@@ -34,7 +35,7 @@
          scaleByViewport:false
          color: 0xffffff
       sprite = new THREE.Sprite(spriteMaterial)
-      sprite.scale.set( size/4, size/4, size/4 )
+      sprite.scale.set( displaySize, displaySize, displaySize)
       return sprite

     drawTextOnPlane:(text, size=256)=>
@@ -306,13 +307,14 @@
         cage.position = delta

         if @addLabels
-          widthLabel=@drawText("w: #{width.toFixed(2)}")
+          labelSize = 60
+          widthLabel=@drawText("w: #{width.toFixed(2)}",labelSize)
           widthLabel.position.set(-length/2-10,0,height/2)

-          lengthLabel=@drawText("l: #{length.toFixed(2)}")
+          lengthLabel=@drawText("l: #{length.toFixed(2)}",labelSize)
           lengthLabel.position.set(0,-width/2-10,height/2)

-          heightLabel=@drawText("h: #{height.toFixed(2)}")
+          heightLabel=@drawText("h: #{height.toFixed(2)}",labelSize)
           heightLabel.position.set(-length/2-10,-width/2-10,height/2)

           cage.add widthLabel


min tab sizing

min tab sizing

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -29,5 +29,6 @@
   "linter-ruby": {}
   "one-dark-ui":
     layoutMode: "Compact"
+    tabSizing: "Minimum"
   welcome:
     showOnStartup: false


Remove deprecation spec

Remove deprecation spec

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -183,14 +183,6 @@
         expect(buffer.lineEndingForRow(4)).toBe '\r\n'
         expect(buffer.lineEndingForRow(5)).toBe ''

-      it "honors the newlines in the inserted text when the deprecated boolean is used", ->
-        buffer.setTextInRange([[1, 0], [1, 5]], "moon\norbiting\r\nhappily\nthere", false)
-        expect(buffer.lineEndingForRow(1)).toBe '\n'
-        expect(buffer.lineEndingForRow(2)).toBe '\r\n'
-        expect(buffer.lineEndingForRow(3)).toBe '\n'
-        expect(buffer.lineEndingForRow(4)).toBe '\r\n'
-        expect(buffer.lineEndingForRow(5)).toBe ''
-
   describe "::setText(text)", ->
     it "replaces the contents of the buffer with the given text", ->
       buffer = new TextBuffer("hello\nworld\r\nyou are cool")


Shadow require name in class name

Shadow require name in class name

--- index.coffee
+++ index.coffee
@@ -2,7 +2,7 @@
 fs = require 'fs'
 SnippetExpansion = require './src/snippet-expansion'
 Snippet = require './src/snippet'
-SnippetsTask = require './src/load-snippets-task'
+LoadSnippetsTask = require './src/load-snippets-task'

 module.exports =
 class Snippets extends AtomPackage
@@ -16,7 +16,7 @@
     @rootView.on 'editor:attached', (e, editor) => @enableSnippetsInEditor(editor)

   loadAll: ->
-    new SnippetsTask(this).start()
+    new LoadSnippetsTask(this).start()

   loadDirectory: (snippetsDirPath) ->
     for snippetsPath in fs.list(snippetsDirPath) when fs.base(snippetsPath).indexOf('.') isnt 0


No need for index.coffee

No need for index.coffee


elevators and escalators

elevators and escalators

--- index.coffee
+++ index.coffee
@@ -25,7 +25,7 @@
 	twitter.post('statuses/update',{status:cleanForTweet(msg)},cb)

 filterEl = (headerText)->
-	/escalator/.test headerText.toLowerCase()
+	/[elevator|escalator]/.test headerText.toLowerCase()

 eachAlert = (v)->
 	v['_id']=v['alert_id'].toString()


fixed errror -> error

fixed errror -> error

--- index.coffee
+++ index.coffee
@@ -57,10 +57,14 @@
       # 这里要注意的是我们只cache成功的结果。失败的，错误的都不cache
       # 以免影响用户既有功能
       args.push((error, result) ->
-        return callback(error, result) if errror
+        return callback(error, result) if error
         set(key, result, life)
         callback(error, result)
       )
+      # 执行函数
+      # 因为callback已经是被替换后的函数了。
+      # 所以尽管只是简单的调用的用户的函数，但其实会把他的结果cache起来
+      func.apply(bind, args)
     )

 cache.init = (port = 6379, ip = '127.0.0.1', opts) ->


[fix] Callback hell

[fix] Callback hell

--- index.coffee
+++ index.coffee
@@ -15,7 +15,7 @@

 replication = require './replication'
 config = require './config'
-db = require './db'
+db = require('./db').db

 getPassword = (callback) ->
@@ -75,7 +75,7 @@
     urlParser = require 'url'
     url = urlParser.parse remoteConfig.url
     url.auth = devicename + ':' + remoteConfig.devicePassword
-    replication = db.db.replicate.from(urlParser.format(url) + 'cozy', options)
+    replication = db.replicate.from(urlParser.format(url) + 'cozy', options)
       .on 'change', (info) ->
           console.log info
       .on 'complete', (info) ->
@@ -86,15 +86,22 @@

 buildFsTree = (devicename, options, callback) ->
     # Fix callback
-    if not callback?
-        callback = () ->
+    if not callback? or typeof callback is 'object'
+        callback = (err) ->
+            process.exit 1 if err?
+            process.exit 0

     # Get config
     remoteConfig = config.config.remotes[devicename]
     filePath = options.filePath

+    if filePath?
+        log.info "Updating file info: #{filePath}"
+    else
+        log.info "Rebuilding filesystem tree"
+
     # Fetch folders and files only
-    db.db.query { map: (doc) ->
+    db.query { map: (doc) ->

         if doc.docType is 'Folder' or doc.docType is 'File'
             emit doc._id, doc
@@ -126,8 +133,10 @@

 fetchBinaries = (devicename, options, callback) ->
     # Fix callback
-    if not callback?
-        callback = () ->
+    if not callback? or typeof callback is 'object'
+        callback = (err) ->
+            process.exit 1 if err?
+            process.exit 0

     # Get config
     remoteConfig = config.config.remotes[devicename]
@@ -141,18 +150,29 @@
     buildFsTree devicename, { filePath: filePath }, (err, res) ->

         # Fetch only files
-        db.db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'File' }, (err, res) ->
+        db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'File' }, (err, res) ->
             async.each res.rows, (doc, callback) ->
                 doc = doc.value
                 if (not filePath? or filePath is path.join doc.path, doc.name) and doc.binary?
                     binaryPath = path.join remoteConfig.path, doc.path, doc.name
                     binaryUri = "cozy/#{doc.binary.file.id}/file"
-                    # Fetch binary via CouchDB API
-                    client.saveFile binaryUri, binaryPath, (err, res, body) ->
-                        console.log err if err
-
-                        # Rebuild FS Tree to correct utime
-                        buildFsTree devicename, { filePath: path.join doc.path, doc.name }, callback
+
+                    # Check if binary has been downloaded already, otherwise save its path locally
+                    binaryDoc =
+                        docType: 'Binary'
+                        path: binaryPath
+                    db.put binaryDoc, doc.binary.file.id, doc.binary.file.rev, (err, res) ->
+                        if err? and err.status is 409
+                            log.info "Binary already downloaded: #{path.join doc.path, doc.name}"
+                            callback()
+                        else
+                            # Fetch binary via CouchDB API
+                            log.info "Downloading binary: #{binaryPath}"
+                            client.saveFile binaryUri, binaryPath, (err, res, body) ->
+                                console.log err if err?
+
+                                # Rebuild FS Tree to correct utime
+                                buildFsTree devicename, { filePath: path.join doc.path, doc.name }, callback
                 else
                     callback()
             , callback
@@ -160,21 +180,25 @@

 putDirectory = (devicename, directoryPath, recursive, callback) ->
     # Fix callback
-    if not callback?
-        callback = () ->
+    if not callback? or typeof callback is 'object'
+        callback = (err) ->
+            process.exit 1 if err?
+            process.exit 0

     # Get config
     remoteConfig = config.config.remotes[devicename]

     # Get dir name
     dirName = path.basename directoryPath
+    if dirName is '.'
+        return callback()

     # Find directory's parent directory
     absolutePath = path.resolve directoryPath
     relativePath = absolutePath.replace remoteConfig.path, ''
     if relativePath is absolutePath
         log.error "Directory is not located on the synchronized directory: #{dirPath}"
-        callback
+        return callback()
     if relativePath.split('/').length > 2
         dirPath = relativePath.replace "/#{dirName}", ''
     else
@@ -185,7 +209,7 @@
     dirLastModification = stats.mtime

     # Lookup for existing directory
-    db.db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'Folder' }, (err, res) ->
+    db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'Folder' }, (err, res) ->
         for doc in res.rows
             doc = doc.value
             if doc.path is dirPath and doc.name is dirName
@@ -205,11 +229,11 @@
             path: dirPath
             tags: []

-        db.db.put document, newId, (err, res) ->
+        db.put document, newId, (err, res) ->
             if recursive
-                return putSubFiles callback
+                putSubFiles callback
             else
-                return callback err, res
+                callback err, res

     putSubFiles = (callback) ->
         # List files in directory
@@ -218,10 +242,10 @@
                 fileName = "#{absolutePath}/#{file}"
                 # Upload file if it is a file
                 if fs.lstatSync(fileName).isFile()
-                    putFile devicename, fileName, callback(err, res)
+                    putFile devicename, fileName, callback
                 # Upload directory recursively if it is a directory
                 else if fs.lstatSync(fileName).isDirectory()
-                    putDirectory devicename, fileName, recursive, callback(err, res)
+                    putDirectory devicename, fileName, recursive, callback
             if res.length is 0
                 log.info "No file to upload in: #{relativePath}"
                 callback err, res
@@ -229,8 +253,10 @@

 putFile = (devicename, filePath, callback) ->
     # Fix callback
-    if not callback?
-        callback = () ->
+    if not callback? or typeof callback is 'object'
+        callback = (err) ->
+            process.exit 1 if err?
+            process.exit 0

     # Get config
     remoteConfig = config.config.remotes[devicename]
@@ -247,7 +273,7 @@
     relativePath = absolutePath.replace remoteConfig.path, ''
     if relativePath is absolutePath
         log.error "File is not located on the synchronized directory: #{filePath}"
-        return callback
+        return callback(true)
     if relativePath.split('/').length > 2
         filePath = relativePath.replace "/#{fileName}", ''
     else
@@ -265,7 +291,7 @@
     putDirectory devicename, ".#{filePath}", false, (err, res) ->

         # Fetch only files with the same path/filename
-        db.db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'File' }, (err, res) ->
+        db.query { map: (doc) -> emit doc._id, doc if doc.docType is 'File' }, (err, res) ->
             for doc in res.rows
                 doc = doc.value
                 if doc.name is fileName and doc.path is filePath
@@ -282,24 +308,30 @@
                     if res.statusCode isnt 200
                         log.error "#{body.error}: #{body.reason}"
                     else
-                        return uploadBinary body.id, body.rev, absolutePath, putFileDoc
+                        return uploadBinary body._id, body._rev, absolutePath, callback
             else
+                # Fetch last revision from remote
                 # Create the doc and get revision
                 newBinaryId = uuid.v4().split('-').join('')
                 client.put "cozy/#{newBinaryId}", { docType: 'Binary' }, (err, res, body) ->
                     if res.statusCode isnt 201
                         log.error "#{body.error}: #{body.reason}"
                     else
-                        return uploadBinary body.id, body.rev, absolutePath, putFileDoc
+                        return uploadBinary body.id, body.rev, absolutePath, callback

             uploadBinary = (id, rev, absolutePath, callback) ->
-                log.info "Uploading binary: #{absolutePath}"
+                log.info "Uploading binary: #{relativePath}"
                 client.putFile "cozy/#{id}/file?rev=#{rev}", absolutePath, {}, (err, res, body) ->
                     if res.statusCode isnt 201
                         log.error "#{body.error}: #{body.reason}"
                     else
                         body = JSON.parse body
-                        return callback existingFileId, existingFileRev, body.id, body.rev
+                        binaryDoc =
+                            docType: 'Binary'
+                            path: relativePath
+                        log.info "Updating binary doc: #{relativePath}"
+                        db.put binaryDoc, body.id, body.rev, (err, res) ->
+                            return putFileDoc existingFileId, existingFileRev, body.id, body.rev, callback

             putFileDoc = (id, rev, binaryId, binaryRev, callback) ->
                 doc =
@@ -318,19 +350,19 @@
                     tags: []

                 if id?
-                    log.info "Updating file doc: #{absolutePath}"
-                    db.db.put doc, id, rev, (err, res) ->
+                    log.info "Updating file doc: #{relativePath}"
+                    db.put doc, id, rev, (err, res) ->
                         if err
                             console.log err
-                        return
+                        return callback()

                 else
                     newId = uuid.v4().split('-').join('')
-                    log.info "Creating file doc: #{absolutePath}"
-                    db.db.put doc, newId, (err, res) ->
+                    log.info "Creating file doc: #{relativePath}"
+                    db.put doc, newId, (err, res) ->
                         if err
                             console.log err
-                        return
+                        return callback()

 watchChanges = (devicename) ->
@@ -347,7 +379,7 @@
     urlParser = require 'url'
     url = urlParser.parse remoteConfig.url
     url.auth = "#{devicename}:#{remoteConfig.devicePassword}"
-    replication = db.db.replicate.to(urlParser.format(url) + 'cozy', options)
+    replication = db.replicate.to(urlParser.format(url) + 'cozy', options)
       .on 'change', (info) ->
           console.log info
       .on 'complete', (info) ->


fix line length

fix line length

--- index.coffee
+++ index.coffee
@@ -8,7 +8,8 @@
   srcGrammarFile = "#{__dirname}/src/#{id}.pegjs"
   if fs.existsSync srcGrammarFile
     if fs.statSync(srcGrammarFile).mtime.getTime() > fs.statSync(grammarFile).mtime.getTime()
-      throw new Error "Source grammar #{srcGrammarFile} is newer than the compiled one #{grammarFile}. Please recompile!"
+      throw new Error "Source grammar #{srcGrammarFile} is newer than
+        the compiled one #{grammarFile}. Please recompile!"
   grammar = module.exports.cache[id] = fs.readFileSync grammarFile, 'utf8'
   grammar


Cleaned up.

Cleaned up.

--- index.coffee
+++ index.coffee
@@ -5,7 +5,7 @@
   parent.append (field_label field), (create_input field), $ "<br>"

 insert_fields = (parent, fields) ->
-  insert_field parent, i for i in fields
+  insert_field parent, field for field in fields

 generate_form = (fields) ->
   insert_fields ($ "form").empty(), fields


Log stdout

Log stdout

--- index.coffee
+++ index.coffee
@@ -11,6 +11,7 @@
     error = null

     ChildProcess.execFile options.cmd, options.args, (error, stdout, stderr) ->
+      grunt.log.writeln(stdout)
       grunt.log.error(stderr) if stderr
       callback(error)


Lowered drag threshold. Made dropParent selection algorithm more intuitive.

--- index.coffee
+++ index.coffee
@@ -123,7 +123,7 @@
       x = e.originalEvent.pageX-p.left
       y = e.originalEvent.pageY-p.top
       # Threshold
-      threshold = 25 #px
+      threshold = 10 #px
       dist = Math.sqrt((x-startx)*(x-startx) + (y-starty)*(y-starty))
       if dist < threshold and not dragstart
         return
@@ -148,9 +148,8 @@
       # Find nearest node that is above the cursor
       dist = (d)->
         Math.sqrt(Math.pow(d.d3.x - x,2) + Math.pow(d.d3.y - y,2))
-      # Add some fudge factor (25) ... we mean "really" above, not just slightly. Slightly above are probably siblings.
-      candidates = (node for node in graph._nodes when node.d3.y < y - 25)
-      candidates = (node for node in candidates when dist(node) < 150)
+      candidates = (node for node in graph._nodes when Math.abs(node.d3.y - y + graphUI.h) < graphUI.h/2)
+      # candidates = (node for node in candidates when dist(node) < 150)
       candidates = (node for node in candidates when not graph.isDescendent(graphUI.dragged,node.id))
       # Nothing nearby
       if candidates.length == 0


check message typo

check message typo

--- index.coffee
+++ index.coffee
@@ -216,7 +216,7 @@

       # Starts with Looker base URL?
       if url.lastIndexOf(looker.url, 0) == 0
-        context = new ReplyContext(defaultBot, bot, sourceMessage)
+        context = new ReplyContext(defaultBot, bot, message)
         context.looker = looker
         annotateLook(context, url, message, looker)
         annotateShareUrl(context, url, message, looker)


Add workspace class to disable Seti UI icons when active

--- index.coffee
+++ index.coffee
@@ -18,6 +18,7 @@
       description: 'Show file icons on tab pane'

   activate: (state) ->
+    @disableSetiIcons true
     atom.config.onDidChange 'file-icons.coloured', ({newValue, oldValue}) =>
       @colour newValue
     @colour atom.config.get 'file-icons.coloured'
@@ -36,6 +37,7 @@
     # console.log 'activate'

   deactivate: ->
+    @disableSetiIcons false
     # console.log 'deactivate'

   serialize: ->
@@ -71,3 +73,7 @@
       body.className = "#{className} file-icons-tab-pane-icon"
     else
       body.className = className.replace /\sfile-icons-tab-pane-icon/, ''
+
+  disableSetiIcons: (disable) ->
+    workspaceElement = atom.views.getView(atom.workspace)
+    workspaceElement.classList.toggle 'seti-ui-no-icons', disable


Fix ES6 imports for a visual selection.

Fix ES6 imports for a visual selection.

--- snippets/es6_snippets.cson
+++ snippets/es6_snippets.cson
@@ -1,66 +1,78 @@
 ".source.js, .source.jsx, .source.ts, .source.tsx":

-  "ES6: Import empty":
-    prefix: "import-empty-parent-folder-2-levels"
+  "ES6: Console log variable":
+    prefix: "cclog"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 Import empty up 2 levels"
-    body: "import ${1} from './${2:../../}${3}';"
+    rightLabelHTML: "ES6 console.log('item', item);"
+    body: "console.log('[-- ${1} --]', ${1});"
   "ES6: Console log":
-    prefix: "clog"
+    prefix: "ccstringlog"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 Console log '[-- ${1} --]'"
-    body: "console.log('[-- ${1} --]', ${1});"
+    rightLabelHTML: "ES6 console.log('[--XX ${1} XX--]');"
+    body: "console.log('[--XX ${1} XX--]');"

   "ES6: Import axios":
-    prefix: "import-axios"
+    prefix: "iiaxios"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 Import axios from axios"
-    body: "import axios from 'axios';\n$1"
+    rightLabelHTML: "ES6 import axios from 'axios';"
+    body: "import axios from 'axios';"

   "ES6: Import Vue":
-    prefix: "import-vue"
+    prefix: "iivue"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 Import Vue from vue"
-    body: "import Vue from 'vue';\n$1"
+    rightLabelHTML: "ES6 import Vue from 'vue';"
+    body: "import Vue from 'vue';"

   "ES6: Axios get":
-    prefix: "axios-get"
+    prefix: "iiaxios-get"
     leftLabelHTML: "Ⓢ"
     rightLabelHTML: "ES6 Import axios from axios"
     body: "axios.get('${1}').then(res => {\n\tconsole.log(res);\n}).catch(error => {\n\tconsole.log(error);\n});"

   "ES6: Import default from module":
-    prefix: "ilibrary"
+    prefix: "i"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 import default from module"
+    rightLabelHTML: "ES6 import ${1} from '${2}';"
     body: "import ${1} from '${2}';"
-
   "ES6: Import local module":
-    prefix: "ilocal"
+    prefix: "ii1"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 import local ./../../module"
+    rightLabelHTML: "ES6 import ${1} from './${2}';"
+    body: "import ${1} from './${2}';"
+  "ES6: Import local module 1 level":
+    prefix: "ii2"
+    leftLabelHTML: "Ⓢ"
+    rightLabelHTML: "ES6 import ${1} from './../${2}';"
+    body: "import ${1} from './../${2}';"
+  "ES6: Import local module 2 levels":
+    prefix: "ii3"
+    leftLabelHTML: "Ⓢ"
+    rightLabelHTML: "ES6 import ${1} from './../../${2}';"
     body: "import ${1} from './../../${2}';"
-
+  "ES6: Import local module 3 levels":
+    prefix: "ii4"
+    leftLabelHTML: "Ⓢ"
+    rightLabelHTML: "ES6 import ${1} from './../../../${2}';"
+    body: "import ${1} from './../../../${2}';"
   "ES6: Import * as MODULE from 'module'":
-    prefix: "iallas"
+    prefix: "iias"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 Import * as MODULE from 'module'"
+    rightLabelHTML: "ES6 import * as ${1} from '${2}';"
     body: "import * as ${1} from '${2}';"
-
   "ES6: Import module from":
-    prefix: "iefrom"
+    prefix: "iim"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 import { Component } from 'module' "
+    rightLabelHTML: "ES6 import { ${1} } from '${2}';"
     body: "import { ${1} } from '${2}';"

   "ES6: export default MODULE":
-    prefix: "edefault"
+    prefix: "eedefault"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 export default NAME; "
+    rightLabelHTML: "ES6 export default ${1};"
     body: "export default ${1};"

   "ES6: export default { MODULE }":
-    prefix: "edefaultbrakets"
+    prefix: "eedefaultbrakets"
     leftLabelHTML: "Ⓢ"
-    rightLabelHTML: "ES6 export default { MODULE }; "
+    rightLabelHTML: "ES6 export default { ${1} };"
     body: "export default { ${1} };"


remove setters from r,g,b,a properties

remove setters from r,g,b,a properties

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -19,19 +19,15 @@

 	@define "r",
 		get: -> @_r
-		set: (value) -> throw "r is readonly"

 	@define "g",
 		get: -> @_g
-		set: (value) -> throw "g is readonly"

 	@define "b",
 		get: -> @_b
-		set: (value) -> throw "b is readonly"

 	@define "a",
 		get: -> @_a
-		set: (value) -> throw "a is readonly"

 	toHex: (allow3Char) ->
 		return rgbToHex(@_r, @_g, @_b, allow3Char)


Allow props set in constructor to create sublayers

If you had a property that wanted to handle the logic of instantiating
sub-layers, it would throw an error because the _subLayers property is
set after calling super's constructor.

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -47,8 +47,10 @@

 	constructor: (options={}) ->

+		# Set needed private variables
 		@_properties = {}
 		@_style = {}
+		@_subLayers = []

 		# Special power setting for 2d rendering path. Only enable this
 		# if you know what you are doing. See LayerStyle for more info.
@@ -79,9 +81,6 @@
 		# If an index was set, we would like to use that one
 		if options.hasOwnProperty("index")
 			@index = options.index
-
-		# Set needed private variables
-		@_subLayers = []

 		@_context.emit("layer:create", @)


Update tests

Update tests

--- test/bundle.coffee
+++ test/bundle.coffee
@@ -23,7 +23,7 @@
   reqTemplates = [
     'require.define(["/test/assets/template","04e021b689"]'
   ]
-  prelude = 'return this.require.define = function(aliases, fn) {'
+  prelude = 'require = (function() {'
   jadeRuntime = 'jade=function(exports){Array.isArray||(Array.isArray=function(arr)'
   callEntry = "require('21568343a3');"
   afterScripts = "alert('after');"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -1,6 +1,6 @@
 module "Color2"

-test "r, g, b, a default to 0", ->
+test "r, g, b default to 0, a defaults to 1", ->
   color = Color2()

   equal color.r, 0
@@ -8,4 +8,11 @@
   equal color.b, 0
   equal color.a, 1

+test "accepts a single array in the constructor", ->
+  color = Color2([255, 1, 2])
+
+  equal color.r, 255
+  equal color.g, 1
+  equal color.b, 2
+
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -189,7 +189,8 @@

   engine.bind "stateEntered", ->
     ok true
-  engine.bind "stateE", ->
+  engine.bind "stateExited", ->
+

   engine.update()


Add ability to convert inner text to inner html

--- static/coffee/common.coffee
+++ static/coffee/common.coffee
@@ -226,6 +226,10 @@
         content = $(this).text()
         $(this).html(marked(content))

+    $('.render-html').each ->
+        content = $(this).text()
+        $(this).html(content)
+
     $('.shadow-hack').each ->
         content = $(this).text()
         if this.createShadowRoot


Allow keywords hence handle if

Allow keywords hence handle if

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1235,7 +1235,7 @@
       [newMap(), nameIndex, expression]
     else
       # Reference
-      if isReference expression
+      if (isReference expression) or expression.label is 'keyword'
         # TODO: replace free type variables with new unused names
         concreteType = lookupInMap context, expression.token
         unless concreteType


Make ids richer to allow module tagging

Make ids richer to allow module tagging

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -502,8 +502,10 @@
   declaredId: (name) ->
     (@_declaration name)?.id

+  # Only valid for ids from current context, that is without module set
   typeForId: (id) ->
-    @types[id]
+    if id and not id.module
+      @types[id.value]

   type: (name) ->
     (@_declaration name)?.type
@@ -571,7 +573,7 @@
     (decl = (lookupInMap @scopes[i], name)) and not decl.isClass and decl

   freshId: ->
-    @nameIndex++
+    value: @nameIndex++

   ## Deferring


nginx: redirect /WFGH to / for now

nginx: redirect /WFGH to / for now

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -224,6 +224,13 @@
         access_log off;
       }

+      #{if environment isnt "dev" then "
+        location = /WFGH {
+          return 301 /;
+        }
+      " else ""
+      }
+
       # no need to send static file serving requests to webserver
       # serve static content from nginx
       location /a/ {


don't find the same person twice

don't find the same person twice

--- controllers/people.coffee
+++ controllers/people.coffee
@@ -8,11 +8,14 @@
   next()

 loadPerson = (req, res, next) ->
-  uid = req.param('id') or req.user.id
-  Person.findById uid, (err, person) ->
-    app.te err
-    return next '404' unless person
-    req.person = person
+  if uid = req.param('id')
+    Person.findById uid, (err, person) ->
+      app.te err
+      return next '404' unless person
+      req.person = person
+      next()
+  else
+    req.person = req.user
     next()

 loadTeam = (req, res, next) ->


Remove extra newline

Remove extra newline

--- src/ls-archive.coffee
+++ src/ls-archive.coffee
@@ -7,7 +7,6 @@
       when '.tar' then @listTar(archivePath, callback)
       when '.gz' then @listGzip(archivePath, callback)
       when '.zip' then @listZip(archivePath, callback)
-
       else callback([])

   listZip: (archivePath, callback) ->


:art: Cleanup and more simplification

:art: Cleanup and more simplification

--- src/masked_input.cjsx
+++ src/masked_input.cjsx
@@ -1,7 +1,4 @@
 React = window?.React || require 'react'
-jsdiff = require 'diff'
-
-assert = require('chai').assert

 DEFAULT_TRANSLATIONS =
   '9': /\d/
@@ -37,32 +34,26 @@
           char

     @_cursorPos = @_firstNonMaskIdx
-    value: @_buffer.join('')
+
+    # TODO: Any way we can do this in one pass?
+    value: @_maskedValue(@props.value || '')

   componentDidUpdate: ->
     @_setSelection(@_cursorPos) if @_cursorPos?

   componentDidMount: ->
-    # TODO: any other lifecycle functions we need to modify when there's no mask?
-    return unless @props.mask?
-
-    if @props.onChange? && @props.value?
-      # TODO: should @state.value already be ready to use here?
-      value = @_maskedValue(@props.value)
-      @props.onChange(target: {value}) if value isnt @props.value
+    if @props.mask? && @props.onChange? && @props.value? && @state.value isnt @props.value
+      @props.onChange(target: {value: @state.value})

   render: ->
-    props = {}
     if @props.mask?
-      props.onChange = @_handleChange
-      # props.onKeyPress = @_handleKeyPress
-      props.onKeyDown = @_handleKeyDown
-      props.onFocus = @_handleFocus
-      props.value =
-        if @props.value? && @props.value isnt @state.value
-          @_maskedValue(@props.value)
-        else
-          @state.value
+      props =
+        onChange: @_handleChange
+        onKeyDown: @_handleKeyDown
+        onFocus: @_handleFocus
+        value: @state.value
+    else
+      props = {}

     <input {...@props} {...props} />

@@ -130,20 +121,6 @@

     @_cursorPos = Math.max(begin, @_firstNonMaskIdx)

-  _shiftRight: (pos) ->
-    c = @_getFormatChar(pos)
-    for i in [pos...@props.mask.length]
-      pattern = @_getPattern(i)
-      if pattern?
-        next = @_seekNext(i)
-        t = @_buffer[i]
-        @_buffer[i] = c
-        if next < @props.mask.length && pattern.test(t)
-          c = t
-        else
-          break
-    true
-
   _callOnComplete: (value) ->
     return unless @props.onComplete?
     for i in [0...@props.mask.length]
@@ -181,31 +158,6 @@

     @props.onKeyDown?(e)

-  _handleKeyPress: (e) ->
-    if e.key.length is 1
-      {begin, end} = @_getSelection()
-      @_cursorPos = begin
-      bufferChanged = false
-
-      if begin isnt end
-        @_shiftLeft(begin, end)
-        bufferChanged = true
-
-      next = @_seekNext(@_cursorPos - 1)
-      if next < @props.mask.length && @_getPattern(next).test(e.key)
-        @_shiftRight(next)
-
-        @_buffer[next] = e.key
-        @_cursorPos = @_seekNext(next)
-        value = @_setValue @_buffer.join('')
-        @_callOnComplete value
-      else if bufferChanged
-        @_setValue @_buffer.join('')
-
-      e.preventDefault()
-
-    @props.onKeyPress?(e)
-
   _handleChange: (e) ->
     value = @_maskedValue e.target.value
     @_setValue value
@@ -228,7 +180,6 @@
             break
           else if @_cursorPos > bufferPos
             @_cursorPos--
-            # @_cursorPos ?= i

         if inputPos >= input.length
           @_resetBuffer(bufferPos + 1, @props.mask.length)
@@ -241,47 +192,4 @@

     @_buffer.join('')

-  _maskedValue2: (input) ->
-    @_cursorPos = @_getSelection().begin
-    start = 0
-    diffs = jsdiff.diffChars(@state.value, input)
-
-    for diff, i in diffs
-      break if diff.added || diff.removed
-      start += diff.value.length # count?
-
-    for i in [i...diffs.length]
-      diff = diffs[i]
-
-      continue if diff.removed
-      start = @_something(start, diff.value, diff.added)
-      # start += diff.value.length
-
-    @_buffer.join('')
-
-  _something: (start, input, added) ->
-    pos = 0
-    for i in [start...@props.mask.length]
-      pattern = @_getPattern(i)
-      if pattern?
-        @_buffer[i] = @_getFormatChar(i)
-        while pos++ < input.length
-          c = input[pos - 1]
-          if pattern.test(c)
-            @_buffer[i] = c
-            break
-          else if added
-            @_cursorPos--
-
-        if pos > input.length
-          @_resetBuffer(i + 1, @props.mask.length)
-          break
-
-      else
-        @_cursorPos++ if added
-        if @_buffer[i] is input[pos]
-          pos++
-
-    i
-
 module.exports = MaskedInput


webapp/idle: timestamp comparison as int vs int

--- src/smc-webapp/client_browser.coffee
+++ src/smc-webapp/client_browser.coffee
@@ -92,7 +92,7 @@

         # call this idle_reset like a function
         # will reset timer on *first* call and then every 10secs while being called
-        @idle_reset = _.throttle smc.client._idle_reset, 15 * 1000
+        @idle_reset = _.throttle(smc.client._idle_reset, 15 * 1000)

         # activate a listener on our global body (universal sink for bubbling events, unless stopped!)
         $(document).on("click mousemove keydown focusin", "body", smc.client.idle_reset)
@@ -120,8 +120,9 @@

     # periodically check if the user hasn't been active
     _idle_check: =>
-        # console.log("idle: checking idle #{@_idle_time}")
-        if @_idle_time < new Date()
+        now = (new Date()).getTime()
+        #console.log("idle: checking idle #{@_idle_time} < #{now}")
+        if @_idle_time < now
             @emit('idle', 'away')
             true
         else


fix RAM warning before it is first computed

--- src/smc-webapp/project_warnings.cjsx
+++ src/smc-webapp/project_warnings.cjsx
@@ -41,7 +41,7 @@

 exports.RamWarning = rclass ({name}) ->
-    displayName : 'DiskSpaceWarning'
+    displayName : 'RAMWarning'

     reduxProps :
         projects :
@@ -63,11 +63,12 @@
             return null
         else
             rss = project_status.get('memory')?.get('rss')
-            if rss?
-                memory = Math.round(rss/1000)
-        if quotas.memory - 5 > memory
+            if not rss
+                return
+            memory = Math.round(rss/1000)
+        if quotas.memory > memory + 5
             return null

         <Alert bsStyle='danger' style={alert_style}>
-            <Icon name='exclamation-triangle' /> WARNING: This project is running out of RAM.  Upgrade memory in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings, restart your project or kill some processes.
+            <Icon name='exclamation-triangle' /> WARNING: This project is running low on RAM memory.  Upgrade memory in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings, restart your project or kill some processes.
         </Alert>


atom.project.path doesn't exist anymore

atom.project.path doesn't exist anymore

See: https://github.com/atom/atom/issues/5613

--- lib/linter-scalac.coffee
+++ lib/linter-scalac.coffee
@@ -23,7 +23,7 @@
 		)

 		atom.config.observe('linter-scalac.scalacOptions', =>
-			dotClasspath = atom.project.path + '/.classpath'
+			dotClasspath = atom.project.getPaths()[0] + '/.classpath'

 			if atom.config.get('linter-scalac.scalacOptions')?
 				@options = atom.config.get('linter-scalac.scalacOptions')


JDomain: Remove 'list own domains' permission, make the fetcher simpler

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -28,7 +28,6 @@
       'delete domains'     : ['member']
       'delete own domains' : ['member']
       'list domains'       : ['member']
-      'list own domains'   : ['member']

     sharedMethods:

@@ -169,16 +168,12 @@
     {group} = client.context
     {connection: {delegate}} = client

-    JGroup.one {slug:group}, (err, group)->
-
+    delegate.fetchDomains (err, domains) ->
       return callback err  if err
-      return callback new KodingError("No group found.")  unless group
-
-      delegate.checkPermission group, 'list own domains', (err, hasPermission)->
-
-        return callback err  if err
-        unless hasPermission
-          return callback new KodingError "Access denied",  "ACCESSDENIED"
+      return callback null unless domains
+      callback null, filterDomains domains, delegate, group
+
+

         delegate.fetchDomains (err, domains) ->
           return callback err  if err


Notify: notifyAdmins: to send notification to admins of given group

--- workers/social/lib/social/models/notify.coffee
+++ workers/social/lib/social/models/notify.coffee
@@ -1,5 +1,12 @@
 JName = require './name'
 JUser = require './user'
+
+notifyAdmins = (group, name, data) ->
+
+  group.fetchAdmins (err, admins = []) ->
+    return console.error 'Failed to fetch admins:', err, group  if err
+    admins.forEach (admin) ->
+      admin.sendNotification name, data

 notifyByUsernames = (usernames, name, data) ->
@@ -18,5 +25,6 @@

 module.exports = {
+  notifyAdmins
   notifyByUsernames
 }


Fix module detection when qualified exports

--- lib/editors.coffee
+++ lib/editors.coffee
@@ -43,7 +43,7 @@
       .map (m) -> m.module

   getMainModuleForEditor: (editor) ->
-    res = XRegExp.exec(editor.getText(), /^module\s+(\S+)/m)
+    res = XRegExp.exec(editor.getText(), /^module\s+([\w.]+)/m)
     res[1] if res

   # only distinguishing qualified imports and other - explicit and hiding parts are not respected


:abc: Better Description

:abc: Better Description

--- lib/go-plus.coffee
+++ lib/go-plus.coffee
@@ -92,7 +92,7 @@
       order: 14
     autocompleteBlacklist:
       title: 'Autocomplete Scope Blacklist'
-      description: 'Autocomplete suggestions will not be shown when the cursor is inside the following scope(s).'
+      description: 'Autocomplete suggestions will not be shown when the cursor is inside the following comma-delimited scope(s).'
       type: 'string'
       default: '.source.go .comment'
       order: 15


improved repl rlwrap notice, fixes #42

improved repl rlwrap notice, fixes #42

--- natal.coffee
+++ natal.coffee
@@ -410,14 +410,13 @@

 startRepl = (name, autoChoose) ->
-  log 'Starting REPL'
   try
     exec 'type rlwrap'
   catch
-    log '
-        Warning: rlwrap is not installed.\nInstall it to make the REPL a much
-        better experience with arrow key support.
-        ', 'red'
+    log 'Note: rlwrap is not installed but is recommended for REPL use.', 'yellow'
+    log 'You can optionally install it and run `rlwrap natal repl` for proper arrow key support in the REPL.\n', 'gray'
+
+  log 'Starting REPL'

   try
     lein = child.spawn 'lein', 'trampoline run -m clojure.main -e'.split(' ').concat(


allow for md code blocks (i.e. ```) in strings. Note: No `'` characters can be in the rest of the string.

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1361,6 +1361,12 @@
             'comment': 'This must come before #string-regular and #string-compound to accurately color `"""\' in strings'
           }
           {
+            'match': '```(?=[^\']*")'
+            'name': 'meta.markdown.code.block.stata'
+            'comment': 'color "```" as regular text, allowing for words after ```'
+            'comment': 'see https://github.com/kylebarron/language-stata/issues/53'
+          }
+          {
             'include': '#string-regular'
           }
           {
@@ -1396,6 +1402,12 @@
             'name': 'invalid.illegal.punctuation.stata'
         'name': 'string.quoted.double.stata'
         'patterns': [
+          {
+            'match': '```(?=[^\']*")'
+            'name': 'meta.markdown.code.block.stata'
+            'comment': 'color "```" as regular text, allowing for words after ```'
+            'comment': 'see https://github.com/kylebarron/language-stata/issues/53'
+          }
           {
             'include': '#macro-local-escaped'
           }


Make device.move throw ResinInvalidDeviceType for incompatible device types (not just Error)

Change-Type: patch

--- lib/models/device.coffee
+++ lib/models/device.coffee
@@ -767,7 +767,7 @@
 		.then ({ application, device }) ->

 			if device.device_type isnt application.device_type
-				throw new Error("Incompatible application: #{applicationNameOrId}")
+				throw new errors.ResinInvalidDeviceType("Incompatible application: #{applicationNameOrId}")

 			return pine.patch
 				resource: 'device'


:bug: Fix typos

:bug: Fix typos

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -43,10 +43,10 @@
         spawnedProcess = new BufferedNodeProcess({command, args, options, stdout, stderr, exit})
       else
         spawnedProcess = new BufferedProcess({command, args, options, stdout, stderr, exit})
-      spawnedProcess.onWillThrowError((error, handle) =>
+      spawnedProcess.onWillThrowError(({error, handle}) =>
         return reject(error) if error and error.code is 'ENOENT'
         handle()
-        if error.code is 'EACCESS'
+        if error.code is 'EACCES'
           error = new Error("Failed to spawn command `#{command}`. Make sure it's a file, not a directory and it's executable.")
           error.name = 'BufferedProcessError'
         reject(error)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/oscillator.coffee
+++ src/oscillator.coffee
@@ -1,5 +1,7 @@
 ###*
 Creates an oscillator function with the given parameters.
+
+

 @name Oscillator
 @constructor


Fix panels not update

Fix panels not update

--- views/components/main/parts/miniship.cjsx
+++ views/components/main/parts/miniship.cjsx
@@ -103,10 +103,22 @@
       window.addEventListener 'keydown', (e) =>
         if e.ctrlKey or e.metaKey
           if e.keyCode == 49 && @state.show
+            event = new CustomEvent 'view.main.visible',
+              bubbles: true
+              cancelable: false
+              detail:
+                visible: @state.show
+            window.dispatchEvent event
             @setState
               show: false
               dataVersion: @state.dataVersion + 1
           else if e.keyCode == 50 && !@state.show
+            event = new CustomEvent 'view.main.visible',
+              bubbles: true
+              cancelable: false
+              detail:
+                visible: @state.show
+            window.dispatchEvent event
             @setState
               show: true
               dataVersion: @state.dataVersion + 1


Add the message json improved

Add the message json improved

--- lib/message.coffee
+++ lib/message.coffee
@@ -15,7 +15,7 @@
     return "#{@levels[@level].name} : #{@message} at: #{@date}"
   # Convert the message into a json object
   toJSON:()->
-    return {"level": @level, "message" : @message, label: @toString(), "date": @date}
+    return {"level": @level, "message" : @message, "label": @toString(), "date": @date}

 if typeof module is 'undefined' and typeof window isnt 'undefined'
   window.Message  = Message


added specs for Mock

added specs for Mock

--- spec/Spec/Mock.spec.coffee
+++ spec/Spec/Mock.spec.coffee
@@ -1,8 +1,15 @@
 Spec.describe 'Spec.Mock', ->
   subject 'mock', ->
-    mock 'myMock'
+    mock 'myMock', foo: -> 'bar'

-  describe 'constructor'
+  describe 'constructor', ->
+    it 'sets name', ->
+      @mock.name.should equal 'myMock'
+
+    it 'stubs methods', ->
+      @mock.foo.should beA Function
+      @mock.foo._stub.should beA Spec.MethodStub
+      @mock.foo().should equal 'bar'

   describe 'toString', ->
     it 'makes a string which includes name', ->


Revert "Enhance performance"

Revert "Enhance performance"

This reverts commit 16e273944c8841c572ddc9b4e026d48e4107cb4a.

--- extension/packages/huffman.coffee
+++ extension/packages/huffman.coffee
@@ -47,27 +47,23 @@
   # Solving for `padding` in (2) gives:
   padding = 1 + (numBranches - 1) * numBranchPoints - numElements

-  # Sort the elements after their weights, in descending order, so that the last ones will be the
-  # ones with lowest weight.
-  elements.sort((a, b) -> b.weight - a.weight)
-
   # Pad with zero-weights to be able to form a `numBranches`-ary tree.
   for i in [0...padding] by 1
     elements.push({weight: 0})

   # Construct the Huffman tree.
   for i in [0...numBranchPoints] by 1
+    # Sort the elements after their weights, in descending order, so that the last ones will be the
+    # ones with lowest weight.
+    elements.sort((a, b) -> b.weight - a.weight)
+
     # Replace `numBranches` of the lightest weights with their sum.
     sum = {weight: 0, children: []}
     for i in [0...numBranches] by 1
       lowestWeight = elements.pop()
       sum.weight += lowestWeight.weight
       sum.children.unshift(lowestWeight)
-
-    # Find the index to insert the sum so that the sorting is maintained. That is faster than
-    # sorting the elements in each iteration.
-    break for element, index in elements by -1 when sum.weight >= element.weight
-    elements.splice(index + 1, 0, sum)
+    elements.push(sum)

   root = elements[0] # `elements.length == 1` by now.


Update Directory specs for platform specific issues

--- spec/directory-spec.coffee
+++ spec/directory-spec.coffee
@@ -66,32 +66,45 @@
       waits 20
       runs -> expect(changeHandler.callCount).toBe 0

-  it "includes symlink information about entries", ->
-    entries = directory.getEntries()
-    for entry in entries
-      name = entry.getBaseName()
-      if name is 'symlink-to-dir' or name is 'symlink-to-file'
-        expect(entry.symlink).toBeTruthy()
-      else
-        expect(entry.symlink).toBeFalsy()
+  describe "on #darwin or #linux", ->
+    it "includes symlink information about entries", ->
+      entries = directory.getEntries()
+      for entry in entries
+        name = entry.getBaseName()
+        if name is 'symlink-to-dir' or name is 'symlink-to-file'
+          expect(entry.symlink).toBeTruthy()
+        else
+          expect(entry.symlink).toBeFalsy()

   describe ".relativize(path)", ->
-    it "returns a relative path based on the directory's path", ->
-      absolutePath = directory.getPath()
-      expect(directory.relativize(absolutePath)).toBe ''
-      expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
-      expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee"
-      expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"
+    describe "on #darwin or #linux", ->
+      it "returns a relative path based on the directory's path", ->
+        absolutePath = directory.getPath()
+        expect(directory.relativize(absolutePath)).toBe ''
+        expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
+        expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee"
+        expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"

-    it "returns a relative path based on the directory's symlinked source path", ->
-      symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
-      symlinkDirectory = new Directory(symlinkPath)
-      realFilePath = require.resolve('./fixtures/dir/a')
-      expect(symlinkDirectory.relativize(symlinkPath)).toBe ''
-      expect(symlinkDirectory.relativize(realFilePath)).toBe 'a'
+      it "returns a relative path based on the directory's symlinked source path", ->
+        symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
+        symlinkDirectory = new Directory(symlinkPath)
+        realFilePath = require.resolve('./fixtures/dir/a')
+        expect(symlinkDirectory.relativize(symlinkPath)).toBe ''
+        expect(symlinkDirectory.relativize(realFilePath)).toBe 'a'

-    it "returns the full path if the directory's path is not a prefix of the path", ->
-      expect(directory.relativize('/not/relative')).toBe '/not/relative'
+      it "returns the full path if the directory's path is not a prefix of the path", ->
+        expect(directory.relativize('/not/relative')).toBe '/not/relative'
+
+    describe "on #win32", ->
+      it "returns a relative path based on the directory's path", ->
+        absolutePath = directory.getPath()
+        expect(directory.relativize(absolutePath)).toBe ''
+        expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
+        expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b\\file.coffee"
+        expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"
+
+      it "returns the full path if the directory's path is not a prefix of the path", ->
+        expect(directory.relativize('/not/relative')).toBe "\\not\\relative"

   describe ".contains(path)", ->
     it "returns true if the path is a child of the directory's path", ->
@@ -100,11 +113,12 @@
       expect(directory.contains(path.join(absolutePath, "b", "file.coffee"))).toBe true
       expect(directory.contains(path.join(absolutePath, "file.coffee"))).toBe true

-    it "returns true if the path is a child of the directory's symlinked source path", ->
-      symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
-      symlinkDirectory = new Directory(symlinkPath)
-      realFilePath = require.resolve('./fixtures/dir/a')
-      expect(symlinkDirectory.contains(realFilePath)).toBe true
-
     it "returns false if the directory's path is not a prefix of the path", ->
       expect(directory.contains('/not/relative')).toBe false
+
+    describe "on #darwin or #linux", ->
+      it "returns true if the path is a child of the directory's symlinked source path", ->
+        symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
+        symlinkDirectory = new Directory(symlinkPath)
+        realFilePath = require.resolve('./fixtures/dir/a')
+        expect(symlinkDirectory.contains(realFilePath)).toBe true


fix e2e

fix e2e

--- vue/src/shared/helpers/announcement.coffee
+++ vue/src/shared/helpers/announcement.coffee
@@ -5,7 +5,7 @@
   compact [
     ('parent_group'     if model.isA('group') && model.parent()),
     ('formal_group'     if model.isA('discussion', 'poll', 'outcome') && audienceSize(model, 'formal_group')),
-    ('discussion_group' if model.isA('poll', 'outcome') && audienceSize(model, 'discussion_group')),
+    ('discussion_group' if model.isA('poll', 'outcome') && model.discussion() && audienceSize(model, 'discussion_group')),
     ('voters'           if model.isA('poll', 'outcome') && audienceSize(model, 'voters')),
     ('undecided'        if model.isA('poll') && audienceSize(model, 'undecided')),
     ('non_voters'       if model.isA('poll') && audienceSize(model, 'non_voters') && model.stancesCount > 1)


More tests

More tests

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -116,7 +116,11 @@

         findView.findEditor.setText("sort")
         findView.replaceEditor.setText("dog")
-        findView.replaceAll()
+        findView.replaceNext()
+
+        findView.findEditor.setText("shift")
+        findView.replaceEditor.setText("ok")
+        findView.findNext(false)

         atom.packages.deactivatePackage("find-and-replace")

@@ -131,10 +135,16 @@

       runs ->
         findView.findEditor.trigger('core:move-up')
+        expect(findView.findEditor.getText()).toBe 'shift'
+        findView.findEditor.trigger('core:move-up')
         expect(findView.findEditor.getText()).toBe 'sort'
+        findView.findEditor.trigger('core:move-up')
+        expect(findView.findEditor.getText()).toBe 'items'

         findView.replaceEditor.trigger('core:move-up')
         expect(findView.replaceEditor.getText()).toBe 'dog'
+        findView.replaceEditor.trigger('core:move-up')
+        expect(findView.replaceEditor.getText()).toBe 'cat'

     it "serializes find options ", ->
       editorView.trigger 'find-and-replace:show'


Use Workspace::unregisterOpener

Use Workspace::unregisterOpener

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -406,7 +406,7 @@
           atom.workspace.registerOpener(anotherOpener)

         afterEach ->
-          atom.project.unregisterOpener(anotherOpener)
+          atom.workspace.unregisterOpener(anotherOpener)

         it "updates the result view", ->
           atom.workspaceView.openSync "another"


Partial fix for 653.

Partial fix for 653.

--- nodes.coffee
+++ nodes.coffee
@@ -339,6 +339,8 @@
   # the value of the indexes.
   cacheIndexes: (o) ->
     copy = new ValueNode @base, @properties[0..]
+    if @base instanceof CallNode
+      [@base, copy.base] = @base.compileReference o
     for prop, i in copy.properties
       if prop instanceof IndexNode and prop.contains((n) -> n instanceof CallNode)
         [index, indexVar] = prop.index.compileReference o


slightly optimizing return values of AssignNodes. Issue #539

--- nodes.coffee
+++ nodes.coffee
@@ -758,7 +758,10 @@
     @variable instanceof ValueNode

   makeReturn: ->
-    return new Expressions [this, new ReturnNode(@variable)]
+    if @isStatement()
+      return new Expressions [this, new ReturnNode(@variable)]
+    else
+      super()

   isStatement: ->
     @isValue() and (@variable.isArray() or @variable.isObject())


__extend back to __extends, as is the correct name.

--- nodes.coffee
+++ nodes.coffee
@@ -436,7 +436,7 @@

   # Hooks one constructor into another's prototype chain.
   compile_node: (o) ->
-    ref:  new ValueNode literal utility 'extend'
+    ref:  new ValueNode literal utility 'extends'
     (new CallNode ref, [@child, @parent]).compile o

 #### AccessorNode
@@ -1305,29 +1305,29 @@
   # Correctly set up a prototype chain for inheritance, including a reference
   # to the superclass for `super()` calls. See:
   # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206)
-  extend: """
-          function(child, parent) {
-              var ctor = function(){ };
-              ctor.prototype = parent.prototype;
-              child.__superClass__ = parent.prototype;
-              child.prototype = new ctor();
-              child.prototype.constructor = child;
-            }
-          """
+  __extends:  """
+              function(child, parent) {
+                  var ctor = function(){ };
+                  ctor.prototype = parent.prototype;
+                  child.__superClass__ = parent.prototype;
+                  child.prototype = new ctor();
+                  child.prototype.constructor = child;
+                }
+              """

   # Bind a function to a calling context, optionally including curried arguments.
   # See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47)
-  bind: """
-        function(func, obj, args) {
-            return function() {
-              return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
-            };
-          }
-        """
-
-  hasProp: 'Object.prototype.hasOwnProperty'
-
-  slice: 'Array.prototype.slice'
+  __bind:   """
+            function(func, obj, args) {
+                return function() {
+                  return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);
+                };
+              }
+            """
+
+  __hasProp:'Object.prototype.hasOwnProperty'
+
+  __slice:  'Array.prototype.slice'

 }

@@ -1354,5 +1354,5 @@
 # Helper for ensuring that utility functions are assigned at the top level.
 utility: (name) ->
   ref: "__$name"
-  Scope.root.assign ref, UTILITIES[name]
+  Scope.root.assign ref, UTILITIES[ref]
   ref


Back four commits ... Fixing named function expressions -> function declarations for IE. (grumble grumble)

--- nodes.coffee
+++ nodes.coffee
@@ -998,8 +998,8 @@
     (o.scope.parameter(param)) for param in params
     o.indent = @idt 2 if @className
     code  = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
-    open  = if @className then "(function() {\n#{@idt(1)}return function #{@className}(" else "function("
-    close = if @className then "#{code and @idt(1)}};\n#{@tab}})()" else "#{code and @tab}}"
+    open  = if @className then "(function() {\n#{@idt(1)}function #{@className}(" else "function("
+    close = if @className then "#{code and @idt(1)}};\n#{@idt(1)}return #{@className};\n#{@tab}})()" else "#{code and @tab}}"
     func  = "#{open}#{ params.join(', ') }) {#{code}#{close}"
     o.scope.endLevel()
     return "#{utility 'bind'}(#{func}, #{@context})" if @bound


make rethinkdb startup async in test setup

--- salvus/test/rethink-test.coffee
+++ salvus/test/rethink-test.coffee
@@ -5,8 +5,9 @@

 db = undefined
 setup = (cb) ->
-    db = rethink.rethinkdb(database:'test', debug:false)
     async.series([
+        (cb) ->
+            db = rethink.rethinkdb(database:'test', debug:false, cb:cb)
         (cb) ->
             teardown(cb)
         (cb) ->


remove idiocy

remove idiocy

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -74,7 +74,7 @@
     .pipe gulp.dest '/Users/mc/Desktop'

 # this is also ugly and might work...
-gulp.task 'coverage', [test] ->
+gulp.task 'coverage', [ 'test' ] ->
   run 'mocha --compilers coffee:coffee-script/register
     -r blanket -r should
     -R mocha-lcov-reporter', { silent: true }


only end stream on error for style stream

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -21,8 +21,6 @@
 errorHandler = (err) ->
   util.beep()
   util.log(util.colors.red(err))
-  # https://github.com/floatdrop/gulp-plumber/issues/8
-  this.emit('end')

 #
 # styles
@@ -38,7 +36,12 @@

 styles = ->
   gulp.src('./src/*.sass')
-    .pipe(plumber({ errorHandler }))
+    .pipe(plumber(
+      errorHandler: (err) ->
+        errorHandler(err)
+        # https://github.com/floatdrop/gulp-plumber/issues/8
+        this.emit('end')
+    ))
     .pipe(sourcemaps.init())
     .pipe(sass(
       includePaths: sassPaths


working on NewAnalyzer

working on NewAnalyzer

--- test/NewAnalyzerSpec.coffee
+++ test/NewAnalyzerSpec.coffee
@@ -76,26 +76,55 @@

       describe 'inner state changes', ->
         cases = [
-          { value: "\n",
-            state:
-              endOfLine:        true,
-              escaped:          undefined,
-              quote:            undefined,
-              doubleQuote:      undefined,
-              openParenthesis:  undefined,
-              closeParenthesis: undefined
-          },
-          { value: "\n",
-            state:
-              endOfLine:        true,
-              escaped:          undefined,
-              quote:            undefined,
-              doubleQuote:      undefined,
-              openParenthesis:  undefined,
-              closeParenthesis: undefined
-          }
+          { value: "\n"
+          state:
+            endOfLine:        true
+            escaped:          undefined
+            quote:            undefined
+            doubleQuote:      undefined
+            openParenthesis:  undefined
+            closeParenthesis: undefined },
+          { value: '\\'
+          state:
+            endOfLine:        undefined
+            escaped:          true
+            quote:            undefined
+            doubleQuote:      undefined
+            openParenthesis:  undefined
+            closeParenthesis: undefined },
+          { value: "'"
+          state:
+            endOfLine:        undefined
+            escaped:          undefined
+            quote:            true
+            doubleQuote:      undefined
+            openParenthesis:  undefined
+            closeParenthesis: undefined },
+          { value: '"'
+          state:
+            endOfLine:        undefined
+            escaped:          undefined
+            quote:            undefined
+            doubleQuote:      true
+            openParenthesis:  undefined
+            closeParenthesis: undefined },
+          { value: '('
+          state:
+            endOfLine:        undefined
+            escaped:          undefined
+            quote:            undefined
+            doubleQuote:      undefined
+            openParenthesis:  true
+            closeParenthesis: undefined },
+          { value: ')'
+          state:
+            endOfLine:        undefined
+            escaped:          undefined
+            quote:            undefined
+            doubleQuote:      undefined
+            openParenthesis:  undefined
+            closeParenthesis: true      }
         ]
-

     describe 'main function', ->


Remove bootstrap script

Remove bootstrap script

--- lib/paredit.coffee
+++ lib/paredit.coffee
@@ -1,21 +0,0 @@
-PareditView = require './paredit-view'
-
-require("../out/goog/bootstrap/nodejs")
-debugger;
-require("../out")
-paredit = require("../out/paredit/core")
-
-
-
-module.exports = paredit
-   pareditView: null
-  #
-   activate: (state) ->
-     @pareditView = new PareditView(state.pareditViewState)
-
-  #
-  # deactivate: ->
-  #   @pareditView.destroy()
-  #
-  # serialize: ->
-    # pareditViewState: @pareditView.serialize()


:fire: logs

:fire: logs

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -927,7 +927,6 @@

   stopEdit: ->
     @editing = false
-    console.log(@editorElement)
     @editorElement?.parentNode?.removeChild(@editorElement)
     @textEditorSubscriptions?.dispose()
     @textEditorSubscriptions = null
@@ -936,7 +935,6 @@
   createTextEditor: ->
     @editor ?= atom.workspace.buildTextEditor({mini: true})
     @editorElement ?= atom.views.getView(@editor)
-    console.log(@editor, @editorElement)
     @appendChild(@editorElement)

   subscribeToCellTextEditor: (editor) ->


Add TextDocument::buildDisplayLayer

Add TextDocument::buildDisplayLayer

* Compose Lines, HardTabs and SoftWraps transforms
* Add passThrough operation for Transforms, so that
  they can preserve traversals created by their
  sources.

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/region-map.coffee
+++ src/region-map.coffee
@@ -100,7 +100,7 @@
     @seek(startPosition.traverse(newExtent))

   getPosition: ->
-    @position
+    @position.copy()

   getSourcePosition: ->
-    @sourcePosition
+    @sourcePosition.copy()