test: expected should always exist

test: expected should always exist

--- test/index.coffee
+++ test/index.coffee
@@ -148,6 +148,7 @@
       .flatten().value().join ';'

 create_configs_from_base = (config) ->
+  _(config).defaults expected: [] # For error cases
   new_configs = [config]
   # If on is a string, also try the hash version of on to verify it's the same
   new_configs.push _.extend {}, config, {on: _.object config.on, config.on} if _(config.on).isString()
@@ -162,8 +163,7 @@
         inner: (expected_pair) -> expected_pair[0]? and expected_pair[1]?
         full: -> true
       _(type_spec).map (filter, type) ->
-        _.extend {}, new_config, {type},
-          if new_config.expected then {expected: _(new_config.expected).filter filter} else {}
+        _.extend {}, new_config, {type}, {expected: _(new_config.expected).filter filter}
     .flatten()
     .value()

@@ -171,14 +171,12 @@
   _(BASE_CONFIGS).each (base_config, i) ->
     _(create_configs_from_base base_config).each (config, j) ->
       test_num = "#{i + 1}.#{j + 1}"
-      if config.error
-        asserts = (err, results) -> assert.equal err?.message, config.error
-      else
-        asserts = (err, results) ->
-          assert.ifError err
-          assert.deepEqual sort_pairs(results), sort_pairs(config.expected)
       it "##{test_num} #{if config.error then 'fails' else 'joins'} #{JSON.stringify config}", (done) ->
         [left, right] = _([config.left, config.right]).map (arr) -> _(arr).stream().stream()
         _(join left, right, _(config).pick 'on', 'type').stream().run (err, results) ->
-          asserts err, results
+          if config.error
+            assert.equal err?.message, config.error
+          else
+            assert.ifError err
+            assert.deepEqual sort_pairs(results), sort_pairs(config.expected)
           done()


Make sure 'matches' errors work

Make sure 'matches' errors work

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- test/items.coffee
+++ test/items.coffee
@@ -108,6 +108,17 @@
           find(where name: eq: "Hextech Gunblade", matches: /exte[c|z]h/).should.eql [hextechGunblade]
           find(where name: eq: "Hextech Gunblade", matches: /exte[s|z]h/).should.eql []

+        it "should error if the 'matches' filter is given something other than a regex", ->
+          try
+            find(where name: eq: "Hextech Gunblade", matches: {})
+            chai.assert.fail 0, 0, "Invalid value didn't error!"
+          catch e
+
+          try
+            find(where name: eq: "Hextech Gunblade", matches: 3.14)
+            chai.assert.fail 0, 0, "Invalid value didn't error!"
+          catch e
+
         it "should work with multiple query filters applied at once", ->
           find(where name: "Hextech Gunblade", ap: lt: 66, gt: 54).should.eql [hextechGunblade]
           find(where name: "Hextech Gunblade", ap: lt: 69, gt: 65).should.eql []


add selection count specs

add selection count specs

--- spec/built-in-tiles-spec.coffee
+++ spec/built-in-tiles-spec.coffee
@@ -187,8 +187,12 @@
       it "updates the selection count in the status bar", ->
         jasmine.attachToDOM(workspaceElement)

+        editor.setSelectedBufferRange([[0, 0], [0, 0]])
+        expect(selectionCount.textContent).toBe ''
         editor.setSelectedBufferRange([[0, 0], [0, 2]])
-        expect(selectionCount.textContent).toBe '(2)'
+        expect(selectionCount.textContent).toBe '(1, 2)'
+        editor.setSelectedBufferRange([[0, 0], [1, 30]])
+        expect(selectionCount.textContent).toBe '(2, 60)'

     describe "when the active pane item does not implement getCursorBufferPosition()", ->
       it "hides the cursor position view", ->
@@ -249,6 +253,24 @@
           atom.commands.add('atom-text-editor', 'go-to-line:toggle', eventHandler)
           cursorPosition.click()
           expect(eventHandler).toHaveBeenCalled()
+
+    describe 'the selection count tile', ->
+      beforeEach ->
+        atom.config.set('status-bar.selectionCountFormat', '%L foo %C bar selected')
+
+      it 'respects a format string', ->
+        jasmine.attachToDOM(workspaceElement)
+        editor.setSelectedBufferRange([[0, 0], [1, 30]])
+        expect(selectionCount.textContent).toBe '2 foo 60 bar selected'
+
+      it 'updates when the configuration changes', ->
+        jasmine.attachToDOM(workspaceElement)
+        editor.setSelectedBufferRange([[0, 0], [1, 30]])
+        expect(selectionCount.textContent).toBe '2 foo 60 bar selected'
+
+        atom.config.set('status-bar.selectionCountFormat', 'Selection: baz %C quux %L')
+        expect(selectionCount.textContent).toBe 'Selection: baz 60 quux 2'
+

   describe "the git tile", ->
     gitView = null


Fix coffeelint

Fix coffeelint

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -22,14 +22,15 @@
       it "does not prevent the event's default action", ->
         event = buildKeydownEvent(key: 'q')
         keymapManager.handleKeyboardEvent(event)
-        assert(!event.defaultPrevented)
+        assert(not event.defaultPrevented)
     describe "when the keystroke matches one binding on any particular element", ->
       [events, elementA, elementB] = []

       beforeEach ->
-        elementA = appendContent $$ ->
-          @div class: 'a', ->
-            @div class: 'b c'
+        elementA = appendContent($$(->
+          @div { 'class': 'a' }, ->
+            @div 'class': 'b c'
+        ))
         elementB = elementA.firstChild

         events = []
@@ -70,7 +71,7 @@

           event = buildKeydownEvent(key: 'y', ctrlKey: true, target: elementB)
           keymapManager.handleKeyboardEvent(event)
-          assert(!event.defaultPrevented)
+          assert(not event.defaultPrevented)

           assert.equal(events.length, 3)
           assert.equal(events[0].type, 'y-command')
@@ -149,9 +150,10 @@
       [elementA, elementB, events] = []

       beforeEach ->
-        elementA = appendContent $$ ->
-          @div class: 'a', ->
-            @div class: 'b c d'
+        elementA = appendContent($$(->
+          @div { 'class': 'a' }, ->
+            @div 'class': 'b c'
+        ))
         elementB = elementA.firstChild

         events = []
@@ -204,12 +206,17 @@
       [workspace, editor, editor2, events, inputElement, input2Element] = []

       beforeEach ->
-        workspace = appendContent $$ ->
-          @div class: 'workspace', ->
-            @div class: 'editor', ->
-              @input class: 'input', type: 'text'
-            @div class: 'editor2', ->
-              @input class: 'input2', type: 'text'
+        workspace = appendContent($$(->
+          @div { 'class': 'workspace' }, ->
+            @div { 'class': 'editor' }, ->
+              @input
+                'class': 'input'
+                type: 'text'
+            @div { 'class': 'editor2' }, ->
+              @input
+                'class': 'input2'
+                type: 'text'
+        ))
         editor = workspace.firstChild
         editor2 = editor.nextSibling
         inputElement = document.querySelector('.input')
@@ -384,9 +391,12 @@
       [events, elementA] = []

       beforeEach ->
-        elementA = appendContent $$ ->
-          @div class: 'a', ->
-            @input class: 'input', type: 'text'
+        elementA = appendContent($$(->
+          @div { 'class': 'a' }, ->
+            @input
+              'class': 'input'
+              type: 'text'
+        ))
         inputElement = document.querySelector('.input')
         inputElement.focus()

@@ -472,7 +482,9 @@
         assert.deepEqual(events, ['abc-secret-code'])

     it "only counts entire keystrokes when checking for partial matches", ->
-      element = $$ -> @div class: 'a'
+      element = $$(->
+        @div 'class': 'a'
+      )
       keymapManager.add 'test',
         '.a':
           'ctrl-alt-a': 'command-a'
@@ -486,7 +498,9 @@
       assert.deepEqual(events, ['command-b'])

     it "does not enqueue keydown events consisting only of modifier keys", ->
-      element = $$ -> @div class: 'a'
+      element = $$(->
+        @div 'class': 'a'
+      )
       keymapManager.add 'test', '.a': 'ctrl-a ctrl-alt-b': 'command'
       events = []
       element.addEventListener 'command', -> events.push('command')
@@ -502,7 +516,9 @@
       assert.deepEqual(events, ['command'])

     it "allows solo modifier-keys to be bound", ->
-      element = $$ -> @div class: 'a'
+      element = $$(->
+        @div 'class': 'a'
+      )
       keymapManager.add 'test', '.a': 'ctrl': 'command'
       events = []
       element.addEventListener 'command', -> events.push('command')
@@ -511,10 +527,11 @@
       assert.deepEqual(events, ['command'])

     it "simulates bubbling if the target is detached", ->
-      elementA = $$ ->
-        @div class: 'a', ->
-          @div class: 'b', ->
-            @div class: 'c'
+      elementA = $$(->
+        @div { 'class': 'a' }, ->
+          @div { 'class': 'b' }, ->
+            @div 'class': 'c'
+      )
       elementB = elementA.firstChild
       elementC = elementB.firstChild

@@ -574,7 +591,7 @@

       event = buildKeydownEvent(key: 'A', shiftKey: true, target: document.body)
       keymapManager.handleKeyboardEvent(event)
-      assert(!event.defaultPrevented)
+      assert(not event.defaultPrevented)

     it "rejects bindings with an empty command and logs a warning to the console", ->
       stub(console, 'warn')
@@ -583,7 +600,7 @@

       event = buildKeydownEvent(key: 'A', shiftKey: true, target: document.body)
       keymapManager.handleKeyboardEvent(event)
-      assert(!event.defaultPrevented)
+      assert(not event.defaultPrevented)

     it "rejects bindings without a command and logs a warning to the console", ->
       stub(console, 'warn')
@@ -592,7 +609,7 @@

       event = buildKeydownEvent(key: 'A', shiftKey: true, target: document.body)
       keymapManager.handleKeyboardEvent(event)
-      assert(!event.defaultPrevented)
+      assert(not event.defaultPrevented)

     it "returns a disposable allowing the added bindings to be removed", ->
       disposable1 = keymapManager.add 'foo',
@@ -783,7 +800,7 @@
         assert.deepEqual(keystrokes, ['ctrl-d', 'ctrl-c', 'ctrl-a'])

   describe "::loadKeymap(path, options)", ->
-    @timeout(5000)
+    this.timeout(5000)

     beforeEach ->
       getFakeClock().uninstall()
@@ -887,7 +904,7 @@
           keymapManager.onDidReloadKeymap -> reloaded = true

           afterWaiting = ->
-            assert(!reloaded)
+            assert(not reloaded)

             # Can start watching again after cancelling
             keymapManager.loadKeymap(keymapFilePath, watch: true)


fixed broken 0.6 compatibility

fixed broken 0.6 compatibility

--- spec/other-tests/exports.coffee
+++ spec/other-tests/exports.coffee
@@ -5,6 +5,8 @@
 jscov = require 'jscov'
 powerfs = require 'powerfs'
 opra = require jscov.cover('../..', 'lib', 'opra.js')
+
+existsSync = fs.existsSync || path.existsSync # Compatible with node 0.6 and 0.8

 describe 'something', ->

@@ -26,8 +28,8 @@
         }
       }, (err) ->
         should.not.exist err
-        fs.existsSync(path.resolve(outdir, 'index.html')).should.eql true
-        fs.existsSync(path.resolve(outdir, 'bots')).should.eql true
-        fs.existsSync(path.resolve(outdir, 'extras/crap.txt')).should.eql true
+        existsSync(path.resolve(outdir, 'index.html')).should.eql true
+        existsSync(path.resolve(outdir, 'bots')).should.eql true
+        existsSync(path.resolve(outdir, 'extras/crap.txt')).should.eql true
         fs.readdirSync(path.resolve(outdir, '.code')).should.have.length 3
         done()


Concise up long, long test description.

Concise up long, long test description.

--- test/solid.coffee
+++ test/solid.coffee
@@ -70,7 +70,7 @@
       done() # TODO

   describe "configuration", ->
-    it "should start a server on a port other than the specified port rather than the default one", (done) ->
+    it "can take a port different from the default one", (done) ->
       port = 65432
       server = solid {port: port}, ->
                   "/" : -> "home"


IDE: Fixed paneType argument for helper method.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -276,7 +276,8 @@
     for ideView in @ideViews
       for pane in ideView.tabView.panes
         view = pane.getSubViews().first
-        if paneType and view.getOptions().paneType is paneType
+        if paneType
+          if view.getOptions().paneType is paneType
             callback view
         else
           callback view


AppController: Add maximums to split view.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -61,6 +61,7 @@
         name          : 'BaseSplit'
         sizes         : [ 234, null ]
         minimums      : [ 234, null ]
