Better way to delete cookies on logout

Better way to delete cookies on logout

This fixes #653 #560

--- src/main.coffee
+++ src/main.coffee
@@ -42,14 +42,16 @@
 logout = ->
     promise = client.logout()
     promise.then (res) ->
-      argv = process.argv
-      fs.unlinkSync(paths.chromecookie) if fs.existsSync(paths.chromecookie)
-      spawn = require('child_process').spawn
-      spawn argv.shift(), argv,
-        cwd: process.cwd
-        env: process.env
-        stdio: 'inherit'
-      quit()
+        argv = process.argv
+        spawn = require('child_process').spawn
+        # remove electron cookies
+        mainWindow?.webContents?.session?.clearStorageData([], (data) -> console.log(data))
+        spawn argv.shift(), argv,
+            cwd: process.cwd
+            env: process.env
+            detached: true
+            stdio: 'inherit'
+        quit()
     return promise # like it matters

 seqreq = require './seqreq'


fix for connected

fix for connected

--- src/main.coffee
+++ src/main.coffee
@@ -215,6 +215,10 @@
         console.log 'syncrecent'
         client.syncrecentconversations().then (r) ->
             ipcsend 'syncrecentconversations:response', r
+            # this is because we use syncrecent on reqinit (dev-mode
+            # refresh). if we succeeded getting a response, we call it
+            # connected.
+            ipcsend 'connected'
     , false, (ev, time) -> 1

     # propagate these events to the renderer


work in progress: integrating markdown and gui code

--- src/main.coffee
+++ src/main.coffee
@@ -23,6 +23,7 @@
     textarea.appendTo('#App')

     outputarea = $('<div>')
+    outputarea.attr('id', 'outputarea');
     outputarea.html(markdown('hello *world*'))
     outputarea.css
       position: 'absolute'
