Copy menu bug fix first commit

Copy menu bug fix first commit

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -502,18 +502,19 @@
                     return pay

     is_deleted: (project_id) =>
-        return !!@getIn(['project_map', project_id, 'deleted'])
+        return !!@getIn(['project_map', project_id, 'deleted']) #getIn returns nested data form list pointers. e.g. 'project_map' points to project_id, which points to 'deleted.

     is_hidden_from: (project_id, account_id) =>
         return !!@getIn(['project_map', project_id, 'users', account_id, 'hide'])

     get_project_select_list: (current, show_hidden=true) =>
         map = @get('project_map')
+        console.log(map)
         if not map?
             return
         account_id = webapp_client.account_id
         list = []
-        if current? and map.has(current)
+        if current? and map.has(current)   #current is for current project
             list.push(id:current, title:map.get(current).get('title'))
             map = map.delete(current)
         v = map.toArray()
@@ -525,8 +526,11 @@
             return 0
         others = []
         for i in v
-            if not i.deleted and (show_hidden or not i.get('users').get(account_id).get('hide'))
+            console.log("(not (i.get('deleted') == 'true' ))")
+            console.log((not (i.get('deleted') == true )))
+            if (not (i.get('deleted') == true )) and (show_hidden or not i.get('users').get(account_id).get('hide'))
                 others.push(id:i.get('project_id'), title:i.get('title'))
+                console.log("i.get('title') was just pushed to list")
         list = list.concat others
         return list


sagews: fix %auto bug

sagews: fix %auto bug

--- src/smc-webapp/sagews.coffee
+++ src/smc-webapp/sagews.coffee
@@ -1417,17 +1417,6 @@
                             when 'output'
                                 cell.set_cell_flag(FLAGS.hide_output)

-        if mesg.auto?
-            if opts.mark?
-                line = opts.mark.find()?.from.line
-                if line?
-                    cell = @cell(line)
-                    if cell?
-                        if mesg.auto
-                            cell.set_cell_flag(FLAGS.auto)
-                        else
-                            cell.remove_cell_flag(FLAGS.auto)
-
         # NOTE: Right now the "state object" is a just a list of messages in the output of a cell. It's viewed as something that should get rendered in order, with no dependence between them. Instead alll thoose messages should get fed into one single state object, which then gets rendered each time it changes. React makes that approach easy and efficient. Without react (or something similar) it is basically impossible.  When sage worksheets are rewritten using react, this will change.
         if mesg.clear
             line = opts.mark.find()?.from.line


Mark TODOs

Mark TODOs

--- src/controller.coffee
+++ src/controller.coffee
@@ -101,14 +101,14 @@
     @options.mode = @options.mode.replace /$\/ace\/mode\//, ''

     if @options.mode of modes
-      @mode = new modes[@options.mode] @options.modeOptions
+      @mode = new modes[@options.mode] @options.modeOptions # TODO session
     else
-      @mode = new coffee @options.modeOptions
+      @mode = new coffee @options.modeOptions # TODO session

     @draw = new draw.Draw()

     # No gutter decorations to start
-    @gutterDecorations = {}
+    @gutterDecorations = {} # TODO session

     # ## DOM Population
     # This stage of ICE Editor construction populates the given wrapper
@@ -191,12 +191,12 @@
     # Set up event bindings before creating a view
     @bindings = {}