+        maximums      : [ 400, null ]
         views         : [
           {
             type      : 'custom'


Fix bad rebase merge (equal, not eq)

Fix bad rebase merge (equal, not eq)

--- test/tests.coffee
+++ test/tests.coffee
@@ -14,7 +14,7 @@
   destPath = path.join dataPath, testName
   cleanup = (callback) -> rimraf destPath, callback
   cleanup (error) ->
-    eq not error, true, "path cleaned up properly"
+    equal not error, true, "path cleaned up properly"
     options?.output = destPath
     Docco.document sources, options, ->
       files       = []


Convert transaction amount inputs to Numbers, fixes #11

--- client/tx/lib/tx-builder.coffee
+++ client/tx/lib/tx-builder.coffee
@@ -34,7 +34,7 @@
     val_el = $(this).closest('.address').find('[name=value]')
     spent = el.find('.address input[name=value]').not(val_el)
       .filter(->!!@value)
-      .map(-> Util.parseValue @value).get()
+      .map(-> +Util.parseValue @value).get()
     remain = Math.max 0, balance - (sum_inputs spent) - fees
     val_el.val Util.formatValue remain


improve the test that migrations are properly removed by rollback

--- test/rollback.coffee
+++ test/rollback.coffee
@@ -16,16 +16,20 @@

   it 'should cleanup the migrations collection properly', (done) ->
     dir = path.join __dirname, 'migrations'
+    migrationsCol = db.collection '_migrations'
+
     migrator.runFromDir dir, (err, res) ->
       return done(err) if err
-      migrator.rollback (err, res) ->
+      migrationsCol.find().count (err, count) ->
         return done(err) if err
-        coll.find().count (err, count) ->
+        count.should.be.equal 3
+        migrator.rollback (err, res) ->
           return done(err) if err
-          count.should.be.equal 0
-
-          migrationsCol = db.collection '_migrations'
-          migrationsCol.find().count (err, count) ->
+          coll.find().count (err, count) ->
             return done(err) if err
             count.should.be.equal 0
-            done()
+
+            migrationsCol.find().count (err, count) ->
+              return done(err) if err
+              count.should.be.equal 0
+              done()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/tweening.coffee
+++ test/tweening.coffee
@@ -12,6 +12,5 @@

     equals o.I.x, i

-
 # Clear out the module
 module()


Added functionality for moving items around in a list, for instance when one needs to sort them.

--- list.coffee
+++ list.coffee
@@ -37,15 +37,20 @@
 	renderControllers: (collection) ->
 		controllers = @controllers || {default: @controller}
 		# Dynamically create child controllers
+
+		#Also, change the actual div order!
+		@deactivateAndKillOrphans(@getChildren(), collection)
+
 		for item, i in collection
 			child = @getOrCreateChild item, controllers[item.getClassName()] or controllers.default
+
 			child.listIndex = i
+			child.moveTo i if child.moveTo
 			child.activate()

+		@trigger 'afterRender', @
+
 		console.log "children before deactivate: #{@getChildren().length}" if @debug
-
-		@deactivateAndKillOrphans(@getChildren(), collection)
-		@orderChildren(@getChildren(), collection)

 	getOrCreateChild: (item, controller) ->
 		child = @getChildById(item.getClassName() + item.id)
@@ -75,9 +80,6 @@

 			orphan.deactivate()

-	# Reorder the children in the DOM according to the collection order.
-	orderChildren: (children, collection) ->
-
 	click: (e) ->
 		item = $(e.currentTarget).item()
 		@trigger('select', item, $(e.currentTarget))


add tests for parsing comments

add tests for parsing comments

--- tests/html.coffee
+++ tests/html.coffee
@@ -32,6 +32,7 @@
 test_parse "
foo
", "
foo
"
 test_parse '
1,2
', '
1,2
'
 test_parse 'text', 'text' # parsing lone text as text nodes
+test_parse 'foo'
 test_parse 'HelloWorld'
 test_escape '
', '<p>'
 test_escape '&', '&'


dom style keys are camelCase

dom style keys are camelCase

--- src/modules/cursor.coffee
+++ src/modules/cursor.coffee
@@ -22,7 +22,7 @@
   nameNode = @container.ownerDocument.createElement('span')
   nameNode.classList.add('cursor-name')
   nameNode.textContent = name
-  inner.style['background-color'] = nameNode.style['background-color'] = color
+  inner.style.backgroundColor = nameNode.style.backgroundColor = color
   cursor.appendChild(nameNode)
   cursor.appendChild(inner)
   return cursor


Unmocked http call in cucumber cli stepdefs

--- test/cucumber/step_definitions/cli_stepdefs.coffee
+++ test/cucumber/step_definitions/cli_stepdefs.coffee
@@ -4,11 +4,11 @@

   Given /^you record expected raw HTTP messages:$/, (cmd, callback) ->
-
+    @commandBuffer += ";" + cmd
     callback()

   Given /^you record real raw HTTP message:$/, (cmd, callback) ->
-
+    @commandBuffer += ";" + cmd
     callback()

   When /^you validate messages using following Gavel command:$/, (cmd, callback) ->
@@ -20,8 +20,7 @@
     callback()

   Then /^exit status is (\d+)$/, (expectedExitStatus, callback) ->
-
-    cmd = "PATH=$PATH:" + process.cwd() + "/bin; cd ./test/fixtures " + @commandBuffer
+    cmd = "PATH=$PATH:" + process.cwd() + "/bin; cd /tmp-gavel-* " + @commandBuffer
     console.error cmd
     child = exec cmd, (error, stdout, stderr) ->
       if error


zwave: fix config key mismatch

zwave: fix config key mismatch

--- lib/adapters/zwave/ZWaveAdapter.coffee
+++ lib/adapters/zwave/ZWaveAdapter.coffee
@@ -9,7 +9,7 @@
     @setValid false

   start: ->
-    @_ozw = new OpenZWave(@get('device'))
+    @_ozw = new OpenZWave(@get('controllerDevice'))
     @_ozw.on 'driver ready', (homeid) =>
       @log 'debug', 'Z-Wave connected'
       @setValid true


Fixed unproper behavior in IE, when html4_normalize_path=true.

--- lib/assets/javascripts/_page.js.coffee
+++ lib/assets/javascripts/_page.js.coffee
@@ -14,10 +14,7 @@
           @options.html4_normalize_path == true &&
           window.location.pathname != @options.html4_root_path

-        window.location.href = "#{window.location.protocol}//
-          #{window.location.host}
-          #{@options.html4_root_path}#!
-          #{window.location.pathname}"
+        window.location.href = "#{window.location.protocol}//#{window.location.host}#{@options.html4_root_path}#!#{window.location.pathname}"

       if window.location.hash.indexOf('#!') != -1
         self._call(self._make_state(window.location.hash.substring(2)))


QUALITY: Remove deprecated focus function from tasks state

--- src/app/units/states/tasks/tasks.coffee
+++ src/app/units/states/tasks/tasks.coffee
@@ -34,7 +34,6 @@
     selectedTask: null,
     onSelectedTaskChange: (task) ->
       $scope.taskData.temporaryTaskId = task.id
-      $scope.focusCommentsTextarea?()
       setTaskIdUrlParm(task?.id)
   }

@@ -64,6 +63,4 @@
     # unnecessarily; doing so will cause a re-request in the task
     # list which is not required.
     $event.preventDefault() if fromState == toState
-
-  $scope.focusCommentsTextarea = null
 )


Fixed broken syntax in utils module

Fixed broken syntax in utils module

--- src/readmill/utils.coffee
+++ src/readmill/utils.coffee
@@ -1,6 +1,6 @@
 #  Useful utility functions.
-Annotator.Readmill.utils =
-  # Create private jQuery variable for this class.
+Annotator.Readmill.utils = do ->
+  # Create private jQuery variable for this object.
   jQuery = Annotator.$

   # Public: Binds context of handler methods (beginning with "_on") on the


oops, extra \n

oops, extra \n

--- src/replay/catalog.coffee
+++ src/replay/catalog.coffee
@@ -84,7 +84,7 @@
           for chunks in request.body
             body += chunks[0]
           writeHeaders file, body: jsStringEscape(body)
-        file.write "\n\n"
+        file.write "\n"
         # Response part
         file.write "#{response.status || 200} HTTP/#{response.version || "1.1"}\n"
         writeHeaders file, response.headers


fix a bug in KDRouter, ass :name?/Dashboard route

--- core/KDRouter.coffee
+++ core/KDRouter.coffee
@@ -18,6 +18,8 @@
     @tree   = {} # this is the tree for quick lookups
     @routes = {} # this is the flat namespace containing all routes
     @addRoutes routes
+
+  listen:->
     # this handles the case that the url is an "old-style" hash fragment hack.
     if location.hash.length
       hashFragment = location.hash.substr 1
@@ -92,8 +94,6 @@
     @addRoute route, listener  for own route, listener of routes

   handleRoute:(userRoute, options={})->
-    # log userRoute, options
-    # debugger
     [frag, query...] = (userRoute ? @getDefaultRoute?() ? '/').split '?'

     query = @utils.parseQuery query.join '&'
@@ -143,6 +143,8 @@
       if listeners?.length
         listener.call @, routeInfo, state, path  for listener in listeners

+    return this
+
   handleQuery:(query)->
     query = @utils.stringifyQuery query  unless 'string' is typeof query
     return  unless query.length


cleanup

cleanup

--- app/assets/javascripts/dances.coffee
+++ app/assets/javascripts/dances.coffee
@@ -45,7 +45,6 @@

 move_menu_options = (formation,who) ->
