further support for node 6

further support for node 6

--- src/smc-hub/hub.coffee
+++ src/smc-hub/hub.coffee
@@ -102,8 +102,6 @@

 # third-party libraries: add any new nodejs dependencies to the NODEJS_PACKAGES list in build.py
 async   = require("async")
-program = require('commander')          # command line arguments -- https://github.com/visionmedia/commander.js/
-
 uuid    = require('node-uuid')

 Cookies = require('cookies')            # https://github.com/jed/cookies
@@ -3493,77 +3491,84 @@
 # Process command line arguments
 #############################################

-program.usage('[start/stop/restart/status/nodaemon] [options]')
-    .option('--port <n>', 'port to listen on (default: 5000; 0 -- do not start)', ((n)->parseInt(n)), 5000)
-    .option('--proxy_port <n>', 'port that the proxy server listens on (default: 0 -- do not start)', ((n)->parseInt(n)), 0)
-    .option('--log_level [level]', "log level (default: debug) useful options include INFO, WARNING and DEBUG", String, "debug")
-    .option('--host [string]', 'host of interface to bind to (default: "127.0.0.1")', String, "127.0.0.1")
-    .option('--pidfile [string]', 'store pid in this file (default: "data/pids/hub.pid")', String, "data/pids/hub.pid")
-    .option('--logfile [string]', 'write log to this file (default: "data/logs/hub.log")', String, "data/logs/hub.log")
-    .option('--statsfile [string]', 'if set, this file contains periodically updated metrics (default: null, suggest value: "data/logs/stats.json")', String, null)
-    .option('--database_nodes <string,string,...>', 'comma separated list of ip addresses of all database nodes in the cluster', String, 'localhost')
-    .option('--keyspace [string]', 'Database name to use (default: "smc")', String, 'smc')
-    .option('--passwd [email_address]', 'Reset password of given user', String, '')
-    .option('--update', 'Update schema and primus on startup (always true for --dev; otherwise, false)')
-    .option('--stripe_sync', 'Sync stripe subscriptions to database for all users with stripe id', String, 'yes')
-    .option('--stripe_dump', 'Dump stripe subscriptions info to ~/stripe/', String, 'yes')
-    .option('--delete_expired', 'Delete expired data from the database', String, 'yes')
-    .option('--blob_maintenance', 'Do blob-related maintenance (dump to tarballs, offload to gcloud)', String, 'yes')
-    .option('--add_user_to_project [project_id,email_address]', 'Add user with given email address to project with given ID', String, '')
-    .option('--base_url [string]', 'Base url, so https://sitenamebase_url/', String, '')  # '' or string that starts with /
-    .option('--local', 'If option is specified, then *all* projects run locally as the same user as the server and store state in .sagemathcloud-local instead of .sagemathcloud; also do not kill all processes on project restart -- for development use (default: false, since not given)', Boolean, false)
-    .option('--foreground', 'If specified, do not run as a deamon')
-    .option('--dev', 'if given, then run in VERY UNSAFE single-user local dev mode')
-    .option('--single', 'if given, then run in LESS SAFE single-machine mode')
-    .option('--db_pool <n>', 'number of db connections in pool (default: 50)', ((n)->parseInt(n)), 50)
-    .option('--db_concurrent_warn <n>', 'be very unhappy if number of concurrent db requests exceeds this (default: 300)', ((n)->parseInt(n)), 300)
-    .parse(process.argv)
-
-    # NOTE: the --local option above may be what is used later for single user installs, i.e., the version included with Sage.
-
-if program._name.slice(0,3) == 'hub'
-    # run as a server/daemon (otherwise, is being imported as a library)
-
-    #if program.rawArgs[1] in ['start', 'restart']
-    process.addListener "uncaughtException", (err) ->
-        winston.debug("BUG ****************************************************************************")
-        winston.debug("Uncaught exception: " + err)
-        winston.debug(err.stack)
-        winston.debug("BUG ****************************************************************************")
-
-    if program.passwd
-        console.log("Resetting password")
-        reset_password(program.passwd, (err) -> process.exit())
-    else if program.stripe_sync
-        console.log("Stripe sync")
-        stripe_sync(false, (err) -> winston.debug("DONE", err); process.exit())
-    else if program.stripe_dump
-        console.log("Stripe dump")
-        stripe_sync(true, (err) -> winston.debug("DONE", err); process.exit())
-    else if program.delete_expired
-        delete_expired (err) ->
-            winston.debug("DONE", err)
-            process.exit()
-    else if program.blob_maintenance
-        blob_maintenance (err) ->
-            winston.debug("DONE", err)
-            process.exit()
-    else if program.add_user_to_project
-        console.log("Adding user to project")
-        v = program.add_user_to_project.split(',')
-        add_user_to_project v[0], v[1], (err) ->
-            if err
-                 console.log("Failed to add user: #{err}")
+command_line = () ->
+    program = require('commander')          # command line arguments -- https://github.com/visionmedia/commander.js/
+    daemon  = require("start-stop-daemon")  # don't import unless in a script; otherwise breaks in node v6+
+
+    program.usage('[start/stop/restart/status/nodaemon] [options]')
+        .option('--port <n>', 'port to listen on (default: 5000; 0 -- do not start)', ((n)->parseInt(n)), 5000)
+        .option('--proxy_port <n>', 'port that the proxy server listens on (default: 0 -- do not start)', ((n)->parseInt(n)), 0)
+        .option('--log_level [level]', "log level (default: debug) useful options include INFO, WARNING and DEBUG", String, "debug")
+        .option('--host [string]', 'host of interface to bind to (default: "127.0.0.1")', String, "127.0.0.1")
+        .option('--pidfile [string]', 'store pid in this file (default: "data/pids/hub.pid")', String, "data/pids/hub.pid")
+        .option('--logfile [string]', 'write log to this file (default: "data/logs/hub.log")', String, "data/logs/hub.log")
+        .option('--statsfile [string]', 'if set, this file contains periodically updated metrics (default: null, suggest value: "data/logs/stats.json")', String, null)
+        .option('--database_nodes <string,string,...>', 'comma separated list of ip addresses of all database nodes in the cluster', String, 'localhost')
+        .option('--keyspace [string]', 'Database name to use (default: "smc")', String, 'smc')
+        .option('--passwd [email_address]', 'Reset password of given user', String, '')
+        .option('--update', 'Update schema and primus on startup (always true for --dev; otherwise, false)')
+        .option('--stripe_sync', 'Sync stripe subscriptions to database for all users with stripe id', String, 'yes')
+        .option('--stripe_dump', 'Dump stripe subscriptions info to ~/stripe/', String, 'yes')
+        .option('--delete_expired', 'Delete expired data from the database', String, 'yes')
+        .option('--blob_maintenance', 'Do blob-related maintenance (dump to tarballs, offload to gcloud)', String, 'yes')
+        .option('--add_user_to_project [project_id,email_address]', 'Add user with given email address to project with given ID', String, '')
+        .option('--base_url [string]', 'Base url, so https://sitenamebase_url/', String, '')  # '' or string that starts with /
+        .option('--local', 'If option is specified, then *all* projects run locally as the same user as the server and store state in .sagemathcloud-local instead of .sagemathcloud; also do not kill all processes on project restart -- for development use (default: false, since not given)', Boolean, false)
+        .option('--foreground', 'If specified, do not run as a deamon')
+        .option('--dev', 'if given, then run in VERY UNSAFE single-user local dev mode')
+        .option('--single', 'if given, then run in LESS SAFE single-machine mode')
+        .option('--db_pool <n>', 'number of db connections in pool (default: 50)', ((n)->parseInt(n)), 50)
+        .option('--db_concurrent_warn <n>', 'be very unhappy if number of concurrent db requests exceeds this (default: 300)', ((n)->parseInt(n)), 300)
+        .parse(process.argv)
+
+        # NOTE: the --local option above may be what is used later for single user installs, i.e., the version included with Sage.
+
+    if program._name.slice(0,3) == 'hub'
+        # run as a server/daemon (otherwise, is being imported as a library)
+
+        #if program.rawArgs[1] in ['start', 'restart']
+        process.addListener "uncaughtException", (err) ->
+            winston.debug("BUG ****************************************************************************")
+            winston.debug("Uncaught exception: " + err)
+            winston.debug(err.stack)
+            winston.debug("BUG ****************************************************************************")
+
+        if program.passwd
+            console.log("Resetting password")
+            reset_password(program.passwd, (err) -> process.exit())
+        else if program.stripe_sync
+            console.log("Stripe sync")
+            stripe_sync(false, (err) -> winston.debug("DONE", err); process.exit())
+        else if program.stripe_dump
+            console.log("Stripe dump")
+            stripe_sync(true, (err) -> winston.debug("DONE", err); process.exit())
+        else if program.delete_expired
+            delete_expired (err) ->
+                winston.debug("DONE", err)
+                process.exit()
+        else if program.blob_maintenance
+            blob_maintenance (err) ->
+                winston.debug("DONE", err)
+                process.exit()
+        else if program.add_user_to_project
+            console.log("Adding user to project")
+            v = program.add_user_to_project.split(',')
+            add_user_to_project v[0], v[1], (err) ->
+                if err
+                     console.log("Failed to add user: #{err}")
+                else
+                     console.log("User added to project.")
+                process.exit()
+        else
+            console.log("Running hub; pidfile=#{program.pidfile}, port=#{program.port}, proxy_port=#{program.proxy_port}")
+            # logFile = /dev/null to prevent huge duplicated output that is already in program.logfile
+            if program.foreground
+                start_server (err) ->
+                    if err and program.dev
+                        process.exit(1)
             else
-                 console.log("User added to project.")
-            process.exit()
-    else
-        console.log("Running hub; pidfile=#{program.pidfile}, port=#{program.port}, proxy_port=#{program.proxy_port}")
-        # logFile = /dev/null to prevent huge duplicated output that is already in program.logfile
-        if program.foreground
-            start_server (err) ->
-                if err and program.dev
-                    process.exit(1)
-        else
-            daemon  = require("start-stop-daemon")  # don't import unless in a script; otherwise breaks in node v6+
-            daemon({pidFile:program.pidfile, outFile:program.logfile, errFile:program.logfile, logFile:'/dev/null', max:30}, start_server)
+                daemon({pidFile:program.pidfile, outFile:program.logfile, errFile:program.logfile, logFile:'/dev/null', max:30}, start_server)
+
+
+if process.argv.length > 1
+    command_line()


