Order's note is option now. fixed #1

Order's note is option now.
fixed #1

--- src/hubot-order-menu.coffee
+++ src/hubot-order-menu.coffee
@@ -79,7 +79,10 @@

 		robot.brain.data.orders.push order

-		msg.reply "You order #{food} #{note} $#{money} for <@#{userId}|#{user}>"
+		if(note?)
+			msg.reply "You order #{food} #{note} $#{money} for <@#{userId}|#{user}>"
+		else
+			msg.reply "You order #{food} $#{money} for <@#{userId}|#{user}>"

 	robot.respond /order my/i, (msg) ->
 		date_current = new Date()


Use metadata returned from atom.io API when creating package card in detail view

--- lib/package-detail-view.coffee
+++ lib/package-detail-view.coffee
@@ -93,9 +93,6 @@
         @showErrorMessage()
       else
         @pack = packageData
-        # TODO: this should match Package.loadMetadata from core, but this is
-        # an acceptable hacky workaround
-        @pack.metadata = _.clone(@pack)
         @completeInitialzation()

   showLoadingMessage: ->


remove extra data directory

remove extra data directory


removing duplicate file.

removing duplicate file.

--- app/app.coffee
+++ app/app.coffee
@@ -1,36 +0,0 @@
-http = require('http')
-http.globalAgent.maxSockets = 64
-express = require("express")
-path = require("path")
-
-app = express()
-
-app.configure ->
-  app.set "port", process.env.PORT or 3000
-  app.set "views", __dirname + "/views"
-  app.set "view engine", "jade"
-  app.use express.favicon(path.join(__dirname, 'public/images/favicon.ico'))
-  app.use express.logger("dev")
-  app.use express.urlencoded()
-  app.use express.json()
-  app.use express.methodOverride()
-  app.use app.router
-  app.use express.static(path.join(__dirname, "public"))
-
-app.configure 'development', ->
-  app.use express.errorHandler()
-  app.locals.pretty = true
-
-require("./routers/home") app
-require("./routers/about") app
-require("./routers/get-last-tweet") app
-require("./routers/get-specific-tweet") app
-require("./routers/get-tweet-png") app
-
-exports.app = app
-
-server = require("http").createServer(app)
-port = process.env.PORT or 3000
-server.listen port
-
-console.log "Listening port " + port


Less delay between shots

Less delay between shots

--- capture.coffee
+++ capture.coffee
@@ -32,7 +32,7 @@
     rotation = i * increment
     console.log("#{i}/#{total}: Rotate to #{rotation}")
     page.evaluate(rotateY, increment)
-    delay 200, ->
+    delay 10, ->
       page.render(image_path(rotation))
       callback()


src must be without host

src must be without host

--- lib/assets/javascripts/rubber_ring/image_dragger.coffee
+++ lib/assets/javascripts/rubber_ring/image_dragger.coffee
@@ -6,7 +6,7 @@
     drag_items = document.querySelectorAll(drag_selector);
     for drag_item in drag_items
       addEvent drag_item, 'dragstart', (event) ->
-        event.dataTransfer.setData('src', this.src)
+        event.dataTransfer.setData('src', $(this).attr("src"))

     drop_image = document.querySelector(drop_selector);
     # Tells the browser that we *can* drop on this target


Update changelog-gen config

Update changelog-gen config

--- changelog.cson
+++ changelog.cson
@@ -1,42 +1,42 @@
 sections: [
   {
     name: ':sparkles: Features'
-    match: '^(?:feat|:sparkles:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:feat|:sparkles:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true
   }
   {
     name: ':bug: Bug Fixes'
-    match: '^(?:fix|:bug:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:fix|:bug:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true
   }
   {
     name: ':speedboat: Performance Improvements'
-    match: '^(?:perf|:speedboat:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:perf|:speedboat:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true
   }
   {
     name: ':house: Housekeeping'
-    match: '^(?:chore|:house:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:chore|:house:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true
   }
   {
     name: ':lock: Security Note'
-    match: '^(?:security|:lock:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:security|:lock:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true
   }
   {
     name: ':arrow_up: Dependency Updates'
-    match: '^(?:deps|:dependencies|:arrow_up:)\\((.*)\\):\\s(.*)$'
+    match: '^(?:deps|:dependencies|:arrow_up:)\\((.*)\\)(?::)?\\s(.*)$'
     replace: '\\2'
     grouping_capture: 1
     include_body: true


Improving throttled

Improving throttled

--- app/scripts/services/websocket.coffee
+++ app/scripts/services/websocket.coffee
@@ -6,9 +6,7 @@
     @handlers = []

     @ws.onmessage = @_onmessage.bind(this)
-    @throttledStatus = $.throttle(2000, @sendMessage)
-    @throttledReview = $.throttle(500, @sendMessage)
-    @throttledSendMessage = $.throttle(1000, @sendMessage)
+    @throttledSendMessage = $.throttle(500, @sendMessage)

   clearHandlers: () ->
     @handlers[..] = []
@@ -23,14 +21,7 @@
       handler(obj)

   _onmessage: (message) ->
-    if message.type == 'worker-status'
-      @throttledStatus(message)
-
-    else if message.type == 'new-review'
-      @throttledReview(message)
-
-    else
-      @throttledSendMessage(message)
+    @throttledSendMessage(message)

 angular.module('holmesApp')
   .factory('WebSocketFcty', (ConfigConst, WebSocket) ->


If data doesn't have a link we don't need a embedbox

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
@@ -10,12 +10,14 @@

     super options,data

-    embedOptions = $.extend {}, options, {
+    @embedOptions = $.extend {}, options,
       hasDropdown : no
       delegate : @
-    }

-    @embedBox = new EmbedBox embedOptions, data?.link
+    if data.link?
+      @embedBox = new EmbedBox @embedOptions, data?.link
+    else
+      @embedBox = new KDView

   viewAppended:()->
     return if @getData().constructor is KD.remote.api.CStatusActivity
@@ -50,10 +52,16 @@

   render:=>
     super
-    data = @getData().link or {}
-    @embedBox.setEmbedHiddenItems data.link_embed_hidden_items
-    @embedBox.setEmbedImageIndex data.link_embed_image_index
-    @embedBox?.embedExistingData data.link_embed, {}
+    {link} = @getData()
+
+    if link?
+      if @embedBox.constructor.name is "KDView"
+        @embedBox = new EmbedBox @embedOptions, link
+      @embedBox.setEmbedHiddenItems link.link_embed_hidden_items
+      @embedBox.setEmbedImageIndex link.link_embed_image_index
+      @embedBox.embedExistingData link.link_embed, {}
+    else
+      @embedBox = new KDView

   click:(event)->


Hide the back button if there's only one task to the workflow

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -162,7 +162,8 @@
       <hr />

       <nav className="task-nav">
-        <button type="button" className="back minor-button" disabled={onFirstAnnotation} onClick={@destroyCurrentAnnotation}>Back</button>
+        {if Object.keys(@props.workflow.tasks).length > 1
+          <button type="button" className="back minor-button" disabled={onFirstAnnotation} onClick={@destroyCurrentAnnotation}>Back</button>}
         {if nextTaskKey
           <button type="button" className="continue major-button" disabled={waitingForAnswer} onClick={@addAnnotationForTask.bind this, classification, nextTaskKey}>Next</button>
         else


Proxy history filetree requests through web.

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -200,6 +200,7 @@
 		webRouter.get  "/project/:Project_id/updates", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApiAndInjectUserDetails
 		webRouter.get  "/project/:Project_id/doc/:doc_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
 		webRouter.get  "/project/:Project_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApiAndInjectUserDetails
+		webRouter.get  "/project/:Project_id/filetree/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
 		webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
 		webRouter.post '/project/:project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreDocFromDeletedDoc
 		webRouter.post "/project/:project_id/restore_file", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreFileFromV2


webrtc answer

webrtc answer

--- lib/assets/javascripts/webrtc_rails/main.js.coffee
+++ lib/assets/javascripts/webrtc_rails/main.js.coffee
@@ -8,6 +8,7 @@
   onWebSocketConnected: ->
   onWebSocketReconnectingStarted: ->
   onWebSocketReconnected: ->
+  onWebRTCCall: (remoteUserID) ->
   onWebRTCConnected: ->
   onWebRTCReconnectingStarted: ->
   onWebRTCReconnected: ->
@@ -48,6 +49,11 @@
       )
     else
       alert 'Local stream not running yet - try again.'
+
+  answer: ->
+    @_isCaller = false
+    @_sendOffer()
+    @_peerStarted = true

   sendUserMessage: (type, message) ->
     @_sendMessage(
@@ -130,10 +136,8 @@
           else if event['reconnect'] && @_hangedUp
             @_sendMessage(type: 'hangUp')
           else
-            @_isCaller = false
             @_remoteUserID = event['remoteUserID']
-            @_sendOffer()
-            @_peerStarted = true
+            @onWebRTCCall(@_remoteUserID)
         when 'hangUp'
           @_callAnswerReceived = true
           @_hangUp()


Simplify mode assignment.

Simplify mode assignment.

--- resources/assets/coffee/react/profile-page.coffee
+++ resources/assets/coffee/react/profile-page.coffee
@@ -77,12 +77,11 @@

   render: =>
-    headerStats = stats = @props.allStats[@state.mode]?.data
-
-    if stats?
+    if @state.mode != 'me'
       headerMode = @state.mode
+      headerStats = stats = @props.allStats[@state.mode].data
     else
-      headerMode = @props.initialMode
+      headerMode = @state.initialMode
       headerStats = @props.allStats[headerMode].data

     el 'div', className: 'flex-column flex-full',


Fix commit test

Fix commit test

--- review_front/tests/unit/models/commit-test.coffee
+++ review_front/tests/unit/models/commit-test.coffee
@@ -2,7 +2,7 @@

 moduleForModel 'commit', {
   # Specify the other units that are required for this test.
-  needs: ['model:project', 'model:author', 'model:ticket', 'model:permission']
+  needs: ['model:project', 'model:author', 'model:ticket', 'model:permission', 'model:user']
 }

 test 'it exists', (assert) ->


Print nicer options

Print nicer options

--- app/cli.coffee
+++ app/cli.coffee
@@ -5,6 +5,6 @@

 class CLI extends HawCLI
   @::configFiles.splice 1, 0, path.resolve configDir, 'haw-config'
-  @::options.push ['project-config', null, 'The readymade project definition to load', 'project']
+  @::options.unshift ['project-config', null, 'The readymade project definition to load', 'project']

 module.exports = CLI


Update SubscriptionController unit tests

--- services/web/test/unit/coffee/Subscription/SubscriptionControllerTests.coffee
+++ services/web/test/unit/coffee/Subscription/SubscriptionControllerTests.coffee
@@ -40,7 +40,8 @@

 		@LimitationsManager =
 			userHasSubscriptionOrIsGroupMember: sinon.stub()
-			userHasSubscription : sinon.stub()
+			userHasV1OrV2Subscription : sinon.stub()
+			userHasV2Subscription: sinon.stub()

 		@SubscriptionViewModelBuilder =
 			buildUsersSubscriptionViewModel:sinon.stub().callsArgWith(1, null, @activeRecurlySubscription)
@@ -163,7 +164,7 @@

 		describe "with a user without a subscription", ->
 			beforeEach ->
-				@LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
+				@LimitationsManager.userHasV1OrV2Subscription.callsArgWith(1, null, false)
 				@PlansLocator.findLocalPlanInSettings.returns({})

 			describe "with a valid plan code", ->
@@ -176,7 +177,7 @@

 		describe "with a user with subscription", ->
 			it "should redirect to the subscription dashboard", (done)->
-				@LimitationsManager.userHasSubscription.callsArgWith(1, null, true)
+				@LimitationsManager.userHasV1OrV2Subscription.callsArgWith(1, null, true)
 				@res.redirect = (url)=>
 					url.should.equal "/user/subscription"
 					done()
@@ -184,7 +185,7 @@

 		describe "with an invalid plan code", ->
 			it "should redirect to the subscription dashboard", (done)->
-				@LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
+				@LimitationsManager.userHasV1OrV2Subscription.callsArgWith(1, null, false)
 				@PlansLocator.findLocalPlanInSettings.returns(null)
 				@res.redirect = (url)=>
 					url.should.equal "/user/subscription"
@@ -193,7 +194,7 @@

 		describe "which currency to use", ->
 			beforeEach ->
-				@LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
+				@LimitationsManager.userHasV1OrV2Subscription.callsArgWith(1, null, false)
 				@PlansLocator.findLocalPlanInSettings.returns({})

 			it "should use the set currency from the query string", (done)->
@@ -221,7 +222,7 @@

 		describe "with a recurly subscription already", ->
 			it "should redirect to the subscription dashboard", (done)->
-				@LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
+				@LimitationsManager.userHasV1OrV2Subscription.callsArgWith(1, null, false)
 				@SubscriptionHandler.validateNoSubscriptionInRecurly = sinon.stub().yields(null, false)
 				@res.redirect = (url)=>
 					url.should.equal "/user/subscription"
@@ -334,6 +335,7 @@
 				cvv:"123"
 			@req.body.recurly_token_id = "1234"
 			@req.body.subscriptionDetails = @subscriptionDetails
+			@LimitationsManager.userHasV1OrV2Subscription.yields(null, false)
 			@SubscriptionController.createSubscription @req, @res

 		it "should send the user and subscriptionId to the handler", (done)->
@@ -442,7 +444,7 @@

 		it "should redirect to the plans page if the user does not have a subscription", (done)->
-			@LimitationsManager.userHasSubscription.callsArgWith(1, null, false)
+			@LimitationsManager.userHasV2Subscription.callsArgWith(1, null, false)
 			@res.redirect = (url)->
 				url.should.equal "/user/subscription/plans"
 				done()
@@ -451,7 +453,7 @@

 		it "should pass the plan code to the view - student", (done)->

-			@LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"Student free trial 14 days"})
+			@LimitationsManager.userHasV2Subscription.callsArgWith(1, null, true, {planCode:"Student free trial 14 days"})
 			@res.render = (view, opts)->
 				view.should.equal "subscriptions/upgradeToAnnual"
 				opts.planName.should.equal "student"
@@ -460,7 +462,7 @@

 		it "should pass the plan code to the view - collaborator", (done)->

-			@LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"free trial for Collaborator free trial 14 days"})
+			@LimitationsManager.userHasV2Subscription.callsArgWith(1, null, true, {planCode:"free trial for Collaborator free trial 14 days"})
 			@res.render = (view, opts)->
 				opts.planName.should.equal "collaborator"
 				done()
