Moved templateInstance to componentInternals.

--- lib.coffee
+++ lib.coffee
@@ -248,9 +248,9 @@
     # and this is this dependency mixin, the view might already be created or rendered and callbacks were
     # already called, so we should call them manually here as well. But only if he view has not been destroyed
     # already. For those mixins we do not call anything, there is little use for them now.
-    unless @templateInstance?.view.isDestroyed
-      mixinInstance.onCreated?() if not @_componentInternals.inOnCreated and @templateInstance?.view.isCreated
-      mixinInstance.onRendered?() if not @_componentInternals.inOnRendered and @templateInstance?.view.isRendered
+    unless @_componentInternals.templateInstance?.view.isDestroyed
+      mixinInstance.onCreated?() if not @_componentInternals.inOnCreated and @_componentInternals.templateInstance?.view.isCreated
+      mixinInstance.onRendered?() if not @_componentInternals.inOnRendered and @_componentInternals.templateInstance?.view.isRendered

     # To allow chaining.
     @
@@ -417,6 +417,8 @@

       # We on purpose do not reuse helpers, events, and hooks. Templates are used only for HTML rendering.

+      @component._componentInternals ?= {}
+
       registerHooks template,
         onCreated: ->
           # @ is a template instance.
@@ -443,12 +445,10 @@
           @component = component

           # TODO: Should we support that the same component can be rendered multiple times in parallel? How could we do that? For different component parents or only the same one?
-          assert not @component.templateInstance
-          @component.templateInstance = @
+          assert not @component._componentInternals.templateInstance
+          @component._componentInternals.templateInstance = @

           try
-            @component._componentInternals ?= {}
-
             # We have to know if we should call onCreated on the mixin inside the requireMixin or not. We want to call
             # it only once. If it requireMixin is called from onCreated of another mixin, then it will be added at the
             # end and we will get it here at the end. So we should not call onCreated inside requireMixin because then
@@ -463,8 +463,6 @@
         onRendered: ->
           # @ is a template instance.
           try
-            @component._componentInternals ?= {}
-
             # Same as for onCreated above.
             @component._componentInternals.inOnRendered = true
             componentOrMixin = null
@@ -491,7 +489,7 @@
               componentParent.removeComponentChild component

             # Remove the reference so that it is clear that template instance is not available anymore.
-            delete @component.templateInstance
+            delete @component._componentInternals.templateInstance

       template

@@ -537,7 +535,7 @@
     while component.mixinParent()
       component = component.mixinParent()

-    Blaze.getData(component.templateInstance.view) or null
+    Blaze.getData(component._componentInternals.templateInstance.view) or null

   # Caller-level data context. Reactive. Use this to get in event handlers the data
   # context at the place where event originated (target context). In template helpers
@@ -556,16 +554,16 @@
     Template.instance()?.get('component') or null

   firstNode: ->
-    @templateInstance.firstNode
+    @_componentInternals.templateInstance.firstNode

   lastNode: ->
-    @templateInstance.lastNode
+    @_componentInternals.templateInstance.lastNode

 # We copy utility methods ($, findAll, autorun, subscribe, etc.) from the template instance prototype.
 for methodName, method of Blaze.TemplateInstance::
   do (methodName, method) ->
     BlazeComponent::[methodName] = (args...) ->
-      @templateInstance[methodName] args...
+      @_componentInternals.templateInstance[methodName] args...

 argumentsConstructor = ->
   # This class should never really be created.


Slightly refactor filelist.

Slightly refactor filelist.

--- src/fs_utils/file_list.coffee
+++ src/fs_utils/file_list.coffee
@@ -40,20 +40,19 @@
     clearTimeout @timer if @timer?
     @timer = setTimeout (=> @emit 'ready'), @RESET_TIME

-  _getByPath: (path) ->
+  _findByPath: (path) ->
     @files.filter((file) -> file.path is path)[0]

-  _getAssetByPath: (path) ->
+  _findAssetByPath: (path) ->
     @assets.filter((file) -> file.path is path)[0]

   _compileDependentFiles: (path) ->
-    unless @_isAsset path
-      @files
-        .filter (dependent) =>
-          dependent.cache.dependencies.length
-        .filter (dependent) =>
-          path in dependent.cache.dependencies
-        .forEach(@_compile)
+    @files
+      .filter (dependent) =>
+        dependent.cache.dependencies.length
+      .filter (dependent) =>
+        path in dependent.cache.dependencies
+      .forEach(@_compile)
     @_resetTimer()

   _compile: (file) =>
@@ -84,19 +83,19 @@
     file

   _change: (path, compiler, isHelper) =>
-    return @_compileDependentFiles path if (@_ignored path)
     if @_isAsset path
-      @_copy (@_getAssetByPath(path) ? @_addAsset path)
+      @_copy (@_findAssetByPath(path) ? @_addAsset path)
+    else if @_ignored(path) or not compiler
+      @_compileDependentFiles path
     else
-      return @_compileDependentFiles path unless compiler
-      @_compile (@_getByPath(path) ? @_add path, compiler, isHelper)
-    @_resetTimer()
+      @_compile (@_findByPath(path) ? @_add path, compiler, isHelper)

   _unlink: (path) =>
-    return @_compileDependentFiles path if @_ignored path
     if @_isAsset path
       @assets.splice(@assets.indexOf(path), 1)
+    else if @_ignored path
+      @_compileDependentFiles path
     else
-      file = @_getByPath path
+      file = @_findByPath path
       @files.splice(@files.indexOf(file), 1)
     @_resetTimer()


Add an observeMinimaps method replacing eachMinimapView

--- lib/mixins/view-management.coffee
+++ lib/mixins/view-management.coffee
@@ -1,5 +1,6 @@
 Mixin = require 'mixto'
 {CompositeDisposable} = require 'event-kit'
+{deprecate} = require 'grim'
 MinimapView = null

 # Public: Provides methods to manage minimap views per pane.
@@ -45,12 +46,19 @@
   #            * view - The {MinimapView} instance
   #
   # Returns an {Object}.
-  eachMinimapView: (iterator) ->
+  observeMinimaps: (iterator) ->
     return unless iterator?
     iterator({view: minimapView}) for id,minimapView of @minimapViews
     createdCallback = (minimapView) -> iterator(minimapView)
     disposable = @onDidCreateMinimap(createdCallback)
-    off: => disposable.dispose()
+    disposable.off = ->
+      deprecate('Use Disposable::dispose instead')
+      disposable.dispose()
+    disposable
+
+  eachMinimapView: (iterator) ->
+    deprecate('Use Minimap::observeMinimaps instead')
+    @observeMinimaps(iterator)

   # Internal: Destroys all views currently in use.
   destroyViews: ->


5:1 sloan to candels ratio.  Closes #56.

5:1 sloan to candels ratio.  Closes #56.

--- app/models/subject.coffee
+++ app/models/subject.coffee
@@ -20,7 +20,7 @@
       tree: CandelsTree

   @url: (params) -> @withParams "/projects/galaxy_zoo/groups/#{ @randomSurveyId() }/subjects", params
-  @randomSurveyId: -> if Math.random() > 0.5 then @::surveys.sloan.id else @::surveys.candels.id
+  @randomSurveyId: -> if Math.random() > 0.166667 then @::surveys.sloan.id else @::surveys.candels.id

   @next: ->
     if @current


Fix test

Fix test

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -96,7 +96,7 @@
         it 'should provide it back', (done) ->
           connection.once 'message', (message) ->
             data = message.utf8Data
-            validateSchema event, 'runtime/output/runtime'
+            validateSchema data, 'runtime/output/runtime'
             done()

           send 'runtime', 'getruntime', {}


Refactoring following new documentation. Obsoleted some functions and created new ones. New functions added.

--- src/leaderboards.coffee
+++ src/leaderboards.coffee
@@ -3,6 +3,28 @@
 ClanError = require './ClanError.coffee'

 module.exports =  (appCredentials, gamerCred, domain)->
+
+	post: (score, board, order, info, force, cb)->
+		agent
+		.post "/v2.6/gamer/scores/#{domain}/#{board}?order=#{order}&force=#{force}"
+		.use prefixer
+		.set appCredentials
+		.auth gamerCred.gamer_id, gamerCred.gamer_secret
+		.send { score, info }
+		.end (err, res)->
+			if err? then return cb(err)
+			if res.error then cb new ClanError res.status, res.body else cb null, res.body
+
+	getRank: (score, board, cb)->
+		agent
+		.put "/v2.6/gamer/scores/#{domain}/#{board}"
+		.use prefixer
+		.set appCredentials
+		.auth gamerCred.gamer_id, gamerCred.gamer_secret
+		.send score
+		.end (err, res)->
+			if err? then return cb(err)
+			if res.error then cb new ClanError res.status, res.body else cb null, res.body

 	set: (board, order, score, cb)->
 		agent
@@ -34,3 +56,23 @@
 		.end (err, res)->
 			if err? then return cb(err)
 			if res.error then cb new ClanError res.status, res.body else cb null, res.body
+
+	getFriendsHighscores: (board, page, count, cb)->
+		agent
+		.get "/v2.6/gamer/scores/#{domain}/#{board}?page=#{page}&count=#{count}&type=friendscore"
+		.use prefixer
+		.set appCredentials
+		.auth gamerCred.gamer_id, gamerCred.gamer_secret
+		.end (err, res)->
+			if err? then return cb(err)
+			if res.error then cb new ClanError res.status, res.body else cb null, res.body
+
+	getCenteredHighscores: (board, count, cb)->
+		agent
+		.get "/v2.6/gamer/scores/#{domain}/#{board}?page=me&count=#{count}"
+		.use prefixer
+		.set appCredentials
+		.auth gamerCred.gamer_id, gamerCred.gamer_secret
+		.end (err, res)->
+			if err? then return cb(err)
+			if res.error then cb new ClanError res.status, res.body else cb null, res.body


Added "trash: 1" to Trade Route.

Added "trash: 1" to Trade Route.

--- cards.coffee
+++ cards.coffee
@@ -1493,6 +1493,7 @@
 makeCard "Trade Route", action, {
   cost: 3
   buys: 1
+  trash: 1
   getCoins: (state) ->
     state.tradeRouteValue
 }


:art:

:art:

--- src/marker-index.coffee
+++ src/marker-index.coffee
@@ -2,7 +2,6 @@
 Range = require "./range"
 {addSet, subtractSet, intersectSet, setEqual} = require "./set-helpers"

-# Max number of children allowed in a Node
 BRANCHING_THRESHOLD = 3

 class Node


:art: use DuckTyping, need to respond setInput()

--- lib/view.coffee
+++ lib/view.coffee
@@ -1,4 +1,5 @@
 Base = require './base'
+_ = require 'underscore-plus'

 class ViewModel
   char: null
@@ -10,22 +11,17 @@
     @vimState.onDidFailToCompose => @view.remove()

   confirm: (view) ->
-    if @operation.isMotion() or @operation.isOperator() or @operation.isInsertRegister()
-      @setInput(@view.value)
+    if _.isFunction(@operation.setInput)
+    # if @operation.isMotion() or @operation.isOperator() or @operation.isInsertRegister()
+      @operation.setInput(new Input(@view.value))
     else
       @vimState.operationStack.push(new Input(@view.value))

-  setInput: (input) ->
-    @operation.setInput(new Input(input))
-    @vimState.operationStack.process() # Re-process!!
-
-  getInput: ->
-    @input
-
   cancel: (view) ->
     if @vimState.operationStack.isOperatorPending()
-      if @operation.isMotion() or @operation.isOperator() or @operation.isInsertRegister()
-        @setInput('')
+      if _.isFunction(@operation.setInput)
+      # if @operation.isMotion() or @operation.isOperator() or @operation.isInsertRegister()
+        @operation.setInput(new Input(''))
       else
         @vimState.operationStack.push(new Input(''))


Add a link to each round

Add a link to each round

--- client.coffee
+++ client.coffee
@@ -43,6 +43,7 @@
 		Db.shared.ref('rounds').observeEach (round) !->
 			Ui.item ->
 				Dom.h2 Util.stringToQuestion(round.get('question'))
+				Dom.onTap -> Page.nav ['round', 0]

 renderAdvanded = ->
 	Dom.h2 "Hello, World!"


Code organization

Code organization