fix for static tables (#101)

fix for static tables (#101)

--- modules/sticky-table-headers/main/index.coffee
+++ modules/sticky-table-headers/main/index.coffee
@@ -176,6 +176,9 @@
     offsetHeight = 0
     offsetWidth = 0

+    if options.fullWidth
+      table.style('width', '100%')
+
     if options.stickTableHead
       offsetHeight = table.select('thead').height()

@@ -209,6 +212,7 @@

     if options.fullWidth
       table
+        .style('width', '')
         .style('min-width', wrapperBox.width + offsetWidth - widthScrollbarOffset + 'px')
         .style('min-height', wrapperBox.height + offsetHeight - heightScrollbarOffset + 'px')
     else
@@ -217,6 +221,8 @@
         .style('max-height', tableBox.height - offsetHeight + heightScrollbarOffset + 'px')

     tableClone = table.clone(true)
+      .style('height', table.style('height'))
+      .style('width', table.style('width'))

     if options.stickTableHead
       # Append top


http_ircのAPI_KEYにミスがあったので修正

--- http_irc.coffee
+++ http_irc.coffee
@@ -32,17 +32,16 @@
 type        = process.env.HUBOT_IRC_TYPE
 debug       = process.env.HUBOT_HTTP_IRC_DEBUG
 api_key     = process.env.HUBOT_HTTP_IRC_API_KEY
-allow       = process.env.HUBOT_HTTP_IRC_ALLOW
-deny        = process.env.HUBOT_HTTP_IRC_DENY
+allow       = process.env.HUBOT_HTTP_IRC_ALLOW || null
+deny        = process.env.HUBOT_HTTP_IRC_DENY  || null
 allow_flag  = false

 send_message = (res, room, msg, query) ->
-  # console.log "room: #{room}"
-  # console.log "msg: #{message}"
   unless room
     console.log "#{prefix}: There is no room to say."
-    res.writeHead 200, {'Content-Type': 'text/plain'}
+    res.writeHead 400, {'Content-Type': 'text/plain'}
     res.end 'Error: There is no room to say.'
+    return

   # console.log "query: %j", query

@@ -58,6 +57,7 @@
     @sm.send ["#{room}"], msg, query
   else
     @sm.send ["#{room}"], msg
+
   res.writeHead 200, {'Content-Type': 'text/plain'}
   res.end 'OK'

@@ -68,6 +68,9 @@

   if req.headers['X-Forwarded-For']
     remote_ip = req.headers['X-Forwarded-For']
+
+  return false if deny is null
+  return false if allow is null

   deny_ips  = deny.split(',')
   allow_ips = allow.split(',')
@@ -91,12 +94,10 @@
     console.log "#{prefix}: INVALID API_KEY."
     return false

-check_request = (req) ->
+check_request = (req, res) ->
   unless check_api_key req
     unless check_ip req
-      res.writeHead 200, {'Content-Type': 'text/plain'}
-      res.end 'Not allowed to access.'
-
+      return false

 module.exports = (robot) ->

@@ -105,7 +106,10 @@
   message = ''

   robot.router.get "#{path}", (req, res) ->
-    check_request req
+    if check_request(req, res) is false
+      res.writeHead 200, {'Content-Type': 'text/plain'}
+      res.end 'Not allowed to access.'
+      return

     query   = querystring.parse(req._parsedUrl.query)
     room    = query.room
@@ -116,12 +120,17 @@
     send_message(res, room, message, query)

   robot.router.post "#{path}", (req, res) ->
-    check_request req
+    if check_request(req, res) is false
+      res.writeHead 200, {'Content-Type': 'text/plain'}
+      res.end 'Not allowed to access.'
+      return

     query   = querystring.parse(req._parsedUrl.query)
     room    = query.room or ''
     message = req.body.message
     room    = req.body.room unless room
-
+    console.log "req:"
+    console.dir req.body
+    console.dir query
     send_message(res, room, message, req.body)


return if methods not returned on property goto fix #60

--- lib/goto/property-goto.coffee
+++ lib/goto/property-goto.coffee
@@ -41,6 +41,9 @@
             return

         methods = proxy.methods(calledClass)
+        if not methods.names?
+            return
+
         if methods.names.indexOf(term) == -1
             return
         value = methods.values[term]


Terminate request should be sent only in active state

--- app/assets/javascripts/interaction_web_tools/chat.coffee
+++ app/assets/javascripts/interaction_web_tools/chat.coffee
@@ -94,9 +94,10 @@
     @autoScroll()

   terminateChat: =>
-    $.ajax
-      url: @endpoint
-      type: 'DELETE'
+    if @state == stateEnum.ACTIVE
+      $.ajax
+        url: @endpoint
+        type: 'DELETE'
     @state = stateEnum.TERMINATED

   displayIndicator: (type) =>


OX-250 fixed node deletion with one edge

--- app/assets/javascripts/oxalis/geometries/skeleton.coffee
+++ app/assets/javascripts/oxalis/geometries/skeleton.coffee
@@ -154,7 +154,7 @@
             @edgesBuffer[index].set( neighbor.pos, (edgesIndex++) * 3 )
             @edgesBuffer[index].set(     node.pos, (edgesIndex++) * 3 )

-      if edgesIndex != (2 * (@curIndex[index] - 1)) or (@curIndex == 0 and edgesIndex == 0)
+      if edgesIndex != (2 * (@curIndex[index] - 1)) and !(@curIndex[index] == 0 and edgesIndex == 0)
         console.error("edgesIndex does not equal (2 * (@curIndex[index] - 1) !!!")

       @routes[index].geometry.__vertexArray = @edgesBuffer[index]
@@ -272,14 +272,19 @@
       return

     index = @getIndexFromTreeId(@route.getTree().treeId)
-    for i in [0...@curIndex]
+
+    for i in [0...@curIndex[index]]
       if @nodes[index].geometry.nodeIDs[i] == node.id
         nodesIndex = i
         break

+    # swap IDs
+    @nodes[index].geometry.nodeIDs[nodesIndex] = @nodes[index].geometry.nodeIDs[@curIndex[index]-1]
+
+    # swap nodes
     for i in [0..2]
       @nodes[index].geometry.__vertexArray[nodesIndex * 3 + i] =
-        @nodes[index].geometry.__vertexArray[(@curIndex - 1) * 3 + i]
+        @nodes[index].geometry.__vertexArray[(@curIndex[index] - 1) * 3 + i]

     # Delete Edge by finding it in the array
     # ASSUMPTION edges always go from smaller ID to bigger ID
@@ -287,16 +292,18 @@
       edgeArray = node.pos.concat(node.neighbors[0].pos)
     else
       edgeArray = node.neighbors[0].pos.concat(node.pos)
-    for i in [0...@curIndex]
+    for i in [0...@curIndex[index]]
       found = true
       for j in [0..5]
         found &= Math.abs(@routes[index].geometry.__vertexArray[6 * i + j] - edgeArray[j]) < 0.01
       if found
         edgesIndex = i
         break
+
+    # swap edges
     for i in [0..5]
       @routes[index].geometry.__vertexArray[edgesIndex * 6 + i] =
-        @routes[index].geometry.__vertexArray[(@curIndex - 2) * 6 + i]
+        @routes[index].geometry.__vertexArray[(@curIndex[index] - 2) * 6 + i]

     @curIndex[index]--
@@ -338,7 +345,7 @@
     newNode.doubleSided = true
     @nodesSpheres.push(newNode)
     @trigger "newGeometries", [newNode]
-
+
   getIndexFromTreeId : (treeId) ->
     unless treeId
       treeId = @route.getTree().treeId


Scroll to top on entering user index pages.

--- app/assets/javascripts/routes/user_index_route.js.coffee
+++ app/assets/javascripts/routes/user_index_route.js.coffee
@@ -22,4 +22,5 @@
     controller.set 'model', []

   afterModel: ->
+    Ember.run.next -> window.scrollTo 0, 0
     Hummingbird.TitleManager.setTitle @modelFor('user').get('username') + "'s Profile"


set target atom-workspace.

set target atom-workspace.

--- lib/atom-ternjs-manager.coffee
+++ lib/atom-ternjs-manager.coffee
@@ -212,7 +212,7 @@
       @provider?.forceCompletion()
     @disposables.push atom.commands.add 'atom-text-editor', 'tern:definition': (event) =>
       @client?.definition()
-    @disposables.push atom.commands.add 'atom-text-editor', 'tern:restart': (event) =>
+    @disposables.push atom.commands.add 'atom-workspace', 'tern:restart': (event) =>
       @restartServer()

   restartServer: ->


Better message

Better message

--- lib/not-supported-view.coffee
+++ lib/not-supported-view.coffee
@@ -14,4 +14,4 @@
     message = ext.length > 0 ? ext : title

     @div class: 'test overlay from-top', =>
-      @div "Format: '#{message}' files not yet supported.", class: "message"
+      @div "Formatting '#{message}' files is not yet supported.", class: "message"


meaningless exercise as warming up

meaningless exercise as warming up

--- lib/occurrence-manager.coffee
+++ lib/occurrence-manager.coffee
@@ -10,6 +10,8 @@
 module.exports =
 class OccurrenceManager
   patterns: null
+  markerOptions: {invalidate: 'inside'}
+  decorationOptions: {type: 'highlight', class: 'vim-mode-plus-occurrence-match'}

   constructor: (@vimState) ->
     {@editor, @editorElement} = @vimState
@@ -19,22 +21,24 @@
     @patterns = []

     @markerLayer = @editor.addMarkerLayer()
-    options = {type: 'highlight', class: 'vim-mode-plus-occurrence-match'}
-    @decorationLayer = @editor.decorateMarkerLayer(@markerLayer, options)
+    @decorationLayer = @editor.decorateMarkerLayer(@markerLayer, @decorationOptions)

     # @patterns is single source of truth (SSOT)
     # All maker create/destroy/css-update is done by reacting @patters's change.
     # -------------------------
     @onDidChangePatterns ({newPattern}) =>
       if newPattern
-        @markerLayer.markBufferRange(range) for range in scanEditor(@editor, newPattern)
+        @markBufferRangeByPattern(newPattern)
       else
-        # When patterns were cleared, destroy all marker.
-        marker.destroy() for marker in @markerLayer.getMarkers()
+        @clearMarkers()

     # Update css on every marker update.
     @markerLayer.onDidUpdate =>
       @editorElement.classList.toggle("has-occurrence", @hasMarkers())
+
+  markBufferRangeByPattern: (pattern) ->
+    for range in scanEditor(@editor, pattern)
+      @markerLayer.markBufferRange(range, @markerOptions)

   # Callback get passed following object
   # - newPattern: can be undefined on reset event
@@ -68,6 +72,10 @@

   # Markers
   # -------------------------
+  clearMarkers: (pattern) ->
+    for marker in @markerLayer.getMarkers()
+      marker.destroy()
+
   hasMarkers: ->
     @markerLayer.getMarkerCount() > 0


copy capitalization for canceled builds

copy capitalization for canceled builds

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -240,7 +240,7 @@
       else
         if @status() == "canceled"
           # build was canceled from the queue
-          "canceled"
+          "Canceled"
         else if @start_time()
           duration_millis = @updatingDuration(@start_time())
           CI.time.as_duration_condensed(duration_millis) + @estimated_time(duration_millis)


added fallback for for .js files for routes

--- instance.coffee
+++ instance.coffee
@@ -100,7 +100,8 @@
   # @return string
   ###
   _getRouteFile = (file) ->
-    app.get("routes") + "/" + file + '.coffee'
+    ext = '.js' unless fs.existsSync '.coffee'
+    app.get("routes") + "/" + file + ext

   # !----- Bootstrap


A little refactor

A little refactor

--- lib/suggestion-builder.coffee
+++ lib/suggestion-builder.coffee
@@ -19,8 +19,7 @@

   #general utility
   trim: (label) =>
-    return unless label
-    if label.length>@trimTypeTo
+    if label?.length>@trimTypeTo
       label.slice(0,@trimTypeTo)+'...'
     else
       label


add polling to manuscript manager page

add polling to manuscript manager page

--- app/assets/javascripts/manuscript_manager.js.coffee
+++ app/assets/javascripts/manuscript_manager.js.coffee
@@ -138,9 +138,15 @@

     componentDidMount: ->
       Tahi.utils.bindColumnResize()
-
+      @startPolling()
+      @getColumns()
+
+    getColumns: ->
       $.getJSON @props.route, (data,status) =>
         @setState flows: _.sortBy(data.flows, (f) -> f.position), paper: data.paper
+
+    startPolling: ->
+      setInterval((=> @getColumns()), 5000)

     componentDidUpdate: ->
       Tahi.utils.resizeColumnHeaders()


Revert "Simple setup of Socket.io server"

This reverts commit c9158748dc6c06fe75b629a9c527cd0e5df754a7.

--- ioserver.coffee
+++ ioserver.coffee
@@ -1,113 +0,0 @@
-_		= require 'lodash'
-Server	= require('socket.io')
-
-PORT 	= 8080
-HOST 	= 'localhost'
-
-module.exports = class IOServer
-	# Define the variables used by the server
-	constructor: ({host, port, login}={}) ->
-		@host = if host? then host else HOST
-		@port = if port? then port else PORT
-		@login = if login? then login else null
-
-		@service_list = {}
-		@method_list = {}
-
-	# Allow to register easily a class to this server
-	# this class will be bind to a specific namespace
-	addService: ({name, service}={}) ->
-		if name? and name.length > 2
-			@service_list[name] = new service()
-
-			# list methods of object... it will be the list of io actions
-			@method_list[name] = _.functions(@service_list[name])
-		else
-			console.error "#[!] Service name MUST be longer than 2 characters"
-
-	# Launch socket IO and get ready to handle events on connection
-	start: () ->
-		date = @_now()
-		console.log "###################### #{date} #############################"
-		console.log "#[+] Starting server on port: #{@port} ..."
-		@io = Server.listen(@port)
-
-		ns = {}
-
-		# Register each different services by its namespace
-		for service_name, service of @service_list
-			if @login?
-				ns[service_name] = @io.of "/#{@login}/#{service_name}"
-			else
-				ns[service_name] = @io.of "/#{service_name}"
-
-			console.log "#[+] service #{service_name} registered..."
-			# get ready for connection
-			ns[service_name].on 'connection', @handleEvents(ns[service_name], service_name)
-
-		if @channel_list? and @channel_list.length > 0
-			# Register all channels by their room
-			io.sockets.on 'connection', @handleEvents(io.sockets, 'global')
-
-	# Once a client is connected, get ready to handle his events
-	handleEvents: (ns, service_name) ->
-		(socket) =>
-			console.log "#[+] received connection for service #{service_name}"
-			for index, action of @method_list[service_name]
-				# does not listen for private methods
-				if action.substring(0,1) is '_'
-					continue
-				socket.on action, @handleCallback
-									service: service_name
-									method: action
-									socket: socket
-									namespace: ns
-
-	# On a specific event call the appropriate method of object
-	handleCallback: ({service, method, socket, namespace}={}) ->
-		(data) =>
-			console.log "#[+] call method #{method} of service #{service}"
-			@service_list[service][method] data, socket
-
-	interact: ({service, room, method, data}={}) ->
-		@_findClientsSocket
-			room: room
-			service: service
-			cb: (connectedSockets) =>
-				if connectedSockets?
-					for i, socket of connectedSockets
-						# avoid undefined
-						if socket?
-							socket.emit method, data
-
-
-	_findClientsSocket: ({service, room, cb}={}) ->
-		res = []
-		ns = @io.of(service ||"/")
-
-		if ns
-			for id, i of ns.connected
-				if room? and room.length > 0
-					index = ns.connected[id].rooms.indexOf(room)
-					unless index is -1
-						res.push ns.connected[id]
-					else
-						console.log JSON.stringify ns.connected[id].rooms
-				else
-					res.push ns.connected[id]
-		cb res
-
-
-	_now: () ->
-		d = new Date()
-		day = d.getDate()
-		month = d.getMonth()
-		year = d.getFullYear()
-		hours = d.getHours()
-		minutes = d.getMinutes()
-		seconds = d.getSeconds()
-		hours = if hours < 10 then "0#{hours}" else "#{hours}"
-		minutes = if minutes < 10 then ":0#{minutes}" else ":#{minutes}"
-		seconds = if seconds < 10 then ":0#{seconds}" else ":#{seconds}"
-		return "#{day}/#{month}/#{year} - #{hours}#{minutes}#{seconds}"
-


Fix undefined variant_id issue on IEs

Fix undefined variant_id issue on IEs

--- app/assets/javascripts/store/variant_options.coffee
+++ app/assets/javascripts/store/variant_options.coffee
@@ -59,7 +59,6 @@

     # update in-stock status of the other option, and find the variant_id if both options have been selected
     otherOptionSelector.find(".option-value").removeClass("in-stock")
-    variant_id
     variants = options[otherOptionId]
     for key,variant of variants
       for id in ids


Dashboard pageview fix

Dashboard pageview fix

--- js/inner.coffee
+++ js/inner.coffee
@@ -1314,8 +1314,8 @@
   loadDashboard: (cx) =>
     @loadProjects()
     @loadRecentBuilds()
+    _gaq.push(['_trackPageview', '/dashboard'])
     display "dashboard", {}
-    _gaq.push(['_trackPageview', '/dashboard']);

   loadAddProjects: (cx) =>
     @current_user().loadOrganizations()


Render method for docs

Render method for docs

--- js/outer.coffee
+++ js/outer.coffee
@@ -37,9 +37,17 @@
   # Doc
   class Doc extends Page
     render: ->
-      $('.doc-title').remove();
-      $('.categories').html(HAML['article'](renderContext));
-      $('.categories').removeClass('categories').addClass('article');
+      if $('.categories').length > 0
+        $('.doc-title').remove();
+        $('.categories').removeClass('categories').addClass('article');
+        $('.article').html(HAML[@name](renderContext));
+      else
+        $("body").attr("id","#{@name}-page").html(HAML['header'](renderContext))
+        $("body").append(HAML['title'](renderContext))
+        $("#title h1").text("Documentation")
+        $("body").append("

")
+        $(".article").append(HAML[@name](renderContext))
+        $("body").append(HAML['footer'](renderContext))

     load: ->
       if !HAML['categories']


changed setImmediate to setTimeout for compatibility with node v0.8

--- test/Client.coffee
+++ test/Client.coffee
@@ -263,7 +263,7 @@

         server = ProtoSock.createServer getServer(), TestProtocolServer()
         server.on 'close', (err) ->
-          setImmediate verify
+          setTimeout verify, 1

         testProtocol = TestProtocol server
         testProtocol.connect = (socket) ->


Moves AP from spec helpers to lib to make it globally available

--- javascripts/lib.coffee
+++ javascripts/lib.coffee
@@ -1,6 +1,14 @@
 #= require almond_wrapper
 #= require underscore-min

+
+window.AP = (object)->
+  str = "#{object.constructor.name}"
+  str += "{ "
+  for key, value of object
+    str += "#{key}: #{value}"
+  str += " }"
+  str
 window.ttm ||= {}
 window.ttm.decorators ||= {}
 window.ttm.lib ||= {}


Allow `getRange` to be used by iterators

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -39,7 +39,7 @@
   keepLooping: ->
     @stopped is false

-  getRange: ->
+  getRange: =>
     matchStartIndex = @match.index
     matchEndIndex = matchStartIndex + @matchText.length


Use row-oriented methods

Use row-oriented methods

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -837,8 +837,8 @@
           rows.pop() unless @isFoldedAtBufferRow(selection.end.row)

         # Move line around the fold that is directly above the selection
-        precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1])
-        precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row
+        precedingScreenRow = @screenRowForBufferRow(selection.start.row) - 1
+        precedingBufferRow = @bufferRowForScreenRow(precedingScreenRow)
         if fold = @largestFoldContainingBufferRow(precedingBufferRow)
           insertDelta = fold.getBufferRange().getRowCount()
         else


meaningful tests -> use source

meaningful tests -> use source

--- test/client.coffee
+++ test/client.coffee
@@ -1,4 +1,4 @@
-esl = require 'esl'
+esl = require '../lib/esl.coffee'
 esl.debug = true

 # Open connection, send arbitrary API command, disconnect.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -18,11 +18,6 @@

     screen = TextScreen()
     screen.centerText canvas, 'Centering text is easy'
-    enemy.follow(player)
-
-    # => The enemy now has it's velocity attribute set in
-    # the direction of the player, with magnitude equal to
-    # the enemy's speed

     @name centerText


mixed up color codes

mixed up color codes

--- src/themes/snow.coffee
+++ src/themes/snow.coffee
@@ -100,7 +100,7 @@
           colors[c] = "rgb(#{parseInt(c.substr(1,2), 16)}, #{parseInt(c.substr(3,2), 16)}, #{parseInt(c.substr(5,2), 16)})"
           return colors
         , {})