-    # Instantiate an ICE editor view
-    @view = new view.View @standardViewSettings
-    @paletteView = new view.View helper.extend {}, @standardViewSettings, {
+    # Instantiate an Droplet editor view
+    @view = new view.View @standardViewSettings # TODO session
+    @paletteView = new view.View helper.extend {}, @standardViewSettings, { # TODO session
       showDropdowns: @options.showDropdownInPalette ? false
     }
-    @dragView = new view.View @standardViewSettings
+    @dragView = new view.View @standardViewSettings # TODO session

     boundListeners = []

@@ -258,7 +258,7 @@

     # ## Document initialization
     # We start of with an empty document
-    @tree = new model.Document()
+    @tree = new model.Document() # TODO session

     @resizeBlockMode()

@@ -278,7 +278,7 @@
     modeClass = modes[mode]
     if modeClass
       @options.mode = mode
-      @mode = new modeClass modeOptions
+      @mode = new modeClass modeOptions # TODO session
     else
       @options.mode = null
       @mode = null
@@ -334,8 +334,8 @@
     @resizeDragCanvas()

     # Re-scroll and redraw main
-    @scrollOffsets.main.y = @mainScroller.scrollTop
-    @scrollOffsets.main.x = @mainScroller.scrollLeft
+    @scrollOffsets.main.y = @mainScroller.scrollTop # TODO session
+    @scrollOffsets.main.x = @mainScroller.scrollLeft # TODO session

     @mainCtx.setTransform 1, 0, 0, 1, -@scrollOffsets.main.x, -@scrollOffsets.main.y

@@ -753,9 +753,9 @@
 # We must declare a few
 # fields a populate time
 hook 'populate', 0, ->
-  @undoStack = []
-  @redoStack = []
-  @changeEventVersion = 0
+  @undoStack = [] # TODO session
+  @redoStack = [] # TODO session
+  @changeEventVersion = 0 # TODO session

 # Now we hook to ctrl-z to undo.
 hook 'keydown', 0, (event, state) ->


Fix bug with pixel-shift hack

Fix bug with pixel-shift hack

--- src/controller.coffee
+++ src/controller.coffee
@@ -1359,7 +1359,9 @@
             drag.style.transform = "translate(0px, 0px)"

           # Clear the drag canvas
-          @dragCtx.clearRect 0, 0, drag.width, drag.height
+          # Note that we clear from -1, -1 because of our pixel-shift hack
+          # to allow the border in.
+          @dragCtx.clearRect -1, -1, drag.width, drag.height

           # If we inserted into root, move the cursor to the end of the selection.
           if highlight?


Segment move undo bug fix

Segment move undo bug fix

--- src/controller.coffee
+++ src/controller.coffee
@@ -661,7 +661,7 @@
               segment = new model.Segment()

               @tree.getTokenAtLocation(operation.start).insertBefore segment.start
-              @tree.getTokenAtLocation(operation.end).insert segment.end
+              @tree.getTokenAtLocation(operation.end).insertBefore segment.end

               moveCursorToRaw segment.end


Fixed typo.

Fixed typo.

--- app/assets/javascripts/app/controllers/agent_ticket_view.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_view.js.coffee
@@ -444,7 +444,7 @@
     @key = '#ticket/view/' + @view

     # get data
-    if window.LastRefresh[ @key ] && @start_page is 1
+    if window.LastRefresh[ @key ]
       @tickets_count = window.LastRefresh[ @key ].tickets_count
       @tickets       = window.LastRefresh[ @key ].tickets
       @redirect()


Switch document pointers to document indices in undo stack

--- src/controller.coffee
+++ src/controller.coffee
@@ -697,7 +697,7 @@
       @performFloatingOperation(operation, 'backward')
     else
       @getDocuments()[operation.document].perform(
-        operation.operation, 'backward', [@cursor]
+        operation.operation, 'backward', (if operation.document is 0 then [@cursor] else [])
       ) unless operation is 'CAPTURE'

   @popUndo()
@@ -729,7 +729,7 @@
       @performFloatingOperation(operation, 'forward')
     else
       @getDocuments()[operation.document].perform(
-        operation.operation, 'forward', [@cursor]
+        operation.operation, 'forward', (if operation.document is 0 then [@cursor] else [])
       ) unless operation is 'CAPTURE'

   @popRedo()
@@ -753,7 +753,7 @@
   dropletDocument = node.getDocument()

   if dropletDocument?
-    operation = node.getDocument().remove node, [@cursor]
+    operation = node.getDocument().remove node, (if dropletDocument is @tree then [@cursor] else [])
     @pushUndo {operation, document: @getDocuments().indexOf(dropletDocument)}

     # Remove the floating dropletDocument if it is now
@@ -784,7 +784,7 @@

   if dropletDocument?
     @prepareNode node, container
-    operation = dropletDocument.insert location, node, [@cursor]
+    operation = dropletDocument.insert location, node, (if dropletDocument is @tree then [@cursor] else [])
     @pushUndo {operation, document: @getDocuments().indexOf(dropletDocument)}
     return operation
   else


DRY up subscribe and subscribeToCommand

DRY up subscribe and subscribeToCommand

subscribeToCommand now supports unsubscribing by object.

--- src/stdlib/subscriber.coffee
+++ src/stdlib/subscriber.coffee
@@ -1,23 +1,24 @@
 _ = require 'underscore'

 module.exports =
-  subscribe: (eventEmitter, subscribeArgs...) ->
-    eventEmitter.on(subscribeArgs...)
+  subscribeWith: (eventEmitter, methodName, args) ->
+    eventEmitter[methodName](args...)

     @subscriptions ?= []
     @subscriptionsByObject ?= new WeakMap
     @subscriptionsByObject.set(eventEmitter, []) unless @subscriptionsByObject.has(eventEmitter)

-    eventName = _.first(subscribeArgs)
-    callback = _.last(subscribeArgs)
+    eventName = _.first(args)
+    callback = _.last(args)
     subscription = cancel: -> eventEmitter.off eventName, callback
     @subscriptions.push(subscription)
     @subscriptionsByObject.get(eventEmitter).push(subscription)

-  subscribeToCommand: (view, eventName, callback) ->
-    view.command eventName, callback
-    @subscriptions ?= []
-    @subscriptions.push(cancel: -> view.off eventName, callback)
+  subscribe: (eventEmitter, args...) ->
+    @subscribeWith(eventEmitter, 'on', args)
+
+  subscribeToCommand: (eventEmitter, args...) ->
+    @subscribeWith(eventEmitter, 'command', args)

   unsubscribe: (object) ->
     if object?


chunk tf output

chunk tf output

--- src/tf.coffee
+++ src/tf.coffee
@@ -164,7 +164,15 @@
       else if error
         msg.send {room: msg.message.user.name}, "error:\n```\n#{error}\n```"
       if stdout
-        msg.send {room: msg.message.user.name}, "```\n#{stdout}\n```"
+        if stdout.length < 1024
+          return msg.send {room: msg.message.user.name}, "```\n#{stdout}\n```"
+        out = []
+        for line in stdout.split "\n"
+          if line.match /^\+\s/
+            msg.send {room: msg.message.user.name}, "```\n#{out.join "\n"}\n```"
+            out = []
+          out.push line
+        msg.send {room: msg.message.user.name}, "```\n#{out.join "\n"}\n```"

   robot.respond /tf env ([^\s]+) set ([^\s]+)=(.+)$/i, (msg) ->
     unless robot.auth.isAdmin(msg.envelope.user) or robot.auth.hasRole(msg.envelope.user,tfRole)


hide convlist labels when no starred convs

--- src/ui/views/convlist.coffee
+++ src/ui/views/convlist.coffee
@@ -46,11 +46,11 @@
                 ev.preventDefault()
                 action 'selectConv', c

+        starred = (c for c in convs when conv.isStarred(c))
+        others = (c for c in convs when not conv.isStarred(c))
         div class: 'starred', ->
-            div class: 'label', 'Favorites'
-            starred = (c for c in convs when conv.isStarred(c))
+            div class: 'label', 'Favorites' if starred.length > 0
             starred.forEach renderConv
         div class: 'others', ->
-            div class: 'label', 'Recent'
-            others = (c for c in convs when not conv.isStarred(c))
+            div class: 'label', 'Recent' if starred.length > 0
             others.forEach renderConv


made delay symmetric, removed debug

made delay symmetric, removed debug

--- js-library/app/js/models/fact.coffee
+++ js-library/app/js/models/fact.coffee
@@ -42,12 +42,15 @@
 class AttentionSpan
   constructor: (@options={})->
     @_has_attention = false
+
   attend: ->
     clearTimeout @losing_attention_timeout
-    console.info 'attending'
-    @gained_attention()
+    @gaining_attention_timeout = setTimeout =>
+      @gained_attention()
+    , 300
+
   neglect: ->
-    console.info 'neglecting'
+    clearTimeout @gaining_attention_timeout
     @losing_attention_timeout = setTimeout =>
       @lost_attention()
     , 300
@@ -55,12 +58,10 @@
   has_attention: -> @_has_attention

   lost_attention: ->
-    console.info 'lost attention'
     @_has_attention = false
     @options.lost_attention?()

   gained_attention: ->
-    console.info 'gained attention'
     @_has_attention = true
     @options.gained_attention?()


Use apply_coupon_code route helper in backend/adjustments.js

--- backend/app/assets/javascripts/spree/backend/adjustments.js.coffee
+++ backend/app/assets/javascripts/spree/backend/adjustments.js.coffee
@@ -3,7 +3,7 @@
     return if $("#coupon_code").val().length == 0
     $.ajax
       type: 'PUT'
-      url: Spree.url(Spree.routes.orders_api + '/' + order_number + '/apply_coupon_code.json');
+      url: Spree.url(Spree.routes.apply_coupon_code(order_number))
       data:
         coupon_code: $("#coupon_code").val()
       success: ->


CustomViews: Method separation.

CustomViews: Method separation.

--- client/Dashboard/views/customviews/customviewsdashboardview.coffee
+++ client/Dashboard/views/customviews/customviewsdashboardview.coffee
@@ -57,11 +57,13 @@
       @reloadViews()
       @addNewButton.show()

-  addNew: (data) ->
+  hideViews: ->
     customView.hide() for customView in @customViews
     @noViewLabel.hide()
     @addNewButton.hide()

+  addNew: (data) ->
+    @hideViews()
     appManager = KD.singleton "appManager"
     appManager.require "Teamwork", (app) =>
       config     =


fixed extra row in nested bubbles

fixed extra row in nested bubbles

--- src/viz/types/bubbles.coffee
+++ src/viz/types/bubbles.coffee
@@ -26,7 +26,7 @@
     columns     = Math.ceil(Math.sqrt(dataLength * screenRatio))
     rows        = Math.ceil(Math.sqrt(dataLength / screenRatio))

-  rows-- while (rows - 1) * columns >= vars.data.viz.length  if dataLength > 0
+  rows-- while (rows - 1) * columns >= dataLength if dataLength > 0

   column_width  = vars.width.viz / columns
   column_height = vars.height.viz / rows


Fixed _subCommentsAddView not being a function

--- core/app/backbone/views/sub_comments/sub_comments_list_view.coffee
+++ core/app/backbone/views/sub_comments/sub_comments_list_view.coffee
@@ -11,7 +11,7 @@
   onShow: ->
     if Factlink.Global.signed_in
       @_subCommentsAddView ?= new SubCommentsAddView(addToCollection: @collection)
-      @$('.js-region-sub-comments-form').html @_subCommentsAddView().render().el
+      @$('.js-region-sub-comments-form').html @_subCommentsAddView.render().el

   onClose: ->
     @_subCommentsAddView?.close()


Make activity default sorting to prevent loading weirdness

--- static/js/member-list.coffee
+++ static/js/member-list.coffee
@@ -132,7 +132,7 @@

         # Just to have something in the sort order,
         # this will be overriden once we get data
-        @set_sort_order "name"
+        @set_sort_order "recent_activity"

     _calculate_rankings: (collection) =>
         activity_scores = (model.attributes.stats['recent_activity'] \


FIX: Add missing units.states module

FIX: Add missing units.states module

--- src/app/units/states/states.coffee
+++ src/app/units/states/states.coffee
@@ -0,0 +1,5 @@
+angular.module('doubtfire.units.states', [
+  'doubtfire.units.states.edit-unit-view'
+  'doubtfire.units.states.teacher-view'
+  'doubtfire.units.states.units-admin-view'
+])


Adiciona comportamentos do Price

Adiciona comportamentos do Price

--- src/coffee/portal-price.coffee
+++ src/coffee/portal-price.coffee
@@ -1,18 +1,71 @@
 # DEPENDENCIES:
 # jQuery
+# Dust
+# vtex.utils

 $ = window.jQuery

+
+# DUST FILTERS
+_.extend dust.filters,
+	intAsCurrency: (value) -> _.intAsCurrency value
+
 # CLASSES
 class Price
-	constructor: (@element, @productId, @productData, @options) ->
+	constructor: (@element, @productId, @options) ->
+		@originalHTML = @element.html()
 		@sku = null
-
 		@init()

 	init: =>
+		@render()
 		@bindEvents()
-		@update()
+
+	render: =>
+		if @sku
+			dust.render 'price', @sku, (err, out) =>
+				throw new Error "Price Dust error: #{err}" if err
+				@element.html out
+				@update()
+		else
+			@element.html @originalHTML
+
+	update: =>
+		@hideAll()
+		if @sku.available
+				@showBestPrice()
+
+				if @sku.bestPrice? and @sku.bestPrice < @sku.listPrice
+					@showListPrice()
+					@showSavings()
+
+				if @sku.installments? and @sku.installments > 1
+					@showInstallments()
+				else
+					@showCashPrice()
+
+	findBestPrice: => @element.find('.price-best-price')
+	hideBestPrice: => @findBestPrice().hide()
+	showBestPrice: => @findBestPrice().show()
+	findListPrice: => @element.find('.price-list-price')
+	hideListPrice: => @findListPrice().hide()
+	showListPrice: => @findListPrice().show()
+	findSavings: => @element.find('.price-savings')
+	hideSavings: => @findSavings().hide()
+	showSavings: => @findSavings().show()
+	findInstallments: => @element.find('.price-installments')
+	hideInstallments: => @findInstallments().hide()
+	showInstallments: => @findInstallments().show()
+	findCashPrice: => @element.find('.price-cash')
+	hideCashPrice: => @findCashPrice().hide()
+	showCashPrice: => @findCashPrice().show()
+
+	hideAll: =>
+		@hideBestPrice()
+		@hideListPrice()
+		@hideSavings()
+		@hideInstallments()
+		@hideCashPrice()

 	bindEvents: =>
 		$(window).on 'vtex.sku.selected', @skuSelected
@@ -22,25 +75,31 @@
 		productId == @productId

 	skuSelected: (evt, productId, sku) =>
-		return unless check(productId)
+		return unless @check(productId)
+		@sku = sku
+		@render()

 	skuUnselected: (evt, productId, selectableSkus) =>
-		return unless check(productId)
-
+		return unless @check(productId)
+		@sku = null
+		@render()

 # PLUGIN ENTRY POINT
-$.fn.price = (productId, productData, jsOptions) ->
+$.fn.price = (productId, jsOptions) ->
 	defaultOptions = $.extend {}, $.fn.price.defaults
 	for element in this
 		$element = $(element)
 		domOptions = $element.data()
 		options = $.extend(true, defaultOptions, domOptions, jsOptions)
 		unless $element.data('price')
-			$element.data('price', new Price($element, productId, productData, options))
+			$element.data('price', new Price($element, productId, options))

 	return this

 # PLUGIN DEFAULTS
-$.fn.price.defaults = {}
+$.fn.price.defaults =
+	useOriginalHTML: true
+	useOriginalSku: false
+	originalSku: null


For-sure fixed prod server build

For-sure fixed prod server build

--- includes/webpack-prod-server-config.coffee
+++ includes/webpack-prod-server-config.coffee
@@ -8,7 +8,7 @@
 happyThreadPool = HappyPack.ThreadPool size: 2

 webpackConfig =
-	entry: main: entryFile
+	entry: entryFile
 	output:
 		filename: 'backend.js'
 		libraryTarget: 'commonjs2'


Closes #60 (fixed some bugs, better logging, verified it works).

--- src/lib/config.coffee
+++ src/lib/config.coffee
@@ -138,6 +138,8 @@
     ]
     , (x) -> fs.existsSync x

+    # we can't access `dnschain.globals.gLogger` here because it hasn't
+    # been defined yet unfortunately.
     if dnscConf
         console.info "[INFO] Loading DNSChain config from: #{dnscConf}"
         nconf.file 'user', {file: dnscConf, format: props}
@@ -150,22 +152,26 @@
         chains:
             dnschain: nconf.defaults defaults
         add: (name, paths, type) ->
+            log = dnschain.globals.gLogger
             return if config.chains[name]?
             paths = [paths] unless Array.isArray(paths)
             type = confTypes[type] || confTypes['JSON']

             # if dnschain's config specifies this chain's config path, prioritize it
             # fixes: https://github.com/okTurtles/dnschain/issues/60
-            if config.chains.dnschain.get("#{name}:config")?
-                paths.push config.chains.dnschain.get "#{name}:config"
+            customConfigPath = config.chains.dnschain.get "#{name}:config"
+            if customConfigPath?
+                paths = [customConfigPath]
+                log.info "custom config path for #{name}: #{paths[0]}"

             conf = (new nconf.Provider()).argv().env()
             confFile = _.find paths, (x) -> fs.existsSync x

             if confFile
+                log.info "#{name} configuration path: #{confFile}"
                 conf.file 'user', {file: confFile, format: type}
             else
-                console.warn "[WARN] Couldn't find #{name} configuration:\n%j".bold.yellow, paths
+                log.warn "Couldn't find #{name} configuration:".bold.yellow, paths

             # if dnschain's config specifies this chain's config information, use it as default
             if config.chains.dnschain.get("#{name}")?


how did this even

how did this even

--- resources/assets/coffee/main.coffee
+++ resources/assets/coffee/main.coffee
@@ -36,11 +36,8 @@
 @accountEdit ?= new AccountEdit
 @accountEditPlaystyle ?= new AccountEditPlaystyle
 @accountEditAvatar ?= new AccountEditAvatar
-<<<<<<< HEAD
+@beatmapsetDownloadObserver ?= new BeatmapsetDownloadObserver
 @changelogChartLoader ?= new ChangelogChartLoader
-=======
-@beatmapsetDownloadObserver ?= new BeatmapsetDownloadObserver
->>>>>>> upstream/master
 @checkboxValidation ?= new CheckboxValidation
 @currentUserObserver ?= new CurrentUserObserver
 @editorZoom ?= new EditorZoom


Fix deserializing params: "+" now replaced with a space

--- src/marbles/history.js.coffee
+++ src/marbles/history.js.coffee
@@ -59,7 +59,7 @@
     for q in query
       [key,val] = q.split('=')
       continue unless val
-      val = decodeURIComponent(val)
+      val = decodeURIComponent(val).replace('+', ' ') # + doesn't decode
       val = val.split(',') if val.indexOf(',') != -1
       params[key] = val
     params


readding stuff that git merge had messed up without telling me

Former-commit-id: 3a1d6874626bf0e0042a0593d14af031e8d886cc [formerly 744d126496e25063d03ead8cf98d472ca2d217a0] [formerly 205eb1667dd226af27deca88d937c479d969e115 [formerly b7d44332c5fbe27faf7fc4447a30768a212f31a1]] [formerly f9606d9ba0fed6377a7411003ee32f047a73dca0 [formerly 430d089d081b4a03476de8f41c37ce9f56b60fb8] [formerly ef275e0faed8a263c5b8c1eb8265a55ec1f4d9e0 [formerly ef275e0faed8a263c5b8c1eb8265a55ec1f4d9e0 [formerly f7ea06fb32117d2bfb328af1397a678e9d02f885]]]]
Former-commit-id: a8323ae0b8b744e80480d46fd31de855fba5561a [formerly ba9ee5790bb9964e5e99e6816cc2345dd1df386d] [formerly c66e8fdf5a5eaeed1ff6b7aabd2097f9ba9bcf79 [formerly ecf943b78ec8eece7053037408051ed168538c5e]]
Former-commit-id: ff8570c8673aa6c4cfe4de7643487cbd8d80ec5c [formerly a3baa7b7524657ffa3eabd313f2d5508d9fdf009]
Former-commit-id: 050dc286b8d88aba9e421ba1e0858e800785bbd9
Former-commit-id: b0abc0870b31cf27333790e6bec35bb21e15e05a

--- @client/franklin.coffee
+++ @client/franklin.coffee
@@ -251,6 +251,8 @@
     draw_handle = (can_opine not in [Permission.DISABLED, \
                           Permission.INSUFFICIENT_PRIVILEGES]) || \
                           your_opinion.published
+
+    other_heading = customization "point_labels.#{if mode == 'results' then 'top' else "other"}_header"

     DIV key:@props.slug,

@@ -349,6 +351,48 @@
                       loadPage('/')
                   'Delete'

+              if current_user.is_super_admin
+                SPAN
+                  style:
+                    padding: 10
+
+                  onMouseEnter: =>
+                    @local.copy_to_subdomain = true
+                    save @local
+                  onMouseLeave: =>
+                    @local.copy_to_subdomain = false
+                    save @local
+
+                  A
+                    style: {color: '#888'}
+                    'Copy to subdomain'
+
+                  if @local.copy_to_subdomain
+                    subdomains = fetch('/subdomains').subs
+                    hues = getNiceRandomHues subdomains?.length
+
+                    UL
+                      style:
+                        display: 'inline'
+                      for sub, idx in subdomains
+                        LI
+                          style:
+                            display: 'inline-block'
+                            listStyle: 'none'
+                          A
+                            href: "/proposal/#{@proposal.id}/copy_to/#{sub.id}"
+                            'data-nojax': false
+                            style:
+                              padding: "4px 8px"
+                              fontSize: 18
+                              backgroundColor: hsv_to_rgb(hues[idx], .7, .5)
+                              color: 'white'
+                              display: 'inline-block'
+                            sub.name
+
+
+
+
           if @local.edit_roles
             DIV
               style:
@@ -454,7 +498,9 @@
           #community cons
           CommunityPoints
             key: 'cons'
-            heading: "#{if mode == 'results' then 'Top' else "Others'"} #{capitalize(customization('point_labels.cons'))}"
+            heading: other_heading
+                        .replace('--valences--', capitalize(customization('point_labels.cons')))
+                        .replace('--valence--', capitalize(customization('point_labels.con')))
             newpoint_threshold: newpoint_threshold
             points_draggable: mode == 'crafting'
             points: buildPointsList \
@@ -487,7 +533,9 @@
           #community pros
           CommunityPoints
             key: 'pros'
-            heading: "#{if mode == 'results' then 'Top' else "Others'"} #{capitalize(customization('point_labels.pros'))}"
+            heading: other_heading
+                        .replace('--valences--', capitalize(customization('point_labels.pros')))
+                        .replace('--valence--',  capitalize(customization('point_labels.pro')))
             newpoint_threshold: newpoint_threshold
             points_draggable: mode == 'crafting'
             points: buildPointsList \
@@ -1060,14 +1108,14 @@
       fontWeight: 700
       color: if @proposal.has_focus != 'opinion' then "#eee" else focus_blue

-    heading = "Give Your " + \
-              capitalize(customization('point_labels.' + @props.valence))
-
+    heading = customization("point_labels.your_header")
+                .replace('--valences--', capitalize(customization("point_labels.#{@props.valence}")))
+                .replace('--valence--', capitalize(customization("point_labels.#{@props.valence}")))

     # drop target defs
     dt_stroke_width = 1
     dt_w = POINT_CONTENT_WIDTH + 6
-    dt_h = 63
+    dt_h = 73
     s_w = 8
     s_h = 6

@@ -1382,7 +1430,7 @@
               fontWeight: if browser.high_density_display then '300' else '400'

             "No " + \
-            customization('point_labels.' + @props.key.substring(0, @props.key.length - 1) ) + \
+            customization('point_labels.' + @props.key ) + \
             " given"

@@ -2497,8 +2545,6 @@
                   customization('point_labels.pro')
                 else
                   customization('point_labels.con')
-              " point "
-              BR null
               '(or question) for this proposal'

             UL


Fix a typo

Fix a typo

--- lib/adapters/vera/VeraAdapter.coffee
+++ lib/adapters/vera/VeraAdapter.coffee
@@ -100,7 +100,7 @@
       qs:  Object.assign(options, {id: reqType, 'output_format': 'json'})
     request requestOptions, (err, res, body) =>
       if err?
-        @log 'warn', "Request (#{reqType} failed: #{err}"
+        @log 'warn', "Request (#{reqType}) failed: #{err}"
         deferred.reject err
       else
         deferred.resolve JSON.parse(body)


selectdate can receive focus so this override is not necessary.

--- lib/components/eui-selectdate.coffee
+++ lib/components/eui-selectdate.coffee
@@ -183,8 +183,4 @@
         return startDate.twix(endDate, true).format formatting

-  # Error check should happen without user having to focus on component
-
-  isEntered: true
-
 `export default select`


Add variables to CODAP when node is added to graph

[#156474508]

--- src/code/stores/graph-store.coffee
+++ src/code/stores/graph-store.coffee
@@ -227,6 +227,8 @@
     unless @hasNode node
       @nodeKeys[node.key] = node
       @_graphUpdated()
+      # add variable to CODAP
+      CodapConnect.instance(DEFAULT_CONTEXT_NAME)._createMissingDataAttributes()
       @updateListeners()

   _removeNode: (node) ->


Don't allow Mark buttons to slide off the viewport


CORS header settings (instead of gaping wide open)

--- server/packages/schema/index.coffee
+++ server/packages/schema/index.coffee
@@ -24,11 +24,9 @@
 	# } DRY,
 	serveJson = (res, code, data) ->

-		# http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
-		# `TODO`: Settings-defined headers are what actually make sense here.
-		res.set
-			'Access-Control-Allow-Origin': '*'
-			'Access-Control-Allow-Headers': 'X-Requested-With'
+		# CORS policy enforcement.
+		corsHeaders = nconf.get 'packageSettings:schema:corsHeaders'
+		res.set corsHeaders if corsHeaders?

 		res.json code, data

@@ -149,6 +147,13 @@
 					interceptError res
 				).done()

+# ## Implements hook `packageSettings`
+exports.$packageSettings = ->
+
+	# [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
+	# headers.
+	corsHeaders: null
+
 # ## Implements hook `replContext`
 #
 # Provide the database schema to the REPL context.


paths start with slash

paths start with slash

--- vpn.coffee
+++ vpn.coffee
@@ -11,7 +11,7 @@
 ),
   name: "home"

