fix ShowFolder tests in newfiles

fix ShowFolder tests in newfiles

we were trying to always call Router.Link.restore()
and getting this error:

Teardown failed on when collection is loaded and empty display no matches found:
[object Function] has no method 'restore'

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

--- spec/coffeescripts/react_files/components/ShowFolderSpec.coffee
+++ spec/coffeescripts/react_files/components/ShowFolderSpec.coffee
@@ -7,26 +7,25 @@
 ], (React, Router, ShowFolder, Folder, FolderChild) ->

   showFolderTest = (properties, test) ->
-    # Setup
-    sinon.stub(Router, 'Link').returns('some link')
-    sinon.stub(Folder, 'resolvePath').returns($.Deferred())
+    stubbedLink = sinon.stub(Router, 'Link').returns('some link')
+    stubbedReslovePath = sinon.stub(Folder, 'resolvePath').returns($.Deferred())
     @showFolder = React.renderComponent(ShowFolder(properties), $('#fixtures')[0])

     test()

+    stubbedLink.restore()
+    stubbedReslovePath.restore()
+    React.unmountComponentAtNode($('#fixtures')[0])
+
+
   module 'ShowFolder Rendering',
-    setup: ->
-    teardown: ->
-      Folder.resolvePath.restore()
-      Router.Link.restore()
-      React.unmountComponentAtNode($('#fixtures')[0])

   test 'returns empty div if there is no currentFolder', ->
-    folderObject =
+    props =
       params: {}
       onResolvePath: ->

-    showFolderTest folderObject, ->
+    showFolderTest props, ->
       ok @showFolder.refs.emptyDiv, "empty div displayed"

   test 'displays empty text if the folder is empty', ->
@@ -34,13 +33,13 @@
     folder.files.loadedAll = true
     folder.folders.loadedAll = true

-    folderObject =
+    props =
       params: {}
       onResolvePath: ->
       currentFolder: folder
       query: ''

-    showFolderTest folderObject, ->
+    showFolderTest props, ->
       equal @showFolder.refs.folderEmpty.getDOMNode().textContent, 'This folder is empty', 'displays the empty message'

   test 'when folder are present, FolderChild generates a line item', ->
@@ -48,7 +47,7 @@
     # I can't figure out how to do that. This is the next best thing
     folder = new Folder()
     folder.children = -> [new Folder(cid: '1')]
-    folderObject =
+    props =
       params: {}
       onResolvePath: ->
       currentFolder: folder
@@ -56,21 +55,16 @@
       toggleItemSelected: ->
       selectedItems: []

-    showFolderTest folderObject, ->
+    showFolderTest props, ->
       ok $('.ef-item-row').length, 'generates an item row'

   module 'ShowFolder#registerListeners',
-    setup: ->
-    teardown: ->
-      Folder.resolvePath.restore()
-      Router.Link.restore()
-      React.unmountComponentAtNode($('#fixtures')[0])

   test 'does nothing if there is no currentFolder', ->
     mockProps = sinon.mock()
     folder = new Folder()
     folder.children = -> [new Folder(cid: '1')]
-    folderObject =
+    props =
       params: {}
       onResolvePath: ->
       currentFolder: folder
@@ -78,17 +72,17 @@
       toggleItemSelected: ->
       selectedItems: []

-    showFolderTest folderObject, ->
+    showFolderTest props, ->
       @showFolder.registerListeners(mockProps)
       ok mockProps.never(), "doesn't call methods in the mock"

   test 'applies change handlers to folder when currentFolder exists', ->
     folder = new Folder()
+    folder.children = -> [new Folder(cid: '1')]
     sinon.spy(folder, 'on')
     mockProps = {currentFolder: {folders: folder, files: folder}}

-    folder.children = -> [new Folder(cid: '1')]
-    folderObject =
+    props =
       params: {}
       onResolvePath: ->
       currentFolder: folder
@@ -96,7 +90,7 @@
       toggleItemSelected: ->
       selectedItems: []

-    showFolderTest folderObject, ->
+    showFolderTest props, ->
       @showFolder.registerListeners(mockProps)
       ok folder.on.calledTwice, 'Calls "on" twice'
     folder.on.restore()


Scope name argument properly

Scope name argument properly

--- metrics.coffee
+++ metrics.coffee
@@ -6,8 +6,8 @@
 buildKey = (key)-> "#{name}.#{process.env.NODE_ENV or "development"}.#{key}"

 module.exports =
-	initialize: (name) ->
-		name = name
+	initialize: (_name) ->
+		name = _name

 	set : (key, value, sampleRate = 1)->
 		statsd.set buildKey(key), value, sampleRate


Revert "JS: Remove leftover pagination event handling"

This reverts commit 569a70e400b5b3cdedecaad14a6dbea41beb458e.

--- evesrp/static/js/requestList.coffee
+++ evesrp/static/js/requestList.coffee
@@ -141,6 +141,20 @@
     false

+changePage = (ev) ->
+    $target = jQuery ev.target
+    # Update the page number
+    filters = filter.getFilters()
+    if ($target.attr 'id') == 'prev_page'
+        filters.page -= 1
+    else if ($target.attr 'id') == 'next_page'
+        filters.page += 1
+    else
+        filters.page = parseInt $target.contents()[0].data, 10
+    filter.updateURL()
+    false
+
+
 addQuickFilter = (ev) ->
     $cell = (jQuery ev.target).closest 'td'
     attribute = $cell.data 'attribute'
@@ -158,6 +172,7 @@

 setupEvents = () ->
     (jQuery 'th a.heading').on 'click', changeSort
+    (jQuery 'ul.pagination').on 'click', changePage
     (jQuery '.filterable a').on 'click', addQuickFilter
     $window = jQuery window
     $window.on 'popstate', getRequests


fix binary logger + get name field when binary upload occurs

--- server/controllers/binaries.coffee
+++ server/controllers/binaries.coffee
@@ -2,7 +2,7 @@
 multiparty = require 'multiparty'
 log =  require('printit')
     date: true
-    prefix: 'attachment'
+    prefix: 'binaries'

 db = require('../helpers/db_connect_helper').db_connect()
 deleteFiles = require('../helpers/utils').deleteFiles
@@ -26,14 +26,17 @@
     # Dirty hack to end request if no file were sent when form is fully parsed.
     nofile = true

+    fields = {}
+
     # We read part one by one to avoid writing the full file to the disk
     # and send it directly as a stream.
     form.on 'part', (part) ->
-        log.debug part.name + ' ' + part.filename

         # It's a field we do nothing
         unless part.filename?
-            part.resume()
+            fields[part.name] = ''
+            part.on 'data', (buffer) ->
+                fields[part.name] = buffer.toString()

         # It's a file, we pipe it directly to Couch to avoid too much memory
         # consumption.
@@ -41,7 +44,10 @@
         # the file to the disk and we don't want that.
         else
             nofile = false
-            name = part.filename
+            if fields.name?
+                name = fields.name
+            else
+                name = part.filname

             # Build file data
             fileData =


config: fallback to docker-machine on boot2docker failures

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -252,7 +252,7 @@

       if [[ `uname` == 'Darwin' ]]; then
         if [ -z "$DOCKER_HOST" ]; then
-          echo "You need to export DOCKER_HOST, run 'boot2docker up' and follow the instructions."
+          echo "You need to export DOCKER_HOST, run 'boot2docker up' and follow the instructions. (or run 'eval $(docker-machine env default)')"
           exit 1
         fi
       fi


FIX: ensure that is graded is added to the task on creation

--- src/app/units/unit-tasks-editor/unit-tasks-editor.coffee
+++ src/app/units/unit-tasks-editor/unit-tasks-editor.coffee
@@ -60,6 +60,7 @@
         target_grade: 0
         restrict_status_updates: false
         plagiarism_warn_pct: 80
+        is_graded: false
       }
       $scope.taskAdminData.selectedTask = task
       $scope.taskAdminData.isNew = true


Fixed event issue

Fixed event issue

--- src/scripts/modules/media/footer/downloads/downloads.coffee
+++ src/scripts/modules/media/footer/downloads/downloads.coffee
@@ -7,16 +7,19 @@
   return class DownloadsView extends FooterTabView
     template: template

-    events:
+    parentEvents = FooterTabView.prototype.events
+    _.extend({}, parentEvents,
       'click [data-format="PDF"]': (e) ->
         analytics.sendDownloadAnalytics(@model.get('googleAnalytics'),@model.attributes.title, 'PDF', \
         @model.attributes.downloads[0].path)
+        return true
       'click [data-format="EPUB"]': (e) ->
         analytics.sendDownloadAnalytics(@model.get('googleAnalytics'),@model.attributes.title, 'EPUB', \
         @model.attributes.downloads[1].path)
       'click [data-format="Offline ZIP"]': (e) ->
         analytics.sendDownloadAnalytics(@model.get('googleAnalytics'),@model.attributes.title, 'Offline Zip', \
         @model.attributes.downloads[2].path)
+    )

     initialize: () ->
       super()


return early if out of scope in viewables.js.coffee


Update tests for left-right borders

Update tests for left-right borders

We do not crop left-right borders anymore, this heuristics
tries to crop video covers only and avoids cropping background
regions.

--- spec/GetBorderlessBox.coffee
+++ spec/GetBorderlessBox.coffee
@@ -112,34 +112,6 @@
       out.once 'data', (res) ->
         chai.expect(groups).to.be.eql ['rectangle-ranges']
         expected =
-          x: 64
-          y: 0
-          width: 1152
-          height: 720
-        # filepath = 'borderless2.jpg'
-        # testutils.getCanvasWithImageNoShift filepath, (c) ->
-        #   testutils.cropAndSave "#{filepath}_borderless.png", c, res
-        #   done()
-        checkSimilar chai, res, expected, 3
-        done()
-
-      inSrc = 'borderless2.jpg'
-      testutils.getCanvasWithImageNoShift inSrc, (c) ->
-        canvas.beginGroup groupId
-        canvas.send c
-        canvas.endGroup()
-
-    it 'should remove borders #2', (done) ->
-      @timeout 10000
-      groupId = 'rectangle-ranges'
-      groups = []
-      out.once 'begingroup', (group) ->
-        groups.push group
-      out.once 'endgroup', (group) ->
-        groups.pop()
-      out.once 'data', (res) ->
-        chai.expect(groups).to.be.eql ['rectangle-ranges']
-        expected =
           x: 0
           y: 48
           width: 480
@@ -157,7 +129,7 @@
         canvas.send c
         canvas.endGroup()

-    it 'should remove borders #3', (done) ->
+    it 'should remove borders #2', (done) ->
       @timeout 10000
       groupId = 'rectangle-ranges'
       groups = []
@@ -185,20 +157,20 @@
         canvas.send c
         canvas.endGroup()

-    it 'should remove left and right white borders', (done) ->
-      @timeout 10000
-      groupId = 'rectangle-ranges'
-      groups = []
-      out.once 'begingroup', (group) ->
-        groups.push group
-      out.once 'endgroup', (group) ->
-        groups.pop()
-      out.once 'data', (res) ->
-        chai.expect(groups).to.be.eql ['rectangle-ranges']
-        expected =
-          x: 38
-          y: 0
-          width: 522
+    it 'should not remove left and right white borders', (done) ->
+      @timeout 10000
+      groupId = 'rectangle-ranges'
+      groups = []
+      out.once 'begingroup', (group) ->
+        groups.push group
+      out.once 'endgroup', (group) ->
+        groups.pop()
+      out.once 'data', (res) ->
+        chai.expect(groups).to.be.eql ['rectangle-ranges']
+        expected =
+          x: 0
+          y: 0
+          width: 600
           height: 338
         # filepath = 'borderless1.jpg'
         # testutils.getCanvasWithImageNoShift filepath, (c) ->
@@ -216,20 +188,20 @@
         canvas.send c
         canvas.endGroup()

