Adding another test

Adding another test

--- tests/test_basics.coffee
+++ tests/test_basics.coffee
@@ -1,33 +1,62 @@
 assert  = require 'assert'
 leveldb = require '../lib'
 fs      = require 'fs'
+testCase = require('nodeunit').testCase

-exports['create delete database'] = (test) ->
-   path = "/tmp/test-database"
-   db = new leveldb.DB()
-   db.open path, {create_if_missing: true}, (err) =>
-      # make sure the database opened correctly.
-      assert.ok(not err)
+#{ File Assertions
+_file_exists = (path) ->
+   has_file = true
+   try
+      fs.statSync path
+   catch ex
+      assert.equal(ex.code, 'ENOENT', ex)
+      has_file = false

-      # make sure the database exits now, this will except otherwise
-      fs.statSync(path)
+   return has_file
+
+assert.fileExists = (path) ->
+   assert.ok(_file_exists(path), "File should exist: " + path)
+assert.fileDoesNotExist = (path) ->
+   assert.ok(not _file_exists(path), 'File should not exist: ' + path)
+#}

-      # close the database
-      db.close()

-      # clean up the disk space
-      leveldb.DB.destroyDB(path, {})
+module.exports = testCase({
+   setUp: (done) ->
+      @path = "/tmp/test-database"
+      done()

-      # make sure it cleaned up correctly.
-      no_file = false
-      try
-         fs.statSync path
-      catch ex
-         # The file did not exist
-         assert.equal(ex.code, 'ENOENT')
-         no_file = true
+   tearDown: (done) ->
+      if @db
+         leveldb.DB.destroyDB(@path, {})
+         assert.fileDoesNotExist(@path)
+      done()

-      assert.ok no_file
+   'create delete database': (test) ->
+      @db = new leveldb.DB()

-      test.done()
+      @db.open @path, {create_if_missing: true}, (err) =>
+         assert.ok(not err, err)
+         assert.fileExists @path

+         # close the database
+         @db.close()
+
+         # make sure it cleaned up correctly.
+         test.done()
+
+   'can not create if database already exists': (test) ->
+      assert.fileDoesNotExist @path
+      fh = fs.openSync(@path, "w+")
+      assert.ok fh
+      fs.writeSync(fh, "test data")
+      fs.closeSync(fh)
+
+      db = new leveldb.DB()
+
+      db.open @path, {}, (err) =>
+         assert.ok(err, "Since something exists at path the database should not be created.")
+         fs.unlinkSync(@path)
+         test.done()
+})
+


Refactor use Relax.warn

Refactor use Relax.warn

--- lib/assets/javascripts/relax/controller.coffee
+++ lib/assets/javascripts/relax/controller.coffee
@@ -109,7 +109,7 @@

         unless options.ignoreSamePathConstraint
           @progressBar?.done()
-          console.warn("Async response path is different from current page path")
+          Relax.Utils.warn("Async response path is different from current page path")
           return

       if options.pushState


Change the logger name of ComponentRegistry

--- src/app/Helpers/ComponentRegistry.coffee
+++ src/app/Helpers/ComponentRegistry.coffee
@@ -21,7 +21,7 @@

 AppEngine.initialiseComponentRegistry = (scopes) ->
   #taverse all components and add to registry
-  logger = new AppEngine.Helpers.Logger(@)
+  logger = new AppEngine.Helpers.Logger("ComponentRegistry")

   scopes = [scopes] if !_.isArray(scopes)


.

Former-commit-id: e5d53c0520ac35f1ec81fe7d5b2233f80bce2c4c [formerly 4a3c6d4d6b417bb341b6e4f7ed26589e6e3e920a] [formerly 10e8eca8c21369b83e1eb2ed51716ef184b3efec [formerly cbb01f932cb77631ff28439b342a2b84309a1036]] [formerly 3315d595cc9c237b055c856a8b7389899991d7bc [formerly 7005951dccfa32abea6140a08067197791f58fdd] [formerly e2c65c74818e666fc1bf25097f380d647c750c2c [formerly e2c65c74818e666fc1bf25097f380d647c750c2c [formerly 6482f0a6330ff02cf3b279688cb9a35da49a9893]]]]
Former-commit-id: daa39eecde559b56355fc02e556863495dd1a3fb [formerly ed9f4c48517a16fb7ce1eee8048a260bc45f8a1e] [formerly f05076307ae5fb91ccd11a4e5af571c3a210ea63 [formerly 41fb2baf1aff09c230d77f8966107c19fb590144]]
Former-commit-id: 7488285130c649680dc7a5ad933f181c39de50c8 [formerly 1fcba288509f2461e1c4ed1c73866cfb2e824c1a]
Former-commit-id: a52908fc647a673aeca09d2a509003466f7117c0
Former-commit-id: 26a50c45e879af9aadf0de22179d5e13a37b5445

--- @client/tags.coffee
+++ @client/tags.coffee
@@ -35,7 +35,9 @@

     DIV
       style:
-        padding: 50
+        width: CONTENT_WIDTH()
+        margin: 'auto'
+        paddingTop: 50

       DIV
         style:


Draggable

Draggable

- remove cui-selected class from drag helper clone

--- src/base/DragDropSelect/Draggable.coffee
+++ src/base/DragDropSelect/Draggable.coffee
@@ -484,7 +484,11 @@
 		# console.debug "FINAL helper pos:", globalDrag, diff,  "top", top, "left", left, dump(helper_pos)

 	getSourceCloneForHelper: ->
-		globalDrag.$source.cloneNode(true)
+		helper = globalDrag.$source.cloneNode(true)
+
+		# remove select state (hard to overwrite in CSS)
+		helper.classList.remove('cui-selected')
+		return helper

 	init_helper: (ev, $target, diff) ->
 		drag_source = globalDrag.$source


Remove old plan stuff, and put in the new stuff

--- js/inner.coffee
+++ js/inner.coffee
@@ -810,6 +810,7 @@

       success: () =>
         @cardInfo(stripeInfo.card) if stripeInfo?
+        @oldPlan(@chosenPlan())
         @oldTotal(@total())
         SammyApp.setLocation "/account/plans"
     )


Fix routing

Fix routing

--- js/outer.coffee
+++ js/outer.coffee
@@ -30,7 +30,7 @@
       $('html, body').animate({scrollTop: $(hash).offset().top}, 0)

     display: (cx) =>
-      if HAML? and HAML[@filename cx]?
+      if HAML? and HAML[@name]?
         @init(cx)
       else
         @load(cx)
@@ -111,10 +111,10 @@
       window.Hoptoad.notify data.error

   # Navigation
-  @get "/", (cx) => home.display(cx)
-  @get "/about.*", (cx) => about.display(cx)
-  @get "/privacy.*", (cx) => privacy.display(cx)
-  @get "/docs(.*)", (cx) => docs.display(cx)
+  @get "/about.*", (cx) -> about.display(cx)
+  @get "/privacy.*", (cx) -> privacy.display(cx)
+  @get "/docs(.*)", (cx) -> docs.display(cx)
+  @get "/", (cx) -> home.display(cx)

 # Global polyfills
@@ -122,4 +122,4 @@
   $.getScript("/assets/js/vendor/selectivizr-1.0.2.js")

 # Run the application
-$ -> circle.run window.location.pathname
+$ -> circle.run window.location.pathname.replace(/\/$/, '')


fix https://github.com/sagemathinc/smc/issues/559 -- "task list bug when using the #container hashtag"; might as well, since this was so easy to fix.

--- src/smc-webapp/tasks.coffee
+++ src/smc-webapp/tasks.coffee
@@ -568,7 +568,7 @@
             # First highlight hashtags --
             # Add the highlight-tag CSS class to every hashtag in the task list.
             # select searched-for hashtags by their special class:
-            selector = ("."+tags.substring(1) for tags in search when tags[0] == "#").join(',')
+            selector = (".smc-tasks-hashtag-"+tags.substring(1) for tags in search when tags[0] == "#").join(',')
             e.find(selector).addClass("highlight-tag")

             # Highlight all the search terms for
@@ -804,7 +804,7 @@
                 x0 = [0,0]
                 desc0 = ''
                 for x in v
-                    desc0 += desc.slice(x0[1], x[0]) + "" + desc.slice(x[0], x[1]) + ''
+                    desc0 += desc.slice(x0[1], x[0]) + "" + desc.slice(x[0], x[1]) + ''
                     x0 = x
                 desc = desc0 + desc.slice(x0[1])


Add checks for optional fields

Add checks for optional fields

- Output XSD validation error messages for better debugging

--- src/coffee/export/orders.coffee
+++ src/coffee/export/orders.coffee
@@ -31,6 +31,7 @@
   # 3) Update SPHERE orders with sync information
   ###
   execute: (mappings, targetPath) ->
+    @mappings = mappings
     @startTime = new Date().getTime()
     orderQuery = @_buildOrderQuery @_options.numberOfDays

@@ -38,18 +39,18 @@
       api.queryOrders(@rest, orderQuery)
       @_loadOrdersXsd './examples/xsd/orders.xsd'
     ], (fetchedOrders, ordersXsd) =>
-      utils.assertProductIdMappingIsDefined mappings.productImport.mapping
-      utils.assertVariationIdMappingIsDefined mappings.productImport.mapping
-      utils.assertSkuMappingIsDefined mappings.productImport.mapping
+      utils.assertProductIdMappingIsDefined @mappings.productImport.mapping
+      utils.assertVariationIdMappingIsDefined @mappings.productImport.mapping
+      utils.assertSkuMappingIsDefined @mappings.productImport.mapping
       if fetchedOrders
-        @client.channels.ensure(mappings.orderExport.channel.key, mappings.orderExport.channel.role)
+        @client.channels.ensure(@mappings.orderExport.channel.key, @mappings.orderExport.channel.role)
         .then (createOrGetChannelResult) =>
           @channel = createOrGetChannelResult.body
           # TODO refactor as soon as collection query in SPHERE.IO is fixed
           @unsyncedOrders = @_filterByUnsyncedOrders(fetchedOrders.results, @channel)
           if _.size(@unsyncedOrders) > 0
             @logger.info "[OrderExport] Orders to export count: '#{_.size @unsyncedOrders}'"
-            xmlOrders = @_ordersToXML(@unsyncedOrders, mappings.productImport.mapping)
+            xmlOrders = @_ordersToXML(@unsyncedOrders)
             content = xmlOrders.end(pretty: true, indent: '  ', newline: "\n")
             @_validateXML(content, ordersXsd)
             @fileName = @_getFileName(targetPath)
@@ -140,15 +141,15 @@
     _.filter orders, (order) ->
       _.size(order.syncInfo) is 0 or _.find order.syncInfo, (syncInfo) -> syncInfo.channel.id isnt channel.id

-  _ordersToXML: (orders, mappings) ->
+  _ordersToXML: (orders) ->
     root = builder.create('Orders', { 'version': '1.0', 'encoding': 'UTF-8'})
     root.att('count', orders.length)
     _.each orders, (order, index, list) =>
       orderXML = root.ele("Order", {num: index + 1})
-      @_orderToXML(order, orderXML, mappings)
+      @_orderToXML(order, orderXML)
     root

-  _orderToXML: (order, orderXML, mappings) =>
+  _orderToXML: (order, orderXML) =>
     @logger.info "[OrderExport] Processing order with orderNumber: '#{order.orderNumber}', id: '#{order.id}'"
     orderXML.e('OrderId').t(order.orderNumber)
     orderXML.e('OrderDate').t(order.createdAt)
@@ -156,14 +157,17 @@
     #<xs:element ref="PaymentStatus" minOccurs="0"/>
     #<xs:element ref="CustomerId" minOccurs="0"/>
     #<xs:element ref="TotalAmountProducts" minOccurs="0"/>
-    orderXML.e('TotalAmountProductsNetto').t(@_toAmount(order.taxedPrice.totalNet.centAmount))
-    orderXML.e('TotalAmountVat').t(@_toAmount(order.taxedPrice.taxPortions[0].amount.centAmount))
+    if order.taxedPrice
+      orderXML.e('TotalAmountProductsNetto').t(@_toAmount(order.taxedPrice.totalNet.centAmount)) if order.taxedPrice?.totalNet
+      orderXML.e('TotalAmountVat').t(@_toAmount(order.taxedPrice.taxPortions[0].amount.centAmount)) if order.taxedPrice?.taxPortions?[0].amount
     shippingInfo = order.shippingInfo
     throw new Error "Can not export order as it does not contain shipping info; order id: '#{order.id}'" if not shippingInfo
     shippingCost = @_toAmount(shippingInfo.price.centAmount)
     orderXML.e('ShippingCost').t(shippingCost)
     #<xs:element ref="PaymentCost" minOccurs="0"/>
