STL/dom moving element/manipulations_test from vows to mocha

--- stl/dom/test/unit/element/manipulation_test.coffee
+++ stl/dom/test/unit/element/manipulation_test.coffee
@@ -1,11 +1,11 @@
 #
 # The Element dom-manipulations module unit tests
 #
-# Copyright (C) 2011 Nikolay Nemshilov
+# Copyright (C) 2011-2012 Nikolay Nemshilov
 #
-{describe, assert, server, load} = require('../../test_helper')
-
-server.respond "/manipulations.html": """
+{Browser} = require('../../test_helper')
+
+Browser.respond "/manipulations.html": """

@@ -18,150 +18,146 @@
   """

-test_element = ->
-  load "/manipulations.html", this, (dom)->
-    this.dummy = new dom.Element('div', html: 'dummy')
-    new dom.Element(this.document.getElementById('test'))
-
-
-describe "Element Manipulations", module,
-  "#clone()":
-    topic: test_element
-
-    "should clone content and attributes": (element)->
+describe "Element Manipulations", ->
+  get = (callback)->
+    (done)->
+      Browser.open "/manipulations.html", ($, window)->
+        callback(new $.Element(window.document.getElementById('test')), $, window, window.document)
+        done()
+
+  describe "#clone()", ->
+
+    it "should clone content and attributes", get (element)->
       element._.innerHTML = "bla bla bla"
       clone = element.clone()

-      assert.equal clone._.innerHTML, element._.innerHTML
-      assert.equal clone._.id, element._.id
-
-    "should create a new instance of Element": (element)->
-      assert.instanceOf element.clone(), this.Element
-
-    "should attach a new dom-element in it": (element)->
-      assert.notSame element.clone()._, element._
-
-  "#clear()":
-    topic: test_element
-
-    "should remove all the child elements": (element)->
+      clone._.innerHTML.should.eql element._.innerHTML
+      clone._.id.should.eql element._.id
+
+    it "should create a new instance of Element", get (element, $)->
+      element.clone().should.be.instanceOf $.Element
+
+    it "should attach a new dom-element in it", get (element)->
+      element.clone()._.should.not.equal element._
+
+  describe "#clear()", ->
+
+    it "should remove all the child elements", get (element)->
       element._.innerHTML = 'some content'
       element.clear()

-      assert.equal element._.innerHTML, ''
-
-    "should return the element itself back": (element)->
-      assert.same element.clear(), element
-
-  "#empty()":
-    topic: test_element
-
-    "should say 'true' for an empty element": (element)->
-      element._.innerHTML = ''
-      assert.isTrue element.empty()
-
-    "should say 'true' for an element with spaces only": (element)->
+      element._.innerHTML.should.equal ''
+
+    it "should return the element itself back", get (element)->
+      element.clear().should.equal element
+
+  describe "#empty()", ->
+
+    it "should say 'true' for an empty element", get (element)->
+      element._.innerHTML = ''
+      element.empty().should.be.true
+
+    it "should say 'true' for an element with spaces only", get (element)->
       element._.innerHTML = "  \n\t\n  "
-      assert.isTrue element.empty()
-
-    "should say 'false' for an element with actual content": (element)->
+      element.empty().should.be.true
+
+    it "should say 'false' for an element with actual content", get (element)->
       element._.innerHTML = '0'
-      assert.isFalse element.empty()
-
-  "#html()":
-    topic: test_element
-
-    "should return the element's innerHTML": (element)->
+      element.empty().should.be.false
+
+  describe "#html()", ->
+
+    it "should return the element's innerHTML", get (element)->
       element._.innerHTML = 'some content'
-      assert.equal element.html(), 'some content'
-
-  "#html('content')":
-    topic: test_element
-
-    "should assign the new content": (element)->
+      element.html().should.equal 'some content'
+
+  describe "#html('content')", ->
+
+    it "should assign the new content", get (element)->
       element.html('some new content')

-      assert.equal element._.innerHTML, 'some new content'
-
-    "should return element itself back": (element)->
-      assert.same element.html('boo hoo'), element
-
-  "#text()":
-    topic: test_element
-
-    "should return the element's content as a text": (element)->
+      element._.innerHTML.should.equal 'some new content'
+
+    it "should return element itself back", get (element)->
+      element.html('boo hoo').should.equal element
+
+  describe "#text()", ->
+
+    it "should return the element's content as a text", get (element)->
       element._.innerHTML = 'some inner text'
-      assert.equal element.text(), 'some inner text'
-
-    "should convert HTML escapees into normal chars": (element)->
+      element.text().should.equal 'some inner text'
+
+    it "should convert HTML escapees into normal chars", get (element)->
       element._.innerHTML = 'Beevis & Butthead'
-      assert.equal element.text(), 'Beevis & Butthead'
-
-  "#text('content')":
-    topic: test_element
-
-    "should assign the text and escape special chars": (element)->
+      element.text().should.equal 'Beevis & Butthead'
+
+  describe "#text('content')", ->
+
+    it "should assign the text and escape special chars", get (element)->
       element.text('Beevis & Butthead')

-      assert.equal element._.innerHTML,
-        '<b>Beevis</b> & <u>Butthead</u>'
-
-    "should return the element itself back": (element)->
-      assert.same element.text('boo hoo'), element
-
-  "#remove()":
-    topic: test_element
-
-    "should remove the element out of it's parent element": (element)->
-      element._.appendChild(this.dummy._)
-
-      this.dummy.remove()
-
-      assert.equal element._.innerHTML, ''
-
-    "should return the element itself back": (element)->
-      assert.same element.remove(), element
-
-  "#replace('some content')":
-    topic: test_element
-
-    "should replace itself with a given content": (element)->
-      element.clear()._.appendChild(this.dummy._)
-      result = this.dummy.replace('some text')
-
-      assert.equal element._.innerHTML, 'some text'
-      assert.same  result, this.dummy
-
-
-  "#update(...)":
-    topic: test_element
-
-    "should replace all the element's content": (element)->
+      element._.innerHTML.should.equal '<b>Beevis</b> & <u>Butthead</u>'
+
+    it "should return the element itself back", get (element)->
+      element.text('boo hoo').should.equal element
+
+  describe "#remove()", ->
+
+    it "should remove the element out of it's parent element", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+
+      element._.appendChild(dummy._)
+
+      dummy.remove()
+
+      element._.innerHTML.should.equal ''
+
+    it "should return the element itself back", get (element)->
+      element.remove().should.equal element
+
+  describe "#replace('some content')", ->
+
+    it "should replace itself with a given content", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+
+      element.clear()._.appendChild(dummy._)
+      result = dummy.replace('some text')
+
+      element._.innerHTML.should.equal 'some text'
+      result.should.equal dummy
+
+
+  describe "#update(...)", ->
+
+    it "should replace all the element's content", get (element)->
       element._.innerHTML = 'old content'
       element.update('new content')

-      assert.equal element._.innerHTML, 'new content'
-
-    "should accept dom-wrappers as an argument": (element)->
-      element.update(this.dummy)
-
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should accept raw dom-elements": (element)->
-      element.update(this.dummy._)
-
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should accept arrays of elements": (element)->
-      element.update([this.dummy])
-
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should accept numbers as values": (element)->
+      element._.innerHTML.should.equal 'new content'
+
+    it "should accept dom-wrappers as an argument", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.update(dummy)
+
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should accept raw dom-elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.update(dummy._)
+
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should accept arrays of elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.update([dummy])
+
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should accept numbers as values", get (element)->
       element.update 888
-      assert.equal element._.innerHTML, '888'
-
-    "should eval any embedded scripts": (element)->
+      element._.innerHTML.should.equal '888'
+
+    it "should eval any embedded scripts", get (element, $, window)->
       element.update """
         bla bla bla

@@ -169,99 +165,110 @@

-        """
-
-      assert.equal this.window.test1, 'test-1'
-      assert.equal this.window.test2, 'test-2'
-
-    "should return the element itself back": (element)->
-      assert.same element.update('text'), element
-
-  "#append(item, item, item)":
-    topic: test_element
-
-    "should append elements to the end": (element)->
+        it """
+
+      window.test1.should.equal 'test-1'
+      window.test2.should.equal 'test-2'
+
+    it "should return the element itself back", get (element)->
+      element.update('text').should.equal element
+
+  describe "#append(item, item, item)", ->
+
+    it "should append elements to the end", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+
       element._.innerHTML = 'boo'
-      element.append this.dummy, new this.Element('div', html: 'new')
-
-      assert.equal element._.innerHTML, 'boo
dummy

new
'
-
-    "should append strings to the end": (element)->
+      element.append dummy, new $.Element('div', html: 'new')
+
+      element._.innerHTML.should.equal 'boo
dummy

new
'
+
+    it "should append strings to the end", get (element)->
       element._.innerHTML = 'boo'
       element.append 'eee', 'uuu'

-      assert.equal element._.innerHTML, 'booeeeuuu'
-
-
-  "#insertTo(...)":
-    topic: test_element
-
-    "should insert it into the specified elements": (element)->
-      element._.innerHTML = ''
-      this.dummy.insertTo(element)
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should insert it into raw dom-elements": (element)->
-      element._.innerHTML = ''
-      this.dummy.insertTo(element._)
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should insert it into elements by '#element-id'": (element)->
-      element._.innerHTML = ''
-      this.dummy.insertTo('#test')
-
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should return the element itself back": (element)->
-      assert.same this.dummy.insertTo('#test'), this.dummy
-
-
-  "#insert(...)":
-    topic: test_element
-
-    "should insert dom-wrappers": (element)->
-      element.clear().insert(this.dummy)
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should insert raw dom-elements": (element)->
-      element.clear().insert(this.dummy)
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should insert arrays of elements": (element)->
-      element.clear().insert([this.dummy])
-      assert.equal element._.innerHTML, '
dummy
'
-
-    "should insert plain html content": (element)->
+      element._.innerHTML.should.equal 'booeeeuuu'
+
+
+  describe "#insertTo(...)", ->
+
+    it "should insert it into the specified elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element._.innerHTML = ''
+      dummy.insertTo(element)
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should insert it into raw dom-elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element._.innerHTML = ''
+      dummy.insertTo(element._)
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should insert it into elements by '#element-id'", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+
+      element._.innerHTML = ''
+      dummy.insertTo('#test')
+
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should return the element itself back", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      dummy.insertTo('#test').should.be.same dummy
+
+
+  describe "#insert(...)", ->
+
+    it "should insert dom-wrappers", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert(dummy)
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should insert raw dom-elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert(dummy)
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should insert arrays of elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert([dummy])
+      element._.innerHTML.should.equal '
dummy
'
+
+    it "should insert plain html content", get (element)->
       element.clear().insert('dummy')
-      assert.equal element._.innerHTML, 'dummy'
-
-    "should insert numerical data": (element)->
+      element._.innerHTML.should.equal 'dummy'
+
+    it "should insert numerical data", get (element)->
       element.clear().insert 4.44
-      assert.equal element._.innerHTML, '4.44'
-
-    "should return element itself back": (element)->
-      assert.same element.insert('something'), element
-
-    "should allow insert on top of the elements": (element)->
+      element._.innerHTML.should.equal '4.44'
+
+    it "should return element itself back", get (element)->
+      element.insert('something').should.equal element
+
+    it "should allow insert on top of the elements", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
       element._.innerHTML = "boo"
-      element.insert(this.dummy, 'top')
-
-      assert.equal element._.innerHTML, '
dummy
boo'
-
-    "should allow to insert an element before another": (element)->
-      element.clear().insert(this.dummy)
-      this.dummy.insert(new this.Element('b', html: 'boo'), 'before')
-
-      assert.equal element._.innerHTML, 'boo
dummy
'
-
-    "should allow to insert things after the element": (element)->
-      element.clear().insert(this.dummy)
-      this.dummy.insert(new this.Element('b', html: 'boo'), 'after')
-
-      assert.equal element._.innerHTML, '
dummy
boo'
-
-    "should allow to insert things instead of the element": (element)->
-      element.clear().insert(this.dummy)
-      this.dummy.insert(new this.Element('b', html: 'boo'), 'instead')
-
-      assert.equal element._.innerHTML, 'boo'+      element.insert(dummy, 'top')
+
+      element._.innerHTML.should.equal '
dummy
boo'
+
+    it "should allow to insert an element before another", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert(dummy)
+      dummy.insert(new $.Element('b', html: 'boo'), 'before')
+
+      element._.innerHTML.should.equal 'boo
dummy
'
+
+    it "should allow to insert things after the element", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert(dummy)
+      dummy.insert(new $.Element('b', html: 'boo'), 'after')
+
+      element._.innerHTML.should.equal '
dummy
boo'
+
+    it "should allow to insert things instead of the element", get (element, $)->
+      dummy = new $.Element('div', html: 'dummy')
+      element.clear().insert(dummy)
+      dummy.insert(new $.Element('b', html: 'boo'), 'instead')
+
+      element._.innerHTML.should.equal 'boo'