@@ -51,10 +52,18 @@

 execute = (code) -> (
   md = markdown(code);
-  outputarea.text(md);
+  outputarea.html(md);
+  js = '';
+  n = 0;
+  $('#outputarea code').parent('pre').replaceWith(-> (
+    js += $(this).children().text()
+    replacement = $('<div>')
+    replacement.attr('id', 'js' + n)
+    replacement
+  ))

-#  f = eval(code)
-#  run(f, outputarea)
+  f = eval('(function() {' + js + '})')
+  run(f, $('#js0'))
 )

 showOrHideCode = (-> (


Switch to blockchain.info over SSL

Switch to blockchain.info over SSL

--- client/tx/lib/blockchain/bci.coffee
+++ client/tx/lib/blockchain/bci.coffee
@@ -9,7 +9,7 @@

 # Load unspent inputs (from blockchain.info)
 load_unspent = (address, cb) ->
-  xhr = $.get "http://blockchain.info/unspent?active=#{address}&cors=true"
+  xhr = $.get "https://blockchain.info/unspent?active=#{address}&cors=true"
   xhr.done (res) ->
     if res.unspent_outputs
       unspent = for { tx_hash, tx_output_n, value, script, confirmations } in res.unspent_outputs when confirmations > 0


Expose AjaxQueue to the window

Expose AjaxQueue to the window

--- src/extended-ajax.coffee
+++ src/extended-ajax.coffee
@@ -43,3 +43,5 @@
 		promise.abort = abortFunction

 		return promise
+
+window.AjaxQueue = AjaxQueue


Disallow activation of destroyed panes

Disallow activation of destroyed panes

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@gmail.com>

--- src/pane.coffee
+++ src/pane.coffee
@@ -502,6 +502,8 @@

   # Public: Makes this pane the *active* pane, causing it to gain focus.
   activate: ->
+    throw new Error("Pane has been destroyed") if @isDestroyed()
+
     @container?.setActivePane(this)
     @emit 'activated'
     @emitter.emit 'did-activate'


Add comments to pane.coffee

Add comments to pane.coffee

--- src/pane.coffee
+++ src/pane.coffee
@@ -6,10 +6,15 @@
 PaneRow = require 'pane-row'
 PaneColumn = require 'pane-column'

+# Public: A container which can contains multiple items to be switched between.
+#
+# Items can be almost anything however most commonly they're {Editors}.
+#
+# Most packages won't need to use this class, unless you're interested in
+# building a package that deals with switching between panes or tiems.
 module.exports =
 class Pane extends View

-  ### Internal ###
   @acceptsDocuments: true

   @content: (wrappedView) ->
@@ -26,6 +31,7 @@
   viewsByClassName: null # Views with a setModel() method are stored here
   viewsByItem: null      # Views without a setModel() method are stored here

+  # Private:
   initialize: (args...) ->
     if args[0] instanceof telepath.Document
       @state = args[0]
@@ -80,6 +86,7 @@
     @on 'focusin', => @makeActive()
     @on 'focusout', => @autosaveActiveItem()

+  # Private:
   afterAttach: (onDom) ->
     if @focusOnAttach and onDom
       @focusOnAttach = null
@@ -89,8 +96,8 @@
     @attached = true
     @trigger 'pane:attached', [this]

-  ### Public ###
-
+
+  # Public: Focus this pane.
   makeActive: ->
     for pane in @getContainer().getPanes() when pane isnt this
       pane.makeInactive()
@@ -98,23 +105,28 @@
     @addClass('active')
     @trigger 'pane:became-active' unless wasActive

+  # Public: Unfocus this pane.
   makeInactive: ->
     wasActive = @isActive()
     @removeClass('active')
     @trigger 'pane:became-inactive' if wasActive

+  # Public: Returns whether this pane is currently focused.
   isActive: ->
     @hasClass('active')

+  # Public: Returns the next pane, ordered by creation.
   getNextPane: ->
     panes = @getContainer()?.getPanes()
     return unless panes.length > 1
     nextIndex = (panes.indexOf(this) + 1) % panes.length
     panes[nextIndex]

+  # Public: Returns all contained views.
   getItems: ->
     new Array(@items...)

+  # Public: Switches to the next contained item.
   showNextItem: =>
     index = @getActiveItemIndex()
     if index < @items.length - 1
@@ -122,6 +134,7 @@
     else
       @showItemAtIndex(0)

+  # Public: Switches to the previous contained item.
   showPreviousItem: =>
     index = @getActiveItemIndex()
     if index > 0
@@ -129,15 +142,19 @@
     else
       @showItemAtIndex(@items.length - 1)

+  # Public: Returns the index of the currently active item.
   getActiveItemIndex: ->
     @items.indexOf(@activeItem)

+  # Public: Switch to the item associated with the given index.
   showItemAtIndex: (index) ->
     @showItem(@itemAtIndex(index))

+  # Public: Returns the item at the specified index.
   itemAtIndex: (index) ->
     @items[index]

+  # Public: Focuses the given item.
   showItem: (item) ->
     return if !item? or item is @activeItem

@@ -159,9 +176,11 @@

     @state.set('activeItemUri', item.getUri?())

+  # Private:
   activeItemTitleChanged: =>
     @trigger 'pane:active-item-title-changed'

+  # Public: Add an additional item at the specified index.
   addItem: (item, index=@getActiveItemIndex()+1, options={}) ->
     return if _.include(@items, item)

@@ -171,10 +190,12 @@
     @trigger 'pane:item-added', [item, index]
     item

+  # Public: Remove the currently active item.
   destroyActiveItem: =>
     @destroyItem(@activeItem)
     false

+  # Public: Remove the specified item.
   destroyItem: (item) ->
     container = @getContainer()
     reallyDestroyItem = =>
@@ -189,12 +210,15 @@
     else
       reallyDestroyItem()

+  # Public: Remove and delete all items.
   destroyItems: ->
     @destroyItem(item) for item in @getItems()

+  # Public: Remove and delete all but the currently focused item.
   destroyInactiveItems: ->
     @destroyItem(item) for item in @getItems() when item isnt @activeItem

+  # Public: Prompt the user to save the given item.
   promptToSaveItem: (item) ->
     uri = item.getUri()
     currentWindow = require('remote').getCurrentWindow()
@@ -209,12 +233,15 @@
       when 1 then false
       when 2 then true

+  # Public: Saves the currently focused item.
   saveActiveItem: =>
     @saveItem(@activeItem)

+  # Public: Save and prompt for path for the currently focused item.
   saveActiveItemAs: =>
     @saveItemAs(@activeItem)

+  # Public: Saves the specified item and call the next action when complete.
   saveItem: (item, nextAction) ->
     if item.getUri?()
       item.save?()
@@ -222,6 +249,8 @@
     else
       @saveItemAs(item, nextAction)

+  # Public: Prompts for path and then saves the specified item. Upon completion
+  # it also calls the next action.
   saveItemAs: (item, nextAction) ->
     return unless item.saveAs?

@@ -232,19 +261,24 @@
       item.saveAs(path)
       nextAction?()

+  # Public: Saves all items in this pane.
   saveItems: =>
     @saveItem(item) for item in @getItems()

+  # Public: Autosaves the currently focused item.
   autosaveActiveItem: ->
     @autosaveItem(@activeItem)

+  # Public: Autosaves the given focused item if configured to do so.
   autosaveItem: (item) ->
     @saveItem(item) if config.get('core.autosave') and item.getUri?()

+  # Public: Autosaves the given focused item if configured to do so.
   removeItem: (item) ->
     index = @items.indexOf(item)
     @removeItemAtIndex(index) if index >= 0

+  # Public: Just remove the item at the given index.
   removeItemAtIndex: (index, options={}) ->
     item = @items[index]
     @showNextItem() if item is @activeItem and @items.length > 1
@@ -253,6 +287,7 @@
     @cleanupItemView(item)
     @trigger 'pane:item-removed', [item, index]

+  # Public: Moves the given item to a the new index.
   moveItem: (item, newIndex) ->
     oldIndex = @items.indexOf(item)
     @items.splice(oldIndex, 1)
@@ -261,15 +296,18 @@
     @state.get('items').splice(newIndex, 0, item.getState?() ? item.serialize())
     @trigger 'pane:item-moved', [item, newIndex]

+  # Public: Moves the given item to another pane.
   moveItemToPane: (item, pane, index) ->
     @isMovingItem = true
     @removeItem(item)
     @isMovingItem = false
     pane.addItem(item, index)

+  # Public: Finds the first item that matches the given uri.
   itemForUri: (uri) ->
     _.detect @items, (item) -> item.getUri?() is uri

+  # Public: Focuses the first item that matches the given uri.
   showItemForUri: (uri) ->
     if item = @itemForUri(uri)
       @showItem(item)
@@ -277,6 +315,7 @@
     else
       false

+  # Private:
   cleanupItemView: (item) ->
     if item instanceof $
       viewToRemove = item
@@ -300,6 +339,7 @@
       viewToRemove?.detach() if @isMovingItem and item is viewToRemove
       @parent().view().removeChild(this, updateState: false)

+  # Private:
   viewForItem: (item) ->
     if item instanceof $
       item
@@ -317,35 +357,46 @@
           @viewsByItem.set(item, view)
       view

+  # Private:
   viewForActiveItem: ->
     @viewForItem(@activeItem)

+  # Private:
   serialize: ->
     state = @state.clone()
     state.set('items', item.serialize() for item, index in @items)
     state.set('focused', @is(':has(:focus)'))
     state

+  # Private:
   getState: -> @state

+  # Private:
   adjustDimensions: -> # do nothing

+  # Private:
   horizontalGridUnits: -> 1

+  # Private:
   verticalGridUnits: -> 1

+  # Public: Creates a new pane above with a copy of the currently focused item.
   splitUp: (items...) ->
     @split(items, 'column', 'before')

+  # Public: Creates a new pane below with a copy of the currently focused item.
   splitDown: (items...) ->
     @split(items, 'column', 'after')

+  # Public: Creates a new pane left with a copy of the currently focused item.
   splitLeft: (items...) ->
     @split(items, 'row', 'before')

+  # Public: Creates a new pane right with a copy of the currently focused item.
   splitRight: (items...) ->
     @split(items, 'row', 'after')

+  # Private:
   split: (items, axis, side) ->
     PaneContainer = require 'pane-container'

@@ -372,21 +423,26 @@
     newPane.focus()
     newPane

+  # Private:
   buildPaneAxis: (axis) ->
     switch axis
       when 'row' then new PaneRow()
       when 'column' then new PaneColumn()

+  # Private:
   getContainer: ->
     @closest('#panes').view()

+  # Private:
   copyActiveItem: ->
     @activeItem.copy?() ? deserialize(@activeItem.serialize())

+  # Private:
   remove: (selector, keepData) ->
     return super if keepData
     @parent().view().removeChild(this)

+  # Private:
   beforeRemove: ->
     if @is(':has(:focus)')
       @getContainer().focusNextPane() or rootView?.focus()


Rename to match pattern.

Rename to match pattern.

--- js/src/http/UserDirectoryApi.coffee
+++ js/src/http/UserDirectoryApi.coffee
@@ -22,8 +22,8 @@
   getUsersUrl  = -> Configuration.get('heraclesUrl') + '/directory/users'
   usersInRealmUrl = -> Configuration.get('heraclesUrl') + '/directory'
   setFirstLoginUrl = -> Configuration.get('heraclesUrl') + '/directory/setlogin'
-  getConfirmation = -> Configuration.get('heraclesUrl') + '/registration/confirmation/resend'
-  getVerification = -> Configuration.get('heraclesUrl') + '/registration/verification/'
+  getConfirmationUrl = -> Configuration.get('heraclesUrl') + '/registration/confirmation/resend'
+  getVerificationUrl = -> Configuration.get('heraclesUrl') + '/registration/verification/'
   getUserIdFromEmail = (email) -> Configuration.get('heraclesUrl') + '/directory/validate/sharing/email/' + email
   getUserSettingUrl = (userSetting) -> getUserUrl() + '/setting/' + userSetting

@@ -200,7 +200,7 @@
     resendConfirmationEmail: (credentials = {}) ->
       Promise.resolve(axios(
         Requests.wrapCredentials({
-          url: getConfirmation()
+          url: getConfirmationUrl()
           method: 'GET'
         }, credentials)
       ))
@@ -208,7 +208,7 @@
     sendConfirmationToken: (uuid, token) ->
       Promise.resolve(axios(
         Requests.wrapCredentials({
-          url: getVerification() + uuid + '/' + token
+          url: getVerificationUrl() + uuid + '/' + token
           method: 'GET'
         })
       ))


bugfix: dont capture keys if on inputs.

bugfix: dont capture keys if on inputs.

--- coffee/src/views/player_view.coffee
+++ coffee/src/views/player_view.coffee
@@ -20,11 +20,15 @@

   events: => _.extend super,
     'keydown': (event) =>
+      # skip if focused on input elems
+      if /(input|textarea|select)/i.test event.target.tagName
+        return event
+
       name = _.invert(athena.lib.util.keys)[event.keyCode]
       @eventhub.trigger "Keypress:#{name}"
       if name is 'SPACEBAR'
         event.preventDefault()
-        return false
+      return event

   initialize: =>


Handle dirty git repo scenarios

Handle dirty git repo scenarios

If diversity-push created a new scripts.min.js for testing, this file must be removed again.
If diversity-push finds translation changes these must be committed, and diversity-push should exit nicely.

--- src/push.coffee
+++ src/push.coffee
@@ -67,9 +67,19 @@
     runTest 'gulp jshint'
     runTest 'gulp lint-css:style-names'
     runTest 'gulp lint-css:doiuse'
-    runTest 'gulp translations-update-fail-on-incomplete'
+    runTest 'gulp translations-update-fail-on-incomplet'
     runTest 'gulp protractor:single-run'
     runTest 'gulp karma:single-run'
+
+    # Restore scripts.min.js
+    runCommand 'git checkout scripts.min.js'
+
+    # Are there changes in working tree? Than abort and ask the user to fix things.
+    # Get number of total uncommited files
+    dirty = runCommand('expr $(git status --porcelain 2>/dev/null| egrep "^(M| M)" | wc -l)').output
+    if dirty
+      console.log 'You have unstaged changes that you must take care of. Fix and commit this and then run diversity-push again.'
+      shell.exit 1

     diversityData = JSON.parse readDiversity(settings.diversityPath)
     versionArray = diversityData.version.split '.'


Fix usage of attribute/property terminology

--- lib/assets/javascripts/twine.coffee
+++ lib/assets/javascripts/twine.coffee
@@ -187,7 +187,7 @@
     addKey(rootContext) if node == rootNode
     keys.join('.')

-  valueAttributeForNode = (node) ->
+  valuePropertyForNode = (node) ->
     name = node.nodeName.toLowerCase()
     if name in ['input', 'textarea', 'select']
       if node.getAttribute('type') in ['checkbox', 'radio'] then 'checked' else 'value'
@@ -282,8 +282,8 @@

   Twine.bindingTypes =
     bind: (node, context, definition) ->
-      valueAttribute = valueAttributeForNode(node)
-      value = node[valueAttribute]
+      valueProp = valuePropertyForNode(node)
+      value = node[valueProp]
       lastValue = undefined
       teardown = undefined

@@ -296,9 +296,9 @@
         return if newValue == lastValue # return if we can and avoid a DOM operation

         lastValue = newValue
-        return if newValue == node[valueAttribute]
-
-        node[valueAttribute] = if checkedValueType then newValue == node.value else newValue
+        return if newValue == node[valueProp]
+
+        node[valueProp] = if checkedValueType then newValue == node.value else newValue
         fireCustomChangeEvent(node)

       return {refresh} unless isKeypath(definition)
@@ -308,10 +308,10 @@
           return unless node.checked
           setValue(context, keypath, node.value)
         else
-          setValue(context, keypath, node[valueAttribute])
+          setValue(context, keypath, node[valueProp])

       keypath = keypathForKey(node, definition)
-      twoWayBinding = valueAttribute != 'textContent' && node.type != 'hidden'
+      twoWayBinding = valueProp != 'textContent' && node.type != 'hidden'

       if keypath[0] == '$root'
         context = rootContext
@@ -322,7 +322,7 @@

       if twoWayBinding
         changeHandler = ->
-          return if getValue(context, keypath) == this[valueAttribute]
+          return if getValue(context, keypath) == this[valueProp]
           refreshContext()
           Twine.refreshImmediately()
         $(node).on 'input keyup change', changeHandler
@@ -383,24 +383,24 @@

     indexes

-  setupAttributeBinding = (attributeName, bindingName) ->
-    booleanAttribute = attributeName in ['checked', 'indeterminate', 'disabled', 'readOnly']
+  setupPropertyBinding = (attributeName, bindingName) ->
+    booleanProp = attributeName in ['checked', 'indeterminate', 'disabled', 'readOnly']

     Twine.bindingTypes["bind-#{bindingName}"] = (node, context, definition) ->
       fn = wrapFunctionString(definition, '$context,$root,$arrayPointers', node)
       lastValue = undefined
       return refresh: ->
         newValue = fn.call(node, context, rootContext, arrayPointersForNode(node, context))
-        newValue = !!newValue if booleanAttribute
+        newValue = !!newValue if booleanProp
         return if newValue == lastValue
         node[attributeName] = lastValue = newValue

         fireCustomChangeEvent(node) if attributeName == 'checked'

   for attribute in ['placeholder', 'checked', 'indeterminate', 'disabled', 'href', 'title', 'readOnly', 'src']
-    setupAttributeBinding(attribute, attribute)
-
-  setupAttributeBinding('innerHTML', 'unsafe-html')
+    setupPropertyBinding(attribute, attribute)
+
+  setupPropertyBinding('innerHTML', 'unsafe-html')

   preventDefaultForEvent = (event) ->
     (event.type == 'submit' || event.currentTarget.nodeName.toLowerCase() == 'a') &&


use clearSuggestions

use clearSuggestions

--- lib/atom-ternjs-autocomplete.coffee
+++ lib/atom-ternjs-autocomplete.coffee
@@ -41,8 +41,7 @@
         return new Promise (resolve) ->
             that.client.update(options.editor.getURI(), options.editor.getText()).then =>
                 that.client.completions(options.editor.getURI(), {line: options.position.row, ch: options.position.column}).then (data) =>
-                    that.suggestionsArr = []
-                    that.currentSuggestionIndex = 0
+                    that.clearSuggestions()
                     if !data.completions.length
                         resolve([])
                         that.documentationView.hide()
@@ -89,8 +88,13 @@
         @autocompletePlus.autocompleteManager.runAutocompletion()
         @force = false

+    clearSuggestions: ->
+        @suggestionsArr = []
+        @currentSuggestionIndex = 0
+
     forceCancel: ->
         @autocompletePlus.autocompleteManager.hideSuggestionList()
+        @clearSuggestions()

     hideDocumentation: ->
         @documentationView.hide()
@@ -107,9 +111,11 @@
     registerEvents: ->
         @disposables.push atom.config.observe('autocomplete-plus.maxSuggestions', => maxItems = atom.config.get('autocomplete-plus.maxSuggestions'))
         @disposables.push @autocompletePlus.autocompleteManager.suggestionList.emitter.on 'did-cancel', =>
-            @hideDocumentation()
+            @clearSuggestions()
+            @documentationView.hide()
         @disposables.push @autocompletePlus.autocompleteManager.suggestionList.emitter.on 'did-confirm', =>
-            @hideDocumentation()
+            @clearSuggestions()
+            @documentationView.hide()
         @disposables.push @autocompletePlus.autocompleteManager.suggestionList.emitter.on 'did-select-next', =>
             if ++@currentSuggestionIndex >= @getMaxIndex()
                 @currentSuggestionIndex = 0


removing the extra newline that block comments take

--- nodes.coffee
+++ nodes.coffee
@@ -448,7 +448,7 @@
   makeReturn:      THIS

   compileNode: (o, level) ->
-    code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/\n"
+    code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/"
     code = o.indent + code if (level or o.level) is LEVEL_TOP
     code


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -10,7 +10,7 @@
         #console.log('@instanceDataId',@instanceDataId)
         @oldPieData[@instanceDataId] = ''
         window.oldPieData = window.oldPieData or {}
-        windows.oldPieData[@instanceDataId] = windows.oldPieData[@instanceDataId] or {}
+        window.oldPieData[@instanceDataId] = window.oldPieData[@instanceDataId] or {}

     onData: (data) ->
         #console.log('testData',@testData)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -18,9 +18,9 @@
     #mFontSize = 28
     width = 750 #width
     height = 450 #height
-    radiuso = 120 #outer radius
-    radiusi = 80 #inner radius
-    labelRadius = 160
+    radiuso = 135 #outer radius
+    radiusi = 90 #inner radius
+    labelRadius = 180

     #color = d3.scale.ordinal()
       #.domain([1,10])


Agent pie: edit pointer transition

Agent pie: edit pointer transition

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -320,7 +320,7 @@
         #            return 'M' + (d.r - 2) + ',' + d.b + 'L' + (d.l + 2) + ',' + d.b + ' ' + d.cx + ',' + d.cy
         #    )
         #    #.style('opacity', 1)
-        console.log('pre-transition d: ',pointers.d)
+        console.log('pre-transition d: ',piedata.d)
         pointers.transition().duration(2000).attr("d", (d) ->
                 if d.cx > d.l
                     console.log('if: ','M' + (d.l + 2) + ',' + d.b + 'L' + (d.r - 2) + ',' + d.b + ' ' + d.cx + ',' + d.cy)
@@ -329,7 +329,7 @@
                     console.log('else: ','M' + (d.r - 2) + ',' + d.b + 'L' + (d.l + 2) + ',' + d.b + ' ' + d.cx + ',' + d.cy)
                     'M' + (d.r - 2) + ',' + d.b + 'L' + (d.l + 2) + ',' + d.b + ' ' + d.cx + ',' + d.cy
             )
-        console.log('post-transition d: ',pointers.d)
+        console.log('post-transition d: ',piedata.d)

         # Display total count
         totalTickets = 0


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -229,9 +229,10 @@
         #        return arc b
         #)
         path.transition().duration(2000).attrTween('d', (d,i) ->
+            console.log('window.oldPieData['+myInstanceDataId+']['+i+']: ',window.oldPieData[myInstanceDataId][i])
             window.oldPieData[myInstanceDataId][i] = window.oldPieData[myInstanceDataId][i] ? []
             window.oldPieData[myInstanceDataId][i] = d
-            console.log('window.oldPieData['+myInstanceDataId+']['+i+']',window.oldPieData[myInstanceDataId][i])
+            console.log('This is the real pie data: ',d)

             #if myInstanceDataId = 'resolved' and i = 0
             #    console.log('in tween - OLD DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])


Added jade support

Added jade support


Add some loading messages

Add some loading messages

--- lib/installed-packages-panel.coffee
+++ lib/installed-packages-panel.coffee
@@ -23,20 +23,20 @@
         @h2 class: 'section-heading icon icon-package', =>
           @text 'Community Packages'
           @span outlet: 'communityCount', ' (…)'
-        @div outlet: 'communityPackages', class: 'container package-container'
-
+        @div outlet: 'communityPackages', class: 'container package-container', =>
+          @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading packages…"
       @div class: 'section core-packages', =>
         @h2 class: 'section-heading icon icon-package', =>
           @text 'Core Packages'
           @span outlet: 'coreCount', ' (…)'
-        @div outlet: 'corePackages', class: 'container package-container'
-
+        @div outlet: 'corePackages', class: 'container package-container', =>
+          @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading packages…"
       @div class: 'section dev-packages', =>
         @h2 class: 'section-heading icon icon-package', =>
           @text 'Development Packages'
           @span outlet: 'devCount', ' (…)'
-        @div outlet: 'devPackages', class: 'container package-container'
-
+        @div outlet: 'devPackages', class: 'container package-container', =>
+          @div class: 'alert alert-info loading-area icon icon-hourglass', "Loading packages…"

   initialize: (@packageManager) ->
     @packageViews = []


Fix multiline autocomplete when mix of :: and -> #287

--- lib/services/php-file-parser.coffee
+++ lib/services/php-file-parser.coffee
@@ -522,8 +522,7 @@
         # Get the full text
         return [] if not text

-        elements = text.split(/(?:\-\>|::)/)
-        # elements = text.split("->")
+        elements = text.replace(/\([^\)]*\)/g, '').split(/(?:\-\>|::)/)

         if elements.length == 1
           @isFunction = true


Support passing directory to getRootDir

Support passing directory to getRootDir

--- src/util.coffee
+++ src/util.coffee
@@ -42,9 +42,15 @@
           bufferOrFileOrString.file
         when bufferOrFileOrString instanceof File
           bufferOrFileOrString
+        when bufferOrFileOrString instanceof Directory
+          bufferOrFileOrString
         when typeof bufferOrFileOrString is 'string'
           new File(bufferOrFileOrString)
-    dir = file?.getParent?() ? Util.getRootDirFallback file
+    dir =
+      if Util.isDirectory(file)
+        file
+      else
+        file?.getParent?() ? Util.getRootDirFallback file
     dir = findProjectRoot(dir, dirHasCabalFile) ? findProjectRoot(dir, dirHasSandboxFile)
     unless Util.isDirectory(dir)
       dir = Util.getRootDirFallback file


Added underscore isArray function.

Added underscore isArray function.

--- src/util.coffee
+++ src/util.coffee
@@ -8,6 +8,12 @@

 # Expose an object with a bunch of utility functions on it.
 util.factory "util", ->
+  # Underscore function section
+  ArrayProto    = Array.prototype
+  ObjProto      = Object.prototype
+  FuncProto     = Function.prototype
+  toString      = ObjProto.toString
+  nativeIsArray = Array.isArray

   _inflectionConstants:
     uncountables: [
@@ -67,6 +73,8 @@
   capitalize:   (string) -> string[0].toUpperCase() + string[1..]
   uncapitalize: (string) -> string[0].toLowerCase() + string[1..]

+  isArray: nativeIsArray or (obj) -> toString.call(obj) is "[object Array]"
+
   ##
   ## @name
   ## extendAttributes


Throw warning on missing task

Throw warning on missing task

--- src/invoke/invoke.coffee
+++ src/invoke/invoke.coffee
@@ -18,6 +18,9 @@
   # Prevent recursive calls
   return if invoked[name]
   invoked[name] = true
+
+  unless (task = tasks[name])?
+    throw new Error 'No such task: #{task}'

   # Grab task action, any deps and parsed options
   {action, deps} = tasks[name]


Fix closing bracket handling for addressable outports

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -229,9 +229,10 @@

   getBracketContext: (type, port, scope, idx) ->
     {name, index} = ports.normalizePortName port
+    index = idx if idx?
     portsList = if type is 'in' then @inPorts else @outPorts
-    if portsList[name].isAddressable() and idx
-      port = "#{port}[#{idx}]"
+    if portsList[name].isAddressable()
+      port = "#{name}[#{index}]"
     # Ensure we have a bracket context for the current scope
     @bracketContext[type][port] = {} unless @bracketContext[type][port]
     @bracketContext[type][port][scope] = [] unless @bracketContext[type][port][scope]
@@ -274,7 +275,7 @@
         for port in context.ports
           ipClone = context.closeIp.clone()
           @addToResult result, port, ipClone, true
-          @getBracketContext('out', port, ipClone.scope, ipClone.index).pop()
+          @getBracketContext('out', port, ipClone.scope).pop()

     if result.__bracketContext
       # First see if there are any brackets to forward. We need to reverse
@@ -301,7 +302,7 @@
                 ctx.ports.push portIdentifier
                 @getBracketContext('out', outport, ctx.ip.scope, idx).push ctx
               forwardedOpens.reverse()
-              idxIps.unshift ip for ip in forwardedOpens
+              @addToResult result, outport, ip, true for ip in forwardedOpens
             continue
           # Don't register ports we're only sending brackets to
           datas = ips.filter (ip) -> ip.type is 'data'
@@ -315,7 +316,7 @@
             ctx.ports.push outport
             @getBracketContext('out', outport, ctx.ip.scope).push ctx
           forwardedOpens.reverse()
-          ips.unshift ip for ip in forwardedOpens
+          @addToResult result, outport, ip, true for ip in forwardedOpens

     if result.__bracketClosingAfter?.length
       for context in result.__bracketClosingAfter
@@ -324,7 +325,7 @@
         for port in context.ports
           ipClone = context.closeIp.clone()
           @addToResult result, port, ipClone, false
-          @getBracketContext('out', port, ipClone.scope, ipClone.index).pop()
+          @getBracketContext('out', port, ipClone.scope).pop()

     delete result.__bracketClosingBefore
     delete result.__bracketContext


added comments

added comments

Former-commit-id: 91db97f1327278cf60f3d7ce9f1a8f59185fde04 [formerly f06c13ded4daff724791aee60927db5cd5dd234a]
Former-commit-id: 20237a6b1af286415ff790550ab1d5abc647969d

--- src/algo/ConsensusCalc.coffee
+++ src/algo/ConsensusCalc.coffee
@@ -7,13 +7,14 @@
   seqs = msa.seqs.map (el) -> el.get "seq"
   occs = new Array seqs.length

-  # count the
+  # count the occurences of the chars of a position
   _.each seqs, (el,i) ->
     _.each el, (char, pos) ->
       occs[pos] = {} unless occs[pos]?
       occs[pos][char] = 0 unless occs[pos][char]?
       occs[pos][char]++

+  # now pick the char with most occururences
   _.reduce occs, (memo,occ) ->
     keys = _.keys occ
     memo +=  _.max keys, (key) -> occ[key]


vm: pass errors to callback in `JVM::createVmByNonce`

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -302,8 +302,8 @@

     JPaymentFulfillmentNonce.one { nonce }, (err, nonceObject) =>
       return callback err  if err
-      return { message: "Unrecognized nonce!", nonce }  unless nonceObject
-      return { message: "Invalid nonce!", nonce }  if nonceObject.action isnt "debit"
+      return callback { message: "Unrecognized nonce!", nonce }  unless nonceObject
+      return callback { message: "Invalid nonce!", nonce }  if nonceObject.action isnt "debit"

       { planCode, subscriptionCode } = nonceObject
       { delegate: account } = client.connection


Much cleaner way to create VMs

Much cleaner way to create VMs

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -156,14 +156,17 @@
           hostnameAliases = JVM.createAliases {
             nickname, type, uid, groupSlug
           }
+          users         = [{ id: user.getId(), sudo: yes, owner: yes }]
+          groups        = [{ id: group.getId() }]
+          hostnameAlias = hostnameAliases[0]

           vm = new JVM {
-            planCode      : planCode
-            planOwner     : planOwner
-            users         : [{ id: user.getId(), sudo: yes, owner: yes }]
-            groups        : [{ id: group.getId() }]
-            hostnameAlias : hostnameAliases[0]
+            hostnameAlias
+            planOwner
+            planCode
             webHome
+            groups
+            users
             usage
           }

@@ -172,7 +175,7 @@
             handleError err
             if err
               return console.warn "Failed to create VM for ", \
-                                   {users, groups, hostnameAliases}
+                                   {users, groups, hostnameAlias}

             JVM.createDomains hostnameAliases, hostnameAliases[0]

@@ -405,16 +408,19 @@
         type, uid, groupSlug
       }

+      users = [
+        { id: user.getId(), sudo: yes, owner: yes }
+      ]
+      hostnameAlias = hostnameAliases[0]
+      groups       ?= []

       vm = new JVM {
-        users         : [
-          { id: user.getId(), sudo: yes, owner: yes }
-        ]
-        groups        : groups ? []
-        hostnameAlias : hostnameAliases[0]
+        hostnameAlias
         planOwner
         planCode
         webHome
+        groups
+        users
       }

       vm.save (err)->
@@ -422,7 +428,7 @@
         handleError err
         if err
           return console.warn "Failed to create VM for ", \
-                               {users, groups, hostnameAliases}
+                               {users, groups, hostnameAlias}

         JVM.createDomains hostnameAliases, hostnameAliases[0]
         target.addVm vm, handleError


The Dispatcher shouldn’t deal with what’s on the options hash

--- src/chaplin/dispatcher.coffee
+++ src/chaplin/dispatcher.coffee
@@ -69,17 +69,12 @@
       # Whether to force the controller startup even
       # when current and new controllers and params match
       # Default to false unless explicitly set to true
-      forceStartup = false
-      if options.forceStartup?
-        forceStartup = true if options.forceStartup is true
-
-        # Remove it from the options hash so as the options hash is
-        # passed off to backbone later
-        delete options.forceStartup
+      if options.forceStartup isnt true
+        options.forceStartup = false

       # Check if the desired controller is already active
       isSameController =
-        not forceStartup and
+        not options.forceStartup and
         @currentControllerName is controllerName and
         @currentAction is action and
         # Deep parameters check is not nice but the simplest way for now
@@ -168,11 +163,7 @@
           "#{@currentControllerName} does not provide a historyURL"

       # Tell the router to actually change the current URL
-      # Take parameter hash from and forward it on as well
-      if options.changeURL
-        # Remove it so backbone won't see it
-        delete options.changeURL
-        @publishEvent '!router:changeURL', url, options
+      @publishEvent '!router:changeURL', url, options if options.changeURL

       # Save the URL
       @url = url


half working single-word fade

half working single-word fade

--- app/vis.coffee
+++ app/vis.coffee
@@ -271,7 +271,9 @@

     fadeTo = (to, duration) ->
         (text_object) ->
-            current = text_object.children[0].material.uniforms.opacity.value
+            current = if text_object.children[0]?
+                text_object.children[0].material.uniforms.opacity.value
+            else 0
             i = d3.interpolate current, to
             getTransitionPromise(i, duration) text_object

@@ -367,7 +369,8 @@
     LINE_SPACING = 40

     getWordIndex = (line, word) ->
-        regex = new RegExp "\\b#{word}\\b", "i"
+        expression = if word is "—" then word else "\\b#{word}\\b"
+        regex = new RegExp expression, "i"
         line.search regex

     alignToNode = (parent) ->
@@ -397,15 +400,14 @@

             traverse(root)

-    getOneWord: (text_object, word) ->
+    getWordObjects: (text_object, word) ->
         word_object = new THREE.Object3D()
-
-
-
-        # debugger
+        begin = getWordIndex text_object._line, word
+        end = begin + word.length
+        text_object.children.slice begin, end

     animateLines: (root) =>
-        _getOneWord = @getOneWord
+        # _getOneWord = @getOneWord
         traverse = (node) =>
             return if ! node.children?

@@ -422,14 +424,15 @@
                             .filter (child) -> child isnt node
                         parent = node._parent
                         promises = siblings.concat(parent).map (child) ->
-                                # word_object = _getOneWord child._text_object, node.word
                                 fadeTo(0, 1000) child._text_object
                         return Promise.all promises
-                .then ->
+                .then =>
                     # Fade in children
                     if node.children?
-                        promises = node.children.map (child) ->
-                             fade(0.2, 1, 1000) child._text_object
+                        promises = node.children.map (child) =>
+                            word_objects = @getWordObjects child._text_object, node.word
+                            # The fade functions expect a children array
+                            fadeTo(1, 1000) children: word_objects
                         return Promise.all promises
                 .then ->
                     # Traverse next parent


Fix centering of the place in the map on mobile

Before, when exiting pressing "see in map" in the blog, on a phone, the
item would sometimes be off, anchored to the left border.  We fix this
by never compensating for the panel position on mobile, since the panel
is always going to be either fully on or off.

--- src/coffee/controllers.coffee
+++ src/coffee/controllers.coffee
@@ -308,8 +308,8 @@
                 menuWidth  = if not $scope.seemenu \
                              then 0
                              else document.getElementById("main-menu").offsetWidth
-                panelWidth = $scope.seepanel * mapWidth * (
-                             if $scope.isMobile() then 1 else $scope.expandpanel / 100.0)
+                usePanel   = not $scope.isMobile() and $scope.seepanel
+                panelWidth = usePanel * mapWidth * $scope.expandpanel / 100.0
                 adjust = panelWidth / 2 - menuWidth / 2
                 map.panTo center
                 map.setZoom zoom  if zoom


Position image popup differently, so that it can also be used with fixed toolbar

--- src/plugins/image.coffee
+++ src/plugins/image.coffee
@@ -174,7 +174,7 @@
       toolbarEl = jQuery @options.toolbar
       xposition = editableEl.offset().left + editableEl.outerWidth() - 3
       # 3 is the border width of the contenteditable border
-      yposition = toolbarEl.offset().top - jQuery(document).scrollTop() - 29
+      yposition = toolbarEl.offset().top + toolbarEl.outerHeight() - jQuery(document).scrollTop() + 29
       @options.dialog.dialog("option", "position", [xposition, yposition])
       # do @_getSuggestions


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -151,5 +151,5 @@
     @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
     include: (modules...) ->
-
+      modules.each Modul
       self.extend Module(I, self)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -157,7 +157,7 @@
             I.included_modules.push moduleName
             self.extend Module(I, self)
         else
-          warn "Unable to discover name fo"
+          warn "Unable to discover name for "
           self.extend Module(I, self)

       return self


cassandra -- add a function to ensure that all project owners are listed in the project_owners table

--- salvus/cassandra.coffee
+++ salvus/cassandra.coffee
@@ -1348,6 +1348,30 @@
                             cb()
         ])