-    it 'should remove left and right black borders', (done) ->
-      @timeout 10000
-      groupId = 'rectangle-ranges'
-      groups = []
-      out.once 'begingroup', (group) ->
-        groups.push group
-      out.once 'endgroup', (group) ->
-        groups.pop()
-      out.once 'data', (res) ->
-        chai.expect(groups).to.be.eql ['rectangle-ranges']
-        expected =
-          x: 64
-          y: 0
-          width: 1152
+    it 'should not remove left and right black borders', (done) ->
+      @timeout 10000
+      groupId = 'rectangle-ranges'
+      groups = []
+      out.once 'begingroup', (group) ->
+        groups.push group
+      out.once 'endgroup', (group) ->
+        groups.pop()
+      out.once 'data', (res) ->
+        chai.expect(groups).to.be.eql ['rectangle-ranges']
+        expected =
+          x: 0
+          y: 0
+          width: 1280
           height: 720
         checkSimilar chai, res, expected, 3
         done()


update to use the new frontendeditor

update to use the new frontendeditor

--- vendor/assets/javascripts/ckeditor/ckeditor_actions.coffee
+++ vendor/assets/javascripts/ckeditor/ckeditor_actions.coffee
@@ -15,9 +15,16 @@
 Editor.commitAll = ->
   $.each(CKEDITOR.instances, (key, editor) ->
     dataset = editor.container.$.dataset
-    model = FrontendEditor.getCurrentModel(dataset.object)
-    model.set('id', dataset.id)
-    model.set(dataset.attribute, editor.getData())
+    model = FrontendEditor.findCurrentOrCreateModel(dataset.object)
+
+    values = id: dataset.id
+    values[dataset.attribute] = editor.getData()
+
+    attributes =
+      values: values
+      prefix: dataset.prefix
+      objectName: dataset.object
+    model.setAttributes(attributes)
   )

 Editor.el = ->


Add spec for skip

Add spec for skip

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -1804,6 +1804,16 @@

           expect(willInsertSpy).toHaveBeenCalled()
           expect(didInsertSpy).not.toHaveBeenCalled()
+
+      describe "when the undo option is set to 'skip'", ->
+        beforeEach ->
+          editor.setSelectedBufferRange([[1, 2], [1, 2]])
+
+        it "replaces the selection with the given text", ->
+          range = editor.insertText('x')
+          range = editor.insertText('y', undo: 'skip')
+          editor.undo()
+          expect(buffer.lineForRow(1)).toBe '  yvar sort = function(items) {'

     describe ".insertNewline()", ->
       describe "when there is a single cursor", ->


Social: allow 0 recipient

Social: allow 0 recipient

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -258,9 +258,6 @@
   get url, data, callback

 createChannelWithParticipants = (data, callback) ->
-  if not data.recipients or data.recipients.length < 1
-    return callback new KodingError 'Request is not valid'
-
   url = "#{socialProxyUrl}/channel/initwithparticipants"
   post url, data, callback

@@ -272,9 +269,6 @@
   post url, data, callback

 initPrivateMessage = (data, callback) ->
-  if not data.recipients or data.recipients.length < 1
-    return callback new KodingError 'Request is not valid'
-
   url = "#{socialProxyUrl}/privatechannel/init"
   post url, data, callback


JStackTemplate: Keep template's sum along with the content [#93570126]

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -1,15 +1,15 @@
-
-jraphical       = require 'jraphical'
-JCredentialData = require './credentialdata'
-JName           = require '../name'
-JUser           = require '../user'
-JGroup          = require '../group'
-
-# TODO Credential relations ~g
+jraphical = require 'jraphical'
+crypto    = require 'crypto'
+

 module.exports = class JStackTemplate extends jraphical.Module

   KodingError        = require '../../error'
+
+  JName              = require '../name'
+  JUser              = require '../user'
+  JGroup             = require '../group'
+  JCredentialData    = require './credentialdata'

   {Inflector, secure, ObjectId, signature, daisy} = require 'bongo'
   {Relationship}     = jraphical
@@ -90,10 +90,25 @@

       group           : String

-      template        : String
+      template        :
+        content       : String
+        sum           : String

       # Public keys of JCredentials
       credentials     : [ String ]
+
+
+  generateTemplateObject = (content)->
+
+    content = ''  unless typeof content is 'string'
+
+    return {
+      content
+      sum: crypto.createHash 'sha1'
+        .update content
+        .digest 'hex'
+    }
+

   @create = permit 'create stack template',
@@ -117,7 +132,7 @@
         extras      : data.extras      ? []
         connections : data.connections ? []
         accessLevel : data.accessLevel ? 'private'
-        template    : data.template    ? ''
+        template    : generateTemplateObject data.template
         credentials : data.credentials ? []

       stackTemplate.save (err)->
@@ -188,6 +203,9 @@

       delete data.originId
       delete data.group
+
+      if data.template?
+        data.template = generateTemplateObject data.template

       @update $set: data, (err)-> callback err


SocialWorker: remove daisy usage

SocialWorker: remove daisy usage

--- workers/social/testhelper/models/computeproviders/machinehelper.coffee
+++ workers/social/testhelper/models/computeproviders/machinehelper.coffee
@@ -1,5 +1,5 @@
 { _
-  daisy
+  async
   expect } = require '../../../testhelper/index'

 JUser      = require '../../../lib/social/models/user/index'
@@ -20,13 +20,13 @@

 generateMachineParamsByAccount = (account, callback) ->

-  _client =
+  client =
     connection :
       delegate : account
     context    :
       group    : 'koding'

-  generateMachineParams _client, (err, data) ->
+  generateMachineParams client, (err, data) ->
     return callback err  if err
     callback null, data

@@ -40,29 +40,24 @@

   queue = [

-    ->
+    (next) ->
       JUser.createUser userInfo, (err, user_, account_) ->
-        return callback err  if err
         [user, account] = [user_, account_]
-        queue.next()
+        next err

-    ->
+    (next) ->
       generateMachineParamsByAccount account, (err, data) ->
-        return callback err  if err
         machineParams = data
-        queue.next()
+        next err

-    ->
+    (next) ->
       JMachine.create machineParams, (err, machine_) ->
-        return callback err  if err
         machine = machine_
-        queue.next()
-
-    -> callback null, { machine, user, account }
+        next err

   ]

-  daisy queue
+  async.series queue, (err) -> callback err, { machine, user, account }

 fetchMachinesByUsername = (username, callback) ->
@@ -74,27 +69,27 @@
     callback machines

-
 createMachine = (client, opts, callback) ->

   [opts, callback] = [callback, opts]  unless callback
   machineParams    = {}
+  machine          = null

   queue = [

-    ->
+    (next) ->
       generateMachineParams client, opts, (err, data) ->
-        return callback err  if err
         machineParams = data
-        queue.next()
+        next err

-    ->
-      JMachine.create machineParams, (err, machine) ->
-        callback err, { machine }
+    (next) ->
+      JMachine.create machineParams, (err, machine_) ->
+        machine = machine_
+        next err

   ]

-  daisy queue
+  async.series queue, (err) -> callback err, { machine }


[♻] Cleaned up “console”.

[♻] Cleaned up “console”.

--- vendor/assets/javascripts/polyfills/console/console-0.1.js.coffee
+++ vendor/assets/javascripts/polyfills/console/console-0.1.js.coffee
@@ -1,41 +1,40 @@
-(-> # Avoid `console` errors in browsers that lack a console
-  method = undefined
+# Avoid `console` errors in browsers that lack a console
+method = undefined

-  noop = ->
-    return
+noop = ->
+  return

-  methods = [
-    'assert'
-    'clear'
-    'count'
-    'debug'
-    'dir'
-    'dirxml'
-    'error'
-    'exception'
-    'group'
-    'groupCollapsed'
-    'groupEnd'
-    'info'
-    'log'
-    'markTimeline'
-    'profile'
-    'profileEnd'
-    'table'
-    'time'
-    'timeEnd'
-    'timeStamp'
-    'trace'
-    'warn'
-  ]
+methods = [
+  'assert'
+  'clear'
+  'count'
+  'debug'
+  'dir'
+  'dirxml'
+  'error'
+  'exception'
+  'group'
+  'groupCollapsed'
+  'groupEnd'
+  'info'
+  'log'
+  'markTimeline'
+  'profile'
+  'profileEnd'
+  'table'
+  'time'
+  'timeEnd'
+  'timeStamp'
+  'trace'
+  'warn'
+]

-  length = methods.length
+length = methods.length

-  console = (window.console = window.console or {})
+console = (window.console = window.console or {})

-  while length--
-    method = methods[length]
+while length--
+  method = methods[length]

-    # Only stub undefined methods
-    console[method] = noop  unless console[method]
-)()+  # Only stub undefined methods
+  console[method] = noop  unless console[method]


Implement @strict modifier

Implement @strict modifier

--- library/schema/domain.coffee
+++ library/schema/domain.coffee
@@ -116,6 +116,20 @@
     @required = (required or []).concat key
     return pointer.apply this, arguments

+# Modify the supplied JSON object reference to mark this object as
+# strict. This means that if an object contains any other properties
+# except for the explicitly declared ones, it will be invalid marked
+# invalid, as specified according to JSON schema draft v4 standard.
+# This applicable to a single object as well as an array of objects.
+module.exports.strict = strict = (reference) ->
+    invalidSubject = "subject is none of an object"
+    subject = reference if reference.type is "object"
+    subject = reference.items if reference.type is "array"
+    assert reference.type is "object", invalidSubject
+    assert _.isObject reference.properties
+    assert _.isObject reference.patternProperties
+    reference.additionalProperties = no; reference
+
 # Create a reference to the JSON enumeration. An enumeration defines
 # what values are valid for this data type. Everything else beyond
 # those values are considered invalid. This version may take array


Add qunit tests for adhoc checkboxes

Add qunit tests for adhoc checkboxes

--- test/javascripts/integration/super_ad_hoc_test.js.coffee
+++ test/javascripts/integration/super_ad_hoc_test.js.coffee
@@ -55,3 +55,23 @@
     click '.task-body .inline-edit-form .button--green:contains("Save")'
     ok Em.$.trim(find('p.inline-edit').text()).indexOf('yahoo') isnt -1
   click '.overlay-close-button:first'
+
+test "Adding a checkbox item to an AdHoc Task", ->
+  visit "/papers/#{ETahi.Test.currentPaper.id}/manage"
+  .then -> ok exists find '.card-content:contains("Super Ad-Hoc")'
+
+  click '.card-content:contains("Super Ad-Hoc")'
+  click '.adhoc-content-toolbar .glyphicon-plus'
+  click '.adhoc-content-toolbar .adhoc-toolbar-item--list'
+  .then ->
+    ok exists find '.inline-edit-form input[type=checkbox]:disabled'
+    ok exists find '.inline-edit-form label[contenteditable]'
+    ok exists find '.button--disabled:contains("Save")'
+  andThen ->
+    Em.$('.inline-edit-form label[contenteditable]')
+    .html("Here is a checkbox list item")
+    .trigger('keyup')
+    click '.task-body .inline-edit-form .button--green:contains("Save")'
+    ok Em.$.trim(find('p.inline-edit').text()).indexOf('checkbox list item') isnt -1
+    ok exists find '.inline-edit-form input[type=checkbox]'
+  click '.overlay-close-button:first'


remove some commented out ugly code

remove some commented out ugly code

--- src/smc-webapp/render.coffee
+++ src/smc-webapp/render.coffee
@@ -13,10 +13,4 @@
     fs = require('fs')
     html = ReactDOMServer.renderToString(billing.render_static_pricing_page())
     filename = '../webapp-lib/policies/_static_pricing_page.html'
-    #s = fs.readFileSync(filename+'.template').toString()
-    #i = s.indexOf('start:SubscriptionGrid')
-    #i = s.indexOf('\n', i) + 1
-    #j = s.indexOf('end:SubscriptionGrid')
-    #j = s.lastIndexOf('\n', j) - 1
-    #s = s.slice(0,i) + html + s.slice(j+1)
     fs.writeFileSync(filename, html)


sagews: better readonly support (not perfect) -- https://github.com/sagemathinc/smc/issues/534

--- src/smc-webapp/sagews.coffee
+++ src/smc-webapp/sagews.coffee
@@ -42,7 +42,7 @@

 class SynchronizedWorksheet extends SynchronizedDocument2
     constructor: (@editor, @opts) ->
-        window.w = @
+        #window.w = @

         # these two lines are assumed, at least by the history browser
         @codemirror  = @editor.codemirror
@@ -61,59 +61,61 @@
             cursor_interval : @opts.cursor_interval
             sync_interval   : @opts.sync_interval
         super @editor, opts0, () =>
