coffeescript fixes

coffeescript fixes

--- common/static/coffee/src/discussion/utils.coffee
+++ common/static/coffee/src/discussion/utils.coffee
@@ -17,6 +17,9 @@

   @loadRoles: (roles)->
     @roleIds = roles
+
+  @loadFlagModerator: (what)->
+    @isFlagModerator = (what=="True")

   @loadRolesFromContainer: ->
     @loadRoles($("#discussion-container").data("roles"))
@@ -50,7 +53,7 @@
       delete_thread           : "/courses/#{$$course_id}/discussion/threads/#{param}/delete"
       upvote_thread           : "/courses/#{$$course_id}/discussion/threads/#{param}/upvote"
       downvote_thread         : "/courses/#{$$course_id}/discussion/threads/#{param}/downvote"
-      pin_thread              : "/courses/#{$$course_id}/discussion/threads/#{param}/pin"
+      pin_thread              : "/courses/#{$$course_id}/discussion/threads/#{param}/pin"
       un_pin_thread           : "/courses/#{$$course_id}/discussion/threads/#{param}/unpin"
       undo_vote_for_thread    : "/courses/#{$$course_id}/discussion/threads/#{param}/unvote"
       follow_thread           : "/courses/#{$$course_id}/discussion/threads/#{param}/follow"


Finished frequency range with no time selections automatically gets a 100% time selection.

--- scripts/controllers/FrequencySelector.coffee
+++ scripts/controllers/FrequencySelector.coffee
@@ -18,7 +18,7 @@
 		'mousedown': 'onMouseDown'
 		'mousemove': 'onMouseMove'
 		'mouseup': 'onMouseUp'
-		'mousedown .times': 'addTimeRange'
+		'mousedown .times': 'onTimesMouseDown'
 		'click .delete': 'onDeleteClick'

 	elements:
@@ -92,24 +92,30 @@
 		workflowTarget = if highHeight > lowHeight then @highCover else @lowCover
 		@workflowContainer.appendTo workflowTarget

-	addTimeRange: (e) =>
+	onTimesMouseDown: (e) =>
 		e.preventDefault()
-
 		x = (e.pageX - @timesContainer.offset().left) / @timesContainer.width()

-		@timeRange = new TimeSelector
+		@addTimeRange x - 0.005, x + 0.005, e
+
+	addTimeRange: (start, end, mouseDown) =>
+		timeRange = new TimeSelector
 			range: @range.timeRanges().create
-				start: x - 0.005
-				end: x + 0.005
-			mouseDown: e
+				start: start
+				end: end
+			mouseDown: mouseDown

-		@timeRange.bind 'select', @select
-		@timeRange.el.appendTo @timesContainer
+		timeRange.bind 'select', @select
+		timeRange.el.appendTo @timesContainer

 	select: =>
 		@el.addClass 'active'

 	deselect: =>
+		# If there are no time range selections,
+		# assume the whole thing should be selected.
+		if @range.timeRanges().all().length is 0
+			@addTimeRange 0, 1
 		@el.removeClass 'active'

 	onDeleteClick: (e) =>


progress on #32

progress on #32

--- test/files/msg_roundtrip.iced
+++ test/files/msg_roundtrip.iced
@@ -160,14 +160,20 @@

 #===============================================================

-exports.generate_clear_sign = (T,cb) ->
+# Also test various new-line scenarios.
+exports.clear_sign_1 = (T,cb) -> clear_sign data.msg, T, cb
+exports.clear_sign_2 = (T,cb) -> clear_sign "foo\nbar", T, cb
+exports.clear_sign_3 = (T,cb) -> clear_sign "foo\nbar\n\n\n", T, cb
+exports.clear_sign_4 = (T,cb) -> clear_sign "foo", T, cb
+exports.clear_sign_5 = (T,cb) -> clear_sign "foo\n\n\n\nbar", T, cb
+
+clear_sign = (msg, T,cb) ->
   key_id = new Buffer data.keys.ids[1], 'hex'
   flags = C.openpgp.key_flags.sign_data
   await ring.find_best_key { key_id, flags }, defer err, signing_key
   T.no_error err
-  msg = new Buffer data.msg, 'utf8'
+  msg = new Buffer msg, 'utf8'
   await clearsign.sign { signing_key, msg }, defer err, outmsg
-  console.log outmsg
   T.no_error err
   await do_message { keyfetch : ring, armored : outmsg }, defer err, literals
   T.no_error err


Fix slow searches by filtering on the front end instead of back end (#430)

--- app/views/editor/campaign/CampaignEditorView.coffee
+++ app/views/editor/campaign/CampaignEditorView.coffee
@@ -251,7 +251,7 @@
       nodeClasses:
         levels: LevelsNode
         level: LevelNode
-        nextLevel: NextLevelNode
+        nextLevel: NextLevelNode
         campaigns: CampaignsNode
         campaign: CampaignNode
         achievement: AchievementNode
@@ -351,33 +351,34 @@
 class LevelsNode extends TreemaObjectNode
   valueClass: 'treema-levels'
   @levels: {}
+  @mapped: []
   ordered: true

-  buildValueForDisplay: (valEl, data) ->
-    @buildValueForDisplaySimply valEl, ''+_.size(data)
-
-  childPropertiesAvailable: -> @childSource
-
-  childSource: (req, res) =>
+  constructor: (args...) ->
+    super(args...)
     s = new Backbone.Collection([], {model:Level})
     s.url = '/db/level'
     s.url += '?archived=false'
-    s.fetch({data: {term:req.term, project: Campaign.denormalizedLevelProperties.join(',')}})
+    s.fetch({data: {project: Campaign.denormalizedLevelProperties.join(',')}})
     s.once 'sync', (collection) =>
       for level in collection.models
         LevelsNode.levels[level.get('original')] = level
         @settings.supermodel.registerModel level
-      mapped = ({label: r.get('name'), value: r.get('original')} for r in collection.models)
-
-      # Sort the results. Prioritize names that start with the search term, then contain the search term.
-      lowerPriority = _.clone(mapped)
-      lowerTerm = req.term.toLowerCase()
-      startsWithTerm = _.filter(lowerPriority, (item) -> _.string.startsWith(item.label.toLowerCase(), lowerTerm))
-      _.pull(lowerPriority, startsWithTerm...)
-      hasTerm = _.filter(lowerPriority, (item) -> _.string.contains(item.label.toLowerCase(), lowerTerm))
-      _.pull(lowerPriority, hasTerm...)
-      sorted = _.flatten([startsWithTerm, hasTerm, lowerPriority])
-      res(sorted)
+      @mapped = ({label: r.get('name'), value: r.get('original')} for r in collection.models)
+
+  buildValueForDisplay: (valEl, data) ->
+    @buildValueForDisplaySimply valEl, ''+_.size(data)
+
+  childPropertiesAvailable: -> @childSource
+
+  childSource: (req, res) =>
+    # Sort the results. Prioritize names that start with the search term, then contain the search term.
+    lowerTerm = req.term.toLowerCase()
+    sorted = _.filter(@mapped, (item) -> _.string.contains(item.label.toLowerCase(), lowerTerm))
+    startsWithTerm = _.filter(sorted, (item) -> _.string.startsWith(item.label.toLowerCase(), lowerTerm))
+    _.pull(sorted, startsWithTerm...)
+    res(_.flatten([startsWithTerm, sorted]))
+

 class LevelNode extends TreemaObjectNode
   valueClass: 'treema-level'
@@ -419,20 +420,29 @@
 class CampaignsNode extends TreemaObjectNode
   valueClass: 'treema-campaigns'
   @campaigns: {}
-
-  buildValueForDisplay: (valEl, data) ->
-    @buildValueForDisplaySimply valEl, ''+_.size(data)
-
-  childPropertiesAvailable: -> @childSource
-
-  childSource: (req, res) =>
+  @mapped = []
+
+  constructor: (args...) ->
+    super(args...)
     s = new Backbone.Collection([], {model:Campaign})
     s.url = '/db/campaign'
-    s.fetch({data: {term:req.term, project: Campaign.denormalizedCampaignProperties}})
+    s.fetch({data: {project: Campaign.denormalizedCampaignProperties}})
     s.once 'sync', (collection) ->
       CampaignsNode.campaigns[campaign.id] = campaign for campaign in collection.models
-      mapped = ({label: r.get('name'), value: r.id} for r in collection.models)
-      res(mapped)
+      @mapped = ({label: r.get('name'), value: r.id} for r in collection.models)
+
+  buildValueForDisplay: (valEl, data) ->
+    @buildValueForDisplaySimply valEl, ''+_.size(data)
+
+  childPropertiesAvailable: -> @childSource
+
+  childSource: (req, res) =>
+    # Sort the results. Prioritize names that start with the search term, then contain the search term.
+    lowerTerm = req.term.toLowerCase()
+    sorted = _.filter(@mapped, (item) -> _.string.contains(item.label.toLowerCase(), lowerTerm))
+    startsWithTerm = _.filter(sorted, (item) -> _.string.startsWith(item.label.toLowerCase(), lowerTerm))
+    _.pull(sorted, startsWithTerm...)
+    res(_.flatten([startsWithTerm, sorted]))

 class CampaignNode extends TreemaObjectNode


fix tests accounts refresh

fix tests accounts refresh

--- server/processes/mailbox_refresh_deep.coffee
+++ server/processes/mailbox_refresh_deep.coffee
@@ -7,6 +7,7 @@
 _ = require 'lodash'
 Message = require '../models/message'
 patchConversation = require '../patchs/conversation'
+ramStore = require '../models/store_account_and_boxes'

 # This process perform the deep refresh of one mailbox
@@ -41,6 +42,9 @@
         @nbStepDone = 0
         @nbOperationDone = 0
         @nbOperationCurrentStep = 1
+
+        if ramStore.getAccount(@mailbox.accountID).isTest()
+            @finished = true

         async.whilst (=> not @finished), @refreshStep, (err) =>
             return done err if err


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -114,6 +114,6 @@
     Point()
   ]
   4.circularPoints (p, i) ->
-    opoints[i]
+    ok Popoints[i]

 module undefined


added different ldap config example

added different ldap config example

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -388,7 +388,7 @@
 	# 	filter: "(uid=:userKey)"
 	# 	failMessage: 'LDAP User Fail'
 	# 	fieldName: 'LDAP User'
-	# 	placeholder: 'LDAP User ID'
+	# 	placeholder: 'email@example.com'
 	# 	emailAtt: 'mail'
 	# 	anonymous: false
 	#	adminDN: 'cn=read-only-admin,dc=example,dc=com'


Use settings instead of hard-coding ports

--- services/web/config/settings.defaults.coffee
+++ services/web/config/settings.defaults.coffee
@@ -91,7 +91,7 @@
 	# running which conflict, or want to run the web process on port 80.
 	internal:
 		web:
-			port: webPort = 3000
+			port: webPort = process.env['WEB_PORT'] or 3000
 			host: process.env['LISTEN_ADDRESS'] or 'localhost'
 		documentupdater:
 			port: docUpdaterPort = 3003
@@ -192,7 +192,7 @@
 	#clsiCookieKey: "clsiserver"

 	# Same, but with http auth credentials.
-	httpAuthSiteUrl: 'http://#{httpAuthUser}:#{httpAuthPass}@localhost:3000'
+	httpAuthSiteUrl: 'http://#{httpAuthUser}:#{httpAuthPass}@#{siteUrl}'

 	maxEntitiesPerProject: 2000


Check in the next evolution of the access

--- library/exposure/access.coffee
+++ library/exposure/access.coffee
@@ -27,7 +27,6 @@
 assert = require "assert"
 asciify = require "asciify"
 connect = require "connect"