-  console.log("formation = "+formation)
   labels =
     switch formation
       when "short_lines" then (


fixed tests whoops

fixed tests whoops

--- services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee
+++ services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee
@@ -332,7 +332,7 @@
 			@req.body =
 				planName:"student"

-			@res.redirect = ()=>
+			@res.send = ()=>
 				@SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true
 				done()

@@ -343,9 +343,8 @@
 			@req.body =
 				planName:"collaborator"

-			@res.redirect = (url)=>
+			@res.send = (url)=>
 				@SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true
-				url.should.equal  "/user/subscription/thank-you"
 				done()

 			@SubscriptionController.processUpgradeToAnnualPlan @req, @res


REMOVE: global, ADD: deffered, ADD: title, description, image

--- vendor/assets/javascripts/socialAdapters.js.coffee
+++ vendor/assets/javascripts/socialAdapters.js.coffee
@@ -4,9 +4,8 @@
   constructor: (@$container, @settings) ->
     if @settings.selectors[@type]
       @$selector = @$container.find(@settings.selectors[@type])
-      @$selectorGlobal = $(@settings.selectors[@type])

-    if @$selector.length and @$selectorGlobal.length
+    if @$selector.length
       @url = @settings.url
       @getCount()
       @initClick()
@@ -22,9 +21,13 @@
     super $container, settings, @type

   getCount: ->
+    deferred = $.Deferred()
+    deferred.then (number) =>
+      @$selector.parent().find('span').text number
+
     window.ODKL ||= {}
-    window.ODKL.updateCount = (idx, number) =>
-      @$selectorGlobal.parent().find('span').text number
+    window.ODKL.updateCount = (idx, number) ->
+      deferred.resolve number

     $.ajax
       url: "http://ok.ru/dk?st.cmd=extLike&uid=odklcnt0&ref=#{@url}"
@@ -33,7 +36,7 @@
   initClick: ->
     @$selector.on 'click', (e) =>
       e.preventDefault()
-      title = encodeURIComponent document.title
+      title = @settings.title ? encodeURIComponent(document.title)
       open "http://ok.ru/dk?st.cmd=addShare&st._surl=#{@url}&title=#{title}", "_blank", "scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0"

 class App.SocialGp extends App.SocialBase
@@ -74,7 +77,7 @@
   initClick: ->
     @$selector.on 'click', (e) =>
       e.preventDefault()
-      title = encodeURIComponent document.title
+      title = @settings.title ? encodeURIComponent(document.title)
       open "https://twitter.com/intent/tweet?text=#{title}&url=#{@url}", "_blank", "scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0"

 class App.SocialFb extends App.SocialBase
@@ -94,9 +97,9 @@
   initClick: ->
     @$selector.on 'click', (e) =>
       e.preventDefault()
-      summary = encodeURIComponent $("meta[property='og:description']").attr("content")
-      image = encodeURIComponent $("meta[property='og:image']").attr("content")
-      title = encodeURIComponent document.title
+      summary = @settings.description ? encodeURIComponent($("meta[property='og:description']").attr("content"))
+      image = @settings.image ? encodeURIComponent($("meta[property='og:image']").attr("content"))
+      title = @settings.title ? encodeURIComponent(document.title)
       params = "s=100&p[url]=#{@url}&p[title]=#{title}&p[summary]=#{summary}&p[images][0]=#{image}"
       open "http://www.facebook.com/sharer.php?m2w&#{params}", "_blank", "scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0"

@@ -107,10 +110,14 @@
     super $container, settings, @type

   getCount: ->
+    deferred = $.Deferred()
+    deferred.then (number) =>
+      @$selector.parent().find('span').text number
+
     window.VK ||= {}
     VK.Share ||= {}
-    VK.Share.count = (index, number) =>
-      @$selectorGlobal.parent().find('span').text number
+    VK.Share.count = (index, number) ->
+      deferred.resolve number

     $.ajax
       url: "http://vk.com/share.php?act=count&index=1&url=#{@url}"
@@ -119,5 +126,5 @@
   initClick: ->
     @$selector.on 'click', (e) =>
       e.preventDefault()
-      title = encodeURIComponent document.title
+      title = @settings.title ? encodeURIComponent(document.title)
       open "http://vk.com/share.php?url=#{@url}&title=#{title}", "_blank", "scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0"


fixed undefined error by chaning require order

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1,29 +1,28 @@
+jraphical        = require 'jraphical'
+Regions          = require 'koding-regions'
+request          = require "request"
 { argv }         = require 'optimist'
 KONFIG           = require('koding-config-manager').load("main.#{argv.c}")
-Regions          = require 'koding-regions'
-request          = require "request"
-jraphical        = require 'jraphical'
 Flaggable        = require '../../traits/flaggable'
 KodingError      = require '../../error'
 { extend, uniq } = require 'underscore'

-
 module.exports = class JUser extends jraphical.Module

   { v4: createId }                   = require 'node-uuid'
   { Relationship }                   = jraphical
   { secure, signature, daisy, dash } = require 'bongo'

-  JLog                               = require '../log'
+  JAccount                           = require '../account'
+  JSession                           = require '../session'
+  JInvitation                        = require '../invitation'
   JName                              = require '../name'
   JGroup                             = require '../group'
+  JLog                               = require '../log'
+  JPaymentPlan                       = require '../payment/plan'
+  JPaymentSubscription               = require '../payment/subscription'
+  ComputeProvider                    = require '../computeproviders/computeprovider'
   Tracker                            = require '../tracker'
-  JSession                           = require '../session'
-  JAccount                           = require '../account'
-  JInvitation                        = require '../invitation'
-  JPaymentPlan                       = require '../payment/plan'
-  ComputeProvider                    = require '../computeproviders/computeprovider'
-  JPaymentSubscription               = require '../payment/subscription'

   @bannedUserList = ['abrt','amykhailov','apache','about','visa','shared-',
                      'cthorn','daemon','dbus','dyasar','ec2-user','http',


fix the ProofObligationView for the new highlighter

--- lib/views/proof-obligation-view.coffee
+++ lib/views/proof-obligation-view.coffee
@@ -6,7 +6,8 @@
     @obligation = params.obligation
     @highlightingInfo = params.highlightingInfo
     if @highlightingInfo?
-      text = highlighter.highlight @obligation, @highlightingInfo
+      highlighting = highlighter.highlight @obligation, @highlightingInfo
+      text = highlighter.highlightToString highlighting
       @html text
     else
       @text @obligation


Update jasmineEmulator.coffee

Update jasmineEmulator.coffee

--- test/jasmineEmulator.coffee
+++ test/jasmineEmulator.coffee
@@ -5,13 +5,13 @@
   befores   = {}
   tests     = {}
   afters    = {}
-  describes = {}
+  root = {describes: {}}
   currentPath = []
   currentPath::last = -> this[@length - 1]
   currentDescribe = undefined

   cleanup = ->
-    eval "delete describes.#{currentPath.join('.')}]"
+    eval "delete root.#{currentPath.join('.')}"
     currentPath.pop()

   {
@@ -25,14 +25,17 @@
       tests[currentPath.last()].push fn

     describe: (name, fn)->
+      _describes = eval "root.#{currentPath.join('.')}" if currentPath.length > 0
+      _describes ||= root.describes
       currentPath.push name
+
       befores[name]   = []
       tests[name]     = []
       afters[name]    = []
-      describes[name] = {func: -> (fn() && cleanup()), name: name, describes: {}}
+      _describes[name] = {func: -> (fn() && cleanup()), name: name, describes: {}}

     run: (context = this)->
-      for _, desc of describes
+      for _, desc of root.describes

         desc.func.call(context)


added test for 3267b433dc

added test for 3267b433dc

--- test/mocha-phantomjs.coffee
+++ test/mocha-phantomjs.coffee
@@ -318,9 +318,9 @@
         @runner done, ['-A', 'mochaUserAgent', fileURL('user-agent')], (code, stdout, stderr) ->
           expect(stdout).to.match /^mochaUserAgent/

-      it 'has a custom user agent via setting flag', (done) ->
-        @runner done, ['-s', 'userAgent=mochaUserAgent', fileURL('user-agent')], (code, stdout, stderr) ->
-          expect(stdout).to.match /^mochaUserAgent/
+      it 'has a custom user agent via setting flag and 2 equal signs', (done) ->
+        @runner done, ['-s', 'userAgent=mocha=UserAgent', fileURL('user-agent')], (code, stdout, stderr) ->
+          expect(stdout).to.match /^mocha=UserAgent/

     describe 'cookies', ->


workers: forcedRecipients email is added

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -130,8 +130,8 @@
   Tracker = require './models/tracker'
   Tracker.setMqClient broker.connection

-  { forcedRecipient } = KONFIG.email
-  Tracker.identify forcedRecipient  if forcedRecipient
+  { forcedRecipientEmail } = KONFIG.email
+  Tracker.identify forcedRecipientEmail  if forcedRecipientEmail

 console.info "Koding Social Worker #{process.pid} has started."


fix fuzzy box

fix fuzzy box

--- graphics.coffee
+++ graphics.coffee
@@ -404,7 +404,7 @@
       @ctx.save()
       @ctx.strokeStyle = 'yellow'
       @ctx.lineWidth = 4
-      @ctx.strokeRect(0, 0+idx*ocs, ocs, ocs)
+      @ctx.strokeRect(-.5, -.5+idx*ocs, ocs, ocs)
       @ctx.restore()

   animatePopIn: (anims, low_scale, scale, pos) ->


Collaboration: Set and update participants.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -726,32 +726,46 @@

     @rtm.on 'FileLoaded', (doc) =>
       @realTimeDoc = doc
-
-      @getWorkspaceSnaphot()
-      @writeWorkspaceSnapshot()  if not @snapshot and @amIHost
-
-    @rtm.on 'MapValueChanged', (map, v) =>
-      @activeTabView.emit 'CollaborationDataUpdated', map.values()
-
-
-  writeWorkspaceSnapshot: ->
-    return  unless @realTimeDoc
-
-    @rtm.create 'map', @realTimeDoc, 'snapshot', @createWorkspaceSnapshot()
-
-
-  getWorkspaceSnaphot: ->
-    return  unless @realTimeDoc
-
-    @snapshot = @rtm.getFromModel @realTimeDoc, 'snapshot'
-
-    return @snapshot
+      nickname     = KD.nick()
+
+      @getParticipants()
+
+      if not @participants
+        @addParticipant yes  if @amIHost
+      else
+        if @participants.indexOf(nickname) is -1
+          log 'acetz: I am not in the participants list, adding myself'
+          @addParticipant()
+        else
+          log 'acetz: I am alread in participants lists'
+
+      log 'acetz: participants:', @participants.asArray()
+
+
+  getParticipants: ->
+    @participants = @rtm.getFromModel @realTimeDoc, 'participants'
+
+
+  addParticipant: (initalizeList = no) ->
+    nickname = KD.nick()
+
+    if initalizeList
+      @rtm.create 'list', @realTimeDoc, 'participants', []
+
+    @participants = @rtm.getFromModel @realTimeDoc, 'participants'
+    @participants.push nickname
+
+    @rtm.create 'map', @realTimeDoc, nickname, @createWorkspaceSnapshot()
+
+    log 'acetz: participant added:', @participants.asArray()

   createWorkspaceSnapshot: ->
     panes = {}

     @forEachSubViewInIDEViews_ (pane) ->
+      return unless pane.serialize
+
       if pane.options.paneType is 'editor'
         unless pane.file.path is 'localfile:/Untitled.txt'
           data = pane.serialize()
@@ -766,14 +780,15 @@
   writeChange: (change) ->
     {context} = change

-    return  if not @realTimeDoc or not context or not @snapshot
-
-    return  if change.origin is KD.nick()
+    return  if not @realTimeDoc or not context
+
+    {paneHash} = context
+    map        = @rtm.getFromModel @realTimeDoc, KD.nick()

     switch change.type

       when 'NewPaneCreated'
-        @snapshot.set context.paneHash, change
+        map.set paneHash, change

       when 'PaneRemoved'
-        @snapshot.delete context.paneHash
+        map.delete paneHash


Collaboration: Fixed participant map creation.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -739,22 +739,22 @@
       @participants = @rtm.getFromModel 'participants'
       @changes      = @rtm.getFromModel 'changes'

+      unless @participants
+        @participants = @rtm.create 'list', 'participants', []
+
       unless @changes
         @changes = @rtm.create 'list', 'changes', []

-      if not @participants
-        @addParticipant yes  if @amIHost
+      isInList = no
+
+      @participants.asArray().forEach (participant) =>
+        isInList = yes  if participant.nickname is nickname
+
+      if not isInList
+        log 'acetz: I am not in the participants list, adding myself'
+        @addParticipant()
       else
-        isInList = no
-
-        @participants.asArray().forEach (participant) =>
-          isInList = yes  if participant.nickname is nickname
-
-        if not isInList
-          log 'acetz: I am not in the participants list, adding myself'
-          @addParticipant()
-        else
-          log 'acetz: I am alread in participants lists'
+        log 'acetz: I am already in participants lists'

       log 'acetz: participants:', @participants.asArray()

@@ -785,18 +785,14 @@
         @participants.insert index, user

-  addParticipant: (initalizeList = no) ->
+  addParticipant: ->
     {hash, nickname} = KD.whoami().profile

-    if initalizeList
-      @rtm.create 'list', 'participants', []
-
-    @participants = @rtm.getFromModel 'participants'
     @participants.push { nickname, hash }

     @rtm.create 'map', "#{nickname}Snapshot", @createWorkspaceSnapshot()

-    log 'acetz: participant added:', @participants.asArray()
+    log 'acetz: participant added:', nickname

   createWorkspaceSnapshot: ->


Finder: Fix the Uploader o_O

Finder: Fix the Uploader o_O

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -29,7 +29,7 @@
       partial  : "Ace Editor"

   getMountVMButton: ->
-    @uploaderPlaceholder = new KDButtonView
+    return new KDButtonView
       title    : "Mount other VMs"
       cssClass : "finder-mountvm clean-gray"
       callback : @bound 'showMountVMModal'


AppController: Separate chat view creation.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1012,6 +1012,11 @@
     if @rtm?.isReady then callback() else @once 'RTMIsReady', => callback()

+  createChatPaneView: (channel) ->
+
+    @getView().addSubView @chat = new IDE.ChatView {}, channel
+    @chat.show()
+

   createChatPane: ->

@@ -1019,8 +1024,7 @@

       return KD.showError err  if err

-      @getView().addSubView @chat = new IDE.ChatView {}, channel
-      @chat.show()
+      @createChatPaneView channel

   showChat: ->


fixed local buffer

fixed local buffer

--- client.coffee
+++ client.coffee
@@ -20,17 +20,34 @@
 App = require 'app'
 Plugin = require 'plugin'

-buffer = Obs.create 0
+exports.render = ->
+	###
+	# create local copy of the scores
+	localScores = Obs.create {}
+	Obs.observe !->
+		Db.shared.iterate 'counters', (v) !->
+			localScores.set 'counters', v.key(), v.peek()

-exports.render = ->
+	done = Obs.create true
+	Obs.observe !->
+		diff = player.get('score') - localPlayer.peek('score')
+		return if diff is 0
+		done.set false
+		setInterval (1/diff), !->
+			localPlayer.incr 'score'
+			if player.get('score') is localPlayer.peek('score') done.set true
+	###
+	buffer = Obs.create 0
+
 	Ui.card !->
-		renderButton()
+		renderButton buffer

 		renderFunny()

-	renderScores()
+	renderScores buffer

-renderButton = !->
+renderButton = (buffer) !->
+	buffering = Obs.create false
 	Dom.div !->
 		Dom.style _userSelect: 'none', textAlign: 'center', maxWidth: '700px', borderRadius: '10px', boxShadow: '0 0 10px #aaa', border: '4px solid '+Plugin.colors().highlight, padding: '10px 16px'
 		Dom.div !->
@@ -38,16 +55,19 @@
 			Dom.text "Click"

 		Dom.onTap !->
+			buffering.set true
 			buffer.incr()
-			if buffer.get() is 1
-				Obs.onTime 1000, !-> flushBuffer buffer

-flushBuffer = !->
-	#counter = Db.shared.ref 'counters', App.userId()
-	log 'buffered', buffer.get()
-	Server.sync 'incr', buffer.get() #, !->
-		#counter.set(counter.get() + buffer.get())
-	buffer.set(0)
+	Obs.observe !->
+		return unless buffering.get()
+		Obs.onTime 1000, !->
+			bufferedClicks = buffer.peek()
+			buffer.set(0)
+
+			log 'buffered', bufferedClicks
+			Server.sync 'incr', bufferedClicks, !->
+				Db.shared.incr 'counters', App.userId(), bufferedClicks
+			buffering.set false

 renderFunny = !->
 	Obs.observe !->
@@ -70,9 +90,11 @@
 renderScores = (buffer) !->
 	Ui.list !->
 		Dom.style margin: '0 15px'
-		Db.shared.iterate 'counters', renderScore, (counter) -> -(counter.get() + if (counter.key() is App.userId()) then buffer.get() else 0)
+		Db.shared.iterate 'counters',
+			((counter) !-> renderScore(counter, buffer)),
+			(counter) -> -(counter.get() + if (counter.key() is App.userId()) then buffer.get() else 0)

-renderScore = (counter) !->
+renderScore = (counter, buffer) !->
 	Ui.item !->
 		userId = +counter.key()
 		if userId is App.userId()


deduplicate install

deduplicate install

--- client/views/install_mod.coffee
+++ client/views/install_mod.coffee
@@ -29,12 +29,20 @@
           closer: false
           sticker: false
   "click .managerBtn": ->
-    Session.set("managerStatus", "Waiting for launcher to connect...")
-    window.open "https://s3-us-west-2.amazonaws.com/d2mpclient/launcher.exe"
-    $.pnotify
-      title: "Download Started"
-      text: "Run the launcher (downloading now) to finish the mod installation."
-      type: "info"
-      delay: 3000
-      closer: false
-      sticker: true
+    if !installingNot?
+      Session.set("managerStatus", "Waiting for launcher to connect...")
+      window.open "https://s3-us-west-2.amazonaws.com/d2mpclient/launcher.exe"
+      $.pnotify
+        title: "Download Started"
+        text: "Run the launcher (downloading now) to finish the mod installation."
+        type: "info"
+        delay: 3000
+        closer: false
+    else
+      $.pnotify
+        title: "Mod Already Installing"
+        text: "You already clicked that button!"
+        type: "error"
+        delay: 1000
+        closer: false
+        sticker: false


Renaming duplicated method from templates to styles, already with proper error handling.

--- www/src/theoricus/core/factory.coffee
+++ www/src/theoricus/core/factory.coffee
@@ -135,6 +135,10 @@

   @param [String] path  path to the style
   ###
-  @template=@::template=( path, fn )->
+  @style=@::style=( path, fn )->
     # console.log "Factory.template( #{path} )"
-    require ['templates/' + path], ( template )-> fn template+    require ['styles/' + path], ( style )->
+      fn style
+    , (err)->
+      console.error 'Style not found: ' + classpath
+      fn null


disable console output for now because of karma

--- tests/mocha/lib/test.coffee
+++ tests/mocha/lib/test.coffee
@@ -71,8 +71,8 @@
     htmlOptions.initial = '' if Tandem.Delta.isDelta(htmlOptions.initial)
     htmlOptions.expected = '' if Tandem.Delta.isDelta(htmlOptions.expected)
     htmlOptions.fn = (testContainer, expectedContainer, args...) =>
-      testEditor = new ScribeEditor(testContainer, { logLevel: 'debug' })
-      expectedEditor = new ScribeEditor(expectedContainer, { logLevel: 'debug' })
+      testEditor = new ScribeEditor(testContainer) #, { logLevel: 'debug' })
+      expectedEditor = new ScribeEditor(expectedContainer) #, { logLevel: 'debug' })
       testEditor.setDelta(@options.initial) if Tandem.Delta.isDelta(@options.initial)
       expectedEditor.setDelta(@options.expected) if Tandem.Delta.isDelta(@options.expected)
       @options.fn.call(null, testEditor, expectedEditor, args...)


the sanest input I can think of that lets us generate all the jobs, recursively, made by a job

--- test/integration/send-message-2-spec.coffee
+++ test/integration/send-message-2-spec.coffee
@@ -9,19 +9,22 @@
 JobManager     = require 'meshblu-core-job-manager'

-describe 'SendMessage2: broadcast+send', ->
-  beforeEach (done)->
-    @db = mongojs 'meshblu-core-test'
-    @collection = @db.collection 'devices'
-    @collection.drop => done()
+xdescribe 'SendMessage2: broadcast+send', ->
+  beforeEach (done) ->
+    @db            = mongojs 'meshblu-core-test'
+    @devices    = @db.collection 'devices'
+    @subscriptions = @db.collection 'subscriptions'

+    @subscriptions.drop =>
+      @devices.drop => done()
+
   beforeEach ->
     redisUri = process.env.REDIS_URI
     @dispatcher = new TestDispatcher
     client = new RedisNS 'meshblu-test', redis.createClient(redisUri)
     client.del 'request:queue'

-  beforeEach (done) ->
+  beforeEach 'create sender device', (done) ->
     @auth =
       uuid: 'sender-uuid'
       token: 'leak'
@@ -32,14 +35,22 @@
       token: bcrypt.hashSync @auth.token, 8
       receiveWhitelist: [ 'receiver-uuid' ]

-    @collection.insert @senderDevice, done
+    @devices.insert @senderDevice, done

-  beforeEach (done) ->
+  beforeEach 'create receiver device', (done) ->
     @receiverDevice =
       uuid: 'receiver-uuid'
       type: 'device:receiver'

-    @collection.insert @receiverDevice, done
+    @devices.insert @receiverDevice, done
+
+  beforeEach 'create subscription', (done) ->
+    subscription =
+      type: 'message.sent'
+      emitterUuid: 'sender-uuid'
+      subscriberUuid: 'sender-uuid'
+
+    @subscriptions.insert subscription

   context "sending to a device with sendWhitelist", ->
     beforeEach (done) ->
@@ -50,13 +61,8 @@
           jobType: 'SendMessage2'
         rawData: JSON.stringify devices:['receiver-uuid'], payload: 'boo'

-      @dispatcher.generateJobs job, (error, @generatedJobs) =>
-        done()
+      @dispatcher.generateJobs job, (error, @generatedJobs) => done()

-    it.only 'should return a 204', ->
-      expectedResponse =
-        metadata:
-          code: 204
-          status: 'No Content'
-
+    it 'should return a 204', ->
+      console.log JSON.stringify @generatedJobs, null, 2
       expect(@generatedJobs).to.exist


prisoner counts showing initially

prisoner counts showing initially

--- app/coffee/game_common.coffee
+++ app/coffee/game_common.coffee
@@ -118,20 +118,26 @@
   sgfObject or= smartgame.parse(getInputSgf() or '(;SZ[19])')
   size = parseInt(sgfObject.gameTrees[0].nodes[0].SZ, 10) or 19

-  $scoreBlock = createScoringDom()
   $navBlock = createNavigationDom sgfObject
   $('#board').empty()
-  React.render (React.createElement BoardDom, size: size,
-                                              sgfObject: sgfObject),
+  React.render (React.createElement BoardAreaDom, {sgfObject: sgfObject}),
                $('#board')[0]
-  $('#board').append($elem) for $elem in [$navBlock, $scoreBlock]
-  # setupState sgfObject
+  $('#board').append($elem) for $elem in [$navBlock]
   return
+
+BoardAreaDom = React.createClass
+  render: ->
+    {boardState, lastPlayed, prisoners} = stateFromSgfObject @props.sgfObject
+    size = parseInt(@props.sgfObject.gameTrees[0].nodes[0].SZ, 10) or 19
+
+    {div} = React.DOM
+    div {},
+        [(React.createElement BoardDom, boardState: boardState, size: size),
+         (React.createElement ScoreDom, prisoners: prisoners)]

 BoardDom = React.createClass
   render: ->
-    sgfObject = smartgame.parse(getInputSgf() or '(;SZ[19])')
-    {boardState, lastPlayed, prisoners} = stateFromSgfObject sgfObject
+    {boardState, size} = @props

     {div} = React.DOM
     topVert = div {className: "board_line board_line_vertical"}
@@ -144,8 +150,6 @@
     blackStone = div {className: "stone black"}
     whiteStone = div {className: "stone white"}

-    size = this.props.size
-
     boardDivsForPos = (i, j) ->
       result = []
       if j > 0 then result.push topVert
@@ -169,13 +173,17 @@
                     (boardDivsForPos i, j),
                     (stoneDivsForPos i, j)

-createScoringDom = ->
-  $scoreBlock = $('

')
-  $scoreBlock.append ('
Black prisoners: ' +
-    '
')
-  $scoreBlock.append ('
White prisoners: ' +
-    '
')
-  $scoreBlock
+ScoreDom = React.createClass
+  render: ->
+    prisoners = @props.prisoners
+
+    {div, span} = React.DOM
+
+    div {className: "score_block"},
+        [div {}, ["Black prisoners: ",
+                  span {className: "prisoners black"}, prisoners.black],
+         div {}, ["White prisoners: ",
+                  span {className: "prisoners white"}, prisoners.white]]

 # move navigation ====================================================
@@ -265,7 +273,7 @@

   If 'moves' is given as an option, include only that many moves."
   {boardState, lastPlayed, prisoners} = stateFromSgfObject sgfObject, options
-  updateBoard board_state
+  updateBoard boardState
   if lastPlayed.x? and lastPlayed.y?
     setLastPlayed ($pointAt lastPlayed.x, lastPlayed.y)
   else


break non-React DOM update functions

break non-React DOM update functions

--- app/coffee/game_common.coffee
+++ app/coffee/game_common.coffee
@@ -11,16 +11,13 @@

 game_common.$pointAt = $pointAt = (x, y) -> $(".row-#{y}.col-#{x}")
 game_common.getInputSgf = getInputSgf = -> $('input#data').val()
-game_common.setResponseSgf = setResponseSgf = (sgf) ->
-  $('#response').val sgf
+# game_common.setResponseSgf = setResponseSgf = (sgf) ->
+  # $('#response').val sgf

 game_common.getBlackPrisoners = -> parseInt($('.prisoners.black').text(), 10)
 game_common.getWhitePrisoners = -> parseInt($('.prisoners.white').text(), 10)
-game_common.setBlackPrisoners = setBlackPrisoners = (count) ->
-  $('.prisoners.black').text count
-game_common.setWhitePrisoners = setWhitePrisoners = (count) ->
-  $('.prisoners.white').text count
-
+
+###
 # setPointColor and helpers

 game_common.setPointColor = setPointColor = ($td, color) ->
@@ -54,6 +51,7 @@
   $('.goban .last-played').remove()

 # end of setPointColor helpers
+###

 game_common.contains_selector = contains_selector = ($context, selector) ->
   "A helper function returns true if the given element/context contains an
@@ -99,13 +97,6 @@
     result[row] ?= []
     result[row][col] = colorFromDom $this
   return result
-
-game_common.updateBoard = updateBoard = (state) ->
-  "set the images and classes of the DOM board to match the given state"
-  for rowArray, row in state
-    for color, col in rowArray
-      setPointColor ($pointAt col, row), color
-  return

 isHandicapPoint = (size, row, column) ->
   switch size
@@ -273,22 +264,6 @@
   return "#{colStr}#{rowStr}"

 # end of move navigation =============================================
-
-
-setupState = (sgfObject, options={}) ->
-  "Update the DOM board to match the given SGF object.
-
-  If 'moves' is given as an option, include only that many moves."
-  {boardState, lastPlayed, prisoners} = stateFromSgfObject sgfObject, options
-  updateBoard boardState
-  if lastPlayed.x? and lastPlayed.y?
-    setLastPlayed ($pointAt lastPlayed.x, lastPlayed.y)
-  else
-    removeLastPlayed()
-
-  setBlackPrisoners prisoners.black
-  setWhitePrisoners prisoners.white
-  return

 stateFromSgfObject = (sgfObject, options={}) ->
   "Get a board state and associated info from an SGF object.


refactor: filter -> forEach to load environments

--- app/config/environment.coffee
+++ app/config/environment.coffee
@@ -1,6 +1,6 @@
 # Require all of the config/environments files. /config.coffee controls which
 # environment files will be loaded.
-window.require.list().filter (module) ->
+window.require.list().forEach (module) ->
   require(module) if new RegExp("^config/environments/").test(module)

 Environment = Ember.Object.extend


Make all data model changes in the routes.

--- app/javascripts/routes.coffee
+++ app/javascripts/routes.coffee
@@ -20,27 +20,57 @@
       else
         resource.get(sbj).$promise

+    # Fetches the subject detail and fixes it up as follows:
+    # * converts the session and encounter dates into moments
+    # * copies the detail properties into the subject
+    # * sets the subject multiSession flag
+    #
     # @param subject the parent object
     # @param Subject the Subject Resource
-    # @returns the detail object
-    getSubjectDetail = (subject, Subject) ->
+    # @returns a promise which resolves to the detail object
+    getSubjectDetail = (subject, Subject, Helpers) ->
       if subject.detail
-        Subject.detail(id: subject.detail).$promise
+        Subject.detail(id: subject.detail).$promise.then (detail) ->
+          for sess in detail.sessions
+            # Set the session subject
+            sess.subject = subject
+            # Fix the acquisition date.
+            Helpers.fixDate(sess, 'acquisition_date')
+          # Fix the encounter dates.
+          Helpers.fixDate(enc, 'date') for enc in detail.encounters
+          # Copy the detail content into the subject.
+          Helpers.copyContent(detail, subject)
+          # Set a flag indicating whether there is more than
+          # one session.
+          subject.multiSession = subject.sessions.length > 1
+          # Resolve to the detail object.
+          detail
       else
         Subject.get(subject).$promise.then (fetched) ->
           # If the fetched subject has no detail, then complain.
           if not fetched.detail
             throw "Subject does not have detail: #{ subject.number }"
-          getSubjectDetail(fetched, Subject)
+          # Recurse.
+          getSubjectDetail(fetched, Subject, Helpers)

     # @param session the parent object
     # @param Session the Session Resource
     # @returns the detail object
-    getSessionDetail = (session, Session, Subject) ->
+    getSessionDetail = (session, Session, Subject, Image, Helpers) ->
       if session.detail
-        Session.detail(id: session.detail).$promise
+        Session.detail(id: session.detail).$promise.then (detail) ->
+          # Copy the fetched detail into the session.
+          Helpers.copyContent(detail, session)
+          # Add the registration.
+          # TODO - handle more than one registration?
+          session.registration = session.registrations[0]
+          # The series numbers.
+          session.seriesNumbers = [1..session.scan.intensity.intensities.length]
+          # Encapsulate the image files.
+          for obj in [session.scan, session.registration]
+            obj.images = Image.imagesFor(obj)
       else
-        getSubjectDetail(session.subject, Subject).then (detail) ->
+        getSubjectDetail(session.subject, Subject, Helpers).then (detail) ->
           # Find the session in the session list.
           sbj_session = _.find detail.sessions, (other) ->
             other.number == session.number
@@ -49,7 +79,7 @@
             throw "Subject #{ subject.number } does not have a session #{ session.number }"
           # Recurse.
           session.detail = sbj_session.detail_id
-          getSessionDetail(session, Session, Subject)
+          getSessionDetail(session, Session, Subject, Image, Helpers)

     $stateProvider
       # The top-level state. This abstract state is the ancestor
@@ -63,6 +93,9 @@
         url: '/quip?project'
         abstract: true
         resolve:
+          # The Helpers service is injected into all child states.
+          Helpers: 'Helpers'
+          # The project is shared by all states.
           project: ($stateParams) ->
             $stateParams.project or 'QIN'
         views:
@@ -100,8 +133,8 @@
       .state 'quip.subject.detail',
         url: '?detail'
         resolve:
-          detail: (subject, Subject) ->
-            getSubjectDetail(subject, Subject)
+          detail: (subject, Subject, Helpers) ->
+            getSubjectDetail(subject, Subject, Helpers)
         views:
           'main@':
             templateUrl: '/partials/subject-detail.html'
@@ -122,8 +155,9 @@
         url: '?detail'
         resolve:
           Session: 'Session'
-          detail: (session, Session, Subject) ->
-            getSessionDetail(session, Session, Subject)
+          Image: 'Image'
+          detail: (session, Session, Subject, Image, Helpers) ->
+            getSessionDetail(session, Session, Subject, Image, Helpers)
         views:
           'main@':
             templateUrl: '/partials/session-detail.html'


Prevent circular reference in toJSON

Prevent circular reference in toJSON

--- app/models/data_source.coffee
+++ app/models/data_source.coffee
@@ -4,6 +4,10 @@
 class DataSource extends AppModel
   defaults:
     data: []
+
+  toJSON: ->
+    json = new Object
+    json[key] = value for key, value of @attributes when key isnt 'tools'

   fetchData: =>
     if @get('type') is 'external'


missing comma

missing comma

--- app/models/weighticket.coffee
+++ app/models/weighticket.coffee
@@ -14,7 +14,7 @@
     description: "Trucks are weighed upon exiting the warehouse"
     display: ["show"]
     modify: ["new", "edit"]
-  externalReference: DS.attr "string"
+  externalReference: DS.attr "string",
     label: "External Reference Number"
     description: "If you have an external weight-ticket tracking system, you can input its value here"
     display: ["show"]


Add an admin-mode toggle

Add an admin-mode toggle

--- app/partials/main-footer.cjsx
+++ app/partials/main-footer.cjsx
@@ -3,9 +3,12 @@
 Translate = require 'react-translate-component'
 {Link} = require 'react-router'
 ZooniverseLogoType = require './zooniverse-logotype'
+auth = require '../api/auth'
+apiClient = require '../api/client'

 counterpart.registerTranslations 'en',
   footer:
+    adminMode: 'Admin mode'
     discover:
       title: 'Projects'
       projectList: 'Projects'
@@ -27,6 +30,25 @@
 module.exports = React.createClass
   displayName: 'MainFooter'

+  getInitialState: ->
+    user: null
+
+  componentDidMount: ->
+    auth.listen 'change', @handleAuthChange
+    apiClient.listen 'change', @handleClientChange
+    @handleAuthChange()
+
+  componentWillUnmount: ->
+    auth.stopListening 'change', @handleAuthChange
+    apiClient.stopListening 'change', @handleClientChange
+
+  handleAuthChange: ->
+    auth.checkCurrent().then (user) =>
+      @setState {user}
+
+  handleClientChange: ->
+    @forceUpdate()
+
   render: ->

@@ -34,6 +56,12 @@
           <Link to="home" className="main-logo-link">
             <ZooniverseLogoType />
           </Link>
+

+          {if @state.user?.admin or @state.user?.id is '3' # brian-testing
+
+              <input type="checkbox" checked={apiClient.params.admin} onChange={@toggleAdminMode} />{' '}
+              <Translate content="footer.adminMode" />
+            }

@@ -64,3 +92,6 @@

+
+  toggleAdminMode: (e) ->
+    apiClient.update 'params.admin': e.target.checked or undefined


fix rendering of mailchimp newsletter

fix rendering of mailchimp newsletter

--- server/utils/jwz_tools.coffee
+++ server/utils/jwz_tools.coffee
@@ -56,9 +56,13 @@
             'h4'
         ]
         safeAttributes = [
+            # general
             'style', 'class', 'background',
             # tables
-            'colspan', 'rowspan', 'width', 'align', 'font-size'
+            'colspan', 'rowspan', 'height', 'width', 'align', 'font-size',
+            'cellpadding', 'cellspacing', 'border', 'valign'
+            # body
+           'leftmargin', 'marginwidth', 'topmargin', 'marginheight', 'offset',
             #microdata
             'itemscope', 'itemtype', 'itemprop', 'content'
         ]


Use a TreeWalker to traverse so nodes at all depths are recorded

--- src/trix/text_view.coffee
+++ src/trix/text_view.coffee
@@ -9,16 +9,9 @@

   render: ->
     selectedRange = @getSelectedRange()
-    @nodes = []
-    @positions = []
-
     @element.innerHTML = ""
-    for container in @createContainersForText()
-      @element.appendChild(container)
-      for node in container.childNodes
-        @nodes.push(node)
-        @positions.push(node.trixPosition)
-
+    @element.appendChild(container) for container in @createContainersForText()
+    @recordNodePositions()
     @setSelectedRange(selectedRange)

   createContainersForText: ->
@@ -54,6 +47,16 @@
       containers.push(container)

     containers
+
+  recordNodePositions: ->
+    @nodes = []
+    @positions = []
+    walker = document.createTreeWalker(@element)
+    while walker.nextNode()
+      if node = walker.currentNode
+        if node.trixPosition?
+          @nodes.push(node)
+          @positions.push(node.trixPosition)

   getSelectedRange: ->
     selection = window.getSelection()


temorarily removed connect server gulp code

--- gulp/dev.coffee
+++ gulp/dev.coffee
@@ -11,10 +11,10 @@

 test_dir = "./tests/**/*.*"

-connect.server
-  livereload: true
-  port: 4000
-  root: path.resolve("./")
+# connect.server
+#   livereload: false
+#   port: 4000
+#   root: path.resolve("./")

 rebundle = ->
   bundler.bundle()
@@ -27,7 +27,7 @@
       message: "New Build Compiled"
       icon:    __dirname + "/../icon.png"
     ))
-    .pipe connect.reload()
+    # .pipe connect.reload()
     .on "error", notify.onError(error)

 bundler = watchify(browserify(watchify.args))
@@ -43,4 +43,4 @@
     fileName = path.relative("./", file.path)
     gutil.log gutil.colors.cyan(fileName), "changed"

-    gulp.src(test_dir).pipe connect.reload()
+    # gulp.src(test_dir).pipe connect.reload()


Add missing replacement in gulpfile.

Add missing replacement in gulpfile.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -33,6 +33,7 @@
   .pipe $.replace(noDevStrings[0], devStrings[0])
   .pipe $.replace(noDevStrings[1], devStrings[1])
   .pipe $.replace(noDevStrings[2], devStrings[2])
+  .pipe $.replace(noDevStrings[3], devStrings[3])
   .pipe gulp.dest(homePath)
   .pipe $.shell([
     [pluginInstall, pluginClean, pluginUpdate].join('; ')
@@ -43,6 +44,7 @@
   .pipe $.replace(devStrings[0], noDevStrings[0])
   .pipe $.replace(devStrings[1], noDevStrings[1])
   .pipe $.replace(devStrings[2], noDevStrings[2])
+  .pipe $.replace(devStrings[3], noDevStrings[3])
   .pipe gulp.dest(homePath)
   .pipe $.shell([
     [pluginInit, pluginInstall, pluginInit, pluginClean, pluginUpdate].join('; ')


feat(stats title): use package.json name as stats title

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -725,6 +725,10 @@

 # Execute Plato complexity analysis
 gulp.task 'plato', ['clean:working'], ->
+	options =
+		plato:
+			title: pkg.name
+
 	srcs = []

 	srcs.push src =
@@ -767,7 +771,7 @@
 		.pipe gulp.dest DIST_DIRECTORY
 		.on 'error', onError

-		.pipe plato STATS_DIRECTORY
+		.pipe plato STATS_DIRECTORY, options.plato
 		.on 'error', onError

 # Prepare for compilation


Adjust delay to improve dev

Adjust delay to improve dev

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -31,6 +31,7 @@
   js       : 'app/public/assets/js'

 src =
+  views    : 'app/client/views/**/*.hbs'
   sass:
     main   : 'app/client/scss/' + dist.name + '.scss'
     files  : ['app/client/scss/**/**']
@@ -87,6 +88,7 @@
     proxy: "http://127.0.0.1:#{config.server.port}"
     files: ['app/public/assets/**/*.*']
     port: config.serverDev.port
+    reloadDelay: 1500
   return

 gulp.task 'build', ['css', 'js']
@@ -95,3 +97,4 @@
   gulp.start ['build', 'server']
   gulp.watch src.sass.files, ['css']
   gulp.watch [src.js.main, src.js.tag], ['js']
+  gulp.watch([src.views, src.js.tag]).on('change', reload)


autobuild update

autobuild update

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -73,7 +73,8 @@
     .pipe connect.reload()

-gulp.task "default", ["compass","coffee","connect", "watch" ]
+gulp.task "default", ["compass","coffee","connect" ]
+gulp.task "default", ["compass","coffee","connect", "sketch", "watch" ]

 gulp.task 'sketch', () ->
   gulp.src base + 'sketch/svg/*.sketch'


Try ecmascript5_strict.

Try ecmascript5_strict.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -81,6 +81,7 @@
     compilerPath: paths.compiler
     compilerFlags:
       closure_entry_point: 'app.main'
+      language_in: 'ECMASCRIPT5_STRICT'
       externs: paths.externs

 gulp.task 'compile-serverapp', ->
@@ -88,6 +89,7 @@
     compilerPath: paths.compiler
     compilerFlags:
       closure_entry_point: 'server.main'
+      language_in: 'ECMASCRIPT5_STRICT'
       externs: paths.externs
         .concat este.getExterns dirs.nodeJsExterns
       # To have compiled code readable, so we can trace errors.


:bug: fix browserify build bug

:bug: fix browserify build bug

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -32,6 +32,7 @@
     dest: ''
     bundleExternal: false
     minify: false
+    detectGlobals: false
   ,
     suffix: 'browser'
     src: 'src/browser.coffee'
@@ -39,6 +40,7 @@
     dest: 'public/js'
     bundleExternal: true
     minify: true
+    detectGlobals: true
 ]

 gulp.task "browserify", ("browserify:#{it.suffix}" for it in target)
@@ -53,6 +55,7 @@
           extensions: ['.coffee', '.js']
           debug: true
           transform: ['coffee-reactify']
+          detectGlobals: it.detectGlobals
           bundleExternal: it.bundleExternal
         .pipe buffer()
         .pipe sourcemaps.init


made the fake espruino path relative so that I dont have my username in the commit logs

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -34,7 +34,7 @@
             capture:
               input: true
               output: false
-            fakePath: '/Users/MYUSER/dev/quadcopter/Espruino/espruino')
+            fakePath: '../Espruino/espruino')
     .on 'data', (data) -> gutil.log(data.contents.toString())

 gulp.task 'test', ->
@@ -47,5 +47,5 @@
           capture:
             output: false
             input: true
-          fakePath: '/Users/MYUSER/dev/quadcopter/Espruino/espruino')
+          fakePath: '../Espruino/espruino')
   .on 'data', (data) -> gutil.log(data.contents.toString())


fix osx application name

fix osx application name

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -20,6 +20,9 @@
   # the symlinks in the binary.
   gutil.log 'Copying prebuilt binary to', gutil.colors.magenta(outbin)
   execSync 'cp -R node_modules/electron-prebuilt/dist/Electron.app Yakayak.app'
+  gutil.log 'Change app name to Yakyak'
+  execSync 'sed -i.bak s/Electron/Yakyak/ Yakayak.app/Contents/Info.plist'
+  execSync 'mv Yakayak.app/Contents/MacOS/Electron Yakayak.app/Contents/MacOS/Yakyak'

 gulp.task 'pre', ->
   copyPrebuilt() unless fs.existsSync outbin


use compass by default

use compass by default

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -36,7 +36,7 @@
         if event.type is "changed"
           compile(config)

-gulp.task "default", ["server"], ->
+gulp.task "default", ["server", "compass"], ->
   reload = livereload()
   gulp.watch("harp/**").on "change", (file) ->
     reload.changed(file.path)


unpacks icons for use in SnoreToast

unpacks icons for use in SnoreToast

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -58,7 +58,7 @@
 deploy_options = {
     dir: path.join __dirname, 'app'
     asar:
-        unpackDir: "node_modules/node-notifier/vendor/**"
+        unpackDir: "{node_modules/node-notifier/vendor/**,icons}"
     icon: path.join __dirname, 'src', 'icons', 'icon'
     out: outdeploy
     overwrite: true


don’t try to focus input if it is not in place

--- src/ui/views/input.coffee
+++ src/ui/views/input.coffee
@@ -59,4 +59,5 @@
     el = document.activeElement
     if !el or not (el.nodeName in ['INPUT', 'TEXTAREA'])
         # steal it!!!
-        document.querySelector('.input textarea').focus()
+        el = document.querySelector('.input textarea')
+        el.focus() if el


camelcase hexaFlip

camelcase hexaFlip

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -4,7 +4,7 @@
 # http://oxism.com
 # Copyright 2013, MIT License

-baseName = 'hexaflip'
+baseName = 'hexaFlip'
 className = baseName[0].toUpperCase() + baseName.slice 1
 testEl = document.createElement 'div'
 prefixList = ['Webkit', 'Moz', 'O', 'ms']
@@ -27,9 +27,9 @@
   touchSensitivity: .7

-class window.Hexaflip
-
-  className: baseName
+class window.HexaFlip
+
+  className: baseName.toLowerCase()
   _urlRx: /^((https?:)?\/\/)|(data:)/
   _faceNames: ['front', 'bottom', 'back', 'top', 'left', 'right']
   _faceSequence: @::_faceNames.slice 0, 4
@@ -233,12 +233,12 @@

 if window.jQuery? or window.$?.data?
-  $.fn.hexaflip = (sets, options) ->
+  $.fn.hexaFlip = (sets, options) ->
     return @ unless css.transform

     if typeof sets is 'string'
       methodName = sets
-      unless typeof Hexaflip::[methodName] is 'function'
+      unless typeof HexaFlip::[methodName] is 'function'
         console.warn "#{ baseName }: No such method `#{ methodName }`" if devMode
         return @

@@ -253,5 +253,5 @@
         if instance = $.data el, baseName
           return instance
         else
-          $.data el, baseName, new Hexaflip el, sets, options
-
+          $.data el, baseName, new HexaFlip el, sets, options
+


Fixed env.debug method (only prints in debug mode)

--- src/core/utilities/debug.coffee
+++ src/core/utilities/debug.coffee
@@ -1,5 +1,6 @@

 module.exports = (env) ->
-    debug = () ->
-    	console.log.apply this, arguments
-    return debug+	debug = () ->
+		if env.config?.debug
+			console.log.apply this, arguments
+	return debug


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -15,6 +15,8 @@
     destroySpy = true

   obj.I.x = 10000
-  obj.I.y =
+  obj.I.y = 4000
+
+

 module()


Workspace: Pane launcher isn't active by default.

--- client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
+++ client/landing/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
@@ -2,7 +2,7 @@

   constructor: (options = {}, data) ->

-    options.cssClass = "workspace-launcher active"
+    options.cssClass = "workspace-launcher"
     options.partial  = "<span>+</span>"

     super options, data


fix(work): Treat work window as if it's always visible

--- src/browser/nylas-window.coffee
+++ src/browser/nylas-window.coffee
@@ -61,7 +61,7 @@
       webPreferences:
         directWrite: true

-    if @mainWindow
+    if @neverClose
       # Prevents DOM timers from being suspended when the main window is hidden.
       # Means there's not an awkward catch-up when you re-show the main window.
       options.webPreferences.pageVisibility = true


Fix routing on external links. Closes #148.

--- src/chaplin/views/layout.coffee
+++ src/chaplin/views/layout.coffee
@@ -129,9 +129,8 @@

       # Is it an external link?
       currentHostname = location.hostname.replace('.', '\\.')
-      external = el.hostname isnt '' and
-        not ///#{currentHostname}$///i.test(el.hostname)
-      if external
+      internal = el.hostname is '' or currentHostname is el.hostname
+      unless internal
         # Open external links normally
         # You might want to enforce opening in a new tab here:
         #event.preventDefault()


Move the parenthesis

Move the parenthesis

--- src/coffee/dropship_file.coffee
+++ src/coffee/dropship_file.coffee
@@ -105,7 +105,7 @@
     basename = @url.split('#', 2)[0].split('?', 2)[0]
     while basename.substring(basename.length - 1) == '/'
       basename = basename.substring 0, basename.length - 1
-    decodeURIComponent(basename.substring basename.lastIndexOf('/') + 1)
+    decodeURIComponent basename.substring(basename.lastIndexOf('/') + 1)

   # Called while the file is downloading.
   #


Teamwork: Implemented previewFile for Teamwork.

--- client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
+++ client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
@@ -24,6 +24,17 @@
         joinedUser = snapshot.name()
         return if not joinedUser or joinedUser is KD.nick()
         @hidePlaygroundsButton()
+
+
+      if @amIHost()
+        # currently only for host.
+        # bc of clients need to know host's vmName and active pane's file data etc.
+        {viewerHeader} = @getActivePanel().paneLauncher.previewPane.previewer
+        viewerHeader.addSubView new KDButtonView
+          cssClass : "clean-gray workspace-previewer-button"
+          title    : "View active file"
+          callback : =>
+            @previewFile()

   createLoader: ->
     @container.addSubView @loader = new KDCustomHTMLView
@@ -93,3 +104,26 @@
       @getDelegate().showMarkdownModal()
     else
       Panel::showHintModal.call @getActivePanel()
+
+  previewFile: ->
+    activePanel     = @getActivePanel()
+    editor          = activePanel.getPaneByName "editor"
+    file            = editor.getActivePaneFileData()
+    path            = FSHelper.plainPath file.path
+    error           = "File must be under Web folder"
+    isLocal         = path.indexOf("localfile") is 0
+    isNotPublic     = not FSHelper.isPublicPath path
+    {previewPane}   = activePanel.paneLauncher
+    {defaultVmName} = KD.getSingleton "vmController"
+
+    return if isLocal or isNotPublic
+      error         = "This file cannot be previewed" if isLocal
+      new KDNotificationView
+        title       : error
+        cssClass    : "error"
+        type        : "mini"
+        duration    : 2500
+        container   : previewPane
+
+    url = path.replace "/home/#{@getHost()}/Web", defaultVmName
+    previewPane.openUrl url


Fix constants.

Fix constants.

--- src/coffee/exportmapping.coffee
+++ src/coffee/exportmapping.coffee
@@ -67,9 +67,9 @@
     if @header.has(CONS.HEADER_CATEGORIES)
       row[@header.toIndex CONS.HEADER_CATEGORIES] = _.reduce(product.categories or [], (memo, category, index) =>
         memo += GLOBALS.DELIM_MULTI_VALUE unless index is 0
-        memo + if @categoryBy = CONS.HEADER.SLUG
+        memo + if @categoryBy = CONS.HEADER_SLUG
           @categoryService.id2slug[category.id]
-        else if @categoryBy = CONS.HEADER.EXTERNAL_ID
+        else if @categoryBy = CONS.HEADER_EXTERNAL_ID
           @categoryService.id2slug[category.id]
         else
           @categoryService.id2fqName[category.id]


added state apis for the plugin

added state apis for the plugin

--- src/coffee/ezBreadcrumbs.coffee
+++ src/coffee/ezBreadcrumbs.coffee
@@ -379,6 +379,14 @@
         o.generateRandomKey = ->
             return "ezB_#{ Math.random().toString(36).substring(7) }"

+        _this.getState = ->
+            status = o.el.data "#{ pluginName.toLowerCase() }-state"
+
+            return if typeof status is "undefined" then 'decompressed' else status
+
+        _this.isCompressed = ->
+            return if _this.getState() is 'compressed' then true else false
+
         _this.destroy = ->
             o.opts.onDestroy(_this) if $.isFunction(o.opts.onDestroy)


reduce chances of blank printing

reduce chances of blank printing

--- src/smc-webapp/code-editor/print.coffee
+++ src/smc-webapp/code-editor/print.coffee
@@ -49,7 +49,6 @@
     if w.window.print?
         f = ->
             w.window.print()
-            w.window.close()
         # Wait until the render is done, then display print dialog.
-        w.window.setTimeout(f, 0)
+        w.window.setTimeout(f, 250)
     return


(test) disable again test that accidentally got un-Xed.

--- src/spec/integration/import.spec.coffee
+++ src/spec/integration/import.spec.coffee
@@ -1622,7 +1622,7 @@
         done()
       .catch (err) -> done _.prettify(err)

-    it 'should split actions if there are more than 500 in actions array', (done) ->
+    xit 'should split actions if there are more than 500 in actions array', (done) ->
       numberOfVariants = 501
       csvCreator = (productType, newProductName, newProductSlug, rows) ->
         changes = ""


remove stray console log

remove stray console log

--- source/javascripts/app.coffee
+++ source/javascripts/app.coffee
@@ -10,5 +10,4 @@
 			families: ["Lato:400,400italic,300,300italic,100,100italic,700,900"]

 		active: ->
-			console.log "loaded"
 			$('.article-title').bigtext()


added default admin user

added default admin user

--- modules/http/lib/AbstractServer.coffee
+++ modules/http/lib/AbstractServer.coffee
@@ -1,195 +1,207 @@

-module.exports =
+module.exports =

     ##
-    ## A Floyd-Webserver
+    ## A Floyd-Webserver
     ##
     ## Implements an abstract base for a Floyd HTTP-Server Context
     ## the server-socket-communication feature needs to be implemented per platform
     ## take a look at ../platforms/node/lib/Server.coffee for the node implementation
-    ##
+    ##
     class AbstractHttpServer extends floyd.http.Context
-
+
         constructor: (parent)->
             super parent
-
+
             @_hiddenKeys.push 'server'
-
-
-        ##
-        ##
+
+
+        ##
+        ##
         ## @override
         configure: (config)->
-
+
             super new floyd.Config
                 data:
-
-                    ## the tcp-port to listen on for requests.
+
+                    ## the tcp-port to listen on for requests.
                     ## data.host may be set in addition to a port-number
                     ## port may also contain the name of a unix-domain socket to listen on
                     port: 9030

                     ## the base route for children routing
                     route: '/.*'
-
+
                     ## the Content-Type used as fallback for mime.lookup
                     ctype: 'text/html'
-
+
                     ## a directory relative to floyd.appdir
                     public: ['./public']
-
+
                     ## the default index file
                     index: 'index.html'
-
-
+
+
                 ##
                 children: [
-
+
                     id: 'sessions'
-
+
                     type: 'http.sessions.Sessions'
-
+
                     data: config?.data?.sessions || {}
-
-                ,
-
+
+                ,
+
                     id: 'cache'

                     type: 'http.Cache'
-
-                    data: floyd.tools.objects.extend
-                        permissions: false
-                    , config?.data?.cache
-
-                ,
-
+
+                    data: config?.data?.sessions || {}
+                ,
+
                     id: 'users'

                     type: 'stores.Context'
-
-                    data: floyd.tools.objects.extend
-                        permissions: false
-                    , config?.data?.users
-
+
+                    data: config?.data?.users || {}
+
+                    running: ->
+                        if @data.admin
+                            data =
+                                active: true
+                                login: 'admin'
+                                name: 'Default Administrator'
+                                roles: ['admin']
+                                pass: 'bf5d636daff3476c410f43623edaeb7c-SHA256-4-1500-453c9f2107c362678a684434dbf35e0c'
+
+                            if typeof @data.admin is 'object'
+                                data = floyd.tools.objects.extend data,
+                                    @data.admin
+
+                            @has data.login, (err, has)=>
+                                if !has
+                                    @set data.login, data, (err)=>
+                                        console.log(err) if err
                 ]
-
-
+
+
             , config
-
-
+
+
         boot: (done)->
-
+
             ##
             @server = @_createServer (req, res)=>
-
+
                 ##
                 @_handleRequest req, res, (err)=>
-
+
                     ##
                     @_handleError req, res, err
-
+
             ##
             @server.use ?= (mw)=>
-
+
                 @_addMiddleware mw
-
+
             ## call super to boot the actual floyd.Context instance
             super (err)=>
                 return done(err) if err
-
+
                 ## bind to tcp-port
                 @server.listen @data.port, @data.host
-
-
-
+
+
+
                 ## log a small "i'm alive" message
                 @logger.info '%s is now listening on %s:%s', @_loginfo(), (@data.host||''), @data.port
-
+
                 @_emit 'listening', @data.port, @data.host
-
+
                 done()
-
-
+
+
         ##
         _loginfo:()->
-
+
             data = []
             for p in @data.public
                 data.push p.replace floyd.system.libdir, 'floyd:'
-
+
             return data
-
+
         ##
         ##
         ##
         _createServer: (handler)->
             throw new floyd.error.NotImplemented 'http.AbstractServer._createServer'

-
-
+
+
         ##
         ##
         ##
         _prepareRequest: (req, res, done)->
-
+
             ##
             req.uri = req.url.split('?').shift()
-
+
             ##
             if @data.vhosts
                 _ex = @data.vhosts._exclude
                 _uri = req.uri
                 if !@data.vhosts._exclude || (_ex.indexOf(_uri) is -1 && _ex.indexOf(_uri.substr 0, _uri.lastIndexOf('/')+1) is -1)
-
+
                     hostname = req.headers.host
                     for vhost, prefix of @data.vhosts
                         if hostname.substr(0, vhost.length) is vhost
                             req.uri = prefix + req.uri
                             req.url = prefix + req.url
                             req.vhostpath = prefix
-
+
             #console.log req.headers.host, req.uri
-
-            ##
+
+            ##
             ## send handler
             res.send = (content, headers={}, code)=>

                 @_send req, res, content, headers, code
-
+
             ##
             ## redirect handler sends a 302
             req.redirect = res.redirect = (uri, msg='moved to '+uri+'\n', code=302)=>
-
+
                 if uri is 'back'
                     uri = req.headers.referer
-
+
                 res.setHeader 'location', uri
                 res.ctype = 'text/plain'
                 res.send msg, code
-
+
             super req, res, done
-
-
-
+
+
+
         ##
         ##
         ##
         _send: (req, res, content, headers, code)->
-
+
             if typeof headers is 'number'
                 [code, headers] = [headers, code]
-
+
             for key, val of headers
                 res.setHeader key, val

-            res.writeHead (code||res.code||200), 'Content-Type': (res.ctype||@data.ctype)
-
+            res.writeHead (code||res.code||200), 'Content-Type': (res.ctype||@data.ctype)
+
             if content && req.method isnt 'HEAD' && code isnt 304
                 res.write content

             res.end()
-
-
+
+
         ##
         ##
         ##
@@ -197,12 +209,12 @@
             if !res.err
                 if err.status is 302
                     return req.redirect err.message
-
+
                 res.ctype = 'text/plain'
                 res.code = err.status
-
+
                 @_send req, res, err.message+'\n'
-
+
                 res.err = err
             else
-                @logger.error err
+                @logger.error err


Adds UI Prototyping Controller and associated action files

Route to controller only active in development
includes misc mathjax examples I used for equation builder

--- javascripts/widgets/ui_elements.coffee
+++ javascripts/widgets/ui_elements.coffee
@@ -1,9 +1,9 @@
 #= require almond_wrapper
 #= require lib
 #= require jquery
+#= require widgets/mathml

-
-ttm.define 'widgets/ui_elements', ['lib/class_mixer'], (class_mixer)->
+ttm.define 'widgets/ui_elements', ['lib/class_mixer', 'widgets/mathml'], (class_mixer, mathml_renderer)->
   class Button
     initialize: (@opts={})->
     render: (opts={})->
@@ -18,13 +18,42 @@
     initialize: (@opts={})->
     render: (opts={})->
       opts = _.extend({},@opts, opts)
-      figure = $("
#{opts.default || '0'}
")
-      opts.element.append figure
+      @figure = $("
#{opts.default || '0'}
")
+      opts.element.append @figure
+      @figure
+
+    update: (value)->
+      @figure.html(value)
   class_mixer(MathDisplay)
+
+  class MathMLDisplay
+    initialize: (@opts={})->
+      @mathml_renderer = @opts.mathml_renderer || mathml_renderer
+    render: (opts={})->
+      opts = _.extend({}, @opts, opts)
+      @figure = $("""
+

+          <math xmlns=\"http://www.w3.org/1998/Math/MathML\">
+                  <mrow><mn>0</mn></mrow>
+          </math>
+
""")
+      @math_element = @figure.find('math')
+      opts.element.append @figure
+      @figure
+
+    update: (mathml)->
+      #@math_element.html(mathml)
+
+      @mathml_renderer.render(@math_element[0])
+
+
+  class_mixer(MathMLDisplay)
+

   exports =
     button_builder: Button
     math_display_builder: MathDisplay
+    mathml_display_builder: MathMLDisplay

   return exports


Fix editing icons only showing up the first time, introduced when fixing #38

--- src/resources/forms/orbeon/newbuilder/resources/grid/action-icons.coffee
+++ src/resources/forms/orbeon/newbuilder/resources/grid/action-icons.coffee
@@ -59,11 +59,13 @@
             # Move cell editor controls under this td
             gridTd = YD.get gridTdId
             if gridTd?
-                if $(gridTd).children('.fb-hover').length == 0                          # Create a div.fb-hover inside the td, if there isn't one already
+                hoverDiv = $(gridTd).children('.fb-hover')
+                # Create a div.fb-hover inside the td, if there isn't one already
+                if hoverDiv.length == 0
                     hoverDiv = $('<div>').addClass('fb-hover')
-                    hoverDiv.append(triggerGroups)
                     hoverDiv.append($(gridTd).children())
                     $(gridTd).append(hoverDiv)
+                hoverDiv.append(triggerGroups)
                 $(triggerGroups).css('display', '')
                 # Show/hide triggers depending on relevance rules
                 for trigger in triggers


fix to oab reindex script, try again

fix to oab reindex script, try again

--- server/api/scripts/2noddy.coffee
+++ server/api/scripts/2noddy.coffee
@@ -75,7 +75,7 @@
       API.es.reindex 'oab', 'request', API.es._mapping, undefined, false, _oab_requests_sherpa

       # oab_support is the same format so does not need reprocessing, but will need reindexing anyway to match new mapping
-      API.es.reindex 'oab', 'support'
+      #API.es.reindex 'oab', 'support'

       API.log {msg:'Scripting oab from oab into oab, but udpated and mapped for noddy, complete', level:'info', notify:true}
       return true


factor spectrogram into its own class

factor spectrogram into its own class

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

 HIST_NUM_BINS = 16

-FREQ_SLIDER_FACTOR = 2
-FREQ_SLIDER_MAX = MAX_FREQ/FREQ_SLIDER_FACTOR
+FREQ_SLIDER_MAX = MAX_FREQ
 FREQ_SLIDER_MIN = 0
+FREQ_SLIDER_START = FREQ_SLIDER_MAX/2

 GAIN_DEFAULT = 1
 GAIN_MIN = 1
@@ -38,15 +38,9 @@

     @throttled_request_data = _.throttle((=> @request_data()), 40)

-    # Set up image plot for the spectrogram
-    @image_width = NGRAMS
-    @image_height = 256
-    @image = [new ArrayBuffer(SPECTROGRAM_LENGTH * NGRAMS * 4)]
-    @spec_source = Collections('ColumnDataSource').create(
-      data:{image: @image}
-    )
-    spec_model = @create_spec()
-    @spec_view = new spec_model.default_view(model: spec_model)
+    @spec_plot = new SpectrogramPlot({
+      width: NGRAMS, height: SPECTROGRAM_LENGTH/2, border: BORDER
+    })

     @power_plot = new SimpleIndexPlot({
       x0: 0, x1: window.TIMESLICE, y0: -0.5, y1: 0.5, width: 550, height: 220, border: BORDER
@@ -86,29 +80,13 @@
     for i in [0..(data[1].length-1)]
       data[1][i] *= @gain

-    cmap = new ColorMapper({}, {
-      palette: all_palettes["YlGnBu-9"],
-      low: 0,
-      high: 10
-    })
-    buf = cmap.v_map_screen(data[0])
-
-    image32 = new Uint32Array(@image[0])
-    buf32 = new Uint32Array(buf)
-
-    # update the spectrogram
-    for i in [0..(SPECTROGRAM_LENGTH-1)]
-      for j in [(NGRAMS-1)..1]
-        image32[i*NGRAMS + j] = image32[i*NGRAMS + j - 1]
-      image32[i*NGRAMS] = buf32[i]
-    @spec_source.set('data', {image: @image})
-    @spec_source.trigger('change', @spec_source, {})
+    @spec_plot.update(data[0])

     @power_plot.update(data[1])

-    @fft_plot.update(data[0][0..data[0].length/FREQ_SLIDER_FACTOR])
-
-    @hist_plot.update(data[0][0..data[0].length/FREQ_SLIDER_FACTOR], @fft_range[0], @fft_range[1])
+    @fft_plot.update(data[0])
+
+    @hist_plot.update(data[0], @fft_range[0], @fft_range[1])

   set_freq_range : (event, ui) ->
     [min, max] = @fft_range = ui.values
@@ -124,10 +102,10 @@
     slider_div = $('

')
     slider_div.slider({
       animate: "fast",
-      step: 1
-      min: FREQ_SLIDER_MIN
-      max: FREQ_SLIDER_MAX
-      values: [FREQ_SLIDER_MIN, FREQ_SLIDER_MAX]
+      step: 1,
+      min: FREQ_SLIDER_MIN,
+      max: FREQ_SLIDER_MAX,
+      values: [FREQ_SLIDER_MIN, FREQ_SLIDER_MAX],
       slide: ( event, ui ) =>
         @set_freq_range(event, ui)
     });
@@ -142,7 +120,7 @@
     slider_div = $('

')
     slider_div.slider({
       animate: "fast",
-      step: 0.1
+      step: 0.1,
       min: GAIN_MIN,
       max: GAIN_MAX,
       value: GAIN_DEFAULT,
@@ -164,7 +142,8 @@
         button.text('pause')
         @paused = false
         @request_data())
-
+    button.css('float', 'left')
+    button.css('margin', '30px')
     controls.append(button)

     controls.css('clear', 'both')
@@ -175,8 +154,8 @@
     div = $('

')
     $('body').append(div)
     myrender = () =>
-      div.append(@spec_view.$el)
-      @spec_view.render()
+      div.append(@spec_plot.view.$el)
+      @spec_plot.render()

       foo = $('

')
       foo.append(@power_plot.view.$el)
@@ -191,19 +170,33 @@
       @hist_plot.render()
     _.defer(myrender)

-  create_spec: () ->
-    xrange = Collections('Range1d').create({start: 0, end: NGRAMS})
-    yrange = Collections('Range1d').create({start: 0, end: MAX_FREQ})
-
-    plot_model = Collections('Plot').create(
-      x_range: xrange
-      y_range: yrange
+class SpectrogramPlot
+  constructor: (options) ->
+    @cmap = new ColorMapper({}, {
+      palette: all_palettes["YlGnBu-9"],
+      low: 0,
+      high: 10
+    })
+
+    @image_width = NGRAMS
+    @image_height = 256
+    @image = new ArrayBuffer(SPECTROGRAM_LENGTH * NGRAMS * 4)
+    @source = Collections('ColumnDataSource').create(
+      data:{image: [@image]}
+    )
+
+    @xrange = Collections('Range1d').create({start: 0, end: NGRAMS})
+    @yrange = Collections('Range1d').create({start: 0, end: MAX_FREQ})
+
+    @model = Collections('Plot').create(
+      x_range: @xrange
+      y_range: @yrange
       border_fill: "#fff"
-      canvas_width: @image_width + 2*BORDER
-      canvas_height: @image_height + 2*BORDER
-      outer_width: @image_width + 2*BORDER
-      outer_height: @image_height + 2*BORDER
-      border: BORDER
+      canvas_width: options.width + 2*options.border
+      canvas_height: options.height + 2*options.border
+      outer_width: options.width + 2*options.border
+      outer_height: options.height + 2*options.border
+      border: options.border
       tools: []
     )

@@ -214,8 +207,9 @@
         location: 'min'
         bounds: 'auto'
       }
-      parent: plot_model.ref()
-    )
+      parent: @model
+    )
+
     yaxis = Collections('GuideRenderer').create(
       guidespec: {
         type: 'linear_axis'
@@ -223,30 +217,52 @@
         location: 'min'
         bounds: 'auto'
       }
-      parent: plot_model.ref()
-    )
-
-    glyphspec = {
-      type: 'image_rgba'
-      x: 0
-      y: 0
-      dw: NGRAMS
-      dh: MAX_FREQ
-      width: NGRAMS,
-      height: SPECTROGRAM_LENGTH,
-      image: 'image'
-      palette:
-        default: 'YlGnBu-9'
-    }
+      parent: @model
+    )
+
     glyph = Collections('GlyphRenderer').create({
-      data_source: @spec_source.ref()
-      xdata_range: xrange.ref()
-      ydata_range: yrange.ref()
-      glyphspec: glyphspec
-    })
-    plot_model.add_renderers([glyph, xaxis, yaxis])
-
-    return plot_model
+      data_source: @source
+      xdata_range: @xrange
+      ydata_range: @yrange
+      glyphspec: {
+        type: 'image_rgba'
+        x: 0
+        y: 0
+        dw: NGRAMS
+        dh: MAX_FREQ
+        width: NGRAMS,
+        height: SPECTROGRAM_LENGTH,
+        image: 'image'
+        palette:
+          default: 'YlGnBu-9'
+      }
+    })
+
+    @model.add_renderers([glyph, xaxis, yaxis])
+    @view = new @model.default_view(model: @model)
+
+  update: (fft) ->
+    buf = @cmap.v_map_screen(fft)
+
+    image32 = new Uint32Array(@image)
+    buf32 = new Uint32Array(buf)
+
+    for i in [0..(SPECTROGRAM_LENGTH-1)]
+      for j in [(NGRAMS-1)..1]
+        image32[i*NGRAMS + j] = image32[i*NGRAMS + j - 1]
+      image32[i*NGRAMS] = buf32[i]
+
+    @source.set('data', {image: [@image]})
+    @source.trigger('change', @spec_source, {})
+
+  set_xrange: (x0, x1) ->
+    @xrange.set({'start': x0, 'end' : x1})
+
+  set_yrange: (y0, y1) ->
+    @yrange.set({'start': y0, 'end' : y1})
+
+  render: () ->
+    @view.render()

 class RadialHistogramPlot
   constructor: (options) ->
@@ -272,24 +288,22 @@
       tools: []
     )

-    glyphspec = {
-      type: 'annular_wedge',
-      line_color: null
-      x: 0
-      y: 0
-      fill: '#688AB9'
-      fill_alpha: 'fill_alpha'
-      inner_radius: 'inner_radius'
-      outer_radius: 'outer_radius'
-      start_angle: 'start_angle'
-      end_angle: 'end_angle'
-    }
-
     glyph = Collections('GlyphRenderer').create({
       data_source: @source
       xdata_range: @range
       ydata_range: @range
-      glyphspec: glyphspec
+      glyphspec: {
+        type: 'annular_wedge',
+        line_color: null
+        x: 0
+        y: 0
+        fill: '#688AB9'
+        fill_alpha: 'fill_alpha'
+        inner_radius: 'inner_radius'
+        outer_radius: 'outer_radius'
+        start_angle: 'start_angle'
+        end_angle: 'end_angle'
+      }
     })

     @model.add_renderers([glyph])
@@ -403,7 +417,7 @@
     @xrange.set({'start': x0, 'end' : x1})

   set_yrange: (y0, y1) ->
-    @yrange.set({'start': x0, 'end' : x1})
+    @yrange.set({'start': y0, 'end' : y1})

   render: () ->
     @view.render()


Remove describe.only

Remove describe.only

--- test/database-spec.coffee
+++ test/database-spec.coffee
@@ -111,7 +111,7 @@
       ntb.cols.steder = steder_old
       done()

-describe.only '#_parseFields()', ->
+describe '#_parseFields()', ->
   ntb = ntb = new Database 'mongodb://localhost:27017/ntb_test'

   it 'should return empty list if fields object is undefined', ->


test/geojsonwriter: fix test for *An empty geometry collection JSON*

http://travis-ci.org/#!/kashif/node-geos/builds/1095522

--- test/geojsonwriter-test.coffee
+++ test/geojsonwriter-test.coffee
@@ -19,8 +19,8 @@
       assert.equal json.type, 'Point'

     "has undefined coordinates and bbox": (json) ->
-      assert.strictEqual json.coordinates, null
-      assert.strictEqual json.bbox, null
+      assert.isNull json.coordinates
+      assert.isNull json.bbox

   "An empty geometry collection JSON":
     topic: ->
@@ -30,8 +30,8 @@
       assert.equal json.type, 'GeometryCollection'

     "has correct geometries and bbox": (json) ->
-      assert.strictEqual json.geometries, []
-      assert.strictEqual json.bbox, null
+      assert.deepEqual json.geometries, []
+      assert.isNull json.bbox

   "A geometry JSON":
     topic: ->


rm console

rm console

--- apps/artist_new/routes.coffee
+++ apps/artist_new/routes.coffee
@@ -15,7 +15,6 @@
 ReferrerParser = require 'referer-parser'

 @index = (req, res, next) =>
-  console.log 'next', next
   return next() unless res.locals.sd.ARTIST_PAGE_FORMAT is 'new' or res.locals.sd.NODE_ENV is 'development'
   artist = new Artist id: req.params.id
   carousel = new Carousel artist: artist


Fixed incorrect nearest neighbor test

Fixed incorrect nearest neighbor test

Exception to "zero derivatives" is actually at the midpoints, not the
whole points.

--- test/specs/nearest.spec.coffee
+++ test/specs/nearest.spec.coffee
@@ -18,8 +18,8 @@
 		expect(s 2.2).toEqual arr[2]
 		expect(s 2.8).toEqual arr[3]

-	it 'should have zero derivatives for fractions', ->
-		expect(deriv(s) 0.1).toBeCloseTo 0
+	it 'should have zero derivatives where fraction != .5', ->
+		expect(deriv(s) 1).toBeCloseTo 0
 		expect(deriv(s) 1.1).toBeCloseTo 0
 		expect(deriv(s) 1.9).toBeCloseTo 0
 		expect(deriv(s) 3.2).toBeCloseTo 0


fix for #546

fix for #546

--- src/compositeunsubscribe.coffee
+++ src/compositeunsubscribe.coffee
@@ -15,7 +15,10 @@
       @remove unsub
       _.remove subscription, @starting
     unsub = subscription @unsubscribe, unsubMe
-    @subscriptions.push unsub unless (@unsubscribed or ended)
+    unless (@unsubscribed or ended)
+      @subscriptions.push unsub
+    else
+      unsub()
     _.remove subscription, @starting
     unsub
   remove: (unsub) ->


refactor

refactor

--- test/symphony-test.coffee
+++ test/symphony-test.coffee
@@ -114,42 +114,23 @@
         assert.fail(0, 1,"Failed with error #{error}")

 describe 'Object model test suite', () ->
-  it 'parse a V2Message', () ->
-    msg = {
-            id: 'foobar'
-            v2messageType: 'V2Message'
-            streamId: 'baz'
-            message: '<messageML>Hello World</messageML>'
-            fromUserId: 12345
-          }
-    user = {
-             userAttributes: {
-               displayName: 'John Doe'
+  for text in ['<messageML>Hello World</messageML>', 'Hello World']
+    it 'parse a V2Message containing ' + text, () ->
+      msg = {
+              id: 'foobar'
+              v2messageType: 'V2Message'
+              streamId: 'baz'
+              message: text
+              fromUserId: 12345
+            }
+      user = {
+               userAttributes: {
+                 displayName: 'John Doe'
+               }
              }
-           }
-    v2 = new V2Message(user, msg)
-    assert.equal('Hello World', v2.text)
-    assert.equal('foobar', v2.id)
-    assert.equal(12345, v2.user.id)
-    assert.equal('John Doe', v2.user.name)
-    assert.equal('baz', v2.room)
-
-  it 'parse a V2Message with plaintext if that happens to arrive', () ->
-    msg = {
-            id: 'foobar'
-            v2messageType: 'V2Message'
-            streamId: 'baz'
-            message: 'Hello World'
-            fromUserId: 12345
-          }
-    user = {
-             userAttributes: {
-               displayName: 'John Doe'
-             }
-           }
-    v2 = new V2Message(user, msg)
-    assert.equal('Hello World', v2.text)
-    assert.equal('foobar', v2.id)
-    assert.equal(12345, v2.user.id)
-    assert.equal('John Doe', v2.user.name)
-    assert.equal('baz', v2.room)
+      v2 = new V2Message(user, msg)
+      assert.equal('Hello World', v2.text)
+      assert.equal('foobar', v2.id)
+      assert.equal(12345, v2.user.id)
+      assert.equal('John Doe', v2.user.name)
+      assert.equal('baz', v2.room)


Tests: Use 'node' as prog instead of imgflo-url

Would fail on Travis CI, probably cause they don't have /bin/env
or it cannot look up 'node' due to their multi-node.js setup.

--- spec/cli.coffee
+++ spec/cli.coffee
@@ -9,8 +9,9 @@
     'IMGFLO_API_SECRET': config.secret
   environ.IMGFLO_API_SERVER = config.server if config.server

-  prog = path.join __dirname, '../bin', 'imgflo-url'
-  args = [ graphname ]
+  prog = 'node'
+  bin = path.join __dirname, '../bin', 'imgflo-url'
+  args = [ bin, graphname ]
   for key, value of params
     args.push "--#{key}=#{value}"


Set body's visibility to hidden when unloading

Previously the display was changed to none via $.hide() which accidentally
affected the ability of package's to serialize DOM properties such as
scrollTop since the value would always be zero when the display was none.

The goal here is to just prevent a flicker when refreshing the
editor window and setting visibility to hidden still accomplishes this.

--- src/atom.coffee
+++ src/atom.coffee
@@ -194,7 +194,7 @@
     @menu.update()

     $(window).on 'unload', =>
-      $(document.body).hide()
+      $(document.body).css('visibility', 'hidden')
       @unloadEditorWindow()
       false


Minor fix for saveChanges being called twice

--- src/base.coffee
+++ src/base.coffee
@@ -453,14 +453,13 @@

     if editing
       shouldRemove = @shouldTryToRemoveFromParent()
-      @saveChanges(@getValEl())
-      @flushChanges() unless shouldRemove
       unless aggressive or @isValid()
         @refreshErrors() # make sure workingSchema's errors come through
         return
       if shouldRemove and $(@$el[0].nextSibling)?.hasClass('treema-add-child') and offset is 1
         offset = 2
       @endExistingEdits()
+      @flushChanges() unless shouldRemove
       @select()

     ctx = @traversalContext(offset)