remove the mystery space. closes #117

remove the mystery space. closes #117

--- lib/result-view.coffee
+++ lib/result-view.coffee
@@ -128,10 +128,7 @@
             else if not @resultType or @resultType == 'text'
                 console.log "rendering as text"
                 @resultType = 'text'
-                if container.innerText.length > 0
-                    container.innerText = container.innerText + (" " + result.data)
-                else
-                    container.innerText = container.innerText + result.data
+                container.innerText = container.innerText + result.data

                 if /\r|\n/.exec(container.innerText.trim())
                     @setMultiline(true)


Clampable#clampToBounds

Clampable#clampToBounds


Fixing jQuery cache issue and interface traffic bar display

--- public/javascripts/application.coffee
+++ public/javascripts/application.coffee
@@ -197,6 +197,7 @@
   # Function for sorting on metadata
   pxl_meta = (node) ->
     if($(node).hasClass('pxl-meta'))
+      $(node).removeData('pxl-meta') # This clears the jQuery cache, allowing it to see the values updated by d3
       $(node).data('pxl-meta')
     else
       node.innerText
@@ -221,6 +222,7 @@

 color_cell = (cell,bgcolor) ->
   return if !cell.firstChild # Exits if the cell is empty
+  $(cell).removeData('pxl-meta') # This clears the jQuery cache, allowing it to see the values updated by d3
   percentage = $(cell).data('pxl-meta')
   if percentage < 80
     color = '#BFB'
@@ -236,12 +238,16 @@

 color_table = ->
-  $('tr').mouseenter ->
+  $('tr').unbind('mouseenter.pixel1')
+  $('tr').bind('mouseenter.pixel1', ->
     for cell in $(this).find('.pxl-histogram')
       color_cell(cell,'#F5F5F5')
-  $('tr').mouseleave ->
+  )
+  $('tr').unbind('mouseleave.pixel1')
+  $('tr').bind('mouseleave.pixel1', ->
     for cell in $(this).find('.pxl-histogram')
       color_cell(cell,'#FFF')
+  )
   bgcolor = '#FFF'
   for cell in document.getElementsByClassName('pxl-histogram')
     color_cell(cell,bgcolor)
@@ -546,11 +552,11 @@
     .append("td")
     .attr('class', 'dynamic') # So we can separate this TR from any child TRs
     # Apply attrs and class if the cell data is a hash and has the attribute/class key
-    .attr('data-pxl-meta', (d,i) -> d && d['pxl-meta'])
     .classed('pxl-meta', (d,i) -> d && d['pxl-meta'])
     .classed('pxl-td-shrink', (d,i) -> d && d['pxl-td-shrink'])
     .classed('pxl-histogram', (d,i) -> d && d['pxl-histogram'])
     .classed('pxl-hidden', (d,i) -> d && d['pxl-hidden'])
+  td.attr('data-pxl-meta', (d,i) -> d && d['pxl-meta'])

   td.html(ident)


more helpful for certain local hook errors

--- code/instance.coffee
+++ code/instance.coffee
@@ -205,10 +205,15 @@
   # Spawn a command locally, needs to be executable
   # TODO: put somewhere more sane, name it properly: exec?
   _local_sh: (command, args, callback) ->
+    if command.match /^\.\./
+      console.log "Command starts .. [#{command}]; that probably won't work"
     cmd = spawn command, args
     cmd.stdout.on 'data', (data) -> console.log data.toString('ascii')
     cmd.stderr.on 'data', (data) -> console.log data.toString('ascii')