+    set_all_project_owners_to_users: (cb) =>
+        # This is solely due to database consistency issues.
+        @select
+            table: 'projects'
+            limit: 100000
+            columns: ['project_id', 'account_id']
+            objectify: false
+            cb: (err, results) =>
+                 f = (r, cb) =>
+                     console.log(r)
+                     if not r[0]? or not r[1]?
+                          console.log("skipping")
+                          cb()
+                          return
+                     @update
+                         table : 'project_users'
+                         set   :
+                               mode : 'owner'
+                         where :
+                               project_id : r[0]
+                               account_id : r[1]
+                         cb: cb
+                 async.map(results, f, cb)
+
     undelete_project: (opts) ->
         opts = defaults opts,
             project_id  : required


fix tests again

fix tests again

--- test/basic-test.coffee
+++ test/basic-test.coffee
@@ -8,12 +8,12 @@

 platform = os.platform()

-testBuild = (module, opts) ->
+testBuild = (moduleName, opts={}) ->
 	opts.quiet = yes
-	msg = 'should build "time@0.11.0"'
+	msg = "should build '#{moduleName}'"
 	msg += " for Atom-Shell@#{opts.target} on #{platform} @#{opts.arch}" if opts.target and opts.arch
 	it msg, (done) ->
-		aspm.installModule 'time@0.11.0', opts, (err) ->
+		aspm.installModule moduleName, opts, (err) ->
 			done err