@@ -468,7 +470,7 @@

 		it "should pass annual as the plan name if the user is already on an annual plan", (done)->

-			@LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"student annual with free trial"})
+			@LimitationsManager.userHasV2Subscription.callsArgWith(1, null, true, {planCode:"student annual with free trial"})
 			@res.render = (view, opts)->
 				opts.planName.should.equal "annual"
 				done()


background script added to manifest

background script added to manifest

--- src/chrome/manifest.cson
+++ src/chrome/manifest.cson
@@ -26,3 +26,9 @@
 ]

 author: "Ben from BenSites.com, ess090502, and contributors"
+
+background:
+  scripts: [
+    "{{entry?name=background.js!./background}}"
+  ]
+  persistent: false


Clean up namespace litter.

Clean up namespace litter.

--- scripts/gh-book/migration-utils.coffee
+++ scripts/gh-book/migration-utils.coffee
@@ -16,6 +16,7 @@

           doc = parser.parseFromString(html, 'text/xml')
           $body = $(doc)
+          $body.find('body *[xmlns="http://www.w3.org/1999/xhtml"]').removeAttr('xmlns')

           # DOMParser does not properly throw an exception, but at least chrome
           # and firefox adds a parsererror element to the page.


filter page done

filter page done

--- port_scan/coffee/web.coffee
+++ port_scan/coffee/web.coffee
@@ -4,6 +4,7 @@
 EventProxy = require 'eventproxy'
 db = mongojs 'test', ['ips']
 app = express()
+app.use express.bodyParser()

 ip2num = (ipstr) ->
   ip = 0
@@ -110,17 +111,39 @@
           body['data'][i.ip]['value'] = (j for j in i.port when j not in ports)
           ep.emit 'one_data'

+app.get '/', (req, res) ->
+  res.setHeader('Content-Type', 'text/html')
+  body = '''
+  <form action="/" method="post">
+  IP  <input type="text" name="ip_tmp" />
+  port <input type="text" name="port_tmp" />
+  <input type="submit" value="Filter" />
+  </form>
+  Usage:
+
只填写IP则返回该IP的端口信息,可填写格式为单个IP或IP段,如"192.168.1.1-192.168.1.255"

+
只填写端口则返回开放该端口的IP列表,可填写格式为单个端口或端口段,如"22-53"

+
若既填写IP也填写端口,则返回该IP段除了填写的端口之外开放的端口信息,可填写IP格式,单个IP或IP段,可填写端口格式为单个端口或用竖线"|"隔开的多个端口,如"22|80"

+  '''
+  res.end body

-
-
-
-
-
-
-
-
-
-
+app.post '/', (req, res) ->
+  ip_tmp = req.body.ip_tmp if req.body.ip_tmp?
+  port_tmp = req.body.port_tmp if req.body.port_tmp?
+  if ip_tmp != '' and port_tmp == ''
+    ips = ip_tmp.split '-'
+    if ips.length == 1
+      res.redirect "/fip/#{ips[0]}-#{ips[0]}"
+    else
+      res.redirect "/fip/#{ips[0]}-#{ips[1]}"
+  else if ip_tmp == '' and port_tmp != ''
+    ports = port_tmp.split '-'
+    if ports.length == 1
+      res.redirect "/fport/#{ports[0]}-#{ports[0]}"
+    else
+      res.redirect "/fport/#{ports[0]}-#{ports[1]}"
+  else
+    ips = ip_tmp.split '-'
+    res.redirect "/wlst/#{ips[0]}-#{ips[1]}:#{port_tmp}"

 app.listen(3000)


Fun stuff

Fun stuff

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -1,5 +1,22 @@
 BASE_URL = "http://127.0.0.1:8000"
 CHAT_SLUG = "hello-world"
+
+avatars = [
+    "https://minotar.net/avatar/clone1018/64.png",
+    "https://minotar.net/avatar/citricsquid/64.png",
+    "https://minotar.net/avatar/Raitsui/64.png",
+    "https://minotar.net/avatar/runforthefinish/64.png",
+    "https://minotar.net/avatar/NoMercyJon/64.png",
+    "https://minotar.net/avatar/Nautika/64.png",
+    "https://minotar.net/avatar/Notch/64.png",
+    "https://minotar.net/avatar/NiteAngel/64.png",
+    "https://minotar.net/avatar/S1NZ/64.png",
+    "https://minotar.net/avatar/drupal/64.png",
+    "https://minotar.net/avatar/ez/64.png",
+]
+
+getAvatarLink = ->
+    avatars[_.random avatars.length]

 # uuid4 = ->
 #     # From http://stackoverflow.com/a/2117523/458610.
@@ -56,8 +73,7 @@
     render: ->
         (Dom.li className: "media",
             (Dom.a className: "pull-left",
-                # TODO: Make email somewhat dynamic.
-                (Dom.img width: 64, height: 64, src: "http://avatars.io/email/dagrevis@gmail.com?size=small", className: "media-object")),
+                (Dom.img width: 64, height: 64, src: getAvatarLink(), className: "media-object")),
             (Dom.div className: "media-body", @props.message))

 ChatMessages = React.createClass


блокировка, для синхронного выполнения команд

--- builder.coffee
+++ builder.coffee
@@ -41,6 +41,8 @@

     @config = {}
     @defaults = {}
+    @commandQue = []
+    @_lock = no

     hasLoad = no
     for configFile in options.configFiles
@@ -71,11 +73,20 @@
     console.log 'defaults', @defaults
     # console.log 'config', @config

+  lock: -> @_lock = yes
+  unlock: ->
+    @_lock = no
+    while @commandQue.length > 0
+      @commandQue.shift()()
+
   run: (cmdstr) ->
     cmdpath = cmdstr.split(':')
     @execConfig(cmdpath[cmdpath.length-1], @_findCommandConfig(cmdpath))

   execConfig: (name, options) ->
+    if @_lock
+      @commandQue.push(=> @execConfig(name, options))
+      return
     type = options._type
     type = 'bundle' unless type?
     return unless @commands[type]?


Use new bulk spell-checking method in task handler

--- lib/spell-check-handler.coffee
+++ lib/spell-check-handler.coffee
@@ -1,18 +1,29 @@
 SpellChecker = require 'spellchecker'