-request = require "request"
 logger = require "winston"
 colors = require "colors"
 async = require "async"
@@ -52,27 +51,6 @@
     # mainly is used to exclude or account for abstract classes.
     @abstract yes

-    # A usable hook that gets asynchronously invoked once a new
-    # socket connection is going to be setup during the handshake.
-    # The method gets a set of parameters that maybe be useful to
-    # have by the actual implementation. Please remember thet the
-    # method is asynchronously wired, so be sure to call `next`.
-    handshake: (context, hso, next) -> @dereference hso, next
-
-    # A hook that will be called prior to firing up the processing
-    # of the service. Please refer to this prototype signature for
-    # information on the parameters it accepts. Beware, this hook
-    # is asynchronously wired in, so consult with `async` package.
-    # Please be sure invoke the `next` arg to proceed, if relevant.
-    ignition: (req, response, next) -> @dereference req, next
-
-    # A usable hook that gets asynchronously invoked once a sentence
-    # comes through an opened channel. This happens every time when
-    # a client tries to invoke a server site provider method. This
-    # is a good place to validate if an invocation is legitimate or
-    # not. If you do not invoke `next` then the call won't happen!
-    sentence: (sob, trail..., next) -> @dereference sob, next
-
     # Dereference the potentially existent entity from the session
     # into the supplied container, where the session is residing. It
     # basically retrieves the hibernated entity, ressurects it and
@@ -85,9 +63,10 @@
         delete container[key] if _.has container, key
         @ressurectEntity ?= (xc, xn) -> xn null, xc
         @ressurectEntity content, (error, entity) ->
-            format = (e) -> "ressurection error: #{e}"
-            return callback format error if error
-            assert p = new Object enumerable: yes
+            format = (m) -> "ressurection error: #{m}"
+            masked = format error.message if error
+            return callback Error masked if error
+            p = enumerable: yes, configurable: yes
             p.get = -> return entity or undefined
             Object.defineProperty container, key, p
             assert container[key]?; callback()
@@ -111,3 +90,39 @@
             session.cookie.maxAge = 2628000000 if rme
             session.save => @dereference container, ->
                 return callback undefined, content
+
+    # A hook that will be called prior to firing up the processing
+    # of the service. Please refer to this prototype signature for
+    # information on the parameters it accepts. Beware, this hook
+    # is asynchronously wired in, so consult with `async` package.
+    # Please be sure invoke the `next` arg to proceed, if relevant.
+    ignition: (request, response, next) ->
+        assert _.isString id = @constructor.identify()
+        format = (m) -> "ingition access error: #{m}"
+        logger.debug "Ingition dereferencing at #{id}"
+        success = "Got valid ignition entity at #{id}"
+        try @dereference request, (error, supply) ->
+            succeeded = _.isObject request.entity
+            logger.debug success.green if succeeded
+            return next undefined if _.isEmpty error
+            assert message = error.message or error
+            logger.error format(message).red
+            return next error
+
+    # A usable hook that gets asynchronously invoked once a new
+    # socket connection is going to be setup during the handshake.
+    # The method gets a set of parameters that maybe be useful to
+    # have by the actual implementation. Please remember thet the
+    # method is asynchronously wired, so be sure to call `next`.
+    handshake: (context, handshake, next) ->
+        assert _.isString id = @constructor.identify()
+        format = (m) -> "handshake access error: #{m}"
+        logger.debug "Handshake dereferencing at #{id}"
+        success = "Got valid handshake entity at #{id}"
+        try @dereference handshake, (error, supply) ->
+            succeeded = _.isObject handshake.entity
+            logger.debug success.green if succeeded
+            return next undefined if _.isEmpty error
+            assert message = error.message or error
+            logger.error format(message).red
+            return next error


Upgrade minor details of schema dsl

Upgrade minor details of schema dsl

--- library/membrane/schema.coffee
+++ library/membrane/schema.coffee
@@ -52,21 +52,27 @@
     assert _.isString(context.id = id), noId
     return rematerialize pointer.apply(context)

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
 # A modificator that marks objects as strict (no additional properties).
-module.exports.strict = strict = -> @additionalProperties = no
+module.exports.strict = strict = (object) ->
+    notObject = "applicable only for an object"
+    assert object.type is "object", notObject
+    object.additionalProperties = no; object

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
 # A modificator that marks arrays as containting only unique items.
-module.exports.unique = unique = (a) -> a.uniqueItems = yes; a
+module.exports.unique = unique = (array) ->
+    notArray = "applicable only for an array"
+    assert array.type is "array", notArray
+    array.uniqueItems = yes; return array

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
@@ -77,7 +83,7 @@
     assert _.isObject(o), "internal error, no outer object"
     (o.required ?= []).push k; p.apply @, arguments

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
@@ -96,7 +102,7 @@
     @properties[key] = f key, p for key, p of compiled
     @type = "object"; return this

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
@@ -110,7 +116,7 @@
     @description = description.toString()
     @type = "array"; pointer.apply items; @

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.
@@ -123,7 +129,7 @@
     @description = description.toString()
     @type = "number"; _.extend this, props; @

-# This defines of the the built in pointers. A pointer is a method
+# This defines one of the built in pointers. A pointer is a method
 # defintion that provides an ability to specify (or point to) to a
 # specific kind of data type. Pointers may create simple or complex
 # data type references and definitions. Refer to their implementation.


Better implementation of @awaiting

Better implementation of @awaiting

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -80,13 +80,10 @@
     # object. Effectively, it is the same as creating the autocall
     # that explicitly binds the event using `on` with the context.
     @awaiting: (event, method) ->
-        isRemote = _.isObject method?.remote
-        notFunction = "no function is passed"
-        invalidEvent = "invalid event supplied"
-        method = external method unless isRemote
-        assert _.isFunction(method), notFunction
+        invalidEvent = "an invalid event supplied"
         assert not _.isEmpty(event), invalidEvent
-        assert method.remote.autocall = Object()
+        assert method = @autocall Object(), method
+        assert _.isObject method.remote.autocall
         method.remote.auto = (symbol, key) -> ->
             t = "#{symbol}.on(%s, #{symbol}.#{key})"
             return format t, JSON.stringify event


Minor fmt/order refactoring

Minor fmt/order refactoring

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -120,20 +120,21 @@
     # refer to the implementation for the understanding of what happens.
     Object.defineProperty Object::, "compose", enumerable: no, value:
         (compound, shader=cloner, silent=yes) ->
-            current = this.hierarchy()
-            foreign = compound.hierarchy()
-            identify = compound.identify()
+            assert current = this.hierarchy()
+            assert foreign = compound.hierarchy()
+            assert identify = compound.identify()
             cmp = (e) -> (c) -> c.similarWith e
+            common = (x) -> _.any foreign, cmp x
+            culrpit = (s) -> not _.any commons, cmp s
             present = _.any current, cmp compound
             duplicate = "duplicate #{identify} compound"
             notAbstract = "the #{identify} is not abstract"
+            orphans = "no common base classes in hierarchy"
             assert not present or silent, duplicate
             assert compound.abstract?(), notAbstract
             return undefined if present and silent
-            culrpit = (shape) -> not _.any commons, cmp shape
-            commons = _.filter current, (x) -> _.any foreign, cmp x
-            orphans = "no common base classes in hierarchy"
-            throw new Error orphans if _.isEmpty commons
+            commons = _.filter current, common
+            assert not _.isEmpty(commons), orphans
             differentiated = _.take current, culrpit
             alternative = _.map differentiated, shader
             compound.composition? this, current, foreign


surgery ops to the old coding

surgery ops to the old coding

--- library/nucleus/routing.coffee
+++ library/nucleus/routing.coffee
@@ -37,6 +37,7 @@
 # the supplied host, among other things. The exact matching logic
 # is up to the handlers that implement the corresponding methods.
 # This router just provides the infrastructure and boilerplating.
+# Therefore, every service defines the matching logic on its own.
 module.exports.ServiceRouter = class ServiceRouter extends Archetype

     # Every router has to have a public constructor that accepts


build fix

build fix

--- gulp/electron-build.coffee
+++ gulp/electron-build.coffee
@@ -24,7 +24,8 @@
         version: 'v0.34.3'
         packaging: true
         asar: false
-        platforms: ['darwin-x64' ,'win32-ia32']
+        platforms: ['darwin-x64']
+        # platforms: ['darwin-x64' ,'win32-ia32']
         platformResources:
           darwin:
               CFBundleDisplayName: packageJson.name


use ricty in atom

use ricty in atom

--- linked_files/.atom/config.cson
+++ linked_files/.atom/config.cson
@@ -8,3 +8,4 @@
   'core': {}
   'editor':
     'invisibles': {}
+    'fontFamily': 'Ricty'


console performance improvement

console performance improvement

--- lib/console/view.coffee
+++ lib/console/view.coffee
@@ -1,5 +1,6 @@
 ResizeDetector = require 'element-resize-detector'
-AnsiConverter = require('ansi-to-html')
+AnsiConverter = require 'ansi-to-html'
+{throttle, delay} = require 'underscore-plus'
 converter = new AnsiConverter()

 class ConsoleElement extends HTMLElement
@@ -38,9 +39,8 @@
     @onfocus = =>
       if document.activeElement == this and @model.getInput()
         @focusLast()
-    # start listening now, since @lock doesn't work properly if @items is empty
-    @resizer.listenTo @items, =>
-      @scrollTop = @scrollHeight
+    # scroll down on subtree modifications if last element is visible
+    @resizer.listenTo @items, => @scrollDownIfVisible()()
     for item in @model.items
       @addItem item
     atom.config.observe 'editor.fontFamily', (value) =>
@@ -202,16 +202,13 @@
     @isLoading = l

   # Scrolling
+  scrollDown: -> throttle (=> delay (=> @lastDivider().scrollIntoView()), 20), 100, {leading: false}
+  scrollDownIfVisible: -> throttle (=> if @isVisible @lastCell() then delay (=> @lastDivider().scrollIntoView()), 20), 150, {leading: false}

   lock: (f) ->
-    if @isVisible @lastDivider()
-      # listen to changes in height from subtree modifications
-      @resizer.listenTo @items, =>
-        @scrollTop = @scrollHeight
-      f()
-      @scrollTop = @scrollHeight
-    else
-      @resizer.removeAllListeners @items
-      f()
+    if @isVisible @lastCell()
+      @scrollDown()()
+    f()
+

 module.exports = ConsoleElement = document.registerElement 'ink-console', prototype: ConsoleElement.prototype


Listen for autocomplete:select on Editor

--- src/app/keymaps/autocomplete.coffee
+++ src/app/keymaps/autocomplete.coffee
@@ -1,6 +1,8 @@
 window.keymap.bindKeys '.editor',
   'escape': 'autocomplete:toggle'

+window.keymap.bindKeys '.editor.autocomplete',
+  'enter': 'autocomplete:select'
+
 window.keymap.bindKeys '#autocomplete',
   'escape': 'autocomplete:remove'
-  'enter': 'autocomplete:select'


:green_heart: Fixes broken tests to undefined lineOverdraw setting

--- spec/minimap-editor-view-spec.coffee
+++ spec/minimap-editor-view-spec.coffee
@@ -26,6 +26,7 @@
       minimapView = new MinimapView editorView
       minimapView.attachToPaneView()
       minimapView.height 5
+      minimapView.miniEditorView.lineOverdraw = 10
       minimapView.updatePositions = ->

       minimapEditorView = minimapView.miniEditorView
@@ -157,6 +158,6 @@
           waitsFor -> updateCallback.callCount is 1

         it 'not decorates the rendered line with the specified class', ->