-            @process_sage_updates(caller:"constructor")
+            @readonly = @_syncstring.get_read_only()  # TODO: harder problem -- if file state flips between read only and not, need to rerender everything...
+            @process_sage_updates(caller:"constructor")   # MUST be after @readonly is set.
+
             @status cb: (err, status) =>
                 if not status?.running
                     @execute_auto_cells()

-        @init_worksheet_buttons()
-        @init_html_editor_buttons()
-
-        @execution_queue = new ExecutionQueue(@_execute_cell_server_side, @)
-
-        @on 'sync', () =>
-            #console.log("sync")
-            @process_sage_update_queue()
-
-        @editor.on 'show', (height) =>
-            @process_sage_updates(caller:"show")
-            @set_all_output_line_classes()
-
-        v = [@codemirror, @codemirror1]
-        for cm in v
-            cm.on 'beforeChange', (instance, changeObj) =>
-                #console.log("beforeChange (#{instance.name}): #{misc.to_json(changeObj)}")
-                # Set the evaluated flag to false for the cell that contains the text
-                # that just changed (if applicable)
-                if changeObj.origin == 'redo'
-                    return
-                if changeObj.origin == 'undo'
-                    return
-                if changeObj.origin? and changeObj.origin != 'setValue'
-                    @remove_this_session_flags_from_changeObj_range(changeObj)
-
-                if changeObj.origin == 'paste'
-                    changeObj.cancel()
-                    # WARNING: The Codemirror manual says "Note: you may not do anything
-                    # from a "beforeChange" handler that would cause changes to the
-                    # document or its visualization."  I think this is OK below though
-                    # since we just canceled the change.
-                    @remove_cell_flags_from_changeObj(changeObj, ACTION_SESSION_FLAGS)
-                    @_apply_changeObj(changeObj)
-                    @process_sage_updates(caller:"paste")
-                    @sync()
-
-            cm.on 'change', (instance, changeObj) =>
-                #console.log('changeObj=', changeObj)
-                if changeObj.origin == 'undo' or changeObj.origin == 'redo'
-                    return
-                start = changeObj.from.line
-                stop  = changeObj.to.line + changeObj.text.length + 1 # changeObj.text is an array of lines
-                if not @_update_queue_start? or start < @_update_queue_start
-                    @_update_queue_start = start
-                if not @_update_queue_stop? or stop > @_update_queue_stop
-                    @_update_queue_stop = stop
+            @execution_queue = new ExecutionQueue(@_execute_cell_server_side, @)
+
+            @on 'sync', () =>
+                #console.log("sync")
                 @process_sage_update_queue()
+
+            @editor.on 'show', (height) =>
+                @process_sage_updates(caller:"show")
+                @set_all_output_line_classes()
+
+            @init_worksheet_buttons()
+
+            v = [@codemirror, @codemirror1]
+            for cm in v
+                cm.on 'beforeChange', (instance, changeObj) =>
+                    #console.log("beforeChange (#{instance.name}): #{misc.to_json(changeObj)}")
+                    # Set the evaluated flag to false for the cell that contains the text
+                    # that just changed (if applicable)
+                    if changeObj.origin == 'redo'
+                        return
+                    if changeObj.origin == 'undo'
+                        return
+                    if changeObj.origin? and changeObj.origin != 'setValue'
+                        @remove_this_session_flags_from_changeObj_range(changeObj)
+
+                    if changeObj.origin == 'paste'
+                        changeObj.cancel()
+                        # WARNING: The Codemirror manual says "Note: you may not do anything
+                        # from a "beforeChange" handler that would cause changes to the
+                        # document or its visualization."  I think this is OK below though
+                        # since we just canceled the change.
+                        @remove_cell_flags_from_changeObj(changeObj, ACTION_SESSION_FLAGS)
+                        @_apply_changeObj(changeObj)
+                        @process_sage_updates(caller:"paste")
+                        @sync()
+
+                cm.on 'change', (instance, changeObj) =>
+                    #console.log('changeObj=', changeObj)
+                    if changeObj.origin == 'undo' or changeObj.origin == 'redo'
+                        return
+                    start = changeObj.from.line
+                    stop  = changeObj.to.line + changeObj.text.length + 1 # changeObj.text is an array of lines
+                    if not @_update_queue_start? or start < @_update_queue_start
+                        @_update_queue_start = start
+                    if not @_update_queue_stop? or stop > @_update_queue_stop
+                        @_update_queue_stop = stop
+                    @process_sage_update_queue()
+

     close: =>
         @execution_queue.close()
@@ -689,6 +691,7 @@
             cm            : undefined    # only markup changes, etc., using the given editor (uses all visible ones by default)
             pad_bottom    : 10           # ensure there are this many blank lines at bottom of document
             caller        : undefined
+        #console.log("process_sage_updates", @readonly, opts.caller)
         # For each line in the editor (or starting at line start), check if the line
         # starts with a cell or output marker and is not already marked.
         # If not marked, mark it appropriately, and possibly process any
@@ -793,7 +796,7 @@
                         input.addClass('sagews-input-live')
                         input.click((e) => @_handle_input_cell_click(e, mark))

-                if not @opts.static_viewer
+                if not @readonly
                     elt = marks[0].element
                     if FLAGS.waiting in flagstring
                         elt.data('execute',FLAGS.waiting)
@@ -819,7 +822,7 @@

                 uuid = x.slice(1,37)
                 if context.uuids[uuid]
-                    # seen this before -- so change it
+                    # seen this id before (in a previous cell!) -- so change it
                     uuid = misc.uuid()
                     cm.replaceRange(uuid, {line:line, ch:1}, {line:line, ch:37})
                 context.uuids[uuid] = true


Removed quoting in initial summary from new case modal