-wordRegex = /(?:^|[\s\[\]"'])([a-zA-Z]+([a-zA-Z']+[a-zA-Z])?)(?=[\s\.\[\]:,"']|$)/g
+module.exports = ({id, text}) ->
+  misspelledCharacterRanges = SpellChecker.checkSpelling(text)

-module.exports = ({id, text}) ->
   row = 0
+  rangeIndex = 0
+  characterIndex = 0
   misspellings = []
-  for line in text.split('\n')
-    while matches = wordRegex.exec(line)
-      word = matches[1]
-      continue if word in ['GitHub', 'github']
-      continue unless SpellChecker.isMisspelled(word)
+  while characterIndex < text.length and rangeIndex < misspelledCharacterRanges.length
+    lineBreakIndex = text.indexOf('\n', characterIndex)
+    if lineBreakIndex is -1
+      lineBreakIndex = Infinity

-      startColumn = matches.index + matches[0].length - word.length
-      endColumn = startColumn + word.length
-      misspellings.push([[row, startColumn], [row, endColumn]])
+    loop
+      range = misspelledCharacterRanges[rangeIndex]
+      if range and range.start < lineBreakIndex
+        misspellings.push([
+          [row, range.start - characterIndex],
+          [row, range.end - characterIndex]
+        ])
+        rangeIndex++
+      else
+        break
+
+    characterIndex = lineBreakIndex + 1
     row++
+
   {id, misspellings}


tests-billing: Update paid billing tests for the dynamic billing era

Resolves: #579
Change-type: patch
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@resin.io>

--- tests/integration/models/billing.spec.coffee
+++ tests/integration/models/billing.spec.coffee
@@ -143,25 +143,34 @@
 				resin.models.billing.getPlan()
 				.then (plan) ->
 					m.chai.expect(plan).to.deep.match
-						title: 'TestDev'
-						name: 'TestDev plan'
-						code: 'testdev'
-						tier: 'testdev'
+						title: 'Team member'
+						name: 'Team member plan'
+						code: 'free'
+						tier: 'free'
 						addOns: []
+						addonPlan:
+							addOns: []
+							billing:
+								charges: []
+								currency: 'USD'
+								totalCostCents: '0'
+							code: 'addons'
+							currentPeriodEndDate: ''
+							uuid: ''
 						billing:
 							currency: 'USD'
 							charges: [
 								{
 									itemType: 'plan'
-									name: 'TestDev plan'
-									code: 'testdev'
+									name: 'Team member plan'
+									code: 'free'
 									unitCostCents: '0'
 									quantity: '1'
 								}
 								{
 									itemType: 'support'
 									name: 'Standard support'
-									code: 'standard'
+									code: 'core'
 									unitCostCents: '0'
 									quantity: '1'
 								}
@@ -172,8 +181,6 @@
 							name: 'Standard support'

 					m.chai.expect(plan).to.have.property('currentPeriodEndDate').that.is.a('string')
-					m.chai.expect(plan).to.have.property('id').that.is.a('string')
-					m.chai.expect(plan).to.have.property('id').that.has.length(32)

 		describe 'resin.models.billing.getBillingInfo()', ->
 			givenABillingAccountIt 'should return a billing info object', ->


Fix typo in zh-HANS.coffee

Fix typo in zh-HANS.coffee

--- app/locale/zh-HANS.coffee
+++ app/locale/zh-HANS.coffee
@@ -75,7 +75,7 @@
     courses_available_in: "课程可用如下语言：JavaScript，Python，还有Java（即将推出！）"
     boast: "以难解的谜题为豪，它们复杂到可以吸引游戏玩家和程序员。"
     winning: "RPG游戏和编程作业的完美结合，使得教育充满乐趣。"
-    ruu_class: "管理计算机科学课程的所有东西都在这里，无需相关背景。"
+    run_class: "管理计算机科学课程的所有东西都在这里，无需相关背景。"
     teachers: "教师！"
     teachers_and_educators: "教师和教育工作者"
     class_in_box: "了解我们的一站式课堂如何让你的课程如虎添翼。"


Added tracking of failed android emulator start

--- lib/testing/env/android.coffee
+++ lib/testing/env/android.coffee
@@ -131,10 +131,29 @@

 runEmulator = (env, logsReader, callback) ->
     logsReader.log "Checking run android devices"
+    pending = true
+
+    finishWhenReady = (delay = 0) ->
+        unless pending
+            return
+
+        if isDeviceAvailable(env.deviceSerialNumber)
+            pending = false
+            callback null
+            return
+
+        if delay is 0
+            logsReader.log "Waiting for device"
+        else if delay % (DEVICE_RUN_TRY_DELAY * 10) is 0
+            logsReader.log "Waiting for device (#{delay / 1000}s)"
+        setTimeout ->
+            finishWhenReady delay + DEVICE_RUN_TRY_DELAY
+        , DEVICE_RUN_TRY_DELAY
+
     getDeviceSerialNumberByName env.emulatorName, (serialNumber) ->
         if serialNumber
             env.deviceSerialNumber = serialNumber
-            callback null
+            finishWhenReady()
             return

         logsReader.log "Starting android emulator"
@@ -143,27 +162,17 @@
         cmd = EMULATOR
         cmd += " -port #{port}"
         cmd += " -avd #{env.emulatorName}"
-        emulatorProcess = childProcess.exec cmd, cwd: SDK_DIR
+        cmd += " -no-audio"
+        emulatorProcess = childProcess.exec cmd, cwd: SDK_DIR, (err, stdout, stderr) ->
+            if (err or stderr) and pending
+                pending = false
+                log.error err or stderr
+                callback new Error 'Cannot run android emulator'
         emulatorProcess.stdout.pipe process.stdout
-
-        callbackCalled = false
-        emulatorProcess.stdout.on 'data', ->
-            unless callbackCalled
-                callbackCalled = true
-                callback null
-    return
-
-runTests = (env, logsReader, callback, delay = 0) ->
-    unless isDeviceAvailable(env.deviceSerialNumber)
-        if delay is 0
-            logsReader.log "Waiting for device"
-        else if delay % (DEVICE_RUN_TRY_DELAY * 10) is 0
-            logsReader.log "Waiting for device (#{delay / 1000}s)"
-        setTimeout ->
-            runTests env, logsReader, callback, delay + DEVICE_RUN_TRY_DELAY
-        , DEVICE_RUN_TRY_DELAY
-        return
-
+        finishWhenReady()
+    return
+
+runTests = (env, logsReader, callback) ->
     logsReader.log "Running android tests on #{env.deviceSerialNumber}"
     createdSerialNumbersByName[env.emulatorName] = env.deviceSerialNumber
     androidProcess = androidRun


Upgrade log-list-view

Upgrade log-list-view

--- lib/views/log-list-view.coffee
+++ lib/views/log-list-view.coffee
@@ -2,7 +2,8 @@
 Path = require 'path'
 fs = require 'fs-plus'

-{$$, BufferedProcess, SelectListView} = require 'atom'
+{BufferedProcess} = require 'atom'
+{$$, SelectListView} = require 'atom-space-pen-views'

 GitShow = require '../models/git-show'

@@ -17,7 +18,7 @@

   initialize: (@data, @onlyCurrentFile) ->
     super
-    @addClass 'overlay from-top'
+    @show()
     @parseData()

   parseData: ->
@@ -27,10 +28,18 @@
         tmp = item.match /([\w\d]{7});\|(.*);\|(.*);\|(.*)/
         {hash: tmp?[1], author: tmp?[2], title: tmp?[3], time: tmp?[4]}
     )
-    atom.workspaceView.append this
     @focusFilterEditor()

   getFilterKey: -> 'title'
+
+  show: ->
+    @panel ?= atom.workspace.addModalPanel(item: this)
+    @panel.show()
+
+  cancelled: -> @hide()
+
+  hide: ->
+    @panel?.hide()

   viewForItem: (commit) ->
     $$ ->


update comment

update comment

--- src/helpers/mathjax.coffee
+++ src/helpers/mathjax.coffee
@@ -37,7 +37,7 @@
       node.classList.add(MATH_RENDERED_CLASS)

 # Install a debounce around typesetting function so that it will only run once
-# every 10ms even if called multiple times in that period
+# every Xms even if called multiple times in that period
 typesetDocument = _.debounce( typesetDocument, 100)


:bug: Hanging indent must be a positive value

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -152,6 +152,7 @@
       softWrapHangingIndentationSpaces:
         type: 'integer'
         default: 0
+        minimum: 0
       scrollSensitivity:
         type: 'integer'
         default: 40


Comma, not a period

Comma, not a period

--- src/parsers/slackapp.coffee
+++ src/parsers/slackapp.coffee
@@ -19,7 +19,7 @@
   parsed

 module.exports = (lines) ->
-  [nick, ts. ampm] = []
+  [nick, ts, ampm] = []

   result = []
   for line in lines


Force @max to be at least 1

Force @max to be at least 1

--- resources/assets/coffee/_classes/stacked-bar-chart.coffee
+++ resources/assets/coffee/_classes/stacked-bar-chart.coffee
@@ -37,8 +37,7 @@
           value: y
           height: if previousData? then previousData.value + previousData.height else 0

-    @max = d3.max _.map @data, (m) -> _.sumBy m, 'value'
-    @max = 1 if @max == 0
+    @max = Math.max(1, _.map(@data, (m) -> _.sumBy(m, 'value'))...)

     @resize()


Simplify logic

Simplify logic

--- resources/assets/coffee/react/beatmapset-page/info.coffee
+++ resources/assets/coffee/react/beatmapset-page/info.coffee
@@ -125,9 +125,8 @@
       .slice(0, 21)
       .value()

-    if tags.length == 21
-      tags.pop()
-      tagsOverload = true
+    tagsOverload = tags.length == 21
+    tags.pop() if tagsOverload

     div className: 'beatmapset-info',
       if @state.isEditingDescription


clean up deps

clean up deps

--- app/lib/analytics.coffee
+++ app/lib/analytics.coffee
@@ -1,3 +1,6 @@
+querystring = require 'querystring'
+http = require 'http'
+
 ###
 This will log a user interaction using the analytics.zooniverse.org API.
 eventData should be of the format:
@@ -46,7 +49,4 @@
   req.write "{\"events\":[" + dataString + "]}"
   req.end()
   return
-
-querystring = require("querystring")
-http = require("http")
-exports.logEvent = logEvent
+module.exports = logEvent


more test coverage

more test coverage

--- _src/test/main.coffee
+++ _src/test/main.coffee
@@ -1,5 +1,6 @@
 should = require('should')
 _map = require('lodash/map')
+_difference = require('lodash/difference')

 Schema = require( "../." )

@@ -13,9 +14,12 @@
 			"a":
 				type: "string"
 				required: true
-
 			"b":
 				type: "number"
+
+			"c":
+				foreignReq: ["b"]
+				type: "boolean"

 		, { name: "settings" } )

@@ -33,11 +37,20 @@
 			"name":
 				type: "string"
 				required: true
+				trim: true
+				sanitize: true
+				striphtml: true
 				check:
 					operand: ">="
 					value: 4

 			"nickname":
+				type: "string"
+				check:
+					operand: "<"
+					value: 10
+
+			"nicknameb":
 				type: "string"
 				check:
 					operand: "<"
@@ -135,6 +148,7 @@
 			_data =
 				name: "John"
 				nickname: "johndoe"
+				nicknameb: "johndoe"
 				type: "ab"
 				email: "john@do.com"
 				sex: "M"
@@ -142,7 +156,7 @@
 				list: [1,2,3]
 				age: 23
 				timezone: "CET"
-				settings: { a: "foo" }
+				settings: { a: "foo", b: 123, c: true }
 				props: { foo: "bar" }
 				active: false
 				money: 1001
@@ -291,14 +305,33 @@
 			done()
 			return

-		it "failing string length too high", ( done )->
-			err = userValidator.validate( { name: "John", nickname: "johntheipsumdo", age: 0 } )
+		it "failing string length too high (lt)", ( done )->
+			err = userValidator.validate( { name: "John", nickname: "johntheipsumdo", age: 23 } )
 			should.exist( err )
 			err.name.should.eql( "EVALIDATION_USER_LENGTH_NICKNAME" )
 			err.should.have.property( "def" )
 				.and.have.property( "check" )
 				.and.have.property( "value" )
 				.and.eql( 10 )
+			done()
+			return
+
+		it "failing string length too high (lte)", ( done )->
+			err = userValidator.validate( { name: "John", nicknameb: "johntheipsu", age: 23 } )
+			should.exist( err )
+			err.name.should.eql( "EVALIDATION_USER_LENGTH_NICKNAMEB" )
+			err.should.have.property( "def" )
+				.and.have.property( "check" )
+				.and.have.property( "value" )
+				.and.eql( 10 )
+			done()
+			return
+
+		it "cheching trim and sanitize", ( done )->
+			data = { name: " John <script>alert(234)</script><b>Doe</b>    ", age: 23 }
+			err = userValidator.validate( data )
+			should.not.exist( err )
+			data.name.should.equal( "John Doe" )
 			done()
 			return

@@ -483,9 +516,6 @@
 			err.type.should.eql( "object" )
 			done()
 			return
-
-
-		return

 	describe 'Single Key -', ->
 		it "successfull validate a single key", ( done )->
@@ -577,6 +607,21 @@
 			should.exist( err )
 			err.name.should.eql( "EVALIDATION_USER_LENGTH_NAME" )
 			err.def.idx.should.eql( 1 )
+			done()
+			return
+
+		it "invalid name multi", ( done )->
+			_data = [ 45, "Doe", "johndo.com", 23 ]
+			err = userValidatorArray.validateMulti( _data, { type: "create" } )
+			should.exist( err )
+			for _e in err
+				switch _e.type
+					when "length"
+						_e.name.should.eql( "EVALIDATION_USER_LENGTH_NAME" )
+						_e.def.idx.should.eql( 1 )
+					when "email"
+						_e.name.should.eql( "EVALIDATION_USER_EMAIL_EMAIL" )
+						_e.def.idx.should.eql( 2 )
 			done()
 			return

@@ -653,6 +698,8 @@
 			_data.age.should.eql( 23 )
 			done()
 			return
+
+

 		it "failing validate with fnSkip", ( done )->
 			_data = { name: "John", email: "john@do.com" }
@@ -711,4 +758,21 @@
 				.and.eql( _expName )
 			done()
 			return
+		return
+
+	describe 'Helper Methods -', ->
+		it "keys method", ( done )->
+			_keys = userValidator.keys()
+			_exp = "name,nickname,nicknameb,type,email,sex,tag,list,age,timezone,settings,props,active,money,checkA,checkB,flagA,flagB,comment,settings_list".split( "," )
+			_difference( _keys, _exp ).should.length( 0 )
+			_difference( _exp, _keys ).should.length( 0 )
+			done()
+			return
+
+		it "trim method", ( done )->
+			userValidator.trim( "   a b  e t  " ).should.equal( "a b  e t" )
+			done()
+			return
+
+		return
 	return


Enhance the service log formatting

Enhance the service log formatting

--- library/gearing/service.coffee
+++ library/gearing/service.coffee
@@ -59,10 +59,10 @@
     # Supports implicit extraction of captured groups in the match.
     # Use this to configure what resources should match with service.
     @resource: (pattern) ->
-        inspected = util.inspect(pattern)
         duplicate = pattern in (@resources or [])
         regexify = (s) -> new RegExp "^#{RegExp.escape(s)}$"
         pattern = regexify(pattern)  if _.isString(pattern)
+        inspected = pattern.unescape()?.underline or pattern.source
         associate = "Associating #{inspected} resource with #{@name}"
         notRegexp = "The #{inspected} is not a valid regular expression"
         throw new Error(notRegexp) unless _.isRegExp(pattern)
@@ -75,10 +75,10 @@
     # Supports implicit extraction of captured groups in the match.
     # Use this to configure what domains should match with service.
     @domain: (pattern) ->
-        inspected = util.inspect(pattern)
         duplicate = pattern in (@domains or [])
         regexify = (s) -> new RegExp(RegExp.escape(s))
         pattern = regexify(pattern)  if _.isString(pattern)
+        inspected = pattern.unescape()?.underline or pattern.source
         associate = "Associating #{inspected} domain with #{@name}"
         notRegexp = "The #{inspected} is not a valid regular expression"
         throw new Error(notRegexp) unless _.isRegExp(pattern)


Unify the way domain is created

Unify the way domain is created

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -27,7 +27,6 @@
 asciify = require "asciify"
 connect = require "connect"
 logger = require "winston"
-domain = require "domain"
 assert = require "assert"
 colors = require "colors"
 nconf = require "nconf"
@@ -123,8 +122,8 @@
     # the domains and error handling itself. This method is generally
     # used only once per the domain declaration. See `provider`.
     @guarded: (method, socket) ->
-        guarded = domain.create()
         identify = @identify().underline
+        guarded = require("domain").create()
         assert _.isFunction o = Marshal.serialize
         assert _.isFunction i = Marshal.deserialize
         location = "Breakpoint at @#{method}#%s"


Minor touch

Minor touch

--- library/nucleus/scoping.coffee
+++ library/nucleus/scoping.coffee
@@ -92,7 +92,7 @@
         assert _.isString(prefix), "invalid prefix"
         throw new Error unknown unless basis in dirs
         prefix = prefix + "-" + unique if unique
-        return paths.join basis, prefix
+        return paths.join(basis, prefix).toString()

     # This method is responsible for starting up the scope object.
     # This means initialization of all its necessary routines and
@@ -105,7 +105,7 @@
         logger.info "Incorporating up the #{@tag.bold} scope".cyan
         logger.info "Assuming the #{fpath.underline} config".cyan
         exists = fs.existsSync fpath; nconf.file fpath if exists
-        for directory in nconf.get("env:dirs") or []
+        for directory in nconf.get("env:dirs") or new Array
             assert _.isNumber mode = nconf.get "env:mode"
             msg = "Environment mkdir at %s with 0%s mode".yellow
             logger.info msg, directory.underline, mode.toString 8
@@ -120,7 +120,7 @@
         logger.info "Dissipating the #{@tag.bold} scope".grey
         logger.info "Used #{fpath.underline} as config".grey
         assert _.isArray preserve = nconf.get "env:preserve"
-        for directory in nconf.get("env:dirs") or []
+        for directory in nconf.get("env:dirs") or new Array
             continue if directory in (preserve or Array())
             msg = "Wiping out the env directory at %s".yellow
             logger.info msg, directory.underline


Cleanup iterator and honor `options.as_buffer=false` in `key()`, `value()`, and `current()`

--- src/leveldb/iterator.coffee
+++ src/leveldb/iterator.coffee
@@ -9,49 +9,51 @@
   valid: ->
     @self.valid()

-  seek: (key, callback) ->
+  seek: (key, callback = noop) ->
     key = new Buffer key unless Buffer.isBuffer key
-    @self.seek key, callback or noop
+    @self.seek key, callback

   seekSync: (key) ->
     key = new Buffer key unless Buffer.isBuffer key
     @self.seek key

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

   firstSync: ->
     @self.first()

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

   lastSync: ->
     @self.last()

-  next: (options, callback) ->
-    if typeof options is 'function'
-      callback = options
-      options = null
-    @self.next callback or noop
+  next: (callback = noop) ->
+    @self.next callback

   nextSync: ->
     @self.next()

-  prev: (callback) ->
-    if typeof options is 'function'
-      callback = options
-      options = null
-    @self.prev callback or noop
+  prev: (callback = noop) ->
+    @self.prev callback

   prevSync: ->
     @self.prev()

   key: (options) ->
-    @self.key options
+    key = @self.key options
+    key = key.toString 'utf8' unless options.as_buffer
+    key

   value: (options) ->
-    @self.value options
+    val = @self.value options
+    val = val.toString 'utf8' unless options.as_buffer
+    val

-  current: (options) ->
-    @self.current()
+  current: (options = {}) ->
+    [ key, val ] = @self.current()
+    val = val.toString 'utf8' unless options.as_buffer
+    key = key.toString 'utf8' unless options.as_buffer
+    [ key, val ]
+


cistrans: delete trailing whitespace

cistrans: delete trailing whitespace

--- cistrans/cistrans.coffee
+++ cistrans/cistrans.coffee
@@ -622,7 +622,7 @@

       svg.selectAll("circle.plotPXG")
          .transition().duration(slowtime)
-         .attr("cx", (d,i) ->
+         .attr("cx", (d,i) ->
               g = Math.abs(data.geno[marker][i])
               sx = data.sex[i]
               if(data.pmark[marker].chr is "X")


add affiliations checking

add affiliations checking

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -39,10 +39,45 @@
     publishModel: "subscribers"
     creationDate: new Date().toISOString()

+AFFILIATION_SUBSETS =
+  owner: [ "moderator", "publisher", "member", "none" ]
+  moderator: [ "publisher", "member", "none" ]
+  publisher: [ "member", "none" ]
+  member: [ "none" ]
+
+isAffiliationSubset = (subset, affiliation) ->
+  subset == affiliation or
+            (AFFILIATION_SUBSETS.hasOwnProperty(affiliation) and
+             AFFILIATION_SUBSETS[affiliation].indexOf(subset) >= 0)
+

 class OperationContext
     constructor: (opts) ->
         @actor = opts.actor

+    checkAffiliation: (cb) ->
+        async.parallel [(cb) ->
+            t.getConfig req.node, cb
+        , (cb) ->
+            t.getAffiliation req.node, req.from, cb
+        , (cb) ->
+            t.getSubscription req.node, req.from, cb
+        ], (error, results) ->
+            if results
+                [config, affiliation, subscription] = results
+                if affiliation is "none" and (!config.accessModel? or config.accessModel is "open")
+                    affiliation = "member"
+                if affiliation is "member" and config.publishModel is "publishers" and subscription is "subscribed"
+                    affiliation = "publisher"
+        if isAffiliationSubset(@requiredAffiliation, affiliation)
+            cb()
+        else
+            cb new errors.Forbidden(requiredAffiliation + " affiliation required")
+
+    run: (steps, cb) ->
+        async.series steps, cb
+
 operation = (steps...) ->
-    return (opts, cb) ->+    return (opts, cb) ->
+        ctx = new OperationContext(opts)
+        ctx.run steps, cb


Documentation

Documentation

--- src/point.coffee
+++ src/point.coffee
@@ -418,29 +418,43 @@
     @name length
     @methodOf Point#

-    @returns The length of the vector from the origin to this point.
-    @type Number
+    @returns {Number} The length of the vector from the origin to this point.
     ###
     length: ->
       Math.sqrt(@dot(this))

     ###*
     Calculate the magnitude of this Point (Vector).
+
+

+       point = Point(5, 7)
+
+       point.magnitude()
+    => 8.602325267042627
+

+
     @name magnitude
     @methodOf Point#

-    @returns The magnitude of this point as if it were a vector from (0, 0) -> (x, y).
-    @type Number
+    @returns {Number} The magnitude of this point as if it were a vector from (0, 0) -> (x, y).
     ###
     magnitude: ->
       @length()

     ###*
     Returns the direction in radians of this point from the origin.
+
+

+       point = Point(0, 1)
+
+       point.direction()
+    => 1.5707963267948966 # Math.PI / 2
+

+
     @name direction
     @methodOf Point#

-    @type Number
+    @returns {Number} The direction in radians of this point from the origin
     ###
     direction: ->
       Math.atan2(@y, @x)
@@ -451,8 +465,7 @@
     @methodOf Point#

     @param {Point} other The point to dot with this point.
-    @returns The dot product of this point dot other as a scalar value.
-    @type Number
+    @returns {Number} The dot product of this point dot other as a scalar value.
     ###
     dot: (other) ->
       @x * other.x + @y * other.y
@@ -467,25 +480,42 @@
     @methodOf Point#

     @param {Point} other The point to cross with this point.
-    @returns The cross product of this point with the other point as scalar value.
-    @type Number
+    @returns {Number} The cross product of this point with the other point as scalar value.
     ###
     cross: (other) ->
       @x * other.y - other.x * @y

     ###*
-    Computed the Euclidean between this point and another point.
+    Compute the Euclidean distance between this point and another point.
+
+

+       pointA = Point(2, 3)
+       pointB = Point(9, 2)
+
+       pointA.distance(pointB)
+    => 7.0710678118654755 # Math.sqrt(50)
+

+
     @name distance
     @methodOf Point#

     @param {Point} other The point to compute the distance to.
-    @returns The distance between this point and another point.
-    @type Number
+    @returns {Number} The distance between this point and another point.
     ###
     distance: (other) ->
       Point.distance(this, other)

   ###*
+  Compute the Euclidean distance between two points.
+
+

+     pointA = Point(2, 3)
+     pointB = Point(9, 2)
+
+     Point.distance(pointA, pointB)
+  => 7.0710678118654755 # Math.sqrt(50)
+

+
   @name distance
   @fieldOf Point
   @param {Point} p1


fixed missleading log file

fixed missleading log file

--- services/filestore/app/coffee/s3Wrapper.coffee
+++ services/filestore/app/coffee/s3Wrapper.coffee
@@ -98,7 +98,7 @@
 		options = buildDefaultOptions(bucketName, "head", key)
 		request options, (err, res)->
 			if err?
-				logger.err err:err, res:res, bucketName:bucketName, key:key, "something went wrong copying file in aws"
+				logger.err err:err, res:res, bucketName:bucketName, key:key, "something went wrong checking file in aws"
 			if !res?
 				logger.err err:err, res:res, bucketName:bucketName, key:key, "no response object returned when checking if file exists"
 				err = new Error("no response from s3 #{bucketName} #{key}")


Fix a bug where scopes were being duplicated for every single token

This improves scroll performance by generating many fewer span elements
to render lines.

--- src/lines-component.coffee
+++ src/lines-component.coffee
@@ -212,7 +212,7 @@

     # Find a common prefix
     for scope, i in desiredScopes
-      break unless scopeStack[i]?.scope is desiredScopes[i]
+      break unless scopeStack[i] is desiredScopes[i]

     # Pop scopes until we're at the common prefx
     until scopeStack.length is i


Native promises don't support .finally

Native promises don't support .finally

--- src/liquid/template.coffee
+++ src/liquid/template.coffee
@@ -59,11 +59,19 @@
     if options?.filters
       context.registerFilters options.filters...

+    copyErrors = (actualResult) =>
+      @errors = context.errors
+      actualResult
+
     @root.render(context)
     .then (chunks) ->
       Liquid.Helpers.toFlatString chunks
-    .finally =>
+    .then (result) ->
       @errors = context.errors
+      result
+    , (error) ->
+      @errors = context.errors
+      throw error

   # Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize
   # the passed source


Basicly styled redeem button

Basicly styled redeem button

--- client/Account/views/referralsystem.coffee
+++ client/Account/views/referralsystem.coffee
@@ -47,9 +47,11 @@
     #     appManager.tell "Account", "showReferrerModal",
     #       linkView    : getYourReferrerCode

-    wrapper.addSubView redeem = new CustomLinkView
-      title : "Redeem your VM space"
-      click : => @showRedeemReferralPointModal()
+    wrapper.addSubView redeem = new KDButtonView
+        cssClass  : 'account-add-big-btn'
+        title     : 'Redeem your VM space'
+        icon      : yes
+        callback  : => @showRedeemReferralPointModal()

 class AccountReferralSystemList extends KDListView


Restore method of sorting renderers and improve naming convention

Sorting was broken in PR #2393. This restores the original approach
and improves naming convention, so that we explicitly see that we
sort renderer views and not models.

--- bokehjs/src/coffee/common/plot.coffee
+++ bokehjs/src/coffee/common/plot.coffee
@@ -295,12 +295,14 @@
     indices = {}
     for renderer, i in @mget("renderers")
       indices[renderer.id] = i
-    sortKey = (renderer) -> indices[renderer.id]
+
+    sortKey = (renderer_view) -> indices[renderer_view.model.id]

     for level in levels
-      renderers = _.sortBy(_.values(@levels[level]), sortKey)
-      for renderer in renderers
-        renderer.render()
+      renderer_views = _.sortBy(_.values(@levels[level]), sortKey)
+
+      for renderer_view in renderer_views
+        renderer_view.render()

     ctx.restore()


Update spy to not be on prototype

Update spy to not be on prototype

Small tweak as I learn sinon better.

--- bokehjs/test/models/plots/plot.coffee
+++ bokehjs/test/models/plots/plot.coffee
@@ -185,8 +185,8 @@
     @test_plot._doc_attached()
     @test_plot_view = new @test_plot.default_view({ 'model': @test_plot })

-  it "should call own :update_constraints method", ->
-    spy = sinon.spy(PlotView.prototype, 'update_constraints')  # Setup
+  it "should call own update_constraints method", ->
+    spy = sinon.spy(@test_plot_view, 'update_constraints')
     @test_plot_view.render()
     expect(spy.calledOnce).to.be.true


comment for unfuddle.coffee

comment for unfuddle.coffee

--- src/scripts/unfuddle.coffee
+++ src/scripts/unfuddle.coffee
@@ -1,3 +1,8 @@
+# Interacting with unfuddle
+#
+# <project shortcut> #ticket_number - display unfuddle ticket
+#
+
 Http  = require 'http'
 Https = require 'https'
 Qs    = require 'querystring'


stubbed out api.attributes.coffee

stubbed out api.attributes.coffee

--- test/api.attributes.coffee
+++ test/api.attributes.coffee
@@ -17,3 +17,56 @@
   Tests
 ###

+describe '$(...)', ->
+  describe '.attr', ->
+
+    it '() : should get all the attributes'
+
+    it '(invalid key) : invalid attr should get undefined', ->
+      should.strictEqual undefined, $('.apple', fruits).attr('lol')
+
+    it '(valid key) : valid attr should get value', ->
+      $('.apple', fruits).attr('class').should.equal 'apple'
+
+    it '(key, value) : should set attr', ->
+      $fruits = $(fruits)
+
+      $pear = $('.pear', $fruits).attr('id', 'pear')
+      $('#pear', $fruits).should.have.length 1
+
+    it '(map) : object map should set multiple attributes'
+
+  describe '.removeAttr', ->
+
+    it '(key) : should remove a single attr'
+
+  describe '.hasClass', ->
+
+    it '(valid class) : should return true'
+
+    it '(invalid class) : should return false'
+
+  describe '.addClass', ->
+
+    it '(first class) : should add the class to the element'
+
+    it '(single class) : should add the class to the element'
+
+    it '(class class class) : should add multiple classes to the element'
+
+    it '(fn) : should add classes returned from the function'
+
+  describe '.removeClass', ->
+
+    it '() : should remove all the classes'
+
+    it '(no class) : should not remove anything'
+
+    it '(single class) : should remove a single class from the element'
+
+    it '(class class class) : should remove multiple classes from the element'
+
+    it '(fn) : should remove classes returned from the function'
+
+
+


Pad queries with newlines

Pad queries with newlines

--- src/quest.coffee
+++ src/quest.coffee
@@ -245,7 +245,7 @@
       queries = new Splitter().split queries
     result = null
     for i, query of queries
-      console.log query.green
+      console.log "\n#{query}\n".green
       if cb?
         @client.query(query, cb)
       else


Add missing )

Add missing )

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -64,7 +64,7 @@
         {assetName: 'atom-api.json', sourcePath: 'atom-api.json'}
       ]
     when 'win32'