Add PHP-specific snippets

Add PHP-specific snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -6,8 +6,7 @@
  .source.cpp,
  .source.cs,
  .source.java,
- .source.js,
- .source.php':
+ .source.js':

   # Conditional blocks

@@ -129,11 +128,27 @@

   'function':
     'prefix': 'f'
-    'body': "function ${1:name}($2) {\n\t$3\n}$4"
+    'body': "function ${1:name}( $2 ) {\n\t$3\n}$4"
+
+  'if':
+    'prefix': 'if'
+    'body': "if ( ${1:condition} ) {\n\t$2\n} $3"
+
+  'else if':
+    'prefix': 'elseif'
+    'body': "else if ( ${1:condition} ) {\n\t$2\n} $3"
+
+  'else':
+    'prefix': 'else'
+    'body': "else {\n\t$1\n} $2"
+
+  'for':
+    'prefix': 'for'
+    'body': "for ( ${1:\$i} = ${2:0}; ${1:\$i} < ${3:end}; ${1:\$i} += ${4:1} ) {\n\t$5\n}$6"

   'foreach':
     'prefix': 'foreach'
-    'body': "foreach ( ${1:object} as ${2:value} ) {\n\t${3:// code}\n}$4"
+    'body': "foreach ( ${1:\$object} as ${2:\$value} ) {\n\t${3:// code}\n}$4"

 # Inline PHP Blocks (meant to be intermixed with HTML)


Add saml config options

Add saml config options

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -425,6 +425,65 @@
 			rejectUnauthorized: process.env["SHARELATEX_LDAP_TLS_OPTS_REJECT_UNAUTH"] == "true"
 			ca:ca_paths  # e.g.'/etc/ldap/ca_certs.pem'

+if process.env["SHARELATEX_SAML_ENTRYPOINT"]
+	# NOTE: see https://github.com/bergie/passport-saml/blob/master/README.md for docs of `server` options
+	settings.externalAuth = true
+	settings.saml =
+		server:
+			# strings
+			entryPoint: process.env["SHARELATEX_SAML_ENTRYPOINT"]
+			callbackUrl: process.env["SHARELATEX_SAML_CALLBACK_URL"]
+			issuer: process.env["SHARELATEX_SAML_ISSUER"]
+			cert: process.env["SHARELATEX_SAML_CERT"]
+			privateCert: process.env["SHARELATEX_SAML_PRIVATE_CERT"]
+			decryptionPvk: process.env["SHARELATEX_SAML_DECRYPTION_PVK"]
+			signatureAlgorithm: process.env["SHARELATEX_SAML_SIGNATURE_ALGORITHM"]
+			identifierFormat: process.env["SHARELATEX_SAML_IDENTIFIER_FORMAT"]
+			attributeConsumingServiceIndex: process.env["SHARELATEX_SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX"]
+			authnContext: process.env["SHARELATEX_SAML_AUTHN_CONTEXT"]
+			authnRequestBinding: process.env["SHARELATEX_SAML_AUTHN_REQUEST_BINDING"]
+			validateInResponseTo: process.env["SHARELATEX_SAML_VALIDATE_IN_RESPONSE_TO"]
+			cacheProvider: process.env["SHARELATEX_SAML_CACHE_PROVIDER"]
+			logoutUrl: process.env["SHARELATEX_SAML_LOGOUT_URL"]
+			additionalLogoutParams: process.env["SHARELATEX_SAML_ADDITIONAL_LOGOUT_PARAMS"]
+			logoutCallbackUrl: process.env["SHARELATEX_SAML_LOGOUT_CALLBACK_URL"]
+			disableRequestedAuthnContext: process.env["SHARELATEX_SAML_DISABLE_REQUESTED_AUTHN_CONTEXT"] == 'true'
+			forceAuthn: process.env["SHARELATEX_SAML_FORCE_AUTHN"] == 'true'
+			skipRequestCompression: process.env["SHARELATEX_SAML_SKIP_REQUEST_COMPRESSION"] == 'true'
+			acceptedClockSkewMs: (
+				if _saml_skew = process.env["SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS"]
+					try
+						parseInt(_saml_skew)
+					catch e
+						console.error "Cannot parse SHARELATEX_SAML_ACCEPTED_CLOCK_SKEW_MS"
+				else
+					undefined
+			)
+			requestIdExpirationPeriodMs: (
+				if _saml_exiration = process.env["SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS"]
+					try
+						parseInt(_saml_expiration)
+					catch e
+						console.error "Cannot parse SHARELATEX_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS"
+				else
+					undefined
+			)
+
+		identityServiceName: process.env["SHARELATEX_SAML_IDENTITY_SERVICE_NAME"]
+
+	if _saml_additionalParams = process.env["SHARELATEX_SAML_ADDITIONAL_PARAMS"]
+		try
+			settings.saml.server.additionalAuthorizeParams = JSON.parse(_saml_additionalParams)
+		catch e
+			console.error "Cannot parse SHARELATEX_SAML_ADDITIONAL_PARAMS"
+
+	if _saml_additionalAuthorizeParams = process.env["SHARELATEX_SAML_ADDITIONAL_AUTHORIZE_PARAMS"]
+		try
+			settings.saml.server.additionalAuthorizeParams = JSON.parse(_saml_additionalAuthorizeParams )
+		catch e
+			console.error "Cannot parse SHARELATEX_SAML_ADDITIONAL_PARAMS"
+
+
 if settings.externalAuth and settings?.nav?.header?
 	results = []
 	for button in settings.nav.header


- added option to set lang code - removed commeted out learn header code - increased max pass length to 150

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -148,6 +148,12 @@

 	behindProxy: process.env["SHARELATEX_BEHIND_PROXY"] or false

+	# Site Languages
+	i18n:
+		subdomainLang:
+			www: {lngCode:process.env["SHARELATEX_SITE_LANGUAGE"] or "en", url: siteUrl}
+		defaultLng: process.env["SHARELATEX_SITE_LANGUAGE"] or "en"
+
 	# Spell Check Languages
 	# ---------------------
 	#
@@ -329,9 +335,6 @@
 	catch e
 		console.error("could not parse SHARELATEX_HEADER_NAV_LINKS, not valid JSON")

-# if process.env["SHARELATEX_PROXY_LEARN"]?
-# 	settings.nav.header.push({text: "help", class: "subdued", dropdown: [{text: "documentation", url: "/learn"}] })
-

 # Sending Email
 # -------------
@@ -381,7 +384,7 @@

 	settings.passwordStrengthOptions =
 		pattern: process.env["SHARELATEX_PASSWORD_VALIDATION_PATTERN"] or "aA$3"
-		length: {min:process.env["SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH"] or 8, max: process.env["SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH"] or 50}
+		length: {min:process.env["SHARELATEX_PASSWORD_VALIDATION_MIN_LENGTH"] or 8, max: process.env["SHARELATEX_PASSWORD_VALIDATION_MAX_LENGTH"] or 150}


ComputeController::destroy for managed vms

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -372,15 +372,29 @@

   destroy: (machine, force)->

-    return if methodNotSupportedBy machine
-
     destroy = (machine)=>
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+      if machine?.provider is 'managed'
+
+        options =
+          machineId : machine._id
+          provider  : machine.provider
+
+        @emit "MachineBeingDestroyed", machine
+
+        remote.api.ComputeProvider.remove options, (err)=>
+          return  if err
+
+          @_clearTrialCounts machine
+          @reset yes
+
+        return

       @eventListener.triggerState machine,
         status      : Machine.State.Terminating
         percentage  : 0
-
-      machine.getBaseKite( createIfNotExists = no ).disconnect()

       call = @getKloud().destroy { machineId: machine._id }


Fix for the case when two threads are creating same user

--- server/models/user.coffee
+++ server/models/user.coffee
@@ -33,7 +33,11 @@
         progress: Number

 usersSchema.methods.upsert = () ->
-    @update(this, {upsert: true})
+    # https://jira.mongodb.org/browse/SERVER-14322
+    try
+        @update(this, {upsert: true})
+    catch
+        logger.info "Could not upsert a user"

 usersSchema.methods.updateChocos = ->
     @chocos = await calculateChocos @_id


remove unused sorting methods

remove unused sorting methods

--- client/lanes/models/query/CollectionResult.coffee
+++ client/lanes/models/query/CollectionResult.coffee
@@ -35,15 +35,6 @@
     ensureLoaded: ->
         @collection.ensureLoaded().then(@)

-    sortByIndex: (field, asc) ->
-        @sort =
-            asc: asc, index: @query.fields.indexOf(field)
-            visibleIndex: @query.fields.visible.indexOf(field)
-        @collection.comparator = (a, b) ->
-            sort = field.comparator(a, b)
-            if asc then sort else sort * -1
-        @query.trigger('sort')
-        @collection.sort()

     rowRepresentation: (rowNum) ->
         @modelAt(rowNum)
@@ -51,6 +42,26 @@
     valueForField: (rowNum, field) ->
         @modelAt(rowNum)[field.id]

+    _updateSort: ->
+        field = @query.sortField
+        asc = @query.sortAscending
+
+
+        @collection.comparator = field.sortBy or (a, b) ->
+            a = a[field.id]; b = b[field.id]
+            result = if a < b then -1 else if b > a then 1 else 0
+            if asc then result else result * -1
+
+
+    #     @sort =
+    #         asc: asc, index: @query.fields.indexOf(field)
+    #         visibleIndex: @query.fields.visible.indexOf(field)
+    #     @collection.comparator = (a, b) ->
+    #         sort = field.comparator(a, b)
+    #         if asc then sort else sort * -1
+    #     @query.trigger('sort')
+    #     @collection.sort()
+
 Object.defineProperties Lanes.Models.Query.CollectionResult.prototype,
     length:
         get: -> @query.src.length


added skype_id

added skype_id

--- client/templates/user_profile/user_profile.coffee
+++ client/templates/user_profile/user_profile.coffee
@@ -3,8 +3,10 @@
     e.preventDefault()

     telephone_nr = $(e.target).find('[name=telephone_nr]').val()
+    skype_id = $(e.target).find('[name=skype_id]').val()

     Meteor.users.update this._id, {$set: {telephone_nr: telephone_nr}}
+    Meteor.users.update this._id, {$set: {skype_id: skype_id}}
     Router.go 'home'

   'click .cancel': (e) -> Router.go 'home'


Label

Label

--- both/collections/users.coffee
+++ both/collections/users.coffee
@@ -5,6 +5,7 @@
   picture:
     type: String
     optional:true
+    label: 'Profile picture'

   firstName:
     type: String


Yandex.Money donate

Yandex.Money donate

--- components/modules/Home/includes/js/donate.coffee
+++ components/modules/Home/includes/js/donate.coffee
@@ -21,7 +21,7 @@
 							Гаманець Bitcoin:1F8Wq9t562hHGcXfSDK7ZUZJhUjroZhwRg

-							Якщо ж у вас немає Bitcoin чи ви не знаєте як ним користуватись - можете поповнити наш гаманець Яндекс.Деньги (є можливість переказати на нього також Webmoney чи поповнити в терміналі як альтернативні відносно анонімні варіанти).
+							Якщо ж у вас немає Bitcoin чи ви не знаєте як ним користуватись - можете поповнити наш гаманець Яндекс.Деньги (є можливість переказати на нього також Webmoney чи поповнити в терміналі як альтернативні відносно анонімні варіанти).

 							Гаманець Яндекс.Деньги: 41001991014032


