client.js: GM-specific methods for up/downloa

--- src-client/index.coffee
+++ src-client/index.coffee
@@ -1,6 +1,5 @@
 $        = require 'jquery'
-_        = require 'lodash'
-Async    = require 'async'
+_        = require 'lodash' Async    = require 'async'
 Request  = require 'superagent'

 if typeof window isnt 'undefined'
@@ -11,6 +10,15 @@
 	exp = module.exports

 _requireArg = (arg) -> "Must provide '#{arg}' argument"
+
+_parseHeaders = (str) ->
+	ret = {}
+	for line in str.split(/\n/)
+		colonIndex = line.indexOf(':')
+		k = line.substr(0, colonIndex).trim().toLowerCase()
+		v = line.substr(colonIndex + 1).trim()
+		ret[k] = v
+	return ret

 class Bootstrap

@@ -29,7 +37,7 @@
 		return inner

 	setProgressBar : (uri, percent) -> @getProgressBar(uri).css('width', percent + "%")
-	getProgressBar : (uri) -> $(".progress[data-uri='#{uri}'] .progress-bar")
+	getProgressBar : (uri) -> $(".progress[data-uri='#{uri}'] > .progress-bar")

 class Utils

@@ -149,6 +157,59 @@
 						return opts.onComplete execution
 					return opts.onProgress execution
 		pollId = setTimeout poll, @pollInterval
+
+	GM_downloadBlob: (uri, opts) ->
+		onError    = opts.onError    or @defaultHandler.error
+		onStarted  = opts.onStarted  or @defaultHandler.started
+		onSuccess  = opts.onSuccess  or @defaultHandler.success
+		onProgress = opts.onProgress or @defaultHandler.progress
+		GM_xmlhttpRequest
+			url: uri
+			method: 'GET'
+			onprogress: (e) =>
+				onProgress e
+			onload: (e) ->
+				console.log 'Finished downloading PDF'
+				responseText = e.responseText
+				responseLength = responseText.length
+				console.log "It is #{responseLength} bytes long"
+				console.log "Parsing headers"
+				headers = _parseHeaders e.responseHeaders
+				if e.status == 200 and headers['content-type'] is 'application/pdf'
+					console.log 'PDF Download successful'
+					blob = new Blob([e.response], {type: headers['content-type']})
+					console.log 'Created Blob from PDF'
+					success = true
+					onSuccess blob
+				else
+					console.error headers
+					onError e, headers
+
+	uploadBlob: (blob, opts) ->
+		unless blob instanceof Blob
+			return @defaultHandler.error("Argument must be a Blob")
+		onError    = opts.onError    or @defaultHandler.error
+		onStarted  = opts.onStarted  or @defaultHandler.started
+		onSuccess  = opts.onSuccess  or @defaultHandler.success
+		onProgress = opts.onProgress or @defaultHandler.progress
+		tags       = opts.tags or []
+		formData = new FormData()
+		formData.append 'file', blob
+		formData.append 'tags', tags.join(',')
+		formData.append 'mediaType', blob.type
+		Request
+			.post(@apiUrl 'upload')
+			.set 'accept', 'application/json'
+			.send(formData)
+			.on 'progress', (ev) ->
+				onProgress ev
+			.end (err, res) ->
+				if err
+					console.error error
+					return onError {err}
+				uri = res.headers.location
+				console.log 'finished'
+				return onSuccess uri

 	uploadFiles: (opts = {}) ->
 		if typeof opts isnt 'object'
@@ -208,3 +269,4 @@
 exp.Bootstrap = new Bootstrap()
 exp.InfolinkClient = InfolinkClient
 exp.Utils = Utils
+exp.jQuery = $


update analyzer for word delimiting, html handling

--- movie-index/indexer.coffee
+++ movie-index/indexer.coffee
@@ -7,8 +7,7 @@

 client = new elasticsearch.Client({
-  # host:"localhost:9200"
-  host:args[0]
+  host:args[0] or "localhost:9200"
 })

@@ -83,6 +82,46 @@
   def

 # console.log processedMovies
+
+settings = {
+  "analysis": {
+    "char_filter": {
+       "replace": {
+        "type": "mapping",
+        "mappings": [
+          "&=> and "
+        ]
+      }
+    },
+    "filter": {
+      "word_delimiter" : {
+        "type" : "word_delimiter",
+        "split_on_numerics" : false,
+        "split_on_case_change" : true,
+        "generate_word_parts" : true,
+        "generate_number_parts" : true,
+        "catenate_all" : true,
+        "preserve_original":true,
+        "catenate_numbers":true
+      }
+    },
+    "analyzer": {
+      "default": {
+        "type": "custom",
+        "char_filter": [
+          "html_strip",
+          "replace"
+        ],
+        "tokenizer": "whitespace",
+        "filter": [
+            "lowercase",
+            "word_delimiter"
+        ]
+      }
+    }
+  }
+}
+
 mapping = {
   index:"movies"
   type:"movie"
@@ -115,8 +154,9 @@
   commands.push(m)
 client.indices.delete {index:"movies"}, (err, res)->
   console.log(err, res)
-  client.indices.create {index:"movies"}, (err, res)->
+  client.indices.create {index:"movies", body:{settings:settings}}, (err, res)->
     console.log(err, res)
+
     client.indices.putMapping mapping, (err, res)->
       console.log(err, res)
       client.bulk {body:commands}, (err, res)->


bug fix

bug fix

--- src/buttons/button.coffee
+++ src/buttons/button.coffee
@@ -78,7 +78,7 @@
       false

     @editor.on 'blur', =>
-      return unless @body.is(':visible') and @body.is('[contenteditable]')
+      return unless @editor.body.is(':visible') and @editor.body.is('[contenteditable]')
       @setActive false
       @setDisabled false


Blur if pressing escape in input element

--- _includes/coffeescript/main.coffee
+++ _includes/coffeescript/main.coffee
@@ -117,5 +117,9 @@
           if next?
             @currentChapter().current = next
             @arrange()
+    else
+      switch event.which
+        when 27 # escape
+          $(document.activeElement).blur()

 window.Presentation = Presentation


Fix some tests

Fix some tests

--- test/unit/controllers/projectsControllerSpec.coffee
+++ test/unit/controllers/projectsControllerSpec.coffee
@@ -136,6 +136,7 @@
     ]
 }

+
 describe "projectsController", ->
     APIURL = "http://localhost:8000/api/v1"

@@ -153,9 +154,8 @@
                 $scope: scope
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
-            httpBackend.whenGET(APIURL+"/projects").respond(200, [
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/projects").respond(200, [
                 {name: "test proj 1", slug: "test-proj-1"},
                 {name: "test proj 2", slug: "test-proj-2"}
             ])
@@ -180,6 +180,7 @@
             expect(ctrl.scope.projects[0].slug).to.be.equal("test-proj-1")
             expect(ctrl.scope.projects[1].slug).to.be.equal("test-proj-2")

+
     describe "ShowProjectsController", ->
         httpBackend = null
         scope = null
@@ -191,8 +192,7 @@
                 $scope: scope
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -201,7 +201,7 @@
             httpBackend.verifyNoOutstandingRequest()

         it "should have the project list when call showProjects", ->
