Documented the prev() iterator method

Documented the prev() iterator method

--- src/dahelpers.coffee
+++ src/dahelpers.coffee
@@ -779,9 +779,17 @@
     # #### `iterator.next()`
     #
     # Returns the next member of a sequence that comes after the member
-    # returned in the previous call. When called for the first time, it returns
-    # the first item. Once all members have been returned, it throws an
-    # exception.
+    # returned in the previous call or a call to `iterator.get()`. When no next
+    # memeber exist, it throws an exception. When none of the getter methods
+    # have been called, it returns the first member.
+    #
+    # #### `iterator.prev()`
+    #
+    # Returns the previous member of a sequence that comes before the member
+    # retruned in the previous call or a call to `iterator.get()`. When no
+    # previous member exists, it throws an exception. When none of the getter
+    # methods have been called or the current index of the iterator is 0, it
+    # throws an exception.
     #
     # #### `iterator.each(callback)`
     #


Change `batch` API - update tests

Change `batch` API - update tests

--- tests.coffee
+++ tests.coffee
@@ -677,7 +677,7 @@
 ###
 @test
 @description Check batch ASYNC
-db.batch(tasks, undefined, false, true)
+db.batch(tasks, {plain: true}, false, true)
 ###
 Tinytest.add 'db.batch [With custom ID] [no data transform (plain)]', (test) ->
   batch = db.batch [
@@ -694,7 +694,7 @@
     ,
       method: "POST"
       to: db.__service.cypher.endpoint
-      body: query: "MATCH (n:BatchTestPlain) DELETE n"], undefined, false, true
+      body: query: "MATCH (n:BatchTestPlain) DELETE n"], plain: true

   test.equal batch.length, 3
   for res in batch
@@ -709,7 +709,7 @@
 ###
 @test
 @description Check batch ASYNC REACTIVE
-db.batch(tasks, undefined, true)
+db.batch(tasks, {reactive: true}, true)
 ###
 Tinytest.add 'db.batch [With custom ID] [REACTIVE]', (test) ->
   batch = db.batch [
@@ -722,7 +722,7 @@
       method: "POST"
       to: db.__service.cypher.endpoint
       body: query: "MATCH (n:BatchTestReactive) RETURN n"
-      id: 999], undefined, true
+      id: 999], reactive: true

   test.equal batch.length, 2
   for res in batch


Tests for invalid return values from generated fields.

--- tests.coffee
+++ tests.coffee
@@ -5,6 +5,7 @@
 CircularFirsts = new Meteor.Collection 'CircularFirsts', transform: (doc) => new CircularFirst doc
 CircularSeconds = new Meteor.Collection 'CircularSeconds', transform: (doc) => new CircularSecond doc
 Recursives = new Meteor.Collection 'Recursives', transform: (doc) => new Recursive doc
+IdentityGenerators = new Meteor.Collection 'IdentityGenerators', transform: (doc) => new IdentityGenerator doc

 if Meteor.isServer
   # Initialize the database
@@ -15,6 +16,7 @@
   CircularFirsts.remove {}
   CircularSeconds.remove {}
   Recursives.remove {}
+  IdentityGenerators.remove {}
   Meteor.users.remove {}

   Meteor.publish null, ->
@@ -31,6 +33,8 @@
     CircularSeconds.find()
   Meteor.publish null, ->
     Recursives.find()
+  Meteor.publish null, ->
+    IdentityGenerators.find()

 # The order of documents here tests delayed definitions

@@ -154,6 +158,20 @@
     collection: Recursives
     fields:
       other: @ReferenceField 'self', ['content'], false
+
+class IdentityGenerator extends Document
+  # Other fields:
+  #   source
+
+  @Meta =>
+    collection: IdentityGenerators
+    fields:
+      result: @GeneratedField 'self', ['source'], (fields) ->
+        return [fields._id, fields.source]
+      results: [
+        @GeneratedField 'self', ['source'], (fields) ->
+          return [fields._id, fields.source]
+      ]

 Document.redefineAll()

@@ -355,7 +373,32 @@
   test.equal Recursive.Meta.fields.other.targetDocument.Meta.collection, Recursives
   test.equal Recursive.Meta.fields.other.fields, ['content']

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal IdentityGenerator.Meta.collection, IdentityGenerators
+  test.equal _.size(IdentityGenerator.Meta.fields), 2
+  test.instanceOf IdentityGenerator.Meta.fields.result, IdentityGenerator._GeneratedField
+  test.isNull IdentityGenerator.Meta.fields.result.ancestorArray, IdentityGenerator.Meta.fields.result.ancestorArray
+  test.isTrue _.isFunction IdentityGenerator.Meta.fields.result.generator
+  test.equal IdentityGenerator.Meta.fields.result.sourcePath, 'result'
+  test.equal IdentityGenerator.Meta.fields.result.sourceDocument, IdentityGenerator
+  test.equal IdentityGenerator.Meta.fields.result.targetDocument, IdentityGenerator
+  test.equal IdentityGenerator.Meta.fields.result.sourceCollection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.result.targetCollection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.result.sourceDocument.Meta.collection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.result.targetDocument.Meta.collection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.result.fields, ['source']
+  test.instanceOf IdentityGenerator.Meta.fields.results, IdentityGenerator._GeneratedField
+  test.equal IdentityGenerator.Meta.fields.results.ancestorArray, 'results'
+  test.isTrue _.isFunction IdentityGenerator.Meta.fields.results.generator
+  test.equal IdentityGenerator.Meta.fields.results.sourcePath, 'results'
+  test.equal IdentityGenerator.Meta.fields.results.sourceDocument, IdentityGenerator
+  test.equal IdentityGenerator.Meta.fields.results.targetDocument, IdentityGenerator
+  test.equal IdentityGenerator.Meta.fields.results.sourceCollection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.results.targetCollection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.results.sourceDocument.Meta.collection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.results.targetDocument.Meta.collection, IdentityGenerators
+  test.equal IdentityGenerator.Meta.fields.results.fields, ['source']
+
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]

   test.equal UserLink.Meta._initialized, 0
   test.equal PostLink.Meta._initialized, 1
@@ -364,6 +407,7 @@
   test.equal CircularFirst.Meta._initialized, 4
   test.equal Recursive.Meta._initialized, 5
   test.equal Post.Meta._initialized, 6
+  test.equal IdentityGenerator.Meta._initialized, 7

   test.isUndefined UserLink.Meta._delayed, UserLink.Meta._delayed
   test.isUndefined PostLink.Meta._delayed, PostLink.Meta._delayed
@@ -372,6 +416,7 @@
   test.isUndefined CircularFirst.Meta._delayed, CircularFirst.Meta._delayed
   test.isUndefined Recursive.Meta._delayed, Recursive.Meta._delayed
   test.isUndefined Post.Meta._delayed, Post.Meta._delayed
+  test.isUndefined IdentityGenerator.Meta._delayed, IdentityGenerator.Meta._delayed

   test.isUndefined UserLink.Meta._meta._delayed, UserLink.Meta._meta._delayed
   test.isUndefined PostLink.Meta._meta._delayed, PostLink.Meta._meta._delayed
@@ -380,6 +425,7 @@
   test.isUndefined CircularFirst.Meta._meta._delayed, CircularFirst.Meta._meta._delayed
   test.isUndefined Recursive.Meta._meta._delayed, Recursive.Meta._meta._delayed
   test.isUndefined Post.Meta._meta._delayed, Post.Meta._meta._delayed
+  test.isUndefined IdentityGenerator.Meta._meta._delayed, IdentityGenerator.Meta._meta._delayed

 testAsyncMulti 'meteor-peerdb - references', [
   (test, expect) ->
@@ -741,7 +787,7 @@
   , /Reference field directly in an array cannot be optional/

   # Invalid document should not be added to the list
-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]

 Tinytest.add 'meteor-peerdb - invalid nested arrays', (test) ->
   test.throws ->
@@ -755,7 +801,7 @@
   , /Field cannot be in a nested array/

   # Invalid document should not be added to the list
-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]

 testAsyncMulti 'meteor-peerdb - circular changes', [
   (test, expect) ->
@@ -1400,7 +1446,7 @@
     test.equal intercepted.message, "Not all delayed document definitions were successfully retried: BadPost"
     test.equal intercepted.level, 'error'

-    test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+    test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]
     test.equal Document.Meta.delayed.length, 1

     # Clear delayed so that we can retry tests without errors
@@ -2119,7 +2165,7 @@
       meta.fields.third = @ReferenceField secondReferenceA
       meta

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]
   test.equal Document.Meta.delayed.length, 3
   test.equal Document.Meta.delayed[0][0], First
   test.equal Document.Meta.delayed[1][0], Second
@@ -2146,7 +2192,7 @@
       meta.fields.third = @ReferenceField secondReferenceB
       meta

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]
   test.equal Document.Meta.delayed.length, 3
   test.equal Document.Meta.delayed[0][0], First
   test.equal Document.Meta.delayed[1][0], Second
@@ -2162,7 +2208,7 @@
       meta.fields.third = @ReferenceField Person
       meta

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]
   test.equal Document.Meta.delayed.length, 3
   test.equal Document.Meta.delayed[0][0], First
   test.equal Document.Meta.delayed[1][0], Second
@@ -2178,7 +2224,7 @@
       meta.fields.first = @ReferenceField Person
       meta

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator]
   test.equal Document.Meta.delayed.length, 3
   test.equal Document.Meta.delayed[0][0], First
   test.equal Document.Meta.delayed[1][0], Second
@@ -2192,7 +2238,7 @@
   firstReferenceA = First
   Document._retryDelayed()

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, Second]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator, Second]
   test.equal Document.Meta.delayed.length, 2
   test.equal Document.Meta.delayed[0][0], First
   test.equal Document.Meta.delayed[1][0], Third
@@ -2230,7 +2276,7 @@
   firstReferenceB = Posts
   Document._retryDelayed()

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, Second, First]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator, Second, First]
   test.equal Document.Meta.delayed.length, 1
   test.equal Document.Meta.delayed[0][0], Third

@@ -2281,7 +2327,7 @@
   secondReferenceA = First
   Document._retryDelayed()

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, Second, First]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator, Second, First]
   test.equal Document.Meta.delayed.length, 1
   test.equal Document.Meta.delayed[0][0], Third

@@ -2332,7 +2378,7 @@
   secondReferenceB = Posts
   Document._retryDelayed()

-  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, Second, First, Third]
+  test.equal Document.Meta.list, [UserLink, PostLink, CircularSecond, Person, CircularFirst, Recursive, Post, IdentityGenerator, Second, First, Third]
   test.equal Document.Meta.delayed.length, 0

   test.isUndefined Document.Meta._delayed, Document.Meta._delayed
@@ -3757,4 +3803,80 @@
       transform: null # So that we can use test.equal

     test.isFalse @post, @post