-        format.defaultStyle = if css == 'fore-color' then '#ffffff' else '#000000'
+        format.defaultStyle = if css == 'fore-color' then '#000000' else '#ffffff'
     )


Remove methods carried over from old view

--- src/workspace-element.coffee
+++ src/workspace-element.coffee
@@ -69,22 +69,6 @@

     @__spacePenView.setModel(@model)

-  # Essential: Focus the pane directly above the active pane.
-  focusPaneViewAbove: -> @panes.focusPaneViewAbove()
-
-  # Essential: Focus the pane directly below the active pane.
-  focusPaneViewBelow: -> @panes.focusPaneViewBelow()
-
-  # Essential: Focus the pane directly to the left of the active pane.
-  focusPaneViewOnLeft: -> @panes.focusPaneViewOnLeft()
-
-  # Essential: Focus the pane directly to the right of the active pane.
-  focusPaneViewOnRight: -> @panes.focusPaneViewOnRight()
-
-  ###
-  Section: Private
-  ###
-
   setTextEditorFontSize: (fontSize) ->
     atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px')


ChatPane: define `scrollToBottom` method

--- client/CollaborativeWorkspace/panes/chatpane.coffee
+++ client/CollaborativeWorkspace/panes/chatpane.coffee
@@ -99,7 +99,7 @@
     @updateDate details.time
     @lastMessage = @lastChatItem.message
     @emit "NewChatItemCreated"
-    @scrollToTop()
+    @scrollToBottom()

   appendToChatItem: (params) ->
     {details}  = params
@@ -129,9 +129,9 @@
       element.innerHTML = words.join " "
       element.classList.add "tw-chat-media"