-            httpBackend.expectGET(APIURL+"/projects").respond(200, [{name: "test proj 1", slug: "test-proj-1"},
+            httpBackend.expectGET("#{APIURL}/projects").respond(200, [{name: "test proj 1", slug: "test-proj-1"},
                                                                     {name: "test proj 2", slug: "test-proj-2"}])
             ctrl.scope.showProjects()
             httpBackend.flush()
@@ -211,11 +211,12 @@
             expect(ctrl.scope.myProjects[1].slug).to.be.equal("test-proj-2")

         it "should have an empty project list when call showProjects and ther server have no projects", ->
-            httpBackend.expectGET(APIURL+"/projects").respond(400, [])
+            httpBackend.expectGET("#{APIURL}/projects").respond(400, [])
             ctrl.scope.showProjects()
             httpBackend.flush()

             expect(ctrl.scope.myProjects).to.be.lengthOf(0)
+

     describe "ProjecAdminMainController", ->
         httpBackend = null
@@ -236,8 +237,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.whenGET("#{APIURL}/resolver?project=test").respond(200, {project: 1})
             httpBackend.whenGET("#{APIURL}/projects/1?").respond(200, FIXTURES.project)
             httpBackend.whenPATCH("#{APIURL}/projects/1", {name:"New name"}).respond(
@@ -307,8 +307,7 @@
                 $routeParams: routeParams,
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.whenGET("#{APIURL}/resolver?project=test").respond(200, {project: 1})
             httpBackend.whenGET("#{APIURL}/projects/1?").respond(200, FIXTURES.project)
             httpBackend.flush()
@@ -361,8 +360,7 @@
                 $confirm: confirmMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.whenGET("#{APIURL}/resolver?project=test").respond(200, {project: 1})
             httpBackend.whenGET("#{APIURL}/projects/1?").respond(200, FIXTURES.project)
             httpBackend.flush()
@@ -422,8 +420,7 @@
                 $routeParams: routeParams,
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.whenGET("#{APIURL}/resolver?project=test").respond(200, {project: 1})
             httpBackend.whenGET("#{APIURL}/projects/1?").respond(200, FIXTURES.project)
             httpBackend.whenGET("#{APIURL}/users?project=1").respond(200, FIXTURES.users)
@@ -489,8 +486,7 @@
                 $routeParams: routeParams,
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.whenGET("#{APIURL}/resolver?project=test").respond(200, {project: 1})
             httpBackend.whenGET("#{APIURL}/projects/1?").respond(200, FIXTURES.project)
             httpBackend.whenGET("#{APIURL}/users?project=1").respond(200, FIXTURES.users)
@@ -525,7 +521,7 @@

         #TODO: Finish me

-    # FIRST
+
     describe "UserStoryStatusesAdminController", ->
         httpBackend = null
         scope = null
@@ -543,8 +539,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -567,9 +562,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/userstory-statuses?", {
+            httpBackend.expectPOST("#{APIURL}/userstory-statuses?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/userstory-statuses?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/userstory-statuses?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -586,7 +581,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/userstory-statuses?",
+            httpBackend.expectPOST("#{APIURL}/userstory-statuses?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -604,13 +599,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/userstory-statuses", o))
-
-            httpBackend.expectPOST(APIURL+"/userstory-statuses/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/userstory-statuses", o))
+
+            httpBackend.expectPOST("#{APIURL}/userstory-statuses/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_userstory_statuses: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "PointsAdminController", ->
@@ -630,8 +628,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -654,9 +651,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/points?", {
+            httpBackend.expectPOST("#{APIURL}/points?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/points?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/points?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -673,7 +670,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/points?",
+            httpBackend.expectPOST("#{APIURL}/points?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -691,15 +688,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/points", o))
-
-            httpBackend.expectPOST(APIURL+"/points/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/points", o))
+
+            httpBackend.expectPOST("#{APIURL}/points/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_points: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "TaskStatusesAdminController", ->
@@ -719,8 +717,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -743,9 +740,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/task-statuses?", {
+            httpBackend.expectPOST("#{APIURL}/task-statuses?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/task-statuses?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/task-statuses?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -762,7 +759,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/task-statuses?",
+            httpBackend.expectPOST("#{APIURL}/task-statuses?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -780,15 +777,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/task-statuses", o))
-
-            httpBackend.expectPOST(APIURL+"/task-statuses/bulk_update_order",
+            modelList= _.map(itemList, (o) -> $model.make_model("choices/task-statuses", o))
+
+            httpBackend.expectPOST("#{APIURL}/task-statuses/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_task_statuses: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "IssueStatusesAdminController", ->
@@ -808,8 +806,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -832,9 +829,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/issue-statuses?", {
+            httpBackend.expectPOST("#{APIURL}/issue-statuses?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/issue-statuses?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/issue-statuses?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -851,7 +848,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/issue-statuses?",
+            httpBackend.expectPOST("#{APIURL}/issue-statuses?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -869,15 +866,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/issue-statuses", o))
-
-            httpBackend.expectPOST(APIURL+"/issue-statuses/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/issue-statuses", o))
+
+            httpBackend.expectPOST("#{APIURL}/issue-statuses/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_issue_statuses: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "IssueTypesAdminController", ->
@@ -897,8 +895,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -921,9 +918,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/issue-types?", {
+            httpBackend.expectPOST("#{APIURL}/issue-types?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/issue-types?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/issue-types?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -940,7 +937,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/issue-types?",
+            httpBackend.expectPOST("#{APIURL}/issue-types?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -958,15 +955,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/issue-types", o))
-
-            httpBackend.expectPOST(APIURL+"/issue-types/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/issue-types", o))
+
+            httpBackend.expectPOST("#{APIURL}/issue-types/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_issue_types: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "PrioritiesAdminController", ->
@@ -986,8 +984,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -1010,9 +1007,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/priorities?", {
+            httpBackend.expectPOST("#{APIURL}/priorities?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/priorities?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/priorities?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -1029,7 +1026,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/priorities?",
+            httpBackend.expectPOST("#{APIURL}/priorities?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -1047,15 +1044,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/priorities", o))
-
-            httpBackend.expectPOST(APIURL+"/priorities/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/priorities", o))
+
+            httpBackend.expectPOST("#{APIURL}/priorities/bulk_update_order",
                     {project: FIXTURES.project.id, bulk_priorities: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "SeveritiesAdminController", ->
@@ -1075,8 +1073,7 @@
                 $gmFlash: gmFlashMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -1099,9 +1096,9 @@

         it "should create on success", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/severities?", {
+            httpBackend.expectPOST("#{APIURL}/severities?", {
                          project: FIXTURES.project.id, name: "test"}).respond(202, {test: "test"})
-            httpBackend.expectGET(APIURL+"/severities?project=1").respond(200, [{
+            httpBackend.expectGET("#{APIURL}/severities?project=1").respond(200, [{
                                                project: FIXTURES.project.id, name: "test"}])

             ctrl.openForm()
@@ -1118,7 +1115,7 @@

         it "should create on error", ->
             sinon.spy(ctrl.gmFlash, "error")
-            httpBackend.expectPOST(APIURL+"/severities?",
+            httpBackend.expectPOST("#{APIURL}/severities?",
                     {project: FIXTURES.project.id, name: "error test"}).respond(400, {_error_message: "error"})

             ctrl.openForm()
@@ -1136,15 +1133,16 @@

         it "should allow to save resorted", inject ($model) ->
             itemList = [{id: 1, order: 2}, {id: 2, order: 1}, {id: 3, order: 3}]
-            ctrl.scope[ctrl.instanceModel] = _.map(itemList, (o) ->
-                $model.make_model("choices/severities", o))
-
-            httpBackend.expectPOST(APIURL+"/severities/bulk_update_order",
+            modelList = _.map(itemList, (o) -> $model.make_model("choices/severities", o))
+
+            httpBackend.expectPOST("#{APIURL}/severities/bulk_update_order",
                 {project: FIXTURES.project.id, bulk_severities: [[1,0],[2,1],[3,2]]}).respond(200)
-            promise = ctrl.resort(ctrl.instanceModel)
-            httpBackend.flush()
-
-            promise.should.be.fulfilled
+
+            promise = ctrl.sortableOnUpdate(modelList)
+            httpBackend.flush()
+
+            promise.should.be.fulfilled.then =>
+                expect(ctrl.scope[ctrl.model]).to.be.equal(modelList)

     describe "ChoiceController", ->
@@ -1171,8 +1169,7 @@
                 $confirm: confirmMock
             })
             httpBackend = $httpBackend
-            httpBackend.whenGET(APIURL+"/locales/en/app.json").respond(200, {})
-            httpBackend.whenGET(APIURL+"/sites").respond(200, {test: "test"})
+            httpBackend.whenGET("#{APIURL}/sites").respond(200, {test: "test"})
             httpBackend.flush()
         ))

@@ -1181,7 +1178,7 @@
             httpBackend.verifyNoOutstandingRequest()

         it "should allow to open and close form", ->
-            httpBackend.expectGET(APIURL+"/priorities/#{object.id}").respond(200, {id: 1, name: "Test status"})
+            httpBackend.expectGET("#{APIURL}/priorities/#{object.id}").respond(200, {id: 1, name: "Test status"})

             expect(ctrl.scope.formOpened).to.be.false
             expect(object.name).to.be.equal("Test status")
@@ -1199,8 +1196,8 @@
             expect(object.name).to.be.equal("Test status")

         it "should allow to update an object", ->
-            httpBackend.expectPATCH(APIURL+"/priorities/#{object.id}", {name: "New status"}).respond(202, "Ok")
-            httpBackend.expectGET(APIURL+"/priorities/#{object.id}").respond(200, {id: 1, name: "New status"})
+            httpBackend.expectPATCH("#{APIURL}/priorities/#{object.id}", {name: "New status"}).respond(202, "Ok")
+            httpBackend.expectGET("#{APIURL}/priorities/#{object.id}").respond(200, {id: 1, name: "New status"})

             expect(ctrl.scope.formOpened).to.be.false
             expect(object.name).to.be.equal("Test status")


5299: Add trailing square brackets for array parameters (distributor and order_cycle)

--- app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee
+++ app/assets/javascripts/admin/orders/controllers/orders_controller.js.coffee
@@ -34,8 +34,8 @@
       'q[bill_address_lastname_start]': $scope['q']['bill_address_lastname_start'],
       # Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
       'q[completed_at_not_null]': $scope['q']['completed_at_not_null'] || null,
-      'q[distributor_id_in]': $scope['q']['distributor_id_in'],
-      'q[order_cycle_id_in]': $scope['q']['order_cycle_id_in'],
+      'q[distributor_id_in][]': $scope['q']['distributor_id_in'],
+      'q[order_cycle_id_in][]': $scope['q']['order_cycle_id_in'],
       'q[s]': $scope.sorting || 'completed_at desc',
       per_page: $scope.per_page,
       page: page


don't remove the init listener

don't remove the init listener

--- app/assets/javascripts/controllers/application_controller.js.coffee
+++ app/assets/javascripts/controllers/application_controller.js.coffee
@@ -17,7 +17,7 @@
             @pushUpdate(esData)

     Ember.$.ajax(params)
-  )
+  ).on('init')

   pushUpdate: (esData)->
     Ember.run =>


Workspace: Fixed floating pane launcher.

--- client/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
+++ client/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
@@ -8,12 +8,13 @@

     super options, data

-    @sessionKeys      = {}
-    @panel            = @getDelegate()
-    @workspace        = @panel.getDelegate()
-    @container        = new KDView cssClass: "workspace-floating-panes"
-    @keysRef          = @workspace.workspaceRef.child "floatingPaneKeys"
-    @isJoinedASession = @workspace.getOptions().joinedASession
+    @sessionKeys       = {}
+    @panel             = @getDelegate()
+    @workspace         = @panel.getDelegate()
+    @container         = new KDView cssClass: "workspace-floating-panes"
+    @keysRef           = @workspace.workspaceRef.child "floatingPaneKeys"
+    @isJoinedASession  = @workspace.getOptions().joinedASession
+    @lastActivePaneKey = null

     @panel.addSubView @container

@@ -45,11 +46,21 @@

   handleLaunch: (paneKey) ->
     pane = @[paneKey]
+    if @lastActivePaneKey is paneKey
+      @hidePane pane
+    else
+      lastActivePane = @getPaneByType @lastActivePaneKey
+      @hidePane lastActivePane if lastActivePane
+      @showPane pane, paneKey
+
+  hidePane: (pane) ->
+    pane.unsetClass "active"
+    @lastActivePaneKey = null
+
+  showPane: (pane, paneKey) ->
+    return @chat.dock.emit "click"  if paneKey is "chat"
     pane.setClass "active"
-
-    KD.getSingleton("windowController").addLayer pane
-    pane.on "ReceivedClickElsewhere", =>
-      pane.unsetClass "active"
+    @lastActivePaneKey = paneKey

   createChatPane: ->
     @container.addSubView @chat = new ChatPane


activity: contentdisplay fix prolly from a merge issue

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -252,7 +252,7 @@
       when "JDiscussion"   then @createDiscussionContentDisplay activity
       when "JBlogPost"     then @createBlogPostContentDisplay activity
       when "JTutorial"     then @createTutorialContentDisplay activity
-    @utils.defer -> callback contentDisplayController
+    @utils.defer -> callback controller

   showContentDisplay:(contentDisplay)->
     contentDisplayController = @getSingleton "contentDisplayController"


default-apps: openFile method should now the clicked context menu item for opening the file with selected app.

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -58,11 +58,12 @@
         @emit "file.opened", nodeData
         @setBlurState()

-  openFile:(nodeView, event)->
+  openFile:(nodeView, contextMenuItem)->

     return unless nodeView
     file = nodeView.getData()
-    KD.getSingleton("appManager").openFile file
+    app  = contextMenuItem?.getData().title or null
+    @getSingleton("appManager").openFile file, app

   previewFile:(nodeView, event)->

@@ -479,7 +480,7 @@
   cmUpload:       (nodeView, contextMenuItem)-> KD.getSingleton("appManager").notify()
   cmDownload:     (nodeView, contextMenuItem)-> KD.getSingleton("appManager").notify()
   cmGitHubClone:  (nodeView, contextMenuItem)-> KD.getSingleton("appManager").notify()
-  cmOpenFile:     (nodeView, contextMenuItem)-> @openFile nodeView
+  cmOpenFile:     (nodeView, contextMenuItem)-> @openFile nodeView, contextMenuItem
   cmPreviewFile:  (nodeView, contextMenuItem)-> @previewFile nodeView
   cmCompile:      (nodeView, contextMenuItem)-> @compileApp nodeView
   cmRunApp:       (nodeView, contextMenuItem)-> @runApp nodeView


Fix nodeType detection

Fix nodeType detection

--- src/client/style/observer.coffee
+++ src/client/style/observer.coffee
@@ -37,12 +37,15 @@
     false

 processDocumentMutations = (prefix, doc, mutations)->
-  styleSandbox.processDocument(prefix, doc) if hasStyleMutations(mutations)
+  if hasStyleMutations(mutations)
+    styleSandbox.processDocument(prefix, doc)

 # Collect all style mutations in the mutation batch, and add / remove them
 processNodeMutations = (prefix, mutations)->
   _.map mutations, (mutation)->
-    return styleSandbox.addStyle(prefix, mutation.target) if isStyleTag(mutation.target)
+    # Style mutations don't interest us for now. we just skip them
+    # return styleSandbox.addStyle(prefix, mutation.target) if isStyleTag(mutation.target)
+
     # Can't be a style and also have addedNodes...
     _.map mutation.addedNodes, (node)->
       styleSandbox.addStyle(prefix, style) for style in getStyles(node)
@@ -54,15 +57,15 @@

   observe : (target)->
     @observeDocument(target) if target.nodeType==9 # document
-    @observeNode(target)     if target.nodeType==3 # node
+    @observeNode(target)     if target.nodeType==1 # node

   process : (target)->
     @processDocument(target) if target.nodeType==9 # document
-    @processNode(target)     if target.nodeType==3 # node
+    @processNode(target)     if target.nodeType==1 # node

   processNode : (node)->
-    _.map node.querySelectorAll('style'), styleSandbox.processStyle.bind(@, @prefix)
+    _.map node.querySelectorAll('style'), styleSandbox.addStyle.bind(@, @prefix)

   observeNode: (node)->
     return unless node


Update bootstrap-tour.coffee

Update bootstrap-tour.coffee

--- src/coffee/bootstrap-tour.coffee
+++ src/coffee/bootstrap-tour.coffee
@@ -483,7 +483,6 @@
         animation: step.animation
         container: step.container
         template: step.template
-        selector: step.element
       )
       .popover 'show'


Adds deleteTeamMedium

Adds deleteTeamMedium

--- src/collections/teamMedia.coffee
+++ src/collections/teamMedia.coffee
@@ -34,6 +34,14 @@
     .pop().callback callback

+  exports.deleteTeamMedium = (teamMedium, callback) ->
+    unless teamMedium
+      throw new TSArgsError 'teamsnap.deleteTeamMedium',
+        '`teamMedium` must be provided'
+
+    @deleteItem teamMedium, callback
+
+
 exports.assignMediaToGroup = (teamMediumId, teamMediaGroupId, callback) ->
   unless teamMediumId and @isId teamMediumId
     throw new TSArgsError 'teamsnap.assignMediaToGroup', 'must provide a
@@ -64,7 +72,7 @@
     .pop().callback callback

-exports.rotateTeamMediumImage = (teamMediumId, rotateDirection) ->
+exports.rotateTeamMediumImage = (teamMediumId, rotateDirection, callback) ->
   unless teamMediumId and @isId teamMediumId
     throw new TSArgsError 'teamsnap.rotateTeamMediumImage', 'must provide a
     teamMediumId'
@@ -75,5 +83,5 @@
     teamMediumId: teamMediumId
     rotateDirection: rotateDirection

-  @collections.teamMedia.exec('rotateImageTeamMedium', params)
+  @collections.teamMedia.exec('rotateTeamMediumImage', params)
     .pop().callback callback


fake - commit

fake - commit

--- app/utils/connector-rfb.coffee
+++ app/utils/connector-rfb.coffee
@@ -36,3 +36,4 @@

 `export default ConnectorRFB`
+


fix a typo bug breaking HUD and help dialog

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -18,7 +18,8 @@
     [isComplete, callbacks] = [document.readyState == "complete", []]
     unless isComplete
       window.addEventListener "load", (onLoad = forTrusted (event) ->
-        return unless event.target == window
+        # The target is ensured to be on document. See https://w3c.github.io/uievents/#event-type-load
+        return unless event.target == document
         window.removeEventListener "load", onLoad, true
         isComplete = true
         callback() for callback in callbacks


Assign body element to `this`

Assign body element to `this`

--- lib/fonts-cjk.coffee
+++ lib/fonts-cjk.coffee
@@ -1,16 +1,16 @@
 module.exports =
   run: ->
-    body = document.body
+    @body = document.body

     triggerMeasurements = ->
       atom.workspace.increaseFontSize()
       atom.workspace.decreaseFontSize()

-    setBodyAttribute = (attr, value) ->
+    setBodyAttribute = (attr, value) =>
       unless value == '(Default)'
-        body.setAttribute(attr, value)
+        @body.setAttribute(attr, value)
       else
-        body.removeAttribute(attr)
+        @body.removeAttribute(attr)

     applyFont = ->
       editorFont = atom.config.get('fonts-cjk.editorFont')
@@ -44,10 +44,9 @@
     setTimeout(triggerMeasurements, 500)

   stop: ->
-    body = document.body
-    body.removeAttribute('data-fonts-cjk-editorFont')
-    body.removeAttribute('data-fonts-cjk-markdownPreviewFont')
-    body.removeAttribute('data-fonts-cjk-workspaceFont')
+    @body.removeAttribute('data-fonts-cjk-editorFont')
+    @body.removeAttribute('data-fonts-cjk-markdownPreviewFont')
+    @body.removeAttribute('data-fonts-cjk-workspaceFont')

     style = document.getElementById('fonts-cjk-style')
     style?.parentNode.removeChild(style)


fix: minor fix

fix: minor fix

--- app/utils/humanize-cvss.coffee
+++ app/utils/humanize-cvss.coffee
@@ -42,7 +42,7 @@
       'N': 'None'}}

   splitVector = cvssVector.replace("CVSS:3.0/", "").split "/"
-  for splittedVector in splitVector
+  for splittedValue in splitVector
     sv = splittedValue.split ":"
     key = sv[0]
     value = sv[1]


Temporary fix for home page static rendering / i18n interference bug

--- app/views/HomeView.coco.coffee
+++ app/views/HomeView.coco.coffee
@@ -192,5 +192,7 @@
     @cleanupModals()
     super()

-  mergeWithPrerendered: (el) ->
-    true
+  # 2021-06-08: currently causing issues with i18n interpolation, disabling for now
+  # TODO: understand cause, performance impact
+  #mergeWithPrerendered: (el) ->
+  #  true


String#extension

String#extension

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -20,6 +20,8 @@
   equals "README.md".extension(), "md"

   equals "jquery.min.js".extension(), "js"
+
+

 test "#humanize", ->
   equals "employee_salary".humanize(), "Employee salary"


See #1201. Crystal beautifier does not support any options

--- src/beautifiers/crystal.coffee
+++ src/beautifiers/crystal.coffee
@@ -10,7 +10,7 @@
   link: "http://crystal-lang.org"

   options: {
-    Crystal: true
+    Crystal: false
   }

   beautify: (text, language, options) ->


Remove obsolete code fragments from popup spec

--- spec_app/spec/javascripts/up/popup_spec.js.coffee
+++ spec_app/spec/javascripts/up/popup_spec.js.coffee
@@ -300,7 +300,7 @@
       it 'prefers to replace a selector within the popup', ->
         $outside = affix('.foo').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.foo')
+        up.popup.attach($link, target: '.foo')
         @respondWith("
old inside
")
         up.extract('.foo', "
new text
")
         expect($outside).toBeInDOM()
@@ -310,7 +310,7 @@
       it 'auto-closes the popup when a replacement from inside the popup affects a selector behind the popup', ->
         affix('.outside').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.inside')
+        up.popup.attach($link, target: '.inside')
         @respondWith("
old inside
")
         up.extract('.outside', "
new outside
", origin: $('.inside'))
         expect($('.outside')).toHaveText('new outside')
@@ -338,7 +338,7 @@
       it 'does not auto-close the popup when a replacement from inside the popup affects a selector inside the popup', ->
         affix('.outside').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.inside')
+        up.popup.attach($link, target: '.inside')
         @respondWith("
old inside
")
         up.extract('.inside', "
new inside
", origin: $('.inside'))
         expect($('.inside')).toHaveText('new inside')
@@ -347,7 +347,7 @@
       it 'does not auto-close the popup when a replacement from outside the popup affects a selector outside the popup', ->
         affix('.outside').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.inside')
+        up.popup.attach($link, target: '.inside')
         @respondWith("
old inside
")
         up.extract('.outside', "
new outside
", origin: $('.outside'))
         expect($('.outside')).toHaveText('new outside')
@@ -356,7 +356,7 @@
       it 'does not auto-close the popup when a replacement from outside the popup affects a selector inside the popup', ->
         affix('.outside').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.inside')
+        up.popup.attach($link, target: '.inside')
         @respondWith("
old inside
")
         up.extract('.inside', "
new inside
", origin: $('.outside'))
         expect($('.inside')).toHaveText('new inside')
@@ -370,7 +370,7 @@
       it 'closes a popup on mousedown (in case an [up-instant] link removes its parent and thus a click event never fires)', ->
         affix('.outside').text('old outside')
         $link = affix('.link')
-        up.popup.attach($link, href: '/path', target: '.inside')
+        up.popup.attach($link, target: '.inside')
         @respondWith("
inside
")
         Trigger.mousedown($('body'))
         expect($('.up-popup')).not.toExist()


window is a protected word

window is a protected word

--- src/browser/application.coffee
+++ src/browser/application.coffee
@@ -31,16 +31,15 @@
     {devMode, test, specDirectory, logFile} = options

     if test
-      window = @runSpecs({exitWhenDone: true, @resourcePath, specDirectory, devMode, logFile})
+      appWindow = @runSpecs({exitWhenDone: true, @resourcePath, specDirectory, devMode, logFile})
     else
-      window = new AppWindow(options)
+      appWindow = new AppWindow(options)
       @menu = new AppMenu(pkg: @pkgJson)

-
-      @menu.attachToWindow(window)
+      @menu.attachToWindow(appWindow)
       @handleMenuItems(@menu)

-    window.show()
+    appWindow.show()

   handleMenuItems: (menu) ->
     menu.on 'application:quit', -> app.quit()


Don’t openPath on window startup for spec windows

It causes specs to fail because the window loses focus somehow when
this gets run.

--- src/browser/atom-window.coffee
+++ src/browser/atom-window.coffee
@@ -58,7 +58,7 @@
     @browserWindow.loadUrl @getUrl(loadSettings)
     @browserWindow.focusOnWebView() if @isSpec

-    @openPath(pathToOpen, initialLine, initialColumn)
+    @openPath(pathToOpen, initialLine, initialColumn) unless @isSpecWindow()

   getUrl: (loadSettingsObj) ->
     # Ignore the windowState when passing loadSettings via URL, since it could


chore(web): put files from push to root

chore(web): put files from push to root

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -76,6 +76,11 @@
     .pipe gulp.dest './dist/'
     .pipe connect.reload()

+gulp.task 'push', ->
+  gulp.src ['./push/*']
+  .pipe gulp.dest './dist/'
+  .pipe connect.reload()
+
 gulp.task 'watch', ['server'], ->
   gulp.watch ['./app/**/*.coffee'], ['coffee']
   gulp.watch ['./styles/**/*.scss'], ['sass']
@@ -106,9 +111,9 @@
     root: ['./dist/', './']
     livereload: true

-gulp.task 'build', ['assets', 'browserify', 'sass', 'html', 'usemin']
+gulp.task 'build', ['assets', 'browserify', 'sass', 'html', 'usemin', 'push']

-gulp.task 'build:dev', ['assets', 'browserify:watchify', 'sass', 'html']
+gulp.task 'build:dev', ['assets', 'browserify:watchify', 'sass', 'html', 'push']

 gulp.task 'dev', ['build:dev', 'server', 'watch']


Implement changes recommended in the pull request.

--- repl.coffee
+++ repl.coffee
@@ -111,9 +111,8 @@

   fd = fs.openSync filename, 'a'

-  historyCommand = if repl.commandsWithLeadingDot() then '.history' else 'history'
   repl.rli.addListener 'line', (code) ->
-    if code and code.length and code isnt historyCommand and lastLine isnt code
+    if code and code.length and code isnt '.history' and lastLine isnt code
       # Save the latest command in the file
       fs.write fd, "#{code}\n"
       lastLine = code
@@ -121,16 +120,16 @@
   repl.rli.on 'exit', -> fs.close fd

   # Add a command to show the history stack
-  repl.commands[historyCommand] =
+  repl.commands[getCommandId(repl, '.history')] =
     help: 'Show command history'
     action: ->
       repl.outputStream.write "#{repl.rli.history[..].reverse().join '\n'}\n"
       repl.displayPrompt()

-# Adapt load help inherited from the node REPL
-correctLoadHelp = (repl) ->
-  loadCommand = if repl.commandsWithLeadingDot() then '.load' else 'load'
-  repl.commands[loadCommand].help = 'Load code from a file into this REPL session'
+getCommandId = (repl, commandName) ->
+  # Node 0.11 changed API, a command such as '.help' is now stored as 'help'
+  commandsHaveLeadingDot = ! repl.commands['help']
+  if commandsHaveLeadingDot then commandName else commandName[1..]

 module.exports =
   start: (opts = {}) ->
@@ -145,8 +144,8 @@
     opts = merge replDefaults, opts
     repl = nodeREPL.start opts
     repl.on 'exit', -> repl.outputStream.write '\n'
-    repl.commandsWithLeadingDot = -> ! @commands.help
     addMultilineHandler repl
     addHistory repl, opts.historyFile, opts.historyMaxInputSize if opts.historyFile
-    correctLoadHelp repl
+    # Adapt help inherited from the node REPL
+    repl.commands[getCommandId(repl, '.load')].help = 'Load code from a file into this REPL session'
     repl


follow up on before optimization.

follow up on before optimization.

--- src/client/deserializer.coffee
+++ src/client/deserializer.coffee
@@ -1,4 +1,5 @@
 NodeCompressor = require('./shared/node_compressor')
+
 exports.deserialize = (nodes, sibling, client) ->
     first = true
     if sibling != null
@@ -51,7 +52,11 @@
                     else
                         node = doc.createComment(record.value)
                     client.nodes.add(node, record.id)
-                parent.insertBefore(node, sibling)
+                if first
+                    first = false
+                    parent.insertBefore(node, sibling)
+                else
+                    parent.appendChild(node)

             when 'doctype'
                 node = doc.implementation.createDocumentType(record.name, record.pid, record.sid)


Fixes clean test

Fixes clean test

--- tests/build/clean_test.coffee
+++ tests/build/clean_test.coffee
@@ -18,6 +18,6 @@
       .after -> done()
       .cwd dir
       .writeFile path.join(buildDir, 'garbage')
-      .run 'tangle app build'
+      .run 'tangle app build clean'
       .end ->
         buildDir.should.be.a.directory().and.empty


component rendering bugfix

component rendering bugfix

--- src/core/container.coffee
+++ src/core/container.coffee
@@ -170,10 +170,10 @@
       if @appendContainers ||= @generateComponentElements
         panel = @make(@componentTag, componentContainerElement, '')
         @$append( panel )
-        component.container ||= componentContainerElement.id
+        component.container ||= '#' + componentContainerElement.id

       if _.isString(component.appendTo)
-        component.container ||= component.appendTo
+        component.container ||= '#' + component.appendTo

       component.container ||= @$bodyEl()


home/myteam: encode team name before submit

--- client/home/lib/myteam/components/hometeamsettings/container.coffee
+++ client/home/lib/myteam/components/hometeamsettings/container.coffee
@@ -71,15 +71,11 @@

   onUpdate: ->

-    dataToUpdate = {}
-    title = @state.teamName
+    return  if @state.teamName is @state.team.get 'title'

-    if title isnt @state.team.get 'title'
-      dataToUpdate.title = title
+    title = Encoder.htmlEncode @state.teamName

-    return  unless dataToUpdate.title
-
-    @updateTeam { dataToUpdate }
+    @updateTeam { dataToUpdate: { title } }

   updateTeam: ({ dataToUpdate }) ->


Fix indentation

Fix indentation

--- client/landing/app/MainApp/filetree/modals/vmdangermodalview.coffee
+++ client/landing/app/MainApp/filetree/modals/vmdangermodalview.coffee
@@ -3,13 +3,13 @@
   constructor:(options = {}, data)->

     options.action    or= 'Danger Zone'
-    options.callback  ?= -> log "#{options.action} performed"
     options.title     or= options.action
     options.content   or= "

CAUTION! This will destroy the #{options.name} VM including all its data. This action CANNOT be undone.

Please enter #{data} into the field below to continue:

"
-    options.overlay   ?= yes
-    options.width     ?= 500
-    options.height    ?= 'auto'
-    options.tabs      ?=
+    options.callback   ?= -> log "#{options.action} performed"
+    options.overlay    ?= yes
+    options.width      ?= 500
+    options.height     ?= 'auto'
+    options.tabs       ?=
       forms                  :
         dangerForm           :
           callback           : =>


incremental

incremental

--- src/command_line.coffee
+++ src/command_line.coffee
@@ -1,4 +1,5 @@
 fs         = require 'fs'
+path       = require 'path'
 {view}     = require '../lib/view'
 program    = require 'commander'

@@ -38,13 +39,27 @@

 # -----------------------------------------------------------------------------

-recurseRun = (path) ->
-  stat = fs.statSync path
-  console.log stat
+recurseRun = (start_path, curr_path) ->
+  stats = fs.statSync curr_path
+  if stats.isDirectory()
+    files = fs.readdirSync curr_path
+    for file in files
+      sub_path = path.normalize "#{curr_path}/#{file}"
+      if file.match /\.toffee$/
+        recurseRun start_path, sub_path
+      else if not (file in ['.','..'])
+        sub_stats = fs.statSync sub_path
+        if sub_stats.isDirectory()
+          recurseRun start_path, sub_path
+  else
+    console.log "Visiting #{curr_path}"

 run = exports.run = ->
-  console.log program.args
-  #recurseRun path
+  if program.args.length isnt 1
+    console.log "Unexpected input. toffee --help for examples"
+    process.exit 1
+  else
+    recurseRun program.args[0], program.args[0]

 # -----------------------------------------------------------------------------


fix :class

fix :class

--- src/directive/attr.coffee
+++ src/directive/attr.coffee
@@ -52,6 +52,7 @@
         else if attrName is 'class'
             if not d[1]
                 throw 'Class is not declared'
+            isTemplate = false
             list = d.slice 1
             setter = (value) ->
                 if value


feat(snippets): converts React snippets in atom to ES6-style

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -98,116 +98,105 @@
     });
     """

-  # Gulp
-  'Gulp Task':
-    'prefix': 'task'
-    'body': """
-    gulp.task('${1}', [${2}], function () {
-      ${3}
-    });
-    """
-
   # React
-  'React: componentDidMount: fn() { ... }':
+  'React: component':
+    'prefix' : 'rcm'
+    'body' : """
+    class ${1} extends React.Component {
+      constructor () {
+        super();
+      }
+
+      render () {
+        ${2}
+      }
+    }
+    """
+
+  'React: export default component':
+    'prefix' : 'expdrcm'
+    'body' : """
+    export default class ${1} extends React.Component {
+      constructor () {
+        super();
+      }
+
+      render () {
+        ${2}
+      }
+    }
+    """
+
+  'React: componentDidMount () { ... }':
     'prefix' : 'cdm'
     'body' : """
-    componentDidMount : function () {
-      ${1}
-    }
-    """
-
-  'React: componentDidUpdate: fn(pp, ps) { ... }':
+    componentDidMount () {
+      ${1}
+    }
+    """
+
+  'React: componentDidUpdate (pp, ps) { ... }':
     'prefix': 'cdup'
     'body': """
-    componentDidUpdate : function (prevProps, prevState) {
-      ${1}
-    }
-    """
-
-  'React: componentWillMount: fn() { ... }':
+    componentDidUpdate (prevProps, prevState) {
+      ${1}
+    }
+    """
+
+  'React: componentWillMount () { ... }':
     'prefix': 'cwm'
     'body': """
-    componentWillMount : function () {
-      ${1}
-    }
-    """
-
-  'React: componentWillReceiveProps: fn(np) { ... }':
+    componentWillMount () {
+      ${1}
+    }
+    """
+
+  'React: componentWillReceiveProps (np) { ... }':
     'prefix': 'cwr'
     'body': """
-    componentWillReceiveProps : function (nextProps) {
-      ${1}
-    }
-    """
-
-  'React: componentWillUnmount: fn() { ... }':
+    componentWillReceiveProps (nextProps) {
+      ${1}
+    }
+    """
+
+  'React: componentWillUnmount () { ... }':
     'prefix': 'cwun'
     'body': """
-    componentWillUnmount : function() {
-      ${1}
-    }
-    """
-
-  'React: componentWillUpdate: fn(np, ns) { ... }':
+    componentWillUnmount () {
+      ${1}
+    }
+    """
+
+  'React: componentWillUpdate (np, ns) { ... }':
     'prefix': 'cwu'
     'body': """
-    componentWillUpdate : function (nextProps, nextState) {
-      ${1}
-    }
-    """
-
-  'React: cx({ ... })':
-    'prefix': 'cx'
-    'body': """
-    cx({
-      $1 : $2
-    });
-    """
-
-  'React: forceUpdate(...)':
-    'prefix': 'fup'
-    'body': """
-    forceUpdate(${1:callback});
-    """
-
-  'React: getDefaultProps: fn() { return {...} } ':
-    'prefix': 'gdp'
-    'body': """
-    getDefaultProps : function () {
-      return {${1};
-    }
-    """
-
-  'React: getInitialState: fn() { return {...} } ':
-    'prefix': 'gis'
-    'body': """
-    getInitialState : function() {
-      return {
-        ${1} : ${2}
-      };
-    }
-    """
-
-  'React: isMounted()':
-    'prefix': 'ism'
-    'body': """
-    isMounted()
+    componentWillUpdate (nextProps, nextState) {
+      ${1}
+    }
+    """
+
+  'React: defaultProps { ... }':
+    'prefix': 'defprops'
+    'body': """
+    static defaultProps = {
+      ${1} : ${2}
+    }
     """

   'React: propTypes { ... }':
     'prefix': 'pt'
     'body': """
-    propTypes: {
+    static propTypes = {
       ${1} : React.PropTypes.${2:string}
     }
     """

-  'React: render: fn() { return ... }':
+  'React: render () { return ... }':
     'prefix': 'ren'
     'body': """
-    render : function () {
+    render () {
       return (
-        ${1:<div />}
+        ${1}
       );
     }
     """
@@ -215,15 +204,15 @@
   'React: setState({ ... })':
     'prefix': 'sst'
     'body': """
-    setState({
+    this.setState({
       ${1} : ${2}
     });
     """

-  'React: shouldComponentUpdate: fn(np, ns) { ... }':
+  'React: shouldComponentUpdate (np, ns) { ... }':
     'prefix': 'scu'
     'body': """
-    shouldComponentUpdate : function (nextProps, nextState) {
+    shouldComponentUpdate (nextProps, nextState) {
       ${1}
     }
     """
@@ -243,7 +232,7 @@
   'React: render(component, container, [callback])':
     'prefix': 'rrc'
     'body': """
-    React.render(${1:<$2 />}, ${3:document.body}${4:, ${5:callback}})
+    React.render(${1:<$2 />}, ${3:document.body}${4:, ${5:callback}});
     """

 ########################


fix potential xss vector in screenreader gradebook, fixes CNVS-17660

i haven't been able to exploit this yet, but that doesn't mean it's not
possible ;) ... make sure we sanitize the error messages we're putting
in here

Change-Id: I0a66696101834c6872d3b9c6e48a09c6525f6364
Reviewed-on: https://gerrit.instructure.com/46289
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Cody Cutrer <c4fe2b1d90ef8f2548c8aeabfa633434316f0305@instructure.com>
QA-Review: Caleb Guanzon <54fa1f30d451e4f3c341a8f3c47c9215fd0be9c6@instructure.com>
Product-Review: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>

--- app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee
+++ app/coffeescripts/ember/screenreader_gradebook/controllers/screenreader_gradebook_controller.coffee
@@ -13,7 +13,8 @@
   'compiled/grade_calculator'
   'compiled/gradebook2/OutcomeGradebookGrid'
   '../../shared/components/ic_submission_download_dialog_component'
-  ], (ajax, round, userSettings, fetchAllPages, parseLinkHeader, I18n, Ember, _, tz, AssignmentDetailsDialog, AssignmentMuter, GradeCalculator, outcomeGrid, ic_submission_download_dialog ) ->
+  'str/htmlEscape'
+  ], (ajax, round, userSettings, fetchAllPages, parseLinkHeader, I18n, Ember, _, tz, AssignmentDetailsDialog, AssignmentMuter, GradeCalculator, outcomeGrid, ic_submission_download_dialog, htmlEscape) ->

   {get, set, setProperties} = Ember

@@ -62,7 +63,7 @@
       if Ember.$('#flash_message_holder li').size() > 0
         close = Ember.$('#flash_message_holder li a').text().trim()
         message = Ember.$('#flash_message_holder li').text().replace(close,'').trim()
-        node = Ember.$("#{message}")
+        node = Ember.$("#{htmlEscape(message)}")
         Ember.$(node).appendTo(Ember.$('#flash_screenreader_holder'))
     ).on('init')


Fix box compilation (call intersect methods correctly)

--- src/compile.asm.coffee
+++ src/compile.asm.coffee
@@ -58,27 +58,24 @@
             asm.intersect { chamfer: true }, halfspaces[0], halfspaces[1], halfspaces[2], asm.invert halfspaces[3..6]...
           else
             # Chamfer only edges (TODO: is this going to work as expected?)
-            asm.intersect (
-              { chamfer: true }
-              asm.intersect { chamfer: true }, halfspaces[0], halfspaces[1], (asm.invert halfspaces[3], halfspaces[4])
-              halfspaces[2]
-              asm.invert halfspaces[5] )
+            asm.intersect { chamfer: true },
+              asm.intersect { chamfer: true }, halfspaces[0], halfspaces[1], (asm.invert halfspaces[3], halfspaces[4]),
+              halfspaces[2],
+              asm.invert halfspaces[5]
         else
           # Group intersections according to the edges that are chamfered
           # TODO: This is not yet implemented, for now chamfer nothing...
-          asm.intersect (
-            {}
-            halfspaces[0]
-            halfspaces[1]
-            halfspaces[2]
-            asm.invert halfspaces[3..6]... )
+          asm.intersect {},
+            halfspaces[0],
+            halfspaces[1],
+            halfspaces[2],
+            asm.invert halfspaces[3..6]...
       else
-        asm.intersect (
-          {}
-          halfspaces[0]
-          halfspaces[1]
-          halfspaces[2]
-          asm.invert halfspaces[3..6]... )
+        asm.intersect {},
+          halfspaces[0],
+          halfspaces[1],
+          halfspaces[2],
+          asm.invert halfspaces[3..6]...
     sphere: (node) ->
       # TODO
       {}


Don't send a message until the previous one finishes, guaranteeing output order same as input.

--- src/hubot/campfire.coffee
+++ src/hubot/campfire.coffee
@@ -4,12 +4,13 @@

 class Campfire extends Robot
   send: (user, strings...) ->
-    for str in strings
-      @bot.Room(user.room).speak str, (err, data) ->
+    if strings.length > 0
+      @bot.Room(user.room).speak strings.shift(), (err, data) =>
         console.log "campfire error: #{err}" if err
+        @send user, strings...

   reply: (user, strings...) ->
-    @send user, "#{user.name}: #{str}" for str in strings
+    @send user, strings.map((str) -> "#{user.name}: #{str}")...

   run: ->
     self = @


data isn't a function anymore

data isn't a function anymore

--- app/dsl.coffee
+++ app/dsl.coffee
@@ -109,7 +109,7 @@
     else
       Context.add_component ctx, React.DOM.div null,
         "What do you want to do with #{lead_string}?"
-        _.map ['data', 'graph', 'img', 'url'], (f) ->
+        _.map ['graph', 'img', 'url'], (f) ->
           Components.ExampleComponent value: "#{f} #{object.to_js_string()}", run: true
     true


pipe deadline sigint to exit

pipe deadline sigint to exit

--- src/commands/run.coffee
+++ src/commands/run.coffee
@@ -18,6 +18,7 @@
   # running = []
   rl = readline.createInterface process.stdin, process.stdout
   rl.setPrompt ''
+  rl.on 'SIGINT', process.exit
   hostlength = 20
   for s in config.servers then hostlength = Math.max(hostlength, s.host.length+2)
   params = params.parse()


run: fix refresh line in multihost

run: fix refresh line in multihost

--- src/commands/run.coffee
+++ src/commands/run.coffee
@@ -24,6 +24,7 @@
   params = params.parse()
   multihost = params.hosts?.length isnt 1 and config.servers.length isnt 1
   times = {}
+  multihost = true
   run(config, params)
   .on 'context', (ctx) ->
     ctx
@@ -51,7 +52,7 @@
       # rl.write '\n'
       rl.cursor = 0
       rl.line = ''
-      rl._refreshLine()
+      rl._refreshLine() unless multihost
       if err or status?
         rl.write line
         rl.write '\n'


Enable all tests

Enable all tests

--- tests/src/ScriptsTests.coffee
+++ tests/src/ScriptsTests.coffee
@@ -7,7 +7,7 @@
 path = require 'path'
 _ = require('underscore')

-describe.only "scripts", ->
+describe "scripts", ->

   spacejamBinDir = path.resolve(__dirname, "../../bin")
   meteorStubDir = path.resolve(__dirname, "../bin")


Fixed media keys

Fixed media keys

--- app/coffee/modules/key/keys.coffee
+++ app/coffee/modules/key/keys.coffee
@@ -110,9 +110,9 @@
     # { row: 'media_r0', key: 'F13', keycode: 57, icon: 'fa-step-backward' }
     # { row: 'media_r0', key: 'F13', keycode: 57, icon: 'fa-play' }
     # { row: 'media_r0', key: 'F13', keycode: 57, icon: 'fa-step-forward' }
-    { row: 'media_r0', key: 'MUTE', keycode: 127, icon: 'fa-volume-off', dec: 128 }
-    { row: 'media_r0', key: 'VOLUME_UP', keycode: 129, icon: 'fa-volume-up', dec: 128 }
-    { row: 'media_r0', key: 'VOLUME_DN', keycode: 128, icon: 'fa-volume-down', dec: 129 }
+    { row: 'media_r0', key: 'MUTE', keycode: 5127, icon: 'fa-volume-off', dec: 127 }
+    { row: 'media_r0', key: 'VOLUME_UP', keycode: 5129, icon: 'fa-volume-up', dec: 128 }
+    { row: 'media_r0', key: 'VOLUME_DN', keycode: 5128, icon: 'fa-volume-down', dec: 129 }

     # Navigation Keys
     { row: 'nav_r0', key: 'PGUP', keycode: 4075, dec: 75 }


Added funcSize animation function. Fixed margin computation for non resizeable images.

--- src/jquery.rondell.coffee
+++ src/jquery.rondell.coffee
@@ -2,7 +2,7 @@
   jQuery rondell plugin
   @name jquery.rondell.js
   @author Sebastian Helzle (sebastian@helzle.net or @sebobo)
-  @version 0.8.2
+  @version 0.8.3
   @date 11/19/2011
   @category jQuery plugin
   @copyright (c) 2009-2011 Sebastian Helzle (www.sebastianhelzle.net)
@@ -12,7 +12,7 @@
 (($) ->
   # Global rondell stuff
   $.rondell =
-    version: '0.8.3-beta'
+    version: '0.8.3'
     name: 'rondell'
     defaults:
       resizeableClass: 'resizeable'
@@ -70,6 +70,7 @@
         end: undefined
       funcEase: 'easeInOutQuad' # Easing function name for the movement of items
       theme: 'default' # CSS theme class which gets added to the rondell container
+      preset: '' # Configuration preset
       effect: null # Special effect function for the focused item

   # Add default easing function for rondell to jQuery if missing
@@ -107,6 +108,8 @@
       Math.pow(Math.abs(layerDiff) / rondell.itemCount, 0.5) * Math.PI
     funcOpacity: (layerDist, rondell) ->
       if rondell.visibleItems > 1 then Math.max(0, 1.0 - Math.pow(layerDist / rondell.visibleItems, 2)) else 0
+    funcSize: (layerDist, rondell) ->
+      1

     showCaption: (layerNum) =>
       # Restore automatic height and show caption
@@ -370,8 +373,11 @@
       layerDiff = @funcDiff(layerPos - @currentLayer, @)
       layerDiff *= -1 if layerPos < @currentLayer

-      newX = @funcLeft(layerDiff, @) + (@itemProperties.size.width - item.sizeSmall.width) / 2
-      newY = @funcTop(layerDiff, @) + (@itemProperties.size.height - item.sizeSmall.height) / 2
+      itemWidth = item.sizeSmall.width * @funcSize(layerDiff, @)
+      itemHeight = item.sizeSmall.height * @funcSize(layerDiff, @)
+
+      newX = @funcLeft(layerDiff, @) + (@itemProperties.size.width - itemWidth) / 2
+      newY = @funcTop(layerDiff, @) + (@itemProperties.size.height - itemHeight) / 2

       newZ = @zIndex + (if layerDiff < 0 then layerPos else -layerPos)
       fadeTime = @fadeTime + @itemProperties.delay * layerDist
@@ -387,8 +393,8 @@
         item.object.removeClass('rondellItemNew rondellItemFocused').stop(true)
         .css('z-index', newZ)
         .animate(
-            width: item.sizeSmall.width
-            height: item.sizeSmall.height
+            width: itemWidth
+            height: itemHeight
             left: newX
             top: newY
             opacity: newOpacity
@@ -400,7 +406,7 @@
         unless item.small
           item.small = true
           if item.icon and not item.resizeable
-            margin = (@itemProperties.size.height - item.icon.height()) / 2
+            margin = (itemHeight - item.icon.height()) / 2
             item.icon.stop(true).animate(
                 marginTop: margin
                 marginBottom: margin


Pass noon when closing handle without callback

--- src/leveldb/handle.coffee
+++ src/leveldb/handle.coffee
@@ -41,7 +41,7 @@
     @self.valid()

   close: (callback) ->
-    @self.close(callback)
+    @self.close callback or noop

   closeSync: ->
     @self.close()


use while loop to check item

use while loop to check item

--- scripts/feed_checker.coffee
+++ scripts/feed_checker.coffee
@@ -47,7 +47,7 @@
           # ignore meta
           # .on 'meta', (meta) ->
           .on 'readable', ->
-            next.push this.read()
+            while item = this.read() then next.push item
           .on 'end', ->
             if prev.length != 0 then next.forEach (article) ->
               if is_new prev, article.guid


Clear visual errors after code change (#304)

--- ozaria/site/views/play/level/tome/SpellView.ozar.coffee
+++ ozaria/site/views/play/level/tome/SpellView.ozar.coffee
@@ -723,9 +723,9 @@
       _.throttle @notifySpellChanged, 300
       _.throttle @updateLines, 500
       _.throttle @hideProblemAlert, 500
+      _.throttle @clearAetherDisplay.bind(@), 250
     ]
-    unless @options.level.get('ozariaType') == 'capstone'
-      onAnyChange.push(_.debounce(@updateAether, 500))
+
     onSignificantChange.push _.debounce @checkRequiredCode, 750 if @options.level.get 'requiredCode'
     onSignificantChange.push _.debounce @checkSuspectCode, 750 if @options.level.get 'suspectCode'
     onAnyChange.push _.throttle @updateHTML, 10 if @options.level.isType('web-dev')


:bug: don't set one-line-upper-offset when cursor is last line in visual-linewise

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -165,7 +165,9 @@
       {cursor} = s
       if @submode is 'linewise'
         unless s.isReversed()
-          style.setProperty('top', '-1.5em') unless s.isEmpty()
+          {start, end} = s.getBufferRange()
+          unless start.row is end.row is @editor.getLastBufferRow()
+            style.setProperty('top', '-1.5em') unless s.isEmpty()
         if point = swrap(s).getCharacterwiseHeadPosition()
           style.setProperty('left', "#{point.column}ch")
       else


client: home: removed inner header from utilities

--- client/home/lib/utilities/homeutilitieskd.coffee
+++ client/home/lib/utilities/homeutilitieskd.coffee
@@ -23,7 +23,6 @@
       @kdInstallView?.destroy()
       @addSubView @kdInstallView = new kd.CustomHTMLView
         partial : """
-
KD: Koding + Your Localhost!

kd  is a command line program that allows you to use your local
           IDE with your VMs. Copy and paste the command below into your terminal.

           #{key}


allowing more flexible whitespace-started regexes. You have to wrap 'em in parens or assign to 'em, but it's better than nothing.

--- lexer.coffee
+++ lexer.coffee
@@ -163,7 +163,8 @@
   # JavaScript and Ruby, borrow slash balancing from `@balancedToken`, and
   # borrow interpolation from `@interpolateString`.
   regexToken: ->
-    return false unless @chunk.match REGEX_START
+    return false unless first = @chunk.match REGEX_START
+    return false if first[1] is ' ' and @tag() not in ['CALL_START', '=']
     return false if     include NOT_REGEX, @tag()
     return false unless regex = @balancedToken ['/', '/']
     return false unless end = @chunk.substr(regex.length).match REGEX_END
@@ -527,7 +528,7 @@
 LAST_DENT     = /\n([ \t]*)/

 # Regex-matching-regexes.
-REGEX_START         = /^\/[^\/ ]/
+REGEX_START         = /^\/([^\/])/
 REGEX_INTERPOLATION = /([^\\]#\{.*[^\\]\})/
 REGEX_END           = /^(([imgy]{1,4})\b|\W|$)/
 REGEX_ESCAPE        = /\\[^\$]/g


Social: add fetcher function for popular topics

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -34,11 +34,19 @@
         return callback err if err
         return callback null, mapChannels result

+  fetchPopularTopics = (options, callback)->
+    getCurrentGroup (group)->
+      options.groupName = group.slug
+      {SocialChannel} = KD.remote.api
+      SocialChannel.fetchPopularTopics options, (err, result)->
+        return callback err if err
+        return callback null, mapChannels result

   channel:
     list: fetchChannels
     fetchActivity: fetchChannelActivity
     fetchGroupActivity: fetchGroupActivity
+    fetchPopularTopics: fetchPopularTopics

   messageApiFunc = (name, rest..., callback)->
     KD.remote.api.SocialMessage[name] rest..., (err, res)->


Account: improved routes for landing on /Account/... paths

--- client/account/lib/routehandler.coffee
+++ client/account/lib/routehandler.coffee
@@ -1,20 +1,18 @@
 kd = require 'kd'
 KDNotificationView = kd.NotificationView
-isLoggedIn = require 'app/util/isLoggedIn'
 lazyrouter = require 'app/lazyrouter'

-handleSection = (callback) -> kd.singletons.mainController.ready ->
+handleSection = (path, callback) ->

-  if isLoggedIn()
-    appManager = kd.singleton('appManager')
-    if appManager.getFrontApp()?.getOption('name') is 'Account'
-      callback appManager.getFrontApp()
-    else appManager.open 'Account', callback
-  else
-    kd.singletons.router.handleRoute '/'
+  { appManager, router } = kd.singletons
+  unless appManager.getFrontApp()
+    appManager.once 'AppIsBeingShown', -> router.handleRoute path
+    router.handleRoute '/IDE'
+  else appManager.open 'Account', callback

-handle = ({params:{section}}) -> handleSection (app) -> app.openSection section
+handle = ({params:{section}}, path) ->
+  handleSection path, (app) -> app.openSection section

 module.exports = -> lazyrouter.bind 'account', (type, info, state, path, ctx) ->

@@ -28,5 +26,5 @@
       new KDNotificationView title: "Verification failed!"
       ctx.clear()
     when 'referrer' then kd.singletons.router.handleRoute '/'
-    when 'section' then handle info
+    when 'section' then handle info, path


[Email Confirmation] move clear back to where it is

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -315,7 +315,7 @@
           else
             new KDNotificationView
               title: "Thanks for confirming your email address!"
-            @clear()
+          @clear()

       '/member/:username': ({params:{username}})->
         @handleRoute "/#{username}", replaceState: yes


work from home

work from home

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -9,7 +9,6 @@
     @on 'AlreadyHere', ->
       new KDNotificationView title: "You're already here!"

-    # @utils.defer =>
     unless @userRoute
       @handleRoute defaultRoute,
         shouldPushState: yes
@@ -105,6 +104,7 @@
         slug = stripTemplate route, konstructor
         selector[usedAsPath] = slug
         konstructor?.one selector, (err, model)=>
+          error err  if err?
           @openContent name, section, model, route
       else
         @handleNotFound route
@@ -140,7 +140,7 @@
       (sec)=> @createContentDisplayHandler sec
     )

-    nouns = createLinks(
+    section = createLinks(
       'Account Activity Apps Groups Members StartTab Topics'
       (sec)-> ({params:{name}, query})-> @go sec, name, query
     )
@@ -157,22 +157,22 @@
       '/:name?/Join'      : ({params:{name}})-> mainController.doJoin name
       '/:name?/Recover'   : ({params:{name}})-> mainController.doRecover name

-      # nouns
-      '/:name?/Groups'                  : nouns.Groups
-      '/:name?/Activity'                : nouns.Activity
-      '/:name?/Members'                 : nouns.Members
-      '/:name?/Topics'                  : nouns.Topics
-      '/:name?/Develop'                 : nouns.StartTab
-      '/:name?/Apps'                    : nouns.Apps
-      '/:name?/Account'                 : nouns.Account
-
-      # content displays:
+      # section
+      '/:name?/Groups'                  : section.Groups
+      '/:name?/Activity'                : section.Activity
+      '/:name?/Members'                 : section.Members
+      '/:name?/Topics'                  : section.Topics
+      '/:name?/Develop'                 : section.StartTab
+      '/:name?/Apps'                    : section.Apps
+      '/:name?/Account'                 : section.Account
+
+      # content
       '/:name?/Topics/:topicSlug'       : content.Topics
       '/:name?/Activity/:activitySlug'  : content.Activity
       '/:name?/Apps/:appSlug'           : content.Apps

       '/:name?/Recover/:recoveryToken': ({params:{recoveryToken}})->
-        return if recoveryToken is 'Password'
+        return  if recoveryToken is 'Password'
         mainController.appReady =>
           # TODO: DRY this one
           $('body').addClass 'login'


Client: Battle window scrolls up to the battle pane itself.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -977,8 +977,11 @@
     done()

   continueTurn: (done) =>
+    $battleWindow = @$el.closest('.battle_window')
     @$('.battle_summary').empty().hide()
-    @$el.closest('.battle_window').scrollTop(0)
+    offset = @$('.battle_pane').offset().top + $battleWindow.scrollTop()
+    offset -= $battleWindow.offset().top
+    $battleWindow.scrollTop(offset)
     done()

   makeMove: (e) =>


Add bdc in the markdown cheatsheet

Add bdc in the markdown cheatsheet

--- client/src/common/services/dynamic-fields.coffee
+++ client/src/common/services/dynamic-fields.coffee
@@ -96,6 +96,10 @@
       key: '{ahead:value1 behind:value2}'
       description: 'Conditional value whether the team is behind or ahead according to the burndown chart. Example: {ahead:Ahead behind:Behind}: {gap} points'
       icon: 'owl'
+    result.push
+      key: '{bdc}'
+      description: 'Display the burndown chart as an image'
+      icon: 'trending-down'
     result

   ready: (sprint, project) ->


Some canned texts result in actions.

Some canned texts result in actions.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -359,9 +359,22 @@
     cannedTextName = CannedMapReverse[cannedInteger]
     cannedText = @getCannedText(cannedTextName, args)
     @addLog(cannedText)
-    done()
-
-  getCannedText: (cannedTextName, args) ->
+    @actOnCannedText(cannedTextName, cannedText, done)
+
+  # Some canned text requires special actions.
+  # For example, misses require a delay, and also prints to the battle pane.
+  actOnCannedText: (cannedTextName, cannedText, done) =>
+    switch cannedTextName
+      when 'MOVE_MISS', 'MOVE_FAIL', 'IMMUNITY'
+        @addSummary(cannedText)
+        setTimeout(done, 500)
+      when 'CRITICAL_HIT', 'SUPER_EFFECTIVE', 'NOT_VERY_EFFECTIVE'
+        @addSummary(cannedText)
+        done()
+      else
+        done()
+
+  getCannedText: (cannedTextName, args) =>
     cannedText = 'Please refresh to see this text!'
     genIndex   = ALL_GENERATIONS.indexOf(@model.get('generation'))
     language   = 'en'
@@ -918,23 +931,30 @@

   addMoveMessage: (owner, pokemon, moveName) =>
     @chatView.print("
#{owner}'s #{pokemonHtml(pokemon)} used #{moveName}!
")
-    @addSummary("#{owner}'s #{pokemon.get('name')} used #{moveName}!")
+    @addSummary("#{owner}'s #{pokemon.get('name')} used #{moveName}!", newline: true)

   addLog: (message) =>
     @chatView.print("
#{message}
")

-  addSummary: (message) =>
+  addSummary: (message, options = {}) =>
     return  if @skip?
     $summary = @$('.battle_summary')
     $summary.show()
-    $p = $("
").html(message).hide()
-    $summary.append($p)
-    $p.slideDown()
+    $p = $summary.children().last()
+    if $p.length == 0 || $p.is('.newline') || options.newline
+      $p = $("
").html(message).hide()
+      $p.addClass('newline')  if options.newline
+      $p.appendTo($summary)
+    else
+      html = $p.html()
+      $p.html("#{html} #{message}")
+    $p.slideDown(200)
     setTimeout((->
       $p.slideUp ->
         $p.remove()
         $summary.hide()  if $summary.children().length == 0
     ), 3000)
+    $p

   beginTurn: (turn, done) =>
     @chatView.print("
Turn #{turn}
")


FIXES #380: problem with @ and instanceof

--- lexer.coffee
+++ lexer.coffee
@@ -90,7 +90,8 @@
   identifier_token: ->
     return false unless id: @match IDENTIFIER, 1
     @name_access_type()
-    accessed: include ACCESSORS, @tag 0
+    spaced:   @prev() and @prev().spaced
+    accessed: include(ACCESSORS, @tag(0)) and not spaced
     tag: 'IDENTIFIER'
     tag: id.toUpperCase()     if not accessed and include(KEYWORDS, id)
     @identifier_error id      if include RESERVED, id


fixing block comment indentation

fixing block comment indentation

--- lexer.coffee
+++ lexer.coffee
@@ -389,7 +389,7 @@
     if herecomment
       if HEREDOC_ILLEGAL.test doc
         @error "block comment cannot contain \"*/\", starting"
-      return doc if doc.indexOf('\n') <= 0
+      return doc if doc.indexOf('\n') < 0
     else
       while match = HEREDOC_INDENT.exec doc
         attempt = match[1]


component-lab: define a base css class for Label

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

--- client/component-lab/Text/Label.coffee
+++ client/component-lab/Text/Label.coffee
@@ -6,6 +6,7 @@

 Label = ({ size, type, children, monospaced }) ->
   className = generateClassName [
+    'Label'
     textStyles['Label']
     textStyles[size]
     type and textStyles[type]


TODO for handling JUnit input

TODO for handling JUnit input

--- lib/atom-python-test.coffee
+++ lib/atom-python-test.coffee
@@ -53,6 +53,7 @@
       atomPythonTestView = AtomPythonTest.atomPythonTestView

       junitViewer = require('junit-viewer')
+      # TODO: check with parse junitViewer.xml
       parsedResults = junitViewer.parse(AtomPythonTest.testResultsFilename.name)

       if parsedResults.junit_info.tests.error > 0 and code != 0


Allow to config Kadira

Allow to config Kadira

--- server/lib/_settings.coffee
+++ server/lib/_settings.coffee
@@ -16,6 +16,9 @@
 	if settings.CDN_PREFIX?
 		WebAppInternals.setBundledJsCssPrefix settings.CDN_PREFIX

+configKadira = (settings) ->
+	if settings.kadira?
+		Kadira.connect(settings.kadira.appId, settings.kadira.appSecret)

 Settings.find().observe
 	added: (settings) ->
@@ -23,9 +26,11 @@
 		configLoginServices settings
 		loadEnvConfigs settings
 		configCDN settings
+		configKadira settings

 	changed: (settings) ->
 		Meteor.settings = settings
 		configLoginServices settings
 		loadEnvConfigs settings
 		configCDN settings
+		configKadira settings


In calculateAllHashes, split by user

In calculateAllHashes, split by user

--- server/models/submit.coffee
+++ server/models/submit.coffee
@@ -1,6 +1,7 @@
 mongoose = require('mongoose')

 import Hash from './Hash'
+import User from './user'

 import calculateHashes from '../hashes/calculateHashes'

@@ -94,19 +95,22 @@
         outcome: "CT"

 submitsSchema.statics.calculateAllHashes = () ->
-    submits = await Submit.find({})
-    promises = []
-    count = 0
-    for s in submits
-        promises.push(s.calculateHashes())
-        count++
-        if promises.length >= 10
-            logger.info("Calculating 10 hashes, waiting for completion (#{count} / #{submits.length})")
-            await awaitAll(promises)
-            logger.info("Calculated 10 hashes, continuing (#{count} / #{submits.length})")
-            promises = []
-    await awaitAll(promises)
-    logger.info("Calculates all hashes")
+    users = await User.find({})
+    for u, userI in users
+        submits = await Submit.findByUser(u._id)
+        promises = []
+        count = 0
+        for s in submits
+            promises.push(s.calculateHashes())
+            count++
+            if promises.length >= 10
+                logger.info("Calculating 10 hashes, waiting for completion (#{count} / #{submits.length}  | #{userI} / #{users.length})")
+                await awaitAll(promises)
+                logger.info("Calculated 10 hashes, continuing (#{count} / #{submits.length}  | #{userI} / #{users.length})")
+                promises = []
+        await awaitAll(promises)
+        logger.info("Calculated all hashes for user #{userI} / #{users.length}")
+    logger.info("Calculated all hashes for all users")

 submitsSchema.index({ user : 1, problem: 1, time: 1 })
 submitsSchema.index({ user : 1, problem: 1, outcome: 1 })


make CompositeModelView look for the className as a template name - be less strict about selectors for find_view_placeholder - hack: look at 'url' property to know if an object has already should listen for change events (to avoid redrawing in forms and in contenteditable tags

--- guts.coffee
+++ guts.coffee
@@ -72,7 +72,7 @@
   _rendered: false

   get_template: =>
-    template = @template or @options.template
+    template = @template or @options.template or @className
     if @className isnt template
       throw "CompositeModelView : error : templates should be named after the semantic class (#{@className}, #{template})"
     template
@@ -81,9 +81,7 @@
   # ... or throw
   find_view_placeholder: ($el, child_view) ->
     # Find the placeholder
-    selector = if child_view.tagName then child_view.tagName else ''
-    selector += ".#{child_view.className}"
-    # selector = "[data-template='#{view.chunk}']"
+    selector = ".#{child_view.className}"

     $placeholder = $el.find(selector)

@@ -176,7 +174,8 @@
     if @options.models
       throw 'CompositeModelForm : error : forms do not support multiple associated models'
     super
-    @listenToOnce @model, 'change', @rerender
+    if not @model.has('url')
+      @listenToOnce @model, 'change', @rerender

   save: =>
     @listenToOnce @model, 'sync', =>


Use underscore extend method

Use underscore extend method

which does what we want

--- src/js/views/notification.coffee
+++ src/js/views/notification.coffee
@@ -10,7 +10,7 @@
     fade_time: 'slow'

   constructor: (options) ->
-    @options = $.extend {}, @default_options, options
+    @options = _.extend {}, @default_options, options

   render: ->
     @$el = $("<#{@tagName}/>")


Send verbose output to stderr

Send verbose output to stderr

--- lib/cli.iced
+++ lib/cli.iced
@@ -15,7 +15,7 @@
     "  -v, --verbose           Output progress info"
   ], argv

-  console.log "Loading files..."  if options.verbose
+  process.stderr.write "Loading files...\n"  if options.verbose
   await
     fs.readFile options.file1, 'utf8', defer(err1, data1)
     fs.readFile options.file2, 'utf8', defer(err2, data2)
@@ -23,12 +23,13 @@
   throw err1 if err1
   throw err2 if err2

-  console.log "Parsing old file..."  if options.verbose
+  process.stderr.write "Parsing old file...\n"  if options.verbose
   json1 = JSON.parse(data1)
-  console.log "Parsing new file..."  if options.verbose
+  process.stderr.write "Parsing new file...\n"  if options.verbose
   json2 = JSON.parse(data2)

-  console.log "Running diff..."  if options.verbose
+  process.stderr.write "Running diff...\n"  if options.verbose
   result = diff(json1, json2)

-  process.stdout.write JSON.stringify(result)
+  process.stderr.write "Producing output...\n"  if options.verbose
+  process.stdout.write JSON.stringify(result, null, 2)


need to add tool to main.coffee as well

need to add tool to main.coffee as well

--- bokehjs/src/coffee/main.coffee
+++ bokehjs/src/coffee/main.coffee
@@ -105,6 +105,7 @@
   Bokeh.EmbedTool              = require("tool/embed_tool")
   Bokeh.PanTool                = require("tool/pan_tool")
   Bokeh.PreviewSaveTool        = require("tool/preview_save_tool")
+  Bokeh.ResetTool              = require("tool/reset_tool")
   Bokeh.ResizeTool             = require("tool/resize_tool")
   Bokeh.CrosshairTool          = require("tool/crosshair_tool")
   Bokeh.WheelZoomTool          = require("tool/wheel_zoom_tool")


bootstrap connection: catch error

bootstrap connection: catch error

--- bootstrap/lib/bootstrap.coffee
+++ bootstrap/lib/bootstrap.coffee
@@ -5,8 +5,9 @@

 module.exports = (options, callback) ->
   {public_key, bootstrap} = @config.connection
-  # ctx.log "SSH login to #{bootstrap.username}@#{bootstrap.host}:#{bootstrap.port}"
+  options.log "SSH login to #{bootstrap.username}@#{bootstrap.host}:#{bootstrap.port}"
   connect bootstrap, (err, ssh) ->
+    return callback err if err
     cmd = """
       sed -i.back 's/.*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config;
       mkdir -p /root/.ssh; chmod 700 /root/.ssh;


Added owns snippet for relationship versioning

--- completions.cson
+++ completions.cson
@@ -837,6 +837,21 @@
     }
   ]

+'owns':
+  'prefix': 'owns'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'minVersion': '4.0'
+  'body': """
+  /**
+   * Relationship version ownership
+   * @var array
+   */
+  private static \\$owns = [
+  \t\'${1}\'
+  ];
+  """
+
 'casting':
   'prefix': 'casting'
   'module': 'silverstripe/framework'


Adding touchstart and touchend events to collector.

--- collector/scripts/touchrecorder.coffee
+++ collector/scripts/touchrecorder.coffee
@@ -1,5 +1,5 @@
 class TouchRecorder
-	constructor: (@pingRate = 500, @stream = new Stream(), @mtntrckr = new MotionTracker(), start = true, @events = "keydown keyup") ->
+	constructor: (@pingRate = 500, @stream = new Stream(), @mtntrckr = new MotionTracker(), start = true, @events = "keydown keyup touchstart touchend") ->
 		@initialiseEvents()
 		@running = start
 		if start


refactor

refactor

--- src/constructor.coffee
+++ src/constructor.coffee
@@ -18,7 +18,7 @@

 init = ->

-  funcsToApply = {}
+  mixedIn = {}

   Z = (obj) ->
     resolvedObject = resolveCompletely(obj)
@@ -26,11 +26,10 @@
     resultingPromise = tools.objectCreate(overrideLayer)

     overrides.forEach (name) ->
-      superMethod = resolvedObject[name]
       overrideLayer[name] = (args...) ->
-        Z superMethod.apply(this, args)
+        Z resolvedObject[name].apply(this, args)

-    tools.pairs(funcsToApply).forEach ([name, func]) ->
+    tools.pairs(mixedIn).forEach ([name, func]) ->
       resultingPromise[name] = (args...) ->
         Z resultingPromise.then (resolved) ->
           func.apply({ value: resolved }, args)
@@ -39,7 +38,7 @@

   Z.mixin = tools.proc (hash) ->
     tools.pairs(hash).forEach ([name, func]) ->
-      funcsToApply[name] = func
+      mixedIn[name] = func

   Z


More refactor

More refactor

--- lib/ghc-modi-process.coffee
+++ lib/ghc-modi-process.coffee
@@ -30,7 +30,7 @@
         line!="OK" && line!=""
       callback lines.map (line)->
         line.replace(/\r/g,'\n')
-    @process.stdin.write(command)
+    @process.stdin.write(command+'\n')

   runModCmd: (args,callback) =>
     CP.execFile @modPath, args, {}, (error,result) ->
@@ -64,7 +64,7 @@
     @withTempFile text, (path,close) =>
       cpos = crange.start
       command = "type "+path+" "+(cpos.row+1)+
-        " "+(cpos.column+1)+"\n"
+        " "+(cpos.column+1)
       @runCmd command, (lines) ->
         close()
         [range,type]=lines.reduce ((acc,line) ->
@@ -82,14 +82,14 @@

   getInfo: (text,symbol,callback) =>
     @withTempFile text, (path,close) =>
-      command = "info "+path+" "+symbol+"\n"
+      command = "info "+path+" "+symbol
       @runCmd command, (lines) ->
         close()
         callback lines.join('\n')

   doCheck: (text, callback) =>
     @withTempFile text, (path,close) =>
-      command = "check "+path+"\n"
+      command = "check "+path
       @runCmd command, (lines) ->
         close()
         lines.forEach (line) ->


Removed no longer used superclass view

Removed no longer used superclass view

--- core/app/backbone/factlink/composite_view.coffee
+++ core/app/backbone/factlink/composite_view.coffee
@@ -1,8 +0,0 @@
-Backbone.Factlink ||= {}
-class Backbone.Factlink.CompositeView extends Backbone.Marionette.CompositeView
-  appendHtml: (collectionView, itemView) ->
-    #TODO: also allow for adding in the middle
-    if (collectionView.collection.indexOf(itemView.model) == 0)
-      @$(@itemViewContainer).prepend(itemView.el)
-    else
-      @$(@itemViewContainer).append(itemView.el)


start competition endpoint

start competition endpoint

--- http.coffee
+++ http.coffee
@@ -14,6 +14,8 @@
     ['/competitions/all', 'get', listCompetitions]
     ['/competitions/by-owner', 'get', listCompetitionsByOwner]
     ['/competitions/by-membership', 'get', listCompetitionsByMembership]
+    ['/competition', 'post', startCompetition]
+    ['/competition/end', 'post', endCompetition]
     # [/competition\/\w+/withdraw/, 'post', withdrawFromCompetition]
     # [/competition\/\w+/join/, 'post', joinCompetition]
     # [/competition\/\w+/, 'get', listCompetition]
@@ -79,6 +81,22 @@

 listCompetitionsByMembership = (req, res) ->

+startCompetition = (req, res) ->
+  @api.startCompetition(req.body.userId, req.body.name)
+  .then((competition) ->
+    res.json(201, competition)
+    console.log("[HTTP] 201: started competition: #{competition}")
+  )
+  .fail((err) ->
+    errorMsg = "failed to start competition. reason: #{err}"
+    res.json(500, {error: errorMsg})
+    console.error("[HTTP] 500: #{errorMsg}")
+    console.error(err.stack)
+  )
+  .done()
+
+endCompetition = (req, res) ->
+
 joinCompetition = (req, res) ->

 withdrawFromCompetition = (req, res) ->


tłumaczenie pliku menu_linux

tłumaczenie pliku menu_linux

--- def/pl/menu_linux.cson
+++ def/pl/menu_linux.cson
@@ -98,34 +98,34 @@
           "&Transpose":
             value: "Transponuj(&T)"
       Folding:
-        value: "Folding"
+        value: "Zwijanie kodu"
         submenu:
           "&Fold":
-            value: "Fold(&F)"
+            value: "Zwiń(&F)"
           "&Unfold":
-            value: "Unfold(&U)"
+            value: "Rozwiń(&U)"
           "Unfold &All":
-            value: "Unfold All(&A)"
+            value: "Rozwiń wszystko(&A)"
           "Fol&d All":
-            value: "Fold All(&D)"
+            value: "Zwiń wszystko(&D)"
           "Fold Level 1":
-            value: "Fold Level 1"
+            value: "Zwijanie poziom 1"
           "Fold Level 2":
-            value: "Fold Level 2"
+            value: "Zwijanie poziom 2"
           "Fold Level 3":
-            value: "Fold Level 3"
+            value: "Zwijanie poziom 3"
           "Fold Level 4":
-            value: "Fold Level 4"
+            value: "Zwijanie poziom 4"
           "Fold Level 5":
-            value: "Fold Level 5"
+            value: "Zwijanie poziom 5"
           "Fold Level 6":
-            value: "Fold Level 6"
+            value: "Zwijanie poziom 6"
           "Fold Level 7":
-            value: "Fold Level 7"
+            value: "Zwijanie poziom 7"
           "Fold Level 8":
-            value: "Fold Level 8"
+            value: "Zwijanie poziom 8"
           "Fold Level 9":
-            value: "Fold Level 9"
+            value: "Zwijanie poziom 9"
       "&Preferences":
         value: "Preferencje(&P)"
       "Config…":
@@ -135,11 +135,11 @@
       "Keymap…":
         value: "Mapowanie klawiszy..."
       "Snippets…":
-        value: "Snippets..."
+        value: "Wycinki..."
       "Stylesheet…":
-        value: "Stylesheet..."
+        value: "Arkusz stylów..."
       "Reflow Selection":
-        value: "Reflow Selection"
+        value: "Usuń formatowanie"
       Bookmark:
         value: "Zakładki"
         submenu:
@@ -156,7 +156,7 @@
       "Go to Line":
         value: "Idź do linii"
       "Select Grammar":
-        value: "Sprawdź gramatykę"
+        value: "Sprawdź gramatykę składni"
   "&View":
     value: "Widok(&V)"
     submenu:
@@ -190,18 +190,18 @@
           "Close Pane":
             value: "Zamknij panel"
       Developer:
-        value: "Developer"
+        value: "Opcje programistyczne"
         submenu:
           "Open In &Dev Mode…":
-            value: "Open In Dev Mode(&D)..."
+            value: "Otwórz w trybie programistycznym(&D)..."
           "&Reload Window":
-            value: "Reload Window(&R)"
+            value: "Przeładuj okno(&R)"
           "Run Package &Specs":
-            value: "Run Package Specs(&S)"
+            value: "Uruchom specyfikację pakietu(&S)"
           "Run &Benchmarks":
-            value: "Run Benchmarks(&B)"
+            value: "Uruchom benchmark(&B)"
           "Toggle Developer &Tools":
-            value: "Toggle Developer Tools(&T)"
+            value: "Narzędzia programistyczne(&T)"
       "&Increase Font Size":
         value: "Zwiększ rozmiar czcionki(&I)"
       "&Decrease Font Size":
@@ -209,7 +209,7 @@
       "Re&set Font Size":
         value: "Zresetuj rozmiar czcionki(&S)"
       "Toggle Soft &Wrap":
-        value: "Toggle Soft Wrap(&W)"
+        value: "Przełącz na miękkie zawijanie(&W)"
       "Toggle Command Palette":
         value: "Przełącz na linię poleceń"
       "Toggle Tree View":
@@ -218,33 +218,33 @@
     value: "Zaznaczenia(&S)"
     submenu:
       "Add Selection &Above":
-        value: "Add Selection Above(&A)"
+        value: "Dodaj zaznaczenie powyżej(&A)"
       "Add Selection &Below":
-        value: "Add Selection Below(&B)"
+        value: "Dodaj zaznaczenie poniżej(&B)"
       "S&plit into Lines":
-        value: "Split into Lines(&P)"
+        value: "Podziel na linie(&P)"
       "Single Selection":
-        value: "Single Selection"
+        value: "Pojedyncze zaznaczenie"
       "Select to &Top":
-        value: "Select to Top(&T)"
+        value: "Zaznacz do góry(&T)"
       "Select to Botto&m":
-        value: "Select to Bottom(&M)"
+        value: "Zaznacz do dołu(&M)"
       "Select &Line":
-        value: "Select Line(&L)"
+        value: "Zaznacz linię(&L)"
       "Select &Word":
-        value: "Select Word(&W)"
+        value: "Zaznacz słowo(&W)"
       "Select to Beginning of W&ord":
-        value: "Select to Beginning of Word(&O)"
+        value: "Zaznacz do początku słowa(&O)"
       "Select to Beginning of L&ine":
-        value: "Select to Beginning of Line(&I)"
+        value: "Zaznacz do początku linii(&I)"
       "Select to First &Character of Line":
-        value: "Select to First Character of Line(&C)"
+        value: "Zaznacz do pierwszego znaku w linii(&C)"
       "Select to End of Wor&d":
-        value: "Select to End of Word(&D)"
+        value: "Zaznacz do końca słowa(&D)"
       "Select to End of Lin&e":
-        value: "Select to End of Line(&E)"
+        value: "Zaznacz do końca linii(&E)"
       "Select Inside Brackets":
-        value: "Select Inside Brackets"
+        value: "Zaznacz wewnątrz nawiasów"
   "F&ind":
     value: "Szukaj(&I)"
     submenu:


Add "open .atom" button to general config view

--- src/app/general-config-panel.coffee
+++ src/app/general-config-panel.coffee
@@ -20,3 +20,8 @@
             @label for: 'core.autosave', =>
               @input id: 'core.autosave', type: 'checkbox'
               @text 'Auto-Save on Focus Change'
+
+        @button outlet: 'openDotAtomButton', type: 'button', class: 'btn btn-default', "Open '#{config.configDirPath}'"
+
+  initialize: ->
+    @openDotAtomButton.on 'click', -> atom.open(config.configDirPath)


Update minimap version

Update minimap version

--- lib/minimap-git-diff.coffee
+++ lib/minimap-git-diff.coffee
@@ -16,7 +16,7 @@

     @minimapModule = require @minimap.path

-    return @deactivate() unless @minimapModule.versionMatch('1.x')
+    return @deactivate() unless @minimapModule.versionMatch('2.x')
     @minimapModule.registerPlugin 'git-diff', this

   deactivate: ->