--- static/coffee/modals.coffee
+++ static/coffee/modals.coffee
@@ -61,7 +61,7 @@
 # Open new case modal
 #=====================================================================
 modals.controller 'NewCaseModalController', [ '$scope', '$modalInstance', 'message', 'partners', ($scope, $modalInstance, message, partners) ->
-  $scope.summary = '"' + message.text + '"'
+  $scope.summary = message.text
   $scope.partners = partners
   $scope.assignee = if partners then partners[0] else null


Update Atom editor settings

Update Atom editor settings

--- atom/dot-atom/config.cson
+++ atom/dot-atom/config.cson
@@ -1,5 +1,6 @@
 "*":
   Hydrogen:
+    kernelspec: "{}"
     languageMappings: '''
       {
           "Pweave markdown": "Python 3",
@@ -7,7 +8,7 @@
       }
     '''
     startDir: "projectDirOfFile"
-    startupCode: "{\"Python 3\": \"%matplotlib inline\\n%config InlineBackend.figure_format = 'svg'\"}"
+    startupCode: "{\"Python 3\": \"%matplotlib inline\"}"
   "atom-beautify":
     bash:
       beautify_on_save: true
@@ -25,8 +26,19 @@
       indent_size: 2
     yaml:
       disabled: true
+  "atom-clock":
+    showClockIcon: true
   "autocomplete-python":
     useKite: false
+  "atom-html-preview":
+    enableMathJax: true
+    scrollToCursor: true
+    triggerOnSave: true
+  build:
+    saveOnBuild: true
+    scrollOnError: true
+  "build-make":
+    jobs: 1
   core:
     disabledPackages: [
       "language-gfm"
@@ -47,6 +59,7 @@
     userId: "a9970b71-c592-4094-a1ca-5774c7ea9f09"
   "linter-ui-default": {}
   "markdown-preview-plus":
+    enablePandoc: true
     grammars: [
       "source.gfm"
       "source.litcoffee"
@@ -56,6 +69,15 @@
       "text.plain"
       "text.plain.null-grammar"
     ]
+    pandocArguments: [
+      "--mathjax"
+      "--smart"
+      "--no-highlight"
+    ]
+    pandocBibliography: true
+    pandocMarkdownFlavor: "markdown+tex_math_single_backslash+multiline_tables+autolink_bare_uris+ascii_identifiers-implicit_figures"
+    useLazyHeaders: false
+    useNativePandocCodeStyles: true
   minimap:
     plugins:
       "highlight-selected": true
@@ -64,6 +86,11 @@
       linterDecorationsZIndex: 0
       pigments: true
       pigmentsDecorationsZIndex: 0
+  "platformio-ide-terminal":
+    core:
+      scrollback: 8000
+    style:
+      fontFamily: "Hack"
   welcome:
     showOnStartup: false
 ".basic.html.text":


Support a default value when undefined in the review panel toggles.

--- services/web/public/coffee/ide/review-panel/directives/reviewPanelToggle.coffee
+++ services/web/public/coffee/ide/review-panel/directives/reviewPanelToggle.coffee
@@ -6,6 +6,7 @@
 		scope:
 			onToggle: '&'
 			ngModel: '='
+			valWhenUndefined: '=?'
 			disabled: '=?'
 			onDisabledClick: '=?'
 		link: (scope) ->
@@ -18,6 +19,8 @@
 					scope.onDisabledClick()
 			scope.localModel = scope.ngModel
 			scope.$watch "ngModel", (value) ->
+				if scope.valWhenUndefined? and !value?
+					value = scope.valWhenUndefined
 				scope.localModel = value

 		template: """


Fix history frontend tests.

Fix history frontend tests.

--- services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee
+++ services/web/test/unit_frontend/coffee/ide/history/HistoryV2ManagerTests.coffee
@@ -15,14 +15,17 @@
 				nextBeforeTimestamp: null
 				atEnd: false
 				selection: {
+					label: null
 					updates: []
+					docs: {}
 					pathname: null
-					docs: {}
 					range: {
 						fromV: null
 						toV: null
 					}
 				}
+				error: null
+				showOnlyLabels: false
 				labels: null
 				diff: null
 				files: []


Point at new translation fusion table, use hostname for editor url

--- src/js/cts-cite-driver.coffee
+++ src/js/cts-cite-driver.coffee
@@ -12,8 +12,8 @@
   google_scope: 'https://www.googleapis.com/auth/fusiontables https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
   cts_endpoint: '1_DFxPLkDrZt2JTgFo04nI6zQ9AsnnqMNRlUBb2Sq'
   cts_urn: 'urn:cts:greekLit:tlg1389.tlg001.dc3'
-  cite_table_id: '11_Igu6u5961Dkz-cfbJOgKdYkQMwnoe3AQXw8T-K'
-  cite_collection_editor_url: 'http://ryanfb.github.io/harpocration/src/index.html'
+  cite_table_id: '1YOwprxInXb03cho6DQ20jVefAHF6a3fqhj3SGIxk'
+  cite_collection_editor_url: "http://#{window.location.hostname}/harpokration-cite/src/index.html"

 google_oauth_parameters_for_fusion_tables =
   response_type: 'token'
@@ -318,6 +318,7 @@
 $(document).ready ->
   console.log('ready')
   cts_cite_collection_driver_config = $.extend({}, default_cts_cite_collection_driver_config, window.cts_cite_collection_driver_config)
+  console.log(cts_cite_collection_driver_config['cite_collection_editor_url'])
   google_oauth_parameters_for_fusion_tables['client_id'] = cts_cite_collection_driver_config['google_client_id']
   google_oauth_parameters_for_fusion_tables['scope'] = cts_cite_collection_driver_config['google_scope']


Prefactor test layout

Prefactor test layout

--- src/layout/test_layout.coffee
+++ src/layout/test_layout.coffee
@@ -11,21 +11,15 @@
   game_dict = game({name: 'example', level: 1})
   config = layout(game_dict)

-  test "config exists", (t) ->
+  test "layout config", (t) ->
     t.ok game_dict, 'game_dict'
+    t.ok header, 'header'
+    t.ok controls, 'controls'
     t.ok config, 'config'
+    t.ok grid, 'grid'
     console.log 'game config', config
     t.end()

-  test "layout header", (t) ->
-    t.ok header, 'header'
+  test "layout sprites", (t) ->
     t.end()

-  test "layout controls", (t) ->
-    t.ok controls, 'controls'
-    t.end()
-
-  test "layout grid", (t) ->
-    t.ok grid, 'grid'
-    t.end()
-


Fix auto-connect order

Fix auto-connect order

--- src/lib/InternalSocket.coffee
+++ src/lib/InternalSocket.coffee
@@ -209,6 +209,10 @@
     isIP = event is 'ip' and IP.isIP payload
     ip = if isIP then payload else @legacyToIp event, payload

+    if not @isConnected() and autoConnect and @brackets.length is 0
+      # Connect before sending
+      @connect()
+
     if event is 'begingroup'
       @brackets.push payload
     if isIP and ip.type is 'openBracket'
@@ -225,10 +229,6 @@
       return if @brackets.length is 0
       @brackets.pop()

-    if not @isConnected() and autoConnect and @brackets.length is 0
-      # Connect before sending
-      @connect()
-
     # Emit the IP Object
     @emitEvent 'ip', ip


move..

move..

--- src/lib/requestfactory.coffee
+++ src/lib/requestfactory.coffee
@@ -20,6 +20,37 @@

         delayedRequestQueue.push ->
             request.get options, callback
+
+entry = -> new RequestFactory()
+entry.sessions = []
+
+if argv.cookie?
+    cookieRaw = argv.cookie
+else
+    cookieRaw = Config.Auth.CookieRaw
+
+# turn into an array
+cookieRaw = [cookieRaw] if typeof cookieRaw is 'string'
+
+for cookies, index in cookieRaw
+
+    map = {}
+    jar = request.jar()
+
+    for cookie in cookies.split(';')
+
+        cookie = cookie.trim()
+        continue if cookie.length is 0
+
+        jar.setCookie request.cookie(cookie), 'http://www.ingress.com' if cookie.length isnt 0
+
+        pair = cookie.split '='
+        map[pair[0]] = unescape pair[1]
+
+    entry.sessions.push
+        index:   index
+        cookies: map
+        jar:     jar

 class RequestFactory

@@ -218,38 +249,5 @@
             callback error, response, body

-entry = ->
-
-    return new RequestFactory()
-
-entry.sessions = []
-
-if argv.cookie?
-    cookieRaw = argv.cookie
-else
-    cookieRaw = Config.Auth.CookieRaw
-
-# turn into an array
-cookieRaw = [cookieRaw] if typeof cookieRaw is 'string'
-
-for cookies, index in cookieRaw
-
-    map = {}
-    jar = request.jar()
-
-    for cookie in cookies.split(';')
-
-        cookie = cookie.trim()
-        continue if cookie.length is 0
-
-        jar.setCookie request.cookie(cookie), 'http://www.ingress.com' if cookie.length isnt 0
-
-        pair = cookie.split '='
-        map[pair[0]] = unescape pair[1]
-
-    entry.sessions.push
-        index:   index
-        cookies: map
-        jar:     jar

 module.exports = entry


unauthorised detection bug fix

unauthorised detection bug fix

--- src/lib/requestfactory.coffee
+++ src/lib/requestfactory.coffee
@@ -192,7 +192,7 @@
                 process.exit 0
                 return false

-            if body.indexOf('User not authenticated') > -1
+            if body.indexOf('Sign in') > -1 or body.indexOf('User not authenticated') > -1
                 logger.error '[Auth] Authentication failed. Please update the cookie.'
                 process.exit 0
                 return false


the most minor of changes

the most minor of changes

--- app/assets/javascripts/admin/spree_product_grid_sort.coffee
+++ app/assets/javascripts/admin/spree_product_grid_sort.coffee
@@ -12,7 +12,7 @@
   initializeGrid: =>
     options =
       widget_margins: [10, 10]
-      widget_base_dimensions: [140, 260]
+      widget_base_dimensions: [140, 200]
       min_cols: 6 # TODO: CONFIGME
       draggable: { stop:  @updateGridOrder }
       serialize_params: @extractGridOrder


Speedup initial tab loading.

Speedup initial tab loading.

--- app/assets/javascripts/app/lib/app_post/task_manager.coffee
+++ app/assets/javascripts/app/lib/app_post/task_manager.coffee
@@ -515,7 +515,7 @@
                   persistent: true
                   init:       true
                 )
-              task_count * 350
+              task_count * 250
               undefined
               'task'
             )
@@ -534,7 +534,7 @@
               persistent: false
               init:       true
             )
-          task_count * 350
+          task_count * 250
           undefined
           'task'
         )


Add the title and class when building the <li>

--- app/assets/javascripts/glimpse/views/performance_bar.coffee
+++ app/assets/javascripts/glimpse/views/performance_bar.coffee
@@ -94,10 +94,8 @@
     left = @mapH(offset)
     width = @mapH(time)

-    bar = $("

")
     title = "#{name}: #{PerformanceBar.formatTime(time)}"
-    title += "\n\n#{info}".replace(/\n/g, '
') if info
-    bar.attr 'title', title
+    bar = $ '

', title: title, class: 'tooltip'
     bar.css
       width: "#{width}px"
       left:  "#{left}px"


Show the response time on page load

Show the response time on page load

--- app/assets/javascripts/glimpse/views/performance_bar.coffee
+++ app/assets/javascripts/glimpse/views/performance_bar.coffee
@@ -115,6 +115,15 @@

 updateStatus = (html) ->
   $('#serverstats-status').html html
+
+showResponseTime = ->
+  resp = $('#glimpse-server_response_time')
+  time = PerformanceBar.formatTime resp.data('time')
+
+  span = $('<span>', {'class': 'tooltip', title: 'Response time for this page'})
+    .text(time)
+  updateStatus span
+
 pjaxStart = null
 $(document).on 'pjax:start', (event) ->
   pjaxStart = event.timeStamp
@@ -146,5 +155,6 @@
     pjaxStart = null
   , 0

-$(document).on 'glimpse:update', ->
-  renderPerformanceBar()+$ ->
+  renderPerformanceBar()
+  showResponseTime()


fix destinationssub-menu for legacy global-nav

--- app/assets/javascripts/lib/melbourne/destination_nav.coffee
+++ app/assets/javascripts/lib/melbourne/destination_nav.coffee
@@ -86,6 +86,7 @@
             $(_item).addClass('highlight')
         else
           $(_menuItems).find('.submenu').addClass('hidden')
+        return false
       )

       $(_item).children('a').add(submenu).on('click',(e)=>


add translate provider mock to filter spec

--- app/modules/components/filter/filter.controller.spec.coffee
+++ app/modules/components/filter/filter.controller.spec.coffee
@@ -22,11 +22,27 @@
     $controller = null
     mocks = {}

+    _mockTranslate = ->
+        mocks.translate = {
+            instant: sinon.stub()
+        }
+
+        $provide.value("$translate", mocks.translate)
+
+    _mocks = ->
+        module (_$provide_) ->
+            $provide = _$provide_
+
+            _mockTranslate()
+
+            return null
+
     _inject = ->
         inject (_$controller_) ->
             $controller = _$controller_

     _setup = ->
+        _mocks()
         _inject()

     beforeEach ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -34,6 +34,7 @@
       equals obj, obj2
     else if index is 2
       equals obj, obj3
+    se

   equals indexSum, 3


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -4,7 +4,7 @@
   engine = Engine()

   engine.add
-    t
+    type: ''

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -4,7 +4,7 @@
   engine = Engine()

   engine.add
-
+    class:
     x: 0
     y:


update tests

update tests

--- services/document-updater/test/unit/coffee/DocumentManager/flushDocTests.coffee
+++ services/document-updater/test/unit/coffee/DocumentManager/flushDocTests.coffee
@@ -24,7 +24,7 @@
 	describe "when the doc is in Redis", ->
 		beforeEach ->
 			@RedisManager.getDoc = sinon.stub().callsArgWith(1, null, @lines, @version)
-			@TrackChangesManager.flushDocChanges = sinon.stub().callsArg(2)
+			@TrackChangesManager.flushDocChangesIfNeeded = sinon.stub().callsArg(2)
 			@PersistenceManager.setDoc = sinon.stub().callsArgWith(4)
 			@DocumentManager.flushDocIfLoaded @project_id, @doc_id, @callback


Add appropriate query strings to the end of invite link

--- services/web/app/coffee/Features/Collaborators/CollaboratorsEmailHandler.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsEmailHandler.coffee
@@ -36,6 +36,11 @@
 					replyTo: project.owner_ref.email
 					project:
 						name: project.name
-					inviteUrl: "#{Settings.siteUrl}/project/#{project._id}/invite/token/#{invite.token}"
+					inviteUrl: "#{Settings.siteUrl}/project/#{project._id}/invite/token/#{invite.token}?" + [
+						"project_name=#{encodeURIComponent(project.name)}"
+						"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
+						"r=#{project.owner_ref.referal_id}" # Referal
+						"rs=ci" # referral source = collaborator invite
+					].join("&")
 					owner: project.owner_ref
 				EmailHandler.sendEmail "projectInvite", emailOptions, callback


changed textarea to #msg_message and fixed clearing of value

--- app/assets/javascripts/chatrooms.js.coffee
+++ app/assets/javascripts/chatrooms.js.coffee
@@ -56,19 +56,18 @@
   params = $("#new_msg").serialize()
   console.log params
   $.post(url,params,() ->
+    $("#msg_message").val("")
     updateChatroom()
-    $("textarea").html(" ")
   )

 $(document).ready ->
   setInterval(updateChatroom, 10000)

-  $("textarea").autoResize()
-  $("textarea").keypress (event) ->
+  $("#msg_message").autoResize()
+  $("#msg_message").keypress (event) ->
     notNewAnymore()
     if event.ctrlKey and (event.keyCode == 13 or event.keyCode == 10)
       $("#send-message").click()
-

   $("#send-message").click( (event) ->
     event.preventDefault()


now displaying name via ajax properly

now displaying name via ajax properly

--- app/assets/javascripts/chatrooms.js.coffee
+++ app/assets/javascripts/chatrooms.js.coffee
@@ -53,7 +53,8 @@
     last = msg_id
     $.each(data, (key, val) ->
       whose = (email) -> if email == val.user.email then return 'mine' else return 'other'
-      email = '' + val.user.email.split("@")[0] + ': '
+      who = -> if val.user.display_name? then return val.user.display_name else return val.user.email.split("@")[0]
+      email = '' + who() + ': '
       time = '
' + val.created_at_in_local + '
'
       m = '

' + email + val.message + '
' + time + '
'
       html = '
'+m+'
'


Remove plan_price from addEcImpression

Remove plan_price from addEcImpression

--- app/coffee/modules/common/analytics.coffee
+++ app/coffee/modules/common/analytics.coffee
@@ -105,7 +105,7 @@
         })
         @.trackEvent("ecommerce", "add-step", step, stepId)

-    addEcImpression: (plan, plan_price, page, position) ->
+    addEcImpression: (plan, page, position) ->
         return if not @.initialized
         return if not @win.ga

@@ -118,7 +118,6 @@
         @win.ga('ec:addProduct', {
             'id': plan.plan_id,
             'name': plan.name,
-            'price': plan_price,
             'category': "plans",
             'quantity': 1,
             'position': 1,


`grepo.coffee` now completes both -n and -l

--- apps/grepo_base/static/coffee/grepo.coffee
+++ apps/grepo_base/static/coffee/grepo.coffee
@@ -24,14 +24,30 @@

 grepo.complete = (event, term) ->
   if event.keyCode is 9   # Try to auto-complete on TAB.
-    options = grepo.parse term.get_command()
+    command = term.get_command()
+    [command..., key, value] = command.split /\s+/

-    if options? and options.language?
-      # a) auto-complete language
-      if not options.keywords.length
-        term.set_command "grepo -l #{grepo.match options.language} "
+    if key[0] isnt "-"
+      command.push key
+
+      # seems like we have a -fBAR case.
+      if value[0] is "-"
+        [key, value] = [value.substring(0, 2), value.substring(2)]
+      # okay, complete keyword
       else
         # query the server for the closest matching tag.
+        return
+
+    # complete an argument.
+    switch key
+      when "-n"
+        value = parseInt(value or "10", 10) * 2
+      when "-l"
+        value = grepo.match(value)
+
+    command.push key
+    command.push value
+    term.set_command command.join(" ")

     return no


Let the browser paint the page before highlighting code

--- assets/javascripts/views/pages/base.coffee
+++ assets/javascripts/views/pages/base.coffee
@@ -13,7 +13,8 @@
     @prepare?() unless fromCache
     @activate()
     @delay @afterRender if @afterRender
-    $.requestAnimationFrame(@paintCode) if @highlightNodes.length > 0
+    if @highlightNodes.length > 0
+      $.requestAnimationFrame => $.requestAnimationFrame(@paintCode)
     return

   highlightCode: (el, language) ->


minor but important fix, update widget canceling fixed for firefox

--- client/landing/app/Applications/Activity.kdapplication/widgets/widgetview.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/widgetview.coffee
@@ -24,7 +24,7 @@

     @mainInputTabs.on "ResetWidgets", => @resetWidgets()

-    @mainInputTabs.on 'ReceivedClickElsewhere', =>
+    @mainInputTabs.on 'ReceivedClickElsewhere', (event)=>
       unless $(event.target).closest('.activity-status-context').length > 0
         @resetWidgets()


Modes; fix focus return value for InsertModeTrigger.

--- content_scripts/mode_insert.coffee
+++ content_scripts/mode_insert.coffee
@@ -53,9 +53,10 @@
     @push
       focus: (event) =>
         triggerSuppressor.unlessSuppressed =>
-          return unless DomUtils.isFocusable event.target
-          new InsertMode
-            targetElement: event.target
+          @alwaysContinueBubbling =>
+            if DomUtils.isFocusable event.target
+              new InsertMode
+                targetElement: event.target

     # We may already have focussed an input, so check.
     if document.activeElement and DomUtils.isEditable document.activeElement
@@ -63,7 +64,7 @@
         targetElement: document.activeElement

 # Used by InsertModeBlocker to suppress InsertModeTrigger; see below.
-triggerSuppressor = new Utils.Suppressor true
+triggerSuppressor = new Utils.Suppressor true # Note: true == @continueBubbling

 # Suppresses InsertModeTrigger.  This is used by various modes (usually by inheritance) to prevent
 # unintentionally dropping into insert mode on focusable elements.


Add buttons to modals

Add buttons to modals

--- vendor/assets/javascripts/fustrate/components/modal.coffee
+++ vendor/assets/javascripts/fustrate/components/modal.coffee
@@ -3,6 +3,7 @@
   @type: null
   @icon: null
   @title: null
+  @buttons: []

   @fadeSpeed: 250

@@ -27,7 +28,8 @@
     @settings.previousModal = $()

     @setTitle @constructor.title, icon: @constructor.icon
-    @setContent content
+    @setContent content, false
+    @setButtons @constructor.buttons, false

     @_reloadUIElements()
     @addEventListeners()
@@ -58,12 +60,54 @@
     else
       $('.modal-title span', @modal).html title

-  setContent: (content) =>
+  setContent: (content, { reload: true }) =>
     $('.modal-content', @modal).html content

     @settings._cachedHeight = undefined

-    @_reloadUIElements()
+    @_reloadUIElements() if reload
+
+  setButtons: (buttons, { reload: true }) =>
+    if buttons?.length < 1
+      $('.modal-buttons', @modal).empty()
+
+      return
+
+    list = []
+
+    for button in buttons
+      if typeof button is 'string'
+        list.push """
+          <button data-button="#{button}" class="#{button} expand">
+            #{button.titleize()}
+          </button>"""
+      else if typeof button is 'object'
+        for name, options of button
+          if typeof options is 'object'
+            text = options.text
+          else if typeof options is 'string'
+            text = options
+
+          text ?= name.titleize()
+
+          list.push(
+            $("<button data-button=\"#{name}\" class=\"expand\">")
+              .text(text)
+              .addClass(options.type ? name)
+              .outerHTML()
+          )
+
+    columns = list.map (button) -> "
#{button}
"
+
+    $('.modal-buttons', @modal)
+      .empty()
+      .html "
#{columns.join('')}
"
+
+    $('.row .columns', @modal).addClass("large-#{12 / columns.length}")
+
+    @settings._cachedHeight = undefined
+
+    @_reloadUIElements() if reload

   addEventListeners: =>
     @modal
@@ -185,6 +229,7 @@
           ×

+

""").appendTo(@settings.appendTo)

   @_defaultClasses: ->


Add last version of vue-chartjs class

Add last version of vue-chartjs class

--- app/assets/javascripts/chart/vue-chart.js.coffee
+++ app/assets/javascripts/chart/vue-chart.js.coffee
@@ -4,17 +4,18 @@
   $(document).ready ->

     class ChartVueJs
-      contructor: (chartType, chartOptions) ->
+      constructor: (chartType, chartOptions) ->
         Vue.component chartType, chartOptions

     class VueBarChart extends ChartVueJs
-      contructor: ->
-      Vue.component 'bar-chart',
-        extends: VueChartJs.Bar,
-        props: ['options'],
-        mixins: [VueChartJs.mixins.reactiveProp],
-        mounted: ->
-          this.renderChart(this.chartData, this.options)
+      constructor: ->
+        super 'bar-chart',
+          extends: VueChartJs.Bar,
+          props: ['options'],
+          mixins: [VueChartJs.mixins.reactiveProp],
+          mounted: ->
+            this.renderChart(this.chartData, this.options)
+

       addValueInBar: (chart) ->
         ctx = chart.ctx
@@ -22,8 +23,8 @@
         ctx.fillStyle = "rgba(0, 0, 0, 1)"
         ctx.textBaseline = 'bottom'

-        this.data.datasets.forEach (dataset, indexDataset) ->
-          meta = chartInstance.controller.getDatasetMeta(indexDataset)
+        chart.data.datasets.forEach (dataset, indexDataset) ->
+          meta = chart.controller.getDatasetMeta(indexDataset)
           meta.data.forEach (bar, indexData) ->
             data = dataset.data[indexData]
             barHeight = bar._model.y - bar._model.base


Add method to update the values of a model

--- app/assets/javascripts/models/active_page.coffee
+++ app/assets/javascripts/models/active_page.coffee
@@ -35,6 +35,10 @@

     error_tag = $("[data-error][data-k-uuid='" + @uuid + "']")
     error_tag.hide()
+
+  update_vals_on_page: ->
+    $.each @attributes, (attr, val) =>
+      $("[data-k-uuid='" + @uuid + "'][data-attr='" + attr + "']").val(val)

   dirty_from_page: ->
     dirty = []


fixed leaflet paths, again

fixed leaflet paths, again

--- app/assets/javascripts/modules/map/map.js.coffee
+++ app/assets/javascripts/modules/map/map.js.coffee
@@ -11,6 +11,9 @@
              MouseEventsLayer = ns.MouseEventsLayer,
              Dataset = @edsc.models.data.Dataset,
              page = @edsc.page) ->
