Deactivated plugin load from config.js

Deactivated plugin load from config.js

--- src/core/pluginsEngine/plugins.coffee
+++ src/core/pluginsEngine/plugins.coffee
@@ -73,8 +73,6 @@
 		return

 	pluginsEngine.init = (callback) ->
-		for plugin in env.config.plugins
-			pluginsEngine.load plugin
 		try
 			jf.readFile process.cwd() + '/plugins.json', (err, obj) ->
 				throw err if err
@@ -84,25 +82,6 @@
 				for pluginname, pluginversion of obj
 					pluginsEngine.load pluginname

-				# Checking if auth plugin is present. Else uses default
-				# if not shared.auth?
-				# 	console.log 'Using default auth'
-				# 	auth_plugin = require(env.config.root + '/default_plugins/auth/index')(env)
-				# 	pluginsEngine.plugin['auth'] = auth_plugin
-
-				# # Loading front if not overriden
-				# if not shared.front?
-				# 	console.log 'Using default front'
-				# 	front_plugin = require(env.config.root + '/default_plugins/front/index')(env)
-				# 	pluginsEngine.plugin['front'] = front_plugin
-
-				# # Loading request
-				# request_plugin = require(env.config.root + '/default_plugins/request/index')(env)
-				# pluginsEngine.plugin['request'] = request_plugin
-
-				# # Loading me
-				# me_plugin = require(env.config.root + '/default_plugins/me/index')(env)
-				# pluginsEngine.plugin['me'] = me_plugin
 				callback true
 		catch e
 			console.log 'An error occured: ' + e.message


put street back in the search index

put street back in the search index

--- search_index/search_index.coffee
+++ search_index/search_index.coffee
@@ -11,7 +11,7 @@
     @children[ch]

   putMatches: (to, n) ->
-    if @loc?
+    if @name?
       to.push {name: @name, coords: @loc}
       n -= 1
     for own ch,child of @children


Fixed updateRoom bug

Fixed updateRoom bug

--- server/methods/updateRoom.coffee
+++ server/methods/updateRoom.coffee
@@ -6,10 +6,7 @@

 		room = ChatRoom.findOne rid

-		if room.u._id isnt Meteor.userId() or room.t not in ['c', 'p']
-			throw new Meteor.Error 403, 'Not allowed'
-
-		if name? and name isnt room.name
+		if room.t not in ['d'] and name? and name isnt room.name
 			Meteor.call 'saveRoomName', rid, name
 		Meteor.call 'relabelRoom', rid, accessPermissions
 		if users?


added missing button tint color

added missing button tint color

--- templates/default/config/config.coffee
+++ templates/default/config/config.coffee
@@ -27,6 +27,7 @@
 #Steroids.config.navigationBar.titleColor = ""
 #Steroids.config.navigationBar.titleShadowColor = ""

+#Steroids.config.navigationBar.buttonTintColor = ""
 #Steroids.config.navigationBar.buttonTitleColor = ""
 #Steroids.config.navigationBar.buttonShadowColor = ""


fixed coord area filtering

fixed coord area filtering

--- src/viz/helpers/shapes/coordinates.coffee
+++ src/viz/helpers/shapes/coordinates.coffee
@@ -81,7 +81,7 @@
         reduced.geometry.coordinates = reduced.geometry.coordinates.filter (c,i) ->
           dist = distances[i]
           a = areas[i]
-          dist is 0 or a/dist > dist_cutoff
+          dist is 0 or a/dist >= dist_cutoff

         coords = largest.geometry.coordinates[0]
         if coords and largest.geometry.type is "MultiPolygon"


Hide Send Feedback from status bar

Hide Send Feedback from status bar

--- build/tasks/install-unpublished-packages.coffee
+++ build/tasks/install-unpublished-packages.coffee
@@ -32,7 +32,7 @@
     done = @async()

     installPackage 'spark', 'welcome-spark', '0.19.0', ->
-      installPackage 'spark', 'feedback-spark', '0.34.0', ->
+      installPackage 'spark', 'feedback-spark', '0.35.0', ->
         installPackage 'spark', 'release-notes-spark', '0.36.0', ->
           installPackage 'spark', 'language-spark', '0.3.0', ->
             done()


Remove duplicated tests for identifier token

--- test/tokenizer.coffee
+++ test/tokenizer.coffee
@@ -532,23 +532,6 @@
       tl = g._tokenize "a | b | c"
       assert.equal tl.length, 5

-  describe "identifier", ()->
-    sample_list = "
-      qwerty
-      myvar123
-      someCamelCase
-      some_snake_case
-      CAPSLOCK
-      $
-      $scope
-    ".split " "
-    for sample in sample_list
-      do (sample)->
-        it "should tokenize '#{sample}' as identifier", ()->
-        v = g._tokenize sample
-        assert.equal v.length, 1
-        assert.equal v[0][0].mx_hash.hash_key, "identifier"
-
   describe "TODO", ()->


fix(contact): only limit length if too long

--- src/flux/stores/contact-store.coffee
+++ src/flux/stores/contact-store.coffee
@@ -83,8 +83,8 @@
       return Promise.each extensions, (ext) =>
         return ext.findAdditionalContacts(search, results).then (contacts) =>
           results = contacts
-      .then () =>
-        results.length = limit
+      .then =>
+        if (results.length > limit) then results.length = limit
         return Promise.resolve(results)

   isValidContact: (contact) =>


Is it the install specs?

Is it the install specs?

--- spec/install-spec.coffee
+++ spec/install-spec.coffee
@@ -8,12 +8,12 @@
 apm = require '../lib/apm-cli'
 Install = require '../lib/install'

-describe 'apm install', ->
+fdescribe 'apm install', ->
   [atomHome, resourcePath] = []

   beforeEach ->
     spyOnToken()
-    silenceOutput()
+    silenceOutput(true)

     atomHome = temp.mkdirSync('apm-home-dir-')
     process.env.ATOM_HOME = atomHome


mocked out builder

mocked out builder

--- spec/lib/bus-spec.coffee
+++ spec/lib/bus-spec.coffee
@@ -1,6 +1,8 @@
 EventEmitter = require('events').EventEmitter

-describe 'bus.io', ->
+describe.only 'bus', ->
+
+  Given -> @date = new Date

   Given ->
     @Sio = class Sio extends EventEmitter
@@ -8,6 +10,19 @@
         if not (@ instanceof Sio)
           return new Sio
       listen: ->