Fixes a bug in the rate service

Fixes a bug in the rate service

--- app/assets/javascripts/services/rate_service.js.coffee
+++ app/assets/javascripts/services/rate_service.js.coffee
@@ -1,11 +1,11 @@
 App.service 'rateService', ['$http', ($http) ->

-  @maximumRateBetween = (currency, startTime, endTime) ->
+  @maximumRateBetween = (startTime, endTime) ->

     start = moment(startTime).format("YYYYMMDD")
     ending = moment(endTime).format("YYYYMMDD")

-    parameters = "?start_date=" + start + "&end_date=" + ending + "&currency=" + currency
+    parameters = "?start_date=" + start + "&end_date=" + ending
     $http
       method: "GET"
       url: "/api/v1/rates" + parameters


Use correct ENV variable for node popup link targets.

--- app/assets/javascripts/views/node-popup.view.js.coffee
+++ app/assets/javascripts/views/node-popup.view.js.coffee
@@ -7,7 +7,7 @@
   ).property()

   linkTarget: (()->
-    if ENV.EMBEDDED then '_blank' else null
+    if Ember.ENV.EMBEDDED then '_blank' else null
   ).property()

 Wheelmap.NodePopupStatusDropdownView = Ember.View.extend


Status update form: return to original state if no text is entered.

--- app/assets/javascripts/views/user_index_view.js.coffee
+++ app/assets/javascripts/views/user_index_view.js.coffee
@@ -4,6 +4,12 @@
       @$(".status-form").autosize append: "\n"
       @$(".status-update-panel .panel-footer").slideDown(200)
     ).bind this
+    @$(".status-form").blur (->
+      if @$(".status-form").val().replace(/\s/g, '').length == 0
+        @$(".status-form").val('')
+        @$(".status-form").trigger "autosize.destroy"
+        @$(".status-update-panel .panel-footer").slideUp(200)
+    ).bind this

   willClearRender: ->
     @$(".status-form").trigger "autosize.destroy"


Converted if-elseif chain into switch statement

--- lib/messaging/incoming-message-dispatcher.coffee
+++ lib/messaging/incoming-message-dispatcher.coffee
@@ -26,24 +26,16 @@

       msg = message.substring messageCategory.length + 1

-      if messageCategory == 'TABLEUPDATED'
-        @emitter.emit 'variable-table-updated', msg
-      else if messageCategory == 'POSITIONUPDATED'
-        @emitter.emit 'position-updated', msg
-      else if messageCategory == 'CALLSTACKUPDATED'
-        @emitter.emit 'call-stack-updated', msg
-      else if messageCategory == 'READY'
-        @emitter.emit 'ready'
-      else if messageCategory == 'TERMINATECOMMUNICATION'
-        @emitter.emit 'terminate-communication'
-      else if messageCategory == 'ENDOFREPLAYTAPE'
-        @emitter.emit 'end-of-replay-tape'
-      else if messageCategory == 'AUTOSTEPPINGENABLED'
-        @emitter.emit 'auto-stepping-enabled'
-      else if messageCategory == 'AUTOSTEPPINGDISABLED'
-        @emitter.emit 'auto-stepping-disabled'
-      else
-        console.log "Cannot handle message category '#{messageCategory}'!"
+      switch messageCategory
+        when 'TABLEUPDATED' then @emitter.emit 'variable-table-updated', msg
+        when 'POSITIONUPDATED' then @emitter.emit 'position-updated', msg
+        when 'CALLSTACKUPDATED' then @emitter.emit 'call-stack-updated', msg
+        when 'READY' then @emitter.emit 'ready'
+        when 'TERMINATECOMMUNICATION' then @emitter.emit 'terminate-communication'
+        when 'ENDOFREPLAYTAPE' then @emitter.emit 'end-of-replay-tape'
+        when 'AUTOSTEPPINGENABLED' then @emitter.emit 'auto-stepping-enabled'
+        when 'AUTOSTEPPINGDISABLED' then @emitter.emit 'auto-stepping-disabled'
+        else console.log "Cannot handle message category '#{messageCategory}'!"

     return


only triggering autoreload on hiding the datepicker, otherwise what the user modifies the date editing the field directly the page will be reloaded submitting an invalid date

--- vendor/assets/javascripts/wice_grid_init.js.coffee
+++ vendor/assets/javascripts/wice_grid_init.js.coffee
@@ -85,7 +85,7 @@
     return

   $('.wice-grid-container .date-filter div[id$=_date_placeholder] input:text[data-provide=datepicker]').each (index, removeLink) ->
-    $(removeLink).datepicker().on 'changeDate', (hello) ->
+    $(removeLink).datepicker().on 'hide', (hello) ->
       $self = $(hello.currentTarget)

       eventToTriggerOnChange = $self.data('close-calendar-event-name')


Added 2013 link to new navbar

Added 2013 link to new navbar

--- web/coffee/navbar.coffee
+++ web/coffee/navbar.coffee
@@ -1,8 +1,10 @@

 apiOffline =
+  2013: "https://2013.picoctf.com"
   News: "/news"

 teacherLoggedIn =
+  2013: "https://2013.picoctf.com"
   Game: "/game/"
   Problems: "/problems"
   Shell: "/shell"
@@ -12,20 +14,24 @@
     FAQ: "/faq"
     Sponsors: "/sponsors"
     News: "/news"
+    Teachers: "/teachers"
   Account:
     Manage: "/account"
     Logout: "/logout"

 teacherLoggedInNoCompetition =
+  2013: "https://2013.picoctf.com"
   Classroom: "/classroom"
   FAQ: "/faq"
   News: "/news"
   Contact: "/contact"
+  Teachers: "/teachers"
   Account:
     Manage: "/account"
     Logout: "/logout"

 userLoggedIn =
+  2013: "https://2013.picoctf.com"
   Game: "/game/"
   Problems: "/problems"
   Shell: "/shell"
@@ -40,16 +46,18 @@
     Logout: "/logout"

 userLoggedInNoCompetition =
+  2013: "https://2013.picoctf.com"
   Team: "/team"
   FAQ: "/faq"
   News: "/news"
-  Contact: "/contact"
+  Contact: "/contact"
   Account:
     Manage: "/account"
     Logout: "/logout"

 userNotLoggedIn =
+  2013: "https://2013.picoctf.com"
   Registration: "/registration"
   # Scoreboard: "/scoreboard"
   # About:
@@ -59,6 +67,7 @@
   FAQ: "/faq"
   News: "/news"
   Contact: "/contact"
+  Teachers: "/teachers"
   Login: "/login"

 loadNavbar = (renderNavbarLinks, renderNestedNavbarLinks) ->


12th Commit

12th Commit

--- protractor.conf.coffee
+++ protractor.conf.coffee
@@ -16,4 +16,7 @@
     includeStackTrace: false
   @useAllAngular2AppRoots: true

+if process.env.TRAVIS
+  Config.capabilities = browserName: 'firefox'
+
 exports.config = Config


Don't bundle config files

Don't bundle config files

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -94,6 +94,10 @@
       '.travis.yml'
       'appveyor.yml'
       '.idea'
+      '.editorconfig'
+      '.lint'
+      '.lintignore'
+      '.eslintrc'
     ]

     packageNames.forEach (packageName) -> ignoredPaths.push(path.join(packageName, 'spec'))


Don't include benchmark folders

Don't include benchmark folders

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -88,14 +88,19 @@

     testFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}te?sts?#{_.escapeRegExp(path.sep)}")
     exampleFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}examples?#{_.escapeRegExp(path.sep)}")
+    benchmarkFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}benchmarks?#{_.escapeRegExp(path.sep)}")

     nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
     filterNodeModule = (pathToCopy) ->
+      return true if benchmarkFolderPattern.test(pathToCopy)
+
       pathToCopy = path.resolve(pathToCopy)
       nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)

     packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
     filterPackage = (pathToCopy) ->
+      return true if benchmarkFolderPattern.test(pathToCopy)
+
       pathToCopy = path.resolve(pathToCopy)
       packageFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)


Defer install to ensure the DOM is fully loaded early IEs

--- app/assets/javascripts/local_time/install.coffee
+++ app/assets/javascripts/local_time/install.coffee
@@ -1,7 +1,10 @@
 install = ->
   LocalTime.start()
+
+defer = (fn) ->
+  window.requestAnimationFrame?(fn) ? setTimeout(fn, 16)

 if document.readyState is "loading"
   addEventListener("DOMContentLoaded", install, false)
 else
-  install()
+  defer(install)


Remove unneeded atom-keymap ignores

Remove unneeded atom-keymap ignores

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -81,9 +81,6 @@
       path.join('resources', 'win')

       # These are only require in dev mode when the grammar isn't precompiled
-      path.join('atom-keymap', 'node_modules', 'loophole')
-      path.join('atom-keymap', 'node_modules', 'pegjs')
-      path.join('atom-keymap', 'node_modules', '.bin', 'pegjs')
       path.join('snippets', 'node_modules', 'loophole')
       path.join('snippets', 'node_modules', 'pegjs')
       path.join('snippets', 'node_modules', '.bin', 'pegjs')


Make the decorators classes

Make the decorators classes

--- app/assets/javascripts/sugar/rich_text.js.coffee
+++ app/assets/javascripts/sugar/rich_text.js.coffee
@@ -1,11 +1,10 @@
-markdownDecorator =
+class MarkdownDecorator
+  blockquote: (str)     -> ["", ("> " + line for line in str.split("\n")).join("\n"), ""]
   bold: (str)           -> ["**", str, "**"]
+  code: (str, language) -> ["```#{language}\n", str, "\n```"]
   emphasis: (str)       -> ["_", str, "_"]
+  image: (url)          -> ["![](", url, ")"]
   link: (url, name)     -> ["[", name, "](#{url})"]
