Cleaned up TextInputView

Cleaned up TextInputView

--- core/app/backbone/factlink/text_input_view.coffee
+++ core/app/backbone/factlink/text_input_view.coffee
@@ -1,14 +1,16 @@
 Backbone.Factlink ||= {}
 class Backbone.Factlink.TextInputView extends Backbone.Marionette.ItemView
   events:
-    'click': 'focusInput'
     'keydown .typeahead': 'parseKeyDown'
-    'keyup .typeahead': 'updateModel'
-    'input .typeahead': 'updateModel'
+    'keyup .typeahead': '_updateModel'
+    'input .typeahead': '_updateModel'

   triggers:
     'focus .typeahead': 'focus'
     'blur .typeahead': 'blur'
+
+  ui:
+    inputField: '.typeahead'

   templateHelpers: =>
     placeholder: @options.placeholder
@@ -16,10 +18,10 @@
   template: 'generic/text_input'

   initialize: ->
-    @listenTo @model, 'change', @updateHtml
+    @listenTo @model, 'change', @_updateHtml

   focusInput: ->
-    _.defer => @$inputField().focus()
+    _.defer => @ui.inputField.focus()

   parseKeyDown: (e) ->
     eventHandled = false
@@ -34,12 +36,7 @@
       e.preventDefault()
       e.stopPropagation()

-  updateModel: -> @model.set text: @$inputField().val()
-  updateHtml: ->
-    if @model.get('text') != @$inputField().val()
-      @$inputField().val(@model.get('text'))
-
-  enable: -> @$inputField().prop 'disabled', false
-  disable: ->@$inputField().prop 'disabled', true
-
-  $inputField: -> @$('.typeahead')
+  _updateModel: -> @model.set text: @ui.inputField.val()
+  _updateHtml: ->
+    if @model.get('text') != @ui.inputField.val()
+      @ui.inputField.val(@model.get('text'))


fixed #2 (wording 'twelfth')

fixed #2 (wording 'twelfth')

--- prototyping/arrayPrototyping.coffee
+++ prototyping/arrayPrototyping.coffee
@@ -323,7 +323,7 @@
         set: (val) ->
             @[10] = val
             return @
-    twelveth:
+    twelfth:
         get: () ->
             return @[11]
         set: (val) ->


State bug fixed

State bug fixed

--- utils/item.coffee
+++ utils/item.coffee
@@ -110,7 +110,7 @@
 	createBindingSetter: (propName, setFunc) ->
 		(val) ->
 			# synchronize with default state
-			if @_data.states and @_data.state is ''
+			if @_data.states and @_data.state is '' and propName isnt 'state' and propName isnt 'parent'
 				@_data.states[''][propName] = val

 			if val and isArray val.binding


JGroup: setOAuth: remove sensitive data on oauth disable

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1181,7 +1181,16 @@
       # TODO: add remove existing sessions feature here ~ GG
       if not enabled

-        @updateAndNotify notifyOptions, { $unset: dataToUpdate }, callback
+        @updateAndNotify notifyOptions, { $unset: dataToUpdate }, (err) =>
+          return callback err  if err
+
+          @fetchData (err, data) ->
+            return callback err  if err
+
+            dataToUnset = {}
+            dataToUnset["data.#{provider}.applicationSecret"] = null
+            data.update { $unset: dataToUnset }, callback
+
         return

       validateOptions = { url, applicationId, applicationSecret }


[LoadingFree] set default values

[LoadingFree] set default values

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -11,6 +11,9 @@

   options.intro   ?= no
   options.client or= {}
+  options.client.context or= {}
+  options.client.context.group or= "koding"
+

   prefetchedFeeds = {}
   {bongoModels, client, intro} = options


[grunt] remove no longer used lodash method from Gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -1,4 +1,4 @@
-{ map, includes } = require('lodash')
+{ includes } = require('lodash')

 module.exports = (grunt) ->
   'use strict'


Reduce documentation threshold

Reduce documentation threshold

I deleted a bunch of documented methods. I haven't added any new
undocumented methods, but now there are fewer overall documented
methods, which is breaking the docs threshold spec. I don't think it
makes sense to start documenting non-related methods in this branch
right now.

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -353,7 +353,7 @@
         expect(errors).toBe 0

         coverage = parseFloat results.pop().match(/.+?%/)
-        expect(coverage).toBeGreaterThan 80
+        expect(coverage).toBeGreaterThan 79

         # stderr
         expect(docRunner.argsForCall[0][2]).toBe ''


fix grammar in spec description; plural to singlular

--- spec/autosave-spec.coffee
+++ spec/autosave-spec.coffee
@@ -47,7 +47,7 @@
         document.body.focus()
         expect(initialActiveItem.save).toHaveBeenCalled()

-      it "suppresses autosave if the files doesn't exist", ->
+      it "suppresses autosave if the file does not exist", ->
         document.body.focus()
         expect(initialActiveItem.save).not.toHaveBeenCalled()


Specs: Make linter happy

Specs: Make linter happy

--- spec/hydrogen-spec.coffee
+++ spec/hydrogen-spec.coffee
@@ -6,35 +6,35 @@
 path = require 'path'
 fs = require 'fs'

-describe "Atom config", ->
-    it "should set config value", ->
-        Config.setJson "set", "foo"
-        expect(JSON.parse atom.config.get "Hydrogen.set").toEqual("foo")
+describe 'Atom config', ->
+    it 'should set config value', ->
+        Config.setJson 'set', 'foo'
+        expect(JSON.parse atom.config.get 'Hydrogen.set').toEqual('foo')

-    it "should read config values", ->
-        atom.config.set "Hydrogen.read", JSON.stringify "bar"
-        expect(Config.getJson "read").toEqual("bar")
+    it 'should read config values', ->
+        atom.config.set 'Hydrogen.read', JSON.stringify 'bar'
+        expect(Config.getJson 'read').toEqual('bar')

-    it "should return {} for broken config", ->
-        atom.config.set "Hydrogen.broken", "foo"
-        expect(Config.getJson "broken").toEqual({})
+    it 'should return {} for broken config', ->
+        atom.config.set 'Hydrogen.broken', 'foo'
+        expect(Config.getJson 'broken').toEqual({})

-describe "Port config manager", ->
-    it "should build port config", ->
+describe 'Port config manager', ->
+    it 'should build port config', ->
         ports = [60000...60004]
         config = ConfigManager.buildConfiguration ports
         expect(config.version).toEqual(5)
         expect(config.key.length).toEqual(36)
-        expect(config.signature_scheme).toEqual("hmac-sha256")
-        expect(config.transport).toEqual("tcp")
-        expect(config.ip).toEqual("127.0.0.1")
+        expect(config.signature_scheme).toEqual('hmac-sha256')
+        expect(config.transport).toEqual('tcp')
+        expect(config.ip).toEqual('127.0.0.1')
         expect(config.hb_port).toEqual(ports[0])
         expect(config.control_port).toEqual(ports[1])
         expect(config.shell_port).toEqual(ports[2])
         expect(config.stdin_port).toEqual(ports[3])
         expect(config.iopub_port).toEqual(ports[4])

-    it "should write a port config file", ->
+    it 'should write a port config file', ->
         fileStoragePath = path.join(__dirname, '..', 'kernel-configs')
         try
             fileNumberBefore = fs.readdirSync(fileStoragePath).length
@@ -45,25 +45,25 @@
             fileNumberAfter = fs.readdirSync(fileStoragePath).length
             expect(fileNumberAfter).toEqual(fileNumberBefore + 1)

-describe "Port finder", ->
-    it "should find a free port", ->
+describe 'Port finder', ->
+    it 'should find a free port', ->
         portfinder.find (port) ->
             expect(port).toMatch(/\d{1,}/)
-    it "should find 3 free ports", ->
+    it 'should find 3 free ports', ->
         portfinder.findMany 3, (ports) ->
             expect(ports.length).toEqual(3)
             expect(ports[0]).toMatch(/\d{1,}/)
             expect(ports[1]).toMatch(/\d{1,}/)
             expect(ports[2]).toMatch(/\d{1,}/)
