test/mock: added test for findById with not found id

this officially makes the mock version of findById more thoroughly tested than
the actual one (at least in terms of semantics)

--- test/mock.coffee
+++ test/mock.coffee
@@ -38,9 +38,16 @@
             assert.deepEqual data, {foo: 'baz'}
             done()

-  _.each ['51a5a56f4867bbdf51054055', '51a5a56f4867bbdf51054054'], (id) ->
-    it "supports findById(#{id})", (done) ->
-      @clever.Student.findById id, (err, student) ->
+  describe 'findById', ->
+    _.each ['51a5a56f4867bbdf51054055', '51a5a56f4867bbdf51054054'], (id) ->
+      it "finds a student", (done) ->
+        @clever.Student.findById id, (err, student) ->
+          assert.ifError err
+          assert.equal student.get('id'), id
+          done()
+
+    it "returns undefined if the id is not found", (done) ->
+      @clever.Student.findById 'not an existing id', (err, student) ->
         assert.ifError err
-        assert.equal student.get('id'), id
+        assert not student, 'Expected student to be undefined'
         done()


improve patch

improve patch

--- server/lib/init.coffee
+++ server/lib/init.coffee
@@ -67,8 +67,9 @@
             return cb(err) if err? or apps.length is 0
             async.forEachSeries apps, (app, cb) ->
                 # Check if access exists
-                db.view 'access/byApp', key:app.id, (err, accesses) ->
-                    return cb(err) if err? or accesses.length is 0
+                app = app.value
+                db.view 'access/byApp', key:app._id, (err, accesses) ->
+                    return cb(err) if err? or accesses.length > 0
                     if accesses?.length is 0
                         # Create it if necessary
                         if docType is "device"


Added failing test for the BreezeJS select keyword

--- test/test.coffee
+++ test/test.coffee
@@ -12,72 +12,75 @@
 buildUri = (query) -> uriBuilder.buildUri query, manager.metadataStore

 before ->
-	breeze.config.initializeAdapterInstances uriBuilder: 'json-api'
-	ds = new (breeze.DataService)(
-		serviceName: 'http://localhost:9000'
-		hasServerMetadata: false)
-	manager = new (breeze.EntityManager)(dataService: ds)
-	uriBuilder = breeze.config.getAdapterInstance('uriBuilder', 'json-api')
+  breeze.config.initializeAdapterInstances uriBuilder: 'json-api'
+  ds = new (breeze.DataService)(
+    serviceName: 'http://localhost:9000'
+    hasServerMetadata: false)
+  manager = new (breeze.EntityManager)(dataService: ds)
+  uriBuilder = breeze.config.getAdapterInstance('uriBuilder', 'json-api')

 beforeEach -> query = (new (breeze.EntityQuery)).from('people')

 describe "000. Init", ->
-	it "should not be null", -> expect(breeze).to.not.be.null
+  it "should not be null", -> expect(breeze).to.not.be.null

 describe "005. Basic query building", ->

-	describe 'breeze.EntityQuery().from("people")', ->
+  describe 'breeze.EntityQuery().from("people")', ->

-		describe 'where', ->
-			it 'unset returns "people"', -> expect(buildUri(query)).to.equal('people')
-			it '"name == Scott" returns "people?filter[name]=Scott"', -> expect(decodeURIComponent(buildUri(query.where("name", "==", "Scott")))).to.equal('people?filter[name]=Scott')
-			it '"id == 55555" returns "people/55555"', -> expect(decodeURIComponent(buildUri(query.where("id", "==", "55555")))).to.equal('people/55555')
-			it '"name in [Raynor,LiLi]" returns people?filter[name]=Raynor,LiLi', -> expect(decodeURIComponent(buildUri(query.where('name','in', ['Raynor','LiLi'])))).to.equal('people?filter[name]=Raynor,LiLi')
+    describe 'where', ->
+      it 'unset returns "people"', -> expect(buildUri(query)).to.equal('people')
+      it '"name == Scott" returns "people?filter[name]=Scott"', -> expect(decodeURIComponent(buildUri(query.where("name", "==", "Scott")))).to.equal('people?filter[name]=Scott')
+      it '"id == 55555" returns "people/55555"', -> expect(decodeURIComponent(buildUri(query.where("id", "==", "55555")))).to.equal('people/55555')
+      it '"name in [Raynor,LiLi]" returns people?filter[name]=Raynor,LiLi', -> expect(decodeURIComponent(buildUri(query.where('name','in', ['Raynor','LiLi'])))).to.equal('people?filter[name]=Raynor,LiLi')

-		describe 'unsupported operators', ->
-			it '"name startsWith A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','startsWith', 'A'))).to.throw('startswith is currently not supported by JSON-API')
-			it '"name endsWith A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','endsWith', 'A'))).to.throw('endswith is currently not supported by JSON-API')
-			it '"name contains A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','contains', 'A'))).to.throw('contains is currently not supported by JSON-API')
-			it '"name ne Scott" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','ne', 'Scott'))).to.throw('ne is currently not supported by JSON-API')
-			it '"age gt 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','gt', 20))).to.throw('gt is currently not supported by JSON-API')
-			it '"age ge 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','ge', 20))).to.throw('ge is currently not supported by JSON-API')
-			it '"age lt 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','lt', 20))).to.throw('lt is currently not supported by JSON-API')
-			it '"age le 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','le', 20))).to.throw('le is currently not supported by JSON-API')
+    describe 'unsupported operators', ->
+      it '"name startsWith A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','startsWith', 'A'))).to.throw('startswith is currently not supported by JSON-API')
+      it '"name endsWith A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','endsWith', 'A'))).to.throw('endswith is currently not supported by JSON-API')
+      it '"name contains A" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','contains', 'A'))).to.throw('contains is currently not supported by JSON-API')
+      it '"name ne Scott" to throw unsupported operator exception', -> expect( -> buildUri(query.where('name','ne', 'Scott'))).to.throw('ne is currently not supported by JSON-API')
+      it '"age gt 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','gt', 20))).to.throw('gt is currently not supported by JSON-API')
+      it '"age ge 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','ge', 20))).to.throw('ge is currently not supported by JSON-API')
+      it '"age lt 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','lt', 20))).to.throw('lt is currently not supported by JSON-API')
+      it '"age le 20" to throw unsupported operator exception', -> expect( -> buildUri(query.where('age','le', 20))).to.throw('le is currently not supported by JSON-API')

 describe "010. Advanced query building", ->

-		describe 'Predicates', ->
+    describe 'Predicates', ->

-				andPredicate = breeze.Predicate.create('firstName', '==', 'Scott').and(   'lastName', '==', 'Raynor');
-				it "'firstName', '==', 'Scott' AND 'lastName', '==', 'Raynor' returns people?filter[firstName]=Scott&filter[lastName]=Raynor", -> expect(decodeURIComponent(buildUri(query.where(andPredicate)))).to.equal('people?filter[firstName]=Scott&filter[lastName]=Raynor');
+        andPredicate = breeze.Predicate.create('firstName', '==', 'Scott').and(   'lastName', '==', 'Raynor');
+        it "'firstName', '==', 'Scott' AND 'lastName', '==', 'Raynor' returns people?filter[firstName]=Scott&filter[lastName]=Raynor", -> expect(decodeURIComponent(buildUri(query.where(andPredicate)))).to.equal('people?filter[firstName]=Scott&filter[lastName]=Raynor');

-				orPredicate = breeze.Predicate.create('firstName', '==', 'Scott').or(   'lastName', '==', 'Raynor');
-				it "'firstName', '==', 'Scott' OR 'lastName', '==', 'Raynor' to throw unsupported operator exception", -> expect( -> buildUri(query.where(orPredicate))).to.throw('or is currently not supported by JSON-API')
+        orPredicate = breeze.Predicate.create('firstName', '==', 'Scott').or(   'lastName', '==', 'Raynor');
+        it "'firstName', '==', 'Scott' OR 'lastName', '==', 'Raynor' to throw unsupported operator exception", -> expect( -> buildUri(query.where(orPredicate))).to.throw('or is currently not supported by JSON-API')

-				andOrPredicate = breeze.Predicate.create('firstName', '==', 'Scott').and(   'lastName', '==', 'Raynor').or('age', '==', '25');
-				it "'firstName', '==', 'Scott' AND 'lastName', '==', 'Raynor' or 'age' == '25' to throw unsupported operator exception", -> expect( -> buildUri(query.where(orPredicate))).to.throw('or is currently not supported by JSON-API')
+        andOrPredicate = breeze.Predicate.create('firstName', '==', 'Scott').and(   'lastName', '==', 'Raynor').or('age', '==', '25');
+        it "'firstName', '==', 'Scott' AND 'lastName', '==', 'Raynor' or 'age' == '25' to throw unsupported operator exception", -> expect( -> buildUri(query.where(orPredicate))).to.throw('or is currently not supported by JSON-API')

 describe '015. OrderBy', ->

-		describe 'orderBy single', ->
-				it '"firstName" returns "people?sort=firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName')))).to.equal('people?sort=firstName')
-				it '"firstName desc" returns "people?sort=-firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc')))).to.equal('people?sort=-firstName')
-				it '"firstName desc", false" returns "people?sort=firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc', false)))).to.equal('people?sort=firstName')
-				it '"firstName, true" returns "people?sort=-firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName', true)))).to.equal('people?sort=-firstName')
+    describe 'orderBy single', ->
+        it '"firstName" returns "people?sort=firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName')))).to.equal('people?sort=firstName')
+        it '"firstName desc" returns "people?sort=-firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc')))).to.equal('people?sort=-firstName')
+        it '"firstName desc", false" returns "people?sort=firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc', false)))).to.equal('people?sort=firstName')
+        it '"firstName, true" returns "people?sort=-firstName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName', true)))).to.equal('people?sort=-firstName')

-		describe 'orderBy multiple', ->
-				it '"firstName, lastName" returns "people?sort=firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName')))).to.equal('people?sort=firstName,lastName')
-				it '"firstName desc, lastName" returns "people?sort=-firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName')))).to.equal('people?sort=-firstName,lastName')
-				it '"firstName, lastName desc" returns "people?sort=firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName desc')))).to.equal('people?sort=firstName,-lastName')
-				it '"firstName desc, lastName desc" returns "people?sort=-firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName desc')))).to.equal('people?sort=-firstName,-lastName')
-				it '"firstName desc, lastName desc", false returns "people?sort=firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName desc', false)))).to.equal('people?sort=firstName,lastName')
-				it '"firstName, lastName", true returns "people?sort=-firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName', true)))).to.equal('people?sort=-firstName,-lastName')
+    describe 'orderBy multiple', ->
+        it '"firstName, lastName" returns "people?sort=firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName')))).to.equal('people?sort=firstName,lastName')
+        it '"firstName desc, lastName" returns "people?sort=-firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName')))).to.equal('people?sort=-firstName,lastName')
+        it '"firstName, lastName desc" returns "people?sort=firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName desc')))).to.equal('people?sort=firstName,-lastName')
+        it '"firstName desc, lastName desc" returns "people?sort=-firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName desc')))).to.equal('people?sort=-firstName,-lastName')
+        it '"firstName desc, lastName desc", false returns "people?sort=firstName,lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName desc, lastName desc', false)))).to.equal('people?sort=firstName,lastName')
+        it '"firstName, lastName", true returns "people?sort=-firstName,-lastName"', -> expect(decodeURIComponent(buildUri(query.orderBy('firstName, lastName', true)))).to.equal('people?sort=-firstName,-lastName')
+
+describe '020. Select', ->
+    it 'should return people?include=firstName when query.select("firstName")', -> expect(decodeURIComponent(buildUri(query.select('firstName')))).to.equal('people?include=firstName')
+    it 'should return people?include=firstName,lastName when query.select("firstName, lastName")', -> expect(decodeURIComponent(buildUri(query.select('firstName')))).to.equal('people?include=firstName,lastName')

 describe '030. Expand', ->