-      cp path.join(buildDir, 'installer', 'AtomSetup.exe', path.join(buildDir, 'AtomSetup.exe')
+      cp path.join(buildDir, 'installer', 'AtomSetup.exe'), path.join(buildDir, 'AtomSetup.exe')

       [
         {assetName: 'atom-windows.zip', sourcePath: 'Atom'}


Prepare docs when building all branches

Prepare docs when building all branches

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -20,9 +20,9 @@
   {cp} = require('./task-helpers')(grunt)

   grunt.registerTask 'publish-build', 'Publish the built app', ->
-    return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
-    tasks = ['upload-assets']
-    tasks.unshift('build-docs', 'prepare-docs') if process.platform is 'darwin'
+    tasks = []
+    tasks.push('build-docs', 'prepare-docs') if process.platform is 'darwin'
+    tasks.push('upload-assets') if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
     grunt.task.run(tasks)

   grunt.registerTask 'prepare-docs', 'Move api.json to atom-api.json', ->


added no color menu option

added no color menu option

--- src/menu/defaultmenu.coffee
+++ src/menu/defaultmenu.coffee
@@ -169,6 +169,9 @@
       menuColor.addNode "Hydrophobicity", =>
         @msa.g.colorscheme.set "scheme","hydrophobicity"

+      menuColor.addNode "No color", =>
+        @msa.g.colorscheme.set "scheme","foo"
+
       menuColor.addNode "Toggle background", =>
         @msa.g.colorscheme.set "colorBackground", !@msa.g.colorscheme.get("colorBackground")


Fix bug about vertial layout, close #833

--- views/services/layout.cjsx
+++ views/services/layout.cjsx
@@ -59,6 +59,7 @@
   else
     $('kan-game')?.style?.display = ''
   if url != 'http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/' and !(url?.startsWith('http://osapi.dmm.com/gadgets/ifr'))
+    return if window.layout is 'vertical'
     $('kan-game #webview-wrapper')?.style?.width = "#{webviewWidth}px"
     adjustWebviewHeight "#{window.innerHeight - poiControlHeight}px"
     factor = null


Remove coverage requirements during R&D

Remove coverage requirements during R&D

--- gulp.coffee
+++ gulp.coffee
@@ -166,10 +166,10 @@
   }))
   .pipe(gCoverageEnforcer({
     thresholds : {
-      statements : 100,
-      branches : 100,
-      lines : 100,
-      functions : 100
+      statements : 0,
+      branches : 0,
+      lines : 0,
+      functions : 0
     },
     coverageDirectory : 'coverage',
     rootDirectory : ''


fix mvp typo

fix mvp typo

--- parts/prophet-info.cjsx
+++ parts/prophet-info.cjsx
@@ -23,9 +23,9 @@

           {
             if @props.mvp == true
-              {@props.ship.damage}
+              {@props.ship.hp.damage}
             else
-              {@props.ship.damage}
+              {@props.ship.hp.damage}
           }


Consensus::toString

Consensus::toString

--- src/skuld.coffee
+++ src/skuld.coffee
@@ -51,7 +51,7 @@
     add : (norn) -> @bind (old) => @unit old.concat norn
     prepare : (skuld) -> throw "NI"
     remove : (norns) -> throw "NI"
-#    toString : () -> @bind (norns) -> "Consensus(#{norns})"
+    toString : () -> @bind (norns) => "Consensus(#{norns})"
   })
   , (List norns), TConsensus)