-    orderXML.e('TotalAmount').t(@_toAmount(order.taxedPrice.totalGross.centAmount))
+    totalAmount = order.totalPrice
+    totalAmount = order.taxedPrice.totalGross.centAmount if order.taxedPrice?.totalGross
+    orderXML.e('TotalAmount').t(@_toAmount(totalAmount))
     #<xs:element ref="Comment" minOccurs="0"/>
     #<xs:element ref="CostsChangings" minOccurs="0"/>
     # TODO use real data for PaymentMethod
@@ -180,12 +184,12 @@
     lineItemsXML = orderXML.ele("OrderLines", {'count': order.lineItems.length})
     _.each order.lineItems, (lineItem, index, list) =>
       lineItemXML = lineItemsXML.ele("OrderLine", {num: index + 1})
-      @_lineItemToXML(lineItem, lineItemXML, mappings)
-
-  _lineItemToXML: (lineItem, lineItemXML, mappings) =>
+      @_lineItemToXML(lineItem, lineItemXML)
+
+  _lineItemToXML: (lineItem, lineItemXML) =>
     lineItemXML.e('OrderLineId').t(lineItem.id)
-    productId = utils.getVariantAttValue(mappings, 'ProductId', lineItem.variant)
-    variationId = utils.getVariantAttValue(mappings, 'VariationId', lineItem.variant)
+    productId = utils.getVariantAttValue(@mappings.productImport.mapping, 'ProductId', lineItem.variant)
+    variationId = utils.getVariantAttValue(@mappings.productImport.mapping, 'VariationId', lineItem.variant)
     lineItemXML.e('ProductId').t(productId)
     name = _.values(lineItem.name)[0]
     lineItemXML.e('ProductName').t(name)
@@ -218,12 +222,15 @@
     el.e('City').t(address.city) if address.city
     el.e('Country').t(address.country) if address.country
     el.e('PhonePrivate').t(address.phone) if address.phone
-    el.e('EmailAddress').t(address.email) if address.email
+    if address.email
+      el.e('EmailAddress').t(address.email)
+    else if @mappings.orderExport.defaultEmail
+      el.e('EmailAddress').t(@mappings.orderExport.defaultEmail)

   _validateXML: (xml, xsd) ->
     xmlDoc = libxmljs.parseXmlString xml
     xsdDoc = libxmljs.parseXmlString xsd
     result = xmlDoc.validate(xsdDoc)
-    throw new Error "XML validation against XSD schema failed. XML content: \n #{xml}" if not result
+    throw new Error "XML validation against XSD schema failed. Validation errors: #{xmlDoc.validationErrors} XML content: #{xml}" if not result

 module.exports = Orders


Fix function call for map money on export.

--- src/coffee/exportmapping.coffee
+++ src/coffee/exportmapping.coffee
@@ -111,7 +111,7 @@
     switch attributeTypeDef.name
       when CONS.ATTRIBUTE_TYPE_SET then @_mapSetAttribute(attribute, attributeTypeDef)
       when CONS.ATTRIBUTE_TYPE_ENUM, CONS.ATTRIBUTE_TYPE_LENUM then attribute.value.key
-      when CONS.ATTRIBUTE_TYPE_MONEY then _mapMoney attribute
+      when CONS.ATTRIBUTE_TYPE_MONEY then @_mapMoney attribute
       else attribute.value

   _mapLocalizedAttribute: (attribute, productType, row) ->


bump version to 1.0.9 in ezBreadcrumbs.coffee

--- src/coffee/ezBreadcrumbs.coffee
+++ src/coffee/ezBreadcrumbs.coffee
@@ -2,7 +2,7 @@
  * EzBreadcrumbs - jQuery plugin that transforms a breadcrumbs to a responsive one. Useful when making responsive websites.
  * Copyright(c) Exequiel Ceasar Navarrete <exequiel.navarrete@ninthdesign.com>
  * Licensed under MIT