-    cmd.on 'exit', callback
+    cmd.on 'exit', (code, signal) ->
+      if code or signal
+        console.log "exit status #{code} signal #{signal}"
+      callback()

 _common_ssh_args = (key) -> [
   '-o', 'LogLevel=ERROR',


preparing documentation for 0.9.5

preparing documentation for 0.9.5

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -20,7 +20,7 @@
   require.registerExtension '.coffee', (content) -> compile content

 # The current CoffeeScript version number.
-exports.VERSION = '0.9.4'
+exports.VERSION = '0.9.5'

 # Expose helpers for testing.
 exports.helpers = require './helpers'


check existence of "path" for browser execution

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -294,7 +294,7 @@
 # Generates the source map for a coffee file and stores it in the local cache variable.
 getSourceMap = (filename) ->
   return sourceMaps[filename] if sourceMaps[filename]
-  return unless path.extname(filename) in extensions
+  return unless path?.extname(filename) in extensions
   answer = compileFile filename, true
   sourceMaps[filename] = answer.sourceMap


Implementation of handling incorrect overloading

--- src/Overloadable.coffee
+++ src/Overloadable.coffee
@@ -20,6 +20,12 @@

 #overload is always bound to a signature container of an overloadable function
 overload = (argumentsList, functionToCall) ->
+    if not Array.isArray(argumentsList) or argumentsList.length is 0
+        throw new Error "You should pass a nonempty array of arguments expected in a signature"
+
+    unless typeof functionToCall is "function"
+        throw new Error "You should pass a function that will be assigned to array of arguments"
+
     @push { argumentsList, functionToCall }

 invoke = (signaturesContainer, invocationArguments, defaultFunction) ->


increase minimum guaranteed RAM per project by 50%

--- src/smc-hub/kucalc/quota.coffee
+++ src/smc-hub/kucalc/quota.coffee
@@ -10,8 +10,8 @@

 # Min possible **guaranteed** RAM.
 MIN_POSSIBLE_MEMORY =
-    member    : 200
-    nonmember : 100
+    member    : 300
+    nonmember : 200

 misc = require('smc-util/misc')


Chat: Change editing to a map

Chat: Change editing to a map

In the future, we may store each user's current edits

--- src/smc-webapp/editor_chat.cjsx
+++ src/smc-webapp/editor_chat.cjsx
@@ -52,11 +52,12 @@
         m = messages = @redux.getStore(@name).get('messages')
         for x in changes
             if x.insert
-                # console.log('Received', x.insert)
+                console.log('Received', x.insert)
                 # OPTIMIZATION: make into custom conversion to immutable
+                if not x.insert.editing
+                     x.insert.editing = {}
                 message = immutable.fromJS(x.insert)
                 message = message.set('history', immutable.Stack(immutable.fromJS(x.insert.history)))
-                message = message.set('editing', immutable.Set(x.insert.editing))
                 messages = messages.set("#{x.insert.date - 0}", message)
             else if x.remove
                 messages = messages.delete(x.remove.date - 0)
@@ -92,7 +93,8 @@
         author_id = @redux.getStore('account').get_account_id()

         if is_editing
-            editing = message.get('editing').add(author_id)
+            # TODO: Save edit changes
+            editing = message.get('editing').set(author_id, 'TODO')
         else
             editing = message.get('editing').remove(author_id)

@@ -159,7 +161,7 @@
                 alert_message(type:'error', message:"json in #{@filename} is broken")
             else
                 v = {}
-                #console.log("DATA ON LOAD:", syncdb.select())
+                console.log("DATA ON LOAD:", syncdb.select())
                 for x in syncdb.select()
                     if x.history
                         x.history = immutable.Stack(immutable.fromJS(x.history))
@@ -169,10 +171,8 @@
                             author_id : x.sender_id
                             date      : x.date
                         x.history = immutable.Stack([initial])
-                    if x.editing
-                        x.editing = immutable.Set(x.editing)
-                    else
-                        x.editing = immutable.Set()
+                    if not x.editing
+                        x.editing = {}
                     v[x.date - 0] = x
                 actions.setState(messages : immutable.fromJS(v))
                 syncdb.on('change', actions._syncdb_change)
@@ -229,7 +229,7 @@
         @props.message.get('history').peek().get('content') ? ''

     is_editing: ->
-        @props.message.get('editing').includes(@props.account_id)
+        @props.message.get('editing').has(@props.account_id)

     sender_is_viewer: ->
         @props.account_id == @props.message.get('sender_id')
@@ -240,7 +240,7 @@
         </div>

     editing_status: ->
-        other_editors = @props.message.get('editing').remove(@props.account_id)
+        other_editors = @props.message.get('editing').remove(@props.account_id).keySeq()
         if @is_editing()
             if other_editors.size == 1
                 # This user and someone else is also editing
@@ -256,6 +256,7 @@
             else
                 text = "You are now editing ... Shift+Enter to submit changes."
         else
+            console.log(other_editors.toJS(), other_editors.size)
             if other_editors.size == 1
                 # One person is editing
                 text = "#{@props.get_user_name(other_editors.first())} is editing this message"


removed space

removed space

--- src/smc-webapp/project_new.cjsx
+++ src/smc-webapp/project_new.cjsx
@@ -26,7 +26,6 @@
 {React, ReactDOM, Actions, Store, Table, rtypes, rclass, Redux}  = require('./smc-react')

 ReactDOMServer = require('react-dom/server')
-

 {Col, Row, Button, ButtonGroup, ButtonToolbar, FormControl, FormGroup, Panel, Well, SplitButton, MenuItem, Alert} = require('react-bootstrap')
 {ErrorDisplay, Icon, Loading, TimeAgo, Tip, ImmutablePureRenderMixin, Space} = require('./r_misc')


namespaced unbind

namespaced unbind

--- test/bindable.coffee
+++ test/bindable.coffee
@@ -58,7 +58,7 @@
   o.bind "test.TestNamespace", ->
     ok true

-  o.trigger ".TestNamespace"
+  o.trigger "test"

   o.unbind ".TestNamespace"
   o.trigger ".TestNamespace"


Add new error type.

Add new error type.

--- helper/errors.coffee
+++ helper/errors.coffee
@@ -36,4 +36,10 @@
   defaultExplanation: "The rate limit for prodived credentials was reached"
   defaultResponse: "Try again later - OR - Ask the API developer to increase the limit"

+errors.create
+  name: "NullBodyData"
+  defaultMessage: "The response is empty"
+  defaultExplanation: "In rare cases the response has no data and it seems like a bug"
+  defaultResponse: "Try again"
+
 module.exports = errors


Apparently my development machine isn't performant enough ;)

--- tests/sockets.coffee
+++ tests/sockets.coffee
@@ -29,7 +29,7 @@

   'server broadcasts': (t) ->
     t.expect 'reached1', 'reached2', 'data1', 'data2'
-    t.wait 3000
+    t.wait 10000

     zapp = zappa port++, ->
       @on shout: ->
@@ -54,7 +54,7 @@

   'server ack': (t) ->
     t.expect 'got-foo', 'acked', 'data'
-    t.wait 3000
+    t.wait 10000

     zapp = zappa port++, ->
       @on foo: ->
@@ -71,7 +71,7 @@
   'server rooms': (t) ->
     t.expect 'joined1', 'room1', 'joined2', 'room2',
       'reached1', 'reached2', 'data1', 'data2'
-    t.wait 3000
+    t.wait 10000

     zapp = zappa port++, ->
       @on join: ->


debug output to catch syncrecent bug

debug output to catch syncrecent bug

--- src/ui/models/connection.coffee
+++ src/ui/models/connection.coffee
@@ -31,11 +31,13 @@
         return if @lastActive == active
         timegap = active - @lastActive
         if not force and timegap > 10 * 60 * 1000
+            console.log 'syncrecent', force, timegap
             # if we have a gap of more than 10 minutes, we will
             # reinitialize all convs using syncrecentconversations
             # (sort of like client startup)
             later -> action 'syncrecentconversations'
         else if not force and timegap > 40000
+            console.log 'syncallnew', force, timegap
             # if we have a gap of more than 40 seconds we try getting
             # any events we may have missed during that gap. notice
             # that we get 'noop' every 20-30 seconds, so there is no


Don't open the virtual keyboard on small screens.

The keyboard obstructs the time picker widget on mobile.

--- src/views/route-settings.coffee
+++ src/views/route-settings.coffee
@@ -240,6 +240,8 @@
             datetime = moment @model.getDatetime()
             today = new Date()
             tomorrow = moment(today).add 1, 'days'
+            # try to avoid opening the mobile virtual keyboard
+            disable_keyboard: $(window).innerHeight() < 700
             is_today: not @forceDateInput and datetime.isSame(today, 'day')
             is_tomorrow: datetime.isSame tomorrow, 'day'
             params: @model


Update config.cson

Update config.cson

--- editor/config.cson
+++ editor/config.cson
@@ -67,6 +67,3 @@
     showOnStartup: false
   whitespace:
     ignoreWhitespaceOnCurrentLine: false
-".ini.source":
-  editor:
-    tabLength: 4


pricing: reintroduce developer and enterprise routes

--- client/Pricing/AppController.coffee
+++ client/Pricing/AppController.coffee
@@ -1,4 +1,21 @@
 class PricingAppController extends KDViewController
+
+  handler = (callback)->
+    KD.singleton('appManager').open 'Pricing', callback
+
+  KD.registerAppClass this,
+    name                         : "Pricing"
+    routes                       :
+      "/:name?/Pricing"          : -> KD.singletons.router.handleRoute '/Pricing/Developer'
+      "/:name?/Pricing/:section" : ({params:{section}})->
+        handler (app)->
+          app.resourcePackPlan.hide()
+          app.customPlan.hide()
+          switch section
+            when 'Developer'
+              app.resourcePackPlan.show()
+            when 'Enterprise'
+              app.customPlan.show()

   KD.registerAppClass this,
     name  : "Pricing"


Fixes #587: Use the correct channel object

--- src/api/channels.coffee
+++ src/api/channels.coffee
@@ -60,7 +60,7 @@
     this.status = 201
     logger.info 'User %s created channel with id %s', this.authenticated.email, channel.id

-    processPostAddTriggers channel
+    processPostAddTriggers channelData
   catch err
     # Error! So inform the user
     utils.logAndSetResponse this, 400, "Could not add channel via the API: #{err}", 'error'
@@ -145,7 +145,7 @@
     this.body = 'The channel was successfully updated'
     logger.info 'User %s updated channel with id %s', this.authenticated.email, id

-    processPostUpdateTriggers channel
+    processPostUpdateTriggers channelData
   catch err
     # Error! So inform the user
     utils.logAndSetResponse this, 500, "Could not update channel by id: #{id} via the API: #{e}", 'error'


:bug: handle clicking next/previous

:bug: handle clicking next/previous

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -114,10 +114,7 @@

       e.preventDefault()
       e.stopPropagation()
-      href = $(e.currentTarget).attr('href')
-      # this sends the page without a version number (which causes analytics to fire).
-      # It fires twice because @mediaParent.trackAnalytics() also sends an event.
-      router.navigate href, {trigger: false}, () => @mediaParent.trackAnalytics()
+      # Rely on the Content.sendPageViewAnalytics to fire

     closeContentsOnSmallScreen: ->
       if window.innerWidth < 640


added some more padding between navi and products.

--- public/views/widget_view.coffee
+++ public/views/widget_view.coffee
@@ -16,7 +16,7 @@

     <style type="text/css">
       .epages-shop-navigation {
-        margin-bottom: 30px;
+        margin-bottom: 50px;
       }
       .epages-shop-navigation > div {
         float: left;


Change RooBot's email

Change RooBot's email

--- scripts/git.coffee
+++ scripts/git.coffee
@@ -58,7 +58,7 @@
       repo.getHeadCommit()
     .then (parent) ->
       author = Git.Signature.now user.name, user.email
-      committer = Git.Signature.now 'roobot', 'website@gpa-centex.org'
+      committer = Git.Signature.now 'RooBot', 'roobot@gpa-centex.org'
       repo.createCommit 'HEAD', author, committer, message, tree, [parent]
     .then (oid) ->
       callback oid


Remove test-paths

Remove test-paths

--- .config/projects.cson
+++ .config/projects.cson
@@ -121,8 +121,6 @@
 	title: "file-icons"
 	paths: [
 		"/Users/johngardner/.atom/packages/file-icons"
-		"/Users/johngardner/Desktop/PathsTest/Root1/Root2"
-		"/Users/johngardner/Desktop/PathsTest"
 	]
 	devMode: true
 "language-viml":


Add snippets for Node and Makefiles

Add snippets for Node and Makefiles

--- .config/snippets.cson
+++ .config/snippets.cson
@@ -17,12 +17,20 @@
 # If you are unfamiliar with CSON, you can read more about it here:
 # https://github.com/bevry/cson#what-is-cson

-'.source.js':
-	'Object key — key: "value"':
-		'prefix': ':'
-		'body': ':\t'

+# Universal
 "*":
 	"HTML Document":
 		prefix: "htmldoc"
 		body: '<!DOCTYPE html>\n\n\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n<meta http-equiv="X-UA-Compatible" content="IE=edge" />\n<meta name="viewport" content="initial-scale=1, minimum-scale=1" />\n<title>$1</title>\n\n\n\n\n\n\n\t\n\t$2\n\t\n\n'
+
+	"Node Script":
+		prefix: "node"
+		body: '#!/usr/local/bin/node --es_staging\n"use strict";\n\n'
+
+
+# Makefile
+".source.makefile":
+	"Windows check":
+		prefix: "iswin"
+		body: '# Windows: CMD.exe\nifdef SystemRoot\n\tIS_WINDOWS = 1\nendif\n\n# Windows: MinGW / COMMAND.COM\nifdef SYSTEMROOT\n\tIS_WINDOWS = 1\nendif\n\n'


Added __setitem__ to List

Added __setitem__ to List

--- src/builtin/list.coffee
+++ src/builtin/list.coffee
@@ -91,6 +91,23 @@
   __reversed__: ->
     return new ListReverseIterator(@)

+  __setitem__: (index, element) ->
+    exceededNegativeRange = index.__abs__().__gt__(len(@)).value
+    exceededPositiveRange = index.__ge__(len(@)).value
+    if exceededNegativeRange or exceededPositiveRange
+      raise new IndexError "list assignment index out of range"
+    if index.value > -1
+      @value[index.value] = element
+    else
+      @value[len(@).value + index.value] = element
+    return
+
+  # **Unimplemented**
+  __setslice__: (leftIndex, rightIndex) ->
+
+  # **Unimplemented**
+  __sizeof__: ->
+
   append: (element) ->
     @value.push element
     return


Fix incorrect state save when exiting

Fix incorrect state save when exiting

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -105,7 +105,7 @@
         app.quit()
         return
     @windows.splice(@windows.indexOf(window), 1)
-    @saveState() unless window.isSpec
+    @saveState(true) unless window.isSpec

   # Public: Adds the {AtomWindow} to the global window list.
   addWindow: (window) ->
@@ -116,14 +116,14 @@

     unless window.isSpec
       focusHandler = => @lastFocusedWindow = window
-      blurHandler = => @saveState()
+      blurHandler = => @saveState(false)
       window.browserWindow.on 'focus', focusHandler
       window.browserWindow.on 'blur', blurHandler
       window.browserWindow.once 'closed', =>
         @lastFocusedWindow = null if window is @lastFocusedWindow
         window.browserWindow.removeListener 'focus', focusHandler
         window.browserWindow.removeListener 'blur', blurHandler
-      window.browserWindow.webContents.once 'did-finish-load', => @saveState()
+      window.browserWindow.webContents.once 'did-finish-load', => @saveState(false)

   # Creates server to listen for additional atom application launches.
   #
@@ -213,7 +213,7 @@
     @openPathOnEvent('application:open-license', path.join(process.resourcesPath, 'LICENSE.md'))

     app.on 'before-quit', =>
-      @saveState() if @hasEditorWindows()
+      @saveState(false)
       @quitting = true

     app.on 'will-quit', =>
@@ -221,7 +221,7 @@
       @deleteSocketFile()

     app.on 'will-exit', =>
-      @saveState() if @hasEditorWindows()
+      @saveState(false)
       @killAllProcesses()
       @deleteSocketFile()

@@ -439,14 +439,15 @@
         console.log("Killing process #{pid} failed: #{error.code ? error.message}")
     delete @pidsToOpenWindows[pid]

-  saveState: ->
+  saveState: (allowEmpty=false) ->
     return if @quitting
     states = []
     for window in @windows
       unless window.isSpec
         if loadSettings = window.getLoadSettings()
           states.push(initialPaths: loadSettings.initialPaths)
-    @storageFolder.store('application.json', states)
+    if states.length > 0 or allowEmpty
+      @storageFolder.store('application.json', states)

   hasEditorWindows: ->
     @windows.some (window) -> not window.isSpec


Correct resolve to window-bootstrap

Correct resolve to window-bootstrap

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -251,7 +251,7 @@
         bootstrapScript = require.resolve(path.join(global.devResourcePath, 'src', 'window-bootstrap'))
       else
         resourcePath = @resourcePath
-        bootstrapScript = require.resolve('./window-bootstrap')
+        bootstrapScript = require.resolve('../window-bootstrap')
       openedWindow = new AtomWindow({pathToOpen, initialLine, bootstrapScript, resourcePath, devMode, initialSize})

     if pidToKillWhenClosed?


:fire: whitespace

:fire: whitespace

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -141,7 +141,6 @@

     # Check for update after Atom has fully started and the menus are created
     setTimeout((-> autoUpdater.checkForUpdates()), 10000)
-

   checkForUpdate: ->
     autoUpdater.once 'update-available', ->


Don't save window state when closing spec windows

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -104,7 +104,7 @@
       if process.platform in ['win32', 'linux']
         app.quit()
         return
-    @saveState()
+    @saveState() unless window.isSpec

   # Public: Adds the {AtomWindow} to the global window list.
   addWindow: (window) ->


Teamwork: Minor code refactor.

Teamwork: Minor code refactor.

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -70,12 +70,12 @@
         callback  : => @createEditor()
       "Terminal"  :
         callback  : => @createTerminal()
-      "Preview"   :
+      "Browser"   :
         callback  : => @createPreview()
-      "Chat"      :
-        callback  : => @createChat()
       "Drawing Board":
         callback  : => @createDrawingBoard()
+      # "Chat"      :
+      #   callback  : => @createChat()
     }

   createTabFromFirebaseData: (data) ->
@@ -110,8 +110,6 @@
         indexKey : "dashboard"

-  openFile: (file, content) ->
-    @createEditor file, content
   createDrawingBoard: (sessionKey, indexKey) ->
     indexKey  = indexKey or @createSessionKey()
     pane      = new KDTabPaneView { title: "Drawing Board", indexKey }
@@ -144,6 +142,9 @@
         indexKey  : indexKey

     @workspace.addToHistory "$0 opened a new editor"  if isLocal
+
+  openFile: (file, content) ->
+    @createEditor file, content

   createTerminal: (sessionKey, indexKey) ->
     indexKey = indexKey or @createSessionKey()


Nao esconde buy button no modo lista

Nao esconde buy button no modo lista

--- src/coffee/portal-buy-button.coffee
+++ src/coffee/portal-buy-button.coffee
@@ -146,7 +146,7 @@
 			@element.hide()
 		if @options.hideUnselected and not @skuData
 			@element.hide()
-		if @productData and (@productData.available == false or @productData.displayMode == 'lista')
+		if @productData and @productData.available is false
 			@element.hide()

 	buyButtonHandler: (evt) =>


ListView: fixed a bug where when lastToFirst option was set,  items were appended out of order

--- src/components/list/listview.coffee
+++ src/components/list/listview.coffee
@@ -120,16 +120,15 @@

     if index is 0
       if boxed
-        @packageItem itemInstance
-      else
-        @addSubView itemInstance, null, lastToFirst
+      then @packageItem itemInstance
+      else @addSubView itemInstance, null, lastToFirst

     else if index > 0

       item = itemInstance.getElement()

       if lastToFirst
-        index = @items.length - index - 2
+        index = @items.length - index - 1
         node  = @items[index].getElement()
         node.parentNode.insertBefore item, node
       else
@@ -151,7 +150,8 @@
     } = @getOptions()

     operation = if lastToFirst then 'prepend' else 'append'
-    newBox    = =>
+
+    newBox = =>
       box = @createBox()
       box.addSubView itemInstance

@@ -166,7 +166,7 @@
   createBox: ->

     @boxes.push box = new KDListViewBox
-    @addSubView box
+    @addSubView box, null, @getOptions().lastToFirst
     box.on 'HeightChanged', (height) => @updateBoxProps box, height
     box.on 'BoxIsEmptied', (id)=>


Increase point boundary for extra life

Increase point boundary for extra life

--- src/components/player/Player.coffee
+++ src/components/player/Player.coffee
@@ -1,3 +1,5 @@
+EXTRA_LIFE_POINT_BOUNDARY = 15000
+
 Crafty.c 'Player',
   init: ->
     @reset()
@@ -48,7 +50,7 @@

   eligibleForExtraLife: ->
     @lastExtraLifeThreshold ||= 0
-    if @points - @lastExtraLifeThreshold >= 10000
+    if @points - @lastExtraLifeThreshold >= EXTRA_LIFE_POINT_BOUNDARY
       yes

   rewardExtraLife: ->


correct module references

correct module references

--- src/jobs/a-projects-get/job.coffee
+++ src/jobs/a-projects-get/job.coffee
@@ -9,6 +9,8 @@

 https://cloud.google.com/compute/docs/reference/latest/projects/get

+20170904 2310 - error on github authentication.
+
 ###

@@ -16,7 +18,7 @@
 http   = require 'http'
 _      = require 'lodash'

-class ListEventsByUser
+class ProjectsGet
   constructor: ({@encrypted}) ->
     @github = new Github
       debug: true