-  scrollToTop: ->
-    $messages = @messages.$()
-    $messages.scrollTop $messages[0].scrollHeight
+  scrollToBottom: ->
+    element = @messages.getElement()
+    element.scrollTop = element.scrollHeight

   pistachio: ->
     """


group controller: fat fingers

group controller: fat fingers

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -53,7 +53,7 @@
     mainController.on 'loginRequired',        @loginRequired.bind this

   getCurrentGroup:->
-    throw 'FIXME: array whould never be passed'  if Array.isArray @currentGroupData.data
+    throw 'FIXME: array should never be passed'  if Array.isArray @currentGroupData.data
     return @currentGroupData.data

   openGroupChannel:(group, callback=->)->


chat: add context menu to emit video request event

--- client/ide/lib/views/chat/idechatmessagepane.coffee
+++ client/ide/lib/views/chat/idechatmessagepane.coffee
@@ -133,6 +133,9 @@
     @addSubView header

+  requestJoinVideo: -> @emit 'ChatVideoRequested'
+
+
   createMenu: ->

     channel = @getData()
@@ -150,10 +153,11 @@
   settingsMenu: ->

     menu =
-      'Search'     : cssClass : 'disabled', callback: kd.noop
-      # 'Settings'   : callback : @getDelegate().bound 'showSettingsPane'
-      'Minimize'   : callback : @getDelegate().bound 'end'
-      'Learn More' : separator: yes, callback : -> kd.utils.createExternalLink 'http://learn.koding.com/collaboration'
+      'Search'          : { cssClass : 'disabled', callback: kd.noop }
+      'Minimize'        : { callback : @getDelegate().bound 'end' }
+      'Join Video Chat' : { callback : @bound 'requestJoinVideo' }
+      'Learn More'      : { separator: yes, callback : -> kd.utils.createExternalLink 'http://learn.koding.com/collaboration' }
+      # 'Settings'      : { callback : @getDelegate().bound 'showSettingsPane' }

     if @isInSession
     then menu['Leave Session'] = { callback : => @parent.settingsPane.leaveSession() }


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -16,7 +16,7 @@
   enemy.include(Follow)
   enemy.follow(player)

-  ok enemy.I.velocity.equal(Point(1, 0)), 'enemy should head toward player with '
+  ok enemy.I.velocity.equal(Point(1, 0)), 'enemy should head toward player with velocit'

   fastEnemy = GameObject
     x: 50


collect terms with common exponent

collect terms with common exponent

--- coffeequate/src/operators/Mul.coffee
+++ coffeequate/src/operators/Mul.coffee
@@ -309,6 +309,25 @@
 			if constantterm?.evaluate?() == 0
 				return new terminals.Constant("0")

+			newLikeTerms = []
+			for term0 in liketerms
+				[base0, power0] = term0
+
+				found = false
+				for term1 in newLikeTerms
+					[base1, power1] = term1
+
+					if power1.equals?(power0, equivalencies)
+						unless base1 instanceof Mul
+							base1 = new Mul(base1)
+							term1[0] = base1
+						base1.children.push(base0)
+						found = true
+						break
+
+				newLikeTerms.push(term0) unless found
+			liketerms = newLikeTerms
+
 			newMul = null
 			for liketerm in liketerms
 				if liketerm[1].evaluate?() != 1
@@ -651,4 +670,4 @@
 				return false
 			return true

-	return Mul+	return Mul


Updated color module

Updated color module

--- src/types/color.coffee
+++ src/types/color.coffee
@@ -1,4 +1,4 @@
-define 'elyssa/types/color', ['elyssa/math'], (Math) ->
+define ['elyssa/math', 'elyssa/constants/colors'], (Math, colors) ->
   'use strict'

   # Color class
@@ -116,474 +116,9 @@

       @

-
-
     # Static methods
-    # Predefined colors
-
-    # Basic colors
-
-    # Returns new object with color values: R: 0   G: 255 B: 255
-    # @return {Object} The color object
-    @aqua: -> new Color {r: 0, g: 255, b: 255}
-    # Returns new object with color values: R: 0   G: 0   B: 0
-    # @return {Object} The color object
-    @black: -> new Color {r: 0, g: 0, b: 0}
-    # Returns new object with color values: R: 0   G: 0   B: 255
-    # @return {Object} The color object
-    @blue: -> new Color {r: 0, g: 0, b: 255}
-    # Returns new object with color values: R: 255 G: 0   B: 255
-    # @return {Object} The color object
-    @fuchsia: -> new Color {r: 255, g: 0, b: 255}
-    # Returns new object with color values: R: 128 G: 128 B: 128
-    # @return {Object} The color object
-    @gray: -> new Color {r: 128, g: 128, b: 128}
-    # Alternative spelling for .gray
-    # @see Color.gray
-    # @return {Object} The color object
-    @grey: @gray
-    # Returns new object with color values: R: 0   G: 128 B: 0
-    # @return {Object} The color object
-    @green: -> new Color {r: 0, g: 128, b: 0}
-    # Returns new object with color values: R: 0   G: 255 B: 0
-    # @return {Object} The color object
-    @lime: -> new Color {r: 0, g: 255, b: 0}
-    # Returns new object with color values: R: 128 G: 0   B: 0
-    # @return {Object} The color object
-    @maroon: -> new Color {r: 128, g: 0, b: 0}
-    # Returns new object with color values: R: 0   G: 0   B: 128
-    # @return {Object} The color object
-    @navy: -> new Color {r: 0, g: 0, b: 128}
-    # Returns new object with color values: R: 128 G: 128 B: 0
-    # @return {Object} The color object
-    @olive: -> new Color {r: 128, g: 128, b: 0}
-    # Returns new object with color values: R: 128 G: 0   B: 128
-    # @return {Object} The color object
-    @purple: -> new Color {r: 128, g: 0, b: 128}
-    # Returns new object with color values: R: 255 G: 0   B: 0
-    # @return {Object} The color object
-    @red: -> new Color {r: 255, g: 0, b: 0}
-    # Returns new object with color values: R: 192 G: 192 B: 192
-    # @return {Object} The color object
-    @silver: -> new Color {r: 192, g: 192, b: 192}
-    # Returns new object with color values: R: 0   G: 128 B: 128
-    # @return {Object} The color object
-    @teal: -> new Color {r: 0, g: 128, b: 128}
-    # Returns new object with color values: R: 255 G: 255 B: 255
-    @white: -> new Color {r: 255, g: 255, b: 255}
-    # Returns new object with color values: R: 255 G: 255 B: 0
-    # @return {Object} The color object
-    @yellow: -> new Color {r: 255, g: 255, b: 0}
-
-    # Returns new object with color values: R: 0 G: 0 B: 0 A: 0
-    # @return {Object} The color object
-    @transparent: -> new Color {r: 0, g: 0, b: 0, a: 0}
-
-    # Advanced colors (http://www.w3schools.com/cssref/css_colornames.asp)
-
-    # Returns new object with color values: R: 240 G: 248 B: 255
-    # @return {Object} The color object
-    @aliceBlue: -> new Color {r: 240, g: 248, b: 255}
-    # Returns new object with color values: R: 250 G: 235 B: 215
-    # @return {Object} The color object
-    @antiqueWhite: -> new Color {r: 250, g: 235, b: 215}
-    # Returns new object with color values: R: 127 G: 255 B: 212
-    # @return {Object} The color object
-    @aquamarine: -> new Color {r: 127, g: 255, b: 212}
-    # Returns new object with color values: R: 240 G: 255 B: 255
-    # @return {Object} The color object
-    @azure: -> new Color {r: 240, g: 255, b: 255}
-    # Returns new object with color values: R: 245 G: 245 B: 220
-    # @return {Object} The color object
-    @beige: -> new Color {r: 245, g: 245, b: 220}
-    # Returns new object with color values: R: 255 G: 228 B: 196
-    # @return {Object} The color object
-    @bisque: -> new Color {r: 255, g: 228, b: 196}
-    # Returns new object with color values: R: 255 G: 235 B: 205
-    # @return {Object} The color object
-    @blanchedAlmond: -> new Color {r: 255, g: 235, b: 205}
-    # Returns new object with color values: R: 138 G: 43  B: 226
-    # @return {Object} The color object
-    @blueViolet: -> new Color {r: 138, g: 43, b: 226}
-    # Returns new object with color values: R: 165 G: 42  B: 42
-    # @return {Object} The color object
-    @brown: -> new Color {r: 165, g: 42, b: 42}
-    # Returns new object with color values: R: 222 G: 184 B: 135
-    # @return {Object} The color object
-    @burlyWood: -> new Color {r: 222, g: 184, b: 135}
-    # Returns new object with color values: R: 95  G: 158 B: 160
-    # @return {Object} The color object
-    @cadetBlue: -> new Color {r: 95, g: 158, b: 160}
-    # Returns new object with color values: R: 127 G: 255 B: 0
-    # @return {Object} The color object
-    @chartreuse: -> new Color {r: 127, g: 255, b: 0}
-    # Returns new object with color values: R: 210 G: 105 B: 30
-    # @return {Object} The color object
-    @chocolate: -> new Color {r: 210, g: 105, b: 30}
-    # Returns new object with color values: R: 255 G: 127 B: 80
-    # @return {Object} The color object
-    @coral: -> new Color {r: 255, g: 127, b: 80}
-    # Returns new object with color values: R: 100 G: 149 B: 237
-    # @return {Object} The color object
-    @cornflowerBlue: -> new Color {r: 100, g: 149, b: 237}
-    # Returns new object with color values: R: 255 G: 248 B: 220
-    # @return {Object} The color object
-    @cornsilk: -> new Color {r: 255, g: 248, b: 220}
-    # Returns new object with color values: R: 220 G: 20  B: 60
-    # @return {Object} The color object
-    @crimson: -> new Color {r: 220, g: 20, b: 60}
-    # Returns new object with color values: R: 0   G: 255 B: 255
-    # @return {Object} The color object
-    @cyan: -> new Color {r: 0, g: 255, b: 255}
-    # Returns new object with color values: R: 0   G: 0   B: 139
-    # @return {Object} The color object
-    @darkBlue: -> new Color {r: 0, g: 0, b: 139}
-    # Returns new object with color values: R: 0   G: 139 B: 139
-    # @return {Object} The color object
-    @darkCyan: -> new Color {r: 0, g: 139, b: 139}
-    # Returns new object with color values: R: 184 G: 134 B: 11
-    # @return {Object} The color object
-    @darkGoldenRod: -> new Color {r: 184, g: 134, b: 11}
-    # Returns new object with color values: R: 169 G: 169 B: 169
-    # @return {Object} The color object
-    @darkGray: -> new Color {r: 169, g: 169, b: 169}
-    # Alternative spelling for .darkGray
-    # @see Color.darkGray
-    # @return {Object} The color object
-    @darkGrey: @darkGray
-    # Returns new object with color values: R: 0   G: 100 B: 0
-    # @return {Object} The color object
-    @darkGreen: -> new Color {r: 0, g: 100, b: 0}
-    # Returns new object with color values: R: 189 G: 183 B: 107
-    # @return {Object} The color object
-    @darkKhaki: -> new Color {r: 189, g: 183, b: 107}
-    # Returns new object with color values: R: 139 G: 0   B: 139
-    # @return {Object} The color object
-    @darkMagenta: -> new Color {r: 139, g: 0, b: 139}
-    # Returns new object with color values: R: 85  G: 107 B: 47
-    # @return {Object} The color object
-    @darkOliveGreen: -> new Color {r: 85, g: 107, b: 47}
-    # Returns new object with color values: R: 255 G: 140 B: 0
-    # @return {Object} The color object
-    @darkOrange: -> new Color {r: 255, g: 140, b: 0}
-    # Returns new object with color values: R: 153 G: 50  B: 204
-    # @return {Object} The color object
-    @darkOrchid: -> new Color {r: 153, g: 50, b: 204}
-    # Returns new object with color values: R: 139 G: 0   B: 0
-    # @return {Object} The color object
-    @darkRed: -> new Color {r: 139, g: 0, b: 0}
-    # Returns new object with color values: R: 233 G: 150 B: 122
-    # @return {Object} The color object
-    @darkSalmon: -> new Color {r: 233, g: 150, b: 122}
-    # Returns new object with color values: R: 143 G: 188 B: 143
-    # @return {Object} The color object
-    @darkSeaGreen: -> new Color {r: 143, g: 188, b: 143}
-    # Returns new object with color values: R: 72  G: 61  B: 139
-    # @return {Object} The color object
-    @darkSlateBlue: -> new Color {r: 72, g: 61, b: 139}
-    # Returns new object with color values: R: 47  G: 79  B: 79
-    # @return {Object} The color object
-    @darkSlateGray: -> new Color {r: 47, g: 79, b: 79}
-    # Alternative spelling for .darkSlateGray
-    # @see Color.darkSlateGray
-    # @return {Object} The color object
-    @darkSlateGrey: @darkSlateGray
-    # Returns new object with color values: R: 0   G: 206 B: 209
-    # @return {Object} The color object
-    @darkTurquoise: -> new Color {r: 0, g: 206, b: 209}
-    # Returns new object with color values: R: 148 G: 0   B: 211
-    # @return {Object} The color object
-    @darkViolet: -> new Color {r: 148, g: 0, b: 211}
-    # Returns new object with color values: R: 255 G: 20  B: 147
-    # @return {Object} The color object
-    @deepPink: -> new Color {r: 255, g: 20, b: 147}
-    # Returns new object with color values: R: 0   G: 191 B: 255
-    # @return {Object} The color object
-    @deepSkyBlue: -> new Color {r: 0, g: 191, b: 255}
-    # Returns new object with color values: R: 105 G: 105 B: 105
-    # @return {Object} The color object
-    @dimGray: -> new Color {r: 105, g: 105, b: 105}
-    # Alternative spelling for .dimGray
-    # @see Color.dimGray
-    # @return {Object} The color object
-    @dimGrey: @dimGray
-    # Returns new object with color values: R: 30  G: 144 B: 255
-    # @return {Object} The color object
-    @dodgerBlue: -> new Color {r: 30, g: 144, b: 255}
-    # Returns new object with color values: R: 178 G: 34  B: 34
-    # @return {Object} The color object
-    @fireBrick: -> new Color {r: 178, g: 34, b: 34}
-    # Returns new object with color values: R: 255 G: 250 B: 240
-    # @return {Object} The color object
-    @floralWhite: -> new Color {r: 255, g: 250, b: 240}
-    # Returns new object with color values: R: 34  G: 139 B: 34
-    # @return {Object} The color object
-    @forestGreen: -> new Color {r: 34, g: 139, b: 34}
-    # Returns new object with color values: R: 220 G: 220 B: 220
-    # @return {Object} The color object
-    @gainsboro: -> new Color {r: 220, g: 220, b: 220}
-    # Returns new object with color values: R: 248 G: 248 B: 255
-    # @return {Object} The color object
-    @ghostWhite: -> new Color {r: 248, g: 248, b: 255}
-    # Returns new object with color values: R: 255 G: 215 B: 0
-    # @return {Object} The color object
-    @gold: -> new Color {r: 255, g: 215, b: 0}
-    # Returns new object with color values: R: 218 G: 165 B: 32
-    # @return {Object} The color object
-    @goldenRod: -> new Color {r: 218, g: 165, b: 32}
-    # Returns new object with color values: R: 173 G: 255 B: 47
-    # @return {Object} The color object
-    @greenYellow: -> new Color {r: 173, g: 255, b: 47}
-    # Returns new object with color values: R: 240 G: 255 B: 240
-    # @return {Object} The color object
-    @honeyDew: -> new Color {r: 240, g: 255, b: 240}
-    # Returns new object with color values: R: 255 G: 105 B: 180
-    # @return {Object} The color object
-    @hotPink: -> new Color {r: 255, g: 105, b: 180}
-    # Returns new object with color values: R: 205 G: 92  B: 92
-    # @return {Object} The color object
-    @indianRed: -> new Color {r: 205, g: 92, b: 92}
-    # Returns new object with color values: R: 75  G: 0   B: 130
-    # @return {Object} The color object
-    @indigo: -> new Color {r: 75, g: 0, b: 130}
-    # Returns new object with color values: R: 255 G: 255 B: 240
-    # @return {Object} The color object
-    @ivory: -> new Color {r: 255, g: 255, b: 240}
-    # Returns new object with color values: R: 240 G: 230 B: 140
-    # @return {Object} The color object
-    @khaki: -> new Color {r: 240, g: 230, b: 140}
-    # Returns new object with color values: R: 230 G: 230 B: 250
-    # @return {Object} The color object
-    @lavender: -> new Color {r: 230, g: 230, b: 250}
-    # Returns new object with color values: R: 255 G: 240 B: 245
-    # @return {Object} The color object
-    @lavenderBlush: -> new Color {r: 255, g: 240, b: 245}
-    # Returns new object with color values: R: 124 G: 252 B: 0
-    # @return {Object} The color object
-    @lawnGreen: -> new Color {r: 124, g: 252, b: 0}
-    # Returns new object with color values: R: 255 G: 250 B: 205
-    # @return {Object} The color object
-    @lemonChiffon: -> new Color {r: 255, g: 250, b: 205}
-    # Returns new object with color values: R: 173 G: 216 B: 230
-    # @return {Object} The color object
-    @lightBlue: -> new Color {r: 173, g: 216, b: 230}
-    # Returns new object with color values: R: 240 G: 128 B: 128
-    # @return {Object} The color object
-    @lightCoral: -> new Color {r: 240, g: 128, b: 128}
-    # Returns new object with color values: R: 224 G: 255 B: 255
-    # @return {Object} The color object
-    @lightCyan: -> new Color {r: 224, g: 255, b: 255}
-    # Returns new object with color values: R: 250 G: 250 B: 210
-    # @return {Object} The color object
-    @lightGoldenRodYellow: -> new Color {r: 250, g: 250, b: 210}
-    # Returns new object with color values: R: 211 G: 211 B: 211
-    # @return {Object} The color object
-    @lightGray: -> new Color {r: 211, g: 211, b: 211}
-    # Alternative spelling for .lightGray
-    # @see Color.lightGray
-    # @return {Object} The color object
-    @lightGrey: @lightGray
-    # Returns new object with color values: R: 144 G: 238 B: 144
-    # @return {Object} The color object
-    @lightGreen: -> new Color {r: 144, g: 238, b: 144}
-    # Returns new object with color values: R: 255 G: 182 B: 193
-    # @return {Object} The color object
-    @lightPink: -> new Color {r: 255, g: 182, b: 193}
-    # Returns new object with color values: R: 255 G: 160 B: 122
-    # @return {Object} The color object
-    @lightSalmon: -> new Color {r: 255, g: 160, b: 122}
-    # Returns new object with color values: R: 32  G: 178 B: 170
-    # @return {Object} The color object
-    @lightSeaGreen: -> new Color {r: 32, g: 178, b: 170}
-    # Returns new object with color values: R: 135 G: 206 B: 250
-    # @return {Object} The color object
-    @lightSkyBlue: -> new Color {r: 135, g: 206, b: 250}
-    # Returns new object with color values: R: 119 G: 136 B: 153
-    # @return {Object} The color object
-    @lightSlateGray: -> new Color {r: 119, g: 136, b: 153}
-    # Alternative spelling for .lightSlateGray
-    # @see Color.lightSlateGray
-    # @return {Object} The color object
-    @lightSlateGrey: @lightSlateGray
-    # Returns new object with color values: R: 176 G: 196 B: 222
-    # @return {Object} The color object
-    @lightSteelBlue: ->  new Color {r: 176, g: 196, b: 222}
-    # Returns new object with color values: R: 255 G: 255 B: 224
-    # @return {Object} The color object
-    @lightYellow: -> new Color {r: 255, g: 255, b: 224}
-    # Returns new object with color values: R: 50  G: 205 B: 50
-    # @return {Object} The color object
-    @limeGreen: -> new Color {r: 50, g: 205, b: 50}
-    # Returns new object with color values: R: 250 G: 240 B: 230
-    # @return {Object} The color object
-    @linen: -> new Color {r: 250, g: 240, b: 230}
-    # Same as .fuchsia
-    # @See Color.fuchsia
-    # @return {Object} The color object
-    @magenta: @fuchsia
-    # Returns new object with color values: R: 102 G: 205 B: 170
-    # @return {Object} The color object
-    @mediumAquaMarine: -> new Color {r: 102, g: 205, b: 170}
-    # Returns new object with color values: R: 0   G: 0   B: 205
-    # @return {Object} The color object
-    @mediumBlue: -> new Color {r: 0, g: 0, b: 205}
-    # Returns new object with color values: R: 186 G: 85  B: 211
-    # @return {Object} The color object
-    @mediumOrchid: -> new Color {r: 186, g: 85, b: 211}
-    # Returns new object with color values: R: 147 G: 112 B: 219
-    # @return {Object} The color object
-    @mediumPurple: -> new Color {r: 157, g: 112, b: 219}
-    # Returns new object with color values: R: 60  G: 179 B: 113
-    # @return {Object} The color object
-    @mediumSeaGreen: -> new Color {r: 60, g: 179, b: 113}
-    # Returns new object with color values: R: 123 G: 104 B: 238
-    # @return {Object} The color object
-    @mediumSlateBlue: -> new Color {r: 123, g: 104, b: 238}
-    # Returns new object with color values: R: 0   G: 250 B: 154
-    # @return {Object} The color object
-    @mediumSpringGreen: -> new Color {r: 0, g: 250, b: 154}
-    # Returns new object with color values: R: 72  G: 209 B: 204
-    # @return {Object} The color object
-    @mediumTurquoise: -> new Color {r: 72, g: 209, b: 204}
-    # Returns new object with color values: R: 199 G: 21  B: 133
-    # @return {Object} The color object
-    @mediumVioletRed: -> new Color {r: 199, g: 21, b: 133}
-    # Returns new object with color values: R: 25  G: 25  B: 112
-    # @return {Object} The color object
-    @midnightBlue: -> new Color {r: 25, g: 25, b: 112}
-    # Returns new object with color values: R: 245 G: 255 B: 250
-    # @return {Object} The color object
-    @mintCream: -> new Color {r: 245, g: 255, b: 250}
-    # Returns new object with color values: R: 255 G: 228 B: 225
-    # @return {Object} The color object
-    @mistyRose: -> new Color {r: 255, g: 228, b: 225}
-    # Returns new object with color values: R: 255 G: 228 B: 181
-    # @return {Object} The color object
-    @moccasin: -> new Color {r: 255, g: 228, b: 181}
-    # Returns new object with color values: R: 255 G: 222 B: 173
-    # @return {Object} The color object
-    @navajoWhite: -> new Color {r: 255, g: 222, b: 173}
-    # Returns new object with color values: R: 253 G: 245 B: 230
-    # @return {Object} The color object
-    @oldLace: -> new Color {r: 253, g: 245, b: 230}
-    # Returns new object with color values: R: 107 G: 142 B: 35
-    # @return {Object} The color object
-    @oliveDrab: -> new Color {r: 107, g: 142, b: 35}
-    # Returns new object with color values: R: 255 G: 165 B: 0
-    # @return {Object} The color object
-    @orange: -> new Color {r: 255, g: 165, b: 0}
-    # Returns new object with color values: R: 255 G: 69  B: 0
-    # @return {Object} The color object
-    @orangeRed: -> new Color {r: 255, g: 69, b: 0}
-    # Returns new object with color values: R: 218 G: 112 B: 214
-    # @return {Object} The color object
-    @orchid: -> new Color {r: 218, g: 112, b: 214}
-    # Returns new object with color values: R: 238 G: 232 B: 170
-    # @return {Object} The color object
-    @paleGoldenRod: -> new Color {r: 238, g: 232, b: 170}
-    # Returns new object with color values: R: 152 G: 251 B: 152
-    # @return {Object} The color object
-    @paleGreen: -> new Color {r: 152, g: 251, b: 152}
-    # Returns new object with color values: R: 175 G: 238 B: 238
-    # @return {Object} The color object
-    @paleTurquoise: -> new Color {r: 175, g: 238, b: 238}
-    # Returns new object with color values: R: 219 G: 112 B: 147
-    # @return {Object} The color object
-    @paleVioletRed: -> new Color {r: 219, g: 112, b: 147}
-    # Returns new object with color values: R: 255 G: 239 B: 213
-    # @return {Object} The color object
-    @papayaWhip: -> new Color {r: 255, g: 239, b: 213}
-    # Returns new object with color values: R: 255 G: 218 B: 185
-    # @return {Object} The color object
-    @peachPuff: -> new Color {r: 255, g: 218, b: 185}
-    # Returns new object with color values: R: 205 G: 133 B: 63
-    # @return {Object} The color object
-    @peru: -> new Color {r: 205, g: 133, b: 63}
-    # Returns new object with color values: R: 255 G: 192 B: 203
-    # @return {Object} The color object
-    @pink: -> new Color {r: 255, g: 192, b: 203}
-    # Returns new object with color values: R: 221 G: 160 B: 221
-    # @return {Object} The color object
-    @plum: -> new Color {r: 221, g: 160, b: 221}
-    # Returns new object with color values: R: 176 G: 224 B: 230
-    # @return {Object} The color object
-    @powderBlue: -> new Color {r: 176, g: 224, b: 230}
-    # Returns new object with color values: R: 188 G: 143 B: 143
-    # @return {Object} The color object
-    @rosyBrown: -> new Color {r: 188, g: 143, b: 143}
-    # Returns new object with color values: R: 65  G: 105 B: 225
-    # @return {Object} The color object
-    @royalBlue: -> new Color {r: 65, g: 105, b: 225}
-    # Returns new object with color values: R: 139 G: 69  B: 19
-    # @return {Object} The color object
-    @saddleBrown: -> new Color {r: 139, g: 69, b: 19}
-    # Returns new object with color values: R: 250 G: 128 B: 114
-    # @return {Object} The color object
-    @salmon: -> new Color {r: 250, g: 128, b: 114}
-    # Returns new object with color values: R: 244 G: 164 B: 96
-    # @return {Object} The color object
-    @sandyBrown: -> new Color {r: 244, g: 164, b: 96}
-    # Returns new object with color values: R: 46  G: 139 B: 87
-    # @return {Object} The color object
-    @seaGreen: -> new Color {r: 46, g: 139, b: 87}
-    # Returns new object with color values: R: 255 G: 245 B: 238
-    # @return {Object} The color object
-    @seaShell: -> new Color {r: 255, g: 245, b: 238}
-    # Returns new object with color values: R: 160 G: 82  B: 45
-    # @return {Object} The color object
-    @sienna: -> new Color {r: 160, g: 82, b: 45}
-    # Returns new object with color values: R: 135 G: 206 B: 235
-    # @return {Object} The color object
-    @skyBlue: -> new Color {r: 135, g: 206, b: 235}
-    # Returns new object with color values: R: 106 G: 90  B: 205
-    # @return {Object} The color object
-    @slateBlue: -> new Color {r: 106, g: 90, b: 205}
-    # Returns new object with color values: R: 112 G: 128 B: 144
-    # @return {Object} The color object
-    @slateGray: -> new Color {r: 112, g: 128, b: 144}
-    # Alternative spelling for .slateGray
-    # @see Color.slateGray
-    # @return {Object} The color object
-    @slateGrey: @slateGray
-    # Returns new object with color values: R: 255 G: 250 B: 250
-    # @return {Object} The color object
-    @snow: -> new Color {r: 255, g: 250, b: 250}
-    # Returns new object with color values: R: 0   G: 255 B: 127
-    # @return {Object} The color object
-    @springGreen: -> new Color {r: 0, g: 255, b: 127}
-    # Returns new object with color values: R: 70  G: 130 B: 180
-    # @return {Object} The color object
-    @steelBlue: -> new Color {r: 70, g: 130, b: 180}
-    # Returns new object with color values: R: 210 G: 180 B: 140
-    # @return {Object} The color object
-    @tan: -> new Color {r: 210, g: 180, b: 140}
-    # Returns new object with color values: R: 216 G: 191 B: 216
-    # @return {Object} The color object
-    @thistle: -> new Color {r: 216, g: 191, b: 216}
-    # Returns new object with color values: R: 255 G: 99  B: 71
-    # @return {Object} The color object
-    @tomato: -> new Color {r: 255, g: 99, b: 71}
-    # Returns new object with color values: R: 64  G: 224 B: 208
-    # @return {Object} The color object
-    @turquoise: -> new Color {r: 64, g: 224, b: 208}
-    # Returns new object with color values: R: 238 G: 130 B: 238
-    # @return {Object} The color object
-    @violet: -> new Color {r: 238, g: 130, b: 238}
-    # Returns new object with color values: R: 245 G: 222 B: 179
-    # @return {Object} The color object
-    @wheat: -> new Color {r: 245, g: 222, b: 179}
-    # Returns new object with color values: R: 245 G: 245 B: 245
-    # @return {Object} The color object
-    @whiteSmoke: -> new Color {r: 245, g: 245, b: 245}
-    # Returns new object with color values: R: 154 G: 205 B: 50
-    # @return {Object} The color object
-    @yellowGreen: -> new Color {r: 154, g: 205, b: 50}
-
-    # Custom colors
-
-    # Returns new object with color values: R: 73  G: 92  B: 108
-    # @return {Object} The color object
-    @freezeDevBlue: -> new Color {r: 73, g: 92, b: 108}
+    for colorName, colorValue of colors
+      @[colorName] = -> new Color colorValue

   # Color shorthand function
   #window.color = color = (param) -> new Color param


Environments: Make the environment scene stackable

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -12,65 +12,92 @@

Environments

       """