-		it 'expand siblings', -> expect(decodeURIComponent(buildUri(query.expand('siblings')))).to.equal('people?include=siblings')
-		it 'expand siblings, cousins', -> expect(decodeURIComponent(buildUri(query.expand('siblings,cousins')))).to.equal('people?include=siblings,cousins')
-		it 'expand siblings.pets', -> expect(decodeURIComponent(buildUri(query.expand('siblings.pets')))).to.equal('people?include=siblings.pets')
-		it 'expand siblings.pets, cousins', -> expect(decodeURIComponent(buildUri(query.expand('siblings.pets, cousins')))).to.equal('people?include=siblings.pets,cousins')
-
+    it 'expand siblings', -> expect(decodeURIComponent(buildUri(query.expand('siblings')))).to.equal('people?include=siblings')
+    it 'expand siblings, cousins', -> expect(decodeURIComponent(buildUri(query.expand('siblings,cousins')))).to.equal('people?include=siblings,cousins')
+    it 'expand siblings.pets', -> expect(decodeURIComponent(buildUri(query.expand('siblings.pets')))).to.equal('people?include=siblings.pets')
+    it 'expand siblings.pets, cousins', -> expect(decodeURIComponent(buildUri(query.expand('siblings.pets, cousins')))).to.equal('people?include=siblings.pets,cousins')


Specs for aSlide and aControl settings

Specs for aSlide and aControl settings

--- test/test.coffee
+++ test/test.coffee
@@ -118,6 +118,14 @@
       @$carousel.circular()

     describe 'handles settings:', ->
+      it 'the slides selector defaults to ".slides .slide"', ->
+        @$carousel.circular()
+        expect(@$carousel.circular('settings').aSlide).to.equal '.slides .slide'
+
+      it 'the controls selector defaults to ".controls .control"', ->
+        @$carousel.circular()
+        expect(@$carousel.circular('settings').aControl).to.equal '.controls .control'
+
       it 'autoStart is true by default', ->
         _resume = sinon.spy $.fn.circular.private, 'resume'
         @$carousel.circular()


purpose of offshore team

purpose of offshore team

--- scripts/offshore.coffee
+++ scripts/offshore.coffee
@@ -94,3 +94,6 @@

   robot.respond /when Prem is gone and we are lost in darkness/i, (msg)->
     msg.send "The offshore team shall know no fear! The offshore team will be triumphant! :ahsan: :sachin: :prem: :kostas: :chris: :drmatt:"
+
+  robot.respond /what is the purpose of offshore team/i, (msg)->
+    msg.send "To crush our bugs, see them driven before us, and to hear the lamentation of their women"


Update examples for the 2.0.0 release

Update examples for the 2.0.0 release

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -57,22 +57,22 @@

Now with your newly baked Icon component, you can easily add icons to your site.

         {"""
-        <Icon icon="drafts" size=72 style={{fill: 'green'}} />
-        <Icon icon="account-child" size=72 />
-        <Icon icon="accessibility" size=72 />
-        <Icon icon="call" size=72 />
-        <Icon icon="content-copy" size=72 style={{fill: 'blue'}} />
-        <Icon icon="schedule" size=72 style={{stroke:'red'}} />
+        <Icon name="drafts" size=72 style={{fill: 'green'}} />
+        <Icon name="account-child" size=72 />
+        <Icon name="accessibility" size=72 />
+        <Icon name="call" size=72 />
+        <Icon name="content-copy" size=72 style={{fill: 'blue'}} />
+        <Icon name="schedule" size=72 style={{stroke:'red'}} />
         """}

Which outputs

-        <Icon icon="drafts" size=72 style={{fill: 'green'}} />
-        <Icon icon="account-child" size=72 />
-        <Icon icon="accessibility" size=72 />
-        <Icon icon="call" size=72 />
-        <Icon icon="content-copy" size=72 style={{fill: 'blue'}} />
-        <Icon icon="schedule" size=72 style={{stroke:'red'}} />
+        <Icon name="drafts" size=72 style={{fill: 'green'}} />
+        <Icon name="account-child" size=72 />
+        <Icon name="accessibility" size=72 />
+        <Icon name="call" size=72 />
+        <Icon name="content-copy" size=72 style={{fill: 'blue'}} />
+        <Icon name="schedule" size=72 style={{stroke:'red'}} />

       </Container>


FIX: Remove existing merge conflict code

--- src/app/tasks/task-comments-viewer/task-comments-viewer.coffee
+++ src/app/tasks/task-comments-viewer/task-comments-viewer.coffee
@@ -43,16 +43,8 @@
       false

     $scope.addComment = () ->
-<<<<<<< Updated upstream
-      TaskComment.create { project_id: $scope.project.project_id, task_definition_id: $scope.task.task_definition_id, comment: $scope.comment.text },
-        (response) ->
-          if ! $scope.task.comments
-            $scope.task.comments = []
-          $scope.task.comments.unshift response
-=======
       taskService.addComment task, $scope.comment.text,
         (success) ->
->>>>>>> Stashed changes
           $scope.comment.text = ""
           analyticsService.event "View Task Comments", "Added new comment"
         (response) ->


change conflicting key map

change conflicting key map

--- keymaps/md.cson
+++ keymaps/md.cson
@@ -12,5 +12,5 @@
   "cmd-k": "md-writer:insert-link"
   "cmd-i": "md-writer:toggle-italic-text"
   "cmd-b": "md-writer:toggle-bold-text"
-  "cmd-`": "md-writer:toggle-code-text"
+  "cmd-'": "md-writer:toggle-code-text"
   "cmd-h": "md-writer:toggle-strikethrough-text"


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -25,7 +25,7 @@
   # => 2.5 # half the player's width

   player.I.y
-  # => 320 - (17 / )
+  # => 320 - (17 / 2)


Update factories

Update factories

--- spec/javascripts/factories/events.js.coffee
+++ spec/javascripts/factories/events.js.coffee
@@ -2,3 +2,10 @@
   @sequence 'title', (n) -> "Event ##{n}"

   f.startDate = -> new Date()
+  f.image     =
+    image:
+      image:
+        small:
+          url: 'http://small-image.png'
+        large:
+          url: 'http://large-image.png'


Fix failing Jasmine tests. TODO: mock collaborators in this test.

--- spec/javascripts/save_on_change_spec.coffee
+++ spec/javascripts/save_on_change_spec.coffee
@@ -1,3 +1,5 @@
+#= require iframe-saver
+#= require save-indicator
 #= require save-on-change
 #= require mock-ajax


Fixing test we broke for save-on-change.js

--- spec/javascripts/save_on_change_spec.coffee
+++ spec/javascripts/save_on_change_spec.coffee
@@ -50,7 +50,7 @@
       runs ->
         enter_text_area($("#live-textarea"),text_to_type)
         expect($("#live-textarea").text()).toBe text_to_type
-      waits 700 #wait-interval-s
+      waits 2100 #wait-interval-s - NOTE dependence on magic number from main script
       runs ->
         request = mostRecentAjaxRequest();
         expect(request.url).toBe('/embeddable/open_response_answers/165');


Fixed kati

Fixed kati

--- src/coffee/index.coffee
+++ src/coffee/index.coffee
@@ -31,8 +31,7 @@
   runnerHand1 = new Monolith.Models.CardModel(cardId: '04109', faceUp: true) # Lucky Find
   runnerHand2 = new Monolith.Models.CardModel(cardId: '02047', faceUp: true) # Test Run
   runnerHand3 = new Monolith.Models.CardModel(cardId: '04047', faceUp: true) # Torch
-  runnerHand4 = new Monolith.Models.CardModel(cardId: '01034') # Diesel
-  runnerHand5 = new Monolith.Models.CardModel() # Diesel
+  runnerHand4 = new Monolith.Models.CardModel(cardId: '01034', faceUp: true) # Diesel

   runnerResourceTokens = new Monolith.Models.TokensCollection([new Monolith.Models.TokensModel(type: 'credit', amount: 3)])
   runnerResource = new Monolith.Models.CardModel(cardId: '02091', faceUp: true, tokens: runnerResourceTokens) # Kati Jones
@@ -59,12 +58,12 @@
   runnerPiles = new Monolith.Models.PileCollection([runnerTempPile, runnerIdPile, runnerDeck, lastNoCardsPile])
   corpPiles = new Monolith.Models.PileCollection([firstNoCardsPile, corpIdPile, corpDeck, corpTrash])

-  runnerHand = new Monolith.Models.CardCollection([runnerHand1, runnerHand2, runnerHand3, runnerHand4, runnerHand5])
+  runnerHand = new Monolith.Models.CardCollection([runnerHand1, runnerHand2, runnerHand3, runnerHand4])
   corpHand = new Monolith.Models.CardCollection([corpHand1, corpHand2, corpHand3, corpHand4, corpHand5])

   # Tokens

-  runnerCredits = new Monolith.Models.TokensModel(type: 'credit', amount: 5)
+  runnerCredits = new Monolith.Models.TokensModel(type: 'credit', amount: 3)
   corpCredits = new Monolith.Models.TokensModel(type: 'credit', amount: 15)
   corpBadPub = new Monolith.Models.TokensModel(type: 'bad-publicity', amount: 1)


Expose Assets methods via RocketChat.Assets

--- packages/rocketchat-assets/server/assets.coffee
+++ packages/rocketchat-assets/server/assets.coffee
@@ -10,11 +10,11 @@

 assets =
 	'logo':
-		label: 'logo (svg, png)'
+		label: 'logo (svg, png, jpg)'
 		defaultUrl: 'images/logo/logo.svg'
 		constraints:
 			type: 'image'
-			extensions: ['svg', 'png']
+			extensions: ['svg', 'png', 'jpg', 'jpeg']
 			width: undefined
 			height: undefined
 	'favicon':
@@ -73,6 +73,52 @@
 			extensions: ['png']
 			width: 256
 			height: 256
+
+
+RocketChat.Assets = new class
+	setAsset: (binaryContent, contentType, asset) ->
+		if not assets[asset]?
+			throw new Meteor.Error "Invalid_asset"
+
+		extension = mime.extension(contentType)
+		if extension not in assets[asset].constraints.extensions
+			throw new Meteor.Error "Invalid_file_type", contentType
+
+		file = new Buffer(binaryContent, 'binary')
+		if assets[asset].constraints.width? or assets[asset].constraints.height?
+			dimensions = sizeOf file
+
+			if assets[asset].constraints.width? and assets[asset].constraints.width isnt dimensions.width
+				throw new Meteor.Error "Invalid_file_width"
+
+			if assets[asset].constraints.height? and assets[asset].constraints.height isnt dimensions.height
+				throw new Meteor.Error "Invalid_file_height"
+
+		rs = RocketChatFile.bufferToStream file
+		RocketChatAssetsInstance.deleteFile asset
+		ws = RocketChatAssetsInstance.createWriteStream asset, contentType
+		ws.on 'end', Meteor.bindEnvironment ->
+			Meteor.setTimeout ->
+				RocketChat.settings.updateById "Assets_#{asset}", {
+					url: "/assets/#{asset}.#{extension}"
+					defaultUrl: assets[asset].defaultUrl
+				}
+			, 200
+
+		rs.pipe ws
+		return
+
+	unsetAsset: (asset) ->
+		if not assets[asset]?
+			throw new Meteor.Error "Invalid_asset"
+
+		RocketChatAssetsInstance.deleteFile asset
+
+		RocketChat.settings.updateById "Assets_#{asset}", {defaultUrl: assets[asset].defaultUrl}
+		return
+
+	refreshClients: ->
+		process.emit('message', {refresh: 'client'})

 RocketChat.settings.addGroup 'Assets'