--- src/include/repository.coffee
+++ src/include/repository.coffee
@@ -385,15 +385,11 @@
     beginCaptures:
       2: name: 'storage.type.data.haskell'
     patterns: [
-        include: '#comments'
-      ,
-        include: '#family_and_instance'
-      ,
-        include: '#where'
-      ,
-        include: '#assignment_op'
-      ,
-        include: '#type_signature'
+        {include: '#comments'}
+        {include: '#family_and_instance'}
+        {include: '#where'}
+        {include: '#assignment_op'}
+        {include: '#type_signature'}
     ]
   keywords: [
     name: 'keyword.other.haskell'


changing storeSlug to store_slug - fixes create product bug

--- app/assets/javascripts/admin/stores.js.coffee
+++ app/assets/javascripts/admin/stores.js.coffee
@@ -144,7 +144,6 @@
         ($ '.fetch-more-user-photos').data('maxId', user_photos.max_id)

     window.render_user_photos = (user_photos) ->
-        console.log user_photos
         $product_form_wrapper.find('.product-photos')
             .replaceWith(
                 Mustache.render(
@@ -272,7 +271,7 @@
                 ($ @).parent().remove()

             .on 'click', '.render-new-product-form', ->
-                render_new_product_form({storeSlug: gon.store_slug})
+                render_new_product_form({store_slug: gon.store_slug})
                 update_h2(Mustache.render(templates.stores_h2_remove_template, {}))

             .on 'click', '.hide-new-product-form, .hide-product-form', ->


build drag events for multiple files

build drag events for multiple files

--- spec/event-helpers.coffee
+++ spec/event-helpers.coffee
@@ -12,9 +12,12 @@
       Object.keys(dataTransfer.data).map((key) -> {type: key})
   )

+  for entry in dragged
+    entry.classList.add('selected')
+
   dragStartEvent = new DragEvent('dragstart')
-  Object.defineProperty(dragStartEvent, 'target', value: dragged)
-  Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged)
+  Object.defineProperty(dragStartEvent, 'target', value: dragged[0])
+  Object.defineProperty(dragStartEvent, 'currentTarget', value: dragged[0])
   Object.defineProperty(dragStartEvent, 'dataTransfer', value: dataTransfer)

   dropEvent = new DragEvent('drop')


Add modal modify folder

Add modal modify folder

--- BackofficeBundle/Resources/public/coffee/mediatheque/GalleryCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/mediatheque/GalleryCollectionView.coffee
@@ -58,7 +58,6 @@
           )
   clickRedirect: (event) ->
     event.preventDefault()
-    console.log event
     $('.modal-title').text $(event.target).html()
     view = new adminFormView(url: @medias.get('links')._self_folder)
 )


spec: Fix asynchronous tests and use of @

* Fixed asynchronous tests. Atom uses Jasmine v1.3 and the `done` wasn't
  introduced until Jasmine v2.0. Refactored the tests to use the
  function `waitsForPromise` provided by Atom.

* Fixed use of `@`.

