now the pantool kind of works

now the pantool kind of works

--- src/coffee/bokeh_views.coffee
+++ src/coffee/bokeh_views.coffee
@@ -634,19 +634,16 @@

           @_start_drag(e, x, y)
         else
-          @_drag(e, x, y)
+          @_drag(e.foo, e.foo, e, x, y)
           e.preventDefault()
           e.stopPropagation())

   mouse_coords : (e, x, y) ->
     [x_, y_] = [@plot_model.rxpos(x), @plot_model.rypos(y)]
-    console.log(x, x_, y, y_)
-    #return [x_, y_]
-    return [x, y]
+    return [x_, y_]

   _start_drag : (e, x, y) ->
-    console.log("_start_drag")
     @dragging = true
     [@x, @y] = @mouse_coords(e, x, y)
     xmappers = (@model.resolve_ref(x) for x in @mget('xmappers'))
@@ -668,7 +665,6 @@
       [x, y] = @mouse_coords(e, x__, y__)
       xdiff = x - @x
       ydiff = y - @y
-      console.log("diff", @x, x, xdiff)
       [@x, @y] = [x, y]
     xmappers = (@model.resolve_ref(x) for x in @mget('xmappers'))
     ymappers = (@model.resolve_ref(x) for x in @mget('ymappers'))


Add 'debug' emit category

Add 'debug' emit category

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -37,7 +37,8 @@
     @_callbacks = @_callbacks || {}
     # Create namespace for this event
     @_callbacks[event] = [] unless @_callbacks[event]
-    @_callbacks[event].push fn
+    if fn?
+      @_callbacks[event].push fn
     return @

   emit: (event, args...) ->
@@ -45,7 +46,7 @@
     callbacks = @_callbacks[event]

     if callbacks
-      callback.apply @, args for callback in callbacks
+      callback?.apply @, args for callback in callbacks

     return @

@@ -118,6 +119,7 @@
     "maxfilesexceeded"
     "maxfilesreached"
     "queuecomplete"
+    "debug"
   ]

   defaultOptions:
@@ -908,6 +910,7 @@
   _addFile: (file) ->
     matchingFiles = (f for f in @files when f.name == file.name)
     for matchingFile in matchingFiles
+      @emit "debug", "Removing duplicate file '#{matchingFile.name}'"
       @removeFile(matchingFile)

     if file.status != Dropzone.EXISTING


Dropzone now falls back nicely in IE8.

Dropzone now falls back nicely in IE8.

#39

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -226,8 +226,8 @@

     @element = document.querySelector @element if typeof @element == "string"
-
-    throw new Error "Invalid dropzone element." unless @element instanceof HTMLElement
+
+    throw new Error "Invalid dropzone element." unless @element instanceof (HTMLElement ? Element)

     throw new Error "Dropzone already attached." if Dropzone.forElement @element


client: home: better messaging for machine details

--- client/home/lib/virtualmachines/components/machineslist/machinedetails.coffee
+++ client/home/lib/virtualmachines/components/machineslist/machinedetails.coffee
@@ -85,12 +85,12 @@
   specs.push provider = jMachine.provider
   specs.push type = jMachine.meta?.instance_type ? 't2.micro'
   specs.push ram  = {
-    't2.nano'   : '512M'
-    't2.micro'  : '1GB'
-    't2.medium' : '4GB'
-  }[type] ? '1GB'
-  specs.push cpu = '1x'
-  specs.push disk = if size? then "#{size}GB" else 'N/A'
+    't2.nano'   : '512MB RAM'
+    't2.micro'  : '1GB RAM'
+    't2.medium' : '4GB RAM'
+  }[type] ? '1GB RAM'
+  specs.push cpu = '1x CPU'
+  specs.push disk = if size? then "#{size}GB HDD" else 'N/A'

   return specs


Hide the panel when it looses focues

Hide the panel when it looses focues

--- client/landing/app/Applications/Chat.kdapplication/Views/mainchatpanel.coffee
+++ client/landing/app/Applications/Chat.kdapplication/Views/mainchatpanel.coffee
@@ -2,7 +2,7 @@

   constructor:->
     super
-      cssClass : 'main-chat-panel visible'
+      cssClass : 'main-chat-panel'

     @registerSingleton "chatPanel", @, yes

@@ -10,6 +10,11 @@
     @conversationList = new ChatConversationListView
     @conversationListController = new ChatConversationListController
       view : @conversationList
+
+    @on 'PanelVisibilityChanged', (visible)=>
+      if visible
+        @getSingleton('windowController').addLayer @
+        @once 'ReceivedClickElsewhere', @bound 'hide'

   createConversation:(data)->
     # Data includes chatChannel and the conversation
@@ -19,6 +24,7 @@
     @addSubView @header
     @addSubView @conversationList
     @conversationListController.loadItems()
+    @show()

   show:->
     @setClass 'visible'


api.koding.com references removed

api.koding.com references removed

--- client/landing/app/Applications/Home.kdapplication/Views/welcomeheader.coffee
+++ client/landing/app/Applications/Home.kdapplication/Views/welcomeheader.coffee
@@ -10,7 +10,6 @@
     # else if $(event.target).is "a"
     if $(event.target).is "a"
       $.ajax
-        # url       : KD.config.apiUri+'https://api.koding.com/1.0/logout'
         url       : "/beta.txt"
         success   : (response)=>
           modal = new KDModalView


bind this

bind this

--- src/formdata.coffee
+++ src/formdata.coffee
@@ -21,7 +21,7 @@
     constructor: (@name, @value)->

     convertToString: ()->