+
+  # Fix leaflet default image path
+  L.Icon.Default.imagePath = '/assets/leaflet-0.7/images' unless L.Icon.Default.imagePath?

   # Constructs and performs basic operations on maps
   # This class wraps the details of setting up the map used by the application,


Increased slider speed on info page by the double.

Signed-off-by: Philip Vieira <3ba3974e96044dcde818e6310ed799abe10671d4@vallin.se>

--- app/assets/javascripts/views/root/info.js.coffee
+++ app/assets/javascripts/views/root/info.js.coffee
@@ -26,7 +26,7 @@

     setTimeout =>
       @.$('ul#info-contributors-list').simplyScroll
-        speed: 4
+        speed: 8
     , 10

   show: ->


fix assignment due date editing if there are no sections for the course

test plan:
  - as an admin, create a course from the "Courses" tab in the admin
    settings for the account (you can get there by hovering over
    "Courses" in the top menu navigation, then clicking the account's
    name under "Managed Accounts."
  - Create an assignment for the course. Toggle "advanced options" on
    the assignment edit page. You should see the typical datepicker
    stuff there.
  - Save the assignment and make sure the dates get saved as usual.

fixes CNVS-5524

Change-Id: I88724bd745683473d9db494852a5982621b0c7f0
Reviewed-on: https://gerrit.instructure.com/20108
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Myller de Araujo <e5c7c5e1b7f2ce5e9870cc98ec52b20b3006cb48@instructure.com>
Product-Review: Stanley Stuart <2b43fb8b7a234825d50dd49ce7892d78a59da8f3@instructure.com>

--- app/coffeescripts/bundles/assignment_edit.coffee
+++ app/coffeescripts/bundles/assignment_edit.coffee
@@ -1,5 +1,6 @@
 # manage groups is for the add_group_category dialog
 require [
+  'compiled/models/Section'
   'compiled/models/Assignment'
   'compiled/views/assignments/EditView'
   'compiled/collections/SectionCollection'
@@ -12,7 +13,7 @@
   'compiled/views/assignments/PeerReviewsSelector'
   'grading_standards'
   'manage_groups'
-], (Assignment, EditView, SectionCollection, DueDateList, DueDateListView,
+], (Section,Assignment, EditView, SectionCollection, DueDateList, DueDateListView,
 OverrideView, AssignmentGroupSelector, GradingTypeSelector,
 GroupCategorySelector, PeerReviewsSelector) ->

@@ -22,6 +23,8 @@
   assignment.urlRoot = ENV.URL_ROOT

   sectionList = new SectionCollection ENV.SECTION_LIST
+  if !sectionList.length
+    sectionList.add Section.defaultDueDateSection()
   dueDateList = new DueDateList assignment.get('assignment_overrides'), sectionList, assignment

   assignmentGroupSelector = new AssignmentGroupSelector


chore(test): update test error for mocha upgrade

--- test/load-config-test.coffee
+++ test/load-config-test.coffee
@@ -70,6 +70,7 @@
         result.should.be.deep.equal
           str: 'hello'
         done()
+      return
     it 'should load config asynchronously promise', ()->
       loadConfig.load __dirname+'/fixture/'
       .then (result)->
@@ -79,6 +80,9 @@
           str: 'hello'

   describe 'object usage', ->
+    before ->
+      loadConfig::files = ['index', 'readme', 'config']
+      loadConfig.register ['.jsn', 'jon', '.json'], JSON.parse
     it 'should create a new Config object', ->
       result = new loadConfig 'path1', {hi:112}
       result.should.have.property 'path', 'path1'
@@ -115,6 +119,7 @@
         result.should.be.deep.equal
           str: 'hello'
         done()
+      return

     it 'should load config asynchronously with raise error if nothing loaded', (done)->
       result = new loadConfig __dirname+'/fixture/sds'
@@ -122,6 +127,7 @@
         should.exist err
         should.not.exist result
         done()
+      return

     it 'should load config asynchronously overwrite path', (done)->
       result = new loadConfig __dirname+'/fixture/con'
@@ -132,6 +138,7 @@
         result.should.be.deep.equal
           str: 'hello'
         done()
+      return

   describe 'fake filesystem', ->
     fakeFS = require 'load-config-file/test/fake-fs'
@@ -162,6 +169,7 @@
           expectedResult['p/config'+k] = encoding: 'ascii'
         fakeFS.result.should.be.deep.equal expectedResult
         done(err)
+      return

     describe 'external file names', ->
       it 'should load config synchronously with a config file name', ->
@@ -188,6 +196,7 @@
             expectedResult['p/as'+k] = file:'as', encoding: 'utf8'
           fakeFS.result.should.be.deep.equal expectedResult
           done(err)
+        return

       it 'should load config asynchronously with multi config file names', (done)->
         loadConfig 'p/', file:['as','23'], (err, result)->
@@ -198,3 +207,4 @@
             expectedResult['p/23'+k] = file:['as','23'], encoding: 'utf8'
           fakeFS.result.should.be.deep.equal expectedResult
           done(err)
+        return


Add test to document streaming trait

Add test to document streaming trait

--- test/model/shape.spec.coffee
+++ test/model/shape.spec.coffee
@@ -2,6 +2,13 @@
 AWS = helpers.AWS

 describe 'AWS.Model.Shape', ->
+
+  describe 'Traits', ->
+    it 'inherits streaming trait', ->
+      api = new AWS.Model.Api shapes: S1: { 'type': 'blob', 'streaming': true }
+      shape = AWS.Model.Shape.create { members: { body: { shape: 'S1' } } }, { api: api }
+      expect(shape.members.body.isStreaming).to.eql(true)
+
   describe 'TimestampShape', ->
     describe 'toType()', ->
       it 'converts unix timestamps', ->


Add test coverage.

Add test coverage.

--- test/unit/routerSpec.coffee
+++ test/unit/routerSpec.coffee
@@ -25,34 +25,27 @@
           birth_date: moment([1961, 6, 7]).valueOf()
           scan_sets:
             t1:
-              modeling: [
-                results: [
-                  delta_k_trans:
-                    average: 2.6
-                ]
-              ]
-            t2:
-              modeling: [
-                results: [
-                  delta_k_trans:
-                    average: 2.4
-                ]
-              ]
-          registration_configurations:
-            reg_01:
-              modeling: [
-                results: [
-                  delta_k_trans:
-                    average: 2.3
-                ]
-              ]
-            reg_02:
-              modeling: [
-                results: [
-                  delta_k_trans:
-                    average: 2.1
-                ]
-              ]
+              registration:
+                reg_01:
+                  modeling:
+                    pk_01:
+                      results: [
+                        delta_k_trans:
+                          average: 2.3
+                      ]
+                reg_02:
+                  modeling:
+                    pk_02:
+                      results: [
+                        delta_k_trans:
+                          average: 2.1
+                      ]
+              modeling:
+                pk_03:
+                  results: [
+                    delta_k_trans:
+                      average: 2.6
+                  ]
           sessions: [
             number: 1
             acquisition_date: moment('July 1, 2013').valueOf()
@@ -82,6 +75,9 @@
                 reg_01:
                   intensity:
                     intensities: [3.1]
+                reg_02:
+                  intensity:
+                    intensities: [2.7]

       beforeEach ->
         # Fake the router service module.
@@ -111,40 +107,38 @@
         $httpBackend.verifyNoOutstandingExpectation()
         $httpBackend.verifyNoOutstandingRequest()

-      describe.only 'Subject', ->
+      describe 'Subject', ->
         subject = null

         beforeEach ->
           subject = _.clone(mock.subject)
-
-        # Validates the resolved subject.
-        validate = (subject) ->
-          # There should be an age.
-          expect('birthDate' of subject, "Subject is missing a birthDate property")
-            .to.be.true
+          Router.getSubject(subject)
+          $httpBackend.flush()
+
+        it 'should set the subject birth date', ->
           expect(subject.birthDate, "Subject is missing a birth date").to.exist
           expect(subject.birthDate.valueOf(), "Subject birth date is incorrect")
             .to.equal(mock.subject_detail.birth_date)
-
-          # There should be encounters.
+
+        it 'should set the subject encounters', ->
           expect(subject.encounters, "Subject is missing encounters").to.exist
-          expect(subject.encounters.length, "Subject encounters length is incorrect")
-            .to.equal(1)
+          expect(subject.encounters.length, "Subject encounters length is" +
+                                            " incorrect").to.equal(1)
           enc = subject.encounters[0]
           expect(enc._cls, "Encounter type is incorrect")
             .to.equal(mock.subject_detail.encounters[0]._cls)
-
-          # There should be treatments.
+
+        it 'should extend the subject treatments', ->
           expect(subject.treatments, "Subject is missing treatments").to.exist
-          expect(subject.treatments.length, "Subject encounters length is incorrect")
-            .to.equal(1)
+          expect(subject.treatments.length, "Subject encounters length is" +
+                                            " incorrect").to.equal(1)
           trt = subject.treatments[0]
-          expect(trt.treatment_type, "Treatment type is incorrect")
+          expect(trt.treatmentType, "Treatment type is incorrect")
             .to.equal(mock.subject_detail.treatments[0].treatment_type)
           expect(trt.begin_date.valueOf(), "Treatment begin date is incorrect")
             .to.equal(mock.subject_detail.treatments[0].begin_date)
-
-          # Validate the modeling.
+
+        it 'should set the subject modeling', ->
           expect(subject.modeling, "Subject is missing modeling").to.exist
           # There are two registration modeling objects.
           regMdl = subject.modeling.registration
@@ -153,109 +147,90 @@
           expect(regMdl.length, "The subject modeling registration count" +
                                 " is incorrect")
             .to.equal(2)
-          # There are two scan modeling objects.
+          # There is one scan modeling objects.
           scanMdl = subject.modeling.scan
           expect(scanMdl, "The subject modeling scan is missing").to.exist
           expect(scanMdl.length, "The subject modeling registration count" +
                                  " is incorrect")
-            .to.equal(2)
+            .to.equal(1)
           # The scan modeling is for the T1 scan.
           mdl = subject.modeling.scan[0]
+          expect(mdl.key, "Modeling is missing a key").to.exist
           expect(mdl.source, "Modeling is missing a source").to.exist
-          expect(mdl.source.key, "Modeling source is missing the key")
-            .to.exist
-          expect(mdl.source.key, "Modeling source key is incorrect")
+          expect(mdl.source.scanType, "The scan modeling source is missing" +
+                                      " the scan type").to.exist
+          expect(mdl.source.scanType, "Modeling source scan type is incorrect")
             .to.equal('t1')
-          mockMdl = mock.subject_detail.scan_sets.t1.modeling[0]
+          mockMdl = mock.subject_detail.scan_sets.t1.modeling.pk_03
           expect(mdl.results, "Modeling is missing results").to.exist
           expect(mdl.results.length, "Modeling results count is incorrect").
             to.equal(1)
           mdlResult = mdl.results[0]
-          expect(mdlResult.deltaKTrans, "Modeling results is missing deltaKTrans")
-            .to.exist
-          expect(mdlResult.deltaKTrans.average, "Modeling deltaKTrans is incorrect")
+          expect(mdlResult.deltaKTrans, "Modeling results is missing" +
+                                        " deltaKTrans").to.exist
+          expect(mdlResult.deltaKTrans.average, "Modeling deltaKTrans is" +
+                                                " incorrect")
             .to.equal(mockMdl.results[0].delta_k_trans.average)
           # The modeling result object refers to its respective session.
-          expect(mdlResult.session, "Modeling results is missing session").to.exist
+          expect(mdlResult.session, "Modeling results is missing session")
+            .to.exist
           expect(mdlResult.session, "Modeling results session is incorrect")
             .to.equal(subject.sessions[0])
-
-          # There should be sessions.
+
+        it 'should reflect the subject sessions', ->
           expect(subject.sessions, "Subject is missing sessions").to.exist
-          expect(subject.sessions.length, "Subject sessions length is incorrect")
-            .to.equal(1)
-          expect(subject.isMultiSession, "Subject multi-session flag is incorrect")
-            .to.be.false
-          sess = subject.sessions[0]
-
-          # There should be a parent subject.
-          expect(sess.subject, "Session is missing a subject").to.exist
-
-        it 'should fetch the subject by the secondary key properties', ->
-          Router.getSubject(subject).then ->
-            validate(subject)
-          # Dispatch the backend request.
-          $httpBackend.flush()
-
-      describe 'Session Detail', ->
-        mockSession = null
-
-        # Validates the resolved session.
-        validate = (session) ->
-          scan = session.scans.t1
-          expect(scan, "Session is missing the T1 scan").to.exist
-          mockScan = mock.session_detail.scans.t1
-          expect(scan.intensity, "Session scan is incorrect")
-            .to.deep.eql(mockScan.intensity)
-          expect(scan.intensity, "Session scan is incorrect")
-            .to.deep.eql(mockScan.intensity)
-          regs = scan.registration
-          expect(scan.registration, "Session is missing the T1 registration")
-            .to.exist
-          reg = scan.registration.reg_01
-          expect(reg, "Session is missing the T1 reg_01 registration").to.exist
-          mock_reg = mock.session_detail.scans.t1.registration.reg_01
-          expect(reg.intensity, "Session registration is incorrect")
-            .to.deep.eql(mock_reg.intensity)
-
-        beforeEach ->
-          mockSession = mock.subject_detail.sessions[0]
-          # The session subject reference is set in the routes
-          # session resolution.
-          mockSession.subject = mock.subject
-
-        it 'should fetch the session detail', ->
-          session = _.clone(mockSession)
-          Router.getSessionDetail(session).then ->
-            validate(session)
-          # Dispatch the backend request.
-          $httpBackend.flush()
-
-      describe 'Scan', ->
-        mockSession = null
-
-        beforeEach ->
-          mockSession = _.clone(mock.subject_detail.sessions[0])
-          # The session subject reference is set in the routes
-          # session resolution.
-          mockSession.subject = mock.subject
-
-        it 'should find the scan container in a fetched session', ->
-          session = _.clone(mockSession)
-          session.scans = mock.session_detail.scans
-          mockScan = mock.session_detail.scans.t1
-          actual = Router.getScan(session, mockScan.name)
-          expect(actual, 'Image container missing').to.exist
-          expect(actual, 'Image container incorrect')
-            .to.equal(mock.session_detail.scans.t1)
-
-        it 'should fetch a session if necessary to obtain the scan', ->
-          session = _.clone(mockSession)
-          mockScan = mock.session_detail.scans.t1
-          Router.getScan(session, mockScan.name).then (container) ->
-            expect(container, 'Image container missing').to.exist
-            expect(container.intensity, 'Image container intensity incorrect').
-              to.eql(mockScan.intensity)
-
-          # Dispatch the backend request.
-          $httpBackend.flush()
+          expect(subject.sessions.length, "Subject sessions length is" +
+                                          " incorrect").to.equal(1)
+          expect(subject.isMultiSession, "Subject multi-session flag is" +
+                                         " incorrect").to.be.false
+
+        describe 'Session', ->
+          mockSession = null
+          session = null
+
+          beforeEach ->
+            session = _.clone(subject.sessions[0])
+            Router.getSessionDetail(session)
+            $httpBackend.flush()
+
+          it 'should set the session subject reference', ->
+            expect(session.subject, "Session is missing a subject reference")
+              .to.exist
+            expect(session.subject, "Session subject reference is incorrect")
+              .to.equal(subject)
+
+          describe 'Scan', ->
+            scan = null
+
+            beforeEach ->
+              scan = Router.getScan(session, 't1')
+
+            it 'should extend the session scan', ->
+              expect(scan, "The session is missing the T1 scan").to.exist
+              expect(scan.session, "The session T1 scan is missing the" +
+                                   " session reference").to.exist
+              expect(scan.session, "The session T1 scan subject reference" +
+                                   " is incorrect").to.equal(session)
+
+            it 'should set the session scan intensity', ->
+              mockIntensity = mock.session_detail.scans.t1.intensity
+              expect(scan.intensity, "The scan intensity is incorrect")
+                .to.deep.eql(mockIntensity)
+
+            describe 'Registration', ->
+              registration = null
+
+              beforeEach ->
+                registration = Router.getRegistration(scan, 'reg_01')
+
+              it 'should extend the registration', ->
+                expect(registration, "The scan is missing the reg_01" +
+                                     " registration").to.exist
+                expect(registration.scan, "The registration is missing the" +
+                                          " scan reference").to.exist
+                expect(registration.scan, "The registration scan reference" +
+                                          " is incorrect").to.equal(scan)
+                mock_reg = mock.session_detail.scans.t1.registration.reg_01
+                expect(registration.intensity, "The registration intensity" +
+                                               " is incorrect")
+                  .to.deep.eql(mock_reg.intensity)


Emit event after linking the layout directive.

--- services/web/public/coffee/ide/directives/layout.coffee
+++ services/web/public/coffee/ide/directives/layout.coffee
@@ -117,5 +117,10 @@
 								element.layout().hide("east")
 							else
 								element.layout().show("east")
+
+				post: (scope, element, attrs) ->
+					name = attrs.layout
+					state = element.layout().readState()
+					scope.$broadcast "layout:#{name}:linked", state
 		}
 	]