-    it "should find 2 additional free ports", ->
+    it 'should find 2 additional free ports', ->
         portfinder.findManyHelper 2, [60000], (ports) ->
             expect(ports.length).toEqual(3)
             expect(ports[0]).toEqual(60000)
             expect(ports[1]).toMatch(/\d{1,}/)
             expect(ports[2]).toMatch(/\d{1,}/)

-describe "Kernel manager", ->
-    firstKernelSpecString = """{
+describe 'Kernel manager', ->
+    firstKernelSpecString = '''{
         "kernelspecs": {
             "ijavascript": {
                 "spec": {
@@ -80,8 +80,8 @@
                 "resource_dir": "/home/user/node_modules/ijavascript/images"
             }
         }
-    }"""
-    secondKernelSpecString = """{
+    }'''
+    secondKernelSpecString = '''{
         "kernelspecs": {
             "python2": {
                 "resource_dir": "/usr/local/lib/python2.7/site-packages/ipykernel/resources",
@@ -99,7 +99,7 @@
                 }
             }
         }
-    }"""
+    }'''

     firstKernelSpec = JSON.parse firstKernelSpecString
     secondKernelSpec = JSON.parse secondKernelSpecString
@@ -108,92 +108,92 @@
     kernelSpecs.kernelspecs.python2 = secondKernelSpec.kernelspecs.python2

     beforeEach ->
-        atom.config.set "Hydrogen.kernelspec", ""
+        atom.config.set 'Hydrogen.kernelspec', ''

-    describe "parseKernelSpecSettings", ->
-        it "should parse kernelspecs from settings", ->
-            atom.config.set "Hydrogen.kernelspec", firstKernelSpecString
+    describe 'parseKernelSpecSettings', ->
+        it 'should parse kernelspecs from settings', ->
+            atom.config.set 'Hydrogen.kernelspec', firstKernelSpecString

             parsed = KernelManager::parseKernelSpecSettings()

             expect(parsed).toEqual(firstKernelSpec.kernelspecs)

-        it "should return {} if no kernelspec is set", ->
+        it 'should return {} if no kernelspec is set', ->
             expect(KernelManager::parseKernelSpecSettings()).toEqual({})

-        it "should return {} if invalid kernelspec is set", ->
-            atom.config.set "Hydrogen.kernelspec", "invalid"
+        it 'should return {} if invalid kernelspec is set', ->
+            atom.config.set 'Hydrogen.kernelspec', 'invalid'
             expect(KernelManager::parseKernelSpecSettings()).toEqual({})

-    describe "saveKernelSpecs", ->
-        it "should not write invalid json strings to settings", ->
-            KernelManager::saveKernelSpecs("invalid")
-            expect(atom.config.get "Hydrogen.kernelspec").toEqual("")
+    describe 'saveKernelSpecs', ->
+        it 'should not write invalid json strings to settings', ->
+            KernelManager::saveKernelSpecs('invalid')
+            expect(atom.config.get 'Hydrogen.kernelspec').toEqual('')

-        it "should not write invalid kernelspecs to json", ->
+        it 'should not write invalid kernelspecs to json', ->
             KernelManager::saveKernelSpecs('{"invalid": "kernel"}')
-            expect(atom.config.get "Hydrogen.kernelspec").toEqual("")
+            expect(atom.config.get 'Hydrogen.kernelspec').toEqual('')

-        it "should save kernelspecs to settings", ->
+        it 'should save kernelspecs to settings', ->
             KernelManager::saveKernelSpecs(firstKernelSpecString)

-            config = JSON.parse atom.config.get "Hydrogen.kernelspec"
+            config = JSON.parse atom.config.get 'Hydrogen.kernelspec'
             expect(config).toEqual(firstKernelSpec)

-        it "should add kernelspecs to settings", ->
-            atom.config.set "Hydrogen.kernelspec", firstKernelSpecString
+        it 'should add kernelspecs to settings', ->
+            atom.config.set 'Hydrogen.kernelspec', firstKernelSpecString
             KernelManager::saveKernelSpecs(secondKernelSpecString)
-            config = JSON.parse atom.config.get "Hydrogen.kernelspec"
+            config = JSON.parse atom.config.get 'Hydrogen.kernelspec'

             expect(config.kernelspecs.ijavascript).toEqual(firstKernelSpec.kernelspecs.ijavascript)
             expect(config.kernelspecs.python2).toEqual(secondKernelSpec.kernelspecs.python2)

-    describe "getAllKernelSpecs", ->
-        it "should return an array with specs", ->
-            atom.config.set "Hydrogen.kernelspec", JSON.stringify kernelSpecs
+    describe 'getAllKernelSpecs', ->
+        it 'should return an array with specs', ->
+            atom.config.set 'Hydrogen.kernelspec', JSON.stringify kernelSpecs
             allKernelSpecs = KernelManager::getAllKernelSpecs()

             expect(allKernelSpecs.length).toEqual(2)
             expect(allKernelSpecs[0]).toEqual(kernelSpecs.kernelspecs.ijavascript.spec)
             expect(allKernelSpecs[1]).toEqual(kernelSpecs.kernelspecs.python2.spec)

-    describe "getAllKernelSpecsFor", ->
-        it "should return an array with specs for given language", ->
-            atom.config.set "Hydrogen.kernelspec", JSON.stringify kernelSpecs
-            allKernelSpecsForPython = KernelManager::getAllKernelSpecsFor("python")
+    describe 'getAllKernelSpecsFor', ->
+        it 'should return an array with specs for given language', ->
+            atom.config.set 'Hydrogen.kernelspec', JSON.stringify kernelSpecs
+            allKernelSpecsForPython = KernelManager::getAllKernelSpecsFor('python')

             expect(allKernelSpecsForPython.length).toEqual(1)
             expect(allKernelSpecsForPython[0]).toEqual(kernelSpecs.kernelspecs.python2.spec)

-        it "should return an empty array", ->
-            atom.config.set "Hydrogen.kernelspec", JSON.stringify kernelSpecs
-            allKernelSpecsForJulia = KernelManager::getAllKernelSpecsFor("julia")
+        it 'should return an empty array', ->
+            atom.config.set 'Hydrogen.kernelspec', JSON.stringify kernelSpecs
+            allKernelSpecsForJulia = KernelManager::getAllKernelSpecsFor('julia')

             expect(allKernelSpecsForJulia).toEqual([])

-    describe "getKernelSpecFor", ->
-        it "should return spec for given language", ->
-            atom.config.set "Hydrogen.kernelspec", JSON.stringify kernelSpecs
-            kernelSpecForPython = KernelManager::getKernelSpecFor("python")
+    describe 'getKernelSpecFor', ->
+        it 'should return spec for given language', ->
+            atom.config.set 'Hydrogen.kernelspec', JSON.stringify kernelSpecs
+            kernelSpecForPython = KernelManager::getKernelSpecFor('python')

             console.log kernelSpecForPython
             expect(kernelSpecForPython).toEqual(kernelSpecs.kernelspecs.python2.spec)

-        it "should return undefined", ->
-            atom.config.set "Hydrogen.kernelspec", JSON.stringify kernelSpecs
-            kernelSpecForJulia = KernelManager::getKernelSpecFor("julia")
+        it 'should return undefined', ->
+            atom.config.set 'Hydrogen.kernelspec', JSON.stringify kernelSpecs
+            kernelSpecForJulia = KernelManager::getKernelSpecFor('julia')

             expect(kernelSpecForJulia).toBeUndefined()

-    it "should read lower case name from grammar", ->
+    it 'should read lower case name from grammar', ->
         grammar = atom.grammars.getGrammars()[0]
-        expect(KernelManager::getLanguageFor grammar).toEqual("null grammar")
+        expect(KernelManager::getLanguageFor grammar).toEqual('null grammar')

-    it "should update kernelspecs", ->
+    it 'should update kernelspecs', ->
         KernelManager::updateKernelSpecs()

         waits(3000)
         runs ->
-            kernelspec = JSON.parse atom.config.get "Hydrogen.kernelspec"
+            kernelspec = JSON.parse atom.config.get 'Hydrogen.kernelspec'
             expect(kernelspec).not.toBeUndefined()
             expect(kernelspec.kernelspecs).not.toBeUndefined()


Cleanup

Cleanup

--- spec/src/grid_spec.coffee
+++ spec/src/grid_spec.coffee
@@ -2,7 +2,7 @@

 describe "ring", ->
   it "returns coordinates for a ring two (2) tiles from center", ->
-    grid = new Grid()
+    grid = new Grid
     result = grid.ring(-1, -1, 2)
     expect(result).toContain({ q:  1, r: -1 })
     expect(result).toContain({ q:  1, r: -2 })
@@ -19,7 +19,7 @@

 describe "hexagon", ->
   beforeEach ->
-    @grid = new Grid()
+    @grid = new Grid

   it "returns coordinates for a solid hexagonal shape stretching two (2) tiles from center", ->
     result = @grid.hexagon(-1, -1, 2, true)
@@ -68,7 +68,7 @@

 describe "getCenterXY", ->
   it "calculates pixel coordinates based on hexagon settings", ->
-    grid = new Grid()
+    grid = new Grid
     grid.tileSpacing = 0
     grid.tileSize = 100
     grid.pointyTiles = false
@@ -86,7 +86,7 @@

 describe "axialDistance", ->
   beforeEach ->
-    @grid = new Grid()
+    @grid = new Grid

   it "distance from (0,0) to (0,0) is 0", ->
     result = @grid.axialDistance(0, 0, 0, 0)
@@ -102,25 +102,25 @@

 describe "roundCube", ->
   it "calculates the cube coordinates based on the two lowest round() coordinate diffs to get the third and returns the whole set", ->
-    grid = new Grid()
+    grid = new Grid
     result = grid.roundCube({ x: 0.49, y: 1.95, z: 2.95 })
     expect(result).toEqual({ x: -5, y: 2, z: 3 })

 describe "cubeToAxial", ->
   it "extracts (q,r) from (x,z) in (x,y,z)", ->
-    grid = new Grid()
+    grid = new Grid
     result = grid.cubeToAxial({ x: 3, y: -1, z: -2 })
     expect(result).toEqual({ q: 3, r: -1 })

 describe "axialToCube", ->
   it "calculates (x,y,z) from (q,r)", ->
-    grid = new Grid()
+    grid = new Grid
     result = grid.axialToCube({ q: 3, r: -1 })
     expect(result).toEqual({ x: 3, y: -1, z: -2 })

 describe "neighbors", ->
   it "returns neighbor coordinates", ->
-    grid = new Grid()
+    grid = new Grid
     result = grid.neighbors(0, 0)
     expect(result).toContain({ q:  1, r:  0 })
     expect(result).toContain({ q:  1, r: -1 })
@@ -132,7 +132,7 @@

 describe "pixelToAxial", ->
   it "returns q and r from pixels coordinates and scale", ->
-    grid = new Grid()
+    grid = new Grid
     expect(grid.pixelToAxial(0, -150)).toEqual({ q: 0, r: 1 })
     expect(grid.pixelToAxial(-150, 0)).toEqual({ q: -1, r: 1 })
     expect(grid.pixelToAxial(0, 150)).toEqual({ q: 0, r: -1 })


minor improvements to spectrogam

minor improvements to spectrogam

--- demo/spectrogram/static/coffee/spectrogram.coffee
+++ demo/spectrogram/static/coffee/spectrogram.coffee
@@ -11,8 +11,6 @@

 window.TIMESLICE = 40 # ms

-BORDER = 50
-
 NGRAMS = 800

 HIST_NUM_BINS = 16
@@ -38,17 +36,14 @@

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

-    @spec_plot = new SpectrogramPlot({
-      width: NGRAMS, height: SPECTROGRAM_LENGTH/2, border: BORDER
-    })
+    @spec_plot = new SpectrogramPlot({ width: NGRAMS, height: SPECTROGRAM_LENGTH/2+80 })

     @power_plot = new SimpleIndexPlot({
-
-      x0: 0, x1: window.TIMESLICE, y0: -0.5, y1: 0.5, width: 550, height: 180, border: BORDER
+      x0: 0, x1: window.TIMESLICE+0.1, y0: -0.6, y1: 0.7, width: 800, height: 250
     })

     @fft_plot = new SimpleIndexPlot({
-      x0: FREQ_SLIDER_MIN, x1: FREQ_SLIDER_MAX, y0: -1, y1: 20, width: 550, height: 180, border: BORDER
+      x0: FREQ_SLIDER_MIN, x1: FREQ_SLIDER_MAX, y0: -1, y1: 26, width: 800, height: 250
     })

     @hist_plot = new RadialHistogramPlot({
@@ -74,7 +69,6 @@
           => @throttled_request_data())

   on_data: (data) ->
-
     # apply the gain
     for i in [0..(data[0].length-1)]
       data[0][i] *= @gain
@@ -207,41 +201,34 @@
       x_range: @xrange.ref()
       y_range: @yrange.ref()
       border_fill: "#fff"
-      canvas_width: options.width + options.border + 80
-      canvas_height: options.height + 2*options.border
-      outer_width: options.width + 2*options.border
-      outer_height: options.height + 2*options.border
-      border: options.border
-      border_left: 80
+      canvas_width: options.width
+      canvas_height: options.height
+      outer_width: options.width
+      outer_height: options.height
       tools: []
       title: ""
     )

     xaxis = Collections('GuideRenderer').create(
+      plot: @model.ref()
       guidespec: {
         type: 'linear_axis'
         dimension: 0
-        location: 'min'
-        bounds: 'auto'
-      }
+      }
+    )
+
+    yaxis = Collections('GuideRenderer').create(
       plot: @model.ref()
-    )
-
-    yaxis = Collections('GuideRenderer').create(
       guidespec: {
         type: 'linear_axis'
         dimension: 1
-        location: 'min'
-        bounds: 'auto'
-      }
-      major_label_standoff: 40
-      plot: @model.ref()
+      }
     )

     glyph = Collections('GlyphRenderer').create({
-      data_source: @source
-      xdata_range: @xrange
-      ydata_range: @yrange
+      data_source: @source.ref()
+      xdata_range: @xrange.ref()
+      ydata_range: @yrange.ref()
       glyphspec: {
         type: 'image_rgba'
         x: 'x'
@@ -255,8 +242,7 @@
           default: 'YlGnBu-9'
       }
     })
-
-    @model.add_renderers([glyph, xaxis, yaxis])
+    @model.add_renderers([glyph.ref(), xaxis.ref(), yaxis.ref()])
     @view = new @model.default_view(model: @model)

   update: (fft) ->
@@ -311,7 +297,6 @@
       canvas_height: options.height
       outer_width:   options.width
       outer_height:  options.height
-      border: 0
       tools: []
       title: ""
     )
@@ -391,32 +376,36 @@
       x_range: @xrange.ref()
       y_range: @yrange.ref()
       border_fill: "#fff"
-      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
+      canvas_width:  options.width
+      canvas_height: options.height
+      outer_width:   options.width
+      outer_height:  options.height
       tools: []
       title: ""
     )

     xaxis = Collections('GuideRenderer').create(
+      plot: @model.ref()
       guidespec: {
         type: 'linear_axis'
         dimension: 0
-        location: 'min'
-        bounds: 'auto'
-      }
+      }
+    )
+
+    yaxis = Collections('GuideRenderer').create(
       plot: @model.ref()
-    )
-
-    yaxis = Collections('GuideRenderer').create(
       guidespec: {
         type: 'linear_axis'
         dimension: 1
-        location: 'min'
-        bounds: 'auto'
-      }
+      }
+    )
+
+    yrule = Collections('GuideRenderer').create(
       plot: @model.ref()
+      guidespec: {
+        type: 'rule'
+        dimension: 1
+      }
     )

     glyph = Collections('GlyphRenderer').create({
@@ -431,7 +420,7 @@
       }
     })

-    @model.add_renderers([glyph.ref(), xaxis.ref(), yaxis.ref()])
+    @model.add_renderers([glyph.ref(), xaxis.ref(), yaxis.ref(), yrule.ref()])
     @view = new @model.default_view(model: @model)

   update: (ys) ->


working on selection tool

working on selection tool

--- src/coffee/bokeh_views.coffee
+++ src/coffee/bokeh_views.coffee
@@ -666,7 +666,7 @@

 #  tools

-class PanToolView extends PlotWidget
+class PanToolView_ extends PlotWidget
   initialize : (options) ->
     @dragging = false
     super(options)
@@ -755,14 +755,6 @@
           @_selecting(e, x, y)
           e.preventDefault()
           e.stopPropagation())
-    select_button = $('<button> Selction Tool </button>')
-    @plotview.$el.find('.button_bar').append(select_button)
-    select_button.click(=>
-      if @button_selecting
-        @stop_drag()
-      else
-        @_start_selecting("foo", 0, 0)
-        @button_selecting = true)

   mouse_coords : (e, x, y) ->
@@ -867,13 +859,46 @@
     @render_end()
     return null

+class PanToolView extends PanToolView_
+  initialize : (options) ->
+    super(options)
+    @selecting = false
+    @button_clicked = false
+
+  bind_events : (plotview) ->
+    console.log("pantoolview bind_events")
+    @plotview = plotview
+    @plotview.mousedownCallbacks.push((e, x, y) =>
+      if @button_panning
+        @dragging = false
+        @button_panning = false
+      else
+        @
+        @_start_drag(e, x, y))
+
+
+    @plotview.moveCallbacks.push((e, x, y) =>
+      if @button_panning and @dragging
+        @_drag(e.foo, e.foo, e, x, y)
+        e.preventDefault()
+        e.stopPropagation())
+    pantool_button = $('<button> Pan Tool </button>')
+    @plotview.$el.find('.button_bar').append(pantool_button)
+    pantool_button.click(=>
+      if @button_panning
+        @dragging = false
+        @button_panning = false
+      else
+        #@_start_drag("foo", 0, 0)
+        @button_panning = true)
+
+
+
 class SelectionToolView extends SelectionToolView_
   """this version only works via the toolbar button   """
   initialize : (options) ->
     super(options)
     @selecting = false
-    @select_button_clicked = false
-

   bind_events : (plotview) ->
     console.log("SelectionToolView bind_events")


refactoring

refactoring

--- src/stamina-calculator.coffee
+++ src/stamina-calculator.coffee
@@ -8,13 +8,13 @@
       temp_stamina = 0
       while temp_stamina <= max_stamina
         if now_stamina < temp_stamina
-          recovery_time.push (temp_stamina - now_stamina) * @stamina_recovery_time
+          recovery_time.push @getRecoveredTime(temp_stamina - now_stamina)
         temp_stamina += multiple_stamina
     return recovery_time

   getNextMaxStaminaTime: (now_stamina, max_stamina) ->
     if now_stamina < max_stamina
-      return (max_stamina - now_stamina) * @stamina_recovery_time
+      return @getRecoveredTime(max_stamina - now_stamina)
     return 0

   getNextLevelupTimes: (next_exp, get_exp) ->


Handle deleting multiple directories

Handle deleting multiple directories

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -417,14 +417,35 @@
     packageDir = null
     devDir = path.join("dev", "packages")
     for packageDirPath in @packageDirPaths
-      packageDir = packageDirPath if not packageDirPath.endsWith(devDir)
+      if not packageDirPath.endsWith(devDir)
+        packageDir = packageDirPath
+        break

     if packageDir?
-      autocompletePlusPath = path.join(packageDir, 'autocomplete-plus')
-      if fs.isSymbolicLinkSync(autocompletePlusPath)
-        fs.unlink(autocompletePlusPath)
-      else if fs.isDirectorySync(autocompletePlusPath)
-        fs.remove(autocompletePlusPath, ->)
+      dirsToRemove = [
+        path.join(packageDir, 'autocomplete-plus')
+        path.join(packageDir, 'autocomplete-atom-api')
+        path.join(packageDir, 'autocomplete-css')
+        path.join(packageDir, 'autocomplete-html')
+      ]
+      for dirToRemove in dirsToRemove
+        @uninstallDirectory(dirToRemove)
+    return
+
+  uninstallDirectory: (directory) ->
+    symlinkPromise = new Promise (resolve) ->
+      fs.isSymbolicLink directory, (isSymLink) -> resolve(isSymLink)
+
+    dirPromise = new Promise (resolve) ->
+      fs.isDirectory directory, (isDir) -> resolve(isDir)
+
+    Promise.all([symlinkPromise, dirPromise]).then (values) ->
+      [isSymLink, isDir] = values
+      console.log directory, isSymLink, isDir
+      if isSymLink
+        fs.unlink(directory)
+      else if isDir
+        fs.remove(directory, ->)

 if Grim.includeDeprecatedAPIs
   EmitterMixin = require('emissary').Emitter


canvas div + some refactoring

canvas div + some refactoring

--- src/scripts/directive.coffee
+++ src/scripts/directive.coffee
@@ -31,10 +31,7 @@
         compile: (element, attr, linker) ->
           ($scope, $element, $attr, controller) ->

-            console.log controller
-
             match = $attr.ngScroll.match /^\s*(\w+)\s+in\s+(\w+)\s*$/
-
             if !match
               throw Error "Expected ngScroll in form of '_item_ in _datasource_' but got '#{$attr.ngScroll}'"

@@ -49,19 +46,28 @@

                 ###

-                List of used element methods defined in JQuery but not in JQuery Lite
+                List of used element methods available in JQuery but not in JQuery Lite
                 in other words if you want to remove dependency on JQuery the following methods are to be implemented:
+
                 element.height()
                 element.outerHeight(true)
                 element.height(value) = only for Top/Bottom padding elements
                 element.scrollTop()
                 element.scrollTop(value)
-                element.position()
+                element.offset()

                 ###

                 viewport = controller[0] || angular.element(window)
                 canvas = controller[1] || element.parent()
+                if canvas[0] == viewport[0]
+                  # if canvas and the viewport are the same create a new div to service as canvas
+                  contents = canvas.contents()
+                  canvas = angular.element('<div/>')
+                  viewport.append canvas
+                  canvas.append contents
+
+

                 topPadding = angular.element('<div/>')
                 element.before topPadding
@@ -95,14 +101,11 @@
                   #(buffer.length == 0 || scope.defaultText(buffer[buffer.length-1]).toLowerCase() <= globals.selectedText.toLowerCase()) ||
                   # and we have enough for the scrollbar to show up
                   item = buffer[buffer.length-1]
-
-                  result = !eof &&
-                  (!item ||
-                  item.element.offset().top - canvas.offset().top + item.element.outerHeight(true) <
-                    viewport.scrollTop() + viewport.height() + bufferPadding())
-
-                  console.log 'load bottom ' + result
-                  result
+                  if item
+                    !eof && item.element.offset().top - canvas.offset().top + item.element.outerHeight(true) <
+                      viewport.scrollTop() + viewport.height() + bufferPadding()
+                  else
+                    true

                 clipBottom = ->
                     # clip off the invisible items form the bottom
@@ -128,10 +131,8 @@
                     console.log "clipped off bottom #{overage} bottom padding #{bottomHeight}"

                 shouldLoadTop = ->
-                  result = first > 1 &&
+                  first > 1 &&
                     buffer[0].element.offset().top - canvas.offset().top > viewport.scrollTop() - bufferPadding()
-                  console.log 'load top ' + result
-                  result

                 clipTop = ->
                   # clip off the invisible items form the top
@@ -167,18 +168,17 @@
                   enqueueFetch(true) if reloadRequested || shouldLoadBottom()
                   enqueueFetch(false) if shouldLoadTop()

+                adjustHeight = (padding, element) ->
+                  padding.height(Math.max(0,padding.height() - element.outerHeight(true)))
+
                 append = (item) ->
-                  if buffer.length > 0
-                    insertAfter = buffer[buffer.length-1].element
-                  else
-                    insertAfter = topPadding
                   itemScope = $scope.$new()
                   itemScope[itemName] = item
                   wrapper =
                     scope: itemScope
                   linker itemScope,
                     (clone) ->
-                      insertAfter.after clone
+                      bottomPadding.before clone
                       wrapper.element = clone
                       buffer.push wrapper
                   # using watch is the only way I found to gather the 'real' height of the thing - the height after the item
@@ -188,14 +188,13 @@
                       bottomPadding.height(Math.max(0,bottomPadding.height() - wrapper.element.outerHeight(true)))

                 prepend = (item) ->
-                  insertAfter = topPadding
                   itemScope = $scope.$new()
                   itemScope[itemName] = item
                   wrapper =
                     scope: itemScope
                   linker itemScope,
                   (clone) ->
-                    insertAfter.after clone
+                    topPadding.after clone
                     wrapper.element = clone
                     buffer.unshift wrapper
                   # using watch is the only way I found to gather the 'real' height of the thing - the height after the item


Encourage proper spelling

Encourage proper spelling

It's a subtle thing but since it shows up in the hubot help a user noticed.

--- src/scripts/encourage.coffee
+++ src/scripts/encourage.coffee
@@ -9,7 +9,7 @@
 #
 # Commands:
 #   hubot encourage me
-#   hubot encourge name
+#   hubot encourage name
 #   hubot encourage all
 #
 # Author:


Don't initialize IterationView bindings until it's added to the superview.

--- src/view/iterator_view.coffee
+++ src/view/iterator_view.coffee
@@ -14,6 +14,7 @@
     iterationView.set(@iteratorName, item)

     @subviews.set(item, iterationView)
+    iterationView.initializeBindings()

   finishAppendItems: ->
     node = @get('node')
@@ -33,4 +34,5 @@

     super

+  bindImmediately: false
   addToDOM: null


disable java side-by-side mode, since it doesn't work when the file is in a subdirectory; also it leaves around a .html file, which is sloppy.

--- src/smc-webapp/editor.coffee
+++ src/smc-webapp/editor.coffee
@@ -191,11 +191,13 @@
     opts   : {indent_unit:4, tab_size:4, mode:'gfm2'}
     name   : "Rmd"

+###
 file_associations['java'] =
     editor : 'html-md'
     icon   : 'fa-file-code-o'
     opts   : {indent_unit:4, tab_size:4, mode:'text/x-java'}
     name   : "Java"
+###

 file_associations['rst'] =
     editor : 'html-md'


sagews or ipynb

sagews or ipynb

--- src/smc-webapp/editor.coffee
+++ src/smc-webapp/editor.coffee
@@ -492,7 +492,7 @@
             extraKeys["Shift-Enter"] = =>
                 alert_message
                     type    : "error"
-                    message : "You can only evaluate code in a file that ends with the extension 'sagews'.   Create a Sage Worksheet instead."
+                    message : "You can only evaluate code in a file that ends with the extension 'sagews' or 'ipynb'.   Create a Sage Worksheet or Jupyter notebook instead."

         # Layouts:
         #   0 - one single editor


webapp/project: small reorga of projects page, more distinct "new project" workflow with explanations, highlighting, ...

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -29,6 +29,7 @@
 misc = require('smc-util/misc')
 {required, defaults} = misc
 {html_to_text} = require('./misc_page')
+{SiteName} = require('./customize')

 markdown = require('./markdown')

@@ -635,6 +636,12 @@
             description_text : ''
             error            : ''

+    toggle_editing: ->
+        if @state.state == 'view'
+            @start_editing()
+        else
+            @cancel_editing()
+
     create_project : ->
         token = misc.uuid()
         @setState(state:'saving')
@@ -659,14 +666,14 @@
             @create_project()

     render_input_section : ->
-        <Well style={backgroundColor: '#ffffff'}>
+        #<Row>
+        #    <Col sm=12 style={color: '#666', fontWeight: 'bold', fontSize: '15pt'}>
+        #        <Icon name='plus-circle' /> Create a new project
+        #    </Col>
+        #</Row>
+        <Well style={backgroundColor: '#DCEFDC'}>
             <Row>
-                <Col sm=12 style={color: '#666', fontWeight: 'bold', fontSize: '15pt'}>
-                    <Icon name='plus-circle' /> Create a New Project
-                </Col>
-            </Row>
-            <Row>
-                <Col sm=5 style={color: '#666'}>
+                <Col sm=5 style={color: '#444'}>

Title:

                     <Input
                         ref         = 'new_project_title'
@@ -678,7 +685,7 @@
                         autoFocus   />
                 </Col>

-                <Col sm=5 style={color: '#666'}>
+                <Col sm=7 style={color: '#444'}>

Description:

                     <Input
                         ref         = 'new_project_description'
@@ -695,17 +702,11 @@
             </Row>

             <Row>
-                <Col sm=12>
-
 You can change the title and description at any time later.

-                </Col>
-            </Row>
-
-            <Row>
-                <Col sm=12>
+                <Col sm=5>
                     <ButtonToolbar>
                         <Button
                             disabled = {@state.title_text == '' or @state.state == 'saving'}
-                            bsStyle  = 'primary'
+                            bsStyle  = 'success'
                             onClick  = {@create_project} >
                             Create project
                         </Button>
@@ -717,6 +718,29 @@
                     </ButtonToolbar>
                     {@render_error()}
                 </Col>
+                <Col sm=7>
+
(You can change the title and description at any time later.)

+                </Col>
+            </Row>
+            <Space/>
+

+            <Row>
+                <Col sm=12 style={color:'#555'}>
+

+                        A "project" in <SiteName/> is your very own private workspace.
+                        All your computations and programs run inside it.
+                        After opening your newly created project, go ahead and create your SageMath worksheets,
+                        Jupyter notebooks, textfiles, LaTeX documents or even upload your very own data files.
+

+                    <Space/>
+

+                        You can access your projects around the clock and work without restrictions on time.
+                        Free projects start with a certain basic quota of allowed resource usage.
+                        These quotas and the quality of hosting can be greatly increased
+                        by subscribing to a plan and
+                        upgrading your projects.
+

+                </Col>
             </Row>
         </Well>

@@ -725,25 +749,26 @@
             <ErrorDisplay error={@state.error} onClose={=>@setState(error:'')} />

     render : ->
-        switch @state.state
-            when 'view'
-                <Row>
-                    <Col sm=3>
-                        <Button
-                            bsStyle = 'primary'
-                            block
-                            type    = 'submit'
-                            onClick = {@start_editing}>
-                            <Icon name='plus-circle' /> New Project...
-                        </Button>
-                    </Col>
-                </Row>
-            when 'edit', 'saving'
-                <Row>
-                    <Col sm=12>
-                        {@render_input_section()}
-                    </Col>
-                </Row>
+        new_proj_btn =  <Col sm=4>
+                            <Button
+                                bsStyle = {if @state.state == 'view' then 'success' else 'default'}
+                                active  = {@state.state != 'view'}
+                                block
+                                type    = 'submit'
+                                onClick = {@toggle_editing}>
+                                <Icon name='plus-circle' /> Create a new project...
+                            </Button>
+                        </Col>
+
+        new_proj_dialog = <Col sm=12>
+                            <Space/>
+                            {@render_input_section()}
+                          </Col>
+
+        <Row>
+            {new_proj_btn}
+            {if @state.state != 'view' then new_proj_dialog}
+        </Row>

 ProjectsFilterButtons = rclass
     displayName : 'ProjectsFilterButtons'
@@ -761,16 +786,18 @@
         show_deleted_button : false

     render_deleted_button : ->
+        style = if @props.deleted then 'warning' else "default"
         if @props.show_deleted_button
-            <Button onClick = {=>redux.getActions('projects').setState(deleted: not @props.deleted)}>
+            <Button onClick={=>redux.getActions('projects').setState(deleted: not @props.deleted)} bsStyle={style}>
                 <Icon name={if @props.deleted then 'check-square-o' else 'square-o'} fixedWidth /> Deleted
             </Button>
         else
             return null

     render_hidden_button : ->
+        style = if @props.hidden then 'warning' else "default"
         if @props.show_hidden_button
-            <Button onClick = {=>redux.getActions('projects').setState(hidden: not @props.hidden)}>
+            <Button onClick = {=>redux.getActions('projects').setState(hidden: not @props.hidden)} bsStyle={style}>
                 <Icon name={if @props.hidden then 'check-square-o' else 'square-o'} fixedWidth /> Hidden
             </Button>

@@ -844,10 +871,12 @@
     displayName : 'Projects-ProjectsListingDescription'

     propTypes :
-        deleted           : rtypes.bool
-        hidden            : rtypes.bool
-        selected_hashtags : rtypes.object
-        search            : rtypes.string
+        deleted             : rtypes.bool
+        hidden              : rtypes.bool
+        selected_hashtags   : rtypes.object
+        search              : rtypes.string
+        nb_projects         : rtypes.number
+        nb_projects_visible : rtypes.number

     getDefaultProps : ->
         deleted           : false
@@ -856,8 +885,13 @@
         search            : ''

     render_header : ->
-        desc = "Showing #{if @props.deleted then 'deleted ' else ''}#{if @props.hidden then 'hidden ' else ''}projects"
-
{desc}

+        if @props.nb_projects > 0 and (@props.hidden or @props.deleted)
+            d = if @props.deleted then 'deleted ' else ''
+            h = if @props.hidden then 'hidden ' else ''
+            a = if @props.hidden and @props.deleted then ' and ' else ''
+            n = @props.nb_projects_visible
+            desc = "Only showing #{n} #{d}#{a}#{h} #{misc.plural(n, 'project')}"
+
{desc}

     render_alert_message : ->
         query = @props.search.toLowerCase()
@@ -875,6 +909,7 @@

     render : ->

+            <Space/>
             {@render_header()}
             {@render_alert_message()}

@@ -962,9 +997,10 @@
     displayName : 'Projects-ProjectList'

     propTypes :
-        projects : rtypes.array.isRequired
-        show_all : rtypes.bool.isRequired
-        redux    : rtypes.object
+        nb_projects : rtypes.number
+        projects    : rtypes.array.isRequired
+        show_all    : rtypes.bool.isRequired
+        redux       : rtypes.object

     getDefaultProps : ->
         projects : []
@@ -1001,10 +1037,16 @@
         return listing

     render : ->
-

-            {@render_list()}
-            {@render_show_all()}
-

+        if @props.nb_projects == 0
+            <Alert bsStyle='info'>
+                You have not created any projects yet.
+                Click on "Create a new project" above to start working with <SiteName/>!
+            </Alert>
+        else
+

+                {@render_list()}
+                {@render_show_all()}
+

 parse_project_tags = (project) ->
     project_information = (project.title + ' ' + project.description).toLowerCase()
@@ -1207,7 +1249,9 @@
                 return <LoginLink />
             else
                 return
 <Loading />

-        <Grid fluid className='constrained'>
+
+        visible_projects = @visible_projects()
+        <Grid fluid className='constrained' style={minHeight:"80vh"}>
             <Well style={marginTop:'1em',overflow:'hidden'}>
                 <Row>
                     <Col sm=4>
@@ -1240,19 +1284,22 @@
                 <Row>
                     <Col sm=12>
                         <ProjectsListingDescription
-                            hidden            = {@props.hidden}
-                            deleted           = {@props.deleted}
-                            search            = {@props.search}
-                            selected_hashtags = {@props.selected_hashtags[@filter()]} />
+                            nb_projects         = {@project_list().length}
+                            nb_projects_visible = {visible_projects.length}
+                            hidden              = {@props.hidden}
+                            deleted             = {@props.deleted}
+                            search              = {@props.search}
+                            selected_hashtags   = {@props.selected_hashtags[@filter()]} />
                     </Col>
                 </Row>
                 <Row>
                     <Col sm=12>
                         <ProjectList
-                            projects = {@visible_projects()}
-                            show_all = {@props.show_all}
-                            user_map = {@props.user_map}
-                            redux    = {@props.redux} />
+                            nb_projects = {@project_list().length}
+                            projects    = {visible_projects}
+                            show_all    = {@props.show_all}
+                            user_map    = {@props.user_map}
+                            redux       = {@props.redux} />
                     </Col>
                 </Row>
             </Well>


JUser:authenticateClient: pass session object with account, tidy-up code

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -246,47 +246,72 @@
                 Payment.deleteAccount client, (err)=>
                   @logout client, callback

-  @isRegistrationEnabled =(callback)->
+  @isRegistrationEnabled = (callback)->
+
     JRegistrationPreferences = require '../registrationpreferences'
     JRegistrationPreferences.one {}, (err, prefs)->
       callback err? or prefs?.isRegistrationEnabled or no

-  @authenticateClient:(clientId, context, callback)->
-    JSession.one {clientId}, (err, session)=>
+
+  @authenticateClient: (clientId, context, callback)->
+
+    error = (message, rest...)->
+      console.error "[JUser::authenticateClient] #{message}", rest...
+
+    JSession.one { clientId }, (err, session)=>
+
       if err
-        console.error "JUser.authenticateClient error finding session", {err, clientId}
+
+        error "error finding session", { err, clientId }
         callback createKodingError err
+
       else unless session?
+
         JSession.createSession (err, { session, account })->
-          return callback err  if err?
-          callback null, account
+
+          if err?
+            error "failed to create session", { err }
+            return callback err
+          else
+            callback null, { session, account }
+
       else
-        {username} = session
-        if username?
-          JUser.one {username}, (err, user)=>
-            if err
-              console.error "JUser.authenticateClient error finding user with username", {
-                err, username }
-
-              callback createKodingError err
-            else unless user?
-              console.warn "JUser#authenticateClient no user found with username", {
-                username }
-
-              @logout clientId, callback
-            else
-              user.fetchAccount context, (err, account)->
-                if err
-                  console.warn "JUser#authenticateClient error fetching account", {
-                    context }
-
-                  callback createKodingError err
-                else
-                  #JAccount.emit "AccountAuthenticated", account
-                  callback null, account
-        else
-          console.warn "JUser#authenticateClient no username found", {session}
-          @logout clientId, callback
+
+        { username } = session
+
+        unless username?
+
+          error "no username found", { session }
+          @logout clientId, (err)->
+
+            callback createKodingError "no username found, logged out."
+
+          return
+
+        JUser.one {username}, (err, user)=>
+
+          if err?
+
+            error "error finding user with username", { err, username }
+            callback createKodingError err
+
+          else unless user?
+
+            error "no user found with username", { username }
+            @logout clientId, callback
+
+          else
+
+            user.fetchAccount context, (err, account)->
+
+              if err?
+
+                error "error fetching account", { context }
+                callback createKodingError err
+
+              else
+
+                callback null, { session, account }

   getHash =(value)->


Fix wrong import of base module

Fix wrong import of base module

--- src/coffee/cilantro/models/context/nodes/base.coffee
+++ src/coffee/cilantro/models/context/nodes/base.coffee
@@ -1,7 +1,7 @@
 define [
     'underscore'
     'backbone'
-    './base'
+    '../../base'
 ], (_, Backbone, base) ->


Add tests for network event order

Add tests for network event order

--- spec/NetworkLifecycle.coffee
+++ spec/NetworkLifecycle.coffee
@@ -776,10 +776,18 @@

     it 'should forward new-style brackets as expected', (done) ->
       expected = [
+        'START'
+        'DATA -> IN Leg2() CONN'
+        'Leg2() OUT -> IN2 PcMerge() CONN'
+        'DATA -> IN Leg2() DATA foo'
         'Leg2() OUT -> IN2 PcMerge() DATA fooLeg2'
+        'Leg2() OUT -> IN2 PcMerge() DISC'
+        'DATA -> IN Leg2() DISC'
+        'Leg1() OUT -> IN1 PcMerge() CONN'
         'Leg1() OUT -> IN1 PcMerge() < 1'
         'Leg1() OUT -> IN1 PcMerge() < a'
         'Leg1() OUT -> IN1 PcMerge() DATA bazLeg1'
+        'PcMerge() OUT -> IN Wp() CONN'
         'PcMerge() OUT -> IN Wp() < 1'
         'PcMerge() OUT -> IN Wp() < a'
         'PcMerge() OUT -> IN Wp() DATA 1bazLeg1:2fooLeg2:PcMerge'
@@ -787,18 +795,24 @@
         'PcMerge() OUT -> IN Wp() > a'
         'Leg1() OUT -> IN1 PcMerge() > 1'
         'PcMerge() OUT -> IN Wp() > 1'
+        'PcMerge() OUT -> IN Wp() DISC'
+        'Leg1() OUT -> IN1 PcMerge() DISC'
+        'Wp() OUT -> IN Leg3() CONN'
         'Wp() OUT -> IN Leg3() < 1'
         'Wp() OUT -> IN Leg3() < a'
         'Wp() OUT -> IN Leg3() DATA 1bazLeg1:2fooLeg2:PcMergeWp'
         'Wp() OUT -> IN Leg3() > a'
         'Wp() OUT -> IN Leg3() > 1'
-      ]
-      received = []
-
-      wasStarted = false
-      checkStart = ->
-        chai.expect(wasStarted).to.equal false
-        wasStarted = true
+        'Wp() OUT -> IN Leg3() DISC'
+        'END'
+      ]
+      received = []
+
+      wasStarted = false
+      checkStart = ->
+        received.push 'START'
+      receiveConnect = (event) ->
+        received.push "#{event.id} CONN"
       receiveEvent = (event) ->
         prefix = ''
         switch event.type
@@ -812,22 +826,27 @@
             prefix = '>'
             data = "#{prefix} #{event.data}"
         received.push "#{event.id} #{data}"
-      checkEnd = ->
-        c.network.removeListener 'start', checkStart
+      receiveDisconnect = (event) ->
+        received.push "#{event.id} DISC"
+      checkEnd = ->
+        received.push 'END'
+        c.network.graph.removeInitial 'foo', 'Leg2', 'in'
+        c.network.removeListener 'start', checkStart
+        c.network.removeListener 'connect', receiveConnect
         c.network.removeListener 'ip', receiveEvent
-        c.network.removeListener 'end', checkEnd
-        chai.expect(wasStarted).to.equal true
-        chai.expect(received).to.eql expected
-        done()
-      c.network.on 'start', checkStart
+        c.network.removeListener 'disconnect', receiveDisconnect
+        c.network.removeListener 'end', checkEnd
+        chai.expect(received).to.eql expected
+        done()
+      c.network.on 'start', checkStart
+      c.network.on 'connect', receiveConnect
       c.network.on 'ip', receiveEvent
-      c.network.once 'end', checkEnd
-
-      c.start (err) ->
-        return done err if err
-        in2.connect()
-        in2.send 'foo'
-        in2.disconnect()
+      c.network.on 'disconnect', receiveDisconnect
+      c.network.once 'end', checkEnd
+
+      c.network.graph.addInitial 'foo', 'Leg2', 'in'
+      c.start (err) ->
+        return done err if err
         in1.connect()
         in1.beginGroup 1
         in1.beginGroup 'a'


Triple click works with selections made with moused dragging.

--- spec/atom/editor-spec.coffee
+++ spec/atom/editor-spec.coffee
@@ -412,12 +412,12 @@
         expect(range.end).toEqual({row: 5, column: 27})
         expect(editor.getCursorPosition()).toEqual(row: 5, column: 27)

-      it "creates a selection from the initial double click to mouse cursor's location ", ->
+      it "creates a selection from word underneath double click to mouse cursor's location ", ->
         editor.attachToDom()
         editor.css(position: 'absolute', top: 10, left: 10)

         # double click
-        [pageX, pageY] = window.pixelPositionForPoint(editor, [4, 10])
+        [pageX, pageY] = window.pixelPositionForPoint(editor, [4, 7])
         editor.lines.trigger mousedownEvent({pageX, pageY, originalEvent: {detail: 1}})
         $(document).trigger 'mouseup'
         editor.lines.trigger mousedownEvent({pageX, pageY, originalEvent: {detail: 2}})
@@ -427,7 +427,7 @@
         editor.lines.trigger mousemoveEvent({pageX, pageY})

         range = editor.selection.getRange()
-        expect(range.start).toEqual({row: 4, column: 10})
+        expect(range.start).toEqual({row: 4, column: 4})
         expect(range.end).toEqual({row: 5, column: 27})
         expect(editor.getCursorPosition()).toEqual(row: 5, column: 27)

@@ -439,7 +439,41 @@
         editor.lines.trigger mousemoveEvent({pageX, pageY})

         range = editor.selection.getRange()
-        expect(range.start).toEqual({row: 4, column: 10})
+        expect(range.start).toEqual({row: 4, column: 4})
+        expect(range.end).toEqual({row: 5, column: 27})
+        expect(editor.getCursorPosition()).toEqual(row: 5, column: 27)
+
+
+      it "creates a selection from line underneath triple click to mouse cursor's location ", ->
+        editor.attachToDom()
+        editor.css(position: 'absolute', top: 10, left: 10)
+
+        # double click
+        [pageX, pageY] = window.pixelPositionForPoint(editor, [4, 7])
+        editor.lines.trigger mousedownEvent({pageX, pageY, originalEvent: {detail: 1}})
+        $(document).trigger 'mouseup'
+        editor.lines.trigger mousedownEvent({pageX, pageY, originalEvent: {detail: 2}})
+        $(document).trigger 'mouseup'
+        editor.lines.trigger mousedownEvent({pageX, pageY, originalEvent: {detail: 3}})
+
+        # moving changes selection
+        [pageX, pageY] = window.pixelPositionForPoint(editor, [5, 27])
+        editor.lines.trigger mousemoveEvent({pageX, pageY})
+
+        range = editor.selection.getRange()
+        expect(range.start).toEqual({row: 4, column: 0})
+        expect(range.end).toEqual({row: 5, column: 27})
+        expect(editor.getCursorPosition()).toEqual(row: 5, column: 27)
+
+        # mouse up may occur outside of editor, but still need to halt selection
+        $(document).trigger 'mouseup'
+
+        # moving after mouse up should not change selection
+        [pageX, pageY] = window.pixelPositionForPoint(editor, [8, 8])
+        editor.lines.trigger mousemoveEvent({pageX, pageY})
+
+        range = editor.selection.getRange()
+        expect(range.start).toEqual({row: 4, column: 0})
         expect(range.end).toEqual({row: 5, column: 27})
         expect(editor.getCursorPosition()).toEqual(row: 5, column: 27)


code clean up

code clean up

--- src/driver/driverCache.coffee
+++ src/driver/driverCache.coffee
@@ -81,9 +81,14 @@
         }
       else
         # ToDo: make new And filter
+
         filter.filters = filtersWithoutTime
         return {
-          filter
+          filter: {
+            operation: 'filter'
+            type: 'and'
+            filters: filtersWithoutTime
+          }
           timeFilter
         }
     else # Only time filter exists


Format the template name consistently

Format the template name consistently

--- resources/tools/templates/controller.coffee
+++ resources/tools/templates/controller.coffee
@@ -3,7 +3,7 @@
   filters.register 'templateTitle', -> ({title}) ->
     title.replace(/-->/g, '\u21E8').replace(/<--/g, '\u21E6')

-  injectables = ['$scope', '$log', '$timeout', '$q', 'Mines', 'ClassUtils']
+  injectables = ['$scope', '$log', '$timeout', '$q', '$filter', 'Mines', 'ClassUtils']

   validPath = (model, path) ->
     try
@@ -37,7 +37,7 @@
       f = isBetween model, inputType, outputType
       (templateData t for t in templates when f t) # Need to json-ify data for presentation.

-  return Array injectables..., (scope, log, timeout, Q, Mines, ClassUtils) ->
+  return Array injectables..., (scope, log, timeout, Q, filters, Mines, ClassUtils) ->
     scope.defaults = {}

     setTemplates = ({query}) -> (ts) ->
@@ -55,7 +55,7 @@
         verb:
           ed: "ran"
           ing: "running"
-        thing: "#{ q.title } template query"
+        thing: "#{ filters('templateTitle')(q) } template query"
         tool: 'show-table',
         data:
           url: scope.connection.root,


fix validation code mangling bug

fix validation code mangling bug

--- jsapp/xlform/src/mv.validationLogicHelpers.coffee
+++ jsapp/xlform/src/mv.validationLogicHelpers.coffee
@@ -98,11 +98,16 @@

           #{t("Validation Code:")}

-            <input type="text" name="constraint" id="#{@context.helper_factory.current_question.cid}-handcode" class="text" value="#{@criteria}">
+            <input type="text" name="constraint" id="#{@context.helper_factory.current_question.cid}-handcode" class="text" value="">

       """)
       @textarea = @$handCode.find('#' + @context.helper_factory.current_question.cid + '-handcode')
+      # Textarea starts with empty value - setting it in HTML caused error with
+      # mangled values (due to " character). Set initial value here, so all the
+      # special characters are saved.
+      @textarea.val(@criteria)
+      return

   validationLogicHelpers


Fix flaky specs

Fix flaky specs

--- spec/spell-check-spec.coffee
+++ spec/spell-check-spec.coffee
@@ -39,10 +39,9 @@
     misspellingMarkers = null
     waitsFor ->
       misspellingMarkers = getMisspellingMarkers()
-      misspellingMarkers.length > 0
-
-    runs ->
-      expect(misspellingMarkers.length).toBe 4
+      misspellingMarkers.length is 4
+
+    runs ->
       expect(textForMarker(misspellingMarkers[0])).toEqual "thiss"
       expect(textForMarker(misspellingMarkers[1])).toEqual "sentencts"
       expect(textForMarker(misspellingMarkers[2])).toEqual "edn"
@@ -56,10 +55,9 @@
     misspellingMarkers = null
     waitsFor ->
       misspellingMarkers = getMisspellingMarkers()
-      misspellingMarkers.length > 0
-
-    runs ->
-      expect(misspellingMarkers.length).toBe 2
+      misspellingMarkers.length is 2
+
+    runs ->
       expect(textForMarker(misspellingMarkers[0])).toEqual "chok"
       expect(textForMarker(misspellingMarkers[1])).toEqual "bok"

@@ -72,7 +70,7 @@
     misspellingMarkers = null
     waitsFor ->
       misspellingMarkers = getMisspellingMarkers()
-      misspellingMarkers.length > 0
+      misspellingMarkers.length is 1

     runs ->
       expect(misspellingMarkers.length).toBe 1
@@ -83,13 +81,10 @@
     advanceClock(editor.getBuffer().getStoppedChangingDelay())
     atom.config.set('spell-check.grammars', ['source.js'])

-    misspellingMarkers = null
     waitsFor ->
-      misspellingMarkers = getMisspellingMarkers()
-      misspellingMarkers.length > 0
-
-    runs ->
-      expect(misspellingMarkers.length).toBe 1
+      getMisspellingMarkers().length is 1
+
+    runs ->
       editor.moveToEndOfLine()
       editor.insertText('a')
       advanceClock(editor.getBuffer().getStoppedChangingDelay())
@@ -107,10 +102,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         atom.config.set('spell-check.grammars', [])
         expect(getMisspellingMarkers().length).toBe 0

@@ -122,10 +116,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         atom.commands.dispatch(workspaceElement, 'spell-check:toggle')
         expect(getMisspellingMarkers().length).toBe 0

@@ -136,11 +129,12 @@
       advanceClock(editor.getBuffer().getStoppedChangingDelay())
       atom.config.set('spell-check.grammars', ['source.js'])

-      waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+      misspellingMarkers = null
+      waitsFor ->
+        misspellingMarkers = getMisspellingMarkers()
+        misspellingMarkers.length is 1
+
+      runs ->
         editor.setGrammar(atom.grammars.selectGrammar('.txt'))
         expect(getMisspellingMarkers().length).toBe 0

@@ -215,10 +209,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -234,10 +227,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -253,10 +245,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 2
+        getMisspellingMarkers().length is 2
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -272,10 +263,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 2
+        getMisspellingMarkers().length is 2
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -291,10 +281,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -310,10 +299,9 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 1
+        getMisspellingMarkers().length is 1
+
+      runs ->
         editor.destroy()
         expect(getMisspellingMarkers().length).toBe 0

@@ -329,9 +317,8 @@
       atom.config.set('spell-check.grammars', ['source.js'])

       waitsFor ->
-        getMisspellingMarkers().length > 0
-
-      runs ->
-        expect(getMisspellingMarkers().length).toBe 2
-        editor.destroy()
-        expect(getMisspellingMarkers().length).toBe 0
+        getMisspellingMarkers().length is 2
+
+      runs ->
+        editor.destroy()
+        expect(getMisspellingMarkers().length).toBe 0


Test that we don’t serialize snapshots for destroyed marker layers

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -924,6 +924,15 @@
       expect(markerLayerA.id).not.toBe(markerLayerB.id)
       expect(bufferB.getMarker(marker1A.id)).toBeUndefined()

+    it "doesn't attempt to serialize snapshots for destroyed marker layers", ->
+      buffer = new TextBuffer(text: "abc")
+      markerLayer = buffer.addMarkerLayer(maintainHistory: true, persistent: true)
+      markerLayer.markPosition([0, 3])
+      buffer.insert([0, 0], 'x')
+      markerLayer.destroy()
+
+      expect(-> buffer.serialize()).not.toThrowError()
+
     it "doesn't remember marker layers when calling serialize with {markerLayers: false}", ->
       bufferA = new TextBuffer(text: "world")
       layerA = bufferA.addMarkerLayer(maintainHistory: true)


fix spec

fix spec

--- spec/text-object-spec.coffee
+++ spec/text-object-spec.coffee
@@ -859,20 +859,23 @@

       it 'select fold row range except start row', ->
         set cursor: [13, 0]
-        ensure 'viz', selectedBufferRange: rangeForLine(10, 25)
+        ensure 'viz', selectedBufferRange: rangeForRows(10, 25)

       it 'select fold row range except start row', ->
         set cursor: [19, 0]
-        ensure 'viz', selectedBufferRange: rangeForLine(19, 23)
-
+        ensure 'viz', selectedBufferRange: rangeForRows(19, 23)
+
+      # it 'can expand selection', ->
+      #   set cursor: [23, 0]
+      #   ensure 'viz', selectedBufferRange: [[19, 0], [24, 0]]
     describe 'a-fold', ->
       it 'select fold row range', ->
         set cursor: [13, 0]
-        ensure 'vaz', selectedBufferRange: rangeForLine(9, 25)
+        ensure 'vaz', selectedBufferRange: rangeForRows(9, 25)

       it 'select fold row range', ->
         set cursor: [19, 0]
-        ensure 'vaz', selectedBufferRange: rangeForLine(18, 23)
+        ensure 'vaz', selectedBufferRange: rangeForRows(18, 23)

   # Although following test picks specific language, other langauages are alsoe supported.
   describe 'Function', ->


nodes: added missing jump-guard in While::makeReturn, fixing #1850

--- nodes.coffee
+++ nodes.coffee
@@ -1232,7 +1232,7 @@
     if res
       super
     else
-      @returns = yes
+      @returns = not @jumps loop: yes
       this

   addBody: (@body) ->


second part of fixing #1416

second part of fixing #1416

--- nodes.coffee
+++ nodes.coffee
@@ -455,7 +455,7 @@
   # Tag this invocation as creating a new instance.
   newInstance: ->
     base = @variable.base or @variable
-    if base instanceof Call
+    if base instanceof Call and not base.isNew
       base.newInstance()
     else
       @isNew = true


fixed non-recursive traverse() for CodeNode

--- nodes.coffee
+++ nodes.coffee
@@ -722,7 +722,7 @@
   # Custom `traverse` implementation that uses the `real_children`.
   traverse: (block) ->
     block this
-    block(child) for child in @real_children()
+    child.traverse block for child in @real_children()

   toString: (idt) ->
     idt ||= ''


:memo: Add parameters and returns for internal method

--- src/geom/mixins/spline.coffee
+++ src/geom/mixins/spline.coffee
@@ -145,6 +145,10 @@
       @memoize('points', points).concat()

     # Internal: Validates the length of the vertices {Array}.
+    #
+    # vertices - The {Array} of vertices to validate.
+    #
+    # Returns a {Boolean}.
     validateVertices: (vertices) ->
       vertices.length % segmentSize is 1 and
       vertices.length >= segmentSize + 1


Another hack to switch to get script instead of an ajax call

--- lib/assets/javascripts/_request_manager.js.coffee
+++ lib/assets/javascripts/_request_manager.js.coffee
@@ -18,14 +18,15 @@

     # Perform XHtmlHttpRequest
     #
-    $.ajax(
-      url: state.url
-      headers:
-        'X-Wiselinks': state.data.render
-        'X-Wiselinks-Referer': state.data.referer
-
-      dataType: "js"
-    )
+    $.getScript(state.url)
+    # $.ajax(
+    #   url: state.url
+    #   headers:
+    #     'X-Wiselinks': state.data.render
+    #     'X-Wiselinks-Referer': state.data.referer
+    #
+    #   dataType: "js"
+    # )

   _normalize: (url) ->
     return unless url?