-Router.route "driver_lines"
+Router.route "/driver_lines"
 Router.route "/driver_lines/:_id", (->
   @render "driver_line_show",
     data: ->
@@ -19,7 +19,7 @@
 ),
   name: "driver_line_show"

-Router.route "neuron_types"
+Router.route "/neuron_types"
 Router.route "/neuron_types/:_id", (->
   @render "neuron_type_show",
     data: ->
@@ -27,7 +27,7 @@
 ),
   name: "neuron_type_show"

-Router.route "neuropils"
+Router.route "/neuropils"
 Router.route "/neuropils/:_id", (->
   @render "neuropil_show",
     data: ->
@@ -35,7 +35,7 @@
 ),
   name: "neuropil_show"

-Router.route "binary_data"
+Router.route "/binary_data"
 Router.route "/binary_data/:_id", (->
   @render "binary_data_show",
     data: ->
@@ -43,9 +43,9 @@
 ),
   name: "binary_data_show"

-Router.route "RecentChanges"
+Router.route "/RecentChanges"

-Router.route "Search", ->
+Router.route "/Search", ->
   @render "Search",
     data: ->
       @params


Change default context directory from `data` to `fixtures`

--- src/middleware.coffee
+++ src/middleware.coffee
@@ -40,7 +40,7 @@
             return next() unless req.file and req.handler

             if req.handler.mime.output is 'text/html'
-                espy.findFor req.file.path, (context) ->
+                espy.findFor req.file.path, 'fixtures', (context) ->
                     set = req.query.context
                     if set
                         req.context = context[set]


fix(initial-sync): Fix sync sidebar showing all the time

--- internal_packages/notifications/lib/sidebar/initial-sync-activity.cjsx
+++ internal_packages/notifications/lib/sidebar/initial-sync-activity.cjsx
@@ -29,8 +29,12 @@
     incomplete = 0

     for acctId, state of @state.sync
-      for model, modelState of state
-        incomplete += 1 unless modelState.complete
+      for modelName of NylasSyncStatusStore.ModelsForSync
+        modelState = state[modelName]
+        continue unless modelState
+
+        if not modelState.complete
+          incomplete += 1
         if modelState.count
           count += modelState.count / 1
           fetched += modelState.fetched / 1


clean up alignment

clean up alignment

--- js/src/service/SharingClient.coffee
+++ js/src/service/SharingClient.coffee
@@ -24,7 +24,7 @@
   CryptoServiceLoader     = require 'kryptnostic.crypto-service-loader'
   CryptoServiceMarshaller = require 'kryptnostic.crypto-service-marshaller'

-  log     = Logger.get('SharingClient')
+  log = Logger.get('SharingClient')

   { validateId, validateUuids } = validators


Safeguard assignment

Safeguard assignment

--- src/property_validator.coffee
+++ src/property_validator.coffee
@@ -101,5 +101,5 @@

   removeRequiredProperty: (key) ->
-    @requiredProperties[key] = undefined
+    @requiredProperties?[key] = undefined


修改“没有模板”提示句

修改“没有模板”提示句

--- lib/settings/panels-code-view.coffee
+++ lib/settings/panels-code-view.coffee
@@ -97,7 +97,7 @@
                 #   $('.' + projectName).find('.loading-mask').removeClass('hidden')
                 #   util.updateRepo(fileDir, success)
       else
-        @templatesList.html '
没有找到任何框架
'
+        @templatesList.html '
没有找到任何模板
'

   addNewCode: ->
     addNewFramework.activate();


Added DoNotTrack

Added DoNotTrack

--- node/tracking.coffee
+++ node/tracking.coffee
@@ -118,6 +118,12 @@
 	set:

 		visit: (req, res) ->
+
+			if req.headers?.dnt? is 1
+
+				# Do not track
+				res.json 400, { error: 'DoNotTrack is enabled. Ackee ignores you.', details: null }
+				return false

 			parse.visits req, ->

@@ -160,6 +166,12 @@

 		duration: (req, res) ->

+			if req.headers?.dnt? is 1
+
+				# Do not track
+				res.json 400, { error: 'DoNotTrack is enabled. Ackee ignores you.', details: null }
+				return false
+
 			parse.duration req, ->

 				timezoneOffset	= (+new Date().getTimezoneOffset()) * 60


fix get-label

fix get-label

--- src/render/test_render.coffee
+++ src/render/test_render.coffee
@@ -23,7 +23,7 @@
     contents = god(rx, dict)
     render(contents)

-  get_label = (tag) -> tag.attr.class.split(" ")[0]
+  get_label = (tag) -> tag.attr.class[0]

   test "rx_mock exists", (t) ->
     t.ok world.T, 'Has HTML Tags'


Removed bad spacing

Removed bad spacing

--- src/scripts/virustotal.coffee
+++ src/scripts/virustotal.coffee
@@ -35,7 +35,7 @@
             vt_json = JSON.parse(body)

             if vt_json.response_code == 1
-              summary = """ VirusTotal Result: #{vt_json.resource}
+              summary = """VirusTotal Result: #{vt_json.resource}
               - Scanned at: #{vt_json.scan_date}
               - Results:    #{vt_json.positives}/#{vt_json.total}
               - Link:       #{vt_json.permalink}
@@ -63,7 +63,7 @@
             vt_json = JSON.parse(body)

             if vt_json.response_code == 1
-              summary = """ VirusTotal URL Result: #{vt_json.url}
+              summary = """VirusTotal URL Result: #{vt_json.url}
               - Scanned at: #{vt_json.scan_date}
               - Results:    #{vt_json.positives}/#{vt_json.total}
               - Link:       #{vt_json.permalink}
@@ -93,7 +93,7 @@

               if vt_json.response_code == 1

-                summary = """ VirusTotal IP Result: #{ip}
+                summary = """VirusTotal IP Result: #{ip}
                 - Detected Communicating Samples: #{vt_json.detected_communicating_samples.length}
                 - Detected URLs #{vt_json.detected_urls.length}


[up-poll] aborts requests (needs tests)

[up-poll] aborts requests (needs tests)

--- lib/assets/javascripts/unpoly/radio.coffee
+++ lib/assets/javascripts/unpoly/radio.coffee
@@ -57,11 +57,15 @@
   # TODO: Docs for up.radio.startPolling()
   startPolling = (element, options = {}) ->
     interval = options.interval ? e.numberAttr(element, 'up-interval') ? config.pollInterval
+
     timer = null
     running = true

+    lastRequest = null
+    options.onQueued = ({ request }) -> lastRequest = request
+
     doReload = ->
-      if document.hidden
+      if isPollingPaused()
         doSchedule()
       else
         u.always(up.reload(element, options), doSchedule)
@@ -73,8 +77,12 @@
     doSchedule()

     return ->
-      running = false     # Stop scheduling if we're waiting for a response
-      clearTimeout(timer) # Stop a scheduled timer
+      running = false      # Don't execute the up.always() handler
+      lastRequest?.abort() # Abort any pending request
+      clearTimeout(timer)  # Stop a scheduled timer
+
+  isPollingPaused = ->
+    return document.hidden

   ###**
   Elements with an `[up-poll]` attribute are [reloaded](/up.reload) from the server periodically.


Track atom.styles event subscriptions

Track atom.styles event subscriptions

--- lib/deprecation-cop-status-bar-view.coffee
+++ lib/deprecation-cop-status-bar-view.coffee
@@ -28,8 +28,8 @@
     userStylesheetPath = atom.styles.getUserStyleSheetPath()
     stylesChanged = (element) =>
       @updateDeprecatedSelectorCount() if element.getAttribute('source-path') is userStylesheetPath