Add default resizer values to layout directive.

--- services/web/public/coffee/ide/directives/layout.coffee
+++ services/web/public/coffee/ide/directives/layout.coffee
@@ -11,12 +11,12 @@
 					if attrs.spacingOpen?
 						spacingOpen = parseInt(attrs.spacingOpen, 10)
 					else
-						spacingOpen = 24
+						spacingOpen = window.uiConfig.defaultResizerSizeOpen

 					if attrs.spacingClosed?
 						spacingClosed = parseInt(attrs.spacingClosed, 10)
 					else
-						spacingClosed = 24
+						spacingClosed = window.uiConfig.defaultResizerSizeClosed

 					options =
 						spacing_open: spacingOpen


:bug: fix trying to get none-existing .gitginore file

--- src/atom/watch-project-task.coffee
+++ src/atom/watch-project-task.coffee
@@ -13,7 +13,13 @@

   # massage from atom to chokidar-friendly ignored item
   ignored = ignoredNames
-    .concat if excludeVcsIgnoredPaths then gitignoreGlobs(Path.join(projectPath, '.gitignore')) else []
+    .concat if excludeVcsIgnoredPaths
+        try
+          gitignoreGlobs(Path.join(projectPath, '.gitignore'))
+        catch
+          []
+      else
+        []
     .concat if excludeVcsIgnoredPaths then ['.gitignore'] else []
     .map (name) ->
       Path.join(projectPath, name)