--- spec/hydrogen-spec.coffee
+++ spec/hydrogen-spec.coffee
@@ -35,7 +35,6 @@
     secondKernelSpecString = '''{
         "kernelspecs": {
             "python2": {
-                "resource_dir": "/usr/local/lib/python2.7/site-packages/ipykernel/resources",
                 "spec": {
                     "language": "python",
                     "display_name": "Python 2",
@@ -59,74 +58,91 @@
     kernelSpecs.kernelspecs.python2 = secondKernelSpec.kernelspecs.python2
     kernelSpecsString = JSON.stringify kernelSpecs

+    kernelManager = null
+
     beforeEach ->
-        @kernelManager = new KernelManager()
+        kernelManager = new KernelManager()
         atom.config.set 'Hydrogen.kernelspec', ''

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

-            parsed = @kernelManager.getKernelSpecsFromSettings()
+            parsed = kernelManager.getKernelSpecsFromSettings()

             expect(parsed).toEqual(firstKernelSpec.kernelspecs)

         it 'should return {} if no kernelspec is set', ->
-            expect(@kernelManager.getKernelSpecsFromSettings()).toEqual({})
+            expect(kernelManager.getKernelSpecsFromSettings()).toEqual({})

         it 'should return {} if invalid kernelspec is set', ->
             atom.config.set 'Hydrogen.kernelspec', 'invalid'
-            expect(@kernelManager.getKernelSpecsFromSettings()).toEqual({})
+            expect(kernelManager.getKernelSpecsFromSettings()).toEqual({})

     describe 'mergeKernelSpecs', ->
         it 'should merge kernelspecs', ->
-            @kernelManager._kernelSpecs = firstKernelSpec.kernelspecs
-            @kernelManager.mergeKernelSpecs secondKernelSpec.kernelspecs
+            kernelManager._kernelSpecs = firstKernelSpec.kernelspecs
+            kernelManager.mergeKernelSpecs secondKernelSpec.kernelspecs

-            specs = @kernelManager._kernelSpecs
+            specs = kernelManager._kernelSpecs
             expect(specs).toEqual(kernelSpecs.kernelspecs)

     describe 'getAllKernelSpecs', ->
-        it 'should return an array with specs', (done) ->
-            @kernelManager._kernelSpecs = kernelSpecs.kernelspecs
-            @kernelManager.getAllKernelSpecs (specs) ->
-                expect(specs.length).toEqual(2)
-                expect(specs[0]).toEqual(kernelSpecs.kernelspecs.ijavascript.spec)
-                expect(specs[1]).toEqual(kernelSpecs.kernelspecs.python2.spec)
-                done
+        it 'should return an array with specs', ->
+            waitsForPromise -> new Promise (resolve, reject) ->
+                kernelManager._kernelSpecs = kernelSpecs.kernelspecs
+                kernelManager.getAllKernelSpecs (specs) ->
+                    expect(specs.length).toEqual(2)
+                    expect(specs[0]).toEqual(
+                        kernelSpecs.kernelspecs.ijavascript.spec
+                    )
+                    expect(specs[1]).toEqual(
+                        kernelSpecs.kernelspecs.python2.spec
+                    )
+                    resolve()

     describe 'getAllKernelSpecsFor', ->
-        it 'should return an array with specs for given language', (done) ->
-            @kernelManager._kernelSpecs = kernelSpecs.kernelspecs
-            @kernelManager.getAllKernelSpecsFor 'python', (specs) ->
-                expect(specs.length).toEqual(1)
-                expect(specs[0]).toEqual(kernelSpecs.kernelspecs.python2.spec)
-                done
+        it 'should return an array with specs for given language', ->
+            waitsForPromise -> new Promise (resolve, reject) ->
+                kernelManager._kernelSpecs = kernelSpecs.kernelspecs
+                kernelManager.getAllKernelSpecsFor 'python', (specs) ->
+                    expect(specs.length).toEqual(1)
+                    expect(specs[0]).toEqual(
+                        kernelSpecs.kernelspecs.python2.spec
+                    )
+                    resolve()

-        it 'should return an empty array', (done) ->
-            @kernelManager._kernelSpecs = kernelSpecs.kernelspecs
-            @kernelManager.getAllKernelSpecsFor 'julia', (specs) ->
-                expect(specs).toEqual([])
-                done
+        it 'should return an empty array', ->
+            waitsForPromise -> new Promise (resolve, reject) ->
+                kernelManager._kernelSpecs = kernelSpecs.kernelspecs
+                kernelManager.getAllKernelSpecsFor 'julia', (specs) ->
+                    expect(specs).toEqual([])
+                    resolve()

     describe 'getKernelSpecFor', ->
-        it 'should return spec for given language', (done) ->
-            @kernelManager._kernelSpecs = kernelSpecs.kernelspecs
-            @kernelManager.getKernelSpecFor 'python', (kernelSpecForPython) ->
-                expect(kernelSpecForPython).toEqual(kernelSpecs.kernelspecs.python2.spec)
-                done
+        it 'should return spec for given language', ->
+            waitsForPromise -> new Promise (resolve, reject) ->
+                kernelManager._kernelSpecs = kernelSpecs.kernelspecs
+                kernelManager.getKernelSpecFor 'python', (kernelSpec) ->
+                    expect(kernelSpec).toEqual(
+                        kernelSpecs.kernelspecs.python2.spec
+                    )
+                    resolve()

-        it 'should return undefined', (done) ->
-            @kernelManager._kernelSpecs = kernelSpecs.kernelspecs
-            @kernelManager.getKernelSpecFor 'julia', (kernelSpecForJulia) ->
-                expect(kernelSpecForJulia).toBeUndefined()
-                done
+        it 'should return undefined', ->
+            waitsForPromise -> new Promise (resolve, reject) ->
+                kernelManager._kernelSpecs = kernelSpecs.kernelspecs
+                kernelManager.getKernelSpecFor 'julia', (kernelSpecForJulia) ->
+                    expect(kernelSpecForJulia).toBeUndefined()
+                    resolve()

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

-    it 'should update kernelspecs', (done) ->
-        @kernelManager.getKernelSpecsFromJupyter (err, specs) ->
-            expect(specs).toEqual(jasmine.any(Object))
-            done
+    it 'should update kernelspecs', ->
+        waitsForPromise -> new Promise (resolve, reject) ->
+            kernelManager.getKernelSpecsFromJupyter (err, specs) ->
+                unless err
+                    expect(specs).toEqual(jasmine.any(Object))
+                resolve()


inline constant.

inline constant.

--- app/assets/javascripts/models/timer.js.coffee
+++ app/assets/javascripts/models/timer.js.coffee
@@ -1,5 +1,4 @@
 class Stronglifters.Timer
-  @ONE_SECOND=1000
   constructor: (view) ->
     @view = view

@@ -7,10 +6,10 @@
     @stop()

     @view.set('timer', 0)
-    @intervalId = setInterval @refreshTimer, @ONE_SECOND
+    @intervalId = setInterval @refreshTimer, 1000

   refreshTimer: =>
-    @view.add('timer', @ONE_SECOND)
+    @view.add('timer', 1000)
     @view.set('clock', moment.utc(@view.get('timer')).format('mm:ss'))

   stop: =>


Fix minor style things

Fix minor style things

--- app/assets/javascripts/plugins/borg.js.coffee
+++ app/assets/javascripts/plugins/borg.js.coffee
@@ -116,7 +116,7 @@

   insertSorted: (toInsert, title) ->
     inserted = false
-    $('.task-lights',@$container).each ->
+    @$container.find('.task-lights').each ->
       title2 = $('.task-lights-title',this).text()
       # Sort shorter names first, so that the sort ends up
       # like [sb1,sb2,sb10] not [sb1,sb10,sb2]
@@ -124,7 +124,7 @@
         toInsert.insertBefore(this)
         inserted = true
         return false
-    toInsert.insertBefore($('.section-bottom',@$container)) unless inserted
+    toInsert.insertBefore(@$container.find('.section-bottom')) unless inserted

   genBoxes: ->
     boxes = document.createDocumentFragment();


quick fix to show loading and hide content w/ pt-link

--- app/assets/javascripts/pt/tags/link.js.coffee
+++ app/assets/javascripts/pt/tags/link.js.coffee
@@ -11,6 +11,9 @@
       $container = $("##{attr.link}")
       url        = $el.attr 'href'

+      # we want to hide any previous content immediately
+      $container.html 'Loading...'
+
       unless $el.data 'ptLinkLoading'
         $el.data 'ptLinkLoading', true


Fixed JS error that was failing the builds

--- app/assets/javascripts/users_select.js.coffee
+++ app/assets/javascripts/users_select.js.coffee
@@ -2,7 +2,8 @@
   constructor: (currentUser) ->
     @usersPath = "/autocomplete/users.json"
     @userPath = "/autocomplete/users/:id.json"
-    @currentUser = JSON.parse(currentUser)
+    if currentUser?
+      @currentUser = JSON.parse(currentUser)

     $('.js-user-search').each (i, dropdown) =>
       $dropdown = $(dropdown)
@@ -20,7 +21,7 @@
       $value = $block.find('.value')
       $loading = $block.find('.block-loading').fadeOut()

-      $block.on('click','.js-assign-yourself' , (e) =>
+      $block.on('click', '.js-assign-yourself', (e) =>
         e.preventDefault()
         assignTo(@currentUser.id)
       )
@@ -39,7 +40,7 @@
         ).done (data) ->
           $loading.fadeOut()
           $selectbox.hide()
-
+
           if data.assignee
             user =
               name: data.assignee.name
@@ -67,7 +68,7 @@

           <% } else { %>

-          No assignee -
+          No assignee -

             assign yourself

@@ -175,7 +176,7 @@
           listClosingTags = "

"

-
+
           if username is ''
             listWithUserName = ''


ignore irrelevant enrollments in gradebook2

fixes CNVS-28685

Test plan:
  * set up a course where a student is an observer of another student
    (but also a student in the course himself)
  * the gradebook should load

Change-Id: I421ae51964eaecc2208740ffff99cfef2271eb0c
Reviewed-on: https://gerrit.instructure.com/76951
Tested-by: Jenkins
Reviewed-by: Keith T. Garner <a1a80143a840c140f31c3495ebbd9ec906e09fed@instructure.com>
Reviewed-by: Derek Bender <d24d8a7ee520e34b1cec38800225d9f36b552df7@instructure.com>
QA-Review: Amber Taniuchi <ef4bfd1e6d278f6bc7d4a478f02be2cb9581a0a5@instructure.com>
Product-Review: Keith T. Garner <a1a80143a840c140f31c3495ebbd9ec906e09fed@instructure.com>

--- app/coffeescripts/gradebook2/Gradebook.coffee
+++ app/coffeescripts/gradebook2/Gradebook.coffee
@@ -295,6 +295,7 @@

     gotChunkOfStudents: (students) =>
       for student in students
+        student.enrollments = _.filter student.enrollments, @isStudentEnrollment
         isStudentView = student.enrollments[0].type == "StudentViewEnrollment"
         student.sections = student.enrollments.map (e) -> e.course_section_id

@@ -308,6 +309,9 @@
         @setupGrading(students)

       @grid?.render()
+
+    isStudentEnrollment: (e) =>
+      e.type == "StudentEnrollment" || e.type == "StudentViewEnrollment"

     setupGrading: (students) =>
       # fill in dummy submissions, so there's something there even if the


improve deletion regex

improve deletion regex

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -255,7 +255,7 @@
 				@status.cmd = true
 				$('.cmd').removeClass('hidden')
 			when 8
-				if $('#editor').html().match /^<(h1|h2|p|pre|code|figure) .*>
<\/(h1|h2|p|pre|code|figure)>$/
+				if $('#editor').html().match /^<(h1|h2|p|pre|code|figure) [^>]*>
<\/(h1|h2|p|pre|code|figure)>$/
 					e.preventDefault()
 					e.stopPropagation()


show set link url

show set link url

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -148,7 +148,7 @@
 		$('#toolpad').css 'left', left - 90
 		el = @getSelectedElement()
 		if el.is 'a'
-			$('#toolpad input').attr 'href', el.val()
+			$('#toolpad input').val el.attr('href')
 			@openLinkInput()

 		@hideTooltip()
@@ -158,6 +158,7 @@
 		$('#toolpad li:first-child').removeClass 'toggled'
 		@lastLinkElement = null
 		@linkRange = null
+		$('#toolpad input').val ''
 		@status.showToolpad = false
 	openLinkInput: ->
 		$('#toolpad li:first-child').addClass 'toggled'
@@ -205,11 +206,12 @@
 					@saveSelection 'toolpad'
 					@showToolpadOverSelection 'toolpad'
 					@restoreSelection 'toolpad'
+					self.linkRange = self.selection().getRangeAt 0
 		# apply link URL
 		$('#toolpad').delegate 'li:first-child', 'click', (e)->
 			self.openLinkInput()
-		.delegate 'li:first-child', 'mouseenter', (e)->
-			self.linkRange = self.selection().getRangeAt 0
+		# .delegate 'li:first-child', 'mouseenter', (e)->
+		# 	self.linkRange = self.selection().getRangeAt 0
 		.delegate 'input', 'keydown', (e)->
 				# self.lastLinkElement.setAttribute 'href', $(@).val()
 			if e.keyCode == 13


Disable test code to get lock mode

Disable test code to get lock mode

--- app/scripts/pocket.coffee
+++ app/scripts/pocket.coffee
@@ -21,7 +21,7 @@

     @element = document.createElement 'div'
     @element.classList.add @Class.BASE
-    @element.classList.add @Class.STATE_LOCKED
+    # @element.classList.add @Class.STATE_LOCKED
     @element.innerText = 'Pocket'
     @element.addEventListener 'mouseover', @onMouseOver.bind(@)
     @element.addEventListener 'mouseleave', @onMouseLeave.bind(@)


Words View: fixing alignment of bar graph labels

--- app/scripts/directives/words-bar-chart.coffee
+++ app/scripts/directives/words-bar-chart.coffee
@@ -40,7 +40,7 @@
             .attr("fill", "#1047A9")
             .classed("wordsBarText", true)
             .attr("y", ((d, i)-> i * 35 + 22))
-            .attr("x", 10)
+            .attr("x", 5)
             .text((d)-> d.ngram)
             .transition()
               .duration(1000)


clean up stream handling for file copy

clean up stream handling for file copy

--- services/clsi/app/coffee/UrlCache.coffee
+++ services/clsi/app/coffee/UrlCache.coffee
@@ -75,8 +75,9 @@
 		readStream = fs.createReadStream(from)
 		writeStream.on "error", callbackOnce
 		readStream.on "error", callbackOnce
-		writeStream.on "close", () -> callbackOnce()
-		readStream.pipe(writeStream)
+		writeStream.on "close", callbackOnce
+		writeStream.on "open", () ->
+			readStream.pipe(writeStream)

 	_clearUrlFromCache: (project_id, url, callback = (error) ->) ->
 		UrlCache._clearUrlDetails project_id, url, (error) ->


Returns context

Returns context

--- src/bodyguard.coffee
+++ src/bodyguard.coffee
@@ -10,5 +10,7 @@
         if obj[method]?
           obj[method] = noop

+    context
+

   bodyguard


Bullet proof the sorting in bowers

Bullet proof the sorting in bowers

--- library/membrane/preflight.coffee
+++ library/membrane/preflight.coffee
@@ -102,7 +102,8 @@
         bowerings = (@constructor?.bowerings ?= [])
         return next f context if f = bowerings.cached
         options = directory: bowerings.directory
-        match = (k) -> (bower) -> bower.target is k
+        esc = (p) -> new RegExp RegExp.escape "#{p}"
+        match = (k) -> (b) -> b.target.match esc k
         sorter = (v, k) -> _.findIndex bowerings, match(k)
         list(paths: yes, options).on "end", (paths) ->
             bowerings.cached = (context) ->


Added center

Added center

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -17,7 +17,19 @@
       return @value
     else
       return @value[0].toUpperCase() + @value.slice(1).toLowerCase()
-
+
+  center: (width, fillchar = " ") ->
+    if width <= @value.__len__
+      return @value
+    delta = width - @value.__len__
+    pad = ''
+    pad = for i in Math.floor(delta, 2)
+            pad += fillchar
+    if delta % 2 == 0
+      return pad + @value + pad
+    else
+      return pad + @value + pad + fillchar
+
   isalpha: ->
     alphas = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     if @value.__len__ == 0
@@ -56,4 +68,4 @@
       for char in @value
         if not char in spaces
           return false
-      return true
+      return true


collision with regular bounds

collision with regular bounds

--- src/collision.coffee
+++ src/collision.coffee
@@ -220,7 +220,7 @@
           xw = target.width/2
           yw = target.height/2

-          # Convert from bound
+          # Convert from bounds rect t
           return Collision.rayRectangle source, direction,
             x: target.x + xw
             y: target.y + yw


elektron: menu: commented out check for updates item

--- elektron/src/applicationmenu.coffee
+++ elektron/src/applicationmenu.coffee
@@ -43,10 +43,10 @@
         role: 'about'
       ,
         type: 'separator'
-      ,
-        label: 'Check for updates'
-        role: 'update'
-        submenu: []
+      # ,
+      #   label: 'Check for updates'
+      #   role: 'update'
+      #   submenu: []
       ,
         type: 'separator'
       ,


Fixes "guarded at" stack trace position in V8. (cherry picked from commit 17111de)

--- src/contracts.coffee
+++ src/contracts.coffee
@@ -806,7 +806,7 @@
 getModName = (isServer) ->
   st = printStackTrace(e: new Error())
   # in the stacktrace the frame above this one is where we were guarded/used
-  guardedAt = st[2]
+  guardedAt = if st[0] is 'Error' then st[3] else st[2]
   # pull out the filename (which will become our module) and line
   # number (the location in the module where the guard/use occured)
   # stack traces look like: {anonymous}()@file:///Path/to/file.js:4242


spacing things out a little bit

spacing things out a little bit

--- src/cscodegen.coffee
+++ src/cscodegen.coffee
@@ -108,16 +108,20 @@
     parentClassName = parent?.className
     usedAsExpression = parent? and parentClassName isnt 'Block'
     src = switch ast.className
+
       when 'Program'
         options.ancestors.unshift ast
         generate ast.block, options
+
       when 'Block'
         options.ancestors.unshift ast
         sep = '\n'
         sep = "#{sep}\n" if parentClassName is 'Program'
         (generate s, options for s in ast.statements).join sep
+
       when 'Identifier'
         ast.data
+
       when 'Int'
         absNum = if ast.data < 0 then -ast.data else ast.data
         # if number is a power of two (at least 2^4) or hex is a shorter
@@ -126,8 +130,10 @@
           "0x#{ast.data.toString 16}"
         else
           ast.data.toString 10
+
       when 'String'
         "'#{formatStringData ast.data}'"
+
       when 'Function', 'BoundFunction'
         options.ancestors.unshift ast
         options.precedence = precedence['AssignmentExpression']
@@ -142,6 +148,7 @@
         switch ast.className
           when 'Function' then "#{paramList}->#{body}"
           when 'BoundFunction' then "#{paramList}=>#{body}"
+
       when 'AssignOp'
         prec = precedence[ast.className]
         needsParens = prec < options.precedence
@@ -150,6 +157,7 @@
         assignee = generate ast.assignee, options
         expr = generate ast.expr, options
         "#{assignee} = #{expr}"
+
       when 'SeqOp'
         prec = precedence[ast.className]
         needsParens = prec < options.precedence
@@ -158,6 +166,7 @@
         left = generate ast.left, options
         right = generate ast.right, options
         "#{left}; #{right}"
+
       when 'LogicalOrOp', 'LogicalAndOp', 'BitOrOp', 'BitXorOp', 'BitAndOp', 'LeftShiftOp', 'SignedRightShiftOp', 'UnsignedRightShiftOp', 'EQOp', 'NEQOp', 'LTOp', 'LTEOp', 'GTOp', 'GTEOp', 'InOp', 'OfOp', 'InstanceofOp', 'PlusOp', 'SubtractOp', 'MultiplyOp', 'DivideOp', 'RemOp', 'ExistsOp'
         op = operators[ast.className]
         if ast.className in ['InOp', 'OfOp', 'InstanceofOp'] and parentClassName is 'LogicalNotOp'
@@ -170,6 +179,7 @@
         left = parens left if needsParensWhenOnLeft ast.left
         right = generate ast.right, options
         "#{left} #{op} #{right}"
+
       when 'UnaryPlusOp', 'UnaryNegateOp', 'LogicalNotOp', 'BitNotOp', 'DoOp', 'TypeofOp', 'PreIncrementOp', 'PreDecrementOp'
         op = operators[ast.className]
         prec = precedence[ast.className]
@@ -184,6 +194,7 @@
         options.precedence = prec
         options.ancestors.unshift ast
         "#{op}#{generate ast.expr, options}"
+
       when 'UnaryExistsOp', 'PostIncrementOp', 'PostDecrementOp', 'Spread'
         op = operators[ast.className]
         prec = precedence[ast.className]
@@ -193,6 +204,7 @@
         expr = generate ast.expr, options
         expr = parens expr if needsParensWhenOnLeft ast.expr
         "#{expr}#{op}"
+
       when 'NewOp'
         op = operators[ast.className]
         prec = precedence[ast.className]
@@ -209,6 +221,7 @@
         args = args.join ', '
         args = " #{args}" if ast.arguments.length > 0
         "#{op}#{ctor}#{args}"
+
       when 'FunctionApplication', 'SoakedFunctionApplication'
         if ast.className is 'FunctionApplication' and ast.arguments.length is 0 and not usedAsExpression
           generate (new DoOp ast.function), options
@@ -224,6 +237,7 @@
             arg
           argList = if ast.arguments.length is 0 then '()' else " #{args.join ', '}"
           "#{fn}#{op}#{argList}"
+
       when 'MemberAccessOp', 'SoakedMemberAccessOp', 'ProtoMemberAccessOp', 'SoakedProtoMemberAccessOp'
         op = operators[ast.className]
         prec = precedence[ast.className]
@@ -233,6 +247,7 @@
         expr = generate ast.expr, options
         expr = parens expr if needsParensWhenOnLeft ast.expr
         "#{expr}#{op}#{ast.memberName}"
+
       when 'DynamicMemberAccessOp', 'SoakedDynamicMemberAccessOp', 'DynamicProtoMemberAccessOp', 'SoakedDynamicProtoMemberAccessOp'
         op = operators[ast.className]
         prec = precedence[ast.className]
@@ -244,10 +259,13 @@
         options.precedence = 0
         indexingExpr = generate ast.indexingExpr, options
         "#{expr}#{op}[#{indexingExpr}]"
+
       when 'ConcatOp'
         left = formatInterpolation ast.left, options
         right = formatInterpolation ast.right, options
         "\"#{left}#{right}\""
+
       else
         throw new Error "Non-exhaustive patterns in case: #{ast.className}"
+
     if needsParens then (parens src) else src


use the latest pipes image

use the latest pipes image

--- src/coffee/compose.coffee
+++ src/coffee/compose.coffee
@@ -15,7 +15,7 @@
           'bigboat.service.type': 'net'
         subDomain = "#{instance}.#{config.domain}.#{config.tld}"
         netcontainer =
-          image: 'ictu/pipes:dhcpcd'
+          image: 'ictu/pipes'
           environment: eth0_pipework_cmd: networkValue
           hostname: "#{serviceName}.#{subDomain}"
           dns_search: subDomain


Fix sass compiler.

Fix sass compiler.

--- src/compilers/sass.coffee
+++ src/compilers/sass.coffee
@@ -53,5 +53,5 @@
     throw err if err?

     cb null, """
-    module.exports = #{JSON.stringify res.css}
+    module.exports = #{JSON.stringify res.css.toString()}
     """


Add snippet for inserting degree symbol (U+00B0)

--- src/configs/snippets.cson
+++ src/configs/snippets.cson
@@ -27,6 +27,10 @@
 	Alphabet:
 		prefix: "alphabet"
 		body: "${1:ABCDEFGHIJKLMNOPQRSTUVWXYZ} ${2:0123456789}"
+
+	°:
+		prefix: "deg"
+		body: "°"

 	θ:
 		prefix: "theta"


Fixed unused line

Fixed unused line

--- src/emoticon-stats.coffee
+++ src/emoticon-stats.coffee
@@ -27,8 +27,6 @@
         @robot.brain.on 'loaded', =>
             @robot.brain.data.emoticonCounts = @robot.brain.data.emoticonCounts || {}
             @cache.emoticonCounts = @robot.brain.data.emoticonCounts
-            delete @robot.brain.data.emoticonCounts[undefined]
-

     initCounts: (counts) ->
         @cache.emoticonCounts = counts


Updated generic-parser

Updated generic-parser

--- src/generic-parser.coffee
+++ src/generic-parser.coffee
@@ -37,7 +37,11 @@
     Utilities
   ###

-
+  collectUntilFalse: (fn)->
+    returnString = ''
+    while string = fn()
+      returnString += string
+    return returnString

   ###
     Known Length Token Extraction


change event bind function

change event bind function

--- src/jquery_adapter.coffee
+++ src/jquery_adapter.coffee
@@ -1,6 +1,6 @@
 class JQueryAdapter extends Adapter
   bind: (selector, event, fn) ->
-    @find(selector).bind(event, fn)
+    jQuery(document).on(event, selector, fn)

   find: (selector) ->
     jQuery(selector)


Colour support based on category

Colour support based on category

--- src/languages/html.coffee
+++ src/languages/html.coffee
@@ -6,41 +6,141 @@
 ATTRIBUTE_CLASSES = ['#attribute']

 COLORS = {
-  'Default': 'cyan',
-  '#comment': 'grey'
-  'a': 'grey',
-  'b': 'teal',
-  'body': 'return',
-  'br': 'command',
-  'button': 'yellow',
-  'center': 'red',
-  'div': 'amber',
-  'document': 'bluegrey',
-  'font': 'value',
-  'form': 'deeporange',
-  'h1': 'teal',
-  'h3': 'indigo',
-  'head': 'cyan',
-  'hr': 'lime',
-  'html': 'amber',
-  'iframe': 'green',
-  'img': 'green',
-  'input': 'brown',
-  'label': 'lightblue',
-  'li': 'pink',
-  'link': 'purple',
-  'marquee': 'command',
-  'meta': 'error',
-  'option': 'control',
-  'p': 'deeppurple',
-  'script': 'orange',
-  'select': 'indigo',
-  'strong': 'yellow',
-  'table': 'lightgreen',
-  'td': 'lightblue',
-  'title': 'green',
-  'tr': 'bluegrey',
-  'ul': 'blue'
+  #Metadata
+  '#documentType': 'lightblue'
+  html: 'lightblue'
+  head: 'lightblue'
+  body: 'lightblue'
+  title: 'lightblue'
+  link: 'lightblue'
+  meta: 'lightblue'
+  style: 'lightblue'
+  script: 'lightblue'
+  base: 'lightblue'
+
+  #Grouping
+  p: 'purple'
+  h1: 'purple'
+  h2: 'purple'
+  h3: 'purple'
+  h4: 'purple'
+  h5: 'purple'
+  h6: 'purple'
+  hr: 'purple'
+  div: 'purple'
+  span: 'purple'
+  ul: 'purple'
+  ol: 'purple'
+  li: 'purple'
+  dl: 'purple'
+  dt: 'purple'
+  dd: 'purple'
+  pre: 'purple'
+  blockquote: 'purple'
+  figure: 'purple'
+  figcaption: 'purple'
+  main: 'purple'
+  dd: 'purple'
+
+  #Content
+  a: 'lightgreen'
+  img: 'lightgreen'
+  iframe: 'lightgreen'
+  i: 'lightgreen'
+  b: 'lightgreen'
+  u: 'lightgreen'
+  sub: 'lightgreen'
+  sup: 'lightgreen'
+  br: 'lightgreen'
+  em: 'lightgreen'
+  strong: 'lightgreen'
+  small: 'lightgreen'
+  s: 'lightgreen'
+  cite: 'lightgreen'
+  q: 'lightgreen'
+  dfn: 'lightgreen'
+  abbr: 'lightgreen'
+  ruby: 'lightgreen'
+  rt: 'lightgreen'
+  rp: 'lightgreen'
+  data: 'lightgreen'
+  time: 'lightgreen'
+  code: 'lightgreen'
+  var: 'lightgreen'
+  samp: 'lightgreen'
+  kbd: 'lightgreen'
+  mark: 'lightgreen'
+  bdi: 'lightgreen'
+  bdo: 'lightgreen'
+  wbr: 'lightgreen'
+  '#text': 'lightgreen'
+
+  #Sections
+  article: 'orange'
+  section: 'orange'
+  nav: 'orange'
+  aside: 'orange'
+  hgroup: 'orange'
+  header: 'orange'
+  footer: 'orange'
+  address: 'orange'
+
+  #Table
+  table: 'indigo'
+  caption: 'indigo'
+  colgroup: 'indigo'
+  col: 'indigo'
+  tbody: 'indigo'
+  thead: 'indigo'
+  tfoot: 'indigo'
+  tr: 'indigo'
+  td: 'indigo'
+  th: 'indigo'
+
+  #Form
+  form: 'deeporange'
+  input: 'deeporange'
+  textarea: 'deeporange'
+  label: 'deeporange'
+  button: 'deeporange'
+  select: 'deeporange'
+  option: 'deeporange'
+  optgroup: 'deeporange'
+  datalist: 'deeporange'
+  keygen: 'deeporange'
+  output: 'deeporange'
+  progress: 'deeporange'
+  meter: 'deeporange'
+  fieldset: 'deeporange'
+  legend: 'deeporange'
+
+  #Embedded
+  embed: 'teal'
+  object: 'teal'
+  param: 'teal'
+  video: 'teal'
+  audio: 'teal'
+  source: 'teal'
+  track: 'teal'
+  map: 'teal'
+  area: 'teal'
+
+  #Other known tags
+  ins: 'pink'
+  del: 'pink'
+  details: 'pink'
+  summary: 'pink'
+  menu: 'pink'
+  menuitem: 'pink'
+  dialog: 'pink'
+  noscript: 'pink'
+  template: 'pink'
+  canvas: 'pink'
+  svg: 'pink'
+  frameset: 'pink'
+
+  #Unknow tag fallback
+  'Default': 'yellow'
 }

 DEFAULT_INDENT_DEPTH = '  '


add plainClone()

add plainClone()

--- src/lib/base-model.coffee
+++ src/lib/base-model.coffee
@@ -248,12 +248,13 @@

     ###*
-    create clone
-
-    @method clone
-    @return {BaseModel}
-    ###
-    clone: ->
+    clone the model as a plain object
+
+    @method plainClone
+    @public
+    @return {Object}
+    ###
+    plainClone: ->

         plainObject = {}

@@ -262,10 +263,25 @@
         for own prop, value of @

             if modelProps.isModel and value instanceof BaseModel
-                plainObject[prop] = value.clone()
+                plainObject[prop] = value.plainClone()

             else
                 plainObject[prop] = Util.clone value
+
+        return plainObject
+
+
+    ###*
+    create clone
+
+    @method clone
+    @public
+    @return {BaseModel}
+    ###
+    clone: ->
+
+        plainObject = @plainClone()
+        modelProps = @getModelProps()

         return @getFacade().createModel modelProps.modelName, plainObject


get rid of ugly stupid hack; no longer necessary

--- src/smc-hub/postgres-user-queries.coffee
+++ src/smc-hub/postgres-user-queries.coffee
@@ -1016,12 +1016,7 @@
                                     subs[value] = user_query.project_id
                                     cb()
                                 else
-                                    f = ->
-                                        cb("FATAL: you do not have read access to this project")
-                                    # A basic anti-DOS measure, e.g,. against clients that have
-                                    # signed out and are still sitting their querying, or whatever.
-                                    # Obviously, this is inspired by something in the logs.
-                                    setTimeout(f, 1000)
+                                    cb("FATAL: you do not have read access to this project")
                 when 'project_id-public'
                     if not user_query.project_id?
                         cb("FATAL: must specify project_id")


Remove not used usage of containment

Remove not used usage of containment

--- core/KDScrollView.coffee
+++ core/KDScrollView.coffee
@@ -127,7 +127,6 @@
     o = @getOptions()
     dragOptions =
       drag : @_drag
-      containment : "parent"
     if o.type = "vertical"
       dragOptions.axis = "y"
     else


Fix snippets spec

Fix snippets spec

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -21,8 +21,8 @@

     window.loadPackage("snippets")

-    editor = rootView.getActiveEditor()
-    editSession = rootView.getActiveEditSession()
+    editor = rootView.getActiveView()
+    editSession = rootView.getActivePaneItem()
     buffer = editor.getBuffer()
     rootView.simulateDomAttachment()
     rootView.enableKeymap()


Add mutation spec to combineAsArray

Add mutation spec to combineAsArray

--- spec/specs/combine.coffee
+++ spec/specs/combine.coffee
@@ -41,6 +41,16 @@
         src = series(1, ["same", error()])
         Bacon.combineAsArray(src, src)
       [["same", "same"], error()])
+
+  it "does not mutate given array", ->
+    lol = Bacon.once("lol")
+    bal = "bal"
+    streams = [lol, bal]
+    Bacon.combineAsArray(streams)
+    expect(streams.length).to.equal(2)
+    expect(streams[0]).to.equal(lol)
+    expect(streams[1]).to.equal(bal)
+
   it "toString", ->
     expect(Bacon.constant(1).combine(Bacon.constant(2), (->)).toString()).to.equal("Bacon.constant(1).combine(Bacon.constant(2),function)")
   describe "with random methods on Array.prototype", ->


activity: fix reference error

activity: fix reference error

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -102,6 +102,8 @@

   renderBody: ->

+    return null  unless thread = @state.channelThread
+

         <PublicChatPane ref='pane' thread={thread}/>
@@ -110,6 +112,8 @@

   renderSidebar: ->
+
+    return null  unless thread = @state.channelThread

     <ThreadSidebar
       channelThread={thread}


pass mode into codemirror options

pass mode into codemirror options

--- src/smc-webapp/assistant/body.cjsx
+++ src/smc-webapp/assistant/body.cjsx
@@ -201,6 +201,7 @@
                 <CodeMirrorStatic
                     value={code}
                     style={height:undefined,overflowX:undefined,whiteSpace:undefined}
+                    options={immutable.fromJS({mode:{name:get_codemirror_mode(@props.lang)}})}
                 />
             </Col>
             <Col sm={6}>
@@ -237,3 +238,15 @@
                     @render_body()
             }
         </Modal.Body>
+
+get_codemirror_mode = (language) ->
+    mode = {
+    python: "python",
+    sage: "sage",
+    r: "r",
+    gap: "",
+    julia: "text/x-julia",
+    octave: "text/x-octave",
+    bash: "shell",
+    }[language]
+    return mode


interrupted

interrupted

--- src/state_machine.module.js.coffee
+++ src/state_machine.module.js.coffee
@@ -1,45 +1,45 @@
 class StateMachine

-	states: null
-	initialState: null
-	currentState: null
+	constructor: (opts={})->
+		states = opts.states || []
+		transitions = opts.transitions || []

-	transitions: null
-	currentTransition: null
+		currentState = initialState = opts.initialState || null

-	constructor: (opts={})->
-		@states = opts.states || []
-		@transitions = opts.transitions || []
+		# @attemptTransition: (transitionName) ->
+		# 	if @isReady() and @transitionIsPossible(transitionName)
+		# 		@currentTransition = @transitions[transitionName]
+		# 		@performTransition(transitionName)
+		# 		true
+		# 	else
+		# 		false

-		@currentState = @initialState = opts.initialState || null
+		# @isReady: ->
+		# 	@currentTransition is null

-	attemptTransition: (transitionName) ->
-		if @isReady() and @transitionIsPossible(transitionName)
-			@currentTransition = @transitions[transitionName]
-			@performTransition(transitionName)
-			true
-		else
-			false
+		# @transitionIsPossible: (transitionName) ->
+		# 	if transition = @transitions[transitionName]
+		# 		@currentState != transition.to && @currentState == transition.from
+		# 	else
+		# 		false

-	isReady: ->
-		@currentTransition is null
+		# @performTransition: (transitionName) ->
+
+		# # Time to complete the transition and free up the state machine
+		# @onTransitionComplete: ->
+		# 	if @currentTransition
+		# 		@currentState = @currentTransition.to
+		# 		@currentTransition = null
+		# 		true
+		# 	else
+		# 		false

-	transitionIsPossible: (transitionName) ->
-		if transition = @transitions[transitionName]
-			@currentState != transition.to && @currentState == transition.from
-		else
-			false
+		@states: ->

-	performTransition: (transitionName) ->
-
-	# Time to complete the transition and free up the state machine
-	onTransitionComplete: ->
-		if @currentTransition
-			@currentState = @currentTransition.to
-			@currentTransition = null
-			true
-		else
-			false
+		@transitions: ->

+		@currentState: ->
+
+		@initialState: ->

 module.exports = StateMachine


修复系统消息弹出在头部的bug。

修复系统消息弹出在头部的bug。

--- src/public/javascripts/index.coffee
+++ src/public/javascripts/index.coffee
@@ -204,7 +204,7 @@
     # Display the welcome message
     message = "欢迎进入飞聊 – 当前用户数：#{data.numUsers}"
     log(message, {
-      prepend: true
+      #prepend: true
     })
   )


[worker][model][activity] updated permission for reading group activity feed

--- workers/social/lib/social/models/activity/index.coffee
+++ workers/social/lib/social/models/activity/index.coffee
@@ -286,36 +286,35 @@
   @on 'BucketIsUpdated',   notifyCache.bind this, 'BucketIsUpdated'
   @on 'UserMarkedAsTroll', notifyCache.bind this, 'UserMarkedAsTroll'

-  @fetchPublicActivityFeed = permit 'read activity',
-    success:(client, options, callback)->
-      {delegate} = client.connection
-      if not delegate
-        callback null, []
-      else
-        groupName = options.groupName
-        unless groupName then return callback new Error "Group name is undefined"
-        JGroup = require '../group'
-        JGroup.one slug : groupName, (err, group)=>
-          if err then return callback err
-          unless group then return callback {error: "Group not found"}
-          group.canOpenGroup client, (err, res)->
-            if err then return callback {error: "Not allowed to open this group"}
-
-            timestamp  = options.timestamp
-            rawStartDate  = if timestamp? then parseInt(timestamp, 10) else (new Date).getTime()
-            # this is for unix and javascript timestamp differance
-            startDate  = Math.floor(rawStartDate/1000)
-
-            neo4jConfig = KONFIG.neo4j
-            requestOptions =
-              startDate : startDate
-              neo4j : neo4jConfig
-              group :
-                groupName : group.slug
-                groupId : group._id
-
-
-            FetchAllActivityParallel = require './../graph/fetch'
-            fetch = new FetchAllActivityParallel requestOptions
-            fetch.get (results)->
-              callback null, results+  @fetchPublicActivityFeed = secure (client, options, callback)->
+    {delegate} = client.connection
+    if not delegate
+      callback null, []
+    else
+      groupName = options.groupName
+      unless groupName then return callback new Error "Group name is undefined"
+      JGroup = require '../group'
+      JGroup.one slug : groupName, (err, group)=>
+        if err then return callback err
+        unless group then return callback {error: "Group not found"}
+        group.canReadActivity client, (err, res)->
+          if err then return callback {error: "Not allowed to open this group"}
+
+          timestamp  = options.timestamp
+          rawStartDate  = if timestamp? then parseInt(timestamp, 10) else (new Date).getTime()
+          # this is for unix and javascript timestamp differance
+          startDate  = Math.floor(rawStartDate/1000)
+
+          neo4jConfig = KONFIG.neo4j
+          requestOptions =
+            startDate : startDate
+            neo4j : neo4jConfig
+            group :
+              groupName : group.slug
+              groupId : group._id
+
+
+          FetchAllActivityParallel = require './../graph/fetch'
+          fetch = new FetchAllActivityParallel requestOptions
+          fetch.get (results)->
+            callback null, results


this is a test

this is a test

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -37,6 +37,7 @@

   deactivate: ->
     @subscriptions.dispose()
+    @subscriptions = null

   # Automatic conversion, based on scope
   autoConvert: ->


send recaptcha_token with signup args

send recaptcha_token with signup args

--- lib/app/lifecycle.coffee
+++ lib/app/lifecycle.coffee
@@ -44,6 +44,7 @@
       sign_up_referer: req.body.signupReferer
       accepted_terms_of_service: req.body.accepted_terms_of_service,
       agreed_to_receive_emails: req.body.agreed_to_receive_emails,
+      recaptcha_token: req.body.recaptcha_token
     ).end (err, sres) ->
       if err and err.message is 'Email is invalid.'
         suggestion = Mailcheck.run(email: req.body.email)?.full


temporary textorum element wrapper hack for multi-root elements

--- src/textorum/tinymce/loader.coffee
+++ src/textorum/tinymce/loader.coffee
@@ -65,7 +65,14 @@
   saveFromText = (text) ->
     xmlDoc = helper.parseXML text
     if helper.hasDomError(xmlDoc)
-      return serializeError(xmlDoc)
+      # FIXME: temporary textorum wrapper for multi-element roots
+      text = '
' + text + '
'
+      newXmlDoc = helper.parseXML text
+      if helper.hasDomError(newXmlDoc)
+        return serializeError(xmlDoc)
+      xmlDoc = newXmlDoc
+    # if helper.hasDomError(xmlDoc)
+    #   return serializeError(xmlDoc)
     revNewDoc = revprocessor.transformToDocument(xmlDoc)
     (new XMLSerializer()).serializeToString(revNewDoc)
       .replace(/\/\/TEXTORUM\/\/DOCTYPE-SYSTEM\/\//,


Fix graphql header

Fix graphql header

--- lib/graphql.coffee
+++ lib/graphql.coffee
@@ -10,7 +10,7 @@
       .set 'Accept', 'application/json'

     if (token = user?.get?('access_token') or user?.access_token)?
-      post.set 'X-AUTH-TOKEN"': token
+      post.set 'X-AUTH-TOKEN': token

     post
       .send


:fire: blank line

:fire: blank line

--- lib/count-manager.coffee
+++ lib/count-manager.coffee
@@ -12,7 +12,6 @@
     if @vimState.operationStack.isOperatorPending()
       @reset()
     num = if _.isNumber(e) then e else parseInt(@getKeystrokeForEvent(e))
-
     @count ?= 0
     @count = (@count * 10) + num


Update java.coffee

Update java.coffee

--- lib/grammars/java.coffee
+++ lib/grammars/java.coffee
@@ -19,7 +19,7 @@
         sourcePath = GrammarUtils.Java.getProjectPath context
         if windows
           return ["/c javac -Xlint #{context.filename} && java #{className}"]
-        else ['-c', "javac -sourcepath '#{sourcePath}' -d /tmp '#{context.filepath}' && java -Dfile.encoding=UTF-8 -cp /tmp #{classPackages}#{className}"]
+        else ['-c', "javac -J-Dfile.encoding=UTF-8 -sourcepath '#{sourcePath}' -d /tmp '#{context.filepath}' && java -Dfile.encoding=UTF-8 -cp /tmp #{classPackages}#{className}"]
     }
   Kotlin:
     'Selection Based': {


Update if id exist

Update if id exist

--- lib/project.coffee
+++ lib/project.coffee
@@ -42,8 +42,9 @@
     @save()

   isCurrent: =>
-    path = atom.project.getPaths()[0]
-    if path is @props.paths[0]
+    activePath = atom.project.getPaths()[0]
+    projectPath = @props.paths[0]
+    if activePath is projectPath
       return true
     return false

@@ -63,13 +64,16 @@
       @projectSettings.load(@props.settings)

   save: =>
+    @db ?= new DB()
     props = {}
     for key, value in @setProps
       props[key] = @props[key]

-    @db ?= new DB()
-    @db.add props, (newProject) =>
-      @props._id = newProject._id
+    if @props._id
+      @db.update(props)
+    else
+      @db.add props, (newProject) =>
+        @props._id = newProject._id

   remove: ->
     @db = new DB() unless @db


reverted commit 2d0c2d9

reverted commit 2d0c2d9

--- lib/welcome.coffee
+++ lib/welcome.coffee
@@ -41,6 +41,7 @@
         @show()
         Reporter ?= require './reporter'
         Reporter.sendEvent('show-on-initial-load')
+        atom.config.set('welcome.showOnStartup', false)

   show: ->
     atom.workspace.open(WelcomeUri)


Only show welcome buffer on first launch

--- lib/welcome.coffee
+++ lib/welcome.coffee
@@ -1,6 +1,10 @@
 path = require 'path'

 module.exports =
+  configDefaults:
+    showWelcomeBuffer: true
+
   activate: ->
-    console.log path.join(__dirname, 'welcome.md')
+    return unless atom.config.get('welcom.showWelcomeBuffer')
     atom.rootView.open path.join(__dirname, 'welcome.md')
+    atom.config.set('welcom.showWelcomeBuffer', false)


Allow zero image coordinates - fixes #25

--- lib/mixins/images.coffee
+++ lib/mixins/images.coffee
@@ -5,13 +5,13 @@
         @_imageRegistry = {}
         @_imageCount = 0

-    image: (src, x, y, options) ->
+    image: (src, x, y, options = {}) ->
         if typeof x is 'object'
             options = x
             x = null

-        x = x or options?.x or @x
-        y = y or options?.y or @y
+        x = x ? options.x ? @x
+        y = y ? options.y ? @y

         if @_imageRegistry[src]
             [image, obj, label] = @_imageRegistry[src]
@@ -22,34 +22,33 @@
             label = "I" + (++@_imageCount)
             @_imageRegistry[src] = [image, obj, label]

-        w = options?.width or image.width
-        h = options?.height or image.height
+        w = options.width or image.width
+        h = options.height or image.height

-        if options
-            if options.width and not options.height
-                wp = w / image.width
-                w = image.width * wp
-                h = image.height * wp
+        if options.width and not options.height
+            wp = w / image.width
+            w = image.width * wp
+            h = image.height * wp

-            else if options.height and not options.width
-                hp = h / image.height
-                w = image.width * hp
-                h = image.height * hp
+        else if options.height and not options.width
+            hp = h / image.height
+            w = image.width * hp
+            h = image.height * hp

-            else if options.scale
-                w = image.width * options.scale
-                h = image.height * options.scale
+        else if options.scale
+            w = image.width * options.scale
+            h = image.height * options.scale

-            else if options.fit
-                [bw, bh] = options.fit
-                bp = bw / bh
-                ip = image.width / image.height
-                if ip > bp
-                    w = bw
-                    h = bw / ip
-                else
-                    h = bh
-                    w = bh * ip
+        else if options.fit
+            [bw, bh] = options.fit
+            bp = bw / bh
+            ip = image.width / image.height
+            if ip > bp
+                w = bw
+                h = bw / ip
+            else
+                h = bh
+                w = bh * ip

         # Set the current y position to below the image if it is in the document flow
         @y += h if @y is y


Adjust roundedRect() to draw circular arcs for corners - rather than distorted ones with quadraticCurveTo()

--- lib/mixins/vector.coffee
+++ lib/mixins/vector.coffee
@@ -71,15 +71,18 @@
     @addContent "#{x} #{y} #{w} #{h} re"

   roundedRect: (x, y, w, h, r = 0) ->
+    # amount to inset control points from corners (see `ellipse`)
+    c = r * (1.0 - KAPPA)
+
     @moveTo x + r, y
     @lineTo x + w - r, y
-    @quadraticCurveTo x + w, y, x + w, y + r
+    @bezierCurveTo x + w - c, y, x + w, y + c, x + w, y + r
     @lineTo x + w, y + h - r
-    @quadraticCurveTo x + w, y + h, x + w - r, y + h
+    @bezierCurveTo x + w, y + h - c, x + w - c, y + h, x + w - r, y + h
     @lineTo x + r, y + h
-    @quadraticCurveTo x, y + h, x, y + h - r
+    @bezierCurveTo x + c, y + h, x, y + h - c, x, y + h - r
     @lineTo x, y + r
-    @quadraticCurveTo x, y, x + r, y
+    @bezierCurveTo x, y + c, x + c, y, x + r, y

   ellipse: (x, y, r1, r2 = r1) ->
     # based on http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas/2173084#2173084


Fix that setting a location range didn't notify the delegate of the change

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -20,8 +20,8 @@
     else
       new Trix.LocationRange start, end

-    @currentLocationRange = locationRange
     @setDOMRange(locationRange)
+    @updateCurrentLocationRange(locationRange)

   setLocationRangeFromPoint: (point) ->
     locationRange = @getLocationRangeAtPoint(point)
@@ -80,8 +80,9 @@
   getBlockElements: ->
     @delegate?.selectionManagerDidRequestBlockElements?()

-  updateCurrentLocationRange: (domRange = getDOMRange()) ->
-    return unless locationRange = @createLocationRangeFromDOMRange(domRange)
+  updateCurrentLocationRange: (locationRange) ->
+    locationRange ?= @createLocationRangeFromDOMRange(getDOMRange())
+    return unless locationRange
     if (@currentLocationRange and not locationRange) or not locationRange?.isEqualTo(@currentLocationRange)
       @currentLocationRange = locationRange
       @delegate?.locationRangeDidChange?(@currentLocationRange)


made the docs a bit more logical

made the docs a bit more logical

--- src/scripts/opendns-umbrella.coffee
+++ src/scripts/opendns-umbrella.coffee
@@ -8,7 +8,7 @@
 #   OPENDNS_KEY - Sign up at https://investigate.opendns.com
 #
 # Commands:
-#   hubot opendns <domain> - Gets OpenDNS Domain Reputation
+#   hubot opendns whats up with <domain> - Gets OpenDNS Domain Reputation
 #   hubot opendns rr <ip> - Gets OpenDNS Resource Record history for a given IP
 #   hubot opendns secinfo <domain> - Gets OpenDNS Security Information for a given domain
 #


Fixup for focus in vim-mode-spec.coffee

Fixup for focus in vim-mode-spec.coffee

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -1,7 +1,7 @@
 RootView = require 'root-view'
 Keymap = require 'keymap'

-fdescribe "VimMode", ->
+describe "VimMode", ->
   [editor, originalKeymap] = []

   beforeEach ->


fix the use of config

fix the use of config

--- libs/npm-up.coffee
+++ libs/npm-up.coffee
@@ -140,9 +140,8 @@

     deps = prepare()

-    Promise.promisify(npm.load,
-        loaded: false
-    )()
+    Promise.promisify(npm.load)
+        loglevel: 'error'
     .then ->
         console.log 'Checking npm update...'.green
         Promise.all _.map deps, getNewVersion
@@ -218,12 +217,11 @@
 npmUpGlobal = (opts)->
     parseOpts opts

-    Promise.promisify(npm.load,
-        loaded: false
-    )()
+    Promise.promisify(npm.load)
+        loglevel: 'error'
+        global: true
     .then ->
         console.log 'Reading global packages...'.green
-        npm.config.set 'global', true
         # known issue: only the first dir will be listed in PATH
         Promise.promisify(npm.commands.ls)(null, true)
     .then (data) ->


diffsync ready message from hub always results in a retry right now, which means many pointless errors.; also slightly improve hub logging messages.

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -330,7 +330,7 @@
     # Pushing messages to this particular connected client
     #######################################################
     push_to_client: (mesg) =>
-        winston.debug("hub --> client (#{@account_id}): #{misc.trunc(to_safe_str(mesg),300)}") if mesg.event != 'pong'
+        winston.debug("hub --> client (client=#{@id}): #{misc.trunc(to_safe_str(mesg),300)}") if mesg.event != 'pong'
         @push_data_to_client(JSON_CHANNEL, to_json(mesg))

     push_data_to_client: (channel, data) ->
@@ -555,7 +555,7 @@
             winston.error("error parsing incoming mesg (invalid JSON): #{mesg}")
             return
         if mesg.event.slice(0,4) != 'ping' and mesg.event != 'codemirror_bcast'
-            winston.debug("client --> hub: #{misc.trunc(to_safe_str(mesg), 300)}")
+            winston.debug("client --> hub (client=#{@id}): #{misc.trunc(to_safe_str(mesg), 300)}")
         handler = @["mesg_#{mesg.event}"]
         if handler?
             handler(mesg)
@@ -1988,7 +1988,7 @@
                             # reconnect messages, and keep going fine, eventually.
                             @diffsync_clients = {}

-                            cb()
+                            cb()

     set_content: (content) =>
         @diffsync_server.live = content
@@ -2080,14 +2080,12 @@
                     winston.debug("CodeMirrorSession -- push_edits returned -- #{err}")

             if before != @diffsync_server.live
-                # Master view of the document changed, so suggest other clients sync
-                for id, ds of @diffsync_clients
-                    if client.id != id
-                        ds.remote.sync_ready()
-                # Also, sync new state with upstream local_hub.
-                @sync (err) =>
-                    if err
-                        @reconnect()
+                # Sync new state with upstream local_hub.
+                @sync () =>
+                    # View of the document changed and we're done syncing with upstream, so suggest other clients sync with us.
+                    for id, ds of @diffsync_clients
+                        if client.id != id
+                            ds.remote.sync_ready()

     get_snapshot: () =>
         return @diffsync_server.live  # TODO -- only ok now since is a string and not a reference...


Update pt translation

Update pt translation

--- lib/translations/pt.cson
+++ lib/translations/pt.cson
@@ -7,18 +7,18 @@
 "no" : "Não"
 "menu":
     "title": "Menu"
-    "home": "Inicio"
+    "home": "Início"
     "tags": "Etiquetas"
-    "pages": "Pages"
+    "pages": "Páginas"
     "posts": "Posts"
-    "authors": "Authors"
+    "authors": "Autores"
     "search": "Buscar"
     "bookmarks": "Favoritos"
     "socialNetworks": "Redes Sociais"
     "categories": "Categorias"
     "settings": "Configurações"
-    "customPosts": "Custom posts"
-    "customTaxonomy": "Custom Taxonomy"
+    "customPosts": "Posts customizados"
+    "customTaxonomy": "Taxonomia customizada"
 "pushNotifications":
     "newContent":
         "title": "Nova publicação!"
@@ -39,25 +39,25 @@
 "category":
     "title": "Categoria {{name}}"
 "home":
-    "title": "Inicio"
+    "title": "Início"
 "search":
     "inputPlaceholder": "Buscar"
-    "title": "Search",
+    "title": "Buscar",
     "titleQuery": "Buscar: {{query}}"
 "sharing":
     "shared": "Compartilhado!"
 "authors":
-    "title": "Authors"
+    "title": "Autores"
 "author":
-    "title": "Author: {{name}}"
+    "title": "Autor: {{name}}"
 "pages":
-    "title": "Pages"
+    "title": "Páginas"
 "posts":
-    "title": "Inicio"
+    "title": "Início"
     "empty": "Não encontrou novas publicações!"
     "featured": "Em destaque"
 "post":
-    "comments": "Comments"
+    "comments": "Comentários"
     "openInBrowser": "Abrir no navegador"
 "about":
     "title": "Sobre"
@@ -67,12 +67,12 @@
     "fontSize": "Tamanho da fonte"
     "language": "Idioma"
 "languages":
-    "en": "English"
-    "fr": "French"
-    "zh": "Chinese"
-    "es": "Spanish"
+    "en": "Inglês"
+    "fr": "Francês"
+    "zh": "Chinês"
+    "es": "Espanhol"
     "pl": "Polaco"
-    "de": "German"
+    "de": "Alemão"
     "pt": "Português"
     "it": "Italiano"
     "nl": "Holandês"
@@ -82,3 +82,4 @@
     "large": "Grande"
     "x-large": "Muito grande"
     "xx-large": "Extra grande"
+


actually, remove these pending testing

actually, remove these pending testing

--- keymaps/julia-client.cson
+++ keymaps/julia-client.cson
@@ -15,17 +15,8 @@
 '.platform-darwin atom-workspace':
   'cmd-i cmd-r': 'julia-client:open-a-repl'
   'cmd-shift-i cmd-shift-r': 'julia-client:start-repl-client'
-  'cmd-i cmd-m': 'julia-client:set-working-module'
-  'cmd-shift-i cmd-shift-m': 'julia-client:reset-working-module'

 '.platform-win32 atom-text-editor[data-grammar="source julia"]:not([mini]),
  .platform-linux atom-text-editor[data-grammar="source julia"]:not([mini])':
   'ctrl-enter': 'julia-client:evaluate'
   'ctrl-shift-enter': 'julia-client:evaluate-all'
-
-'.platform-win32 atom-workspace
- .platform-linux atom-workspace':
-  'ctrl-i ctrl-r': 'julia-client:open-a-repl'
-  'ctrl-shift-i ctrl-shift-r': 'julia-client:start-repl-client'
-  'ctrl-i ctrl-m': 'julia-client:set-working-module'
-  'ctrl-shift-i ctrl-shift-m': 'julia-client:reset-working-module'


Fix moving cursor from attachment at beginning / end of document

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -291,15 +291,17 @@
     if @editingAttachment
       locationRange = @document.getLocationRangeOfAttachment(@editingAttachment)
     else
+      originalLocationRange = @getLocationRange()
       expandedRange = @getExpandedRangeInDirection(direction)
       locationRange = @document.locationRangeFromRange(expandedRange)
+      canEditAttachment = not locationRange.isEqualTo(originalLocationRange)

     if direction is "backward"
       @setSelectionForLocationRange(locationRange.start)
     else
       @setSelectionForLocationRange(locationRange.end)

-    if expandedRange
+    if canEditAttachment
       if attachment = @getAttachmentAtLocationRange(locationRange)
         @editAttachment(attachment)


Fix deprecated selector in keymap

Fix deprecated selector in keymap

--- keymaps/to-the-hubs.cson
+++ keymaps/to-the-hubs.cson
@@ -1,4 +1,4 @@
-'.workspace':
+'atom-workspace':
   'alt-g o': 'open-on-github:file'
   'alt-g b': 'open-on-github:blame'
   'alt-g h': 'open-on-github:history'


Fixed merge conflicts

Fixed merge conflicts

--- test.coffee
+++ test.coffee
@@ -1,6 +1,5 @@
 test = require "ava"
 {IncomingMessage} = require "http"
-Stream =  require "stream"
 {Socket} = require "net"

 busboy = require "."
@@ -23,4 +22,6 @@
   bb = busboy t.context.reqMock
   t.true bb instanceof Promise

+test "Should throw an error if request parameter isn't an instance of http.IncomingMessage", (t)->
+  t.throws busboy({}), "Request parameter must be an instance of http.IncomingMessage."
   await return


fix turbolinks bug with javascripts

fix turbolinks bug with javascripts

--- app/assets/javascripts/chart_generation.coffee
+++ app/assets/javascripts/chart_generation.coffee
@@ -1,4 +1,4 @@
-$ ->
+$(document).on 'ready page:load', ->
   drawCharts(gon.chart_data.blood_pressure, 'Blood pressure')

   $(".btn-group#chart-list ul.dropdown-menu li#blood_pressure").click ->
@@ -16,4 +16,4 @@

 drawCharts = (chartData, chartTitle) ->
   new Chart($("canvas#chart").get(0).getContext("2d")).Line(chartData)
-  $("h2#chart_title").text(chartTitle)
+  $("h2#chart_title").text(chartTitle)


create basic API

create basic API

--- lib/util/progress.coffee
+++ lib/util/progress.coffee
@@ -1,10 +1,28 @@
+# TODO: move determinate progress bars to the front
+# TODO: hover UI with progress stack, descriptions
+
 module.exports =

   metres: []

+  push: (p) ->
+    if @metres.indexOf(p) == -1
+      @metres.push p
+      if @metres.length == 1
+        @showTile p
+      p.destroy = => @remove p
+      p
+
+  remove: (p) ->
+    i = @metres.indexOf p
+    @metres.splice i, 1 if i > -1
+    if @metres.length == 0
+      @hideTile()
+    if i == 0 and @metres.length > 0
+      @showTile @metres[0]
+
   consumeStatusBar: (bar) ->
     @statusBar = bar
-    @test()

   progressView: (p) ->
     span = document.createElement 'span'
@@ -24,10 +42,10 @@

     span

-  test: ->
-    @p = {progress: 0}
-    @statusBar.addLeftTile item: @progressView(@p), priority: -10
+  hideTile: ->
+    @tile?.destroy()
+    delete @tile

-  # for i in [0..100]
-  #   do (i) =>
-  #     setTimeout (=> @p.progress = i/100), i*10
+  showTile: (p) ->
+    @hideTile()
+    @tile ?= @statusBar.addLeftTile item: @progressView(p), priority: -10


chore: more quick words to log out

chore: more quick words to log out

--- init/snippets.cson
+++ init/snippets.cson
@@ -171,6 +171,14 @@
     'prefix': 'hi'
     'body': 'console.log(\'hi\');'

+  'yolo':
+    'prefix': 'yolo'
+    'body': 'console.log(\'yolo\');'
+
+  'death':
+    'prefix': 'death'
+    'body': 'console.log(\'death\');'
+
   'there':
     'prefix': 'there'
     'body': 'console.log(\'there\');'


change color of expression bar graph

change color of expression bar graph

--- static/js/controllers/designer.js.coffee
+++ static/js/controllers/designer.js.coffee
@@ -28,6 +28,53 @@
     barShowStroke: false
   }

+  expression_colors = [
+        {
+          "fillColor": "#2B333B" # dark color from sidebar
+        },
+        {
+          "fillColor": "#a0a0a0"
+        },
+        {
+          "fillColor": "#bdbdbd"
+        },
+        {
+          "fillColor": "#c7c7c7"
+        },
+        {
+          "fillColor": "#dadada"
+        },
+        {
+          "fillColor": "#f2eaea"
+        }
+      ]
+
+  if $scope.tissues_enabled
+    expression_colors = [
+        {
+          "fillColor": "#2B333B" # dark color from sidebar
+        },
+        {
+          "fillColor": "#51D2B7"
+        },
+        {
+          "fillColor": "#a0a0a0"
+        },
+        {
+          "fillColor": "#bdbdbd"
+        },
+        {
+          "fillColor": "#c7c7c7"
+        },
+        {
+          "fillColor": "#dadada"
+        },
+        {
+          "fillColor": "#f2eaea"
+        }
+      ]
+
+
   expression_options = base_options
   guide_options = base_options
   guide_options.scaleOverride = false
@@ -38,22 +85,7 @@
       'labels':  [] # set later
       'series':  ['Mean All Tissues', 'Mean Selected Tissues', 'Brain', 'Heart', 'Kidney', 'Liver', 'Skin']
       'options': expression_options
-      'colors': [
-        {
-          "fillColor": "#2B333B" # dark color from sidebar
-        },
-        {
-          "fillColor": "#51D2B7" # light gray from exon background
-        },
-        {
-          "fillColor": "rgba(224, 108, 112, 1)",
-          "strokeColor": "rgba(207,100,103,1)",
-          "pointColor": "rgba(220,220,220,1)",
-          "pointStrokeColor": "#fff",
-          "pointHighlightFill": "#fff",
-          "pointHighlightStroke": "rgba(151,187,205,0.8)"
-        }
-      ]
+      'colors': expression_colors
     },
     'guides': {
       'data':    [[]] # set later


jupyter: fix issue with language info

jupyter: fix issue with language info

--- src/smc-webapp/jupyter/store.coffee
+++ src/smc-webapp/jupyter/store.coffee
@@ -114,7 +114,7 @@
             more_output   : more_output

     get_language_info: =>
-        return @getIn(['backend_kernel_info','language_info']) ? @getIn(['settings', 'metadata', 'language_info'])
+        return @getIn(['backend_kernel_info','language_info']) ? @getIn(['metadata', 'language_info'])

     get_more_output: (id) =>
         if @_is_project


[Backport] Fixing ADMIN.PROJECT_VALUES.ERROR_DELETE_ALL translation

--- app/coffee/modules/admin/project-values.coffee
+++ app/coffee/modules/admin/project-values.coffee
@@ -293,7 +293,7 @@
             subtitle = value.name

             if _.keys(choices).length == 0
-                return $confirm.error("ADMIN.PROJECT_VALUES.ERROR_DELETE_ALL")
+                return $confirm.error($translate.instant("ADMIN.PROJECT_VALUES.ERROR_DELETE_ALL"))

             title = $translate.instant("ADMIN.COMMON.TITLE_ACTION_DELETE_VALUE")
             text = $translate.instant("ADMIN.PROJECT_VALUES.REPLACEMENT")


Store picker for destroying later

Store picker for destroying later

--- autoform-pickadate.coffee
+++ autoform-pickadate.coffee
@@ -52,7 +52,7 @@
   data = @data

   $input.pickadate data.atts.pickadateOptions
-  $picker = $input.pickadate('picker')
+  @picker = $picker = $input.pickadate('picker')

   # After selection ensure focus is lost, fixes an issue when switching tabs
   # and returning to the window would open the picker
@@ -82,9 +82,6 @@

 Template.afPickadate.destroyed = ->
-  $input  = @$('input')
-  if $input
-    $picker = $input.pickadate('picker')
-    if $picker && $picker.stop
-      $picker.stop()
+  if @picker && @picker.stop
+    @picker.stop()


delay close until queue is empty

delay close until queue is empty

--- src/dt-stream.coffee
+++ src/dt-stream.coffee
@@ -127,6 +127,9 @@
             @emit 'resume'
             @paused = no
         @queue.shift()?() while not @paused and @queue.length
+        if @closed? and not @queue.length
+            @closed?()
+            @closed = yes
         return

     # eventlisteners
@@ -153,7 +156,7 @@
     onremove: (el) ->
         # close stream if builder is already closed
         @opened_tags--
-        if @opened_tags is 0 and @builder.closed is 'pending'
+        if @opened_tags is 0 and @builder.closed is 'pending' and not @queue.length
             @closed?()
             @closed = yes
         # cleanup element


Don't activate all forms on page

Don't activate all forms on page

--- lineman/app/services/key_event_service.coffee
+++ lineman/app/services/key_event_service.coffee
@@ -26,5 +26,7 @@
       !event.ctrlKey and !event.altKey and !_.contains(['INPUT', 'TEXTAREA', 'SELECT'], active.nodeName)

     submitOnEnter: (scope) ->
-      @registerKeyEvent scope, 'pressedEnter', scope.submit, (active, event) ->
-        (event.ctrlKey or event.metaKey) and _.contains(active.classList, 'lmo-primary-form-input')
+      @registerKeyEvent scope, 'pressedEnter', scope.submit, (active, event) =>
+        (event.ctrlKey or event.metaKey) and
+        angular.element(active).scope() == scope and
+        _.contains(active.classList, 'lmo-primary-form-input')


CSS Item::keys::focus sync with browser

CSS Item::keys::focus sync with browser

--- src/renderer/impl/css/level0/textInput.coffee
+++ src/renderer/impl/css/level0/textInput.coffee
@@ -83,11 +83,11 @@

         # manage focus
         innerElem.addEventListener 'focus', (e) ->
+            self.keys.focus = true
             unless self.keys.focus
                 e.preventDefault()
         innerElem.addEventListener 'blur', (e) ->
-            if self.keys.focus
-                data.innerElem.focus()
+            self.keys.focus = false
         @keys.onFocusChange (oldVal) ->
             data.innerElem[if !oldVal then 'focus' else 'blur']()


:shirt:

:shirt:

--- src/match-iterator.coffee
+++ src/match-iterator.coffee
@@ -209,7 +209,7 @@
       # If the match occurs at the beginning of the chunk, expand the chunk
       # in case the match could have started earlier.
       break if matchStartIndex is @chunkStartIndex and @chunkStartIndex > @startIndex
-      break if matchStartIndex > @chunkEndIndex or (matchStartIndex == @endIndex and @rangeEndColumn > 0)
+      break if matchStartIndex > @chunkEndIndex or (matchStartIndex is @endIndex and @rangeEndColumn > 0)

       if matchEndIndex > @chunkEndIndex
         @regex.lastIndex = 0


mp4: Increase verbosity of MP4File.dump()

--- mp4.coffee
+++ mp4.coffee
@@ -77,7 +77,7 @@
     if not @boxes?
       throw new Error "parse() must be called before dump"
     for box in @boxes
-      process.stdout.write box.dump 0, 1
+      process.stdout.write box.dump 0, 2
     return

   getSPS: ->


take out debugger

take out debugger

--- app/controllers/settings.coffee
+++ app/controllers/settings.coffee
@@ -10,9 +10,9 @@
     sshKeyDeleted: ->
       @set('model.customSshKey', null)

-    deactivate: ->
-      console.log('deactivate')
-      debugger
+    # deactivate: ->
+    #   console.log('deactivate')
+    #   debugger

 `export default SettingsController`


FEM Lab now corrects for non-bool Task.required states (#6216)

--- app/pages/lab-fem/workflow.cjsx
+++ app/pages/lab-fem/workflow.cjsx
@@ -536,18 +536,28 @@

           {if @state.selectedTaskKey? and @props.workflow.tasks[@state.selectedTaskKey]?
-            TaskEditorComponent = taskComponents[@props.workflow.tasks[@state.selectedTaskKey].type]?.Editor
+            task = @props.workflow.tasks[@state.selectedTaskKey]
+            if task.required is 'true'
+              task.required = true
+            if task.required is 'false'
+              task.required = false
+            TaskEditorComponent = taskComponents[task.type]?.Editor
+            taskWithDefaults = {
+              required: false,
+              ...task
+            }

               {if TaskEditorComponent  # Note: ShortcutEditor deprecated (and removed entirely) for FEM-lab
                 <TaskEditorComponent
                   workflow={@props.workflow}
-                  task={@props.workflow.tasks[@state.selectedTaskKey]}
+                  task={taskWithDefaults}
                   taskPrefix="tasks.#{@state.selectedTaskKey}"
                   project={@props.project}
                   onChange={@handleTaskChange.bind this, @state.selectedTaskKey}
                 />
               else
-
Editor is not available.
}
+
Editor is not available.

+              }

@@ -574,7 +584,8 @@
           else

Choose a task to edit. The configuration for that task will appear here.

-
}
+

+          }


fixes

fixes

--- app/scripts/mbdatepicker.coffee
+++ app/scripts/mbdatepicker.coffee
@@ -14,7 +14,7 @@
   link: ($scope, $element, $attributes) ->
     scopeExpression = $attributes.outsideClick
     onDocumentClick = (event) ->
-      isChild = $element[0] == event.target.parentNode;
+      isChild = $element.find(event.target.tagName).length > 0;
       $scope.$apply scopeExpression  unless isChild
       return


Removing use of Array.filter for IE compatibility.

--- src/glyphtree.coffee
+++ src/glyphtree.coffee
@@ -360,7 +360,7 @@
       remove: (node) ->
         if node in @nodes
           node.element().remove()
-          @nodes = @nodes.filter (n) -> n isnt node
+          @nodes = (n for n in @nodes when n isnt node)
         else
           throw new Error('Node not in this container')


nice! - get first key from hash

nice! - get first key from hash

--- src/notice/capsule.coffee
+++ src/notice/capsule.coffee
@@ -7,9 +7,8 @@
         set: (opts) ->

             local = {}
-            for key of opts
-                break
-
+            break for key of opts
+
             if opts.watched?
                 if opts.protected


Try to highlight lines in log a few times before giving up

--- app/utils/lines-selector.coffee
+++ app/utils/lines-selector.coffee
@@ -31,16 +31,24 @@

   willDestroy: ->
     @location.setHash('')
+    @destroyed = true

   loadLineNumbers: (element, multiple) ->
     @setHashValueWithLine(element, multiple)
     @highlightLines()

-  highlightLines: ->
+  highlightLines: (tries) ->
+    tries ||= 0
     @removeAllHighlights()

     if lines = @getSelectedLines()
-      @element.find('p:visible').slice(lines.first - 1, lines.last).addClass('highlight')
+      elements = @element.find('p:visible').slice(lines.first - 1, lines.last)
+      if elements.length
+        elements.addClass('highlight')
+      else if tries < 4
+        Ember.run.later this, (-> @highlightLines(tries + 1) unless @destroyed) , 500
+        return
+
     @scroll.tryScroll()
     @unfoldLines()


Reorganize code

Reorganize code

--- app/views/home-page-view.coffee
+++ app/views/home-page-view.coffee
@@ -19,6 +19,9 @@
     super
     new Std()
     @skipFrame = true
+    @physicsManager = new PhysicsManager()
+    @soundManager = new SoundManager() if mediator.PlayWithSounds
+    @inputManager = new InputManager()
     @entitySpawnManager = new EntitySpawnManager()

     # developement only, will be removed in release build
@@ -27,11 +30,6 @@
     # the first level
     level = 'level1'
     mediator.levels[level] = new Level()
-
-    @physicsManager = new PhysicsManager()
-    @soundManager = new SoundManager() if mediator.PlayWithSounds
-    @inputManager = new InputManager()
-

     @loadLevel(level)


Don't return writes on HEAD requests

Don't return writes on HEAD requests

--- src/podroller/core.coffee
+++ src/podroller/core.coffee
@@ -120,13 +120,13 @@

         parser.on "id3v2", (buf) =>
             # got an ID3
-            console.log "check got an ID3!", buf
+            #console.log "check got an ID3!", buf
             #rstream.destroy()
             cb?(buf)

         parser.on "header", (h,buf) =>
             # got a frame
-            console.log "check got a header!"
+            #console.log "check got a header!"
             #rstream.destroy()
             cb?()

@@ -143,7 +143,7 @@
             # compute our final size
             fsize = (id3?.length||0) + (predata?.length||0) + size

-            console.log "id3 length is ", id3.length
+            console.log "id3 length is ", id3?.length||0

             @listeners++

@@ -155,40 +155,44 @@
                 "Content-Length":       fsize

             console.log "final size should be ", fsize
-
+
+            console.log "request method is ", req.method
+
             res.writeHead 200, headers

-            # if we have an id3, write that
-            res.write id3 if id3
+            if req.method = "HEAD"
+                res.end()
+            else
+                # if we have an id3, write that
+                res.write id3 if id3

-            # write the preroll
-            res.write predata if predata
-
-            # now set up our file read as a stream
-            console.log "creating read stream. #{@listeners} active downloads."
-            rstream = fs.createReadStream filename, bufferSize:256*1024
-
-            rstream.pipe res, end:false
-
-            rstream.on "end", =>
-                # got to the end of the file.  close our response
-                console.log "wrote #{ res.socket?.bytesWritten } bytes. #{@listeners} active downloads."
-                res.end()
-
-            req.connection.on "end", =>
-                # connection aborted.  destroy our stream
-                @listeners--
-                console.log "wrote #{ res.socket?.bytesWritten } bytes. #{@listeners} active downloads."
-                rstream?.destroy() if rstream?.reading
-
-            req.connection.on "close", =>
-                @listeners--
-                rstream?.destroy() if rstream?.reading
-
-            req.connection.setTimeout 30*1000, =>
-                # handle connection timeout
-                res.end()
-                rstream?.destroy() if rstream?.reading
+                # write the preroll
+                res.write predata if predata
+
+                # now set up our file read as a stream
+                console.log "creating read stream. #{@listeners} active downloads."
+                rstream = fs.createReadStream filename, bufferSize:256*1024
+
+                rstream.pipe res, end:false
+
+                rstream.on "end", =>
+                    # got to the end of the file.  close our response
+                    console.log "wrote #{ res.socket?.bytesWritten } bytes. #{@listeners} active downloads."
+                    res.end()
+
+                req.connection.on "end", =>
+                    # connection aborted.  destroy our stream
+                    console.log "wrote #{ res.socket?.bytesWritten } bytes. #{@listeners} active downloads."
+                    rstream?.destroy() if rstream?.readable
+
+                req.connection.on "close", =>
+                    console.log "(conn close) in close. #{@listeners} active downloads."
+                    @listeners--
+
+                req.connection.setTimeout 30*1000, =>
+                    # handle connection timeout
+                    res.end()
+                    rstream?.destroy() if rstream?.readable

     #----------


simplify variable names

simplify variable names

--- src/rules/eol_last.coffee
+++ src/rules/eol_last.coffee
@@ -10,11 +10,11 @@
     lintLine: (line, lineApi) ->
         return null unless lineApi.isLastLine()

-        lastLineIsNewline = line.length is 0
+        isNewline = line.length is 0

-        nextToLastLineIsNewline = if lineApi.lineCount > 1
+        previousIsNewline = if lineApi.lineCount > 1
             lineApi.lines[lineApi.lineNumber - 1].length is 0
         else
             no

-        return true unless lastLineIsNewline && !nextToLastLineIsNewline
+        return true unless isNewline && !previousIsNewline


Update batman.coffee

Update batman.coffee

--- src/scripts/batman.coffee
+++ src/scripts/batman.coffee
@@ -11,7 +11,8 @@
 #   hubot batman
 #
 # Author:
-#   hiking325
+#   hiking325
+#   Twitter - @GlenRuck

 batman_deny = [


Gerrit: Better control over rooms for event stream.

Use an environment variable HUBOT_GERRIT_EVENTSTREAM_ROOMS to set
specific room targets for the event stream, or disable it entirely.

--- src/scripts/gerrit.coffee
+++ src/scripts/gerrit.coffee
@@ -5,6 +5,14 @@
 # hubot gerrit ignore events for <project> - Don't spam about events happening for the specified project.
 # hubot gerrit report events for <project> - Reset Hubot to spam about events happening for the specified project.
 #
+
+# Required - The SSH URL for your Gerrit server.
+sshUrl = process.env.HUBOT_GERRIT_SSH_URL || ""
+
+# Optional - A comma separated list of rooms to receive spam about Gerrit events.
+#   If not set, messages will be sent to all room of which Hubot is a member.
+#   To disable event stream spam, use the value "disabled"
+eventStreamRooms = process.env.HUBOT_GERRIT_EVENTSTREAM_ROOMS

 cp = require "child_process"
 url = require "url"
@@ -32,12 +40,12 @@
 nameOf = (account) -> account?.name || account?.email || "Gerrit"

 module.exports = (robot) ->
-  gerrit = url.parse process.env.HUBOT_GERRIT_SSH_URL || ""
+  gerrit = url.parse sshUrl
   gerrit.port = 22 unless gerrit.port
   if gerrit.protocol != "ssh:" || gerrit.hostname == ""
     robot.logger.error "Gerrit commands inactive because HUBOT_GERRIT_SSH_URL=#{gerrit.href} is not a valid SSH URL"
   else
-    eventStreamMe robot, gerrit
+    eventStreamMe robot, gerrit unless eventStreamRooms == "disabled"
     robot.respond /gerrit (?:search|query)(?: me)? (.+)/i, searchMe robot, gerrit
     robot.respond /gerrit (ignore|report)(?: me)? events for (.+)/i, ignoreOrReportEventsMe robot, gerrit

@@ -90,14 +98,15 @@
     if msg == null
       robot.logger.info "Gerrit stream-events: Unrecognized event #{data}"
     else if msg && isWanted projectFor json
-      robot.messageRoom room, "Gerrit: #{msg}" for room in robotRooms robot
+      # Bug in messageRoom? Doesn't work with multiple rooms
+      #robot.messageRoom room, "Gerrit: #{msg}" for room in robotRooms robot
+      robot.send room: room, "Gerrit: #{msg}" for room in robotRooms robot

-# So this is kind of terrible - not sure of a better way to do this for now
 robotRooms = (robot) ->
-  roomlistish = /^HUBOT_.+_ROOMS/i
-  roomlists = (v for k,v of process.env when roomlistish.exec(k) isnt null)
-  if roomlists.length != 0
-    roomlists[0].split(",")
-  else
-    robot.logger.error "Gerrit stream-events: Unable to determine the list of rooms"
-    [ "dummy" ]
+  roomlists =
+    if eventStreamRooms
+      [ eventStreamRooms ]
+    else
+      v for k,v of process.env when /^HUBOT_.+_ROOMS/i.exec(k) isnt null
+  robot.logger.error "Gerrit stream-events: Unable to determine the list of rooms" if roomlists.length == 0
+  r for r in (roomlists[0] || "").split "," when r isnt ""


next url is in argv

next url is in argv

--- hn.coffee
+++ hn.coffee
@@ -3,7 +3,8 @@
 jquery = fs.readFileSync("./jquery.js", "utf-8")

 MORE_HREF = '\/x?fnid='
-nextUrl = '/x?fnid=vX3PX8DtnP8o25bmASKTfm' #'/news'
+nextUrl = process.argv[2] #'/news'
+
 cnt = 0
 errCnt = 0

@@ -15,7 +16,7 @@
 setInterval check, 5000

 loadPage = (url) ->
- url = '' if not url?
+ url = '/news' if not url?
  time = new Date().getTime()
  jsdom.env
    url: "http://news.ycombinator.com#{url}",


Improved the refresh rate for the color tab, and made it work with containers.

--- app/views/editor/thang/colors_tab_view.coffee
+++ app/views/editor/thang/colors_tab_view.coffee
@@ -55,7 +55,7 @@
     animations = (a.animation for key, a of actionDict when a.animation)
     index = @offset % animations.length
     animation = animations[index]
-    return unless animation
+    return @updateContainer() unless animation
     @stage.removeChild(@movieClip) if @movieClip
     options = {colorConfig: {}}
     options.colorConfig[@currentColorGroupTreema.keyForParent] = @colorConfig
@@ -68,6 +68,23 @@
     @movieClip.regX = @movieClip.nominalBounds.x
     @movieClip.regY = @movieClip.nominalBounds.y
     @stage.addChild @movieClip
+
+  updateContainer: ->
+    actionDict = @thangType.getActions()
+    idle = actionDict.idle
+    @stage.removeChild(@container) if @container
+    return unless idle?.container
+    options = {colorConfig: {}}
+    options.colorConfig[@currentColorGroupTreema.keyForParent] = @colorConfig
+    @spriteBuilder.setOptions options
+    @spriteBuilder.buildColorMaps()
+    @container = @spriteBuilder.buildContainerFromStore idle.container
+    larger = Math.min(400 / @container.bounds.width, 400 / @container.bounds.height)
+    @container.scaleX = larger
+    @container.scaleY = larger
+    @container.regX = @container.bounds.x
+    @container.regY = @container.bounds.y
+    @stage.addChild @container

   createShapeButtons: ->
     buttons = $('

').prop('id', 'shape-buttons')
@@ -145,6 +162,7 @@
       shapes.push(key) if colors[shape.fc]

     @currentColorGroupTreema.set('/', shapes)
+    @updateMovieClip()

 class ColorGroupNode extends TreemaNode.nodeMap.array
   collection: false


Extracted some methods and did some cleanup

--- core/app/backbone/modules/discussion_modal_on_frontend.coffee
+++ core/app/backbone/modules/discussion_modal_on_frontend.coffee
@@ -12,16 +12,19 @@

   background_page_url = null

-  shouldCloseDiscussionModal = (fragment) ->
-    !FactlinkApp.showFactRegex.test(fragment) && FactlinkApp.discussionModalRegion.currentView?
+  openingModalPage = (fragment) ->
+    FactlinkApp.showFactRegex.test(fragment)
+
+  modalCurrentlyOpened = ->
+    FactlinkApp.discussionModalRegion.currentView?

   setBackgroundPageUrlHook = (fragment) ->
-    unless FactlinkApp.showFactRegex.test(fragment) || background_page_url == fragment
+    unless openingModalPage(fragment)
       background_page_url = fragment
     true

   abortIfAlreadyOnBackgroundPageHook = (fragment) ->
-    if shouldCloseDiscussionModal(fragment)
+    if !openingModalPage(fragment) && modalCurrentlyOpened()
       DiscussionModalOnFrontend.closeDiscussion()

       already_on_the_background_page = (fragment == background_page_url)


jupyter: if cell is deleted but still running and produces output... bring it back properly.

--- src/smc-webapp/jupyter/project-actions.coffee
+++ src/smc-webapp/jupyter/project-actions.coffee
@@ -306,8 +306,12 @@

         @ensure_backend_kernel_setup()

-        cell   = @store.get('cells').get(id)
-        input  = (cell.get('input') ? '').trim()
+        orig_cell   = @store.get('cells').get(id)
+        if not orig_cell?
+            # nothing to do -- cell deleted
+            return
+
+        input  = (orig_cell.get('input') ? '').trim()

         @_running_cells ?= {}

@@ -331,6 +335,13 @@
         handler = @_output_handler(cell)

         handler.on 'change', (save) =>
+            if not @store.getIn(['cells', id])
+                # The cell was deleted, but we just got some output
+                # NOTE: client shouldn't allow deleting running or queued
+                # cells, but we still want to do something useful/sensible.
+                # We put cell back where it was with same input.
+                cell.input = orig_cell.get('input')
+                cell.pos   = orig_cell.get('pos')
             @syncdb.set(cell, save)

         handler.once 'done', =>
@@ -367,7 +378,7 @@
                     handler.error(err)
                     return
                 if mesg.done
-                    # special internal cocalc message.
+                    # done is a special internal cocalc message.
                     handler.done()
                     return
                 if mesg.msg_type == 'clear_output'


add onreadystatechange to document

add onreadystatechange to document

The purpose of this is to eliminate the need for the user to include <body onload='init()'> in their html page. It still needs to be tested.

--- src/greenhorn.coffee
+++ src/greenhorn.coffee
@@ -24,6 +24,8 @@
 @keysDown = (key = false for key in (new Array(256)))

 #document event handlers
+document.onreadystatechange = ->
+    if document.readystate is 'complete' then init?()
 document.onkeydown = (e) ->
     e.preventDefault()
     keysDown[e.keyCode] = true


Added download link and logger to loop

Added download link and logger to loop

--- source/Downloader.coffee
+++ source/Downloader.coffee
@@ -69,3 +69,5 @@
     winston.info('Fetched links.')
     $('.item').find('.desc').each (i, element) ->
       titles.push($(this).text().trim())
+      downloads.push($(links[i]).attr('data-url'))
+      winston.info('Parsed title.')


Fix toolbar link/strike button order

Fix toolbar link/strike button order

--- src/trix/elements/trix_toolbar_element.coffee
+++ src/trix/elements/trix_toolbar_element.coffee
@@ -19,8 +19,8 @@

       <button type="button" class="bold" data-attribute="bold" data-key="b">Bold</button>
       <button type="button" class="italic" data-attribute="italic" data-key="i">Italic</button>
+      <button type="button" class="link" data-attribute="href" data-action="link" data-key="k">Link</button>
       <button type="button" class="strike" data-attribute="strike">Strike</button>
-      <button type="button" class="link" data-attribute="href" data-action="link" data-key="k">Link</button>


Add getGroups function to db and fixes

Add getGroups function to db and fixes

Added a getGroups function to the db module.

Changed the get function so it returns the source documents of each
elastic search document, instead of including the whole elastic search
documents to the calling module.

--- app/db.coffee
+++ app/db.coffee
@@ -128,12 +128,35 @@
 			query: filtered: filter: range: date: gt: 'now-3h'
 			sort: date: order: "asc"
 	.then(
-		(events) ->
-			query.onSuccess events.hits.hits
+		(esEvents) ->
+			events = []
+			for event in esEvents.hits.hits
+				events.push event._source
+			query.onSuccess events
 		(error) ->
 			query.onError
 				error: error
 				module 'db')
+
+
+# Get all groups from group index
+# @param [object] query
+# @option query [function] onSuccess
+# @option query [function] onError
+getGroups = (query) ->
+	client.search
+		index: 'groups'
+		body: query: match_all: {}
+	.then(
+		(esGroups) ->
+			groups = []
+			for group in esGroups.hits.hits
+				groups.push group._source
+			query.onSuccess groups
+		(error) ->
+			query.onError
+				error: error
+				module: 'db')

 # Export set and setGroup functions
@@ -141,3 +164,4 @@
 	'set': set
 	'setGroup': setGroup
 	'get': get
+	'getGroups': getGroups