Fixed tests WIP.

Fixed tests WIP.

--- tests/batman/view/loop_rendering_test.coffee
+++ tests/batman/view/loop_rendering_test.coffee
@@ -1,7 +1,8 @@
 helpers = if typeof require is 'undefined' then window.viewHelpers else require './view_helper'
 getPs = (view) -> $('p', view.get('node')).map(-> @innerHTML).toArray()

-QUnit.module "Batman.View loop rendering",
+QUnit.module "Batman.View loop rendering"
+
 asyncTest 'it should allow simple loops', 1, ->
   source = '

'
   objects = new Batman.Set('foo', 'bar', 'baz')
@@ -20,14 +21,13 @@

 asyncTest 'it should allow loops over undefined values', 3, ->
   source = '

'
-  context = Batman()
   Batman.developer.suppress()
-  helpers.render source, context, (node, view) ->
+  helpers.render source, {}, (node, view) ->
     deepEqual getPs(view), []
-    context.set 'objects', new Batman.Set('foo', 'bar', 'baz')
+    view.set('objects', new Batman.Set('foo', 'bar', 'baz'))
     delay ->
       deepEqual getPs(view), ['foo', 'bar', 'baz']
-      context.unset 'objects'
+      view.unset('objects')
       delay ->
         deepEqual getPs(view), []
         Batman.developer.unsuppress()
@@ -53,16 +53,16 @@
 asyncTest 'it should atomically reorder DOM nodes when the set is reordered', ->
   source = '

'
   objects = new Batman.Set({id: 1, name: 'foo'}, {id: 2, name: 'bar'})
-  context = Batman({objects, currentSort: 'id'})
+  context = {objects, currentSort: 'id'}

   helpers.render source, context, (node, view) ->
     deepEqual getPs(view), ['foo', 'bar']
     # multiple reordering all at once should not end up with duplicate DOM nodes
-    context.set 'currentSort', 'name'
-    delay =>
-      context.set 'currentSort', 'id'
+    view.set('currentSort', 'name')
+    delay =>
+      view.set('currentSort', 'id')
       delay =>
-        context.set 'currentSort', 'name'
+        view.set('currentSort', 'name')
         delay =>
           deepEqual getPs(view), ['bar', 'foo']

@@ -84,26 +84,24 @@
     deepEqual getPs(view).sort(), ['foo', 'bar'].sort()
     QUnit.start()

-asyncTest 'the ready event should wait for all children to be rendered', ->
+asyncTest 'prevents superview ready until first batch of children are rendered', 3, ->
   source = '

'
   objects = new Batman.Set('foo', 'bar', 'baz')
+
   node = document.createElement 'div'
   node.innerHTML = source
-  view = new Batman.View
-    context: Batman({objects})
-    node: node
-  ok !view.event('ready').oneShotFired, 'make sure views render async'
-  view._renderer.on 'parsed', =>
-    ok !view.event('ready').oneShotFired, 'make sure parsed fires before rendered'
+
+  view = new Batman.View({objects, node})
+
+  view.event('ready').oneShot = true
+  ok !view.event('ready')._oneShotFired, 'make sure views render async'
+
   view.on 'ready', =>
-    tracking = {foo: false, bar: false, baz: false}
-    node = $(view.get('node')).children()
-    for i in [0...node.length]
-      tracking[node[i].innerHTML] = true
-      equal node[i].className,  'present'
-    for k in ['foo', 'bar', 'baz']
-      ok tracking[k], "Object #{k} was found in the source"
-    QUnit.start()
+    ok view.get('node').childNodes.length > 1
+    ok view.event('ready')._oneShotFired
+    QUnit.start()
+
+  view.initializeBindings()

 asyncTest 'it should continue to render nodes after the loop', 1, ->
   source = '

'
@@ -154,37 +152,35 @@

 asyncTest 'it should update the whole set of nodes if the collection changes', ->
   source = '

'
-  context = new Batman.Object
-    objects: new Batman.Set('foo', 'bar', 'baz')
+  context = {objects: new Batman.Set('foo', 'bar', 'baz')}
+
   Batman.developer.suppress()
   helpers.render source, false, context, (node, view) ->
     equal $('.present', node).length, 3
-    context.set('objects', new Batman.Set('qux', 'corge'))
+    view.set('objects', new Batman.Set('qux', 'corge'))
     delay =>
       equal $('.present', node).length, 2
-      context.set('objects', null)
+      view.set('objects', null)
       delay =>
         equal $('.present', node).length, 0
-        context.set('objects', new Batman.Set('mario'))
+        view.set('objects', new Batman.Set('mario'))
         delay 60, =>
           equal $('.present', node).length, 1
           Batman.developer.unsuppress()

 asyncTest 'it should not fail if the collection is cleared', ->
   source = '

'
-  context = new Batman.Object
-    objects: new Batman.Set('foo', 'bar', 'baz')
+  context = {objects: new Batman.Set('foo', 'bar', 'baz')}

   helpers.render source, false, context, (node, view) ->
     equal $('.present', node).length, 3
-    context.get('objects').clear()
+    view.get('objects').clear()
     delay =>
       equal $('.present', node).length, 0

 asyncTest 'it should not fail if the iterator is killed', 1, ->
   source = '

'
-  context = new Batman.Object
-    objects: new Batman.Set([0...100]...)
+  context = {objects: new Batman.Set([0...100]...)}

   oldIterator = Batman.DOM.IteratorBinding
   instance = false
@@ -206,10 +202,10 @@
 asyncTest 'previously observed collections shouldn\'t have any effect if they are replaced', ->
   source = '

'
   oldObjects = new Batman.Set('foo', 'bar', 'baz')
-  context = new Batman.Object(objects: oldObjects)
-
-  helpers.render source, false, context, (node, view) ->
-    context.set('objects', new Batman.Set('qux', 'corge'))
+  context = {objects: oldObjects}
+
+  helpers.render source, false, context, (node, view) ->
+    view.set('objects', new Batman.Set('qux', 'corge'))
     oldObjects.add('no effect')
     delay =>
       equal $('.present', node).length, 2
@@ -261,20 +257,20 @@

 asyncTest 'it should update as a hash has items added and removed', 8, ->
   source = '

'
-  context = new Batman.Object
+  context =
     playerScores: new Batman.Hash
       mario: 5

   helpers.render source, context, (node, view) ->
     equal $(':first', node).attr('id'), 'mario'
     equal $(':first', node).html(), '5'
-    context.playerScores.set 'link', 10
+    view.playerScores.set 'link', 10
     delay =>
       equal $(':first', node).attr('id'), 'mario'
       equal $(':first', node).html(), '5'
       equal $(':nth-child(2)', node).attr('id'), 'link'
       equal $(':nth-child(2)', node).html(), '10'
-      context.playerScores.unset 'mario'
+      view.playerScores.unset 'mario'
       delay =>
         equal $(':first', node).attr('id'), 'link'
         equal $(':first', node).html(), '10'
@@ -307,7 +303,7 @@
   oldDeferEvery = Batman.DOM.IteratorBinding::deferEvery
   Batman.DOM.IteratorBinding::deferEvery = 0.5
   x = new Batman.Set([0...500]...)
-  context = Batman({x})
+  context = {x}
   source = '''

'''
@@ -318,12 +314,12 @@

 asyncTest 'it shouldn\'t become desynchronized if the foreach collection observer fires with the same collection', ->
   x = new Batman.Set("a", "b", "c", "d", "e")
-  context = Batman({x})
+  context = {x}
   source = '

'
   helpers.render source, context, (node, view) ->
     deepEqual getPs(view), ['a', 'b', 'c', 'd', 'e']
-    context.observe 'x', spy = createSpy()
-    context.property('x').fire(x,x,'x')
+    view.observe 'x', spy = createSpy()
+    view.property('x').fire(x,x,'x')
     delay ->
       ok spy.called
       deepEqual getPs(view), ['a', 'b', 'c', 'd', 'e']
@@ -396,48 +392,45 @@

 asyncTest 'it should stop previous ongoing renders if items are removed', ->
   getSet = (seed) -> new Batman.Set(seed, seed+1, seed+2)
-  context = Batman
-    all: getSet(1)
+  context = {all: getSet(1)}

   source = '

'
   helpers.render source, false, context, (node, view) ->
     deepEqual getVals(node), [1,2,3]
-    context.get('all').add(4)
-    context.get('all').remove(4)
+    view.get('all').add(4)
+    view.get('all').remove(4)
     delay ->
       deepEqual getVals(node), [1,2,3]

 asyncTest 'it should stop previous ongoing renders if the collection is changed', ->
   getSet = (seed) -> new Batman.Set(seed, seed+1, seed+2)
-  context = Batman
-    all: getSet(1)
+  context = {all: getSet(1)}

   source = '

'
   helpers.render source, false, context, (node, view) ->
     deepEqual getVals(node), [1,2,3]
-    context.set('all', getSet(5))
-    context.set('all', getSet(10))
+    view.set('all', getSet(5))
+    view.set('all', getSet(10))
     delay ->
       deepEqual getVals(node), [10,11,12]

 asyncTest 'it should stop previous ongoing renders if collection changes, but intersects', ->
   getSet = (seed) -> new Batman.Set(seed, seed+1, seed+2)
-  context = Batman
-    all: getSet(1)
+  context = {all: getSet(1)}

   source = '

'
   helpers.render source, false, context, (node, view) ->
     deepEqual getVals(node), [1,2,3]
-    context.set('all', getSet(2))
+    view.set('all', getSet(2))
     delay ->
       deepEqual getVals(node), [2,3,4]
-      context.set('all', getSet(3))
-      context.set('all', getSet(4))
+      view.set('all', getSet(3))
+      view.set('all', getSet(4))
       delay ->
         deepEqual getVals(node), [4,5,6]

 asyncTest 'it shouldn\'t become desynchronized if the collection originates from a partial', ->
-  context = Batman
+  context =
     parent: Batman
       children: new Batman.Set("a", "b", "c", "d", "e")

@@ -454,16 +447,16 @@
     deepEqual getPs(view), ['a', 'b', 'c', 'd', 'e']
     delay ->
       deepEqual getPs(view), ['a', 'b', 'c', 'd', 'e']
-      context.get('parent.children').remove('b')
+      view.get('parent.children').remove('b')
       delay ->
         deepEqual getPs(view), ['a', 'c', 'd', 'e']
         for k in ['c', 'e']
-          context.get('parent.children').remove(k)
+          view.get('parent.children').remove(k)
         delay ->
           deepEqual getPs(view), ['a','d']

 asyncTest 'it should propagate notifications of inner binding creation to bindings outside the loop', ->
-  context = Batman
+  context =
     children: new Batman.Set("a", "b", "c", "d", "e")

   spy = createSpy()
@@ -483,7 +476,7 @@
   helpers.render source, context, (node, view) ->
     delay ->
       ok spy.callCount, 5
-      context.children.add 'f'
+      view.children.add 'f'
       delay ->
         ok spy.callCount, 6

@@ -494,10 +487,8 @@
   Batman.DOM.AbstractBinding::die = ->
     dieVals.push [@key, @node.innerHTML] if @key is 'item.id'

-  context = new Batman
-  helpers.render source, context
-  , (node) ->
-    context.set 'items', new Batman.Set({id: 4}, {id: 5}, {id: 6})
-    context.set 'items', new Batman.Set({id: 7}, {id: 8}, {id: 9})
+  helpers.render source, {}, (node, view) ->
+    view.set 'items', new Batman.Set({id: 4}, {id: 5}, {id: 6})
+    view.set 'items', new Batman.Set({id: 7}, {id: 8}, {id: 9})
     delay ->
       deepEqual dieVals, [['item.id', '4'], ['item.id', '5'], ['item.id', '6']]


doc: mention apm help <command>

doc: mention apm help <command>

--- src/apm-cli.coffee
+++ src/apm-cli.coffee
@@ -31,6 +31,8 @@
     where <command> is one of:
         available, develop, help, install, link, links, list,
         publish, rebuild, uninstall, unlink