+]
+
+testAsyncMulti 'meteor-peerdb - warnings for generated fields', [
+  (test, expect) ->
+    Log._intercept 3 if Meteor.isServer # Three to see if we catch more than expected
+
+    IdentityGenerators.insert
+      source: 'foobar'
+    ,
+      expect (error, identityGeneratorId) =>
+        test.isFalse error, error?.toString?() or error
+        test.isTrue identityGeneratorId
+        @identityGeneratorId = identityGeneratorId
+
+    # Sleep so that observers have time to update documents
+    Meteor.setTimeout expect(), 500
+,
+  (test, expect) ->
+    if Meteor.isServer
+      intercepted = Log._intercepted()
+
+      # One or two because it depends if the client tests are running at the same time
+      test.isTrue 1 <= intercepted.length <= 2, intercepted
+
+      # We are testing only the server one, so let's find it
+      for i in intercepted
+        break if i.indexOf(@identityGeneratorId) isnt -1
+      intercepted = EJSON.parse i
+
+      test.equal intercepted.message, "Generated field 'results' defined as an array with selector '#{ @identityGeneratorId }' was updated with a non-array value: 'foobar'"
+      test.equal intercepted.level, 'warn'
+
+    @identityGenerator = IdentityGenerators.findOne @identityGeneratorId,
+      transform: null # So that we can use test.equal
+
+    test.equal @identityGenerator,
+      _id: @identityGeneratorId
+      source: 'foobar'
+      result: 'foobar'
+
+    Log._intercept 3 if Meteor.isServer # Three to see if we catch more than expected
+
+    IdentityGenerators.update @identityGeneratorId,
+      $set:
+        source: ['foobar2']
+    ,
+      expect (error, res) =>
+        test.isFalse error, error?.toString?() or error
+        test.isTrue res
+
+    # Sleep so that observers have time to update documents
+    Meteor.setTimeout expect(), 500
+,
+  (test, expect) ->
+    if Meteor.isServer
+      intercepted = Log._intercepted()
+
+      # One or two because it depends if the client tests are running at the same time
+      test.isTrue 1 <= intercepted.length <= 2, intercepted
+
+      # We are testing only the server one, so let's find it
+      for i in intercepted
+        break if i.indexOf(@identityGeneratorId) isnt -1
+      intercepted = EJSON.parse i
+
+      test.equal intercepted.message, "Generated field 'result' not defined as an array with selector '#{ @identityGeneratorId }' was updated with an array value: [ 'foobar2' ]"
+      test.equal intercepted.level, 'warn'
+
+    @identityGenerator = IdentityGenerators.findOne @identityGeneratorId,
+      transform: null # So that we can use test.equal
+
+    test.equal @identityGenerator,
+      _id: @identityGeneratorId
+      source: ['foobar2']
+      result: 'foobar'
+      results: ['foobar2']
 ]


Fixed double click submit highscore

Fixed double click submit highscore

--- app/client/game/result.coffee
+++ app/client/game/result.coffee
@@ -22,15 +22,18 @@
 Template.submit.events
   'click button#submit-highscore': (evt) ->
     evt.preventDefault()
+    $('button#submit-highscore').attr 'disabled', true

     name = "#{$('input#submit-name').val()}".replace /^\s+|\s+$/g, ""
     unless name
       FlashMessages.sendError "Dit navn kan ikke være tomt"
+      $('button#submit-highscore').attr 'disabled', false
       return

     Meteor.call 'submitHighscore', name, currentGameId(), (error, result) ->
       if error?
         console.log error
+        $('button#submit-highscore').attr 'disabled', false
       else
         Router.go 'highscores'


<controller> Actuator - Harmonizing with Sensors Controller.

--- server/controllers/actuator.coffee
+++ server/controllers/actuator.coffee
@@ -27,23 +27,25 @@
 module.exports.create = (req, res) ->
 	data = req.body
 	Actuator.create data, (err, actuator) ->
-		if err?
-			# @todo Special case when error is 'Device already added'?
-			res.send error: "Server error while creating actuator.", 500
+		if err
+			if err == 'Device already added'
+				res.send actuator, 202
+			else
+				res.send error: err, 500
 		else
 			res.send actuator, 201

 module.exports.update = (req, res) ->
 	data = req.body
 	req.actuator.updateAttributes data, (err, actuator) ->
-		if err?
-			res.send error: "Server error while saving actuator", 500
+		if err
+			res.send error: err, 500
 		else
 			res.send actuator, 200

 module.exports.delete = (req, res) ->
 	req.actuator.destroy (err) ->
-		if err?
-			res.send error: "Server error while deleting actuator", 500
+		if err
+			res.send error: err, 500
 		else
-			res.send success: true, 200
+			res.send success: true, 200


Chain promises via a reduce abuse trick

Chain promises via a reduce abuse trick

That way we create lists for a card then the next one and so on