-    atom.styles.onDidUpdateStyleElement(stylesChanged)
-    atom.styles.onDidAddStyleElement(stylesChanged)
+    @subscriptions.add atom.styles.onDidUpdateStyleElement(stylesChanged)
+    @subscriptions.add atom.styles.onDidAddStyleElement(stylesChanged)

   destroy: ->
     @subscriptions.dispose()


fixes getting nested children

fixes getting nested children

--- src/shared/corporation.coffee
+++ src/shared/corporation.coffee
@@ -46,9 +46,8 @@
     (@subsidiaries || []).concat(@divisions || [])
   allChildren: ->
     @children().reduce (a, child) ->
-      a.concat child.allChildren()
-      a.concat child
-    , []
+      (a.concat child.allChildren()).concat child
+    , [@]
   allBrands: ->
     @allChildren().concat(@).reduce (arr, child) ->
       arr.concat child.brands || []


Add TAPi18n.__ to slashcommand params

Add TAPi18n.__ to slashcommand params

--- packages/rocketchat-ui-message/message/popup/messagePopupConfig.coffee
+++ packages/rocketchat-ui-message/message/popup/messagePopupConfig.coffee
@@ -145,7 +145,7 @@
 					if command.indexOf(filter) > -1
 						commands.push
 							_id: command
-							params: item.params
+							params: TAPi18n.__ item.params
 							description: TAPi18n.__ item.description

 				commands = commands.sort (a, b) ->


clarify that the options for commercial ui elements are 'yes' or 'no'

--- src/smc-util/db-schema.coffee
+++ src/smc-util/db-schema.coffee
@@ -817,7 +817,7 @@
         desc    : "Email address that user is directed to use for support requests"
         default : "help@sagemath.com"
     commercial:
-        name    : "Commercial UI elements"
+        name    : "Commercial UI elements ('yes' or 'no')"
         desc    : "Whether or not to include user interface elements related to for-pay upgrades and features.  Set to 'yes' to include these elements."
         default : "no"


webapp/console: proper error reporting for init file creation

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -31,6 +31,7 @@
 misc = require('smc-util/misc')
 {copy, filename_extension, required, defaults, to_json, uuid, from_json} = require('smc-util/misc')
 {redux} = require('./smc-react')
+{alert_message} = require('./alerts')

 misc_page = require('./misc_page')

@@ -667,7 +668,7 @@
                     err_on_exit : false
                     cb          : (err, output) =>
                         if err
-                            console.log("ERROR creating initfile:", err)
+                            alert_message(type:'error', message:"problem creating initfile: #{err}")
                         else
                             @opts.editor?.editor.project_page.open_file(path:initfn)


refactor TeX generation proper into separate method


FIX: Provide correct user to user settings modal in header ctrl

--- src/app/common/header/header.coffee
+++ src/app/common/header/header.coffee
@@ -23,7 +23,7 @@
     units.length == 1 || unit.role == "Tutor"

   $scope.openUserSettings = () ->
-    UserSettingsModal.show(user)
+    UserSettingsModal.show $scope.currentUser

   $scope.openNotificationSettings = ->
     UserNotificationSettingsModal.show $scope.currentUser


Changed the default naming conventions of routes

--- src/app/mixins/model-manager.coffee
+++ src/app/mixins/model-manager.coffee
@@ -2,9 +2,9 @@
   modelName: ''
   humanModelName: ''

-  afterSaveRoute: (-> @get('modelName')).property('modelName')
-  afterCancelRoute: (-> Ember.String.pluralize(@get('modelName'))).property('modelName')
-  afterDestroyRoute: (-> Ember.String.pluralize(@get('modelName'))).property('modelName')
+  afterSaveRoute: (-> @get('modelName').underscore()).property('modelName')
+  afterCancelRoute: (-> @get('modelName').underscore().pluralize()).property('modelName')
+  afterDestroyRoute: (-> @get('modelName').underscore().pluralize()).property('modelName')

   removeConfirmationTitle: '确认删除'
   removeConfirmationMessage: (->


Remove isPreInstalled attribute

Remove isPreInstalled attribute

--- src/beautifiers/terraformfmt.coffee
+++ src/beautifiers/terraformfmt.coffee
@@ -8,7 +8,6 @@
 module.exports = class Terraformfmt extends Beautifier
   name: "terraformfmt"
   link: "https://www.terraform.io/docs/commands/fmt.html"
-  isPreInstalled: false

   options: {
     Terraform: false
@@ -36,4 +35,4 @@
       ])
       .then(=>
         @readFile(tempFile)
-      )+      )


Save window state when atom.exit() is called

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -213,6 +213,7 @@
       @deleteSocketFile()

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


Update feed url

Update feed url

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

   # Private: Enable updates unless running from a local build of Atom.
   setupAutoUpdater: ->
-    autoUpdater.setFeedUrl "http://localhost:9393/releases/latest?version=#{@version}"
+    autoUpdater.setFeedUrl "https://atom.io/api/updates?version=#{@version}"

     autoUpdater.on 'checking-for-update', =>
       @applicationMenu.showInstallUpdateItem(false)


file editor now stores the file format also

--- app/assets/javascripts/type_station/editables/file_editor.js.coffee
+++ app/assets/javascripts/type_station/editables/file_editor.js.coffee
@@ -21,14 +21,14 @@
     .bind 'cloudinarydone', (e, data) ->
       $element = $("##{$(this).data('elementId')}")
       for imageTag in $('.ts-editable-image-tag', $element)
-        $(imageTag).attr('src', $.cloudinary.url(data.result.public_id, $(imageTag).data()))
+        $(imageTag).attr('src', $.cloudinary.url(data.result.public_id, $.extend({}, {format: data.result.format}, $(imageTag).data())))
       for linkTag in $('.ts-editable-link-tag', $element)
-        $(linkTag).attr('href', $.cloudinary.url(data.result.public_id, {resource_type: 'raw'}))
+        $(linkTag).attr('href', $.cloudinary.url(data.result.public_id, {resource_type: 'raw', format: data.result.format}))
       $(this).prop('disabled', false)
       $('.ts-progress-bar').remove()

       model = editor.ts.getModel editor.data.url
-      model.set(editor.data.field, { field: editor.data.field, value: {identifier: data.result.public_id}, type: editor.data.type })
+      model.set(editor.data.field, { field: editor.data.field, value: {identifier: data.result.public_id, format: data.result.format}, type: editor.data.type })

   $label
     # .append $labelText


Fix UploadFigures card.

Fix UploadFigures card.

--- app/assets/javascripts/views/overlays/figure_overlay_view.js.coffee
+++ app/assets/javascripts/views/overlays/figure_overlay_view.js.coffee
@@ -2,12 +2,16 @@
   templateName: 'overlays/figure_overlay'
   layoutName: 'layouts/overlay_layout' #TODO: include assignee here?
   uploads: []