Accept an optional directory for Kona.Sounds.load

--- src/sound.coffee
+++ src/sound.coffee
@@ -15,12 +15,30 @@
   # Ex:
   #
   #     Kona.Sounds.load {
+  #       'fire' : 'audio/enemy_fire.ogg'
+  #     }
+  #
+  # You can also specify a folder where sounds are located.
+  #
+  # Ex:
+  #
+  #     Kona.Sounds.load 'audio/', {
   #       'fire' : 'enemy_fire.ogg'
   #     }
   #
-  load: (sounds={}) ->
-    for name, src of sounds
-      @sounds[name] = new Kona.Sound(src)
+  load: (dir_or_sounds, sounds={}) ->
+    if _.isString(dir_or_sounds)
+      # Directory and sounds provided
+      dir = dir_or_sounds
+      for name, src of sounds
+        sep  = if dir.slice(-1) == '/' then '' else '/'
+        path = "#{dir}#{sep}#{src}"
+        @sounds[name] = new Kona.Sound(path)
+    else
+      # Only sounds provided
+      sounds = dir_or_sounds
+      for name, src of sounds
+        @sounds[name] = new Kona.Sound(src)

   # Play a sound by name if it exists,


Set false to commonjs for legacy

Set false to commonjs for legacy

I realized that there is an issue where legacy options are not getting..
"priority". I'm not sure if it really matters, since anyone legacy mode
won't have the plus namespace declared, and thusly won't have any
options overriding the legacy options..

Anyway, i'm adding this line to try and make legacy actually run like it
would on the official compiler.

--- bin/kdc-plus.coffee
+++ bin/kdc-plus.coffee
@@ -243,6 +243,7 @@
   # Our legacy options, used for the legacy calls below
   legacyOptions =
     coffee        : true
+    commonjs      : false
     bare          : true
     pistachioThis : true


plotly relative barmode

plotly relative barmode

--- src/plotly_renderers.coffee
+++ src/plotly_renderers.coffee
@@ -113,8 +113,8 @@
         "Horizontal Bar Chart": makePlotlyChart({type: 'bar', orientation: 'h'},
             {barmode: 'group'}, true)
         "Horizontal Stacked Bar Chart": makePlotlyChart({type: 'bar', orientation: 'h'},
-            {barmode: 'stack'}, true)
+            {barmode: 'relative'}, true)
         "Bar Chart": makePlotlyChart({type: 'bar'}, {barmode: 'group'})
-        "Stacked Bar Chart": makePlotlyChart({type: 'bar'}, {barmode: 'stack'})
+        "Stacked Bar Chart": makePlotlyChart({type: 'bar'}, {barmode: 'relative'})
         "Line Chart": makePlotlyChart()
         "Scatter Chart": makePlotlyScatterChart()


Fix spec that wasn't actually testing anything

--- spec/extensions/tree-view-spec.coffee
+++ spec/extensions/tree-view-spec.coffee
@@ -56,8 +56,8 @@
       grandchild = child.find('.entries > li:contains(a-dir/)').view()
       grandchild.disclosureArrow.click()

-      rootDirectoryView.find('> .disclosure-arrow').click()
-      rootDirectoryView.find('> .disclosure-arrow').click()
+      rootDirectoryView.collapse()
+      rootDirectoryView.expand()

       # previously expanded descendants remain expanded
       expect(rootDirectoryView.find('> .entries > li:contains(dir/) > .entries > li:contains(a-dir/) > .entries').length).toBe 1


Fix language's scope classification

Fix language's scope classification

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -1,5 +1,5 @@
 name: "Roff"
-scopeName: "source.roff"
+scopeName: "text.roff"
 fileTypes: [
 	"1", "1b", "1c", "1has", "1in", "1m", "1s", "1x",
 	"2",


chore: watch the unit tests dir so they're executed when changed

--- grunt/watch.coffee
+++ grunt/watch.coffee
@@ -2,5 +2,5 @@
 # @see https://github.com/gruntjs/grunt-contrib-watch
 module.exports =
   server:
-    files: ['src/**/*.js','src/**/*.coffee']
+    files: ['src/**/*.js','src/**/*.coffee','test/unit/**/*.js','test/unit/**/*.coffee']
     tasks: ['coffeelint', 'jshint', 'mochaTest:unit']


Graphic JS specs refactored so they do not rely on templates

--- spec/javascripts/module/graphic_spec.js.coffee
+++ spec/javascripts/module/graphic_spec.js.coffee
@@ -2,15 +2,40 @@
 #= require spec_helper
 #= require modules
 #= require module/graphic
+#= require sinon

-describe "Graphic#constructor", ->
-  describe 'with a visible drawer', ->
-    before () ->
+describe "Module.Graphic", ->
+  describe "constructor", ->
+    before ->
       @container = 'container404829'
       @metric_name = 'Pain'
       @module_id = '54405'
-      $('body').html(JST['templates/metric_results']({container: @container, metric_name: @metric_name, module_id: @module_id}))

-    it "should construct a graphic", ->
-      graphic = new Module.Graphic(@container, @metric_name, @module_id)
+      @drawer = sinon.stub()
+      sinon.stub(window, "$")
+      $.withArgs("tr#"+@container).returns(@drawer)

+    context 'when the drawer is hidden', ->
+      before ->
+        @drawer.is = sinon.stub().withArgs(':hidden').returns(true)
+
+      it "should show the drawer and start to load a graphic", ->
+        @drawer.slideDown = sinon.spy()
+
+        Module.Graphic.prototype.load = sinon.spy()
+
+        @graphic = new Module.Graphic(@container, @metric_name, @module_id)
+
+        assert.isTrue(@drawer.slideDown.calledOnce)
+        assert.isTrue(@graphic.load.calledOnce)
+
+    context 'when the drawer is visible', ->
+      before ->
+        @drawer.is = sinon.stub().withArgs(':hidden').returns(false)
+
+      it 'should hide the drawer', ->
+        @drawer.slideUp = sinon.spy()
+
+        @graphic = new Module.Graphic(@container, @metric_name, @module_id)
+
+        assert.isTrue(@drawer.slideUp.calledOnce)


refactored shareWithHelper in computeproviders/managed.coffee

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -226,6 +226,22 @@
         account.sendNotification 'MachineListUpdated', { machineUId, action }

+  validateTarget = (target, user, callback) ->
+    # At least one target is required
+    if not target or target.length is 0
+      return callback new KodingError 'A target required.'
+
+    # Max 9 target can be passed
+    if target.length > 9
+      return callback new KodingError \
+        'It is not allowed to change more than 9 state at once.'
+
+    # Owners cannot unassign them from a machine
+    # Only another owner can unassign any other owner
+    if user.username in target
+      return callback \
+        new KodingError 'You are not allowed to change your own state!'
+
   # Private Methods
   # ---------------

@@ -558,25 +574,6 @@
         @update $set: { label }, (err) -> kallback err, slug

-  shareWithHelper =
-
-    validateTarget : (target, user, callback) ->
-      # At least one target is required
-      if not target or target.length is 0
-        return callback new KodingError 'A target required.'
-
-      # Max 9 target can be passed
-      if target.length > 9
-        return callback new KodingError \
-          'It is not allowed to change more than 9 state at once.'
-
-      # Owners cannot unassign them from a machine
-      # Only another owner can unassign any other owner
-      if user.username in target
-        return callback \
-          new KodingError 'You are not allowed to change your own state!'
-
-
   # .shareWith can be used like this:
   #
   # JMachineInstance.shareWith {
@@ -605,7 +602,7 @@

       { target, permanent, asUser } = options

-      shareWithHelper.validateTarget target, user, callback
+      validateTarget target, user, callback

       # For Koding provider credential field is username
       # and we don't allow them to be removed from users


Use node_modules libraries

Use node_modules libraries

--- gulp/config.coffee
+++ gulp/config.coffee
@@ -5,11 +5,14 @@
       "./assets/css/**/*.scss"
     ]
     haml   : "./**/*.haml"
-    fonts  : "./bower_components/**/fonts/**"
+    fonts  : [
+      "./node_modules/bootstrap-sass/assets/fonts/bootstrap/**"
+      "./node_modules/font-awesome/fonts/**"
+    ]
     lib    :
       js : [
-        "./bower_components/jquery/dist/jquery.min.js"
-        "./bower_components/bootstrap-sass/assets/javascripts/bootstrap.min.js"
+        "./node_modules/jquery/dist/jquery.min.js"
+        "./node_modules/bootstrap-sass/assets/javascripts/bootstrap.min.js"
       ]

   dist :


Deprecate Token::scopes

Deprecate Token::scopes

--- src/token.coffee
+++ src/token.coffee
@@ -20,6 +20,10 @@
   firstNonWhitespaceIndex: null
   firstTrailingWhitespaceIndex: null
   hasInvisibleCharacters: false