@@ -51,4 +53,4 @@
     error.code = code
     return error

-module.exports = ListEventsByUser
+module.exports = ProjectsGet


re order settings

re order settings

--- packages/rocketchat-theme/server/variables.coffee
+++ packages/rocketchat-theme/server/variables.coffee
@@ -1,30 +1,30 @@
-RocketChat.theme.addPublicColor "content-background-color", "#FFF"
 RocketChat.theme.addPublicColor "primary-background-color", "#04436A"
+RocketChat.theme.addPublicColor "primary-font-color", "#444444"
 RocketChat.theme.addPublicColor "secondary-background-color", "#F4F4F4"
+RocketChat.theme.addPublicColor "secondary-font-color", "#7F7F7F"
 RocketChat.theme.addPublicColor "tertiary-background-color", "#EAEAEA"
-RocketChat.theme.addPublicColor "primary-font-color", "#444444"
-RocketChat.theme.addPublicColor "secondary-font-color", "#7F7F7F"
 RocketChat.theme.addPublicColor "tertiary-font-color", "rgba(255, 255, 255, 0.6)"
 RocketChat.theme.addPublicColor "quaternary-font-color", "#FFF"
-RocketChat.theme.addPublicColor "input-font-color", "rgba(255, 255, 255, 0.85)"
-RocketChat.theme.addPublicColor "link-font-color", "#008CE3"
-RocketChat.theme.addPublicColor "info-font-color", "#AAAAAA"
-RocketChat.theme.addPublicColor "info-active-font-color", "#FF0000"
-RocketChat.theme.addPublicColor "smallprint-font-color", "#C2E7FF"
-RocketChat.theme.addPublicColor "smallprint-hover-color", "#FFFFFF"
-RocketChat.theme.addPublicColor "status-online", "#35AC19"
-RocketChat.theme.addPublicColor "status-offline", "rgba(150, 150, 150, 0.50)"
-RocketChat.theme.addPublicColor "status-busy", "#D30230"
-RocketChat.theme.addPublicColor "status-away", "#FCB316"
+
+RocketChat.theme.addPublicColor "action-buttons-color", "#13679a"
+RocketChat.theme.addPublicColor "active-channel-background-color", "rgba(255, 255, 255, 0.075)"
+RocketChat.theme.addPublicColor "active-channel-font-color", "rgba(255, 255, 255, 0.75)"
+RocketChat.theme.addPublicColor "blockquote-background", "#CCC"
+RocketChat.theme.addPublicColor "clean-buttons-color", "rgba(0, 0, 0, 0.025)"
 RocketChat.theme.addPublicColor "code-background", "#F8F8F8"
 RocketChat.theme.addPublicColor "code-border", "#CCC"
 RocketChat.theme.addPublicColor "code-color", "#333"
-RocketChat.theme.addPublicColor "blockquote-background", "#CCC"
+RocketChat.theme.addPublicColor "content-background-color", "#FFF"
+RocketChat.theme.addPublicColor "custom-scrollbar-color", "rgba(255, 255, 255, 0.05)"
+RocketChat.theme.addPublicColor "info-active-font-color", "#FF0000"
+RocketChat.theme.addPublicColor "info-font-color", "#AAAAAA"
+RocketChat.theme.addPublicColor "input-font-color", "rgba(255, 255, 255, 0.85)"
+RocketChat.theme.addPublicColor "link-font-color", "#008CE3"
 RocketChat.theme.addPublicColor "message-hover-background-color", "#f9f9f9"
-RocketChat.theme.addPublicColor "active-channel-background-color", "rgba(255, 255, 255, 0.075)"
-RocketChat.theme.addPublicColor "active-channel-font-color", "rgba(255, 255, 255, 0.75)"
-RocketChat.theme.addPublicColor "custom-scrollbar-color", "rgba(255, 255, 255, 0.05)"
-RocketChat.theme.addPublicColor "action-buttons-color", "#FFF"
-RocketChat.theme.addPublicColor "clean-buttons-color", "rgba(0, 0, 0, 0.025)"
+RocketChat.theme.addPublicColor "smallprint-font-color", "#C2E7FF"
+RocketChat.theme.addPublicColor "smallprint-hover-color", "#FFFFFF"
+RocketChat.theme.addPublicColor "status-away", "#FCB316"
+RocketChat.theme.addPublicColor "status-busy", "#D30230"
+RocketChat.theme.addPublicColor "status-offline", "rgba(150, 150, 150, 0.50)"
+RocketChat.theme.addPublicColor "status-online", "#35AC19"
 RocketChat.theme.addPublicColor "unread-notification-color", "#1dce73"
-


don't do initials if avatar is small

don't do initials if avatar is small


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -41,12 +41,12 @@
     parsedColor = null

     if arguments.length == 2
-      if arguments[0].length == 3
-        parsedColor = [parseInt(color[0]), parseInt(color[1]), parseInt(color[2]), parseFloat(color[1])]
-    else if arguments.length > 2
+      if color.length == 3
+        parsedColor = [parseInt(color[0]), parseInt(color[1]), parseInt(color[2]), parseFloat(arguments[1])]
+    if arguments.length > 2
       alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [parseInt(arguments[0]), parseInt(arguments[1]), parseInt(arguments[2]), parseFloat(alpha)]
-    else if arguments.length > 2 && Object.prototype.toString.call(color) == '[object Array]'
+    else if color.length > 2 && Object.prototype.toString.call(color) == '[object Array]'
       alpha = if color[3]? then color[3] else 1
       parsedColor = [
         parseInt(color[0]),


feat(properties): add ability to lazily define JSON resource classes (and theoretically also newly API resource classes, though they're not implemented in this commit)

--- coffee/classes/081_base_resource.tail.coffee
+++ coffee/classes/081_base_resource.tail.coffee
@@ -45,22 +45,25 @@
 	#	*	'=@'                          - both = and @
 	#	*	function extends BaseResource - the property will be serialized into and deserialized from JSON as an instance of the
 	#		                                given resource
+	#	*	'=@BaseResourceOnAngoolar'    - a function extending BaseResource with that name will be found on angoolar and used
+	#                                       to serialize the corresponding JSON and/or API property.
 	$_properties: {}

 	# This allows us to have properties of the resource be instances of other resources. The following is an example using
-	# all possible parameters on each resource property. The key of each property of $_propertyToResourceMapping is the property
+	# all possible parameters on each resource property. The key of each property of $_propertyToResourceJsonMapping is the property
 	# of this resource that will be populated with an instance or an array of instances of the resource class given by each
 	# each corresponding resourceClass, which must be a class extending BaseResource.
 	#
 	# (If the jsonExpression refers to a property that's an array, it will loop through each object in the array and add an
 	# instance of the given resourceClass for each.)
-	# $_propertyToResourceMapping:
+	# $_propertyToResourceJsonMapping:
 	# 	assets: # our property name
 	# 		assets: # the json property name
 	# 			angoolar.Asset # the BaseResource-extending class used to make an instance for each (if an array) object in the json property
 	#		weirdAssets:
 	#			angoolar.WeirdAsset
-	$_propertyToResourceMapping: {}
+	$_propertyToResourceJsonMapping: {}
+	$_propertyToResourceApiMapping : {}
 	# As an aside for future development, I would like to see this work with the $_propertyToApiMapping member to actually
 	# allow the propertyToApiMapping object contain all the property references, but this to determine whether those references
 	# are simple (or just what their JSON representations are) or class instances, given by this member. And maybe that would
@@ -75,15 +78,17 @@
 		unless @constructor::hasOwnProperty( '$_propertiesConfigured' ) and @constructor::$_propertiesConfigured
 			@constructor::$parse = angular.injector( [ 'ng' ] ).get "$parse"

-			@constructor::$_properties                = angoolar.prototypallyExtendPropertyObject @, '$_properties'
-			@constructor::$_propertyToJsonMapping     = angoolar.prototypallyExtendPropertyObject @, '$_propertyToJsonMapping'
-			@constructor::$_propertyToApiMapping      = angoolar.prototypallyExtendPropertyObject @, '$_propertyToApiMapping'
-			@constructor::$_propertyToResourceMapping = angoolar.prototypallyExtendPropertyObject @, '$_propertyToResourceMapping'
-			@constructor::$_allProperties             = _.union(
+			@constructor::$_properties                    = angoolar.prototypallyExtendPropertyObject @, '$_properties'
+			@constructor::$_propertyToJsonMapping         = angoolar.prototypallyExtendPropertyObject @, '$_propertyToJsonMapping'
+			@constructor::$_propertyToApiMapping          = angoolar.prototypallyExtendPropertyObject @, '$_propertyToApiMapping'
+			@constructor::$_propertyToResourceJsonMapping = angoolar.prototypallyExtendPropertyObject @, '$_propertyToResourceJsonMapping'
+			@constructor::$_propertyToResourceApiMapping  = angoolar.prototypallyExtendPropertyObject @, '$_propertyToResourceApiMapping'
+			@constructor::$_allProperties                 = _.union(
 				_.keys @constructor::$_properties
 				_.keys @constructor::$_propertyToJsonMapping
 				_.keys @constructor::$_propertyToApiMapping
-				_.keys @constructor::$_propertyToResourceMapping
+				_.keys @constructor::$_propertyToResourceJsonMapping
+				_.keys @constructor::$_propertyToResourceApiMapping
 			)

 			if @$_idProperty?.length and not @$_properties[ @$_idProperty ]?
@@ -94,17 +99,31 @@
 				apiProperty  = @$_makeApiProperty property

 				if angular.isString propertyUsage
-					inJson = -1 isnt propertyUsage.indexOf '='
-					inApi  = -1 isnt propertyUsage.indexOf '@'
-
-					@constructor::$_propertyToJsonMapping[ property ] = jsonProperty if inJson
-					@constructor::$_propertyToApiMapping[  property ] = apiProperty  if inApi
+					if /[@=]{1,2}/.test propertyUsage
+						inJson = -1 isnt propertyUsage.indexOf '='
+						inApi  = -1 isnt propertyUsage.indexOf '@'
+
+						@constructor::$_propertyToJsonMapping[ property ] = jsonProperty if inJson
+						@constructor::$_propertyToApiMapping[  property ] = apiProperty  if inApi
+					else
+						usageMatches = propertyUsage.match /([@=]{0,2})([^@=]+)/
+						usage             = usageMatches[ 1 ]
+						resourceClassName = usageMatches[ 2 ]
+
+						usage = '=' unless usage?.length
+
+						inJson = -1 isnt propertyUsage.indexOf '='
+						inApi  = -1 isnt propertyUsage.indexOf '@'
+
+						if angular.isFunction angoolar[ resourceClassName ]
+							@constructor::$_propertyToResourceJsonMapping[ property ] = angoolar[ resourceClassName ] if inJson
+							@constructor::$_propertyToResourceApiMapping[  property ] = angoolar[ resourceClassName ] if inApi

 				else if angular.isFunction propertyUsage
 					resourceMapping = {}
 					resourceMapping[ jsonProperty ] = propertyUsage

-					@constructor::$_propertyToResourceMapping[ property ] = resourceMapping
+					@constructor::$_propertyToResourceJsonMapping[ property ] = resourceMapping

 			@constructor::$_propertiesConfigured = yes

@@ -131,7 +150,7 @@
 			jsonExpressionSetter json, propertyExpressionGetter @

 		# Assign all the aggregated resources
-		angular.forEach @$_propertyToResourceMapping, ( aggregateResourceDefinition, propertyExpression ) =>
+		angular.forEach @$_propertyToResourceJsonMapping, ( aggregateResourceDefinition, propertyExpression ) =>
 			propertyExpressionGetter = @$parse propertyExpression

 			angular.forEach aggregateResourceDefinition, ( resourceClass, jsonExpression ) =>
@@ -184,7 +203,7 @@
 				propertyExpressionSetter @, jsonValue

 			# Assign all the aggregated resources
-			angular.forEach @$_propertyToResourceMapping, ( aggregateResourceDefinition, propertyExpression ) =>
+			angular.forEach @$_propertyToResourceJsonMapping, ( aggregateResourceDefinition, propertyExpression ) =>
 				# We will first assume we're not going to be attributing these aggregated resources to the given propertyExpression evaluation as an array unless (1), any
 				# of the aggregated resources (given by its corresponding jsonExpression) is an array, or (2), we have multiple aggregated resources
 				# that each correspond to this same propertyExpression evaluation.


delete child objects upon parent deletion hooks

--- server/main.coffee
+++ server/main.coffee
@@ -71,6 +71,7 @@

 userCanEditTblContentCheck = (userId, doc, fieldNames, modifier, options) ->
     return userCanEditTblContent(doc.tbl_id, userId)
+
 Reference.before.update (userId, doc, fieldNames, modifier, options) -> return true

 MechanisticEvidence.before.update userCanEditTblContentCheck
@@ -79,17 +80,29 @@

 EpiResult.before.update userCanEditTblContentCheck

-
 # Remove hooks
+Tables.before.remove (userId, doc) ->
+    # also remove all results
+    EpiResult.remove({tbl_id: doc._id})
+    EpiDescriptive.remove({tbl_id: doc._id})
+    MechanisticEvidence.remove({tbl_id: doc._id})

 userCanRemoveTblContentCheck = (userId, doc) ->
     return userCanEditTblContent(doc.tbl_id, userId)

 Reference.before.remove (userId, doc) -> return true

-MechanisticEvidence.before.remove userCanRemoveTblContentCheck
+MechanisticEvidence.before.remove (userId, doc) ->
+    # also remove all child documents
+    if userCanRemoveTblContentCheck(userId, doc) is false then return false
+    MechanisticEvidence.remove({parent: doc._id})
+    return true

-EpiDescriptive.before.remove userCanRemoveTblContentCheck
+EpiDescriptive.before.remove (userId, doc) ->
+    # also remove all results
+    if userCanRemoveTblContentCheck(userId, doc) is false then return false
+    EpiResult.remove({parent_id: doc._id})
+    return true

 EpiResult.before.remove userCanRemoveTblContentCheck


download photos with lowel api

download photos with lowel api

--- server/controllers/photo.coffee
+++ server/controllers/photo.coffee
@@ -1,10 +1,12 @@
-Photo = require '../models/photo'
 async = require 'async'
 fs = require 'fs'
 qs = require 'qs'
 im = require 'imagemagick'
 multiparty = require 'multiparty'
+
+Photo = require '../models/photo'
 sharing = require './sharing'
+downloader = require '../helpers/downloader'

 app = null
 module.exports.setApp = (ref) -> app = ref
@@ -133,9 +135,21 @@
         onError = (err) -> next err if err

         if req.photo._attachments?[which]
-            stream = req.photo.getFile which, onError
+            path = "/data/#{req.photo.id}/attachments/#{which}"
+            downloader.download path, (stream) ->
+                if stream.statusCode is 200
+                    stream.pipe res
+                else
+                    return res.sendfile './server/img/error.gif'
+
         else if req.photo.binary?[which]
-            stream = req.photo.getBinary which, onError
+            path = "/data/#{req.photo.id}/binaries/#{which}"
+            downloader.download path, (stream) ->
+                if stream.statusCode is 200
+                    stream.pipe res
+                else
+                    return res.sendfile './server/img/error.gif'
+
         else
             return res.sendfile './server/img/error.gif'

@@ -147,7 +161,6 @@
         #     if couchres.headers.etag is req.headers['if-none-match']
         #         myres.send 304

-        stream.pipe res

 # Get mid-size version of the picture


IT WORKS NOW I GUESS

IT WORKS NOW I GUESS


uncomment the code let hubot can communicate specific person


react browser prefixes don't seem to be working

Former-commit-id: 905dd37f9247bbdad55f8ca6ad681618bb9103e8 [formerly 8243572a239297045955191916b5e92ad4f7dd39] [formerly eca7f1707124a03dad390c037c9cf3b7d3625a1c [formerly f60c278db78d63821163a67a2db72209ec2d035d]] [formerly 13280825ef2cac8be342a5b4609903c346c52200 [formerly 812835e613550c4c5e480834bc140ceaff82319c] [formerly 7ca1f631730b429db65d212ee8634f130f0426be [formerly 7ca1f631730b429db65d212ee8634f130f0426be [formerly 7093b51440c835a389d8e9bfb0913019d00db082]]]]
Former-commit-id: 30d7280daa87e88f6cb46a8287689296eb35c8b8 [formerly 4ea2d6502e87eb662bed6c3c1865cc161a24b558] [formerly d34c838d495d4ad003355e9b3e8d55b871a946fd [formerly 7fd4916a6a71f7b9d0610a29c466485e66aa938e]]
Former-commit-id: 819cc1cc3934106484763b40d2ef2893944986f0 [formerly 6dafa4257293ca0a52994433dfef7b0589a24c5c]
Former-commit-id: 3b7cf4631e8693d7759fed6df091730734822c42
Former-commit-id: 35b3da2105dad66a354c869c00377f4a890e4cac

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -411,10 +411,10 @@
 css.crossbrowserify = (props, as_str = false) ->
   if props.transform
     _.extend props,
-      WebkitTransform : props.transform
-      msTransform : props.transform
-      MozTransform : props.transform
-      OTransform : props.transform
+      '-webkit-transform' : props.transform
+      '-ms-transform' : props.transform
+      '-moz-transform' : props.transform
+      '-o-transform' : props.transform

   if props.userSelect
     _.extend props,


start implementing data stack

start implementing data stack


Precompile main stylesheet from each theme

--- tasks/prebuild-less-task.coffee
+++ tasks/prebuild-less-task.coffee
@@ -19,10 +19,16 @@
         path.resolve('static')
         path.resolve('vendor')
       ]
+      themeMains = []
       for theme in configuration
         # TODO Use AtomPackage class once it runs outside of an Atom context
-        themePath = path.resolve('node_modules', theme, 'stylesheets')
-        importPaths.unshift(themePath) if grunt.file.isDir(themePath)
+        themePath = path.resolve('node_modules', theme)
+        stylesheetsDir = path.join(themePath, 'stylesheets')
+        {main} = grunt.file.readJSON(path.join(themePath, 'package.json'))
+        main ?= 'index.less'
+        mainPath = path.join(themePath, main)
+        themeMains.push(mainPath) if grunt.file.isFile(mainPath)
+        importPaths.unshift(stylesheetsDir) if grunt.file.isDir(stylesheetsDir)

       grunt.log.writeln("Building LESS cache for #{configuration.join(', ').yellow}")
       lessCache = new LessCache
@@ -33,3 +39,7 @@
       for file in @filesSrc
         grunt.log.writeln("File #{file.cyan} created in cache.")
         lessCache.readFileSync(file)
+
+      for file in themeMains
+        grunt.log.writeln("File #{file.cyan} created in cache.")
+        lessCache.readFileSync(file)


Avoid implicitly returning from a directive controller constructor function.

--- services/web/public/coffee/directives/selectAll.coffee
+++ services/web/public/coffee/directives/selectAll.coffee
@@ -13,6 +13,7 @@

 				@clearSelectAllState = () ->
 					$scope.$broadcast "select-all:clear"
+				return
 			]
 			link: (scope, element, attrs) ->

@@ -63,6 +64,8 @@
 					ignoreChanges = true
 					scope.$apply () ->
 						scope.ngModel = !scope.ngModel
+						if !scope.ngModel
+							selectAllListController.clearSelectAllState()
 					ignoreChanges = false
 		}