--- assets/javascripts/app.coffee
+++ assets/javascripts/app.coffee
@@ -273,21 +273,24 @@
   ).then( (lists) =>
     console.log("lists", lists)
     cards = []
-    for list in lists
-      cards = cards.concat createCards(list)
-    Promise.all(cards)
+    lists.reduce( (acc, list) ->
+      acc.then( -> Promise.all(createCards(list)))
+         .then( (listCards) -> cards = cards.concat(listCards))
+    , Promise.resolve()).then(-> cards)
   ).then((cards) =>
     console.log("cards", cards)
     checkLists = []
-    for card in cards
-      checkLists = checkLists.concat createCheckLists(card)
-    Promise.all(checkLists)
+    cards.reduce( (acc, card) ->
+      acc.then(-> Promise.all(createCheckLists(card)))
+         .then((cardLists) -> checkLists =  checkLists.concat(cardLists))
+    , Promise.resolve()).then(-> checkLists)
   ).then((checkLists) =>
     console.log("checkLists", checkLists)
     items = []
-    for checkList in checkLists
-      items = items.concat createCheckItems(checkList)
-    Promise.all(items)
+    checkLists.reduce( (acc, list) ->
+      acc.then(-> Promise.all(createCheckItems(list)))
+         .then((listItems) -> items = items.concat listItems)
+    , Promise.resolve()).then(-> Promise.all(items))
   ).then(->
     log.debug("Ordering the lists", lists)
     reorderLists(trelloBoard)


NEW: support for event.clone -

NEW: support for event.clone -

--- source/Art/Engine/Events/PointerEvent.coffee
+++ source/Art/Engine/Events/PointerEvent.coffee
@@ -17,15 +17,17 @@
     super type, props, time
     @pointer = pointer

+  clone: -> @newEvent()
+
   emptyObject = {}
   newEvent: (options = emptyObject)->
     e = new PointerEvent(
-      options.type || @type
-      options.pointer || @pointer
-      options.time || @time
+      options.type    ? @type
+      options.pointer ? @pointer
+      options.time    ? @time
     )
     e.timeStamp = @timeStamp
-    e.target = options.target || @target
+    e.target = options.target ? @target
     e

   @getter


Fix minor mistake of partial application

--- library/nucleus/api.coffee
+++ library/nucleus/api.coffee
@@ -84,7 +84,7 @@
         throw new Error missing unless method of this
         variables = [tokens.resource, tokens.domain]
         headers = @upstreamAsync "headers", _.identity
-        partial = _.partialRight headers, request, response
+        partial = _.partial headers, request, response
         response.on "header", -> partial variables...
         prestreamer = @upstreamAsync "preprocess", =>
             this[method](request, response, variables...)


Only populate list from timeout when still on DOM

--- src/app/select-list.coffee
+++ src/app/select-list.coffee
@@ -45,7 +45,9 @@

   schedulePopulateList: ->
     clearTimeout(@scheduleTimeout)
-    @scheduleTimeout = setTimeout((=> @populateList()), @inputThrottle)
+    populateCallback = =>
+      @populateList() if @isOnDom()
+    @scheduleTimeout = setTimeout(populateCallback,  @inputThrottle)

   setArray: (@array) ->
     @populateList()


JSONを監視対象に追加

JSONを監視対象に追加

--- grunt/esteWatch.coffee
+++ grunt/esteWatch.coffee
@@ -18,6 +18,9 @@
 		grunt.config ["assemble", "dev_pc", "files"]
 		return ["assemble:dev_pc"]

+	json: (filepath) ->
+		return ["assemble:dev_pc"]
+
 	scss: (filepath) ->
 		return ["sass:dev_pc", "autoprefixer:dev"]


Enable all specs.

Enable all specs.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -9,7 +9,7 @@
   beforeEach ->
     @import = new CustomerXmlImport Config

-  xit 'should stop as updating customers isnt support yet', (done) ->
+  it 'should stop as updating customers isnt support yet', (done) ->
     rawXml = '
 <root>
   <Customer>


fix sort of one node

fix sort of one node

--- tree2.coffee
+++ tree2.coffee
@@ -385,6 +385,9 @@
     return new CollectionAnimation([new NodeAnimation(node, dest, duration)], t)

   sortNodes: (compare, t) ->
+    if @nodes.length == 1
+      return
+
     anims = []
     anims.push(@makeLineupAnim(default_node_radius*2, .1, t))

@@ -421,6 +424,7 @@

     @animations = @animations.concat(anims)

+    return

 lerp2d = (t, p0, p1) ->
   if t < 0


fixes based on review

fixes based on review

--- test/map.coffee
+++ test/map.coffee
@@ -5,42 +5,35 @@
 sinon = require 'sinon'

 describe '_.map', ->
-  it 'accepts fn (sync/async), calls it on each chunk, then passes the fn result along', (done) ->
-    input  = [{a:'1', b:'2'},{c:'2', d:'3'}]
-    synch  = (chunk) -> _(chunk).keys()
-    asynch = (chunk, cb) -> cb null, _(chunk).keys() # TODO: error handling
-    expected = _(input).map(_.keys)
-    async.forEach [synch, asynch], (fn, cb_fe) ->
+  synch  = (chunk) -> _(chunk).keys()
+  asynch = (chunk, cb) -> cb null, _(chunk).keys() # TODO: error handling
+
+  _.each [synch, asynch], (fn) ->
+
+    it "accepts fn #{fn}, calls it on each chunk, then passes the fn result along", (done) ->
+      input  = [{a:'1', b:'2'},{c:'2', d:'3'}]
+      expected = _(input).map(_.keys)
       spy = sinon.spy fn
       new Understream(input).map(spy).run (err, result) ->
         assert.ifError err
         assert.deepEqual expected, result
         assert.equal spy.callCount, 2
         assert.deepEqual spy.args[i][0], input[i] for i in input.length
-        cb_fe()
-    , done
+        done()

-  it 'fn (sync/async), do not block setTimeout while running', (done) ->
-    input = []
-    for i in [1..100]  # spend some time in the stream
-      d = {}
-      d["a#{i}"] = i.toString()
-      d["b#{i}"] = i+1
-      input.push d
-    synch  = (chunk) -> _(chunk).keys()
-    asynch = (chunk, cb) -> cb null, _(chunk).keys() # TODO: error handling
-    expected = _(input).map(_.keys)
-    async.forEach [synch, asynch], (fn, cb_fe) ->
+    it "does not block fn #{fn} setTimeout while running", (done) ->
+      input = ({a:i, b:i+1} for i in [1..100])  # spend some time in the stream
+      expected = _(input).map(_.keys)
       spy = sinon.spy fn
-      nextTick = false
-      setTimeout =>  # setup a timeout that will run soon
-        nextTick = true
+
+      timeout_ran = false
+      setTimeout ->  # setup a timeout that will run soon
+        timeout_ran = true
       , 10
       new Understream(input).map(spy).run (err, result) ->
-        assert.equal nextTick, true  # make sure setTimeout ran during map
+        assert.equal timeout_ran, true  # make sure setTimeout ran during map
         assert.ifError err
         assert.deepEqual expected, result
         assert.equal spy.callCount, 100
         assert.deepEqual spy.args[i][0], input[i] for i in input.length
-        cb_fe()
-    , done
+        done()


fix offset calculation to match pdflistview

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -157,6 +157,7 @@
 		pageTop = $(element).offset().top - $(element).parent().offset().top
 		console.log('top of page scroll is', pageTop, 'vs', page.elemTop)
 		console.log('inner height is', $(element).innerHeight())
+		currentScroll = $(element).parent().scrollTop()
 		offset = position.offset
 		# convert offset to pixels
 		return $scope.document.getPdfViewport(page.pageNum).then (viewport) ->
@@ -164,7 +165,7 @@
 			pageOffset = viewport.convertToViewportPoint(offset.left, offset.top)
 			console.log 'addition offset =', pageOffset
 			console.log 'total', pageTop + pageOffset[1]
-			Math.round(pageTop + pageOffset[1]) ## 10 is margin
+			Math.round(pageTop + pageOffset[1] + currentScroll) ## 10 is margin

 	@setPdfPosition = (page, position) ->
 		console.log 'required pdf Position is', position


use presence

use presence

--- resources/assets/coffee/osu_common.coffee
+++ resources/assets/coffee/osu_common.coffee
@@ -195,7 +195,7 @@

   urlPresence: (url) ->
-    "url(#{url})" if url?
+    "url(#{url})" if osu.presence(url)

   navigate: (url, keepScroll, {action = 'advance'} = {}) ->


Delete Should Respond With 204

Delete Should Respond With 204

--- server/controller/library.coffee
+++ server/controller/library.coffee
@@ -45,7 +45,7 @@
       books: book_id
   .exec()
   .then (user) ->
-    res.send 201, user: user
+    res.send 204 #no content
   .then null, (err) ->
     res.send 500, err: err


Remove 'inivisibles' key from .atom/config.cson

This setting only shows up when atom is running.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -11,7 +11,6 @@
     'fontSize': 11
     'showInvisibles': true
     'showIndentGuide': true
-    'invisibles': {}
   'core':
     'disabledPackages': [
       'metrics'


Expand 'ctrl-alt-*' keymappings in Atom.

Add left/right selection movement mappings mirroring the new default
mappings in Atom 1.8.0+.

--- home/.atom/keymap.cson
+++ home/.atom/keymap.cson
@@ -33,15 +33,14 @@
 ###############################################################################

 ###############################################################################
-# map ctrl-up / ctrl-down to line movement.
-#
-# This mirrors the current vim configuration.
-#
-# The existing keymappings for these operations are ctrl-cmd-up / ctrl-cmd-down
+# map ctrl-alt-[up|down] to move line and ctrl-alt-[left|right] to
+# move selection.
 ###############################################################################
 'body':
-  'ctrl-alt-up': 'editor:move-line-up'
-  'ctrl-alt-down': 'editor:move-line-down'
+  'ctrl-alt-up': 'editor:move-line-up' # Existing: ctrl-cmd-up
+  'ctrl-alt-down': 'editor:move-line-down' # Existing: ctrl-cmd-down
+  'ctrl-alt-left': 'editor:move-selection-left' # Existing: cmd-ctrl-left
+  'ctrl-alt-right': 'editor:move-selection-right' # Existing: cmd-ctrl-right

 ###############################################################################
 # map ', t t b' to symbols-tree-view toggle.


remove fileuploadbuilder

remove fileuploadbuilder

--- vendor/assets/javascripts/rails_admin/themes/glyph/src/glyph-theme.coffee
+++ vendor/assets/javascripts/rails_admin/themes/glyph/src/glyph-theme.coffee
@@ -1,32 +1,32 @@
-class FileUploadBuilder
-  template:
-    """
-

-

-

-
-
-

-
-          Sélectionnez
-          Changer
-
-        Supprimer
-

-

-    """
+# class FileUploadBuilder
+#   template:
+#     """
+#

+#

+#

+#
+#
+#

+#
+#           Sélectionnez
+#           Changer
+#
+#         Supprimer
+#

+#

+#     """

-  constructor: (@$el) ->
-    $uploadedFile = @$el.parent().find('a, img').addClass('uploaded-file').remove()
+#   constructor: (@$el) ->
+#     $uploadedFile = @$el.parent().find('a, img').addClass('uploaded-file').remove()

-    unless @$el.data("file-upload")
-      $container = @$el.parent().html(@template)
-      # Append input to file buttons
-      $container.find(".btn-file").append(@$el)
-      # Add preview link if existing
-      $container.prepend($uploadedFile) if $uploadedFile.length
-      # Store object in "file-upload" data key on dom element
-      @$el.data("file-upload", this)
+#     unless @$el.data("file-upload")
+#       $container = @$el.parent().html(@template)
+#       # Append input to file buttons
+#       $container.find(".btn-file").append(@$el)
+#       # Add preview link if existing
+#       $container.prepend($uploadedFile) if $uploadedFile.length
+#       # Store object in "file-upload" data key on dom element
+#       @$el.data("file-upload", this)

 class SpinnerBuilder


Fix a small typo

Fix a small typo

--- content/groups.coffee
+++ content/groups.coffee
@@ -86,4 +86,4 @@
   "the-journal-of-horticulture-cottage-gardener-and-country-gentlemen":
     image: 'groups/journal-horticulture.jpg'
     alt: 'The Journal of horticulture, cottage gardener and country gentlemen'
-    description: '1878. This is the third journal, variously named the ‘Cottage Gardiner’. It was aimed at an amateur gardening audience, and was edited by Robert Hogg (a plant nursery owner) and George W. Johnson, the author and gardener. The journal changes names seven more times before terminating production during WWI.'+    description: '1878. This is the third journal, variously named the ‘Cottage Gardener’. It was aimed at an amateur gardening audience, and was edited by Robert Hogg (a plant nursery owner) and George W. Johnson, the author and gardener. The journal changes names seven more times before terminating production during WWI.'


spec: original-fs module

spec: original-fs module

--- spec/asar-spec.coffee
+++ spec/asar-spec.coffee
@@ -391,3 +391,17 @@
       ipc.on 'dirname', (event, dirname) ->
         assert.equal dirname, path.dirname(p)
         done()
+
+  describe 'original-fs module', ->
+    originalFs = require 'original-fs'
+
+    it 'uses the original fs api', ->
+      changedApis = ['readFile', 'stat', 'lstat', 'realpath', 'exists']
+      unchangedApis = ['read', 'write', 'writeFile', 'close']
+      assert.notStrictEqual fs[api], originalFs[api] for api in changedApis
+      assert.strictEqual fs[api], originalFs[api] for api in unchangedApis
+
+    it 'treats .asar as file', ->
+      file = path.join fixtures, 'asar', 'a.asar'
+      stats = originalFs.statSync file
+      assert stats.isFile()


Remove unneeded exists check

Remove unneeded exists check

--- spec/file-spec.coffee
+++ spec/file-spec.coffee
@@ -1,6 +1,5 @@
 path = require 'path'
 fs = require 'fs-plus'
-
 File = require '../lib/file'
 PathWatcher = require '../lib/main'

@@ -15,7 +14,7 @@

   afterEach ->
     file.off()
-    fs.removeSync(filePath) if fs.existsSync(filePath)
+    fs.removeSync(filePath)
     PathWatcher.closeAllWatchers()

   describe "when the file has not been read", ->


Test for proper gutter selection color

Test for proper gutter selection color

--- spec/init-spec.coffee
+++ spec/init-spec.coffee
@@ -94,7 +94,7 @@
             @syntax-gutter-text-color: #080808;
             @syntax-gutter-text-color-selected: #080808;
             @syntax-gutter-background-color: #F5F5F5;
-            @syntax-gutter-background-color-selected: #F5F5F5;
+            @syntax-gutter-background-color-selected: rgba(92, 108, 125, 0.07);
           """
           expect(fs.readFileSync(path.join(themePath, 'stylesheets', 'base.less'), 'utf8')).toContain """
             @import "syntax-variables";


Fixed unit tests

Fixed unit tests

They were not changed to account for the expand field defaults in a
query that was modified.

--- spec/jira.spec.coffee
+++ spec/jira.spec.coffee
@@ -307,7 +307,6 @@
         expect(@cb).toHaveBeenCalledWith null, '{"body":"none"}'

     it "Passes a search query to Jira, default options", ->
-        fields = ["summary", "status", "assignee", "description"]
         options =
             rejectUnauthorized: true
             uri: makeUrl "search"
@@ -318,7 +317,8 @@
                 jql: 'aJQLstring'
                 startAt: 0
                 maxResults: 50
-                fields: fields
+                fields: ["summary", "status", "assignee", "description"]
+                expand: ["schema", "names"]
             auth:
               user: 'test'
               pass: 'test'
@@ -352,6 +352,7 @@
                 startAt: 200
                 maxResults: 100
                 fields: fields
+                expand: ["schema", "names"]
             auth:
               user: 'test'
               pass: 'test'


resurrection.account: set delete anchor content as "Unlink"

--- client/Account/views/linkedaccts.coffee
+++ client/Account/views/linkedaccts.coffee
@@ -97,7 +97,7 @@
     """

       {cite{ #(title)}}
-
+      Unlink


#376 update  spec

#376 update  spec

--- spec/operator-modifier-spec.coffee
+++ spec/operator-modifier-spec.coffee
@@ -90,7 +90,7 @@
         editor.insertText('!!!')
         ensure "escape",
           mode: 'normal'
-          numCursors: 8
+          numCursors: 1
           text: """

           !!!: xxx: !!!:


fix lint

fix lint

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

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


Use correct textmate package names

Use correct textmate package names

--- spec/link-spec.coffee
+++ spec/link-spec.coffee
@@ -6,8 +6,8 @@
   [editor] = []

   beforeEach ->
-    atom.activatePackage('javascript.tmbundle', sync: true)
-    atom.activatePackage('hyperlink-helper.tmbundle', sync: true)
+    atom.activatePackage('javascript-tmbundle', sync: true)
+    atom.activatePackage('hyperlink-helper-tmbundle', sync: true)
     window.rootView = new RootView
     rootView.open('sample.js')
     atom.activatePackage('link')


sidebar: add default stack updated to sidebar component

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/sidebar/container.coffee
+++ client/app/lib/sidebar/container.coffee
@@ -11,6 +11,7 @@

 SidebarFlux = require 'app/flux/sidebar'

+DefaultStackUpdated = require './components/defaultstackupdated'
 SidebarResources = require './components/resources'
 SidebarFooterLogo = require './components/footerlogo'

@@ -25,7 +26,6 @@

   constructor: (props) ->
     super props
-
     @state = { loading: yes }

@@ -44,6 +44,9 @@

     <Scroller className={curry 'activity-sidebar', @props.className}>

+      {@props.isDefaultStackUpdated and
+        <DefaultStackUpdated />}
+
       <SidebarResources
         loading={@state.loading}
         hasTemplate={!!@props.templates.length}


Fixed require

Fixed require

--- spec/main-spec.coffee
+++ spec/main-spec.coffee
@@ -1,3 +1,5 @@
+require('./helpers')
+
 fs = require('fs')
 glob = require('glob')
 deref = require('../lib')


Add specs about main.coffee

Add specs about main.coffee

--- spec/main-spec.coffee
+++ spec/main-spec.coffee
@@ -9,12 +9,16 @@
   activationPromise = null

   beforeEach ->
+    # Create a fake workspace and open a sample file
     atom.workspaceView = new WorkspaceView
+    atom.workspaceView.openSync 'sample.coffee'
+
     activationPromise = atom.packages.activatePackage 'coffee-refactor'
+

   describe "when the test:toggle event is triggered", ->
     it "attaches and then detaches the view", ->
-      expect(atom.workspaceView.find('.coffee-refactor')).not.toExist()
+      expect(atom.workspaceView.find('.coffee-refactor-reference')).not.toExist()

       # This is an activation event, triggering it will cause the package to be
       # activated.
@@ -24,6 +28,7 @@
         activationPromise

       runs ->
-        # expect(atom.workspaceView.find('.coffee-refactor')).toExist()
+        console.log atom.workspaceView.find('.coffee-refactor-reference').length
+        expect(atom.workspaceView.find('.coffee-refactor-reference')).toExist()
         # atom.workspaceView.trigger 'test:toggle'
         # expect(atom.workspaceView.find('.coffee-refactor')).not.toExist()


remove redundant test case for bus

remove redundant test case for bus

--- spec/specs/bus.coffee
+++ spec/specs/bus.coffee
@@ -59,10 +59,6 @@
     bus.onValue((x) -> values.push(x))
     expect(values).to.deep.equal(["x", "y"])

-  it "handles end() calls even when there are no subscribers", ->
-    bus = new Bacon.Bus()
-    bus.end()
-
   it "throws if a non-observable is plugged", ->
     expect(-> new Bacon.Bus().plug(undefined)).to.throw()

@@ -113,7 +109,7 @@
     busB.end()
     busA.push('foo')
     expect(failed).to.equal(false)
-
+
   it "respects end() calls before subscribers", ->
     failed = false
     bus = new Bacon.Bus()


Rename editSession to editor

Rename editSession to editor

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -32,7 +32,7 @@
       expect(atom.workspaceView.panes.find('.pane > .tab-bar').length).toBe 0

 describe "TabBarView", ->
-  [item1, item2, editSession1, pane, tabBar] = []
+  [item1, item2, editor1, pane, tabBar] = []

   class TestView extends View
     @deserialize: ({title, longTitle}) -> new TestView(title, longTitle)
@@ -47,7 +47,7 @@
     atom.deserializers.add(TestView)
     item1 = new TestView('Item 1')
     item2 = new TestView('Item 2')
-    editSession1 = atom.workspaceView.openSync('sample.js')
+    editor1 = atom.workspaceView.openSync('sample.js')
     pane = atom.workspaceView.getActivePane()
     pane.addItem(item1, 0)
     pane.addItem(item2, 2)
@@ -63,7 +63,7 @@
       expect(tabBar.find('.tab').length).toBe 3

       expect(tabBar.find('.tab:eq(0) .title').text()).toBe item1.getTitle()
-      expect(tabBar.find('.tab:eq(1) .title').text()).toBe editSession1.getTitle()
+      expect(tabBar.find('.tab:eq(1) .title').text()).toBe editor1.getTitle()
       expect(tabBar.find('.tab:eq(2) .title').text()).toBe item2.getTitle()

     it "highlights the tab for the active pane item", ->
@@ -88,10 +88,10 @@
       expect(tabBar.tabAtIndex(1).find('.title')).toHaveText 'Item 3'

     it "adds the 'modified' class to the new tab if the item is initially modified", ->
-      editSession2 = atom.project.openSync('sample.txt')
-      editSession2.insertText('x')
-      pane.showItem(editSession2)
-      expect(tabBar.tabForItem(editSession2)).toHaveClass 'modified'
+      editor2 = atom.project.openSync('sample.txt')
+      editor2.insertText('x')
+      pane.showItem(editor2)
+      expect(tabBar.tabForItem(editor2)).toHaveClass 'modified'

   describe "when an item is removed from the pane", ->
     it "removes the item's tab from the tab bar", ->
@@ -124,17 +124,17 @@

   describe "when a tab's close icon is clicked", ->
     it "destroys the tab's item on the pane", ->
-      tabBar.tabForItem(editSession1).find('.close-icon').click()
+      tabBar.tabForItem(editor1).find('.close-icon').click()
       expect(pane.getItems().length).toBe 2
-      expect(pane.getItems().indexOf(editSession1)).toBe -1
-      expect(editSession1.destroyed).toBeTruthy()
+      expect(pane.getItems().indexOf(editor1)).toBe -1
+      expect(editor1.destroyed).toBeTruthy()
       expect(tabBar.getTabs().length).toBe 2
       expect(tabBar.find('.tab:contains(sample.js)')).not.toExist()

   describe "when a tab item's title changes", ->
     it "updates the title of the item's tab", ->
-      editSession1.buffer.setPath('/this/is-a/test.txt')
-      expect(tabBar.tabForItem(editSession1)).toHaveText 'test.txt'
+      editor1.buffer.setPath('/this/is-a/test.txt')
+      expect(tabBar.tabForItem(editor1)).toHaveText 'test.txt'

   describe "when two tabs have the same title", ->
     it "displays the long title on the tab if it's available from the item", ->
@@ -156,18 +156,18 @@

   describe "when a tab item's modified status changes", ->
     it "adds or removes the 'modified' class to the tab based on the status", ->
-      tab = tabBar.tabForItem(editSession1)
-      expect(editSession1.isModified()).toBeFalsy()
+      tab = tabBar.tabForItem(editor1)
+      expect(editor1.isModified()).toBeFalsy()
       expect(tab).not.toHaveClass 'modified'

-      editSession1.insertText('x')
-      advanceClock(editSession1.buffer.stoppedChangingDelay)
-      expect(editSession1.isModified()).toBeTruthy()
+      editor1.insertText('x')
+      advanceClock(editor1.buffer.stoppedChangingDelay)
+      expect(editor1.isModified()).toBeTruthy()
       expect(tab).toHaveClass 'modified'

-      editSession1.undo()
-      advanceClock(editSession1.buffer.stoppedChangingDelay)
-      expect(editSession1.isModified()).toBeFalsy()
+      editor1.undo()
+      advanceClock(editor1.buffer.stoppedChangingDelay)
+      expect(editor1.isModified()).toBeFalsy()
       expect(tab).not.toHaveClass 'modified'

   describe "when a pane item moves to a new index", ->
@@ -175,7 +175,7 @@
       expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
       pane.moveItem(item2, 1)
       expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "Item 2", "sample.js"]
-      pane.moveItem(editSession1, 0)
+      pane.moveItem(editor1, 0)
       expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 1", "Item 2"]
       pane.moveItem(item1, 2)
       expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"]
@@ -201,7 +201,7 @@
       describe "when it is dropped on tab that's later in the list", ->
         it "moves the tab and its item, shows the tab's item, and focuses the pane", ->
           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-          expect(pane.getItems()).toEqual [item1, editSession1, item2]
+          expect(pane.getItems()).toEqual [item1, editor1, item2]
           expect(pane.activeItem).toBe item2
           spyOn(pane, 'focus')

@@ -210,14 +210,14 @@
           tabBar.onDrop(dropEvent)

           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 1", "Item 2"]
-          expect(pane.getItems()).toEqual [editSession1, item1, item2]
+          expect(pane.getItems()).toEqual [editor1, item1, item2]
           expect(pane.activeItem).toBe item1
           expect(pane.focus).toHaveBeenCalled()

       describe "when it is dropped on a tab that's earlier in the list", ->
         it "moves the tab and its item, shows the tab's item, and focuses the pane", ->
           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-          expect(pane.getItems()).toEqual [item1, editSession1, item2]
+          expect(pane.getItems()).toEqual [item1, editor1, item2]
           expect(pane.activeItem).toBe item2
           spyOn(pane, 'focus')

@@ -226,14 +226,14 @@
           tabBar.onDrop(dropEvent)

           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "Item 2", "sample.js"]