@@ -175,9 +221,9 @@
 		unless hasPermission
 			throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

-		process.emit('message', {refresh: 'client'})
-
-Meteor.methods
+		RocketChat.Assets.refreshClients
+
+
 	unsetAsset: (asset) ->
 		unless Meteor.userId()
 			throw new Meteor.Error 'invalid-user', "[methods] unsetAsset -> Invalid user"
@@ -186,15 +232,9 @@
 		unless hasPermission
 			throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

-		if not assets[asset]?
-			throw new Meteor.Error "Invalid_asset"
-
-		RocketChatAssetsInstance.deleteFile asset
-
-		RocketChat.settings.updateById "Assets_#{asset}", {defaultUrl: assets[asset].defaultUrl}
-
-
-Meteor.methods
+		RocketChat.Assets.unsetAsset asset
+
+
 	setAsset: (binaryContent, contentType, asset) ->
 		unless Meteor.userId()
 			throw new Meteor.Error 'invalid-user', "[methods] setAsset -> Invalid user"
@@ -203,36 +243,7 @@
 		unless hasPermission
 			throw new Meteor.Error 'manage-assets-not-allowed', "[methods] unsetAsset -> Manage assets not allowed"

-		if not assets[asset]?
-			throw new Meteor.Error "Invalid_asset"
-
-		extension = mime.extension(contentType)
-		if extension not in assets[asset].constraints.extensions
-			throw new Meteor.Error "Invalid_file_type", contentType
-
-		file = new Buffer(binaryContent, 'binary')
-
-		if assets[asset].constraints.width? or assets[asset].constraints.height?
-			dimensions = sizeOf file
-
-			if assets[asset].constraints.width? and assets[asset].constraints.width isnt dimensions.width
-				throw new Meteor.Error "Invalid_file_width"
-
-			if assets[asset].constraints.height? and assets[asset].constraints.height isnt dimensions.height
-				throw new Meteor.Error "Invalid_file_height"
-
-		rs = RocketChatFile.bufferToStream file
-		RocketChatAssetsInstance.deleteFile asset
-		ws = RocketChatAssetsInstance.createWriteStream asset, contentType
-		ws.on 'end', Meteor.bindEnvironment ->
-			Meteor.setTimeout ->
-				RocketChat.settings.updateById "Assets_#{asset}", {
-					url: "/assets/#{asset}.#{extension}"
-					defaultUrl: assets[asset].defaultUrl
-				}
-			, 200
-
-		rs.pipe ws
+		RocketChat.Assets.setAsset binaryContent, contentType, asset
 		return


Add toString to `ScopeDescriptor`

Add toString to `ScopeDescriptor`

--- src/scope-descriptor.coffee
+++ src/scope-descriptor.coffee
@@ -44,3 +44,6 @@
         scope = ".#{scope}" unless scope[0] is '.'
         scope
       .join(' ')
+
+  toString: ->
+    @getScopeChain()


Lock the res

Lock the res

--- public/app/index.coffee
+++ public/app/index.coffee
@@ -15,7 +15,9 @@

     # Bind a resize function
     (resize = ()=>
+        base = [1024, 576]
         aspect = 16.0 / 9.0
+        canvas.width 1024 ## XXX: Locking scaling until later
         width = canvas.width()
         canvas.height (width / aspect)
         console.log "W: #{canvas.width()}, H: #{canvas.height()}: AR[e]: #{aspect} AR[a]: #{canvas.width() / canvas.height()}")()


invitation

invitation

--- server/app/bongo/models/invitation.coffee
+++ server/app/bongo/models/invitation.coffee
@@ -13,7 +13,7 @@
     indexes         :
       code          : 'unique'
     sharedMethods   :
-      static        : ['create','byCode','sendBetaInviteFromClient'] # ,'__createBetaInvites'
+      static        : ['create','byCode','sendBetaInviteFromClient','grantInvitesFromClient']
     schema          :
       code          : String
       inviteeEmail  : String
@@ -43,65 +43,6 @@
         targetType  : JAccount
         as          : 'redeemer'

-  # @__attemptToFixChrisFuckup =(callback)->
-  #   i = 0
-  #   JAccount.one {'profile.nickname': 'devrim'}, (err, devrim)=>
-  #     if err
-  #       callback err
-  #     else
-  #       @all { 'origin.constructorName': $exists: no }, (err, invites)->
-  #         invites.forEach (invite)->
-  #           if err
-  #             callback err
-  #           else
-  #             invite.update {$set: origin: ObjectRef devrim}, {multi: yes}, callback
-  #             # invite.addInvitedBy devrim, (err)->
-  #             #   console.log err
-  #             #   console.log 'finished', i++
-  #             #   callback 'ok'
-  #
-  # @__sendBetaInvites = bongo.secure do->
-  #   betaTestersEmails = fs.readFileSync 'invitee-emails2.txt', 'utf-8'
-  #   # betaTestersEmails = 'chris123412341234@jraphical.com'
-  #   betaTestersHTML   = fs.readFileSync 'email/beta-testers-invite.txt', 'utf-8'
-  #   protocol = 'https://'
-  #   (client,callback)->
-  #     i = 0
-  #     recipients = []
-  #     {host, port} = server
-  #     account = client.connection.delegate
-  #     unless 'super-admin' in account.globalFlags
-  #       return callback new KodingError "not authorized"
-
-  #     # host = 'localhost:3000'
-  #     # protocol = 'http://'
-  #     uniq(betaTestersEmails.split '\n').slice(2983, 3999).forEach (email)=>
-  #       recipients.push =>
-  #         @one {inviteeEmail: email}, (err, invite)=>
-  #           if err
-  #             console.log err
-  #           else if invite?
-  #             url = "#{protocol}#{host}/invitation/#{invite.code}"
-  #             # bitly.shorten url, (err, response)=>
-  #             #   shortenedUrl = response.data.url
-  #             #   if shortenedUrl?
-  #                 # shortenedUrl = url
-  #             personalizedMail = betaTestersHTML.replace '#{url}', url#shortenedUrl
-
-  #             Emailer.send
-  #               From      : @getInviteEmail()
-  #               To        : email
-  #               Subject   : '[Koding] Here is your beta invite!'
-  #               TextBody  : personalizedMail
-  #             , (err)->
-  #               # console.log 'finished', i++, err
-  #               recipients.next(err)
-  #               # else console.log email
-  #           else
-  #             console.log "no invitation was found for #{email}"
-  #             recipients.next null
-  #     recipients.push callback
-  #     daisy recipients

   createBetaInvite = (options,callback)->
     {inviterUsername,inviteeEmail,inviteType} = options
@@ -203,33 +144,23 @@
           else
             log "[JInvitation.sendBetaInvite] something got messed up."

-
-  # @__createBetaInvites =do ->
-  #   #betaTestersEmails = 'chris123412341234@jraphical.com'
-  #   betaTestersEmails = fs.readFileSync('./invitee-emails2.txt', 'utf-8')
-  #   #betaTestersEmails = 'chris123123@jraphical.com'
-  #   (callback)->
-  #     JAccount.one {'profile.nickname': 'devrim'}, (err, devrim)=>
-  #       i = 0
-  #       recipients = []
-  #       uniq(betaTestersEmails.split '\n').forEach (email)->
-  #         code = crypto
-  #           .createHmac('sha1', 'kodingsecret')
-  #           .update(email)
-  #           .digest('hex')
-  #         # console.log email, code
-  #         recipients.push ->
-  #           invite = new JInvitation {
-  #             code
-  #             inviteeEmail  : email
-  #             maxUses       : 1
-  #             origin        : ObjectRef(devrim)
-  #             type          : 'launchrock'
-  #           }
-  #           invite.save (err)->
-  #             recipients.next(err)
-  #       recipients.push -> callback(recipients)
-  #       daisy recipients
+  @grantInvitesFromClient = bongo.secure (client, options,callback)->
+    account = client.connection.delegate
+    # unless 'super-admin' in account.globalFlags
+    unless account?.profile?.nickname is 'devrim'
+      return callback new KodingError "not authorized"
+    else
+      if options.batch?
+        callback "not implemented yet"
+      else
+        {username,quota} = options
+        quota = 3 if quota is ''
+        @grant {'profile.nickname':username},quota,(err)->
+          if err
+            callback err
+          else
+            callback null, "#{quota} invites granted to #{username}"
+

   @grant =(selector, quota, callback)->
     unless quota > 0


Added new command to show teamcity aliases

--- src/scripts/teamcity.coffee
+++ src/scripts/teamcity.coffee
@@ -20,6 +20,7 @@
 #   hubot tc build start <buildType> - Adds a build to the queue for the specified build type
 #   hubot tc build start <buildType> of <project> - Adds a build to the queue for the specified build type of the specified project
 #   hubot tc build stop all <buildType> id <buildId> of <project> - Stops all currently running builds of a given buildType. Project parameter is optional. Please note that the special 'all' keyword will kill all currently running builds ignoring all further parameters. hubot tc build stop all all
+#   hubot tc show aliases
 #
 # Author:
 #   Micah Martin and Jens Jahnke
@@ -178,6 +179,10 @@

       createAndPublishBuildMap(builds['build'], msg)

+  robot.respond /tc show aliases/i, (msg) ->
+    for key, value of aliases
+      msg.send key + ": " + value
+
   robot.respond /tc build start (.*)/i, (msg) ->
     configuration = buildName = msg.match[1]
     project = null


Add error handling for JSON parse errors.

--- src/server/db/amazon.coffee
+++ src/server/db/amazon.coffee
@@ -326,11 +326,24 @@
           callback?('Failed to get snapshot metadata')
       else
         item = results.get_snapshot.Items[0]
+
+        try
+          snapshot = JSON.parse(results.get_data.buffer.toString())
+        catch error
+          snapshot = {}
+          console.error('Failure: data was corrupt for Snapshot('+docName+'-'+item.v.N+')')
+
+        try
+          meta = JSON.parse(item.meta.S)
+        catch error
+          meta = {}
+          console.error('Failure: metadata was corrupt for Snapshot('+docName+'-'+item.v.N+')')
+
         data =
           v: parseInt(item.v.N)
-          snapshot: JSON.parse(results.get_data.buffer.toString())
+          snapshot: snapshot
           type: item.type.S
-          meta: JSON.parse(item.meta.S)
+          meta: meta

         callback?(null, data)
     )
@@ -424,10 +437,22 @@
         callback?('Failed to fetch operations')
       else
         data = []
-        for op in results.get_metadata.Items
+        for operation in results.get_metadata.Items
+          try
+            op = JSON.parse(operation.op.S)
+          catch error
+            op = {}
+            console.error('Failure: data was corrupt for Operation('+docName+'-'+operation.v.N+')')
+
+          try
+            meta = JSON.parse(operation.meta.S)
+          catch error
+            meta = {}
+            console.error('Failure: metadata was corrupt for Operation('+docName+'-'+operation.v.N+')')
+
           item = {
-            op:   JSON.parse(op.op.S)
-            meta: JSON.parse(op.meta.S)
+            op: op
+            meta: meta
           }
           data.push(item)


ignore tag in CTR mode