Adds Hurricane Electric and SANS ISC for IP reputation. Fixes spelling of Robtex.

--- src/scripts/reputation-links.coffee
+++ src/scripts/reputation-links.coffee
@@ -20,9 +20,11 @@
     target_ip = msg.match[1].toLowerCase()

     reputation_links = """#{target_ip} IP Reputation:
-    - Robtext:    https://ip.robtex.com/#{target_ip}.html
+    - Robtex:     https://ip.robtex.com/#{target_ip}.html
     - CentralOps: http://centralops.net/co/DomainDossier.aspx?addr=#{target_ip}&dom_dns=1&dom_whois=1&net_whois=1
     - IPVoid:     http://www.ipvoid.com/scan/#{target_ip}/
+    - HE:         http://bgp.he.net/ip/#{target_ip}#_whois
+    - SANS ISC:   https://isc.sans.edu/api/ip/#{target_ip}
     """

     msg.send reputation_links


Refactored and cleaned up coffee compile tests

--- spec/coffee-compile-spec.coffee
+++ spec/coffee-compile-spec.coffee
@@ -1,11 +1,10 @@
 CoffeeCompileView = require '../lib/coffee-compile-view'
 {WorkspaceView} = require 'atom'

-describe "CoffeeCompile compile on save without preview", ->
+describe "CoffeeCompile", ->
   beforeEach ->
     atom.workspaceView = new WorkspaceView
     atom.workspace     = atom.workspaceView.model
-    spyOn CoffeeCompileView, "saveCompiled"

     waitsForPromise "language-coffee-script to activate", ->
       atom.packages.activatePackage('language-coffee-script')
@@ -17,98 +16,78 @@
       'text.plain.null-grammar'
     ])

-  it "should call savedCompiled", ->
-    atom.config.set("coffee-compile.compileOnSaveWithoutPreview", true)
-
-    waitsForPromise "coffee-compile package to activate", ->
-      atom.packages.activatePackage('coffee-compile')
-
     atom.workspaceView.attachToDom()

-    waitsForPromise "fixture file to open", ->
-      atom.workspace.open "coffee-compile-fixtures.coffee"
-
-    runs ->
-      atom.workspaceView.trigger "core:save"
-      expect(CoffeeCompileView.saveCompiled).toHaveBeenCalled()
-
-  it "should not call saveCompiled when option is disabled", ->
-    atom.config.set("coffee-compile.compileOnSaveWithoutPreview", false)
-
-    waitsForPromise "coffee-compile package to activate", ->
-      atom.packages.activatePackage('coffee-compile')
-
-    atom.workspaceView.attachToDom()
-
-    waitsForPromise "fixture file to open", ->
-      atom.workspace.open "coffee-compile-fixtures.coffee"
-
-    runs ->
-      atom.workspaceView.trigger "core:save"
-      expect(CoffeeCompileView.saveCompiled).not.toHaveBeenCalled()
-
-describe "CoffeeCompile compile on save", ->
-  beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    atom.workspace     = atom.workspaceView.model
-
-    spyOn CoffeeCompileView, "saveCompiled"
     spyOn(CoffeeCompileView.prototype, "renderCompiled").andCallThrough()

-    waitsForPromise "coffee-compile package to activate", ->
-      atom.packages.activatePackage('coffee-compile')
+  describe "compile on save without preview", ->
+    beforeEach ->
+      spyOn CoffeeCompileView, "saveCompiled"

-    atom.workspaceView.attachToDom()
+    it "should call savedCompiled", ->
+      atom.config.set("coffee-compile.compileOnSaveWithoutPreview", true)

-    waitsForPromise "fixture file to open", ->
-      atom.workspace.open "coffee-compile-fixtures.coffee"
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

-  it "should call savedCompiled", ->
-    atom.config.set "coffee-compile.compileOnSave", true
+      waitsForPromise "fixture file to open", ->
+        atom.workspace.open "coffee-compile-fixtures.coffee"

-    runs ->
-      atom.workspaceView.getActiveView().trigger "coffee-compile:compile"
+      runs ->
+        atom.workspaceView.trigger "core:save"
+        expect(CoffeeCompileView.saveCompiled).toHaveBeenCalled()

-    waitsFor ->
-      CoffeeCompileView::renderCompiled.callCount > 0
+    it "should not call saveCompiled when option is disabled", ->
+      atom.config.set("coffee-compile.compileOnSaveWithoutPreview", false)

-    runs ->
-      expect(CoffeeCompileView.saveCompiled).toHaveBeenCalled()
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

-  it "should not call savedCompiled", ->
-    atom.config.set "coffee-compile.compileOnSave", false
+      waitsForPromise "fixture file to open", ->
+        atom.workspace.open "coffee-compile-fixtures.coffee"

-    runs ->
-      atom.workspaceView.getActiveView().trigger "coffee-compile:compile"
+      runs ->
+        atom.workspaceView.trigger "core:save"
+        expect(CoffeeCompileView.saveCompiled).not.toHaveBeenCalled()

-    waitsFor ->
-      CoffeeCompileView::renderCompiled.callCount > 0
+  describe "compile on save", ->
+    beforeEach ->
+      spyOn CoffeeCompileView, "saveCompiled"

-    runs ->
-      expect(CoffeeCompileView.saveCompiled).not.toHaveBeenCalled()
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

-describe "CoffeeCompile", ->
-  beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    atom.workspace     = atom.workspaceView.model
-    spyOn(CoffeeCompileView.prototype, "renderCompiled").andCallThrough()
+      waitsForPromise "fixture file to open", ->
+        atom.workspace.open "coffee-compile-fixtures.coffee"

-    waitsForPromise "coffee-compile package to activate", ->
-      atom.packages.activatePackage('coffee-compile')
+    it "should call savedCompiled", ->
+      atom.config.set "coffee-compile.compileOnSave", true

-    waitsForPromise "language-coffee-script to activate", ->
-      atom.packages.activatePackage('language-coffee-script')
+      runs ->
+        atom.workspaceView.getActiveView().trigger "coffee-compile:compile"

-    atom.config.set('coffee-compile.grammars', [
-      'source.coffee'
-      'source.litcoffee'
-      'text.plain'
-      'text.plain.null-grammar'
-    ])
+      waitsFor ->
+        CoffeeCompileView::renderCompiled.callCount > 0