cleaned up test a littl

cleaned up test a littl

--- test/basicTests.coffee
+++ test/basicTests.coffee
@@ -37,8 +37,9 @@
     sq = (x)->x*x
     log = Math.log
     objectiveFunc = (n, x)->
+      partial = -0.9189385332046727 - log(x[1]) - log(0.5*erfc((0.7071067811865475*(-1 + x[0]))/x[1]) - 0.5*erfc((0.7071067811865475*x[0])/x[1]))
       return _.reduce(data, (sum, val)->
-        return sum + -0.9189385332046727 - (0.5*sq(val - x[0]))/sq(x[1]) - log(x[1]) - log(0.5*erfc((0.7071067811865475*(-1 + x[0]))/x[1]) - 0.5*erfc((0.7071067811865475*x[0])/x[1]))
+        return sum - (0.5*sq(val - x[0]))/sq(x[1]) + partial
       , 0)

     options = {
@@ -50,6 +51,17 @@
       lowerBounds:[0, -5]
       upperBounds:[1, 5]
     }
+    expectedResult = {
+      maxObjectiveFunction: 'Success',
+      lowerBounds: 'Success',
+      upperBounds: 'Success',
+      xToleranceRelative: 'Success',
+      initalGuess: 'Success',
+      status: 'Success: Optimization stopped because xToleranceRelative or xToleranceAbsolute was reached',
+      parameterValues: [ 1, 0.1 ],
+      outputValue: 78.4539
+    }
+    checkResults(nlopt(options), expectedResult)
   )

   it('newton + simplex', ()->


handle graf--iframe select/delete

handle graf--iframe select/delete

--- app/assets/javascripts/dante/editor.js.coffee
+++ app/assets/javascripts/dante/editor.js.coffee
@@ -13,6 +13,9 @@
     "drop"    : "handleDrag"
     "click .graf--figure .aspectRatioPlaceholder" : "handleGrafFigureSelectImg"
     "click .graf--figure figcaption"   : "handleGrafFigureSelectCaption"
+
+    "mouseover .graf--figure.graf--iframe" : "handleGrafFigureSelectIframe"
+    "mouseleave .graf--figure.graf--iframe" : "handleGrafFigureUnSelectIframe"
     "keyup .graf--figure figcaption"   : "handleGrafCaptionTyping"

     "mouseover .markup--anchor" : "displayPopOver"
@@ -290,6 +293,20 @@
     $(element).parent(".graf--figure").addClass("is-selected is-mediaFocused")
     @selection().removeAllRanges()

+  handleGrafFigureSelectIframe: (ev)->
+    utils.log "FIGURE IFRAME SELECT"
+    element = ev.currentTarget
+    @iframeSelected = element
+    @markAsSelected( element )
+    $(element).addClass("is-selected is-mediaFocused")
+    @selection().removeAllRanges()
+
+  handleGrafFigureUnSelectIframe: (ev)->
+    utils.log "FIGURE IFRAME UNSELECT"
+    element = ev.currentTarget
+    @iframeSelected = null
+    $(element).removeClass("is-selected is-mediaFocused")
+
   handleGrafFigureSelectCaption: (ev)->
     utils.log "FIGCAPTION"
     element = ev.currentTarget
@@ -677,7 +694,6 @@
         return false if @isFirstChar() && !_.isEmpty( $(anchor_node).text().trim() )

       #remove graf figure is is selected but not in range (not focus on caption)
-      debugger
       if $(".is-selected").hasClass("graf--figure") && !anchor_node?
         @replaceWith("p", $(".is-selected"))
         @setRangeAt($(".is-selected")[0])


fix input-connected

fix input-connected

--- app/assets/javascripts/input-connected.coffee
+++ app/assets/javascripts/input-connected.coffee
@@ -15,8 +15,14 @@
     data.forEach (opt) ->
       element.append($("<option></option>").attr("value", opt.value).text(opt.text))

-  updateTargetComponent = (data, componentTarget) ->
+  updateTargetComponent = (data, componentTarget, component) ->
     updateOptionsHtml(data, componentTarget)
+
+    parentDiv = componentTarget.parents('.input-group')
+    if parentDiv.hasClass('select-field-refresh')
+      refreshBtn = parentDiv.find('.input-refresh-button')
+      refreshBtn.attr('value', component.val()).attr('name', component.attr('name'))
+
     componentTarget.multiSelect('refresh')  if componentTarget.hasClass('multi-column')
     componentTarget.selectpicker('refresh') if componentTarget.hasClass('selectpicker')
     componentTarget.multiselect('rebuild')  if componentTarget.hasClass('multi-select')
@@ -66,7 +72,7 @@
         if mode == "remote"
           params = { "#{ name }": values }
           $.ajax({ url: target.url, data: params }).done (data) ->
-            updateTargetComponent(data, componentTarget)
+            updateTargetComponent(data, componentTarget, component)

         if mode == "local"
           data = target.data || settings.target.data
@@ -74,6 +80,6 @@
             values = [].concat.apply([], [values]) # flatten
             return values.includes(String(value.connect_option_id))

-          updateTargetComponent(data, componentTarget)
+          updateTargetComponent(data, componentTarget, component)

 )(jQuery)


Updating security_policy resource snippet

--- snippets/security_policy.cson
+++ snippets/security_policy.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe security_policy':
     'prefix': 'security_policy'
-    'body': "# http://inspec.io/docs/reference/resources/security_policy/\ndescribe security_policy('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/security_policy/\ndescribe security_policy do\n  its('${1:policy_name}') { should${2:_not} eq '${3:value}' }${4}\nend"


Rename variable to pattern to shadow config key path

--- src/packages/wrap-guide/src/wrap-guide.coffee
+++ src/packages/wrap-guide/src/wrap-guide.coffee
@@ -28,9 +28,9 @@
     return @defaultColumn unless _.isArray(customColumns)
     for customColumn in customColumns
       continue unless _.isObject(customColumn)
-      regex = customColumn['pattern']
-      continue unless regex
-      return parseInt(customColumn['column']) if new RegExp(regex).test(path)
+      pattern = customColumn['pattern']
+      continue unless pattern
+      return parseInt(customColumn['column']) if new RegExp(pattern).test(path)
     @defaultColumn

   updateGuide: ->


"All Grade Periods" can be selected in gradebook

fixes CNVS-21047

Also removed a bunch of fat arrows that were driving my linter up the wall.

Test Plan:
  1. Create a course
  2. Enable grading periods
  3. Create at least 2 grading periods
  4. Have at least one assignment in each grading period
  5. In gradebook, select "All Grading Periods"
  6. Verifiy that the assignments for all grading periods displays

Change-Id: If2d851d4a57100e5275bf281c1f364225a6f7309
Reviewed-on: https://gerrit.instructure.com/56802
Tested-by: Jenkins
Reviewed-by: Spencer Olson <7cc0890d6f1f70810a023e7173eca5724d6c8e49@instructure.com>
Reviewed-by: Derek Bender <f0b7c914e079fab3ddf3033f0ce795c4592909ff@instructure.com>
QA-Review: Adrian Foong <84c1094366ed084f9dbd0e92a3de1886a73fdc57@instructure.com>
Product-Review: Cody Poll <5859f3fb5e0914506e3e2923810f09e59325ddd4@instructure.com>

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

     getGradingPeriodToShow: () ->
       currentPeriodId = userSettings.contextGet('gradebook_current_grading_period')
-      if currentPeriodId && @gradingPeriodIsActive(currentPeriodId)
+      if currentPeriodId && (@isAllGradingPeriods(currentPeriodId) || @gradingPeriodIsActive(currentPeriodId))
         currentPeriodId
       else
         ENV.GRADEBOOK_OPTIONS.current_grading_period_id
@@ -224,7 +224,7 @@
         false

     onShow: ->
-      $(".post-grades-placeholder").show();
+      $(".post-grades-placeholder").show()
       return if @startedInitializing
       @startedInitializing = true

@@ -334,7 +334,7 @@

     defaultSortType: 'assignment_group'

-    studentsThatCanSeeAssignment: (potential_students, assignment) =>
+    studentsThatCanSeeAssignment: (potential_students, assignment) ->
       if ENV.GRADEBOOK_OPTIONS.differentiated_assignments_enabled
         _.pick potential_students, assignment.assignment_visibility...
       else
@@ -353,7 +353,7 @@
       else
         @gradebookColumnOrderSettings

-    setStoredSortOrder: (newSortOrder) =>
+    setStoredSortOrder: (newSortOrder) ->
       unless @isInvalidCustomSort()
         url = ENV.GRADEBOOK_OPTIONS.gradebook_column_order_settings_url
         $.ajaxJSON(url, 'POST', {column_order: newSortOrder})
@@ -411,7 +411,7 @@
         else throw "unhandled column sort condition"
       @wrapColumnSortFn(fn)

-    compareAssignmentPositions: (a, b) =>
+    compareAssignmentPositions: (a, b) ->
       diffOfAssignmentGroupPosition = a.object.assignment_group.position - b.object.assignment_group.position
       diffOfAssignmentPosition = a.object.position - b.object.position

@@ -419,7 +419,7 @@
       # will work when there are less than 1000000 assignments in an assignment_group
       return (diffOfAssignmentGroupPosition * 1000000) + diffOfAssignmentPosition

-    compareAssignmentDueDates: (a, b) =>
+    compareAssignmentDueDates: (a, b) ->
       aDate = if a.object.due_at then (+a.object.due_at / 1000) else Number.MAX_VALUE
       bDate = if b.object.due_at then (+b.object.due_at / 1000) else Number.MAX_VALUE
       if aDate is bDate