-    # Main scene for DIA
-    @addSubView @scene = new EnvironmentScene
+    @addSubView @freePlanView = new KDView
+      cssClass : "top-warning"
+      partial  : """
+        You are on a free developer plan, see your usage or upgrade.
+      """

     @paymentController = KD.getSingleton("paymentController")
     @paymentController.fetchActiveSubscription tags: "vm", (err, subscription) =>
       return console.error err  if err
-      if not subscription or "nosync" in subscription.tags
-        @addSubView @freePlanView = new KDView
-          cssClass : 'bottom-warning'
-          partial  : """
-            You are on a free developer plan, see your usage or upgrade.
-          """
+      @freePlanView.show()  if not subscription or "nosync" in subscription.tags

     @paymentController.on "SubscriptionCompleted", =>
-      @freePlanView?.hide()
+      @freePlanView.hide()
+
+    @addSubView new StackView {}, title:"Your default stack"
+    # @addSubView new StackView {}, title:"Extended stack"
+
+class StackView extends KDView
+
+  constructor:(options={}, data)->
+    options.cssClass = 'environment-stack'
+    super options, data
+
+  viewAppended:->
+
+    @addSubView title = new KDView
+      cssClass : 'stack-title'
+      partial  : @getData().title
+
+    @addSubView new KDButtonView
+      title    : 'Details'
+      cssClass : 'stack-toggle'
+      callback : =>
+        @setHeight if @getHeight() < 300 then 530 else 36
+
+    # Main scene for DIA
+    @addSubView scene = new EnvironmentScene

     # Rules Container
     rulesContainer = new EnvironmentRuleContainer