+
+  Object.defineProperty @::, 'scopes', get: ->
+    deprecate 'Use ::scopeDescriptor instead'
+    @scopeDescriptor

   constructor: ({@value, @scopeDescriptor, @isAtomic, @bufferDelta, @isHardTab}) ->
     @screenDelta = @value.length


ignore metadata test.

ignore metadata test.

--- test/odata.metadata.coffee
+++ test/odata.metadata.coffee
@@ -1,3 +1,4 @@
+###
 should = require('should')
 request = require('supertest')
 odata = require('../.')
@@ -27,3 +28,4 @@
         res.body.resources.book.should.be.have.property('title')
         res.body.resources.book.title.should.be.equal('String')
         done()
+###


Test inclusion of session_state parameter at authorize/connect endpoints

--- test/oidc/authorize.coffee
+++ test/oidc/authorize.coffee
@@ -72,6 +72,8 @@
     it 'should provide state', ->
       res.redirect.should.have.been.calledWith sinon.match 'state=r4nd0m'

+    it 'should not provide session_state', ->
+      res.redirect.should.not.have.been.calledWith sinon.match('session_state=')

@@ -207,6 +209,9 @@
     it 'should provide state', ->
       res.redirect.should.have.been.calledWith sinon.match req.connectParams.state

+    it 'should provide session_state', ->
+      res.redirect.should.have.been.calledWith sinon.match('session_state=')
+

@@ -263,6 +268,8 @@
     it 'should provide state', ->
       res.redirect.should.have.been.calledWith sinon.match 'state=r4nd0m'

+    it 'should provide session_state', ->
+      res.redirect.should.have.been.calledWith sinon.match('session_state=')

@@ -317,6 +324,8 @@
     it 'should provide state', ->
       res.redirect.should.have.been.calledWith sinon.match req.connectParams.state

+    it 'should provide session_state', ->
+      res.redirect.should.have.been.calledWith sinon.match('session_state=')

@@ -378,6 +387,8 @@
     it 'should provide state', ->
       res.redirect.should.have.been.calledWith sinon.match req.connectParams.state

+    it 'should provide session_state', ->
+      res.redirect.should.have.been.calledWith sinon.match('session_state=')


Fix Facebook link

Fix Facebook link