- * Version 1.0.8
+ * Version 1.0.9
 ###
 (($, window, document, undefined_) ->
     "use strict"


flesh out rx-mock test

flesh out rx-mock test

--- src/test/test_render.coffee
+++ src/test/test_render.coffee
@@ -20,6 +20,14 @@
     render(world)

   test "rx_mock exists", (t) ->
+    world = god(rx_mock(rx), {})
+    t.ok world.T, 'Has HTML Tags'
+    t.ok world.T().div, 'Has HTML div'
+    t.ok world.SVG, 'Has SVG Tags'
+    t.ok world.SVG().path, 'Has SVG path'
+    t.end()
+
+  test "render_dict", (t) ->
     result = render_dict({})
     console.log result
     t.equal result.tag, "div", "mock div"


more mac hacks

more mac hacks

--- init-nw.coffee
+++ init-nw.coffee
@@ -222,22 +222,21 @@
       mb = new gui.Menu type: 'menubar'
       mb.createMacBuiltin 'Dyalog'
       mb.items[0].submenu.removeAt 0 # remove built-in "About Dyalog" that doesn't do anything useful
-      # for "Special Characters..." and "Start Dictation..." see https://github.com/nwjs/nw.js/issues/2812
-      for x, ix in m # try to merge new menu with existing menu:
-        if x[''].replace(/_/, '') == 'Help' then x[''] += ' ' # get rid of Help>Search
+      # For "Special Characters..." and "Start Dictation...": see https://github.com/nwjs/nw.js/issues/2812
+      # I discovered that if I remove "Copy" they go away, but then Cmd+C stops working.
+      for x, ix in m
+        if x[''].replace(/_/, '') in ['Edit', 'Help'] then x[''] += ' ' # a hack to get rid of Help>Search
         ourMenu = render x
         if ix
-          theirMenu = null; for y in mb.items when y.label == ourMenu.label then theirMenu = y; break
+          theirMenu = null; for y in mb.items when y.label == ourMenu.label.replace(/\ $/, '') then theirMenu = y; break
         else
           theirMenu = mb.items[0]
+        if theirMenu # try to merge new menu with existing menu
+          ourMenu.submenu.append new gui.MenuItem type: 'separator'
+          while theirMenu.submenu.items.length
+            y = theirMenu.submenu.items[0]; theirMenu.submenu.remove y
+            ourMenu.submenu.append y
+          mb.remove theirMenu
         mb.insert ourMenu, ix
-        if theirMenu
-          i = 0
-          while ourMenu.submenu.items?.length
-            y = ourMenu.submenu.items[0]; ourMenu.submenu.remove y; theirMenu.submenu.insert y, i++
-          theirMenu.submenu.insert new gui.MenuItem(type: 'separator'), i
-          mb.remove ourMenu
-          mb.remove theirMenu
-          mb.insert theirMenu, ix
       nww.menu = mb
       return


Use OnigRegExp::testSync

Use OnigRegExp::testSync

--- src/language-mode.coffee
+++ src/language-mode.coffee
@@ -293,7 +293,7 @@
     return unless increaseIndentRegex and decreaseIndentRegex

     line = @buffer.lineForRow(bufferRow)
-    return unless decreaseIndentRegex.test(line)
+    return unless decreaseIndentRegex.testSync(line)

     currentIndentLevel = @editor.indentationForBufferRow(bufferRow)
     return if currentIndentLevel is 0
@@ -302,7 +302,7 @@
     precedingLine = @buffer.lineForRow(precedingRow)

     desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
-    desiredIndentLevel -= 1 unless increaseIndentRegex.test(precedingLine)
+    desiredIndentLevel -= 1 unless increaseIndentRegex.testSync(precedingLine)
     if desiredIndentLevel >= 0 and desiredIndentLevel < currentIndentLevel
       @editor.setIndentationForBufferRow(bufferRow, desiredIndentLevel)


r/w updates

r/w updates

--- grovepi.coffee
+++ grovepi.coffee
@@ -27,20 +27,35 @@
 		wire = new i2c @address, device: "/dev/i2c-1"
 		@wire = wire  if debug.mode

+	send: ( cmd, data, callback ) ->
+		writeCmd = if Array.isArray data then 'writeBytes' else 'writeByte'
+		wire[writeCmd] cmd, data, ( error ) ->
+			callback()
+			debug.log "Send", cmd, data
+			debug.log "ERROR:", error if error
+
+	receive: ( port, args... ) ->
+		if args[1]
+			size = args[0]
+			callback = args[1]
+		else
+			callback = args[0]
+		readCmd = if size then 'readBytes' else 'readByte'
+		writeArgs = [ port ]
+		writeArgs.push size  if size
+		wire[readCmd] writeArgs..., (error, data) ->
+			callback data
+			debug.log "Reveive", port, data
+			debug.log "ERROR:", error if error
+
 	write: ( type, port, data, callback ) ->
 		data = [port].concat data
 		switch type
 			when 'digital'
-				writeCmd = if Array.isArray data then 'writeBytes' else 'writeByte'
-				wire[writeCmd] CMD.digital.write, data, ( error ) ->
-					callback()
-					debug.log "Digital write", CMD.digital.write, data
-					debug.log "ERROR:", error if error
+				@send CMD.digital.write, data, callback
 			when 'analog'
-				wire.writeByte CMD.analog.write, data,  ( error ) ->
-					callback()
-					debug.log "Analog write", CMD.analog.write, data
-					debug.log "ERROR:", error if error
+				@send CMD.analog.write, data, callback
+
 	read: ( type, args... ) ->
 		switch type
 			when 'digital'


remove erroneous test

remove erroneous test

--- node/spec/unit/add-spec.coffee
+++ node/spec/unit/add-spec.coffee
@@ -1,4 +0,0 @@
-describe 'adding numbers', ->
-  Given -> @subject = requireSubject('lib/add')
-  When -> @result = @subject.add(5, 7)
-  Then -> @result == 12


Update load-fixtures.coffee

Update load-fixtures.coffee

--- src/load-fixtures.coffee
+++ src/load-fixtures.coffee
@@ -1,5 +1,5 @@
-#!/usr/bin/env node
-
+`#!/usr/bin/env node
+`
 require 'coffee-script/register'
 optimist = require 'optimist'
 app = require '../../../server/server'


start segment in writePath fixed

start segment in writePath fixed

--- modules/levelcreator/app/assets/javascripts/level_creator/plugins/write_path_to_meta_file.coffee
+++ modules/levelcreator/app/assets/javascripts/level_creator/plugins/write_path_to_meta_file.coffee
@@ -42,7 +42,7 @@
       segmentPayload = {}
       segmentPayload.isEndSegment = _.contains(endValues, segment.value)

-      segmentPayload.isStartSegment = _.contains(startValue, segment.value)
+      segmentPayload.isStartSegment = startValue is segment.value
       segmentPayload.path = segment.path
       segmentPayload.id = segment.id
       segmentPayload.value = segment.value


skip update when selection wasn't empty

skip update when selection wasn't empty

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -106,6 +106,7 @@
     @narrowEditor.onDidChangeCursorPosition (event) =>
       {oldBufferPosition, newBufferPosition, textChanged, cursor} = event
       return if @isLocked() or
+        not cursor.selection.isEmpty()
         textChanged or
         (newBufferPosition.row is 0) or
         (oldBufferPosition.row is newBufferPosition.row)


Add closeButton option to modals, defaulting to true

--- vendor/assets/javascripts/mercury/modal.js.coffee
+++ vendor/assets/javascripts/mercury/modal.js.coffee
@@ -22,7 +22,12 @@

   build: ->
     @element = jQuery('
', {class: 'mercury-modal loading'})
-    @element.html('
×
')
+    @element.html("""
+

+
+  #{@options.closeButton || jQuery.type(@options.closeButton) == 'undefined' ? '×' : ''}
+

+    """)
     @element.append('

')

     @overlay = jQuery('
', {class: 'mercury-modal-overlay'})


updated atom config

updated atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -13,10 +13,6 @@
       "repositories"
       "tmp"
       "vendor"
-    ]
-    themes: [
-      "unity-ui"
-      "glacier-syntax"
     ]
     hideGitIgnoredFiles: true
     disabledPackages: [
@@ -35,3 +31,4 @@
   "release-notes":
     viewedVersion: "0.89.0"
   "atom-terminal": {}
+  minimap: {}


Spec :lipstick:

Spec :lipstick:

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -166,7 +166,7 @@
           findResultsView = editor.find('.search-results')

           rootView.open('sample.coffee')
-          expect(findResultsView.children().length).toEqual 0
+          expect(findResultsView.children()).toHaveLength 0

       describe "when all active pane items are closed", ->
         it "updates the result count", ->
@@ -177,7 +177,7 @@
           findResultsView = editor.find('.search-results')

           editor.trigger 'core:close'
-          expect(findResultsView.children().length).toEqual 0
+          expect(findResultsView.children()).toHaveLength 0

       describe "when the active pane item is not an edit session", ->
         [anotherOpener] = []
@@ -197,7 +197,7 @@
           findResultsView = editor.find('.search-results')

           rootView.open "another"
-          expect(findResultsView.children().length).toEqual 0
+          expect(findResultsView.children()).toHaveLength 0

       describe "when a new edit session is activated on a different pane", ->
         it "updates the result view and selects the correct text", ->
@@ -212,9 +212,9 @@
         it "highlights the found text in the new edit session (and removes the highlights from the other)", ->
           findResultsView = editor.find('.search-results')

-          expect(findResultsView.children().length).toEqual 6
+          expect(findResultsView.children()).toHaveLength 6
           newEditor = editor.splitRight(project.open('sample.coffee'))
-          expect(findResultsView.children().length).toEqual 0
+          expect(findResultsView.children()).toHaveLength 0

     describe "when the buffer contents change", ->
       it "re-runs the search", ->
@@ -222,7 +222,7 @@
         editor.setCursorBufferPosition([4,0])
         editor.insertText("items items")

-        expect(findResultsView.children().length).toEqual 8
+        expect(findResultsView.children()).toHaveLength 8
         expect(findView.resultCounter.text()).toEqual('8 found')
         expect(editor.getSelectedBufferRange()).toEqual [[4, 11], [4, 11]]

@@ -291,13 +291,13 @@

       it "only highlights matches", ->
         expect(findResultsView.parent()[0]).toBe editor.underlayer[0]
-        expect(findResultsView.children().length).toEqual 6
+        expect(findResultsView.children()).toHaveLength 6

         findView.findEditor.setText 'notinthefilebro'
         findView.focus()
         $(document.activeElement).trigger 'core:confirm'

-        expect(findResultsView.children().length).toEqual 0
+        expect(findResultsView.children()).toHaveLength 0

   describe "replacing", ->
     beforeEach ->
@@ -339,7 +339,7 @@
           $('.find-and-replace .btn-all').click()
           expect(findView.resultCounter.text()).toEqual('no results')
           expect(editor.getText()).not.toMatch /items/
-          expect(editor.getText().match(/\bcats\b/g).length).toMatch 6
+          expect(editor.getText().match(/\bcats\b/g)).toHaveLength 6
           expect(editor.getSelectedBufferRange()).toEqual [[2, 0], [2, 0]]

       describe "when the 'find-and-replace:replace-all' event is triggered", ->
@@ -347,7 +347,7 @@
           editor.trigger 'find-and-replace:replace-all'
           expect(findView.resultCounter.text()).toEqual('no results')
           expect(editor.getText()).not.toMatch /items/
-          expect(editor.getText().match(/\bcats\b/g).length).toMatch 6
+          expect(editor.getText().match(/\bcats\b/g)).toHaveLength 6
           expect(editor.getSelectedBufferRange()).toEqual [[2, 0], [2, 0]]

     describe "replacement patterns", ->


testing for behaviour of c%

testing for behaviour of c%

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -679,6 +679,89 @@
           keydown('G', shift: true)
           keydown('escape')
           expect(editor.getText()).toBe("12345\n\n")
+
+    describe "when followed by a %", ->
+      beforeEach ->
+        editor.setText("12345(67)8\nabc(d)e\nA()BCDE")
+
+      describe "before brackets or on the first one", ->
+        it "replaces inclusively until matching bracket", ->
+          editor.setCursorScreenPosition([0, 1])
+          editor.addCursorAtScreenPosition([1, 1])
+          editor.addCursorAtScreenPosition([2, 1])
+          keydown('c')
+          keydown('%')
+          editor.insertText('x')
+          expect(editor.getText()).toBe("1x8\naxe\nAxBCDE")
+          expect(vimState.mode).toBe "insert"
+
+      describe "inside brackets or on the ending one", ->
+        it "replaces inclusively backwards until matching bracket", ->
+          editor.setCursorScreenPosition([0, 6])
+          editor.addCursorAtScreenPosition([1, 5])
+          editor.addCursorAtScreenPosition([2, 2])
+          keydown('c')
+          keydown('%')
+          editor.insertText('x')
+          # this differs from VIM, which deletes the character originally under cursor
+          expect(editor.getText()).toBe("12345x67)8\nabcx)e\nAx)BCDE")
+          expect(vimState.mode).toBe "insert"
+
+      describe "after or without brackets", ->
+        it "deletes nothing", ->
+          editor.setText("12345(67)8\nabc(d)e\nABCDE")
+          editor.setCursorScreenPosition([0, 9])
+          editor.addCursorAtScreenPosition([2, 2])
+          keydown('c')
+          keydown('%')
+          expect(editor.getText()).toBe("12345(67)8\nabc(d)e\nABCDE")
+          expect(vimState.mode).toBe "command"
+
+      describe "repeats correctly with .", ->
+        beforeEach ->
+          editor.setCursorScreenPosition([0, 1])
+          keydown('c')
+          keydown('%')
+          editor.insertText('x')
+          keydown('escape')
+
+        it "undoes correctly with u", ->
+          expect(editor.getText()).toBe("1x8\nabc(d)e\nA()BCDE")
+          expect(vimState.mode).toBe "command"
+          keydown 'u'
+          expect(editor.getText()).toBe("12345(67)8\nabc(d)e\nA()BCDE")
+
+        it "repeats correctly before a bracket", ->
+          editor.setCursorScreenPosition([1, 0])
+          keydown('.')
+          expect(editor.getText()).toBe("1x8\nxe\nA()BCDE")
+          expect(vimState.mode).toBe "command"
+
+        it "repeats correctly on the opening bracket", ->
+          editor.setCursorScreenPosition([1, 3])
+          keydown('.')
+          expect(editor.getText()).toBe("1x8\nabcxe\nA()BCDE")
+          expect(vimState.mode).toBe "command"
+
+        it "repeats correctly inside brackets", ->
+          editor.setCursorScreenPosition([1, 4])
+          keydown('.')
+          # this differs from VIM, which deletes the character originally under cursor
+          expect(editor.getText()).toBe("1x8\nabcxd)e\nA()BCDE")
+          expect(vimState.mode).toBe "command"
+
+        it "repeats correctly on the closing bracket", ->
+          editor.setCursorScreenPosition([1, 5])
+          keydown('.')
+          # this differs from VIM, which deletes the character originally under cursor
+          expect(editor.getText()).toBe("1x8\nabcx)e\nA()BCDE")
+          expect(vimState.mode).toBe "command"
+
+        it "does nothing when repeated after a bracket", ->
+          editor.setCursorScreenPosition([2, 3])
+          keydown('.')
+          expect(editor.getText()).toBe("1x8\nabc(d)e\nA()BCDE")
+          expect(vimState.mode).toBe "command"

     describe "when followed by a goto line G", ->
       beforeEach ->


other hacks

other hacks

--- spec/poly-test/full.coffee
+++ spec/poly-test/full.coffee
@@ -451,7 +451,7 @@
       padding: 72px;
       -webkit-column-width: 400px;
       column-width: 400px;
-      overflow-x: #{window.atab && 'auto' || 'hidden'};
+      overflow-x: #{window.atob && 'auto' || 'hidden'};
       font-size: 20px;
       line-height: 30px;
     }
@@ -786,14 +786,14 @@

         for type, j in ['with intrinsic condition', 'with linear condition']
           do (type, j) ->
-            expectation = document.all && !document.body.style.msTouchAction? && 544 || 480
+            expectation = !window.atob && 544 || 480
             describe type, ->
               it 'Adaptive aspect', (done) ->
                 container = document.createElement('div')
                 container.style.height = '640px'
                 container.style.width = '640px'
                 container.style.position = 'absolute'
-                overflow = window.atab && 'auto' || 'hidden'
+                overflow = window.atob && 'auto' || 'hidden'
                 container.style.overflow = overflow
                 container.style.left = 0
                 container.style.top = 0


Use emit instead of trigger for Directory events

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -750,7 +750,7 @@
             directoryChangeHandler = jasmine.createSpy("directory-change")
             dirView.on "tree-view:directory-modified", directoryChangeHandler

-            dirView.directory.trigger 'contents-changed'
+            dirView.directory.emit 'contents-changed'
             expect(directoryChangeHandler).toHaveBeenCalled()
             expect(treeView.find('.selected').text()).toBe path.basename(filePath)


:lipstick:

:lipstick:

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -858,7 +858,6 @@
         dirView2.expand()
         fileView2 = treeView.find('.file:contains(test-file2.txt)').view()
         fileView3 = treeView.find('.file:contains(test-file3.txt)').view()
-

     describe "tree-view:copy", ->
       LocalStorage = window.localStorage


Use onDemand option from voxel-plugins

Use onDemand option from voxel-plugins

Requires deathcap/voxel-plugins@e4ffd54950b487c3cf907cf049f3ec03245b4f06

--- index.coffee
+++ index.coffee
@@ -133,10 +133,7 @@
   }

   for name, opts of configuration
-    if opts.onDemand  # TODO: add to voxel-registry
-      plugins.preconfigure name, opts
-    else
-      plugins.add name, opts
+    plugins.add name, opts

   plugins.loadAll()
   ## plugins are loaded from here on out ##


Production mode fixes; verbose option added

--- index.coffee
+++ index.coffee
@@ -7,7 +7,9 @@
 uglify = require 'uglify-js'
 Module = require 'module'

-{utils, log, assert} = Neft
+utils = require 'utils'
+log = require 'log'
+assert = require './node_modules/assert'

 processFile = require './process'
 buildResult = require './result'
@@ -22,6 +24,9 @@
 	assert.isFunction callback

 	BUNDLE_FILE_PATH = __dirname + "/bundle.#{utils.uid()}.tmp.js"
+
+	unless opts.verbose
+		log.enabled = log.ERROR

 	logtime = log.time 'Resolve bundle modules'
 	fs.writeFileSync BUNDLE_FILE_PATH, "(#{processFile})()"
@@ -62,12 +67,14 @@
 			cleaner.write bundle
 			bundle = cleaner.toString()
 			log.end logtime
+		else
+			bundle = bundle.replace ///<production>([^]*?)<\/production>///gm, ''

 		if opts.minify
 			logtime = log.time 'Minimalize'
 			fs.writeFileSync './tmp.js', bundle, 'utf-8'

-			cp.exec "./node_modules/uglify-js/bin/uglifyjs " +
+			cp.exec "uglifyjs " +
 			"./tmp.js " +
 			"--screw-ie8 " +
 			"--compress negate_iife=false,keep_fargs " +


simplified healthcheck command (stability fix)

--- index.coffee
+++ index.coffee
@@ -25,10 +25,10 @@
     scriptBaseDir: env.assert 'SCRIPT_BASE_DIR'
   net_container:
     healthcheck:
-      test: env.get 'NETWORK_HEALTHCHECK_TEST', "if [ ! -f /tmp/healthcheck ]; then ifconfig eth0 | grep 'inet addr:10.25'; [ $$? -eq 0 ] && touch /tmp/healthcheck; else sleep 30; ifconfig eth0 | grep 'inet addr:10.25'; fi"
-      interval:  env.get 'NETWORK_HEALTHCHECK_INTERVAL', '1s'
-      timeout: env.get 'NETWORK_HEALTHCHECK_TIMEOUT', '35s'
-      retries: parseInt(env.get 'NETWORK_HEALTHCHECK_RETRIES', 10)
+      test: env.get 'NETWORK_HEALTHCHECK_TEST', "ifconfig eth0 | grep 'inet addr:10.25'"
+      interval:  env.get 'NETWORK_HEALTHCHECK_INTERVAL', '30s'
+      timeout: env.get 'NETWORK_HEALTHCHECK_TIMEOUT', '5s'
+      retries: parseInt(env.get 'NETWORK_HEALTHCHECK_RETRIES', 4)

 console.log 'Config \n\n', config, '\n\n'


fixing irritating issue.

fixing irritating issue.

--- index.coffee
+++ index.coffee
@@ -25,6 +25,15 @@
 else
   draghint.classList.remove 'hidden'

+cores =
+  'snes9x-next': require 'snes9x-next'
+  'vba-next': require 'vba-next'
+  'nestopia': require 'nestopia'
+  'gambatte': require 'gambatte'
+  'gw': require 'gw'
+  'vecx': require 'vecx'
+  'picodrive': require 'picodrive'
+
 window.retro = retro = document.createElement 'canvas', 'x-game'
 document.body.appendChild retro

@@ -50,7 +59,7 @@
     throw new Error 'no rom!' if not rom
     retro.md5 = sparkmd5.ArrayBuffer.hash rom
     Promise.all([
-      System.import settings.extensions[extension]
+      Promise.resolve cores[settings.extensions[extension]]
       localForage.getItem retro.md5
     ]).then ([core, save]) ->
       stop() if retro.running


Small fix that won't throw exceptions in development because of coffee wrapping this in an anonymous function

--- lib/generators/templates/application.js.coffee
+++ lib/generators/templates/application.js.coffee
@@ -3,5 +3,5 @@
 #= require ember-data
 #= require_self
 #= require <%= application_name.underscore %>
-<%= application_name.camelize %> = Ember.Application.create()
+window.<%= application_name.camelize %> = Ember.Application.create()


Update atom config

Update atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -29,13 +29,16 @@
   gofmt: {}
   "gometalinter-linter": {}
   linter: {}
+  "linter-eslint": {}
   "linter-rubocop":
     command: "/Users/jstotz/.rbenv/shims/rubocop"
   "linter-ruby":
     rubyExecutablePath: "/Users/jstotz/.rbenv/shims/ruby"
-  "linter-ui-default": {}
+  "linter-ui-default":
+    showPanel: true
   "markdown-preview": {}
-  "markdown-preview-enhanced": {}
+  "markdown-preview-enhanced":
+    previewTheme: "mpe-github-syntax"
   "markdown-preview-plus": {}
   metrics: {}
   minimap: {}


Configure rubocop linter

Configure rubocop linter

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -34,3 +34,6 @@
     showOnStartup: false
   "release-notes":
     viewedVersion: "0.89.0"
+  "linter-rubocop":
+    configFileName: "config/style_guides/.ruby-style.yml"
+  react: {}


Pass correct raw value for content in integration scripts

--- packages/rocketchat-integrations/server/api/api.coffee
+++ packages/rocketchat-integrations/server/api/api.coffee
@@ -98,7 +98,7 @@
 				url_raw: @request.url
 				url_params: @urlParams
 				content: @bodyParams
-				content_raw: @request.body
+				content_raw: @request._readableState?.buffer?.toString()
 				headers: @request.headers
 				user:
 					_id: @user._id


kitra520.coffee: Reflect change to Yocto Morty

Also, change getting started links

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

--- kitra520.coffee
+++ kitra520.coffee
@@ -33,9 +33,9 @@
 	].concat(postProvisioningInstructions)

 	gettingStartedLink:
-		windows: 'http://docs.resin.io/#/pages/installing/gettingStarted-RushUp-Kitra520.md#windows'
-		osx: 'http://docs.resin.io/#/pages/installing/gettingStarted-RushUp-Kitra520.md#on-mac-and-linux'
-		linux: 'http://docs.resin.io/#/pages/installing/gettingStarted-RushUp-Kitra520.md#on-mac-and-linux'
+		windows: 'https://docs.resin.io/kitra520/nodejs/getting-started/#adding-your-first-device'
+		osx: 'https://docs.resin.io/kitra520/nodejs/getting-started/#adding-your-first-device'
+		linux: 'https://docs.resin.io/kitra520/nodejs/getting-started/#adding-your-first-device'

 	supportsBlink: false

@@ -43,7 +43,7 @@
 		machine: 'kitra520'
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
-		version: 'yocto-jethro'
+		version: 'yocto-morty'
 		deployArtifact: 'resin-image-flasher-kitra520.resinos-img'
 		compressed: true


comment parts in the OPF

comment parts in the OPF

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -21,6 +21,7 @@
     branch: true # This element will show up in the sidebar listing

     initialize: () ->
+      # For TocNode, let it know this is the root
       super {root:@}

       # Contains all entries in the OPF file (including images)
@@ -44,16 +45,24 @@
           @_localNavAdded[model.id] = model

+      # If a node was added-to/removed-from a TocNode ensure it is/is-not in the set of `tocNodes`
+      # TODO: This may be redundant and may be able to be removed
       @tocNodes.on 'tree:add',    (model, collection, options) => @tocNodes.add model, options
       @tocNodes.on 'tree:remove', (model, collection, options) => @tocNodes.remove model, options

-
+      # When the book title changes update the OPF XML
+      # TODO: use the value of `@get('title')` when serializing instead.
       @on 'change:title', (model, value, options) =>
         $title = @$xml.find('title')
         if value != $title.text()
           $title.text(value)
           @_save()

+      # When a title changes on one of the nodes in the ToC:
+      #
+      # 1. remember the change
+      # 2. try to autosave
+      # 3. if a remote conflict occurse the remembered change will be replayed (see `onReloaded`)
       @tocNodes.on 'change:title', (model, value, options) =>
         return if not model.previousAttributes()['title'] # skip if we are parsing the file
         return if @ == model # Ignore if changing the OPF title
@@ -109,6 +118,12 @@
       # This is useful when the OPF file was remotely updated
       @_localAddedItems[model.id] = model

+    # Called on "autosave".
+    # Only save the navModel and new files (and all the OPF files).
+    # Delay the save and if more than one thing changed during SAVE_DELAY
+    # only save once.
+    #
+    # Reason for SAVE_DELAY: a "move" is 2 operations, `remove` followed by `add`
     _save: ->
       clearTimeout(@_savingTimeout)
       @_savingTimeout = setTimeout (() =>
@@ -116,7 +131,8 @@
         delete @_savingTimeout
       ), SAVE_DELAY

-
+    # A book is not loaded until the navModel is loaded.
+    # Once the navModel is loaded, "autosave" whenever it changes.
     _loadComplex: (fetchPromise) ->
       fetchPromise
       .then () =>


refactor references to puzzle piece grid

--- views/puzzle.coffee
+++ views/puzzle.coffee
@@ -199,7 +199,7 @@

   construct_piece: (sym) ->
     @sym = sym
-    @hexes = @pattern.get_hexes()
+    @hexes = @get_hexes()
     @hex_box.set_hex_box(sym)
     wd_ht = @hex_box.get_box_size()
     @width = wd_ht[0]
@@ -208,6 +208,14 @@
     @pattern.draw_piece_pattern()
     @cut_piece_from_photo()
     @draw_piece_ab(-7,5)
+
+
+  get_hexes: () ->
+    hexes = []
+    for bb in [1..10]
+      for aa in [1..24]
+        hexes.push([aa,bb]) if @puzzle.grid[bb][aa] == @sym
+    return hexes

   cut_piece_from_photo: () ->
@@ -302,14 +310,6 @@
       if aa%2 != anchor_a%2
         if anchor_a%2 == 0 then yy = yy+10 else yy = yy-10
       @hex_draw.fill_hex_xy(xx,yy,"#000000")
-
-
-  get_hexes: () ->
-    hexes = []
-    for bb in [1..10]
-      for aa in [1..24]
-        hexes.push([aa,bb]) if @grid[bb][aa] == @sym
-    return hexes

@@ -681,8 +681,8 @@
       msg_in = xhr.responseText
       response = JSON.parse(msg_in)
       @pstring = response.pstring
-      grid = get_pattern_grid(@pstring)
-      @mask = new MissingPiecesMask(app, grid)
+      app.grid = get_pattern_grid(@pstring)
+      @mask = new MissingPiecesMask(app, app.grid)
       app.piece.construct_piece()
       app.pz_status.start_new_puzzle()
   xhr.send()


Adds stricter regex for mobile redirect

Adds stricter regex for mobile redirect

--- lib/middleware/redirect_mobile.coffee
+++ lib/middleware/redirect_mobile.coffee
@@ -36,6 +36,6 @@
 router.get '/inquiry/*', isResponsive
 router.get '/consign', isResponsive
 router.get '/professional-buyer*', isResponsive
-router.get '/life-at-artsy|/artsy-education|/gallery-insights|/artsy-partner-updates', isResponsive
+router.get '/^\/life-at-artsy$|^\/artsy-education$|^\/gallery-insights$|^\/artsy-partner-updates$', isResponsive
 router.use redirect
 module.exports = router


Fixed error on returning nearby deals

Fixed error on returning nearby deals

--- server/controllers/index.coffee
+++ server/controllers/index.coffee
@@ -59,4 +59,4 @@
           nearbyDeals.sort (a, b) ->
             a.dist - b.dist

-      res.jsonp  [dealcount: dealc, usercount: userc, shopcount: shopc,neardeals: nearbyDeals]+          res.jsonp  [dealcount: dealc, usercount: userc, shopcount: shopc,neardeals: nearbyDeals]


Bug hotfix

Bug hotfix

--- server/d2mpclient/socket.coffee
+++ server/d2mpclient/socket.coffee
@@ -31,7 +31,6 @@
 @deleteMod = (sock, mod)->
   return if !sock?
   command = "deletemod:"+mod.name
-  log.info "#{client._id} -> delete #{mod.name}"
   sock.send command
 @installMod = (client, mod)->
   sock = clientSockets[client._id]


Log number of queued events when starting/finishing a sync

--- src/main/recorder.coffee
+++ src/main/recorder.coffee
@@ -74,7 +74,7 @@
     @waiting.push({ success, error })

     if @semaphore > 0
-      Myna.log("Myna.Recorder.sync", "queued")
+      Myna.log("Myna.Recorder.sync", "queued", @waiting.length)
     else
       @semaphore++

@@ -112,7 +112,7 @@
           params:  params

       finish = (successEvents, errorEvents, cancelled = false) =>
-        Myna.log("Myna.Recorder.sync.finish", successEvents, errorEvents)
+        Myna.log("Myna.Recorder.sync.finish", successEvents, errorEvents, @waiting.length)

         if errorEvents.length > 0
           @requeueEvents(errorEvents)


Use main remChosen method for ui removal

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -94,9 +94,7 @@

     @$wrapper.on 'click.velge', '.velge-list .remove', (event) ->
       $target = $(event.currentTarget).parent().find('.name')
-      self.unchoose($target.text())
-      self.renderChoices()
-      self.renderChosen()
+      self.velge.remChosen(name: $target.text())
       false

     @$wrapper.on 'click.velge', '.velge-trigger', (event) ->
@@ -114,9 +112,6 @@

   choose: (name) ->
     @store.update({ name: name }, { chosen: true })
-
-  unchoose: (name) ->
-    @store.update({ name: name }, { chosen: false })

   submit: (name) ->
     return false unless @store.validate(name)


Unspecified min interval should be set to 0

--- app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_multiword_input.jsx.coffee
+++ app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_multiword_input.jsx.coffee
@@ -190,7 +190,7 @@
         word = ".+#{word}" if isEnd
         str = "\"#{word}\""
       if min or max
-        str = "[]{#{min ? ''},#{max ? ''}} " + str
+        str = "[]{#{min ? 0},#{max ? ''}} " + str

       str


remove stray log

remove stray log

--- src/views/themes/default/coffee/dashboard/session.coffee
+++ src/views/themes/default/coffee/dashboard/session.coffee
@@ -8,7 +8,6 @@
       url: document.URL.split("?")[0].split("#")[0].replace(/\/$/, "") + '/session.json',
       dataType: 'json',
       success: (data) ->
-        console.log(data)
         if(!data.active) then document.location.reload()
       #return
     })