-      new Promise((resolve)->
+      new Promise((resolve)=>
         s = []
         s.push("Content-Disposition: form-data; name=#{@name};#{LF}#{LF}")
         s.push("#{@value}#{LF}")
@@ -39,16 +39,15 @@
       , new Array(view.length)).join('')

     _readBlobAsArrayBuffer: ()->
-      self = @
-      new Promise((resolve)->
+      new Promise((resolve)=>
         reader = new FileReader()
-        reader.readAsArrayBuffer(self.souce)
-        reader.onload = ()->
-          resolve(self._readArrayBufferAsString(reader.result))
+        reader.readAsArrayBuffer(@souce)
+        reader.onload = ()=>
+          resolve(@_readArrayBufferAsString(reader.result))
       )

     _readBlobAsBinary: ()->
-      new Promise((resolve)->
+      new Promise((resolve)=>
         resolve(@souce.getAsBinary())
       )


Make parens optional and check for at prefix

--- lib/jasmine-node/failure-tree.coffee
+++ lib/jasmine-node/failure-tree.coffee
@@ -32,7 +32,7 @@
       @filterStackTrace(item)

   filterJasmineLines: (stackTraceLines) ->
-    jasminePattern = /\(.*\/jasmine.js:\d+:\d+\)\s*$/
+    jasminePattern = /^\s*at\s+.*\(?.*\/jasmine.js:\d+:\d+\)?\s*$/

     index = 0
     while index < stackTraceLines.length


updated drawgrid, changed strokestyle and stroke behaviour

--- clock.coffee
+++ clock.coffee
@@ -130,9 +130,8 @@
       @ctx.fillText(text, @canvas.width/2+offset, @canvas.height/2-@radii[item]+offset) if text

   drawGrid: ->
-    return false if not @config.show_grid
     @ctx.beginPath()
-    @ctx.lineWidth = 1
+    @ctx.lineWidth = 2
     @ctx.strokeStyle = "rgba(0,0,0,.5)"
     if @config.show_grid
       @ctx.moveTo(@canvas.width/2, 0)


properly reset private vars

properly reset private vars

--- src/core/resource/base.coffee
+++ src/core/resource/base.coffee
@@ -80,7 +80,7 @@
       f._reset()

     f._reset = () ->
-      f._t = null
+      f.__vars._t = null

     f.serialize = () ->
       #serialization here


Rebuild touch emulator after disabling

Rebuild touch emulator after disabling

--- framer/Extras/TouchEmulator.coffee
+++ framer/Extras/TouchEmulator.coffee
@@ -221,3 +221,4 @@
 exports.disable = ->
 	return unless touchEmulator
 	touchEmulator.destroy()
+	touchEmulator = null


Remove whitespace

Remove whitespace

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -862,20 +862,20 @@
       'textpath', 'title', 'tref', 'tspan', 'use', 'view', 'vkern']

     setSVGProp = (elt, name, value) ->
-    	if value instanceof ObsCell
-    		do (name) ->
-    			value.onSet.sub ([old, val]) -> elt.setAttribute name, value
-    	else
-    		elt.setAttribute name, value
-
-		updateSVGContents = (contents) ->
-    	(elt.removeChild elt.firstChild) while elt.firstChild
-    	if _.isArray(contents)
-    	  (elt.appendChild node) for node in toNodes(contents)
-    	else if _.isString(contents)
-    	  updateSVGContents([contents])
-    	else
-    	  throw 'Unknown type for contents: ' + contents.constructor.name
+      if value instanceof ObsCell
+        do (name) ->
+          value.onSet.sub ([old, val]) -> elt.setAttribute name, value
+      else
+        elt.setAttribute name, value
+
+    updateSVGContents = (contents) ->
+      (elt.removeChild elt.firstChild) while elt.firstChild
+      if _.isArray(contents)
+        (elt.appendChild node) for node in toNodes(contents)
+      else if _.isString(contents)
+        updateSVGContents([contents])
+      else
+        throw 'Unknown type for contents: ' + contents.constructor.name

     rxt.svg_mktag = mktag = (tag) ->
       (arg1, arg2) ->
@@ -885,20 +885,23 @@
         for name, value of _.omit(attrs, _.keys(specialAttrs))
           setSVGProp(elt, name, value)

-      	if contents instanceof ObsArray
-      	  contents.onChange.sub ([index, removed, added]) ->
-      	    (elt.removeChild elt.childNodes[index]) for i in [0...removed.length]
-      	    toAdd = toNodes(added)
-      	    if index == elt.childNodes.length
-      	      (elt.appendChild node) for node in toAdd
-      	    else
-      	      (elt.childNodes[index].insertBefore node) for node in toAdd
-      	else if contents instanceof ObsCell
-      	  contents.onSet.sub(([old, val]) -> updateSVGContents(val))
-      	else
-      	  updateSVGContents(contents)
+        console.log("mktag contents #{contents} ")
+        if contents?
+          if contents instanceof ObsArray
+            contents.onChange.sub ([index, removed, added]) ->
+              (elt.removeChild elt.childNodes[index]) for i in [0...removed.length]
+              toAdd = toNodes(added)
+              if index == elt.childNodes.length
+                (elt.appendChild node) for node in toAdd
+              else
+                (elt.childNodes[index].insertBefore node) for node in toAdd
+          else if contents instanceof ObsCell
+            contents.onSet.sub(([old, val]) -> updateSVGContents(val))
+          else
+            updateSVGContents(contents)

-      	for key of attrs when key of specialAttrs
+        console.log("mktag specialAttrs #{specialAttrs} ")
+        for key of attrs when key of specialAttrs
             specialAttrs[key](elt, attrs[key], attrs, contents)
         elt


Try to correct #329

Try to correct #329

--- lib/tooltip/function-provider.coffee
+++ lib/tooltip/function-provider.coffee
@@ -45,13 +45,13 @@
         else
           description += accessModifier + ' ' + returnType + ' <strong>' + term + '</strong>' + '('

-        if value.args.parameters.length > 0
+        if value.args.parameters?.length > 0
             description += value.args.parameters.join(', ');

-        if value.args.optionals.length > 0
+        if value.args.optionals?.length > 0
             description += '['

-            if value.args.parameters.length > 0
+            if value.args.parameters?.length > 0
                 description += ', '

             description += value.args.optionals.join(', ')


get builder class from opts

get builder class from opts

--- src/template.coffee
+++ src/template.coffee
@@ -1,5 +1,5 @@
 { EventEmitter } = require 'events'
-{ Builder } = require 'asyncxml'
+{ Builder:DefaultBuilder } = require 'asyncxml'
 EVENTS = ['new','add','attr','attr:remove','text','remove','data','close','end']

 schema =
@@ -122,6 +122,8 @@
         opts.self_closing = self_closing[s]?(opts)
         opts.schema = schema[s]?(opts).split(' ')
         opts.end ?= on
+        # get builder class from options
+        Builder = opts.Builder ? DefaultBuilder

         class ExtendedBuilder extends Builder
         ff ExtendedBuilder::, opts.schema


Export telepath Model

Export telepath Model

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -1,4 +1,4 @@
-{Document, Point, Range} = require 'telepath'
+{Document, Model, Point, Range} = require 'telepath'

 module.exports =
   _: require 'underscore-plus'
@@ -9,6 +9,7 @@
   File: require '../src/file'
   fs: require 'fs-plus'
   Git: require '../src/git'
+  Model: Model
   Point: Point
   Range: Range


Fix inheritance.

Fix inheritance.

--- src/fs_utils/file_list.coffee
+++ src/fs_utils/file_list.coffee
@@ -164,4 +164,4 @@
         file.removed = true if file and not file.disposed
     @resetTimer()

-util.inherits FileList, EventEmitter
+FileList.prototype.__proto__ = EventEmitter.prototype


pass namespaced classes fname in outside scope


Better exiting and refactor (#108)

Better exiting and refactor (#108)

* Better exiting and refactor

* Check websockpool exists

--- apis/js/koheron-websocket-client/src/kclient.coffee
+++ apis/js/koheron-websocket-client/src/kclient.coffee
@@ -32,6 +32,7 @@
         @pool = []
         @free_sockets = []
         @socket_counter = 0
+        @exiting = false

         for i in [0..@pool_size-1]
             if window?
@@ -44,9 +45,9 @@
             @pool.push(websocket)

             websocket.onopen = (evt) =>
-                # console.log "WebSocket " + @socket_counter.toString() + " connected to " + url + "\n"
-
                 @waitForConnection(websocket, 100, =>
+                    if @exiting then return
+                    # console.log "WebSocket " + @socket_counter.toString() + " connected to " + url + "\n"
                     console.assert(websocket.readyState == 1, "Websocket not ready")
                     @free_sockets.push(@socket_counter)
                     if @socket_counter == 0 then onopen_callback()
@@ -69,12 +70,17 @@
             , interval)

     getSocket: (sockid) ->
+        if @exiting then return null
         if sockid not in @free_sockets and sockid >= 0 and sockid < @pool_size
             return @pool[sockid]
         else
             throw new ReferenceError('Socket ID ' + sockid.toString() + ' not available')

     requestSocket: (callback) ->
+        if @exiting
+            callback(-1)
+            return
+
         if @free_sockets? and @free_sockets.length > 0
             sockid = @free_sockets[@free_sockets.length-1]
             @free_sockets.pop()
@@ -83,11 +89,14 @@
                 callback(sockid)
             )
         else # All sockets are busy. We wait a bit and retry.
-            setTimeout( =>
+            console.assert(@free_sockets.length == 0, 'Non empty free_sockets')
+            setTimeout( =>
                 @requestSocket(callback)
             , 100)

     freeSocket: (sockid) ->
+        if @exiting
+            return
         if sockid not in @free_sockets and sockid >= 0 and sockid < @pool_size
             @free_sockets.push(sockid)
         else
@@ -97,11 +106,12 @@
                 console.error "Undefined Socket ID"

     exit: ->
+        @exiting = true
+        @free_sockets = []
+        @socket_counter = 0
+
         for websocket in @pool
             websocket.close()
-
-        @free_sockets = []
-        @socket_counter = 0

 # === Helper functions to build binary buffer ===

@@ -276,6 +286,7 @@

     subscribeServerBroadcast: (callback) ->
         @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return callback(null, null)
             @broadcast_socketid = sockid
             broadcast_socket = @websockpool.getSocket(sockid)
             broadcast_socket.send(Command(1, 5, 'I', 0)) # Server broadcasts on channel 0
@@ -290,27 +301,33 @@
         )

     broadcastPing: ->
-        @websockpool.requestSocket( (sockid) =>
+        if not @websockpool? then return
+
+        @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return
             websocket = @websockpool.getSocket(sockid)
             websocket.send(Command(1, 6))
-            @websockpool.freeSocket(sockid)
+            if @websockpool?
+                @websockpool.freeSocket(sockid)
         )

     exit: ->
-        if @broadcast_socketid >= 0
-            @websockpool.freeSocket(@broadcast_socketid)
-            @broadcast_socketid = -1
         @websockpool.exit()
+        delete @websockpool

     # ------------------------
     #  Send
     # ------------------------

-    send: (cmd) ->
-        @websockpool.requestSocket( (sockid) =>
+    send: (cmd) ->
+        if not @websockpool? then return
+
+        @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return
             websocket = @websockpool.getSocket(sockid)
             websocket.send(cmd)
-            @websockpool.freeSocket(sockid)
+            if @websockpool?
+                 @websockpool.freeSocket(sockid)
         )

     ###*
@@ -341,95 +358,72 @@
                     websocket.onmessage = (evt) =>
                         array = new Uint32Array evt.data
                         cb(array[0])
+                        if @websockpool?
+                            @websockpool.freeSocket(sockid)
+                else
+                    if @websockpool?
                         @websockpool.freeSocket(sockid)
-                else
-                    @websockpool.freeSocket(sockid)
         )

     # ------------------------
     #  Receive
     # ------------------------

-    readUint32Array: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
+    _readBase: (cmd, fn) ->
+        if not @websockpool? then return fn(null)
+
+        @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return fn(null)
             websocket = @websockpool.getSocket(sockid)
             websocket.send(cmd)

             websocket.onmessage = (evt) =>
-                array = new Uint32Array evt.data
-                fn(array)
-                @websockpool.freeSocket(sockid)
+                fn(evt.data)
+                if @websockpool?
+                    @websockpool.freeSocket(sockid)
+        )
+
+    readUint32Array: (cmd, fn) ->
+        @_readBase(cmd, (data) =>
+            array = new Uint32Array data
+            fn(array)
         )

     readFloat32Array: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                array = new Float32Array evt.data
-                fn(array)
-                @websockpool.freeSocket(sockid)
+        @_readBase(cmd, (data) =>
+            array = new Float32Array data
+            fn(array)
         )

     readUint32: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                array = new Uint32Array evt.data
-                fn(array[0])
-                @websockpool.freeSocket(sockid)
+        @_readBase(cmd, (data) =>
+            array = new Uint32Array data
+            fn(array[0])
         )

     readInt32: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                array = new Int32Array evt.data
-                fn(array[0])
-                @websockpool.freeSocket(sockid)
+        @_readBase(cmd, (data) =>
+            array = new Int32Array data
+            fn(array[0])
         )

     readFloat32: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                array = new Float32Array evt.data
-                fn(array[0])
-                @websockpool.freeSocket(sockid)
+        @_readBase(cmd, (data) =>
+            array = new Float32Array data
+            fn(array[0])
         )

     readFloat64: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                array = new Float64Array evt.data
-                fn(array[0])
-                @websockpool.freeSocket(sockid)
+        @_readBase(cmd, (data) =>
+            array = new Float64Array data
+            fn(array[0])
         )

     readBool: (cmd, fn) ->
-        @readUint32(cmd, (num) ->
-            fn(num == 1)
-        )
+        @readUint32(cmd, (num) => fn(num == 1))

     readTuple: (cmd, fmt, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                fn(@deserialize(fmt, evt.data))
-                @websockpool.freeSocket(sockid)
-        )
+        @_readBase(cmd, (data) => fn(@deserialize(fmt, data)))

     deserialize: (fmt, data) ->
         dv = new DataView(data)
@@ -474,18 +468,10 @@
         return tuple

     readString: (cmd, fn) ->
-        @websockpool.requestSocket( (sockid) =>
-            websocket = @websockpool.getSocket(sockid)
-            websocket.send(cmd)
-
-            websocket.onmessage = (evt) =>
-                fn(evt.data.toString())
-                @websockpool.freeSocket(sockid)
-        )
+        @_readBase(cmd, (data) => fn(data.toString()))

     readJSON: (cmd, fn) ->
         @readString(cmd, (str) => fn(JSON.parse(str)))
-

     # ------------------------
     #  Devices
@@ -493,6 +479,7 @@

     getCmds: (callback) ->
         @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return fn(null)
             websocket = @websockpool.getSocket(sockid)
             console.assert(websocket.readyState == 1, 'Websocket [ID = ' + sockid.toString() + '] not ready')
             websocket.send(Command(1,1))
@@ -517,7 +504,8 @@

                 if evt.data == "EOC\n"
                     callback()
-                    @websockpool.freeSocket(sockid)
+                    if @websockpool?
+                        @websockpool.freeSocket(sockid)
         )

     getDeviceList: (fn) ->
@@ -529,6 +517,7 @@

     getDevStatus: (callback) ->
         @websockpool.requestSocket( (sockid) =>
+            if sockid < 0 then return fn(null)
             websocket = @websockpool.getSocket(sockid)
             websocket.send(Command(1,3))

@@ -544,7 +533,8 @@
                 else
                     console.log "Devices status updated"
                     callback()
-                    @websockpool.freeSocket(sockid)
+                    if @websockpool?
+                        @websockpool.freeSocket(sockid)
         )

     getDevice: (devname) ->


add `.btn-close` handler which removes closest `.close-wrap`

--- app/assets/javascripts/ab_admin/core/init.js.coffee
+++ app/assets/javascripts/ab_admin/core/init.js.coffee
@@ -63,6 +63,10 @@

   initHotkeys() if window.gon?.hotkeys

+  $(document).on 'click', '.btn-close', (e) ->
+    e.preventDefault()
+    $(this).closest('.close-wrap').remove()
+

 #    $('form .region_ac').regionAc()
 #    new NestedFieldsAdder


Add authorization check on admin route.

Add authorization check on admin route.

Since we're loading the model asynchronously a 403
from the server won't trigger the error state and the
subsequent transition back to the dashboard.

--- app/assets/javascripts/routes/admin_route.js.coffee
+++ app/assets/javascripts/routes/admin_route.js.coffee
@@ -1,4 +1,9 @@
 ETahi.AdminRoute = ETahi.AuthorizedRoute.extend
+  beforeModel: ->
+    Ember.$.ajax '/admin/journals/authorization',
+      headers:
+        'Tahi-Authorization-Check': true
+
   actions:
     viewUserDetails: (user) ->
       @controllerFor('adminJournalUser').set('model', user)


Add chapter image to Story info panel

Add chapter image to Story info panel

--- app/frontend/javascripts/views/story-info.js.coffee
+++ app/frontend/javascripts/views/story-info.js.coffee
@@ -40,6 +40,7 @@
         id:           @model.get('id')
         name:         @model.get('name')
         description:  @model.get('description')
+        image:        @model.get('image').huge.url
         edit:         @edit
         #image: if @node.get('image') then @node.get('image').huge.url else null
       }


fix for sales-summary quantity_purchased displaying the value as a currency

--- src/components/widgets/sales-summary/sales-summary.directive.coffee
+++ src/components/widgets/sales-summary/sales-summary.directive.coffee
@@ -76,9 +76,9 @@
         percentageInnerCutout: 50,
         tooltipFontSize: 12,
       }
-      angular.merge(pieOptions, {currency: 'hide'}) if $scope.filter.value=='quantity_sold'
+      angular.merge(pieOptions, {currency: 'hide'}) if $scope.filter.value.toLowerCase().indexOf('quantity') >= 0
       chartData = ChartFormatterSvc.pieChart(pieData, pieOptions)