+
+    Run apm help <command> to see the more details about a specific command.
   """
   options.alias('v', 'version').describe('version', 'Print the apm version')
   options.alias('h', 'help').describe('help', 'Print this usage message')


Fix tests expectation

Fix tests expectation

--- spec/ToFullscale.coffee
+++ spec/ToFullscale.coffee
@@ -63,7 +63,7 @@
     describe 'with WordPress.com images', ->
       it 'should return correct URL for non-sized', (done) ->
         newUrl.on 'data', (image) ->
-          chai.expect(image).to.equal 'https://tctechcrunch2011.files.wordpress.com/2013/07/henri-bergius3.jpg'
+          chai.expect(image).to.equal 'http://tctechcrunch2011.files.wordpress.com/2013/07/henri-bergius3.jpg'
           done()
         url.send 'http://tctechcrunch2011.files.wordpress.com/2013/07/henri-bergius3.jpg'
       it 'should return correct URL for thumbnails', (done) ->
@@ -89,7 +89,7 @@
     describe 'with Wikimedia Commons thumbnails', ->
       it 'should return correct URL for non-sized', (done) ->
         newUrl.on 'data', (image) ->
-          chai.expect(image).to.equal 'https://upload.wikimedia.org/wikipedia/commons/7/7a/India_-_Varanasi_green_peas_-_2714.jpg'
+          chai.expect(image).to.equal 'http://upload.wikimedia.org/wikipedia/commons/7/7a/India_-_Varanasi_green_peas_-_2714.jpg'
           done()
         url.send 'http://upload.wikimedia.org/wikipedia/commons/7/7a/India_-_Varanasi_green_peas_-_2714.jpg'
       it 'should return correct URL for thumbnails', (done) ->


disable loading indicator when opened modal

--- js-library/app/js/jail_iframe/classes/fact.coffee
+++ js-library/app/js/jail_iframe/classes/fact.coffee
@@ -28,6 +28,7 @@
     FactlinkJailRoot.openFactlinkModal @id

   _onModalOpened: ->
+    @show_button.stopLoading()
     FactlinkJailRoot.off 'modalOpened', @_onModalOpened, @

   destroy: ->


Use available contract if only one in ADType#undress

--- src/type/ad_type.coffee
+++ src/type/ad_type.coffee
@@ -68,10 +68,16 @@

     infotype  = null
     undresser = null
-    $u.find @contracts, (contract, name) ->
-      [infotype, dresser, undresser] = contract
-      infotype.isSuperTypeOf(as)

+    # locate what contract to use
+    if $u.size(@contracts)==1
+      [infotype, dresser, undresser] = $u.values(@contracts)[0]
+    else
+      $u.find @contracts, (contract, name) ->
+        [infotype, dresser, undresser] = contract
+        infotype.isSuperTypeOf(as)
+
+    # undress if found
     if undresser?
       infotype.undress(undresser(value), as)
     else


generic popover-list class

generic popover-list class

--- src/packages/autocomplete/src/autocomplete-view.coffee
+++ src/packages/autocomplete/src/autocomplete-view.coffee
@@ -8,7 +8,7 @@
     rootView.eachEditor (editor) ->
       new AutocompleteView(editor) if editor.attached and not editor.mini

-  @viewClass: -> "autocomplete #{super}"
+  @viewClass: -> "autocomplete #{super} popover-list"

   editor: null
   currentBuffer: null


Removing extra whitespace

Removing extra whitespace

--- test/unit/util.spec.coffee
+++ test/unit/util.spec.coffee
@@ -140,8 +140,8 @@

   describe "extendAttributes", ->
     defaults =
-      width:   10
-      height:  20
+      width:  10
+      height: 20

     attrs =
       width: 50


Clear menu template after each spec run

Clear menu template after each spec run

This is needed since packages don't destroy menus when deactivated
so the template currently grows larger each time a package is activated.

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -111,6 +111,7 @@
   keymap.bindingSets = bindingSetsToRestore
   keymap.bindingSetsByFirstKeystroke = bindingSetsByFirstKeystrokeToRestore
   atom.deactivatePackages()
+  atom.menu.template = []

   window.rootView?.remove?()
   atom.rootView?.remove?() if atom.rootView isnt window.rootView


Added some unit tests

Added some unit tests

--- specs/txiki.spec.coffee
+++ specs/txiki.spec.coffee
@@ -1,19 +1,38 @@
 describe "txiki unit test", ->
-
+  'use strict'

   it "should extend an object", ->
     hello =
       init: ->
         return 'Hello'
-
     world =
       name: ->
         return 'Tierra'

-    TK.extend(hello, world)
-
+    u.extend(hello, world)
     expect(hello.name()).toEqual('Tierra')

+  it "should subscribe to an event", ->
+    hello = (e) ->
+      return e
+    u.subscribe 'sayHello', hello
+
+  it "shoud publish to a subscribe event", ->
+    res = u.publish 'sayHello', 'Hello David'
+    expect(res[0]).toEqual('Hello David')

   it "should return a new instance", ->
-    expect(TK('selector') instanceof TK).toBe(true)+    expect(u('selector') instanceof u).toBe(true)
+
+  it "should response to a GET request", (done) ->
+    spyOn(u, 'ajax').and.callFake (options) ->
+      options.success 'Ajax OK'
+
+    opts =
+      type: 'GET'
+      url: '/api'
+      success: (res) ->
+        expect(res).toEqual 'Ajax OK'
+        done()
+
+    u.ajax opts


FIX: Update tutorial enrolment success to use returned list

--- src/app/common/services/unit-service.coffee
+++ src/app/common/services/unit-service.coffee
@@ -416,17 +416,6 @@
     student.isEnrolledIn = (tutorialId) ->
       _.find(student.tutorial_enrolments, (enrolment) -> enrolment.tutorial_id == tutorialId)?

-    # Update the student's list of tutorial enrolments based on them enrolling in a new tutorial.
-    # The passed in enrolment data indicates the new tutorial they are enrolled in and its steam
-    # enrolled is pass true if they enrolled in this tutorial, otherwise it is false
-    student.updateTutorialEnrolments = (enrolment, enrolled) ->
-      _.each student.tutorial_enrolments, (value) ->
-        if !enrolment.stream || value.stream_abbr == enrolment.stream_abbr
-          if enrolled
-            value.tutorial_id = enrolment.tutorial_id
-          else
-            value.tutorial_id = undefined
-
     # Updat student enrolment within a unni
     student.updateUnitEnrolment = () ->
       newEnrollment = !student.enrolled
@@ -448,10 +437,8 @@
       # analyticsService.event 'Teacher View - Students Tab', 'Changed Student Tutorial'
       if student.isEnrolledIn(newId)
         fn = Project.tutorialEnrolment.delete
-        enrol = false
       else
         fn = Project.tutorialEnrolment.create
-        enrol = true

       fn(
         {
@@ -459,9 +446,9 @@
           project_id:             student.project_id
           tutorial_abbreviation:  tutorial.abbreviation,
         },
-        (enrolment) ->
+        (successResponse) ->
+          student.tutorial_enrolments = successResponse.enrolments
           alertService.add "success", "Tutorial enrolment updated for #{student.name}", 3000
-          student.updateTutorialEnrolments(enrolment, enrol)
         (response) ->
           alertService.add "danger", "Failed to update tutorial enrolment. #{response?.data?.error}", 8000
       )


remove logging

remove logging

--- index.coffee
+++ index.coffee
@@ -54,13 +54,10 @@
       increaseIndentRegex = @increaseIndentRegexForScopeDescriptor(scopeDescriptor)
       precedingRow = @buffer.previousNonBlankRow(bufferRow)

-
       return indent if precedingRow < 0

       precedingLine = @buffer.lineForRow(precedingRow)

-      #indent += 1 if increaseIndentRegex.testSync(precedingLine);
-      console.log(precedingLine,indent)
       indent -= 1 if precedingLine and decreaseNextLineIndentRegex.testSync(precedingLine)

       return Math.max(indent, 0)


Bindings related code has been moved to separated repo

--- index.coffee
+++ index.coffee
@@ -1,22 +1,16 @@
 'use strict'

 parser = require './parser'
+bindingParser = require 'neft-binding/parser'

 {utils, Renderer, assert} = Neft

 ATTRIBUTE = 'attribute'

 # options
-i = 1
-BINDING_THIS_TO_TARGET_OPTS = 1
+{BINDING_THIS_TO_TARGET_OPTS} = bindingParser

 ids = idsKeys = itemsKeys = extensions = queries = null
-
-repeatString = (str, amount) ->
-	r = str
-	for i in [0...amount-1] by 1
-		r += str
-	r

 isAnchor = (obj) ->
 	assert obj.type is ATTRIBUTE, "isAnchor: type must be an attribute"
@@ -26,12 +20,7 @@
 isBinding = (obj) ->
 	assert obj.type is ATTRIBUTE, "isBinding: type must be an attribute"

-	try
-		func = new Function 'console', "'use strict'; return #{obj.value};"
-		func.call null
-		return false
-
-	true
+	bindingParser.isBinding obj.value

 getByTypeDeep = (elem, type, callback) ->
 	if elem.type is type
@@ -92,97 +81,15 @@
 	else
 		r

+isPublicBindingId = (id) ->
+	id is 'this' or id is 'app' or id is 'view' or ids.hasOwnProperty(id) or id of Renderer
+
 bindingAttributeToString = (obj) ->
-	binding = ['']
-	val = obj.value
-	opts = obj._parserOptions or 0
-
-	# split to types
-	val += ' '
-	lastBinding = null
-	isString = false
-	for char, i in val
-		if char is '.' and lastBinding
-			lastBinding.push ''
-			continue
-
-		if lastBinding and (isString or ///[a-zA-Z_0-9$]///.test(char))
-			lastBinding[lastBinding.length - 1] += char
-		else if ///[a-zA-Z_$]///.test(char)
-			lastBinding = [char]
-			binding.push lastBinding
-		else
-			if lastBinding is null
-				binding[binding.length - 1] += char
-			else
-				lastBinding = null
-				binding.push char
-
-		if /'|"/.test(char) and val[i-1] isnt '\\'
-			isString = not isString
-
-	# filter by ids
-	for elem, i in binding when typeof elem isnt 'string'
-		[id] = elem
-		if id is 'Renderer'
-			elem.shift()
-			[id] = elem
-		if id is 'parent' or id is 'nextSibling' or id is 'previousSibling' or id is 'target'
-			elem.unshift "this"
-		else if opts & BINDING_THIS_TO_TARGET_OPTS and id is 'this'
-			elem.splice 1, 0, 'target'
-		else if (id is 'this' or id is 'app' or id is 'view' or ids.hasOwnProperty(id) or id of Renderer) and (i is 0 or binding[i-1][binding[i-1].length - 1] isnt '.')
-			continue
-		else
-			binding[i] = elem.join '.'
-
-	# split texts
-	i = -1
-	n = binding.length
-	while ++i < n
-		if typeof binding[i] is 'string'
-			if typeof binding[i-1] is 'string'
-				binding[i-1] += binding[i]
-
-			else if binding[i].trim() isnt ''
-				continue
-
-			binding.splice i, 1
-			n--
-
-	# split
-	text = ''
-	hash = ''
-	for elem, i in binding
-		if typeof elem is 'string'
-			hash += elem
-		else if elem.length > 1
-			if binding[i-1]? and text
-				text += ", "
-
-			text += repeatString('[', elem.length-1)
-			text += "'#{elem[0]}'"
-			if elem[0] is "this"
-				hash += "this"
-			else
-				hash += "#{elem[0]}"
-			elem.shift()
-			for id, i in elem
-				text += ", '#{id}']"
-				hash += ".#{id}"
-		else
-			if elem[0] is "this"
-				hash += "this"
-			else
-				hash += "#{elem[0]}"
-
-	hash = hash.trim()
-	text = text.trim()
-
+	binding = bindingParser.parse obj.value, isPublicBindingId, obj._parserOptions
 	args = idsKeys+''
-	func = "function(#{args}){return #{hash}}"
-
-	"[#{func}, [#{text}]]"
+	func = "function(#{args}){return #{binding.hash}}"
+
+	"[#{func}, [#{binding.connections}]]"

 stringify =
 	function: (elem) ->


Conditions are tested over ssh

Conditions are tested over ssh

--- test/conditions.coffee
+++ test/conditions.coffee
@@ -2,44 +2,50 @@
 fs = require 'fs'
 http = require 'http'
 should = require 'should'
+they = require 'superexec/lib/they'
 conditions = if process.env.MECANO_COV then require '../lib-cov/conditions' else require '../lib/conditions'

 describe 'conditions', ->

   describe 'if', ->

-    it 'should bypass if not present', (next) ->
+    they 'should bypass if not present', (ssh, next) ->
       conditions.if(
-        {}
+        ssh: ssh
         () -> should.be.ok false
         next
       )
-    it 'should succeed if `true`', (next) ->
+    they 'should succeed if `true`', (ssh, next) ->
       conditions.if(
+        ssh: ssh
         if: true
         () -> should.be.ok false
         next
       )
-    it 'should failed if `false`', (next) ->
+    they 'should failed if `false`', (ssh, next) ->
       conditions.if(
+        ssh: ssh
         if: false
         next
         () -> should.be.ok false
       )
-    it 'should succeed on `succeed` callback', (next) ->
+    they 'should succeed on `succeed` callback', (ssh, next) ->
       conditions.if(
+        ssh: ssh
         if: (options, failed, succeed) -> succeed()
         () -> should.be.ok false
         next
       )
-    it 'should failed on `failed` callback', (next) ->
+    they 'should failed on `failed` callback', (ssh, next) ->
       conditions.if(
+        ssh: ssh
         if: (options, failed, succeed) -> failed()
         next
         () -> should.be.ok false
       )
-    it 'should pass error object on `failed` callback', (next) ->
+    they 'should pass error object on `failed` callback', (ssh, next) ->
       conditions.if(
+        ssh: ssh
         if: (options, failed, succeed) -> failed new Error 'cool'
         (err) -> err.message is 'cool' and next()
         () -> should.be.ok false
@@ -47,32 +53,36 @@

   describe 'if_exists', ->

-    it 'should pass if not present', (next) ->
+    they 'should pass if not present', (ssh, next) ->
       conditions.if_exists(
-        {}
+        ssh: ssh
         () -> should.be.ok false
         next
       )
-    it 'should succeed if dir exists', (next) ->
+    they 'should succeed if dir exists', (ssh, next) ->
       conditions.if_exists(
+        ssh: ssh
         if_exists: __dirname
         () -> should.be.ok false
         -> next()
       )
-    it 'should skip if file does not exists', (next) ->
+    they 'should skip if file does not exists', (ssh, next) ->
       conditions.if_exists(
+        ssh: ssh
         if_exists: './oh_no'
         next
         () -> should.be.ok false
       )
-    it 'should succeed if all files exists', (next) ->
+    they 'should succeed if all files exists', (ssh, next) ->
       conditions.if_exists(
+        ssh: ssh
         if_exists: [__dirname, __filename]
         () -> should.be.ok false
         -> next()
       )
-    it 'should skip if at least one file exists', (next) ->
+    they 'should skip if at least one file exists', (ssh, next) ->
       conditions.if_exists(
+        ssh: ssh
         if_exists: [__dirname, './oh_no']
         next
         () -> should.be.ok false
@@ -80,54 +90,61 @@

   describe 'not_if_exists', ->

-    it 'should succeed if not present', (next) ->
+    they 'should succeed if not present', (ssh, next) ->
       conditions.not_if_exists(
-        {}
+        ssh: ssh
         () -> should.be.ok false
         next
       )
-    it 'should skip if dir exists', (next) ->
+    they 'should skip if dir exists', (ssh, next) ->
       conditions.not_if_exists(
+        ssh: ssh
         not_if_exists: __dirname
         next
         () -> should.be.ok false
       )
-    it 'should succeed if dir does not exists', (next) ->
+    they 'should succeed if dir does not exists', (ssh, next) ->
       conditions.not_if_exists(
+        ssh: ssh
         not_if_exists: './oh_no'
         () -> should.be.ok false
         -> next()
       )
-    it 'should succeed if no file exists', (next) ->
+    they 'should succeed if no file exists', (ssh, next) ->
       conditions.not_if_exists(
+        ssh: ssh
         not_if_exists: ['./oh_no', './eh_no']
         () -> should.be.ok false
         -> next()
       )
-    it 'should skip if at least one file exists', (next) ->
+    they 'should skip if at least one file exists', (ssh, next) ->
       conditions.not_if_exists(
+        ssh: ssh
         not_if_exists: ['./oh_no', __filename]
         next
         () -> should.be.ok false
       )

   describe 'should_exist', ->
-    it 'should succeed if file exists', (next) ->
+    they 'should succeed if file exists', (ssh, next) ->
       conditions.should_exist(
+        ssh: ssh
         should_exist: __filename
         () -> should.be.ok false
         -> next()
       )
-    it 'should failed if file does not exist', (next) ->
+    they 'should failed if file does not exist', (ssh, next) ->
       conditions.should_exist(
+        ssh: ssh
         should_exist: './oh_no'
         (err) ->
           err.should.be.a 'object'
           next()
         () -> should.be.ok false
       )
-    it 'should failed if at least one file does not exist', (next) ->
+    they 'should failed if at least one file does not exist', (ssh, next) ->
       conditions.should_exist(
+        ssh: ssh
         should_exist: ['./oh_no', __filename]
         (err) ->
           err.should.be.a 'object'
@@ -136,22 +153,25 @@
       )

   describe 'should_not_exist', ->
-    it 'should succeed if file doesnt exist', (next) ->
+    they 'should succeed if file doesnt exist', (ssh, next) ->
       conditions.should_not_exist(
+        ssh: ssh
         should_not_exist: './oh_no'
         () -> should.be.ok false
         next
       )
-    it 'should failed if file exists', (next) ->
+    they 'should failed if file exists', (ssh, next) ->
       conditions.should_not_exist(
+        ssh: ssh
         should_not_exist: __filename
         (err) ->
           err.should.be.a 'object'
           next()
         () -> should.be.ok false
       )
-    it 'should failed if at least one file exists', (next) ->
+    they 'should failed if at least one file exists', (ssh, next) ->
       conditions.should_not_exist(
+        ssh: ssh
         should_not_exist: ['./oh_no', __filename]
         (err) ->
           err.should.be.a 'object'


Loop through the callbacks backwards

Loop through the callbacks backwards

This was necessary because if a listener was removed in the course of iterating through this list, the i would advance, skipping a callback.  Plus, if the removed item was the last in the list, it would create an undefined error because the array was too short for the i

--- index.coffee
+++ index.coffee
@@ -73,7 +73,6 @@
 	e - Event - native event object
 	###
 	fire: (e) =>
-		events = @handlers[e.type]
-		event(e) for event in events.callbacks
+		_.eachRight @handlers[e.type].callbacks, (cb) -> cb(e); true

 module.exports = new WindowEventMediator()


Remove unneeded div

Remove unneeded div

--- src/components/scores/concept-coach-cell.cjsx
+++ src/components/scores/concept-coach-cell.cjsx
@@ -10,14 +10,12 @@

   render: ->

-

-        <Router.Link className="score" to='viewTaskStep'
-          data-assignment-type="#{@props.task.type}"
-          params={courseId: @props.courseId, id: @props.task.id, stepIndex: 1}
-        >
-          {@props.task.correct_exercise_count} of {@props.task.exercise_count}
-        </Router.Link>
-

+      <Router.Link className="score" to='viewTaskStep'
+        data-assignment-type="#{@props.task.type}"
+        params={courseId: @props.courseId, id: @props.task.id, stepIndex: 1}
+      >
+        {@props.task.correct_exercise_count} of {@props.task.exercise_count}
+      </Router.Link>

         <Time format='MMM. D' date={@props.task.last_worked_at} />


escape html method, printing escaped text, printing escaped code

--- src/browser.coffee
+++ src/browser.coffee
@@ -1,5 +1,22 @@
-module.exports.print = (value) ->
+module.exports.escapeHtml = (html) ->
+	map =
+    '&': '&',
+    '<': '<',
+    '>': '>',
+    '"': '"',
+    "'": '''
+  html.replace /[&<>"']/g, (m) ->
+  		map[m]
+
+module.exports.print = (value, escaped = true) ->
 	div = document.createElement 'div'
+	value = module.exports.escapeHtml value if escaped
+	div.innerHTML = value
+	document.body.appendChild div
+
+module.exports.code = (value, escaped = true) ->
+	div = document.createElement 'pre'
+	value = module.exports.escapeHtml value if escaped
 	div.innerHTML = value
 	document.body.appendChild div


rm parent_done ref after used

rm parent_done ref after used

--- src/browser.coffee
+++ src/browser.coffee
@@ -197,6 +197,7 @@
                 el._browser.insert.call?()
         else
             @animation.push(el._browser.insert.call)
+        el._browser.parent_done = yes

     replace_callback: (oldtag, newtag) ->
         @fn.replace(oldtag, newtag)


[Supported Browsers] Move safari 10 to unsupported

--- src/lib/middleware/unsupported_browser.coffee
+++ src/lib/middleware/unsupported_browser.coffee
@@ -5,7 +5,7 @@
 uaParser = require 'ua-parser'

 isUnsupported = (ua, req) ->
-  not req.cookies.continue_with_bad_browser and ((ua.family is 'IE' and ua.major <=11) or (ua.family is 'Safari' and ua.major < 10))
+  not req.cookies.continue_with_bad_browser and ((ua.family is 'IE' and ua.major <=11) or (ua.family is 'Safari' and ua.major <= 10))

 getRedirectTo = (req) ->
   req.body['redirect-to'] or req.query['redirect-to'] or req.query['redirect_uri'] or parse(req.get('Referrer') or '').path or '/'


continue evaluation

continue evaluation

--- spec/operator-transform-string-spec.coffee
+++ spec/operator-transform-string-spec.coffee
@@ -120,8 +120,11 @@
       describe "when followed by a >", ->
         it "indents the current line", ->
           ensure '> >',
-            text: "12345\nabcde\n  ABCDE"
-            cursor: [2, 2]
+            textC_: """
+            12345
+            abcde
+            __|ABCDE
+            """

     describe "on the first line", ->
       beforeEach ->
@@ -130,8 +133,11 @@
       describe "when followed by a >", ->
         it "indents the current line", ->
           ensure '> >',
-            text: "  12345\nabcde\nABCDE"
-            cursor: [0, 2]
+            textC_: """
+            __|12345
+            abcde
+            ABCDE
+            """

       describe "when followed by a repeating >", ->
         beforeEach ->
@@ -160,7 +166,7 @@
       it "allows repeating the operation", ->
         ensure '.', text: "    12345\nabcde\nABCDE"

-    describe "in visual mode and stayOnTransformString enabled", ->
+    fdescribe "in visual mode and stayOnTransformString enabled", ->
       beforeEach ->
         settings.set('stayOnTransformString', true)
         set cursor: [0, 0]
@@ -168,44 +174,39 @@
       it "indents the currrent selection and exits visual mode", ->
         ensure 'v j >',
           mode: 'normal'
-          cursor: [1, 2]
-          text: """
+          textC: """
             12345
-            abcde
+            |abcde
           ABCDE
           """
       it "when repeated, operate on same range when cursor was not moved", ->
         ensure 'v j >',
           mode: 'normal'