+
+  Given ->
+    @Builder = class Builder extends EventEmitter
+      constructor: ->
+        if not (@ instanceof Builder)
+          return new Builder
+      data: ->
+        actor: 'me'
+        action: 'say'
+        content: 'hello'
+        target: 'you'
+        created: @created
+

   Given ->
     @Bus = requireSubject 'lib/bus', {
@@ -44,7 +59,7 @@
         action: 'say'
         content: 'hello'
         target: 'you'
-        creatd: new Date
+        created: @date
     When -> @message = @bus.message @params
     Then -> expect(@message.data()).toBe @params
     And -> expect(@message.listeners('built').length).toBe 1


Added tests for formatKeyValue

Added tests for formatKeyValue

--- test/util_test.coffee
+++ test/util_test.coffee
@@ -1,20 +1,20 @@
-chalkboard = require "../lib/util"
+util = require "../lib/util"

 exports.utilTest =
   "Capitalize Test": (test) ->
-    test.equal chalkboard._capitalize("test"), "Test"
+    test.equal util.capitalize("test"), "Test"
     test.done()

   "Empty String": (test) ->
-    test.equal chalkboard._capitalize(""), ""
+    test.equal util.capitalize(""), ""
     test.done()

   "Repeat Char Test": (test) ->
-    test.equal chalkboard._repeatChar("#", 3), "###"
+    test.equal util.repeatChar("#", 3), "###"
     test.done()

   "Repeat Char No Count Test": (test) ->
-    test.equal chalkboard._repeatChar("#"), ""
+    test.equal util.repeatChar("#"), ""
     test.done()

   "Get languages test for Coffee": (test) ->
@@ -26,10 +26,44 @@
       commentRegex: /^\s*(?:#){1,2}\s*(?:@(\w+))?(?:\s*(.*))?/
       blockRegex:   /###/

-    test.deepEqual chalkboard._getLanguages("/test/test.coffee"), validSettings
+    test.deepEqual util.getLanguages("/test/test.coffee"), validSettings

     test.done()

   "Not supported language": (test) ->
-    test.equal chalkboard._getLanguages("/test/test.fake"), null, "Should return null"
+    test.equal util.getLanguages("/test/test.fake"), null, "Should return null"
+    test.done()
+
+  "Default formatKeyValue with string value": (test) ->
+    output = util.formatKeyValue "name", "testing"
+    test.equal output, "### Name\ntesting\n"
+    test.done()
+
+  "formatKeyValue with string value with no new line": (test) ->
+    output = util.formatKeyValue "name", "testing", false
+    test.equal output, "### Name\ntesting"
+    test.done()
+
+  "formatKeyValue with string value with header level 1": (test) ->
+    output = util.formatKeyValue "name", "testing", true, 1
+    test.equal output, "# Name\ntesting\n"
+    test.done()
+
+  "formatKeyValue with an array": (test) ->
+    output = util.formatKeyValue "list", [
+      "item1"
+      "item2"
+    ]
+    test.equal output, "### List\n-   item1  \n-   item2  \n\n"
+    test.done()
+
+  "formatKeyValue with an array of object": (test) ->
+    output = util.formatKeyValue "list", [
+      {name: "Chalk", type: "Item", description: "Writing tool"}
+    ]
+
+    test.equal output, """
+      ### List
+      **Chalk**  \nType: `Item`  \nWriting tool  \n\n
+    """
     test.done()


Every paren ever

Every paren ever

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -394,7 +394,7 @@

     describe "as a motion", ->
       it "moves the cursor to the specified search pattern", ->
-        keydown '/'
+        keydown('/')

         editor.commandModeInputView.editor.setText 'def'
         editor.commandModeInputView.editor.trigger 'core:confirm'
@@ -403,7 +403,7 @@

       it "loops back around", ->
         editor.setCursorBufferPosition([3, 0])
-        keydown '/'
+        keydown('/')
         editor.commandModeInputView.editor.setText 'def'
         editor.commandModeInputView.editor.trigger 'core:confirm'

@@ -411,91 +411,91 @@

       describe "repeating", ->
         beforeEach ->
-          keydown '/'
+          keydown('/')
           editor.commandModeInputView.editor.setText 'def'
           editor.commandModeInputView.editor.trigger 'core:confirm'

         describe "the n keybinding", ->
           it "repeats the last search", ->
-            keydown 'n'
+            keydown('n')
             expect(editor.getCursorBufferPosition()).toEqual [3, 0]

         describe "the N keybinding", ->
           it "repeats the last search backwards", ->
             editor.setCursorBufferPosition([0, 0])
-            keydown 'N', shift: true
+            keydown('N', shift: true)
             expect(editor.getCursorBufferPosition()).toEqual [3, 0]

       describe "composing", ->
         it "composes with operators", ->
-          keydown 'd'
-          keydown '/'
-          editor.commandModeInputView.editor.setText 'def'
-          editor.commandModeInputView.editor.trigger 'core:confirm'
+          keydown('d')
+          keydown('/')
+          editor.commandModeInputView.editor.setText('def')
+          editor.commandModeInputView.editor.trigger('core:confirm')
           expect(editor.getText()).toEqual "def\nabc\ndef\n"

         it "repeats correctly with operators", ->
-          keydown 'd'
-          keydown '/'
-          editor.commandModeInputView.editor.setText 'def'
-          editor.commandModeInputView.editor.trigger 'core:confirm'
-
-          keydown '.'
+          keydown('d')
+          keydown('/')
+          editor.commandModeInputView.editor.setText('def')
+          editor.commandModeInputView.editor.trigger('core:confirm')
+
+          keydown('.')
           expect(editor.getText()).toEqual "def\n"

     describe "when reversed as ?", ->
       it "moves the cursor backwards to the specified search pattern", ->
-        keydown '?'
-        editor.commandModeInputView.editor.setText 'def'
-        editor.commandModeInputView.editor.trigger 'core:confirm'
+        keydown('?')
+        editor.commandModeInputView.editor.setText('def')
+        editor.commandModeInputView.editor.trigger('core:confirm')
         expect(editor.getCursorBufferPosition()).toEqual [3, 0]

       describe "repeating", ->
         beforeEach ->
-          keydown '?'
-          editor.commandModeInputView.editor.setText 'def'
-          editor.commandModeInputView.editor.trigger 'core:confirm'
+          keydown('?')
+          editor.commandModeInputView.editor.setText('def')
+          editor.commandModeInputView.editor.trigger('core:confirm')

         describe 'the n keybinding', ->
           it "repeats the last search backwards", ->
             editor.setCursorBufferPosition([0, 0])
-            keydown 'n'
+            keydown('n')
             expect(editor.getCursorBufferPosition()).toEqual [3, 0]

         describe 'the N keybinding', ->
           it "repeats the last search forwards", ->
             editor.setCursorBufferPosition([0, 0])
-            keydown 'N', shift: true
+            keydown('N', shift: true)
             expect(editor.getCursorBufferPosition()).toEqual [1, 0]

     describe "using search history", ->
       beforeEach ->
-        keydown '/'
-        editor.commandModeInputView.editor.setText 'def'
-        editor.commandModeInputView.editor.trigger 'core:confirm'
+        keydown('/')
+        editor.commandModeInputView.editor.setText('def')
+        editor.commandModeInputView.editor.trigger('core:confirm')
         expect(editor.getCursorBufferPosition()).toEqual [1, 0]

-        keydown '/'
-        editor.commandModeInputView.editor.setText 'abc'
-        editor.commandModeInputView.editor.trigger 'core:confirm'
+        keydown('/')
+        editor.commandModeInputView.editor.setText('abc')
+        editor.commandModeInputView.editor.trigger('core:confirm')
         expect(editor.getCursorBufferPosition()).toEqual [2, 0]

       it "allows searching history in the search field", ->
-        keydown '/'
-        editor.commandModeInputView.editor.trigger 'core:move-up'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'abc'
-        editor.commandModeInputView.editor.trigger 'core:move-up'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'def'
-        editor.commandModeInputView.editor.trigger 'core:move-up'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'def'
+        keydown('/')
+        editor.commandModeInputView.editor.trigger('core:move-up')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('abc')
+        editor.commandModeInputView.editor.trigger('core:move-up')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('def')
+        editor.commandModeInputView.editor.trigger('core:move-up')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('def')

       it "resets the search field to empty when scrolling back", ->
-        keydown '/'
-        editor.commandModeInputView.editor.trigger 'core:move-up'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'abc'
-        editor.commandModeInputView.editor.trigger 'core:move-up'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'def'
-        editor.commandModeInputView.editor.trigger 'core:move-down'
-        expect(editor.commandModeInputView.editor.getText()).toEqual 'abc'
-        editor.commandModeInputView.editor.trigger 'core:move-down'
+        keydown('/')
+        editor.commandModeInputView.editor.trigger('core:move-up')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('abc')
+        editor.commandModeInputView.editor.trigger('core:move-up')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('def')
+        editor.commandModeInputView.editor.trigger('core:move-down')
+        expect(editor.commandModeInputView.editor.getText()).toEqual('abc')
+        editor.commandModeInputView.editor.trigger('core:move-down')
         expect(editor.commandModeInputView.editor.getText()).toEqual ''


Ensure spyOnConfig doesn't shadow real implementation

Resolved unintended side effect of spying on atom.config where we would
previously completely shadowed the underlying implementation.

--- spec/spec-helpers.coffee
+++ spec/spec-helpers.coffee
@@ -20,8 +20,10 @@
     Object.defineProperty(process, 'platform', {__proto__: null, value: name})

   spyOnConfig: (key, value) ->
+    get = atom.config.get
     spyOn(atom.config, 'get').andCallFake (_key) ->
       return value if _key is key
+      get.call(atom.config, key)

   setTimeoutInterval: (interval) ->
     env = jasmine.getEnv()


spec: webview test should avoid affecting each other

--- spec/webview-spec.coffee
+++ spec/webview-spec.coffee
@@ -160,7 +160,7 @@
         assert.equal e.message, 'undefined undefined undefined undefined'
         done()
       webview.src = "file://#{fixtures}/pages/c.html"
-      webview.partition = "test"
+      webview.partition = 'test1'
       document.body.appendChild webview

     it 'inserts node symbols when set', (done) ->
@@ -169,7 +169,7 @@
         done()
       webview.setAttribute 'nodeintegration', 'on'
       webview.src = "file://#{fixtures}/pages/d.html"
-      webview.partition = "test"
+      webview.partition = 'test2'
       document.body.appendChild webview

     it 'isolates storage for different id', (done) ->
@@ -180,7 +180,7 @@
       window.localStorage.setItem 'test', 'one'
       webview.addEventListener 'console-message', listener
       webview.src = "file://#{fixtures}/pages/partition/one.html"
-      webview.partition = "test"
+      webview.partition = 'test3'
       document.body.appendChild webview

     it 'uses current session storage when no id is provided', (done) ->


For loading saved models, precompile formulae for faster execution

--- src/code/models/relationship.coffee
+++ src/code/models/relationship.coffee
@@ -32,6 +32,8 @@
   checkFormula: ->
     if @isDefined
       @evaluate(1, 1) #sets the @hasError flag if there is a problem
+      if not @hasError and not @func?
+        @func = (math.compile @formula).eval

   evaluate: (inV,outV, maxIn=100, maxOut=100)->
     result = Relationship.errValue


Ase sku also as direct property of a lineitem.

--- src/coffee/orderdistribution.coffee
+++ src/coffee/orderdistribution.coffee
@@ -211,6 +211,7 @@
         retailerSKU = masterSKU2retailerSKU[masterSKU]
         continue unless retailerSKU
         li.variant.sku = retailerSKU
+        li.sku = retailerSKU # Set sku also directly on line item
         li.variant.attributes = [] unless li.variant.attributes
         a =
           name: 'mastersku'


Stupid A/A test to attempt to diagnose A/B testing issues

--- js/ab_test_definitions.coffee
+++ js/ab_test_definitions.coffee
@@ -16,6 +16,7 @@
     use_ks_outer: [true, false]
     first_cta_button_text: ["A", "B", "C"]
     pricing_maintain_infrastructure: [true, false]
+    a_is_a: [true, false]

   overrides:
     [


Buy Buttona aceita opçao queryParams

Buy Buttona aceita opçao queryParams

--- src/coffee/portal-buy-button.coffee
+++ src/coffee/portal-buy-button.coffee
@@ -133,6 +133,9 @@

 		if @options.target
 			queryParams.push("target=#{@options.target}")
+
+		for key, value of @options.queryParams
+			queryParams.push("#{key}=#{value}")

 		url = "/checkout/cart/add?#{queryParams.join('&')}"

@@ -208,3 +211,4 @@
 	hideUnavailable: true
 	target: null
 	multipleProductIds: false
+	queryParams: {}


add integration test for automatic staff context switching

--- test/integration/switch_context.coffee
+++ test/integration/switch_context.coffee
@@ -1,9 +1,9 @@
 should = require 'should'
-{wd40, browser, base_url, login_url, home_url, prepIntegration} = require './helper'
+{wd40, browser, base_url, login_url, logout_url, home_url, prepIntegration} = require './helper'

 request = require 'request'

-describe 'Context switch', ->
+describe 'Context switch (non-staff)', ->
   prepIntegration()

   before (done) ->
@@ -37,3 +37,27 @@
             text.should.not.include 'Prune'
             text.should.not.include 'Apricot'
             done()
+
+describe 'Context switch (staff)', ->
+  prepIntegration()
+
+  context 'When I log in as a staff member', ->
+    before (done) ->
+      wd40.fill '#username', 'teststaff', ->
+        wd40.fill '#password', process.env.CU_TEST_STAFF_PASSWORD, ->
+          wd40.click '#login', done
+
+    context 'And I try to access a normal user’s dataset directly', ->
+      before (done) ->
+        browser.get "#{base_url}/dataset/1057304856/settings", done
+
+      it 'I see the dataset contents', (done) ->
+        browser.waitForElementByCss '#toolbar', 4000, ->
+          browser.waitForElementByCss 'iframe', 2000, done
+
+      it 'I have been automatically switched into the user’s account', (done) ->
+        browser.get home_url, ->
+          wd40.getText '#subnav-path', (err, text) ->
+            text.should.not.include 'General Test Testington’s data hub'
+            text.should.include 'Mr F Greedy’s data hub'
+            done()


fixed test case for combined tests

fixed test case for combined tests

--- testSpecApp/app/specs/rotations.coffee
+++ testSpecApp/app/specs/rotations.coffee
@@ -1,5 +1,5 @@
 describe "Rotations", ->
-  orientations = [
+  allOrientations = [
     "portraitUpsideDown"
     "landscapeLeft"
     "landscapeRight"
@@ -14,7 +14,7 @@
     it "should be a function", ->
       steroids.screen.rotate.should.be.a "function"

-    for orientation in orientations then do (orientation) ->
+    for orientation in allOrientations then do (orientation) ->
       it "should rotate to #{orientation}", (done) ->
         setTimeout ->
           steroids.screen.rotate orientation,
@@ -27,8 +27,10 @@
     it "should not rotate to unallowed orientations", (done) ->
       steroids.screen.setAllowedRotations "portrait",
         onSuccess: ->
-          steroids.screen.rotate "portrait",
-            onSuccess: -> done()
+          steroids.screen.rotate "portraitUpsideDown",
+            onSuccess: ->
+              steroids.screen.setAllowedRotations allOrientations
+              done()
             onFailure: (error) ->
               done new Error error
         onFailure: (error) ->


ListView: Remove deleted item from items array

--- src/components/list/listview.coffee
+++ src/components/list/listview.coffee
@@ -61,6 +61,7 @@
     destroy = (item, i) =>
       @emit 'ItemWasRemoved', item, i
       item.destroy()
+      @items.splice i, 1
       return yes

     if index


metrics: fix weekly delta

metrics: fix weekly delta

--- src/components/metrics/index.coffee
+++ src/components/metrics/index.coffee
@@ -95,8 +95,8 @@
       _.map charted.results, (result) ->
         resultsByApp[result.appName] ?= {}
         resultsByApp[result.appName][charted.metric.name] = {
-          currentWeek: result.weeklyAggregates[0]
-          lastWeek: result.weeklyAggregates[1]
+          currentWeek: result.weeklyAggregates[1]
+          lastWeek: result.weeklyAggregates[0]
           formatter: charted.formatter
         }
       return resultsByApp


Fixes search on non-String values, fixes #1107

--- app/assets/javascripts/admin/models/pagination_collection.coffee
+++ app/assets/javascripts/admin/models/pagination_collection.coffee
@@ -123,7 +123,10 @@
       @state.filter = (model) ->
         return _.any(fields, (fieldName) ->
           value = model.get(fieldName)
-          return if value? then !!value.match(regexp) else false
+          if value?
+            return !!value.toString().match(regexp)
+          else
+            return false
         )

     @_reset()


Load course before tasks

Load course before tasks

--- src/components/task-plan/index.cjsx
+++ src/components/task-plan/index.cjsx
@@ -3,7 +3,7 @@
 {HomeworkPlan} = require './homework'
 {ReadingPlan} = require './reading'
 LoadableItem = require '../loadable-item'
-
+{CourseStore, CourseActions} = require '../../flux/course'
 {TaskPlanStore, TaskPlanActions} = require '../../flux/task-plan'

 PLAN_TYPES =
@@ -62,11 +62,8 @@
     store: TaskPlanStore
     actions: TaskPlanActions

-  render: ->
-    id = @getId()
-    {courseId} = @context.router.getCurrentParams()
+  loadTasks: (id, courseId) ->
     Type = @getType()
-
     <LoadableItem
       id={id}
       store={TaskPlanStore}
@@ -74,4 +71,15 @@
       renderItem={-> <Type id={id} courseId={courseId} />}
     />

+  render: ->
+    id = @getId()
+    {courseId} = @context.router.getCurrentParams()
+    <LoadableItem
+      id={courseId}
+      store={CourseStore}
+      actions={CourseActions}
+      renderItem={ => @loadTasks(id, courseId) }
+    />
+
+
 module.exports = {ReadingShell, HomeworkShell}


TicketMerge: use veryLarge modal size

TicketMerge: use veryLarge modal size

might fix #1266

--- app/assets/javascripts/app/controllers/agent_ticket_merge.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_merge.coffee
@@ -3,7 +3,7 @@
   buttonCancel: true
   buttonSubmit: true
   head: 'Merge'
-  large: true
+  veryLarge: true
   shown: false

   constructor: ->


Entity uris listed in permissions are minimal

--- assets/javascripts/views/002_permissions_fields_picker.js.coffee
+++ assets/javascripts/views/002_permissions_fields_picker.js.coffee
@@ -205,7 +205,7 @@
     @getOption()?.value

   getText: =>
-    @getOption().name || @getOption().entity.replace(/^https?:\/\/(?:www\.)?/, '')
+    @getOption().name || TentStatus.Helpers.minimalEntity(@getOption().value)

   isGroup: =>
     !!@getOption().group


unselecting node by esc or mouse click on background

--- frontend/bubbles/bubbles-aiddata.coffee
+++ frontend/bubbles/bubbles-aiddata.coffee
@@ -153,6 +153,16 @@
   .json('map', "data/world-countries.json")
   .csv('countries', "data/aiddata-countries.csv")
   .onload (data) ->
+
+
+    svg.append("rect")
+      .attr("x", 0)
+      .attr("y", 0)
+      .attr("width", bubblesChartWidth)
+      .attr("height", bubblesChartHeight)
+      .attr("fill", "white")
+      .on 'click', (d) -> clearNodeSelection()
+

     fitProjection(mapProj, data.map, [[0,20],[bubblesChartWidth, bubblesChartHeight*0.8]], true)

@@ -310,6 +320,7 @@
       .enter().append('path')
         .attr('d', mapProjPath)
         .attr("fill", "#f0f0f0")
+        .on 'click', (d) -> clearNodeSelection()

     flows = svg.append("g")
       .attr("class", "flows")
@@ -427,7 +438,13 @@
               flows.selectAll("line").remove()

-
+    clearNodeSelection = ->
+      if selectedNode != null
+        d3.select(selectedNode).selectAll("circle").classed("selected", false)
+        flows.selectAll("line").remove()
+        selectedNode = null
+
+    $(document).keyup (e) -> if e.keyCode == 27 then clearNodeSelection()

     bubble.append("circle")


Check autoIndent toggle setting on didStopChanging

--- lib/auto-indent.coffee
+++ lib/auto-indent.coffee
@@ -111,6 +111,7 @@

   # Buffer has stopped changing. Indent as required
   didStopChanging: () ->
+    return unless @autoJsx
     selectedRange = @editor.getSelectedBufferRange()
     highestRow = Math.max selectedRange.start.row, selectedRange.end.row
     if highestRow isnt @highestSelectedRow


simplify FlintSenderManager

simplify FlintSenderManager

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/sender/FlintSenderManager.coffee
+++ lib/sender/FlintSenderManager.coffee
@@ -23,16 +23,20 @@

 class FlintSenderManager extends EventEmitter

-    constructor: (@appId, @device, @useHeartbeat) ->
-        if not @appId or not @device
+    constructor: (@options) ->
+        if not @options
             throw 'FlintSenderManager constructor error'

-        @urlBase = @device.urlBase
+        @appId = @options.appId
+
+        @urlBase = @options.urlBase
         @serviceUrl = @urlBase + '/apps/' + @appId
-        @host = @device.host
-
-        if @useHeartbeat is undefined
+        @host = @options.host
+
+        if @options.useHeartbeat is undefined
             @useHeartbeat = true
+        else
+            @useHeartbeat = @options.useHeartbeat

         @appState = {}
         @additionalData = {}
@@ -41,7 +45,7 @@
         @heartbeatInterval = 3 * 1000
         @heartbeatTimerId = null

-        @defMessageChannel = null
+        @messageChannel = null
         @messageBusList = {}

     #
@@ -49,9 +53,8 @@
     #
     getAdditionalData: ->
         return @additionalData['customData']
-    #
-    # listener:
-    # on 'appstate', (result, state, additionaldata)
+
+    #
     # callback:
     #     (result, state, additionaldata)
     #
@@ -60,7 +63,6 @@
             'Accept': 'application/xml; charset=utf8'
         @_getState @serviceUrl, headers, (result, state) =>
             callback? result, state, @additionalData
-            @emit 'appstate', result, state, @additionalData

     _getState: (url, headers, callback) ->
         @_request 'GET', url, headers, null, (statusCode, responseText) =>
@@ -74,6 +76,7 @@
         lines = state.split '\n'
         lines.splice 0, 1
         responseText = lines.join ''
+        # todo: different in IE
         parser = new DOMParser()
         doc = parser.parseFromString responseText, 'text/xml'

@@ -98,20 +101,10 @@
                 for i of items
                     if items[i].tagName and items[i].innerHTML
                         _tmpAdditionalData[items[i].tagName] = items[i].innerHTML
-                #                changed = false
                 for key, value of _tmpAdditionalData
                     if @additionalData[key] isnt value
-#                        changed = true
                         @emit key + 'available', value
-                #                if not changed
-                #                    _additionalData = @additionalData
-                #                    for key, value of _additionalData
-                #                        if _tmpAdditionalData[key] isnt value
-                #                            changed = true
-                #                            break
                 @additionalData = _tmpAdditionalData
-#                if changed
-#                    @emit 'additionaldatachanged', @additionalData

     #
     # listener:
@@ -135,10 +128,8 @@
         @_launch 'join', appInfo, callback

     _onLaunchResult: (type, result, token, callback) ->
-        if callback
-            callback type, result, token
-        else
-            @emit type, result, token
+        callback? type, result, token
+
         # if success
         if result
             # to get additionaldata before starting heartbeat
@@ -245,19 +236,12 @@
                     else # join failed, stop failed
                         @_onStop 'appstop', false, callback

-    #
-    # listener:
-    # on 'appdisconnect', result
-    #
     disconnect: (callback) ->
         @_stopHeartbeat()
         @_stop 'disconnect', @serviceUrl, callback

     _onStop: (type, result, callback) ->
-        if callback
-            callback? type, result
-        else
-            @emit type, result
+        callback? type, result

     _stop: (stopType, url, callback) ->
         if not @token
@@ -294,16 +278,16 @@
             xhr.send ''

     _createMessageChannel: ->
-        if not @defMessageChannel
-            @defMessageChannel = new SenderMessageChannel FlintConstants.DEFAULT_CHANNEL_NAME
-            @defMessageChannel.on 'open', () =>
+        if not @messageChannel
+            @messageChannel = new SenderMessageChannel FlintConstants.DEFAULT_CHANNEL_NAME
+            @messageChannel.on 'open', () =>
                 console.log 'sender message channel open!!!'
-            @defMessageChannel.on 'close', () =>
+            @messageChannel.on 'close', () =>
                 console.log 'sender message channel close!!!'
-            @defMessageChannel.on 'error', () =>
+            @messageChannel.on 'error', () =>
                 console.log 'sender message channel error!!!'
-            @_openMessageChannel @defMessageChannel
-        return @defMessageChannel
+            @_openMessageChannel @messageChannel
+        return @messageChannel

     _openMessageChannel: (channel) ->
         @.once channel.getName() + 'available', (channelUrl) =>
@@ -315,8 +299,8 @@
     createMessageBus: (namespace) ->
         if not namespace
             namespace = FlintConstants.DEFAULT_NAMESPACE
-        if not @defMessageChannel
-            @defMessageChannel = @_createMessageChannel()
+        if not @messageChannel
+            @messageChannel = @_createMessageChannel()
         messageBus = @_createMessageBus namespace
         return messageBus

@@ -325,11 +309,11 @@
         if @messageBusList[namespace]
             messageBus = @messageBusList[namespace]
         else
-            messageBus = new SenderMessageBus @defMessageChannel, namespace
+            messageBus = new SenderMessageBus @messageChannel, namespace
             @messageBusList[namespace] = messageBus
         return messageBus

-    createPeer: ->
+    _createPeer: ->
         peer = new Peer
             host: @host
             port: '9433'
@@ -361,8 +345,8 @@
         return peer

     _clean: ->
-        @defMessageChannel?.close()
-        @defMessageChannel = null
+        @messageChannel?.close()
+        @messageChannel = null
         @messageBusList = null

 module.exports = FlintSenderManager


Fixed actionHandler Browse not working if options.BrowseFlag is an array

--- lib/services/ContentDirectory.coffee
+++ lib/services/ContentDirectory.coffee
@@ -56,7 +56,7 @@
         browseCallback = (err, resp) =>
           cb null, (if err? then @buildSoapError(err) else resp)

-        switch options?.BrowseFlag
+        switch options?.BrowseFlag?.toString()
           when 'BrowseMetadata'
             @browseMetadata options, browseCallback
           when 'BrowseDirectChildren'


use foreach with ignoreLengthChange

use foreach with ignoreLengthChange

--- test-integration/task-plan/cleanup.coffee
+++ test-integration/task-plan/cleanup.coffee
@@ -12,18 +12,7 @@
     Calendar.verify(@)

   @it 'Deletes all drafts (not really a test but nice cleanup)', ->
-    # Since we are deleting we need a special forEach that will not complain when the length changes
-    # forEach = (css, fn, fn2) =>
-    #   # Need to query multiple times because we might have moved screens so els are stale
-    #   @driver.findElements(css: css).then (els1) =>
-    #     index = 0
-    #     fn2?(els1) # Allow for things like printing "Deleting 20 drafts"
-    #     _.each els1, (el) =>
-    #       @driver.findElement(css: css).then (el) =>
-    #         index += 1
-    #         fn.call(@, el, index, els1.length)
-
-    forEach(@, '.plan:not(.is-published)',
+    @utils.forEach(css: '.plan:not(.is-published)', ignoreLengthChange: true,
       (plan, index, total) =>
         plan.click()
         new ReadingBuilder(@).edit(action: 'DELETE').then ->
@@ -35,7 +24,7 @@
     )

     # Delete published but not opened plans
-    forEach(@, '.plan.is-published:not(.is-open)',
+    @utils.forEach(css: '.plan.is-published:not(.is-open)', ignoreLengthChange: true,
       (plan, index, total) =>
         @addTimeout(10) # Published plans take a while to delete
         plan.click()


activity: add refresh for regular message panes

Topic, Private Message etc.. Everything except public pane.

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -57,6 +57,13 @@
     KD.singletons.windowController.addFocusListener @bound 'handleFocus'

+  refreshContent: ->
+
+    @listController.removeAllItems()
+    @listController.showLazyLoader()
+    @populate()
+
+
   createScrollView: ->

     @scrollView = new KDCustomScrollView


use font-face for pdfjs rendering

use font-face for pdfjs rendering

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -9,7 +9,7 @@
 			JOB_QUEUE_INTERVAL: 25

 			constructor: (@url, @options) ->
-				PDFJS.disableFontFace = true  # avoids repaints, uses worker more
+				# PDFJS.disableFontFace = true  # avoids repaints, uses worker more
 				# PDFJS.disableAutoFetch = true # enable this to prevent loading whole file
 				# PDFJS.disableStream
 				# PDFJS.disableRange


Delete store.coffee

Delete store.coffee

更新為js檔案

--- src/devtools/panel/app/store.coffee
+++ src/devtools/panel/app/store.coffee
@@ -1,18 +0,0 @@
-# 存储控制器
-((exports)->
-  exports.tohken = {} if !exports.tohken
-  exports.tohken.store = {
-    get: (key, pre, json = false)->
-      data = localStorage.getItem(key)
-      if data != null
-        return if json then JSON.parse(data) else data
-      else
-        return pre
-    set: (key, value)->
-      localStorage.setItem(key, value)
-    remove: (key)->
-      localStorage.removeItem(key)
-    reset: ->
-      localStorage.clear()
-  }
-)(window)


Remove log line

Remove log line


test error

test error


StatusBarMenu: Move separator from defaults to editor menu items.

--- client/ide/views/statusbar/statusbarmenu.coffee
+++ client/ide/views/statusbar/statusbarmenu.coffee
@@ -27,9 +27,9 @@
     items.Save        = callback: -> appManager.tell 'IDE', 'saveFile'
     items['Save All'] = callback: -> appManager.tell 'IDE', 'saveAllFiles'
     items.customView  = @syntaxSelector = new IDE.SyntaxSelectorMenuItem
+    items.separator   = type: 'separator'

   addDefaultMenuItems: (items, appManager) ->
-    items.separator   = type: 'separator'
     items.Shortcuts   = callback: -> appManager.tell 'IDE', 'showShortcutsView'
     items.Contribute  = callback: -> KD.utils.createExternalLink 'https://github.com/koding/IDE'
     items.Quit        = callback: ->


null is the correct attribute removal key

--- tests/scripts/unit/undo-manager.coffee
+++ tests/scripts/unit/undo-manager.coffee
@@ -28,7 +28,7 @@
     boldDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { bold: true })
     undoTests.run('insert attribute', {}, boldDelta)

-    unboldDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { bold: false })
+    unboldDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { bold: null })
     undoTests.run('remove attribute', {
       initial: Tandem.Delta.getInitial("The lazy fox\n").compose(boldDelta)
     }, unboldDelta)
@@ -36,7 +36,7 @@
     blueStrikeDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { color: 'blue', strike: true })
     undoTests.run('insert color/strike', {}, blueStrikeDelta)

-    undoBlueStrikeDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { color: false, strike: false })
+    undoBlueStrikeDelta = Tandem.Delta.makeRetainDelta(13, 0, 12, { color: null, strike: null })
     undoTests.run('remove color/strike', {
       initial: Tandem.Delta.getInitial("The lazy fox\n").compose(blueStrikeDelta)
     }, undoBlueStrikeDelta)


Removed anon access to /home

Removed anon access to /home

--- src/app/home/home.coffee
+++ src/app/home/home.coffee
@@ -1,7 +1,5 @@
-angular.module("doubtfire.home", [
-]
-).config(($stateProvider) ->
-
+angular.module("doubtfire.home", [])
+.config(($stateProvider) ->
   $stateProvider.state("home",
     url: "/home"
     views:
@@ -10,7 +8,7 @@
         templateUrl: "home/index.tpl.html"
     data:
       pageTitle: "_Home_"
-      roleWhitelist: ['basic', 'admin', 'anon']
+      roleWhitelist: ['basic', 'admin']
   )
 ).controller("HomeCtrl", ($scope, $state, UnitRole) ->
   $scope.unitRoles = UnitRole.query()


added multiple selector test

added multiple selector test

--- test/cheerio.coffee
+++ test/cheerio.coffee
@@ -104,4 +104,10 @@
     $a.should.have.length 1
     $a[0].children[0].data.should.equal 'Save'

+  it 'should be able to select multiple elements: $(".apple, #fruits")', ->
+    $elems = $('.apple, #fruits', fruits)
+    $elems.should.have.length 2
+    testAppleSelect $($elems[0])
+    $elems[1].attribs.id.should.equal 'fruits'
+


further simplified the code

further simplified the code

--- lib/idris-model.coffee
+++ lib/idris-model.coffee
@@ -91,59 +91,34 @@
   running: ->
     ! !@process

-  load: (uri, callback) ->
-    id = ++@requestId
-    cmd = [[':load-file', uri], id ]
-    @callbacks[id] = callback
-    @warnings[id] = []
-    @sendCommand cmd
+  getUID: -> ++@requestId

   sendCommand: (cmd) ->
     Logger.logOutgoingCommand cmd
     @process.stdin.write utils.serialize(cmd)

-cmds = [
-  [
-    ':docs-for'
-    'docsFor'
-  ]
-  [
-    ':type-of'
-    'getType'
-  ]
-]
-cmds.forEach (info) ->
-  IdrisModel.prototype[info[1]] = (word, callback) ->
-    id = ++@requestId
-    cmd = [[info[0], word], id]
+  prepareCommand: (cmd, callback) ->
+    id = @getUID()
     @callbacks[id] = callback
     @warnings[id] = []
-    @sendCommand cmd
+    @sendCommand [cmd, id]

-cmds = [
-  [
-    ':case-split'
-    'caseSplit'
-  ]
-  [
-    ':add-clause'
-    'addClause'
-  ]
-]
-cmds.forEach (info) ->
+  load: (uri, callback) ->
+    @prepareCommand [':load-file', uri], callback

-  IdrisModel.prototype[info[1]] = (line, word, callback) ->
-    id = ++@requestId
-    cmd = [[info[0], line, word], id]
-    @callbacks[id] = callback
-    @warnings[id] = []
-    @sendCommand cmd
+  docsFor: (word, callback) ->
+    @prepareCommand [':docs-for', word], callback

-IdrisModel::proofSearch = (line, word, callback) ->
-  id = ++@requestId
-  cmd = [[':proof-search', line, word, []], id]
-  @callbacks[id] = callback
-  @warnings[id] = []
-  @sendCommand cmd
+  getType: (word, callback) ->
+    @prepareCommand [':type-of', word], callback
+
+  caseSplit: (line, word, callback) ->
+    @prepareCommand [':case-split', line, word], callback
+
+  addClause: (line, word, callback) ->
+    @prepareCommand [':add-clause', line, word], callback
+
+  proofSearch = (line, word, callback) ->
+    @prepareCommand [':proof-search', line, word, []], callback

 module.exports = IdrisModel


Move code around

Move code around

--- src/lazy-runtime/LazyRuntime.coffee
+++ src/lazy-runtime/LazyRuntime.coffee
@@ -125,21 +125,15 @@

 LazyRuntime::_turn = (pointer, char) ->
-	if char == 'p'
-		e = String.fromCharCode @programState.pop()
-		y = @programState.pop()
-		x = @programState.pop()
-		@put x, y, e
-	else
-		dir = switch char
-			when '|'
-				if @programState.pop() then '^' else 'v'
-			when '_'
-				if @programState.pop() then '<' else '>'
-			when '?'
-				'^<v>'[Math.random() * 4 | 0]
+	dir = switch char
+		when '|'
+			if @programState.pop() then '^' else 'v'
+		when '_'
+			if @programState.pop() then '<' else '>'
+		when '?'
+			'^<v>'[Math.random() * 4 | 0]

-		pointer.turn dir
+	pointer.turn dir

 	pointer.advance()

@@ -183,7 +177,14 @@
 		# program ended
 		break if currentChar == '@'

-		@_turn pointer, currentChar
+		if currentChar == 'p'
+			e = String.fromCharCode @programState.pop()
+			y = @programState.pop()
+			x = @programState.pop()
+			@put x, y, e
+			pointer.advance()
+		else
+			@_turn pointer, currentChar

 	return


added fading support for editor popups

added fading support for editor popups

--- modules/gui/lib/editor/Editor.coffee
+++ modules/gui/lib/editor/Editor.coffee
@@ -9,7 +9,11 @@
         configure: (config)->

             config = super new floyd.Config
-
+
+                data:
+                    popup:
+                        fade: false
+
                 template: ->
                     div class:'editor Buttons floyd-loading', style:'opacity: .35'

@@ -49,11 +53,14 @@
         _buildButtons: (buttons)->

             _popup = (editor, fn)=>
+                editor.type ?= 'gui.ViewContext'
+
                 floyd.tools.gui.popup @,
                     type: editor.popup || 'gui.widgets.Popup'

                     data:
                         class: editor.class || 'dialog'
+                        fade: @data.popup.fade

                     view:
                         children: [ editor ]


Fix jump

Fix jump

--- lib/preferences.coffee
+++ lib/preferences.coffee
@@ -39,14 +39,6 @@

       window.FrenchMenu.pref.done = true

-      # Restore the flag when the settings panel is closed
-      editorPane = atom.workspace.getActivePane()
-      if editorPane
-        handler = editorPane.onDidRemoveItem (event) =>
-          if event.item.uri == 'atom://config'
-            window.JapaneseMenu.pref.done = false
-          handler.dispose()
-
     catch e
       console.error "Echec de traduction en Francais", e


Added createImage and drawImage methods to Sketch.

--- src/client/sketch.coffee
+++ src/client/sketch.coffee
@@ -174,5 +174,20 @@
     @g.stroke()
     @g.closePath()

+  createImage: (width, height) ->
+    imageData = @g.createImageData(width, height)
+
+    imageData.setPixel = (x, y, r, g, b, a = 255) ->
+      index = (x + y * @width) * 4
+
+      @data[index + 0] = r
+      @data[index + 1] = g
+      @data[index + 2] = b
+      @data[index + 3] = a
+
+    imageData
+
+  drawImage: (imageData, x, y) -> @g.putImageData(imageData, x, y)
+
 module.exports =
   Sketch: Sketch


Use double quotes.

Use double quotes.

--- src/coffee/import.coffee
+++ src/coffee/import.coffee
@@ -176,7 +176,7 @@
             humanReadable = JSON.stringify body, null, '  '
             deferred.reject "Problem on #{action}ing product:\n" + humanReadable
         else
-          deferred.reject 'Problem on #{action}ing product: ' + body
+          deferred.reject "Problem on #{action}ing product: " + body

     deferred.promise


Fix formatting boolean value in wrong way

Fixes issue that some methods aren't offered as completions:
https://github.com/dgutov/robe/issues/92

--- lib/robe-client.coffee
+++ lib/robe-client.coffee
@@ -58,5 +58,6 @@
           resolve(data)

   _formatArg: (arg) ->
-    return arg if arg? and arg isnt ''
-    '-'
+    return 'yes' if arg is true
+    return '-' if arg is false or not arg?
+    arg


:fire: Remove comments

:fire: Remove comments

--- src/components/view.cjsx
+++ src/components/view.cjsx
@@ -9,7 +9,6 @@
 class View extends React.Component
   @displayName = 'View'

-  # TODO: Unmount views when switching
   constructor: (@props) ->
     @lastView = localStorage.getItem('zenit:last-view')?.split(',') || []

@@ -18,11 +17,6 @@

   componentDidMount: ->
     @unsubscribe = ApplicationStore.listen(@onStateChange)
-
-    ###
-    ApplicationDelegate.on 'inject-view', (view) =>
-      @_onStackChanged(if typeof view is 'object' then view else [view])
-    ###

   onStateChange: =>
     @setState stack: ApplicationStore.getViews()


Stub render

Stub render

--- src/config/layout.coffee
+++ src/config/layout.coffee
@@ -14,15 +14,15 @@
   render: (world) ->
     div = world.T().div
     div {id: "layout"}, [
-      div {id: "controls"}
+      div {id: "controls"}, render world.find_child('controls')
       div {id: "grid"}, draw world.find_child('grid')
-      div {id: "inspector"}
+      div {id: "inspector"}, render world.find_child('inspector')
     ]

   _LABEL: "layout"
   _CHILDREN: [
-    #controls
+    controls
     grid
-    #inspector
+    inspector
   ]
 }


Only add view to attached editors

Only add view to attached editors

--- lib/spell-check.coffee
+++ lib/spell-check.coffee
@@ -12,4 +12,5 @@

   activate: ->
     rootView.eachEditor (editor) =>
-      editor.underlayer.append(@createView(editor)) if editor.getPane()?
+      if editor.attached and editor.getPane()?
+        editor.underlayer.append(@createView(editor))


Linted the coffeescript.

Linted the coffeescript.

--- gitorial/coffee/main.coffee
+++ gitorial/coffee/main.coffee
@@ -15,7 +15,7 @@
         .done (data) ->
             gitorial.session.username = data.username
             gitorial.session.loggedin = data.username isnt ""
-            gitorial.router();
+            gitorial.router()
             return
         .fail gitorial.routes.fail
         return
@@ -64,7 +64,7 @@
                 return

             $ '.user-listing-title'
-            .on 'click', (e) ->
+            .on 'click', (e) ->
                 e.preventDefault()
                 gitorial.tutorials.handleClick(e)
                 return


Use backbone for loading once backbone is loaded

--- core/app/assets/javascripts/intermediate.coffee
+++ core/app/assets/javascripts/intermediate.coffee
@@ -23,7 +23,7 @@
       showFrame.onload = onLoadSuccess
       # Somehow only lower case letters seem to work for those events --mark
       $(document).bind "modalready", onLoadSuccess
-      showFrame.src = "/facts/" + id + "?layout=client"
+      loadUrl "/facts/" + id #+ "?layout=client"
       showFrame.className = "overlay"
       return # don't return anything unless you have a callback on the other site of easyXdm

@@ -37,7 +37,7 @@
       showFrame.onload = onLoadSuccess
       # Somehow only lower case letters seem to work for those events --mark
       $(document).bind "modalready", onLoadSuccess
-      showFrame.src = "/facts/new" + "?fact=" + encodeURIComponent(text) + "&url=" + encodeURIComponent(siteUrl) + "&title=" + encodeURIComponent(siteTitle) + "&guided=" + encodeURIComponent(guided) + "&layout=client"
+      loadUrl "/facts/new" + "?fact=" + encodeURIComponent(text) + "&url=" + encodeURIComponent(siteUrl) + "&title=" + encodeURIComponent(siteTitle) + "&guided=" + encodeURIComponent(guided) + "&layout=client"
       showFrame.className = "overlay"
       onFactlinkCreated = (e, id) ->
         remote.highlightNewFactlink text, id
@@ -53,4 +53,11 @@
           showFrame.contentWindow.position top, left
       return # don't return anything unless you have a callback on the other site of easyXdm

+loadUrl = (url)->
+  history = showFrame.contentWindow.Backbone?.history
+  if history?
+    history.loadUrl url
+  else
+    showFrame.src = url
+
 $("iframe").preventScrollPropagation()


Boyscouting

Boyscouting

--- core/app/assets/javascripts/intermediate.coffee
+++ core/app/assets/javascripts/intermediate.coffee
@@ -11,7 +11,7 @@

   local:
     showFactlink: (id, successFn) ->
-      url = "/client/facts/" + id
+      url = "/client/facts/#{id}"
       showUrl url, successFn
       return # don't return anything unless you have a callback on the other site of easyXdm


Require coffee version in tests

Require coffee version in tests

So we don't have to have the compiled version for tests to work.

--- test/gozer_test.coffee
+++ test/gozer_test.coffee
@@ -1,5 +1,5 @@
 express = require('express')
-Gozer = require('../lib/gozer')
+Gozer = require('../src/gozer')

 startServer = (options = {}) ->
   app = express()


Control if exists session

Control if exists session

--- lib/zen.request.coffee
+++ lib/zen.request.coffee
@@ -4,12 +4,13 @@

   session: (request)->
     session = undefined
-    key = global.ZEN.session.authorization
-    session = request.headers[key] if request.headers[key]?
-    request.headers.cookie?.split(";").forEach (cookie) ->
-      parts = cookie.split("=")
-      key = parts[0].trim()
-      session = (parts[1] or "" ).trim() if key is global.ZEN.session.cookie
+    if global.ZEN.session
+      key = global.ZEN.session.authorization
+      session = request.headers[key] if key and request.headers[key]?
+      request.headers.cookie?.split(";").forEach (cookie) ->
+        parts = cookie.split("=")
+        key = parts[0].trim()
+        session = (parts[1] or "" ).trim() if key is global.ZEN.session.cookie
     session

   required: (values, request, response) ->


Delaying rerendering a until next tick

Delaying rerendering a until next tick

--- framer/TextLayer.coffee
+++ framer/TextLayer.coffee
@@ -168,7 +168,7 @@
 		promise = Utils.isFontFamilyLoaded(value)
 		if _.isObject(promise)
 			promise.then ->
-				layer.renderText()
+				setTimeout(layer.renderText, 0)
 	)
 	@define "fontWeight", textProperty(@, "fontWeight")
 	@define "fontStyle", textProperty(@, "fontStyle", "normal", _.isString)


Fixup 0f97b59, rough port of media-grid js.

--- src/js/jquery.hlf.media-grid.coffee
+++ src/js/jquery.hlf.media-grid.coffee
@@ -98,11 +98,11 @@
       return

     _adjustItemToBottomEdge: ($item) ->
+      $item.css { top: 'auto', bottom: 0 }
+      return
+
+    _adjustItemToRightEdge: ($item) ->
       $item.css { left: 'auto', right: 0 }
-      return
-
-    _adjustItemToRightEdge: ($item) ->
-      $item.css { top: 'auto', bottom: 0 }
       return

     _handleDimmingOnTogglingItem: ($item, dimmed) ->
@@ -119,7 +119,7 @@
     _handleEnteringDimmedItem: ($item) ->
       return no unless @$el.hasClass @classNames.dimmed
       _.delay =>
-        return if @$expandedItem.hasClass @classNames.focused
+        return if @$expandedItem?.hasClass @classNames.focused
         @$el.removeClass @classNames.dimmed
         return
       , @dimDelay


Clean up sequencing for W3C log files

Clean up sequencing for W3C log files

* Make sure we set the proper flags to append to the log file if we start
  up with an existing one
* Use the callback from our first write to know when we're done opening
* Don't write log lines while we're still opening

--- src/streammachine/logger/index.coffee
+++ src/streammachine/logger/index.coffee
@@ -130,8 +130,7 @@
             process.addListener "SIGHUP", =>
                 # re-open our log file
                 console.log "w3c reloading log file"
-                @close()
-                @open()
+                @close => @open()

         #----------

@@ -141,7 +140,7 @@
                 # for a valid w3c log, level should == "request", meta.
                 logline = "#{meta.ip} #{strftime(new Date(meta.time),"%F %T")} #{meta.path} 200 #{escape(meta.ua)} #{meta.bytes} #{meta.seconds}\n"

-                if @file
+                if @file && !@_opening
                     # make sure there aren't any queued writes
                     unless _u(@queued).isEmpty
                         q = @queued
@@ -162,9 +161,12 @@

         open: (cb) ->
             if @_opening
+                console.log "W3C already opening... wait."
                 # we're already trying to open.  return an error so we queue the message
                 cb?(true)
                 return true
+
+            console.log "W3C opening log file."

             # otherwise, open the file
             @_opening = true
@@ -178,26 +180,25 @@
                     # existing file...  don't write headers, just open so we can
                     # start appending
                     initFile = false
-
-            @file = fs.createWriteStream @options.filename
+
+            @file = fs.createWriteStream @options.filename, flags:(if initFile then "w" else "r+")

             @file.once "open", (err) =>
                 console.log "w3c log open with ", err
                 if initFile
                     # write our initial w3c lines
-                    @file.write "#Software: StreamMachine\n#Version: 0.1.0\n#Fields: c-ip date time cs-uri-stem c-status cs(User-Agent) sc-bytes x-duration\n"
-
-                @once "flush", =>
-                    @_opening = false
-                    console.log "w3c open complete"
-                    cb?(false)
+                    @file.write "#Software: StreamMachine\n#Version: 0.1.0\n#Fields: c-ip date time cs-uri-stem c-status cs(User-Agent) sc-bytes x-duration\n", "utf8", =>
+                      @_opening = false
+                      console.log "w3c open complete"
+                      cb?(false)

                 @flush()

         #----------

         close: (cb) ->
-            @file?.end()
+            @file?.end null, null, =>
+              console.log "W3C log file closed."
             @file = null

         #----------


Switch to new-style helpers

Switch to new-style helpers

--- hamwerk/client/todos.coffee
+++ hamwerk/client/todos.coffee
@@ -75,10 +75,11 @@
     input.focus()
     input.select()

-Template.contents.showEverything = ->
-    return true if Meteor.userId()?
-    return true if !online
-    return false
+Template.contents.helpers
+    showEverything: ->
+        return true if Meteor.userId()?
+        return true if !online
+        return false

 # Classes #

@@ -86,47 +87,50 @@
     for dow in (DateOMatic.getDowName(n).toLowerCase() for n in [0..6])
         $(".#{dow} input[type='time']").prop("disabled", not $(".#{dow} input[type='checkbox']").prop("checked"))

-Template.classes.classes = ->
-    # Fake dependency on update_interval to update periodically.
-    Meteor.extra_garbage_of_tomfoolery = Session.get "update_interval"
-
-    classes = Offline.smart.classes().find({}).fetch()
-    sortIterator = (oneClass) ->
-        return oneClass.name unless oneClass.schedule?
-        [sun, mon, tue, wed, thu, fri, sat] = oneClass.schedule
-        dateStrings = _.compact [
-            if sun.enabled
-                "Sunday at #{sun.time}"
-            if mon.enabled
-                "Monday at #{mon.time}"
-            if tue.enabled
-                "Tuesday at #{tue.time}"
-            if wed.enabled
-                "Wednesday at #{wed.time}"
-            if thu.enabled
-                "Thursday at #{thu.time}"
-            if fri.enabled
-                "Friday at #{fri.time}"
-            if sat.enabled
-                "Saturday at #{sat.time}"
-        ]
-        dates = _.map dateStrings, DateOMatic.parseFuzzyFutureDateAndTime
-        deltas = _.map dates, (date) ->
-            delta = DateOMatic.msDifferential date
-            if delta > 603900000
-                delta = delta - 603900000
-            delta
-        smallestDelta = Math.min deltas...
-        return smallestDelta
-    sortedClasses = _.sortBy classes, sortIterator
-    return sortedClasses
-
-Template.classes.fake_all_class_list = -> [_id: ""]
-
-Template.classes.edit_disabled = -> if online() then "" else "disabled"
-Template.classes.show_edit = -> online()
-Template.new_assignment_box.disabled = -> if online() then "" else "disabled"
-Template.classes.class_color = -> @color
+Template.classes.helpers
+    classes: ->
+        # Fake dependency on update_interval to update periodically.
+        Meteor.extra_garbage_of_tomfoolery = Session.get "update_interval"
+
+        classes = Offline.smart.classes().find({}).fetch()
+        sortIterator = (oneClass) ->
+            return oneClass.name unless oneClass.schedule?
+            [sun, mon, tue, wed, thu, fri, sat] = oneClass.schedule
+            dateStrings = _.compact [
+                if sun.enabled
+                    "Sunday at #{sun.time}"
+                if mon.enabled
+                    "Monday at #{mon.time}"
+                if tue.enabled
+                    "Tuesday at #{tue.time}"
+                if wed.enabled
+                    "Wednesday at #{wed.time}"
+                if thu.enabled
+                    "Thursday at #{thu.time}"
+                if fri.enabled
+                    "Friday at #{fri.time}"
+                if sat.enabled
+                    "Saturday at #{sat.time}"
+            ]
+            dates = _.map dateStrings, DateOMatic.parseFuzzyFutureDateAndTime
+            deltas = _.map dates, (date) ->
+                delta = DateOMatic.msDifferential date
+                if delta > 603900000
+                    delta = delta - 603900000
+                delta
+            smallestDelta = Math.min deltas...
+            return smallestDelta
+        sortedClasses = _.sortBy classes, sortIterator
+        return sortedClasses
+
+    fake_all_class_list: -> [_id: ""]
+
+    edit_disabled: -> if online() then "" else "disabled"
+    show_edit: -> online()
+    class_color: -> @color
+
+Template.new_assignment_box.helpers
+    disabled: -> if online() then "" else "disabled"

 Template.classes.events
     "mousedown .class": (evt) -> Router.setList(this._id) if @_id?
@@ -160,23 +164,28 @@
                 Offline.save()
         evt.target.value = ""

-Template.classes.active = ->
-    if Session.equals("class_id", this._id)
-        "active"
-    else
-        ""
+Template.classes.helpers
+    active: ->
+        if Session.equals("class_id", this._id)
+            "active"
+        else
+            ""

 # Class Editing #

-Template.edit_class_modal.name = -> Offline.smart.classes().findOne(Session.get("editing_class"))?.name
-Template.edit_class_modal.abbr = -> Offline.smart.classes().findOne(Session.get("editing_class"))?.abbr
-Template.edit_class_modal.class_color = -> Offline.smart.classes().findOne(Session.get("editing_class"))?.color
-for dow, n in (DateOMatic.getDowName(n).toLowerCase() for n in [0..6])
-    do (dow, n) ->
-        Template.edit_class_modal["#{dow}Checked"] = ->
-            if Offline.smart.classes().findOne(Session.get("editing_class"))?.schedule?[n]?.enabled then "checked" else ""
-        Template.edit_class_modal["#{dow}Time"] = ->
-            Offline.smart.classes().findOne(Session.get("editing_class"))?.schedule?[n]?.time
+Template.edit_class_modal.helpers
+    name: -> Offline.smart.classes().findOne(Session.get("editing_class"))?.name
+    abbr: -> Offline.smart.classes().findOne(Session.get("editing_class"))?.abbr
+    class_color: -> Offline.smart.classes().findOne(Session.get("editing_class"))?.color
+do ->
+    ecmHelpers = {}
+    for dow, n in (DateOMatic.getDowName(n).toLowerCase() for n in [0..6])
+        do (dow, n) ->
+            ecmHelpers["#{dow}Checked"] = ->
+                if Offline.smart.classes().findOne(Session.get("editing_class"))?.schedule?[n]?.enabled then "checked" else ""
+            ecmHelpers["#{dow}Time"] = ->
+                Offline.smart.classes().findOne(Session.get("editing_class"))?.schedule?[n]?.time
+    Template.edit_class_modal.helpers ecmHelpers

 Template.edit_class_modal.events
     "click .delete": ->
@@ -208,23 +217,24 @@

 # New Assignment Box #

-Template.new_assignment_box.sample = ->
-    rand = (min, max) -> Math.floor(Math.random() * (max - min + 1) + min)
-
-    types = [
-        -> "today"
-        -> "tomorrow"
-        -> "#{DateOMatic.getMonthName(rand(0, 11))} #{rand(1, 28)}"
-        -> "#{DateOMatic.getMonthName(rand(0, 11))} #{rand(1, 28)}, #{new Date().getFullYear()+1}"
-        -> "#{DateOMatic.getDowName(rand(0, 6))}"
-        -> "#{DateOMatic.getDowName(rand(0, 6)).slice(0, 3)}"
-        -> "#{rand(1, 10)} days from now"
-    ]
-    task = "read chapter #{rand(1, 15)} due #{_.sample(types)()}"
-    if Session.equals("class_id", "")
-        "#{_(Offline.smart.classes().find().fetch()).chain().pluck(_.shuffle(['name','abbr'])[0]).shuffle().value()[0]} #{task}"
-    else
-        "#{task}"
+Template.new_assignment_box.helpers
+    sample: ->
+        rand = (min, max) -> Math.floor(Math.random() * (max - min + 1) + min)
+
+        types = [
+            -> "today"
+            -> "tomorrow"
+            -> "#{DateOMatic.getMonthName(rand(0, 11))} #{rand(1, 28)}"
+            -> "#{DateOMatic.getMonthName(rand(0, 11))} #{rand(1, 28)}, #{new Date().getFullYear()+1}"
+            -> "#{DateOMatic.getDowName(rand(0, 6))}"
+            -> "#{DateOMatic.getDowName(rand(0, 6)).slice(0, 3)}"
+            -> "#{rand(1, 10)} days from now"
+        ]
+        task = "read chapter #{rand(1, 15)} due #{_.sample(types)()}"
+        if Session.equals("class_id", "")
+            "#{_(Offline.smart.classes().find().fetch()).chain().pluck(_.shuffle(['name','abbr'])[0]).shuffle().value()[0]} #{task}"
+        else
+            "#{task}"

 # Assignments #

@@ -326,61 +336,71 @@
                 $("#new-assignment").parent().addClass("has-error")
                 return

-Template.assignments.assignments = ->
-    # Fake dependency on update_interval to update periodically.
-    Meteor.extra_garbage_of_tomfoolery = Session.get "update_interval"
-
-    # Determine which assignments to display in main pane,
-    # selected based on class_id and tag_filter.
-    class_id = Session.get "class_id"
-
-    sel = class_id: class_id
-    if class_id is ""
-        sel = {}
-
-    return _(Offline.smart.assignments().find(sel).fetch()).chain()
-           .sortBy((obj) -> new Date(obj.timestamp).getTime())
-           .sortBy((obj) -> new Date(obj.due).getTime())
-           .sortBy("done")
-           .groupBy("done")
-           .pairs()
-           .map(([truthiness, list]) -> _.sortBy(list, (obj) ->
-                if truthiness is "true"
-                    Math.abs(DateOMatic.msDifferential(new Date(obj.due)))
-                else
-                    new Date(obj.due).getTime()
-            ))
-           .reduce(((memo, list) -> memo.concat(list)), [])
-           .value()
-
-Template.assignment_item.precise_due_date = -> DateOMatic.stringify(@due, yes, no, yes)
-
-Template.assignment_item.editable_due_date = -> DateOMatic.stringify(@due, no, yes, yes)
-
 div = (a, b) -> (a - a % b) / b

-Template.assignment_item.fuzzy_due_date = -> if DateOMatic.isFuture(@due) then "in #{DateOMatic.fuzzyDifferential(@due)}" else "#{DateOMatic.fuzzyDifferential(@due)} ago"
-
-Template.assignment_item.done_class = -> if this.done then "muted" else ""
-
-Template.assignment_item.done_checkbox = -> if this.done then "check-" else ""
-
-Template.assignment_item.editing = -> Session.equals("editing_itemname", this._id)
-
-Template.assignment_item.class_color = ->
-    Offline.smart.classes().findOne(@class_id)?.color
-
-Template.assignment_item.color_class = ->
-    if @done
-        return "list-group-item-success"
-    msLeft = new Date(@due).getTime() - (new Date()).getTime()
-    if msLeft < 0
-        return "list-group-item-danger"
-    if div(msLeft, 1000 * 60 * 60) < 24
-        return "list-group-item-warning"
-    if div(msLeft, 1000 * 60 * 60 * 24) < 2
-        return "list-group-item-info"
-    return ""
+assignmentHelpers =
+    assignments: ->
+        # Fake dependency on update_interval to update periodically.
+        Meteor.extra_garbage_of_tomfoolery = Session.get "update_interval"
+
+        # Determine which assignments to display in main pane,
+        # selected based on class_id and tag_filter.
+        class_id = Session.get "class_id"
+
+        sel = class_id: class_id
+        if class_id is ""
+            sel = {}
+
+        return _(Offline.smart.assignments().find(sel).fetch()).chain()
+               .sortBy((obj) -> new Date(obj.timestamp).getTime())
+               .sortBy((obj) -> new Date(obj.due).getTime())
+               .sortBy("done")
+               .groupBy("done")
+               .pairs()
+               .map(([truthiness, list]) -> _.sortBy(list, (obj) ->
+                    if truthiness is "true"
+                        Math.abs(DateOMatic.msDifferential(new Date(obj.due)))
+                    else
+                        new Date(obj.due).getTime()
+                ))
+               .reduce(((memo, list) -> memo.concat(list)), [])
+               .value()
+
+    precise_due_date: ->
+        DateOMatic.stringify(@due, yes, no, yes)
+
+    editable_due_date: ->
+        DateOMatic.stringify(@due, no, yes, yes)
+
+    fuzzy_due_date: ->
+        if DateOMatic.isFuture(@due) then "in #{DateOMatic.fuzzyDifferential(@due)}" else "#{DateOMatic.fuzzyDifferential(@due)} ago"
+
+    done_class: ->
+        if this.done then "muted" else ""
+
+    done_checkbox: ->
+        if this.done then "check-" else ""
+
+    editing: ->
+        Session.equals("editing_itemname", this._id)
+
+    class_color: ->
+        Offline.smart.classes().findOne(@class_id)?.color
+
+    color_class: ->
+        if @done
+            return "list-group-item-success"
+        msLeft = new Date(@due).getTime() - (new Date()).getTime()
+        if msLeft < 0
+            return "list-group-item-danger"
+        if div(msLeft, 1000 * 60 * 60) < 24
+            return "list-group-item-warning"
+        if div(msLeft, 1000 * 60 * 60 * 24) < 2
+            return "list-group-item-info"
+        return ""
+
+Template.assignments.helpers assignmentHelpers
+Template.assignment_item.helpers assignmentHelpers

 Template.assignment_item.events
     "click .check": ->


Add specs for highlighting matches

Add specs for highlighting matches

--- spec/command-palette-spec.coffee
+++ spec/command-palette-spec.coffee
@@ -90,3 +90,35 @@
       document.body.focus()
       atom.commands.dispatch(workspaceElement, 'command-palette:toggle')
       expectCommandsForElement(workspaceElement)
+
+  describe "match highlighting", ->
+    beforeEach ->
+      jasmine.attachToDOM(workspaceElement)
+
+    it "highlights an exact match", ->
+      palette.filterEditorView.getModel().setText('Application: About')
+      palette.populateList()
+      resultView = palette.getSelectedItemView()
+
+      matches = resultView.find('.character-match')
+      expect(matches.length).toBe 1
+      expect(matches.last().text()).toBe 'Application: About'
+
+    it "highlights a partial match", ->
+      palette.filterEditorView.getModel().setText('Application')
+      palette.populateList()
+      resultView = palette.getSelectedItemView()
+
+      matches = resultView.find('.character-match')
+      expect(matches.length).toBe 1
+      expect(matches.last().text()).toBe 'Application'
+
+    it "highlights multiple matches in the file name", ->
+      palette.filterEditorView.getModel().setText('ApplicationAbout')
+      palette.populateList()
+      resultView = palette.getSelectedItemView()
+
+      matches = resultView.find('.character-match')
+      expect(matches.length).toBe 2
+      expect(matches.first().text()).toBe 'Application'
+      expect(matches.last().text()).toBe 'About'


:white_check_mark: Add spec for EditorRegistry::forEach

--- spec/editor-registry-spec.coffee
+++ spec/editor-registry-spec.coffee
@@ -24,6 +24,14 @@
       atom.workspace.destroyActivePaneItem()
       expect(editorRegistry.editorLinters.size).toBe(0)

+  describe '::forEach', ->
+    it 'calls the callback once per editorLinter', ->
+      editorRegistry.create(atom.workspace.getActiveTextEditor())
+      timesCalled = 0
+      editorRegistry.forEach -> ++timesCalled
+      editorRegistry.forEach -> ++timesCalled
+      expect(timesCalled).toBe(2)
+
   describe '::ofTextEditor', ->
     it 'returns undefined when invalid key is provided', ->
       expect(editorRegistry.ofTextEditor(null)).toBeUndefined()
@@ -34,6 +42,7 @@
       activeEditor = atom.workspace.getActiveTextEditor()
       editorRegistry.create(activeEditor)
       expect(editorRegistry.ofTextEditor(activeEditor)).toBeDefined()
+
   describe '::ofActiveTextEditor', ->
     it 'returns undefined if active pane is not a text editor', ->
       expect(editorRegistry.ofActiveTextEditor()).toBeUndefined()


try to fix travisCI failure

try to fix travisCI failure

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -36,7 +36,7 @@

         waitsForPromise ->
           atom.packages.activatePackage('vim-mode-plus').then (pack) ->
-            return fn(pack)
+            return Promise.resolve(fn(pack))

         runs ->
           oldPaths = Object.keys(savedCache)


:shirt:

:shirt:

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -1181,7 +1181,7 @@

       waitsForPromise ->
         try
-         atom.packages.deactivatePackage("package-that-throws-on-deactivate")
+          atom.packages.deactivatePackage("package-that-throws-on-deactivate")
         catch error
           thrownError = error

@@ -1375,8 +1375,8 @@
           atom.themes.activateThemes()

       afterEach ->
-          waitsForPromise ->
-            atom.themes.deactivateThemes()
+        waitsForPromise ->
+          atom.themes.deactivateThemes()

       it "enables and disables a theme", ->
         packageName = 'theme-with-package-file'


Fix test

Fix test

--- spec/squirrel-update-spec.coffee
+++ spec/squirrel-update-spec.coffee
@@ -79,7 +79,7 @@

       jasmine.unspy(Spawner, 'spawn')
       spyOn(Spawner, 'spawn').andCallFake (command, args, callback) ->
-        if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut'
+        if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut' and args?[3].match /Desktop/i
           fs.writeFileSync(desktopShortcutPath, '')
         else
           # simply ignore other commands


changeback to github name of course

changeback to github name of course

--- test/lib/fspath.coffee
+++ test/lib/fspath.coffee
@@ -115,8 +115,8 @@
       './':true
       './lib':true
       './lib/index.coffee':true
-      '../fspath':true
-      '../fspath/package.json':true
+      '../node-fspath':true
+      '../node-fspath/package.json':true
       './fake':false
       './fake.file':false
     }
@@ -136,8 +136,8 @@
       './':false
       './lib':false
       './lib/index.coffee':true
-      '../fspath':false
-      '../fspath/package.json':true
+      '../node-fspath':false
+      '../node-fspath/package.json':true
       './fake':false
       './fake.file':false
     }
@@ -158,8 +158,8 @@
       './':true
       './lib':true
       './lib/index.coffee':false
-      '../fspath':true
-      '../fspath/package.json':false
+      '../node-fspath':true
+      '../node-fspath/package.json':false
       './fake':false
       './fake.file':false
     }