-
+
       # calls chart.draw()
       $scope.drawTrigger.notify(chartData)


Only show text if different from canonical text

--- src/js/cts-cite-driver.coffee
+++ src/js/cts-cite-driver.coffee
@@ -29,7 +29,10 @@
   translation_div.append $('<span>').attr('class','urn').append(edit_translation_a)
   translation_div.append $('<span>').attr('class','author').text(translation[2])
   # translation_div.append $('<span>').attr('class','timestamp').text(translation[3])
-  translation_div.append $('<span>').attr('class','entry_text').text(translation[4])
+  canonical_translation = $("li##{urn_to_id(translation[1])} .source_text p").text()
+  if translation[4].trim() != canonical_translation.trim()
+    console.log("Canonical: #{canonical_translation}")
+    translation_div.append $('<span>').attr('class','entry_text').text(translation[4])
   translation_div.append $('<span>').attr('class','translation_text').text(translation[5])
   translation_div.append $('<span>').attr('class','note').text(translation[6])
   $("li##{urn_to_id(translation[1])}").append translation_div


Pass the Bridge instance to the callback so we don't have to muck with the context.

--- application/chrome/content/wesabe/dom/Bridge.coffee
+++ application/chrome/content/wesabe/dom/Bridge.coffee
@@ -60,7 +60,7 @@
       @dispatch response
     ), true

-    fn?.call?(this)
+    fn?.call?(this, this)

   getBridgeElement: ->
     @document.byId("_xulBridge")


use DocSignatures component

use DocSignatures component

--- src/renderer/components/doc-detail-parameters-table-component.coffee
+++ src/renderer/components/doc-detail-parameters-table-component.coffee
@@ -3,6 +3,7 @@
 Link = require './link-component'
 DocTableComponent = require './doc-table-component'
 colors = require './colors/color-definition'
+DocSignaturesTypeComponent = require './signatures/doc-signatures-type-component'

 ###
 @props.current [required] local current which is child of current factor
@@ -19,18 +20,7 @@
           alt_text = 'no description'
           table_row = []
           table_row.push {prm.name}
-          elm = []
-          elm.push {prm.type.name}
-          if prm.type.typeArguments?
-            elm.push {'<'}
-            prm.type.typeArguments.forEach (targ, i) ->
-              elm.push {targ.name}
-              if i != prm.type.typeArguments.length - 1
-                elm.push ,
-            elm.push {'>'}
-          if prm.type.isArray
-            elm.push []
-          table_row.push {elm}
+          table_row.push <DocSignaturesTypeComponent type={prm.type} emphasisStyle={styles.emphasis} />
           table_row.push {prm.comment?.shortText || prm.comment?.text || alt_text}
           table.push table_row
         <DocTableComponent prefix="#{@props.current.id}-prm" table={table} />
@@ -42,9 +32,6 @@

   type:
     color: colors.general.r.light
-
-  oblique:
-    fontStyle: 'italic'

   emphasis:
     color: colors.general.r.default


Fix log format

Fix log format

--- src/main/coffee/logger.coffee
+++ src/main/coffee/logger.coffee
@@ -33,7 +33,7 @@
     level = chalk.yellow level
   else if level is "DEBUG"
     level = chalk.blue level
-  console.log "#{chalk.gray timestamp()} #{level} #{chalk.cyan tag} #{message}"
+  console.log "#{chalk.gray timestamp()} #{level} [#{chalk.cyan tag}] #{message}"

 Logger.prototype.e = (message) -> log 'ERROR', @.tag, message if 0 <= this.level
 Logger.prototype.w = (message) -> log 'WARN',  @.tag, message if 1 <= this.level


add missing delegate reference

add missing delegate reference

--- workers/social/lib/social/models/computeproviders/provisioner.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.coffee
@@ -263,6 +263,7 @@
         return callback new KodingError 'Nothing to update'

       fieldsToUpdate = { 'meta.modifiedAt' : new Date }
+      { delegate }   = client.connection

       if content?


reqorded auto-complete disable input

reqorded auto-complete disable input

--- client/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -128,8 +128,8 @@
       pistachio : """

For links, please provide a protocol such as
         http://
-
-        Don't touch my Status Update.<input name="stop-sanitizing" class="stop-sanitizing" type="checkbox" />
+
+        Disable URL auto-complete.<input name="stop-sanitizing" class="stop-sanitizing" type="checkbox" />

       """


Fix typo in permissionmodal updatePermission method.

--- client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
+++ client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
@@ -111,13 +111,11 @@
         callback    : =>

           group.updatePermissions @reducedList(), (err,res)=>
-            log 'updated permissions',err,res
             @buttons["Save"].hideLoader()
-            # TODO: do something with this callback
             unless err
-              new KDNotificationView
+              new KDNotificationView
                 title : "Group permissions have been updated."
-            KD.showError "An error occured while updating permissions."
+            KD.showError err

     options.fields or= optionizePermissions roles, permissionSet
     super options,data


Workspace: Use htmlmixed syntax for other file extensions.

--- client/landing/CollaborativeWorkspace/panes/collaborativeeditorpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/collaborativeeditorpane.coffee
@@ -96,7 +96,7 @@
     CodeMirror.modeURL = "#{cdnRoot}/mode/%N/%N.js"
     fileExtension      = file.getExtension()
     syntaxHandler      = __aceSettings.syntaxAssociations[fileExtension]
-    modeName           = null
+    modeName           = "htmlmixed"
     corrections        =
       html             : "htmlmixed"
       json             : "javascript"


member feeder using left nav again

member feeder using left nav again