-  describe "should open a new pane", ->
+      runs ->
+        expect(CoffeeCompileView.saveCompiled).toHaveBeenCalled()
+
+    it "should not call savedCompiled", ->
+      atom.config.set "coffee-compile.compileOnSave", false
+
+      runs ->
+        atom.workspaceView.getActiveView().trigger "coffee-compile:compile"
+
+      waitsFor ->
+        CoffeeCompileView::renderCompiled.callCount > 0
+
+      runs ->
+        expect(CoffeeCompileView.saveCompiled).not.toHaveBeenCalled()
+
+  describe "open a new pane", ->
     beforeEach ->
-      atom.workspaceView.attachToDom()
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

       waitsForPromise "fixture file to open", ->
         atom.workspace.open "coffee-compile-fixtures.coffee"
@@ -150,7 +129,9 @@
   describe "focus editor after compile", ->
     beforeEach ->
       atom.config.set "coffee-compile.focusEditorAfterCompile", true
-      atom.workspaceView.attachToDom()
+
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

       waitsForPromise ->
         atom.workspace.open "test.coffee"
@@ -169,7 +150,9 @@
   describe "when the editor's grammar is not coffeescript", ->
     it "should not preview compiled js", ->
       atom.config.set "coffee-compile.grammars", []
-      atom.workspaceView.attachToDom()
+
+      waitsForPromise "coffee-compile package to activate", ->
+        atom.packages.activatePackage('coffee-compile')

       waitsForPromise ->
         atom.workspace.open "coffee-compile-fixtures.coffee"


Use new decoration updated event method

Use new decoration updated event method

--- spec/display-buffer-spec.coffee
+++ spec/display-buffer-spec.coffee
@@ -1074,7 +1074,7 @@

     describe "when a decoration is updated via Decoration::update()", ->
       it "emits an 'updated' event containing the new and old params", ->
-        decoration.on 'updated', updatedSpy = jasmine.createSpy()
+        decoration.onDidUpdate updatedSpy = jasmine.createSpy()
         decoration.update type: 'gutter', class: 'two'

         {oldParams, newParams} = updatedSpy.mostRecentCall.args[0]


Gracefully handle malformed link annotations.

--- entry/TextBlock.cjsx
+++ entry/TextBlock.cjsx
@@ -19,7 +19,11 @@
         text = new NOAT(@props.block.content)
         @props.block.annotations?.forEach (anno) ->
             attrs = {}
-            attrs.href = anno.url if anno.type is 'link'
+            if anno.type is 'link'
+                # Avoid trying to render links without a url specified.
+                unless anno.url
+                    return
+                attrs.href = anno.url
             text.add(TAG_MAP[anno.type], anno.start, anno.end, attrs)

         # Choose the appropriate tag for the given block's role.


Turn off default word wrap in Atom.

Turn off default word wrap in Atom.

--- env/atom.config.cson
+++ env/atom.config.cson
@@ -44,7 +44,6 @@
 		scrollPastEnd: true
 		showIndentGuide: true
 		showInvisibles: true
-		softWrap: true
 		softWrapAtPreferredLineLength: true
 		tabLength: 4
 	"event-watch":


activity: define `ActivityInputView::getPosition` method

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -76,6 +76,17 @@
     return  if @getTokens().length >= TOKEN_LIMIT
     super

+  getPosition: ->
+    {startContainer, startOffset} = KD.utils.getSelectionRange()
+    {parentNode} = startContainer
+
+    position = 0
+    for node in @getEditableElement().childNodes
+      break  if node is startContainer or node is parentNode
+      position += node.textContent.length
+
+    return position + startOffset
+
   focus: ->
     return  if @focused
     super


resurrection.activity: add created tag's id instead of `JTag` instance

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -57,7 +57,7 @@
         tagCreateJobs = suggestedTags.map (data) ->
           ->
             JTag.create title: data.$suggest, (err, tag) ->
-              tags.push tag
+              tags.push id: tag.getId()
               createdTags[tag.title] = tag
               tagCreateJobs.fin()


fix pricing test

fix pricing test

--- test/integration/pricing.coffee
+++ test/integration/pricing.coffee
@@ -27,8 +27,7 @@

     context 'when I click the "explorer" plan', ->
       before (done) ->
-        browser.elementByCssIfExists '.plan.explorer', (err, free) ->
-          free.click done
+        wd40.click '.plan.explorer a', done

       it 'takes me to the sign up page', (done) ->
         wd40.trueURL (err, url) ->


Don't calculate response distribution if we haven't seen any responses.

This avoids silly divide-by-zero errors.

--- dashboard/assets/javascripts/views.js.coffee
+++ dashboard/assets/javascripts/views.js.coffee
@@ -44,6 +44,8 @@
     total = @get 'item.totalResponses'
     counts = @get 'item.responseCountsByBucket'

+    return if total == 0
+
     for pair in counts
       [bucket, count] = pair
       width = (100 * (count / total)) + '%'


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -18,7 +18,7 @@
   # his position back inside the viewport.
   player.update()

-  # x, y
+  # x, y position is
   player.I.x
   # => 2.5
 </pre></code>


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -14,6 +14,7 @@
   player.I.x = -400

   # update the player so ClampBounds can set
+
   player.update()
 </pre></code>


Workspace: Load default workspace if none found

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -128,11 +128,15 @@

     machineLabel    = machine.slug or machine.label
     workspaceSlug   = 'my-workspace'
+    username        = machine.data.credential

     if latestWorkspace and latestWorkspace.machineLabel is machineLabel
       for ws in KD.userWorkspaces when ws.slug is latestWorkspace.workspaceSlug
         {workspaceSlug} = latestWorkspace

+    if workspaceSlug is 'my-workspace'
+      loadWorkspace {machineLabel, workspaceSlug, username}
+
     KD.utils.defer ->
       KD.getSingleton('router').handleRoute "/IDE/#{machineLabel}/#{workspaceSlug}"

@@ -221,4 +225,4 @@

         if workspace
         then loadWorkspace params, workspace
-        else routeToLatestWorkspace()
+        else routeToLatestWorkspace {params}


missed a branch

missed a branch

--- js/app.coffee
+++ js/app.coffee
@@ -448,7 +448,7 @@

         VM.selected sel

-        VM.loadProject cx.params.username, cx.params.project, cx.params.branch
+        VM.loadProject cx.params.username, cx.params.project, branch

     @get '^/gh/:username/:project/:build_num',
       (cx) ->


Remove unneeded mkdir call

Remove unneeded mkdir call

--- src/coffee-cache.coffee
+++ src/coffee-cache.coffee
@@ -4,7 +4,6 @@
 CoffeeScript = require 'coffee-script'
 CSON = require 'season'
 fs = require 'fs-plus'
-mkdir = require('mkdirp').sync

 cacheDir = path.join(fs.getHomeDirectory(), 'compile-cache')
 coffeeCacheDir = path.join(cacheDir, 'coffee')
@@ -25,7 +24,6 @@
   if btoa? and JSON? and unescape? and encodeURIComponent?
     js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{filePath}"
   try
-    mkdir(path.dirname(cachePath))
     fs.writeFileSync(cachePath, js)
   js


proper sort usage, still errors

proper sort usage, still errors

--- src/coffee/ticks.coffee
+++ src/coffee/ticks.coffee
@@ -357,11 +357,17 @@
   return output_arr

+num_comp = (x, y) ->
+  if (x > y)
+    return 1
+  if (y < x)
+    return -1
+  return 0
 window.argsort = (arr) ->
   sorted_arr = []
   for x in arr
     sorted_arr.push(x)
-  sorted_arr.sort()
+  sorted_arr.sort(num_comp)
   #debugger
   ret_arr = []
   for y, i in arr
@@ -372,6 +378,9 @@
     #ret_arr.push(sorted_arr.indexOf(y))
   ret_arr