-          cursor: [1, 2]
-          text: """
+          textC: """
             12345
-            abcde
+            |abcde
           ABCDE
           """
         ensure '.',
           mode: 'normal'
-          cursor: [1, 4]
-          text: """
+          textC: """
               12345
-              abcde
+              |abcde
           ABCDE
           """
       it "when repeated, operate on relative range from cursor position with same extent when cursor was moved", ->
         ensure 'v j >',
           mode: 'normal'
-          cursor: [1, 2]
-          text: """
+          textC: """
             12345
-            abcde
+            |abcde
           ABCDE
           """
         ensure 'l .',
           mode: 'normal'
-          cursor: [1, 5]
-          text_: """
+          textC_: """
           __12345
-          ____abcde
+          ____a|bcde
           __ABCDE
           """


tweak probabilities for lm

tweak probabilities for lm

--- test/types/json.coffee
+++ test/types/json.coffee
@@ -85,7 +85,7 @@
 			key = p
 		operand = parent[key]

-		if randomReal() < 0.2 and parent != container and Array.isArray(parent)
+		if randomReal() < 0.4 and parent != container and Array.isArray(parent)
 			# List move
 			newIndex = randomInt parent.length

@@ -156,7 +156,6 @@
 				{p:path, ld:clone(obj)}
 		else
 			# Object
-
 			k = randomKey(operand)

 			if randomReal() > 0.5 or not k?


Added test for argsToObject.

Added test for argsToObject.

--- test/utils-test.coffee
+++ test/utils-test.coffee
@@ -7,6 +7,13 @@
 utils = require '../src/scripts/utils.coffee'

 describe 'utils module', ->
+	describe 'argsToObject', ->
+		it 'returns a function that when given args it returns an object with those args mapped to provided property keys', ->
+			test =
+				foo: 'bar'
+				baz: 'quux'
+			expect(utils.argsToObject('foo', 'baz')('bar', 'quux')).toEqual test
+
 	describe 'array', ->
 		it 'converts non-arrays to array', ->
 			test = [ 'item1', 'item2' ]


Programmatic controls

Programmatic controls

--- src/control.coffee
+++ src/control.coffee
@@ -26,7 +26,7 @@
       draw_caller('go', 0)
       draw_caller('turn', -1)
     ]