--- client/landing/app/Applications/Members.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Members.kdapplication/AppController.coffee
@@ -20,7 +20,7 @@
     KD.getSingleton("appManager").tell 'Feeder', 'createContentFeedController', {
       itemClass             : MembersListItemView
       listControllerClass   : MembersListViewController
-      useHeaderNav          : yes
+      useHeaderNav          : no
       noItemFoundText       : "There is no member."
       limitPerPage          : 10
       # onboarding            :


remove county label since LA and AK use different labels

--- source/javascripts/county-map.coffee
+++ source/javascripts/county-map.coffee
@@ -75,7 +75,7 @@

     el.append('span')
       .attr('class', 'title')
-      .text((d) -> "#{d.name.replace('County', '')} County")
+      .text((d) -> "#{d.name.replace('County', '')}")

     if props.male_18_24
       stars = el.append('ul')


For #2382: handle case of id already prefixed

--- src/resources-packaged/ops/javascript/orbeon/xforms/Document.coffee
+++ src/resources-packaged/ops/javascript/orbeon/xforms/Document.coffee
@@ -37,7 +37,7 @@

         [controlId, resolvedControl] =
             if _.isString(controlIdOrElem)
-                controlId = controlIdOrElem
+                givenControlId = controlIdOrElem

                 formId =
                     if _.isElement(formElem)
@@ -47,7 +47,16 @@

                 ns = ORBEON.xforms.Globals.ns[formId]

-                [controlId, document.getElementById(ns + controlId)]
+                # For backward compatibility, handle the case where the id is already prefixed.
+                # This is not great as we don't know for sure whether the control starts with a namespace, e.g. `o0`,
+                # `o1`, etc. It might be safer to disable the short namespaces feature because of this.
+                namespacedControlId =
+                    if (givenControlId.indexOf(ns) == 0)
+                        givenControlId
+                    else
+                        ns + givenControlId
+
+                [givenControlId, document.getElementById(namespacedControlId)]
             else
                 [controlIdOrElem.id, controlIdOrElem]


handle exception in parsing retrieved json from aws

--- app/coffee/MongoAWS.coffee
+++ app/coffee/MongoAWS.coffee
@@ -74,8 +74,11 @@

 		inputStream.on 'data', (line) ->
 				if line.length > 2
-					ops.push(JSON.parse(line))
-					sz += line.length
+					try
+						ops.push(JSON.parse(line))
+					catch err
+						return callback(err)
+					sz += line.length
 				if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE
 					inputStream.pause()
 					MongoAWS.handleBulk ops.slice(0), sz, () ->


Improve check to not throw errors in production (#5210)

--- app/core/initialize.coffee
+++ app/core/initialize.coffee
@@ -114,6 +114,7 @@

   showError = (text) ->
     return if currentErrors >= 3
+    return if app.isProduction() and not me.isAdmin() # Don't show noty error messages in production when not an admin
     return unless me.isAdmin() or document.location.href.search(/codecombat.com/) is -1 or document.location.href.search(/\/editor\//) isnt -1
     ++currentErrors
     unless webkit?.messageHandlers  # Don't show these notys on iPad


Update thang.coffee

Update thang.coffee

add getGoalState

--- app/lib/world/thang.coffee
+++ app/lib/world/thang.coffee
@@ -38,6 +38,9 @@
     event.thang = @
     @world.publishNote channel, event

+  getGoalState: (goalID) ->
+    @world.getGoalState goalID
+
   setGoalState: (goalID, status) ->
     @world.setGoalState goalID, status


Use jQuery "on" for event hook.

Use jQuery "on" for event hook.

--- app/assets/javascripts/spree/frontend/spree_multi_currency.js.coffee
+++ app/assets/javascripts/spree/frontend/spree_multi_currency.js.coffee
@@ -1,5 +1,5 @@
 $ ->
-  $('#currency').change ->
+  $('#currency').on 'change', ->
     $.ajax(
       type: 'POST'
       url: $(this).data('href')


fix(LMS-1755): fixed issue LMS-1755 Restricted file access not validating dates

--- app/coffeescripts/react_files/components/RestrictedDialogForm.coffee
+++ app/coffeescripts/react_files/components/RestrictedDialogForm.coffee
@@ -63,6 +63,10 @@

     setRestrictedAccess: ->
       attributes = @refs.restrictedSelection.extractFormValues()
+      if Date.parse(attributes.unlock_at) and Date.parse(attributes.lock_at)
+        if attributes.unlock_at >= attributes.lock_at
+          return $.flashError(I18n.t('There was an error setting availability dates.'))
+
       promises = @props.models.map (item) ->
         # Calling .save like this (passing data as the 'attrs' property on
         # the 'options' argument instead of as the first argument) is so that we just send


Hide embed box if there is an error

Hide embed box if there is an error

--- client/app/Applications/Activity.kdapplication/views/embedbox.coffee
+++ client/app/Applications/Activity.kdapplication/views/embedbox.coffee
@@ -304,6 +304,7 @@
       @hide()
     else
       log "EmbedBox encountered an Error!",data?.error_type,data?.error_message
+      @hide()

   embedExistingData:(data={},options={},callback=noop)=>
     unless data.type is "error"


group read me: button display logic changed, get rid of markdown encoding raw html default string

--- client/app/Applications/Groups.kdapplication/Views/readmeview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/readmeview.coffee
@@ -80,16 +80,19 @@
     group.fetchReadme (err, readme)=>
       if not err and readme?
         partial = readme.content or getDefaultGroupReadme group.title
-        group.canEditGroup (err, allowed)=>
-          @readmeEditButton.show() if allowed
+        # group.canEditGroup (err, allowed)=>
+        #   @readmeEditButton.show() if allowed
       else
-        partial =
+        partialHTML =
           if err?.message then err.message
           else @getDefaultGroupReadme group.title

+      group.canEditGroup (err, allowed)=>
+        @readmeEditButton.show() if allowed
+
       #SA: is this line being used anywhere, this overrides the logic above
       #@readme = readme?.content or partial
-      @readmeView.updatePartial @utils.applyMarkdown partial
+      @readmeView.updatePartial partialHTML or @utils.applyMarkdown partial
       @highlightCode()
       JView::viewAppended.call @
       @emit "readmeReady"


update foundation

update foundation

--- data/points/foundation.coffee
+++ data/points/foundation.coffee
@@ -2,7 +2,7 @@
 title: "Foundation"
 function: [ "layout-framework" ]
 language: [ "sass", "javascript", "css", "html" ]
-theory: [ "responsive-layout" ]
+theory: [ "responsive-layout", "flat-design" ]
 relevance: [ "bootstrap", "sass" ]
 desc: "一个Mobile First的前端响应式布局的框架。"
 reference:


Added warnings for missing scene text & choices

--- src/NovelManager.coffee
+++ src/NovelManager.coffee
@@ -107,8 +107,10 @@
       s.combinedText = ""
       s.parsedText = ""
       if s.text == undefined
+        console.warn "WARNING! scene "+s.name+" has no text"
         s.text = ""
       if s.choices == undefined
+        console.warn "WARNING! scene "+s.name+" has no choices"
         s.choices = []
       for c in s.choices
         c.parsedText = ""


Fixed issues with duplicating projects.

Fixed issues with duplicating projects.

--- src/ProjectUtils.coffee
+++ src/ProjectUtils.coffee
@@ -19,7 +19,7 @@
     collections = _.without(CollectionUtils.getAll(), Projects)
     _.each Collections.getMap(collections), (collection, name) ->
       # TODO(aramk) This will ignore collectionType references which don't have "projectId" fields.
-      result[name] = collection.findByProject?(id).fetch()
+      result[name] = collection.findByProject?(id).fetch() ? []
     result

   # Constructs new models from the given JSON serialization. IDs are used to retain references
@@ -46,10 +46,11 @@
       idMap = idMaps[name] = {}
       _.each json[name], (model) ->
         oldModelId = model._id
+        oldModel = collection.findOne(oldModelId)
         # Ignore documents in the JSON which don't have a projectId and exist in the collection,
         # indicating they should be shared between projects.
-        projectId = SchemaUtils.getParameterValue(model, SchemaUtils.projectIdProperty)
-        return if !projectId? && collection.findOne(oldModelId)?
+        projectId = model[SchemaUtils.projectIdProperty]
+        return if collection != Projects && !projectId? && oldModel?
         createDf = Q.defer()
         createDfs.push(createDf.promise)
         delete model._id
@@ -63,6 +64,7 @@
             newModelId = result
             idMap[oldModelId] = newModelId
             createDf.resolve(newModelId)
+
     refDfs = []
     Q.all(createDfs).then(Meteor.bindEnvironment(
       ->
@@ -105,10 +107,13 @@
   # @returns {Object.<String, Object>} A map of collection names to maps of old IDs to new IDs for
   #     the models in that collection.
   duplicate: (id, args) ->
+    Logger.info('Duplicating project', id, args)
     json = @toJson(id)
     if args?.callback?
       json = args.callback(json) ? json
-    @fromJson(json)
+    result = @fromJson(json)
+    Logger.info('Duplicated project', id)
+    result

   downloadInBrowser: (id) ->
     Logger.info 'Exporting project', id
@@ -221,7 +226,7 @@
     _.each CollectionUtils.getAll(), (collection) ->
       count = collection.remove(selector)
       if count > 0
-        console.log('Removed ' + count + ' ' + Collections.getName(collection))
+        Logger.info('Removed ' + count + ' ' + Collections.getName(collection))
     files = Files.find(selector).fetch()
     Logger.info('Removing files for project', doc, files)
     Files.remove(selector)


Fix major bug causing server crash on exception

--- src/ProjectUtils.coffee
+++ src/ProjectUtils.coffee
@@ -235,9 +235,9 @@
   # Updating project or models in the project will update the modified date of a project.

   getCurrentDate = -> moment().toDate()
-  updateProjectModifiedDate = _.throttle Meteor.bindEnvironment (projectId, userId) ->
+  updateProjectModifiedDate = Meteor.bindEnvironment (projectId, userId) ->
     Projects.update projectId, $set: {dateModified: getCurrentDate(), userModified: userId}
-  , 5000
+    updateProjectModifiedDate = _.throttle(updateProjectModifiedDate, 5000)

   Projects.before.insert (userId, doc) ->
     unless doc.dateModified


payment: subscription controls for account page

--- client/landing/app/Applications/Account.kdapplication/views/subscriptions.coffee
+++ client/landing/app/Applications/Account.kdapplication/views/subscriptions.coffee
@@ -11,14 +11,36 @@
     list = @getListView()

     list.on 'ItemWasAdded', (item) =>
-      item.on 'UnsubscribeRequested', ->
-        modal = KDModalView.confirm
-          title       : 'Are you sure?'
-          description : 'Are you sure you want to cancel your subscription?'
-          ok          :
-            title     : 'Unsubscribe'
+      subscription = item.getData()
+
+      item.on 'UnsubscribeRequested', =>
+        @confirm 'cancel', subscription
+
+      item.on 'ReactivateRequested', =>
+        @confirm 'resume', subscription

       item.on 'PlanChangeRequested', -> debugger
+
+  getConfirmationText = (action, subscription) -> switch action
+    when 'cancel'
+      'Are you sure you want to cancel this subscription?'
+    when 'resume'
+      'Are you sure you want to reactivate this subscription?'
+
+  getConfirmationButtonText = (action, subscription) -> switch action
+    when 'cancel' then 'Unsubscribe'
+    when 'resume' then 'Reactivate'
+
+  confirm: (action, subscription, callback) ->
+    modal = KDModalView.confirm
+      title       : 'Are you sure?'
+      description : getConfirmationText action, subscription
+      subView     : new SubscriptionView {}, subscription
+      ok          :
+        title     : getConfirmationButtonText action, subscription
+        callback  : callback ? => subscription[action] =>
+          modal.destroy()
+          @loadItems()

   loadItems:->
     @removeAllItems()
@@ -48,7 +70,6 @@
       iconClass : 'refresh'
       callback  : @getListView().emit.bind @getListView(), 'Reload'

-
 class AccountSubscriptionsList extends KDListView

   constructor:(options={}, data)->
@@ -56,7 +77,6 @@
     options.itemClass or= AccountSubscriptionsListItem

     super options, data
-

 class AccountSubscriptionsListItem extends KDListItemView

@@ -69,75 +89,55 @@

     @subscription = new SubscriptionView {}, @getData()

-    @changeLink = new CustomLinkView
-      title     : 'change plan'
-      click     : (e) =>
-        e.preventDefault()
-        @emit 'PlanChangeRequested'
+    @controls = new SubscriptionControls {}, @getData()

-    @unsubscribeLink = new CustomLinkView
-      title     : 'unsubscribe'
-      click     : (e) =>
-        e.preventDefault()
-        @emit 'UnsubscribeRequested'
-
-
-  # confirmOperation: (message, cb)->
-  #   modal = new KDModalView
-  #     title        : 'Warning'
-  #     content      : "
#{message}
"
-  #     height       : 'auto'
-  #     overlay      : yes
-  #     buttons      :
-  #       Continue   :
-  #         loader   :
-  #           color  : '#ffffff'
-  #           diameter : 16
-  #         style    : 'modal-clean-gray'
-  #         callback : ->
-  #           modal.destroy()
-  #           cb?()
-
-  # editPlan: (listView, data)->
-  #   cb = (err)-> listView.emit 'Reload'  unless err
-
-  #   if data.status is 'canceled'
-  #     @confirmOperation 'Are you sure you want to resume your subscription?', ->
-  #       data.resume cb
-  #   else
-  #     @confirmOperation 'Are you sure you want to cancel your subscription?', ->
-  #       data.cancel cb
+    @forwardEvents @controls, [
+      'PlanChangeRequested'
+      'UnsubscribeRequested'
+      'ReactivateRequested'
+    ]

   viewAppended: JView::viewAppended

-  describeSubscription: (quantity, verbPhrase) ->
-    """
-    Subscription for #{ @utils.formatPlural quantity, 'plan' } #{verbPhrase}
-    """
-
   pistachio:->
-    { quantity, plan, status, renew, expires } = @getData()
-
-    { feeAmount } = plan
-
-    statusNotice = ''
-    if status in ['active', 'modified']
-      statusNotice = @describeSubscription quantity, "is active"
-    else if status is 'canceled'
-      statusNotice = @describeSubscription quantity, "will end soon"
-
-    dateNotice = ''
-    if plan.type isnt 'single'
-      if status is 'active'
-        dateNotice = "Plan will renew on #{dateFormat renew}"
-      else if status is 'canceled'
-        dateNotice = "Plan will be available till #{dateFormat expires}"
-
-    displayAmount = @utils.formatMoney feeAmount / 100
-
     """

       {{> @subscription}}
-
{{> @changeLink}} | {{> @unsubscribeLink}}

+      {{> @controls}}

     """
+
+class SubscriptionControls extends JView
+
+  getStatusInfo: (subscription = @getData()) ->
+    switch subscription.status
+      when 'active'
+        text        : 'unsubscribe'
+        event       : 'UnsubscribeRequested'
+        showChange  : yes
+      when 'canceled', 'expired'
+        text        : 'reactivate'
+        event       : 'ReactivateRequested'
+        showChange  : no
+
+  viewAppended: ->
+    @unsetClass 'kdview'
+    @setClass 'controls'
+
+    { text, event, showChange } = @getStatusInfo()
+
+    if showChange
+      @changeLink = new CustomLinkView
+        title     : 'change plan'
+        click     : (e) =>
+          e.preventDefault()
+          @emit 'PlanChangeRequested'
+      @addSubView @changeLink
+      @setPartial ' | '
+
+    @statusLink = new CustomLinkView
+      title     : text
+      click     : (e) =>
+        e.preventDefault()
+        @emit event
+    @addSubView @statusLink


toggle _rollbar logging to prevent error msgs from 3rd party app developers

--- client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -98,6 +98,7 @@
             left : -5
         click    : =>
           @showLoader()
+          @utils.stopLoggingToRollbar()
           @getSingleton("kodingAppsController").compileApp \
             manifest.name, (err)=>
               @hideLoader()


Fix js error when expanding graf.

Fix js error when expanding graf.

Set placeholder value for relationship captions.

--- community/browser/lib/visualization/utils/pairwiseArcsRelationshipRouting.coffee
+++ community/browser/lib/visualization/utils/pairwiseArcsRelationshipRouting.coffee
@@ -21,7 +21,7 @@
           "external"

   shortenCaption: (relationship, caption, targetWidth) ->
-    shortCaption = caption
+    shortCaption = caption || 'caption'
     while true
       if shortCaption.length <= 2
         return ['', 0]


Fix: Exp number incremen on master. Sage/CODAP

Original work didn't account for new documents loading without having run a simulation at least once.

Story:
Experiment increment doesn't work in translated versions of SageModeler/CODAP

[#159214059]
https://www.pivotaltracker.com/story/show/159214059

--- src/code/models/codap-connect.coffee
+++ src/code/models/codap-connect.coffee
@@ -95,9 +95,10 @@
       if !ret or ret.success
         if (attrs = ret?.values?.collections?[1]?.attrs?)
           @_initialSyncAttributeProperties attrs
-        @_getExperimentNumber()
       else
         @_createDataContext()
+      @updateExperimentColumn()
+      @_getExperimentNumber()

   # initial synchronization; primarily used for synchronizing legacy documents
   _initialSyncAttributeProperties: (attrs, isLoaded) ->
@@ -106,7 +107,7 @@
     if @_attrsToSync and @_attrsAreLoaded
       @_syncAttributeProperties @_attrsToSync, true
       @_attrsToSync = null
-      @updateExperimentColumn()
+

   _createDataContext: ->
     sampleDataAttrs = @_getSampleAttributes()


Remove attribute instead of modifying

Remove attribute instead of modifying

--- resources/assets/coffee/_classes/forum-topic-title.coffee
+++ resources/assets/coffee/_classes/forum-topic-title.coffee
@@ -38,7 +38,7 @@

   cancel: =>
     @abort()
-    $(@toggleables).attr('data-title-edit', 0)
+    $(@toggleables).removeAttr('data-title-edit')
     @input[0].value = @input[0].defaultValue


remove erroreous references to text properties

--- static/coffee/renderers/image.coffee
+++ static/coffee/renderers/image.coffee
@@ -3,7 +3,6 @@

 Glyph = glyph.Glyph
-text_properties = glyph.text_properties

 image = (view, glyphspec, data) ->
@@ -17,7 +16,6 @@
   [sx, sy] = view.map_to_screen(x, glyph.x.units, y, glyph.y.units)
   image = (glyph.select("image", obj) for obj in data)
   angle = (glyph.select("angle", obj) for obj in data) # TODO deg/rad
-  text = (glyph.select("text", obj) for obj in data)

   # fast and slow paths are the same
   for i in [0..sx.length-1]


Use thread id for comment id

Use thread id for comment id

--- services/document-updater/app/coffee/RangesTracker.coffee
+++ services/document-updater/app/coffee/RangesTracker.coffee
@@ -107,7 +107,7 @@
 		addComment: (op, metadata) ->
 			# TODO: Don't allow overlapping comments?
 			@comments.push comment = {
-				id: @newId()
+				id: op.t or @newId()
 				op: # Copy because we'll modify in place
 					c: op.c
 					p: op.p


Refresh nav on content type creation

Refresh nav on content type creation

--- BackofficeBundle/Resources/public/coffee/FullPageFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/FullPageFormView.coffee
@@ -36,6 +36,7 @@
     $(event.target).ajaxSubmit
       context: button: $(".submit_form",event.target).parent()
       success: (response) ->
+        displayMenu() if $(event.target).attr('name') is 'content_type'
         new viewClass(viewContext.addOption(
           html: response
         ))


[client-app] remove unused async message body loading & `returnsModel`

Summary:
We no longer optimistically fetch messages when we load their bodies. We
always get the full message with bodies returned via the in memory delta
stream

Test Plan: manual

Reviewers: spang, halla, juan

Reviewed By: spang, halla, juan

Differential Revision: https://phab.nylas.com/D4066

--- packages/client-app/internal_packages/message-list/lib/message-item-body.cjsx
+++ packages/client-app/internal_packages/message-list/lib/message-item-body.cjsx
@@ -31,7 +31,6 @@
     @state =
       showQuotedText: DraftHelpers.isForwardedMessage(@props.message)
       processedBody: null
-      error: null

   componentWillMount: =>
     @_unsub = MessageBodyProcessor.subscribe @props.message, (processedBody) =>
@@ -39,7 +38,6 @@

   componentDidMount: =>
     @_mounted = true
-    @_onFetchBody() if not _.isString(@props.message.body)

   componentWillReceiveProps: (nextProps) ->
     if nextProps.message.id isnt @props.message.id
@@ -70,11 +68,6 @@
         message={@props.message}
         onLoad={@props.onLoad}
       />
-    else if @state.error
-

-        Sorry, this message could not be loaded. (Status code {@state.error.statusCode})
-        Try Again
-

     else

         <RetinaImg
@@ -92,23 +85,6 @@
   _toggleQuotedText: =>
     @setState
       showQuotedText: !@state.showQuotedText
-
-  _onFetchBody: =>
-    request = new NylasAPIRequest
-      api: NylasAPI
-      options:
-        path: "/messages/#{@props.message.id}"
-        accountId: @props.message.accountId
-        returnsModel: true
-    request.run()
-    .then =>
-      return unless @_mounted
-      @setState({error: null})
-      # message will be put into the database and the MessageBodyProcessor
-      # will provide us with the new body once it's been processed.
-    .catch (error) =>
-      return unless @_mounted
-      @setState({error})

   _mergeBodyWithFiles: (body) =>
     # Replace cid: references with the paths to downloaded files


Improve locationData of implicit object braces.

Set location of generated left brace to start of content, so the corresponding AST nodes don't span preceding spaces, blank lines or comments.

--- rewriter.coffee
+++ rewriter.coffee
@@ -325,12 +325,17 @@
     @scanTokens (token, i, tokens) ->
       return 1 if     token[2]
       return 1 unless token.generated or token.explicit
-      {last_line, last_column} = tokens[i - 1]?[2] ? last_line: 0, last_column: 0
+      if token[0] is '{' and nextLocation=tokens[i + 1]?[2]
+          {first_line: line, first_column: column} = nextLocation
+      else if prevLocation = tokens[i - 1]?[2]
+          {last_line: line, last_column: column} = prevLocation
+      else
+          line = column = 0
       token[2] =
-        first_line:   last_line
-        first_column: last_column
-        last_line:    last_line
-        last_column:  last_column
+        first_line:   line
+        first_column: column
+        last_line:    line
+        last_column:  column
       1

   # Because our grammar is LALR(1), it can't handle some single-line


make the connection log messages nicer

make the connection log messages nicer

--- salvus/client_browser.coffee
+++ salvus/client_browser.coffee
@@ -2,13 +2,15 @@

 exports.connect = (url) ->
     new Connection(url)
+
+{walltime} = require('misc')
+t = walltime()

 class Connection extends client.Connection
     _connect: (url, ondata) ->
         @url = url
         @ondata = ondata
         console.log("websocket -- connecting to '#{url}'...")
-        t = require('misc').mswalltime()

         opts =
             ping      : 8000
@@ -22,7 +24,7 @@
         conn = new Primus(url, opts)
         @_conn = conn
         conn.on 'open', () =>
-            console.log("websocket -- open", require('misc').mswalltime(t))
+            console.log("websocket -- connected in #{walltime(t)} seconds")
             @_connected = true
             @emit("connected", 'websocket')

@@ -37,6 +39,7 @@
         conn.on 'close', () =>
             console.log("websocket -- closed")
             @_connected = false
+            t = walltime()
             @emit("connecting")

         conn.on 'data', (data) =>


storage: fix bug in how optimal sequence computed

--- salvus/storage_server.coffee
+++ salvus/storage_server.coffee
@@ -355,7 +355,7 @@
         x = w[w.length-1].split('--')
         if x[0] == x[1]
             return w
-        v = v.shift()  # delete first element -- it's not the end of a valid sequence.
+        v.shift()  # delete first element -- it's not the end of a valid sequence.

 projects = {}


add placeholder object to contentful config so it's not immediately broken

--- root/contentful.coffee
+++ root/contentful.coffee
@@ -2,7 +2,7 @@
   access_token: '<%= delivery_token %>'
   management_token: '<%= management_token %>'
   space_id: '<%= space_id %>'
-  content_types:
+  content_types: {}                   # remove these object braces once the config below is filled out
     # posts:                          # data will be made available through this key on the `contentful` object in your templates
     #   id: 'xxxx'                    # ID of your content type
     #   filters: {}                   # passes filters to the call to contentful's API, see contentful's docs for more info


Add tests for nextPage override.

Add tests for nextPage override.

Fixes #711

--- test/dynamodb/document_client.coffee
+++ test/dynamodb/document_client.coffee
@@ -581,3 +581,56 @@
       docClient.get {Key: foo: 1}, (err, data) ->
         expect(data).to.eql(output)
         done()
+
+  describe 'response.nextPage', ->
+
+    client = null; response = null; request = null
+
+    beforeEach ->
+      client = new AWS.DynamoDB.DocumentClient({paramValidation: false})
+      request = client.query({ExpressionAttributeValues: { foo: 'bar' }})
+      response = request.response
+
+    fill = (err, data) ->
+      request.emit('validate', [request])
+      response.error = err
+      response.data = data
+
+    it 'returns null if there are no more pages', ->
+      fill(null, {})
+      expect(response.nextPage()).to.equal(null)
+
+    it 'returns a request object with the next page marker filled in params', ->
+      fill(null, LastEvaluatedKey: { fizz: 'pop' })
+      req = response.nextPage()
+      expect(req.params.ExclusiveStartKey.fizz).to.equal('pop')
+      expect(req.operation).to.equal(response.request.operation)
+
+    it 'uses untranslated params', ->
+      fill(null, LastEvaluatedKey: 'baz')
+      req = response.nextPage()
+      expect(req.params.ExpressionAttributeValues.foo).to.equal('bar')
+
+    it 'throws error if response returned an error and there is no callback', ->
+      fill(new Error('error!'), null, true)
+      expect(-> response.nextPage()).to.throw('error!')
+
+    it 'sends the request if passed with a callback', (done) ->
+      helpers.mockHttpResponse 200, {}, ['']
+      fill(null, LastEvaluatedKey: 'baz')
+      response.nextPage (err, data) ->
+        expect(err).to.equal(null)
+        expect(data).to.eql({})
+        done()
+
+    it 'passes null to callback if there are no more pages', ->
+      fill(null, {})
+      response.nextPage (err, data) ->
+        expect(err).to.equal(null)
+        expect(data).to.equal(null)
+
+    it 'passes error through if original response returned an error', ->
+      fill('error!', null)
+      response.nextPage (err, data) ->
+        expect(err).to.equal('error!')
+        expect(data).to.equal(null)


Clean up

Clean up

--- elements/QuetzalElement.coffee
+++ elements/QuetzalElement.coffee
@@ -70,6 +70,9 @@
     if elementClass::hasOwnProperty "style"
       style = "<style>#{elementClass::style}</style>"

+    # Wire up the contentChanged event.
+    # REVIEW: The class should have some way of indicating it actually wants
+    # to consume this event so we don't create unnecessary observers.
     observer = new MutationObserver =>
       event = document.createEvent "CustomEvent"
       event.initCustomEvent "contentChanged", false, false, null
@@ -80,6 +83,7 @@
       subtree: true

     if style? or @template?
+      # Create the shadow DOM and populate it.
       root = @webkitCreateShadowRoot()
       innerHTML = style ? ""
       innerHTML += @template ? ""
@@ -137,9 +141,6 @@
       elementClass = elementClass.__super__?.constructor
     null

-  # Holds wrapper for each class.
-  _wrappers: {}
-

 ###
 Allow registration of Quetzal element classes with browser.


groups: group creation payment wiring

groups: group creation payment wiring

--- client/landing/app/Applications/Groups.kdapplication/Views/groupcreation.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupcreation.coffee
@@ -21,11 +21,8 @@
     # p.getToken ->
     #   console.log "Emailed PIN"

-  charge:(callback)->
-    p.subscribe
-      pin: '0000'
-    , (err, subs)->
-      console.log subs
+  charge:(p, callback)->
+    p.subscribe {pin: '0000'}, callback

   viewAppended:->

@@ -49,6 +46,7 @@
           change               : =>
             # log @typeSelector.inputs.selector.getValue()
             @hostSelector?.show()
+            @setPositions()

     @fetchRecurlyPlans (err, plans)=>

@@ -62,7 +60,9 @@
             title                : "Pay & Create the Group"
             style                : "modal-clean-gray hidden"
             type                 : "button"
-            callback             : -> log "hello"
+            callback             : =>
+
+
         fields                   :
           label                  :
             itemClass            : KDCustomHTMLView
@@ -86,6 +86,7 @@
               submit.show()
               desc.$('p').addClass 'hidden'
               desc.$('p').eq(index).removeClass 'hidden'
+              @setPositions()
           desc                   :
             itemClass            : KDCustomHTMLView
             cssClass             : "hidden"


:new: Add a spec about attribute change firing

--- spec/custom-element-spec.coffee
+++ spec/custom-element-spec.coffee
@@ -5,7 +5,7 @@
   it 'fires the events in order', ->
     lastRan = null
     element = new (registerCustomElement(getCustomElement({
-      name: 'x-custom-element'
+      name: 'x-spec-event-order'
       created: -> lastRan = 'created'
       initialize: -> lastRan = 'initialize'
       attached: ->
@@ -22,3 +22,14 @@
     element.remove()
     expect(lastRan).toBe('detached')

+  it 'does not fire attributeChanged on config change', ->
+    executed = false
+    element = new (registerCustomElement(getCustomElement({
+      name: 'x-spec-attributechanged-fired'
+      attributeChanged: -> executed = true
+      config: {asd: {type: Boolean, default: false}}
+    })))
+    element.setAttribute('asd', false)
+    expect(executed).toBe(false)
+    element.setAttribute('asd-a', false)
+    expect(executed).toBe(true)


updating atom snippets

updating atom snippets

--- atom.symlink/snippets.cson
+++ atom.symlink/snippets.cson
@@ -14,22 +14,48 @@
 #     'body': 'console.log $1'
 #

+# HTML
+'.text.html':
+  'Empty favicon':
+    'prefix': 'favicon'
+    'body': '<link rel="icon" href="data:;base64,iVBORw0KGgo=">'
+  'Lets get startet':
+    'prefix': 'html'
+    'body': """
+      <!DOCTYPE html>
+      <html lang="da">
+      <head>
+        <meta charset="utf-8">
+        <meta http-equiv="x-ua-compatible" content="ie=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>$1</title>
+        <base href="/">
+        <link rel="stylesheet" href="styles.css">
+        <style>
+          body { margin:0; }
+        </style>
+      </head>
+      <body>
+        $2
+        <script src="app.js"></script>
+      </body>
+      </html>
+    """
+
+# JavaScript
+'.source.js':
+  'Prevent default':
+    'prefix': 'pd'
+    'body': 'e.preventDefault()'
+  'Use Strict':
+    'prefix': 'us'
+    'body': '\'use strict\''
+  'Console log':
+    'prefix': 'log'
+    'body': 'console.log($1)'
+
+# TypeScript
 '.source.ts':
   'Prevent default':
     'prefix': 'log'
     'body': 'console.log($1);'
-
-'.source.js':
-  'Prevent default':
-    'prefix': 'pd'
-    'body': 'e.preventDefault();'
-
-'.source.js':
-  'Use Strict':
-    'prefix': 'us'
-    'body': '\'use strict\';'
-
-'.text.html':
-  'Empty favicon':
-    'prefix': 'favicon'
-    'body': '<link rel="icon" href="data:;base64,iVBORw0KGgo=">'


add context block snippet

add context block snippet

--- atom.symlink/snippets.cson
+++ atom.symlink/snippets.cson
@@ -31,3 +31,11 @@
 import React from 'react';
 import { mount } from 'enzyme';
     """
+
+'.coffee':
+  'context test block':
+    'prefix': 'co'
+    'body': """
+      context '${1:someContext}', ->
+
+    """


AJ-2481 clean up annoying angular.js formatting error

--- app/assets/javascripts/angular/controllers/GradeRubricCtrl.js.coffee
+++ app/assets/javascripts/angular/controllers/GradeRubricCtrl.js.coffee
@@ -3,7 +3,8 @@
   $scope.metrics = []
   $scope.gradedMetrics = []
   $scope.courseBadges = {}
-  $scope.rubricGrades = {} # index in hash with metric_id as key
+  # index in hash with metric_id as key
+  $scope.rubricGrades = {}

   $scope.pointsPossible = 0
   $scope.pointsGiven = 0
@@ -251,7 +252,10 @@
     this.comments = ""
     this.id = if attrs.id then attrs.id else null
     this.rubricGrade = $scope.rubricGrades[this.id]
-    this.rubricGradeTierId = this.rubricGrade ? this.rubricGrade.tier_id : null
+    if this.rubricGrade is not null
+      this.rubricGradeTierId = this.rubricGrade.tier_id
+    else
+      this.rubricGradeTierId = null
     this.addTiers(attrs["tiers"]) if attrs["tiers"] #add tiers if passed on init
     this.name = if attrs.name then attrs.name else ""
     this.rubricId = if attrs.rubric_id then attrs.rubric_id else $scope.rubricId
@@ -262,7 +266,7 @@
       self = this
       newTier = new TierPrototype(self, attrs)
       self.tiers.push newTier
-      if rubricGrade and self.rubricGradeTierId == newTier.id
+      if self.rubricGrade and self.rubricGradeTierId == newTier.id
         self.selectedTier = newTier
     addTiers: (tiers)->
       self = this


remove extra translation code

remove extra translation code

--- src/components/widgets/hr-leaves-schedule/hr-leaves-schedule.directive.coffee
+++ src/components/widgets/hr-leaves-schedule/hr-leaves-schedule.directive.coffee
@@ -68,19 +68,8 @@
       scope.$watch getEvents, (events) ->
         if events.length > 0
           element.fullCalendar('destroy')
-
-          $translate([
-            "impac.common.action.calendar.left",
-            "impac.common.action.calendar.center",
-            "impac.common.action.calendar.right"]).then(
-            (translations) ->
-              calendarOptions.header.left = translations["impac.common.action.calendar.left"]
-              calendarOptions.header.center = translations["impac.common.action.calendar.center"]
-              calendarOptions.header.right = translations["impac.common.action.calendar.right"]
-
-              angular.extend(calendarOptions, {events: scope.events})
-              element.fullCalendar(calendarOptions)
-          )
+          angular.extend(calendarOptions, {events: scope.events})
+          element.fullCalendar(calendarOptions)
       ,true
   }
 )


bugfix for player overview page

bugfix for player overview page

--- src/coffee/controllers/player/player-overview-ctrl.coffee
+++ src/coffee/controllers/player/player-overview-ctrl.coffee
@@ -127,4 +127,6 @@
     Player.observe().then null, null, ->
       $scope.initialize()

+    $scope.initialize()
+
 ]


Make timeline things appear only for ClassroomProgramCourses

TODO: Discuss whether this is desired.  Original logic was to show timeline
things if !course.legacy.

--- app/assets/javascripts/components/course.cjsx
+++ app/assets/javascripts/components/course.cjsx
@@ -61,7 +61,7 @@
     user_role = if user_obj? then user_obj.role else -1

     if (user_role > 0 || @getCurrentUser().admin) && !@state.course.legacy && !@state.course.published
-      if CourseStore.isLoaded() && !(@state.course.submitted || @state.published)
+      if CourseStore.isLoaded() && !(@state.course.submitted || @state.published) && @state.course.type == 'ClassroomProgramCourse'
         alerts.push (

@@ -126,7 +126,7 @@

       )

-    unless @state.course.legacy
+    if @state.course.type == 'ClassroomProgramCourse'
       timeline = (

<Link to={"#{@_courseLinkParams()}/timeline"} activeClassName='active'>{I18n.t("courses.timeline_link")}</Link>


Adjust drawing handle size (#5537)

Adjust drawing handle size (#5537)

Multiply the drawing tool drag handle size by 2.5 on screens smaller than 900px.

--- app/classifier/drawing-tools/drag-handle.cjsx
+++ app/classifier/drawing-tools/drag-handle.cjsx
@@ -2,8 +2,8 @@
 createReactClass = require 'create-react-class'
 Draggable = require('../../lib/draggable').default

-RADIUS = 4
-OVERSHOOT = 4
+RADIUS = if screen.width > 900 then 4 else 10
+OVERSHOOT = if screen.width > 900 then 4 else 10

 module.exports = createReactClass
   displayName: 'DragHandle'


Creating a response for what a specific user is working on.

--- src/scripts/working-on.coffee
+++ src/scripts/working-on.coffee
@@ -16,18 +16,36 @@

 module.exports = (robot) ->

+  robot.respond /what is @?([\w .\-]+) working on(\?)?$/i, (msg) ->
+    name = msg.match[1].trim()

-  robot.respond /(what\'s|what is) (everyone|everybody) working on(\?)?/i, (msg) ->
-    messageText = '';
-    users = robot.brain.users()
-    for k, u of users
-        if u.workingon
-            messageText += "#{u.name} is working on #{u.workingon}\n"
+    if name is "you"
+      msg.send "I dunno, robot things I guess."
+    else if name.toLowerCase() is robot.name.toLowerCase()
+      msg.send "World domination!"
+    else if name.match(/(everybody|everyone)/i)
+      messageText = '';
+      users = robot.brain.users()
+      for k, u of users
+          if u.workingon
+              messageText += "#{u.name} is working on #{u.workingon}\n"
+          else
+              messageText += ""
+      if messageText.trim() is "" then messageText = "Nobody told me a thing."
+      msg.send messageText
+    else
+      users = robot.brain.usersForFuzzyName(name)
+      if users.length is 1
+        user = users[0]
+        user.workingon = user.workingon or [ ]
+        if user.workingon.length > 0
+          msg.send "#{name} is working on #{user.workingon}."
         else
-            messageText += ""
-    if messageText.trim() is "" then messageText = "Nobody told me a thing."
-    msg.send messageText
-
+          msg.send "#{name} is slacking off."
+      else if users.length > 1
+        msg.send getAmbiguousUserText users
+      else
+        msg.send "#{name}? Who's that?"

   robot.respond /(i\'m|i am|im) working on (.*)/i, (msg) ->
     name = msg.message.user.name
@@ -40,4 +58,4 @@
       msg.send "I found #{user.length} people named #{name}"
     else
       msg.send "I have never met #{name}"
-
+


remove console.log

remove console.log

--- apps/fair_info/test/templates/visitors.coffee
+++ apps/fair_info/test/templates/visitors.coffee
@@ -27,7 +27,6 @@
     beforeEach ->
       @fair = new Fair fabricate 'fair', location: {summary: '401 Broadway New York, New York'}
       @html = render({profile: @profile, fair: @fair, infoMenu: @infoMenu.infoMenu})
-      console.log @html

     it 'should render address', ->
       @html.should.containEql '401 Broadway New York, New York'


The Aloha plugin for popovers changed names to avoid AdBlock

--- scripts/configs/aloha.coffee
+++ scripts/configs/aloha.coffee
@@ -49,7 +49,7 @@
         'common/table'
         'extra/draganddropfiles'
         'common/image'
-        'oer/popover'
+        'oer/overlay'
         'oer/math'
         'oer/assorted'
         'oer/note'


Restore proper table plugin. Again.

Restore proper table plugin. Again.

--- scripts/configs/aloha.coffee
+++ scripts/configs/aloha.coffee
@@ -50,7 +50,7 @@
         'common/paste'
         'common/block'
         'common/list'
-        'common/table'
+        'oer/table'
         'extra/draganddropfiles'
         'common/image'
         'oer/overlay'


webterm: Avoid errors until timeout is removed.

--- client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -43,17 +43,17 @@

     @on "ReceivedClickElsewhere", =>
       @focused = false
-      @terminal.setFocused false if @terminal?
+      @terminal?.setFocused false
       @getSingleton('windowController').removeLayer @

     $(window).bind "blur", =>
-      @terminal.setFocused false
+      @terminal?.setFocused false

     $(window).bind "focus", =>
-      @terminal.setFocused @focused
+      @terminal?.setFocused @focused

     $(document).on "paste", (event) =>
-      @terminal.server.input event.originalEvent.clipboardData.getData("text/plain") if @focused
+      @terminal?.server.input event.originalEvent.clipboardData.getData("text/plain") if @focused

     KD.whoami().tellKite
       kiteName: 'webterm',
@@ -94,10 +94,8 @@

   setKeyView:->
     @getSingleton('windowController').addLayer @
-    # checking existence of terminal because of the initial timeout
-    # this may run before it is created
     @focused = true
-    @terminal.setFocused true if @terminal?
+    @terminal?.setFocused true
     super

   click: ->


Fixed unitialized frame counter bug.

Fixed unitialized frame counter bug.

--- emulator/core/units/apu.coffee
+++ emulator/core/units/apu.coffee
@@ -61,7 +61,8 @@
     writeFrameCounter: (value) ->
         @frameFiveStepMode = (value & 0x80) isnt 0 # 0 - mode 4 (4-step counter) / 1 - mode 5 (5-step counter)
         @frameIrqEnabled = (value & 0x40) is 0     # IRQ generation is enabled (during mode 4)
-        @frameStep = 0                             # Timer is reseted after 3 or 4 CPU cycle (delay not emulated yet)
+        @frameStep = 0                             # Step of the frame counter
+        @frameCounter = @getFrameCounterMax()      # Counter should be actualy reseted after 3 or 4 CPU cycle (delay not emulated yet)
         unless @frameIrqEnabled
             @clearFrameIRQ()                       # Disabling IRQ clears IRQ flag
         if @frameFiveStepMode


light Charts seems to work

light Charts seems to work

--- muleview/app/view/ChartsView.coffee
+++ muleview/app/view/ChartsView.coffee
@@ -19,94 +19,97 @@
       sorters: ["sortValue"]
       data: retentions

-
     @defaultRetention ||= @retentionsStore.getAt(0).get("name")
     @keys = Ext.Object.getKeys(@data[@defaultRetention])
     @store = @createEmptyStore()

+    # Create all stores and light charts:
+    @stores = {}
+    @lightCharts = {}
+    @retentionsStore.each (retention) =>
+      name = retention.get("name")
+      @stores[name] = @createStore(name)
+      @lightCharts[name] = @createLightChart(retention)

     # Init component:
     @items = @items()
     @callParent()
+    @retentionsStore.each (retention) =>
+      @rightPanel.add(@lightCharts[retention.get("name")])
     @showRetention @defaultRetention

   items: ->
     [
-      @chartContainer = Ext.create "Ext.panel.Panel",
-        region: "center"
-        header: false
-        layout: "fit"
-        tbar: [
-            @retCombo = Ext.create "Ext.form.field.ComboBox",
-              fieldLabel: "Show"
-              forceSelection: true
-              # editable: false
-              labelWidth: 40
-              displayField: "title"
-              valueField: "name"
-              store: @retentionsStore
-              width: "auto"
-              listeners:
-                scope: @
-                select: (me, retentions)->
-                  retention = retentions[0]
-                  return unless retention
-                  Muleview.event "viewChange", @key, retention.get("name")
-          ,
-            xtype: "button"
-            text: "Edit Alerts"
-          ,
-            xtype: "button"
-            text: "Select Subkeys"
-          ,
-            xtype: "button"
-            text: "Refresh"
-        ]
-        items: [
-          @chart = Ext.create "Muleview.view.MuleChart",
-            showAreas: true
-            keys: @keys
-            topKey: @key
-            alerts: @alerts
-            store: @store
-        ]
-
+        @chartContainer = Ext.create "Ext.panel.Panel",
+          region: "center"
+          header: false
+          layout: "fit"
+          tbar: [
+              @retCombo = Ext.create "Ext.form.field.ComboBox",
+                fieldLabel: "Show"
+                forceSelection: true
+                # editable: false
+                labelWidth: 40
+                displayField: "title"
+                valueField: "name"
+                store: @retentionsStore
+                width: "auto"
+                listeners:
+                  scope: @
+                  select: (me, retentions)->
+                    retention = retentions[0]
+                    return unless retention
+                    Muleview.event "viewChange", @key, retention.get("name")
+            ,
+              xtype: "button"
+              text: "Edit Alerts"
+            ,
+              xtype: "button"
+              text: "Select Subkeys"
+            ,
+              xtype: "button"
+              text: "Refresh"
+          ]
+          items: [
+            @chart = Ext.create "Muleview.view.MuleChart",
+              showAreas: true
+              keys: @keys
+              topKey: @key
+              alerts: @alerts
+              store: @store
+          ]
       ,
-      @rightPanel = Ext.create "Ext.panel.Panel",
-        title: "Previews"
-        cls: "rightPanel"
-        region: "east"
-        split: true
-        width: "20%"
-        collapsible: true
-        layout:
-          type: "vbox"
-          align: "stretch"
-        defaults:
-          flex: 1
+        @rightPanel = Ext.create "Ext.panel.Panel",
+          title: "Previews"
+          cls: "rightPanel"
+          region: "east"
+          split: true
+          width: "20%"
+          collapsible: true
+          layout:
+            type: "vbox"
+            align: "stretch"
+          items: @lightCharts
     ]

   showRetention: (retName) ->
-    console.log('ChartsView.coffee\\ 88: retName:', retName);
     return unless retName and retName != @currentRetName
-    console.log("ChartsView.coffee\\ 90: <HERE>");
     @store.removeAll()
-    @store.loadData(@getStore(retName).data.items)
-    console.log('ChartsView.coffee\\ 91: @retCombo.getValue():', @retCombo.getValue());
+    @store.loadData(@stores[retName].data.items)
     @retCombo.select retName
-    console.log('ChartsView.coffee\\ 93: @retCombo.getValue():', @retCombo.getValue());
+    lightChart.setVisible(lightChart.retention != retName) for own _, lightChart of @lightCharts
     @currentRetName = retName

   # Creates a flat store from a hash of {
   #   key1 => [[count, batch, timestamp], ...],
   #   key2 => [[count, batch, timestamp], ...]
   # }
-  createStore: (retentionData) ->
+  createStore: (retName) ->
     # Create initial store:
     store = @createEmptyStore()
     # Convert data to timestamps-based hash:
     timestamps = {}
-    for own key, keyData of retentionData
+    for own key, keyData of @data[retName]
       for [count, _, timestamp] in keyData
         unless timestamps[timestamp]
           timestamps[timestamp] = {
@@ -131,20 +134,13 @@
           property: "timestamp"
       ]

-  # Lazy-create each retention's store
-  getStore: (retName) ->
-    @stores ||= {}
-    unless @stores[retName]
-      @stores[retName] = @createStore(@data[retName])
-    ans = @stores[retName]
-    ans
-
-  # createBigChart: (retName) ->
-  #   keys = key for own key of @data[retName]
-  #   Ext.create "Muleview.view.MuleChartPanel",
-  #     title: @retentionsStore.getById(retName).get("title")
-  #     retention: retName
-  #     keys: keys
-  #     topKey: @key
-  #     alerts: @alerts
-  #     store: @getStore(retName)
+  createLightChart: (retention) ->
+    retName = retention.get("name")
+    Ext.create "Muleview.view.MuleLightChart",
+      title: retention.get("title")
+      keys: @keys
+      flex: 1
+      topKey: @key
+      hidden: true
+      retention: retName
+      store: @stores[retName]


properly remove touch height hack

properly remove touch height hack

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -873,8 +873,6 @@

         # Determine number of rows from the height of the row, as computed above.
         height = elt.height()
-        if IS_TOUCH
-            height -= 60
         new_rows = Math.max(1, Math.floor(height / row_height))

         # Resize the renderer


get rid of error when setting email

get rid of error when setting email

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -192,13 +192,13 @@
                 if err
                     @setState
                         state    : 'edit'
-                        error    : "Error saving -- #{err}"
+                        error    : "Error -- #{err}"
                 else
-                    set_account_table(email_address: @state.email_address)
                     @setState
                         state    : 'view'
                         error    : ''
                         password : ''
+
     is_submittable: ->
         return @state.password != '' and @state.email_address != @props.email_address


make terms of service checkbox EVEN CLEARER.

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -607,15 +607,15 @@
         # get automatically deleted.

             <Alert bsStyle='warning' style={marginTop:'10px'}>
-
WARNING: This is a temporary account!
+
WARNING: You are using a temporary account!

-                Sign up below!
+                Sign up below:

FREE

Avoid losing all your work

Get added to courses and projects that you were invited to

Create support tickets

-
Unlock additional features and controls

+
Unlock additional features and controls, including unlimited additional projects, realtime collaboration and much, much more

             </Alert>

@@ -657,7 +657,12 @@
     render_terms_of_service: () ->
         if not @props.is_anonymous
             return
-        <FormGroup style={ fontSize: "12pt", margin: "20px" }>
+        style = {padding:'10px 20px'}
+        if @state.terms_checkbox
+            style.border = '2px solid #ccc'
+        else
+            style.border = '2px solid red'
+        <FormGroup style={ style }>
             <Checkbox
               onChange={(e) => this.setState({ terms_checkbox: e.target.checked })}
             >


delete some password quality code we don't use (it really didn't turn out to be important for us)

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -341,7 +341,6 @@
         @setState
             state    : 'edit'
             error    : ''
-            zxcvbn   : undefined
             old_password : ''
             new_password : ''
             strength     : 0
@@ -351,7 +350,6 @@
             state    : 'view'
             old_password : ''
             new_password : ''
-            zxcvbn   : undefined
             strength     : 0

     save_new_password: ->
@@ -372,7 +370,7 @@
             strength     : 0

     is_submittable: ->
-        return @state.new_password.length >= 6 and @state.new_password and @state.new_password != @state.old_password and (not @state.zxcvbn? or @state.zxcvbn?.score > 0)
+        return @state.new_password.length >= 6 and @state.new_password and @state.new_password != @state.old_password

     change_button: ->
         if @is_submittable()
@@ -385,15 +383,6 @@
     render_error: ->
         if @state.error
             <ErrorDisplay error={@state.error} onClose={=>@setState(error:'')} style={marginTop:'15px'}  />
-
-    password_meter: ->
-        result = @state.zxcvbn
-        if result?
-            score = ['Very weak', 'Weak', 'So-so', 'Good', 'Awesome!']
-            return <div style={marginBottom: '1em'}>
-                <ProgressBar striped bsStyle='info' now={2*result.entropy} />
-                {score[result.score]} (crack time: {result.crack_time_display})
-            </div>

     render_edit: ->
         <Well style={marginTop:'3ex'}>
@@ -416,11 +405,10 @@
                         ref         = 'new_password'
                         value       = {@state.new_password}
                         placeholder = 'New password'
-                        onChange    = {=>x=ReactDOM.findDOMNode(@refs.new_password).value; @setState(zxcvbn:password_score(x), new_password:x)}
+                        onChange    = {=>x=ReactDOM.findDOMNode(@refs.new_password).value; @setState(new_password:x)}
                     />
                 </FormGroup>
             </form>
-            {@password_meter()}
             <ButtonToolbar>
                 {@change_button()}
                 <Button bsStyle='default' onClick={@cancel_editing}>Cancel</Button>
@@ -1427,22 +1415,3 @@
         err = misc.to_json(err)
     alert_message(type:"error", message:"Settings error -- #{err}")

-# returns password score if password checker library
-# loaded; otherwise returns undefined and starts load
-zxcvbn = undefined
-password_score = (password) ->
-    return  # temporary until loading iof zxcvbn below is fixed. See https://github.com/sagemathinc/cocalc/issues/687
-    # if the password checking library is loaded, render a password strength indicator -- otherwise, don't
-    ###
-    if zxcvbn?
-        if zxcvbn != 'loading'
-            # explicitly ban some words.
-            return zxcvbn(password, ['sagemath','salvus','sage','sagemathcloud','smc','mathematica','pari'])
-    else
-        zxcvbn = 'loading'
-        require.ensure [], =>
-            zxcvbn = require("script!zxcvbn/zxcvbn.js")
-            # $.getScript '/static/zxcvbn/zxcvbn.js', () =>
-            #    zxcvbn = window.zxcvbn
-    return
-    ###


Add mocked shopfront

Add mocked shopfront

--- spec/javascripts/unit/darkswarm/controllers/checkout/shop_variant_controller_spec.js.coffee
+++ spec/javascripts/unit/darkswarm/controllers/checkout/shop_variant_controller_spec.js.coffee
@@ -20,7 +20,7 @@
       CartMock =
         adjust: ->
           true
-      ctrl = $controller 'ShopVariantCtrl', {$scope: scope, $modal: $modal, Cart: CartMock}
+      ctrl = $controller 'ShopVariantCtrl', {$scope: scope, $modal: $modal, Cart: CartMock, Shopfront: {}}

   it "initializes the quantity for shop display", ->
     expect(scope.variant.line_item.quantity).toEqual 0


Update initialize.js.coffee

Update initialize.js.coffee

Update for Turbo. https://turbo.hotwired.dev/reference/events

--- app/assets/javascripts/effective_datatables/initialize.js.coffee
+++ app/assets/javascripts/effective_datatables/initialize.js.coffee
@@ -208,3 +208,5 @@
 $(document).on 'turbolinks:load', -> initializeDataTables()
 $(document).on 'turbolinks:render', -> initializeDataTables()
 $(document).on 'turbolinks:before-cache', -> destroyDataTables()
+$(document).on 'turbo:load', -> initializeDataTables()
+$(document).on 'turbo:before-cache', -> destroyDataTables()


When clicking on sidebar links, load entries after loading feeds.

This way in most cases the client will detect that no entries have been received for a given feed, and its unread count will be set to zero even if a non-zero count is returned by the server.

A wrong count may still be displayed if the Ajax call to retrieve feeds is slower than the one to retrieve entries, but this should be rare.

--- app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
+++ app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
@@ -141,8 +141,8 @@
     if feed?
       currentFeedSvc.set feed
       tooltipSvc.feed_title_tooltip()
+      feedsFoldersSvc.load_feed feed_id
       readSvc.read_entries_page()
-      feedsFoldersSvc.load_feed feed_id
       menuCollapseSvc.close()
       sidebarVisibleSvc.set false
     return
@@ -154,8 +154,8 @@
     # If the "all subscriptions" link is not enabled, do nothing.
     if $scope.all_subscriptions_enabled()
       currentFolderSvc.set folder
+      feedsFoldersSvc.load_folder_feeds folder
       readSvc.read_entries_page()
-      feedsFoldersSvc.load_folder_feeds folder
       menuCollapseSvc.close()
       sidebarVisibleSvc.set false
     return


sagews: make it so in a worksheet __file__ is the name of the current worksheet.

--- salvus/local_hub.coffee
+++ salvus/local_hub.coffee
@@ -825,8 +825,8 @@
                 # Set path to be the same as the file.
                 mesg = message.execute_code
                     id       : misc.uuid()
-                    code     : "os.chdir(salvus.data['path'])"
-                    data     : {path: misc.path_split(@path).head}
+                    code     : "os.chdir(salvus.data['path']);__file__=salvus.data['file']"
+                    data     : {path: misc.path_split(@path).head, file:abspath(@path)}
                     preparse : false
                 socket.write_mesg('json', mesg)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -11,7 +11,7 @@
     x: 15
     y: 30

-  # hold
+  # hold one of th

   # update the player so ClampBounds can set
   # his position back inside the viewport.


Add missing onLastGoalFinished callback for CompoundPlayer.

--- application/chrome/content/wesabe/download/CompoundPlayer.coffee
+++ application/chrome/content/wesabe/download/CompoundPlayer.coffee
@@ -66,4 +66,7 @@
     startNextPlayer()

   resume: ->
-    @currentPlayer && @currentPlayer.resume()
+    @currentPlayer?.resume()
+
+  onLastGoalFinished: ->
+    @currentPlayer?.onLastGoalFinished?()


changed default argument of expects

changed default argument of expects

--- lisp.coffee
+++ lisp.coffee
@@ -37,7 +37,7 @@
   isEOF: ->
     @pos == @code.length

-  expects: (pattern, throwing = true) ->
+  expects: (pattern, throwing = false) ->
     valid = @getChar() && (pattern instanceof RegExp and pattern.test @getChar()) || pattern == @getChar()
     if !valid && throwing
       throw "unexpected \"#{@getChar()}\", expects \"#{pattern}\""
@@ -48,7 +48,7 @@
     valid = @code[@pos...@pos + str.length] == str

   forwards: (pattern) ->
-    @expects pattern
+    @expects pattern, true
     @pos++

   forwards_str: (str) ->
@@ -59,36 +59,36 @@
     c = @getChar()

     #number
-    if @expects /[0-9]/, false
+    if @expects /[0-9]/
       num = ''
-      while @expects /[0-9]/, false
+      while @expects /[0-9]/
         num += @getChar()
         @pos++
       return new Atom(parseInt(num))

     #string
-    if @expects '"', false
+    if @expects '"'
       @forwards '"'
       str = ''
-      until @expects '"', false
+      until @expects '"'
         str += @getChar()
         @pos++
       @forwards '"'
       return new Atom(str)

     #nil
-    if @expects_str 'nil', false
+    if @expects_str 'nil'
       @forwards_str 'nil'
       return new Nil

     #t
-    if @expects 't', false
+    if @expects 't'
       @forwards 't'
       return new T

   fun_name: ->
     ret = ''
-    while @expects /[\w!#$%&=-~^|*+<>?_]/, false
+    while @expects /[\w!#$%&=-~^|*+<>?_]/
       ret += @getChar()
       @pos++
     return ret
@@ -96,7 +96,7 @@
   list: ->
     @forwards '('
     values = []
-    until @expects(')', false) or @isEOF()
+    until @expects(')') or @isEOF()
       values.push @expr()
       @skip()
     @forwards ')'
@@ -108,7 +108,7 @@
     funname = @fun_name()

     isSP = SpecialForm.NAMES.indexOf(funname) != -1
-    until @expects(')', false) or @isEOF()
+    until @expects(')') or @isEOF()
       @skip()
       args.push @expr(isSP)

@@ -118,13 +118,13 @@
     return new klass(funname, args)

   expr: (isSP) ->
-    if @expects("'", false) or isSP #value
-      @forwards "'" if @expects "'", false
-      if @expects '(', false #list
+    if @expects("'") or isSP #value
+      @forwards "'" if @expects "'"
+      if @expects '(' #list
         return @list()
       else #atom
         return @atom()
-    else if @expects '(', false #calling function or special form
+    else if @expects '(' #calling function or special form
       return @call_fun()
     else #atom
       return @atom()


No more Mikey :(

No more Mikey :(

--- scripts/name-expander.coffee
+++ scripts/name-expander.coffee
@@ -10,7 +10,6 @@
 merlin_lax_members = [
   "Jesse Howarth",
   "Josiah Kiehl",
-  "mmatsumura",
   "Jamie Winsor",
   "Istvan"
 ]


simplify message

simplify message

--- scripts/sns-notifications.coffee
+++ scripts/sns-notifications.coffee
@@ -4,9 +4,6 @@
     robot.messageRoom("46691_curbot_stage_notifications@conf.hipchat.com",
       """
       Received notification:
-        TopicArn:   #{msg.topicArn}
         Topic:      #{msg.topic}
-        Message Id: #{msg.messageId}
-        Subject:    #{msg.subject}
         Message:    #{msg.message}
       """)


Scripts: log err

Scripts: log err

--- scripts/user-updater/main.coffee
+++ scripts/user-updater/main.coffee
@@ -87,7 +87,8 @@

           queue.push ->
             account.migrateOldAppStorageIfExists { appId, version }, (err) ->
-              return queue.fin err  if err
+              console.log "Migrating appStorage with id #{storage._id}"
+              console.log "Error on appStorage with id #{storage._id}"  if err
               queue.fin()

         dash queue, (err) ->


logging: extract format_datetime method.

--- scripts/logging.coffee
+++ scripts/logging.coffee
@@ -7,7 +7,7 @@
 #   None
 #
 # Commands:
-#   hubot log search <words> -- このチャンネルの発言ログを検索
+#   hubot log search <regexp> -- このチャンネルの発言ログを検索
 #
 # Author:
 #   nagachika
@@ -22,9 +22,29 @@
     text = msg.message.text
     fluent.emit "chatlog", {room: room, user: username, text: text}

+
+  format_datetime = (date) ->
+    month = date.getMonth()
+    day = date.getDate()
+    hour = date.getHours()
+    min = date.getMinutes()
+    sec = date.getSeconds()
+    if month < 10
+      month = "0" + month
+    if day < 10
+      day = "0" + day
+    if hour < 10
+      hour = "0" + hour
+    if min < 10
+      min = "0" + min
+    if sec < 10
+      sec = "0" + sec
+    return "#{date.getFullYear()}-#{month}-#{day} #{hour}:#{min}:#{sec}"
+
+
   # use docker host mongodb
   mongo = require("mongodb").MongoClient
-  robot.respond /log search (.+)/, (msg) ->
+  robot.respond /log\s+search\+(.+)/, (msg) ->
     mongo.connect "mongodb://172.17.42.1:27017/hubot_#{process.env.HUBOT_SLACK_TEAM}", (err, db) ->
       if (err)
         msg.send "mongodb connection failure: #{err}"
@@ -37,22 +57,7 @@
             buf = "#{items.length} matches:\n"
             for m in items
               date = m["time"]
-              month = date.getMonth()
-              day = date.getDate()
-              hour = date.getHours()
-              min = date.getMinutes()
-              sec = date.getSeconds()
-              if month < 10
-                month = "0" + month
-              if day < 10
-                day = "0" + day
-              if hour < 10
-                hour = "0" + hour
-              if min < 10
-                min = "0" + min
-              if sec < 10
-                sec = "0" + sec
-              date_str = "#{date.getFullYear()}-#{month}-#{day} #{hour}:#{min}:#{sec}"
+              date_str = format_datetime(date)
               buf += "#{m["user"]}:#{date_str}: #{m["text"]}\n"
             msg.send buf
           db.close


Fix issue with audio being muted at odd times

--- scripts/players.coffee
+++ scripts/players.coffee
@@ -103,11 +103,12 @@
             adjustingVolume = false

         try
-            media.volume = window.localStorage.volume
-            if isVideo
-                volume.querySelector('.amount').style.height = window.localStorage.volume * 100 + '%'
-            else
-                volume.querySelector('.amount').style.width = window.localStorage.volume * 100 + '%'
+            if window.localStorage.volume?
+                media.volume = window.localStorage.volume
+                if isVideo
+                    volume.querySelector('.amount').style.height = window.localStorage.volume * 100 + '%'
+                else
+                    volume.querySelector('.amount').style.width = window.localStorage.volume * 100 + '%'
         catch ex
             # This doesn't work in iframes, and catching it prevents everything from breaking


Fix formatting

Fix formatting

--- main.coffee
+++ main.coffee
@@ -1,11 +1,15 @@
 require 'colors'
-async = require 'async'
-Client = require('request-json').JsonClient
+
+async    = require 'async'
+Client   = require('request-json').JsonClient
 Mockaroo = require 'mockaroo'
-fs = require 'fs'
-S = require 'string'
-util = require 'util'
-path = require 'path'
+fs       = require 'fs'
+S        = require 'string'
+util     = require 'util'
+path     = require 'path'
+
+InvalidApiKeyError      = Mockaroo.errors.InvalidApiKeyError
+UsageLimitExceededError = Mockaroo.errors.UsageLimitExceededError

 DB_NAME = process.env.DB_NAME or 'cozy'

@@ -328,9 +332,9 @@
                             .then (records) ->
                                 callback null, records
                             .catch (err) ->
-                                if err instanceof Mockaroo.errors.InvalidApiKeyError
+                                if err instanceof InvalidApiKeyError
                                     err = 'invalid api key'
-                                else if err instanceof Mockaroo.errors.UsageLimitExceededError
+                                else if err instanceof UsageLimitExceededError
                                     err = 'usage limit exceeded'
                                 else
                                     err = error
@@ -338,7 +342,9 @@
                                 @log err.red
                                 callback err
                     catch e
-                        err = "[ERROR] Cannot initialize Mockaroo client -- #{e}".red
+                        err = """
+                            [ERROR] Cannot initialize Mockaroo client -- #{e}
+                        """
                         @log err.red

                 else