@@ -446,8 +446,8 @@
         else
           return @compareAssignmentPositions(a, b)

-    wrapColumnSortFn: (wrappedFn) =>
-      (a, b) =>
+    wrapColumnSortFn: (wrappedFn) ->
+      (a, b) ->
         return -1 if b.type is 'total_grade'
         return  1 if a.type is 'total_grade'
         return -1 if b.type is 'assignment_group' and a.type isnt 'assignment_group'
@@ -618,10 +618,10 @@
           else
             (SubmissionCell[assignment.grading_type] || SubmissionCell).formatter(row, col, submission, assignment, @grid)

-    staticCellFormatter: (row, col, val) =>
+    staticCellFormatter: (row, col, val) ->
       "
#{htmlEscape(val)}
"

-    uneditableCellFormatter: (row, col) =>
+    uneditableCellFormatter: (row, col) ->
       "

"

     groupTotalFormatter: (row, col, val, columnDef, student) =>
@@ -645,11 +645,11 @@
         templateOpts.hideTooltip = @weightedGroups() and not @totalGradeWarning
       groupTotalCellTemplate templateOpts

-    htmlContentFormatter: (row, col, val, columnDef, student) =>
+    htmlContentFormatter: (row, col, val, columnDef, student) ->
       return '' unless val?
       val

-    calculateAndRoundGroupTotalScore: (score, possible_points) =>
+    calculateAndRoundGroupTotalScore: (score, possible_points) ->
       grade = (score / possible_points) * 100
       round(grade, DISPLAY_PRECISION)

@@ -761,7 +761,7 @@
         else if submission.score?
           htmlLines.push submission.score
         # add lines for dropped, late, resubmitted
-        Array::push.apply htmlLines, $.map(SubmissionCell.classesBasedOnSubmission(submission, assignment), (c)=> GRADEBOOK_TRANSLATIONS["#submission_tooltip_#{c}"])
+        Array::push.apply htmlLines, $.map(SubmissionCell.classesBasedOnSubmission(submission, assignment), (c)-> GRADEBOOK_TRANSLATIONS["#submission_tooltip_#{c}"])
       else if assignment.points_possible?
         htmlLines.push htmlEscape(I18n.t('points_out_of', "out of %{points_possible}", points_possible: assignment.points_possible))

@@ -907,7 +907,7 @@
         currentGradingPeriod: @gradingPeriodToShow)
       @gradingPeriodMenu.render()

-    updateCurrentGradingPeriod: (period) =>
+    updateCurrentGradingPeriod: (period) ->
       userSettings.contextSet 'gradebook_current_grading_period', period
       window.location.reload()

@@ -1022,7 +1022,7 @@
           )
       , 2000)

-    initPreviousGradebookExportLink: () =>
+    initPreviousGradebookExportLink: () ->
       link = $('#csv_export_options').children('li').last().children()
       link.on 'click', (event) ->
         event.preventDefault()
@@ -1045,7 +1045,7 @@
       , 200)
       loading

-    setExportButtonTitle: (updated_title) =>
+    setExportButtonTitle: (updated_title) ->
       $($('#download_csv').children('span').contents()[2]).replaceWith(updated_title)

     studentNamesToggle: (e) =>
@@ -1097,7 +1097,7 @@
           delete student.beforeFilteredRow

       # put the removed items back in their proper order
-      _.each @userFilterRemovedRows.reverse(), (removedStudentItem) =>
+      _.each @userFilterRemovedRows.reverse(), (removedStudentItem) ->
         data.splice removedStudentItem.index, 0, removedStudentItem.data
       @userFilterRemovedRows = []

@@ -1106,7 +1106,7 @@
         index = data.length
         while index--
           student = data[index]
-          matched = _.any propertiesToMatch, (prop) =>
+          matched = _.any propertiesToMatch, (prop) ->
             student[prop]?.match new RegExp term, 'i'
           if not matched
             # remove the student, save the item and its index so we can put it
@@ -1140,7 +1140,7 @@
         showPointsPossible: assignment.points_possible?

     customColumnDefinitions: ->
-      @customColumns.map (c) =>
+      @customColumns.map (c) ->
         id: "custom_col_#{c.id}"
         name: htmlEscape c.title
         field: "custom_col_#{c.id}"
@@ -1372,7 +1372,7 @@
         @addDroppedClass(student)
       @grid.invalidate()

-    localeSort: (a, b) =>
+    localeSort: (a, b) ->
       (a || "").localeCompare b || "",
         window.I18n.locale,
         sensitivity: 'accent', numeric: true
@@ -1514,6 +1514,9 @@
       else
         linkContainer.html(hideLink())

+    isAllGradingPeriods: (currentPeriodId) ->
+      currentPeriodId == "0"
+
     # this method should be removed after a month in production
     alignCoursePreferencesWithLocalStorage: () ->
       local_storage_show_point_totals = userSettings.contextGet('show_point_totals')


change ValidatedMixin to use $.raw()

change ValidatedMixin to use $.raw()

test plan:
 * create a group set with a malicious name
   - "><script>alert('BROKEN')</script>
 * no alert should be shown
 * attempt to create a second group set with the same name
   - the ui should not "hang"
   - an error should be shown indicating the duplicated name

Change-Id: I3ff6067dedd8016e76934ac5ca14cd70ffa8850f
Reviewed-on: https://gerrit.instructure.com/45231
Reviewed-by: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>
QA-Review: Steven Shepherd <e794458448443812fb2685675aab3bbd1a3891bc@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Product-Review: Matt Fairbourn <5f353e1c97bb9cc046b9db20beca0b9392f6f1b2@instructure.com>

--- app/coffeescripts/views/ValidatedMixin.coffee
+++ app/coffeescripts/views/ValidatedMixin.coffee
@@ -7,6 +7,7 @@
   'jquery.toJSON'
   'jquery.disableWhileLoading'
   'jquery.instructure_forms'
+  'jquery.instructure_misc_helpers'
 ], (Backbone, $, _, preventDefault, htmlEscape) ->

   ValidatedMixin =