-          expect(pane.getItems()).toEqual [item1, item2, editSession1]
+          expect(pane.getItems()).toEqual [item1, item2, editor1]
           expect(pane.activeItem).toBe item2
           expect(pane.focus).toHaveBeenCalled()

       describe "when it is dropped on itself", ->
         it "doesn't move the tab or item, but does make it the active item and focuses the pane", ->
           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-          expect(pane.getItems()).toEqual [item1, editSession1, item2]
+          expect(pane.getItems()).toEqual [item1, editor1, item2]
           expect(pane.activeItem).toBe item2
           spyOn(pane, 'focus')

@@ -242,14 +242,14 @@
           tabBar.onDrop(dropEvent)

           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-          expect(pane.getItems()).toEqual [item1, editSession1, item2]
+          expect(pane.getItems()).toEqual [item1, editor1, item2]
           expect(pane.activeItem).toBe item1
           expect(pane.focus).toHaveBeenCalled()

       describe "when it is dropped on the tab bar", ->
         it "moves the tab and its item to the end", ->
           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-          expect(pane.getItems()).toEqual [item1, editSession1, item2]
+          expect(pane.getItems()).toEqual [item1, editor1, item2]
           expect(pane.activeItem).toBe item2
           spyOn(pane, 'focus')

@@ -258,7 +258,7 @@
           tabBar.onDrop(dropEvent)

           expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"]
-          expect(pane.getItems()).toEqual [editSession1, item2, item1]
+          expect(pane.getItems()).toEqual [editor1, item2, item1]

     describe "when a tab is dragged to a different pane", ->
       [pane2, tabBar2, item2b] = []
@@ -270,7 +270,7 @@

       it "removes the tab and item from their original pane and moves them to the target pane", ->
         expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-        expect(pane.getItems()).toEqual [item1, editSession1, item2]
+        expect(pane.getItems()).toEqual [item1, editor1, item2]
         expect(pane.activeItem).toBe item2

         expect(tabBar2.getTabs().map (tab) -> tab.text()).toEqual ["Item 2"]
@@ -283,7 +283,7 @@
         tabBar.onDrop(dropEvent)

         expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2"]
-        expect(pane.getItems()).toEqual [editSession1, item2]
+        expect(pane.getItems()).toEqual [editor1, item2]
         expect(pane.activeItem).toBe item2

         expect(tabBar2.getTabs().map (tab) -> tab.text()).toEqual ["Item 2", "Item 1"]
@@ -294,7 +294,7 @@
     describe "when a non-tab is dragged to pane", ->
       it "has no effect", ->
         expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-        expect(pane.getItems()).toEqual [item1, editSession1, item2]
+        expect(pane.getItems()).toEqual [item1, editor1, item2]
         expect(pane.activeItem).toBe item2
         spyOn(pane, 'focus')

@@ -302,7 +302,7 @@
         tabBar.onDrop(dropEvent)

         expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
-        expect(pane.getItems()).toEqual [item1, editSession1, item2]
+        expect(pane.getItems()).toEqual [item1, editor1, item2]
         expect(pane.activeItem).toBe item2
         expect(pane.focus).not.toHaveBeenCalled()

@@ -311,5 +311,5 @@
         [dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(1), tabBar.tabAtIndex(1))
         tabBar.onDragStart(dragStartEvent)

-        expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual editSession1.getPath()
-        expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual 'file://' + editSession1.getPath()
+        expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual editor1.getPath()
+        expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual 'file://' + editor1.getPath()


TerminalPane: Listen webtermView click event to mark that ideView as active.

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -105,6 +105,9 @@

     terminalPane = new IDE.TerminalPane { machine, path }
     @createPane_ terminalPane, { name: 'Terminal' }
+
+    terminalPane.once 'WebtermCreated', =>
+      terminalPane.webtermView.on 'click', @bound 'click'

   createDrawingBoard: ->
     @createPane_ new IDE.DrawingPane, { name: 'Drawing' }


Open preview in a new browser window/tab

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -17,7 +17,7 @@
     @tabView.on 'MachineWebPageRequested',  @bound 'openMachineWebPage'
     @tabView.on 'ShortcutsViewRequested',   @bound 'createShortcutsView'
     @tabView.on 'TerminalPaneRequested',    @bound 'createTerminal'
-    @tabView.on 'PreviewPaneRequested',     (url) => @createPreview url
+    @tabView.on 'PreviewPaneRequested',     (url) -> window.open "http://#{url}"
     @tabView.on 'DrawingPaneRequested',     @bound 'createDrawingBoard'
     @tabView.on 'ViewNeedsToBeShown',       @bound 'showView'
     @tabView.on 'TabNeedsToBeClosed',       @bound 'closeTabByFile'


Only write group metadata when there is metadata

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -652,7 +652,7 @@
       groupData =
         name: group.name
         nodes: group.nodes
-      if group.metadata
+      if Object.keys(group.metadata).length
         groupData.metadata = group.metadata
       json.groups.push groupData


Added username to schema

Added username to schema