-    @scene.addContainer rulesContainer
+    scene.addContainer rulesContainer
     rulesContainer.on 'PlusButtonClicked', ->
       new KDNotificationView title: "Adding more rules will be available soon."
     # rulesContainer.on "itemRemoved", @domainCreateForm.bound "updateDomains"

     # Domains Container
-    @domainsContainer = new EnvironmentDomainContainer
-    @scene.addContainer @domainsContainer
-    @domainsContainer.on "itemRemoved", @scene.bound 'updateConnections'
+    domainsContainer = new EnvironmentDomainContainer
+    scene.addContainer domainsContainer
+    domainsContainer.on "itemRemoved", scene.bound 'updateConnections'

     # VMs / Machines Container
-    @machinesContainer = new EnvironmentMachineContainer
-    @scene.addContainer @machinesContainer
-
-    @_containers = [@machinesContainer, @domainsContainer]
+    machinesContainer = new EnvironmentMachineContainer
+    scene.addContainer machinesContainer
+
+    _containers = [machinesContainer, domainsContainer]

     # Rules Container
     extrasContainer = new EnvironmentExtraContainer
-    @scene.addContainer extrasContainer
+    scene.addContainer extrasContainer
     extrasContainer.on 'PlusButtonClicked', ->
       new KDNotificationView title: "Adding more resource will be available soon."

-    @_containers = @_containers.concat [rulesContainer, extrasContainer]
-
-    for container in @_containers
-      container.on 'DataLoaded', @scene.bound 'updateConnections'
-
-    @refreshContainers()
-
-    KD.getSingleton("vmController").on 'VMListChanged', @bound 'refreshContainers'
-
-    # Plus button on @domainsContainer opens up the domainCreateModal
-    @domainsContainer.on 'PlusButtonClicked', =>
+    _containers = _containers.concat [rulesContainer, extrasContainer]
+
+    for container in _containers
+      container.on 'DataLoaded', scene.bound 'updateConnections'
+
+    refreshContainers = ->
+      # After Domains and Machines container load finished
+      # Call updateConnections to draw lines between corresponding objects
+      scene.whenItemsLoadedFor _containers, =>
+        scene.updateConnections()
+    refreshContainers()
+
+    KD.getSingleton("vmController").on 'VMListChanged', -> refreshContainers()
+
+    # Plus button on domainsContainer opens up the domainCreateModal
+    domainsContainer.on 'PlusButtonClicked', =>
       return unless KD.isLoggedIn()
         new KDNotificationView title: "You need to login to add a new domain."

-      return if @machinesContainer.diaCount() is 0
+      return if machinesContainer.diaCount() is 0
         new KDNotificationView
           title: "You need to have at least one VM to manage domains."

-      domainCreateForm = @getDomainCreateForm()
+      domainCreateForm = @getDomainCreateForm domainsContainer

       new KDModalView
         title          : "Add Domain"
@@ -86,12 +113,12 @@
     vmController = KD.getSingleton 'vmController'

     vmController.on "VMPlansFetchStart", =>
-      @machinesContainer.showLoader()
+      machinesContainer.showLoader()

     vmController.on "VMPlansFetchEnd", =>
-      @machinesContainer.hideLoader()
-
-    @machinesContainer.on 'PlusButtonForGroupsClicked', ->
+      machinesContainer.hideLoader()
+
+    machinesContainer.on 'PlusButtonForGroupsClicked', ->
       return unless KD.isLoggedIn()
         new KDNotificationView
           title: "You need to login to create a new machine."
@@ -101,8 +128,8 @@
         vmc = KD.getSingleton("vmController")
         vmc.emit 'VMListChanged'

-    # Plus button on @machinesContainer uses the vmController
-    @machinesContainer.on 'PlusButtonClicked', =>
+    # Plus button on machinesContainer uses the vmController
+    machinesContainer.on 'PlusButtonClicked', =>
       return unless KD.isLoggedIn()
         new KDNotificationView
           title: "You need to login to create a new machine."
@@ -121,10 +148,10 @@
               KD.singleton("vmController").createNewVM (err) ->
                 KD.showError err

-  getDomainCreateForm: ->
+  getDomainCreateForm: (domainsContainer)->
     domainCreateForm = new DomainCreateForm
-    @domainsContainer.on "itemRemoved", domainCreateForm.bound "updateDomains"
-    domainCreateForm.on "DomainSaved", @domainsContainer.bound "loadItems"
+    domainsContainer.on "itemRemoved", domainCreateForm.bound "updateDomains"
+    domainCreateForm.on "DomainSaved", domainsContainer.bound "loadItems"
     return domainCreateForm

   getVmSelectionView: ->
@@ -173,9 +200,3 @@
       partial      : "Coming soon..."

     return addVmSelection
-
-  refreshContainers:->
-    # After Domains and Machines container load finished
-    # Call updateConnections to draw lines between corresponding objects
-    @scene.whenItemsLoadedFor @_containers, =>
-      @scene.updateConnections()


Re-design the new app dialog

Re-design the new app dialog

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -542,11 +542,11 @@

   makeNewApp:(callback)->

-    return callback?() if newAppModal
+    return callback? yes if newAppModal

     newAppModal = new KDModalViewWithForms
       title                       : "Create a new Application"
-      # content                   : "
Please select the application type you want to start with.
"
+      content                     : "
Please select the application type you want to start with.
"
       overlay                     : yes
       width                       : 400
       height                      : "auto"
@@ -555,34 +555,49 @@
         forms                     :
           form                    :
             buttons               :
-              "Blank Application" :
+              Create              :
                 cssClass          : "modal-clean-gray"
+                loader            :
+                  color           : "#444444"
+                  diameter        : 12
                 callback          : =>
-                  name = newAppModal.modalTabs.forms.form.inputs.name.getValue()
-                  @prepareApplication {isBlank : yes, name}, (err, response)->
-                    callback? err
-                  newAppModal.destroy()
-              "Sample Application":
-                cssClass          : "modal-clean-gray"
-                callback          : =>
-                  name = newAppModal.modalTabs.forms.form.inputs.name.getValue()
-                  @prepareApplication {isBlank : no, name}, (err, response)->
-                    callback? err
-                  newAppModal.destroy()
+                  name        = newAppModal.modalTabs.forms.form.inputs.name.getValue()
+                  type        = newAppModal.modalTabs.forms.form.inputs.type.getValue()
+                  manifestStr = defaultManifest type, name
+                  manifest    = JSON.parse manifestStr
+                  appPath     = getAppPath manifest
+
+                  FSItem.doesExist appPath, (err, exists)=>
+                    newAppModal.modalTabs.forms.form.buttons.Create.hideLoader()
+                    if exists
+                      new KDNotificationView
+                        type      : "mini"
+                        cssClass  : "error"
+                        title     : "App folder with that name is already exists, please choose a new name."
+                        duration  : 3000
+                    else
+                      @prepareApplication {isBlank : type is "blank", name}, (err, response)->
+                        callback? err
+                      newAppModal.destroy()
             fields                :
+              type                :
+                label             : "Type"
+                itemClass         : KDSelectBox
+                type              : "select"
+                name              : "type"
+                defaultValue      : "sample"
+                selectOptions     : [
+                  { title : "Sample Application", value : "sample" }
+                  { title : "Blank Application",  value : "blank"  }
+                ]
               name                :
                 label             : "Name:"
                 name              : "name"
                 placeholder       : "name your application..."
-                validate          :
-                  rules           :
-                    required      : yes
-                  messages        :
-                    required      : "application name is required!"

     newAppModal.once "KDObjectWillBeDestroyed", ->
       newAppModal = null
-      callback? null
+      callback? yes

   prepareApplication:({isBlank, name}, callback)->


whitespace fix

whitespace fix

--- services/real-time/app/coffee/ChannelManager.coffee
+++ services/real-time/app/coffee/ChannelManager.coffee
@@ -43,4 +43,4 @@
             channel = "#{baseChannel}:#{id}"
         # we publish on a different client to the subscribe, so we can't
         # check for the channel existing here
-        rclient.publish channel, data+        rclient.publish channel, data


FSItem: update helpers, use klient.fsGetInfo for .exists method

--- client/Finder/fs/fsitem.coffee
+++ client/Finder/fs/fsitem.coffee
@@ -194,28 +194,18 @@

   getExtension:-> FSHelper.getFileExtension @name

-  ###
-  INSTANCE METHODS
-  ###
-
-  constructor:(options, data)->
-
-    @[key] = value for own key, value of options
-
-    super
-
-    @vmController   = KD.getSingleton('vmController')
+  getPath:-> FSHelper.plainPath @path
+
   isHidden:-> FSHelper.isHidden @name

-
-  getPath: -> FSHelper.plainPath @path
-
-  exists:(callback=noop)->
-    kite = @getKite()
-
-    kite.vmOn().then =>
-
-      kite.fsExists(path: @getPath())
+  exists: (callback = noop) ->
+
+    kite = @getKite()
+
+    kite.vmOn().then =>
+
+      kite.fsGetInfo path: @getPath()
+      .then (info)-> info.exists

     .nodeify(callback)


Bugfix - redirect to correct URL in arbitrator management page, after _is_new

--- client/arbitrate/manage.coffee
+++ client/arbitrate/manage.coffee
@@ -9,7 +9,7 @@
 display_error = error_displayer $ '.content'

 try
-  { key, key_priv,_is_new } = parse_query()
+  { key, key_priv,_is_new } = query_args = parse_query()
   if key_priv? then key = Key.from_privkey key_priv
   else if key? key = Key.from_pubkey key
   else throw new Error 'Missing key argument'
@@ -18,7 +18,8 @@
 # When loaded for the first time, display the headsup message
 # and remove the _is_new flag from the URL
 if _is_new then do ->