-  image: (url)          -> ["![](", url, ")"]
-  blockquote: (str)     -> ["", ("> " + line for line in str.split("\n")).join("\n"), ""]
-  spoiler: (str)        -> ["
", str, "
"]
-  code: (str, language) -> ["```#{language}\n", str, "\n```"]
   quote: (text, html, username, permalink) ->
     wrapInBlockquote = (str) ->
       ("> " + line for line in str.split("\n")).join("\n")
@@ -15,15 +14,15 @@
       "Posted by #{username}:"
     quotedPost = wrapInBlockquote("#{cite}\n\n#{html}")
     ["", quotedPost + "\n\n", ""]
-
-htmlDecorator =
+  spoiler: (str)        -> ["
", str, "
"]
+
+class HtmlDecorator
+  blockquote: (str)     -> ["
", str, "
"]
   bold: (str)           -> ["", str, ""]
+  code: (str, language) -> ["```#{language}\n", str, "\n```"]
   emphasis: (str)       -> ["", str, ""]
+  image: (url)          -> [""]
   link: (url, name)     -> ["", name, ""]
-  image: (url)          -> [""]
-  blockquote: (str)     -> ["
", str, "
"]
-  spoiler: (str)        -> ["
", str, "
"]
-  code: (str, language) -> ["```#{language}\n", str, "\n```"]
   quote: (text, html, username, permalink) ->
     cite = if permalink
       "Posted by #{username}:"
@@ -32,6 +31,7 @@
     content = html.replace(/\n/g, "").replace(/
/g, "\n")
     quotedPost = "
#{cite}#{content}
"
     ["", quotedPost + "\n\n", ""]
+  spoiler: (str)        -> ["
", str, "
"]

 # Gets the selected text from an element
 getSelection = (elem) -> $(elem).getSelection().text
@@ -92,7 +92,7 @@
   emojiBar = $("

").insertBefore(textarea)
   emojiBar.hide()

-  decorator = -> markdownDecorator
+  decorator = -> new MarkdownDecorator

   if $(textarea).data('formats')
     formats = $(textarea).data('formats').split(" ")
@@ -112,10 +112,10 @@
     format = newFormat
     if format == "markdown"
       label = "Markdown"
-      decorator = -> markdownDecorator
+      decorator = -> new MarkdownDecorator
     else if format == "html"
       label = "HTML"
-      decorator = -> htmlDecorator
+      decorator = -> new HtmlDecorator

     formatButton.find("a").html(label)


Remove uneeded variable.

Remove uneeded variable.

--- app/assets/javascripts/views/card_view.js.coffee
+++ app/assets/javascripts/views/card_view.js.coffee
@@ -2,7 +2,6 @@
   templateName: 'card'
   classNames: ['card']
   classNameBindings: ['completed', 'isMessage']
-  canRemoveCard: false

   setupTooltip: (->
     @.$().find('.remove-card').tooltip()


improved open keyboard for smartphones

improved open keyboard for smartphones

--- client/view.coffee
+++ client/view.coffee
@@ -24,7 +24,7 @@

 	'click #menu-type div': (e, t) ->
 		Session.set "type", $(e.currentTarget).text()
-		$('input').focus()
+		Session.set 'openKeyboard', true
 		window.snapper.close()

@@ -38,4 +38,9 @@
 	window.snapper = new Snap
 		element: document.getElementById('content')
 		disable: 'right'
-		maxPosition: 201+		maxPosition: 201
+
+	window.snapper.on 'animated', ->
+	  if Session.get 'openKeyboard'
+	  	$('input').focus()
+	  	Session.set 'openKeyboard', false


raspberrypi3.coffee: Set Yocto version to Warrior

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@balena.io>

--- raspberrypi3.coffee
+++ raspberrypi3.coffee
@@ -30,7 +30,7 @@
 		machine: 'raspberrypi3'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-thud'
+		version: 'yocto-warrior'
 		deployArtifact: 'resin-image-raspberrypi3.resinos-img'
 		compressed: true


workers: use isnt instead of is not

workers: use isnt instead of is not

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1650,7 +1650,7 @@
     return callback null  if foreignAuthType is 'github'

     # TODO: temporarily disable recaptcha for groups
-    if slug? and slug is not 'koding'
+    if slug? and slug isnt 'koding'
       return callback null

     request.post url, {form:{response, secret}}, (err, res, raw)->


Improve some docs

Improve some docs

--- lib/assets/javascripts/up/flow.js.coffee
+++ lib/assets/javascripts/up/flow.js.coffee
@@ -2,16 +2,11 @@
 Changing page fragments programmatically
 ========================================

-This module contains Up's core functions to insert, change
-or destroy page fragments.
-
-\#\#\# Incomplete documentation!
-
-We need to work on this page:
-
-- Explain the UJS approach vs. pragmatic approach
-- Examples
-
+This module contains Up.js's core functions to [change](/up.replace) or [destroy](/up.destroy)
+  page fragments via Javascript.
+
+All the other Up.js modules (like [`up.link`](/up.link) or [`up.modal`](/up.modal))
+are based on this module.

 @class up.flow
 ###


/tasks returns a list. Reflecting that in the client

--- maproulette/static/js/maproulette.coffee
+++ maproulette/static/js/maproulette.coffee
@@ -289,9 +289,8 @@
   near = "#{map.getCenter().lng}|#{map.getCenter().lat}" if not near
   url = "/api/c/challenges/#{challenge}/tasks?near=#{near}"
   $.getJSON url, (data) ->
-    currentTask = data
-    currentTask.startTime = new Date().getTime()
-    showTask(data)
+    currentTask = data[0]
+    showTask(data[0])

 changeMapLayer = (layerUrl, layerAttrib = tileAttrib) ->
   ###
@@ -330,9 +329,7 @@
   setDelay 1, msgClose()
   payload = {
       "action": action,
-      "editor": editor,
-      "startTime": currentTask.startTime,
-      "endTime": new Date().getTime() }
+      "editor": editor}
   near = currentTask.center
   challenge = currentChallenge.id
   task_id = currentTask.id


history mode support

history mode support

--- lib/console/console.coffee
+++ lib/console/console.coffee
@@ -1,4 +1,3 @@
-# TODO: more generic commands
 # TODO: buffering
 # TODO: autocomplete

@@ -90,37 +89,47 @@
     ed.getCursors().length == 1 and
     ed.getCursors()[0].getBufferPosition().isEqual [0, 0]

+  setMode: (cell, mode) ->
+    ed = ed = cell.querySelector('atom-text-editor').getModel()
+    if not mode
+      delete ed.inkConsoleMode
+      if @view.defaultGrammar then ed.setGrammar @view.defaultGrammar
+      @view.setIcon cell, 'chevron-right'
+    else
+      ed.inkConsoleMode = mode
+      if mode.grammar then ed.setGrammar mode.grammar
+      @view.setIcon cell, mode.icon
+
   watchModes: (cell) ->
     @edListener?.dispose()
     ed = cell.querySelector('atom-text-editor').getModel()
     @edListener = ed.onWillInsertText (e) =>
       if (mode = @modes()[e.text]) and @cursorAtBeginning(ed) and not ed.inkConsoleMode
         e.cancel()
-        ed.inkConsoleMode = mode.name
-        if mode.grammar then ed.setGrammar mode.grammar
-        @view.setIcon cell, mode.icon
+        @setMode cell, mode

   cancelMode: (e) ->
     ed = e.currentTarget.getModel()
     cell = e.currentTarget.parentElement.parentElement
     if @cursorAtBeginning(ed) and ed.inkConsoleMode
-      delete ed.inkConsoleMode
-      if @view.defaultGrammar then ed.setGrammar @view.defaultGrammar
-      @view.setIcon cell, 'chevron-right'
+      @setMode cell

   logInput: ->
     @history ?= []
-    input = @view.getInputEd().getText()
-    if input && input != @history[@history.length-1] then @history.push input
+    ed = @view.getInputEd()
+    input = ed.getText()
+    mode = ed.inkConsoleMode
+    if input && input != @history[@history.length-1]?.input then @history.push {input, mode}
     @historyPos = @history.length
-    console.log @history

   previous: ->
     if @historyPos > 0
       @historyPos--
-      @view.getInputEd().setText @history[@historyPos]
+      @view.getInputEd().setText @history[@historyPos].input
+      @setMode @view.getInput(), @history[@historyPos].mode

   next: ->
     if @historyPos < @history.length
       @historyPos += 1
-      @view.getInputEd().setText (@history[@historyPos] or "")
+      @view.getInputEd().setText (@history[@historyPos]?.input or "")
+      @setMode @view.getInput(), @history[@historyPos]?.mode


Small change to authorize service

Small change to authorize service

--- src/app/common/services/AuthorizeService.coffee
+++ src/app/common/services/AuthorizeService.coffee
@@ -1,13 +1,13 @@
 service = (Restangular) ->
 	self = this
-	post = (id, cancel) ->
+	post = (id, accept) ->
 		rest = Restangular.all "decision"
 		json =
 			transaction_id: id
-			cancel: cancel
-		rest.post json
-	self.accept = (id) -> post id, false
-	self.deny = (id) -> post id, true
+			cancel: !accept
+		return rest.post json
+	self.accept = (id) -> post id, true
+	self.deny = (id) -> post id, false
 	self

 service.$inject = ["Restangular"];


#215 workaround replace conflicts with scope for replace-mode by R command

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -37,10 +37,14 @@

   run: (klass, properties) ->
     # Temporary set while command is running
-    scope = klass.getCommandNameWithoutPrefix()
-    @editorElement.classList.add(scope)
-    @subscribe new Disposable =>
-      @editorElement.classList.remove(scope)
+    if scope = klass.getCommandNameWithoutPrefix?()
+      # [NOTE]
+      # adding/removing 'replace' css class conflicts with `insert-mode.replace` mode.
+      # Activated with `R`. So simply make `replace` as exeption.
+      unless scope is 'replace'
+        @editorElement.classList.add(scope)
+        @subscribe new Disposable =>
+          @editorElement.classList.remove(scope)

     klass = Base.getClass(klass) if _.isString(klass)
     try


Use atom.resolveProxy when possible

Use atom.resolveProxy when possible

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -40,17 +40,23 @@
   setProxyServers: (callback) =>
     session = atom.getCurrentWindow().webContents.session
     session.resolveProxy 'http://atom.io', (httpProxy) =>
-      @applyProxyToEnv 'http_proxy', httpProxy, =>
-        session.resolveProxy 'https://atom.io', (httpsProxy) =>
-          @applyProxyToEnv 'https_proxy', httpsProxy, callback
-
-  applyProxyToEnv: (envName, proxy, callback) ->
+      @applyProxyToEnv('http_proxy', httpProxy)
+      session.resolveProxy 'https://atom.io', (httpsProxy) =>
+        @applyProxyToEnv('https_proxy', httpsProxy)
+        callback()
+
+  setProxyServersAsync: (callback) =>
+    httpProxyPromise = atom.resolveProxy('http://atom.io').then((proxy) => @applyProxyToEnv('http_proxy', proxy))
+    httpsProxyPromise = atom.resolveProxy('https://atom.io').then((proxy) => @applyProxyToEnv('https_proxy', proxy))
+    Promise.all([httpProxyPromise, httpsProxyPromise]).then(callback)
+
+  applyProxyToEnv: (envName, proxy) ->
     if proxy?
       proxy = proxy.split(' ')
       switch proxy[0].trim().toUpperCase()
         when 'DIRECT' then delete process.env[envName]
         when 'PROXY'  then process.env[envName] = 'http://' + proxy[1]
-    callback()
+    return

   runCommand: (args, callback) ->
     command = atom.packages.getApmPath()
@@ -65,7 +71,10 @@

     if atom.config.get('core.useProxySettingsWhenCallingApm')
       bufferedProcess = new BufferedProcess({command, args, stdout, stderr, exit, autoStart: false})
-      @setProxyServers -> bufferedProcess.start()
+      if atom.resolveProxy?
+        @setProxyServersAsync -> bufferedProcess.start()
+      else
+        @setProxyServers -> bufferedProcess.start()
       return bufferedProcess
     else
       return new BufferedProcess({command, args, stdout, stderr, exit})


Fixing Issue #552, Indentation bug with chained calls with nested object literals...

--- lexer.coffee
+++ lexer.coffee
@@ -205,6 +205,7 @@
       return @newlineToken indent
     else if size > @indent
       return @suppressNewlines() if noNewlines
+      @outdebt = 0
       diff = size - @indent
       @token 'INDENT', diff
       @indents.push diff
@@ -215,15 +216,24 @@

   # Record an outdent token or multiple tokens, if we happen to be moving back
   # inwards past several recorded indents.
-  outdentToken: (moveOut, noNewlines) ->
-    if moveOut > -@outdebt
-      while moveOut > 0 and @indents.length
-        lastIndent = @indents.pop()
-        @token 'OUTDENT', lastIndent
-        moveOut -= lastIndent
-    else
-      @outdebt += moveOut
-    @outdebt = moveOut unless noNewlines
+  outdentToken: (moveOut, noNewlines, close) ->
+    while moveOut > 0
+      len = @indents.length - 1
+      if @indents[len] is undefined
+        moveOut = 0
+      else if @indents[len] is @outdebt
+        moveOut -= @outdebt
+        @outdebt = 0
+      else if @indents[len] < @outdebt
+        @outdebt -= @indents[len]
+        moveOut -= @indents[len]
+      else
+        dent = @indents.pop()
+        dent -= @outdebt
+        moveOut -= dent
+        @outdebt = 0
+        @token 'OUTDENT', dent
+    @outdebt -= moveOut if dent
     @token 'TERMINATOR', "\n" unless @tag() is 'TERMINATOR' or noNewlines
     true


first stub at heregex

first stub at heregex

--- lexer.coffee
+++ lexer.coffee
@@ -33,7 +33,7 @@
   # Before returning the token stream, run it through the [Rewriter](rewriter.html)
   # unless explicitly asked not to.
   tokenize: (code, options) ->
-    code     = code.replace(/\r/g, '').replace /\s+$/, ''
+    code     = code.replace(/\r/g, '').replace TRAILING_SPACES, ''
     o        = options or {}
     @code    = code         # The remainder of the source code.
     @i       = 0            # Current character position we're parsing.
@@ -124,8 +124,11 @@
         return false unless match = SIMPLESTR.exec @chunk
         @token 'STRING', (string = match[0]).replace MULTILINER, '\\\n'
       when '"'
-        return false unless string = @balancedToken ['"', '"'], ['#{', '}']
-        @interpolateString string
+        return false unless string = @balancedString @chunk, [['"', '"'], ['#{', '}']]
+        if ~string.indexOf '#{'
+          @interpolateString string
+        else
+          @token 'STRING', @escapeLines string
       else
         return false
     @line += count string, '\n'
@@ -135,14 +138,14 @@
   # Matches heredocs, adjusting indentation to the correct level, as heredocs
   # preserve whitespace, but ignore indentation to the left.
   heredocToken: ->
-    return false unless match = @chunk.match HEREDOC
+    return false unless match = HEREDOC.exec @chunk
     heredoc = match[0]
     quote = heredoc.charAt 0
     doc = @sanitizeHeredoc match[2], {quote, indent: null}
-    if quote is '"'
+    if quote is '"' and ~doc.indexOf '#{'
       @interpolateString quote + doc + quote, heredoc: yes
     else
-      @token 'STRING', quote + doc + quote
+      @token 'STRING', quote + @escapeLines(doc, yes) + quote
     @line += count heredoc, '\n'
     @i += heredoc.length
     true
@@ -168,31 +171,41 @@

   # Matches regular expression literals. Lexing regular expressions is difficult
   # to distinguish from division, so we borrow some basic heuristics from
-  # JavaScript and Ruby, borrow slash balancing from `@balancedToken`, and
+  # JavaScript and Ruby, borrow slash balancing from `@balancedString`, and
   # borrow interpolation from `@interpolateString`.
   regexToken: ->
-    return false unless first = @chunk.match REGEX_START
+    return false if @chunk.charAt(0) isnt '/'
+    return @heregexToken match if match = HEREGEX.exec @chunk
+    return false unless first = REGEX_START.exec @chunk
     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 regex = @balancedString @chunk, [['/', '/']]
     return false unless end = @chunk[regex.length..].match REGEX_END
     flags = end[0]
-    if REGEX_INTERPOLATION.test regex
+    if ~regex.indexOf '#{'
       str = regex.slice 1, -1
-      str = str.replace REGEX_ESCAPE, '\\$&'
-      @tokens.push ['(', '('], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']
-      @interpolateString "\"#{str}\"", escapeQuotes: yes
+      @tokens.push ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']
+      @interpolateString "\"#{str}\"", regex: yes
       @tokens.push [',', ','], ['STRING', "\"#{flags}\""] if flags
-      @tokens.push [')', ')'], [')', ')']
+      @tokens.push ['CALL_END', ')']
     else
       @token 'REGEX', regex + flags
     @i += regex.length + flags.length
     true

-  # Matches a token in which the passed delimiter pairs must be correctly
-  # balanced (ie. strings, JS literals).
-  balancedToken: (delimited...) ->
-    @balancedString @chunk, delimited
+  # Matches experimental, multiline and extended regular expression literals.
+  heregexToken: (match) ->
+    [heregex, body, flags] = match
+    @i += heregex.length
+    unless ~body.indexOf '#{'
+      @token 'REGEX', '/' + body.replace(HEREGEX_OMIT, '') + '/' + flags
+      return true
+    @token 'IDENTIFIER', 'RegExp'
+    @tokens.push ['CALL_START', '(']
+    @interpolateString "\"#{body}\"", regex: yes, heregex: yes
+    @tokens.push [',', ','], ['STRING', '"' + flags + '"'] if flags
+    @tokens.push ['CALL_END', ')']
+    true

   # Matches newlines, indents, and outdents, and determines which is which.
   # If we can detect that the current line is continued onto the the next line,
@@ -399,7 +412,7 @@
             i += close.length - 1
             i += 1 unless levels.length
             break
-          else if starts str, open, i
+          if starts str, open, i
             levels.push(pair)
             i += open.length - 1
             break
@@ -419,49 +432,50 @@
   # new Lexer, tokenize the interpolated contents, and merge them into the
   # token stream.
   interpolateString: (str, options) ->
-    {heredoc, escapeQuotes} = options or {}
-    quote = str.charAt 0
-    return @token 'STRING', str if quote isnt '"' or str.length < 3
+    return @token 'STRING', @escapeLines(str, heredoc) if str.length < 5  # "#{}"
+    {heredoc, regex} = options or= {}
     lexer  = new Lexer
     tokens = []
-    i = pi = 1
-    end = str.length - 1
-    while i < end
-      if str.charAt(i) is '\\'
+    pi = 1
+    i  = 0
+    while char = str.charAt i += 1
+      if char is '\\'
         i += 1
-      else if expr = @balancedString str[i..], [['#{', '}']]
-        if pi < i
-          s = quote + @escapeLines(str[pi...i], heredoc) + quote
-          tokens.push ['STRING', s]
-        inner = expr.slice(2, -1).replace /^[ \t]*\n/, ''
-        if inner.length
-          inner = inner.replace RegExp('\\\\' + quote, 'g'), quote if heredoc
-          nested = lexer.tokenize "(#{inner})", line: @line
-          (tok[0] = ')') for tok, idx in nested when tok[0] is 'CALL_END'
-          nested.pop()
-          tokens.push ['TOKENS', nested]
-        else
-          tokens.push ['STRING', quote + quote]
-        i += expr.length - 1
-        pi = i + 1
-      i += 1
+        continue
+      unless char is '#' and str.charAt(i+1) is '{' and
+             (expr = @balancedString str[i+1..], [['{', '}']])
+        continue
+      if pi < i
+        tokens.push ['STRING', '"' + @escapeLines(str[pi...i], heredoc) + '"']
+      inner = expr.slice(1, -1).replace(LEADING_SPACES, '').replace(TRAILING_SPACES, '')
+      if inner.length
+        inner = inner.replace /\\\"/g, '"' if heredoc
+        nested = lexer.tokenize "(#{inner})", line: @line
+        (tok[0] = ')') for tok in nested when tok[0] is 'CALL_END'
+        nested.pop()
+        tokens.push ['TOKENS', nested]
+      else
+        tokens.push ['STRING', '""']
+      i += expr.length
+      pi = i + 1
     if i > pi < str.length - 1
-      s = str[pi...i].replace MULTILINER, if heredoc then '\\n' else ''
-      tokens.push ['STRING', quote + s + quote]
+      s = @escapeLines str.slice(pi, -1), heredoc
+      tokens.push ['STRING', '"' + s + '"']
     tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING'
-    interpolated = tokens.length > 1
+    interpolated = not regex and tokens.length > 1
     @token '(', '(' if interpolated
     {push} = tokens
-    for token, i in tokens
-      [tag, value] = token
+    for [tag, value], i in tokens
+      @token '+', '+' if i
       if tag is 'TOKENS'
         push.apply @tokens, value
-      else if tag is 'STRING' and escapeQuotes
-        escaped = value.slice(1, -1).replace(/"/g, '\\"')
-        @token tag, "\"#{escaped}\""
-      else
-        @token tag, value
-      @token '+', '+' if i < tokens.length - 1
+        continue
+      if regex
+        value = value.slice 1, -1
+        value = value.replace /[\\\"]/g, '\\$&'
+        value = value.replace HEREGEX_OMIT, '' if options.heregex
+        value = '"' + value + '"'
+      @token tag, value
     @token ')', ')' if interpolated
     tokens

@@ -544,9 +558,11 @@

 # Regex-matching-regexes.
 REGEX_START         = /^\/([^\/])/
-REGEX_INTERPOLATION = /[^\\]#\{.*[^\\]\}/
 REGEX_END           = /^[imgy]{0,4}(?![a-zA-Z])/
 REGEX_ESCAPE        = /\\[^#]/g
+
+HEREGEX      = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/
+HEREGEX_OMIT = /\s+(?:#.*)?/g

 # Token cleaning regexes.
 MULTILINER      = /\n/g
@@ -554,6 +570,8 @@
 HEREDOC_INDENT  = /\n+([ \t]*)/g
 ASSIGNED        = /^\s*@?[$A-Za-z_][$\w]*[ \t]*?[:=][^:=>]/
 NEXT_CHARACTER  = /^\s*(\S?)/
+LEADING_SPACES  = /^\s+/
+TRAILING_SPACES = /\s+$/

 # Compound assignment tokens.
 COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']


making REGEXP a bit stricter.

making REGEXP a bit stricter.


Send absolute energy in API

Send absolute energy in API

--- api/points.coffee
+++ api/points.coffee
@@ -59,6 +59,7 @@

 formatGamePoint = (game_point) ->
     result = game_point
+    result.energy = Math.abs(result.energy)
     return result

 selectGamePointFromReq = (pointId, gameId, callback) ->


use node-core writable for hashaccumulator

cannot take advantage of parallel stuff anyway

--- lib/transforms/join.coffee
+++ lib/transforms/join.coffee
@@ -1,5 +1,6 @@
 Readable = require 'readable-stream'
-{Writable,Transform} = require 'writable-stream-parallel'
+Writable = require 'readable-stream/writable'
+{Transform} = require 'writable-stream-parallel'
 _      = require 'underscore'
 debug  = require('debug') 'us:join'
 util = require 'util'


Some cleanup

Some cleanup

--- lib/views/synced-fs.coffee
+++ lib/views/synced-fs.coffee
@@ -16,6 +16,8 @@

     @fs = fs
     @ws = fs.ws
+    @sep = if /^win/.test(process.platform) then '\\' else '/'
+    @windows = if @sep == '\\' then true else false

     # Default text
     @text(" Learn")
@@ -27,8 +29,6 @@
     console.log("PATH: " + path)
     self = this
     files = []
-    sep = if /^win/.test(process.platform) then '\\' else '/'
-    console.log("SEP: " + sep)

     if file_sys.existsSync(path)
       #if sep == '\\'
@@ -43,7 +43,7 @@
       #else
       files = file_sys.readdirSync(path)
       files.forEach (file, index) ->
-        curPath = path + sep + file
+        curPath = path + @sep + file

         if sep == '\\'
           console.log('checking if dir. curPath: ' + curPath)
@@ -58,7 +58,7 @@
           self.deleteDirectoryRecursive(curPath)
           #out = crossSpawn.sync('takeown', ['/f', curPath, '/r', '/d', 'y'])
         else
-          if sep == '\\'
+          if @windows
             console.log('DELETING FILE: ' + curPath)
             #out2 = crossSpawn.sync('takeown', ['/F', curPath])
             #console.log('CURPATH: ' + curPath)
@@ -75,24 +75,24 @@
     @ws.onopen = (e) =>
       @element.style.color = '#73c990'
     @ws.onmessage = (e) =>
-      sep = if /^win/.test(process.platform) then '\\' else '/'
       try
         console.log(e.data)
         event = JSON.parse(e.data)
         switch event.event
           when 'remote_create'
-            console.log('Created: ' + event.location + sep + event.file)
+            console.log('Created: ' + event.location + @sep + event.file)
             if event.directory
-              if sep == '\\'
-                mkdirp.sync(atom.getUserWorkingDirPath() + sep + event.location + sep + event.file)
+              if @windows
+                exec('mkdir ' + atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file)
               else
-                exec('mkdir ' + atom.getUserWorkingDirPath() + sep + event.location + sep + event.file)
+                mkdirp.sync(atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file)
             else
-              if sep == '\\'
-                mkdirp.sync(atom.getUserWorkingDirPath() + sep + event.location)
+              if @windows
+                exec('mkdir ' + atom.getUserWorkingDirPath() + @sep + event.location)
               else
-                exec('mkdir ' + atom.getUserWorkingDirPath() + sep + event.location)
-              file_sys.openSync(atom.getUserWorkingDirPath() + sep + event.location + sep + event.file, 'a')
+                mkdirp.sync(atom.getUserWorkingDirPath() + @sep + event.location)
+
+              file_sys.openSync(atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file, 'a')

               @ws.send JSON.stringify({
                 action: 'request_content',
@@ -101,17 +101,20 @@
               })
           when 'content_response'
             content = new Buffer(event.content, 'base64').toString()
-            file_sys.writeFileSync atom.getUserWorkingDirPath() + sep + event.location + sep + event.file, content
+            file_sys.writeFileSync atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file, content
           when 'remote_delete'
             if event.directory
-              rmdir atom.getUserWorkingDirPath() + sep + event.location + sep + event.file, (error) ->
-                console.log(error)
-              #if event.location.length
-              #  this.deleteDirectoryRecursive atom.getUserWorkingDirPath() + sep + event.location + sep + event.file
-              #else
-              #  this.deleteDirectoryRecursive atom.getUserWorkingDirPath() + sep + event.file
+              if @windows
+                rmdir atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file, (error) ->
+                  console.log('RMDIR ERROR: ' + error)
+              else
+                if event.location.length
+                  this.deleteDirectoryRecursive atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file
+                else
+                  this.deleteDirectoryRecursive atom.getUserWorkingDirPath() + @sep + event.file
             else
-              delPath = atom.getUserWorkingDirPath() + sep + event.location + sep + event.file
+              delPath = atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file
+
               if file_sys.existsSync(delPath)
                 file_sys.unlinkSync(delPath)
           when 'remote_move_from'
@@ -120,11 +123,12 @@
             console.log('move_to')
           when 'remote_modify'
             if !event.directory
-              if sep == '\\'
-                mkdirp.sync(atom.getUserWorkingDirPath() + sep + event.location)
+              if @windows
+                exec('mkdir ' + atom.getUserWorkingDirPath() + @sep + event.location)
               else
-                exec('mkdir ' + atom.getUserWorkingDirPath() + sep + event.location)
-              file_sys.openSync(atom.getUserWorkingDirPath() + sep + event.location + sep + event.file, 'a')
+                mkdirp.sync(atom.getUserWorkingDirPath() + @sep + event.location)
+
+              file_sys.openSync(atom.getUserWorkingDirPath() + @sep + event.location + @sep + event.file, 'a')

               @ws.send JSON.stringify({
                 action: 'request_content',
@@ -132,12 +136,12 @@
                 file: event.file
               })
           when 'remote_open'
-            console.log('Opened: ' + event.location + sep + event.file)
+            console.log('Opened: ' + event.location + @sep + event.file)

             if event.location == ''
               atom.workspace.open(event.file)
             else
-              atom.workspace.open(event.location + sep + event.file)
+              atom.workspace.open(event.location + @sep + event.file)

       catch err
         console.log(err)


HTML5 parser available directly from require

--- lib/zombie/document.coffee
+++ lib/zombie/document.coffee
@@ -20,7 +20,7 @@
     QuerySelector:            true

   # JSDOM's way of creating a document.
-  jsdomBrowser = JSDOM.browserAugmentation(HTML) #, parser: browser.htmlParser)
+  jsdomBrowser = JSDOM.browserAugmentation(HTML, parser: browser.htmlParser)
   # HTTP header Referer, but Document property referrer
   document = new jsdomBrowser.HTMLDocument(referrer: referer)
   JSDOMSelectors.applyQuerySelector(document, HTML)


Updated descriptions From #36

Updated descriptions
From #36

--- app/config.coffee
+++ app/config.coffee
@@ -37,7 +37,7 @@
       }]
     work:
       label: 'Work'
-      description: 'The MOH and his team inspected the health impact of London’s trades and industries. From reporting on the working conditions of homeworkers to the effects of tanneries and factories on their surroundings, they had the power to issue fines for breaking local by-laws and public health laws.'
+      description: 'The medical officer inspected the health impact of London’s trades and industries. From reporting on the working conditions of homeworkers to the effects of tanneries and factories on their surroundings, they had the power to issue fines for breaking local by-laws and public health laws.'
       tools: [{
         label: 'Type of work'
         value: 'type'
@@ -85,7 +85,7 @@
     #   }]
     food:
       label: 'Food'
-      description: 'The health aspects of food production and sales came under the remit of the Medical Officer. His team of food inspectors investigated all aspects of the city’s food supply, from food importers and manufacturers to abattoirs, from butchers and bakers shops to street vendors. They were particularly concerned with food adulteration – identifying hidden substances that shouldn’t be there – and incidents of food poisoning.'
+      description: 'The medical officer inspected all aspects of the city’s food supply, from food importers and manufacturers to abattoirs, from butchers and bakers shops to street vendors. He was particularly concerned with food adulteration – identifying hidden substances that shouldn’t be there – and incidents of food poisoning.'
       tools: [{
         label: 'Type of food or drink'
         value: 'food'
@@ -109,7 +109,7 @@
       }]
     housing:
       label: 'Housing'
-      description: 'Much of the information about housing in the MOH reports is about the living conditions of London’s poor. The Medical Officers were well aware that poverty played a role in the health of Londoners. They reported on overcrowded slums and the results of poor construction such as leaking sewers, poor ventilation and lack of sunlight due to closely packed houses. They recommended measures such as fumigation after infestations and cleaning and whitewashing after disease outbreaks.'
+      description: 'Much of the information about housing in the reports is about the living conditions of London’s poor. The medical officers reported on overcrowded slums and the results of poor construction such as leaking sewers, poor ventilation and lack of sunlight due to closely packed houses. They recommended measures such as fumigation after infestations and cleaning after disease outbreaks.'
       tools: [{
         label: 'Housing problem'
         value: 'type'


only document supported parameters

only document supported parameters

--- app/server.coffee
+++ app/server.coffee
@@ -20,6 +20,7 @@
 server = modules.create 'server', ({fn, component_fn, cmd, component_cmd, settings, doc}) ->
   functions_promise = null
   function_names = null
+  server_option_names = null

   build_function_doc = (ctx, doc) ->
     FunctionDocsComponent {ctx, docs: docs.function_docs[doc.function_name]}
@@ -36,8 +37,13 @@
         value = summary: '(undocumented)'
       Documentation.register_documentation ['server', 'functions', n], value

-    _.each docs.parameter_docs, (d, n) ->
-      Documentation.register_documentation ['server', 'parameters', n], parameter_name: n, summary: 'A server parameter', complete: build_parameter_doc
+    _.each _.sortBy(server_option_names, _.identity), (n) ->
+      d = docs.parameter_docs[n]
+      if d?
+        value = parameter_name: n, summary: 'A server parameter', complete: build_parameter_doc
+      else
+        value = summary: '(undocumented)'
+      Documentation.register_documentation ['server', 'parameters', n], value

   Documentation.register_documentation ['server', 'functions'], index: true
   Documentation.register_documentation ['server', 'parameters'], index: true
@@ -181,12 +187,15 @@

   init: ->
     if settings.get('type') == 'lead'
+      server_option_names = ['start', 'from', 'end', 'until']
       unless function_names
         functions_promise = http.get(server.url 'functions').then (functions) ->
           function_names = _.filter Object.keys(functions), (f) -> f.indexOf('-') == -1
           initDocs()
     else
       function_names = graphite_function_names
+      server_option_names = Object.keys(docs.parameter_docs)
+      server_option_names.push 'start', 'end'
       initDocs()
       function_names

@@ -367,12 +376,6 @@
     # flatten one level of nested arrays
     targets = Array.prototype.concat.apply [], targets

-    if settings.get('type') == 'lead'
-      server_option_names = ['start', 'from', 'end', 'until']
-    else
-      server_option_names = Object.keys(docs.parameter_docs)
-      server_option_names.push 'start', 'end'
-
     server_options = _.extend {},
       _.pick(default_options, server_option_names),
       default_options.server_options,


Always show an item to prevent logo fallthrough.

--- picoCTF-web/web/coffee/navbar.coffee
+++ picoCTF-web/web/coffee/navbar.coffee
@@ -23,7 +23,8 @@
   News: "/news"

-userNotLoggedIn = {}
+userNotLoggedIn =
+  News: "/news"

 adminLoggedIn =
   Management: "/management"


Fix a typo causing module.paths to be always set as the cwd.

`options.fileName` was used instead of `options.filename`.

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -107,7 +107,7 @@
   mainModule.moduleCache and= {}

   # Assign paths for node_modules loading
-  dir = if options.fileName
+  dir = if options.filename
     path.dirname fs.realpathSync options.filename
   else
     fs.realpathSync '.'


Add some error formatting tests

Add some error formatting tests

Thanks to them i discovered that the parser errors where indicating the wrong token ¬¬

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -141,5 +141,9 @@
   # Disregard Jison's message, it contains redundant line numer information.
   message = "unexpected #{token}"

+  # FIXME The `loc` received does not correspond to the `token` token, but to
+  # the one before it instead. That's why the test in error_messages.coffee is
+  # failing. There should be a way to get the location data... maybe accessing
+  # the lexer's tokens?
   {first_line, first_column, last_line, last_column} = loc
   throw new CompilerError message, first_line, first_column, last_line, last_column


parser inline code fix

parser inline code fix

--- coffee/parser.coffee
+++ coffee/parser.coffee
@@ -129,6 +129,8 @@
          @addNode new Code {}
          last = i
          cur = i = text.indexOf TOKEN_MATCHES.code, i
+         if i is -1
+          cur = i = L
          add()
          @node = @node.parent()
          i += TOKEN_MATCHES.code.length


sample

sample

--- coffee/sample.coffee
+++ coffee/sample.coffee
@@ -14,6 +14,13 @@
  ###**<<https://github.com/vpj/docscript(Fork Me on Github)>>
  ---

+ Layout is inspired by style of books and handouts of Edward R. Tufte
+ and Richard Feynman.^^1^^
+
+ ---
+ ^^1^^ <<http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0000hB
+ (Book design: advice and examples)>>
+ ---

  We are using docscript to generate software user manuals at
  **<<http://forestpin.com(Forestpin)>>**.
@@ -46,11 +53,12 @@
     ---

- ##Visualizing numerical tables ^^1^^
+ ##Visualizing numerical tables ^^2^^

   ---
-  ^^1^^ Was taken from a blog post I wrote sometime back on
-  <<http://blog.varunajayasiri.com(my blog)>>.
+  ^^2^^ Was taken from a <<http://blog.varunajayasiri.com/variable-length-underlining-to-help-see-data-in-a-glance
+  (blog post)>> I wrote sometime back.
+  .
   ---

   Here's a number of alternatives to displaying table of numbers. We


Enable status line of holds list to display properly for openils 1.6 In version 1.6, the status line does not have values for queue position and total available copies.

--- src/account/holds.coffee
+++ src/account/holds.coffee
@@ -43,22 +43,32 @@
 	 <%= author %>
 	 <%= types %>
 	'''
-	tpl_status_line = (status) ->
-		switch status
-			when 'Ready for Pickup'
-				_.template '''
-
-					<%= status %> at <%= pickup %>
-					| Expires on <%= shelf %>
-
+	tpl_status_line = (o) ->
+		a = if o.status is 'Ready for Pickup'
+			'''
+			<%= status %> at <%= pickup %>
+			Expires on <%= shelf %>
+
+			'''
+		else
+			b = if o.queue_position and o.potential_copies
+					'''
+					Position <%= posn %> of <%= total %>
+					'''
+				else
+					''
+			c = if o.total_holds
+					'''
+					<%= avail %> Copies available
+					'''
+				else
+					''
+			d = '''
+				Pick up at <%= pickup %>
+				Expires on <%= expire %>
 				'''
-			else
-				_.template '''
-				Position <%= posn %> of <%= total %>
-				| <%= avail %> Copies available
-				| Pick up at <%= pickup %>
-				| Expires on <%= expire %>
-				'''
+			b + c + d
+		_.template a

 	pad = (x) -> if x < 10 then '0' + x else x
 	datestamp = (x) ->
@@ -138,7 +148,7 @@
 							author: "| #{o.mvr.author}" if o.mvr.author
 							types: "| #{(o.mvr.types_of_resource).join ', '}" if o.mvr.types_of_resource

-						$('.status_line', @).append (tpl_status_line o.status)
+						$('.status_line', @).append (tpl_status_line o)
 							status: o.status if o.status
 							posn:	o.queue_position
 							total:	o.total_holds


Import just i18nObj since file has no strings

closes: CORE-1330 CORE-1176

Since this file has no strings that it needs to translate. (Ie: no calls
To I18n.t(…) it can just import i18nObj.

Test plan:
* screenreader gradebook assignment subtotal grades component should
  Work the same as before

Change-Id: I7d0cd9c5cf7b56b35e1b5a7522a211ff042e924c
Reviewed-on: https://gerrit.instructure.com/147814
Tested-by: Jenkins
Reviewed-by: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
QA-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/ember/screenreader_gradebook/components/assignment_subtotal_grades_component.coffee
+++ app/coffeescripts/ember/screenreader_gradebook/components/assignment_subtotal_grades_component.coffee
@@ -16,7 +16,7 @@
 # with this program. If not, see <http://www.gnu.org/licenses/>.

 define [
-  'i18n!sr_gradebook'
+  'i18nObj'
   'ember'
   '../../../util/round'
   'jsx/gradebook/GradingSchemeHelper'


Implement left/right arrow buttons in vim mode

--- src/atom/vim-mode.coffee
+++ src/atom/vim-mode.coffee
@@ -37,6 +37,8 @@
       'l': 'move-right'
       'w': 'move-to-next-word'
       'b': 'move-to-previous-word'
+      'left': 'move-left'
+      'right': 'move-right'

     @handleCommands
       'insert': => @activateInsertMode()


Preventing exception when Model/View/Collection don't have name property

--- shared/model_utils.coffee
+++ shared/model_utils.coffee
@@ -45,6 +45,7 @@

 uppercaseRe = /([A-Z])/g
 utils.underscorize = (name) ->
+  return undefined unless name?
   name = name.replace(uppercaseRe, (char) -> "_" + char.toLowerCase())
   name = name.slice(1) if name[0] is '_'
   name


Fix redundant assignment of emptyValueLabel

--- src/coffee/cilantro/ui/controls/select.coffee
+++ src/coffee/cilantro/ui/controls/select.coffee
@@ -52,8 +52,6 @@
             super(options)

         initialize: (options) ->
-            @options.emptyValueLabel = options.emptyValueLable or '---'
-
             if not @collection
                 @collection = new SelectionCollection
                 @collection.url = => @model.links.values
@@ -62,7 +60,7 @@

         onCollectionSync: ->
             @collection.add(
-                {label: @options.emptyValueLabel, value: ''},
+                {label: @options.emptyValueLabel or '---', value: ''},
                 {at: 0})

             @render()


Support fixed starting angle

Support fixed starting angle

--- src/components/behaviors/bullet_circle.coffee
+++ src/components/behaviors/bullet_circle.coffee
@@ -5,6 +5,7 @@
   bulletCircle: (options) ->
     @shootConfig =  defaults(options,
       burstAmount: 10
+      angle: 'random'
     )
     this

@@ -17,7 +18,11 @@
     wo[1] *= (@scale ? 1)

     angleRange = 360 / @shootConfig.burstAmount
-    startAngle = Math.random() * angleRange
+    ang = if @shootConfig.angle == 'random'
+      Math.random()
+    else
+      @shootConfig.angle
+    startAngle = ang * angleRange

     for i in [0...@shootConfig.burstAmount]
       @shootConfig.projectile(


different buffering behaviors

different buffering behaviors

--- src/buffer-stream.coffee
+++ src/buffer-stream.coffee
@@ -28,29 +28,38 @@

 class BufferStream extends Stream
-    constructor: (@encoding = 'utf8') ->
+    constructor: ({@encoding, @size} = {}) ->
+        # defaults
+        @encoding ?= 'utf8'
+        @size ?= 'none'
+        # states
         @finished = no
         @paused = off
         @enabled = on
         @writable = on
         @readable = on
+        # values
         @splitters = []
-        @buffer = new Buffer(0)
         @__defineGetter__ 'length', () => @buffer.length
+        # init
+        @reset()
         super

     getBuffer:   () => @buffer
     toString:    () => @buffer.toString()
     destroySoon: () => @destroy()
     setEncoding: (@encoding) =>
+    setSize:     (@size) =>
+        @flush() if not @paused and @size is 'none'

     pause:  () =>
         @paused = on
-        @emit('pause')
+        @emit('pause') if @size is 'none'
     resume: () =>
+        @emit('drain') if @paused
         @paused = off
-        @emit('resume')
-        @flush() unless @enabled
+        @emit('resume') if @size is 'none'
+        @flush() if not @enabled or @size is 'none'
         if @finished
             @emit('end')
             @emit('close')
@@ -68,11 +77,16 @@
             @enabled = off
             @flush() unless @paused

+    reset: () =>
+        if typeof @size is 'number'
+            @buffer = new Buffer(@size)
+        else
+            @buffer = new Buffer(0)

     flush: () =>
         return unless @buffer.length
         @emit('data', @buffer)
-        @buffer = new Buffer(0)
+        @reset()

     split: (args...) =>
         if args.length is 1 and isArray(args[0])
@@ -93,12 +107,25 @@
         else @emit('error',
             new Error("Argument should be either a buffer or a string."))

-        if @enabled or @paused
-            @buffer = concat_(@buffer, buffer)
-            split.call(this) if @enabled and @splitters.length
-        else if not @paused
-            @emit('data', buffer)
-        yes # it's safe to immediately write again
+        if @size is 'flexible'
+            if @enabled or @paused
+                @buffer = concat_(@buffer, buffer)
+                split.call(this) if @enabled and @splitters.length
+            else if not @paused
+                @emit('data', buffer)
+            yes # it's safe to immediately write again
+
+        else if @size is 'none'
+            if @paused
+                @buffer = concat_(@buffer, buffer)
+                split.call(this) if @enabled and @splitters.length
+                no # because the sink is full
+            else
+                @emit('data', buffer)
+                yes # the sink is'nt full yet
+
+        else # size is a number
+            throw new Error("not implemented yet :(") # TODO

     end: (buffer, encoding) =>
         @write(buffer, encoding) if buffer


trim down reports to compile to speed up while in progress

--- queries/_runner.coffee
+++ queries/_runner.coffee
@@ -9,28 +9,28 @@
     name: "all"
     values: ["all"]
   }
-  {
-    name: "player"
-    sql: "GET_PLAYER_NAME(p.playerid,'','') = '?'"
-    values: ["Dan", "Tom", "JD", "Jeran", "Guest", "Spirk"]
-  }
-  {
-    name: "year"
-    sql: "YEAR(t.matchdate) = ?"
-    values: [2004,2007,2008,2009,2010,2011,2012]
-  }
-  {
-    name: "location"
-    sql: "GET_LOCATION_NAME(t.location, 'ADDY') = '?'"
-    values: [
-      "1217 (Pomona, CA)"
-      "23C (Pomona, CA)"
-      "Mt. Johnson (Rancho Cucamonga, CA)"
-      "425 (Upland, CA)"
-      "207E (Encino, CA)"
-      "14211 (Sherman Oaks, CA)"
-    ]
-  }
+  # {
+  #   name: "player"
+  #   sql: "GET_PLAYER_NAME(p.playerid,'','') = '?'"
+  #   values: ["Dan", "Tom", "JD", "Jeran", "Guest", "Spirk"]
+  # }
+  # {
+  #   name: "year"
+  #   sql: "YEAR(t.matchdate) = ?"
+  #   values: [2004,2007,2008,2009,2010,2011,2012]
+  # }
+  # {
+  #   name: "location"
+  #   sql: "GET_LOCATION_NAME(t.location, 'ADDY') = '?'"
+  #   values: [
+  #     "1217 (Pomona, CA)"
+  #     "23C (Pomona, CA)"
+  #     "Mt. Johnson (Rancho Cucamonga, CA)"
+  #     "425 (Upland, CA)"
+  #     "207E (Encino, CA)"
+  #     "14211 (Sherman Oaks, CA)"
+  #   ]
+  # }
 ]


always set state from url on init

always set state from url on init

--- resources/assets/coffee/react/beatmaps/main.coffee
+++ resources/assets/coffee/react/beatmaps/main.coffee
@@ -69,8 +69,8 @@
       loading: false
       filters: null
       isExpanded: null
-      @stateFromUrl()
-
+
+    @state = _.extend @state, @stateFromUrl()
     @state.columnCount = @columnCount()

     @backToTop = React.createRef()


Fix #350

Fix #350

--- gulp/tasks/build.coffee
+++ gulp/tasks/build.coffee
@@ -37,7 +37,7 @@

       options = minimist argv, known_options

-      options_str = ''
+      options_str = '-s'
       for k of known_options.default
         if options[k]
           if k == 'a'
@@ -45,4 +45,4 @@
           else
             options_str += " -#{k} #{options[k]}"

-      gulp.src('run.py').pipe $.start [{match: /run.py$/, cmd: 'python run.py -s'}]
+      gulp.src('run.py').pipe $.start [{match: /run.py$/, cmd: "python run.py #{options_str}"}]


payment: prevent user from seeing the form if she has a block

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -1,6 +1,9 @@
 class PricingAppView extends KDView

   JView.mixin @prototype
+
+  TOO_MANY_ATTEMPT_BLOCK_DURATION = 24 * 60 * 1000
+  TOO_MANY_ATTEMPT_BLOCK_KEY = 'BlockForTooManyAttempts'

   getInitialState : ->
     {
@@ -150,7 +153,35 @@
       callback()

-  planSelected: (options) ->
+  preventBlockedUser: (options, callback) ->
+
+    @appStorage.fetchValue TOO_MANY_ATTEMPT_BLOCK_KEY, (result) =>
+
+      return callback()  unless result
+
+      now = Date.now()
+      difference = now - result.timestamp
+
+      if difference < TOO_MANY_ATTEMPT_BLOCK_DURATION
+
+        @workflowController = new PaymentWorkflow { state: options, delegate: this }
+
+        @workflowController.on 'WorkflowStarted', =>
+          @workflowController.failedAttemptLimitReached()
+
+        return
+
+      @removeBlockFromUser()
+
+      return callback()
+
+
+  removeBlockFromUser: ->
+
+    KD.utils.defer => @appStorage.unsetKey TOO_MANY_ATTEMPT_BLOCK_KEY
+
+
+  planSelected: (options) -> @preventBlockedUser options, =>

     return KD.singletons
       .router


pricing: remove product list

pricing: remove product list

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -65,13 +65,6 @@

#{group.title} has been successfully created

#{subtitle}

           """
-
-      productList = products.map (product) ->
-        "
#{subscription.quantities[product.planCode]}x #{product.title}
"
-
-      thankYou.addSubView new KDCustomHTMLView
-        cssClass : "product-list"
-        partial  : productList.join ""

       if @formData.loggedIn
         thankYou.addSubView new KDButtonView


raspberrypi3-64.coffee: Set yocto version to thud

Signed-off-by: Andrei Gherzan <8dc7c09356db0b1762fb140617d7375c0a5a4131@balena.io>

--- raspberrypi3-64.coffee
+++ raspberrypi3-64.coffee
@@ -29,7 +29,7 @@
 		machine: 'raspberrypi3-64'
 		image: 'resin-image'
 		fstype: 'resinos-img'
-		version: 'yocto-sumo'
+		version: 'yocto-thud'
 		deployArtifact: 'resin-image-raspberrypi3-64.resinos-img'
 		compressed: true


You can now create documents with nested attributes

--- client/collection_view.coffee
+++ client/collection_view.coffee
@@ -86,6 +86,16 @@
     $create_row = $('#admin-create-row')
     new_doc = {}
     for field in $create_row.find('input[type="text"]')
-      new_doc[field.name] = field.value
+      # Unflatten the field names (e.g. foods.app -> {foods: {app:}})
+      keys = field.name.split('.')
+      final_key = keys.pop()
+
+      doc_iter = new_doc
+      for key in keys
+        doc_iter[key] = {} unless doc_iter[key]
+        doc_iter = doc_iter[key]
+
+      doc_iter[final_key] = field.value
+
       field.value = ''
     Meteor.call "admin_#{Session.get('collection_name')}_insert", new_doc


Fix snippets spec

Fix snippets spec

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -67,8 +67,7 @@
     waitsForPromise ->
       atom.packages.activatePackage("language-haskell")
     waitsForPromise ->
-      snippets = atom.packages.getLoadedPackage('snippets') ? \
-        atom.packages.loadPackage("#{process.env.HOME}/.atom/packages/snippets")
+      snippets = atom.packages.loadPackage('snippets')
       snippets.activate()
       .then ->
         Snippets = snippets.mainModule


Fix 'clears operator' spec.

Fix 'clears operator' spec.

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -1,3 +1,4 @@
+$ = require 'jquery'

 RootView = require 'root-view'

@@ -11,7 +12,29 @@
     atom.activatePackage('vim-mode', immediate: true)

     editor = rootView.getActiveView()
+    editor.enableKeymap()
     vimState = editor.vimState
+
+  keydown = (key, {element, ctrl, shift, alt, meta}={}) ->
+    dispatchKeyboardEvent = (target, eventArgs...) ->
+      e = document.createEvent("KeyboardEvent")
+      e.initKeyboardEvent eventArgs...
+      target.dispatchEvent e
+
+    dispatchTextEvent = (target, eventArgs...) ->
+      e = document.createEvent("TextEvent")
+      e.initTextEvent eventArgs...
+      target.dispatchEvent e
+
+    element ||= document.activeElement
+    eventArgs = [true, true, null, key, 0, ctrl, alt, shift, meta] # bubbles, cancelable, view, key, location
+
+    canceled = not dispatchKeyboardEvent(element, "keydown", eventArgs...)
+    dispatchKeyboardEvent(element, "keypress", eventArgs...)
+    if not canceled
+       if dispatchTextEvent(element, "textInput", eventArgs...)
+         element.value += key
+    dispatchKeyboardEvent(element, "keyup", eventArgs...)

   describe "initialize", ->
     it "puts the editor in command-mode initially", ->
@@ -41,15 +64,15 @@
       expect(editor.getCursorScreenPosition()).toEqual [1,0]

     it "clears the operator stack when commands can't be composed", ->
-      editor.trigger keydownEvent('d')
-      expect(vimMode.opStack.length).toBe 1
-      editor.trigger keydownEvent('x')
-      expect(vimMode.opStack.length).toBe 0
-
-      editor.trigger keydownEvent('d')
-      expect(vimMode.opStack.length).toBe 1
-      editor.trigger keydownEvent('\\') # \ is an unused key in vim
-      expect(vimMode.opStack.length).toBe 0
+      keydown('d', element: editor[0])
+      expect(vimState.opStack.length).toBe 1
+      keydown('x', element: editor[0])
+      expect(vimState.opStack.length).toBe 0
+
+      keydown('d', element: editor[0])
+      expect(vimState.opStack.length).toBe 1
+      keydown('\\', element: editor[0])
+      expect(vimState.opStack.length).toBe 0

     describe "the escape keybinding", ->
       it "clears the operator stack", ->


IDE: Moved including ndpane.js to under 3rd party section.

--- client/ide/includes.coffee
+++ client/ide/includes.coffee
@@ -2,9 +2,6 @@

   # namespacing
   "namespaces.coffee"
-
-  # ndpane 0.1.2
-  "../../website/a/js/ndpane.min.js"

   # finder
   "finder/idefindercontextmenucontroller.coffee"
@@ -76,6 +73,7 @@
   # 3rd party
   "../../website/a/js/sketchpad.js"
   "../../website/a/js/gapi.js"
+  "../../website/a/js/ndpane.min.js"

   # collaboration
   "RealTimeManager.coffee"


Fixed View.createElement

Fixed View.createElement

--- scripts/coffee/lib/view/view.coffee
+++ scripts/coffee/lib/view/view.coffee
@@ -6,9 +6,9 @@

 		@elements = []

-	newElement: (obj, @position) ->
+	newElement: (obj, position) ->

-		@elements.push el = new Element obj, @position
+		@elements.push el = new Element obj, position

 		return el


New hue pattern

New hue pattern

--- examples/node/midi_particles.coffee
+++ examples/node/midi_particles.coffee
@@ -128,8 +128,10 @@
         x = radius * Math.cos theta
         y = radius * Math.sin theta

-        # One rainbow per octave
-        hue = (p.note.key - 60 + 0.1) / 12.0
+        # Hop around between almost-opposing colors, eventually going
+        # around the rainbow. These ratios control what kinds of color
+        # schemes we get for different chords.
+        hue = (p.note.key - 60 + 0.1) * (7 / 12.0)
         p.color = OPC.hsv hue, 0.5, 0.8

         # Intensity mapped to velocity, nonlinear


Nicer output. Less warnings.

Nicer output. Less warnings.

--- src/coffee/export.coffee
+++ src/coffee/export.coffee
@@ -78,23 +78,24 @@
           @customerGroupService.buildMaps customerGroups.body.results
           @taxService.buildMaps taxes.body.results

-          console.log "Number of product types: #{productTypes.body.total}."
-          _.each productTypes.body.results, (productType) =>
+          console.log "Fetched #{productTypes.body.total} product type(s)."
+          _.each productTypes.body.results, (productType) ->
             header._productTypeLanguageIndexes(productType)

           processChunk = (products) =>
-            console.log "Fetched #{products.body.count} product(s) - #{products.body.offset}/#{products.body.total}."
+            current = products.body.offset + products.body.count
+            console.log "Fetched #{products.body.count} product(s) - #{(100 * current / products.body.total).toFixed(1)}% of #{products.body.total}."
             csv = []
             _.each products.body.results, (product) ->
               csv = csv.concat exportMapping.mapProduct(product, productTypes.body.results)
             @_saveCSV(outputFile, csv, true)

           @_saveCSV(outputFile, [ header.rawHeader ] )
-          .then () =>
+          .then (r) =>

             @_getProductService(staged)
             .process(processChunk, {accumulate: false})
-            .then (result) =>
+            .then (result) ->
               Promise.resolve "Export done."

   exportAsJson: (outputFile) ->


Added problems

Added problems

--- server/materials/data/topics/games_cyclic.coffee
+++ server/materials/data/topics/games_cyclic.coffee
@@ -9,6 +9,6 @@
     return {
         topic: topic("Игры на циклических графах", "Задачи на игры на циклических графах", [
             label("TODO"),
-            problem(80),
+            problem(3390),
         ]),
     }


Clear the select2 field after adding product to the table.

--- app/assets/javascripts/admin/suppliers_code.js.coffee
+++ app/assets/javascripts/admin/suppliers_code.js.coffee
@@ -16,6 +16,7 @@
   $(".variant_autocomplete").select2
     placeholder: "Select a variant"
     minimumInputLength: 4
+    allowClear: true
     ajax:
       url: Spree.routes.variants_search
       datatype: "json"
@@ -53,6 +54,7 @@

     # Reset fields
     $fieldset.data("sku", "").data("name", "").data("qty", 0)
+    $fieldset.find('.variant_autocomplete').select2('data', null)

     return false


Following the removal of universe

Following the removal of universe

--- admin/static/coffee/namespaces/index.coffee
+++ admin/static/coffee/namespaces/index.coffee
@@ -7,7 +7,6 @@
         # Use a namespace-specific template for the namespace list
         template: Handlebars.compile $('#database_list-template').html()
         className: 'databases_list-container'
-        error_template: Handlebars.compile $('#error_adding_namespace-template').html()
         alert_message_template: Handlebars.compile $('#alert_message-template').html()

         events:
@@ -40,18 +39,9 @@
                 @databases_length = databases.models.length

             if need_update
-                if @datacenters_length isnt 0 and @databases_length isnt 0
-                    @.$('.user_alert_space-cannot_create_namespace').html ''
-                    @.$('.user_alert_space-cannot_create_namespace').css 'display', 'none'
-                    @.$('.add-namespace').removeProp 'disabled'
-                else
-                    @.$('.user_alert_space-cannot_create_namespace').html @error_template
-                        need_datacenter: @datacenters_length is 0
-                        need_database: @databases_length is 0
-                        need_something: @datacenters_length is 0 or @databases_length is 0
-                    @.$('.user_alert_space-cannot_create_namespace').css 'display', 'block'
-                    @.$('.add-namespace').prop 'disabled', 'disabled'
-
+                @.$('.user_alert_space-cannot_create_namespace').html ''
+                @.$('.user_alert_space-cannot_create_namespace').css 'display', 'none'
+                @.$('.add-namespace').removeProp 'disabled'

         render: (message) =>
             super
@@ -184,7 +174,6 @@
         # Use a namespace-specific template for the namespace list
         tagName: 'table'
         template: Handlebars.compile $('#namespace_list-template').html()
-        error_template: Handlebars.compile $('#error_adding_namespace-template').html()

@@ -212,13 +201,8 @@

         add_namespace: (event) =>
             event.preventDefault()
-            if datacenters.length is 0
-                @.$('#user-alert-space').html @error_template
-                @.$('#user-alert-space').alert()
-            else
-                log_action 'add namespace button clicked'
-                @add_namespace_dialog.render()
-                $('#focus_namespace_name').focus()
+            @add_namespace_dialog.render()
+            $('#focus_namespace_name').focus()

         remove_namespace: (event) =>
             log_action 'remove namespace button clicked'


Do not send extra headers for multipart uploading parts

--- app/assets/javascripts/uploadcare/files/object.coffee
+++ app/assets/javascripts/uploadcare/files/object.coffee
@@ -220,7 +220,6 @@
                 , false
               xhr
             url: parts[partNo]
-            headers: {'X-UC-User-Agent': @settings._userAgent}
             crossDomain: true
             type: 'PUT'
             processData: false


update german pack for 1.4.0

update german pack for 1.4.0

--- app/assets/javascripts/uploadcare/locale/de.js.coffee
+++ app/assets/javascripts/uploadcare/locale/de.js.coffee
@@ -33,7 +33,7 @@
       tabs:
         names:
           preview: 'Vorschau'
-          file: 'Computer'
+          file: 'Lokale Dateien'
           facebook: 'Facebook'
           dropbox: 'Dropbox'
           gdrive: 'Google Drive'
@@ -47,8 +47,9 @@
         file:
           drag: 'Ziehen Sie eine Datei hier hinein'
           nodrop: 'Laden Sie Dateien von Ihrem PC hoch'
+          cloudsTip: 'Cloud Speicher<br>und soziale Dienste'
           or: 'oder'
-          button: 'Wählen Sie eine Datei von Ihrem PC'
+          button: 'Wählen Sie eine lokale Datei'
           also: 'Sie köhnen es auch wählen von'
         url:
           title: 'Dateien vom Web'


fix pt's pluralize

fix pt's pluralize

--- app/assets/javascripts/uploadcare/locale/pt.js.coffee
+++ app/assets/javascripts/uploadcare/locale/pt.js.coffee
@@ -94,6 +94,6 @@
 # Pluralization rules taken from:
 # http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 uploadcare.namespace 'uploadcare.locale.pluralize', (ns) ->
-  ns.en = (n) ->
-    return 'um' if n == 1
-    'outros'
+  ns.pt = (n) ->
+    return 'one' if n == 1
+    'other'


Remove WillReceiveProps

Remove WillReceiveProps

--- app/classifier/tasks/drawing/marking-initializer.cjsx
+++ app/classifier/tasks/drawing/marking-initializer.cjsx
@@ -14,9 +14,6 @@
     frame: 0
     getEventOffset: null

-  componentWillReceiveProps: ->
-    @findSchema()
-
   render: ->
     toolDescription = @props.task.tools[@props.annotation._toolIndex]
     marksFromCurrentTool = @props.annotation.value.filter (mark) =>
@@ -29,6 +26,7 @@
     </Draggable>

   handleInitStart: (e) ->
+    @findSchema
     tasks = require '..' # Circular

     taskDescription = @props.workflow.tasks[@props.annotation.task]


Add editor.completions to config schema

Add editor.completions to config schema

It was implicitly part of the schema already, but it used
to be set in a way that bypassed schema validations

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -98,6 +98,11 @@
         type: ['string', 'null']

       # These can be used as globals or scoped, thus defaults.
+      completions:
+        type: "array"
+        items:
+          type: "string"
+        default: []
       fontFamily:
         type: 'string'
         default: ''


Changed commandline varaible (path) to conf

--- src/config/config.coffee
+++ src/config/config.coffee
@@ -14,12 +14,12 @@

 	# Load the configuration-values
 	# user specified config override
-	if nconf.get("path")
-		nconf.file 'customConfigOverride', nconf.get('path')
+	if nconf.get("conf")
+		nconf.file 'customConfigOverride', nconf.get('conf')

-	# development environment override
+	# environment override
 	if environment
-		nconf.file 'developmentOverride', 'config/' + environment + '.json'
+		nconf.file 'environmentOverride', 'config/' + environment + '.json'

 	# load the default config file
 	nconf.file 'default', 'config/default.json'