--- both/collections/users.coffee
+++ both/collections/users.coffee
@@ -226,6 +226,12 @@
 )

 Schemas.User = new SimpleSchema(
+
+  username:
+    type: String
+    regEx: /^[a-z0-9A-Z_]{3,15}$/
+    optional: true
+

   emails:
     type: [Object]


Add missing docs.

Add missing docs.

--- src/hamlc.coffee
+++ src/hamlc.coffee
@@ -39,6 +39,8 @@
   #   compiler options.
   #
   # @param [String] source the Haml Coffee source
+  # @param [String] name the template name
+  # @param [String] namespace the template namespace
   # @param [Object] options the compiler options
   # @return [String] the template source code
   #


Don't include nan

Don't include nan

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -53,7 +53,7 @@
       path.join('jasmine-reporters', 'ext')
       path.join('jasmine-node', 'node_modules', 'gaze')
       path.join('jasmine-node', 'spec')
-      path.join('nan', 'nan.h')
+      path.join('node_modules', 'nan')
       path.join('build', 'binding.Makefile')
       path.join('build', 'config.gypi')
       path.join('build', 'gyp-mac-tool')


Moved the underscore method names

Moved the underscore method names

--- src/index.coffee
+++ src/index.coffee
@@ -14,6 +14,115 @@
     Q.all([unresolvedKeys, unresolvedValues]).then ([resolvedKeys, resolvedValues]) ->
       _.object(_.zip(resolvedKeys, resolvedValues))

+
+
+
+underscoreMethods = [
+  # COLLECTIONS
+  'each'
+  # 'map' -- ignored in favor of the native method
+  # 'reduce' -- ignored in favor of the native method
+  # 'reduceRight' -- ignored in favor of the native method
+  # 'find' -- ignored in favor of the native method
+  'filter'
+  'where'
+  'findWhere'
+  'reject'
+  # 'every' -- ignored in favor of the native method
+  # 'some' -- ignored in favor of the native method
+  'contains'
+  'invoke'
+  'pluck'
+  'max'
+  'min'
+  'sortBy'
+  'groupBy'
+  'indexBy'
+  'countBy'
+  'shuffle'
+  'sample'
+  'toArray'
+  'size'
+
+  # ARRAYS
+  'first'
+  'initial'
+  'last'
+  'rest'
+  'compact'
+  'flatten'
+  'without'
+  'union'
+  'intersection'
+  'difference'
+  'uniq'
+  'zip'
+  'object'
+  'indexOf'
+  'lastIndexOf'
+  'sortedIndex'
+  'range'
+
+  # FUNCTIONS -- not sure if these makes sense yet
+  # 'bind'
+  # 'bindAll'
+  # 'partial'
+  # 'memoize'
+  # 'delay'
+  # 'defer'
+  # 'throttle'
+  # 'debounce'
+  # 'once'
+  # 'after'
+  # 'wrap'
+  # 'compose'
+
+  # OBJECTS
+  'keys'
+  'values'
+  'pairs'
+  'invert'
+  'functions'
+  'extend'
+  'pick'
+  'omit'
+  'defaults'
+  'clone'
+  'tap'
+  'has'
+  'isEqual'
+  'isEmpty'
+  'isElement'
+  'isArray'
+  'isObject'
+  'isArguments'
+  'isFunction'
+  'isString'
+  'isNumber'
+  'isFinite'
+  'isBoolean'
+  'isDate'
+  'isRegExp'
+  'isNaN'
+  'isNull'
+  'isUndefined'
+
+  # UTILITY
+  # 'noConflict' -- not applicable
+  'identity'
+  'times'
+  'random'
+  # 'mixin' -- I have no idea how this would affect things
+  'uniqueId'
+  'escape'
+  'unescape'
+  'result'
+  'template'
+
+  # CHAINING -- zee has its own chaining
+  # 'chain'
+  # 'value'
+]

@@ -34,112 +143,6 @@
   genericMethods = ['toString']
   arrayMethods = ['reverse', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'every', 'some', 'filter', 'find', 'findIndex', 'map', 'reduce', 'reduceRight']
   stringMethods = ['split']
-  underscoreMethods = [
-    # COLLECTIONS
-    'each'
-    # 'map' -- ignored in favor of the native method
-    # 'reduce' -- ignored in favor of the native method
-    # 'reduceRight' -- ignored in favor of the native method
-    # 'find' -- ignored in favor of the native method
-    'filter'
-    'where'
-    'findWhere'
-    'reject'
-    # 'every' -- ignored in favor of the native method
-    # 'some' -- ignored in favor of the native method
-    'contains'
-    'invoke'
-    'pluck'
-    'max'
-    'min'
-    'sortBy'
-    'groupBy'
-    'indexBy'
-    'countBy'
-    'shuffle'
-    'sample'
-    'toArray'
-    'size'
-
-    # ARRAYS
-    'first'
-    'initial'
-    'last'
-    'rest'
-    'compact'
-    'flatten'
-    'without'
-    'union'
-    'intersection'
-    'difference'
-    'uniq'
-    'zip'
-    'object'
-    'indexOf'
-    'lastIndexOf'
-    'sortedIndex'
-    'range'
-
-    # FUNCTIONS -- not sure if these makes sense yet
-    # 'bind'
-    # 'bindAll'
-    # 'partial'
-    # 'memoize'
-    # 'delay'
-    # 'defer'
-    # 'throttle'
-    # 'debounce'
-    # 'once'
-    # 'after'
-    # 'wrap'
-    # 'compose'
-
-    # OBJECTS
-    'keys'
-    'values'
-    'pairs'
-    'invert'
-    'functions'
-    'extend'
-    'pick'
-    'omit'
-    'defaults'
-    'clone'
-    'tap'
-    'has'
-    'isEqual'
-    'isEmpty'
-    'isElement'
-    'isArray'
-    'isObject'
-    'isArguments'
-    'isFunction'
-    'isString'
-    'isNumber'
-    'isFinite'
-    'isBoolean'
-    'isDate'
-    'isRegExp'
-    'isNaN'
-    'isNull'
-    'isUndefined'
-
-    # UTILITY
-    # 'noConflict' -- not applicable
-    'identity'
-    'times'
-    'random'
-    # 'mixin' -- I have no idea how this would affect things
-    'uniqueId'
-    'escape'
-    'unescape'
-    'result'
-    'template'
-
-    # CHAINING -- zee has its own chaining
-    # 'chain'
-    # 'value'
-  ]

   underscoreEachMethods = ['omit', 'pick', 'keys']


* [bug] the task-registry-series uses the logger attribute to output

--- src/index.coffee
+++ src/index.coffee
@@ -61,7 +61,8 @@
         initMissedTasks vMissedTasks
     return
   initLogger: (aOptions)->
-    Task::logger = new Logger aOptions.logger
+    # TODO: the tasks.logger should use the Task::logger. (remove tasks.logger attribute)
+    tasks.logger = Task::logger = new Logger aOptions.logger
     @logger.colors.process = 'green'
     @logger.colors.processed = 'green'
     @logger.statusLevels.process = 'notice'


add protocol detecting back

add protocol detecting back

--- src/index.coffee
+++ src/index.coffee
@@ -56,7 +56,7 @@
           (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
           (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
           m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-          })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+          })(window,document,'script',('https:'===window.location.protocol?'https:':'http:') + '//www.google-analytics.com/analytics.js','ga');
           ga('create', '#{@option.account}');
           ga('send', 'pageview');
         """)
@@ -82,7 +82,7 @@
           var _hmt = _hmt || [];
           (function() {
             var hm = document.createElement("script");
-            hm.src = "//hm.baidu.com/hm.js?#{@option.account}";
+            hm.src = ('https:'===window.location.protocol?'https:':'http:') + "//hm.baidu.com/hm.js?#{@option.account}";
             var s = document.getElementsByTagName("script")[0];
             s.parentNode.insertBefore(hm, s);
           })();


typo: proccess

typo: proccess

--- src/index.coffee
+++ src/index.coffee
@@ -15,7 +15,7 @@
 KEY = process.env.THETVDB_API_KEY
 unless KEY
   console.log "You should set up 'THETVDB_API_KEY' env variable. Go here to get it: http://thetvdb.com/?tab=apiregister"
-  proccess.exit(1)
+  process.exit(1)

 cmd = process.argv[2]
 args = process.argv.slice(3)


Make sure to send the summoner name when you load matches too

--- src/index.coffee
+++ src/index.coffee
@@ -93,7 +93,7 @@
             if err
               throw err
             else
-              page.send 'loadingMatchlistFinished', {}
+              page.send 'loadingMatchlistFinished', {summonerName: config.accounts[0].name}
               util.log 'Yay we added the matches to the database'
               mainFunction()
       else


path is optional for controllers

path is optional for controllers

--- src/index.coffee
+++ src/index.coffee
@@ -131,7 +131,7 @@
                 ctrl::path = moduleInfo.url + ctrl::path  or  '/'
             else
                 ctrl::path =
-                    for p in ctrl::path
+                    for p in ctrl::path or []
                         moduleInfo.url + p

             app.controller ctrl


Needed a flat version.

Needed a flat version.

--- src/lists.coffee
+++ src/lists.coffee
@@ -48,16 +48,16 @@
   car = (c) -> c[0]
   cdr = (c) -> c[1]

-  vectorToList = (v, p) ->
+  vectorToList = (v, p, d = true) ->
     p = if p? then p else 0
     if p >= v.length then return nil
     # Annoying, but since lists are represented as nested arrays, they
     # have to be intercepted first.  The use of duck-typing here is
     # frustrating.
     item = if pairp(v[p]) then v[p] else
-      if vectorp(v[p]) then vectorToList(v[p]) else v[p]
+      if (d and vectorp(v[p])) then vectorToList(v[p]) else v[p]
     cons(item, vectorToList(v, p + 1))
-
+
   list = (v...) ->
     ln = v.length
     (nl = (a) ->


send quit command to nevoim when window is closed

--- src/nvim/nvim.coffee
+++ src/nvim/nvim.coffee
@@ -18,12 +18,19 @@

     @nvim_process = child_process.spawn 'nvim', nvim_args, stdio: ['pipe', 'pipe', process.stderr]
+    @nvim_running = true
     console.log 'child process spawned: ' + @nvim_process.pid

     @nvim_process.on 'close', =>
+      @nvim_running = false
       console.log 'child process closed'
       @session.detach()
       remote.require('app').quit()
+
+    window.onbeforeunload = (e) =>
+      if @nvim_running
+        @session.request 'vim_command', ['qa!'], ->
+        false

     @session = new Session
     @session.attach @nvim_process.stdin, @nvim_process.stdout


Fix from code inspect: Use "not" instead of "!".

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -49,7 +49,7 @@
 # Validates that `fragments` is an array of CodeFragment objects.
 # If PARANOID is false, then this does nothing.
 checkFragments = (fragments, node=null) ->
-  if !PARANOID then return fragments
+  if not PARANOID then return fragments

   nodeName = if node then " from #{node.constructor.name}" else ""
   if !fragments


Exit and reboot if an error is occurred.

--- src/robot.coffee
+++ src/robot.coffee
@@ -71,9 +71,9 @@

     @on 'error', (err, res) =>
       @invokeErrorHandlers(err, res)
+      process.exit(1)
     @onUncaughtException = (err) =>
       @emit 'error', err
-      process.exit(1)
     process.on 'uncaughtException', @onUncaughtException

   # Public: Adds a custom Listener with the provided matcher, options, and


Dependencies handled by NPM

Dependencies handled by NPM

--- src/steam.coffee
+++ src/steam.coffee
@@ -1,10 +1,5 @@
 # Description:
 #   Steam Web API w/ Dota 2
-#
-# Dependencies:
-#   "moment": "^2.8.3"
-#   "msgpack": "^0.2.3"
-#   "ref": "^0.3.2"
 #
 # Configuration:
 #   STEAM_API_KEY


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -19,7 +19,7 @@

       else
         f = Easing[data.easing](data.start, data.end)
-        t = (I.age - data.startTime) /
+        t = (I.age - data.startTime) / data.duration
         I[property] = f()

   tween: (duration, properties) ->


GameObject.Effect

GameObject.Effect

--- src/tween.coffee
+++ src/tween.coffee
@@ -20,7 +20,7 @@
         delete I.activeTweens[property]
       else
         f = Easing[data.easing](data.start, data.end)
-        t = (I.age - data.startTime) / data.duration
+        t = (I.age + - data.startTime) / data.duration
         I[property] = f(t)

   ###*


Be consistent.

Be consistent.

Either we don't allow port in the options object, or we allow host as well.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -456,6 +456,7 @@
       when 'object'
         for k, v of a
           switch k
+            when 'host' then host = v
             when 'port' then port = v
             when 'disable_io' then disable_io = v


added feature of getting offset and setting css

--- src/coffee/domUtils.coffee
+++ src/coffee/domUtils.coffee
@@ -30,7 +30,7 @@

   ###
-   * @param {String tag}
+   * @param {String} tag
    * @type HTMLElement
   ###
   createElement:( tag )-> document.createElement( tag )
@@ -50,7 +50,23 @@
     )
     children

+  ###
+  # @param {String} el
+  # @param {Object} attrs
+  ###
+  css:( el, attrs )->
+
+    for key, attr of attrs
+      el.style[key] = attr
+
+  ###
+  #
+  ###
+  offset:( el )->
+    box = el.getBoundingClientRect()
+    ownDoc = el.ownerDocument
+    docElem = ownDoc.documentElement
+    top:box.top + ( docElem.scrollTop ) - ( docElem.clientTop || 0 )
+    left:box.left + ( docElem.scrollLeft ) - ( docElem.clientLeft || 0 )

-
-


Server: export suggestedusername min, max lengths

--- servers/lib/server/handlers/api/helpers.coffee
+++ servers/lib/server/handlers/api/helpers.coffee
@@ -1,5 +1,8 @@
 errors = require './errors'
 koding = require '../../bongo'
+
+SUGGESTED_USERNAME_MIN_LENGTH = 4
+SUGGESTED_USERNAME_MAX_LENGTH = 15

 sendApiError = (res, error) ->

@@ -32,12 +35,9 @@
     return callback null

-
 isSuggestedUsernameLengthValid = (suggestedUsername) ->

-  if 4 <= suggestedUsername?.length <= 15
-  then yes
-  else no
+  return SUGGESTED_USERNAME_MIN_LENGTH <= suggestedUsername?.length <= SUGGESTED_USERNAME_MAX_LENGTH

 isUsernameLengthValid = (username) ->
@@ -45,17 +45,17 @@
   { JUser } = koding.models
   { minLength, maxLength } = JUser.getValidUsernameLengthRange()

-  if minLength <= username?.length <= maxLength
-  then yes
-  else no
-
+  return minLength <= username?.length <= maxLength

 module.exports = {
   sendApiError
   sendApiResponse
+  checkApiAvailability
   isUsernameLengthValid
-  checkApiAvailability
   isSuggestedUsernameLengthValid
+
+  SUGGESTED_USERNAME_MIN_LENGTH
+  SUGGESTED_USERNAME_MAX_LENGTH
 }


Made Player strokeWidth 5 instead of 3

Made Player strokeWidth 5 instead of 3

--- player.coffee
+++ player.coffee
@@ -24,7 +24,7 @@
       @positions.push(pos)
       @current_line = new fabric.Polyline([], {
         stroke: 'blue',
-        stokeWidth: 3,
+        strokeWidth: 5,
         fille: 'white'
         left: 200
         top: 200


Restored numbering

Restored numbering

Chapters stopped being numbered. Fixed.

--- src/scripts/modules/media/tabs/contents/contents.coffee
+++ src/scripts/modules/media/tabs/contents/contents.coffee
@@ -20,7 +20,7 @@
           sectionNumber = cumulativeChapters[depth] ? 0
         if not (
           atTopLevel or
-          isCcap and sectionNumber is 0 and title.match(/^Introduction/)
+          isCcap and sectionNumber is 0
         )
           sectionNumber += 1
           cumulativeChapters[depth] = sectionNumber
@@ -51,9 +51,14 @@

     initialize: () ->
       super()
-      @listenTo(@model, 'change:editable removeNode moveNode add:contents', @render)
-      cumulativeChapters = []
-      numberChapters(@model.attributes.contents.models) if @model.attributes.contents?.models?
+      @listenTo(@model, 'change:editable removeNode moveNode', @render)
+      @listenTo(@model, 'change:contents', =>
+        nodes = @model.attributes.contents?.models
+        if nodes?
+          cumulativeChapters = []
+          numberChapters(nodes)
+          @render()
+        )

     onRender: () ->
       @$el.addClass('table-of-contents')


Don't normalize path passed to synctex

Don't normalize path passed to synctex

--- services/clsi/app/coffee/CompileManager.coffee
+++ services/clsi/app/coffee/CompileManager.coffee
@@ -58,7 +58,7 @@
 		# might not match the file path on the host. The .synctex.gz file however, will be accessed
 		# wherever it is on the host.
 		base_dir = Settings.path.synctexBaseDir(project_id)
-		file_path = Path.join(base_dir, file_name)
+		file_path = base_dir + "/" + file_name
 		synctex_path = Path.join(Settings.path.compilesDir, project_id, "output.pdf")
 		CompileManager._runSynctex ["code", synctex_path, file_path, line, column], (error, stdout) ->
 			return callback(error) if error?


added comments

added comments

--- npm/src/helpers.coffee
+++ npm/src/helpers.coffee
@@ -4,6 +4,7 @@
 async     = require 'async'
 fileUtil  = require 'file'

+# copy source to destination recursively
 exports.copy = (source, target) ->
   exec 'cp -R ' + source + ' ' + target, (error, stdout, stderr) ->
     console.log(stdout) if stdout
@@ -52,6 +53,7 @@
       files = filterForJsFiles files
       callback err, files.sort()

+# create directory path for the given file
 exports.mkdirsForFile = (file, mode) ->
   newPath = file.replace(/\/[^\/]*$/, '')
   fileUtil.mkdirsSync newPath, mode


Use new uuid require format

Use new uuid require format

--- src/transport.coffee
+++ src/transport.coffee
@@ -5,7 +5,7 @@
 # ***** END LICENSE BLOCK *****

 stream = require('stream')
-uuid = require('uuid')
+uuidv4 = require('uuid/v4')
 utils = require('./utils')

 class Transport
@@ -21,7 +21,7 @@

 class SockJSConnection extends stream.Stream
     constructor: (@_session) ->
-        @id  = uuid.v4()
+        @id  = uuidv4()
         @headers = {}
         @prefix = @_session.prefix


Suppress 'mockery' warning when requiring 'axiom' file in tests

--- test/core.coffee
+++ test/core.coffee
@@ -18,7 +18,7 @@

     mockery.registerAllowables [
       join(__dirname, '..', 'package')
-      join(__dirname, '..', 'Axiom')
+      join(__dirname, '..', 'axiom')
       join(__dirname, '..', 'axiom', 'base')
     ]


added login link to reset message

added login link to reset message

--- services/web/public/coffee/forms.coffee
+++ services/web/public/coffee/forms.coffee
@@ -119,7 +119,7 @@
 				type:'POST'
 				data: formData
 				success: (data)->
-					new Message text:"Your password has been reset"
+					new Message text:"Your password has been reset. Login here."
 				error:(data)->
 					new Message type:"error", text:"Something went wrong processing your request."


Key bindings; more small tweaks.

Key bindings; more small tweaks.

--- content_scripts/mode_key_handler.coffee
+++ content_scripts/mode_key_handler.coffee
@@ -1,8 +1,25 @@
+# Example key mapping (@keyMapping):
+#   i:
+#     command: "enterInsertMode", ... # This is a registryEntry object (as too are the other commands).
+#   g:
+#     g:
+#       command: "scrollToTop", ...
+#     t:
+#       command: "nextTab", ...
+#
+# This key-mapping structure is generated by Commands.generateKeyStateMapping() and may be arbitrarily deep.
+# Observe that @keyMapping["g"] is itself also a valid key mapping.  At any point, the key state (@keyState)
+# consists of a (non-empty) list of such mappings.

 class KeyHandlerMode extends Mode
   keydownEvents: {}
   setKeyMapping: (@keyMapping) -> @reset()
   setPassKeys: (@passKeys) -> @reset()
+
+  # Reset the key state, optionally retaining the count provided.
+  reset: (@countPrefix = 0) ->
+    bgLog "Clearing key state, set count=#{@countPrefix}."
+    @keyState = [@keyMapping]

   constructor: (options) ->
     @commandHandler = options.commandHandler ? (->)
@@ -17,22 +34,22 @@

   onKeydown: (event) ->
     keyChar = KeyboardUtils.getKeyCharString event
-    if KeyboardUtils.isEscape event
-      if @countPrefix == 0 and @keyState.length == 1
-        @continueBubbling
-      else
-        @keydownEvents[event.keyCode] = true
-        @reset()
-        false # Suppress event.
-    else if keyChar and @keyCharIsMapped keyChar
+    isEscape = KeyboardUtils.isEscape event
+    if isEscape and @countPrefix == 0 and @keyState.length == 1
+      @continueBubbling
+    else if isEscape
+      @keydownEvents[event.keyCode] = true
+      @reset()
+      false # Suppress event.
+    else if @keyCharIsMapped keyChar
       @unlessKeyCharIsPassKey keyChar, =>
         @keydownEvents[event.keyCode] = true
         @handleKeyChar event, keyChar
     else if keyChar
       @continueBubbling
     else if (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(keyChar) or @isCountKey keyChar)
-      # It looks like we will be handling a subsequent keypress event, so suppress propagation of this event
-      # to prevent triggering page event listeners (e.g. Google instant Search).
+      # It looks like we will possibly be handling a subsequent keypress event, so suppress propagation of
+      # this event to prevent triggering page event listeners (e.g. Google instant Search).
       @unlessKeyCharIsPassKey keyChar, =>
         @keydownEvents[event.keyCode] = true
         DomUtils.suppressPropagation event
@@ -43,7 +60,7 @@
   onKeypress: (event) ->
     keyChar = KeyboardUtils.getKeyCharString event
     @unlessKeyCharIsPassKey keyChar, =>
-      if keyChar and @keyCharIsMapped keyChar
+      if @keyCharIsMapped keyChar
         @handleKeyChar event, keyChar
       else if @isCountKey keyChar
         digit = parseInt keyChar
@@ -61,35 +78,28 @@
     else
       @continueBubbling

-  handleKeyChar: (event, keyChar) ->
-    bgLog "Handling key #{keyChar}, mode=#{@name}."
-    @advanceKeyState keyChar
-    command = (@keyState.filter (entry) -> entry.command)[0]
-    if command?
-      count = if 0 < @countPrefix then @countPrefix else 1
-      @reset()
-      bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}."
-      @commandHandler {command, count}
-    false # Suppress event.
-
+  # This tests whether there is a mapping of keyChar in the current key state.
   keyCharIsMapped: (keyChar) ->
     (mapping for mapping in @keyState when keyChar of mapping)[0]?

-  # The next key state is the current mappings matching keyChar plus @keyMapping.
-  advanceKeyState: (keyChar) ->
-    newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping)
-    @keyState = [newMappings..., @keyMapping]
-
-  # Reset the state (as if no keys had been handled), but optionally retaining the count provided.
-  reset: (@countPrefix = 0) ->
-    bgLog "Clearing key queue, set count=#{@countPrefix}."
-    @keyState = [@keyMapping]
+  handleKeyChar: (event, keyChar) ->
+    bgLog "Handling key #{keyChar}, mode=#{@name}."
+    # Advance the key state.  The new key state is the current mappings of keyChar, plus @keyMapping.
+    @keyState = (mapping[keyChar] for mapping in @keyState when keyChar of mapping)
+    @keyState.push @keyMapping
+    command = (mapping for mapping in @keyState when mapping.command)[0]
+    if command?
+      count = if 0 < @countPrefix then @countPrefix else 1
+      bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}."
+      @reset()
+      @commandHandler {command, count}
+    false # Suppress event.

   isCountKey: (keyChar) ->
     keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9'

   # Keystrokes are *never* considered passKeys if the user has begun entering a command.  So, for example, if
-  # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys.
+  # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys.
   unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) ->
     if @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "")
       @stopBubblingAndTrue


removed commented line

removed commented line

--- src/scripts/daterangepicker/date-range-picker-button.coffee
+++ src/scripts/daterangepicker/date-range-picker-button.coffee
@@ -19,7 +19,7 @@
         top: @parent.offset().top - @parent.scrollTop()
         left: @parent.offset().left - @parent.scrollLeft()
       parentRightEdge = @parent[0].clientWidth + @parent.offset().left
-    # @parent.css({ position: 'relative' })
+
     switch @options.opens
       when 'left'
         @container.css


Made the tour detect all deselects properly by setting an interval.

--- core/app/assets/javascripts/tour.coffee
+++ core/app/assets/javascripts/tour.coffee
@@ -1,24 +1,34 @@
 class window.InteractiveTour extends Backbone.View
   helpTextDelay: 560

+  detectSelecting: ->
+    if FACTLINK.getSelectionInfo().text.length > 0
+      @state.select_text()
+
+  detectDeselecting: ->
+    if FACTLINK.getSelectionInfo().text.length <= 0
+      @state.deselect_text()
+
   bindLibraryLoad: ->
-    $(window).on 'factlink.libraryLoaded', ->
-      FACTLINK.hideDimmer()
+    $(window).on 'factlink.libraryLoaded', => @onLibraryLoaded()

-      $('#create-your-first-factlink').on 'mouseup', ->
-        if FACTLINK.getSelectionInfo().text.length > 0
-          tour.state.select_text()
-        else
-          tour.state.deselect_text()
+  onLibraryLoaded: ->
+    FACTLINK.hideDimmer()

-      FACTLINK.on 'modalOpened', ->
-        tour.state.open_modal()
+    @detectDeselectingInterval = window.setInterval (=> @detectDeselecting()), 200 unless @detectDeselectingInterval?

-      FACTLINK.on 'modalClosed', ->
-        tour.state.close_modal()
+    $('#create-your-first-factlink').on 'mouseup', =>
+      @detectSelecting()
+      @detectDeselecting()

-      FACTLINK.on 'factlinkAdded', ->
-        tour.state.create_factlink()
+    FACTLINK.on 'modalOpened', =>
+      @state.open_modal()
+
+    FACTLINK.on 'modalClosed', =>
+      @state.close_modal()
+
+    FACTLINK.on 'factlinkAdded', =>
+      @state.create_factlink()

   renderExtensionButton: ->
     @extensionButton = new ExtensionButtonMimic()
@@ -32,6 +42,9 @@
     @bindLibraryLoad()

     @createStateMachine()
+
+  close: ->
+    window.clearInterval @detectDeselectingInterval

   createStateMachine: ->
     @state = StateMachine.create


added test setContentProvider

added test setContentProvider

--- test/main.coffee
+++ test/main.coffee
@@ -55,4 +55,18 @@
         # expect(fn()).to.deep.equal { encoding: 'utf8', prefix: '@', postfix: '', contentProvider:null }

+    describe '#setContentProvider()', ->

+      fn = metaEngine1.setContentProvider
+
+      it 'existance', ->
+
+        expect(metaEngine1).to.have.property('setContentProvider').that.is.a('function')
+
+      it.skip 'input-output sets', ->
+
+        # expect(fn()).to.deep.equal { encoding: 'utf8', prefix: '@', postfix: '', contentProvider:null }
+
+
+
+


Use new hash event syntax for view subviews.

--- src/view/view.coffee
+++ src/view/view.coffee
@@ -28,16 +28,12 @@
     @subviews = new Batman.Hash
     @_yieldNodes = {}

-    @subviews.on 'itemsWereAdded', (subviewNames...) =>
-      for name in subviewNames
-        subview = @subviews.get(name)
-        @_addSubview(subview, name)
+    @subviews.on 'itemsWereAdded', (subviewNames, newSubviews) =>
+      @_addSubview(subview, subviewNames[i]) for subview, i in newSubviews
       return

-    @subviews.on 'itemsWereRemoved', (subviewNames...) =>
-      for name in subviewNames
-        subview = @subviews.get(name)
-        subview._removeFromSuperview()
+    @subviews.on 'itemsWereRemoved', (subviewNames, oldSubviews) =>
+      subview._removeFromSuperview() for subview in oldSubviews
       return

     @subviews.on 'itemsWereChanged', (subviewNames, newSubviews, oldSubviews) =>
@@ -75,18 +71,20 @@
   @accessor 'node',
     get: ->
       return @node if @node
+
       node = document.createElement('div')
       node.innerHTML = @get('html')
       @set('node', node)
+
     set: (key, node) ->
       @node = node
-
       Batman.developer.do =>
         (if node == document then document.body else node).setAttribute('data-batman-view', @constructor.name)

       @initializeYields()
       @initializeBindings()
-      node
+
+      return node

   initializeYields: ->
     yieldNodes = Batman.DOM.querySelectorAll(@node, '[data-yield]')


Fix battle-result graph

Fix battle-result graph

--- server/app/assets/javascript/graph.coffee
+++ server/app/assets/javascript/graph.coffee
@@ -30,15 +30,15 @@

   mainPlot: (raw, active, min, max = moment().valueOf()) ->
     min_ = min ?= 0
-    rangeData = raw[0].data.filter (x) -> min_ < x[0] and x[0] < max
-    rangeExps = rangeData.map (x) -> x[1]
-    option.yaxis = {min: _.min(rangeExps)}
-    first = _.min(rangeData.map (x) -> x[0])
+    data = ((r.data.filter (x) -> min_ < x[0] and x[0] < max) for r in raw)
+#    rangeExps = rangeData.map (x) -> x[1]
+#    option.yaxis = {min: _.min(rangeExps)}
     if min?
+      first = _.min(data[0].map (x) -> x[0])
       min = Math.max(min, first)
       newOpt = $.extend true, {}, option,
         xaxis: { min: min, max: max }
-      @plot = $.plot(@chart, raw, newOpt)
+      @plot = $.plot(@chart, data, newOpt)

       # Delete plot if no data
       y = @plot.getYAxes()[0]
@@ -46,7 +46,7 @@
       if yDiff < 1
         $(@chart).replaceWith('')
     else
-      @plot = $.plot(@chart, raw, option)
+      @plot = $.plot(@chart, data, option)

   overviewPlot: (raw) ->
     $.plot(@overview, raw, optionO)


changing wifi threshold

changing wifi threshold

--- server/app/controllers/measurement.coffee
+++ server/app/controllers/measurement.coffee
@@ -44,8 +44,8 @@
       res.status(500)
       return
     res.json(data.map (item) ->
-      if(parseFloat(item.wifiAPs) < 8.0)
-        return [item.location.coordinates[0], item.location.coordinates[1], parseFloat(item.wifiAPs)/8.0]
+      if(parseFloat(item.wifiAPs) < 15.0)
+        return [item.location.coordinates[0], item.location.coordinates[1], parseFloat(item.wifiAPs)/15.0]
       else
         return [item.location.coordinates[0], item.location.coordinates[1], 1.0]
     )


roll dependencies for next and finish into one place

--- tutorial.coffee
+++ tutorial.coffee
@@ -69,13 +69,7 @@

   nextEnabled: ->
     @stepDep.depend()
-    actionDep = @actionDeps[@step]
-    beforeEnd = @step < (@steps.length - 1)
-    if actionDep
-      actionDep.depend()
-      return beforeEnd and actionDep.completed
-    else
-      return beforeEnd
+    return @step < (@steps.length - 1) and @stepCompleted()

   stepCompleted: ->
     @stepDep.depend()
@@ -84,11 +78,11 @@
       actionDep.depend()
       return actionDep.completed
     else
-      return
+      return true

   finishEnabled: ->
     @stepDep.depend()
-    return @step is @steps.length - 1
+    return @step is @steps.length - 1 and @stepCompleted()

   currentTemplate: ->
     @stepDep.depend()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -61,7 +61,7 @@
   ###
   each: (selector, fn) ->
     self.find(selector).each (obj, index) ->
-      fn(obj, index
+      fn(obj, index)

   find: (selector) ->
     results = []


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -21,7 +21,8 @@
   @param {Point} position
   ###
   closest: (selector, position) ->
-    self.find(selector).sort ()
+    self.find(selector).sort (a, b) ->
+

   ###*
   Get a selection of GameObjects that match the specified selector criteria. The selector language


P2 DONE: operator-activate-insert-mode-spec #270

--- spec/operator-activate-insert-mode-spec.coffee
+++ spec/operator-activate-insert-mode-spec.coffee
@@ -1,8 +1,9 @@
+# SPEC_MIGRATION: P2 DONE #270
 {getVimState, dispatch} = require './spec-helper'
 settings = require '../lib/settings'
 {inspect} = require 'util'

-describe "Operator ActivateInsertMode family", ->
+ffdescribe "Operator ActivateInsertMode family", ->
   [set, ensure, keystroke, editor, editorElement, vimState] = []

   beforeEach ->
@@ -27,7 +28,7 @@

     it "is repeatable", ->
       set cursor: [0, 0]
-      keystroke '3s'
+      keystroke '3 s'
       editor.insertText 'ab'
       ensure 'escape', text: 'ab345'
       set cursor: [0, 2]
@@ -35,14 +36,14 @@

     it "is undoable", ->
       set cursor: [0, 0]
-      keystroke '3s'
+      keystroke '3 s'
       editor.insertText 'ab'
       ensure 'escape', text: 'ab345'
       ensure 'u', text: '012345', selectedText: ''

     describe "in visual mode", ->
       beforeEach ->
-        keystroke 'vls'
+        keystroke 'v l s'

       it "deletes the selected characters and enters insert mode", ->
         ensure
@@ -92,7 +93,7 @@
       # Should be here, but I commented out before I have confidence.
       # ensure 'kS', text: '\n'
       # Folowing line include Bug ibelieve.
-      ensure 'kS', text: '\n12345'
+      ensure 'k S', text: '\n12345'
     # Can't be tested without setting grammar of test buffer
     xit "respects indentation", ->

@@ -116,20 +117,20 @@

         it "deletes the current line and enters insert mode", ->
           set cursor: [1, 1]
-          ensure 'cc',
+          ensure 'c c',
             text: "12345\n  \nABCDE\n"
             cursor: [1, 2]
             mode: 'insert'

         it "is repeatable", ->
-          keystroke 'cc'
+          keystroke 'c c'
           editor.insertText("abc")
           ensure 'escape', text: "12345\n  abc\nABCDE\n"
           set cursor: [2, 3]
           ensure '.', text: "12345\n  abc\n  abc\n"

         it "is undoable", ->
-          keystroke 'cc'
+          keystroke 'c c'
           editor.insertText("abc")
           ensure 'escape', text: "12345\n  abc\nABCDE\n"
           ensure 'u', text: "12345\n  abcde\nABCDE\n", selectedText: ''
@@ -137,7 +138,7 @@
       describe "when the cursor is on the last line", ->
         it "deletes the line's content and enters insert mode on the last line", ->
           set cursor: [2, 1]
-          ensure 'cc',
+          ensure 'c c',
             text: "12345\nabcde\n"
             cursor: [2, 0]
             mode: 'insert'
@@ -145,7 +146,7 @@
       describe "when the cursor is on the only line", ->
         it "deletes the line's content and enters insert mode", ->
           set text: "12345", cursor: [0, 2]
-          ensure 'cc',
+          ensure 'c c',
             text: ""
             cursor: [0, 0]
             mode: 'insert'
@@ -153,7 +154,7 @@
     describe "when followed by i w", ->
       it "undo's and redo's completely", ->
         set cursor: [1, 1]
-        ensure 'ciw',
+        ensure 'c i w',
           text: "12345\n\nABCDE"
           cursor: [1, 0]
           mode: 'insert'
@@ -164,16 +165,16 @@
           text: "12345\nfg\nABCDE"
           mode: 'normal'
         ensure 'u', text: "12345\nabcde\nABCDE"
-        ensure [ctrl: 'r'], text: "12345\nfg\nABCDE"
+        ensure 'ctrl-r', text: "12345\nfg\nABCDE"

       it "repeatable", ->
         set cursor: [1, 1]
-        ensure 'ciw',
+        ensure 'c i w',
           text: "12345\n\nABCDE"
           cursor: [1, 0]
           mode: 'insert'

-        ensure ['escape', 'j.'],
+        ensure 'escape j .',
           text: "12345\n\n"
           cursor: [2, 0]
           mode: 'normal'
@@ -181,7 +182,7 @@
     describe "when followed by a w", ->
       it "changes the word", ->
         set text: "word1 word2 word3", cursorBuffer: [0, 7]
-        ensure ['cw', 'escape'], text: "word1 w word3"
+        ensure 'c w escape', text: "word1 w word3"

     describe "when followed by a G", ->
       beforeEach ->
@@ -191,12 +192,12 @@
       describe "on the beginning of the second line", ->
         it "deletes the bottom two lines", ->
           set cursor: [1, 0]
-          ensure ['cG', 'escape'], text: '12345\n\n'
+          ensure 'c G escape', text: '12345\n\n'

       describe "on the middle of the second line", ->
         it "deletes the bottom two lines", ->
           set cursor: [1, 2]
-          ensure ['cG', 'escape'], text: '12345\n\n'
+          ensure 'c G escape', text: '12345\n\n'

     describe "when followed by a goto line G", ->
       beforeEach ->
@@ -205,12 +206,12 @@
       describe "on the beginning of the second line", ->
         it "deletes all the text on the line", ->
           set cursor: [1, 0]
-          ensure ['c2G', 'escape'], text: '12345\n\nABCDE'
+          ensure 'c 2 G escape', text: '12345\n\nABCDE'

       describe "on the middle of the second line", ->
         it "deletes all the text on the line", ->
           set cursor: [1, 2]
-          ensure ['c2G', 'escape'], text: '12345\n\nABCDE'
+          ensure 'c 2 G escape', text: '12345\n\nABCDE'

   describe "the C keybinding", ->
     beforeEach ->
@@ -379,9 +380,9 @@
       beforeEach ->
         set cursor: [16, 0]
       it "insert at previous fold start row", ->
-        ensure 'g[', cursor: [9, 2], mode: 'insert'
+        ensure 'g [', cursor: [9, 2], mode: 'insert'
       it "insert at next fold start row", ->
-        ensure 'g]', cursor: [18, 4], mode: 'insert'
+        ensure 'g ]', cursor: [18, 4], mode: 'insert'

     describe "when cursor is at fold start row", ->
       # Nothing special when cursor is at fold start row,
@@ -389,9 +390,9 @@
       beforeEach ->
         set cursor: [20, 6]
       it "insert at previous fold start row", ->
-        ensure 'g[', cursor: [18, 4], mode: 'insert'
+        ensure 'g [', cursor: [18, 4], mode: 'insert'
       it "insert at next fold start row", ->
-        ensure 'g]', cursor: [22, 6], mode: 'insert'
+        ensure 'g ]', cursor: [22, 6], mode: 'insert'

   describe "the i keybinding", ->
     beforeEach ->
@@ -505,21 +506,21 @@
       set text: "", cursor: [0, 0]
       keystroke 'i'
       editor.insertText(initialText)
-      ensure ["escape", 'gg'], text: initialText, cursor: [0, 0]
+      ensure "escape g g", text: initialText, cursor: [0, 0]

     describe "repeat insertion count times", ->
-      it "[case-i]", -> ensureInsertionCount '3i', insert: '=', text: "===*\n*\n", cursor: [0, 2]
-      it "[case-o]", -> ensureInsertionCount '3o', insert: '=', text: "*\n=\n=\n=\n*\n", cursor: [3, 0]
-      it "[case-O]", -> ensureInsertionCount '3O', insert: '=', text: "=\n=\n=\n*\n*\n", cursor: [2, 0]
+      it "[case-i]", -> ensureInsertionCount '3 i', insert: '=', text: "===*\n*\n", cursor: [0, 2]
+      it "[case-o]", -> ensureInsertionCount '3 o', insert: '=', text: "*\n=\n=\n=\n*\n", cursor: [3, 0]
+      it "[case-O]", -> ensureInsertionCount '3 O', insert: '=', text: "=\n=\n=\n*\n*\n", cursor: [2, 0]

       describe "children of Change operation won't repeate insertion count times", ->
         beforeEach ->
           set text: "", cursor: [0, 0]
           keystroke 'i'
           editor.insertText('*')
-          ensure ["escape", 'gg'], text: '*', cursor: [0, 0]
-
-        it "[case-c]", -> ensureInsertionCount '3cw', insert: '=', text: "=", cursor: [0, 0]
-        it "[case-C]", -> ensureInsertionCount '3C', insert: '=', text: "=", cursor: [0, 0]
-        it "[case-s]", -> ensureInsertionCount '3s', insert: '=', text: "=", cursor: [0, 0]
-        it "[case-S]", -> ensureInsertionCount '3S', insert: '=', text: "=", cursor: [0, 0]
+          ensure 'escape g g', text: '*', cursor: [0, 0]
+
+        it "[case-c]", -> ensureInsertionCount '3 c w', insert: '=', text: "=", cursor: [0, 0]
+        it "[case-C]", -> ensureInsertionCount '3 C', insert: '=', text: "=", cursor: [0, 0]
+        it "[case-s]", -> ensureInsertionCount '3 s', insert: '=', text: "=", cursor: [0, 0]
+        it "[case-S]", -> ensureInsertionCount '3 S', insert: '=', text: "=", cursor: [0, 0]


Check that the states are not terminal for minimax benchmark

--- test/perf.coffee
+++ test/perf.coffee
@@ -11,7 +11,7 @@

 run = (s, f) ->
   new Benchmark.Suite()
-    .add s, -> f()
+    .add s, f
     .on 'cycle', (e) -> it e.target, ->
     .run async: false

@@ -57,7 +57,9 @@

     state = new ConnectFour
     state = playTurn state for i in [0...10]
+    state.isTerminal().should.be.false
     run 'minimax depth 2 play turn after 10 turns', -> playTurn state

     state = playTurn state for i in [0...10]
+    state.isTerminal().should.be.false
     run 'minimax depth 2 play turn after 20 turns', -> playTurn state


initialize textarea popover immediately on render


Temporary disabled all code changing the focus.

--- client/publication.coffee
+++ client/publication.coffee
@@ -6,7 +6,7 @@
 publicationCacheHandle = null

 # If set to an annotation id, focus on next render
-focusAnnotationId = null
+#focusAnnotationId = null

 # We use our own reactive variable for publicationDOMReady and not Session to
 # make sure it is not preserved when site autoreloads (because of a code change).
@@ -85,7 +85,7 @@
     @_pages = []
     @_highlighter = new Highlighter @_$displayWrapper, true

-    focusAnnotationId = null
+    #focusAnnotationId = null

     PDFJS.getDocument(@url(), null, null, @_progressCallback).then (@_pdf) =>
       # Maybe this instance has been destroyed in meantime
@@ -259,7 +259,7 @@
     Notify.debug "Destroying publication #{ @_id }"

     currentPublication = null
-    focusAnnotationId = null
+    #focusAnnotationId = null

     pages = @_pages or []
     @_pages = null # To remove references to pdf.js elements to allow cleanup, and as soon as possible as this disables other callbacks
@@ -334,7 +334,7 @@
       Notify.error "Error rendering page #{ page.pdfPage.pageNumber }", args

   showTEI: =>
-    focusAnnotationId = null
+    #focusAnnotationId = null

     # To make sure we are starting with empty slate
     @_$displayWrapper.empty()
@@ -977,6 +977,7 @@
 Template.publicationAnnotations.destroyed = ->
   $(document).off '.publicationAnnotations'

+###
 focusAnnotation = (body) ->
   return unless body

@@ -995,6 +996,7 @@
 focusEditor = ($editor) ->
   currentPublication?._highlighter?._annotator?._deselectAllHighlights()
   $editor.focus()
+###

 Template.publicationAnnotationsItem.events
   'click .edit-button': (e, template) ->
@@ -1074,10 +1076,12 @@
     $('.viewer .display-wrapper .highlights-layer .highlights-layer-highlight').trigger 'annotationMouseleave', [@data._id]
     return # Make sure CoffeeScript does not return anything

+  ###
   if focusAnnotationId is @data._id
     focusAnnotationId = null

-    focusAnnotation $(@findAll '.body[contenteditable=true]').get(0)
+    focusAnnotation $(@findAll '.annotation-content-editor').get(0)
+  ###

 Template.publicationAnnotationsItem.canModify = ->
   @hasMaintainerAccess Meteor.person()
@@ -1153,7 +1157,7 @@

         LocalAnnotation.documents.remove @_id

-        focusAnnotationId = annotationId
+        #focusAnnotationId = annotationId

         Meteor.Router.toNew Meteor.Router.annotationPath Session.get('currentPublicationId'), Session.get('currentPublicationSlug'), annotationId
     else
@@ -1166,7 +1170,7 @@
           $unset:
             editing: ''

-        focusAnnotationId = @_id
+        #focusAnnotationId = @_id

         Meteor.Router.toNew Meteor.Router.annotationPath Session.get('currentPublicationId'), Session.get('currentPublicationSlug'), @_id

@@ -1179,7 +1183,7 @@
       $set:
         editing: true

-    focusEditor $(e.currentTarget)
+    #focusEditor $(e.currentTarget)

     return # Make sure CoffeeScript does not return anything


More tweak

More tweak

--- modules/admin/index.coffee
+++ modules/admin/index.coffee
@@ -33,9 +33,13 @@

   restartFunc: (msg)=>
     @bot.sendMessage msg.channel, 'FocaBot is restarting...'
-    setTimeout process.exit, 2000 # Let's hope PM2 restarts it :)
+    .then ()-> setTimeout process.exit, 2000 # Let's hope PM2 restarts it :)

   updateFunc: (msg,args,bot)=>
+      @pullFunc msg,args,bot
+      .then ()=> @restartFunc msg,args,bot
+
+  pullFunc: (msg,args,bot)=> new Promise (resolve, reject)=>
     childProcess.exec 'git pull origin master', (error, stdout, stderr)->
       bot.sendMessage msg.channel, """
                        ```diff
@@ -43,9 +47,8 @@

                        """+stdout+"""
                        ```