use the correct prototype

use the correct prototype

--- src/bone/pluginator.coffee
+++ src/bone/pluginator.coffee
@@ -12,13 +12,13 @@
         0
     return plugsSort

-view = require("../bone/view")
+viewType = require("../bone/view")

 # pluginator mixin
 # handles + order plugins
 # gives a view a hierarchical / nested layout
 # a view must have a `render` method
-pluginator = view.extend
+pluginator = viewType.extend

   # mix & shake
   #mixin: (view) ->
@@ -79,7 +79,7 @@
   # TODO: check for memory leaks
   removeViews: ->
     views = @_views()
-    for key,el of @.views
+    for key,el of views
       el.undelegateEvents()
       el.unbind()
       el.removeViews() if el.removeViews?
@@ -102,8 +102,7 @@
   # The region is no longer usable after being removed.
   remove: ->
     @removeViews()
-    view.remove.apply @
-    #ViewPrototype.remove.apply @
+    viewType::remove.apply @

   _views : ->
     @.views = {} unless @.views?


Account for mid-line comments

Account for mid-line comments

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -121,7 +121,7 @@
 			end = change.end
 			range = new Range(end.row, 0, end.row, end.column)
 			lineUpToCursor = @editor.getSession().getTextRange(range)
-			if lineUpToCursor.slice(0, 1) == '%'
+			if lineUpToCursor.match(/.*%.*/)
 				return
 			commandFragment = getLastCommandFragment(lineUpToCursor)
 			# Check that this change was made by us, not a collaborator