-          minimapEditorView.removeAllLineClasses()
+          minimapEditorView.removeAllLineClasses()

           expect(minimapEditorView.find('some-class').length).toEqual(0)


Remove tests of deprecated pane container behavior

--- spec/pane-container-view-spec.coffee
+++ spec/pane-container-view-spec.coffee
@@ -30,183 +30,6 @@

   afterEach ->
     deserializerDisposable.dispose()
-
-  describe ".getActivePaneView()", ->
-    it "returns the most-recently focused pane", ->
-      focusStealer = $$ -> @div tabindex: -1, "focus stealer"
-      focusStealer.attachToDom()
-      container.attachToDom()
-
-      pane2.focus()
-      expect(container.getFocusedPane()).toBe pane2
-      expect(container.getActivePaneView()).toBe pane2
-
-      focusStealer.focus()
-      expect(container.getFocusedPane()).toBeUndefined()
-      expect(container.getActivePaneView()).toBe pane2
-
-      pane3.focus()
-      expect(container.getFocusedPane()).toBe pane3
-      expect(container.getActivePaneView()).toBe pane3
-
-  describe ".eachPaneView(callback)", ->
-    it "runs the callback with all current and future panes until the subscription is cancelled", ->
-      panes = []
-      subscription = container.eachPaneView (pane) -> panes.push(pane)
-      expect(panes).toEqual [pane1, pane2, pane3]
-
-      panes = []
-      pane4 = pane3.splitRight(pane3.copyActiveItem())
-      expect(panes).toEqual [pane4]
-
-      panes = []
-      subscription.off()
-      pane4.splitDown()
-      expect(panes).toEqual []
-
-  describe "pane-container:active-pane-item-changed", ->
-    [pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
-    beforeEach ->
-      item1a = new TestView('1a')
-      item1b = new TestView('1b')
-      item2a = new TestView('2a')
-      item2b = new TestView('2b')
-      item3a = new TestView('3a')
-
-      container = atom.views.getView(new PaneContainer).__spacePenView
-      pane1 = container.getRoot()
-      pane1.activateItem(item1a)
-      container.attachToDom()
-
-      activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
-      container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
-
-    describe "when there is one pane", ->
-      it "is triggered when a new pane item is added", ->
-        pane1.activateItem(item1b)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
-
-      it "is not triggered when the active pane item is shown again", ->
-        pane1.activateItem(item1a)
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-      it "is triggered when switching to an existing pane item", ->
-        pane1.activateItem(item1b)
-        activeItemChangedHandler.reset()
-
-        pane1.activateItem(item1a)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
-
-      it "is triggered when the active pane item is destroyed", ->
-        pane1.activateItem(item1b)
-        activeItemChangedHandler.reset()
-
-        pane1.destroyItem(item1b)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
-
-      it "is not triggered when an inactive pane item is destroyed", ->
-        pane1.activateItem(item1b)
-        activeItemChangedHandler.reset()
-
-        pane1.destroyItem(item1a)
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-      it "is triggered when all pane items are destroyed", ->
-        pane1.destroyItem(item1a)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
-
-    describe "when there are two panes", ->
-      [pane2] = []
-
-      beforeEach ->
-        pane2 = pane1.splitLeft(item2a)
-        activeItemChangedHandler.reset()
-
-      it "is triggered when a new pane item is added to the active pane", ->
-        pane2.activateItem(item2b)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
-
-      it "is not triggered when a new pane item is added to an inactive pane", ->
-        pane1.activateItem(item1b)
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-      it "is triggered when the active pane's active item is destroyed", ->
-        pane2.activateItem(item2b)
-        activeItemChangedHandler.reset()
-
-        pane2.destroyItem(item2b)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
-
-      it "is not triggered when an inactive pane's active item is destroyed", ->
-        pane1.activateItem(item1b)
-        activeItemChangedHandler.reset()
-
-        pane1.destroyItem(item1b)
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-      it "is triggered when the active pane is destroyed", ->
-        pane2.remove()
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
-
-      it "is not triggered when an inactive pane is destroyed", ->
-        pane1.remove()
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-      it "is triggered when the active pane is changed", ->
-        pane1.activate()
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
-
-    describe "when there are multiple panes", ->
-      beforeEach ->
-        pane2 = pane1.splitRight(item2a)
-        activeItemChangedHandler.reset()
-
-      it "is triggered when a new pane is added", ->
-        pane2.splitDown(item3a)
-        expect(activeItemChangedHandler.callCount).toBe 1
-        expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
-
-      it "is not triggered when an inactive pane is destroyed", ->
-        pane3 = pane2.splitDown(item3a)
-        activeItemChangedHandler.reset()
-
-        pane1.remove()
-        pane2.remove()
-        expect(activeItemChangedHandler).not.toHaveBeenCalled()
-
-  describe ".focusNextPaneView()", ->
-    it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
-      container.attachToDom()
-      container.focusNextPaneView()
-      expect(pane1.activeItem).toMatchSelector ':focus'
-      container.focusNextPaneView()
-      expect(pane2.activeItem).toMatchSelector ':focus'
-      container.focusNextPaneView()
-      expect(pane3.activeItem).toMatchSelector ':focus'
-      container.focusNextPaneView()
-      expect(pane1.activeItem).toMatchSelector ':focus'
-
-  describe ".focusPreviousPaneView()", ->
-    it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
-      container.attachToDom()
-      container.getPaneViews()[0].focus() # activate first pane
-
-      container.focusPreviousPaneView()
-      expect(pane3.activeItem).toMatchSelector ':focus'
-      container.focusPreviousPaneView()
-      expect(pane2.activeItem).toMatchSelector ':focus'
-      container.focusPreviousPaneView()
-      expect(pane1.activeItem).toMatchSelector ':focus'
-      container.focusPreviousPaneView()
-      expect(pane3.activeItem).toMatchSelector ':focus'

   describe "changing focus directionally between panes", ->
     [pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []


Set default path on both files and folders

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -496,7 +496,7 @@
       properties: properties.concat(['multiSelections', 'createDirectory'])
       title: 'Open'

-    if process.platform is 'linux' and type is 'file'
+    if process.platform is 'linux'
       if projectPath = @lastFocusedWindow?.projectPath
         openOptions.defaultPath = projectPath


More chaining.

More chaining.

--- src/coffee/utils/order-utils.coffee
+++ src/coffee/utils/order-utils.coffee
@@ -35,39 +35,36 @@
   @return list with actions
   ###
   actionsMapDeliveries: (diff, old_obj) ->
-    actions = []
-    deliveriesDiffs = diff.shippingInfo.deliveries if diff.shippingInfo
-    if deliveriesDiffs
-      # iterate over returnInfo instances
-      _.each _.keys(deliveriesDiffs), (deliveryIndex) ->
-        if deliveryIndex isnt '_t'
-          deliveryDiff = deliveriesDiffs[deliveryIndex]
-          if _.isArray deliveryDiff
-            # delivery was added
-            delivery = _.last deliveryDiff
-            action =
-              action: 'addDelivery'
-            _.each _.keys(delivery), (key) ->
-              action[key] = delivery[key]
-            actions.push action

-          else
-            # iterate over parcel instances
-            _.each _.keys(deliveryDiff.parcels), (parcelIndex) ->
-              if parcelIndex  isnt '_t'
-                parcelDiff = deliveryDiff.parcels[parcelIndex]
-
-                if _.isArray parcelDiff
-                  # delivery was added
-                  parcel = _.last parcelDiff
-                  action =
-                    action: 'addParcelToDelivery'
-                    deliveryId: old_obj.shippingInfo.deliveries[deliveryIndex].id
-                  _.each _.keys(parcel), (key) ->
-                    action[key] = parcel[key]
-                  actions.push action
-    actions
-
+    return [] unless _.has(diff, 'shippingInfo') and _.has(diff.shippingInfo, 'deliveries')
+    # iterate over returnInfo instances
+    actions = _.chain diff.shippingInfo.deliveries
+      .filter (item, key) -> key isnt '_t'
+      .map (deliveryDiff, deliveryIndex) ->
+        if _.isArray deliveryDiff
+          # delivery was added
+          delivery = _.last deliveryDiff
+          action =
+            action: 'addDelivery'
+          _.each _.keys(delivery), (key) ->
+            action[key] = delivery[key]
+          action
+        else
+          # iterate over parcel instances
+          _.chain deliveryDiff.parcels
+            .filter (item, key) -> key isnt '_t' and  _.isArray item
+            .map (parcelDiff) ->
+              # delivery was added
+              parcel = _.last parcelDiff
+              action =
+                action: 'addParcelToDelivery'
+                deliveryId: old_obj.shippingInfo.deliveries[deliveryIndex].id
+              _.each parcel, (item, key) ->
+                action[key] = item
+              action
+            .value()
+      .value()
+    _.flatten actions

@@ -78,43 +75,43 @@
   @return list with actions
   ###
   actionsMapReturnInfo: (diff, old_obj) ->
-    actions = []
-    returnInfoDeltas = diff['returnInfo']
-    if returnInfoDeltas
-      # iterate over returnInfo instances
-      _.chain returnInfoDeltas
-        .filter (item, key) -> key isnt '_t'
-        .map (returnInfoDelta, returnInfoDeltaKey) ->
-          if _.isArray returnInfoDelta
-            # get last added item
-            returnInfo = _.last returnInfoDelta
-            action =
-              action: 'addReturnInfo'
-            _.each returnInfo, (value, key) ->
-              action[key] = value
-            actions.push action

-            # TODO: split into multiple actions (addReturnInfo + setReturnShipmentState/setReturnPaymentState)
-            #   in case shipmentState/paymentState already transitioned to a non-initial state
-          else
-            returnInfo = returnInfoDelta
-            # iterate over returnItem instances
-            actions = _.chain returnInfo.items
-              .filter (item, key) -> key isnt '_t'
-              .map (item, itemKey) ->
-                # iterate over all returnInfo status actions
-                _.chain actionsListReturnInfoState()
-                  .filter (actionDefinition) -> _.has(item, actionDefinition.key)
-                  .map (actionDefinition) ->
-                    action =
-                      action: actionDefinition.action
-                      returnItemId: old_obj.returnInfo[returnInfoDeltaKey].items[itemKey].id
-                    action[actionDefinition.key] = helper.getDeltaValue item[actionDefinition.key]
-                    action
-                  .value()
-              .value()
-       .value()
+    return [] unless _.has(diff, 'returnInfo')
+    # iterate over returnInfo instances
+    actions = _.chain diff['returnInfo']
+      .filter (item, key) -> key isnt '_t'
+      .map (returnInfoDelta, returnInfoDeltaKey) ->
+        if _.isArray returnInfoDelta
+          # get last added item
+          returnInfo = _.last returnInfoDelta
+          action =
+            action: 'addReturnInfo'
+          _.each returnInfo, (value, key) ->
+            action[key] = value
+          action
+
+          # TODO: split into multiple actions (addReturnInfo + setReturnShipmentState/setReturnPaymentState)
+          #   in case shipmentState/paymentState already transitioned to a non-initial state
+        else
+          returnInfo = returnInfoDelta
+          # iterate over returnItem instances
+          actions = _.chain returnInfo.items
+            .filter (item, key) -> key isnt '_t'
+            .map (item, itemKey) ->
+              # iterate over all returnInfo status actions
+              _.chain actionsListReturnInfoState()
+                .filter (actionDefinition) -> _.has(item, actionDefinition.key)
+                .map (actionDefinition) ->
+                  action =
+                    action: actionDefinition.action
+                    returnItemId: old_obj.returnInfo[returnInfoDeltaKey].items[itemKey].id
+                  action[actionDefinition.key] = helper.getDeltaValue item[actionDefinition.key]
+                  action
+                .value()
+            .value()
+      .value()
     _.flatten actions
+

 ###
 Exports object


ListView: commented out / deleted not yet implemented boxed view features

--- src/components/list/listview.coffee
+++ src/components/list/listview.coffee
@@ -16,9 +16,9 @@
     @items = []
     @boxes = []

-    if @getOptions().boxed
-      @on 'viewAppended', =>
-        @parent.on 'scroll', @bound 'handleScroll'
+    # if @getOptions().boxed
+    #   @on 'viewAppended', =>
+    #     @parent.on 'scroll', @bound 'handleScroll'

   empty: ->
@@ -176,24 +176,11 @@
         break
       @boxes.splice(index, 1)[0].destroy()  if index?

-
     return box

-
-  updateBoxProps: (box, height) ->
-
-    # log @boxes.indexOf(box), height

   # handle vertical and horizontal scrolls separately - SY
   handleScroll:->
-
-    # log box.size for box in @boxes
-    # log @parent.fractionOfHeightBelowFold view : @boxes.first
-
-    # log 'scrollaki'
-    # log @boxes
-
-

   getItemIndex: (item) -> @items.indexOf item


Add middleware for catching validation errors

--- app/middlewares/error.coffee
+++ app/middlewares/error.coffee
@@ -3,6 +3,13 @@
     res.status 404
     res.render 'error/404', url: req.url

-  internalError: (err, req, res, next) ->
+  validationError: (err, req, res, next) ->
+    if err.code == 'E_VALIDATION'
+      req.flash 'error', err.reason
+      res.redirect 'back'
+    else
+      next err
+
+  internalError: (err, req, res) ->
     res.status 500
     res.render 'error/500'


Correction resolveDoublon

Correction resolveDoublon

--- server/migration/resolveDoublonConsommable.coffee
+++ server/migration/resolveDoublonConsommable.coffee
@@ -7,7 +7,7 @@
     FROM "groupeV"
     INNER JOIN groupe ON (groupe_id = groupe.id)
     WHERE actif
-    ORDER BY groupe_id, date
+    ORDER BY groupe_id, lieu_id, date
   """
 .then (consommables) ->
   last = consommables.rows[0]


Fixed the path in migrations.

Fixed the path in migrations.

--- server/migrations/0018-publication-cacheid.coffee
+++ server/migrations/0018-publication-cacheid.coffee
@@ -1,6 +1,6 @@
 getOldFilename = (document) ->
     if document.source is 'arXiv'
-      'pdf' + Storage._path.sep + 'arXiv' + Storage._path.sep + document.foreignId + '.pdf'
+      'pdf' + Storage._path.sep + 'arxiv' + Storage._path.sep + document.foreignId + '.pdf'
     else
       # We use import also as a fallback for any unsupported document source.
       # This allows us to go first backward in migrations and then again forward.


config: reduce number of activities fetced on first load

--- config/main.staging.coffee
+++ config/main.staging.coffee
@@ -138,7 +138,7 @@
     useStaticFileServer: no
     staticFilesBaseUrl: "https://koding.com"
     runtimeOptions:
-      activityFetchCount : 50
+      activityFetchCount : 20
       precompiledApi: yes
       authExchange: authExchange
       github        :


Config: Fixed main-vagrant email port.

Config: Fixed main-vagrant email port.

--- config/main.vagrant.coffee
+++ config/main.vagrant.coffee
@@ -235,7 +235,7 @@
     disconnectTimeout: 3e3
     vhost       : 'kite'
   email         :
-    host        : 'localhost'
+    host        : 'localhost:3020'
     protocol    : 'http:'
     defaultFromAddress: 'hello@koding.com'
   emailWorker   :


TeamLoginTab: show gitlabLogin if the team has gitlab integration

--- client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
@@ -21,7 +21,7 @@
     super options, data

     { mainController } = kd.singletons
-    { group }          = kd.config
+    { group, gitlab } = kd.config

     @header = new MainHeaderView
       cssClass : 'team'
@@ -44,6 +44,9 @@
           @form.button.hideLoader()
           @form.tfcode.show()
           @form.tfcode.setFocus()
+
+    if group.slug is gitlab?.team
+      @form.gitlabLogin.show()

     ['button', 'gitlabButton'].forEach (button) =>
       @form[button].unsetClass 'solid medium green'


Social: added simple handlers for cheking if status constant is valid

--- workers/social/lib/social/models/invitation.coffee
+++ workers/social/lib/social/models/invitation.coffee
@@ -96,6 +96,11 @@
     operation = $set : { status: 'accepted' }
     @update operation, callback

+  # validTypes holds states that can still redeemable
+  validTypes: ['pending']
+
+  isValid:-> @status in @validTypes
+
   # remove deletes an invitation from database
   remove$: permit 'send invitations',
     success: (client, callback) ->
@@ -116,7 +121,6 @@
       options.limit   = Math.min options.limit, 25 # admin can fetch max 25 record
       options.skip    = 0

-      console.log selector, options
       JInvitation.some selector, options, callback

   # search searches database with given query string, adds `starting


Defensive check in sanitized email setter

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -113,7 +113,9 @@
       sanitizedEmail:
         type        : String
         validate    : JName.validateEmail
-        set         : (value) -> emailsanitize value, excludeDots: yes, excludePlus: yes
+        set         : (value) ->
+          return  unless typeof value is 'string'
+          emailsanitize value, excludeDots: yes, excludePlus: yes
       password      : String
       salt          : String
       twofactorkey  : String


analytics: track user registers

analytics: track user registers

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1300,6 +1300,7 @@
         token = jwt.sign { username }, secret, { expiresInMinutes: confirmExpiresInMinutes }

         analytics.identify userId: username, traits: { jwtToken: token, host: publicHostname }
+        analytics.track userId: username, event: 'registered'
     ]

     daisy queue


Social: following trait calls SocialMessage.Follow for notifications

--- workers/social/lib/social/traits/followable.coffee
+++ workers/social/lib/social/traits/followable.coffee
@@ -160,11 +160,8 @@
               followee  : this
               follower  : follower

-            @emit 'FollowHappened',
-              origin    : this
-              actorType : 'follower'
-              follower  : ObjectRef(follower).data
-              group     : group
+            SocialNotification = require '../models/socialapi/notification'
+            SocialNotification.follow client, this, (err) -> warn err  if err

             follower.updateFollowingCount @, action


profile: fix logged out styling

profile: fix logged out styling

--- servers/lib/crawler/staticpages/profile.coffee
+++ servers/lib/crawler/staticpages/profile.coffee
@@ -13,6 +13,7 @@
   <html lang="en">
   <head>
     <title>#{nickname} - Koding</title>
+    <style>body, html {height: 100%}</style>
     #{getGraphMeta()}
   </head>
   <body itemscope itemtype="http://schema.org/WebPage" class="super profile">
@@ -21,6 +22,15 @@
       #{putContent(account, statusUpdates)}
     </div>
     #{analytics()}
+    <script>
+      (function(d) {
+        var config = {
+          kitId: 'rbd0tum',
+          scriptTimeout: 3000
+        },
+        h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='//use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
+      })(document);
+    </script>
   </body>
   </html>
   """


Update impersonation session cookie using helper

--- servers/lib/server/handlers/impersonate.coffee
+++ servers/lib/server/handlers/impersonate.coffee
@@ -1,4 +1,8 @@
 koding = require './../bongo'
+
+{
+  setSessionCookie
+} = require '../helpers'

 module.exports = (req, res) ->
   { JAccount, JSession } = koding.models
@@ -27,6 +31,7 @@
         JSession.remove { clientId }, (err) ->
           console.error 'Could not remove session:', err  if err

-          res.cookie 'clientId', session.clientId, { path : '/'  if session.clientId }
+          setSessionCookie res, session.clientId
+
           res.clearCookie 'realtimeToken'
           res.status(200).send { success: yes }


Fix spec title

Fix spec title

--- spec/autocomplete-manager-integration-spec.coffee
+++ spec/autocomplete-manager-integration-spec.coffee
@@ -78,7 +78,7 @@
             expect(wordElement.textContent).toBe 'method(something)'
             expect(wordElement.querySelector('.snippet-completion').textContent).toBe 'something'

-        it "accepts the ", ->
+        it "accepts the snippet when autocomplete-plus:confirm is triggered", ->
           triggerAutocompletion(editor, true, 'm')

           runs ->


OverlayTrigger becomes Button/Modal on course rename

--- src/components/course-settings/rename-course.cjsx
+++ src/components/course-settings/rename-course.cjsx
@@ -41,6 +41,13 @@

   getInitialState: ->
     course_name: @props.course.name
+    showModal: false
+
+  close: ->
+    @setState({showModal: false})
+
+  open: ->
+    @setState({showModal: true})

   validate: (name) ->
     error = CourseStore.validateCourseName(name, @props.course.name)
@@ -51,7 +58,7 @@
     unless @state.invalid
       CourseActions.save(@props.courseId, course: {name: @state.course_name})
       CourseStore.once 'saved', ->
-        @refs.overlay.hide()
+        @close()

   renderForm: ->
     formClasses = classnames 'modal-body', 'teacher-edit-course-form', 'is-invalid-form': @state?.invalid
@@ -60,8 +67,13 @@

     <BS.Modal
       {...@props}
-      title={'Rename Course'}
+      show={@state.showModal}
+      onHide={@close}
       className='teacher-edit-course-modal'>
+
+      <BS.Modal.Header closeButton>
+        <BS.Modal.Title>Rename Course</BS.Modal.Title>
+      </BS.Modal.Header>

         <RenameCourseField
@@ -86,14 +98,11 @@
     </BS.Modal>

   render: ->
-    <BS.OverlayTrigger
-    rootClose={true}
-    trigger='click'
-    ref='overlay'
-    overlay={@renderForm()}>
-        <BS.Button bsStyle='link' className='edit-course'>
-           Rename Course
-        </BS.Button>
-    </BS.OverlayTrigger>
+
+      <BS.Button onClick={@open} bsStyle='link' className='edit-course'>
+         Rename Course
+      </BS.Button>
+      {@renderForm()}
+

 module.exports = RenameCourse


Update login handler for 0.7.2

Update login handler for 0.7.2

Login hooks are still required to deal with resume properly

--- lib/accounts_mturk.coffee
+++ lib/accounts_mturk.coffee
@@ -83,18 +83,11 @@
     workerId: loginRequest.workerId
     userId: userId

-  stampedToken = Accounts._generateStampedLoginToken();
-
   ###
-    TODO
-    Check how we approach using resume tokens - disable resume and force login each time?
+    TODO Use login handlers and address the issue of resume tokens
     https://github.com/meteor/meteor/issues/1835
   ###

-  Meteor.users.update userId,
-    $push: {'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken) }
-
   return {
-    id: userId,
-    token: stampedToken.token
+    userId: userId,
   }


bug : an update of the tasks is required when a note is open for the second time in the same editor

--- app/CNeditor/editor.coffee
+++ app/CNeditor/editor.coffee
@@ -436,6 +436,7 @@
         return true

+
     ###* -----------------------------------------------------------------------
      * When a line is a task (its div has dataset.type = task) and we don't have
      * the corresponding model of task, we then create this Task.
@@ -456,6 +457,7 @@
         return true

+
     _setTaskToLine : (lineDiv) ->
         # console.log '=== _setTaskToLine'
         id = lineDiv.dataset.id
@@ -463,6 +465,7 @@
             if t.id == id or t.internalId == id
                 lineDiv.task = t
                 t.lineDiv = lineDiv
+                @_updateTaskLine(t)
                 return true

         # if the id stored in the hmtml line is a temporary id and that there is
@@ -484,6 +487,10 @@
                 realtimer.watchOne t
                 @_updateTaskLine(t) #task may have change when note was not open

+            .fail () =>
+                console.log "editor : t.fetch.fail()",t.id
+                @_turneTaskIntoLine(t.lineDiv)
+
             t.on 'change', (t)=>
                 console.log ' editor : change from fetch detected !', t.id
                 console.log t.changedAttributes()
@@ -501,6 +508,7 @@
         btn.addEventListener('click', @_toggleTaskCB)

         return true
+

     _updateTaskLine : (t) ->
@@ -514,6 +522,7 @@
                 newState = 'undone'
             if currentTaskState != newState
                 t.lineDiv.dataset.state = newState
+

     _stackTaskChange : (task,action) ->


implement revert by refetching

implement revert by refetching

--- src/scripts/modules/media/editbar/editbar.coffee
+++ src/scripts/modules/media/editbar/editbar.coffee
@@ -9,7 +9,8 @@
     template: template

     events:
-      'click .save': 'save'
+      'click .save':    'save'
+      'click .revert':  'revert'

     initialize: () ->
       super()
@@ -17,3 +18,8 @@

     save: () ->
       @model.save()
+
+    revert: () ->
+      model = @model # `@model` is cleared when editable is set to false
+      model.set('editable', false)
+      model.fetch()


:fallen_leaf: Forgot to remove old code

:fallen_leaf: Forgot to remove old code

--- app/assets/js/login.coffee
+++ app/assets/js/login.coffee
@@ -1,22 +0,0 @@
-$ ->
-  input =
-    user: $('#textboxUsername')
-    pass: $('#textboxPassword')
-    hash: $('#hiddenUserhash')
-
-  # As we have Javascript, there's no need for HTML5 attribute
-  inp.removeAttr('required') for i, inp of input
-
-  $('form.login').submit (e) ->
-    hash = input.user.val() + input.pass.val()
-
-    # This totally rapes requirements set in registration form
-    # but whatever... it's like a secret easter egg or something
-    return 0 and e.preventDefault() if hash.length < 8
-
-    hash = CryptoJS.SHA512(hash).toString().toUpperCase()
-    hash = CryptoJS.SHA256(hash).toString().toUpperCase()
-    hash = CryptoJS.RIPEMD160(hash).toString().toUpperCase()
-
-    inp.val '' for i, inp of input
-    input.hash.val hash


https fixes

https fixes

--- app/config/settings.coffee
+++ app/config/settings.coffee
@@ -4,15 +4,15 @@
     classifyUrl: 'http://localhost:9295/#/classify'
     talkHost: 'http://localhost:9296'
     analytics: { }
-
+
   developmentRemote:
     apiHost: 'https://dev.zooniverse.org'
     classifyUrl: 'http://zooniverse-demo.s3-website-us-east-1.amazonaws.com/sunspots/#/classify'
     talkHost: 'http://zooniverse-demo.s3-website-us-east-1.amazonaws.com/sunspots_talk'
     analytics: { }
-
+
   production:
-    apiHost: 'http://www.sunspotter.org'
+    apiHost: 'https://www.sunspotter.org'
     apiPath: '/_ouroboros_api/proxy'
     classifyUrl: 'http://www.sunspotter.org/pre-launch/#/classify'
     talkHost: 'http://talk.sunspotter.org'


profiling utitlity function

profiling utitlity function

--- subtree/bokehjs/static/coffee/common/plot.coffee
+++ subtree/bokehjs/static/coffee/common/plot.coffee
@@ -103,11 +103,13 @@

   initialize: (options) ->
     super(_.defaults(options, @default_options))
+
     #@throttled_render = _.throttle(@render, 15)
     @throttled_render = throttleAnimation(@render, 15)
     @throttled_render_canvas = throttleAnimation(@render_canvas, 15)
     @plotnum = window.plot_count
     window.plot_count+=1
+    window['plot'+@plotnum]=@
     @title_props = new text_properties(@, {}, 'title_')

     @view_state = new ViewState({
@@ -492,3 +494,15 @@
 exports.PlotView = PlotView
 exports.PNGView = PNGView
 exports.plots = new Plots
+window.exercise_panning = ->
+  start_time = new Date()
+  _.delay((->
+    plot10.update_range({sdx:1, sdy:2, xr:{end:10, start:0}, yr:{start:-3, end:70}})),
+    200)
+  _.delay((->
+    plot10.update_range({sdx:1, sdy:2, xr:{end:10, start:0}, yr:{start:-3, end:30}})), 400)
+  _.delay((->
+    plot10.update_range({sdx:1, sdy:2, xr:{end:20, start:0}, yr:{start:-3, end:30}})), 600)
+  end_time = new Date()
+  console.log(end_time - start_time, "ms")
+


Do not update notify if already notified.

--- app/assets/javascripts/app/lib/app_post/task_manager.coffee
+++ app/assets/javascripts/app/lib/app_post/task_manager.coffee
@@ -335,6 +335,7 @@
     task = @get(key)
     if !task
       throw "No such task with '#{key}' to notify"
+    return if task.notify
     task.notify = true
     @taskUpdate(task)


Do not send a parameter with the current time when requesting user data via Ajax.

It was keeping clients from leveraging HTTP caching.

--- app/assets/javascripts/ng-services/ng-UserDataSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-UserDataSvc.js.coffee
@@ -10,8 +10,7 @@
   # Load user configuration data via AJAX into the root scope
   #---------------------------------------------
   load_data: ->
-    now = new Date()
-    $http.get("/api/user_data.json?time=#{now.getTime()}")
+    $http.get("/api/user_data.json")
     .success (data)->
       $rootScope.subscribed_feeds_count = data["subscribed_feeds_count"]
     .error (data, status)->


Added comment

Added comment

--- core/app/backbone/views/facts/top_fact_share_buttons_view.coffee
+++ core/app/backbone/views/facts/top_fact_share_buttons_view.coffee
@@ -26,5 +26,5 @@
       selector: selector
       tooltipViewFactory: =>
         view = contentViewConstructor()
-        @listenTo view, 'success', @render
+        @listenTo view, 'success', @render # closes popovers
         view


unless instead of if !

unless instead of if !

--- src/scripts/archer.coffee
+++ src/scripts/archer.coffee
@@ -40,7 +40,7 @@
       msg.send dialog

   # Make it possible to turn off a few of the more NSFW ones
-  if !process.env.HUBOT_ARCHER_SFW
+  unless process.env.HUBOT_ARCHER_SFW

     robot.hear /^benoit/i, (msg) ->
       msg.send "balls"


[specs] auth.refreshUserData returns a promise now

--- app/utils/test-auth.coffee
+++ app/utils/test-auth.coffee
@@ -48,6 +48,7 @@
     if @get('state') == 'signed-in'
       'a-token'

-  refreshUserData: (->)
+  refreshUserData: ->
+    return Ember.RSVP.Promise.resolve()

 `export default Auth`


Workaround for skipping specific volumes

--- src/js/ogl-browse.coffee
+++ src/js/ogl-browse.coffee
@@ -67,6 +67,8 @@
     repo_url_fragment = _.last(repo.html_url.split('/'))
     if repo_url_fragment.match(ocr_pattern)
       ocr_identifier = repo_url_fragment.replace(ocr_pattern,'').replace(scan_pattern,'')
+      if ocr_identifier == 'ddd'
+        continue
       repo_link = $('<a>').attr('href',repo.html_url).attr('target','_blank').text(repo_url_fragment)
       repo_li_id = repo_url_fragment.replace(/\./g,'_')
       repo_li = $('<li>').attr('id',repo_li_id).attr('class','list-group-item')


Fix -oo for the first key

Fix -oo for the first key

--- admin/static/coffee/util.coffee
+++ admin/static/coffee/util.coffee
@@ -315,7 +315,7 @@
     if s is null
         return "+∞"
     else if s is ""
-        return "+∞"
+        return "-∞"
     else if typeof s is "string" and s[0]? and s[0] is 'N'
         s = s.slice(s.indexOf("%23")+3)
         if _.isNaN(parseFloat(s)) is false


(fix) Send correct disconnect signal.

(fix) Send correct disconnect signal.

--- src/test_processor.coffee
+++ src/test_processor.coffee
@@ -53,7 +53,7 @@
         fullCode = deactivateConsoleAPI + "\n" + flavoredCode

         sandbox = runner.run fullCode, customApi
-        config.testProcessor?.init?(sandox)
+        config.testProcessor?.init?(sandbox)
         return sandbox

       return {


track cleanup

track cleanup

--- src/layout/header.coffee
+++ src/layout/header.coffee
@@ -2,18 +2,16 @@
 {make} = require '../render/make'

 exports.header = (level_dict) ->
-  report = (event, key) ->
+  track = (event, key) ->
     dict = { _LABEL: key, _EXPORTS: [event], name: (world) -> world.get(key) }
     dict[key] = 0
-    dict[event] = (world, args) ->
-      console.log "log event #{event}", world.get(key)
-      world.put key, world.get(key) + 1
+    dict[event] = (world, args) -> world.put key, world.get(key) + 1
     dict
   metric = (event) ->
     key = "#{event}s"
     make.columns key, [
       _.capitalize "#{key}: "
-      report(event, key)
+      track(event, key)
       "/"
       "#{level_dict.goal[key]}"
     ]


Document batch

Document batch

--- src/leveldb/batch.coffee
+++ src/leveldb/batch.coffee
@@ -1,11 +1,73 @@
 binding = require '../../build/leveldb.node'
+
+###
+
+    A batch holds a sequence of put/delete operations to atomically write to
+    the database.
+
+    Usage:
+
+        var leveldb = require('leveldb');
+
+        var db = leveldb.openSync('/tmp/test.db')
+          , batch = db.batch();
+
+        batch
+          .put('foo', 'bar')
+          .del('baz')
+          .put('pass', 'xyzzy')
+          .writeSync();
+
+        var db2 = leveldb.openSync('/tmp/test2.db')
+          , batch2 = new leveldb.Batch;
+
+        batch2
+          .put('foo', 'coconut')
+          .del('pass');
+
+        db.writeSync(batch2);
+        db2.writeSync(batch2);
+
+        // batch2 has not been cleared to allow for reuse.
+        batch2.clear();
+
+        // works because the batch is cleared after committing when using
+        // batch.commit() or batch.commitSync()
+        batch
+          .put('hello', 'world')
+          .put('goodbye', 'world');
+
+        db.writeSync(batch);
+        db2.writeSync(batch);
+
+###

 exports.Batch = class Batch

   noop = ->

+
+  ###
+
+      Constructor.
+
+      @param {Handle} [handle] Pass a database handle to use with
+        `batch.write()` or `batch.writeSync()`.
+
+  ###
+
   constructor: (@handle) ->
     @self = new binding.Batch
+
+
+  ###
+
+      Add a put operation to the batch.
+
+      @param {String|Buffer} key The key to put.
+      @param {String|Buffer} val The value to put.
+
+  ###

   put: (key, val) ->
     key = new Buffer key unless Buffer.isBuffer key
@@ -13,10 +75,32 @@
     @self.put key, val
     @

+
+  ###
+
+      Add a delete operation to the batch.
+
+      @param {String|Buffer} key The key to delete.
+
+  ###
+
   del: (key, val) ->
     key = new Buffer key unless Buffer.isBuffer key
     @self.del key
     @
+
+
+  ###
+
+      Commit the batch operations to disk.
+
+      @param {Object} [options] Optional options. See also the
+        `leveldb/options.h` header file.
+        @param {Boolean} [options.sync] Set to truthy value to flush write
+          to disk before returning.
+      @param {Function} [callback] Optional callback.
+
+  ###

   write: (options, callback) ->
     throw new Error 'No handle' unless @handle
@@ -30,11 +114,25 @@
       callback? err
     @

+
+  ###
+
+      Commit the batch operations to disk synchronously. See `Batch.write()`.
+
+  ###
+
   writeSync: (options) ->
     throw new Error 'No handle' unless @handle
     @handle.write @self, options
     @self.clear()
     @

+
+  ###
+
+      Reset this batch instance by removing all pending operations.
+
+  ###
+
   clear: ->
     @self.clear()


Extract ExpectsMethod.build()

Extract ExpectsMethod.build()

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -23,9 +23,9 @@
     @check_mock_usage(args)
     @parse_args(args)
     @check_expects_method_name()
-    @install_expects_method(@_mock_methods)
     @create_mock_objects()
     @create_empty_mock_methods_list()
+    @install_expects_method(@_mock_methods)   # TODO: remove arg

   run_test_function: ->
     @_test_function.apply(null, @_mock_objects)
@@ -77,13 +77,18 @@
     @_mock_methods = new MockMethodList()

   build_expects_method: ->
-    that = @
+    ExpectsMethod.build(@_expects_method_name, @_mock_methods)
+
+
+class ExpectsMethod
+
+  @build: (expects_method_name, mock_methods)->
     (method_name) ->
       fail(messages.ExpectsUsage) unless method_name?
       fail(messages.ExpectsUsage) if arguments.length != 1
       fail(messages.NotAnExistingMethod, method_name) unless is_mock_object(@) or has_method(@, method_name)
       fail(messages.PreExistingProperty, method_name) if has_property(@, method_name)
-      fail(messages.ReservedMethodName, method_name) if method_name == that._expects_method_name        # TODO: extract is_reserved_method_name()
+      fail(messages.ReservedMethodName, method_name) if method_name == expects_method_name        # TODO: extract is_reserved_method_name()
       expectations = @[method_name]?.expectations
       if not expectations
         mock_method = (args...) ->
@@ -105,9 +110,9 @@
         mock_method.find_errors = ->
           @expectations.find_errors(@method_name)
         @[method_name] = mock_method
-        that._mock_methods.add(mock_method)
+        mock_methods.add(mock_method)
       expectations.create_expectation()
-
+

 class MockObject


Introduce MockFunction for code grouping and scoping.

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -3,53 +3,55 @@

-mock = (args...) ->
-  fail(messages.MockUsage) if args.length < 1 or 2 < args.length
-  fail(messages.MockUsage) if args.length == 1 and not is_function(args[0])
-  fail(messages.MockUsage) if args.length == 2 and not is_function(args[1])
-  fail(messages.MockBadUsage) if args.length == 2 and not has_property(args[0], "expects_method_name") and not has_property(args[0], "mock_count")
-  test_function = ( if args.length == 1 then args[0] else args[1] )
-  expects_method_name = ( if args.length == 2 then args[0].expects_method_name ) ? "expects"    # TODO: use merge idiom?  what happens if expects_method_name is not a valid method name?
-  mock_count = ( if args.length == 2 then args[0].mock_count ) ? 5                              # TODO: what happens if mock_count is not a number?
-  fail(messages.ExpectsMethodAlreadyExists, expects_method_name) if Object.prototype[expects_method_name]?
-  mock_objects = ( new MockObject() for i in [1..mock_count] )
-  mock_methods = new MockMethodList()
-  Object.prototype[expects_method_name] = (method_name) ->
-    fail(messages.ExpectsUsage) unless method_name?
-    fail(messages.ExpectsUsage) if arguments.length != 1
-    fail(messages.NotAnExistingMethod, method_name) unless is_mock_object(@) or has_method(@, method_name)
-    fail(messages.PreExistingProperty, method_name) if has_property(@, method_name)
-    fail(messages.ReservedMethodName, method_name) if method_name == expects_method_name        # TODO: extract is_reserved_method_name()
-    expectations = @[method_name]?.expectations
-    if not expectations
-      mock_method = (args...) ->
-        expectations.check_for_duplicate_expectations(method_name)                              # TODO: explain why we do this here
-        expectation = expectations.find_expectation(args...)
-        fail(messages.UnknownExpectation, method_name, args) unless expectation
-        expectation._called = yes
-        throw expectation._throws if expectation._throws
-        expectation._returns
-      mock_method.object = @
-      mock_method.method_name = method_name
-      mock_method.original_method = @[method_name]
-      mock_method.expectations = expectations = new ExpectationList()
-      mock_method.restore_original_method = ->
-        if @original_method?
-          @object[@method_name] = @original_method
-        else
-          delete @object[@method_name]
-      mock_method.find_errors = ->
-        @expectations.find_errors(@method_name)
-      @[method_name] = mock_method
-      mock_methods.add(mock_method)
-    expectations.create_expectation()
-  try
-    test_function.apply(null, mock_objects)
-    errors = mock_methods.find_errors()
-    fail( errors.join("\n") + "\n" ) unless errors.length == 0
-  finally
-    mock_methods.restore_original_methods()
-    delete Object.prototype[expects_method_name]
+class MockFunction
+
+  @mock: (args...) ->
+    fail(messages.MockUsage) if args.length < 1 or 2 < args.length
+    fail(messages.MockUsage) if args.length == 1 and not is_function(args[0])
+    fail(messages.MockUsage) if args.length == 2 and not is_function(args[1])
+    fail(messages.MockBadUsage) if args.length == 2 and not has_property(args[0], "expects_method_name") and not has_property(args[0], "mock_count")
+    test_function = ( if args.length == 1 then args[0] else args[1] )
+    expects_method_name = ( if args.length == 2 then args[0].expects_method_name ) ? "expects"    # TODO: use merge idiom?  what happens if expects_method_name is not a valid method name?
+    mock_count = ( if args.length == 2 then args[0].mock_count ) ? 5                              # TODO: what happens if mock_count is not a number?
+    fail(messages.ExpectsMethodAlreadyExists, expects_method_name) if Object.prototype[expects_method_name]?
+    mock_objects = ( new MockObject() for i in [1..mock_count] )
+    mock_methods = new MockMethodList()
+    Object.prototype[expects_method_name] = (method_name) ->
+      fail(messages.ExpectsUsage) unless method_name?
+      fail(messages.ExpectsUsage) if arguments.length != 1
+      fail(messages.NotAnExistingMethod, method_name) unless is_mock_object(@) or has_method(@, method_name)
+      fail(messages.PreExistingProperty, method_name) if has_property(@, method_name)
+      fail(messages.ReservedMethodName, method_name) if method_name == expects_method_name        # TODO: extract is_reserved_method_name()
+      expectations = @[method_name]?.expectations
+      if not expectations
+        mock_method = (args...) ->
+          expectations.check_for_duplicate_expectations(method_name)                              # TODO: explain why we do this here
+          expectation = expectations.find_expectation(args...)
+          fail(messages.UnknownExpectation, method_name, args) unless expectation
+          expectation._called = yes
+          throw expectation._throws if expectation._throws
+          expectation._returns
+        mock_method.object = @
+        mock_method.method_name = method_name
+        mock_method.original_method = @[method_name]
+        mock_method.expectations = expectations = new ExpectationList()
+        mock_method.restore_original_method = ->
+          if @original_method?
+            @object[@method_name] = @original_method
+          else
+            delete @object[@method_name]
+        mock_method.find_errors = ->
+          @expectations.find_errors(@method_name)
+        @[method_name] = mock_method
+        mock_methods.add(mock_method)
+      expectations.create_expectation()
+    try
+      test_function.apply(null, mock_objects)
+      errors = mock_methods.find_errors()
+      fail( errors.join("\n") + "\n" ) unless errors.length == 0
+    finally
+      mock_methods.restore_original_methods()
+      delete Object.prototype[expects_method_name]

@@ -184,18 +186,7 @@
 format = (message, args...) ->
   message.replace /{(\d)+}/g, (match, i) ->
     if typeof args[i] isnt 'undefined' then args[i] else match
-
-#
-# [ [], [ "a" ], [ "b", "c" ] ]   => [ "a", "b", "c" ]
-# [ [], [], [] ]                  => []
-#
-# Don't care about this scenario:
-# [ [ [ "a" ] ], [ "b"] ]         => [ [ "a" ], "b" ]
-#
-# See: http://stackoverflow.com/a/10865042
-#
-flatten = (array) ->
-  [].concat.apply([], array)
+

 #
@@ -215,6 +206,6 @@
 # See: http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/
 #
 if module?.exports?
-  module.exports = mock
+  module.exports = MockFunction.mock
 else
-  window.mock = mock
+  window.mock = MockFunction.mock


Use extend vs set

Use extend vs set

--- tutor/src/components/course-settings/roster.cjsx
+++ tutor/src/components/course-settings/roster.cjsx
@@ -43,8 +43,7 @@
         @setState({tabIndex})

   handleSelection: (ev, tabIndex) ->
-    params = @context.router.getCurrentParams()
-    params.tab = tabIndex
+    params = _.extend({}, @context.router.getCurrentParams(), tab: tabIndex)
     @context.router.transitionTo('courseSettings', params)

     unless PH.activePeriods(CourseStore.get(@props.courseId))[tabIndex]


stdin stdout

stdin stdout

--- src/type_inference.coffee
+++ src/type_inference.coffee
@@ -23,6 +23,12 @@
 scope_state_reset = ()->
   current_scope =
     id_map : {
+      stdin : [
+        fake_id mk_type 'Source', [mk_type 'string']
+      ]
+      stdout : [
+        fake_id mk_type 'Sink', [mk_type 'string'],
+      ]
       Math : [
         fake_id mk_type 'object', [], {
           abs   : mk_type 'either', [


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -75,7 +75,7 @@
       eventCallbacks[event] = []

   ###*
-  The trigger method calls all listeners attached to the specified event.
+  Calls all listeners attached to the specified event.

   # calls each event handler bound to "someCustomEvent"


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -93,7 +93,7 @@
         else
           eventCallbacks[event] = []
     else if namespace
-
+      eventCallbac
         eventCallbacks[event] = eventCallbacks.select (callback) ->
           !callback.__PIXIE?[namespace]?


allow accessing help directly by name

allow accessing help directly by name

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -53,6 +53,9 @@
     component_cmd 'help', 'Shows this help', (cmd) ->
       if arguments.length > 0
         if _.isString cmd
+          doc = Documentation.get_documentation cmd
+          if doc?
+            return DocumentationItemComponent name: cmd, doc: doc
           op = @imported_context_fns[cmd]
           if op?
             doc = get_fn_documentation cmd, op


Add test cases for missing opener support

--- spec/latex-spec.coffee
+++ spec/latex-spec.coffee
@@ -79,3 +79,21 @@
       opener = latex.getOpener()

       expect(opener.constructor.name).toEqual('PreviewOpener')
+
+    it "does not support GNU/Linux", ->
+      helpers.overridePlatform('linux')
+      opener = latex.getOpener()
+
+      expect(opener).toBeUndefined()
+
+    it "does not support Windows", ->
+      helpers.overridePlatform('win32')
+      opener = latex.getOpener()
+
+      expect(opener).toBeUndefined()
+
+    it "does not support unknown operating system", ->
+      helpers.overridePlatform('foo')
+      opener = latex.getOpener()
+
+      expect(opener).toBeUndefined()


CollaborativeWorkspace: Show session not active view if user is not there.

--- client/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -138,6 +138,8 @@

   requestPingFromHost: ->
     @requestPingRef.set Date.now()
+    KD.utils.wait 10000, =>
+      @showNotActiveView()  unless @connected

   fetchUsers: ->
     @workspaceRef.once "value", (snapshot) =>


terminal: webTermView => terminalView

terminal: webTermView => terminalView

--- client/ide/filetree/controllers/findertreecontroller.coffee
+++ client/ide/filetree/controllers/findertreecontroller.coffee
@@ -454,9 +454,9 @@
   openTerminalFromHere: (nodeView) ->
     @appManager.open "Terminal", (appInstance) =>
       path          = nodeView.getData().path
-      {webTermView} = @appManager.getFrontApp().getView().tabView.getActivePane().getOptions()
-
-      webTermView.on "WebTermConnected", (server) =>
+      {terminalView} = @appManager.getFrontApp().getView().tabView.getActivePane().getOptions()
+
+      terminalView.on "WebTermConnected", (server) =>
         server.input "cd #{path}\n"

   ###


team: login: automatically fill username from querystring

--- client/landing/site.landing/coffee/team/teamlogintab.coffee
+++ client/landing/site.landing/coffee/team/teamlogintab.coffee
@@ -37,6 +37,13 @@
     @loginForm.button.unsetClass 'solid medium green'
     @loginForm.button.setClass 'SignupForm-button SignupForm-button--green'

+    if location.search isnt '' and location.search.search('username=') > 0
+      username = location.search.split('username=').last.replace(/\&.+/, '')
+      @loginForm.username.input.setValue username
+      @loginForm.username.inputReceivedKeyup()
+
+
+
     @invitationLink = new CustomLinkView
       cssClass    : 'invitation-link'
       title       : 'Ask for an invite'


Don't recurse into packageDirPaths to look for packagePaths

--- spec/spec-suite.coffee
+++ spec/spec-suite.coffee
@@ -8,6 +8,6 @@

 # Run extension specs
 for packageDirPath in config.packageDirPaths
-  for packagePath in fs.listTree(packageDirPath)
+  for packagePath in fs.list(packageDirPath)
     for path in fs.listTree(fs.join(packagePath, "spec")) when /-spec\.coffee$/.test path
       require path


Expose media-grid undim-delay as option.

--- src/js/jquery.hlf.media-grid.coffee
+++ src/js/jquery.hlf.media-grid.coffee
@@ -35,6 +35,7 @@
     defaults: do (pre = 'js-mg-') ->
       autoReady: off
       resizeDelay: 100
+      undimDelay: 1000
       classNames: do ->
         classNames = {}
         keys = [
@@ -114,7 +115,7 @@

     toggleExpandedItemFocus: ($item, focused) ->
       return unless $item?.hasClass @classNames.expanded
-      delay = if focused then 0 else 1000
+      delay = if focused then 0 else @undimDelay
       @toggleItemFocus $item, focused, delay
       return


Minor media-grid handler method return consistency.

--- src/js/jquery.hlf.media-grid.coffee
+++ src/js/jquery.hlf.media-grid.coffee
@@ -110,11 +110,11 @@
         @$el.toggleClass @classNames.dimmed, dimmed
         return
       , @dimDelay
-      return
+      yes

     _handleFocusingOnTogglingItem: ($item) ->
       @$expandedItem = $item.addClass @classNames.focused
-      return
+      yes

     _handleEnteringDimmedItem: ($item) ->
       return no unless @$el.hasClass @classNames.dimmed
@@ -123,17 +123,18 @@
         @$el.removeClass @classNames.dimmed
         return
       , @dimDelay
-      return yes
+      yes

     _handleEnteringExpandedItem: ($item) ->
       return no unless $item.hasClass @classNames.expanded
       $item.addClass @classNames.focused
       @$el.toggleClass @classNames.dimmed
-      return yes
+      yes

     _handleLeavingExpandedItem: ($item) ->
       return no unless $item.hasClass @classNames.expanded
       $item.removeClass @classNames.focused
+      yes

     _isBottomEdgeItem: (i) -> (i + 1) > (@$items.length - @metrics.rowSize)


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -16,8 +16,8 @@

     # Set the cooldown data
     I.cooldowns[name] = {
-      options.target
-      options.approachBy
+      target
+      approachBy
     }

     if value?


superflous file

superflous file

--- src/prolog.parser.coffee
+++ src/prolog.parser.coffee
@@ -1,119 +0,0 @@
-Prolog = require './prolog'
-
-# a programme is an array of rules
-# todo?: assume stream not string
-exports.parseKb = (program) ->
-    tk = new exports.Tokeniser(program)
-    kb = []
-    until tk.type is "eof"
-        kb.push exports.parseRule(tk)
-        tk.consume()
-    kb
-
-# a query is an array of clauses
-exports.parseQuery = (query) ->
-    #exports.parseBody new exports.Tokeniser(query)
-    exports.parseTerm new exports.Tokeniser(query)
-
-# todo: track line/char of token
-class exports.Tokeniser
-    constructor: (@remainder) ->
-        @current = null
-        @type = null
-        @consume()
-
-    # get next token
-    consume: () ->
-        matcher = (type, regex) =>
-            r = @remainder.match regex
-            if r
-                @remainder = r[2]
-                @current = r[1]
-                @type = type
-                true
-            else
-                false
-        # return if we've previously reached eof
-        return if @type is "eof"
-        # eat any leading white space
-        r = @remainder.match /^\s*(.*)$/
-        @remainder = r[1] if r?
-        # and check for eof
-        if @remainder is ""
-            @current = null
-        # looking good: grab next token
-        return if matcher "punc", /^([\(\)\.,\[\]\|\!]|\:\-)(.*)$/
-        return if matcher "var", /^([A-Z_][a-zA-Z0-9_]*)(.*)$/
-        return if matcher "id", /^(\{[^\}]*\})(.*)$/
-        return if matcher "id", /^("[^"]*")(.*)$/
-        return if matcher "id", /^([a-zA-Z0-9][a-zA-Z0-9_]*)(.*)$/
-        return if matcher "id", /^(-[0-9][0-9]*)(.*)$/
-        # bail if our rules havent identified the next token
-        @current = null
-        @type = "eof"
-
-exports.parseRule = (tk) ->
-    head = exports.parseTerm(tk)
-    return unless head?
-    return new Prolog.Rule(head) if tk.current is "."
-    return null if tk.current isnt ":-"
-    tk.consume()
-    body = exports.parseBody(tk)
-    return null if tk.current isnt "."
-    return new Prolog.Rule(head, body)
-
-exports.parseBody = (tk) ->
-    p = new Prolog.List
-    loop
-        t = exports.parseTerm(tk)
-        return null if t is null
-        p.push t
-        break if tk.current isnt ","
-        tk.consume()
-    return p
-
-# Term -> id ( optParamList )
-exports.parseTerm = (tk) ->
-    return null if tk.type isnt "id"
-    functor = tk.current
-    tk.consume()
-    if tk.current isnt "("
-        return new Prolog.Clause(new Prolog.Symbol(functor))
-    tk.consume()
-    p = new Prolog.List()
-    while tk.current isnt ")"
-        return null if tk.type is "eof"
-        part = exports.parsePart(tk)
-        return null if part is null
-        if tk.current is ","
-            tk.consume()
-        else
-            return null if tk.current isnt ")"
-        p.push part
-    tk.consume()
-    return new Prolog.Clause(new Prolog.Symbol(functor), p)
-
-# Part -> var | symbol | clause
-exports.parsePart = (tk) ->
-    if tk.type is "var"
-        name = tk.current
-        tk.consume()
-        return new Prolog.Var(name)
-    else
-        functor = tk.current
-        tk.consume()
-        return new Prolog.Symbol(functor) if tk.current isnt "("
-        tk.consume()
-
-        params = new Prolog.List
-        while tk.current isnt ")"
-            return null if tk.type is "eof"
-            param = exports.parsePart(tk)
-            return null if param is null
-            if tk.current is ","
-                tk.consume()
-            else
-                return null if tk.current isnt ")"
-            params.push param
-        tk.consume()
-        return new Prolog.Clause(new Prolog.Symbol(functor), params)


adding uid set by env var to perform actions

--- src/scripts/drush.coffee
+++ src/scripts/drush.coffee
@@ -12,23 +12,26 @@
     if not process.env.DRUSH_UID?
       msg.send "DRUSH_UID is not set. Cannot complete this action."
       return
-    try
-      process.setuid Number process.env.DRUSH_UID
-      msg.send "New UID set to: " + process.getuid()
-    catch err
-      msg.send "Unable to set uid. " + err

-    #    sitelist = ''
-    #    sitealias = spawn("drush", ["sa"])
-    #    sitealias.stdout.on "data", (data) ->
-    #      sitelist = sitelist + data
+    sitelist = ''
+    sitealias = spawn("drush", ["sa"],
+      cwd: undefined,
+      env: process.env,
+      uid: Number process.env.DRUSH_UID
+    )
+    sitealias.stdout.on "data", (data) ->
+      sitelist = sitelist + data

-    #    sitealias.on "exit", (code) ->
-    #  msg.send sitelist
+    sitealias.on "exit", (code) ->
+      msg.send sitelist

   robot.respond /drush cc (.*)$/i, (msg) ->
     ccout = ''
-    clearcache = spawn("drush", [msg.match[1], "cc", "all"])
+    clearcache = spawn("drush", [msg.match[1], "cc", "all"],
+      cwd: undefined,
+      env: process.env,
+      uid: Number process.env.DRUSH_UID
+    )
     msg.send "This may take a minute, please be patient..."
     clearcache.stdout.on "data", (data) ->
       ccout = ccout + data


Fix previous commit

Fix previous commit

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -120,7 +120,7 @@
         flashPath = findSystemFlashPath()
         errMsg = 'Could not load Pepper Flash Player system plug-in'
       else
-        flashPath = validatePath loc
+        flashPath = loc
         errMsg = "Could not load '#{FILENAME}' from: \n#{loc}"
   else
     if flashSources.length is 0
@@ -130,6 +130,7 @@
       flashPath = getPath src.loc
       usingIndex = i
       break if flashPath?
+  flashPath = validatePath flashPath
   error errMsg if errMsg? and not flashPath?
   flashPath


proper new block relative path

proper new block relative path

--- apps/channel/client.coffee
+++ apps/channel/client.coffee
@@ -8,7 +8,7 @@
 CurrentUser = require '../../models/current_user.coffee'
 BlockCollectionView = require '../../components/block_collection/client/block_collection_view.coffee'
 BlockSkeletonView = require './client/block_skeleton_view.coffee'
-NewBlockView = require '../../../components/new_block/client/new_block_view.coffee'
+NewBlockView = require '../../components/new_block/client/new_block_view.coffee'
 ChannelCollaborationView = require './client/channel_collaboration_view.coffee'

 module.exports = class ChannelView extends Backbone.View


test: Make compileParams() tests consistent with the rest of the project

--- test/unit/compile-uri/compile-params-test.coffee
+++ test/unit/compile-uri/compile-params-test.coffee
@@ -1,4 +1,4 @@
-{expect} = require 'chai'
+{assert} = require 'chai'
 fury = new require 'fury'

 compileParams = require '../../../src/compile-uri/compile-params'
@@ -10,7 +10,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       name: {
         default: undefined,
         example: undefined,
@@ -26,7 +26,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       name: {
         default: undefined,
         example: 'Doe',
@@ -41,7 +41,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       name: {
         default: undefined,
         example: 'Doe',
@@ -57,7 +57,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       name: {
         default: 'Unknown',
         example: undefined,
@@ -72,7 +72,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       names: {
         default: undefined,
         example: [],
@@ -87,7 +87,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       names: {
         default: undefined,
         example: ['One', 'Two'],
@@ -103,7 +103,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       names: {
         default: ['Unknown'],
         example: [],
@@ -118,7 +118,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       names: {
         default: undefined,
         example: ['One', 'Two'],
@@ -134,7 +134,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       names: {
         default: ['Unknown'],
         example: [],
@@ -152,7 +152,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       order: {
         default: undefined,
         example: 'ascending',
@@ -170,7 +170,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       order: {
         default: undefined,
         example: 'decending',
@@ -189,7 +189,7 @@

     parameters = compileParams(hrefVariables)

-    expect(parameters).to.deep.equal({
+    assert.deepEqual(parameters, {
       order: {
         default: 'decending',
         example: 'ascending',


Remove the out commented lines

Remove the out commented lines

--- lib/linter-julia.coffee
+++ lib/linter-julia.coffee
@@ -78,8 +78,6 @@
           connection.on('error',reject)
           connection.on 'data', (chunk) ->
             data.push(chunk)
-            #if chunk.asciiSlice().match("\n\n")
-            #  connection.destroy()
           connection.on 'close', () ->
             allOfData = data.join('')
             resolve(doSomeMagic(allOfData,textEditor))


Always listen to mousewheel but only scroll when setting is enabled

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -55,6 +55,7 @@

     @element.addEventListener "mouseenter", @onMouseEnter.bind(this)
     @element.addEventListener "mouseleave", @onMouseLeave.bind(this)
+    @element.addEventListener "mousewheel", @onMouseWheel.bind(this)
     @element.addEventListener "dragstart", @onDragStart.bind(this)
     @element.addEventListener "dragend", @onDragEnd.bind(this)
     @element.addEventListener "dragleave", @onDragLeave.bind(this)
@@ -389,7 +390,7 @@
       atom.focus()

   onMouseWheel: (event) ->
-    return if event.shiftKey
+    return if event.shiftKey or not @tabScrolling

     @wheelDelta ?= 0
     @wheelDelta += event.wheelDeltaY
@@ -445,12 +446,6 @@
       @tabScrolling = (process.platform is 'linux')
     else
       @tabScrolling = value
-    @tabScrollingThreshold = atom.config.get('tabs.tabScrollingThreshold')
-
-    if @tabScrolling
-      @element.addEventListener 'mousewheel', @onMouseWheel.bind(this)
-    else
-      @element.removeEventListener 'mousewheel', @onMouseWheel.bind(this)

   browserWindowForId: (id) ->
     BrowserWindow ?= require('electron').remote.BrowserWindow


Fix BuiltinType#include to support sub types/prototypes

--- src/finitio/type/builtin_type.coffee
+++ src/finitio/type/builtin_type.coffee
@@ -14,7 +14,7 @@
     if value == null || value == undefined
       helper.failed(this, value)

-    unless value.constructor == @jsType
+    unless @include(value)
       helper.failed(this, value)

     value
@@ -23,7 +23,7 @@
     @jsType.name

   include: (value) ->
-    value.constructor == @jsType
+    value instanceof @jsType || value.constructor == @jsType

   equals: (other) =>
     (this is other) or


fix(accounts): Load accounts before adding a new one

--- src/flux/stores/account-store.coffee
+++ src/flux/stores/account-store.coffee
@@ -129,6 +129,7 @@
       console.log JSON.stringify(json)
       throw new Error("Returned account data is invalid")
     return if @_tokens[json.id]
+    @_load()
     @_tokens[json.id] = json.auth_token
     @_accounts.push((new Account).fromJSON(json))
     @_save()


Fix `child_process.fork` patch

Fix `child_process.fork` patch

  * Preserve `options` when called without `args`
  * Don't use `coffee` script as `execPath` (Fixes #2919)

--- register.coffee
+++ register.coffee
@@ -42,10 +42,11 @@
 if child_process
   {fork} = child_process
   binary = require.resolve '../../bin/coffee'
-  child_process.fork = (path, args = [], options = {}) ->
-    execPath = if helpers.isCoffee(path) then binary else null
-    if not Array.isArray args
-      args = []
-      options = args or {}
-    options.execPath or= execPath
+  child_process.fork = (path, args, options) ->
+    if helpers.isCoffee path
+      unless Array.isArray args
+        options = args or {}
+        args = []
+      args = [path].concat args
+      path = binary
     fork path, args, options


remove editorialisation

remove editorialisation

--- register.coffee
+++ register.coffee
@@ -15,9 +15,7 @@
     require.extensions[ext] = loadFile

   # Patch Node's module loader to be able to handle multi-dot extensions.
-  # This is a horrible thing that should not be required. Perhaps, one day,
-  # when a truly benevolent dictator comes to rule over the Republik of Node,
-  # it won't be.
+  # This is a horrible thing that should not be required.
   Module = require 'module'

   findExtension = (filename) ->


Adding return statement.

Adding return statement.

--- tests/test_performance.coffee
+++ tests/test_performance.coffee
@@ -11,4 +11,4 @@
     for i1, idx in intervals
         for i2 in intervals[idx..intervals.length]
             wolf.intervalIntersects(i1, i2)
-    this
+    return this


fixes #45

fixes #45

Former-commit-id: 91a7d6be8c6f56e5ccd38cd3a0f65162f7b4b3e8 [formerly e60483d8fe2bcbd0a0c7e7fb306cfc916ca6d7e5]
Former-commit-id: fd4910219df180288f72cb508cd80f3db85bb7e6

--- src/g/zoomer.coffee
+++ src/g/zoomer.coffee
@@ -56,15 +56,19 @@

   # @param n [int] number of residues to scroll to the right
   setLeftOffset: (n) ->
-    val = (n - 1) * @get('columnWidth')
+    val = (n)
     val = Math.max 0, val
-    @set "_alignmentScrollLeft", val
+    val -= @g.columns.calcHiddenColumns val
+    @set "_alignmentScrollLeft", val * @get('columnWidth')

   # @param n [int] row that should be on top
   setTopOffset: (n) ->
-    val = (n - 1) * @get('rowHeight')
+    val = (n - 1)
+    for i in [0..val] by 1
+      seq = @model.at i
+      height += seq.attributes.height || 1
     val = Math.max 0, val
-    @set "_alignmentScrollTop",val
+    @set "_alignmentScrollTop",val * @get("rowHeight")

   # length of all elements left to the main sequence body: labels, metacell, ..
   getLabelWidth: ->


db-doc immutable: create and apply patches

--- src/smc-util/db-doc-immutable.coffee
+++ src/smc-util/db-doc-immutable.coffee
@@ -20,6 +20,7 @@
 ###

 immutable = require('immutable')
+underscore = require('underscore')

 misc = require('./misc')

@@ -128,12 +129,18 @@
         set   = {}
         for field, val of obj
             if @_primary_keys[field]?
-                where[field] = val
+                if val?
+                    where[field] = val
             else
                 set[field] = val
         return {where:where, set:set}

     set: (obj) =>
+        if misc.is_array(obj)
+            z = @
+            for x in obj
+                z = z.set(x)
+            return z
         {where, set} = @_parse(obj)
         matches = @_select(where)
         n = matches?.first()
@@ -141,7 +148,10 @@
             # edit the first existing record that matches
             before = record = @_records.get(n)
             for field, value of set
-                record = record.set(field, immutable.fromJS(value))
+                if not value?
+                    record = record.delete(field)
+                else
+                    record = record.set(field, immutable.fromJS(value))
             if not before.equals(record)
                 # actual change so update; doesn't change anything involving indexes.
                 return new DBDoc(@_primary_keys, @_records.set(n, record), @_everything, @_indexes)
@@ -166,6 +176,11 @@
             return new DBDoc(@_primary_keys, records, everything, indexes)

     delete: (where) =>
+        if misc.is_array(where)
+            z = @
+            for x in where
+                z = z.delete(x)
+            return z
         # if where undefined, will delete everything
         if @_everything.size == 0
             # no-op -- no data so deleting is trivial
@@ -220,11 +235,12 @@
             return
         return @_records.get(matches.first())

-    is_equal: (other) =>
+    equals: (other) =>
         if @_records == other._records
             return true
-        # TODO: does this really work -- would be amazing...
-        return immutable.Set(@_records).equals(immutable.Set(other._records))
+        if @size != other.size
+            return false
+        return immutable.Set(@_records).add(undefined).equals(immutable.Set(other._records).add(undefined))

     # Conversion to and from an array of records, which is the primary key list followed by the normal Javascript objects
     to_obj: =>
@@ -234,3 +250,97 @@

     to_str: =>
         return (misc.to_json(x) for x in @to_obj()).join('\n')
+
+    # x = javascript object
+    _primary_key_part: (x) =>
+        where = {}
+        for k, v of x
+            if @_primary_keys[k]
+                where[k] = v
+        return where
+
+    make_patch: (other) =>
+        if other.size == 0
+            # Special case -- delete everything
+            return [-1,[{}]]
+
+        t0 = immutable.Set(@_records)
+        t1 = immutable.Set(other._records)
+        # Remove the common intersection -- nothing going on there.
+        # Doing this greatly reduces the complexity in the common case in which little has changed
+        common = t0.intersect(t1).add(undefined)
+        t0 = t0.subtract(common)
+        t1 = t1.subtract(common)
+
+        # Easy very common special cases
+        if t0.size == 0
+            # Special case: t0 is empty -- insert all the records.
+            return [1, t1.toJS()]
+        if t1.size == 0
+            # Special case: t1 is empty -- bunch of deletes
+            v = []
+            t0.map (x) =>
+                v.push(@_primary_key_part(x.toJS()))
+                return
+            return [-1, v]
+
+        # compute the key parts of t0 and t1 as sets
+        k0 = t0.map((x) => x.filter((v,k)=>@_primary_keys[k]))  # means -- set got from t0 by taking only the primary_key columns
+        k1 = t1.map((x) => x.filter((v,k)=>@_primary_keys[k]))
+
+        add = []
+        remove = undefined
+
+        # Deletes: everything in k0 that is not in k1
+        deletes = k0.subtract(k1)
+        if deletes.size > 0
+            remove = deletes.toJS()
+
+        # Inserts: everything in k1 that is not in k0
+        inserts = k1.subtract(k0)
+        if inserts.size > 0
+            inserts.map (k) =>
+                add.push(other.get_one(k.toJS()).toJS())
+                return
+
+        # Everything in k1 that is also in k0 -- these must have all changed
+        changed = k1.intersect(k0)
+        if changed.size > 0
+            changed.map (k) =>
+                obj  = k.toJS()
+                obj0 = @_primary_key_part(obj)
+                from = @get_one(obj0).toJS()
+                to   = other.get_one(obj0).toJS()
+                # undefined for each key of from not in to
+                for k of from
+                    if not to[k]?
+                        obj[k] = undefined
+                # explicitly set each key of to that is different than corresponding key of from
+                for k, v of to
+                    if not underscore.isEqual(from[k], v)
+                        obj[k] = v
+                add.push(obj)
+                return
+
+        patch = []
+        if remove?
+            patch.push(-1)
+            patch.push(remove)
+        if add.length > 0
+            patch.push(1)
+            patch.push(add)
+
+        return patch
+
+    apply_patch: (patch) =>
+        i = 0
+        db = @
+        while i < patch.length
+            if patch[i] == -1
+                db = db.delete(patch[i+1])
+            else if patch[i] == 1
+                db = db.set(patch[i+1])
+            i += 2
+        return db
+
+