ignore tag in CTR mode

--- js/src/crypto/AbstractCryptoService.coffee
+++ js/src/crypto/AbstractCryptoService.coffee
@@ -1,5 +1,5 @@
 # coffeelint: disable=cyclomatic_complexity
-
+
 define 'kryptnostic.abstract-crypto-service', [
   'require'
   'forge'
@@ -22,22 +22,24 @@
         throw new Error 'cypher not implemented'

     encrypt: (key, iv, plaintext) =>
+
       ciphertext = @encryptBuffer( key, iv, Forge.util.createBuffer(plaintext) )
       return ciphertext

     encryptBuffer: (key, iv, buffer) ->
+
       cipher = Forge.cipher.createCipher(@algorithm + '-' + @mode, key)
       cipher.start({ iv })
       cipher.update(buffer)
       cipher.finish()
+
       if @mode is 'GCM'
         return { 'ciphertext': cipher.output.data, 'tag': cipher.mode.tag }
       else
         return cipher.output.data

     decrypt: (key, iv, ciphertext, tag) =>
-      if tag? && @mode is not 'GCM'
-        throw new Error 'CTR does not require a tag'
+
       if @mode is 'GCM' && !tag?
         throw new Error 'GCM requires an auth tag for decryption'

@@ -45,19 +47,20 @@
         buffer = @decryptToBuffer(key, iv, Forge.util.createBuffer(ciphertext), tag)
       else
         buffer = @decryptToBuffer(key, iv, Forge.util.createBuffer(ciphertext))
+
       return buffer.data

     decryptToBuffer: (key, iv, buffer, tag) ->
-      decipher = Forge.cipher.createDecipher(@algorithm + '-' + @mode, key)
-      if tag? && @mode is not 'GCM'
-        throw new Error 'CTR does not require a tag'
+
       if @mode is 'GCM' && !tag?
         throw new Error 'GCM requires an auth tag for decryption'

+      decipher = Forge.cipher.createDecipher(@algorithm + '-' + @mode, key)
       if @mode is 'GCM'
         decipher.start({ 'iv': iv, 'tag': tag })
       else
         decipher.start({ iv })
+
       decipher.update(buffer)
       decipher.finish()
       return decipher.output


Extract compilation into separate public instance method.

--- lib/api/init.coffee
+++ lib/api/init.coffee
@@ -30,12 +30,15 @@
       .then(check_internet_connection)
       .then(ensure_template_is_updated)
       .then(checkout_version)
-      .then(copy_template)
-      .then(run_user_before_render_function)
-      .then(replace_ejs)
+      .then(@compile)
       .then(run_user_after_function)
       .then(-> "project created at '#{@target}'!")

+  compile: ->
+    copy_template.call(@)
+      .with(@)
+      .then(run_user_before_render_function)
+      .then(replace_ejs)
   #
   # @api private
   #


fix typo

fix typo

--- server/s3_config.coffee
+++ server/s3_config.coffee
@@ -1,6 +1,6 @@
 if Meteor.settings.DemoMode
   maxSize = 10485760 # 10 MB
-  console.log "demo mode: max updoad size:",maxSize
+  console.log "demo mode: max upload size:",maxSize
 else
   maxSize = 0 # any size
 allowedFileTypes = new RegExp(".*")


Don't call 'Object.keys' on a 'packageJson' with no 'dependencies' property

--- lib/core.coffee
+++ lib/core.coffee
@@ -19,6 +19,8 @@
   # Grab the project's 'package.json' as an object
   packageJson = retriever.retrieve 'package'

+  return [] unless packageJson.dependencies
+
   # Extract the NPM module names of the 'dependencies'
   dependencies = Object.keys packageJson.dependencies

@@ -58,7 +60,8 @@
     core.modules = _.union core.modules, modules

     # Load the 'axiom-base'
-    core.load 'base', retriever.retrieveExtension 'base'
+    unless 'base' in core.modules
+      core.load 'base', retriever.retrieveExtension 'base'

     # Require each axiom module.
     # Pass to load.


Add limit to Animator.values

Add limit to Animator.values

--- framer/Animator.coffee
+++ framer/Animator.coffee
@@ -22,13 +22,12 @@
 	finished: ->
 		throw Error "Not implemented"

-	values: (delta=1/60)->
-
+	values: (delta=1/60,limit=100)->
 		values = []
-
-		while not @finished()
+		for i in [0..limit]
 			values.push(@next(delta))
-
+			if @finished()
+				break
 		return values


For lineNumber use getCursorBufferPosition()

When using getCursorScreenPosition() with soft wrap the line number is
incorrect and the location in the document is incorrect. Instead use
getCursorBufferPosition() which does not count wrapped lines.
For further details see
https://atom.io/docs/api/v0.187.0/TextEditor#buffer-vs-screen-coordinates

Closes #68

--- lib/composer.coffee
+++ lib/composer.coffee
@@ -148,7 +148,7 @@

     editorDetails =
       filePath: editor.getPath()
-      lineNumber: editor.getCursorScreenPosition().row + 1
+      lineNumber: editor.getCursorBufferPosition().row + 1

   alterParentPath: (targetPath, originalPath) ->
     targetDir = path.dirname(targetPath)


postgres central log insertion and reporting

--- src/smc-hub/postgres.coffee
+++ src/smc-hub/postgres.coffee
@@ -396,11 +396,46 @@
             tables = (k for k, v of SCHEMA when v.fields?.expire? and not v.virtual)
         async.map(tables, f, opts.cb)

+    # write an event to the central_log table
     log: (opts) =>
-        throw Error("NotImplementedError")
-
+        opts = defaults opts,
+            event : required    # string
+            value : required    # object
+            cb    : undefined
+        @_query
+            query  : 'INSERT INTO central_log (id, event, value, time) VALUES ($1::UUID, $2::TEXT, $3::JSONB, NOW())'
+            params : [misc.uuid(), opts.event, opts.value]
+            cb     : (err) => opts.cb?(err)
+
+    # dump a range of data from the central_log table
     get_log: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            start : undefined     # if not given start at beginning of time
+            end   : undefined     # if not given include everything until now
+            event : undefined
+            cb    : required
+        query = 'SELECT * FROM central_log'
+        where = []
+        params = []
+        if opts.start?
+            params.push(opts.start)
+            where.push('time >= $1::TIMESTAMP')
+            if opts.end
+                params.push(opts.end)
+                where.push('time <= $2::TIMESTAMP')
+        else if opts.end?
+            params.push(opts.end)
+            where.push('time <= $1::TIMESTAMP')
+        if opts.event?
+            params.push(opts.event)
+            where.push("event = $#{params.length}::TEXT")
+        if where.length > 0
+            query += " WHERE #{where.join(' AND ')}"
+        @_query
+            query  : query
+            params : params
+            cb     : (err, result) =>
+                opts.cb(err, result?.rows)

     get_user_log: (opts) =>
         throw Error("NotImplementedError")


change link to api docs

change link to api docs

--- src/smc-webapp/api-key.cjsx
+++ src/smc-webapp/api-key.cjsx
@@ -153,7 +153,7 @@

             NOTE: If you do not have a password set, there is a workaround to generate your API key.

-            See the CoCalc API documentation to learn about the API.
+            See the CoCalc API documentation to learn about the API.


test(errors): add test for MethodNotAllowed error

--- src/spec/errors.spec.coffee
+++ src/spec/errors.spec.coffee
@@ -6,6 +6,7 @@
   {name: 'BadRequest', statusCode: 400}
   {name: 'Unauthorized', statusCode: 401}
   {name: 'NotFound', statusCode: 404}
+  {name: 'MethodNotAllowed', statusCode: 405}
   {name: 'ConcurrentModification', statusCode: 409}
   {name: 'InternalServerError', statusCode: 500}
   {name: 'ServiceUnavailable', statusCode: 503}


Provide public access to styles.ui.labels.label and .heading helper methods

--- src/styles/ui/labels.coffee
+++ src/styles/ui/labels.coffee
@@ -17,6 +17,10 @@
       fontWeight: 'bold'

 module.exports =
+
+  label: label
+
+  heading: heading

   h1: heading 20


Landing: fix require, confused with the client side landing :facepalmhere:

--- workers/social/lib/social/render/loggedout/tags/landing.coffee
+++ workers/social/lib/social/render/loggedout/tags/landing.coffee
@@ -1,5 +1,6 @@
-kd          = require 'kd'
-origin      = kd.config.domains.base
+{ argv }    = require 'optimist'
+{ domains } = require('koding-config-manager').load("main.#{argv.c}")
+origin      = domains.base
 title       = 'Koding | Say goodbye to your localhost and write code in the cloud'
 shareUrl    = "https://#{origin}/"
 description = 'Koding is a cloud-based development environment complete with free VMs, IDE & sudo enabled terminal where you can learn Ruby, Go,  Java, NodeJS, PHP, C, C++, Perl, Python, etc.'
@@ -39,5 +40,5 @@
   <meta name="twitter:card"            content="summary_large_image"/>
   <meta name="twitter:image"           content="http://#{twImage}"/>
   <meta name="twitter:description"     content="#{description}"/>
-  <meta name="twitter:domain"          content="#{kd.config.domains.base}">
+  <meta name="twitter:domain"          content="#{domains.base}">
   """


Extract printOverview

Extract printOverview

--- cmd/lookup.coffee
+++ cmd/lookup.coffee
@@ -6,6 +6,12 @@

 # API URL
 api_lookup = (q) -> "#{API_HOST}/api/GetSeries.php?seriesname=#{q}"
+
+printOverview = (s) ->
+  overview = s['Overview'] && s['Overview'][0]
+  if overview
+    overview = overview.substring(0, 256) + '...' if overview.length > 256
+    pr(gray(overview))

 lookup = (q) ->
   return error('Please specify title to look for') unless q
@@ -16,11 +22,7 @@
       aired = s['FirstAired'] || '?'
       resLine = "#{s['seriesid']} #{name} (since #{aired})"
       pr(resLine)
-
-      overview = s['Overview'] && s['Overview'][0]
-      if overview
-        overview = overview.substring(0, 256) + '...' if overview.length > 256
-        pr(gray(overview))
+      printOverview(s)
       pr('  ')

 module.exports = lookup


Make sure image pattern is correctly set during construction

--- framer/SVGLayer.coffee
+++ framer/SVGLayer.coffee
@@ -37,6 +37,7 @@
 		else
 			@elements = []

+		SVG.updateImagePatternSVG(@)
 		SVG.updateGradientSVG(@)

 	@define "elements", @simpleProperty("elements", {})
@@ -62,7 +63,6 @@
 		get: ->
 			return @_image
 		set: (value) ->
-			return if @__constructor
 			@_image = value
 			SVG.updateImagePatternSVG(@)


updateImagePatternSVG when changing image size

--- framer/SVGLayer.coffee
+++ framer/SVGLayer.coffee
@@ -79,6 +79,7 @@
 			else
 				return if not _.isFinite(value.width) or not _.isFinite(value.height)
 				@_setPropertyValue "imageSize", {width: value.width, height: value.height}
+				SVG.updateImagePatternSVG(@)

 	@define "svg",
 		get: ->


coffeescript 2

coffeescript 2

--- lib/init.coffee
+++ lib/init.coffee
@@ -18,10 +18,10 @@

 onUpdate = () -> 0
 plugin = child_process.fork(__dirname + '/plugin')
-bar = (data)-> {
+bar = (data)->
     console.log "result: " ,  data
     onUpdate(data)
-}
+

 plugin.on('message', bar)
 # plugin.send({type: 'whatever'})


Fix typo "pytho3" (#121)

Fix typo "pytho3" (#121)

To resolve #105

--- lib/init.coffee
+++ lib/init.coffee
@@ -20,7 +20,7 @@
       You can use `$PROJECT` or `$PROJECT_NAME` substitution for project-specific
       paths.\n
       For example:
-      `~/.venv/$PROJECT_NAME/bin/python, $PROJECT/venv/bin/python, /usr/bin/pytho3, python`
+      `~/.venv/$PROJECT_NAME/bin/python, $PROJECT/venv/bin/python, /usr/bin/python3, python`
       '''
       order: 1
     }


Bare minimum options as default. This will help remove issues with differing versions of Scala.

--- lib/init.coffee
+++ lib/init.coffee
@@ -1,9 +1,7 @@
 module.exports =
   configDefaults:
     scalacExecutablePath: null
-    scalacOptions: '-Xlint -Ywarn-adapted-args -Ywarn-dead-code -Ywarn-inaccessible -Ywarn-infer-any ' +
-      '-Ywarn-nullary-override -Ywarn-nullary-unit -Ywarn-numeric-widen -Ywarn-unused -Ywarn-unused-import ' +
-      '-Ywarn-value-discard'
+    scalacOptions: '-Xlint'

   activate: ->
     console.log('activate linter-scalac')


socialapi: `MessageEventManager` binds event listeners for a single `SocialMessage` instance

--- client/Main/messageeventmanager.coffee
+++ client/Main/messageeventmanager.coffee
@@ -4,41 +4,30 @@

     super options, data

-    @messages = {}
-
-
-  bindListeners: (message) ->
-
-    @messages[message.getId()] = message
-
-    message
+    data
       .on "InteractionAdded", @bound "addInteraction"
       .on "InteractionRemoved", @bound "removeInteraction"
-      .on "ReplyAdded", @lazyBound "addReply", message
-      .on "ReplyRemoved", @lazyBound "removeReply", message
+      .on "ReplyAdded", @bound "addReply"
+      .on "ReplyRemoved", @bound "removeReply"

   addInteraction: (event) ->

-    {messageId, typeConstant} = event
-
-    return  unless message = @messages[messageId]
-
+    {typeConstant} = event
     fn = @bound "add#{typeConstant.capitalize()}"
-    KD.utils.defer => fn message, event
+    fn event

   removeInteraction: (event) ->

-    {messageId, typeConstant} = event
-
-    return  unless message = @messages[messageId]
-
+    {typeConstant} = event
     fn = @bound "remove#{typeConstant.capitalize()}"
-    KD.utils.defer => fn message, event
+    fn event

-  addLike: (message, {accountOldId, count}) ->
+  addLike: ({accountOldId, count}) ->
+
+    message = @getData()

     {like} = message.interactions

@@ -50,7 +39,9 @@
     message.emit "update"

-  removeLike: (message, {accountOldId, count}) ->
+  removeLike: ({accountOldId, count}) ->
+
+    message = @getData()

     {like} = message.interactions

@@ -63,10 +54,11 @@
     message.emit "update"

-  addReply: (message, plain) ->
+  addReply: (plain) ->

     reply = KD.singleton("socialapi").message.revive plain

+    message = @getData()
     message.replies.push reply
     message.repliesCount++

@@ -74,11 +66,12 @@
     message.emit "update"

-  removeReply: (message, {replyId}) ->
+  removeReply: ({replyId}) ->

     for item in message.replies
       reply = item  if replyId is item.getId()

+    message = @getData()
     message.replies = message.replies.filter (reply) -> reply.getId() isnt replyId
     message.repliesCount--


social-api: define reply response handler function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -123,6 +123,11 @@
     KD.remote.api.SocialMessage[name] rest..., (err, res)->
       return callback err if err
       return callback null, mapActivity res
+
+  messageApiReplyResFunc = (name, rest..., callback)->
+    KD.remote.api.SocialMessage[name] rest..., (err, res)->
+      return callback err if err
+      return callback null, mapActivities res

   channelApiActivitiesResFunc = (name, rest..., callback)->
     KD.remote.api.SocialChannel[name] rest..., (err, result)->


Activity: small refac to socialapicontroller, puts an empty array when there are no items

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -7,12 +7,21 @@
     KD.getSingleton("mainController").ready @bound "openGroupChannel"

   getPrefetchedData:->
+
     data = {}
-    return data unless KD.socialApiData
-    data["popularTopics"]    = mapChannels KD.socialApiData["popularTopics"]
-    data["followedChannels"] = mapChannels KD.socialApiData["followedChannels"]
-    data["pinnedMessages"]   = mapActivities KD.socialApiData["pinnedMessages"]
-    data["privateMessages"]  = mapPrivateMessages KD.socialApiData["privateMessages"]
+
+    return data  unless KD.socialApiData
+
+    { popularTopics, followedChannels
+      pinnedMessages, privateMessages } = KD.socialApiData
+
+    processInCase = (fn, items) -> if items then fn items else []
+
+    data.popularTopics    = processInCase mapChannels, popularTopics
+    data.followedChannels = processInCase mapChannels, followedChannels
+    data.pinnedMessages   = processInCase mapActivities, pinnedMessages
+    data.privateMessages  = processInCase mapPrivateMessages, privateMessages
+
     return data

   openGroupChannel: ->


SyncController : method to get full path of file

--- client/Main/synclocalcontroller.coffee
+++ client/Main/synclocalcontroller.coffee
@@ -108,6 +108,11 @@
   getRecentOpenedFiles: ->
     @openedFiles

+  getFileFullPath:(file)->
+    plainPath = FSHelper.plainPath file.path
+    fileName = "[#{file.vmName}]#{plainPath}"
+    return fileName
+

   saveEditorHistory: ->
     log "NOT IMPLEMENTED YET"


Providers: helper for getting list of supported providers from config

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -340,3 +340,7 @@
                             unwanted results while building your stacks.
                             '''
     credentialFields       : {}
+
+  _getSupportedProviders   : ->
+    (Object.keys this).filter (provider) =>
+      this[provider].supported


utils:impersonate: make sure kite cache cleared out when someone impersonated

--- client/app/lib/util/impersonate.coffee
+++ client/app/lib/util/impersonate.coffee
@@ -1,10 +1,18 @@
 remote = require('../remote').getInstance()
 showErrorNotification = require './showErrorNotification'
+KiteCache = require '../kite/kitecache'

 module.exports = (username) ->
   remote.api.JAccount.impersonate username, (err) =>
     if err
       options = userMessage: "You are not allowed to impersonate"
       showErrorNotification err, options
-    else global.location.reload()
+    else

+      # We need to clear cache just before impersonate
+      # otherwise cache can cause some issue while working
+      # on the impersonated account ~ GG
+      KiteCache.clearAll()
+
+      global.location.reload()
+


fix realtime after import

fix realtime after import

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -9,6 +9,8 @@
         require '../constants/app_constants'

 {reverseDateSort, getSortFunction} = require '../utils/misc'
+
+EPOCH = (new Date(0)).toISOString()

 class MessageStore extends Store

@@ -374,6 +376,10 @@
             lastdate = _messages.last()?.get('date')
             if lastdate
                 SocketUtils.changeRealtimeScope fetchResult.mailboxID, lastdate
+            else if fetchResult.messages.length is 0
+                # we are open to every message
+
+                SocketUtils.changeRealtimeScope fetchResult.mailboxID, EPOCH
             @emit 'change'

         handle ActionTypes.CONVERSATION_FETCH_SUCCESS, ({updated}) ->


Clean test

Clean test

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -479,7 +479,7 @@
         if _filterType is 'flag'
             sequence = sequence.filter _matchFlag

-        if _filterType in ['date']
+        else if _filterType is 'date'
             sequence = sequence.filter _matchRangeDate

         # We dont filter for _filterType from and dest because it is


Remove superfluous code

Remove superfluous code

--- client/src/models/visualisation.coffee
+++ client/src/models/visualisation.coffee
@@ -67,13 +67,6 @@
     @set('filters', filters)

   populateFormattedDates: ->
-    dateFields = @get('indicator').get('indicatorDefinition')?.fields
-    if dateFields?
-      dateFields = _.filter(dateFields, (field) ->
-        field.type is 'date'
-      )
-      dateFields = _.map(dateFields, (field) -> field.name)
-
     for row in @get('data')
       row.formatted ||= {}
       for key, value of row


picture standard

picture standard

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -9,8 +9,8 @@
 				$scope.url = e.target.result
 		)(input.files[0])
 		reader.readAsDataURL(input.files[0])
-	$scope.setSource1 = ->
-		$scope.url = 'img/pano.jpg'
+	$scope.setSource1 = (cam)->
+		$scope.url = cam.pano360.file
 	$scope.setSource2 = ->
 		input = document.getElementById("picUpload")
 		input.click()


not using async button in step footer file

--- src/components/task-step/step-footer.cjsx
+++ src/components/task-step/step-footer.cjsx
@@ -3,7 +3,7 @@
 _ = require 'underscore'
 {StepPanel} = require '../../helpers/policies'

-{CardBody, AsyncButton} = require 'openstax-react-components'
+{CardBody} = require 'openstax-react-components'
 {TaskStore} = require '../../flux/task'
 StepFooterMixin = require './step-footer-mixin'


Add pausable motion component

Add pausable motion component

(needs to be extracted)

--- src/components/utils/WaterSplashes.coffee
+++ src/components/utils/WaterSplashes.coffee
@@ -60,9 +60,31 @@

 })

+Crafty.c('PausableMotion', {
+  required: 'Motion'
+  events:
+    'GamePause': '_handlePause'
+
+  init: ->
+    @_pausingProps = {}
+
+  _handlePause: (pausing) ->
+    motionProps = ['_vx', '_vy', '_ax', '_ay']
+    if pausing
+      @_pausingProps = {}
+      motionProps.forEach((prop) =>
+        @_pausingProps[prop] = this[prop]
+      )
+      @resetMotion()
+    else
+      motionProps.forEach((prop) =>
+        this[prop] = @_pausingProps[prop] || 0
+      )
+})
+
 splashPool = createEntityPool(
   ->
-    Crafty.e('2D, WebGL, Explode, ColorEffects, GameParticle, Motion, Tween')
+    Crafty.e('2D, WebGL, Explode, ColorEffects, GameParticle, PausableMotion, Tween')
       .colorOverride('#FFFFFF')
   200
 )


also update parent if was previously set

--- client/lanes/models/AssociationMap.coffee
+++ client/lanes/models/AssociationMap.coffee
@@ -36,8 +36,9 @@

     replace: (parent, name, model) ->
         parent._cache[name] = model
-        model.parent = parent if model.hasAttribute('parent')
-        model.parent_association = name if model.hasAttribute('parent_association')
+        model.parent = parent if model.hasAttribute?('parent') or model.parent?
+        model.parent_association = name if model.hasAttribute?('parent_association') or model.parent_association?
+
         parent.trigger("change", parent, {})
         parent.trigger("change:#{name}", model, {})


Update count correctly when unfollow

Update count correctly when unfollow

--- server/app/bongo/abstractions/followable.coffee
+++ server/app/bongo/abstractions/followable.coffee
@@ -98,7 +98,7 @@

   unfollow: bongo.secure (client,callback)->
     follower = client.connection.delegate
-    @removeFollower follower, respondWithCount : yes, (err, docs, count)=>
+    @removeFollower follower, respondWithCount : yes, (err, count)=>
       if err
         console.log err
       else


Add missing key and refactor

Add missing key and refactor

--- resources/assets/coffee/react/contest/voting/vote-summary.coffee
+++ resources/assets/coffee/react/contest/voting/vote-summary.coffee
@@ -1,5 +1,5 @@
 ###
-#    Copyright 2015-2017 ppy Pty. Ltd.
+#    Copyright 2015-2018 ppy Pty. Ltd.
 #
 #    This file is part of osu!web. osu!web is distributed with the hope of
 #    attracting more community contributions to the core ecosystem of osu!.
@@ -16,27 +16,18 @@
 #    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
 ###

-{div,i} = ReactDOMFactories
-el = React.createElement
+{div, span} = ReactDOMFactories

-class Contest.Voting.VoteSummary extends React.Component
-  render: ->
-    classes = [
-      'contest__voting-star',
-      'contest__voting-star--smaller',
-      'contest__voting-star--float-right',
-    ]
-    selectedClass = [
-      'contest__voting-star--selected',
-    ]
+baseClass = osu.classWithModifiers('contest__voting-star', ['smaller', 'float-right'])
+selectedClass = 'contest__voting-star--selected'

-    voteSummary = []
-    voteSummary.push _.times Math.max(0, @props.maxVotes - @props.voteCount), ->
-      div className: classes.join(' '),
-        i className: 'fas fa-fw fa-star'
-    voteSummary.push _.times @props.voteCount, ->
-      div className: classes.concat(selectedClass).join(' '),
-        i className: 'fas fa-fw fa-star'
+Contest.Voting.VoteSummary = ({voteCount, maxVotes}) ->
+  div null,
+    for i in [0...maxVotes]
+      className = baseClass
+      className += " #{selectedClass}" if i >= voteCount

-    div {},
-      voteSummary
+      div
+        key: "vote-#{i}"
+        className: className
+        span className: 'fas fa-fw fa-star'


Got game

Got game

--- src/test/test_config.coffee
+++ src/test/test_config.coffee
@@ -26,6 +26,7 @@
     t.end()

   test 'config game', (t) ->
+    t.ok game = world.find_path('.game'), "Got game"
     t.end()

   test 'config program', (t) ->


Parsed arguments to components

Parsed arguments to components

--- source/Commander.coffee
+++ source/Commander.coffee
@@ -2,3 +2,6 @@
   Commander module for MP3 Downloader.
   Written by Jense5.
 ###
+
+# Fetch arguments
+components = require('minimist')(process.argv.slice(2))


:lipstick: Removing a duplicate test.

:lipstick: Removing a duplicate test.

- This was oddly an exact copy and paste of another.  Maybe a bad
  merge...

--- spec/jumpy-spec.coffee
+++ spec/jumpy-spec.coffee
@@ -212,20 +212,6 @@
             atom.commands.dispatch textEditorElement, 'jumpy:e'
             expect(textEditor.getSelections()[0].getText()).toBe 'aa ab ac ad '

-    xdescribe "when the a text selection has begun
-        before a jumpy:toggle event is triggered", ->
-        it "keeps the selection for subsequent jumps", ->
-            textEditorElement.trigger 'jumpy:clear'
-            textEditorElement.trigger 'jumpy:toggle'
-            textEditorElement.trigger 'jumpy:a'
-            textEditorElement.trigger 'jumpy:a'
-            editor.selectRight()
-            editor.selectRight()
-            textEditorElement.trigger 'jumpy:toggle'
-            textEditorElement.trigger 'jumpy:a'
-            textEditorElement.trigger 'jumpy:e'
-            expect(editor.getSelection(0).getText()).toBe 'aa ab ac ad '
-
     xdescribe "when a character is entered that no label has a match for", ->
         it "displays a status bar error message", ->
             textEditorElement.trigger 'jumpy:z'


commiting whatever this is pre linode destroy

--- spec/login_spec.coffee
+++ spec/login_spec.coffee
@@ -7,7 +7,7 @@
 			site: 'http://localhost:5469'

 	beforeEach (done) ->
-		@browser.visit('/', done)
+		@browser.visit('/', done)A

 	it "logs in", (done) ->
 		@browser


Update spec

Update spec

--- spec/mcide-spec.coffee
+++ spec/mcide-spec.coffee
@@ -3,5 +3,3 @@
     waitsForPromise ->
       atom.workspace.open('test.mc.php').then (editor) ->
         expect(editor.getPath()).toContain 'test.mc.php'
-        atom.commands.dispatch(editor, "mcide:set_index_file")
-        expect(atom.config.get('mcide.indexFile')).toContain 'test.mc.php'


Fixed playerspec

Fixed playerspec

--- spec/playerspec.coffee
+++ spec/playerspec.coffee
@@ -1,8 +1,8 @@
 requirejs.config({
-    baseUrl: '../',
+  #baseUrl: '../',
 })

-require(['player'], (Player) ->
+define(['player'], (Player) ->
     describe "Player", ->
       player = null


use deferred objects instead

use deferred objects instead

--- spotify-control.coffee
+++ spotify-control.coffee
@@ -5,6 +5,7 @@
 #   "dbus": "0.2.9"
 #   "pidof": "1.0.2",
 #   "properties": "1.2.1"
+#   "q": "1.0.1"
 #
 # Configuration:
 #   None
@@ -26,6 +27,7 @@
 properties = require 'properties'
 fs = require 'fs'
 DBus = require 'dbus'
+q = require 'q'

 dbus = new DBus()

@@ -36,75 +38,72 @@
 module.exports = (robot) ->

   robot.respond /sp pause$/i, (msg) ->
-    getInterface (err, iface) ->
-      if err
-        msg.send "Error: #{err}"
-      else
-        iface.Pause()
-        msg.send ":pause:"
+    getInterface()
+    .then (iface) ->
+      iface.Pause()
+      msg.send ":pause:"
+    .catch (err) ->
+      msg.send "Error: #{err}"

   robot.respond /sp play$/i, (msg) ->
-    getInterface (err, iface) ->
-      if err
-        msg.send "Error: #{err}"
-      else
-        iface.PlayPause()
-        msg.send ":playpause:"
+    getInterface()
+    .then (iface) ->
+      iface.PlayPause()
+      msg.send ":playpause:"
+    .catch (err) ->
+      msg.send "Error: #{err}"

   robot.respond /sp next$/i, (msg) ->
-    getInterface (err, iface) ->
-      if err
-        msg.send "Error: #{err}"
-      else
+    getInterface()
+      .then (iface) ->
         iface.Next()
         msg.send ":next:"
+      .catch (err) ->
+        msg.send "Error: #{err}"

   robot.respond /sp prev$/i, (msg) ->
-    getInterface (err, iface) ->
-      if err
-        msg.send "Error: #{err}"
-      else
-        #call twice, once to go to start, then again to go to last track
+    getInterface()
+      .then (iface) ->
         iface.Previous()
         iface.Previous()
-        msg.send ":previous:"
+        msg.send ":previous"
+      .catch (err) ->
+        msg.send "Error: #{err}"

-
-spotifyPid = (callback) ->
+spotifyPid = () ->
+  def = q.defer()
   pidof 'spotify', (err, pid) ->
     if err
-      callback err
+      def.reject err
     else if not pid
-      callback "Spotify doesn't seem to be running"
+      def.reject "Spotify doesn't seem to be running"
     else
-      callback err, pid
+      def.resolve pid
+  def.promise

-dbusAddress = (callback) ->
-  spotifyPid (err, pid) ->
-    if err
-      callback err
-    else if not pid
-      callback "Spotify doesn't seem to be running"
-    else
-      callback err, pid
+dbusAddress = () ->
+  def = q.defer()
+  spotifyPid()
+  .then (pid) ->
+    file = fs.readFileSync "/proc/#{pid}/environ", {encoding: 'utf-8'}
+    fixedfile = file.replace /\0/g, "\n"
+    parsed = properties.parse fixedfile, {strict: true}
+    process.env['DBUS_SESSION_BUS_ADDRESS'] = parsed['DBUS_SESSION_BUS_ADDRESS']
+    def.resolve()
+  .catch (err) ->
+    def.reject err
+  def.promise

-dbusAddress = (callback) ->
-  spotifyPid (err, pid) ->
-    if err
-      callback err
-    else
-      file = fs.readFileSync "/proc/#{pid}/environ", {encoding: 'utf-8'}
-      fixedfile = file.replace /\0/g, "\n"
-      parsed = properties.parse fixedfile, {strict: true}
-      process.env['DBUS_SESSION_BUS_ADDRESS'] = parsed['DBUS_SESSION_BUS_ADDRESS']
-      callback()
-
-getInterface = (callback) ->
-  dbusAddress (err) ->
-    if err
-      callback err
-    else
-      bus = dbus.getBus 'session'
-      bus.getInterface service, path, memb, (err, iface) ->
-        callback err, iface
-
+getInterface = () ->
+  def = q.defer()
+  dbusAddress()
+  .then () ->
+    bus = dbus.getBus 'session'
+    bus.getInterface service, path, memb, (err, iface) ->
+      if err
+        def.reject err
+      else
+        def.resolve iface
+  .catch (err) ->
+    def.reject err
+  def.promise


more direct approach

more direct approach

--- lib/frontend.coffee
+++ lib/frontend.coffee
@@ -8,5 +8,5 @@
         selector.show items, (item) =>
           resolve item

-    client.handle 'atompath', ->
-      atom.config.resourcePath
+    client.handle 'electronexe', ->
+      process.execPath


fix merge without fast forward

fix merge without fast forward

--- lib/git-plus.coffee
+++ lib/git-plus.coffee
@@ -130,7 +130,7 @@
     @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:run', -> git.getRepo().then((repo) -> new GitRun(repo))
     @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:merge', -> git.getRepo().then((repo) -> GitMerge(repo))
     @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:merge-remote', -> git.getRepo().then((repo) -> GitMerge(repo, remote: true))
-    @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:merge-no-fast-forward', -> git.getRepo().then((repo) -> GitMerge(repo, no_fast_forward: true))
+    @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:merge-no-fast-forward', -> git.getRepo().then((repo) -> GitMerge(repo, noFastForward: true))
     @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:rebase', -> git.getRepo().then((repo) -> GitRebase(repo))
     @subscriptions.add atom.commands.add 'atom-workspace', 'git-plus:git-open-changed-files', -> git.getRepo().then((repo) -> GitOpenChangedFiles(repo))
     @subscriptions.add atom.commands.add '.tree-view', 'git-plus-context:add', -> git.getRepo().then((repo) -> GitAddContext(repo, contextCommandMap))


:bug: Reduce count on editor-linter destroy

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -22,6 +22,10 @@
         EditorLinter.messages.set(linter, messages)
         @countMessages(messages)
         @emitter.emit 'did-change'
+      EditorLinter.onDidDestroy =>
+        EditorLinter.messages.forEach (messages) =>
+          @countMessages(messages, false)
+          @emitter.emit 'did-change'
     @subscriptions.add atom.workspace.onDidChangeActivePaneItem =>
       @count = File: 0, Project: 0
       @messages.forEach (messages) =>


:art: improve responsibility

:art: improve responsibility

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -275,34 +275,45 @@
   args: [] # e.g args: ['-rn']

   initialize: ->
-    unless BufferedProcess?
-      {BufferedProcess} = require 'atom'
+    BufferedProcess ?= require('atom').BufferedProcess
     @results = []
-    @exitCount = @runCount = 0
+    @processFinished = 0

     @onDidSetTarget =>
       unless @isMode('visual')
         @restore = @preservePoints()
         @target.select()

-      for selection, i in @editor.getSelections()
-        @runExternalCommand(selection.getText())
-        @restore?(selection, i)
-
-  runExternalCommand: (stdin) ->
-    runCount = @runCount++
-    stdout = (output) =>
-      @results[runCount] = output
-
-    exit = (code) =>
-      @exitCount++
-      if @exitCount is @runCount
+      selections = @editor.getSelections()
+      numberOfProcess = selections.length
+      for selection, index in selections
+        @runExternalCommand {
+          command: @command
+          args: @args
+          stdout: @onStdout(index)
+          exit: @onExit(numberOfProcess)
+          stdin: selection.getText()
+        }
+        @restore?(selection, index)
+
+  onStdout: (index) ->
+    (output) =>
+      @results[index] = output
+
+  onExit: (numOfProcess) ->
+    (code) =>
+      @processFinished++
+      if @processFinished is numOfProcess
         @input = @results
         @vimState.operationStack.process()

-    bufferedprocess = new BufferedProcess({@command, @args, stdout, exit})
-    bufferedprocess.process.stdin.write(stdin)
-    bufferedprocess.process.stdin.end()
+  runExternalCommand: (options) ->
+    {stdin} = options
+    delete options.stdin
+    bufferedProcess = new BufferedProcess(options)
+    if stdin
+      bufferedProcess.process.stdin.write(stdin)
+      bufferedProcess.process.stdin.end()

   getNewText: (text) ->
     # Return stdout of external command in order


:fire: debug bring

:fire: debug bring

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -86,12 +86,6 @@
     else
       restore()

-  # [debug]
-  reportSelectionProperties: (subject) ->
-    console.log "#{subject} ============ "
-    for selection in @editor.getSelections()
-      console.log selection.id, inspect(swrap(selection).getProperties())
-
   observeSelectAction: ->
     # Select operator is used only in visual-mode.
     # visual-mode selection modification should be handled by Motion::select(), TextObject::select()
@@ -100,11 +94,9 @@
         unless @isMode('visual')
           @onWillSelectTarget =>
             @updateSelectionProperties()
-            # @reportSelectionProperties('before')
       else
         @onDidSelectTarget =>
           @updateSelectionProperties()
-          # @reportSelectionProperties('before')

     if @isWithOccurrence()
       scanRanges = null


don't autoinstall language-markdown

don't autoinstall language-markdown

--- lib/packages.coffee
+++ lib/packages.coffee
@@ -5,7 +5,6 @@
   'indent-detective'
   'latex-completions'
   'language-julia'
-  'language-markdown'
   'ink'
   'julia-client'
 ]


fixed r2gredis

fixed r2gredis

--- lib/r2gredis.coffee
+++ lib/r2gredis.coffee
@@ -10,10 +10,10 @@
     return client
   # keymap consists of primary key
   keymap: ->
-    keymap = redis.createClient() unless client
+    keymap = redis.createClient() unless keymap
     return keymap
   kill: ->
-    client.quit()
-    keymap.quit()
+    client.quit() if client
+    keymap.quit() if keymap

 }


:memo: fix setting description

:memo: fix setting description

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -42,10 +42,8 @@
     enum: ['smart', 'simple']
     description: """
     When you think undo/redo cursor position has BUG, set this to `simple`.

-

-    `smart`: Good accuracy but have cusor-not-updated-on-different-editor limitation

+    `smart`: Good accuracy but have cursor-not-updated-on-different-editor limitation

     `simple`: Always work, but accuracy is not as good as `smart`.

-

     """
   groupChangesWhenLeavingInsertMode: true
   useClipboardAsDefaultRegister: false


Explicit set end position to not move

Explicit set end position to not move

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -161,7 +161,7 @@

     editor.transact =>
       cursorPosition = editor.getCursorBufferPosition()
-      startPoint = cursorPosition.translate([0, -snippet.prefix.length], [])
+      startPoint = cursorPosition.translate([0, -snippet.prefix.length], [0, 0])
       editor.setSelectedBufferRange([startPoint, cursorPosition])
       @insert(snippet, editor)
     true


Add transferEditSessionAtIndex to Editor

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -89,7 +89,7 @@
     draggedTab.remove()
     draggedTab.insertAfter(droppedNearTab)
     currentDraggedTabIndex = draggedTab.index()
-    toEditor.editSessions.splice(currentDraggedTabIndex, 0, fromEditor.editSessions.splice(previousDraggedTabIndex, 1)[0])
+    fromEditor.transferEditSessionAtIndex(previousDraggedTabIndex, currentDraggedTabIndex, toEditor)

     if !fromPane.find('.tab').length
       fromPane.view().remove()


Kill dead tab view code

Kill dead tab view code

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -12,15 +12,6 @@
   initialize: (@item, @pane) ->
     @item.on? 'title-changed', => @updateTitle()
     @updateTitle()
-#     @buffer = @editSession.buffer
-#     @subscribe @buffer, 'path-changed', => @updateFileName()
-#     @subscribe @buffer, 'contents-modified', => @updateModifiedStatus()
-#     @subscribe @buffer, 'saved', => @updateModifiedStatus()
-#     @subscribe @buffer, 'git-status-changed', => @updateModifiedStatus()
-#     @subscribe @editor, 'editor:edit-session-added', => @updateFileName()
-#     @subscribe @editor, 'editor:edit-session-removed', => @updateFileName()
-#     @updateFileName()
-#     @updateModifiedStatus()

   updateTitle: ->
     return if @updatingTitle


Fix #185

Fix #185

--- lib/main.coffee
+++ lib/main.coffee
@@ -813,7 +813,7 @@
   mapMessageFilenames: (messages, filenameMap, rootPath) ->
     for message in messages
       server = message.filename
-      local = filenameMap[server]
+      local = filenameMap[server] || filenameMap[server.replace('.cpp', '.ino')]
       if local
         local = path.resolve(rootPath, local)
         message.serverFilename = server


Patch #24

Patch #24

--- lib/main.coffee
+++ lib/main.coffee
@@ -119,7 +119,8 @@
           grammar.fileTypes.push 'pros'
           process.nextTick ->
             for e in atom.workspace.getTextEditors()
-              if path.extname(e.getPath()) == '.pros'
+              atom.workspace.getActiveTextEditor()
+              if Boolean(e.getPath()) and path.extname(e.getPath()) == '.pros'
                 e.setGrammar grammar

   deactivate: ->


PlayerView.spec: render the view

PlayerView.spec: render the view

--- coffee/test/views/player_view.spec.coffee
+++ coffee/test/views/player_view.spec.coffee
@@ -6,8 +6,37 @@
   PlayerView = acorn.player.PlayerView
   derives = athena.lib.util.derives

+  # model for PlayerView contruction
+  model =
+    shellModel: new Backbone.Model
+    acornModel: new Backbone.Model
+      thumbnail: acorn.config.img.acorn
+      type: 'image'
+
+  # emulate shell, object with a ContentView property
+  shell = ContentView: athena.lib.View
+  model.shellModel.shell = shell
+
+  # options for ContentView contruction
+  options = model: model
+
+
   it 'should be part of acorn.player', ->
     expect(acorn.player.PlayerView).toBeDefined()

   it 'should derive from athena.lib.ContainerView', ->
     expect(derives PlayerView, athena.lib.ContainerView).toBe true
+
+  it 'should look good', ->
+    # setup DOM
+    acorn.util.appendCss()
+    $player = $('<div>').addClass('acorn-player').appendTo('body')
+
+    # add a PlayerView into the DOM to see how it looks.
+    view = new PlayerView model: model
+    view.$el.width 600
+    view.$el.height 400
+    view.render()
+    $player.append view.el
+
+    view.eventhub.trigger 'show:splash'


console is not available in webworkers

console is not available in webworkers

--- coffeescript/004-webworkers/csmain.coffee
+++ coffeescript/004-webworkers/csmain.coffee
@@ -39,8 +39,6 @@
         worker.onmessage = (event) ->
             data = event.data;
             $('#result').html(data);
-        worker.onerror = (args...)->
-            console.error('WORKER ERROR', args);
         console.info('start of calling from page');
         worker.postMessage(ps)
         console.info('end of calling from page');


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -33,7 +33,7 @@
     velocity: Point(0, 0)

   self.bind "update", ->
-    I
+    I.velocity.x =
     if keydown.left
       I.x -= I.speed


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -34,22 +34,7 @@
     velocity: Point(0, 0)

   self.bind "update", ->
-    I.velocity.x = 0
-    I.velocity.y = 0
-
-    if keydown.left
-      I.velocity.x = -1
-
-    if keydown.right
-      I.velocity.x = 1
-
-    if keydown.up
-      I.velocity.y = -1
-
-    if keydown.down
-      I.velocity.y = 1
-
-    I.velocity = I.velocity.scale(I.speed)
+    self.movement()

   movement: ->
     I.velocity.x = 0


Nginx: added www.kodingen.com redirection to koding.com

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -233,9 +233,16 @@
       # just a random port
       listen #{if environment is "dev" then 8091 else 81};

+      # why we have 2 different if checks? because www redirector block catches
+      # all the requests, we should be more precise with the host

       # redirect http://kodingen.com to https://koding.com
       if ($host = "kodingen.com") {
+        return 301 https://koding.com;
+      }
+
+      # redirect http://www.kodingen.com to https://koding.com
+      if ($host = "www.kodingen.com") {
         return 301 https://koding.com;
       }


add wiki link to help menu

add wiki link to help menu

--- lib/menu.coffee
+++ lib/menu.coffee
@@ -76,6 +76,11 @@
           label: 'About Registers'
           click: ->
             BrowserWindow.getFocusedWindow().send('menu', 'about-view')
+        },
+        {
+          label: 'Wiki'
+          click: ->
+            require('shell').openExternal("https://github.com/Ed-ITSolutions/Registers/wiki")
         }
       ]
     }
@@ -154,6 +159,11 @@
           label: 'About Registers'
           click: ->
             BrowserWindow.getFocusedWindow().send('menu', 'about-view')
+        },
+        {
+          label: 'Wiki'
+          click: ->
+            require('shell').openExternal("https://github.com/Ed-ITSolutions/Registers/wiki")
         }
       ]
     }


Simplify 8975229fd637ca1a91d7c52beb5969fe2b5e4d27.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -13,7 +13,6 @@
 keyPort = null
 isEnabledForUrl = true
 isIncognitoMode = chrome.extension.inIncognitoContext
-isDomReady = false
 passKeys = null
 keyQueue = null
 # The user's operating system.
@@ -254,7 +253,6 @@
 # Initialization tasks that must wait for the document to be ready.
 #
 initializeOnDomReady = ->
-  isDomReady = true
   # Tell the background page we're in the dom ready state.
   chrome.runtime.connect({ name: "domReady" })
   CursorHider.init()
@@ -1157,7 +1155,10 @@
     else
       HUD._tweenId = Tween.fade HUD.displayElement(), 0, 150, -> HUD.hide true, updateIndicator

-  isReady: -> document.body != null and isDomReady
+  isReady: do ->
+    ready = false
+    DomUtils.documentReady -> ready = true
+    -> ready and document.body != null

   # A preference which can be toggled in the Options page. */
   enabled: -> !settings.get("hideHud")


Added value method to get input value

Added value method to get input value

--- source/micro.dom.coffee
+++ source/micro.dom.coffee
@@ -33,6 +33,11 @@
     this.each ->
       this.innerHTML = value

+  value: (value) ->
+    return this[0].value if value is undefined
+    this.each ->
+      this.value = value
+
   append: (el) ->
     this.each ->
       this.appendChild el


better error messages when wakatime cli exits with non-zero return value

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -289,11 +289,18 @@
         if lineno?
           args.push('--lineno')
           args.push(lineno)
-        process.execFile(python, args, (error, stdout, stderr) ->
+        proc = process.execFile(python, args, (error, stdout, stderr) ->
           if error?
-            console.warn error
-          # else
-          #     console.log(args)
+            if stderr? and stderr != ''
+              console.warn stderr
+            if stdout? and stdout != ''
+              console.warn stdout
+            if proc.exitCode == 102
+              console.warn 'Warning: api error (102); Check your ~/.wakatime.log file for more details.'
+            else if proc.exitCode == 103
+              console.warn 'Warning: config parsing error (103); Check your ~/.wakatime.log file for more details.'
+            else
+              console.warn error
         )
         lastHeartbeat = time
         lastFile = file.path


Remove empty callback

Remove empty callback

--- core/app/backbone/views/sub_comments/react_sub_comments.coffee
+++ core/app/backbone/views/sub_comments/react_sub_comments.coffee
@@ -13,7 +13,6 @@

     @props.addToCollection.add(model)
     model.save {},
-      success: =>
       error: =>
         @props.addToCollection.remove(model)
         FactlinkApp.NotificationCenter.error "Your comment '#{model.get('content')}' could not be posted, please try again."


save the session key string, rather than just the id

--- services/web/app/coffee/Features/User/UserSessionsManager.coffee
+++ services/web/app/coffee/Features/User/UserSessionsManager.coffee
@@ -6,13 +6,17 @@
 module.exports = UserSessionsManager =

 	_sessionSetKey: (user) ->
-		console.log ">>", user
 		return "UserSessions:#{user._id}"
+
+	# mimic the key used by the express sessions
+	_sessionKey: (sessionId) ->
+		return "sess:#{sessionId}"

 	onLogin: (user, sessionId, callback=(err)-> ) ->
 		logger.log {user_id: user._id, sessionId}, "onLogin handler"
 		sessionSetKey = UserSessionsManager._sessionSetKey(user)
-		rclient.sadd sessionSetKey, sessionId, (err, response) ->
+		value = UserSessionsManager._sessionKey sessionId
+		rclient.sadd sessionSetKey, value, (err, response) ->
 			if err
 				logger.err {err, user_id: user._id, sessionId}, "error while adding session key to UserSessions set"
 				return callback(err)
@@ -24,7 +28,8 @@
 			logger.log {sessionId}, "no user, for some reason"
 			return callback()
 		sessionSetKey = UserSessionsManager._sessionSetKey(user)
-		rclient.srem sessionSetKey, sessionId, (err, response) ->
+		value = UserSessionsManager._sessionKey sessionId
+		rclient.srem sessionSetKey, value, (err, response) ->
 			if err
 				logger.err {err, user_id: user._id, sessionId}, "error while removing session key from UserSessions set"
 				return callback(err)


capture all pdfng error callbacks with raven

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -36,6 +36,7 @@
 				progressCallback: (progress) ->
 					$scope.$emit 'progress', progress
 				errorCallback: (error) ->
+					Raven.captureMessage?('pdfng error ' + error)
 					$scope.$emit 'pdf:error', error
 			})


REmoved preventSCrollPropagation on .modal-body. Breaks scroll in client.

--- core/app/backbone/modules/modal.coffee
+++ core/app/backbone/modules/modal.coffee
@@ -28,7 +28,6 @@
       @modalRegion.show @options.content_view
       @$('.modal').show()
       @$('.transparent-layer').show()
-      @$('.modal-body').preventScrollPropagation()

     stopPropagation: (e) ->
       e.stopPropagation()


Add displayName

Add displayName

--- core/app/backbone/views/react_fact.coffee
+++ core/app/backbone/views/react_fact.coffee
@@ -1,4 +1,5 @@
 window.ReactFact = React.createBackboneClass
+  displayName: 'ReactFact'
   render: ->
     fact_url_host = @model().factUrlHost()
     _div ['annotation'],


Add line splitter at mission's load csv

Add line splitter at mission's load csv

--- view/coffee/creator/create_mission.coffee
+++ view/coffee/creator/create_mission.coffee
@@ -45,7 +45,8 @@
           @startStation = json.start.name
           @stations = json.stations.map (s) -> {name: s.name}
       loadCSV: ->
-        @stations = _.flatMap @csvStations.split(','), (raw) ->
+        elems = _.flatMap @csvStations.split(/\r\n|\r|\n/), (xs) -> xs.split(',')
+        @stations = _.flatMap elems, (raw) ->
           x = raw.trim()
           if x then [{name: x}] else []
     mounted: ->


green radius

green radius

--- public/js/map.coffee
+++ public/js/map.coffee
@@ -39,8 +39,8 @@
 	displayAd = (ad, color = 'blue') ->
 		marker = L.marker(ad.location, {icon: icons.color(color)}).bindPopup(ad.text)
 		circle = L.circle ad.location, ad.radius,
-			color: 'lightblue',
-			fillColor: 'lightblue',
+			color: "light#{color}",
+			fillColor: "light#{color}",
 			fillOpacity: 0.5

 		# circle.bindPopup "I am a circle."


add test to cover relative path linked resources after redirect

Conflicts:
	test/history_test.coffee

--- test/history_test.coffee
+++ test/history_test.coffee
@@ -26,6 +26,15 @@

     brains.get "/history/referer2", (req, res)->
       res.send "<title>#{req.headers["referer"]}</title>"
+
+    brains.get "/history/login", (req, res)->
+      res.redirect "http://localhost:3003/history/site"
+
+    brains.get "/history/site", (req, res)->
+      res.send """"""
+
+    brains.get "/assets/foo.js", (req, res)->
+      res.send """window.host = "#{req.headers["host"]}"; """

     browser = new Browser()
     brains.ready done
@@ -375,5 +384,13 @@
       assert.equal browser.location.hash, "#with-hash"

+  describe "Linked Asset after redirect", ->
+    before (done)->
+      # We use host.localhost vs localhost as different "hosts"
+      browser.visit "http://host.localhost:3003/history/login", done
+    it 'should load the asset relative to the destination url', ->
+      assert.equal browser.evaluate('window.host'), "localhost:3003"
+
+
   after ->
     browser.destroy()


Responsive fixes

Responsive fixes

--- examples/horizontal-login/coffeescript/horizontal-login.coffee
+++ examples/horizontal-login/coffeescript/horizontal-login.coffee
@@ -13,7 +13,7 @@

   form: (f) ->
     div className: "row",
-      h2 {}, "Please sign in"
+      h2 {className: "col-xs-12"}, "Please sign in"
       f.errors()
       f.input "account",
         autoFocus: true
@@ -28,8 +28,10 @@
         block: true

   render: ->
+    containerClasses =
+      "col-lg-4 col-lg-offset-4 col-md-6 col-md-offset-3 col-xs-12"
     div className: "row",
-      div className: "col-xs-4 col-xs-offset-4",
+      div className: containerClasses,
         frig data: {}, layout: "horizontal", align: "right", @form

 window.addEventListener "load", ->


Fix bug

Fix bug

--- battle-detail.coffee
+++ battle-detail.coffee
@@ -1,10 +1,5 @@
 require 'coffee-react/register'
 require "#{ROOT}/views/env"
-
-try
-	require 'poi-plugin-translator'
-catch error
-  console.error error

 {join} = require 'path-extra'


Reword xy/abilities description.

Reword xy/abilities description.

--- test/xy/abilities.coffee
+++ test/xy/abilities.coffee
@@ -11,7 +11,7 @@

 require '../helpers'

-describe "BW Abilities:", ->
+describe "XY Abilities:", ->
   testWeatherAbility = (name, weather) ->
     describe name, ->
       it "causes #{weather} that ends after 5 turns", ->


Removed not longer needed extra message.

--- app/assets/javascripts/app/controllers/reset_password.js.coffee
+++ app/assets/javascripts/app/controllers/reset_password.js.coffee
@@ -140,7 +140,7 @@
         App.Event.trigger 'notify:removeall'
         @notify
           type: 'success',
-          msg: 'Password reset successfull.'
+          msg: 'Woo hoo! Your password has been changed!'

         # redirect to #
         @navigate '#'
@@ -154,13 +154,6 @@
           msg: 'Something went wrong. Please contact your administrator.'
     )

-
-
-    @html App.view('generic/hero_message')(
-      head:    'Woo hoo! Your password has been changed!'
-      message: 'Please try to login!'
-    )
-
   render_changed_failed: =>
     @html App.view('generic/hero_message')(
       head:    'Failed!'


implement __setState

implement __setState

--- app/assets/javascripts/uploadcare/widget/tabs/camera-tab.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/camera-tab.coffee
@@ -50,6 +50,13 @@
       isLocalhost = document.location.hostname == 'localhost'
       return !! @getUserMedia and Uint8Array and (isSecure or isLocalhost)

+    __setState: (newState) =>
+      oldStates = ['', 'ready', 'requested', 'denied', 'not-founded',
+                   'recording'].join(' uploadcare-dialog-camera-')
+      @container
+          .removeClass(oldStates)
+          .addClass("uploadcare-dialog-camera-#{newState}")
+
     __requestCamera: =>
       @__loaded = true
       @getUserMedia.call(navigator,
@@ -63,10 +70,7 @@
             {minWidth: 1920},
           ]
       , (stream) =>
-        @container
-          .removeClass('uploadcare-dialog-camera-requested')
-          .removeClass('uploadcare-dialog-camera-denied')
-          .addClass('uploadcare-dialog-camera-ready')
+        @__setState('ready')

         @__stream = stream
         if @URL
@@ -78,18 +82,16 @@

       , (error) =>
         if error == "NO_DEVICES_FOUND" or error.name == 'DevicesNotFoundError'
-          @container.addClass('uploadcare-dialog-camera-not-founded')
+          @__setState('not-founded')
         else
-          @container.addClass('uploadcare-dialog-camera-denied')
+          @__setState('denied')
         @__loaded = false
       )

     __revoke: =>
+      @__setState('requested')
+
       @__loaded = false
-      @container
-          .removeClass('uploadcare-dialog-camera-denied')
-          .removeClass('uploadcare-dialog-camera-ready')
-          .addClass('uploadcare-dialog-camera-requested')
       if not @__stream
         return
       if @URL
@@ -124,9 +126,7 @@
         @dialogApi.switchTab('preview')

     __startRecording: =>
-      @container
-          .removeClass('uploadcare-dialog-camera-ready')
-          .addClass('uploadcare-dialog-camera-recording')
+      @__setState('recording')

       @__chunks = []
       @__recorder = new @MediaRecorder(@__stream)
@@ -135,9 +135,7 @@
         @__chunks.push(e.data)

     __stopRecording: =>
-      @container
-          .removeClass('uploadcare-dialog-camera-recording')
-          .addClass('uploadcare-dialog-camera-ready')
+      @__setState('ready')

       @__recorder.onstop = =>
         blob = new Blob(@__chunks, {'type': 'video/webm'})
@@ -148,9 +146,7 @@
       @__recorder.stop()

     __cancelRecording: =>
-      @container
-          .removeClass('uploadcare-dialog-camera-recording')
-          .addClass('uploadcare-dialog-camera-ready')
+      @__setState('ready')

       @__recorder.stop()
       @__chunks = []


Add debug output

Add debug output

--- src/managers/employer_size_manager.coffee
+++ src/managers/employer_size_manager.coffee
@@ -29,6 +29,7 @@
           @determineEmployerSizeFromReports employer, transaction, (err, determinedSize) =>
             return rollbackAndCallback(err) if err?

+            debug "Determined Size: #{determinedSize}"
             employer.size = determinedSize

             onSuccess = (employer) ->