coffeelint - indentation

coffeelint - indentation

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -175,5 +175,5 @@
       @enableClearSearch() if @model.get('searchResults')?
       @updateToc()
       if @model.isPage()
-          @toggleBooksList()
-          @closeContentsOnSmallScreen()
+        @toggleBooksList()
+        @closeContentsOnSmallScreen()


don't force local server (oops)

don't force local server (oops)

--- src/scripts/services/dapiServices.coffee
+++ src/scripts/services/dapiServices.coffee
@@ -21,7 +21,7 @@
   @$inject: ['$log', '$http', '$routeParams']
   constructor: (@log, @http, routeParams) ->
     useLocalServer = routeParams.local ? false
-    useLocalServer = true
+    #useLocalServer = true
     base = if useLocalServer
       'http://localhost:8080'
     else


Fix calculating location ranges around attachments

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -210,11 +210,14 @@
       nodeIsCursorTarget(node.firstChild)

   nodeLength = (node) ->
-    if nodeIsCursorTarget(node)
-      0
-    else if node.length?
-      node.length
-    else if node.tagName?.toLowerCase() is "br"
+    if node.nodeType is Node.TEXT_NODE
+      if nodeIsCursorTarget(node)
+        0
+      else if DOM.closestElementNode(node)?.isContentEditable
+        node.length
+      else
+        0
+    else if node.nodeName in ["BR", "FIGURE"]
       1
     else
       0


commonjs compatibility

commonjs compatibility

--- app/assets/javascripts/uploadcare/namespace.coffee
+++ app/assets/javascripts/uploadcare/namespace.coffee
@@ -1,4 +1,8 @@
-window.uploadcare = {}
+__exports = {}
+if typeof module == "object" and typeof module.exports == "object"
+  module.exports = __exports
+else
+  window.uploadcare = __exports

 uploadcare.namespace = (path, fn) ->
@@ -15,7 +19,7 @@
   parts = key.split('.')
   last = parts.pop()

-  target = window.uploadcare
+  target = __exports
   source = uploadcare

   for part in parts


don't let undefined be paired in pairs

don't let undefined be paired in pairs

--- src/modules/Pairs.coffee
+++ src/modules/Pairs.coffee
@@ -116,7 +116,7 @@
       value? && 1 || 0

   pad: (value, length) ->
-    unless value?.push
+    if value && !value.push
       result = []
       for i in [0...length]
         result.push value


Prevent dodging of bullets

Prevent dodging of bullets

--- app/scripts/lazerscripts/stage1/camera_crew.coffee
+++ app/scripts/lazerscripts/stage1/camera_crew.coffee
@@ -34,7 +34,7 @@
       @moveTo x: .22, y: .45
       @repeat @sequence(
         @moveTo x: .36, y: .47, speed: 25, easing: 'easeInOutQuad'
-        @moveTo x: .22, y: .5, speed: 25, easing: 'easeInOutQuad'
+        @moveTo x: .25, y: .5, speed: 25, easing: 'easeInOutQuad'
       )
     )


Disable broken leaderboard button

Disable broken leaderboard button

--- app/views/play/level/modal/HeroVictoryModal.coffee
+++ app/views/play/level/modal/HeroVictoryModal.coffee
@@ -223,7 +223,8 @@
       @showAmazonHocButton = (gameDevHoc is 'game-dev-hoc') and lastLevel
       @showHoc2016ExploreButton = gameDevHoc and lastLevel

-    c.showLeaderboard = @level.get('scoreTypes')?.length > 0 and not @level.isType('course')
+    #c.showLeaderboard = @level.get('scoreTypes')?.length > 0 and not @level.isType('course')
+    c.showLeaderboard = false # Disable this button since it's broken

     c.showReturnToCourse = not c.showLeaderboard and not me.get('anonymous') and @level.isType('course', 'course-ladder')
     c.isCourseLevel = @level.isType('course')


FIX: ensure that active is updated when units saved

--- src/app/units/partials/directives/unit-contexts.coffee
+++ src/app/units/partials/directives/unit-contexts.coffee
@@ -180,6 +180,7 @@
               description: $scope.unit.description
               start_date: $scope.unit.start_date
               end_date: $scope.unit.end_date
+              active: $scope.unit.active
             }
           }, (unit) ->
             alertService.add("success", "Unit updated.", 2000)


FF & IE should not have to double click datepicker.


Fix issue #219 "PageComponent resets position of layer"

--- framer/Components/PageComponent.coffee
+++ framer/Components/PageComponent.coffee
@@ -98,7 +98,10 @@
 			direction = "right"
 			throw new Error("#{direction} should be in #{directions}")

-		point = {x:0, y:0}
+		# For allowing pages added with 'addPage' to behave consistently with pages added
+		# to the PageComponent using 'superLayer', keep the original page point
+		# so one of the two coordinates is left untouched after the page is added
+		point = page.point

 		if @content.subLayers.length
 			point.x = Utils.frameGetMaxX(@content.contentFrame()) if direction in ["right", "east"]


add additional checks

add additional checks

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -53,6 +53,9 @@
         unless typeof j.prefix is 'undefined'
           sublime.completions.push { trigger: j.prefix, contents: j.body }

+    if sublime.completions.lenght is 0
+      throw new RangeError("No snippets to convert")
+
     # Convert to JSON
     json = JSON.stringify(sublime, null, 2)


factor out program load

factor out program load

--- src/config/programs.coffee
+++ src/config/programs.coffee
@@ -7,7 +7,7 @@
     program.push signal
     console.log "prog: #{world}", world, args, signal, program

-  reload: (world, args) ->
+  load: (world, args) ->
     args = {name: 'default'} unless args?
     {name} = args
     child = world.find_child(name)
@@ -15,6 +15,9 @@
     assert program = child.get('value'), "reload: program value missing"
     world.put('program', program)
     world.put('counter', 0)
+
+  reload: (world, args) ->
+    world.call('load', args)
     world.call('next', args)

   next: (world, args) ->


Fix for recent linter versions

Fix for recent linter versions

--- lib/main.coffee
+++ lib/main.coffee
@@ -77,7 +77,7 @@
     linter = new LinterPurescript(@editors)
     return {
       grammarScopes: ['source.purescript']
-      scope: 'package'
+      scope: 'project'
       lint: (f) -> linter.lint(f)
       lintOnFly: false
     }


main: Use helpers for version checking

main: Use helpers for version checking

Use helpcer.exec instead of child_process to check the version
compatibility of the plugin and coala.

Fixes https://github.com/coala-analyzer/coala-atom/issues/14

--- lib/main.coffee
+++ lib/main.coffee
@@ -29,9 +29,8 @@
     ]

     # Check version of coala
-    version = require('child_process').spawn @executable, ['--version']
-    version.stdout.on 'data', (data) ->
-      if data <= COALA_MIN_VERSION
+    version = helpers.exec(@executable, ['--version']).then (result) ->
+      if result <= COALA_MIN_VERSION
         atom.notifications.addError \
           'You are using an old version of coala !',
           'detail': 'Please upgrade your version of coala.\n


change notification type

change notification type

--- lib/main.coffee
+++ lib/main.coffee
@@ -85,7 +85,7 @@

       pathToISCC = atom.config.get('language-innosetup.pathToISCC')
       if not pathToISCC