Fix reporting tests

Fix reporting tests

--- test/unit/reportsTest.coffee
+++ test/unit/reportsTest.coffee
@@ -100,17 +100,17 @@
     it "should send a  weekly channel report", (done) ->
       sinon.spy(reports, 'fetchWeeklySubscribers')
       reports.sendReports {}, 'weeklyReport', () ->
-        reports.fetchWeeklySubscribers.should.be.called
-        reports.fetchChannelReport.should.be.called
-        reports.sendUserEmail.should.be.called
+        reports.fetchWeeklySubscribers.should.be.called()
+        reports.fetchChannelReport.should.be.called()
+        reports.sendUserEmail.should.be.called()

       done()

     it "should send a  daily channel report", (done) ->
       sinon.spy(reports, 'fetchDailySubscribers')
       reports.sendReports {}, 'weeklyReport', () ->
-        reports.fetchDailySubscribers.should.be.called
-        reports.fetchChannelReport.should.be.called
-        reports.sendUserEmail.should.be.called
+        reports.fetchDailySubscribers.should.be.called()
+        reports.fetchChannelReport.should.be.called()
+        reports.sendUserEmail.should.be.called()

       done()


fixed indentation

fixed indentation

--- src/models/InitialView.coffee
+++ src/models/InitialView.coffee
@@ -12,10 +12,10 @@

 		steroids.nativeBridge.nativeCall
-          method: "dismissInitialView"
-          parameters: parameters
-          successCallbacks: [callbacks.onSuccess]
-          failureCallbacks: [callbacks.onFailure]
+			method: "dismissInitialView"
+			parameters: parameters
+			successCallbacks: [callbacks.onSuccess]
+			failureCallbacks: [callbacks.onFailure]

 	show: (options={}, callbacks={})->
@@ -29,8 +29,8 @@
 				transition: "fade"

 		steroids.nativeBridge.nativeCall
-		  method: "showInitialView"
-		  parameters: parameters
-		  successCallbacks: [callbacks.onSuccess]
-		  failureCallbacks: [callbacks.onFailure]
+			method: "showInitialView"
+			parameters: parameters
+			successCallbacks: [callbacks.onSuccess]
+			failureCallbacks: [callbacks.onFailure]


Don’t need this comment

Don’t need this comment

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -65,7 +65,6 @@
     # Should we disqualify TextEditors with the Grammar text.plain.null-grammar?
     return paneItem instanceof TextEditor

-  # Private: Handles editor events
   handleEvents: =>
     # Track the current pane item, update current editor
     @subscriptions.add(atom.workspace.observeActivePaneItem(@updateCurrentEditor))


Use path.sep instead of "/"

Use path.sep instead of "/"

--- lib/changes/file-summary.coffee
+++ lib/changes/file-summary.coffee
@@ -1,5 +1,6 @@
 shell = require 'shell'
 fs = require 'fs'
+path = require 'path'

 module.exports = class FileSummary
   constructor: ({@file, @status, @icon, @git}) ->
@@ -44,17 +45,17 @@
       @git.emit('did-update-repository')

   getPathInfo: ->
-    pathParts = @file.path().split("/")
+    pathParts = @file.path().split(path.sep)
     filename  = pathParts.pop()
-    dir       = pathParts.join('/')
-    dir      += "/" if dir
+    dir       = pathParts.join(path.sep)
+    dir      += path.sep if dir
     [dir, filename]

   path: ->
     @file.path()

   localPath: ->
-    "#{@git.repoPath}/#{@path()}"
+    "#{@git.repoPath}#{path.sep}#{@path()}"

   exists: ->


Ramp material shadows fix.

Ramp material shadows fix.

--- packages/landsofillusions-assets/engine/mesh/rampmaterial.coffee
+++ packages/landsofillusions-assets/engine/mesh/rampmaterial.coffee
@@ -48,22 +48,31 @@

   DirectionalLight directionalLight;
   float lightIntensity;
+  float shadow;

   for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
     directionalLight = directionalLights[i];

     // Shade using Lambert cosine law.
-    lightIntensity = dot(directionalLight.direction, normalEye);
+    lightIntensity = saturate(dot(directionalLight.direction, normalEye));

-    // Apply shadow map.
-    lightIntensity *= getShadow(directionalShadowMap[i], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[0]);
+    // Apply shadow map. For some reason we must address the map with a constant, not the index i.
+    if (i==0) {
+      shadow = getShadow(directionalShadowMap[0], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[0]);
+    }
+
+    #if NUM_DIR_LIGHTS > 1
+      else if (i==1) {
+        shadow = getShadow(directionalShadowMap[1], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[1]);
+      }
+    #endif

     // Add to total intensity.
-    totalLightIntensity += lightIntensity;
+    totalLightIntensity += lightIntensity * shadow;
   }

   // Shade from ambient to full light based on intensity.
-  float shadeFactor = mix(ambientLightColor.r, 1.0, lightIntensity);
+  float shadeFactor = mix(ambientLightColor.r, 1.0, totalLightIntensity);

   // Dim the color of the cluster by the shade factor.
   vec3 shadedColor = sourceColor * shadeFactor;


Update autoform-file.coffee

Update autoform-file.coffee

--- lib/client/autoform-file.coffee
+++ lib/client/autoform-file.coffee
@@ -16,14 +16,6 @@
   self = @
   @value = new ReactiveVar @data.value

-  @_stopInterceptValue = false
-  @_interceptValue = (ctx) =>
-    unless @_stopInterceptValue
-      t = Template.instance()
-      if t.value.get() isnt false and t.value.get() isnt ctx.value and ctx.value?.length > 0
-        t.value.set ctx.value
-        @_stopInterceptValue = true
-
   @_insert = (file) ->
     collection = getCollection self.data

@@ -36,6 +28,8 @@
     collection.insert file, (err, fileObj) ->
       if typeof self.data.atts?.onAfterInsert is 'function'
         self.data.atts.onAfterInsert err, fileObj
+
+      fileObj.update $set: metadata: owner: Meteor.userId()

       if err then return console.log err
       self.value.set fileObj._id
@@ -65,22 +59,18 @@
     file: getDocument @
     atts: @atts
   file: ->
-    Template.instance()._interceptValue @
     getDocument @
   removeFileBtnTemplate: ->
     @atts?.removeFileBtnTemplate or 'afFileRemoveFileBtnTemplate'
   selectFileBtnTemplate: ->
     @atts?.selectFileBtnTemplate or 'afFileSelectFileBtnTemplate'
+  selectFileBtnData: ->
+    label: @atts.label or 'Choose file'
+    accepts: @atts.accepts
   uploadProgressTemplate: ->
     @atts?.uploadProgressTemplate or 'afFileUploadProgress'

 Template.afFileUpload.events
-  'click .js-af-select-file': (e, t) ->
-    t.$('.js-file').click()
-
-  'change .js-file': (e, t) ->
-    t._insert e.target.files[0]
-
   "dragover .js-af-select-file": (e) ->
     e.stopPropagation()
     e.preventDefault()
@@ -97,6 +87,9 @@
   'click .js-af-remove-file': (e, t) ->
     e.preventDefault()
     t.value.set false
+
+  'fileuploadchange .js-file': (e, t, data) ->
+    t._insert new FS.File data.files[0]

 Template.afFileUploadThumbImg.helpers
   url: ->
@@ -115,3 +108,6 @@
         'file-powerpoint-o'
       else
         'file-o'
+
+Template.afFileSelectFileBtnTemplate.onRendered ->
+  @$('.js-file').fileupload()


Remove deprecated method calls

Remove deprecated method calls

--- lib/command-palette-view.coffee
+++ lib/command-palette-view.coffee
@@ -30,7 +30,7 @@
       @eventElement = @previouslyFocusedElement
     else
       @eventElement = atom.workspaceView
-    @keyBindings = atom.keymap.keyBindingsMatchingElement(@eventElement)
+    @keyBindings = atom.keymap.findKeyBindings(target: @eventElement[0])

     events = []
     for eventName, eventDescription of _.extend($(window).events(), @eventElement.events())
@@ -47,7 +47,7 @@
       @li class: 'event', 'data-event-name': eventName, =>
         @div class: 'pull-right', =>
           for binding in keyBindings when binding.command is eventName
-            @kbd _.humanizeKeystroke(binding.keystroke), class: 'key-binding'
+            @kbd _.humanizeKeystroke(binding.keystrokes), class: 'key-binding'
         @span eventDescription, title: eventName

   confirmed: ({eventName}) ->


Add validation check on Session.open

Add validation check on Session.open

Update validation method to return a message if url is not defined.

--- src/coffee/cilantro/session.coffee
+++ src/coffee/cilantro/session.coffee
@@ -48,8 +48,12 @@
             @started = false
             @opening = false

+        # Ensure a url is defined when the session is initialized
+        # or updated (using set). See http://backbonejs.org/#Model-validate
+        # for details.
         validate: (attrs, options) ->
-            if not attrs.url? then true
+            if not attrs.url?
+                return 'url is required'

         parse: (attrs) ->
             # Title of the API
@@ -96,8 +100,13 @@
             if @opened or @opening
                 return @_opening.promise()

+            # Ensure the session is valid before opening
+            if not @isValid()
+                throw new Error(@validationError)
+
             # Set state and create deferred that will be used for creating
-            # promises while
+            # promises while the session is opening and after it is opened
+            # to maintain a consistent interface.
             @opening = true
             @_opening = $.Deferred()


Add some tests around the bucketing

Add some tests around the bucketing

--- test/lib/algorhythmic.coffee
+++ test/lib/algorhythmic.coffee
@@ -2,5 +2,24 @@
 algorhythm = require('../../lib/algorhythmic')

 describe 'Algorhythmic', ->
-  it 'works', ->
-    expect(algorhythm).to.be.ok
+  describe 'bucketing an identifier', ->
+    bucket = randomString = null
+
+    it 'buckets an empty string', ->
+      expect(algorhythm.convert('')).to.be.ok
+
+    it 'buckets a single character string', ->
+      expect(algorhythm.convert('a')).to.be.ok
+
+    it 'always converts a string to the same bucket', ->
+      for run in [1..100]
+        bucket = algorhythm.convert('foo')
+
+        expect(bucket).to.equal(5)
+
+    it 'always buckets within the given range', ->
+      for run in [1..100]
+        randomString = Math.random().toString(30).substring(2)
+        bucket = algorhythm.convert(randomString)
+
+        expect(bucket).to.be.within(1,10)


cover feed with parents commands

cover feed with parents commands

--- test/phabs_admin_test.coffee
+++ test/phabs_admin_test.coffee
@@ -880,32 +880,66 @@
             .to.eql "The feed from 'bug report' to '#dev' already exist."

     context 'and the feed do not already exists', ->