--- app/views/sharing.coffee
+++ app/views/sharing.coffee
@@ -15,7 +15,7 @@
     title = @model.get('name')
     summary = "I just make this Dashboard on ZooTools"
     """
-      https://www.facbook.com/sharer/sharer.php
+      https://www.facebook.com/sharer/sharer.php
       ?s=100
       &p[url]=#{encodeURIComponent @dashboardHref()}
       &p[title]=#{encodeURIComponent title}


Fixed problem referencing 'global/nav' from layout. Applied solution from @timshadel in mauricemach/zappa#103

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -74,7 +74,12 @@
     fs.statSync(p)
     return true
   catch err
-    return false
+    p = @path
+    try
+      fs.statSync(p)
+      return true
+    catch err
+      return false

 express.View.prototype.__defineGetter__ 'contents', ->
   # Path given by zappa: /path/to/appid/foo.bar.
@@ -90,7 +95,11 @@

   # Try /path/to/foo.bar in filesystem (normal express behaviour).
   p = @path.replace id + '/', ''
-  fs.readFileSync p, 'utf8'
+  try
+    fs.readFileSync p, 'utf8'
+  catch err
+    p = @path
+    fs.readFileSync p, 'utf8'

 # Takes in a function and builds express/socket.io apps based on the rules contained in it.
 zappa.app = (func) ->
@@ -281,6 +290,16 @@
           if app.settings['databag']
             args[1].params = data

+          # Don't change layout: false
+          unless args[1].layout is false
+            # Use the default layout if one isn't given, or layout: true
+            if args[1].layout is true or not args[1].layout?
+              args[1].layout = 'layout'
+
+            # Don't add id if it's there already
+            if args[1].layout.split('/')[0] is not context.id
+              args[1].layout = context.id + '/' + args[1].layout
+
           if args[1].postrender?
             # Apply postrender before sending response.
             res.render args[0], args[1], (err, str) ->


add back event emission in toolbar

add back event emission in toolbar

--- src/modules/toolbar.coffee
+++ src/modules/toolbar.coffee
@@ -14,7 +14,9 @@
         value = if input.tagName == 'SELECT' then input.options[input.selectedIndex].value else !ScribeDOM.hasClass(input, 'active')
         @editor.root.focus()
         range = @editor.getSelection()
-        range.format(format, value, { source: 'user' }) if range
+        if range
+          range.format(format, value, { source: 'user' })
+          @editor.emit(@editor.constructor.events.PREFORMAT, format, value)
       )
     )
   )


Making bare the default for eval in the browser (fixes #2148)

--- browser.coffee
+++ browser.coffee
@@ -5,6 +5,7 @@

 # Use standard JavaScript `eval` to eval code.
 CoffeeScript.eval = (code, options) ->
+  options.bare ?= on
   eval CoffeeScript.compile code, options

 # Running code does not provide access to this scope.


Displays the project name the the build belongs to

--- src/scripts/teamcity.coffee
+++ src/scripts/teamcity.coffee
@@ -21,13 +21,22 @@
         # Sort by build number.
         builds = JSON.parse(body).build.sort((a, b)-> parseInt(b.number) - parseInt(a.number))
         for build in builds
-          if build.running
-            started = Date.parse(build.startDate.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})([+\-]\d{4})/, "$1-$2-$3T$4:$5:$6$7"))
-            elapsed = (Date.now() - started) / 1000
-            seconds = "" + Math.floor(elapsed % 60)
-            seconds = "0#{seconds}" if seconds.length < 2
-            msg.send "#{build.number}, #{build.percentageComplete}% complete, #{Math.floor(elapsed / 60)}:#{seconds} minutes"
-          else if build.status is "SUCCESS"
-            msg.send "#{build.number} is full of win"
-          else if build.status is "FAILUED"
-            msg.send "#{build.number} is #fail"
+          msg.http("http://#{hostname}#{build.href}")
+            .headers(Authorization: "Basic #{new Buffer("#{username}:#{password}").toString("base64")}", Accept: "application/json")
+            .get() (err, res, body) ->
+              if err
+                msg.send "Team city says: #{err}"
+                return
+
+              project = JSON.parse(body)
+
+              if build.running
+                started = Date.parse(build.startDate.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})([+\-]\d{4})/, "$1-$2-$3T$4:$5:$6$7"))
+                elapsed = (Date.now() - started) / 1000
+                seconds = "" + Math.floor(elapsed % 60)
+                seconds = "0#{seconds}" if seconds.length < 2
+                msg.send "#{project.buildType.projectName} - #{build.number}, #{build.percentageComplete}% complete, #{Math.floor(elapsed / 60)}:#{seconds} minutes"
+              else if build.status is "SUCCESS"
+                msg.send "#{project.buildType.projectName} - #{build.number} is full of win"
+              else if build.status is "FAILUED"
+                msg.send "#{project.buildType.projectName} - #{build.number} is #fail"


Switch to tab with index saved before deleting

Sometimes Chrome removes a tab and activates previous one.
This prevents from activating previous-previous tab after removing
current one.

--- coffee/background.coffee
+++ coffee/background.coffee
@@ -120,9 +120,12 @@
         for arg in req.args then switch arg
             when "--focusprev" then prev = true

-        chrome.tabs.getSelected null, (tab) =>
-            chrome.tabs.remove(tab.id)
-            @reqTabFocusPrev(times: 1, args: []) if prev
+        chrome.tabs.getSelected(null, (tab) =>
+            index = tab.index
+            chrome.tabs.remove(tab.id, =>
+                @moveTab(-1, index) if prev
+            )
+        )
         false

     reqTabCloseAll : (req) ->


isolate button test from controls

isolate button test from controls

--- src/test/test_render.coffee
+++ src/test/test_render.coffee
@@ -1,3 +1,4 @@
+{my} = require '../my'
 {god} = require '../god'
 {rx_mock} = require './rx_mock'
 {vector} = require '../god/vector'
@@ -78,11 +79,9 @@
     t.end()

   test "render buttons", (t) ->
-    body = render_mock(controls).body
-    t.equal body.tag, 'div', 'body.tag'
-    t.equal get_label(body), controls._LABEL, 'controls label'
-
-    button = body.body[0]
+    dict = make.buttons('button',["a", "b"], my.control)
+    buttons = render_mock(dict).body.body
+    button = buttons[0]
     t.equal button.tag, 'span', 'button span'
     t.ok style = button.attr.style, 'button style'
     t.ok style.border, 'button border'


enabled line wrapping in the session

enabled line wrapping in the session

--- client/session.coffee
+++ client/session.coffee
@@ -36,7 +36,8 @@
       return

   cm = CodeMirror ($e = $ e)[0],
-    autofocus: true, mode: '', matchBrackets: true, autoCloseBrackets: true, keyMap: 'dyalog', readOnly: true, extraKeys: k
+    autofocus: true, mode: '', lineWrapping: true, matchBrackets: true, autoCloseBrackets: true, readOnly: true
+    keyMap: 'dyalog', extraKeys: k

   exec = (trace) ->
     a = [] # pairs of [lineNumber, contentToExecute]


Minor test cleanup.

Minor test cleanup.

--- tests/js/core.mixin.coffee
+++ tests/js/core.mixin.coffee
@@ -3,7 +3,7 @@
   'underscore'
   'hlf/jquery.extension.hlf.core'
 ], ($, _, hlf) ->
-
+
   QUnit.module 'mixin',
     setup: ->
       @.mixins = {}
@@ -27,7 +27,6 @@
       'Mixin should have been added to mixin collection.'
     assert.strictEqual @mixins[@mixinName], @mixin,
       'Mixin should be accessible.'
-    console.log @mixins[@mixinName].someMethod
     assert.strictEqual @mixins[@mixinName].someMethod.mixin, @mixinName,
       'Mixin method should have mixin name attached.'
     result = hlf.createMixin @mixins, @mixinName, @mixin


add example of publishing a file using API query

--- src/smc-util/message.coffee
+++ src/smc-util/message.coffee
@@ -1669,7 +1669,7 @@
        "id":"9dd3ef3f-002b-4893-b31f-ff51440c855f"}
 ```

-Example of _set_ query.
+Examples of _set_ query.

 Set title and description for a project, given the project id.
 ```
@@ -1683,6 +1683,21 @@
          "query":{},
          "multi_response":false,
          "id":"ad7d6b17-f5a9-4c5c-abc3-3823b1e1773f"}
+```
+
+Make a path public (publish a file).
+```
+  curl -u sk_abcdefQWERTY090900000000: \\
+    -H "Content-Type: application/json" \\
+    -d '{"query":{"public_paths":{"project_id":"29163de6-b5b0-496f-b75d-24be9aa2aa1d", \\
+                                  "path":"myfile.txt", \\
+                                  "description":"a shared text file"}}}' \\
+    https://cocalc.com/api/v1/query
+    ==> {"event":"query",
+         "query":{},
+         "multi_response":false,
+         "id":"ad7d6b17-f5a9-4c5c-abc3-3823b1e1773f"}
+
 ```

 Information on which fields are gettable and settable in the database tables


improve an error message involving stripe...

--- src/smc-webapp/billing.cjsx
+++ src/smc-webapp/billing.cjsx
@@ -36,7 +36,7 @@
     if Stripe?
         cb()
     else
-        $.getScript("https://js.stripe.com/v2/").done(->cb()).fail(->cb('Unable to load Stripe support'))
+        $.getScript("https://js.stripe.com/v2/").done(->cb()).fail(->cb('Unable to load Stripe support; make sure your browser is not blocking stripe.com.'))

 last_subscription_attempt = null


Cleaning whitespace

Cleaning whitespace

--- src/resource_client.coffee
+++ src/resource_client.coffee
@@ -72,9 +72,6 @@
     route = {method: method, path: path}
     route.query = $.param(options.params) if options.params?
     route
-
-
-

   _request: (path, data, callback) ->
     proceed = =>


Close the connection, not the pool

Close the connection, not the pool

--- src/storage/postgres.coffee
+++ src/storage/postgres.coffee
@@ -82,7 +82,7 @@
     adaptPromise(@db.none(sql), callback)

   disconnect: (callback) ->
-    @db.$pool.end()
+    @db.done()
     process.nextTick -> callback(null)

 module.exports = PostgresStorage


Removed debug schedule

Removed debug schedule

--- src/scripts/payroll.coffee
+++ src/scripts/payroll.coffee
@@ -86,7 +86,7 @@

     return response

-  generateDailyReportJob = schedule.scheduleJob '* * * * *', ->
+  generateDailyReportJob = schedule.scheduleJob '0 9 * * *', ->
     if not Organization.ready()
       Logger.warn "Don't make scheduled daily report,
                    Organization isn't ready yet"


jshint is no longer a package I use

jshint is no longer a package I use

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -22,7 +22,6 @@
       "linter-elixirc"
       "autocomplete-elixir"
       "linter-htmlhint"
-      "linter-jshint"
     ]
     excludeVcsIgnoredPaths: false
     ignoredNames: [


Atom updates

Atom updates

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -8,6 +8,8 @@
       "vim-mode"
       "linter-jscs"
       "linter-jshint"
+      "symbols-view"
+      "screen-send"
     ]
     ignoredNames: [
       ".bundle"
@@ -23,8 +25,8 @@
       "base16-tomorrow-dark-theme"
     ]
   editor:
-    autoIndent: false
     fontFamily: "Fira Code"
+    fontSize: 15
     invisibles: {}
     showIndentGuide: true
     tabType: "soft"
@@ -36,6 +38,7 @@
     executablePath: " /opt/boxen/rbenv/shims/scss-lint"
   "release-notes":
     viewedVersion: "0.89.0"
+  "rspec-iterm": {}
   "spell-check":
     grammars: [
       "text.plain"


updated atom config

updated atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,5 +1,9 @@
 "*":
   "atom-alignment": {}
+  "atom-beautify":
+    general:
+      _analyticsUserId: "2a4db25f-5cc8-4bcd-ab25-36b19bc3c9c7"
+    xml: {}
   "autocomplete-elixir":
     elixirPath: "/usr/local/bin/elixir"
     elixrPath: "/usr/local/bin/elixir"
@@ -32,6 +36,7 @@
       ".DS_Store"
     ]
     projectHome: "/Users/steveagalloco/Projects"
+    telemetryConsent: "limited"
     themes: [
       "unity-ui"
       "base16-syntax"


Fix the init.coffee workspaceView deprecation

--- atom.symlink/init.coffee
+++ atom.symlink/init.coffee
@@ -1,6 +1 @@
-path = require 'path'
-
-atom.workspaceView.eachEditorView (editorView) ->
-  editor = editorView.getEditor()
-  if path.extname(editor.getPath()) is '.md'
-    editor.setSoftWrap(true)
+# Add any auto-loaded Atom code on init here.


Refactor of most of the math library pulling components out

--- support/spec_helpers.coffee
+++ support/spec_helpers.coffee
@@ -18,8 +18,16 @@
   cont

-#beforeEach ->
-#  @addMatchers(
-#    toInclude: (value)->
-#      _(@).find((it)-> it == value)
-#  )
+beforeEach ->
+  @addMatchers(
+    toBeInstanceOf: (type)->
+      @message = ->
+        "Expected #{@actual} to be an instance of #{type.name}"
+      @actual instanceof type
+    toBeEqualTo: (other)->
+      @message = ->
+        "Expected #{@actual}.isEqual(#{type}) to be true"
+      @actual.isEqual(other)
+  )
+
+


Set correct group ids for staging

Set correct group ids for staging


Id update

Id update


inline edit -> cancel -> reorder bug

inline edit -> cancel -> reorder bug

--- app/assets/javascripts/effective_datatables/inline_crud.js.coffee
+++ app/assets/javascripts/effective_datatables/inline_crud.js.coffee
@@ -118,10 +118,10 @@
 afterEdit = ($action) ->
   $tr = $action.closest('tr')
   $table = $tr.closest('table')
-  $tr.data('inline-form-original-html', $tr.html())

   html = buildRow($tr.children('td').length, EffectiveForm.remote_form_payload)

+  $tr.data('inline-form-original-html', $tr.children().detach())
   $tr.html(html)
   $tr.addClass('effective-datatables-inline-row')


resource update

resource update

--- app/assets/javascripts/widgit/admin/modules/grid_editor.js.coffee
+++ app/assets/javascripts/widgit/admin/modules/grid_editor.js.coffee
@@ -92,13 +92,21 @@
     $column
       .removeClass 'col-sm-' + attributes.columns
       .addClass 'col-sm-' + columnStart
+
+    $column.resource('update', 'columns', columnStart)

     attributes.columns = columnStart
     $column.attr 'data-attributes', JSON.stringify(attributes)

     #Todo: retrieve this with AJAX
-    $column.after "

"
+    $newColumn = $('

')
+    $newColumn.addClass "col-sm-#{columnEnd}"
+    $newColumn.attr 'data-attributes', JSON.stringify({ columns: columnEnd })
+    $newColumn.html '
'
+    $column.after $newColumn
+    # $column.after "

"

+    $(document).trigger('page:update')
     @createCutOverlays()
     @createMergeOverlays()


Makes new file preview film strip links work

fixes CNVS-16240

Test Plan
  - Enable New Files
  - Preview a folder with multiple files
  - Open up the footer
  - Click on another item in the footer
  - The preview should change to that file.

Change-Id: I144b00a01fb8dfee625a66b49b71a71a03677c8e
Reviewed-on: https://gerrit.instructure.com/43273
Reviewed-by: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
Product-Review: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>

--- app/coffeescripts/react_files/components/FilePreviewFooter.coffee
+++ app/coffeescripts/react_files/components/FilePreviewFooter.coffee
@@ -21,7 +21,7 @@
       @props.otherItems.map (file) =>
         li {className: 'ef-file-preview-footer-list-item', key: file.id},
           figure {className: 'ef-file-preview-footer-item'},
-            ReactRouter.Link {to: @props.to, params: {splat: @props.splat}, query: @props.query(file.id)},
+            ReactRouter.Link {to: @props.to, params: {splat: @props.splat}, query: @props.query(id: file.id)},
             div {
               className: if file.displayName() is @props.displayedItem?.displayName() then 'ef-file-preview-footer-image ef-file-preview-footer-active' else 'ef-file-preview-footer-image'
               style: {'background-image': 'url(' + file.get('thumbnail_url') + ')'}


Make code more functional

Make code more functional

Improve code by making more functional and avoid duplication

--- js/upload_form.coffee
+++ js/upload_form.coffee
@@ -67,37 +67,35 @@
      $("#data-block-" + this.id.split('-')[2]).remove()
 )

-parse_field_ = (tag) ->
-  $(tag).attr(
-    'name'
-     $(tag).attr('name') + '[' + $(tag).val() + ']'
-  )
-  $(tag).val('number')
-
-parse_field = (tag) ->
-  map(
-    (x) -> parse_field_(tag + '-' + x)
-    [2...($('#data-files').children().size() + 2)]
-  )
+parse_field_ = (counter, field) ->
+  f = (tag) ->
+    $(tag).attr(
+      'name'
+      $(tag).attr('name') + '[' + $(tag).val() + ']'
+    )
+    $(tag).val('number')
+  f('#data-' + field + '-parse-' + counter)

 expr_field_ = (counter, field) ->
-  expr_tag = '#expr-' + field + '-' + counter
-  data_tag = '#data-' + field + '-parse-' + counter
-  value = $(data_tag).val()
-  $(expr_tag).val($(expr_tag).val() + value)
+  f = (expr_tag, data_tag) ->
+    $(expr_tag).val(
+      $(expr_tag).val() + $(data_tag).val()
+    )
+  f('#expr-' + field + '-' + counter, '#data-' + field + '-parse-' + counter)

-expr_field = (field) ->
-  map(
-    (x) -> expr_field_(x, field)
-    [2...($('#data-files').children().size() + 2)]
-  )
+field_ = (func) ->
+  (field) ->
+    map(
+      (x) -> func(x, field)
+      [2...($('#data-files').children().size() + 2)]
+    )

 $('#my_form').submit(
   () ->
-    expr_field('x')
-    expr_field('y')
-    expr_field('z')
-    parse_field('#data-x-parse')
-    parse_field('#data-y-parse')
-    parse_field('#data-z-parse')
+    map(
+      (x) ->
+        field_(expr_field_)(x)
+        field_(parse_field_)(x)
+      ['x', 'y', 'z']
+    )
 )


Fix spelling misstake

Fix spelling misstake

--- test/collection-spec.coffee
+++ test/collection-spec.coffee
@@ -56,7 +56,7 @@
             assert.deepEqual res1.body.documents[4], res2.body.documents[0]
             done()

-  it 'should get items after data', (done) ->
+  it 'should get items after specified date', (done) ->
     req.get(url + '&after=' + data[50].endret)
       .expect(200)
       .end (err, res) ->


control-t returns from symbol, not control-shift-t

--- keymaps/vim-mode.cson
+++ keymaps/vim-mode.cson
@@ -162,7 +162,7 @@
   '.': 'vim-mode:repeat'

   'ctrl-]': 'symbols-view:go-to-declaration'
-  'ctrl-T': 'symbols-view:return-from-declaration'
+  'ctrl-t': 'symbols-view:return-from-declaration'

   'ctrl-a': 'vim-mode:increase'
   'ctrl-x': 'vim-mode:decrease'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -15,6 +15,6 @@
     x: 50
     y: 30

-  equals engine.find('')
+  equals engine.find('.finable')

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -25,6 +25,6 @@
     x: 4
     y: 20

-  equals engine.first('type=testObj'), 1
+  equals engine.first('type=testObj'), obj1

 module()


`lastNonWhitespaceColumn` -> `lastWordEndColumn`

Because we want to trim the whitespace based on
the last word ending boundary, as opposed to the
last non whitespace character in the line.

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -215,8 +215,8 @@
       lastWhitespaceScreenColumn = -1
       lastWhitespaceBufferColumn = -1
       lastWhitespaceWidth = -1
-      lastNonWhitespaceScreenColumn = -1
-      lastNonWhitespaceBufferColumn = -1
+      lastWordEndScreenColumn = -1
+      lastWordEndBufferColumn = -1
       lastWrapBufferColumn = 0

       while bufferColumn <= bufferLineLength
@@ -275,8 +275,8 @@
           wrapScreenColumn = lastWhitespaceScreenColumn + 1
           wrapBufferColumn = lastWhitespaceBufferColumn + 1
           wrapWidth = lastWhitespaceWidth + 1
-          trimmedWhitespaceStartScreenColumn = lastNonWhitespaceScreenColumn + 1
-          trimmedWhitespaceStartBufferColumn = lastNonWhitespaceBufferColumn + 1
+          trimmedWhitespaceStartScreenColumn = lastWordEndScreenColumn + 1
+          trimmedWhitespaceStartBufferColumn = lastWordEndBufferColumn + 1

           if wrapBufferColumn <= lastWrapBufferColumn
             wrapScreenColumn = screenColumn
@@ -329,12 +329,14 @@
             screenLineWidth += indentLength

         if character is ' ' or character is '\t'
+          previousCharacter = bufferLine[bufferColumn - 1]
+          if previousCharacter isnt ' ' or previousCharacter isnt '\t'
+            lastWordEndScreenColumn = screenColumn - 1
+            lastWordEndBufferColumn = bufferColumn - 1
+
           lastWhitespaceScreenColumn = screenColumn
           lastWhitespaceBufferColumn = bufferColumn
           lastWhitespaceWidth = screenLineWidth
-        else
-          lastNonWhitespaceScreenColumn = screenColumn
-          lastNonWhitespaceBufferColumn = bufferColumn

         if foldEndBufferPosition?
           if screenColumn > tokensScreenExtent


Assign a screen extent to tokens, but don't add it to the line

This allows to have void tokens with a screen extent (e.g. for proper indent
guides computation) and still correctly calculate each line's length.

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -104,7 +104,7 @@

   bufferDidChange: (change) ->
     {oldRange, newRange} = @expandChangeRegionToSurroundingEmptyLines(change.oldRange, change.newRange)
-    {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToScreenLineBoundaries(oldRange)
+    {startScreenRow, endScreenRow, startBufferRow} = @expandBufferRangeToScreenLineBoundaries(oldRange)

     oldRowExtent = endScreenRow - startScreenRow
     newScreenLines = @buildScreenLines(startBufferRow, newRange.end.row + 1)
@@ -310,7 +310,7 @@

       if eolInvisibleReplacement = @eolInvisibles[@buffer.lineEndingForRow(bufferRow)]
         tokens.push({
-          screenExtent: 0,
+          screenExtent: eolInvisibleReplacement.length,
           bufferExtent: Point(0, 0),
           metadata: {
             eol: eolInvisibleReplacement,
@@ -319,17 +319,19 @@
             void: true
           }
         })
+        screenColumn += eolInvisibleReplacement.length

       while @showIndentGuides and indentGuidesCount > 0 and not previousPositionWasFold
-        distanceToNextTabStop = @tabLength - (tokensScreenExtent % @tabLength)
+        distanceToNextTabStop = @tabLength - (screenColumn % @tabLength)
         tokens.push({
-          screenExtent: 0,
+          screenExtent: distanceToNextTabStop,
           bufferExtent: Point(0, 0),
           metadata: {
-            showIndentGuide: (tokensScreenExtent % @tabLength is 0),
+            showIndentGuide: (screenColumn % @tabLength is 0),
             void: true
           }
         })
+        screenColumn += distanceToNextTabStop
         indentGuidesCount--

       if tokens.length is 0
@@ -636,7 +638,11 @@

     metadata = @spatialTokenIterator.getMetadata()
     if metadata?.void
-      throw new Error('TODO: Not implemented. Support void tokens followed by valid screen positions, such as soft wrap indents.')
+      if options?.clipDirection is 'forward'
+        throw new Error('TODO: Not implemented. Support void tokens followed by valid screen positions, such as soft wrap indents.')
+      else
+        # TODO: here I believe we should actually move to predecessor, until a non-void token is found.
+        screenPosition = Point(@spatialTokenIterator.getScreenStart().row, 0)
     else if comparePoints(screenPosition, @spatialTokenIterator.getScreenEnd()) <= 0
       if (metadata?.atomic and
           comparePoints(screenPosition, @spatialTokenIterator.getScreenStart()) > 0 and


Returning typed expressions instead of types

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -83,6 +83,14 @@
   else
     cb ast, ast.token
   return
+
+crouch = (ast, cb) ->
+  if Array.isArray ast
+    for node in ast
+      crouch node, cb
+  else
+    cb ast
+  ast

 inside = (node) -> node[1...-1]

@@ -1212,10 +1220,19 @@

 # Type inference and checker

+assignTypes = (context, expression) ->
+  [s, _, expression] = infer context, expression, 0
+  crouch expression, (node) ->
+    node.tea = subExp s, node.tea if node.tea
+
 infer = (context, expression, nameIndex) ->
   switch expression.label
-    when 'numerical' then [newMap(), nameIndex, 'Num']
-    when 'string' then [newMap(), nameIndex, 'Text']
+    when 'numerical'
+      expression.tea = 'Num'
+      [newMap(), nameIndex, expression]
+    when 'string'
+      expression.tea = 'Text'
+      [newMap(), nameIndex, expression]
     else
       # Reference
       if isReference expression
@@ -1223,7 +1240,8 @@
         concreteType = lookupInMap context, expression.token
         unless concreteType
           throw new Error "Unbound variable #{expression.token}"
-        [newMap(), nameIndex, concreteType]
+        expression.tea = concreteType
+        [newMap(), nameIndex, expression]
       # Lambda
       else if expression.type is 'function'
         {params, body, wheres} = fnDefinition expression
@@ -1233,20 +1251,20 @@
         argName = freshName nameIndex
         param = definedParams[0]
         context = concat context, newMapWith param.token, argName
-        [s1, nextIndex, A] = infer context, body, nameIndex + 1
-        [s1, nextIndex, (subExp s1, [argName, A])]
+        [s1, nextIndex, body] = infer context, body, nameIndex + 1
+        expression.tea = (subExp s1, [argName, body.tea])
+        [s1, nextIndex, expression]

       # Call
       else if Array.isArray expression
         [op, arg] = inside expression
         # assume call.length is 2
         returnName = freshName nameIndex
-        [s1, nextIndex, A] = infer context, op, nameIndex + 1
-        [s2, nextIndex, B] = infer (subContext s1, context), arg, nextIndex
-        log "op", s2, A, (subExp s2, A)
-        log "operand", [B, returnName]
-        s3 = unify (subExp s2, A), [B, returnName]
-        [(concat s3, s2, s1), nextIndex, (subExp s3, returnName)]
+        [s1, nextIndex, op] = infer context, op, nameIndex + 1
+        [s2, nextIndex, arg] = infer (subContext s1, context), arg, nextIndex
+        s3 = unify (subExp s2, op.tea), [arg.tea, returnName]
+        expression.tea = (subExp s3, returnName)
+        [(concat s3, s2, s1), nextIndex, expression]

 unify = (t1, t2) ->
   unifyWith newMap(), [[t1, t2]]


test: change test configuration - remove auth test

Caused `IVY_WARNING Cannot retrieve task from IronMQ Error: Invalid project/token combination` and break builds.

--- test/test-queue-pass.coffee
+++ test/test-queue-pass.coffee
@@ -69,20 +69,6 @@

 describe 'Queue configuration', ->
   describe 'IronMQ', ->
-    describe 'When I listen to IronMQ queue', ->
-      before (done) ->
-        ivy.listen
-          type: 'ironmq'
-          auth:
-            token: 'dummy'
-            projectId: 'dummyId'
-        , done
-
-      it 'Queue backend should be IronMQ', ->
-        assert.ok queue.queue instanceof IronMQQueue
-
-
-
     describe 'When I try to listen to IronMQ queue without giving authentication', ->
       error = undefined


more tests for Repository.

more tests for Repository.

--- test/test.repository.coffee
+++ test/test.repository.coffee
@@ -70,4 +70,24 @@
 			it "fails for objects that aren't a commit", (done) ->
 				repo.commit fixtures.projectRepo.secondCommit.wscriptBlob, (err, obj) ->
 					should.exist err
-					done()+					done()
+		describe "#tree()", ->
+			it "works", (done) ->
+				repo.tree fixtures.projectRepo.secondCommit.tree, (err, obj) ->
+					should.not.exist err
+					obj.should.be.an.instanceof gitteh.Tree
+					done()
+			it "fails for objects that aren't a tree", (done) ->
+				repo.tree fixtures.projectRepo.secondCommit.id, (err, obj) ->
+					should.exist err
+					done()
+		describe "#blob()", ->
+			it "works", (done) ->
+				repo.blob fixtures.projectRepo.secondCommit.wscriptBlob, (err, obj) ->
+					should.not.exist err
+					obj.should.be.an.instanceof gitteh.Blob
+					done()
+			it "fails for objects that aren't a blob", (done) ->
+				repo.blob fixtures.projectRepo.secondCommit.id, (err, obj) ->
+					should.exist err
+					done()


@epidemian typo

@epidemian typo

--- test/repl.coffee
+++ test/repl.coffee
@@ -87,7 +87,7 @@
   input.emitLine 'a'
   eq '2', output.lastWrite()

-testRepl "existencial assignment of previously declared variable", (input, output) ->
+testRepl "existential assignment of previously declared variable", (input, output) ->
   input.emitLine 'a = null'
   input.emitLine 'a ?= 42'
-  eq '42', output.lastWrite()+  eq '42', output.lastWrite()


test(macModal): Make sure afterHide test is working properly

--- test/unit/modal.spec.coffee
+++ test/unit/modal.spec.coffee
@@ -189,6 +189,7 @@
       expect($rootScope.afterHide).not.toHaveBeenCalled()

       modal.hide()
+      $timeout.flush()

       expect($rootScope.afterHide).toHaveBeenCalled()


Use Sinon stubs for promise cancellation tests

--- test/test.coffee
+++ test/test.coffee
@@ -73,15 +73,12 @@
         expect(error.message.code).to.equal("ECONNREFUSED")

   describe 'cancellable promises', ->
-    oldAbort = undefined
-    abortSpy = undefined

     beforeEach ->
-      oldAbort = request.Request.prototype.abort
-      request.Request.prototype.abort = abortSpy = sinon.spy()
+      sinon.stub request.Request.prototype, 'abort'

     afterEach ->
-      request.Request.prototype.abort = oldAbort
+      request.Request.prototype.abort.restore()

     describe 'cancel without reason', ->

@@ -92,7 +89,7 @@
           .cancel()

         setImmediate ->
-          expect(abortSpy.called).to.be.true
+          expect(request.Request.prototype.abort.called).to.be.true
           done()

       it 'should throw a bluebird CancellationError when the
@@ -121,7 +118,7 @@
           .cancel new CustomCancellationError

         setImmediate ->
-          expect(abortSpy.called).to.be.true
+          expect(request.Request.prototype.abort.called).to.be.true
           done()

       it 'should throw a custom error when the promise is


test: remove block comment

test: remove block comment

--- test/test.coffee
+++ test/test.coffee
@@ -178,22 +178,20 @@
       assert.equal warning, null, "should not have gotten a  warning"
       done()

-###
+# describe 'worker timeout', ->
+#   it 'timeout happens before job complete', (done)->
+#     @timeout 10000
+#     worker = new Worker 'test_1s_timeout', (payload, worker) ->
+#       setTimeout () ->
+#         worker.complete()
+#       , 3000
+#     , _.extend options, timeout: 1000

-describe 'worker timeout', ->
-  it 'timeout happens before job complete', (done)->
-    @timeout 10000
-    worker = new Worker 'test_1s_timeout', (payload, worker) ->
-      setTimeout () ->
-        worker.complete()
-      , 3000
-    , _.extend options, timeout: 1000
-
-    client = new Client options
-    job = client.submitJob 'test_1s_timeout'
-    job.on 'complete', (handle, data) ->
-      assert false, 'job should timeout'
-    job.on 'fail', (handle) ->
-      worker.disconnect()
-      client.disconnect()
-      done()
+#     client = new Client options
+#     job = client.submitJob 'test_1s_timeout'
+#     job.on 'complete', (handle, data) ->
+#       assert false, 'job should timeout'
+#     job.on 'fail', (handle) ->
+#       worker.disconnect()
+#       client.disconnect()
+#       done()


Correcting small indent bug in a commentary, preventing test.coffee from compiling

--- test/test.coffee
+++ test/test.coffee
@@ -95,7 +95,7 @@
       @blockView.on 'change:rotate', @spyHandleRotation
       triggerDragOn(@blockView, '.handleRotation', {pageX: 200, pageY: 150})

-### WORK IN PROGESS
+    ### WORK IN PROGESS
     it 'rotates itself correctly', ->
       elPos = @blockView.$el.position()
       matrix = @blockView.contains.$el.css('transform')
@@ -103,10 +103,9 @@
       #console.log(sign)
       #sign = matrix[1] / Math.abs(matrix[1]) || 1
       #console.log(sign * Math.acos(matrix[0]))
-
+    ###
     it 'emits a rotate event', ->
       expect(@spyHandleRotation.called).to.be.true
-###

   describe 'when dragging the tracker (zone inside the handles)', ->
     beforeEach ->


Another utf8.range

Another utf8.range

--- test/utf8.coffee
+++ test/utf8.coffee
@@ -3,15 +3,17 @@
 utf8 = require './8'
 random = require './random'

-test8 = (z)->
-  assert new Buffer(a = utf8 z).toString() == String.fromCharCode z
-  assert utf8.valid a
-  assert z == utf8.code a
-
 describe 'UTF-8', ->
   it 'is generated correctly', ->
-    for i in [1..108]
-      test8 random 0, 0xFF
-      test8 random 0x100, 0x7FF
-      test8 random 0x800, 0xD7FF
-      test8 random 0xE000, 0xFFFF
+    utf8.ranges
+      min: 0
+      [1..2]
+      min: 0xD800
+      false
+      max: 0xDFFF
+      3
+    .forEach (range)->
+      for i in [1..108]
+        assert utf8.valid q = utf8 z = random.range range
+        assert z == utf8.code q
+        assert new Buffer(q).toString() == String.fromCharCode z


Delete game.coffee

Delete game.coffee

--- assets/js/game.coffee
+++ assets/js/game.coffee
@@ -1,36 +0,0 @@
-ko.bindingHandlers.position =
-    init: (element, value) ->
-        $(element).css 'left', value().left()
-        $(element).css 'top', value().top()
-    update: (element, value) ->
-        $(element).css 'left', value().left()
-        $(element).css 'top', value().top()
-
-lastId = 0
-
-isPrime = (n) ->
-    for x in [2..n/2]
-        if n/x % 1 == 0
-            return false
-    true
-
-class Space
-    width = 9
-    height = 9
-    constructor: (@x, @y) ->
-        @left = width * @x
-        @top = height * @y
-        @id = ++lastId
-        @isOne = isPrime(@id)
-
-class Game
-    constructor: (@name) ->
-        @space = []
-        for y in [1..50]
-            for x in [1..50]
-                @space.push new Space x, y
-
-game = new Game 'hello'
-
-viewModel = ko.mapping.fromJS game
-ko.applyBindings viewModel


fixed disposable error

fixed disposable error

--- lib/controller.coffee
+++ lib/controller.coffee
@@ -83,7 +83,9 @@
       @statusView.onClick(@selectActiveProjectDialog.bind(this),
         @selectActiveTargetDialog.bind(this))
       @updateStatusBar()
-      @subscriptions.add statusBar.addRightTile(item: @statusView, priority:300)
+      # error in Atom 1.12, statusBar no longer has dispose...I suppose we don't need to clean it
+      # @subscriptions.add statusBar.addRightTile(item: @statusView, priority:300)
+      statusBar.addRightTile(item: @statusView, priority:300)

     updateStatusBar: ->
       @statusView?.update(@activeProject.split('/').slice(-1), @getTarget("#target_active")?.name)
@@ -121,7 +123,7 @@
                   try
                       client.end()
                       port = JSON.parse(data)["port"]
-                      remote_arg = "-ex=target remote :#{port}"
+                      remote_arg = "-ex=target remote #{port}"
                       console.log remote_arg
                       args[0] = remote_arg
                   finally