updated how server runs

updated how server runs

--- grunt/express-server.coffee
+++ grunt/express-server.coffee
@@ -2,7 +2,7 @@
   grunt.config 'express',
     all:
       options:
-        cmd: 'coffee'
+        opts: ['node_modules/coffee-script/bin/coffee']
         script: './server.coffee'
         port: 8000


fixes sorting of discrete string axis (closes #502)

--- src/viz/types/helpers/graph/includes/axes.coffee
+++ src/viz/types/helpers/graph/includes/axes.coffee
@@ -179,8 +179,10 @@
           arr
         , []
         counts
-      else
-        uniques(values).sort((a,b) -> a-b)
+      else if values[0].constructor is String
+        uniques(values).sort((a,b) -> "" + a.localeCompare("" + b))
+      else
+        uniques(values).sort((a,b) -> a - b)
     else
       values.sort (a, b) -> a - b
       if vars[axis].scale.value is "log"


jupyter sync: throttle incoming syncstring changes; improve documentation

--- src/smc-webapp/editor_jupyter.coffee
+++ src/smc-webapp/editor_jupyter.coffee
@@ -831,34 +831,49 @@

     # listen for changes to the syncstring
     init_syncstring_change: () =>
-        #dbg = @dbg("syncstring_change")
+        #dbg = @dbg("syncstring_change"); dbg()
         if @read_only
             return
         last_syncstring = @syncstring.live()
         handle_syncstring_change = () =>
             live = @syncstring.live()
             if last_syncstring != live
-                # it really did change
-                #dbg()
+                # It really did change
+                # Get current state of the DOM.  We do this even if not "dirty" -- we always get,
+                # just to be absolutely sure, as this is critical to get right to avoid any data loss.
                 cur_dom = @dom.get(true)
                 if @_last_dom? and @_last_dom != cur_dom
+                    # The DOM changed since the last time we saved, so we compute
+                    # how it changed and apply that patch to the live syncstring
+                    # that just came in.  The result is what we update the DOM
+                    # to equal.
                     patch = dmp.patch_make(@_last_dom, cur_dom)
                     live = dmp.patch_apply(patch, live)[0]
                     @_last_dom = cur_dom
                     @syncstring.live(live)
                 last_syncstring = live
                 if cur_dom != live
-                    @_last_dom = result = @dom.set(live)
+                    # The actual current DOM is different than what we need to set it to be
+                    # equal to, so... we mutate it to equal live.
+                    @_last_dom = result = @dom.set(live)  # the output of this is what really got set.
                     if result != live
-                        console.warn("Jupyter sync: inconsistency during sync")
-                        # If the user then makes a change to this notebook, the fixed
+                        # It is entirely possible, due to weirdness of jupyter or corruption of the
+                        # state of syncstring that setting doesn't result in a DOM that equals what
+                        # we want.  We do NOT just change the syncstring, since that can lead to
+                        # crazy feedback loops.  Instead, we just note this.  If the user actively
+                        # does edit the DOM further, their change will then propogate back out.
+                        # In particular, if the user then makes a change to this notebook, the fixed
                         # version of the syncstring will propogate automatically.  If
                         # they don't, it stays broken.  Having this notebook *fix*
                         # automatically DOES NOT WORK... because if there are multiple
                         # notebooks open at once, they will all fix at once, which
                         # breaks things (due to patch merge)!  Ad infinitum!!
-
-        @syncstring.on('sync', handle_syncstring_change)
+                        console.warn("Jupyter sync: inconsistency during sync")
+
+        # The throttle makes it so now matter how often the syncstring changes, with many
+        # others doing edits (or whatever), we will only ever actually apply those changes
+        # at most once every 2s.  We do fully apply all edits though.
+        @syncstring.on('sync', underscore.throttle(handle_syncstring_change, 2000))
         @syncstring._syncstring.on('metadata-change', @update_save_state)

     ipynb_timestamp: (cb) =>


API node restricted keys became a static property.

--- src/ApiNodes/OVirtApiNode.coffee
+++ src/ApiNodes/OVirtApiNode.coffee
@@ -5,15 +5,6 @@
 _ = require 'lodash'
 ApiNodes = {}
 Mixins = require __dirname + '/../Mixins/'
-
-
-RESTRICTED_KEYS = [
-  'actions'
-  'attributes'
-  'collections'
-  'properties'
-  'resourceLinks'
-]

 #
@@ -41,6 +32,7 @@
 # @include Mixins.PropertyDistributor
 #
 OVirtApiNode = class ApiNodes.OVirtApiNode extends CoffeeMix
+  # Included mixins
   @include Mixins.PropertyDistributor

   # Hack that forces Codo to see properties
@@ -52,6 +44,19 @@
   #
   @API_NODE_TYPES:
     {}
+
+  #
+  # List of the system properties
+  #
+  @RESTRICTED_KEYS: [
+    '$actions'
+    '$attributes'
+    '$collections'
+    '$connection'
+    '$owner'
+    '$properties'
+    '$resourceLinks'
+  ]

   #
   # @property [Object<ApiNodes.OVirtAction>]
@@ -165,7 +170,7 @@
     delete @[key] for key of oldProperties
     return unless typeof properties is 'object'

-    for key, value of properties when key not in RESTRICTED_KEYS
+    for key, value of properties when key not in OVirtApiNode.RESTRICTED_KEYS
       @consumeProperty value
       @[key] = value

@@ -181,7 +186,7 @@
     delete @[key] for key of @_actions
     return unless typeof actions is 'object'

-    for key, value of actions when key not in RESTRICTED_KEYS
+    for key, value of actions when key not in OVirtApiNode.RESTRICTED_KEYS
       @addAction key, value

   #
@@ -197,7 +202,7 @@
     delete @[key] for key of @_resourceLinks
     return unless typeof resourceLinks is 'object'

-    for key, value of resourceLinks when key not in RESTRICTED_KEYS
+    for key, value of resourceLinks when key not in OVirtApiNode.RESTRICTED_KEYS
       @addResourceLink key, value

   #


LocationRange#isEqualTo

LocationRange#isEqualTo

--- src/trix/models/location_range.coffee
+++ src/trix/models/location_range.coffee
@@ -8,6 +8,12 @@
   constructor: (@start, @end) ->
     @end ?= @start
     {@index, @offset} = @start
+
+  isEqualTo: (locationRange) ->
+    @start.index is locationRange?.start?.index and
+      @end.index is locationRange?.end?.index and
+      @start.offset is locationRange?.start?.offset and
+      @end.offset is locationRange?.end?.offset

   isCollapsed: ->
     @start.index is @end.index and @start.offset is @end.offset


user list: added error message for bulk selections

--- app/assets/javascripts/admin/views/user/user_list_view.coffee
+++ app/assets/javascripts/admin/views/user/user_list_view.coffee
@@ -1,6 +1,7 @@
 ### define
 underscore : _
 backbone.marionette : marionette
+libs/toast : Toast
 ./user_list_item_view : UserListItemView
 admin/models/user/user_collection : UserCollection
 admin/views/user/team_role_modal_view : TeamRoleModalView
@@ -91,4 +92,7 @@

       view.$el.modal("show")

+    else
+      Toast.error("No user is selected.")

+


Update hover effect on network map

Update hover effect on network map

--- app/assets/javascripts/modules/protected_areas/base.js.coffee
+++ app/assets/javascripts/modules/protected_areas/base.js.coffee
@@ -31,10 +31,8 @@
             $pa = $(@)

             for paGeometry, i in window.ProtectedPlanet.Map.protectedAreas
-              if i == $pa.data("wdpa-id")
-                paGeometry.setStyle({weight: 3, fillOpacity: .8})
-              else
-                paGeometry.setStyle({weight: 1, fillOpacity: .1})
+              if i != $pa.data("wdpa-id")
+                paGeometry.setStyle({weight: 1, fillOpacity: .1, opacity: .2 })
           )

           $content.find("[data-wdpa-id]").mouseleave( (ev) ->


added TODO

added TODO

--- app/assets/javascripts/monitoring/views/metric_list.js.coffee
+++ app/assets/javascripts/monitoring/views/metric_list.js.coffee
@@ -58,6 +58,8 @@
         #
         # @param [Dygraph] g        the graph which was just panned
         metric.graph._bixby_pan_complete = (g) ->
+          # TODO should probably only load data for visible graphs
+          #      then bring others in when they become visible (page is scrolled)
           metrics.each (m) ->
             if m.graph && m.graph != g
               Bixby.monitoring.load_more_data(m.graph, m)


Add `build-and-sign` grunt task for codesigning Atom

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -298,6 +298,7 @@
   unless process.platform is 'linux' or grunt.option('no-install')
     defaultTasks.push 'install'
   grunt.registerTask('default', defaultTasks)
+  grunt.registerTask('build-and-sign', ['download-electron', 'download-electron-chromedriver', 'build', 'set-version', 'generate-asar', 'codesign:app', 'install'])

 getDefaultChannelAndReleaseBranch = (version) ->
   if version.match(/dev/) or isBuildingPR()


Add atom package compile config.

Add atom package compile config.

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -121,27 +121,27 @@
       dest: appDir
       ext: '.js'

-  # for child in fs.readdirSync('node_modules') when child isnt '.bin'
-  #   directory = path.join('node_modules', child)
-  #   metadataPath = path.join(directory, 'package.json')
-  #   continue unless grunt.file.isFile(metadataPath)
-  #
-  #   {engines, theme} = grunt.file.readJSON(metadataPath)
-  #   if engines?.atom?
-  #     coffeeConfig.glob_to_multiple.src.push("#{directory}/**/*.coffee")
-  #     coffeeConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.coffee")
-  #
-  #     lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less")
-  #     lessConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.less")
-  #
-  #     unless theme
-  #       prebuildLessConfig.src.push("#{directory}/**/*.less")
-  #       prebuildLessConfig.src.push("!#{directory}/spec/**/*.less")
-  #
-  #     csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson")
-  #     csonConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.cson")
-  #
-  #     pegConfig.glob_to_multiple.src.push("#{directory}/lib/*.pegjs")
+  for child in fs.readdirSync('node_modules') when child isnt '.bin'
+    directory = path.join('node_modules', child)
+    metadataPath = path.join(directory, 'package.json')
+    continue unless grunt.file.isFile(metadataPath)
+
+    {engines, theme} = grunt.file.readJSON(metadataPath)
+    if engines?.atom?
+      coffeeConfig.glob_to_multiple.src.push("#{directory}/**/*.coffee")
+      coffeeConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.coffee")
+
+      lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less")
+      lessConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.less")
+
+      unless theme
+        prebuildLessConfig.src.push("#{directory}/**/*.less")
+        prebuildLessConfig.src.push("!#{directory}/spec/**/*.less")
+
+      csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson")
+      csonConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.cson")
+
+      pegConfig.glob_to_multiple.src.push("#{directory}/lib/*.pegjs")

   opts =
     name: pkgName


webapp/sagews: fix fallout of 65bfc5d4b355571f78a408d7203883ed5c736723

--- src/smc-webapp/sagews/sagews.coffee
+++ src/smc-webapp/sagews/sagews.coffee
@@ -1667,10 +1667,10 @@
                 if not CoffeeScript?
                     # DANGER: this is the only async code in process_output_mesg
                     misc_page.load_coffeescript_compiler () =>
-                        sagews_eval(CoffeeScript?.compile(code), @, opts.element, redux)
+                        sagews_eval(CoffeeScript?.compile(code), @, opts.element, undefined, obj, redux)
                 else
                     # DANGER: this is the only async code in process_output_mesg
-                    sagews_eval(CoffeeScript?.compile(code), @, opts.element, redux)
+                    sagews_eval(CoffeeScript?.compile(code), @, opts.element, undefined, obj, redux)
             else
                 # The eval below is an intentional cross-site scripting vulnerability
                 # in the fundamental design of SMC.


Add node drag interaction

Add node drag interaction

--- app/frontend/javascripts/views/visualization-canvas.js.coffee
+++ app/frontend/javascripts/views/visualization-canvas.js.coffee
@@ -126,6 +126,10 @@

   # Drawing methods
   # -------------------
+
+  redraw: ->
+    if @force.alpha() < @force.alphaMin()
+      @onTick()

   # Tick Function
   onTick: =>
@@ -212,8 +216,7 @@
   rescale: ->
     @viewport.translate.x = @viewport.origin.x + @viewport.x - @viewport.offsetx - @viewport.offsetnode.x
     @viewport.translate.y = @viewport.origin.y + @viewport.y - @viewport.offsety - @viewport.offsetnode.y
-    if @force.alpha() < @force.alphaMin()
-      @onTick()
+    @redraw()

   rescaleTransition: ->
     # implement transition here
@@ -221,14 +224,47 @@

   zoom: (value) ->
     super(value)
-    if @force.alpha() < @force.alphaMin()
-      @onTick()
-
+    @redraw()

   # Canvas Mouse Events
   # -------------------

+  # Canvas Drag Events
+  onCanvasDragStart: =>
+    if @node_hovered
+      @force
+        .alphaTarget 0.1
+        .restart()
+    unless @node_hovered
+      @canvas.style 'cursor', 'move'
+
+  onCanvasDragged: =>
+    if @node_hovered
+      @node_hovered.fx = (d3.event.x - @viewport.center.x - @viewport.translate.x) / @viewport.scale
+      @node_hovered.fy = (d3.event.y - @viewport.center.y - @viewport.translate.y) / @viewport.scale
+      @redraw()
+    else
+      @viewport.x  += d3.event.dx
+      @viewport.y  += d3.event.dy
+      @viewport.dx += d3.event.dx
+      @viewport.dy += d3.event.dy
+      @rescale()
+
+  onCanvasDragEnd: =>
+    if @node_hovered
+      @force.alphaTarget 0
+    else
+      @canvas.style 'cursor','default'
+      #@force.alphaTarget(0)
+      # Skip if viewport has no translation
+      if @viewport.dx == 0 and @viewport.dy == 0
+        Backbone.trigger 'visualization.node.hideInfo'
+        return
+      # TODO! Add viewportMove action to history
+      @viewport.dx = @viewport.dy = 0;
+
+  # Canvas Mouse Events
   onCanvasEnter: =>
     @onCanvasMove()

@@ -281,8 +317,7 @@
     # that's make some weird effects in force layout during animation
     #@data_relations_visibles.sort @sortRelations
     # update canvas if force layout stoped
-    if @force.alpha() < @force.alphaMin()
-      @onTick()
+    @redraw()

 module.exports = VisualizationCanvas


Add self defined timeout function like setTimeout but with inverted arguments.

--- app/coffee/utils.coffee
+++ app/coffee/utils.coffee
@@ -57,9 +57,14 @@

     return result

+
+timeout = (wait, continuation) ->
+    return window.setTimeout(continuation, wait)
+
 taiga = @.taiga
 taiga.bindOnce = bindOnce
 taiga.mixOf = mixOf
 taiga.trim = trim
 taiga.toggleText = toggleText
 taiga.groupBy = groupBy
+taiga.timeout = timeout


Remove only from the AggregateRepository spec

--- spec/aggregate_repository.spec.coffee
+++ spec/aggregate_repository.spec.coffee
@@ -1,6 +1,6 @@
 describe 'AggregateRepository', ->

-  describe.only '#findById', ->
+  describe '#findById', ->

     AggregateRoot = null
     aggregateRepository = null


includes extended payload content

includes extended payload content

--- spec/notice/client/client_spec.coffee
+++ spec/notice/client/client_spec.coffee
@@ -232,8 +232,8 @@
                 Date.now = -> 1
                 @EMITTED = {}
                 @client.event 'test event 1', =>
-                    #console.log @EMITTED.capsule.config
-
+                    #console.log @EMITTED.capsule.payload
+
                     @EMITTED.capsule.payload.should.eql

                         _type: 'event'
@@ -242,6 +242,32 @@
                         createdAt: 1

                     done()
+
+            it 'includes extended payload content', (done) ->
+
+                Date.now = -> 1
+                @EMITTED = {}
+                @client.event 'test event 2',
+                    more: 'stuff'
+                    also:
+                        much:
+                            much:
+                                much: 'more stuff'
+                    =>
+                        # console.log @EMITTED.capsule.payload
+                        @EMITTED.capsule.payload.should.eql
+
+                            _type: 'event'
+                            event: 'test event 2'
+                            routingCode: 'x'
+                            createdAt: 1
+                            more: 'stuff'
+                            also:
+                                much:
+                                    much:
+                                        much: 'more stuff'
+
+                        done()

         context 'on socket event', ->


CSS text measuring bug with disappearing texts fixed

--- impl/css/level0/text.coffee
+++ impl/css/level0/text.coffee
@@ -133,36 +133,37 @@
 		queues = [[], []]
 		queue = queues[currentQueue]

-		updateItem = (item) ->
-			data = item._impl
-			isAutoSize = item._autoWidth or item._autoHeight
-			if (text = data.text)
-				if data.containsHTML
-					if isAutoSize
-						updateHTMLTextSize item
-				else
-					if isAutoSize
-						updatePlainTextSize item
-			else
-				data.contentWidth = 0
-				data.contentHeight = 0
-			return
-
 		updateAll = ->
+			thisQueue = queue
 			currentQueue = ++currentQueue % 2
 			queue = queues[currentQueue]
-
 			pending = false
-			for item in queue
+
+			# set content
+			for item in thisQueue
 				data = item._impl
 				data.contentUpdatePending = false
 				if data.containsHTML
 					data.innerElem.innerHTML = data.text
 				else
 					data.innerElem.textContent = data.text
-			for item in queue
-				updateItem item
-			while item = queue.pop()
+
+			# measure
+			for item in thisQueue
+				data = item._impl
+				isAutoSize = item._autoWidth or item._autoHeight
+				if data.text
+					if isAutoSize
+						if data.containsHTML
+							updateHTMLTextSize item
+						else
+							updatePlainTextSize item
+				else
+					data.contentWidth = 0
+					data.contentHeight = 0
+
+			# set sizes
+			while item = thisQueue.pop()
 				data = item._impl
 				item.contentWidth = data.contentWidth
 				item.contentHeight = data.contentHeight


Ooops, derp

Ooops, derp

--- admin/mturkAdmin.coffee
+++ admin/mturkAdmin.coffee
@@ -451,7 +451,7 @@

 Template.tsAdminCompletedAssignmentsTable.events
   "click .ts-admin-refresh-assignment": ->
-    Meteor.call "ts-admin-refresh-assignment", this._id
+    TurkServer.callWithModal "ts-admin-refresh-assignment", this._id

   "click .ts-admin-reject-assignment": ->
     _asstId = this._id


Place export fix

Place export fix

--- app/models/place.coffee
+++ app/models/place.coffee
@@ -1,8 +1,11 @@
 class Place
+
   @name = 'Place'
-  @shema = {
-    name:    { type: String, required: true },
+  @schema = {
+    name: { type: String, required: true },
     discription: { type: String},
-    coord: { type: [Number], index: '2d', required: true }
+    coord: { type: [Number], index: '2d', required: true },
     image: { type: String }
   }
+
+module.exports = Place


Remove console

Remove console

--- apps/blog/routes.coffee
+++ apps/blog/routes.coffee
@@ -7,7 +7,6 @@
 Posts = require '../../collections/posts.coffee'

 @index = (req, res, next) ->
-  console.log('truncate', truncate)
   posts = new Posts
   posts.fetch
     error: next


removing calls to undefined functions, hopefully we will auto reload the package in the future

--- lib/function-name-status-bar.coffee
+++ lib/function-name-status-bar.coffee
@@ -19,7 +19,7 @@
     # @phpFunctionNameView.destroy()

   serialize: ->
-    phpFunctionNameViewState: @phpFunctionNameView.serialize()
+    # phpFunctionNameViewState: @phpFunctionNameView.serialize()

   getCurrentFunctionName: ->


Add large one-way signs.

Add large one-way signs.

--- dev/us.cson
+++ dev/us.cson
@@ -659,6 +659,30 @@
     { type: 'content-4', color: 'black',  content: 'WAY', transform: 'scale(.5,.6) translate(15%,-25%)' }
   ]

+regulatory_one_way_large_left:
+  category: 'regulatory'
+  name: 'one way large left'
+  elements: [
+    { type: 'square-rounded', color: 'white', transform: 'scale(.7,1)' }
+    { type: 'square-rounded', color: 'black', transform: 'scale(.7,1) scale(.95)' }
+    { type: 'square-rounded', color: 'white', transform: 'scale(.7,1) scale(.90)' }
+    { type: 'content-4', color: 'black',  content: 'ONE', transform: 'scale(1,1.2) translate(0,-90%)' }
+    { type: 'content-4', color: 'black',  content: 'WAY', transform: 'scale(1,1.2)' }
+    { type: 'US-arrow-oneway', color: 'black', transform: 'scale(.5) translate(0,45%) rotate(-90deg)' }
+  ]
+
+regulatory_one_way_large_right:
+  category: 'regulatory'
+  name: 'one way large right'
+  elements: [
+    { type: 'square-rounded', color: 'white', transform: 'scale(.7,1)' }
+    { type: 'square-rounded', color: 'black', transform: 'scale(.7,1) scale(.95)' }
+    { type: 'square-rounded', color: 'white', transform: 'scale(.7,1) scale(.90)' }
+    { type: 'content-4', color: 'black',  content: 'ONE', transform: 'scale(1,1.2) translate(0,-90%)' }
+    { type: 'content-4', color: 'black',  content: 'WAY', transform: 'scale(1,1.2)' }
+    { type: 'US-arrow-oneway', color: 'black', transform: 'scale(.5) translate(0,45%) rotate(90deg)' }
+  ]
+
 warning_single_2_reverse_curve:
   category: 'warning'
   name: 'single 2 reverse curve'


client(theme view): make sure we re-render after a hide.

--- src/awraamba/coffee/views.coffee
+++ src/awraamba/coffee/views.coffee
@@ -360,6 +360,10 @@
         collection: @rendered_threads
       # Pause and play on before hide and after show.
       @bind 'beforehide', =>
+        # XXX rewind on hide seems to feel more natural.  Need to silently
+        # set the model to 0 to make sure links to the current pos work.
+        attrs = value: 0
+        @model.set attrs, silent: true
         @player.currentTime 0
         @player.pause()
       @bind 'aftershow', => @player.play()


Do not create an empty note when search panel query is empty

--- lib/notational-velocity-view.coffee
+++ lib/notational-velocity-view.coffee
@@ -89,9 +89,10 @@
       @cancel()
     else
       sanitizedQuery = @getFilterQuery().replace(/\s+$/, '')
-      filePath = path.join(@rootDirectory, sanitizedQuery + '.md')
-      fs.writeFileSync(filePath, '')
-      atom.workspace.open(filePath)
+      if sanitizedQuery.length > 0
+        filePath = path.join(@rootDirectory, sanitizedQuery + '.md')
+        fs.writeFileSync(filePath, '')
+        atom.workspace.open(filePath)
       @cancel()

   destroy: ->


Add auto-complete in the search panel

Add auto-complete in the search panel

- Also delete non-initial partial title matching feature, since it conflicts with creating a new document when there is no match.
- Fix #5.

--- lib/notational-velocity-view.coffee
+++ lib/notational-velocity-view.coffee
@@ -9,26 +9,39 @@
     super
     @addClass('notational-velocity from-top overlay')
     @loadData()
+    @prevFilterQuery = ''
+    @prevCursorPosition = 0

   selectItem: (filterQuery) ->
     if filterQuery.length == 0
+      @prevCursorPosition = 0
       return null

     titlePatterns = [
       ///^#{filterQuery}$///i,
       ///^#{filterQuery}///i,
-      ///(\s|\\|\/)#{filterQuery}///i,
     ]

+    titleItem = null
     for titlePattern in titlePatterns
       titleItems = @items
         .filter (x) -> x.title.match(titlePattern) != null
         .sort (x, y) -> if x.modified.getTime() <= y.modified.getTime() then 1 else -1
       titleItem = if titleItems.length > 0 then titleItems[0] else null
       if titleItem != null
-        return titleItem
+        break

-    return null
+    # If title item is not null, auto-fill the search panel.
+    # But we don't want to fill it when deleting.
+    editor = @filterEditorView.model
+    currCursorPosition = editor.getCursorBufferPosition().column
+    if titleItem != null && @prevCursorPosition < currCursorPosition
+      @prevFilterQuery = titleItem.title
+      editor.setText(titleItem.title)
+      editor.selectLeft(titleItem.title.length - filterQuery.length)
+    @prevCursorPosition = currCursorPosition
+
+    return titleItem

   filter: (filterQuery) ->
     if filterQuery.length == 0
@@ -155,3 +168,10 @@

     else
       @setError(@getEmptyMessage(@items.length, filteredItems.length))
+
+  schedulePopulateList: ->
+    # We can skip it when we are just moving the position of the cursor.
+    currFilterQuery = @getFilterQuery()
+    if @prevFilterQuery != currFilterQuery
+      super
+    @prevFilterQuery = currFilterQuery


webapp/latex: mask more extensions and also support derived names without a dot

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -41,11 +41,11 @@
 project_file = require('project_file')
 wrapped_editors = require('editor_react_wrapper')

-masked_file_exts =
+MASKED_FILE_EXTENSIONS =
     'py'   : ['pyc']
     'java' : ['class']
     'cs'   : ['exe']
-    'tex'  : 'aux bbl blg fdb_latexmk fls glo idx ilg ind lof log nav out snm synctex.gz toc xyc'.split(' ')
+    'tex'  : 'aux bbl blg fdb_latexmk fls glo idx ilg ind lof log nav out snm synctex.gz toc xyc synctex.gz(busy) sagetex.sage sagetex.sout sagetex.scmd sagetex.sage.py NODOT-concordance.tex'.split(' ')

 BAD_FILENAME_CHARACTERS       = '\\'
 BAD_LATEX_FILENAME_CHARACTERS = '\'"()"~%'
@@ -1474,8 +1474,13 @@
             # mask compiled files, e.g. mask 'foo.class' when 'foo.java' exists
             ext = misc.filename_extension(filename).toLowerCase()
             basename = filename[0...filename.length - ext.length]
-            for mask_ext in masked_file_exts[ext] ? [] # check each possible compiled extension
-                filename_map["#{basename}#{mask_ext}"]?.mask = true
+            for mask_ext in MASKED_FILE_EXTENSIONS[ext] ? [] # check each possible compiled extension
+                if misc.startswith(mask_ext, 'NODOT')
+                    bn = basename[... -1]  # exclude the trailing dot
+                    mask_ext = mask_ext['NODOT'.length ...]
+                else
+                    bn = basename
+                filename_map["#{bn}#{mask_ext}"]?.mask = true

     _compute_snapshot_display_names: (listing) ->
         for item in listing