-  figures: Em.computed.alias('controller.paper.figures')
+  figures: null
+  _figures: ( ->
+    @get('controller.paper').then (paper) =>
+      @set('figures', paper.get('figures'))
+  ).observes('controller.paper')

   setupUpload: (->
     uploader = $('.js-jquery-fileupload')
     uploader.fileupload
-      url: "/papers/#{@controller.get('paper.id')}/figures"
+      url: "/papers/#{@controller.get('paperId')}/figures"
       dataType: 'json'
       acceptFileTypes: /(\.|\/)(gif|jpe?g|png|tiff)$/i
       method: 'POST'


HTML escape message content.

HTML escape message content.

--- app/assets/javascripts/web/controllers/message_controller.js.coffee
+++ app/assets/javascripts/web/controllers/message_controller.js.coffee
@@ -4,8 +4,20 @@

   text: ( () ->
     text = @content.get('content') || ""
-    text = text.replace(/\s(?=\s)/g, ' ')
-    text = text.replace(/(\\r\\n|\\n|\\r)/g, '
')
+
+    strategies = [
+      [ /&/g, "&" ],
+      [ /</g, "<" ],
+      [ />/g, ">" ],
+      [ /"/g, """ ],
+      [ /\\\\/g, "\" ],
+      [ /\s(?=\s)/g, ' '],
+      [ /(\\r\\n|\\n|\\r)/g, '
']
+    ]
+
+    for strategy in strategies
+      text = text.replace(strategy[0], strategy[1])
+
     text = text.autoLink({ target: "_blank" })

     return text


detect conflict change to same line when update-real-file

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -268,22 +268,21 @@
       if line isnt item.text
         changes.push({newText: line, item})

-    if changes.length
-      if not @provider.boundToEditor and (modifiedFilePaths = @getModifiedFilePathsInChanges(changes)).length
-        modifiedFilePathsAsString = modifiedFilePaths.map((filePath) -> " - `#{filePath}`").join("\n")
-        message = """
-          Cancelled `update-real-file`.
-          You are trying to update file which have **unsaved modification**.
-          But `narrow:#{@provider.getDashName()}` can not detect unsaved change.
-          To use `update-real-file`, you need to save these files.
-
-          #{modifiedFilePathsAsString}
-          """
+    return unless changes.length
+
+    unless @provider.boundToEditor
+      {success, message} = @ensureNoModifiedFileForChanges(changes)
+      unless success
         atom.notifications.addWarning(message, dismissable: true)
         return

-      @provider.updateRealFile(changes)
-      @setModifiedState(false)
+    {success, message} = @ensureNoConflictForChanges(changes)
+    unless success
+      atom.notifications.addWarning(message, dismissable: true)
+      return
+
+    @provider.updateRealFile(changes)
+    @setModifiedState(false)

   # This function is mapped from `narrow:close`
   # To differentiate `narrow:close` for protected narrow-editor.
@@ -430,9 +429,55 @@

     true

+  detectConflictForChanges: (changes) ->
+    conflictChanges = {}
+    changesByFilePath =  _.groupBy(changes, ({item}) -> item.filePath)
+    for filePath, changesInFile of changesByFilePath
+      changesByRow = _.groupBy(changesInFile, ({item}) -> item.point.row)
+      for row, changesInRow of changesByRow
+        newTexts = _.pluck(changesInRow, 'newText')
+        if _.uniq(newTexts).length > 1
+          conflictChanges[filePath] ?= []
+          conflictChanges[filePath].push(changesInRow...)
+    conflictChanges
+
+  ensureNoConflictForChanges: (changes) ->
+    message = []
+    conflictChanges = @detectConflictForChanges(changes)
+    success = _.isEmpty(conflictChanges)
+    unless success
+      message.push """
+        Cancelled `update-real-file`.
+        Detected **conflicting change to same line**.
+        """
+      for filePath, changesInFile of conflictChanges
+        message.push("- #{filePath}")
+        for {newText, item} in changesInFile
+          message.push("  - #{item.point.translate([1, 1]).toString()}, #{newText}")
+
+    return {success, message: message.join("\n")}
+
   getModifiedFilePathsInChanges: (changes) ->
     _.uniq(changes.map ({item}) -> item.filePath).filter (filePath) ->
       atom.project.isPathModified(filePath)
+
+  ensureNoModifiedFileForChanges: (changes) ->
+    message = ''
+    modifiedFilePaths = @getModifiedFilePathsInChanges(changes)
+    success = modifiedFilePaths.length is 0
+    unless success
+      modifiedFilePathsAsString = modifiedFilePaths.map((filePath) -> " - `#{filePath}`").join("\n")
+      message = """
+        Cancelled `update-real-file`.
+        You are trying to update file which have **unsaved modification**.
+        But `narrow:#{@provider.getDashName()}` can not detect unsaved change.
+        To use `update-real-file`, you need to save these files.
+
+        #{modifiedFilePathsAsString}
+        """
+
+    return {success, message}
+

   observeStopChanging: ->
     @editor.onDidStopChanging =>
@@ -718,8 +763,8 @@
     lineText = String(point.row + 1)
     padding = " ".repeat(maxLineWidth - lineText.length)
     lineHeader = "#{@provider.indentTextForLineHeader}#{padding}#{lineText}"
-    if @showColumnOnLineHeader
-      columnText = String(point.columnText + 1)
+    if @provider.showColumnOnLineHeader
+      columnText = String(point.column + 1)
       padding = " ".repeat(maxColumnWidth - columnText.length)
       lineHeader = "#{lineHeader}:#{padding}#{columnText}"
     lineHeader + ": "


Fixed test_case @dispatch route check

Fixed test_case @dispatch route check

--- src/extras/testing/controller_test_case.coffee
+++ src/extras/testing/controller_test_case.coffee
@@ -7,9 +7,9 @@
       throw new Error( "Unable to deduce controller class name from test class. Please set @controllerClass if not conventional" )

     @controller = params.controller || new @controllerClass
-
+    controllerName = Batman.helpers.camelize(@controllerClass.name.replace(/Controller/,''), true)
     routeMap = Batman.currentApp.get('routes.routeMap')
-    actionRoutes = routeMap.childrenByOrder.filter( (route) => route.controller == @controller.routingKey and route.action ==  action)
+    actionRoutes = routeMap.childrenByOrder.filter( (route) => route.controller == controllerName and route.action ==  action)

     if actionRoutes.length == 0
       @assert false, "Route doesn't exist for action"


Dont list types as we dont use them in golem

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -5689,7 +5689,7 @@
     js: js
   js: js
   ast: ast
-  types: typeEnumaration ctx
+  # types: typeEnumaration ctx
   errors: errors

 compileExpression = (source, moduleName = '@unnamed') ->


Uncomment origin

Uncomment origin

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -4578,7 +4578,7 @@
   typeOrConstraint

 mutateMarkingOrigin = (typeOrConstraint, expression) ->
-  # typeOrConstraint.origin = expression
+  typeOrConstraint.origin = expression

 # Clones constrained types and parts of them
 # Class constraint arguments are not cloned


First attempt at true macro, but values handled wrong

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -519,7 +519,7 @@
     nestedLookupInMap @classParams, typeNamesOfNormalized constraint

 expressionCompile = (ctx, expression) ->
-  throw new Error "invalid expressionCompile args" unless ctx instanceof Context and expression
+  throw new Error "invalid expressionCompile args" unless ctx instanceof Context and expression?
   compileFn =
     if isFake expression
       fakeCompile
@@ -549,7 +549,7 @@
   operatorName = _symbol operator
   if isName operator
     (if operatorName of ctx.macros() and not ctx.arity operatorName
-      macroCompile
+      callMacroCompile
     else if (isFake operator) or (ctx.isDeclared operatorName) and not ctx.arity operatorName
       callUnknownCompile
     else
@@ -562,14 +562,14 @@
       expressionCompile ctx, replicate call,
         (call_ (join [expandedOp], (_arguments call)))

-macroCompile = (ctx, call) ->
+callMacroCompile = (ctx, call) ->
   op = _operator call
   op.label = 'keyword'
   expanded = ctx.macros()[op.symbol] ctx, call
   if isTranslated expanded
     expanded
   else
-    expressionCompile ctx, expanded
+    macroResultCompile ctx, expanded

 isTranslated = (result) ->
   (isSimpleTranslated result) or (Array.isArray result) and (isSimpleTranslated result[0])
@@ -624,7 +624,7 @@
       if nonLabeledArgs.length < positionalParams.length
         # log "currying known call"
         lambda = (fn_ extraParams, sortedCall)
-        compiled = macroCompile ctx, lambda
+        compiled = callMacroCompile ctx, lambda
         retrieve call, lambda
         # TODO: massive hack, erase inserted scope, will have to figure out how to fix this better
         ctx.savedScopes[ctx.savedScopes.length - 1].definitions = newMap()
@@ -632,7 +632,7 @@
       else
         compiled =
           if operator.symbol of ctx.macros()
-            macroCompile ctx, sortedCall
+            callMacroCompile ctx, sortedCall
           else
             callSaturatedKnownCompile ctx, sortedCall
         retrieve call, sortedCall
@@ -745,6 +745,29 @@

 expressionsCompile = (ctx, list) ->
   expressionCompile ctx, expression for expression in list
+
+macroResultCompile = (ctx, value) ->
+  assign = yes
+  translated = switch typeof value
+    when 'boolean', 'number', 'string' then JSON.stringify value
+    when 'object'
+      kind = Object.prototype.toString.call(value).slice(8, -1)
+      switch kind
+        when 'Date' then (jsNew 'Date', [+value])
+        when 'RegExp' then (jsNew 'RegExp', [value.source])
+        else
+          #TODO: rest of immutable
+          if Immutable.Iterable.isIterable value
+            (jsCallMethod 'Immutable', 'List',
+              [(jsCallMethod 'JSON', 'parse', [JSON.stringify(value.toJS())])])
+          else
+            assign = no
+            expressionCompile ctx, value
+  if assign
+    value = tea: toConstrained typeConstant 'Val'
+    assignCompile ctx, value, translated
+  else
+    translated

 tupleCompile = (ctx, form) ->
   elems = _terms form
@@ -1666,6 +1689,54 @@
   assignCompile ctx, call,
     (jsBinaryMulti "+", join (concat formattedArgs), [string_ formatString])

+ms.syntax = ms_syntax = (ctx, call) ->
+  hasName = requireName ctx, 'Name required to declare new algebraic data'
+  [paramTuple, rest...] = _arguments call
+  [body] = rest
+
+  # ctx.addMacro ctx.definitionName(), (ctx) ->
+  #   expressionCompile ctx, body
+
+  if hasName
+    macroName = ctx.definitionName()
+
+    # params = (map (__ token_, _symbol), _terms paramTuple) # freshen
+
+    compiledMacro = translateToJs translateIr ctx,
+        (termCompile ctx, call_ (token_ 'fn'), (join [paramTuple], rest))
+    macroFn = eval compiledMacro
+    ctx.addMacro macroName, (ctx, call) ->
+      # operatorCompile ctx, call
+      # args = termsCompile ctx, (_arguments call)[0..macroFn.length]
+      #callTyping ctx, call
+      macroFn (_arguments call)...
+
+    # ctx.addMacro macroName, (ctx, call) ->
+    #   compiled = termCompile ctx, (fn_ params, body)
+    #   assignCompile ctx, body, compiled
+
+    jsNoop()
+
+
+ms['`'] = ms_quote = (ctx, call) ->
+  [res] = _arguments call
+  # call.tea = toConstrained typeConstant 'Exp'
+  # assignCompile ctx, call, res
+  call.tea = toConstrained typeConstant 'Expression'
+
+  serializeAst = (ast) ->
+    if isForm ast
+      if (_operator ast)?.symbol is ','
+        termCompile ctx, ast
+      else
+        (jsArray (map serializeAst, ast))
+    else
+      (jsValue (JSON.stringify ast))
+  serializeAst res
+
+ms[','] = ms_comma = (ctx, call) ->
+  (expressionCompile ctx, (_arguments call)[0])
+
 ms.macro = ms_macro = (ctx, call) ->
   hasName = requireName ctx, 'Name required to declare a new instance'
   [paramTuple, type, rest...] = _arguments call
@@ -2516,6 +2587,9 @@
           undefined
     args

+jsCallMethod = (object, methodName, args) ->
+  (jsCall (jsAccess object, methodName), args)
+
 jsAccess = (lhs, name) ->
   {js: jsAccessTranslate, lhs, name}

@@ -2638,6 +2712,13 @@

 jsUnaryTranslate = ({op, arg}) ->
   "#{op}#{arg}"
+
+
+jsValue = (value) ->
+  {js: jsValueTranslate, value}
+
+jsValueTranslate = ({value}) ->
+  value

 jsVarDeclaration = (name, rhs) ->
@@ -4077,7 +4158,7 @@
     [expression] = _terms ast
     {js} = compileCtxAstToJs topLevelExpression, ctx, expression
     (finalizeTypes ctx, expression)
-    js: library + immutable + (listOfLines map lookupJs, modules) + js
+    js: library + immutable + (listOfLines map lookupJs, (reverse modules)) + js
     ast: ast
     errors: checkTypes ctx


Fixing an issue with the call back function for rings without ids.

--- viz/earth/geom/draggable_ring.coffee
+++ viz/earth/geom/draggable_ring.coffee
@@ -30,7 +30,7 @@
       this.notifyGuiEventListeners(DraggableRing.DRAG_FINISH_EVENT_CACHED)
       if @callbackFn
         pointStr = this.toOrdinatesString()
-        if @id != null
+        if @id && @id != null
           callbackStr = "#{@id}:#{pointStr}"
         else
           callbackStr = pointStr


fixed Model.Binary.expandCube memory leak

--- app/assets/javascripts/model.coffee
+++ app/assets/javascripts/model.coffee
@@ -355,9 +355,9 @@
 				Math.min(z0, z1, _offset[2])
 			]
 			cubeSize = [
-				Math.max(x0, x1, _offset[0] + _size[0]) - cubeOffset[0] + 1
-				Math.max(y0, y1, _offset[1] + _size[1]) - cubeOffset[1] + 1
-				Math.max(z0, z1, _offset[2] + _size[2]) - cubeOffset[2] + 1
+				Math.max(x1 - x0 + 1, _size[0])
+				Math.max(y1 - y0 + 1, _size[1])
+				Math.max(z1 - z0 + 1, _size[2])
 			]
 		else
 			cubeOffset = [


strip messageML tag

strip messageML tag

--- src/adapter.coffee
+++ src/adapter.coffee
@@ -67,7 +67,8 @@
   _receiveMessage: (message) =>
     user = @symphony.getUser(message.fromUserId)
     user.room = message.streamId
-    message = new TextMessage(new User(message.fromUserId, name: user.displayName), message.id)
+    match = ///<messageML>(.*)</messageML>///i.exec(message.message)
+    message = new TextMessage(new User(message.fromUserId, name: user.displayName), match[1], message.id)
     @robot.receive message

 exports.use = (robot) ->


Track last

Track last

--- src/apm-cli.coffee
+++ src/apm-cli.coffee
@@ -12,13 +12,13 @@

 setupTempDirectory = ->
   temp = require 'temp'
-  temp.track()
   tempDirectory = require('os').tmpdir()
   # Resolve ~ in tmp dir atom/atom#2271
   tempDirectory = path.resolve(fs.absolute(tempDirectory))
   temp.dir = tempDirectory
   try
     fs.makeTreeSync(temp.dir)
+  temp.track()

 setupTempDirectory()


Add sendgrid validation data to user schema (#5889)

--- app/schemas/models/user.coco.coffee
+++ app/schemas/models/user.coco.coffee
@@ -111,6 +111,12 @@
         type: c.shortString() # E.g 'share progress modal parent'
         email: c.shortString()
         sent: c.date() # Set when sent
+
+    validations: c.array, { title: 'Sendgrid email validation results' },
+      c.object {},
+        validationDate: c.date()
+        result: c.object({ additionalProperties: true })
+
   unsubscribedFromMarketingEmails: { type: 'boolean' }

   consentHistory: c.array {title: 'History of consent actions'},


preventing hubot from crashing when msg is not available during init

--- src/bonusly.coffee
+++ src/bonusly.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   Allows users to give 'micro-bonuses' on bonusly via Hubot
+#   Allows users to give 'micro-bonuses' on bonusly via Hubot
 #
 # Dependencies:
 #   None
@@ -13,7 +13,7 @@
 #   hubot bonusly leaderboard <giver|receiver> -  show leaderboard for giving or receiving
 #
 # Notes:
-#   To use this script, you must be signed up for Bonusly (https://bonus.ly)
+#   To use this script, you must be signed up for Bonusly (https://bonus.ly)
 #
 # Author:
 #   doofdoofsf
@@ -25,7 +25,10 @@
   service = 'https://bonus.ly'

   unless token
-    msg.send 'The Bonusly API token is not set. Navigate to https://bonus.ly/api as an _admin_ user (important), grab the access token and set the HUBOT_BONUSLY_ADMIN_API_TOKEN environment variable.'
+    if msg?
+      msg.send 'The Bonusly API token is not set. Navigate to https://bonus.ly/api as an _admin_ user (important), grab the access token and set the HUBOT_BONUSLY_ADMIN_API_TOKEN environment variable.'
+    else
+      console.log 'The Bonusly API token is not set. Navigate to https://bonus.ly/api as an _admin_ user (important), grab the access token and set the HUBOT_BONUSLY_ADMIN environment variable.'
     return

   robot.respond /(bonusly)? bonuses/i, (msg) ->
@@ -76,7 +79,7 @@
       text = ''

     path = '/api/v1/bonuses/create_from_text'
-    post = "access_token=#{token}&giver=#{giver}&client=#{client}&text=#{text}"
+    post = "access_token=#{token}&giver=#{giver}&client=#{client}&text=#{text}"

     msg.http(service)
       .path(path)


Remove unused observer

Remove unused observer

log property in Travis.Job does not change - we create Log instance
when it's accessed and don't refresh it on any occasion, that's why the
observer on log is not needed.

--- assets/scripts/app/views/log.coffee
+++ assets/scripts/app/views/log.coffee
@@ -36,11 +36,6 @@
     versionDidChange: (->
       @rerender() if @get('state') == 'inDOM'
     ).observes('log.version')
-
-    logDidChange: (->
-      console.log 'log view: log did change: rerender' if Log.DEBUG
-      @rerender() if @get('inDOM')
-    ).observes('log')

     createEngine: ->
       console.log 'log view: create engine' if Log.DEBUG


Documentation

Documentation

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -200,11 +200,13 @@
   player = Core
     x: 50
     y: 40
+    width: 10
+    height: 30

   player.include(Bounded)

-  player.position()
-  => {x: 50, y: 40}
+  player.center()
+  => {x: 30, y: 35}
   </pre></code>

   @name center


new method: catalog.getCurrentProductWithVariations

--- src/catalog.coffee
+++ src/catalog.coffee
@@ -22,14 +22,24 @@
 		@cache =
 			productWithVariations: {}

-	# Returns a promise .
+	# Gets a products' complete "skuJSON". Returns a promise.
 	getProductWithVariations: (productId) =>
-		if @cache.productWithVariations[productId]
-			return @promise(@cache.productWithVariations[productId])
+		@promise(@cache.productWithVariations[productId] or $.ajax("#{@BASE_ENDPOINT}/products/variations/#{productId}"))
+			.done (response) =>
+				@setProductWithVariationsCache(productId, response)
+
+	# Private
+	setProductWithVariationsCache: (productId, apiResponse) =>
+		@cache.productWithVariations[productId] = apiResponse
+
+	# Get current product's complete "skuJSON". Returns a promise.
+	getCurrentProductWithVariations: =>
+		if window.skuJson
+			return @promise(window.skuJson)
 		else
-			$.when(@cache.productWithVariations[productId] or $.ajax("#{@_getBaseCatalogSystemURL()}/products/variations/#{productId}"))
-			.done (response) =>
-				@cache.productWithVariations[productId] = response
+			for k, v of @cache.productWithVariations
+				return @promise(v)
+

 	# URL BUILDERS


add a sparse mongo index for finding packs

--- app/coffee/MongoManager.coffee
+++ app/coffee/MongoManager.coffee
@@ -353,6 +353,8 @@
 		db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true }
 		# For finding all updates that affect a project (getProjectUpdates meta.end_ts < before
 		db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true }
+		# For finding all packs that affect a project (use a sparse index so only packs are included)
+		db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}	, { background: true, sparse: true }
 		# For finding updates that don't yet have a project_id and need it inserting
 		db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true }
 		# For finding project meta-data


wire subject talk links to correct project (#5369)

determine the project talk context via the following:
1. use known project context if it exists
2. use the first collection projects link if there is only 1
3. fall back to the originating subject project

--- app/collections/show-list.cjsx
+++ app/collections/show-list.cjsx
@@ -32,14 +32,24 @@
     project: null

   componentWillMount: ->
-    @fetchProject(@props.subject)
-      .then (project) =>
-        @setState {project}
-        @isFavorite(project)
-
+    # if the project context is set use that
+    if @props.projectContext
+      @updateProjectState(@props.projectContext)
+    else
+      @fetchProject(@props.subject)
+        .then (project) =>
+          @updateProjectState(project)
+
+  updateProjectState: (project) ->
+    @setState {project}
+    @isFavorite(project)
+
+  # use the collection project if there is only 1 project for this collection
+  # fallback to the subject project link if no other project context can be determined
   fetchProject: (subject) ->
-    projectRequest = if @props.collection?.links.project?
-      @props.collection.get('project')
+    projectRequest = if @props.collection?.links.projects?.length == 1
+      projectId = @props.collection?.links.projects[0]
+      apiClient.type('projects').get(projectId)
     else
       subject.get('project')


Prevent unnecessary API calls for profile

--- app/controllers/profile.coffee
+++ app/controllers/profile.coffee
@@ -40,19 +40,22 @@
   active: ->
     @el.find('[data-type="sim-freq"]').parent().remove()
     if User.current
-      Favorite.fetch()
-      Recent.fetch()
+      Favorite.fetch per_page: 8
+      Recent.fetch per_page: 8
+      @prefetched = true
     super

   onUserChange: (e, user) =>
-
     if user?
-      Recent.fetch()
-      Favorite.fetch()
+      if not @prefetched and @isActive()
+        Favorite.fetch per_page: 8
+        Recent.fetch per_page: 8
+        @prefetched = true

       @el.find('.user').addClass('show')
       @el.find('.no-user').addClass('hide')
     else
+      @prefetched = false
       @el.find('.user').removeClass('show')
       @el.find('.no-user').removeClass('hide')

@@ -82,15 +85,17 @@
     type = e.target.dataset.type
     model = type.charAt(0).toUpperCase() + type.slice(1)

-    @["#{type}Page"] = Math.max(1, @["#{type}Page"] - 1)
-    zooniverse.models[model].fetch({page: @["#{type}Page"]})
+    prevPage = Math.max 1, @["#{ type }Page"] - 1
+    if prevPage isnt @["#{ type }Page"]
+      @["#{ type }Page"] = prevPage
+      zooniverse.models[model].fetch page: @["#{ type }Page"], per_page: 8

   getNext: (e) ->
     type = e.target.dataset.type
     model = type.charAt(0).toUpperCase() + type.slice(1)

-    @["#{type}Page"] += 1
-    zooniverse.models[model].fetch({page: @["#{type}Page"]})
+    @["#{ type }Page"] += 1
+    zooniverse.models[model].fetch page: @["#{ type }Page"], per_page: 8

 module.exports = Profile


appended options

appended options

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -52,6 +52,8 @@
           ext: '.html'
           src: 'jade/!(_)*.jade'
         ]
+      options:
+        pretty: true
     stylus:
       compile:
         files: [
@@ -60,9 +62,9 @@
           ext: '.css'
           src: ['stylus/!(_)*.styl', 'stylus/!(_)*.stylus']
         ]
+      options:
+        compress: false
     coffee:
-      options:
-        bare: true
       compile:
         files: [
           dest: 'output/'
@@ -70,6 +72,9 @@
           ext: '.js'
           src: 'coffee/*.coffee'
         ]
+      options:
+        bare: true
+        sourceMap: true
     esteWatch:
       options:
         dirs: ['js/**', 'jade/**', 'stylus/**', 'coffee/**']


some changes to Gruntfile.coffee

some changes to Gruntfile.coffee

- appended clean task
- appended develop and rebuild aliases
- changed bower layout
- changed connect server port
- fixed coffee-script source map

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -1,87 +1,124 @@
 module.exports = (grunt) ->

   grunt.initConfig
+
     bower:
       install:
         options:
-          layout: 'byComponent'
+          layout: 'byType'
           targetDir: 'public/lib/'
+
+    clean:
+      main:
+        files: [
+          src: 'public/**/*'
+        ]
+
+    coffee:
+      compile:
+        files: [
+          dest: 'public/js/'
+          expand: true
+          ext: '.js'
+          rename: (dest, matchSrcPath, options) ->
+            require('path').join dest, matchSrcPath.replace 'public/coffee/', ''
+          src: 'public/coffee/**/*.coffee'
+        ]
+      options:
+        bare: true
+        sourceMap: true
+
     connect:
       server:
         options:
           base: 'public/'
           livereload: 35729
-          port: 3000
+
     copy:
-      main:
+      coffee:
         files: [
-          dest: 'public/js/'
+          dest: 'public/'
           expand: true
-          filter: 'isFile'
-          flatten: true
-          src: 'js/*'
+          src: 'coffee/**/*.coffee'
         ]
+      js:
+        files: [
+          dest: 'public/'
+          expand: true
+          src: 'js/**/*.js'
+        ]
+
     jade:
       compile:
         files: [
           dest: 'public/'
           expand: true
           ext: '.html'
-          flatten: true
+          rename: (dest, matchSrcPath, options) ->
+            require('path').join dest, matchSrcPath.replace 'jade/', ''
           src: 'jade/!(_)*.jade'
         ]
       options:
         pretty: true
+
+    less:
+      compile:
+        files: [
+          dest: 'public/css/'
+          expand: true
+          ext: '.css'
+          rename: (dest, matchSrcPath, options) ->
+            require('path').join dest, matchSrcPath.replace 'less/', ''
+          src: 'less/**/!(_)*.less'
+        ]
+
     stylus:
       compile:
         files: [
           dest: 'public/css/'
           expand: true
           ext: '.css'
-          flatten: true
+          rename: (dest, matchSrcPath, options) ->
+            require('path').join dest, matchSrcPath.replace 'stylus/', ''
           src: 'stylus/!(_)*.{styl,stylus}'
         ]
       options:
         compress: false
-    less:
-      compile:
-        files: [
-          dest: 'public/css/'
-          expand: true
-          ext: '.css'
-          flatten: true
-          src: 'less/!(_)*.less'
-        ]
-    coffee:
-      compile:
-        files: [
-          dest: 'public/js/'
-          expand: true
-          ext: '.js'
-          flatten: true
-          src: 'coffee/*.coffee'
-        ]
-      options:
-        bare: true
-        sourceMap: true
+
     esteWatch:
-      options:
-        dirs: ['js/**', 'jade/**', 'stylus/**', 'less/**', 'coffee/**']
-        livereload:
-          enabled: true
-          extensions: ['js', 'jade', 'styl', 'stylus', 'less', 'coffee']
-          port: 35729
-      js: (filepath) ->
-        conf = grunt.config('copy.main.files')[0]
+      coffee: (filepath) ->
+        # copy to public dir
+        conf = grunt.config('copy.coffee.files')[0]
         conf.src = filepath
-        grunt.config 'copy.main.files', [conf]
-        'copy'
+        grunt.config 'copy.coffee.files', [conf]
+        # compile copied file
+        conf = grunt.config('coffee.compile.files')[0]
+        conf.src = filepath.replace 'coffee/', 'public/coffee/'
+        grunt.config 'coffee.compile.files', [conf]
+        ['copy:coffee', 'coffee']
       jade: (filepath) ->
         return if /_[^\/]*\.jade$/.test filepath
         conf = grunt.config('jade.compile.files')[0]
         conf.src = filepath
         grunt.config 'jade.compile.files', [conf]
         'jade'
+      js: (filepath) ->
+        conf = grunt.config('copy.js.files')[0]
+        conf.src = filepath
+        grunt.config 'copy.js.files', [conf]
+        'copy:js'
+      less: (filepath) ->
+        return if /_[^\/]*\.less$/.test filepath
+        conf = grunt.config('less.compile.files')[0]
+        conf.src = filepath
+        grunt.config 'less.compile.files', [conf]
+        'less'
+      options:
+        dirs: ['coffee/**', 'jade/**', 'js/**', 'less/**', 'stylus/**']
+        livereload:
+          enabled: true
+          extensions: ['coffee', 'jade', 'js', 'less', 'styl', 'stylus']
+          port: 35729
       styl: '<%= esteWatch.stylus %>'
       stylus: (filepath) ->
         return if /_[^\/]*\.styl(?:us)?$/.test filepath
@@ -89,19 +126,9 @@
         conf.src = filepath
         grunt.config 'stylus.compile.files', [conf]
         'stylus'
-      less: (filepath) ->
-        return if /_[^\/]*\.less$/.test filepath
-        conf = grunt.config('less.compile.files')[0]
-        conf.src = filepath
-        grunt.config 'less.compile.files', [conf]
-        'less'
-      coffee: (filepath) ->
-        conf = grunt.config('coffee.compile.files')[0]
-        conf.src = filepath
-        grunt.config 'coffee.compile.files', [conf]
-        'coffee'

   grunt.loadNpmTasks 'grunt-bower-task'
+  grunt.loadNpmTasks 'grunt-contrib-clean'
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-connect'
   grunt.loadNpmTasks 'grunt-contrib-copy'
@@ -110,7 +137,8 @@
   grunt.loadNpmTasks 'grunt-contrib-stylus'
   grunt.loadNpmTasks 'grunt-este-watch'

-  grunt.registerTask 'default', ['connect', 'jade', 'stylus', 'less', 'coffee', 'copy', 'esteWatch']
-  grunt.registerTask 'install', ['bower:install']
+  grunt.registerTask 'develop', ['connect', 'esteWatch']
+  grunt.registerTask 'install', ['bower']
+  grunt.registerTask 'rebuild', ['clean', 'copy', 'jade', 'stylus', 'less', 'coffee']

   return


[boneHand] Render after leapmotion pipeline runs, expose scope.render

--- main/bone-hand/leap.bone-hand.coffee
+++ main/bone-hand/leap.bone-hand.coffee
@@ -51,14 +51,8 @@
     renderer.render(scope.scene, camera)
   , false

-  render = ->
-    renderer.render(scope.scene, camera);
-    window.requestAnimationFrame(render);
-
-  render()
-
-
-  scope.render ||= ->
+
+  scope.render ||= (timestamp)->
     renderer.render(scope.scene, scope.camera);

   scope.render()
@@ -378,8 +372,19 @@
     HandMesh.create()
     HandMesh.create()

+    # have rendered called by leap, rather than animation frame, to make sure there's no one-frame-delay.
+    # we wrap this to allow the render method to be replaced
+    if Leap.version.major == 0 && Leap.version.minor < 7 && Leap.version.dot < 4
+      console.warn("BoneHand default scene render requires LeapJS > 0.6.3. You're running have #{Leap.version.full}")
+
+    @on 'frameEnd', (timestamp)->
+      scope.render(timestamp)
+
   @on 'handLost', boneHandLost

+
   {
+
     hand: onHand
+
   }


code-reformat

code-reformat

--- dl/test/test_helper.coffee
+++ dl/test/test_helper.coffee
@@ -1,26 +1,36 @@
+assert = require 'assert'

+module.exports =

-module.exports = th = {}
-
-th.print_result=(tr_object)->
-    console.log 'print_result', JSON.stringify tr_object
-    try
-        tr = signed_transaction_type.fromObject tr_object
-        tr_hex = signed_transaction_type.toHex(tr)
-        ByteBuffer.fromHex(tr_hex).printDebug()
-    catch e
-        if tr_object and tr_object["ref_block_num"]
-            console.log "print_result: unparsed or non-transactoin object",e,e.stack
-
-th.print_hex=(hex)->
-    console.log 'print_hex'
-    ByteBuffer.fromHex(hex).printDebug()
-    try
-        tr = signed_transaction_type.fromHex hex
-        tr_object = signed_transaction_type.toObject(tr)
-        console.log JSON.stringify tr_object
-    catch e
-        console.log "print_hex: unparsed or non-transactoin object",e,e.stack
-
-th.log_error = (error)->
-    console.log 'log_error',error,error.stack
+    print_result: (tr_object)->
+        console.log 'print_result', JSON.stringify tr_object
+        try
+            tr = signed_transaction_type.fromObject tr_object
+            tr_hex = signed_transaction_type.toHex(tr)
+            ByteBuffer.fromHex(tr_hex).printDebug()
+        catch e
+            if tr_object and tr_object["ref_block_num"]
+                console.log "print_result: unparsed or non-transactoin object",e,e.stack
+
+    print_hex: (hex)->
+        console.log 'print_hex'
+        ByteBuffer.fromHex(hex).printDebug()
+        try
+            tr = signed_transaction_type.fromHex hex
+            tr_object = signed_transaction_type.toObject(tr)
+            console.log JSON.stringify tr_object
+        catch e
+            console.log "print_hex: unparsed or non-transactoin object",e,e.stack
+
+    log_error: (error)->
+        console.log 'log_error',error,error.stack
+
+    exception: (message_substring, f)->
+        try
+            f()
+            assert false, "expecting " + message_substring
+        catch e
+            assert(
+                e.toString().indexOf(message_substring) isnt -1
+                "expecting " + message_substring
+            )


Pass the content model to the popover

Pass the content model to the popover

--- src/scripts/modules/media/header/header.coffee
+++ src/scripts/modules/media/header/header.coffee
@@ -31,7 +31,7 @@
     onRender: () ->
       @attachPopover new BookPopoverView
         owner: @$el.find('.info .btn')
-        content: @model.toJSON()
+        model: @model.toJSON()

     toggleSummary: (e) ->
       $summary = @$el.find('.summary')


JStackTemplate: generateStack: call in order, tidy up usage

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -527,32 +527,39 @@
       shouldReviveProvider  : no
       shouldFetchGroupLimit : yes

-      unless @getAt 'config.verified'
-        return callback new KodingError 'Stack is not verified yet'
     , (client, options, callback) ->

       { account, group, user } = client.r

-
-      instanceCount = @machines?.length or 0
-      change        = 'increment'
-
-      details = { account, template: this }
-
-      ComputeProvider.updateGroupResourceUsage {
-        group, change, instanceCount, details
-      }, (err) =>
+      instanceCount = (@getAt 'machines')?.length or 0
+      template = this
+
+      queue = [
+
+        (next) =>
+
+          next unless @getAt 'config.verified'
+          then new KodingError 'Stack is not verified yet'
+          else null
+
+        (next) =>
+
+          change  = 'increment'
+          ComputeProvider.updateGroupResourceUsage {
+            group, change, instanceCount, details: { account, template }
+          }, (err) =>
+            return next err  if err
+            checkTemplateUsage this, account, (err) =>
+              return next err  if err
+              account.addStackTemplate this, next
+
+      ]
+
+      async.series queue, (err) ->
         return callback err  if err

-        checkTemplateUsage this, account, (err) =>
-          return callback err  if err
-
-          account.addStackTemplate this, (err) =>
-
-            details = { account, user, group, client }
-            details.template = this
-
-            ComputeProvider.generateStackFromTemplate details, {}, callback
+        details = { account, user, group, client, template }
+        ComputeProvider.generateStackFromTemplate details, {}, callback

   update$: permit


Adjust taxCategory of shippingInfo to retailer.

--- src/coffee/orderdistribution.coffee
+++ src/coffee/orderdistribution.coffee
@@ -27,7 +27,7 @@
     deferred = Q.defer()
     sort = encodeURIComponent "createdAt DESC"
     rest.GET "/orders?sort=#{sort}", (error, response, body) ->
-      if error
+      if error?
         deferred.reject "Error on fetching orders: " + error
       else if response.statusCode isnt 200
         deferred.reject "Problem on fetching orders (status: #{response.statusCode}): " + body
@@ -38,11 +38,25 @@
         deferred.resolve unsyncedOrders
     deferred.promise

+  getTaxCategory: (rest) ->
+    deferred = Q.defer()
+    rest.GET "/tax-categories?limit=1", (error, response, body) ->
+      if error?
+        deferred.reject "Error on fetching tax category: " + error
+      else if response.statusCode isnt 200
+        deferred.reject "Problem on fetching tax category (status: #{response.statusCode}): " + body
+      else
+        if _.size(body.results) is 1
+          deferred.resolve body.results[0]
+        else
+          deferred.reject "Can't find tax category."
+    deferred.promise
+
   getRetailerProductByMasterSKU: (sku) ->
     deferred = Q.defer()
     query = encodeURIComponent "variants.attributes.mastersku:\"#{sku.toLowerCase()}\""
     @retailerRest.GET "/product-projections/search?staged=true&lang=de&filter=#{query}" , (error, response, body) ->
-      if error
+      if error?
         deferred.reject "Error on fetching products: " + error
       else if response.statusCode isnt 200
         deferred.reject "Problem on fetching products (status: #{response.statusCode}): " + body
@@ -71,27 +85,31 @@
   distribute: (masterOrder) ->
     masterSKUs = @extractSKUs masterOrder

-    gets = _.map masterSKUs, (sku) =>
-      @getRetailerProductByMasterSKU(sku)
-
-    Q.all(gets)
-    .then (retailerProducts) =>
-      masterSKU2retailerSKU = @matchSKUs retailerProducts
-
-      retailerOrder = @replaceSKUs masterOrder, masterSKU2retailerSKU
-      retailerOrder = @removeIdsAndVariantData retailerOrder
-
-      @importOrder(retailerOrder)
-    .then (newOrder) =>
-      Q.all([
-        @inventoryUpdater.ensureChannelByKey @masterRest, @retailerRest._options.config.project_key, CHANNEL_ROLES
-        @inventoryUpdater.ensureChannelByKey @retailerRest, 'master', CHANNEL_ROLES
-      ])
-      .spread (channelInMaster, channelInRetailer) =>
+    @getTaxCategory(@retailerRest)
+    .then (taxCategory) =>
+
+      gets = _.map masterSKUs, (sku) =>
+        @getRetailerProductByMasterSKU(sku)
+
+      Q.all(gets)
+      .then (retailerProducts) =>
+        masterSKU2retailerSKU = @matchSKUs retailerProducts
+
+        retailerOrder = @replaceSKUs masterOrder, masterSKU2retailerSKU
+        retailerOder = @replaceTaxCategories retailerOrder, taxCategory
+        retailerOrder = @removeIdsAndVariantData retailerOrder
+
+        @importOrder(retailerOrder)
+      .then (newOrder) =>
         Q.all([
-          @addSyncInfo(@masterRest, masterOrder.id, masterOrder.version, channelInMaster.id, newOrder.id)
-          @addSyncInfo(@retailerRest, newOrder.id, newOrder.version, channelInRetailer.id, masterOrder.id)
+          @inventoryUpdater.ensureChannelByKey @masterRest, @retailerRest._options.config.project_key, CHANNEL_ROLES
+          @inventoryUpdater.ensureChannelByKey @retailerRest, 'master', CHANNEL_ROLES
         ])
+        .spread (channelInMaster, channelInRetailer) =>
+          Q.all([
+            @addSyncInfo(@masterRest, masterOrder.id, masterOrder.version, channelInMaster.id, newOrder.id)
+            @addSyncInfo(@retailerRest, newOrder.id, newOrder.version, channelInRetailer.id, masterOrder.id)
+          ])

   matchSKUs: (retailerProducts) ->
     allVariants = _.flatten _.map(retailerProducts, (p) -> [p.masterVariant].concat(p.variants or []))
@@ -115,7 +133,7 @@
         externalId: externalId
       ]
     rest.POST "/orders/#{orderId}", data, (error, response, body) ->
-      if error
+      if error?
         deferred.reject "Error on setting sync info: " + error
       else if response.statusCode isnt 200
         humanReadable = JSON.stringify body, null, ' '
@@ -126,8 +144,9 @@

   importOrder: (order) ->
     deferred = Q.defer()
+    console.log "IMPORT %j", order
     @retailerRest.POST "/orders/import", order, (error, response, body) ->
-      if error
+      if error?
         deferred.reject "Error on importing order: " + error
       else if response.statusCode isnt 201
         humanReadable = JSON.stringify body, null, ' '
@@ -143,14 +162,14 @@
         channelID = channel.id
         return true
       return channelID is channel.id
-    if order.lineItems
+    if order.lineItems?
       for li in order.lineItems
-        if li.supplyChannel
+        if li.supplyChannel?
           return false unless checkChannelId li.supplyChannel
         continue unless li.variant
         continue unless li.variant.prices
         for p in li.variant.prices
-          if p.channel
+          if p.channel?
             return false unless checkChannelId p.channel
     true

@@ -158,7 +177,7 @@
     _.map _.filter(order.lineItems or [], (li) -> li.variant? and li.variant.sku? ), (li) -> li.variant.sku

   replaceSKUs: (order, masterSKU2retailerSKU) ->
-    if order.lineItems
+    if order.lineItems?
       for li in order.lineItems
         continue unless li.variant
         continue unless li.variant.sku
@@ -167,9 +186,16 @@

     order

+  replaceTaxCategories: (order, taxCategory) ->
+    if order.shippingInfo? and order.shippingInfo.taxCategory?
+      order.shippingInfo.taxCategory[id] = taxCategory.id
+
+    order
+
   removeIdsAndVariantData: (order) ->
     _.each order.lineItems or [], (li) ->
       delete li.supplyChannel if li.supplyChannel?
+      delete li.id if li.id?
       delete li.productId if li.productId?
       delete li.states if li.states?
       if li.variant?


handle errors normally in addFolder modal

--- services/web/app/coffee/Features/Editor/EditorHttpController.coffee
+++ services/web/app/coffee/Features/Editor/EditorHttpController.coffee
@@ -105,7 +105,7 @@
 			else if error?.message == 'invalid element name'
 				res.status(400).json(req.i18n.translate('invalid_file_name'))
 			else if error?
-				res.status(500).json(req.i18n.translate('generic_something_went_wrong'))
+				next(error)
 			else
 				res.json doc


Quick fix for issue creating new tabs.

Quick fix for issue creating new tabs.

This is a quick fix only (to keep master in a functional state).
openUrlInNewTab is broken, and needs to be refactored.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -157,6 +157,9 @@
       index: tab.index + 1
       selected: true
       windowId: tab.windowId
+    # FIXME(smblott). openUrlInNewTab is being called in two different ways with different arguments.  We
+    # should refactor it such that this check on callback isn't necessary.
+    callback = (->) unless typeof callback == "function"
     chrome.tabs.create tabConfig, callback

 openUrlInIncognito = (request) ->


Help page, use separate row for long bindings.

If there are many bindings for a command, then the column layout on the
help page can become out of whack.  This commit puts such bindings on a
separate table row which spans all three columns.

(Many bindings for a single command are more likely in light of #1697.)

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -149,16 +149,25 @@
     bindings = (commandsToKey[command] || [""]).join(", ")
     if (showUnboundCommands || commandsToKey[command])
       isAdvanced = Commands.advancedCommands.indexOf(command) >= 0
-      html.push(
-        "
",
-        "", Utils.escapeHtml(bindings), "	",
-        ":	", availableCommands[command].description)
-
-      if (showCommandNames)
-        html.push("(#{command})")
-
-      html.push("
")
+      description = availableCommands[command].description
+      if bindings.length < 12
+        helpDialogHtmlForCommand html, isAdvanced, bindings, description, showCommandNames, command
+      else
+        # If the length of the bindings is too long, then we display the bindings on a separate row from the
+        # description.  This prevents the column alignment from becoming out of whack.
+        helpDialogHtmlForCommand html, isAdvanced, bindings, "", false, ""
+        helpDialogHtmlForCommand html, isAdvanced, "", description, showCommandNames, command
   html.join("\n")
+
+helpDialogHtmlForCommand = (html, isAdvanced, bindings, description, showCommandNames, command) ->
+  html.push "
"
+  if description
+    html.push "", Utils.escapeHtml(bindings), "	"
+    html.push "#{if description and bindings then ':' else ''}	", description
+    html.push("(#{command})") if showCommandNames
+  else
+    html.push "", Utils.escapeHtml(bindings)
+  html.push("
")

 #
 # Fetches the contents of a file bundled with this extension.


Code review, clean up.

Code review, clean up.

--- background_scripts/sync.coffee
+++ background_scripts/sync.coffee
@@ -45,67 +45,56 @@
           @log "pull: #{key} <- #{value}"
           @storeAndPropagate key, value
       else
-        console.log "chrome sync callback for Sync.pull() indicates error"
+        console.log "callback for Sync.pull() indicates error"
         console.log chrome.runtime.lastError

   # Asynchronous message from synced storage.
   listener: (changes, area) ->
     for own key, change of changes
       @log "listener: #{key} <- #{change.newValue}"
-      @storeAndPropagate key, change.newValue
+      @storeAndPropagate key, change?.newValue

   # Only ever called from asynchronous synced-storage callbacks (pull and listener).
   storeAndPropagate: (key, value) ->
-    # Value must be JSON.stringifed or undefined.
-    if not @checkHaveStringOrUndefined value
-      return
-    # Ignore, we're not accepting this key.
-    if not @isSyncKey key
-       @log "ignoring: #{key}"
-       return
-    # Ignore, it's unchanged
-    if key of localStorage and localStorage[key] is value
-       @log "unchanged: #{key}"
-       return
+    return if not key of Settings.defaults
+    return if not @isSyncKey key
+    return if value and key of localStorage and localStorage[key] is value

-    # Ok: accept, store and propagate this update.
+    # Ok: store and propagate this update.
     defaultValue = Settings.defaults[key]
     defaultValueJSON = JSON.stringify(defaultValue)

     if value and value != defaultValueJSON
       # Key/value has been changed to non-default value at remote instance.
-      @log "update: #{key}=#{value}"
+      @log "storeAndPropagate update: #{key}=#{value}"
       localStorage[key] = value
       Settings.doPostUpdateHook key, JSON.parse(value)
     else
       # Key has been reset to default value at remote instance.
-      @log "clear: #{key}"
+      @log "storeAndPropagate clear: #{key}"
       if key of localStorage
         delete localStorage[key]
       Settings.doPostUpdateHook key, defaultValue

   # Only called synchronously from within vimium, never on a callback.
-  # No need to propagate updates into the rest of vimium.
+  # No need to propagate updates to the rest of vimium, that's already been done.
   set: (key, value) ->
-    # value has already been JSON.stringifed
-    if not @checkHaveString value
-      return
-    #
     if @isSyncKey key
+      @log "set scheduled: #{key}=#{value}"
       @storage.set @mkKeyValue(key,value), =>
         # Chrome sets chrome.runtime.lastError if there is an error.
         if chrome.runtime.lastError
-          console.log "chrome sync callback for Sync.set() indicates error: " + key
+          console.log "callback for Sync.set() indicates error: #{key} <- #{value}"
           console.log chrome.runtime.lastError
-      @log "set scheduled: #{key}=#{value}"

   # Only called synchronously from within vimium, never on a callback.
   clear: (key) ->
     if @isSyncKey key
+      @log "clear scheduled: #{key}"
       @storage.remove key, =>
         # Chrome sets chrome.runtime.lastError if there is an error.
         if chrome.runtime.lastError
-          console.log "chrome sync callback for Sync.clear() indicates error: " + key
+          console.log "for Sync.clear() indicates error: #{key}"
           console.log chrome.runtime.lastError

   # Should we synchronize this key?
@@ -118,22 +107,7 @@
     obj[key] = value
     obj

-  # Debugging messages.
-  # Disable debugginf by setting root.Sync.debug to anything falsy.
-  # Enabled for the time being (18/4/14) -- smblott.
   log: (msg) ->
     console.log "Sync: #{msg}" if @debug

-  checkHaveString: (thing) ->
-    if typeof(thing) != "string" or not thing
-      @log "Sync: Yikes! this should be a non-empty string: #{typeof(thing)} #{thing}"
-      return false
-    return true
-
-  checkHaveStringOrUndefined: (thing) ->
-    if ( typeof(thing) != "string" and typeof(thing) != "undefined" ) or ( typeof(thing) == "string" and not thing )
-      @log "Sync: Yikes! this should be a non-empty string or undefined: #{typeof(thing)} #{thing}"
-      return false
-    return true
-
 Sync.register()


update to set style-height dynamically

update to set style-height dynamically

--- app/classifier/tasks/text/index.cjsx
+++ app/classifier/tasks/text/index.cjsx
@@ -59,7 +59,11 @@
     onChange: NOOP

   getInitialState: ->
-    rows: 1
+    textareaHeight: undefined
+    initOffsetHeight: undefined
+
+  componentDidMount: ->
+    @setState initOffsetHeight: @refs.textInput.offsetHeight

   setTagSelection: (e) ->
     textTag = e.target.value
@@ -81,34 +85,25 @@
     @props.onChange newAnnotation

   handleClear: (e) ->
-    # keyCode 8 is backspace, 46 is delete
     if e.which is 8 or e.which is 46
-      @setState rows: 1
+      @setState textareaHeight: @state.initOffsetHeight

-  handleResize: ->
-    heightChange = @refs.textInput.scrollHeight - @refs.textInput.offsetHeight + 2
-    return unless heightChange > 0
-    rows = @state.rows
-    if heightChange % 22 is 0
-      # Chrome, Safari increment rows (heightChange) by 22
-      rows = rows + (heightChange / 22)
-    else
-      # Firefox and Edge increment rows by varying amounts, so the following handles the row increases noted
-      switch heightChange
-        when 30, 45
-          rows = rows + 2
-        when 52, 67
-          rows = rows + 3
-        when 75, 90
-          rows = rows + 4
-        else
-          rows = rows + 1
-    @setState rows: rows
+  updateHeight: ->
+    @setState textareaHeight: @refs.textInput.scrollHeight + 2

   render: ->
     <GenericTask question={@props.task.instruction} help={@props.task.help} required={@props.task.required}>
       <label className="answer">
-        <textarea autoFocus={@props.autoFocus} className="standard-input full" rows={@state.rows} ref="textInput" value={@props.annotation.value} onKeyDown={@handleClear} onChange={@handleChange} />
+        <textarea
+          autoFocus={@props.autoFocus}
+          className="standard-input full"
+          ref="textInput"
+          value={@props.annotation.value}
+          onChange={@handleChange}
+          rows="1"
+          style={height: @state.textareaHeight}
+          onKeyDown={@handleClear}
+        />
       </label>
       {if @props.task.text_tags
         <div className="transcription-metadata-tags">
@@ -118,11 +113,7 @@
     </GenericTask>

   handleChange: ->
+    @updateHeight()
     value = @refs.textInput.value
-
-    if @props.annotation.value?.length > value.length
-      @setState rows: 1
-    @handleResize()
-
     newAnnotation = Object.assign @props.annotation, {value}
     @props.onChange newAnnotation


delete the hub upload POST endpoint to improve security (and DDOS vector)

--- src/smc-hub/hub_http_server.coffee
+++ src/smc-hub/hub_http_server.coffee
@@ -213,101 +213,6 @@
             # winston.debug("STRIPE: webhook -- #{err}, #{misc.to_json(fields)}")
         res.send('')
     ###
-
-    router.post '/upload', (req, res) ->
-        if not hub_register.database_is_working()
-            res.status(500).send("file upload failed -- not connected to database")
-            return
-        # See https://github.com/felixge/node-formidable
-        # user uploaded a file
-        winston.debug("User uploading a file...")
-        form = new formidable.IncomingForm()
-        form.parse req, (err, fields, files) ->
-            if err or not files.file? or not files.file.path? or not files.file.name?
-                e = "file upload failed -- #{misc.to_safe_str(err)} -- #{misc.to_safe_str(files)}"
-                winston.debug(e)
-                res.status(500).send(e)
-                return # nothing to do -- no actual file upload requested
-
-            account_id = undefined
-            project_id = undefined
-            dest_dir   = undefined
-            data       = undefined
-            async.series([
-                (cb) ->
-                    # authenticate user
-                    cookies = new Cookies(req, res)
-                    # we prefix base_url to cookies mainly for doing development of SMC inside SMC.
-                    value = cookies.get(opts.base_url + 'remember_me')
-                    if not value?
-                        cb('you must enable remember_me cookies to upload files')
-                        return
-                    x    = value.split('$')
-                    hash = auth.generate_hash(x[0], x[1], x[2], x[3])
-                    opts.database.get_remember_me
-                        hash : hash
-                        cb   : (err, signed_in_mesg) =>
-                            if err or not signed_in_mesg?
-                                cb('unable to get remember_me cookie from db -- cookie invalid')
-                                return
-                            account_id = signed_in_mesg.account_id
-                            if not account_id?
-                                cb('invalid remember_me cookie')
-                                return
-                            winston.debug("Upload from: '#{account_id}'")
-                            project_id = req.query.project_id
-                            dest_dir   = req.query.dest_dir
-                            if dest_dir == ""
-                                dest_dir = '.'
-                            winston.debug("project = #{project_id}; dest_dir = '#{dest_dir}'")
-                            cb()
-                (cb) ->
-                    # auth user access to *write* to the project
-                    access.user_has_write_access_to_project
-                        database   : opts.database
-                        project_id : project_id
-                        account_id : account_id
-                        cb         : (err, result) =>
-                            #winston.debug("PROXY: #{project_id}, #{account_id}, #{err}, #{misc.to_json(result)}")
-                            if err
-                                cb(err)
-                            else if not result
-                                cb("User does not have write access to project.")
-                            else
-                                winston.debug("user has write access to project.")
-                                cb()
-                (cb) ->
-                    #winston.debug(misc.to_json(files))
-                    winston.debug("Reading file from disk '#{files.file.path}'")
-                    fs.readFile files.file.path, (err, _data) ->
-                        if err
-                            cb(err)
-                        else
-                            data = _data
-                            cb()
-
-                # actually send the file to the project
-                (cb) ->
-                    winston.debug("getting project...")
-                    project = hub_projects.new_project(project_id, opts.database, opts.compute_server)
-                    path = dest_dir + '/' + files.file.name
-                    winston.debug("writing file '#{path}' to project...")
-                    project.write_file
-                        path : path
-                        data : data
-                        cb   : cb
-
-            ], (err) ->
-                if err
-                    winston.debug(e)
-                    e = "file upload error -- #{misc.to_safe_str(err)}"
-                    res.status(500).send(e)
-                else
-                    res.send('received upload:\n\n')
-                # delete tmp file
-                if files?.file?.path?
-                    fs.unlink(files.file.path)
-            )

     # Get the http server and return it.
     if opts.base_url


delete a log

delete a log

--- src/smc-webapp/jupyter/status.cjsx
+++ src/smc-webapp/jupyter/status.cjsx
@@ -85,7 +85,6 @@
     render_name: ->
         display_name = @props.kernel_info?.get('display_name')
         if not display_name? and @props.kernels?
-            console.log(@props.kernels.toJS())
             # Definitely an unknown kernel
             closestKernel = closest_kernel_match(@props.kernel,@props.kernels)
             closestKernelDisplayName = closestKernel.get("display_name")