-                       FocaBot is restarting...
                        """
-      setTimeout process.exit, 2000
+      .then resolve

   cleanFunc: (msg,args,bot)=>
     hasError = false


debug

debug

--- JSKit/04_JSMenuView.coffee
+++ JSKit/04_JSMenuView.coffee
@@ -49,6 +49,8 @@
                 item = ui.item.context.textContent
                 @selectMenuItem(item)
                 @closeMenu()
+            classes:
+                "ui-menu": "highlight"

     selectMenuItem:(item)->
         if (@_action?)


TeamsModal: remove destroy on overlay click so we won't loose any change mistakenly

--- client/admin/lib/index.coffee
+++ client/admin/lib/index.coffee
@@ -58,14 +58,15 @@

   constructor: (options = {}, data) ->

-    data       or= kd.singletons.groupsController.getCurrentGroup()
-    options.view = new AdminAppView
-      title      : 'Team Settings'
-      cssClass   : 'AppModal AppModal--admin'
-      width      : 1000
-      height     : '90%'
-      overlay    : yes
-      tabData    : NAV_ITEMS
+    data         or= kd.singletons.groupsController.getCurrentGroup()
+    options.view   = new AdminAppView
+      title        : 'Team Settings'
+      cssClass     : 'AppModal AppModal--admin'
+      width        : 1000
+      height       : '90%'
+      overlay      : yes
+      overlayClick : no
+      tabData      : NAV_ITEMS
     , data

     super options, data


remove console.log

remove console.log

--- vue/src/shared/models/poll_model.coffee
+++ vue/src/shared/models/poll_model.coffee
@@ -205,7 +205,6 @@

   adminsInclude: (user) ->
     stance = @stanceFor(user)
-    console.log '@authorId == user.id', @authorId == user.id
     (@authorId == user.id && (!@groupId || @group().membersInclude(user))) ||
     (stance && stance.admin) ||
     (@discussionId && @discussion().adminsInclude(user)) ||


remove deploy command; revisit later when we support [li]node pools disk reimaging

--- code/li.coffee
+++ code/li.coffee
@@ -108,18 +108,6 @@
       username = 'root'
     Linode.get instancename, (instance) ->
       instance.ssh username
-
-# Deploys an instance by running all its hooks
-# args[2] is the instance
-command.deploy =
-  help: "deploy <instance-name>      Run deployment hooks on instance [OBSOLESCENT]"
-  run: (args) ->
-    Linode.get args[2], (instance) ->
-      callback = (code) ->
-        if code is null then code = 0
-        console.log "Exit code: #{code}"
-        process.exit code
-      instance.run_hooks callback

 # Run all, or a single hook(s) on a specified instance by config
 # args[2] is the instance


stop li.coffee from printing to stderr on require

--- code/li.coffee
+++ code/li.coffee
@@ -1,4 +1,6 @@
 #!/usr/bin/env coffee
+
+_s = require 'underscore.string'

 Linode = (require 'linode').Linode

@@ -45,6 +47,7 @@
     when 'destroy' then destroy(args)
     when 'list' then list(args)
     when undefined then help(args)
-    else process.stderr.write("Try li help for help\n")
+    else process.stderr.write("Try li help for help, not #{args}\n")

-exports.main()
+if _s.endsWith process.argv[1], 'li.coffee'
+  exports.main()


Remove unused toggle-column-list directive

--- app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -77,24 +77,6 @@
         else
           scope.column.visible = true
           element.removeClass "unselected"
-
-
-productsApp.directive "ofnToggleColumnList", [
-  "$compile"
-  ($compile) ->
-    return link: (scope, element, attrs) ->
-      dialogDiv = element.next()
-      element.on "click", ->
-        pos = element.position()
-        height = element.outerHeight()
-        dialogDiv.css(
-          position: "absolute"
-          top: (pos.top + height) + "px"
-          left: pos.left + "px"
-        ).toggle()
-
-]
-

 productsApp.directive "datetimepicker", [
   "$parse"