-    T.div {class: "instructions declarative"}, [
+    T.div {class: "instructions declarative"}, world.bind -> _.flatten [
       world.get('controls').get('children').map draw_buttons
     ]
     T.p "More coming soon..."


Renaming

Renaming

--- src/control.coffee
+++ src/control.coffee
@@ -200,15 +200,15 @@
             if @selectedPosition.isEmpty()
                 return

-            unitPosition = @selectedPosition.value()
-            unitPositionLat = unitPosition.get('location').coordinates[1]
-            unitPositionLon = unitPosition.get('location').coordinates[0]
-            # unitPositionLatitudeLongitudeBounds = @map.getBounds()
+            position = @selectedPosition.value()
+            positionLat = position.get('location').coordinates[1]
+            positionLon = position.get('location').coordinates[0]
+            # positionLatitudeLongitudeBounds = @map.getBounds()
             # todo getBounds
-            minLat = unitPositionLat - .01
-            minLon = unitPositionLon - .01
-            maxLat = unitPositionLat + .01
-            maxLon = unitPositionLon + .01
+            minLat = positionLat - .01
+            minLon = positionLon - .01
+            maxLat = positionLat + .01
+            maxLon = positionLon + .01

             data = {minLat, minLon, maxLat, maxLon}


getting desperate

getting desperate

--- src/scripts/githooks/coreos_restart/status.coffee
+++ src/scripts/githooks/coreos_restart/status.coffee
@@ -36,18 +36,23 @@

 # Monitor the services as they spin-up.
 monitor = async (context, services) ->
+  console.log 1
   {cluster} = context
+  console.log 2
   #while true
-  # Read the status of all services.
+  # Read the status of all services
   status = yield get_status cluster, services
+  console.log 3
   console.log status
   # Check for success. All services must be "active" to pass, but a single
   # failure ruins the whole thing.
+  console.log 4
   is_active = (x) -> x == "active"
   is_failed = (x) -> x == "failed"
+  console.log 5
   success = collect map is_active, status
   failure = collect map is_failed, status
-
+  console.log 6, success, failure
   if true in failure
     return false        # Failure detected. All is lost, haha.
   else if false !in success


repaired return error

repaired return error

--- src/scripts/githooks/coreos_restart/status.coffee
+++ src/scripts/githooks/coreos_restart/status.coffee
@@ -29,7 +29,7 @@
     line = line.split "\t"
     status[line[0][..-9]] = line[2]

-
+  return status

 # Monitor the services as they spin-up.
 monitor = async (context, services) ->
@@ -46,7 +46,7 @@
     for service of services
        pass = false if status[service] != "active"

-    # Return success or wait another 10 seconds.
+    # Return success or wait another 10 seconds.
     if pass then return true else yield sleep 10000

 module.exports = {monitor}


Switch map tile providers

Switch map tile providers

--- src/scripts/lib/zooniverse/controllers/Map.coffee
+++ src/scripts/lib/zooniverse/controllers/Map.coffee
@@ -24,7 +24,7 @@
     tilesId: 998

     # Pick one from http://leaflet-extras.github.io/leaflet-providers/preview/index.html
-    tilesProvider: 'MapQuestOpen.Aerial'
+    tilesProvider: 'Esri.WorldImagery'

     map: null


[AuthModal] Remove unused option.

[AuthModal] Remove unused option.

https://github.com/artsy/force/pull/153/files#diff-dd71c83f0984aa16d35337c4a60e59faL35

--- src/desktop/components/auth_modal/view.coffee
+++ src/desktop/components/auth_modal/view.coffee
@@ -34,7 +34,7 @@

   initialize: (options) ->
     return if isEigen.checkWith options
-    { @destination, @successCallback, @afterSignUpAction } = options
+    { @destination, @afterSignUpAction } = options
     @redirectTo = encodeURIComponent(sanitizeRedirect(options.redirectTo)) if options.redirectTo

     @preInitialize options


Add confirmation endpoint

Add confirmation endpoint

--- src/api/settings.coffee
+++ src/api/settings.coffee
@@ -27,7 +27,7 @@
     'task.*.send.fetchByModule':
       url: 'api/cc/tasks/{collectionUUID}/{moduleUUID}'
       method: 'GET'
-      baseUrl: process?.env?.BASE_URL
+      # baseUrl: process?.env?.BASE_URL
       completedEvent: 'task.{collectionUUID}/{moduleUUID}.receive.fetchByModule'

     'user.send.statusUpdate':
@@ -42,8 +42,15 @@
       method: 'POST'
       useCredentials: true
       baseUrl: process?.env?.BASE_URL
-      failedEvent: 'course.*.registration.failure'
-      completedEvent: 'course.*.registration.complete'
+      failedEvent: 'course.{book_uuid}.receive.registration.failure'
+      completedEvent: 'course.{book_uuid}.receive.registration.complete'

+    'course.*.send.confirmation':
+      url: 'api/enrollment_changes/{id}/approve'
+      method: 'PUT'
+      useCredentials: true
+      baseUrl: process?.env?.BASE_URL
+      failedEvent: 'course.{id}.receive.confirmation.failure'
+      completedEvent: 'course.{id}.receive.confirmation.complete'

 module.exports = settings


ELEm.setPosition works properly now

ELEm.setPosition works properly now

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -291,7 +291,7 @@
     unless y?
       [ x, y ] = x
     @setStyle( _id, 'left', x+'px' )
-    @setStyle( _id, 'top', x+'px' )
+    @setStyle( _id, 'top', y+'px' )

   ###
   Shortcut for filling parent dimensions with optional offset(s)


Cleanup click handler

Cleanup click handler

--- src/scripts/helpers/handlers/popover.coffee
+++ src/scripts/helpers/handlers/popover.coffee
@@ -27,7 +27,8 @@
       @constructor.popovers.push(@$owner)

       # Stop propogation of 'click' events so popover doesn't get auto-closed
-      @$owner.click (e) => e.stopPropagation()
+      @_stopPropagation = (e) -> e.stopPropagation()
+      @$owner.click(@_stopPropagation)

       # Attach event handler to close open popovers on show
       @$owner.on 'show.bs.popover', (e) =>
@@ -61,4 +62,5 @@
     close: () ->
       @constructor.removePopover(@$owner)
       @$owner.popover('destroy')
+      @$owner.off('click', @_stopPropagation)
       super()


Support SequenceExpression.

Support SequenceExpression.

--- js2py.coffee
+++ js2py.coffee
@@ -423,6 +423,8 @@
 generate = (c) ->
   p = new LinePrinter
   isElse = false
+
+  SIDE_EFFECT_FREE = ['Literal', 'Identifier', 'AssignmentExpression']

   maybeParens = (expr) ->
     s = walk expr
@@ -490,8 +492,14 @@
       when 'ExpressionStatement'
         ex = walk c.expression
         if c.expression and \
-           c.expression.type not in ['Literal', 'Identifier', 'AssignmentExpression']
-          p.addLine "#{ex}"
+           c.expression.type not in SIDE_EFFECT_FREE
+          p.addLine ex
+      when 'SequenceExpression'
+        c.expressions.map((expr) ->
+            value = walk(expr)
+            if expr.type not in SIDE_EFFECT_FREE
+                p.addLine value
+        )[c.expressions.length-1]
       when 'CallExpression', 'NewExpression'
         "#{walk c.callee}(#{c.arguments.map(walk).join ', '})"
       when 'ThrowStatement'


Improve axis style

Improve axis style

--- src/chart-d3-bar.coffee
+++ src/chart-d3-bar.coffee
@@ -5,10 +5,12 @@
     rangeBand - 1  # Set 1 pixel margin width

   @xAxis: (xScale) ->
-    d3.svg.axis().scale(xScale).ticks(10).tickSize(6, 6).orient('bottom')
+    d3.svg.axis().scale(xScale).ticks(10).tickSize(3, 3)
+      .tickPadding(5).orient('bottom')

   @yAxis: (yScale) ->
-    d3.svg.axis().scale(yScale).ticks(20).tickSize(6, 0).orient('left')
+    d3.svg.axis().scale(yScale).ticks(20).tickSize(3, 0)
+      .tickPadding(7).orient('left')

   @xScale: (domain, max) ->
     d3.scale.ordinal().domain(domain).rangeRoundBands([0, max], 0, 0)


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -19,7 +19,7 @@
   player.update()

   player.I.x
-  # =>
+  # =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -5,7 +5,7 @@
     width: 32
     height: 32

-  App.
+  App.wi

   self.bind "update", ->
     I.x = I.x.clamp(I.width / 2, App.width - I.width / 2)


:lipstick: Extract color variables filtering in a method

--- lib/color-project.coffee
+++ lib/color-project.coffee
@@ -49,16 +49,19 @@
     variablesObject[variable.name] = variable for variable in @variables
     variablesObject

+  getColorVariables: ->
+    context = @getContext()
+
+    @variables.filter (variable) ->
+      color = variable.color ? context.readColor(variable.value)
+      variable.color ?= color if color? and !variable.color?
+
+      color?
+
   getPalette: ->
     return new Palette unless @variables?

-    context = @getContext()
     colors = {}
-
-    @variables.forEach (variable) ->
-      color = variable.color ? context.readColor(variable.value)
-      if color?
-        variable.color ?= color
-        colors[variable.name] = color
+    @getColorVariables().forEach (variable) -> colors[variable.name] = variable

     new Palette(colors)


should fix #612

should fix #612

--- app/assets/javascripts/ontology-state.js.coffee
+++ app/assets/javascripts/ontology-state.js.coffee
@@ -23,7 +23,7 @@

       if $.inArray(state, finalStates) != -1
         # replace spinner with refresh button
-        container.find(".spinner").replaceWith(" refresh")
+        container.find(".spinner").replaceWith(" refresh")
       else
         enqueue()


Reload changes page every 2 minutes.

Reload changes page every 2 minutes.

--- app/assets/javascripts/reload_changes.js.coffee
+++ app/assets/javascripts/reload_changes.js.coffee
@@ -2,4 +2,4 @@
   if $('h1').html() == 'Changes'
     setTimeout(->
       location.reload()
-    , 10 * 1000)
+    , 120 * 1000)


Bug fix: status field height after submit

--- app/assets/javascripts/status_message.js.coffee
+++ app/assets/javascripts/status_message.js.coffee
@@ -10,7 +10,7 @@

     # Hide the update form
     resetForm = ->
-      $field.attr('rows', 1).val("").height("1.5em").blur()
+      $field.attr('rows', 1).val("").height(19).blur()
       $controls.hide()

     narrow = ->


copy change on label for student drop down menu