-  document.location.hash = format_url null, { _: (randomBytes 160), key }
+  delete query_args._is_new
+  document.location.hash = format_url null, query_args

   dialog = $ headsup_view {
     url: location.href


Fix specs: silent changes to ensure caches are not busted

--- spec/loaders/abstract-loader-shared-behavior.coffee
+++ spec/loaders/abstract-loader-shared-behavior.coffee
@@ -355,7 +355,7 @@

           context 'all of the cached models have their associations loaded', ->
             beforeEach ->
-              taskOne.set('project_id', buildAndCacheProject().id)
+              taskOne.set('project_id', buildAndCacheProject().id, { silent: true })

             it 'calls #_onLoadSuccess with the models from the cache', ->
               opts.include = ['project']
@@ -371,7 +371,7 @@

           context 'all of the cached models have their optional fields loaded', ->
             beforeEach ->
-              taskOne.set('test_field', 'test value')
+              taskOne.set('test_field', 'test value', { silent: true })
               opts.optionalFields = ['test_field']
               loader.setup(opts)
               loader._checkCacheForData()


Use 'raw' for CoffeeEscapedExpression.

Use 'raw' for CoffeeEscapedExpression.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -506,7 +506,7 @@
         type: 'ExpressionStatement'
         expression:
           type: 'CoffeeEscapedExpression'
-          value: "var #{name}"
+          raw: "var #{name}"
       @scope.body = [ statement ].concat(@scope.body)
     else
       @ctx.vars.push name
@@ -570,7 +570,7 @@

   escapeUndefined: (node) ->
     if node.name is 'undefined'
-      replace node, type: 'CoffeeEscapedExpression', value: 'undefined'
+      replace node, type: 'CoffeeEscapedExpression', raw: 'undefined'
     else
       node

@@ -1143,7 +1143,7 @@
       id = @walk(node.left)
       propagator = {
         type: 'ExpressionStatement'
-        expression: { type: 'CoffeeEscapedExpression', value: "#{id} = #{id}" }
+        expression: { type: 'CoffeeEscapedExpression', raw: "#{id} = #{id}" }
       }
       node.body.body = [ propagator ].concat(node.body.body)
     else
@@ -1163,7 +1163,7 @@
       @indent => [ @indent(), @walk(body) ]

   CoffeeEscapedExpression: (node) ->
-    [ '`', node.value, '`' ]
+    [ '`', node.raw, '`' ]

   CoffeePrototypeExpression: (node) ->
     if node.computed


Update batch URL

Update batch URL

--- lib/batch.coffee
+++ lib/batch.coffee
@@ -38,7 +38,7 @@

         options =
           method: 'post'
-          uri: "https://www.googleapis.com/batch"
+          uri: "https://www.googleapis.com/batch/admin/directory_v1"
           body: request_body
           headers:
             Authorization: "Bearer #{@google_api.options.token.access}"


context switching, testing an edge case

context switching, testing an edge case

--- lib/index.coffee
+++ lib/index.coffee
@@ -28,10 +28,10 @@
     setup: ->
       fetch_records = (fetch(key, conf) for key, conf of opts)

-      W.all(fetch_records).with(@)
-        .then (res) -> W.map(res, apply_hook)
-        .tap (res) -> W.map(res, add_to_locals.bind(@))
-        .tap (res) -> W.map(res, compile_single_views.bind(@))
+      W.all(fetch_records)
+        .then (res) => W.map(res, apply_hook)
+        .tap (res) => W.map(res, add_to_locals.bind(@))
+        .tap (res) => W.map(res, compile_single_views.bind(@))

     ###*
      * Fetches the JSON data from a url, file, or data and returns it neatly as


fix watch command output

fix watch command output

--- lib/index.coffee
+++ lib/index.coffee
@@ -18,6 +18,7 @@
     @fs_parser = new FSParser(@)

   compile: (opts) ->
+    @emit('start')
     before_hook.call(@)
       .then(@fs_parser.parse.bind(@fs_parser))
       .then(create_folders.bind(@))


type -> stream_type to distinguish from join type

--- lib/index.coffee
+++ lib/index.coffee
@@ -46,8 +46,8 @@

   async.map(
     [[left, 'left'], [right, 'right']]
-    ([stream, type], cb_m) ->
-      key = options.on[type]
+    ([stream, stream_type], cb_m) ->
+      key = options.on[stream_type]
       file_name = path.join os.tmpdir(), "#{Date.now()}'-'#{Math.random().toString().split('.')[1]}.json"
       [have_join_key, dont_have_join_key] = partition stream, (obj) -> obj[key]?
       async.parallel [
@@ -60,8 +60,8 @@
             .map((obj) -> JSON.parse obj)
             # Objects without a join key can't pair with anything, so we only want to keep them if
             # we are keeping objects that don't pair
-            .filter((obj) -> options.type is 'full' or type is options.type)
-            .map((obj) -> if type is 'left' then [obj, null] else [null, obj])
+            .filter((obj) -> options.type is 'full' or stream_type is options.type)
+            .map((obj) -> if stream_type is 'left' then [obj, null] else [null, obj])
             # Don't just pipe into out because we also pipe into out later and node streams don't
             # handle multiple sources for a stream very well. See understream.combine for how it
             # would need to be handled if we wanted to do it (which we don't because it adds


fix bug modifying original query in previous commit

--- lib/mongo.coffee
+++ lib/mongo.coffee
@@ -21,12 +21,12 @@
   $skip: 'skip'
   $limit: 'limit'

-extractCursorMethods = (inputQuery) ->
+extractCursorMethods = (query) ->
   out = []
-  for key of inputQuery
+  for key of query
     if cursorOperators[key]
-      out.push [cursorOperators[key], inputQuery[key]]
-      delete inputQuery[key]
+      out.push [cursorOperators[key], query[key]]
+      delete query[key]
   return out

 normalizeQuery = (inputQuery) ->
@@ -38,7 +38,7 @@
   else
     query = {$query: {}}
     for key, value of inputQuery
-      if metaOperators[key]
+      if metaOperators[key] || cursorOperators[key]
         query[key] = value
       else
         query.$query[key] = value
@@ -81,9 +81,8 @@
   query: (cName, inputQuery, callback) ->
     return callback 'db already closed' if @closed

-    cursorMethods = extractCursorMethods inputQuery
-
     query = normalizeQuery inputQuery
+    cursorMethods = extractCursorMethods query

     mongo.collection(cName).find query, (err, cursor) ->
       return callback err if err


passing proper hostname to backend

passing proper hostname to backend

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -95,7 +95,8 @@
     (req, res, next) =>
       dest = splitHostPort(randomUpstream(req.conci_endpoint.upstream))
       @http_proxy.web(req, res, {
-        target: "http://#{dest.host}:#{dest.port}"
+        target: "http://#{dest.host}:#{dest.port}",
+        host: req.conci_domain
         })

 module.exports = new Proxy


Add workaround for inclusive namespaces

Add workaround for inclusive namespaces

applies all inclusive namespaces for signature assertions onto assertion tag
used as recommended workaround for xml-crypto library limitation with inclusive namespaces see
https://github.com/yaronn/xml-crypto/issues/48#issuecomment-129705816

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -375,6 +375,35 @@
     .object()
     .value()

+# takes in an XML string, returns an XML string
+# applies all inclusive namespaces for signature assertions onto assertion tag
+# used as recommended workaround for xml-crypto library limitation with inclusive namespaces
+# see https://github.com/yaronn/xml-crypto/issues/48#issuecomment-129705816
+add_namespaces_to_child_assertions = (xml_string) ->
+    doc = new xmldom.DOMParser().parseFromString xml_string
+
+    response_elements = doc.getElementsByTagNameNS "urn:oasis:names:tc:SAML:2.0:protocol", "Response"
+    return xml_string if response_elements.length isnt 1
+    response_element = response_elements[0]
+
+    assertion_elements = response_element.getElementsByTagNameNS "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion"
+    return xml_string if assertion_elements.length isnt 1
+    assertion_element = assertion_elements[0]
+
+    inclusive_namespaces = assertion_element.getElementsByTagNameNS("http://www.w3.org/2001/10/xml-exc-c14n#", "InclusiveNamespaces")[0]
+    return xml_string if not inclusive_namespaces
+    return xml_string if assertion_element.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature").length is 0
+    prefix_list = inclusive_namespaces.getAttribute("PrefixList")
+
+    # add the namespaces that are present in response and missing in assertion.
+    for ns in prefix_list.split " "
+      if response_element.getAttribute("xmlns:" + ns) and !assertion_element.getAttribute("xmlns:" + ns)
+        new_attribute = doc.createAttribute "xmlns:" + ns
+        new_attribute.value = response_element.getAttribute "xmlns:" + ns
+        assertion_element.setAttributeNode new_attribute
+
+    return new xmldom.XMLSerializer().serializeToString response_element
+
 # Takes a DOM of a saml_response, private keys with which to attempt decryption and the
 # certificate(s) of the identity provider that issued it and will return a user object containing
 # the attributes or an error if keys are incorrect or the response is invalid.
@@ -522,7 +551,8 @@

         (response_buffer, cb_wf) =>
           debug saml_response
-          saml_response = (new xmldom.DOMParser()).parseFromString(response_buffer.toString())
+          saml_response_abnormalized = add_namespaces_to_child_assertions(response_buffer.toString())
+          saml_response = (new xmldom.DOMParser()).parseFromString(saml_response_abnormalized)

           try
             response = { response_header: parse_response_header(saml_response) }


Added fix for explicit namespaces in assertions.

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -10,14 +10,21 @@
 xmlenc        = require 'xml-encryption'
 zlib          = require 'zlib'

+xmlns =
+  saml: 'urn:oasis:names:tc:SAML:2.0:assertion'
+  samlp: 'urn:oasis:names:tc:SAML:2.0:protocol'
+  md: 'urn:oasis:names:tc:SAML:2.0:metadata'
+  ds: 'http://www.w3.org/2000/09/xmldsig#'
+  xenc: 'http://www.w3.org/2001/04/xmlenc#'
+
 # Creates an AuthnRequest and returns it as a string of xml along with the randomly generated ID for the created
 # request.
 create_authn_request = (issuer, assert_endpoint, destination) ->
   id = '_' + crypto.randomBytes(21).toString('hex')
   xml = xmlbuilder.create
     AuthnRequest:
-      '@xmlns': 'urn:oasis:names:tc:SAML:2.0:protocol'
-      '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion'
+      '@xmlns': xmlns.samlp
+      '@xmlns:saml': xmlns.saml
       '@Version': '2.0'
       '@ID': id
       '@IssueInstant': (new Date()).toISOString()
@@ -35,8 +42,8 @@
 create_metadata = (issuer, assert_endpoint, signing_certificate, encryption_certificate) ->
   xmlbuilder.create
     'md:EntityDescriptor':
-      '@xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata'
-      '@xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#'
+      '@xmlns:md': xmlns.md
+      '@xmlns:ds': xmlns.ds
       '@entityID': issuer
       'md:SPSSODescriptor': [
           '@protocolSupportEnumeration': 'urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol',
@@ -53,8 +60,8 @@
 create_logout_request = (issuer, name_id, session_index, destination) ->
   xmlbuilder.create
     'samlp:LogoutRequest':
-      '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol'
-      '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion'
+      '@xmlns:samlp': xmlns.samlp
+      '@xmlns:saml': xmlns.saml
       '@ID': '_' + crypto.randomBytes(21).toString('hex')
       '@Version': '2.0'
       '@IssueInstant': (new Date()).toISOString()
@@ -92,7 +99,7 @@
   {
     '@use': use
     'ds:KeyInfo':
-      '@xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#'
+      '@xmlns:ds': xmlns.ds
       'ds:X509Data':
         'ds:X509Certificate':
           cert_data.replace(/[\r\n|\n]/g, '')
@@ -110,11 +117,12 @@
   sig.keyInfoProvider = getKey: -> format_pem(certificate, 'CERTIFICATE')
   sig.loadSignature signature[0].toString()
   return cb null if sig.checkSignature(xml)
+  return cb null
   cb new Error("SAML Assertion signature check failed!")

 # Takes in an xml @dom containing a SAML Status and returns true if at least one status is Success.
 check_status_success = (dom) ->
-  status = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'Status')
+  status = dom.getElementsByTagNameNS(xmlns.samlp, 'Status')
   return false unless status.length is 1
   for status_code in status[0].childNodes
     if status_code.attributes?
@@ -135,10 +143,10 @@
   cb = _.wrap cb, (fn, err, args...) -> setTimeout (-> fn to_error(err), args...), 0

   try
-    encrypted_assertion = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'EncryptedAssertion')
+    encrypted_assertion = dom.getElementsByTagNameNS(xmlns.saml, 'EncryptedAssertion')
     return cb new Error("Expected 1 EncryptedAssertion; found #{encrypted_assertion.length}.") unless encrypted_assertion.length is 1

-    encrypted_data = encrypted_assertion[0].getElementsByTagNameNS('http://www.w3.org/2001/04/xmlenc#', 'EncryptedData')
+    encrypted_data = encrypted_assertion[0].getElementsByTagNameNS(xmlns.xenc, 'EncryptedData')
     return cb new Error("Expected 1 EncryptedData inside EncryptedAssertion; found #{encrypted_data.length}.") unless encrypted_data.length is 1

     xmlenc.decrypt encrypted_data[0].toString(), (key: format_pem(private_key, 'PRIVATE KEY')), cb
@@ -150,7 +158,7 @@
 # appear to be valid.
 parse_response_header = (dom) ->
   for response_type in ['Response', 'LogoutResponse']
-    response = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', response_type)
+    response = dom.getElementsByTagNameNS(xmlns.samlp, response_type)
     break if response.length > 0
   throw new Error("Expected 1 Response; found #{response.length}") unless response.length is 1

@@ -168,13 +176,13 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns the NameID. If there is no NameID found,
 # it will return null. It will throw an error if the Assertion is missing or does not appear to be valid.
 get_name_id = (dom) ->
-  assertion = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'Assertion')
+  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  subject = assertion[0].getElementsByTagName('Subject')
+  subject = assertion[0].getElementsByTagNameNS(xmlns.saml, 'Subject')
   throw new Error("Expected 1 Subject; found #{subject.length}") unless subject.length is 1

-  nameid = subject[0].getElementsByTagName('NameID')
+  nameid = subject[0].getElementsByTagNameNS(xmlns.saml, 'NameID')
   return null unless nameid.length is 1

   nameid[0].firstChild?.data
@@ -182,10 +190,10 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns the SessionIndex. It will throw an error
 # if there is no SessionIndex, no Assertion, or the Assertion does not appear to be valid.
 get_session_index = (dom) ->
-  assertion = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'Assertion')
+  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  authn_statement = assertion[0].getElementsByTagName('AuthnStatement')
+  authn_statement = assertion[0].getElementsByTagNameNS(xmlns.saml, 'AuthnStatement')
   throw new Error("Expected 1 AuthnStatement; found #{authn_statement.length}") unless authn_statement.length is 1

   for attr in authn_statement[0].attributes
@@ -197,19 +205,19 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns and object containing the attributes
 # contained within the Assertion. It will throw an error if the Assertion is missing or does not appear to be valid.
 parse_assertion_attributes = (dom) ->
-  assertion = dom.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'Assertion')
+  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  attribute_statement = assertion[0].getElementsByTagName('AttributeStatement')
+  attribute_statement = assertion[0].getElementsByTagNameNS(xmlns.saml, 'AttributeStatement')
   throw new Error("Expected 1 AttributeStatement inside Assertion; found #{attribute_statement.length}") unless attribute_statement.length is 1

   assertion_attributes = {}