-        notification = atom.notifications.addError(
+        notification = atom.notifications.addWarning(
           "**#{meta.name}**: No valid `ISCC.exe` was specified in your settings",
           dismissable: true,
           buttons: [


:bug: Delete current messages by linter on dispose

--- lib/main.coffee
+++ lib/main.coffee
@@ -30,9 +30,14 @@
       if @_validateLinter(linter)
         @instance.linters.push linter
     new Disposable =>
-      @instance.linters = @instance.linters.filter (item) ->
-        linters.indexOf(item) is -1
-
+      @instance.linters = @instance.linters.filter (item) =>
+        unless item in linters
+          if item.scope is 'project'
+            @instance.messagesProject.delete(item)
+          else
+            @instance.eachLinter (editorLinter) -> editorLinter.messages.delete(item)
+          false
+        true
   consumeStatusBar: (statusBar) ->
     @instance.views.attachBottom(statusBar)


change notification type

change notification type

--- lib/main.coffee
+++ lib/main.coffee
@@ -88,7 +88,7 @@
       nslJar  = atom.config.get('language-nsl.pathToJar')

       if not nslJar
-        notification = atom.notifications.addError(
+        notification = atom.notifications.addWarning(
           "**#{meta.name}**: No valid `nsL.jar` was specified in your settings"
           dismissable: true
           buttons: [


Fix #306

Fix #306

--- lib/main.coffee
+++ lib/main.coffee
@@ -266,8 +266,8 @@

         [code, row] = codeBlock
         if code? and row?
+            @moveDown row
             @createResultBubble code, row
-            @moveDown row

     runCell: () ->
         [startRow, endRow] = CellManager.getCurrentCell()
@@ -285,8 +285,8 @@
                 endRow -= 1
         code = @getRows(startRow, endRow)
         if code?
+            @moveDown endRow
             @createResultBubble code, endRow
-            @moveDown endRow

     removeStatusBarElement: ->
         unless @statusBarElement?


fixed pagination in the seed module

fixed pagination in the seed module

--- lib/seed.coffee
+++ lib/seed.coffee
@@ -11,17 +11,22 @@
 fetchAllPages = (method, path, query, cb, result = []) ->
   query.page = 1 if not query.page?

-  tmp = {}
+  # this is ridiculous, but necessary since the query object is
+  # altered in the angellist module and here for pagination
+  query_for_angellist = {}
   for key of query
-    tmp[key] = query[key]
+    query_for_angellist[key] = query[key]
+  query_for_pagination = {}
+  for key of query
+    query_for_pagination[key] = query[key]

   # fetch data from api
-  angellist[method] tmp, (err, results) ->
+  angellist[method] query_for_angellist, (err, results) ->
     return cb(err) if err

     result.push(
       data: item
-      query: query
+      query: query_for_angellist
     ) for item in results[path]

     if results.page >= results.last_page
@@ -29,8 +34,8 @@
       return client.addSet path, result, cb

     # fetch next page
-    query.page++
-    fetchAllPages method, path, query, cb, result
+    query_for_pagination.page++
+    fetchAllPages method, path, query_for_pagination, cb, result

 fetchAll = (method, path, startups, cb, result = []) ->
   done = 0


File manipulation updates

File manipulation updates


Separate method for preparing self view in PlayerView

--- app/assets/javascripts/player/player_view.js.coffee
+++ app/assets/javascripts/player/player_view.js.coffee
@@ -3,12 +3,14 @@
     'click .start-prompt': 'onStartPromptClick'

   initialize: (options) ->
-    @$el.addClass('not-started')
-
+    @prepareSelfView()
     @createRendererView()
     @createHudView()
     @createMovie()
     @showLoadingIndicator()
+
+  prepareSelfView: ->
+    @$el.addClass('not-started')

   createRendererView: ->
     @rendererView = new this.options.rendererClass(


Fixed spaced chars crash

Fixed spaced chars crash

--- lib/whip.coffee
+++ lib/whip.coffee
@@ -28,8 +28,7 @@
 tokenize = (input) ->
   o = input
     .replace(/\\"/g, "!dquote!")
-    .replace(/' '/g, "'!space!'")
-    .replace(/\\'/g, "!squote!")
+    .replace(/\s+' '\s+/g, "'!space!'")
     .split(/"/)
     .map((x, i) ->
       if i % 2 is 0 # not in string


Uncommenting said bug fix :p

Uncommenting said bug fix :p

--- src/directives/view.coffee
+++ src/directives/view.coffee
@@ -91,7 +91,7 @@
         timeStartedMainView = Date.now()

         onSuccessfulResolution = (args) ->
-#          return if getMatchingBinding(bindings) isnt binding
+          return if getMatchingBinding(bindings) isnt binding

           viewCreated = true


Try to fix some bugs with sticky_footer position

--- vendor/assets/javascripts/jquery.sticky_footer.js.coffee
+++ vendor/assets/javascripts/jquery.sticky_footer.js.coffee
@@ -9,7 +9,8 @@
   return

 $(document).on 'ready page:load page:restore', ->
-  position_footer()
   $(window).scroll position_footer
-  $(window).resize position_footer
+  $(window).resize ->
+    position_footer
+  $(window).resize()
   return


assets vs liabilities: sort chart elements in correct order

--- src/components/widgets/accounts-assets-vs-liabilities/accounts-assets-vs-liabilities.directive.js.coffee
+++ src/components/widgets/accounts-assets-vs-liabilities/accounts-assets-vs-liabilities.directive.js.coffee
@@ -55,6 +55,8 @@
         if _.includes ['ASSET', 'LIABILITY'], sum.classification
           { title: sum.classification, values: sum.totals }

+      datasets = _.sortByOrder(datasets, ['title'])
+
       inputData = { labels: w.content.companies, datasets: _.compact datasets }

       options = {


Call get instead of getDeserializer

Call get instead of getDeserializer

--- src/deserializer-manager.coffee
+++ src/deserializer-manager.coffee
@@ -23,7 +23,7 @@
   deserialize: (state, params) ->
     return unless state?

-    if deserializer = @getDeserializer(state)
+    if deserializer = @get(state)
       stateVersion = state.get?('version') ? state.version
       return if deserializer.version? and deserializer.version isnt stateVersion
       if (state instanceof Document) and not deserializer.acceptsDocuments


:memo: Add registering example

:memo: Add registering example

--- src/deserializer-manager.coffee
+++ src/deserializer-manager.coffee
@@ -1,6 +1,17 @@
 # Public: Manages the deserializers used for serialized state
 #
-# Should be accessed via `atom.deserializers`
+# An instance of this class is always available as the `atom.deserializers`
+# global.
+#
+# ### Registering a deserializer
+#
+# ```coffee
+# class MyPackageView extends View
+#   atom.deserializers.add(this)
+#
+#   @deserialize: (state) ->
+#     new MyPackageView(state)
+# ```
 module.exports =
 class DeserializerManager
   constructor: (@environment) ->


:fire: Remove deprecations from DeserializerManager

--- src/deserializer-manager.coffee
+++ src/deserializer-manager.coffee
@@ -1,5 +1,4 @@
 {Disposable} = require 'event-kit'
-Grim = require 'grim'

 # Extended: Manages the deserializers used for serialized state
 #
@@ -60,9 +59,3 @@

     name = state.get?('deserializer') ? state.deserializer
     @deserializers[name]
-
-if Grim.includeDeprecatedAPIs
-  DeserializerManager::remove = (classes...) ->
-    Grim.deprecate("Call .dispose() on the Disposable return from ::add instead")
-    delete @deserializers[name] for {name} in classes
-    return


:memo:

:memo:

[ci skip]

--- src/display-marker-layer.coffee
+++ src/display-marker-layer.coffee
@@ -84,7 +84,7 @@
   # * `callback` A {Function} that will be called with a {TextEditorMarker}
   #   whenever a new marker is created.
   #
-  # You should prefer {onDidUpdate} when synchronous notifications aren't
+  # You should prefer {::onDidUpdate} when synchronous notifications aren't
   # absolutely necessary.
   #
   # Returns a {Disposable}.


Refactored cdnJS script (no longer uses Underscore)

--- scripts/cdnjs.coffee
+++ scripts/cdnjs.coffee
@@ -1,51 +1,46 @@
 # Description:
-#   CDN JS API - Search front-end dependencies
+#   CDN JS Public API
 #
 # Dependencies:
-#   "underscore": "^1.6.0"
+#   None
 #
 # Configuration:
 #   None
 #
 # Commands:
-#   hubot cdnjs <query> - Search the cdnJS script repository for the URL to the latest library available using the query (returns the first 5 matches)
-#   hubot cdnjs strictly <query> - Performs the default search, but with the addition of a strict list search using Underscore.js (returns the first match)
+#   hubot cdnjs search <query> - Returns the CDNJS URL for the first 5 front-end dependencies matching the search <query>
+#   hubot cdnjs fetch <dependency> - Returns the CDNJS URL for a specific front-end <dependency> (e.g. jQuery)
 #
 # Author:
 #   MrSaints

-_ = require 'underscore'
+module.exports = (robot) ->
+    robot.respond /cdnjs fetch (.*)/i, (msg) ->
+        cdnjs_request msg, (data) ->
+            for resource in data.results
+                if resource.name is msg.match[1]
+                    msg.reply "#{resource.name} - #{resource.latest}"
+                    return

-module.exports = (robot) ->
-    robot.respond /cdnjs (.*)/i, (msg) ->
-        if msg.match[1].match(/^strictly (.*)$/i)
-            getLibraries msg, true
-        else
-            getLibraries msg
+            msg.reply "The front-end dependency you have entered (\"#{msg.match[1]}\") does not exist. Try a different dependency."

-getLibraries = (msg, strict = false) ->
-    query = msg.match[1]
+    robot.respond /cdnjs search (.*)/i, (msg) ->
+        cdnjs_request msg, (data) ->
+            for resource in data.results[0..4]
+                msg.send "#{resource.name} - #{resource.latest}"

-    if strict
-        query = query.split(' ')[1]
+            if data.total > 5
+                msg.reply "There are #{data.total - 5} other front-end dependencies matching your search query: \"#{msg.match[1]}\"."

+cdnjs_request = (msg, handler) ->
     msg.http("http://api.cdnjs.com/libraries")
         .query
-            search: query
+            search: msg.match[1]
         .get() (err, res, body) ->
             data = JSON.parse body

             if data.total is 0
-                msg.reply "Your query ('#{query}') returned no libraries."
+                msg.reply "No front-end dependencies were found using search query: \"#{msg.match[1]}\". Try a different query."
                 return

-            if strict
-                match = _.findWhere data.results, name: query
-
-                if match?
-                    msg.reply "#{match.name} - #{match.latest}"
-                else
-                    msg.reply "Your query ('#{query}') returned no results."
-            else
-                for resource in data.results[0..4]
-                    msg.send "#{resource.name} - #{resource.latest}"+            handler data


added missing link

added missing link

--- src/documents/index.html.coffee
+++ src/documents/index.html.coffee
@@ -66,4 +66,4 @@
 			p "Do you want to quickly learn how to build apps for Windows 8? I break it down for you from File New Project to publishing to the store."

 			div class: "text-right", ->
-				a "get started...."
+				a href: "/pages/31DaysOfWindows8", "get started...."


Panel: Added ui key to header & content

Panel: Added ui key to header & content

--- src/elements/Panel/Panel.coffee
+++ src/elements/Panel/Panel.coffee
@@ -22,6 +22,9 @@
 				content: true

 		@registerTemplate(@panel)
+		if @_ui
+			CUI.dom.setAttribute(@panel.get("header"), "ui", "#{@_ui}:header")
+			CUI.dom.setAttribute(@panel.get("content"), "ui", "#{@_ui}:content")

 		if @_content_placeholder
 			@append(@_content_placeholder, "content")


Avoid shooting a tweet with twitter-native media

--- scripts/shoot.coffee
+++ scripts/shoot.coffee
@@ -9,6 +9,7 @@

     return if data.user.screen_name isnt from
     return if data.entities.user_mentions.length
+    return if data.entities.media

     console.log "Checking tweet: \"#{text}\""


remove outdated Batch Actions dropdown initializer

--- app/assets/javascripts/active_admin/application.js.coffee
+++ app/assets/javascripts/active_admin/application.js.coffee
@@ -11,9 +11,6 @@
     params = window.location.search.split('&')
     regex = /^(q\[|q%5B|q%5b|page|commit)/
     window.location.search = (param for param in params when not param.match(regex)).join('&')
-
-  # Batch Actions dropdown
-  $('.dropdown_button').popover()

   # Filter form: don't send any inputs that are empty
   $('.filter_form').submit ->


ESC_RAS-20: Show the button only on errors

--- app/assets/javascripts/rails_admin/save.unverified.coffee
+++ app/assets/javascripts/rails_admin/save.unverified.coffee
@@ -1,3 +1,5 @@
 $(document).on "rails_admin.dom_ready", ->
-  $('button[name="_save_unverified"]').on "click", ->
+  button = $('button[name="_save_unverified"]')
+  button.toggle !!$('.has-error').length
+  button.on "click", ->
     $('input.skip_validation').val(true)


Add javascript to show section if an option with a matching value is selected

--- app/assets/javascripts/users/notification_rules.js.coffee
+++ app/assets/javascripts/users/notification_rules.js.coffee
@@ -1,3 +1,15 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
+
+$ ->
+	$('select[data-reveal-section]').change(->
+		section = $("##{$(@).val() + $(@).attr('data-reveal-section')}")
+
+		if section
+			$('.hide-unless-selected').hide()
+			section.find(':input').removeAttr('disabled')
+			section.show()
+	).trigger('change')
+
+


Yep, sometimes it JUUUUST FUCKING SUCKS.

--- app/assets/javascripts/views/clouds/user_dialog.js.coffee
+++ app/assets/javascripts/views/clouds/user_dialog.js.coffee
@@ -50,3 +50,11 @@
       @model.ban
         success: (user) =>
           @model.set(user)
+
+  skypeAdd: ->
+    window.open("skype:#{@model.get('skype_name')}?add").close()
+    false
+
+  skypeCall: ->
+    window.open("skype:#{@model.get('skype_name')}?call").close()
+    false


10139: Fix bug with read-only view of questioning form

--- app/assets/javascripts/views/question_form_view.js.coffee
+++ app/assets/javascripts/views/question_form_view.js.coffee
@@ -28,7 +28,7 @@
     div = @$(".form-field[data-field-name=#{attrib}] .control")
     if div.is('.read-only')
       wrapper = div.find('.ro-val')
-      wrapper.data('val') || wrapper.text()
+      if typeof wrapper.data('val') != 'undefined' then wrapper.data('val') else wrapper.text()
     else
       # Rails checkbox fields have a hidden field followed by a checkbox. We need to ignore the hidden.
       field = div.find('input[type!=hidden], select, textarea')


Don't save hidden form inputs.

Don't save hidden form inputs.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -94,7 +94,7 @@
   collection = $('#collection_select').val()
   localStorage[collection] = true
   for child in $('#collection_form').children()
-    if $(child).attr('id') && !$(child).prop('disabled')
+    if $(child).attr('id') && !$(child).prop('disabled') && ($(child).attr('type') != 'hidden')
       localStorage["#{collection}:#{$(child).attr('id')}"] = $(child).val()
   $('#collection_form').after $('<div>').attr('class','alert alert-success').attr('id','save_success').append('Saved.')
   $('#save_success').fadeOut 1800, ->


fix test

fix test

--- apps/search/test/collections/google_search_results.coffee
+++ apps/search/test/collections/google_search_results.coffee
@@ -22,11 +22,11 @@
   it '#moveMatchResultsToTop', ->
     results = new GoogleSearchResults [
       {
-        display: 'bar bar'
+        title: 'bar bar'
         link: '/artwork/foo'
       },
       {
-        display: 'foo bar'
+        title: 'foo bar'
         link: '/artist/foo'
       }
     ]


Fix: permissions are private unless 'Everyone' option selected

--- assets/javascripts/views/001_permissions_fields.js.coffee
+++ assets/javascripts/views/001_permissions_fields.js.coffee
@@ -76,6 +76,7 @@

   buildPermissions: =>
     data = {
+      public: false
       entities: {}
     }
     for option in @options_view.options


need to add px now that we have doctype

need to add px now that we have doctype

--- src/modules/multi-cursor.coffee
+++ src/modules/multi-cursor.coffee
@@ -28,9 +28,9 @@
   return cursor

 _moveCursor = (cursor, referenceNode) ->
-  cursor.elem.style.top = referenceNode.offsetTop
-  cursor.elem.style.left = referenceNode.offsetLeft
-  cursor.elem.style.height = referenceNode.offsetHeight
+  cursor.elem.style.top = referenceNode.offsetTop + 'px'
+  cursor.elem.style.left = referenceNode.offsetLeft + 'px'
+  cursor.elem.style.height = referenceNode.offsetHeight + 'px'
   if parseInt(cursor.elem.style.top) < parseInt(cursor.elem.style.height)
     ScribeDOM.addClass(cursor.elem, 'top')
   else
@@ -95,8 +95,8 @@
     })
     @editor.renderer.on(ScribeRenderer.events.UPDATE, =>
       _.defer( =>
-        @container.style.top = @editor.root.offsetTop
-        @container.style.left = @editor.root.offsetLeft
+        @container.style.top = @editor.root.offsetTop + 'px'
+        @container.style.left = @editor.root.offsetLeft  + 'px'
       )
     )
     this.initListeners()


Clean code

Clean code

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -22,111 +22,104 @@
 @taiga = taiga = {}

 configure = ($routeProvider, $locationProvider, $httpProvider, $provide, tgLoaderProvider) ->
-    $routeProvider.when("/", {templateUrl: "/partials/projects.html"})
-    $routeProvider.when("/project/:pslug/", {templateUrl: "/partials/project.html"})
-    $routeProvider.when("/project/:pslug/backlog", {templateUrl: "/partials/backlog.html", resolve: {loader: tgLoaderProvider.add()}})
-    $routeProvider.when("/project/:pslug/taskboard/:id", {templateUrl: "/partials/taskboard.html"})
-    $routeProvider.when("/project/:pslug/search", {templateUrl: "/partials/search.html", reloadOnSearch: false})
-    $routeProvider.when("/project/:pslug/kanban", {templateUrl: "/partials/kanban.html", resolve: {loader: tgLoaderProvider.add()}})
+    $routeProvider.when("/",
+        {templateUrl: "/partials/projects.html"})
+    $routeProvider.when("/project/:pslug/",
+        {templateUrl: "/partials/project.html"})
+    $routeProvider.when("/project/:pslug/backlog",
+        {templateUrl: "/partials/backlog.html", resolve: {loader: tgLoaderProvider.add()}})
+    $routeProvider.when("/project/:pslug/taskboard/:id",
+        {templateUrl: "/partials/taskboard.html"})
+    $routeProvider.when("/project/:pslug/search",
+        {templateUrl: "/partials/search.html", reloadOnSearch: false})
+    $routeProvider.when("/project/:pslug/kanban",
+        {templateUrl: "/partials/kanban.html", resolve: {loader: tgLoaderProvider.add()}})

     # User stories
     $routeProvider.when("/project/:pslug/us/:usref",
         {templateUrl: "/partials/us-detail.html"})
-
     $routeProvider.when("/project/:pslug/us/:usref/edit",
         {templateUrl: "/partials/us-detail-edit.html"})

     # Tasks
     $routeProvider.when("/project/:pslug/tasks/:taskref",
         {templateUrl: "/partials/task-detail.html"})
-
     $routeProvider.when("/project/:pslug/tasks/:taskref/edit",
         {templateUrl: "/partials/task-detail-edit.html"})

     # Wiki
     $routeProvider.when("/project/:pslug/wiki",
-                {redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"}, )
-
+        {redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"}, )
     $routeProvider.when("/project/:pslug/wiki/:slug",
-                {templateUrl: "/partials/wiki.html"})
-
+        {templateUrl: "/partials/wiki.html"})
     $routeProvider.when("/project/:pslug/wiki/:slug/edit",
-                {templateUrl: "/partials/wiki-edit.html"})
+        {templateUrl: "/partials/wiki-edit.html"})

     # Issues
-    $routeProvider.when("/project/:pslug/issues", {templateUrl: "/partials/issues.html", resolve: {loader: tgLoaderProvider.add()}})
+    $routeProvider.when("/project/:pslug/issues",
+        {templateUrl: "/partials/issues.html", resolve: {loader: tgLoaderProvider.add()}})
     $routeProvider.when("/project/:pslug/issues/:issueref",
         {templateUrl: "/partials/issues-detail.html"})
-
     $routeProvider.when("/project/:pslug/issues/:issueref/edit",
         {templateUrl: "/partials/issues-detail-edit.html"})

     # Admin
     $routeProvider.when("/project/:pslug/admin/project-profile/details",
         {templateUrl: "/partials/admin-project-profile.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-profile/default-values",
         {templateUrl: "/partials/admin-project-default-values.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-profile/features",
         {templateUrl: "/partials/admin-project-features.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/us-status",
         {templateUrl: "/partials/admin-project-values-us-status.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/us-points",
         {templateUrl: "/partials/admin-project-values-us-points.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/task-status",
         {templateUrl: "/partials/admin-project-values-task-status.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/issue-status",
         {templateUrl: "/partials/admin-project-values-issue-status.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/issue-types",
         {templateUrl: "/partials/admin-project-values-issue-types.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/issue-priorities",
         {templateUrl: "/partials/admin-project-values-issue-priorities.html"})
-
     $routeProvider.when("/project/:pslug/admin/project-values/issue-severities",
         {templateUrl: "/partials/admin-project-values-issue-severities.html"})
-
     $routeProvider.when("/project/:pslug/admin/memberships",
         {templateUrl: "/partials/admin-memberships.html"})
-
     $routeProvider.when("/project/:pslug/admin/roles",
         {templateUrl: "/partials/admin-roles.html"})

     # User settings
     $routeProvider.when("/project/:pslug/user-settings/user-profile",
           {templateUrl: "/partials/user-profile.html"})
-
     $routeProvider.when("/project/:pslug/user-settings/user-change-password",
           {templateUrl: "/partials/user-change-password.html"})
-
     $routeProvider.when("/project/:pslug/user-settings/user-avatar",
           {templateUrl: "/partials/user-avatar.html"})
-
     $routeProvider.when("/project/:pslug/user-settings/mail-notifications",
           {templateUrl: "/partials/mail-notifications.html"})
-
     $routeProvider.when("/change-email/:email_token",
           {templateUrl: "/partials/change-email.html"})

     # Auth
-    $routeProvider.when("/login", {templateUrl: "/partials/login.html"})
-    $routeProvider.when("/register", {templateUrl: "/partials/register.html"})
-    $routeProvider.when("/forgot-password", {templateUrl: "/partials/forgot-password.html"})
+    $routeProvider.when("/login",
+        {templateUrl: "/partials/login.html"})
+    $routeProvider.when("/register",
+        {templateUrl: "/partials/register.html"})
+    $routeProvider.when("/forgot-password",
+        {templateUrl: "/partials/forgot-password.html"})
     $routeProvider.when("/change-password",
-          {templateUrl: "/partials/change-password-from-recovery.html"})
-
+        {templateUrl: "/partials/change-password-from-recovery.html"})
     $routeProvider.when("/change-password/:token",
-          {templateUrl: "/partials/change-password-from-recovery.html"})
-
-    $routeProvider.when("/invitation/:token", {templateUrl: "/partials/invitation.html"})
-    $routeProvider.when("/error", {templateUrl: "/partials/error.html"})
-    $routeProvider.when("/not-found", {templateUrl: "/partials/not-found.html"})
+        {templateUrl: "/partials/change-password-from-recovery.html"})
+    $routeProvider.when("/invitation/:token",
+        {templateUrl: "/partials/invitation.html"})
+
+    # Errors/Exceptions
+    $routeProvider.when("/error",
+        {templateUrl: "/partials/error.html"})
+    $routeProvider.when("/not-found",
+        {templateUrl: "/partials/not-found.html"})

     $routeProvider.otherwise({redirectTo: '/not-found'})
     $locationProvider.html5Mode(true)
@@ -155,7 +148,7 @@

     $provide.factory("authHttpIntercept", ["$q", "$location", "$tgConfirm", authHttpIntercept])
     $httpProvider.responseInterceptors.push('authHttpIntercept')
-    $httpProvider.interceptors.push('loaderInterceptor');
+    $httpProvider.interceptors.push('loaderInterceptor')

     window.checksley.updateValidators({
         linewidth: (val, width) ->


volume breakout measures

volume breakout measures

--- lib/compose.coffee
+++ lib/compose.coffee
@@ -1,5 +1,6 @@
-_     = require 'lodash'
-path  = require 'path'
+_         = require 'lodash'
+path      = require 'path'
+resolvep  = require 'resolve-path'

 module.exports = (config) ->
   networkValue = "#{config.host_if} -i eth0 @CONTAINER_NAME@ dhclient @#{config.vlan}"
@@ -30,22 +31,31 @@
         service.network_mode = "service:bb-net-#{serviceName}"
       else service.network_mode = "service:bb-net-#{service.labels['bigboat.service.name']}"

+    resolvePath = (root, path) ->
+      path = path[1...] if path[0] is '/'
+      resolvep root, path
+
     addVolumeMapping = (serviceName, service) ->
       bucketPath = path.join config.dataDir, config.domain, options.storageBucket if options.storageBucket
       service.volumes = service.volumes?.map (v) ->
         vsplit = v.split ':'
-        if vsplit.length is 2
-          if vsplit[1] in ['rw', 'ro']
-            v
-          else if bucketPath
-            "#{path.join bucketPath, vsplit[0]}:#{vsplit[1]}"
-          else vsplit[1]
-        else if vsplit.length is 3
-          if bucketPath
-            "#{path.join bucketPath, vsplit[0]}:#{vsplit[1]}:#{vsplit[2]}"
-          else "#{vsplit[1]}"
-        else v
+        try
+          if vsplit.length is 2
+            if vsplit[1] in ['rw', 'ro']
+              v
+            else if bucketPath
+              "#{resolvePath bucketPath, vsplit[0]}:#{vsplit[1]}"
+            else vsplit[1]
+          else if vsplit.length is 3
+            if bucketPath
+              "#{resolvePath bucketPath, vsplit[0]}:#{vsplit[1]}:#{vsplit[2]}"
+            else "#{vsplit[1]}"
+          else v
+        catch e
+          console.error "Error while mapping volumes. Root: #{bucketPath}, path: #{v}", e
+          null
       delete service.volumes unless service.volumes
+      service.volumes = service.volumes.filter((s) -> s) if service.volumes

     migrateLinksToDependsOn = (serviceName, service) ->
       if service.links


Moved route to the correct part of the init file

--- app/initialize.coffee
+++ app/initialize.coffee
@@ -20,9 +20,9 @@

 # ===== Routes =====
+App.IndexRoute = Ember.Route.extend
+    setupController: (controller) ->
+        controller.set 'content', ['red', 'yellow', 'blue']

 # ===== Router =====
-App.IndexRoute = Ember.Route.extend
-    setupController: (controller) ->
-        controller.set 'content', ['red', 'yellow', 'blue']


Use a special class for namespace.

Use a special class for namespace.

--- lib.coffee
+++ lib.coffee
@@ -44,10 +44,11 @@

   lastValue

-# We have a special field for components. This allows us to have the namespace with the same name
-# as a component, without overriding anything in the component (we do not want to use component
-# object as a namespace object).
-COMPONENTS_FIELD = ''
+class ComponentsNamespace
+  # We have a special field for components. This allows us to have the namespace with the same name
+  # as a component, without overriding anything in the component (we do not want to use component
+  # object as a namespace object).
+  @COMPONENTS_FIELD: ''

 getPathAndName = (name) ->
   assert name
@@ -81,7 +82,7 @@
   match = components

   while (segment = path.shift())?
-    match[segment] = {} unless segment of match
+    match[segment] = new components.constructor() unless segment of match
     match = match[segment]
     assert _.isObject match

@@ -99,7 +100,7 @@
   namespace = getNamespace components, path
   return null unless namespace

-  namespace[COMPONENTS_FIELD]?[name] or null
+  namespace[components.constructor.COMPONENTS_FIELD]?[name] or null

 setComponent = (components, name, component) ->
   assert _.isObject components
@@ -110,12 +111,12 @@

   namespace = createNamespace components, path

-  namespace[COMPONENTS_FIELD] ?= {}
-  assert name not of namespace[COMPONENTS_FIELD]
-  namespace[COMPONENTS_FIELD][name] = component
+  namespace[components.constructor.COMPONENTS_FIELD] ?= new components.constructor()
+  assert name not of namespace[components.constructor.COMPONENTS_FIELD]
+  namespace[components.constructor.COMPONENTS_FIELD][name] = component

 class BaseComponent
-  @components: {}
+  @components: new ComponentsNamespace()

   @register: (componentName, componentClass) ->
     throw new Error "Component name is required for registration." unless componentName


Fixing running tests.

Fixing running tests.

--- lib.coffee
+++ lib.coffee
@@ -5,6 +5,10 @@
       names.add(name)
     obj = Object.getPrototypeOf(obj)
   Array.from(names.values())
+
+copyFunction = (f) ->
+  (args...) ->
+    f.apply @, args

 export class ExpectationManager
   constructor: (@test, @onComplete) ->
@@ -76,6 +80,15 @@
   @_testRegistry: {}

   constructor: ->
+    # We want to copy prototype functions so that we can attach properties to them if necessary.
+    # Otherwise them might be shared between multiple instances of the same base class.
+    @setUpServer = copyFunction @setUpServer
+    @setUp = copyFunction @setUp
+    @setUpClient = copyFunction @setUpClient
+    @tearDownClient = copyFunction @tearDownClient
+    @tearDownServer = copyFunction @tearDownServer
+    @tearDown = copyFunction @tearDown
+
     # Tag server-specific setup/tear down methods so they always run on the server.
     ClassyTestCase.runOnServer @setUpServer
     ClassyTestCase.runOnBoth @setUp


Added comment.

Added comment.

--- lib.coffee
+++ lib.coffee
@@ -169,6 +169,7 @@
     @_componentName or null

   # We allow access to the component name through a method so that it can be accessed in templates in an easy way.
+  # It should never be overridden. The implementation should always be exactly the same as class method implementation.
   componentName: ->
     # Instance method is just a getter, not a setter as well.
     @constructor.componentName()


Used `PickerMixin` in `DateTime` class.

Used `PickerMixin` in `DateTime` class.

--- lib.coffee
+++ lib.coffee
@@ -355,34 +355,22 @@
                       glyph: "chevron-down"

 DateTime = createClass
+  mixins: [
+    PickerMixin
+  ]
+
   propTypes: ->
     label: PropTypes.string
     help: PropTypes.string
-    format: PropTypes.string
-    display: PropTypes.string
-    value: PropTypes.string
-    defaultValue: PropTypes.string

   getDefaultProps: ->
-    format = defaultFormat
-
-    format: format
     datePart: "YYYY-MM-DD"
     timePart: "HH:mm:ss"
     dateGlyph: "calendar"
     timeGlyph: "time"
-    defaultValue: (do moment).format format

   getInitialState: ->
-    {value, defaultValue} = @props
-    value ?= defaultValue
-    {value, dateView: "days"}
-
-  componentWillReceiveProps: ({value})->
-    @setState {value} if value?
-
-  getValue: ->
-    @state.value
+    dateView: "days"

   viewDays: ->
     @setState dateView: "days"
@@ -394,17 +382,12 @@
     @setState dateView: "months"

   updateValue: (field)->
-    value = do @refs[field].getValue
-    {onChange} = @props
-    if onChange?
-      onChange value
-    else
-      @setState {value}
+    @setValue do @refs[field].getValue

   handleYear: ->
     @updateValue "year"
     do @viewDays
-
+
   handleMonth: ->
     @updateValue "month"
     do @viewDays


Fixed problem with ipad

Fixed problem with ipad

--- js/foundation/eventmanager/eventmanager.coffee
+++ js/foundation/eventmanager/eventmanager.coffee
@@ -579,9 +579,9 @@
   _handleMouseMove: ( x, y )->
     @_findNewFocus(x,y)
     @_debugHighlight()
-    @_delegateMouseMove(x,y)
+    _mouseMoveHandled = @_delegateMouseMove(x,y)
     _currentlyDragging = @_delegateDrag(x,y)
-    return _currentlyDragging
+    return ( _mouseMoveHandled or _currentlyDragging )
   #
   # Handle items being dragged, sending #drag(x,y) calls to them
   _delegateDrag: (x, y)->
@@ -597,10 +597,12 @@
   _delegateMouseMove: (x,y)->
     _mouseMoveItems = @_listeners.byEvent.mouseMove
     return false if _mouseMoveItems.length == 0
+    _stop = false
     for _viewId in _mouseMoveItems
       _ctrl = @_views[_viewId]
-      _ctrl.mouseMove(x,y)
-    return true
+      if _ctrl?
+        _stop = true if _ctrl.mouseMove(x,y)
+    return _stop
   #
   # Handle items wanting #startHover( _dragObj ),
   # #hover( _dragObj ) and #endHover( _dragObj ) calls
@@ -899,7 +901,7 @@
     @_cancelTextSelection() unless _endDragIds.length == 0 and _mouseUpIds.length == 0
     @_ieClassNameUnPatch() if BROWSER_TYPE.ie and @_ieClassNamePatched.length
     if Event.hasTouch()
-      Event.stop(e)
+      # Event.stop(e)
       @click(e)
     else if _stop
       Event.stop(e)


Inserting Milestone titles insted of IIDs with GFM auto complete

--- app/assets/javascripts/gfm_auto_complete.js.coffee
+++ app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -90,7 +90,7 @@
       alias: 'milestones'
       searchKey: 'search'
       displayTpl: @Milestones.template
-      insertTpl: '${atwho-at}${id}'
+      insertTpl: '${atwho-at}${title}'
       callbacks:
         beforeSave: (milestones) ->
           $.map milestones, (m) ->


Adjusting styles for ARIA events

Adjusting styles for ARIA events

--- app/assets/javascripts/handle_aria_event.js.coffee
+++ app/assets/javascripts/handle_aria_event.js.coffee
@@ -28,7 +28,7 @@

 AriaEvent.prototype.bindhandler = ()->
   thisObj = this
-  $row_event = @main_element.find('.row ul.thumbnails[role="row"] .span3')
+  $row_event = @main_element.find('.row[role="row"] .span3')
   console.log "aaaa"
   $row_event.bind 'keyup', (e)->
     console.log "key up"
@@ -61,7 +61,7 @@
           $newCell = $("#row_#{new_row_number}_col_#{@maxcol-1}")
           $newCell.attr("tabindex", "0").focus()
         else
-          col_count_last_row = $("#row_index_#{@maxrow - 1} .thumbnails").children('li.span3').size() - 1
+          col_count_last_row = $("#row_index_#{@maxrow - 1}").children('.span3').size() - 1
           $newCell = $("#row_#{@maxrow - 1}_col_#{col_count_last_row}")
           console.log $newCell
           $newCell.attr("tabindex", "0").focus()
@@ -76,20 +76,20 @@

       prev_cell =  $curCell.closest('.row').prev()
       if prev_cell.length
-        $newCell = prev_cell.find(".thumbnails li#row_#{parseInt(row_number)-1}_col_#{col_number}")
+        $newCell = prev_cell.find("#row_#{parseInt(row_number)-1}_col_#{col_number}")
         if $newCell.length == 0
-          $newCell = prev_cell.find(".thumbnails li.span3").last()
+          $newCell = prev_cell.find(".span3").last()
         $newCell.attr("tabindex", "0").focus()
         $curCell.removeAttr("tabindex")
       else
         next_cell = $curCell.closest('.row').next()
         console.debug next_cell
         if next_cell.length
-          $newCell = next_cell.find(".thumbnails li#row_#{parseInt(row_number)+1}_col_#{col_number}")
+          $newCell = next_cell.find("#row_#{parseInt(row_number)+1}_col_#{col_number}")
           console.log $newCell

           if $newCell.length == 0
-            $newCell = next_cell.find(".thumbnails li.span3").last()
+            $newCell = next_cell.find(".span3").last()

           $newCell.attr("tabindex", "0").focus()
           $curCell.removeAttr("tabindex")
@@ -125,20 +125,20 @@

       prev_cell =  $curCell.closest('.row').next()
       if prev_cell.length
-        $newCell = prev_cell.find(".thumbnails li#row_#{parseInt(row_number)+1}_col_#{col_number}")
+        $newCell = prev_cell.find(".span3#row_#{parseInt(row_number)+1}_col_#{col_number}")
         if $newCell.length == 0
-          $newCell = prev_cell.find(".thumbnails li.span3").last()
+          $newCell = prev_cell.find(".span3").last()
         $newCell.attr("tabindex", "0").focus()
         $curCell.removeAttr("tabindex")
       else
         next_cell = $curCell.closest('.row').prev()
         console.debug next_cell
         if next_cell.length
-          $newCell = next_cell.find(".thumbnails li#row_#{parseInt(row_number)-1}_col_#{col_number}")
+          $newCell = next_cell.find("#row_#{parseInt(row_number)-1}_col_#{col_number}")
           console.log $newCell

           if $newCell.length == 0
-            $newCell = next_cell.find(".thumbnails li.span3").last()
+            $newCell = next_cell.find(".span3").last()

           $newCell.attr("tabindex", "0").focus()
           $curCell.removeAttr("tabindex")


make  code less verbose

make  code less verbose

--- modules/plot/main/coffee/Utils.coffee
+++ modules/plot/main/coffee/Utils.coffee
@@ -1,16 +1,13 @@
 doCollisionDetection = (nodes) ->
-  distance = 1
-  nodes.forEach (node, i) ->
-    previousNodes = nodes.slice 0, (i - distance + 1)
+  reductor = (oldDistance, node, i) ->
+    previousNodes = nodes.slice 0, (i - oldDistance + 1)
       .map (n, j) -> [n, j]
-    hx.find previousNodes, ([previousNode, j]) ->
+    possibleNewDistance = hx.find previousNodes, ([previousNode, j]) ->
       prevBox = previousNode.getBoundingClientRect()
       currBox = node.getBoundingClientRect()
-      doesCollide = currBox.left < prevBox.right
-      currentDistance = i - j
-      if doesCollide
-        distance = currentDistance
-      doesCollide
+      i - j if currBox.left < prevBox.right
+    possibleNewDistance or oldDistance
+  distance = nodes.reduce reductor, 1
   nodes.forEach (node, i) ->
     if i % (distance + 1)
       hx.select node


Use resin.errors.handle in app.coffee

Use resin.errors.handle in app.coffee

--- lib/app.coffee
+++ lib/app.coffee
@@ -126,6 +126,6 @@
 	.action(env.remove)

 resin.data.prefix.set resin.config.dataPrefix, (error) ->
-	throw error if error?
+	resin.errors.handle(error) if error?
 	program.parse(process.argv)
 	resin.log.setQuiet(program.quiet)


Move changeProjectDirectory outside the nested code

--- lib/app.coffee
+++ lib/app.coffee
@@ -100,6 +100,12 @@
 	return capitano.command(plugin) if not _.isArray(plugin)
 	return _.each(plugin, capitano.command)

+changeProjectDirectory = (directory) ->
+	try
+		process.chdir(directory)
+	catch
+		errors.handle(new Error("Invalid project: #{directory}"))
+
 nplugm.load 'resin-plugin-*', (error, plugin) ->
 	return console.error(error.message) if error?
 	registerPlugin(plugin.require())
@@ -107,12 +113,6 @@
 	errors.handle(error) if error?

 	cli = capitano.parse(process.argv)
-
-	changeProjectDirectory = (directory) ->
-		try
-			process.chdir(directory)
-		catch
-			errors.handle(new Error("Invalid project: #{directory}"))

 	resin.data.prefix.set resin.settings.get('dataPrefix'), (error) ->
 		errors.handle(error) if error?