--- app/assets/javascripts/views/map_view.js.coffee
+++ app/assets/javascripts/views/map_view.js.coffee
@@ -1,7 +1,7 @@
 templates =
   studentList: _.template """

-
Change student:

+
Student:

<%= currentStudentName %>


Remove unneeded map controls

Remove unneeded map controls

--- app/assets/javascripts/views/map_view.js.coffee
+++ app/assets/javascripts/views/map_view.js.coffee
@@ -47,6 +47,9 @@
       zoom: 14
       mapTypeControlOptions:
         mapTypeIds: ['map_style']
+      disableDefaultUI: true
+      panControl: true
+      zoomControl: true
     }
     @map.mapTypes.set 'map_style', @styledMap()
     @map.setMapTypeId 'map_style'


Do not send POST in the preview

Do not send POST in the preview

--- app/assets/javascripts/widgets/direction.coffee
+++ app/assets/javascripts/widgets/direction.coffee
@@ -300,9 +300,10 @@
     delete params.polygon
     params

-  window.parent.postMessage(JSON.stringify(
-    event: "on_params_changed", params: store_params()
-  ), "*")
+  unless renderOnly?
+    window.parent.postMessage(JSON.stringify(
+      event: "on_params_changed", params: store_params()
+    ), "*")

   if hasValidWaypoints search_params.waypoints
     searchRoute search_params, (polygon) ->


Properly store legacy cookie with a value

--- src/scripts/pages/app/modals/legacy.coffee
+++ src/scripts/pages/app/modals/legacy.coffee
@@ -16,6 +16,6 @@
       e.preventDefault()

       if $(e.currentTarget).find('.checkbox input').is(':checked')
-        document.cookie = "legacy; max-age=#{60*60*24*30*365*4}; path=/;"
+        document.cookie = "legacy=nowarn; max-age=#{60*60*24*30*365*4}; path=/;"

       location.href = @$el.children().data('href') # HACK to pass href value to modal view


ComputeController: emit error with data of KloudError for client-side in errorHandler

--- client/Main/providers/computecontroller.coffee
+++ client/Main/providers/computecontroller.coffee
@@ -177,7 +177,10 @@
             else
               @eventListener.triggerState machine, status: Machine.State.Unknown

-      warn "info err:", err, this
+      @emit "error", { task, err, machine }
+      @emit "error-#{machine._id}", { task, err, machine }
+
+      warn "#{task} failed:", err, this

   destroy: (machine)->
@@ -305,6 +308,10 @@
       (@errorHandler call, 'info', machine) err

+  # Utils beyond this point
+  #
+
+
   requireMachine: (options = {}, callback = noop)-> @ready =>

     {app} = options
@@ -336,9 +343,6 @@
           @storage.setValue identifier, machine.uid

         callback err, machine
-
-
-

   @reviveProvisioner = (machine, callback)->


ComputeController: cosmetic updates

ComputeController: cosmetic updates

--- client/Main/providers/computecontroller.coffee
+++ client/Main/providers/computecontroller.coffee
@@ -18,7 +18,7 @@
         environment  : KD.config.environment

       @eventListener = new ComputeEventListener
-      @computeStateChecker = new ComputeStateChecker
+      @stateChecker  = new ComputeStateChecker

       @on "MachineBuilt",     => do @reset
       @on "MachineDestroyed", => do @reset
@@ -94,8 +94,8 @@
             machines.push machine

         @machines = machines
-        @computeStateChecker.machines = machines
-        @computeStateChecker.start()
+        @stateChecker.machines = machines
+        @stateChecker.start()

         cb null, machines  for cb in queue
         queue = []
@@ -165,7 +165,7 @@

       if @_trials[machine.uid][task]++ < 2
         info "Trying again to do '#{task}'..."
-        KD.singletons.computeController[task] machine
+        this[task] machine
         return yes

@@ -209,13 +209,12 @@

       machine.getBaseKite( createIfNotExists = no ).disconnect()

-      @_clearTrialCounts machine
-
       call = @kloud.destroy { machineId: machine._id }

       .then (res)=>

         log "destroy res:", res
+        @_clearTrialCounts machine
         @eventListener.addListener 'destroy', machine._id

       .timeout ComputeController.timeout


resurrection.teamwork: fix invite button activity widget form display procedure

--- client/Teamwork/Views/teamworkworkspace.coffee
+++ client/Teamwork/Views/teamworkworkspace.coffee
@@ -206,6 +206,11 @@
         @activityWidget.setInputContent "Would you like to join my Teamwork session? #{url}"
         @showActivityWidget()
         @hideShareButtons()
+        @activityWidget.showForm (err, activity) =>
+          return  err if err
+          @activityWidget.hideForm()
+          @notification.hide()
+          @workspaceRef.child("activityId").set activity.getId()

         KD.mixpanel "Teamwork Invite, click", {@sessionKey}


Environment: Remove unneeded codes.

Environment: Remove unneeded codes.

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -39,9 +39,6 @@
       if stack = computeController.findStackFromMachineId id
         computeController.on "apply-#{stack._id}", (event) ->
           reactor.dispatch actions.MACHINE_UPDATED, { id, event }
-
-        if stack.status?.state is Machine.State.Building
-          computeController.eventListener.addListener 'apply', stack._id

   # TODO: when a machine shared/collaborate, SharedMachineInvitation and


video/model: move event handlers to their own methods

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -98,55 +98,16 @@
    * Registers callbacks for service events.
    *
    * @param {OT.Session} session
-   * @listens OT.Session~streamCreated
-   * @listens OT.Session~streamDestroyed
-   * @listens OT.Session~connectionCreated
-   * @listens OT.Session~sessionDisconnected
    * @emits VideoCollaborationModel~HostKickedLoggedInUser
    * @emits VideoCollaborationModel~VideoCollaborationEnded
    * @emits VideoCollaborationModel~VideoCollaborationEndedByNetwork
-   * @emits VideoCollaborationModel~VideoParticipantConnected
-   * @emits VideoCollaborationModel~VideoParticipantDisconnected
   ###
   bindSessionEvents: (session) ->

-    session.on 'streamCreated', (event) =>
-      @subscribeToStream session, event.stream
-
-    session.on 'streamDestroyed', (event) =>
-      { connection } = event.stream
-      @setParticipantLeft connection.connectionId
-
-    session.on 'connectionCreated', (event) =>
-      # We don't want to deal with own user's events, we basically want to
-      # abstract out logged-in user out of the equation here.
-      return  if @isMyConnection event.connection
-
-      # this is gonna be a fake `ParticipantType.Subscriber` like object for
-      # views to start drawing/rendering for this connection, because our
-      # system doesn't necessarily require people to be publishint to session
-      # to mark them as active. For example, showing participant heads on ide
-      # chat window when video active we are filtering people who are connected
-      # to session as actives. Participant heads doesn't care if you are
-      # publishing or not, since user is hearing the video and seeing other
-      # users he/she needs to be labeled as active/connected.
-      subscriber = @registerDefaultSubscriber event.connection
-
-      @incrementConnectionCount()
-
-      @emit 'ParticipantConnected', subscriber
-
-    session.on 'connectionDestroyed', (event) =>
-      return  if @isMyConnection event.connection
-
-      # get the name before unregistering.
-      { nick } = @subscribers[event.connection.id]
-
-      @unregisterSubscriber event.connection.id
-
-      @decrementConnectionCount()
-
-      @emit 'ParticipantDisconnected', helper.defaultSubscriber nick
+    session.on 'connectionCreated'   , @bound 'onConnectionCreated'
+    session.on 'streamCreated'       , @bound 'onStreamCreated'
+    session.on 'streamDestroyed'     , @bound 'onStreamDestroyed'
+    session.on 'connectionDestroyed' , @bound 'onConnectionDestroyed'

     session.on 'sessionDisconnected', (event) =>
       @setState { connected: no }
@@ -175,6 +136,72 @@

   ###*
+   * Opentok session's `connectionCreated` event handler.
+   *
+   * @param {OT.ConnectionEvent} event
+   * @emits VideoCollaborationModel~ParticipantConnected
+  ###
+  onConnectionCreated: (event) ->
+    # We don't want to deal with own user's events, we basically want to
+    # abstract out logged-in user out of the equation here.
+    return  if @isMyConnection event.connection
+
+    # this is gonna be a fake `ParticipantType.Subscriber` like object for
+    # views to start drawing/rendering for this connection, because our
+    # system doesn't necessarily require people to be publishint to session
+    # to mark them as active. For example, showing participant heads on ide
+    # chat window when video active we are filtering people who are connected
+    # to session as actives. Participant heads doesn't care if you are
+    # publishing or not, since user is hearing the video and seeing other
+    # users he/she needs to be labeled as active/connected.
+    subscriber = @registerDefaultSubscriber event.connection
+
+    @incrementConnectionCount()
+
+    @emit 'ParticipantConnected', subscriber
+
+
+  ###*
+   * Opentok session's `connectionCreated` event handler.
+   *
+   * @param {OT.ConnectionEvent} event
+   * @emits VideoCollaborationModel~ParticipantDisconnected
+  ###
+  onConnectionDestroyed: (event) ->
+
+    { connection } = event
+
+    return  if @isMyConnection connection
+
+    @setParticipantLeft connection.id
+    @decrementConnectionCount()
+
+    nick = helper.getNicknameFromConnection connection
+
+    @emit 'ParticipantDisconnected', helper.defaultSubscriber nick
+
+
+  ###*
+   * Opentok session's `streamCreated` event handler.
+   *
+   * @param {OT.StreamEvent} event
+  ###
+  onStreamCreated: (event)->
+
+    @subscribeToStream session, event.stream
+
+
+  ###*
+   * Opentok session's `streamDestroyed` event handler.
+   *
+   * @param {OT.StreamEvent} event
+  ###
+  onStreamDestroyed: (event) ->
+    { connection } = event.stream
+    @setParticipantLeft connection.connectionId
+
+
+  ###*
    * Subscribes to given session's given stream.
    *
    * It uses helper method to subscribe, it's success callback will return the


kite/newkite: wrapping dnode args, WIP, passing to cihangir :)

--- client/landing/app/MainApp/kite/newkite.coffee
+++ client/landing/app/MainApp/kite/newkite.coffee
@@ -2,6 +2,7 @@
 # It is analogous to RemoteKite class in go/koding/newkite/kite/remote.go file.
 class NewKite extends KDObject

+  {setAt} = Bongo.JsPath
   [NOTREADY, READY, CLOSED] = [0,1,3]

   constructor: (kite, authentication, options={})->
@@ -86,7 +87,7 @@
         # replace the handler. when called it will remove the handler.
         @proto.localStore.items[id] = ()=>
           delete @proto.localStore.items[id]
-          response = arguments[0].withArgs[0]
+          response = arguments[0]
           fn.apply null, [response.error, response.result]

   onOpen: ->
@@ -104,11 +105,51 @@
     if @autoReconnect
       KD.utils.defer => @setBackoffTimeout @bound "connect"

+
   onMessage: (evt)->
     data = evt.data
     log "onMessage", data
     req = JSON.parse data
-    @proto.handle(req)
+
+    getCallback = (callbackId) =>
+      unless @proto.remoteStore.has callbackId
+        @proto.remoteStore.add callbackId, =>
+          @proto.request callbackId, [].slice.call arguments
+      cb = @proto.remoteStore.get callbackId
+      return (rest...) =>
+        cb withArgs:rest
+
+    args = req.arguments or []
+    Object.keys(req.callbacks or {}).forEach (strId)->
+      id = parseInt strId, 10
+      path = req.callbacks[id]
+
+      callback = getCallback id
+
+      callback.id = id
+      setAt args, path, callback
+
+    {method} = req
+    switch method
+      when 'methods'
+        @proto.handleMethods args[0]
+      when 'error'
+        @proto.emit 'remoteError', args[0]
+      when 'cull'
+        args.forEach (id)=> @proto.remoteStore.cull id
+      else
+        switch typeof method
+          when 'string'
+            if @proto.instance.propertyIsEnumerable method
+              apply @proto.instance[method], @proto.instance, args
+            else
+              @proto.emit 'error', new Error(
+                "Request for non-enumerable method: #{method}"
+              )
+          when 'number'
+            apply @proto.localStore.get(method), @proto.instance, args[0].withArgs
+
+  apply =(fn, ctx, args)-> fn.apply ctx, args

   onError: (evt)->
     log "#{@kite.name} error: #{evt.data}"


multiple brokers: WIP

multiple brokers: WIP

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -189,7 +189,6 @@
         @openContent null, section, models, route, null, passOptions

   clear:(route, replaceState=yes)->
-    console.log {arguments}
     unless route
       {entryPoint} = KD.config
       if entryPoint?.type is 'group' and entryPoint?.slug?


work from home

work from home

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -41,10 +41,8 @@
     pageTitle = nicenames[app] ? app
     @setPageTitle pageTitle
     unless group?
-      log 'AAAAAA'
       appManager.openApplication app
     else
-      log 'BBBBB'
       appManager.tell app, 'setGroup', group
     if Object.keys(query).length
       appManager.tell app, 'handleQuery', query
@@ -120,24 +118,43 @@
         else
           @loadContent name, section, topicSlug, route

-  getRoutes =->
+  createGoTos =do->
     # Abbreviations used below:
     # "acc" is short for "accumulator"
     # "kv" is short for "key-value pair"
     # "sec" is short for "section"
+    {isArray}   = Array
+    kvMap       = (sec)-> [sec, fn sec]
+    kvReduce    = (acc, sec)-> acc[sec[0]] = sec[1]; acc
+    createGoTos = (names, fn)->
+      names = names.split ' '  unless isArray names
+      names.map(kvMap).reduce kvReduce, {}
+
+  getRoutes =->
+
     mainController = KD.getSingleton 'mainController'

-    kv =(acc, sec)-> acc[sec[0]] = sec[1]; acc
-
-    goToContent = 'Activity Apps Groups Members Topics'
-      .split(' ')
-      .map((sec)=> [sec, @createContentDisplayHandler sec])
-      .reduce kv, {}
-
-    goToSection = 'Account Activity Apps Groups Members StartTab Topics'
-      .split(' ')
-      .map((sec)-> [sec, ({params:{name}, query})-> @go sec, name, query])
-      .reduce kv, {}
+    # kv =(acc, sec)-> acc[sec[0]] = sec[1]; acc
+
+    # goToContent = 'Activity Apps Groups Members Topics'
+    #   .split(' ')
+    #   .map((sec)=> [sec, @createContentDisplayHandler sec])
+    #   .reduce kv, {}
+
+    # goToSection = 'Account Activity Apps Groups Members StartTab Topics'
+    #   .split(' ')
+    #   .map((sec)-> [sec, ({params:{name}, query})-> @go sec, name, query])
+    #   .reduce kv, {}
+
+    goToContent = createGoTos(
+      'Activity Apps Groups Members Topics'
+      (sec)=> @createContentDisplayHandler sec
+    )
+
+    goToSection = createGoTos(
+      'Account Activity Apps Groups Members StartTab Topics'
+      (sec)-> ({params:{name}, query})-> @go sec, name, query
+    )

     routes =


Add a rate-limit to the email-confirm endpoint

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -69,7 +69,6 @@
 		webRouter.get  '/logout', UserController.logout
 		webRouter.get  '/restricted', AuthorizationMiddlewear.restricted

-		webRouter.get '/account-merge/email/confirm', AccountMergeEmailController.confirmMergeFromEmail

 		if Features.hasFeature('registration')
 			webRouter.get '/register', UserPagesController.registerPage
@@ -345,6 +344,15 @@
 		webRouter.post '/admin/messages', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.createMessage
 		webRouter.post '/admin/messages/clear', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.clearMessages

+		webRouter.get '/account-merge/email/confirm',
+			RateLimiterMiddlewear.rateLimit({
+				endpointName: "account-merge-email-confirm",
+				ipOnly: true,
+				maxRequests: 10
+				timeInterval: 60
+			}),
+			AccountMergeEmailController.confirmMergeFromEmail
+
 		privateApiRouter.get '/perfTest', (req,res)->
 			res.send("hello")


Form: Added data for not existed input name.

--- src/tests/components/forms/test_form.coffee
+++ src/tests/components/forms/test_form.coffee
@@ -76,7 +76,7 @@

   it 'should set and update form data', ->
-    form.setData { name: 'John', surname: 'Doe' }
+    form.setData { name: 'John', surname: 'Doe', notExistingInput: 'someValue' }

     expect(form.getInputByName('name').getValue()).toBe 'John'
     expect(form.getInputByName('surname').getValue()).toBe 'Doe'


minor tweaks and fixes

minor tweaks and fixes

--- @client/banner.coffee
+++ @client/banner.coffee
@@ -402,6 +402,8 @@
             transform: 'rotate(45deg)'
             zIndex: 2

+DEFAULT_TEXT_BLOCK_COLOR = "#000000"
+DEFAULT_TEXT_BLOCK_OPACITY = 255 * .8
 CustomizeTextBlock = ReactiveComponent
   displayName: 'CustomizeTextBlock'
   render : ->
@@ -418,7 +420,7 @@
         id: 'text_background_css'
         type: 'color'
         name: 'text_background_css'
-        defaultValue: customization('banner')?.text_background_css
+        defaultValue: customization('banner')?.text_background_css or DEFAULT_TEXT_BLOCK_COLOR
         style:
           width: '100%'
           display: 'block'
@@ -433,7 +435,7 @@
         step: 1
         max: 255
         name: 'text_background_css_opacity'
-        defaultValue: customization('banner')?.text_background_css_opacity
+        defaultValue: customization('banner')?.text_background_css_opacity or DEFAULT_TEXT_BLOCK_OPACITY
         style:
           width: '100%'
           display: 'block'
@@ -599,8 +601,13 @@
         BUTTON
           style:
             border: 'none'
-            color: if is_light then 'rgba(0,0,0,.6)' else 'rgba(255,255,255,.6)'
-            backgroundColor: if is_light then "rgba(255,255,255,.2)" else "rgba(0,0,0,.2)"
+
+            # backgroundColor: if is_light then "rgba(255,255,255,.2)" else "rgba(0,0,0,.2)"
+            # color: if is_light then 'rgba(0,0,0,.6)' else 'rgba(255,255,255,.6)'
+
+            backgroundColor: if is_light then "rgba(0,0,0,.8)" else "rgba(255,255,255,.8)"
+            color: if !is_light then 'black' else 'white'
+
             padding: '4px 8px'
             borderRadius: 8
             cursor: 'pointer'
@@ -631,14 +638,16 @@
       style:
         position: 'absolute'
         left: "50%"
-        marginLeft: -80
-        top: 8
+        marginLeft: -80 - 8*2
+        top: 0
+        padding: "4px 8px"
         zIndex: 2
+        backgroundColor: if !is_light then "rgba(0,0,0,.3)" else "rgba(255,255,255,.3)"

       DIV null,
         BUTTON
           style:
-            backgroundColor: if is_light then "rgba(0,0,0,.7)" else "rgba(255,255,255,.7)"
+            backgroundColor: if is_light then "rgba(0,0,0,.8)" else "rgba(255,255,255,.8)"
             color: if !is_light then 'black' else 'white'
             border: 'none'
             borderRadius: 8
@@ -929,10 +938,11 @@
   has_description = description?.trim().length > 0 && description.trim() != '

'

   is_dark_theme = !is_light_background()
-  text_block_color = edit_banner.text_background_css or customization('banner')?.text_background_css
-  text_block_opacity = parseInt(edit_banner.text_background_css_opacity or customization('banner')?.text_background_css_opacity or 255 * .8)
+  text_block_color = edit_banner.text_background_css or customization('banner')?.text_background_css or DEFAULT_TEXT_BLOCK_COLOR
+  text_block_opacity = parseInt(edit_banner.text_background_css_opacity or customization('banner')?.text_background_css_opacity or DEFAULT_TEXT_BLOCK_OPACITY)
   text_block_background = if has_image_background then "#{text_block_color}#{convert_opacity(text_block_opacity)}" or 'rgba(0, 0, 0, .8)'

+  console.log text_block_background, has_image_background, text_block_color
   if text_block_color && text_block_opacity > 126
     text_block_is_dark = !is_light_background(text_block_color)
   else


linking to github repo

linking to github repo

--- @client/footer.coffee
+++ @client/footer.coffee
@@ -89,18 +89,41 @@
             TechnologyByConsiderit
               size: 26
             BR null
-            A
-              href: 'https://consider.it'
-              style:
-                display: 'inline-block'
-                textDecoration: 'underline'
-                fontSize: 14
-                marginTop: 10
-
-              translator
-                id: "footer.created_your_own_forum"
-                considerit: "Consider.it"
-                'Create your own {considerit} forum'
+
+            DIV
+              style:
+                marginBottom: 18
+
+              A
+                href: 'https://github.com/Considerit/ConsiderIt'
+                style:
+                  textDecoration: 'underline'
+                  fontWeight: 600
+                  fontSize: 14
+
+                target: '_blank'
+
+                "Consider.it is Open Source"
+                IMG
+                  src: asset('product_page/github_logo.png')
+                  style:
+                    height: 18
+                    paddingLeft: 6
+                    verticalAlign: 'middle'
+              ' | '
+
+              A
+                href: 'https://consider.it'
+                style:
+                  display: 'inline-block'
+                  textDecoration: 'underline'
+                  fontSize: 14
+                  marginTop: 10
+                  fontWeight: 600
+
+                translator
+                  id: "footer.created_your_own_forum"
+                  'Create your own forum'

@@ -118,7 +141,7 @@
               DIV
                 style:
                   display: 'inline-block'
-                '© 2016 Consider.it. All rights reserved. '
+                '© 2019 Consider.it. All rights reserved. '

                 TRANSLATE
                   id: 'footer.policies'


when account added, project will be loaded in accountCtrl

--- coffee/controllers/issueCtrl.coffee
+++ coffee/controllers/issueCtrl.coffee
@@ -35,7 +35,6 @@
    start getting issues.
   ###
   $scope.$on 'accountAdded', (e, account) ->
-    # Redmine.get(account).loadProjects @loadSuccess, @loadError, params
     Redmine.get(account).getIssuesOnUser(getIssuesSuccess)
     $scope.accounts.push account
     if not $scope.selectedAccount[0]


strip old releases

strip old releases

--- webpack/release.coffee
+++ webpack/release.coffee
@@ -71,7 +71,7 @@
     release.builds.assets ||= []
     release.builds.assets.sort((a, b) -> (new Date(b.created_at)).getTime() - (new Date(a.created_at)).getTime())
     for asset, i in release.builds.assets
-      continue if i < 10 && asset.name != xpi
+      continue if i < 5 && asset.name != xpi
       yield github({ method: 'DELETE', uri: "/releases/assets/#{asset.id}" })

     console.log("uploading #{xpi} to builds")


couple more tweaks to lexer.coffee

couple more tweaks to lexer.coffee

--- lexer.coffee
+++ lexer.coffee
@@ -5,7 +5,7 @@
 #
 #     [tag, value, line_number]
 #
-# Which is a format that can be fed directly into Jison.
+# Which is a format that can be fed directly into [Jison](http://github.com/zaach/jison).

 # Set up the Lexer for both Node.js and the browser, depending on where we are.
 if process?
@@ -17,7 +17,7 @@
 # Constants
 # ---------

-# Keywords that CoffeScript shares in common with JS.
+# Keywords that CoffeeScript shares in common with JavaScript.
 JS_KEYWORDS: [
   "if", "else",
   "true", "false",
@@ -98,7 +98,7 @@
 # treated as identifiers.
 ACCESSORS: ['PROPERTY_ACCESS', 'PROTOTYPE_ACCESS', 'SOAK_ACCESS', '@']

-# Tokens that, when immediately preceding a 'WHEN', indicate that the 'WHEN'
+# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
 # occurs at the start of a line. We disambiguate these from trailing whens to
 # avoid an ambiguity in the grammar.
 BEFORE_WHEN: ['INDENT', 'OUTDENT', 'TERMINATOR']
@@ -258,7 +258,7 @@
     true

   # We treat all other single characters as a token. Eg.: `( ) , . !`
-  # Multi-character operators are also literal tokens, so that Racc can assign
+  # Multi-character operators are also literal tokens, so that Jison can assign
   # the proper order of operations.
   literal_token: ->
     match: @chunk.match(OPERATOR)
@@ -289,7 +289,7 @@
   # Token Manipulators
   # ------------------

-  # As we consume a new IDENTIFIER, look at the previous token to determine
+  # As we consume a new `IDENTIFIER`, look at the previous token to determine
   # if it's a special kind of accessor.
   name_access_type: ->
     @tag(1, 'PROTOTYPE_ACCESS') if @value() is '::'
@@ -372,7 +372,8 @@
 # -----------------

 # Does a list include a value?
-include: (list, value) -> list.indexOf(value) >= 0
+include: (list, value) ->
+  list.indexOf(value) >= 0

 # Count the number of occurences of a character in a string.
 count: (string, letter) ->


disabled interpolations in normal regexes

--- lexer.coffee
+++ lexer.coffee
@@ -171,26 +171,14 @@

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

   # Matches experimental, multiline and extended regular expression literals.
@@ -199,11 +187,21 @@
     @i += heregex.length
     unless ~body.indexOf '#{'
       re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/')
-      @token 'REGEX', "/#{  re or '(?:)' }/#{flags}"
+      @token 'REGEX', "/#{ re or '(?:)' }/#{flags}"
       return true
     @token 'IDENTIFIER', 'RegExp'
     @tokens.push ['CALL_START', '(']
-    @interpolateString "\"#{body}\"", regex: yes, heregex: yes
+    tokens = []
+    for [tag, value] in @interpolateString('"' + body + '"', regex: yes)
+      if tag is 'TOKENS'
+        tokens.push value...
+      else
+        continue unless value = value.slice(1, -1).replace HEREGEX_OMIT, ''
+        tokens.push ['STRING', '"' + value.replace(/[\\\"]/g, '\\$&') + '"']
+      tokens.push ['+', '+']
+    tokens.pop()
+    @tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0]?[0] is 'STRING'
+    @tokens.push tokens...
     @tokens.push [',', ','], ['STRING', '"' + flags + '"'] if flags
     @tokens.push ['CALL_END', ')']
     true
@@ -355,12 +353,12 @@
       while (match = HEREDOC_INDENT.exec doc)
         attempt = match[1]
         indent = attempt if indent is null or 0 < attempt.length < indent.length
-    doc = doc.replace /\n#{ indent }/g, '\n' if indent
+    doc = doc.replace /// \n #{indent} ///g, '\n' if indent
     return doc if herecomment
     {quote} = options
     doc = doc.replace /^\n/, ''
     doc = doc.replace /\\([\s\S])/g, (m, c) -> if c in ['\n', quote] then c else m
-    doc = doc.replace /#{quote}/g, '\\$&'
+    doc = doc.replace /// #{quote} ///g, '\\$&'
     doc = @escapeLines doc, yes if quote is "'"
     doc

@@ -398,7 +396,6 @@
   # interpolations within strings, ad infinitum.
   balancedString: (str, delimited, options) ->
     options or= {}
-    slash = delimited[0][0] is '/'
     levels = []
     i = 0
     slen = str.length
@@ -417,10 +414,9 @@
             levels.push(pair)
             i += open.length - 1
             break
-      break if not levels.length or slash and str.charAt(i) is '\n'
+      break if not levels.length
       i += 1
     if levels.length
-      return false if slash
       throw new Error "SyntaxError: Unterminated #{levels.pop()[0]} starting on line #{@line + 1}"
     if not i then false else str[0...i]

@@ -446,37 +442,28 @@
       unless char is '#' and str.charAt(i+1) is '{' and
              (expr = @balancedString str[i+1..], [['{', '}']])
         continue
-      if pi < i
-        tokens.push ['STRING', '"' + @escapeLines(str[pi...i], heredoc) + '"']
+      tokens.push ['STRING', '"' + str[pi...i] + '"'] if pi < i
       inner = expr.slice(1, -1).replace(LEADING_SPACES, '').replace(TRAILING_SPACES, '')
       if inner.length
         inner = inner.replace /\\\"/g, '"' if heredoc
-        nested = lexer.tokenize "(#{inner})", line: @line
+        nested = lexer.tokenize "(#{inner}\n)", line: @line
         (tok[0] = ')') for tok in nested when tok[0] is 'CALL_END'
         nested.pop()
+        if nested.length < 5 then nested.pop(); nested.shift()
         tokens.push ['TOKENS', nested]
-      else
-        tokens.push ['STRING', '""']
       i += expr.length
       pi = i + 1
-    if i > pi < str.length - 1
-      s = @escapeLines str.slice(pi, -1), heredoc
-      tokens.push ['STRING', '"' + s + '"']
-    tokens.unshift ['STRING', '""'] unless tokens[0][0] is 'STRING'
-    interpolated = not regex and tokens.length > 1
+    tokens.push ['STRING', '"' + str.slice pi] if i > pi < str.length - 1
+    return tokens if regex
+    interpolated = tokens.length > 1
+    tokens.unshift ['STRING', '""'] unless tokens[0]?[0] is 'STRING'
     @token '(', '(' if interpolated
-    {push} = tokens
     for [tag, value], i in tokens
       @token '+', '+' if i
       if tag is 'TOKENS'
-        push.apply @tokens, value
-        continue
-      if regex
-        value = value.slice 1, -1
-        value = value.replace /[\\\"]/g, '\\$&'
-        value = value.replace HEREGEX_OMIT, '' if options.heregex
-        value = '"' + value + '"'
-      @token tag, value
+        @tokens.push value...
+      else
+        @token tag, @escapeLines value, heredoc
     @token ')', ')' if interpolated
     tokens

@@ -558,10 +545,14 @@
 JSTOKEN    = /^`[^\\`]*(?:\\.[^\\`]*)*`/

 # Regex-matching-regexes.
-REGEX_START         = /^\/([^\/])/
-REGEX_END           = /^[imgy]{0,4}(?![a-zA-Z])/
-REGEX_ESCAPE        = /\\[^#]/g
-
+REGEX = /// ^
+  / (?!\s)  # disallow leading whitespace
+  (?: [^ [ / \n \\ ]+  # every other things
+    | \\.              # anything escaped
+    | \[ ( [^\\\]]+ | \\. )* ]  # character class
+  )+
+  / [imgy]{0,4} (?![A-Za-z])
+///
 HEREGEX      = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/
 HEREGEX_OMIT = /\s+(?:#.*)?/g


Fix incorrect filter config when user enters a blank string

--- src/gral/filters.coffee
+++ src/gral/filters.coffee
@@ -28,7 +28,7 @@
     _filter = k.DEFAULT_FILTER
   _filter

-config = (filter) ->
+config = (filter = '') ->
   store = window?.localStorage ? process.env
   store[k.FILTER_KEY] = filter
   _cachedThreshold = null


hooked up slider to wedge

hooked up slider to wedge

--- examples/audio/static/coffee/spectrogram.coffee
+++ examples/audio/static/coffee/spectrogram.coffee
@@ -106,11 +106,17 @@

     # update the radial histogram data
     hist = new Float32Array(NUM_BINS)
-    bin_start = 0
-    bin_size = Math.ceil(HISTSIZE / NUM_BINS)
+    if @fft_xrange
+      bin_min = Math.round(@fft_xrange.get('start'))
+      bin_max = Math.round(@fft_xrange.get('end'))
+    else
+      bin_min = 0
+      bin_max = 256
+    bin_start = bin_min
+    bin_size = Math.ceil(bin_max - bin_min / NUM_BINS)
     for i in [0..NUM_BINS-1]
       hist[i] = 0
-      bin_end = Math.min(bin_start+bin_size-1, data[0].length)
+      bin_end = Math.min(bin_start+bin_size-1, bin_max)
       for j in [bin_start..bin_end]
         hist[i] += data[0][j]
       bin_start += bin_size


Updated synchronizedtime.coffee

Updated synchronizedtime.coffee

--- synchronizedtime.coffee
+++ synchronizedtime.coffee
@@ -1,4 +1,10 @@
 class SynchronizedTime

-  setTime: (t) ->
-    @time = t
+  @time = 0.0
+
+  @setTimeForTesting: (t) ->
+    SynchronizedTime.time = t
+
+  @getTime: -> SynchronizedTime.time
+
+window.SynchronizedTime = SynchronizedTime


Expose the System abstraction.

Expose the System abstraction.

--- src/finitio.coffee
+++ src/finitio.coffee
@@ -35,6 +35,8 @@
 $u.extend Finitio, require './errors'
 $u.extend Finitio, require './support/contracts'
 ##
+Finitio.System = require './system'
+##
 Finitio.AliasType    = require './type/alias_type'
 Finitio.AdType       = require './type/ad_type'
 Finitio.AnyType      = require './type/any_type'
@@ -46,5 +48,5 @@
 Finitio.SubType      = require './type/sub_type'
 Finitio.TupleType    = require './type/tuple_type'
 Finitio.UnionType    = require './type/union_type'
-
+##
 module.exports = Finitio


Small amount of docco.

Small amount of docco.

--- src/lib/timezone.coffee
+++ src/lib/timezone.coffee
@@ -477,7 +477,8 @@
       time = match.splice(0, 4)
       [ hours, minutes, seconds, milliseconds ] = time if time[0]?

-      # See if we matched a zone offset.
+      # See if we matched a zone offset. A zone offset is ISO an arbitrary
+      # offset and has no information on location or summer time rules.
       zone = match.shift()
       zoneOffset = offsetInMilliseconds(zone) if zone?


Added is_base to schema

Added is_base to schema

--- test/spec/sync/relational/self.tests.coffee
+++ test/spec/sync/relational/self.tests.coffee
@@ -26,6 +26,7 @@
         schema: _.defaults({
           owner: -> ['belongsTo', SelfReference, foreign_key: 'owner_id', as: 'self_references']
           self_references: -> ['hasMany', SelfReference, as: 'owner']
+          is_base: 'Boolean'
         }, BASE_SCHEMA)
         sync: SYNC(SelfReference)


Fix runaway module name

Fix runaway module name

--- src/haskell.coffee
+++ src/haskell.coffee
@@ -421,7 +421,8 @@
       captures:
         0:
           patterns: [
-            include: '#module_name'
+            name: 'support.other.module.haskell'
+            match: /^(?:{className}\.)*{className}\.?/
           ]
     ,
       include: '#type_ctor'


Add cpphs extension to Haskell grammar

Add cpphs extension to Haskell grammar

Closes #103

--- src/haskell.coffee
+++ src/haskell.coffee
@@ -3,7 +3,7 @@

 makeGrammar "grammars/haskell.cson",
   name: 'Haskell'
-  fileTypes: [ 'hs' ]
+  fileTypes: [ 'hs', 'cpphs' ]
   firstLineMatch: '^\\#\\!.*\\brunhaskell\\b'
   scopeName: 'source.haskell'


change 'mkscript' to 'mktscript'

change 'mkscript' to 'mktscript'

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -56,7 +56,7 @@
   job_name              = njs_path.basename source_home
   aux_locator           = njs_path.join source_home, "#{job_name}.aux"
   pdf_locator           = njs_path.join source_home, "#{job_name}.pdf"
-  mkscript_locator      = njs_path.join source_home, "#{job_name}.mkscript"
+  mktscript_locator      = njs_path.join source_home, "#{job_name}.mktscript"
   # tex_inputs_home       = njs_path.resolve __dirname, '..', 'tex-inputs'
   master_name           = options[ 'master' ][ 'filename' ]
   master_ext            = njs_path.extname master_name
@@ -73,7 +73,7 @@
     'master-locator':             master_locator
     'master-name':                master_name
     'pdf-locator':                pdf_locator
-    'mkscript-locator':           mkscript_locator
+    'mktscript-locator':          mktscript_locator
     'source-home':                source_home
     'source-locator':             source_locator
     'source-name':                source_name


:art: helpers module organization

:art: helpers module organization

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -30,6 +30,11 @@
 isLowerCaseCharacter = (character) ->
   character? and character.length is 1 and character.toUpperCase() isnt character

+usKeymap = null
+usCharactersForKeyCode = (code) ->
+  usKeymap ?= require('./us-keymap')
+  usKeymap[code]
+
 exports.normalizeKeystrokes = (keystrokes) ->
   normalizedKeystrokes = []
   for keystroke in keystrokes.split(WHITESPACE_REGEX)
@@ -38,6 +43,56 @@
     else
       return false
   normalizedKeystrokes.join(' ')
+
+normalizeKeystroke = (keystroke) ->
+  if isKeyup = keystroke.startsWith('^')
+    keystroke = keystroke.slice(1)
+  keys = parseKeystroke(keystroke)
+  return false unless keys
+
+  primaryKey = null
+  modifiers = new Set
+
+  for key, i in keys
+    if MODIFIERS.has(key)
+      modifiers.add(key)
+    else
+      # only the last key can be a non-modifier
+      if i is keys.length - 1
+        primaryKey = key
+      else
+        return false
+
+  if isKeyup
+    primaryKey = primaryKey.toLowerCase() if primaryKey?
+  else
+    modifiers.add('shift') if isUpperCaseCharacter(primaryKey)
+    if modifiers.has('shift') and isLowerCaseCharacter(primaryKey)
+      primaryKey = primaryKey.toUpperCase()
+
+  keystroke = []
+  if not isKeyup or (isKeyup and not primaryKey?)
+    keystroke.push('ctrl') if modifiers.has('ctrl')
+    keystroke.push('alt') if modifiers.has('alt')
+    keystroke.push('shift') if modifiers.has('shift')
+    keystroke.push('cmd') if modifiers.has('cmd')
+  keystroke.push(primaryKey) if primaryKey?
+  keystroke = keystroke.join('-')
+  keystroke = "^#{keystroke}" if isKeyup
+  keystroke
+
+parseKeystroke = (keystroke) ->
+  keys = []
+  keyStart = 0
+  for character, index in keystroke when character is '-'
+    if index > keyStart
+      keys.push(keystroke.substring(keyStart, index))
+      keyStart = index + 1
+
+      # The keystroke has a trailing - and is invalid
+      return false if keyStart is keystroke.length
+  keys.push(keystroke.substring(keyStart)) if keyStart < keystroke.length
+  keys

 exports.keystrokeForKeyboardEvent = (event) ->
   {ctrlKey, altKey, shiftKey, metaKey} = event
@@ -110,12 +165,12 @@
 exports.isBareModifier = (keystroke) -> ENDS_IN_MODIFIER_REGEX.test(keystroke)

 exports.keydownEvent = (key, options) ->
-  return keyboardEvent(key, 'keydown', options)
+  return buildKeyboardEvent(key, 'keydown', options)

 exports.keyupEvent = (key, options) ->
-  return keyboardEvent(key, 'keyup', options)
-
-keyboardEvent = (key, eventType, {ctrl, shift, alt, cmd, keyCode, target, location}={}) ->
+  return buildKeyboardEvent(key, 'keyup', options)
+
+buildKeyboardEvent = (key, eventType, {ctrl, shift, alt, cmd, keyCode, target, location}={}) ->
   ctrlKey = ctrl ? false
   altKey = alt ? false
   shiftKey = shift ? false
@@ -179,58 +234,3 @@
     MATCH_TYPES.PARTIAL
   else
     MATCH_TYPES.EXACT
-
-normalizeKeystroke = (keystroke) ->
-  if isKeyup = keystroke.startsWith('^')
-    keystroke = keystroke.slice(1)
-  keys = parseKeystroke(keystroke)
-  return false unless keys
-
-  primaryKey = null
-  modifiers = new Set
-
-  for key, i in keys
-    if MODIFIERS.has(key)
-      modifiers.add(key)
-    else
-      # only the last key can be a non-modifier
-      if i is keys.length - 1
-        primaryKey = key
-      else
-        return false
-
-  if isKeyup
-    primaryKey = primaryKey.toLowerCase() if primaryKey?
-  else
-    modifiers.add('shift') if isUpperCaseCharacter(primaryKey)
-    if modifiers.has('shift') and isLowerCaseCharacter(primaryKey)
-      primaryKey = primaryKey.toUpperCase()
-
-  keystroke = []
-  if not isKeyup or (isKeyup and not primaryKey?)
-    keystroke.push('ctrl') if modifiers.has('ctrl')
-    keystroke.push('alt') if modifiers.has('alt')
-    keystroke.push('shift') if modifiers.has('shift')
-    keystroke.push('cmd') if modifiers.has('cmd')
-  keystroke.push(primaryKey) if primaryKey?
-  keystroke = keystroke.join('-')
-  keystroke = "^#{keystroke}" if isKeyup
-  keystroke
-
-parseKeystroke = (keystroke) ->
-  keys = []
-  keyStart = 0
-  for character, index in keystroke when character is '-'
-    if index > keyStart
-      keys.push(keystroke.substring(keyStart, index))
-      keyStart = index + 1
-
-      # The keystroke has a trailing - and is invalid
-      return false if keyStart is keystroke.length
-  keys.push(keystroke.substring(keyStart)) if keyStart < keystroke.length
-  keys
-
-usKeymap = null
-usCharactersForKeyCode = (code) ->
-  usKeymap ?= require('./us-keymap')
-  usKeymap[code]


don't include test-vendor.js in getTestFiles

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -251,7 +251,7 @@

 getTestFiles = (config) ->
   isTestFile = (generatedFile) ->
-    exports.startsWith generatedFile, sysPath.normalize('test/')
+    exports.startsWith(generatedFile, sysPath.normalize('test/')) and generatedFile.lastIndexOf('vendor') == -1
   joinPublic = (generatedFile) ->
     sysPath.join(config.paths.public, generatedFile)


Have linkTo return HTML

Have linkTo return HTML

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/_module.coffee
+++ vendor/assets/javascripts/fustrate/_module.coffee
@@ -48,7 +48,7 @@
       Fustrate._arrayToClass pieces.slice(1), root[pieces[0]]

   @linkTo: (text, path) ->
-    $('<a>').prop('href', path).html(text)
+    $('<a>').prop('href', path).html(text).outerHTML()

   @ajaxUpload: (url, data) ->
     formData = new FormData
@@ -110,3 +110,10 @@
 Array::remove = (object) ->
   index = @indexOf object
   @splice index, 1 if index isnt -1
+
+jQuery.fn.outerHTML = ->
+  return '' unless @length
+
+  return @[0].outerHTML if @[0].outerHTML
+
+  $('<div>').append(@[0].clone()).remove().html()


Ensure that 'this' is bound correctly, otherwise these callbacks blow up.

--- src/message.coffee
+++ src/message.coffee
@@ -68,7 +68,7 @@
       @_client.logger.debug params
       @_client._apiCall "chat.update", params, @_onUpdateMessage

-  _onUpdateMessage: (data) ->
+  _onUpdateMessage: (data) =>
     @_client.logger.debug data

   deleteMessage: =>
@@ -81,7 +81,7 @@
       @_client.logger.debug params
       @_client._apiCall "chat.delete", params, @_onDeleteMessage

-  _onDeleteMessage: (data) ->
+  _onDeleteMessage: (data) =>
     @_client.logger.debug data

   _onMessageSent: (data) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -145,5 +145,7 @@
       showName: false
       text: null
       width: 100
+
+

     I.meters.push options


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -37,7 +37,7 @@
   equals player.I.alpha, 0.2

   player.update()
-  equals player
+  equals player.I.alpha, 0.2


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -3,7 +3,7 @@
 # sets up player object before each test
 QUnit.testStart = ->
   window.player = GameObject
-    alpha: 1
+    alpha: 0.9

   player.include Flickerable


FIX: Fix filter trying to lowercase numerical value

--- src/app/common/filters/filters.coffee
+++ src/app/common/filters/filters.coffee
@@ -163,27 +163,29 @@

 .filter('taskFilter', ->
   (input, text) ->
-    matchText = text.toLowerCase()
-    if input
-      _.filter  input, (task) ->
-        if task?
-          project = task.project()
-          (task.definition.abbreviation.toLowerCase().indexOf(matchText) >= 0) ||
-            (task.definition.name.toLowerCase().indexOf(matchText) >= 0) ||
-            (project? && ( project.student_id.indexOf(matchText) >= 0 || project.name.toLowerCase().indexOf(matchText) >= 0 || (project.tutorial? && (project.tutorial.abbreviation.toLowerCase().indexOf(matchText) >= 0 || project.tutorName().toLowerCase().indexOf(matchText) >= 0))))
-        else
-          false
-    else
-      input
+    if _.isString text
+      matchText = text.toLowerCase()
+      if input
+        _.filter  input, (task) ->
+          if task?
+            project = task.project()
+            (task.definition.abbreviation.toLowerCase().indexOf(matchText) >= 0) ||
+              (task.definition.name.toLowerCase().indexOf(matchText) >= 0) ||
+              (project? && ( project.student_id.indexOf(matchText) >= 0 || project.name.toLowerCase().indexOf(matchText) >= 0 || (project.tutorial? && (project.tutorial.abbreviation.toLowerCase().indexOf(matchText) >= 0 || project.tutorName().toLowerCase().indexOf(matchText) >= 0))))
+          else
+            false
+      else
+        input
 )

 .filter('projectFilter', ->
   (input, text) ->
-    matchText = text.toLowerCase()
-    if input
-      _.filter  input, (project) -> (project?) && ( project.student_id.indexOf(matchText) >= 0 || project.name.toLowerCase().indexOf(matchText) >= 0 || (project.tutorial? && (project.tutorial.abbreviation.toLowerCase().indexOf(matchText) >= 0 || project.tutorName().toLowerCase().indexOf(matchText) >= 0)) )
-    else
-      input
+    if _.isString text
+      matchText = text.toLowerCase()
+      if input
+        _.filter  input, (project) -> (project?) && ( project.student_id.indexOf(matchText) >= 0 || project.name.toLowerCase().indexOf(matchText) >= 0 || (project.tutorial? && (project.tutorial.abbreviation.toLowerCase().indexOf(matchText) >= 0 || project.tutorName().toLowerCase().indexOf(matchText) >= 0)) )
+      else
+        input
 )

 .filter('taskForPortfolio', (taskService) ->


Remove CookeReaderWriter

Remove CookeReaderWriter

--- src/browser/CookieReaderWriter.coffee
+++ src/browser/CookieReaderWriter.coffee
@@ -1,62 +0,0 @@
-# global document: false, escape: false, unescape: false
-
-#\
-#|*|
-#|*|  :: cookies.js ::
-#|*|
-#|*|  A complete cookies reader/writer framework with full unicode support.
-#|*|
-#|*|  https://developer.mozilla.org/en-US/docs/DOM/document.cookie
-#|*|
-#|*|  This framework is released under the GNU Public License, version 3 or later.
-#|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
-#|*|
-#|*|  Syntaxes:
-#|*|
-#|*|  * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
-#|*|  * docCookies.getItem(name)
-#|*|  * docCookies.removeItem(name[, path])
-#|*|  * docCookies.hasItem(name)
-#|*|  * docCookies.keys()
-#|*|
-#\
-docCookies =
-  getItem: (sKey) ->
-    unescape(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) or null
-
-  setItem: (sKey, sValue, vEnd, sPath, sDomain, bSecure) ->
-    return false  if not sKey or /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)
-    sExpires = ""
-    if vEnd
-      switch vEnd.constructor
-        when Number
-          sExpires = (if vEnd is Infinity then "; expires=Fri, 31 Dec 9999 23:59:59 GMT" else "; max-age=" + vEnd)
-        when String
-          sExpires = "; expires=" + vEnd
-        when Date
-          sExpires = "; expires=" + vEnd.toGMTString()
-    document.cookie = escape(sKey) + "=" + escape(sValue) + sExpires + ((if sDomain then "; domain=" + sDomain else "")) + ((if sPath then "; path=" + sPath else "")) + ((if bSecure then "; secure" else ""))
-    true
-
-  removeItem: (sKey, sPath) ->
-    return false  if not sKey or not @hasItem(sKey)
-    document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ((if sPath then "; path=" + sPath else ""))
-    true
-
-  hasItem: (sKey) ->
-    (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test document.cookie
-
-  keys: -> # optional method: you can safely remove it!
-    aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/)
-    nIdx = 0
-
-    while nIdx < aKeys.length
-      aKeys[nIdx] = unescape(aKeys[nIdx])
-      nIdx++
-    aKeys
-
-exports.read = (key) ->
-  docCookies.getItem key
-
-exports.write = (key, value) ->
-  docCookies.setItem key, value


add some region tests

add some region tests

--- src/plotter.coffee
+++ src/plotter.coffee
@@ -389,7 +389,8 @@
         # are interpolation adds an eliptical (circular) arc to the path
         else if @mode is 'cw' or @mode is 'ccw'
           # throw if tool isn't a circle
-          unless @tools[@currentTool].stroke['stroke-linecap'] is 'round'
+          if not @tools[@currentTool].stroke['stroke-linecap'] is 'round' and
+          not @trace.region
             throw new Error "tool #{@currentTool} is not circular and cannot
                              stroke arcs"
           r = Math.sqrt end.i**2 + end.j**2


update test

update test

--- test/garble_test.coffee
+++ test/garble_test.coffee
@@ -14,5 +14,5 @@

   it 'does register a respond listener', ->
     expect(@robot.respond).to.have.been.calledWithMatch sinon.match( (val) ->
-      val.test /garble/
+      val.test /hubot garble this text/
     )


Addition of page_url helper

Addition of page_url helper

--- manoseimas/widget/frontend/scripts/utils.coffee
+++ manoseimas/widget/frontend/scripts/utils.coffee
@@ -13,6 +13,9 @@
 # Template Renderer
 render = (template, data) ->
     Handlebars.templates[template](data)
+
+page_url = (slug) ->
+    SERVER_URL + "/" + slug

 # Basic display helpers
 show = (elements...) ->
@@ -41,8 +44,7 @@
     (options.fn(v) for v in values).join('')

 # Constructs a URL to webpage identified by the slug
-Handlebars.registerHelper "page_url", (slug) ->
-    SERVER_URL + "/" + slug
+Handlebars.registerHelper "page_url", page_url

 # Fractional math
 Handlebars.registerHelper "fraction", (n, d, scale, options) ->


Rename unit test to update confusing test

--- test/unit/widget.coffee
+++ test/unit/widget.coffee
@@ -6,21 +6,21 @@
 require("../../src/support/Widget").call(ROOT)

 describe "widgets", ->
-  describe "el should be overideable", ->
+  describe "root should be overideable", ->
     before ->
       this.OptionWidget = new ROOT.Widget(
-        el: "div"
+        root: "div"
       )

       this.ExtendWidget = new (ROOT.Widget.extend({
-        el: "wow"
+        root: "wow"
       }))

     it "should persit when passed via the constructor", ->
-      assert.equal(this.OptionWidget.el, "div")
+      assert.equal(this.OptionWidget.root, "div")

     it "should persit when passed via an extend", ->
-      assert.equal(this.ExtendWidget.el, "wow")
+      assert.equal(this.ExtendWidget.root, "wow")

   describe "initialize should be overideable", ->
     beforeEach ->


Mixin response as a string, to comply with the jQuery and Reqwest API.

--- src/extras/testing/xhr_mocking.coffee
+++ src/extras/testing/xhr_mocking.coffee
@@ -16,7 +16,7 @@

       @mixin
         status: status
-        response: response
+        response: JSON.stringify(response)

       before?(this)


coffescript string interpolation

coffescript string interpolation

--- src/scripts/collections/featured-books.coffee
+++ src/scripts/collections/featured-books.coffee
@@ -4,8 +4,10 @@
   $ = require('jquery')
   settings = require('settings')

+  archive = "#{location.protocol}//#{settings.cnxarchive.host}" #Change this in settings.js for development purposes - devarchive.cnx.org
+
   return new class FeaturedBooks extends Backbone.Collection
-    url: settings.cnxarchive + '/extras' # Change this in settings.js for development purposes - devarchive.cnx.org
+    url: "#{archive}/extras"
     model: FeaturedBook

     parse: (response) ->
@@ -15,10 +17,13 @@
         book.title = book.title
         book.description = () ->
           abstract = book.abstract
-          truncatedText = $(abstract).text().substring(0,175) + '...'
-          return truncatedText
-        book.cover = settings.devArchive + book.resourcePath
-        book.link =  '/contents/' + book.id
+          abstractText = $(abstract).text()
+          if abstract isnt null
+           return abstractText.substring(0,175) + '...'
+          else
+           return ''
+        book.cover = "#{archive}#{book.resourcePath}"
+        book.link = "contents/#{book.id}"

       featuredLinks = _.shuffle(books)
       return featuredLinks


remove block comment

remove block comment

--- src/server/controllers/mixins/resource.coffee
+++ src/server/controllers/mixins/resource.coffee
@@ -128,9 +128,7 @@
           This would sort ascending by createdAt and descending by title. Ascending is assumed by default
     ###
     buildConditionsFromParameters: (parameters) ->
-      ###
-          Some parameter names are reserved and have a special meaning
-      ###
+      # Some parameter names are reserved and have a special meaning
       reservedParams = ["sort","include","includes","limit","offset","method","controller","action","format"]

       # only accept parameters that represent an attribute for where conditions


use bundled-in coffee

use bundled-in coffee

--- server.coffee
+++ server.coffee
@@ -1,3 +1,4 @@
+#!node_modules/coffee-script/bin/coffee
 'use strict'