-      beforeEach ->
-        room.robot.brain.data.phabricator.projects = {
-          'bug report': {
-            phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
-            name: 'bug report',
-            feeds: [ ]
-          },
-          'project': { phid: 'PHID-PROJ-1234567' },
-        }
-        room.robot.brain.data.phabricator.aliases = {
-          bugs: 'bug report',
-          bug: 'bug report'
-        }
-        do nock.disableNetConnect
-
-      afterEach ->
-        room.robot.brain.data.phabricator = { }
-        nock.cleanAll()
-
-      context 'phad feed bug to #dev', ->
-        hubot 'phad feed bug to #dev'
-        it 'should say that the feed was created', ->
-          expect(hubotResponse())
-            .to.eql "Ok, 'bug report' is now feeding '#dev'."
-          expect(room.robot.brain.data.phabricator.projects['bug report'].feeds)
-            .to.include '#dev'
+      context 'and project has no parent', ->
+        beforeEach ->
+          room.robot.brain.data.phabricator.projects = {
+            'bug report': {
+              phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
+              name: 'bug report',
+              feeds: [ ]
+            },
+            'project': { phid: 'PHID-PROJ-1234567' },
+          }
+          room.robot.brain.data.phabricator.aliases = {
+            bugs: 'bug report',
+            bug: 'bug report'
+          }
+          do nock.disableNetConnect
+
+        afterEach ->
+          room.robot.brain.data.phabricator = { }
+          nock.cleanAll()
+
+        context 'phad feed bug to #dev', ->
+          hubot 'phad feed bug to #dev'
+          it 'should say that the feed was created', ->
+            expect(hubotResponse())
+              .to.eql "Ok, 'bug report' is now feeding '#dev'."
+            expect(room.robot.brain.data.phabricator.projects['bug report'].feeds)
+              .to.include '#dev'
+
+      context 'and project has a parent', ->
+        beforeEach ->
+          room.robot.brain.data.phabricator.projects = {
+            'project/bug report': {
+              phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
+              name: 'bug report',
+              parent: 'project',
+              feeds: [ ]
+            },
+            'project': {
+              phid: 'PHID-PROJ-1234567',
+              name: 'project',
+              feeds: [ ]
+            },
+          }
+          room.robot.brain.data.phabricator.aliases = {
+            bugs: 'project/bug report',
+            bug: 'project/bug report'
+          }
+          do nock.disableNetConnect
+
+        afterEach ->
+          room.robot.brain.data.phabricator = { }
+          nock.cleanAll()
+
+        context 'phad feed bug to #dev', ->
+          hubot 'phad feed bug to #dev'
+          it 'should say that the feed was created', ->
+            expect(hubotResponse())
+              .to.eql "Ok, 'project/bug report' is now feeding '#dev'."
+            expect(room.robot.brain.data.phabricator.projects['project/bug report'].feeds)
+              .to.include '#dev'

   # ---------------------------------------------------------------------------------
   context 'user wants to add a catchall feed', ->
@@ -1026,34 +1060,69 @@

     context 'and the feed exists', ->
-      beforeEach ->
-        room.robot.brain.data.phabricator.projects = {
-          'bug report': {
-            phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
-            name: 'bug report',
-            feeds: [
-              '#dev'
-            ]
-          },
-          'project': { phid: 'PHID-PROJ-1234567' },
-        }
-        room.robot.brain.data.phabricator.aliases = {
-          bugs: 'bug report',
-          bug: 'bug report'
-        }
-        do nock.disableNetConnect
-
-      afterEach ->
-        room.robot.brain.data.phabricator = { }
-        nock.cleanAll()
-
-      context 'phad remove bug from #dev', ->
-        hubot 'phad remove bug from #dev'
-        it 'should say that the feed was removed', ->
-          expect(hubotResponse())
-            .to.eql "Ok, The feed from 'bug report' to '#dev' was removed."
-          expect(room.robot.brain.data.phabricator.projects['bug report'].feeds)
-            .not.to.include '#dev'
+      context 'and project has no parent', ->
+        beforeEach ->
+          room.robot.brain.data.phabricator.projects = {
+            'bug report': {
+              phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
+              name: 'bug report',
+              feeds: [
+                '#dev'
+              ]
+            },
+            'project': { phid: 'PHID-PROJ-1234567' },
+          }
+          room.robot.brain.data.phabricator.aliases = {
+            bugs: 'bug report',
+            bug: 'bug report'
+          }
+          do nock.disableNetConnect
+
+        afterEach ->
+          room.robot.brain.data.phabricator = { }
+          nock.cleanAll()
+
+        context 'phad remove bug from #dev', ->
+          hubot 'phad remove bug from #dev'
+          it 'should say that the feed was removed', ->
+            expect(hubotResponse())
+              .to.eql "Ok, The feed from 'bug report' to '#dev' was removed."
+            expect(room.robot.brain.data.phabricator.projects['bug report'].feeds)
+              .not.to.include '#dev'
+
+      context 'and project has a parent', ->
+        beforeEach ->
+          room.robot.brain.data.phabricator.projects = {
+            'project/bug report': {
+              phid: 'PHID-PROJ-qhmexneudkt62wc7o3z4',
+              name: 'bug report',
+              parent: 'project',
+              feeds: [ '#dev' ]
+            },
+            'project': {
+              phid: 'PHID-PROJ-1234567',
+              name: 'project',
+              feeds: [ ]
+            },
+          }
+          room.robot.brain.data.phabricator.aliases = {
+            bugs: 'project/bug report',
+            bug: 'project/bug report'
+          }
+          do nock.disableNetConnect
+
+        afterEach ->
+          room.robot.brain.data.phabricator = { }
+          nock.cleanAll()
+
+        context 'phad remove bug from #dev', ->
+          hubot 'phad remove bug from #dev'
+          it 'should say that the feed was removed', ->
+            expect(hubotResponse())
+              .to.eql "Ok, The feed from 'project/bug report' to '#dev' was removed."
+            expect(room.robot.brain.data.phabricator.projects['project/bug report'].feeds)
+              .not.to.include '#dev'
+

     context 'but the feed do not already exists', ->
       beforeEach ->


Update the conf to work.

Update the conf to work.

--- config.coffee
+++ config.coffee
@@ -1,10 +1,14 @@
 exports.config =
+  sourceMaps: false
   paths:
     watched:['client']
   files:
     javascripts:
-      joinTo: 'app.js'
+      joinTo:
+        'javascripts/app.js': /^client(\/|\\)app/
+        'javascripts/vendor.js': /^(bower_components|(client(\/|\\)vendor))/
     stylesheets:
-      joinTo: 'app.css'
+      joinTo: 'stylesheets/app.css'
     templates:
-      joinTo: 'app.js'+      default: 'hbs'
+      joinTo: 'javascripts/app.js'


Add env var to put brunch in production mode.

--- config.coffee
+++ config.coffee
@@ -15,6 +15,7 @@

 exports.config =

+  production: process.env.BRUNCH_ENV is 'production'
   paths:
     public: 'public'
     watched: [


Update HumanReadableLogsRules.coffee

Update HumanReadableLogsRules.coffee

--- services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
+++ services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
@@ -156,7 +156,7 @@
 		regexToMatch: /LaTeX Error: There's no line here to end/
 		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_There%27s_no_line_here_to_end"
 		humanReadableHint: """
-			You have used a \\\ or \\newline command where LaTeX was not expecting one. Make sure that you only use line breaks after blocks of text, and be careful using linebreaks inside lists and other environments.
+			You have used a \\\\ or \\newline command where LaTeX was not expecting one. Make sure that you only use line breaks after blocks of text, and be careful using linebreaks inside lists and other environments.
 		"""
 	,
 		regexToMatch: /LaTeX Error: \\verb ended by end of line/
@@ -168,7 +168,7 @@
 		regexToMatch: /Illegal unit of measure (pt inserted)/
 		extraInfoURL: "https://www.sharelatex.com/learn/Errors%2FIllegal%20unit%20of%20measure%20(pt%20inserted)"
 		humanReadableHint: """
-			You have written a length, but have not specified the appropriate units (pt, mm, cm etc.). If you have not written a length, check that you have not witten a linebreak \\\ followed by square brackets [\u2026] anywhere.
+			You have written a length, but have not specified the appropriate units (pt, mm, cm etc.). If you have not written a length, check that you have not witten a linebreak \\\\ followed by square brackets [\u2026] anywhere.
 		"""
 	,
 		regexToMatch: /Extra \\right/


Draw without failing

Draw without failing

--- src/draw.coffee
+++ src/draw.coffee
@@ -19,10 +19,11 @@
   draw_world = (world) ->
     console.log "draw_world #{world}"
     paths = if world.has_children() then world.map_children(draw_world) else draw_path(world)
+    console.log paths
     SVG.g {
       class: ['draw_world', "#{world}"]
       transform: world.get('transform')
-    }, paths
+    }, _.flatten [paths]

   console.log world
   SVG.svg {
@@ -31,4 +32,6 @@
     class: "svg_grid #{world}"
     width: grid_size
     height: grid_size
-  }#, draw_world(world)
+  }, [
+    draw_world(world)
+  ]


alphabetize contributors list, fix #198

alphabetize contributors list, fix #198

--- src/node_modules/round-contributors/round-contributors-controller.coffee
+++ src/node_modules/round-contributors/round-contributors-controller.coffee
@@ -10,9 +10,13 @@
   $scope.round = round
   $scope.isAdmin = group.isAdmin(AuthService.getCurrentUser())

-  $scope.contributors = _.filter contributors, (contributor) ->
-    # non-admins shouldn't see contributors with no allocations
-    $scope.isAdmin or (!contributor.allocation.eq(0))
+  $scope.contributors = _(contributors)
+    .filter (contributor) ->
+      # non-admins shouldn't see contributors with no allocations
+      $scope.isAdmin or (!contributor.allocation.eq(0))
+    .sortBy (contributor) ->
+      contributor.user.name
+    .value()

   $scope.updateAllocation = (contributor, data) ->
     allocation = allocations[contributor.user.id]


Don't re-read the file on every change event

--- src/file.coffee
+++ src/file.coffee
@@ -428,34 +428,7 @@
         @emit 'moved' if Grim.includeDeprecatedAPIs
         @emitter.emit 'did-rename'
       when 'change', 'resurrect'
-        oldContents = @cachedContents
-        handleReadError = (error) =>
-          # We cant read the file, so we GTFO on the watch
-          @unsubscribeFromNativeChangeEvents()
-
-          handled = false
-          handle = -> handled = true
-          error.eventType = eventType
-          @emitter.emit('will-throw-watch-error', {error, handle})
-          unless handled
-            newError = new Error("Cannot read file after file `#{eventType}` event: #{@path}")
-            newError.originalError = error
-            newError.code = "ENOENT"
-            newError.path
-            # I want to throw the error here, but it stops the event loop or
-            # something. No longer do interval or timeout methods get run!
-            # throw newError
-            console.error newError
-
-        try
-          handleResolve = (newContents) =>
-            unless oldContents is newContents
-              @emit 'contents-changed' if Grim.includeDeprecatedAPIs
-              @emitter.emit 'did-change'
-
-          @read(true).then(handleResolve, handleReadError)
-        catch error
-          handleReadError(error)
+        @emitter.emit 'did-change'

   detectResurrectionAfterDelay: ->
     _.delay (=> @detectResurrection()), 50
@@ -464,7 +437,7 @@
     @exists().then (exists) =>
       if exists
         @subscribeToNativeChangeEvents()
-        @handleNativeChangeEvent('resurrect', @getPath())
+        @handleNativeChangeEvent('resurrect')
       else
         @cachedContents = null
         @emit 'removed' if Grim.includeDeprecatedAPIs


Simplifying the pot model tests

Simplifying the pot model tests

--- test/specs/models/pot.coffee
+++ test/specs/models/pot.coffee
@@ -2,37 +2,34 @@

 describe 'Pot', ->
   Pot = require('models/pot')
+  pot = null
+
+  beforeEach ->
+    pot = new Pot

   it 'should start with 500 credits by default', ->
-    pot = new Pot
     expect( pot.size ).toEqual 500

   it 'should be able to receive credit', ->
-    pot = new Pot
     pot.credit 500
     expect( pot.size ).toEqual 1000

   it 'should only accept positive credits', ->
-    pot = new Pot
     pot.credit -500
     expect( pot.size ).toEqual 500

   it 'should be able to accept debits', ->
-    pot = new Pot
     pot.debit 500
     expect( pot.size ).toEqual 0

   it 'should only accept positive debits', ->
-    pot = new Pot
     pot.debit -500
     expect( pot.size ).toEqual 500

   it 'should not accept debits larger than the pot size', ->
-    pot = new Pot
     pot.debit 1000
     expect( pot.size ).toEqual 500

   it "shouldn't allow a negative amount in the pot", ->
-    pot = new Pot
     pot.size = -500
     expect( pot.save() ).toEqual false