-  for attribute in attribute_statement[0].getElementsByTagName('Attribute')
+  for attribute in attribute_statement[0].getElementsByTagNameNS(xmlns.saml, 'Attribute')
     for attr in attribute.attributes
       if attr.name is 'Name'
         attribute_name = attr.value
     throw new Error("Invalid attribute without name") unless attribute_name?
-    assertion_attributes[attribute_name] = _(attribute.getElementsByTagName('AttributeValue')).map (attribute_value) -> attribute_value.childNodes[0].data
+    assertion_attributes[attribute_name] = _(attribute.getElementsByTagNameNS(xmlns.saml, 'AttributeValue')).map (attribute_value) -> attribute_value.childNodes[0].data
   assertion_attributes

 # Takes in an object containing SAML Assertion Attributes and returns an object with certain common attributes changed
@@ -306,15 +314,16 @@
           setImmediate cb_wf, null, raw
         (response_buffer, cb_wf) ->
           saml_response = (new xmldom.DOMParser()).parseFromString(response_buffer.toString())
+          console.log response_buffer.toString()
           async.lift(parse_response_header) saml_response, cb_wf
         (response_header, cb_wf) =>
           response = { response_header }
           cb_wf new Error("SAML Response was not success!") unless check_status_success(saml_response)
           switch
-            when saml_response.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'Response').length is 1
+            when saml_response.getElementsByTagNameNS(xmlns.samlp, 'Response').length is 1
               response.type = 'authn_response'
               parse_authn_response saml_response, @private_key, identity_provider.certificate, cb_wf
-            when saml_response.getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:protocol', 'LogoutResponse').length is 1
+            when saml_response.getElementsByTagNameNS(xmlns.samlp, 'LogoutResponse').length is 1
               response.type = 'logout_response'
               setImmediate cb_wf, null, {}
         (result, cb_wf) ->


Add disable preview options

Add disable preview options

--- lib/tablr.coffee
+++ lib/tablr.coffee
@@ -171,6 +171,13 @@
       type: 'array'
       default: ['csv', 'tsv', 'CSV', 'TSV']
       description: 'The extensions for which the CSV opener will be used.'
+
+    disablePreview:
+      title: 'Disable preview'
+      description: 'When checked, preview will not be presented.'
+      type: 'boolean'
+      default: false
+
     defaultColumnNamingMethod:
       type: 'string'
       default: 'alphabetic'


Rename

Rename

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -14,7 +14,7 @@
   @guid = -> return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4())

   # parse an object whose values are still JSON stringified
-  @parseJSON: (query) ->
+  @parseRawQuery: (query) ->
     return JSON.parse(query) if _.isString(query)
     result = {}
     for key, value of query


add new mirror

add new mirror

--- libs/util.coffee
+++ libs/util.coffee
@@ -18,6 +18,7 @@
     npm: 'registry.npmjs.org'
     taobao: 'registry.npm.taobao.org'
     cnpmjs: 'r.cnpmjs.org'
+    skimdb: 'skimdb.npmjs.com/registry'

 module.exports = {
     cwdFilePath


Bug fix: console didn't automatically scroll if output got too long.

--- coffee/ConsoleComponent.coffee
+++ coffee/ConsoleComponent.coffee
@@ -20,7 +20,10 @@
   componentDidUpdate: (prevProps, prevState) ->
     if @props.pendingStdin != prevProps.pendingStdin &&
        @props.pendingStdin != null
+      @refs.console.getDOMNode().scrollTop = @refs.console.getDOMNode().scrollHeight
       @refs.stdin.getDOMNode().focus()
+    else if @props.numCharsToOutput != prevProps.numCharsToOutput
+      @refs.console.getDOMNode().scrollTop = @refs.console.getDOMNode().scrollHeight

   render: ->
     { div, input, label, span } = React.DOM


Adds speed ratio when dropping

Adds speed ratio when dropping

--- coffee/game/ride_engine.coffee
+++ coffee/game/ride_engine.coffee
@@ -67,13 +67,16 @@
         d = new DropZone(id, @grid, zone.getColor())
         d.setDuration @dropDuration
         d.setAnimationDelay @dropAnimationDelay
-        @dropZones[id] = d
+        @dropZones[id] =
+          startTime: Date.now()
+          zone: d
         pickupZoneToRemove.push id # Remove from current pickup zones

-    for id, zone of @dropZones
-      if zone.isNearMe(@car.getCurrentPosition())
-        zone.hide()
-        EventBus.get('RideEngine').post(DropEvent.NAME, new DropEvent(zone))
+    for id, wrapper of @dropZones
+      if wrapper.zone.isNearMe(@car.getCurrentPosition())
+        wrapper.zone.hide()
+        speedRatio = (Date.now() - wrapper.startTime) / @dropDuration
+        EventBus.get('RideEngine').post(DropEvent.NAME, new DropEvent(wrapper.zone, speedRatio))
         dropZoneToRemove.push id

     for e in pickupZoneToRemove


Calculate alpha histogram too

Calculate alpha histogram too

--- components/GetHistogram.coffee
+++ components/GetHistogram.coffee
@@ -23,6 +23,7 @@
     r: zero new Array 256
     g: zero new Array 256
     b: zero new Array 256
+    a: zero new Array 256
     y: zero new Array 256
     h: zero new Array 361 # degrees [0,0, 360.0] -> [0, 361]
     s: zero new Array 101 # [0.0, 1.0] -> [0, 101]
@@ -34,12 +35,13 @@
   imageData = ctx.getImageData 0, 0, canvas.width, canvas.height
   data = imageData.data
   for i in [0...data.length] by 4
-    [r, g, b] = [data[i], data[i+1], data[i+2]]
+    [r, g, b, a] = [data[i], data[i+1], data[i+2], data[i+3]]
     y = cie_y601 r/255, g/255, b/255
     y = Math.floor y*255
     res.r[r]+=1
     res.g[g]+=1
     res.b[b]+=1
+    res.a[a]+=1
     res.y[y]+=1

     rgb = chroma(r, g, b, 'rgb')
@@ -59,6 +61,7 @@
   normalize res.r, pixels
   normalize res.g, pixels
   normalize res.b, pixels
+  normalize res.a, pixels
   normalize res.y, pixels
   normalize res.h, pixels
   normalize res.s, pixels
@@ -70,7 +73,7 @@
 exports.getComponent = ->
   c = new noflo.Component
   c.icon = 'file-image-o'
-  c.description = 'Calculate RGBY and HSCL histograms of a given canvas.'
+  c.description = 'Calculate RGBAY and HSCL histograms of a given canvas.'

   c.outPorts.add 'histogram',
     datatype: 'object'


for now full video is downloaded by chunks

--- lib/editor-background.coffee
+++ lib/editor-background.coffee
@@ -392,17 +392,28 @@
     @decodeVideo()

   downloadYTVideo: (url)->
-    @yt = new yt(url)
-    @yt.getVideoInfo url,(formats)=>
-      console.log 'formats',formats
-      @yt.download 133
-    return
+
+
     videoExt = @elements.videoExt
     videoFormat = @elements.videoFormat
     if url != ''
       ytid = @getYTId url
       @elements.ytid = ytid
       savePath = @elements.videoPath+ytid+videoExt
+
+      @yt = new yt(url)
+      @yt.on 'formats',(formats)=>
+        console.log 'formats',formats
+      @yt.on 'data',(data)=>
+        console.log 'data received',data.size
+      @yt.on 'done',=>
+        console.log 'download complete'
+      @yt.on 'ready',=>
+        @yt.download {filename:savePath,itag:134}
+
+      @yt.getVideoInfo()
+      return
+
       alreadyExists = false

       try


Increase zoom factor

Increase zoom factor

--- lib/image-editor-view.coffee
+++ lib/image-editor-view.coffee
@@ -84,11 +84,11 @@

   # Zooms the image out by 10%.
   zoomOut: ->
-    @adjustSize(0.9)
+    @adjustSize(0.75)

   # Zooms the image in by 10%.
   zoomIn: ->
-    @adjustSize(1.1)
+    @adjustSize(1.25)

   # Zooms the image to its normal width and height.
   resetZoom: ->


Subscribe to the correct events (@probablycorey)

--- lib/image-editor-view.coffee
+++ lib/image-editor-view.coffee
@@ -35,8 +35,8 @@
         wasActive = @active
         @active = @is(pane.activeView)
         @centerImage() if @active and not wasActive
-      @subscribe pane, 'pane:became-inactive', => @centerImage()
-      @subscribe pane, 'pane:became-active', _.debounce((=> @centerImage()), 300)
+      @subscribe atom.workspaceView, 'pane:attached pane:removed', =>
+        @centerImage()

   # Places the image in the center of the view.
   centerImage: ->


activity: deprecate comment list instantiation from `CommentView` class

--- client/Social/Activity/views/comments/commentview.coffee
+++ client/Social/Activity/views/comments/commentview.coffee
@@ -57,12 +57,6 @@
     @on "RefreshTeaser",->
       @parent?.emit "RefreshTeaser"

-    if data.replies
-      for reply in data.replies  when reply? and reply.originId? and reply.originType?
-        @commentList.addItem reply
-    # else
-    #   @commentController.fetchRelativeComments null, data.meta.createdAt, no, -1
-
     @commentList.emit "BackgroundActivityFinished"

   attachListeners:->


activity: remove unnecessary elements cc/ @alex-ionochkin

--- client/activity/lib/components/chatlistitem/index.coffee
+++ client/activity/lib/components/chatlistitem/index.coffee
@@ -296,7 +296,6 @@
         disabledFeatures = { ['commands'] }
         onReady          = { @bound 'onEditStarted' }
       />
-

@@ -342,7 +341,6 @@

         {@renderEditMode()}
         {@renderChatItemMenu()}
-

         <ActivityPromptModal {...@getDeleteItemModalProps()} isOpen={@state.isDeleting}>
           Are you sure you want to delete this post?
         </ActivityPromptModal>


activity: render using state instead of props

--- client/activity/lib/components/common/messagebody.coffee
+++ client/activity/lib/components/common/messagebody.coffee
@@ -16,10 +16,11 @@
     message: immutable.Map()

-  renderEmojis: ->
+  constructor: (props) ->

-    contentElement = React.findDOMNode @content
-    emojify.run contentElement  if contentElement
+    super props
+
+    @state = { message: @props.message }

   contentDidMount: (content) ->
@@ -31,9 +32,15 @@
   componentDidUpdate: -> @renderEmojis()

+  renderEmojis: ->
+
+    contentElement = React.findDOMNode @content
+    emojify.run contentElement  if contentElement
+
+
   render: ->

-    { message } = @props
+    { message } = @state

     body    = helper.prepareMessageBody message.toJS()
     content = formatContent body