@@ -65,7 +66,7 @@
         $input = @findField fieldName
         # check for a translations option first, fall back to just displaying otherwise
         html = (htmlEscape(@translations?[message] or message) for {message} in field).join('

')
-        $input.errorBox new htmlEscape.SafeString("
#{html}
")
+        $input.errorBox $.raw("
#{html}
")
         field.$input = $input
         field.$errorBox = $input.data 'associated_error_box'


Added Highway, Embassy, Margrave, and Noble Brigand to actionPriority.

--- basicAI.coffee
+++ basicAI.coffee
@@ -220,14 +220,18 @@
     "Treasury"
     "Conspirator" if my.inPlay.length >= 2
     "Familiar"
+    "Highway"
     "Wishing Well"
     "Great Hall" if state.cardInfo.Crossroads not in my.hand
     "Lighthouse"
     "Haven"
     # Fifth priority: terminal card-drawers, if we have actions to spare.
     "Library" if my.actions > 1 and my.hand.length <= 4
+    "Torturer" if my.actions > 1
+    "Margrave" if my.actions > 1
     "Rabble" if my.actions > 1
     "Smithy" if my.actions > 1
+    "Embassy" if my.actions > 1
     "Watchtower" if my.actions > 1 and my.hand.length <= 4
     "Library" if my.actions > 1 and my.hand.length <= 5
     "Courtyard" if my.actions > 1 and (my.discard.length + my.draw.length) <= 3
@@ -254,6 +258,7 @@
     "Mountebank"
     "Witch"
     "Torturer"
+    "Margrave"
     "Sea Hag"
     "Tribute" # after Cursers but before other terminals, there is probably a better spot for it
     "Goons"
@@ -280,6 +285,7 @@
     "Library" if my.hand.length <= 4
     "Rabble"
     "Smithy"
+    "Embassy"
     "Watchtower" if my.hand.length <= 3
     "Council Room"
     "Library" if my.hand.length <= 5
@@ -308,6 +314,7 @@
     "Trade Route" if wantsToTrash
     "Mint" if my.ai.choose('mint', state, my.hand)
     "Pirate Ship"
+    "Noble Brigand"
     "Thief"
     "Fortune Teller"
     "Bureaucrat"


Menu: Small fix in a regression bug.

Menu: Small fix in a regression bug.

--- src/elements/Menu/Menu.coffee
+++ src/elements/Menu/Menu.coffee
@@ -121,7 +121,7 @@

 	hide: (ev) ->
 		super(ev)
-		@__previousFocusedElement.focus()
+		@__previousFocusedElement?.focus()
 		@

 	hideAll: (ev) ->


Fix failing test

Fix failing test

--- spec/project-manager-spec.coffee
+++ spec/project-manager-spec.coffee
@@ -22,5 +22,5 @@
     it "Makes sure projects.cson exists", ->
       options =
         encoding: 'utf-8'
-      fs.readFile ProjectManager.file, options, (err, data) ->
-        expect(err).toBe null+      fs.readFile ProjectManager.file(), options, (err, data) ->
+        expect(err).toBe null


fix test

fix test

--- test/index-spec.coffee
+++ test/index-spec.coffee
@@ -56,7 +56,7 @@

     describe 'when given a valid uuid', ->
       beforeEach (done) ->
-        @request.get.yields null, null, foo: 'bar'
+        @request.get.yields null, {statusCode: 200}, foo: 'bar'
         @sut.device 'the-uuuuid', (@error, @body) => done()

       it 'should call get', ->


updated mongo query to match new schema

updated mongo query to match new schema


factory for ext Rally global

factory for ext Rally global

--- src/scripts/index.coffee
+++ src/scripts/index.coffee
@@ -1,2 +1,3 @@
 angular.module('Ext', []).factory('Ext', () -> return Ext )
+angular.module('Rally', []).factory('Rally', () -> return Rally )
 angular.module 'rally', ['rally.services']


very minor name change

very minor name change

--- src/environment.coffee
+++ src/environment.coffee
@@ -61,7 +61,7 @@
         marginsRight: 5
         marginsLeft: 5
     #default aniSprite settings
-    SPRITESHEET_ALIGNMENT: 'HORIZONTAL'
+    SPRITESHEET_ORIENTATION: 'HORIZONTAL'
     ANICYCLE_DEFAULT_CONFIG:
         name: 'UNDEFINED'
         numFrames: 8


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -60,7 +60,7 @@

   @name flicker
   @methodOf Flickerable#
-  @param {Number} [duration=2] How long the effect lasts
+  @param {Number} [duration=2] How long the effect lasts in seconds
   @param {Number} [frequency=0.1] The number of frames in between opacity changes
   @param {Number} [alpha=0.5] The alpha value to flicker to
   ###


fixing async status for roster saving and cleaning up unnecessary save function in

--- src/flux/roster.coffee
+++ src/flux/roster.coffee
@@ -2,7 +2,7 @@
 {CrudConfig, makeSimpleStore, extendConfig} = require './helpers'
 _ = require 'underscore'

-SAVING = 'saving'
+LOADED  = 'loaded'

 RosterConfig = {

@@ -14,11 +14,8 @@
     @_local[courseId].push(student)
     @emitChange()

-  save: (courseId, studentId, params) ->
-    @_asyncStatus[studentId] = SAVING
-    @emitChange()
-
   saved: (newProps, studentId) ->
+    @_asyncStatus[studentId] = LOADED
     # update the student from all the courses rosters
     for courseId, roster of @_local
       students = roster.students
@@ -39,8 +36,6 @@
     getActiveStudentsForPeriod: (courseId, periodId) ->
       _.where(@_get(courseId).students, period_id: periodId, is_active: true)

-    isSaving: (studentId) -> @_asyncStatus[studentId] is SAVING
-
 }

 extendConfig(RosterConfig, new CrudConfig())


Age module

Age module

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -118,7 +118,7 @@
     active: true
     created: false
     destroyed: false
-    solid: false
+
     includedModules: []
     excludedModules: []


Update Atom config

Update Atom config

--- atom.cson
+++ atom.cson
@@ -14,7 +14,6 @@
       "language-clojure"
       "language-perl"
       "language-php"
-      "snippets"
       "welcome"
       "spell-check"
       "deprecation-cop"
@@ -52,6 +51,10 @@
   linter:
     lintOnChange: false
   "linter-eslint": {}
+  "linter-ui-default":
+    showPanel: true
+  "markdown-preview":
+    breakOnSingleNewline: true
   minimap:
     displayPluginsControls: false
     plugins:


Allow apiKey to be empty if requests are being batched

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -174,7 +174,11 @@
   # * `data` (Object): Specific data being recorded about this person.

   _validateData: (data) ->
-    if @apiKey then data._k = @apiKey else throw new Error 'API key required'
+    if @apiKey
+      data._k = @apiKey
+    else
+      throw new Error 'API key required' unless @batchClient
+
     if @person then data._p = @person else throw new Error 'Person required'


The entire connection should be deferred until next tick to avoid segmenting data packets and disconnect signal within the same connection

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -352,17 +352,17 @@
   sendInitial: (initial) ->
     initial.socket.connect()
     initial.socket.send initial.data
+    initial.socket.disconnect()
+
+  sendInitials: ->
+    send = =>
+      @sendInitial initial for initial in @initials
+      @initials = []
+
     if typeof process is 'object' and process.title is 'node'
       # nextTick is faster on Node.js
-      process.nextTick ->
-        initial.socket.disconnect()
+      process.nextTick send
     else
-      setTimeout ->
-        initial.socket.disconnect()
-      , 0
-
-  sendInitials: ->
-    @sendInitial initial for initial in @initials
-    @initials = []
+      setTimeout send, 0

 exports.Network = Network


splitview: add an option to forceResize all panels

--- src/components/split/splitview.coffee
+++ src/components/split/splitview.coffee
@@ -305,7 +305,7 @@
     @showPanel index
     @emit 'PanelSetToNormal', panel

-  resizePanel:(value = 0, index = 0, callback = noop)->
+  resizePanel:(value = 0, index = 0, callback = noop, forceResize = no)->

     return  unless @sizes[1]?
     return  if @beingResized
@@ -317,7 +317,7 @@
     askedPanel    = @panels[index]
     affectedPanel = @panels[(index + 1) % 2]

-    if askedPanel._getSize() is value
+    if askedPanel._getSize() is value and not forceResize
       @_resizeDidStop()
       callback()
       return


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -15,7 +15,7 @@

         #$(@node).fadeOut().fadeIn()
         @container = $(@node).parent()
-        @instanceDataId = @container.attr('data-id')
+        @instanceDataId = $(@node).attr('data-id')
         console.log('@instanceDataId',@instanceDataId)

         #


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -4,6 +4,7 @@

     ready: ->
         #@oldPieData = ''
+        @_current = ''

     onData: (data) ->

@@ -178,10 +179,10 @@
             #    b = i(t)
             #    return arc b
         #)
-        path.transition().duration(750).attrTween('d', (d, i, a) ->
+        path.transition().duration(750).attrTween('d', (a) ->
             `var i`
-            i = d3.interpolate(this._current, d)
-            this._current = i(0)
+            i = d3.interpolate(@_current, a)
+            @_current = i(0)
             return (t) ->
                 return arc(i(t))
         )


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -265,7 +265,8 @@
                     return true #don't bother visiting children or checking this node

                 p = node.point
-                v = false, h = false
+                v = false
+                h = false
                 if p #p is defined, i.e., there is a value stored in this node
                     h =  ( ((p.l > d.l) && (p.l <= d.r))
                        || ((p.r > d.l) && (p.r <= d.r))


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -374,5 +374,5 @@
             .attr('font-size',(if typeof totalTickets isnt 'string' then radiusi + 'px' else '2em'))
             .style('opacity', 1)

-        #@oldPieData = piedata
+        @oldPieData = piedata
         return


JVM: createDomains: No need to log mongo duplicate errors

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -121,8 +121,9 @@
         regYears      : 0
         loadBalancer  : { persistance: 'disabled' }
       domainObj.save (err)->
-        return console.log err  if err
-        updateRelationship domainObj
+        if err
+        then console.error err  unless err.code is 11000
+        else updateRelationship domainObj

   @fixUserDomains = permit 'change bundle',
     success: (client, callback)->


Update guest vm info for registered user, works fine.
I kept logs in the code to see what is happening, safe to remove.

We may need to re-visit this code for real username change routines.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -433,8 +433,9 @@

   do ->

-    JGroup  = require './group'
-    JUser   = require './user'
+    JAccount  = require './account'
+    JGroup    = require './group'
+    JUser     = require './user'

     addVm = ({ account, target, user, sudo, groups, groupSlug
                type, planCode, planOwner, webHome })->
@@ -487,34 +488,34 @@
         if err then handleError err
         else user.update { $set: { uid } }, handleError

-    # Do not give free group VMs
-    # JGroup.on 'GroupCreated', ({group, creator})->
-    #   group.fetchBundle (err, bundle)->
-    #     console.log err, bundle
-    #     if err then handleError err
-    #     else if bundle and bundle.sharedVM
-    #       creator.fetchUser (err, user)->
-    #         if err then handleError err
-    #         else
-    #           # Following is just here to register this name in the counters collection
-    #           ((require 'koding-counter') {
-    #             db          : JVM.getClient()
-    #             counterName : "#{group.slug}~"
-    #             offset      : 0
-    #           }).next ->
-    #
-    #           addVm {
-    #             user
-    #             account     : creator
-    #             sudo        : yes
-    #             type        : 'group'
-    #             target      : group
-    #             planCode    : 'free'
-    #             planOwner   : "group_#{group._id}"
-    #             groupSlug   : group.slug
-    #             webHome     : group.slug
-    #             groups      : wrapGroup group
-    #           }
+    JAccount.on 'UsernameChanged', ({ oldUsername, username })->
+      return  unless oldUsername or username
+
+      hostnameAlias = "vm-0.#{oldUsername}.guests.kd.io"
+      newHostNameAlias = "vm-0.#{username}.koding.kd.io"
+
+      console.log "Started to migrate #{oldUsername} to #{username} ..."
+
+      JVM.one {hostnameAlias}, (err, vm)=>
+        return console.error err  if err or not vm
+        console.log "Old vm found, passing..."
+        JAccount.one {'profile.nickname':username}, (err, account)=>
+          return console.error err  if err or not account
+          console.log "New account found, passing..."
+          vm.update {$set: {hostnameAlias: newHostNameAlias}}, (err)=>
+            return console.error err  if err
+            console.log "VM hostnameAlias updated, passing..."
+            nameFactory = (require 'koding-counter')
+              db          : JVM.getClient()
+              offset      : 0
+              counterName : "koding~#{username}~"
+            nameFactory.next (err, uid)=>
+              return console.error err  if err
+              console.log "Counter created with #{uid}, passing..."
+              JVM.ensureDomainSettings {
+                type:'user', nickname:username, groupSlug:'koding'
+                account, vm
+              }

     JGroup.on 'GroupDestroyed', (group)->
       group.fetchVms (err, vms)->


Fix Grunt output not being silenced under Grunt v0.4.5

Grunt v0.4.5 moved grunt.log into separate ‘grunt-legacy-log’ module, see https://github.com/gruntjs/grunt/commit/014929198db0f86615a1431bb63309100b8b04b3. The original proxyquire was therefore no longer working.

--- test/gyp.coffee
+++ test/gyp.coffee
@@ -9,13 +9,12 @@
 	gruntError = e

 # Silence some Grunt output.
-gruntLogStub = {}
-gruntLogStub.header = ->
-gruntLogStub.writeln = -> return { success: -> }
+gruntLogStub = new (require('../node_modules/grunt/node_modules/grunt-legacy-log').Log)()
+gruntLogStub.header = gruntLogStub.writeln = gruntLogStub.success = -> gruntLogStub

 grunt = proxyquire 'grunt', {
 	'./grunt/fail': gruntFailStub,
-	'./grunt/log': gruntLogStub
+	'grunt-legacy-log': { Log: -> gruntLogStub }
 }

 gruntOptions =


bump(DB): update DB version to support new Metadata schema

--- src/flux/stores/database-store.coffee
+++ src/flux/stores/database-store.coffee
@@ -17,7 +17,7 @@
  serializeRegisteredObjects,
  deserializeRegisteredObjects} = require '../models/utils'

-DatabaseVersion = 12
+DatabaseVersion = 13

 DatabasePhase =
   Setup: 'setup'


changed the way that lists handle state

changed the way that lists handle state

--- src/mixins/list.coffee
+++ src/mixins/list.coffee
@@ -29,20 +29,50 @@
     if @collection.params
       @collection.params.page = options.page or 1

+    # states: loading <=> listing
+    #                 <=> error
+    count = @count()
+    @listMeta = new Backbone.Model({
+      state: "listing",
+      count: count,
+    });
+
+    # @listenTo @collection, "all", (event) =>
+    #   console.log(" => #{event}")
     @listenTo @collection, "add", (m) =>
       @added(m)
-    @listenTo @collection, 'reset', @addAll
+    @listenTo @collection, 'update', =>
+      @listMeta.set({state: "listing"})
+      @updateFilters()
+    @listenTo @collection, 'reset', =>
+      @listMeta.set({state: "listing"})
+      @updateFilters()
     @listenTo @collection, "sort", ->
       # @empty()
       # @addAll()
       @sort()
+    @listenTo @collection, "request", =>
+      @listMeta.set({state: "loading"})
+      @showAlerts()
+    @listenTo @collection, "error", =>
+      @listMeta.set({state: "error"})
+      @showAlerts()
+    @listenTo @collection, "sync", =>
+      @listMeta.set({state: "listing"})
+      @showAlerts()
+
+    # removed from the collection
     @listenTo @collection, "remove", @removed
-    @listenTo @collection, "request", @showLoading.bind(@, true)
-    @listenTo @collection, "error", @showError
-    @listenTo @collection, "sync", @showLoading.bind(@, false)
+
+    # destroyed
+    @listenTo @collection, "destroy", @removed

     @listenTo @, "render:post", =>
+      @prevState = @listMeta.get("state")
+      @listMeta.set({state: "loading"})
       @addAll()
+      @listMeta.set({state: @prevState})
+      @showAlerts()

     @listenTo @, "close", (e) =>
       for id, view of @views
@@ -92,7 +122,6 @@

   added: (model, container) ->
     if @filterFunc model, @filters
-      skipShowEmpty = container == undefined
       if not container
         container = @getListElement().get(0)
       view = @getItemView model
@@ -108,9 +137,7 @@
           container.insertBefore(rendered, el)
         else
           container.appendChild(rendered)
-
-      if not skipShowEmpty
-        @showEmpty()
+      @cachedCount += 1
       return view
     return null

@@ -119,7 +146,7 @@
     if view
       view.remove()
       delete @views[model.cid]
-      @showLoading()
+      @cachedCount -= 1
     return view

   addAll: () ->
@@ -128,45 +155,13 @@
     @views = {}
     if @collection.length > 0
       container = document.createDocumentFragment()
+      @cachedCount = 0
       for model in @collection.models
-        @added model, container
+        retval = @added model, container
+        if retval
+          @cachedCount += 1
       if container
         @getListElement().append(container)
-
-    @showEmpty()
-
-  showError: () ->
-    @showLoading(false)
-    @$el.find(@errorSelector).removeClass @emptyToggleClass
-
-  showLoading: (value) ->
-    if value != undefined
-      @loading = value
-    if not @loading
-      @$el.find(@loadingSelector).addClass @emptyToggleClass
-      @showEmpty()
-    else
-      @$el.find(@loadingSelector).removeClass @emptyToggleClass
-      @$el.find(@errorSelector).addClass @emptyToggleClass
-      @$el.find(@emptySelector).addClass @emptyToggleClass
-
-  showEmpty: () ->
-    if not @loading
-      count = null
-      if @emptySelector or @existsSelector
-        count = @count()
-
-      if @emptySelector
-        if count == 0
-          @$(@emptySelector).removeClass @emptyToggleClass
-        else
-          @$(@emptySelector).addClass @emptyToggleClass
-
-      if @existsSelector
-        if count > 0
-          @$el.find(@existsSelector).removeClass @emptyToggleClass
-        else
-          @$el.find(@existsSelector).addClass @emptyToggleClass

   remove: () ->
     for cid, view of @views
@@ -187,7 +182,8 @@
         if view
           view.remove()
           delete @views[model.cid]
-    @showEmpty()
+    @showAlerts()
+    @cachedCount = count
     return count

   count: () ->
@@ -195,7 +191,47 @@
     for model in this.collection.models
       if @filterFunc model, @filters
         count += 1
+    @cachedCount = count
     return count
+
+  showAlerts: () ->
+    state = @listMeta.get("state", "listing")
+    if state == "listing" and @cachedCount == 0
+      state = "empty"
+
+    states = {
+      "loading": @loadingSelector,
+      "error": @errorSelector,
+      "empty": @emptySelector
+    }
+    activated = false
+    for priority, selector of states
+      # show the alert if it is the current state
+      # unless a higher order alert was already activated
+      if not activated and state == priority
+        @showAlert(selector)
+        activated = true
+      else
+        @hideAlert(selector)
+
+    if @cachedCount > 0
+      @showAlert(@existsSelector)
+    else
+      @hideAlert(@existsSelector)
+
+  showAlert: (selector) ->
+    status = @listMeta.get(selector)
+    if not status or status is undefined
+      # console.log("[alert on] #{selector}")
+      @listMeta.set(selector, true)
+      @$el.find(selector).removeClass @emptyToggleClass
+
+  hideAlert: (selector) ->
+    status = @listMeta.get(selector)
+    if status or status is undefined
+      # console.log("[alert off] #{selector}")
+      @listMeta.set(selector, false)
+      @$el.find(selector).addClass @emptyToggleClass

 module.exports =
   mixins:


Add edit menu

Add edit menu

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -52,7 +52,7 @@
             enabled: viewstate.loggedin
             click: (it) -> action 'showconvlast', it.checked
         }, {
-            label: 'Color scheme'
+            label: 'Color Scheme'
             submenu: [
                 {
                     label: 'Default'
@@ -228,7 +228,7 @@
         }
     ]},
     {
-            label: 'Color scheme'
+            label: 'Color Scheme'
             submenu: [
                 {
                     label: 'Default'


console.log stderr

console.log stderr

--- test/helpers/map_exec.coffee
+++ test/helpers/map_exec.coffee
@@ -8,7 +8,9 @@
     exec item, (err, stdout, stderr) ->
       console.log "ran: #{item}"
       console.log "got: #{stdout}"
-      console.log err if err?
+      if err?
+        console.log "STDERR: #{stderr}"
+        console.log err
       cbk err, stdout

   async.map calls, iterator, cb


Remove sinon from button view with menu

Remove sinon from button view with menu

--- test/components/buttons/buttonviewwithmenu.coffee
+++ test/components/buttons/buttonviewwithmenu.coffee
@@ -8,17 +8,14 @@
 describe 'KDButtonViewWithMenu', ->
   beforeEach ->
     @sinon = sinon.sandbox.create()
-    @addCallback = @sinon.spy (->)
-    @editCallback = @sinon.spy (->)
-    @deleteCallback = @sinon.spy (->)
     @view = new KDButtonViewWithMenu
       title : 'Settings'
       iconClass : 'settings'
       cssClass : 'settings-menu'
       menu :
-        'Add Into' : { callback: => @addCallback() }
-        'Edit' : { callback: => @editCallback() }
-        'Delete' : { callback: => @deleteCallback() }
+        'Add Into' : { callback: => (->) }
+        'Edit' : { callback: => (->) }
+        'Delete' : { callback: => (->) }

   afterEach ->
     @sinon.restore()


Fix typos

Fix typos

--- def/ko/context.cson
+++ def/ko/context.cson
@@ -14,13 +14,13 @@
     "core:select-all": "전체 선택"
     "pane:split-up-and-copy-active-item": "위로 분할"
     "pane:split-down-and-copy-active-item": "아래로 분할"
-    "pane:split-left-and-copy-active-item": "완쪽으로 분할"
+    "pane:split-left-and-copy-active-item": "왼쪽으로 분할"
     "pane:split-right-and-copy-active-item": "오른쪽으로 분할"
     "pane:close": "분할 창 닫기"
   "atom-pane":
     "pane:split-up-and-copy-active-item": "위로 분할"
     "pane:split-down-and-copy-active-item": "아래로 분할"
-    "pane:split-left-and-copy-active-item": "완쪽으로 분할"
+    "pane:split-left-and-copy-active-item": "왼쪽으로 분할"
     "pane:split-right-and-copy-active-item": "오른쪽으로 분할"
     "pane:close": "분할 창 닫기"
   "atom-text-editor:not([mini])":


Remove borked demo script.  This script used calls that were not good and never should have existed in the first place.

--- demo/million.coffee
+++ demo/million.coffee
@@ -1,34 +0,0 @@
-assert  = require 'assert'
-leveldb = require '../lib'
-
-console.log 'Creating test database'
-path = '/tmp/large.db'
-db = leveldb.openSync path, create_if_missing: true
-
-console.log 'Serializing and inserting 1,000,000 rows...'
-
-start = Date.now();
-
-for i in [0..1000000]
-  key = "row#{i}"
-  value = JSON.stringify
-    index: i
-    name: "Tim"
-    age: 28
-  db.putSync new Buffer(key), new Buffer(value)
-
-delta = Date.now() - start;
-console.log 'Completed in %d ms', delta
-console.log '%s inserts per second', Math.floor(1000000000 / delta)
-
-data = new Buffer 4096
-data[i] = Math.floor Math.random() * 256 for i in [0..4096]
-
-console.log 'Inserting the same 4k random bytes into 100,000 rows'
-start = Date.now()
-for i in [0..100000]
-  db.putSync new Buffer("binary#{i}"), data
-
-delta = Date.now() - start;
-console.log 'Completed in %d ms', delta
-console.log '%s inserts per second', Math.floor(1000000000 / delta)


SettingsMenu: Render settings menu according to her role and permissions

--- client/Social/Activity/views/settingsview.coffee
+++ client/Social/Activity/views/settingsview.coffee
@@ -57,7 +57,8 @@

     post = @getData()

-    @menu                = @addOwnerMenu()
+    return @menu  unless KD.checkFlag('super-admin')
+
     {activityController} = KD.singletons

     if KD.checkFlag 'exempt', KD.whoami()
@@ -72,11 +73,12 @@
     @addMenuItem 'Impersonate', ->
       {constructorName, _id} = post.account
       KD.remote.cacheable constructorName, _id, (err, owner) ->
-        return KD.showError err  if err
-        return KD.showError message: "Account not found"  unless owner
+        return KD.showErrorNotification err  if err
+        return KD.showNotification "Account not found"  unless owner
         KD.impersonate owner.profile.nickname

-    @addMenuItem 'Add System Tag', => @selectSystemTag post
+    # TODO since system tag is not implemented for new social menu item is regressed
+    # @addMenuItem 'Add System Tag', => @selectSystemTag post

     return @menu

@@ -87,7 +89,7 @@

     @addFollowActionMenu()
     @addOwnerMenu()  if KD.isMyPost @getData()
-    @addAdminMenu()  if KD.checkFlag('super-admin') or KD.hasAccess('delete posts')
+    @addAdminMenu()

     return @menu


use findOne

use findOne

--- client/controllers/curatorSuggestedEvents.coffee
+++ client/controllers/curatorSuggestedEvents.coffee
@@ -48,7 +48,7 @@
     @events.set(events)

   @autorun =>
-    source = CuratorSources.find({_id: @data.selectedSourceId.get()}).fetch()[0]
+    source = CuratorSources.findOne({_id: @data.selectedSourceId.get()})
     if source
       # strip punctuation and create tokens
       tokens = if source.title then source.title.replace(/[~`!@#$%^&*(){}\[\];:"'<,.>?\/\\|_+=-]/g, '').split(' ') else []


remove extra debug route

remove extra debug route

--- services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee
+++ services/web/app/coffee/Features/StaticPages/StaticPagesRouter.coffee
@@ -9,7 +9,6 @@

 		webRouter.get '/tos', HomeController.externalPage("tos", "Terms of Service")
 		webRouter.get '/about', HomeController.externalPage("about", "About Us")
-		webRouter.get '/review', HomeController.externalPage("review", "About Us")

 		webRouter.get '/security', HomeController.externalPage("security", "Security")
 		webRouter.get '/privacy_policy', HomeController.externalPage("privacy", "Privacy Policy")
@@ -20,4 +19,4 @@
 		webRouter.get '/dropbox', HomeController.externalPage("dropbox", "Dropbox and ShareLaTeX")

 		webRouter.get '/university', UniversityController.getIndexPage
-		webRouter.get '/university/*', UniversityController.getPage+		webRouter.get '/university/*', UniversityController.getPage


fix tests

fix tests

--- test/CameraTest.coffee
+++ test/CameraTest.coffee
@@ -2,9 +2,9 @@
   it "center should be 0,0,9", ->
     fieldOfView = 40 / 180 * Math.PI
     c = new Camera($V([0, 0, 10]), $V([0, 0, -1]), $V([0, 1, 0]), 1, fieldOfView, RayConfig.width, RayConfig.height)
-    expect(c.getCenter().elements).toEqual([0, 0, 9])
+    expect(c.center.elements).toEqual([0, 0, 9])
     c = new Camera($V([0, 0, 9]), $V([0, 0, -1]), $V([0, 1, 0]), 1, fieldOfView, RayConfig.width, RayConfig.height)
-    expect(c.getCenter().elements).toEqual([0, 0, 8])
+    expect(c.center.elements).toEqual([0, 0, 8])
     c = new Camera($V([0, 0, 9]), $V([0, 0, 1]), $V([0, 1, 0]), 1, fieldOfView, RayConfig.width, RayConfig.height)
-    expect(c.getCenter().elements).toEqual([0, 0, 10])
+    expect(c.center.elements).toEqual([0, 0, 10])


Removing objectEquality == true on models $watch expression


+ test event on fixed cache.

+ test event on fixed cache.

--- test/cache-test.coffee
+++ test/cache-test.coffee
@@ -158,7 +158,41 @@
       cache.get('c').should.be.equal 'C'
       cache.get('a').should.be.equal 'A'
       should.not.exist cache.get('b')
-  describe "Events on Cache", ->
+
+  describe "Events on Fixed Cache", ->
+    cache = Cache()
+    it 'should listen to "add" event', (done)->
+      expected = Math.random()
+      cache.on 'add', (key, value)->
+        key.should.be.equal 'key'
+        value.should.be.equal expected
+        done()
+      cache.set 'key', expected, fixed: true
+      cache.getFixed('key').should.be.equal expected
+    it 'should listen to "update" event', (done)->
+      newValue = Math.random()
+      oldValue = cache.get 'key'
+      should.exist oldValue
+      cache.on 'update', (key, value, aOldValue)->
+        key.should.be.equal 'key'
+        value.should.be.equal newValue
+        aOldValue.should.be.equal oldValue
+        done()
+      cache.set 'key', newValue
+      oldValue = cache.get 'key'
+      oldValue.should.be.equal newValue
+      cache.getFixed('key').should.be.equal newValue
+    it 'should listen to "del" event', (done)->
+      oldValue = cache.get 'key'
+      should.exist oldValue
+      cache.on 'del', (key, value)->
+        key.should.be.equal 'key'
+        value.should.be.equal oldValue
+        done()
+      cache.del 'key'
+      cache.has('key').should.be.false
+      cache.hasFixed('key').should.be.false
+  describe "Events on LRU Cache", ->
     cache = Cache(2)
     it 'should listen to "add" event', (done)->
       expected = Math.random()


Modal: Added setPosition method.

Modal: Added setPosition method.

--- src/lib/components/modal/Modal.coffee
+++ src/lib/components/modal/Modal.coffee
@@ -2,6 +2,8 @@

 goog.require 'spark.core.View'
 goog.require 'spark.components.Overlay'
+goog.require 'goog.dom'
+goog.require 'goog.style'

 class spark.components.Modal extends spark.core.View
@@ -40,6 +42,8 @@
     @createTitle_   options.title
     @createContent_ options.content
     @createButtons_ options.buttons
+
+    @setPosition()

   ###*
@@ -116,9 +120,18 @@

   ###*
+    Sets left and top values to center modal in the viewport.

     @export
   ###
+  setPosition: ->
+    viewportSize = goog.dom.getViewportSize window
+    modalSize    = goog.style.getSize @getElement()
+
+    left = Math.max viewportSize.width /  2 - modalSize.width  / 2, 0
+    top  = Math.max viewportSize.height / 2 - modalSize.height / 2, 0
+
+    goog.style.setPosition @getElement(), left, top

   ###*


wait for lists

wait for lists

--- test/js/totodoo.coffee
+++ test/js/totodoo.coffee
@@ -27,6 +27,7 @@

 		it 'is the name of the default list', (done) ->
 			text = @driver.findElement(id: 'listName').getText()
+			@driver.wait(-> @driver.isElementPresent @driver.findElement(id: 'todo-list').findElement(tagName: 'li'), 10000)
 			expect(text).to.eventually.equal 'Public list 1'
 			@timeout 10000, done()


Use contain_consecutive_elements everywhere

--- test/mycha_spec.coffee
+++ test/mycha_spec.coffee
@@ -107,23 +107,25 @@
       it 'calls mocha once', ->
         expect(@mycha_call_stub).to.have.been.calledOnce

+      it 'uses the spec reporter', ->
+        expect(@mocha_argument).to.contain_consecutive_elements '--reporter', 'dot'
+
       it 'enables the CoffeeScript compiler', ->
-        expect(@mocha_argument[0]).to.equal '--compilers'
-        expect(@mocha_argument[1]).to.equal 'coffee:coffee-script'
-
-      it 'uses the dot reporter', ->
-        expect(@mocha_argument[2]).to.equal '--reporter'
-        expect(@mocha_argument[3]).to.equal 'dot'
+        expect(@mocha_argument).to.contain_consecutive_elements '--compilers', 'coffee:coffee-script'

       it 'activates colors', ->
-        expect(@mocha_argument[4]).to.equal '--colors',
+        expect(@mocha_argument).to.contain '--colors',

       it 'loads Mychas test helper before the test files', ->
-        expect(@mocha_argument[5]).to.equal path.resolve 'lib/test_helper.coffee'
+        helper_index = @mocha_argument.indexOf path.resolve 'lib/test_helper.coffee'
+        first_test_index = @mocha_argument.indexOf path.resolve 'test_data/two_tests/test/one_test.coffee'
+        second_test_index = @mocha_argument.indexOf path.resolve 'test_data/two_tests/test/two_test.coffee'
+        expect(helper_index).to.be.lessThan first_test_index
+        expect(helper_index).to.be.lessThan second_test_index

       it 'runs only the given test', ->
         expect(@mocha_argument[6]).to.equal path.resolve 'test_data/two_tests/test/two_test.coffee'

       it 'does not provide any other arguments than listed above plus the done callback', ->
-        expect(@mocha_argument.length).to.equal 7
+        expect(@mocha_argument.length).to.equal 8


Inject javascript into head with defer

Inject javascript into head with defer

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -288,8 +288,8 @@
             selector: 'head'
             html: '<link rel="stylesheet" type="text/css" href="style.css"/>'
           ,
-            selector: 'body'
-            html: '<script type="text/javascript" src="script.js"></script>'
+            selector: 'head'
+            html: '<script type="text/javascript" src="script.js" defer="defer"></script>'
           ]
           callback: ($) ->
             template = grunt.file.read('public/template.html')


Missprint

Missprint

--- stl/core/src/hash.coffee
+++ stl/core/src/hash.coffee
@@ -31,7 +31,7 @@
 Hash.include = (params)->
   Class.include.apply(Hash, arguments)

-  for name, method of params
+  for name of params
     do (name)->
       Hash[name] = ->
         args = A(arguments)


Comment out Torrent.

Comment out Torrent.

--- server/modifiers.coffee
+++ server/modifiers.coffee
@@ -185,12 +185,12 @@
 #   return 0x1000

 # Torrent
-attackStatModifier.add 20, (move, battle, user, target) ->
-  type = move.getType(battle, user, target)
-  if user.hasAbility('Torrent') && type == 'Water' &&
-      user.currentHP <= Math.floor(user.stat('hp') / 3)
-    return 0x800
-  return 0x1000
+# attackStatModifier.add 20, (move, battle, user, target) ->
+#   type = move.getType(battle, user, target)
+#   if user.hasAbility('Torrent') && type == 'Water' &&
+#       user.currentHP <= Math.floor(user.stat('hp') / 3)
+#     return 0x800
+#   return 0x1000

 # Guts
 # attackStatModifier.add 30, (move, battle, user, target) ->


removed specs

removed specs

--- spec/block-cursor-spec.coffee
+++ spec/block-cursor-spec.coffee
@@ -1,49 +0,0 @@
-fs = require 'fs'
-path = require 'path'
-blockCursor = require '../lib/block-cursor'
-
-describe 'Block Cursor', ->
-  beforeEach ->
-    waitsForPromise -> atom.packages.activatePackage 'block-cursor'
-
-  it 'adds the block-cursor class to the workspaceView', ->
-    workspaceView = atom.views.getView atom.workspace
-    expect(workspaceView.className).toMatch /\s*block-cursor\s*/
-
-  describe 'config', ->
-    colorsFile = path.join __dirname, '..', 'styles', 'includes', 'colors.less'
-
-    describe 'the primary color option', ->
-      it 'changes only the first line of styles/colors.less', ->
-        initialLines = fs.readFileSync(colorsFile, 'utf8').split '\n'
-        newColor = do ->
-          initialColor = atom.config.get 'block-cursor.primaryColor'
-          initialColor.red = (initialColor.red + 1) % 256
-          initialColor
-        atom.config.set 'block-cursor.primaryColor', newColor
-        newLines = fs.readFileSync(colorsFile, 'utf8').split '\n'
-        expect(newLines[0]).not.toEqual(initialLines[0])
-        expect(newLines[1]).toEqual(initialLines[1])
-
-    describe 'the secondary color option', ->
-      it 'changes only the second line of styles/colors.less', ->
-        initialLines = fs.readFileSync(colorsFile, 'utf8').split '\n'
-        newColor = do ->
-          initialColor = atom.config.get 'block-cursor.secondaryColor'
-          initialColor.red = (initialColor.red + 1) % 256
-          initialColor
-        atom.config.set 'block-cursor.secondaryColor', newColor
-        newLines = fs.readFileSync(colorsFile, 'utf8').split '\n'
-        expect(newLines[0]).toEqual(initialLines[0])
-        expect(newLines[1]).not.toEqual(initialLines[1])
-
-    describe 'the enablePulse option', ->
-      it 'adds the block-cursor-pulse class to the workspaceView if enabled', ->
-        workspaceView = atom.views.getView atom.workspace
-        atom.config.set 'block-cursor.enablePulse', true
-        expect(workspaceView.className).toMatch /\s*block-cursor-pulse\s*/
-
-      it 'removes the block-cursor-pulse class from the workspaceView if disabled', ->
-        workspaceView = atom.views.getView atom.workspace
-        atom.config.set 'block-cursor.enablePulse', false
-        expect(workspaceView.className).not.toMatch /block-cursor-pulse/


stop unwanted events from triggering

stop unwanted events from triggering

--- resources/assets/coffee/_classes/forum.coffee
+++ resources/assets/coffee/_classes/forum.coffee
@@ -182,12 +182,13 @@
               else
                 $(post).offset().top

-    target = if postTop == 0 then 0 else post
-
-    # FIXME: target is off by the size of the sub sticky header on first load
-    # if load more is present on the top.
-    $(window).stop().scrollTo target, 500,
-      offset: if postTop == 0 then 0 else -(StickyHeader.offsetForScrollTo 68) # FIXME: less magic number
+    target = if postTop == 0
+               0
+             else
+               postTop - StickyHeader.offsetForScrollTo(68)
+
+    # using jquery smooth scrollTo will cause unwanted events to trigger on the way down.
+    window.scrollTo window.pageXOffset, target

   initialScrollTo: =>


make startup message consistent

make startup message consistent

--- services/web/app.coffee
+++ services/web/app.coffee
@@ -33,7 +33,7 @@
 port = Settings.port or Settings.internal?.web?.port or 3000
 host = Settings.internal.web.host or "localhost"
 Server.server.listen port, host, ->
-	logger.info("web-sharelatex listening on port #{port}")
+	logger.info "web starting up, listening on #{host}:#{port}"
 	logger.info("#{require('http').globalAgent.maxSockets} sockets enabled")
 	if argv.user
 		process.setuid argv.user


Improve the comment and reference bug for Deps.flush issue.

--- client/import.coffee
+++ client/import.coffee
@@ -112,10 +112,12 @@
       # So that meteor-file knows what to update
       file._id = id

-      # We try to make sure list of files is rendered before hashing
+      # We make sure list of files is rendered before hashing
       Deps.flush()

-      # Just temporary return to the event thread so that everything finishes rendering
+      # TODO: Remove the following workaround for a bug
+      # Deps.flush does not seem to really do it, so we have to use Meteor.setTimeout to workaround
+      # See: https://github.com/meteor/meteor/issues/1619
       Meteor.setTimeout ->
         # TODO: We should read in chunks, not whole file
         reader.readAsArrayBuffer file


resurrection.activity: reset and hide embedbox after input submit succeeded

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -56,6 +56,7 @@
         fn = @bound if activity then "update" else "create"
         fn data, (err, activity) =>
           callback? err, activity
+          @embedBox.resetEmbedAndHide()
     ]

   encodeTagSuggestions: (str, tags) ->


create var only if it will be used

create var only if it will be used

--- client/app/actions/layout_action_creator.coffee
+++ client/app/actions/layout_action_creator.coffee
@@ -113,9 +113,9 @@

     showMessageList: (panelInfo) ->
         params = panelInfo.parameters
-        {accountID, mailboxID, filter, sort, before, after} = params

         # ensure the proper account is selected
+        {accountID, mailboxID} = params
         AccountActionCreator.ensureSelected accountID, mailboxID

         AppDispatcher.handleViewAction


stub out unwanted dependency in unit tests


Login: removed includes

Login: removed includes

--- client/landing/app/coffee/login/includes.coffee
+++ client/landing/app/coffee/login/includes.coffee
@@ -1,18 +0,0 @@
-module.exports = [
-
-  'AppController.coffee'
-  'AppView.coffee'
-
-  # LOGIN VIEWS
-  'Views/loginoptions.coffee'
-  'Views/registeroptions.coffee'
-  'Views/resendmailconfirmationform.coffee'
-  'Views/recoverform.coffee'
-  'Views/resetform.coffee'
-  'Views/finishregistrationform.coffee'
-  'Views/redeemform.coffee'
-
-  # Styles
-  "styl/app.login-signup.styl"
-
-]


Generalizing user pages. renders added to route

--- client/router.coffee
+++ client/router.coffee
@@ -12,13 +12,17 @@
 	 template: 'algopedia'
   }
   @route 'profile',{
-	 template: 'profile'
+	 template: 'userPage'
+    action: ->
+		 Session.set 'lastUserSearch',Meteor.user()
+		 this.render()
   }
   @route 'users',{
     path: '/users/:_id'
     template: 'userPage'
 	 action: ->
 		 Session.set 'lastUserSearch',this.params._id
+		 this.render()
   }
   @route 'pedia',{
     path:'/pedia/:_id'


docs(tap): verbose docs

docs(tap): verbose docs

--- modules/directives/tap/index.coffee
+++ modules/directives/tap/index.coffee
@@ -1,15 +1,27 @@
 # # Tap
-
+# ## Description
+# The `bpTap` allows you to specify custom behavior when element is tapped.
+# ## Usage
+# as attribute
+# `<ANY bp-tap="{expression}">`
+# ### Parameters
+# * `ngClick` – `{expression}` Expression to evaluate upon tap.
 angular.module('bp.directives').directive 'bpTap', deps [
   'bpConfig'
   ], (
   bpConfig
   ) ->
   (scope, element, attrs) ->
+    # ### Options
+    # You can specify some options as attributes to control the behavior.
     options = angular.extend
+      # * `bp-no-scroll` – Prevent page scroll when moving after touchstart
       noScroll: no
+      # * `bp-active-class - `CSS class` - Class to add during tap
       activeClass: 'bp-active'
+      # * `bp-bound-margin - `int` - Pixels allowed to move to fire tap
       boundMargin: 50
+      # * `bp-allow-click - Fire click and tap events
       allowClick: no
     , bpConfig.tap or {}

@@ -17,19 +29,16 @@
       attr = attrs["bp#{key.charAt(0).toUpperCase()}#{key.slice(1)}"]
       if attr? then options[key] = if attr is '' then true else attr

-    # # Intelligent Defaults
-    # ## bp-no-scroll
-    # Apply bp-no-scroll to tappable buttons in a navbar
+    # #### Intelligent Defaults
+    # * Apply `bp-no-scroll` to  `bp-button` within `bp-navbar`
     if element.is('bp-button') and element.parent().is('bp-navbar')
       element.attr 'bp-no-scroll', ''
       options.noScroll = yes
-    # ## bp-bound-margin
-    # Set bound margin to 5 when cell is seated inside an iScroll
+    # * Set `bp-bound-margin` to 5 when `bp-cell` is within `bp-iscroll`
     if element.parents('[bp-iscroll]').length
       element.attr 'bp-bound-margin', '5'
       options.boundMargin = 5
-    # ## bp-tap='fn(arg1,arg2)'
-    # Apply back button CSS class if tap will result in reverse transition.
+    # * Apply `bp-button-back` class if tap will result in reverse transition.
     if /to\(('|")[A-Za-z]+('|"),true\)/.test attrs.bpTap
       element.addClass 'bp-button-back'