+#window.argsort = (arr) ->
+#  arr.map((x) -> _.sortedIndex(arr, x))
+  #_.sortedIndex

 window.auto_interval = (data_low, data_high) ->
     """ Calculates the tick interval for a range.


Update config

Update config

--- etc/atom/config.cson
+++ etc/atom/config.cson
@@ -68,6 +68,7 @@
       "exception-reporting"
     ]
     projectHome: "/Users/stillers/workspace"
+    telemetryConsent: "no"
     themes: [
       "atom-dark-ui"
       "solarized-dark-syntax"


appropriate note added

appropriate note added

--- app.coffee
+++ app.coffee
@@ -132,6 +132,8 @@
   posts: './_posts'
   postsPerPage: 3

+# using my own marked instead of one bundled with poet, because
+# standard doesn't support easy class injection to a <pre> element.
 .addTemplate
   ext: ['markdown', 'md']
   fn: (options) ->


dynamic port for heroku

dynamic port for heroku

--- app.coffee
+++ app.coffee
@@ -11,4 +11,4 @@
     '</body>'
   ].join('')
   res.end html
-.listen process.env.PORT or 8080, '127.0.0.1'
+.listen process.env.PORT or 8080


let extend has several entries

let extend has several entries

--- vendor/assets/javascripts/gmaps/google/objects/bound.coffee
+++ vendor/assets/javascripts/gmaps/google/objects/bound.coffee
@@ -9,5 +9,9 @@
     _.each collection, (object)=>
       object.updateBounds(@)

+  # position can be:
+  # - [ lat, lng]
+  # - { lat: , lng: }
+  # - a google.maps.LatLng
   extend: (value)->
-    @getServiceObject().extend value
+    @getServiceObject().extend @primitives().latLngFromPosition(value)


fix: reserve some files while cleaning up

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -25,7 +25,8 @@
     bower:
       toCopy: [ 'bower_components/**' ]
       copyDest: 'dist/bower_components/'
-      copied: [ 'dist/bower_components' ]
+      copied: [ 'dist/bower_components/**/*' ]
+      reservedFiles: [ '!dist/bower_components/webcomponentsjs/webcomponents-lite.min.js' ]
   app:
     internal:
       index:
@@ -195,7 +196,7 @@
 gulp.task 'cleanup-after-handling-internal-elements', ->
   # clean up individual elements, bower components
   gulp
-    .src [].concat( paths.elements.internal.folders.compiled, paths.dependencies.bower.copied ), { read: false }
+    .src [].concat( paths.elements.internal.folders.compiled, paths.dependencies.bower.copied, paths.dependencies.bower.reservedFiles ), { read: false }
     .pipe $.debug()
     .pipe $.rimraf()


Chrome extension: Add tests

Chrome extension: Add tests

--- test/chromeExtension/storyReducer.coffee
+++ test/chromeExtension/storyReducer.coffee
@@ -1,7 +1,8 @@
-timm = require 'timm'
-{expect} = require './imports'
-reducer = require '../../lib/chromeExtension/reducers/storyReducer'
-_ = require 'lodash'
+_         = require 'lodash'
+timm      = require 'timm'
+{expect}  = require './imports'
+reducer   = require '../../lib/chromeExtension/reducers/storyReducer'
+treeLines = require '../../lib/gral/treeLines'

 #-------------------------------------------------
 # ## Helpers
@@ -88,13 +89,15 @@
     it 'the pathStr of open stories should be updated if necessary', ->
       openStoryRecord0 = _openStory {title: 'story0'}
       openStoryRecord1 = _openStory {title: 'story1', parents: [openStoryRecord0.storyId]}
+      openStoryRecord2 = _openStory {title: 'story2', parents: [openStoryRecord1.storyId]}
       state = reducer state, _recordsReceived [
         _log {msg: "msg0"}
         _log {msg: "msg1"}
         openStoryRecord0
         openStoryRecord1
+        openStoryRecord2
       ]
-      expect(state.mainStory.records[0].numRecords).to.equal 4
+      expect(state.mainStory.records[0].numRecords).to.equal 5
       state = reducer state, _forget({maxRecords: 1})
       topStory = state.mainStory.records[0]
       expect(topStory.records.length).to.equal 1
@@ -104,6 +107,100 @@
       expect(topStory.records[0].records[0].action).to.equal 'CREATED'
       expect(topStory.records[0].records[1].title).to.equal 'story1'
       expect(topStory.records[0].records[1].pathStr).to.equal 'records/0/records/0/records/1'
-      expect(topStory.numRecords).to.equal 2
+      expect(topStory.records[0].records[1].records.length).to.equal 2
+      expect(topStory.records[0].records[1].records[0].action).to.equal 'CREATED'
+      expect(topStory.records[0].records[1].records[1].title).to.equal 'story2'
+      expect(topStory.records[0].records[1].records[1].pathStr).to.equal 'records/0/records/0/records/1/records/1'
+      expect(topStory.numRecords).to.equal 3
       expect(state.openStories[openStoryRecord0.storyId]).to.equal 'records/0/records/0'
       expect(state.openStories[openStoryRecord1.storyId]).to.equal 'records/0/records/0/records/1'
+      expect(state.openStories[openStoryRecord2.storyId]).to.equal 'records/0/records/0/records/1/records/1'
+
+  describe 'multiple actions', ->
+    beforeEach ->
+      openStoryRecord0 = _openStory {title: 'story0'}
+      openStoryRecord1 = _openStory {title: 'story1', parents: [openStoryRecord0.storyId]}
+      attachment = {a: 1, b: 2, c: 3}
+      logRecord = _log
+        id: 'logWithAttachment'
+        storyId: openStoryRecord1.storyId
+        msg: "log with attachment"
+        obj: treeLines attachment
+        objExpanded: false
+      state = reducer state, _recordsReceived [
+        openStoryRecord0
+        openStoryRecord1
+        logRecord
+      ]
+      expect(state.mainStory.records[0].records[0].fExpanded).to.be.true
+      expect(state.mainStory.records[0].records[0].records[1].fExpanded).to.be.true
+
+    it 'should allow expanding/collapsing all stories', ->
+      state = reducer state, {type: 'COLLAPSE_ALL_STORIES'}
+      expect(state.mainStory.records[0].records[0].fExpanded).to.be.false
+      expect(state.mainStory.records[0].records[0].records[1].fExpanded).to.be.false
+      state = reducer state, {type: 'EXPAND_ALL_STORIES'}
+      expect(state.mainStory.records[0].records[0].fExpanded).to.be.true
+      expect(state.mainStory.records[0].records[0].records[1].fExpanded).to.be.true
+
+    it 'should allow expanding/collapsing an individual story', ->
+      state = reducer state, {type: 'TOGGLE_EXPANDED', pathStr: 'records/0/records/0'}
+      expect(state.mainStory.records[0].records[0].fExpanded).to.be.false
+      state = reducer state, {type: 'TOGGLE_EXPANDED', pathStr: 'records/0/records/0'}
+      expect(state.mainStory.records[0].records[0].fExpanded).to.be.true
+      state2 = reducer state, {type: 'TOGGLE_EXPANDED'}
+      expect(state2).to.equal state
+
+    it 'should allow showing stories in a flat/hierarchical way', ->
+      state = reducer state, {type: 'TOGGLE_HIERARCHICAL', pathStr: 'records/0/records/0'}
+      expect(state.mainStory.records[0].records[0].fHierarchical).to.be.false
+      state = reducer state, {type: 'TOGGLE_HIERARCHICAL', pathStr: 'records/0/records/0'}
+      expect(state.mainStory.records[0].records[0].fHierarchical).to.be.true
+      state2 = reducer state, {type: 'TOGGLE_HIERARCHICAL'}
+      expect(state2).to.equal state
+
+    it 'should store quick-find info', ->
+      state = reducer state, {type: 'QUICK_FIND', txt: 'hello'}
+      expect(state.quickFind).to.equal '(hello)'
+      state = reducer state, {type: 'QUICK_FIND', txt: ''}
+      expect(state.quickFind).to.equal ''
+      state = reducer state, {type: 'QUICK_FIND', txt: '.+*'}
+      expect(state.quickFind).to.equal '(\\.\\+\\*)'
+
+    it 'should clear the state when receiving a CX_CONNECTED action', ->
+      state = reducer state, {type: 'CX_CONNECTED'}
+      expect(state.mainStory.records[0].records).to.have.length 0
+      expect(state.mainStory.records[1].records).to.have.length 0
+
+    it 'should clear the state when receiving a CLEAR_LOGS action', ->
+      state = reducer state, {type: 'CLEAR_LOGS'}
+      expect(state.mainStory.records[0].records).to.have.length 0
+      expect(state.mainStory.records[1].records).to.have.length 0
+
+    describe 'expanding attachments', ->
+      it 'in a hierarchical story', ->
+        expect(state.mainStory.records[0].records[0].records[1].records[1].msg).to.contain 'with attachment'
+        expect(state.mainStory.records[0].records[0].records[1].records[1].objExpanded).to.be.false
+        pathStr = "records/0/records/0/records/1"
+        state = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr, recordId: 'logWithAttachment'}
+        expect(state.mainStory.records[0].records[0].records[1].records[1].objExpanded).to.be.true
+        state = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr, recordId: 'logWithAttachment'}
+        expect(state.mainStory.records[0].records[0].records[1].records[1].objExpanded).to.be.false
+
+      it 'in a flat story', ->
+        pathStr = "records/0"
+        state = reducer state, {type: 'TOGGLE_HIERARCHICAL', pathStr}
+        expect(state.mainStory.records[0].fHierarchical).to.be.false
+        state = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr, recordId: 'logWithAttachment'}
+        expect(state.mainStory.records[0].records[0].records[1].records[1].objExpanded).to.be.true
+        state = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr, recordId: 'logWithAttachment'}
+        expect(state.mainStory.records[0].records[0].records[1].records[1].objExpanded).to.be.false
+
+      it 'with incorrect arguments', ->
+        pathStr = "records/0"
+        state2 = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr, recordId: 'xxx'}
+        expect(state2).to.equal state
+        state2 = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr: null, recordId: 'xxx'}
+        expect(state2).to.equal state
+        state2 = reducer state, {type: 'TOGGLE_ATTACHMENT', pathStr: 'unknown/path', recordId: 'xxx'}
+        expect(state2).to.equal state


changes error passing semantics

changes error passing semantics

--- drivers/javascript/src/ast.coffee
+++ drivers/javascript/src/ast.coffee
@@ -12,20 +12,30 @@
         return self

     run: (conn, cb) ->
-        useOutdated = undefined
-        if conn? and typeof(conn) is 'object' and not (conn instanceof Connection)
-            useOutdated = !!conn.useOutdated
-            noreply = !!conn.noreply
-            for own key of conn
-                unless key in ['connection', 'useOutdated', 'noreply']
-                    throw new RqlDriverError "First argument to `run` must be an open connection or { connection: <connection>, useOutdated: <bool>, noreply: <bool>}."
-            conn = conn.connection
-        unless conn instanceof Connection
-            throw new RqlDriverError "First argument to `run` must be an open connection or { connection: <connection>, useOutdated: <bool> }."
+        # Obviously, we can't pass this error to the callback so we have to just throw it
         unless typeof(cb) is 'function'
             throw new RqlDriverError "Second argument to `run` must be a callback to invoke "+
                                      "with either an error or the result of the query."
-        conn._start @, cb, useOutdated, noreply
+
+        try
+            useOutdated = undefined
+            if conn? and typeof(conn) is 'object' and not (conn instanceof Connection)
+                useOutdated = !!conn.useOutdated
+                noreply = !!conn.noreply
+                for own key of conn
+                    unless key in ['connection', 'useOutdated', 'noreply']
+                        throw new RqlDriverError "First argument to `run` must be an open connection or { connection: <connection>, useOutdated: <bool>, noreply: <bool>}."
+                conn = conn.connection
+            unless conn instanceof Connection
+                throw new RqlDriverError "First argument to `run` must be an open connection or { connection: <connection>, useOutdated: <bool> }."
+
+            conn._start @, cb, useOutdated, noreply
+        catch e
+            # It was decided that, if we can, we prefer to invoke the callback
+            # with any errors rather than throw them as normal exceptions.
+            # Thus we catch errors here and invoke the callback instead of
+            # letting the error bubble up.
+            cb(e)

     toString: -> RqlQueryPrinter::printQuery(@)


Cleaning up atom config.

Cleaning up atom config.

--- env/atom.config.cson
+++ env/atom.config.cson
@@ -1,223 +1,40 @@
 {
-  ".basic.html.text":
-    editor:
-      preferredLineLength: 120
-
-  ".c.source":
-    editor:
-      preferredLineLength: 80
-
-  ".c.source ++ *":
-    editor:
-      preferredLineLength: 80
-
-  ".coffee.source":
-    editor:
-      preferredLineLength: 120
-
-  ".css.less.source":
-    editor:
-      preferredLineLength: 120
-
-  ".css.scss.source":
-    editor:
-      preferredLineLength: 120
-
-  ".css.source":
-    editor:
-      preferredLineLength: 120
-
   ".erb.html.text":
     editor:
-      preferredLineLength: 120
       tabLength: 2
-
-  ".gfm.source":
-    editor:
-      preferredLineLength: 120
-
-  ".git-commit.text":
-    editor: {}
-
-  ".git-config.source":
-    editor: {}
-
-  ".git-rebase.text":
-    editor: {}
-
-  ".go.source":
-    editor:
-      preferredLineLength: 120
-
-  ".gohtml.html.text":
-    editor: {}
-
-  ".gotemplate.source":
-    editor: {}
-
-  ".html.jsp.text":
-    editor:
-      preferredLineLength: 120
-
-  ".html.mustache.text":
-    editor:
-      preferredLineLength: 120
-
-  ".html.php.text":
-    editor:
-      preferredLineLength: 120
-
-  ".html.ruby.text":
-    editor:
-      preferredLineLength: 120
-
-  ".hyperlink.text":
-    editor:
-      preferredLineLength: 120
-
-  ".java-properties.source":
-    editor:
-      preferredLineLength: 120
-
-  ".java.source":
-    editor:
-      preferredLineLength: 120
-
-  ".js.rails.source source.jquery.js":
-    editor:
-      preferredLineLength: 120
-
-  ".js.regexp.source":
-    editor:
-      preferredLineLength: 120
-
-  ".js.source":
-    editor:
-      preferredLineLength: 120
-
-  ".json.source":
-    editor:
-      preferredLineLength: 120
-
-  ".junit-test-report.text":
-    editor:
-      preferredLineLength: 120
-
-  ".litcoffee.source":
-    editor:
-      preferredLineLength: 120
-
-  ".makefile.source":
-    editor:
-      preferredLineLength: 120
-
-  ".mustache.source.sql":
-    editor:
-      preferredLineLength: 120
-
-  ".objc.source":
-    editor:
-      preferredLineLength: 120
-
-  ".objc.source ++ *":
-    editor:
-      preferredLineLength: 120
-
-  ".perl.source":
-    editor:
-      preferredLineLength: 120
-
-  ".plain.text":
-    editor:
-      preferredLineLength: 120
-
-  ".plist.source":
-    editor:
-      preferredLineLength: 120
-
-  ".plist.text.xml":
-    editor:
-      preferredLineLength: 120

   ".puppet.source":
     editor:
-      preferredLineLength: 120
       tabLength: 2
-
-  ".python.regexp.source":
-    editor:
-      preferredLineLength: 120
-
-  ".python.source":
-    editor:
-      preferredLineLength: 120
-
-  ".python.text.traceback":
-    editor:
-      preferredLineLength: 120
-
-  ".rails.rjs.ruby.source":
-    editor:
-      preferredLineLength: 120
-
-  ".rails.ruby.source":
-    editor:
-      preferredLineLength: 120

   ".ruby.source":
     editor:
-      preferredLineLength: 120
       tabLength: 2

-  ".ruby.source.sql":
-    editor:
-      preferredLineLength: 120
+  global:
+    "bracket-matcher":
+      autocompleteSmartQuotes: false

-  ".sass.source":
-    editor:
-      preferredLineLength: 120
-
-  ".shell-session.text":
-    editor:
-      preferredLineLength: 120
-
-  ".shell.source":
-    editor:
-      preferredLineLength: 120
-
-  ".source.sql":
-    editor:
-      preferredLineLength: 120
-
-  ".source.strings":
-    editor:
-      preferredLineLength: 120
-
-  ".source.toml":
-    editor:
-      preferredLineLength: 120
-
-  ".source.yaml":
-    editor:
-      preferredLineLength: 120
-
-  ".text.todo":
-    editor:
-      preferredLineLength: 120
-
-  ".text.xml":
-    editor:
-      preferredLineLength: 120
-
-  ".text.xml.xsl":
-    editor:
-      preferredLineLength: 120
-
-  global:
     core:
       disabledPackages: [
         "autocomplete"
         "exception-reporting"
+        "autoflow"
+        "autosave"
+        "background-tips"
+        "bookmarks"
+        "dev-live-reload"
+        "encoding-selector"
+        "image-view"
+        "language-mustache"
+        "language-toml"
+        "metrics"
+        "open-on-github"
+        "snippets"
+        "styleguide"
+        "update-package-dependencies"
+        "welcome"
+        "wrap-guide"
       ]
       themes: [
         "atom-soda-dark-ui"
@@ -234,7 +51,12 @@
     "git-diff":
       showIconsInEditorGutter: true

-    linter: {}
+    "highlight-selected":
+      onlyHighlightWholeWords: true
+
+    linter:
+      lintOnChange: false
+
     "linter-flake8":
       maxLineLength: 120

@@ -271,6 +93,17 @@
         "source.yaml"
       ]

+    "tree-view":
+      hideIgnoredNames: true
+      hideVcsIgnoredFiles: true
+
     welcome:
       showOnStartup: false
+
+  "omni-ruler":
+    columns: [
+      73
+      80
+      100
+    ]
 }


Fixing logging

Fixing logging

--- scripts/steam.coffee
+++ scripts/steam.coffee
@@ -2,7 +2,7 @@
 #   Steam Web API w/ Dota 2
 #
 # Dependencies:
-#   "moment": "^2.8.1"
+#   "moment": "^2.8.3"
 #   "msgpack": "^0.2.3"
 #   "ref": "^0.3.2"
 #
@@ -78,14 +78,13 @@
 GetSteamResult = (msg, endpoint, params = {}, handler, version = 1) ->
     params.key = STEAM_API_KEY

-    # Impl: Try-catch
     msg.http("#{STEAM_API_URL}/#{endpoint}/v#{version}/")
         .query(params)
         .get() (err, res, body) ->
             if err or res.statusCode isnt 200
                 err = "Bad request (invalid Steam web API key)" if res.statusCode is 400
-                msg.reply "An error occurred while attempting to process your request."
-                return robot.logger.error err
+                msg.reply "An error occurred while attempting to process your request. Please try again later."
+                return msg.robot.logger.error err
             handler JSON.parse(body)

 #
@@ -99,7 +98,6 @@
         callback object.response.steamid

 _GetCommunityID = (steamID) ->
-    # Impl: Try-catch
     buffer = new Buffer 8
     buffer.writeUInt64LE steamID, 0
     communityID = buffer.readUInt32LE 0
@@ -174,7 +172,6 @@
     if not STEAM_API_KEY?
         return robot.logger.debug "Missing STEAM_API_KEY in environment. Please set and try again."

-    # Impl: Try-catch
     fs.readFile DOTA_HEROES_DATA_PATH, (err, data) ->
         return robot.logger.error if err
         DOTA_HEROES = msgpack.unpack data
@@ -212,7 +209,7 @@
                 date = moment.unix(match.start_time).fromNow()
                 target = _GetPlayer communityID, match.players
                 hero = _GetHero(target.hero_id).LocalizedName
-                # W / L TBA
+                # TODO: W / L TBA
                 msg.send "Match ID: #{match.match_id} | Lobby: #{DOTA_LOBBIES[match.lobby_type]} | Hero: #{hero} | #{date}"

     # GET Player Dota 2 match details
@@ -238,7 +235,7 @@
                     return
                 faction = _GetFaction target.player_slot
                 msg.reply "#{faction} - #{_GetHero(target.hero_id).LocalizedName} (Lvl #{target.level}) | KDA: #{target.kills}/#{target.deaths}/#{target.assists} | LH: #{target.last_hits} | GPM: #{target.gold_per_min} | XPM: #{target.xp_per_min} | HD: #{target.hero_damage} | TD: #{target.tower_damage} | TGE: #{target.gold_per_min*duration}"
-                # Future: Team fight contribution? Overall contribution algo.
+                # Future (TODO): Team fight contribution? Overall contribution algo.

             if genericID?
                 if _IsSteamID genericID


[Auth] redirect users to new auth mobile pages if enabled

--- src/mobile/apps/auth/routes.coffee
+++ src/mobile/apps/auth/routes.coffee
@@ -23,14 +23,19 @@
   locals =
     redirectTo: redirectUrl(req)
     action: req.query.action
-
-  if locals.action
+
+  if sd.NEW_AUTH_MODAL
+    res.redirect "/login?#{qs.stringify(locals)}"
+  else if locals.action
     res.render 'call_to_action', locals
   else
     res.render 'login', locals

 module.exports.forgotPassword = (req, res) ->
-  res.render 'forgot_password'
+  if sd.NEW_AUTH_MODAL
+    res.redirect '/forgot'
+  else
+    res.render 'forgot_password'

 module.exports.submitForgotPassword = (req, res, next) ->
   new Backbone.Model().save null,
@@ -50,7 +55,10 @@
     action: req.query.action
     error: err?.body.error
     prefill: req.query.prefill
-  if locals.action
+
+  if sd.NEW_AUTH_MODAL
+    res.redirect "/signup?#{qs.stringify(locals)}"
+  else if locals.action
     res.render 'call_to_action', locals
   else if req.query.email
     res.render 'signup_email', locals


fix required prototype bug

fix required prototype bug

--- BackofficeBundle/Resources/public/coffee/addPrototype.coffee
+++ BackofficeBundle/Resources/public/coffee/addPrototype.coffee
@@ -12,7 +12,7 @@
   @collectionHolder = collectionHolder
   @newElementLabel = @collectionHolder.data("prototype-label-new")
   @:: = @collectionHolder.data("prototype").replace(/__name__label__/g, @newElementLabel)
-  @index = @collectionHolder.find("input").length + @collectionHolder.find("select").length
+  @index = @collectionHolder.children().length
   @limit = @collectionHolder.data("limit")
   @addButton = $(PO.formPrototypes.addButton.replace(/__label__/g, @collectionHolder.data("prototype-label-add")))
   @removeButton = $(PO.formPrototypes.removeButton.replace(/__label__/g, @collectionHolder.data("prototype-label-remove")))
@@ -27,8 +27,7 @@
   required = @required
   @collectionHolder.children().each (index) ->
     prototype = $(this)
-    if !required || index > 0
-      prototype = self.createRemoveButton($(this))
+    prototype = self.createRemoveButton($(this))
     if prototype.find(".alert-error").length is 0
       prototype.addClass("old").removeClass("new")
     else
@@ -40,10 +39,14 @@

 PO.formPrototype:: =
   toogleAddButton: ->
-    if @limit is `undefined` or @collectionHolder.find("input").length < @limit
+    if @limit is `undefined` or @index < @limit
       @createAddButton()
     else
       @removeAddButton()
+    if @index < 2 && @required
+      @collectionHolder.children().children("button.prototype-remove").hide()
+    else
+      @collectionHolder.children().children("button.prototype-remove").show()
     return

   createAddButton: ->
@@ -101,5 +104,6 @@

   removePrototype: (removeButton) ->
     removeButton.parent().remove()
+    @index--
     @toogleAddButton()
     return


handle changing avatar files or new user avatars

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -396,6 +396,7 @@

 cached_avatars = {}
 missing_images = {}
+loaded_images = {}

 window.getCanvasAvatar = (user) ->
   cached_avatars[user.key or user] or cached_avatars.default
@@ -464,6 +465,7 @@

   load: ->
     users = fetch '/users'
+    current_user = fetch '/current_user'  # subscribe for changes to login status & avatar
     return if !users.users

     loading = fetch('avatar_loading')
@@ -473,17 +475,26 @@
       cached_avatars.default = createFallbackIcon({key: 'default'})

     avatars_to_load = []
-    for user in users.users
-      if user.key not of cached_avatars and user.key not of missing_images
-        if user.avatar_file_name
-          avatars_to_load.push user
-
-        cached_avatars[user.key] = createFallbackIcon(user)
+    all_users = users.users.slice() or []
+    if current_user.user not in all_users
+      all_users.push current_user.user
+
+    for user in all_users
+      user = fetch user # subscribe to changes to avatar
+
+
+      if user.avatar_file_name
+
+        if user.avatar_file_name not of loaded_images && \
+           user.avatar_file_name not of missing_images
+
+          avatars_to_load.push user
+
+      cached_avatars[user.key] ?= createFallbackIcon(user)

-
     if avatars_to_load.length > 0
-      if !loading.loaded && !loading.loading
+      if !loading.loading
         loading.loading = true
         save loading

@@ -494,15 +505,15 @@

           pic = new Image()
           pic.onload = do(user, pic) => =>
-            cached_avatars[user.key] = @create_avatar pic
+            loaded_images[user.avatar_file_name] = cached_avatars[user.key] = @create_avatar pic
             @loading_cnt -= 1
             if @loading_cnt == 0
               loading.loading = false
-              loading.loaded = true
+              loading.loaded = md5 JSON.stringify Object.keys(loaded_images)
               save loading
           pic.onerror = do(user) => =>
             @loading_cnt -= 1
-            missing_images[user.key] = 1
+            missing_images[user.avatar_file_name] = 1

           setTimeout do(user, pic) -> ->
             pic.src = avatarUrl user, 'large'


fix typo

fix typo

--- SingularityUI/config.coffee
+++ SingularityUI/config.coffee
@@ -46,7 +46,7 @@
             defaultCpus: process.env.SINGUALRITY_DEFAULT_CPUS ? 1
             defaultMemory: process.env.SINGULARITY_DEFAULT_MEMORY ? 128
             defaultHealthcheckIntervalSeconds: process.env.SINGULARITY_DEFAULT_HEALTHCHECK_INTERVAL_SECONDS ? 5
-            defaultHealthcheckTimeoutSeconds: process.env.SINGULARITY_HEALTHCHECK_TIMEOUIT_SECONDS ? 5
+            defaultHealthcheckTimeoutSeconds: process.env.SINGULARITY_HEALTHCHECK_TIMEOUT_SECONDS ? 5
             defaultDeployHealthTimeoutSeconds: process.env.SINGULARITY_DEPLOY_HEALTH_TIMEOUT_SECONDS ? 120
             hideNewDeployButton: process.env.SINGULARITY_HIDE_NEW_DEPLOY_BUTTON ? "false"
             hideNewRequestButton: process.env.SINGULARITY_HIDE_NEW_REQUEST_BUTTON ? "false"


fix default limit...

fix default limit...

--- salvus/page/worksheet1.coffee
+++ salvus/page/worksheet1.coffee
@@ -652,7 +652,6 @@
     get_session = (cb) ->
         if persistent_session == null
             salvus_client.new_session
-                limits: {walltime:5}
                 timeout: 5
                 cb: (error, session) ->
                     if error


Add support for string literals

Add support for string literals


allow editing student id on dropped students

--- tutor/src/components/course-settings/dropped-roster.cjsx
+++ tutor/src/components/course-settings/dropped-roster.cjsx
@@ -5,6 +5,7 @@

 {RosterStore, RosterActions} = require '../../flux/roster'
 UnDropStudentLink = require './undrop-student'
+StudentIdField = require './student-id-field'

 DroppedRoster = React.createClass

@@ -16,6 +17,7 @@

       {student.first_name}
       {student.last_name}
+      <StudentIdField studentId={student.id} courseId={@props.courseId} />

         <UnDropStudentLink
         id="drop-student-popover-#{student.id}"
@@ -37,6 +39,7 @@

             First Name
             Last Name
+            Student ID
             Actions


NEW: "parentSize" etc named size-layouts - convenient AND should improve speed - elliminates 2 object creations per use - in ArtReact AND ArtEngine NEW: "topCenter" etc named location-layouts are now also optimized like the new size-layouts - no more extra object creations

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -14,12 +14,12 @@
 {isInfiniteResult} = require './EpochLayout/Infinity'

 {rgbColor, point, Point, rect, Rectangle, Matrix, matrix, identityMatrix, point0, point1, perimeter0, isPoint, perimeter} = Atomic
+{namedPoints} = Point
 {floor, ceil} = Math
 {globalEpochCycle} = GlobalEpochCycle
 {drawCacheManager} = DrawCacheManager
 {PointLayout} = Layout
 {pointLayout} = PointLayout
-

 {
   each
@@ -51,6 +51,7 @@
   defineModule
   isArray
   formattedInspect
+  object
 } = require 'art-standard-lib'

 stats = clone zeroedStats =
@@ -193,6 +194,43 @@

   defaultSizeLayout = new PointLayout ps: 1
   defaultLocationLayout = new PointLayout 0
+
+
+  namedSizeLayoutsRaw =
+    parentSize:                               ps:1
+    childrenSize:                             cs:1
+    parentHeightSquare:                       hh:1, wh:1
+    childrenSizeMaxParentWidth:               cs:1, max: ww: 1
+    parentHeightChildrenWidth:                hh:1, wcw:1
+    parentWidthChildrenHeight:                ww:1, hch:1
+    parentHeightChildrenWidthMaxParentWidth:  wcw:1, hh:1, max: ww:1
+
+  namedSizeLayouts = object namedSizeLayoutsRaw, (v) -> pointLayout v
+
+  @getSizePointLayout: getSizePointLayout = (v, previousValue) ->
+    if (isString(v) && l = namedSizeLayouts[v])
+      if previousValue
+        pointLayout namedSizeLayoutsRaw[v], previousValue
+      else l
+    else pointLayout v, previousValue || defaultSizeLayout
+
+  namedLocationLayoutsRaw = object namedPoints, (ps) -> {ps}
+  namedLocationLayoutsRaw[0] = 0
+  # NOTE! I am not supporting ".5" and "1" location layout values
+  #   - interpreted as ps: .5 and ps: 1 respectively
+  #   - because for size-layout, values of 1 should be x:1, y:1
+  #   - since we already have "centerCenteR" and "bottomRight" alternatives,
+  #   - I'm arguing for total consistency between size and location layouts
+  namedLocationLayouts = object namedLocationLayoutsRaw, (v) -> pointLayout v
+
+  @getLocationPointLayout: getLocationPointLayout = (v, previousValue) ->
+    if ((isNumber(v) || isString(v)) && l = namedLocationLayouts[v])
+      if previousValue
+        pointLayout namedLocationLayoutsRaw[v], previousValue
+      else l
+    else pointLayout v, previousValue || defaultLocationLayout
+
+    # wcw:1, hh:1, max: ww:1

   # TODO: size only is a draw/drawArea property IF drawOrder is set.
   #   NOTES:
@@ -208,9 +246,14 @@
   #   ACTION: add a postSetter which checks if @_drawOrder is set, and if so, THEN marks draw/drawArea changed
   @drawLayoutProperty
     size:
-      default: ps:1
-      preprocess: (v, previousValue) ->
-        pointLayout v, previousValue || defaultSizeLayout
+      default: "parentSize"
+      validate: (v) ->
+        if isString v
+          !!namedSizeLayouts[v]
+        else
+          true
+
+      preprocess: getSizePointLayout

   @layoutProperty
     ###
@@ -241,7 +284,13 @@

     location:
       default: 0
-      preprocess: (v, oldV) -> pointLayout v, oldV || defaultLocationLayout
+      validate: (v) ->
+        if isString v
+          !!namedLocationLayouts[v]
+        else
+          true
+
+      preprocess: getLocationPointLayout
       postSetter: -> @_locationLayoutDisabled = false

     scale:


Read registry and put to table

Read registry and put to table

--- src/gui/tabs/info/index.html.coffee
+++ src/gui/tabs/info/index.html.coffee
@@ -51,25 +51,22 @@
       # alert active?._()

   hdr 'OS', ->
-    key = "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
-    row ->
-      th 'Name'
-      td @sh.RegRead "#{key}ProductName"
-    row ->
-      th 'Version'
-      td @sh.RegRead "#{key}CurrentVersion"
-    row ->
-      th 'SP'
-      td @sh.RegRead "#{key}CSDVersion"
+    keyvals = (rec)->
+      for k, v of rec
+        row ->
+          th k
+          td @sh.RegRead "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\#{v}"
+      return
+    keyvals
+      Name: 'ProductName'
+      Version: 'CurrentVersion'
+      SP: 'CSDVersion'
     row ->
       th 'Platform'
       platform @x64
-    row ->
-      th 'Owner'
-      td @sh.RegRead "#{key}RegisteredOwner"
-    row ->
-      th 'Path'
-      td @sh.RegRead "#{key}SystemRoot"
+    keyvals
+      Owner: 'RegisteredOwner'
+      Path: 'SystemRoot'

   hdr 'Names', ->
     row ->


Remove unneeded console.log

Remove unneeded console.log

--- spec/stdlib/child-process-spec.coffee
+++ spec/stdlib/child-process-spec.coffee
@@ -121,7 +121,7 @@
           cmd = "for i in {1..20000}; do echo $RANDOM; done"
           options =
             stdout: (data) -> output.push(data)
-            stderr: (data) -> console.log data.length
+            stderr: (data) ->

           ChildProcess.exec(cmd, options)


Set main router on ember app init.

Set main router on ember app init.

--- src/pipes/ember-auto-import.coffee
+++ src/pipes/ember-auto-import.coffee
@@ -39,7 +39,7 @@
     getFileContent: ->
         @log "debug", "#C# #{@config.filename}" if @config.debug

-        results = []
+        results = ["import Em from \"vendor/ember\""]
         objects = []

         for pathname in @state.files.sort()
@@ -50,6 +50,7 @@
             if parts[0] is "pods"
                 type = parts.pop().replace /([a-z])(.*)/,
                     ($0,$1,$2) -> $1.toUpperCase()+$2
+                parts[1] = parts[1].replace /^_/, ""
                 dashedname = parts[1..].join "-"
                 camelcasename = dashedname.replace /(^|\-)([a-z])/g,
                     ($1) -> $1.toUpperCase().replace("-", "")
@@ -57,15 +58,43 @@
                 type = parts[0].replace /([a-z])(.*)s/,
                     ($0,$1,$2) -> $1.toUpperCase()+$2

+                parts[1] = parts[1].replace /^_/, ""
                 dashedname = parts[1..].join "-"
                 camelcasename = dashedname.replace /(^|\-)([a-z])/g,
                     ($1) -> $1.toUpperCase().replace("-", "")

+            camelcasename = "" if camelcasename is "Main"
             modname = "#{camelcasename}#{type}"

             results.push "import #{modname} from \"./#{pathname}\";"
+            if modname is "Router"
+                router = true
+                continue
+
             objects.push "#{modname}"
         results.push "var App = {"
+        results.push "
+            init: function() {
+                this._super.apply(this, arguments);
+                this.Router = Router;
+            },
+            Resolver: Em.DefaultResolver.extend({
+                resolveOther: function(parsedName) {
+                    var factory = this._super(parsedName);
+                    if (factory) {
+                        return factory;
+                    }
+                    parsedName.name = parsedName.name.replace(/\\.([a-z])/, function(a,b) {
+                        return b.toUpperCase();
+                    });
+                    parsedName.name = parsedName.name.replace(/^components\\//, \"\");
+                    factory = this._super(parsedName);
+                    if (factory) {
+                        return factory;
+                    }
+                }
+            }),
+        "
         results.push ("  #{i}: #{i}" for i in objects).join ",\n"
         results.push "};"
         results.push "export default App;"


Editing a container should not toggle it open/close

--- scripts/views/workspace/sidebar/toc-branch.coffee
+++ scripts/views/workspace/sidebar/toc-branch.coffee
@@ -50,7 +50,6 @@
       # when there is a space between `<li>` and the first child.
       # `.editor-node-body` ensures there is never a space.
       'click > .editor-node-body > .editor-expand-collapse': 'toggleExpanded'
-      'click > .editor-node-body > .no-edit-action': 'toggleExpanded'
       'click > .editor-node-body > .edit-settings': 'editSettings'

     # Toggle expanded/collapsed in the View


Fix bugs

Fix bugs

--- views/components/main/parts/miniship-parts/panebody.cjsx
+++ views/components/main/parts/miniship-parts/panebody.cjsx
@@ -204,7 +204,7 @@
                       <span className="hp-progress top-space" style={getStatusStyle @state.label[j]}>
                         {if ship.api_ndock_time
                           [
-                            <OverlayTrigger show = {ship.api_ndock_time} placement='bottom' overlay={<Tooltip>{__ '入渠时间'}: {resolveTime ship.api_ndock_time / 1000}</Tooltip>}>
+                            <OverlayTrigger show = {ship.api_ndock_time} placement='top' overlay={<Tooltip>{__ 'Repair Time'}: {resolveTime ship.api_ndock_time / 1000}</Tooltip>}>
                               <ProgressBar bsStyle={getHpStyle ship.api_nowhp / ship.api_maxhp * 100}
                                            now={ship.api_nowhp / ship.api_maxhp * 100} />
                             </OverlayTrigger>


Server: move checkApiAvailability to top

--- servers/lib/server/handlers/api/createuser.coffee
+++ servers/lib/server/handlers/api/createuser.coffee
@@ -99,6 +99,11 @@
         queue.next()

     ->
+      checkApiAvailability { apiToken }, (err) ->
+        return callback err  if err
+        queue.next()
+
+    ->
       # if username is not provided and suggestedUsername is too long return error
       unless username
         unless isSuggestedUsernameLengthValid suggestedUsername
@@ -115,11 +120,6 @@
       JUser.usernameAvailable username, (err, { kodingUser, forbidden }) ->
         return callback apiErrors.internalError          if err
         return callback apiErrors.usernameAlreadyExists  if kodingUser or forbidden
-        queue.next()
-
-    ->
-      checkApiAvailability { apiToken }, (err) ->
-        return callback err  if err
         queue.next()

     -> callback null, { apiToken, username }


Repos that are "trackers" show a custom icon

--- frontend/coffeescript/app/models/repo.coffee
+++ frontend/coffeescript/app/models/repo.coffee
@@ -44,6 +44,8 @@
             # private attributes of the Repository.
             if @get( 'user' ) != document.user_id
                 attrs.privacy_icon = 'icon-group'
+            else if @get( 'is_tracker' )
+                attrs.privacy_icon = 'icon-list'
             else if @get( 'is_public' )
                 attrs.privacy_icon = 'icon-unlock'
             else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -70,6 +70,4 @@
     I.velocity = I.velocity.norm()

     I.velocity = I.velocity.scale(I.speed)
-
-    unless I.velocity.equal(Point.ZERO)
-      I.facing = I.velocity+


editorView: stylefix: remove excessive space

--- coffee/src/views/editor_view.coffee
+++ coffee/src/views/editor_view.coffee
@@ -67,7 +67,6 @@
     super
     @$el.empty()

-
     text = 'new media'
     text = 'editing ' + @model.acornid() unless @model.isNew()
     @$el.append $('<h2>').addClass('editor-section').text(text)


Compacting for media-grid: solo, _clearDimTimeout.

--- src/js/jquery.hlf.media-grid.coffee
+++ src/js/jquery.hlf.media-grid.coffee
@@ -29,7 +29,6 @@
       autoReady: off
       focusDelay: 1000
       resizeDelay: 100
-      solo: on
       classNames: do ->
         classNames = {}
         keys = ['ready', 'item', 'expanded', 'dimmed', 'focused', 'sample']
@@ -50,23 +49,22 @@

       # Dimming.
       @focusDelay *= parseFloat @$sampleItem.css('transition-duration')
-      if @solo is on
-        @$expandedItem = null
-        @on
-          mouseenter: (e) =>
-            @toggleExpandedItemFocus $(e.currentTarget), on
-            return
-          mouseleave: (e) =>
-            @toggleExpandedItemFocus $(e.currentTarget), off
-            return
-          expand: (e, expanded) =>
-            @_clearDimTimeout() if expanded
-            @toggleItemFocus $(e.currentTarget), expanded, @focusDelay
-            return
-        , ".#{@classNames.item}"
-        @on 'mouseleave', =>
-          @toggleItemFocus @$expandedItem, off, 0
+      @$expandedItem = null
+      @on
+        mouseenter: (e) =>
+          @toggleExpandedItemFocus $(e.currentTarget), on
           return
+        mouseleave: (e) =>
+          @toggleExpandedItemFocus $(e.currentTarget), off
+          return
+        expand: (e, expanded) =>
+          clearTimeout @_dimTimeout if @_dimTimeout? and expanded
+          @toggleItemFocus $(e.currentTarget), expanded, @expandDuration
+          return
+      , ".#{@classNames.item}"
+      @on 'mouseleave', =>
+        @toggleItemFocus @$expandedItem, off, 0
+        return

       # Layout.
       @metrics = {}
@@ -91,7 +89,7 @@
         i = $item.index()
         if @_isRightEdgeItem(i) then @_adjustItemToRightEdge $item
         if @_isBottomEdgeItem(i) then @_adjustItemToBottomEdge $item
-        if @solo is on then @$items.removeClass @classNames.expanded
+        @$items.removeClass @classNames.expanded

       $item.toggleClass @classNames.expanded, expanded
       @$expandedItem = if expanded then $item else null
@@ -121,12 +119,6 @@

     _adjustItemToRightEdge: ($item) ->
       $item.css left: 'auto', right: 0
-      return
-
-    _clearDimTimeout: ->
-      return unless @_dimTimeout?
-      clearTimeout @_dimTimeout
-      @_dimTimeout = null
       return

     _getMetricSamples: ->


Comments

Comments

--- src/6.array.coffee
+++ src/6.array.coffee
@@ -124,8 +124,11 @@
     queryResult = if predicate? then @filter(predicate) else @
     assert(queryResult.length == 1)
     queryResult.at(0)
+# In Microsoft LINQ it still throws if matched elements > 1, because I think
+# it shouldn't throw. I want it more consistent. ----------[
 Array::singleOrNull = (predicate) -> try @single(predicate) catch then null
 Array::singleOrVoid = (predicate) -> try @single(predicate) catch then undefined
+# ]--------------------
 # If array length is 1, then `reduce` will return the single element. That's exactly what
 # `withMax` and `withMin` are for, so we don't need to copy what we did in `sum` method. [
 Array::withMax = (selector) -> @reduce((a, b, index) =>