Fixed issue with make_select2 being called on Select2 instances more than once.

--- app/assets/javascripts/crm_select2.js.coffee
+++ app/assets/javascripts/crm_select2.js.coffee
@@ -10,10 +10,12 @@
   window.crm ||= {}

   crm.make_select2 = ->
-    $(".select2").each ->
+    $(".select2").not(".select2-container, .select2-offscreen").each ->
+    #$(".select2").each ->
       $(this).select2 'width':'resolve'

-    $(".select2_tag").each ->
+    $(".select2_tag").not(".select2-container, .select2-offscreen").each ->
+    #$(".select2_tag").each ->
       $(this).select2
         'width':'resolve'
         tags: $(this).data("tags")


Loading GIF in Simulation Results window

--- app/assets/javascripts/experiments.js.coffee
+++ app/assets/javascripts/experiments.js.coffee
@@ -142,6 +142,8 @@
     $.getJSON "/experiments/#{monitor.experiment_id}/experiment_moes", (data) -> monitor.update_moes(data)

   progress_bar_listener: (event) =>
+    $('#extension-dialog').html(window.loaderHTML)
+
     if (window.event)
       x = window.event.pageX
     else


Dynamic scroll top

Dynamic scroll top

--- app/assets/javascripts/site_search.js.coffee
+++ app/assets/javascripts/site_search.js.coffee
@@ -64,15 +64,14 @@
           _gaq.push(['_trackEvent', 'SearchClickCategory', GAAction,  GALabel])

     # Override user agents and scroll to search box on narrow devices
-    if $(document).width() <= 568
-      newY = 75
-      $("body").css("min-height", $(document).height() + newY)
+    if $(document).width() <= 2568
+      $("body").css("min-height", $(document).height() + $searchField.offset().top)
       $searchField.focus ->
         $searchField.css("font-size", "16px") # hack to prevent iOS from zooming
         times = 0
         i = setInterval ->
-          window.scrollTo(0, newY - 6)
-          if times++ > 10
+          window.scrollTo(0, $searchField.offset().top - 6)
+          if times++ > 20
             clearInterval(i)
         , 10


Fixing wordcount on first editor

Fixing wordcount on first editor

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -789,7 +789,7 @@
           question.find(".list-add").find("li:last-child input").prop("disabled", false)

           idx = question.find(".list-add").find("> li").length
-
+
           question.find(".list-add").find("li:last-child .remove-link").attr("aria-label", "Remove " + ordinal(idx) + " " + entity)
           clear_example = question.find(".list-add").attr("data-need-to-clear-example")
           if (typeof(clear_example) != typeof(undefined) && clear_example != false)
@@ -965,14 +965,13 @@
       CKEDITOR.replace this,
         toolbar: 'mini'
         height: 200
+        wordcount:
+          maxWordCount: $(this).data('word-max')

       CKEDITOR.on 'instanceCreated', (event) ->
         editor = event.editor
         element = editor.element

-        editor.on 'configLoaded', ->
-          editor.config.wordcount =
-            maxWordCount: element.data('word-max')

       CKEDITOR.on 'instanceReady', (event) ->
         target_id = event.editor.name


fix coffeescript to work

fix coffeescript to work

--- app/assets/javascripts/date_for_later.coffee
+++ app/assets/javascripts/date_for_later.coffee
@@ -1,3 +1,3 @@
 $(document).ready ->
-  $('form').on 'change', '#date_for_later', ->
-    $('#notification').toggleClass 'hidden', $(this).val().length > 0
+  $('form').on 'change', '#application_submission_date_for_later', ->
+    $('#notification').toggle( $(this).val().length > 0)


Make delete actually delete

Make delete actually delete

--- app/assets/javascripts/map_display.js.coffee
+++ app/assets/javascripts/map_display.js.coffee
@@ -255,6 +255,7 @@
       domelem.innerHTML = "#{CONSTANTS.i18n.delete}?</a>"
       domelem.onclick = =>
         @constructor.drawnItems.removeLayer layer
+        @drawnLayerChanged()
       @constructor.drawnItems.bindPopup(domelem)
     @drawnLayerChanged()


fix bug

fix bug

--- app/assets/javascripts/s3_form_for.js.coffee
+++ app/assets/javascripts/s3_form_for.js.coffee
@@ -58,7 +58,7 @@
         loadImage file, ((img) ->
             imgElem = $(img)
             imgElem.addClass 'empty-avatar'
-            container = $("#upload_thumbnail")
+            container = $uploadForm.find("#upload_thumbnail")
             container.html('')
             container.append imgElem
           ),


Increasing save interval for OpenResponse

--- app/assets/javascripts/save-on-change.coffee
+++ app/assets/javascripts/save-on-change.coffee
@@ -35,7 +35,7 @@
   constructor: (@$form) ->
     @scheduled_job      = null
     @previous_value     = null
-    @update_interval_s  = 0.6
+    @update_interval_s  = 2.0
     @save_indicator     = new SaveIndicator($("#save"))
     @setupEvents()


Add console.log as an appender fallback before dump().

--- application/chrome/content/wesabe/Logger.coffee
+++ application/chrome/content/wesabe/Logger.coffee
@@ -65,7 +65,9 @@
   # Appenders append text to things, like stdout or a file.
   #
   @::__defineGetter__ 'appender', ->
-    @_appender or @_parent?._appender or (text) -> dump "#{text}\n"
+    @_appender or @_parent?._appender or
+      (console?.log and (text) -> console.log text) or
+      (text) -> dump "#{text}\n"

   @::__defineSetter__ 'appender', (appender) ->
     @_appender = appender


fix test dependencies changed by parent commit

--- test/_setup.coffee
+++ test/_setup.coffee
@@ -22,14 +22,13 @@
 global.arrayEq = (a, b, msg) -> ok arrayEgal(a,b), msg ? "#{inspect a} === #{inspect b}"

-libDir = if typeof _$jscoverage is 'undefined' then 'lib' else 'instrumented'
 global.CoffeeScript = require '..'
-global.CS = require "../#{libDir}/coffee-script/nodes"
-global.JS = require "../#{libDir}/coffee-script/js-nodes"
-global.Repl = require "../#{libDir}/coffee-script/repl"
-global.Parser = require "../#{libDir}/coffee-script/parser"
-{Optimiser: global.Optimiser} = require "../#{libDir}/coffee-script/optimiser"
-{Preprocessor} = require "../#{libDir}/coffee-script/preprocessor"
+global.CS = require "../lib/nodes"
+global.JS = require "../lib/js-nodes"
+global.Repl = require "../lib/repl"
+global.Parser = require "../lib/parser"
+{Optimiser: global.Optimiser} = require "../lib/optimiser"
+{Preprocessor} = require "../lib/preprocessor"

 global.parse = (input) -> Parser.parse Preprocessor.processSync input
 optimiser = new Optimiser


Convert to string if values exist

Convert to string if values exist

--- src/coffee/leveldb/iterator.coffee
+++ src/coffee/leveldb/iterator.coffee
@@ -203,7 +203,7 @@

     # async
     @self.key (err, key) ->
-      key = key.toString 'utf8' unless err or options.as_buffer
+      key = key.toString 'utf8' if key and not options.as_buffer
       callback err, key

@@ -257,7 +257,7 @@
     @self.current (err, kv) ->
       if kv
         [ key, val ] = kv
-        unless err or options.as_buffer
-          key = key.toString 'utf8'
-          val = val.toString 'utf8'
+        unless options.as_buffer
+          key = key.toString 'utf8' if key
+          val = val.toString 'utf8' if val
       callback err, key, val


added some comments

added some comments

--- client/front.coffee
+++ client/front.coffee
@@ -504,12 +504,12 @@
 # the main editor class
 class Editor

+    # create a new editor
     constructor: ->
         @fresh = true
         window.editor = @
         @con = new Connection()
-        @filename = "foo"
-
+        @filename = "untiled"
         @tab_width = 4

         # grab common elements
@@ -521,8 +521,6 @@
         @$highlight = $(".highlight")
         @$errorbox = $("#error-box")
         @$errorbox.hide()
-
-
         # grab careat
         @$caret_line = $("#caret-line")
         @$caret_text = $("#caret-text")
@@ -530,12 +528,6 @@
         @$caret_tail = $("#caret-tail")

         @auth = new Auth()
-
-        # updates
-        #@$doc.keydown (e) =>
-        #    @update()
-        #    return @key(e)
-

         keydown = (e) =>
             @update()
@@ -549,9 +541,7 @@
                 e.preventDefault()
                 return null
             return true
-
         window.addEventListener("keydown", keydown, false)
-

         @$doc.keyup (e) =>
             @update()
@@ -560,7 +550,6 @@
         @$doc.keypress (e) =>
             @update()
             return true
-
         @$doc.mousedown =>
             @mousedown=true
             @update()
@@ -606,9 +595,12 @@
         @requset_update = true
         @workloop()

+    # open current file
     open: (filename) =>
         @con.socket.emit "open",
              filename: filename
+
+    # save current file
     save: =>
         text = @$pad.val()
         # replace tabs by spaces
@@ -620,11 +612,13 @@
             filename: @filename
             data: text

+    # focus the pad
     focus: =>
         $("div.popup").hide()
         @$pad.focus()
         @update()

+    # return the lines selected
     selected_line_range: ->
         start = null
         end = null
@@ -637,6 +631,7 @@
             end = start
         return [start, end]

+    # tab was pressed, complex behavior
     tab: =>
         if $("#search-input").is(":focus")
             $("#replace-input").focus()
@@ -651,6 +646,7 @@
         @indent()
         return

+    # auto complete right at the current cursor
     autocomplete: =>
         [text, [start, end], s] = @get_text_state()
         # if some thing is selected don't auto complete
@@ -674,6 +670,7 @@
             return true
         return false

+    # insert text into the selected range
     insert_text: (add) =>
         [text, [start, end], s] = @get_text_state()
         text = text[..start-1] + add + text[end..]
@@ -681,6 +678,7 @@
         end += add.length
         @set_text_state([text, [start, end], s])

+    # indent selected range
     indent: =>
         [start, end] = @selected_line_range()
         if start == end
@@ -700,6 +698,7 @@
             @$pad[0].selectionStart = @lines[start][1]
             @$pad[0].selectionEnd = @lines[end][2] + @tab_width * (end-start+1)

+    # deindent selected range
     deindent: =>
         [start, end] = @selected_line_range()
         if start == end
@@ -729,20 +728,24 @@
             @$pad[0].selectionStart = @lines[start][1]
             @$pad[0].selectionEnd = @$pad[0].selectionStart + new_length

+    # set the text of the pad to a value
     set_text: (text) ->
         @$pad.val(text)
         @update()

+    # return the text of the pad
     get_text: () ->
         return @$pad.val()

+    # set the state of the text
     set_text_state: (text_state) ->
         @$pad.val(text_state[0])
         @$pad[0].selectionEnd = text_state[1][0]
         @$pad[0].selectionStart = text_state[1][1]
-        @$holder.animate(scrollTop: text_state[2])
+        @$holder.stop(true).animate(scrollTop: text_state[2])
         @update()

+    # gets the state of the text
     get_text_state: () ->
         start = @$pad[0].selectionStart
         end = @$pad[0].selectionEnd
@@ -755,13 +758,11 @@
         ]
         return text_state

-    show_promt: (p) ->
-        $(p).show()
-        $(p+" input").focus()
-
+    # request for an update to be made
     update: =>
         @requset_update = true

+    # redraw the ghost buffer
     real_update: ->
         if performance? and performance.now?
             now = performance.now()
@@ -859,6 +860,7 @@
         #else
         #    print "        update"

+    # scroll to a char position
     scroll_pos: (offset) ->
         line = 0
         for c in @text[0...offset]
@@ -866,23 +868,26 @@
                 line += 1
         @scroll_line(line)

+    # go to a line number
     goto_line: (line_num) ->
         line = @lines[line_num] ? @lines[@lines.length - 1]
         @$pad[0].selectionStart = line[1]
         @$pad[0].selectionEnd = line[1]
         @scroll_line(line[0])

-
+    # animate a scroll to a line number
     scroll_line: (line_num) ->
         line = @lines[line_num] ? @lines[@lines.length - 1]
         y = @get_line_y(line[0])
         y -= @$win.height()/2
-        @$holder.animate(scrollTop: y)
-
+        @$holder.stop(true).animate(scrollTop: y)
+
+    # get line's y cordiante for scrolling
     get_line_y: (line_num) ->
         top = $("#line"+line_num).position().top + 100
         return top

+    # loop that does the work for rendering when update is requested
     workloop: =>
         if @requset_update
             @real_update()


fix #51

fix #51

--- core/api/ticket.coffee
+++ core/api/ticket.coffee
@@ -172,7 +172,7 @@
               if ticket.status == req.body.status
                 return res.json 400, error: 'already_in_status'
               else
-                modifier['status'] = ticket.status
+                modifier['status'] = req.body.status
             else
               return res.json 400, error: 'invalid_status'


Added function getLightenColor

Added function getLightenColor

--- src/services/chart-formatter/chart-formatter.svc.coffee
+++ src/services/chart-formatter/chart-formatter.svc.coffee
@@ -25,6 +25,11 @@
     # Returns a color from the array retrieved from Maestrano Rails app (set in config files)
     @getColor = (index) ->
       return COLORS.array[index%COLORS.array.length]
+
+    # Returns a color from the array retrieved from Maestrano Rails app (set in config files) with applying passed opacity
+    @getLightenColor = (index, alpha) ->
+      htmlColor = COLORS.array[index%COLORS.array.length]
+      return lightenColor(htmlColor, alpha || 1) if htmlColor

     # Removes the # from an HTML color value
     cutHex = (htmlColor) ->
@@ -119,7 +124,7 @@

         enabled: false
         custom: _self.customTooltip
-
+
       elements:
         point:
           hitRadius: 8
@@ -127,7 +132,7 @@
         line:
           tension: 0
           borderWidth: 2
-
+
       legend:
         display: false
     }


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/camera.coffee
+++ test/camera.coffee
@@ -20,7 +20,7 @@
   object.bind 'overlay', ->
     ok true

-
+  can

   camera = Camera()


Fix test

Fix test

--- test/common.coffee
+++ test/common.coffee
@@ -42,6 +42,9 @@
                     fileVal = row[index]
                     srcVal = data[rowNr - 1][key] || ""

+                    if srcVal instanceof Object && srcVal.hyperlink
+                        srcVal = srcVal.value
+
                     # Date handling - comes back from sheet as an OADate, but is Date in source
                     if srcVal instanceof Date
                         fileVal = XLSXInstance._OADateToDate(fileVal).valueOf()


collaborator solutions was misspelled, array property checker was misnamed

--- src/components/question/index.cjsx
+++ src/components/question/index.cjsx
@@ -43,20 +43,20 @@
   getChildContext: ->
     processHtmlAndMath: @props.processHtmlAndMath

-  isArrayMissingProperty: (arrayToCheck, property) ->
+  doesArrayHaveProperty: (arrayToCheck, property) ->
     not _.isEmpty(_.compact(_.pluck(arrayToCheck, property)))

   hasAnswerCorrectness: ->
     {correct_answer_id, model} = @props
     {answers} = model

-    correct_answer_id or @isArrayMissingProperty(answers, 'correctness')
+    correct_answer_id or @doesArrayHaveProperty(answers, 'correctness')

   hasSolution: ->
     {model, correct_answer_id} = @props
     {collaborator_solutions} = model

-    @hasAnswerCorrectness() and @isArrayMissingProperty(collborator_solutions, 'content_html')
+    @hasAnswerCorrectness() and @doesArrayHaveProperty(collaborator_solutions, 'content_html')

   render: ->
     {model, correct_answer_id, exercise_uid, className} = @props


reforatted code in prefs.coffee

reforatted code in prefs.coffee

--- client/prefs.coffee
+++ client/prefs.coffee
@@ -7,17 +7,27 @@
 #   prefs.foo.toggle()          # convenience function for booleans (numbers 0 and 1)
 #   prefs.foo.getDefault()      # retrieve default value
 prefs = @
-[ # name                    default (type is determined from default value; setter enforces type and handles encoding)
-  ['colourScheme',          'Default'] # name of the active colour scheme
-  ['colourSchemes',         []] # objects describing user-defined colour schemes
-  ['favs',                  [host: '127.0.0.1', port: 4502]]
-  ['floating',              0] # floating editor and tracer windows
-  ['floatOnTop',            0] # try to keep floating windows on top of the session
-  ['keys',                  {}] # a mapping between commands and keystrokes, only diffs from the defaults
-  ['kbdLocale',             ''] # e.g. "US", "GB"
-  ['lineNumsTracer',        0]
-  ['lineNumsEditor',        1]
-  ['menu',                  '''
+[ # name             default (type is determined from default value; setter enforces type and handles encoding)
+  ['colourScheme',   'Default'] # name of the active colour scheme
+  ['colourSchemes',  []] # objects describing user-defined colour schemes
+  ['favs',           [host: '127.0.0.1', port: 4502]]
+  ['floating',       0] # floating editor and tracer windows
+  ['floatOnTop',     0] # try to keep floating windows on top of the session
+  ['keys',           {}] # a mapping between commands and keystrokes, only diffs from the defaults
+  ['kbdLocale',      ''] # e.g. "US", "GB"
+  ['lineNumsTracer', 0]
+  ['lineNumsEditor', 1]
+  ['pos',            null] # [x,y,w,h] of the main window, used in ../init-nw.coffee
+  ['posEditor',      [32, 32, 1000, 618]] # [x,y,w,h]
+  ['posTracer',      [32, 32, 1000, 618]] # [x,y,w,h]
+  ['prefixKey',      '`']
+  ['prefixMap',      ''] # pairs of characters; only differences from the default ` map are stored
+  ['wrap',           0] # line wrapping in session
+  ['lbar',           1] # show language bar
+  ['theme',          '']
+  ['title',          '{WSID} - {HOST}:{PORT} (PID: {PID})'] # a.k.a. "caption"
+  ['zoom',           0]
+  ['menu',           '''
     # _x   access key, alt+x
     # =CMD command code; some are special:
     #        LBR FLT WRP TOP render as checkboxes
@@ -77,16 +87,6 @@
       -
       Dyalog Forum             =http://www.dyalog.com/forum
   ''']
-  ['pos',                   null] # [x,y,w,h] of the main window, used in ../init-nw.coffee
-  ['posEditor',             [32, 32, 1000, 618]] # [x,y,w,h]
-  ['posTracer',             [32, 32, 1000, 618]] # [x,y,w,h]
-  ['prefixKey',             '`']
-  ['prefixMap',             ''] # pairs of characters; only differences from the default ` map are stored
-  ['wrap',                  0] # line wrapping in session
-  ['lbar',                  1] # show language bar
-  ['theme',                 '']
-  ['title',                 '{WSID} - {HOST}:{PORT} (PID: {PID})'] # a.k.a. "caption"
-  ['zoom',                  0]
 ]
 .forEach ([k, d]) ->       # k: preference name, d: default value
   t = typeof d; l = []     # t: type, l: listeners


Prevent toolbar dialogs from scrolling the page

Specifically fixes that hiding the link dialog would scroll to the top of the document because it focused the element before thawing the selection.

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -225,19 +225,19 @@
     @recordFormattingUndoEntry()
     @composition.toggleCurrentAttribute(attributeName)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarDidUpdateAttribute: (attributeName, value) ->
     @recordFormattingUndoEntry()
     @composition.setCurrentAttribute(attributeName, value)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarDidRemoveAttribute: (attributeName) ->
     @recordFormattingUndoEntry()
     @composition.removeCurrentAttribute(attributeName)
     @render()
-    @editorElement.focus()
+    @editorElement.focus() unless @selectionFrozen

   toolbarWillShowDialog: (dialogElement) ->
     @composition.expandSelectionForEditing()
@@ -247,8 +247,8 @@
     @editorElement.notify("toolbar-dialog-show", {dialogName})

   toolbarDidHideDialog: (dialogName) ->
+    @thawSelection()
     @editorElement.focus()
-    @thawSelection()
     @editorElement.notify("toolbar-dialog-hide", {dialogName})

   # Selection


Simplify

Simplify

--- src/crypto-balance.coffee
+++ src/crypto-balance.coffee
@@ -8,9 +8,7 @@
   Promise
     .all(fn(addr) for s, fn of services)
     .filter (item) -> !!item
-    .reduce (a, b) ->
-      a.concat b
-    , []
+    .reduce (a, b) -> a.concat b
     .filter (asset) ->
       !asset.address or asset.address == addr
     .map (item) ->


DataBind ignores null / undefined from now on

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -151,13 +151,16 @@
                 throw new Error "Unknown type to bind to: #{@type}"

         applyContext: (context)->
-            context.watch @attr, (value)=> @setValue value
-            $(@node).change ()=>
+            context.watch @attr, (value)=>
+                @setValue value if value?
+
+            $(@node).change ()=>
                 try
                     context.get(@attr, false)(@getValue())
                 catch err
                     context.set @attr, @getValue()
-            @setValue context.get @attr
+            val = context.get @attr
+            @setValue val if val?
             this


Pass options in DisplayBuffer::screenRangeForBufferRange

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -617,10 +617,10 @@
   # bufferRange - The {Range} to convert
   #
   # Returns a {Range}.
-  screenRangeForBufferRange: (bufferRange) ->
+  screenRangeForBufferRange: (bufferRange, options) ->
     bufferRange = Range.fromObject(bufferRange)
-    start = @screenPositionForBufferPosition(bufferRange.start)
-    end = @screenPositionForBufferPosition(bufferRange.end)
+    start = @screenPositionForBufferPosition(bufferRange.start, options)
+    end = @screenPositionForBufferPosition(bufferRange.end, options)
     new Range(start, end)

   # Given a screen range, this converts it into a buffer position.


:non-potable_water: Use subscribe for tokenized buffer event listeners

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -46,8 +46,8 @@
     @foldsByMarkerId = {}
     @updateAllScreenLines()
     @createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
-    @tokenizedBuffer.on 'grammar-changed', (grammar) => @trigger 'grammar-changed', grammar
-    @tokenizedBuffer.on 'changed', @handleTokenizedBufferChange
+    @subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @trigger 'grammar-changed', grammar
+    @subscribe @tokenizedBuffer, 'changed', @handleTokenizedBufferChange
     @subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
     @subscribe @buffer, 'marker-created', @handleBufferMarkerCreated


:memo:

:memo:

--- src/keymap-manager.coffee
+++ src/keymap-manager.coffee
@@ -220,7 +220,7 @@
   # * `bindings` An {Object} whose top-level keys point at sub-objects mapping
   #   keystroke patterns to commands.
   # * `priority` A {Number} used to sort keybindings which have the same
-  #   specificity.
+  #   specificity. Defaults to `0`.
   add: (source, keyBindingsBySelector, priority=0) ->
     addedKeyBindings = []
     for selector, keyBindings of keyBindingsBySelector


Changed Hubo LIVE socket.io server to point to hubovision.us

--- src/documents/live/index.js.coffee
+++ src/documents/live/index.js.coffee
@@ -6,7 +6,7 @@
 LIVE.connectToServer = () ->
   if (LIVE.use_socket)
     console.log('Trying to connect to server...')
-    LIVE.socket = io.connect(':6060', {'force new connection':true, timeout: 3000})
+    LIVE.socket = io.connect('hubovision.us:6060', {'force new connection':true, timeout: 3000})
     socket = LIVE.socket
     # Due to a design flaw in socket.io? the events won't work if the initial
     # connection fails due to the server not running.


Standardize action name in sprite.perform

--- src/layout/sprites.coffee
+++ src/layout/sprites.coffee
@@ -90,10 +90,10 @@
     console.log args
     # TODO: use language.coffee
     # this is bad. it should be getting this from language.coffee
-    signal = word.split " "
-    signal[2] = parseInt signal[2]
+    action = word.split " "
+    action[2] = parseInt action[2]

-    world.call 'perform', signal
+    world.call 'perform', action

   perform: (world, action) ->
     [method, key, value] = action


Remove excess fund and fix bug on win

Remove excess fund and fix bug on win

--- src/lib/collection.coffee
+++ src/lib/collection.coffee
@@ -86,9 +86,6 @@
         callback collection

     downloadCollection: ->
-      fs.exists params.dlPath, (exists) ->
-        fs.mkdirSync params.dlPath if not exists
-
       @getCachedCollection (collection) ->
         if collection.length isnt 0
           collectionBase = collection


Extract MockFunction instance variables _test_function, _expects_method_name, and _mock_count

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -6,36 +6,41 @@
 class MockFunction

   @mock: (args...) ->
-    new MockFunction(args)
-
-  constructor: (args) ->
-    _check_mock_usage(args)
-    [ expects_method_name, mock_count, test_function ] = _parse_args(args)
-    fail(messages.ExpectsMethodAlreadyExists, expects_method_name) if Object.prototype[expects_method_name]?
+    mock = new MockFunction()
+    mock.setup(args)
+
+  constructor: ->
+    @_test_function = undefined
+    @_expects_method_name = undefined
+    @_mock_count = undefined
+
+  setup: (args) ->
+    @check_mock_usage(args)
+    @parse_args(args)
+    fail(messages.ExpectsMethodAlreadyExists, @_expects_method_name) if Object.prototype[@_expects_method_name]?
     mock_methods = new MockMethodList()
-    _install_expects_method(expects_method_name, mock_methods)
-    mock_objects = ( new MockObject() for i in [1..mock_count] )
+    _install_expects_method(@_expects_method_name, mock_methods)
+    mock_objects = ( new MockObject() for i in [1..@_mock_count] )
     try
-      test_function.apply(null, mock_objects)
+      @_test_function.apply(null, mock_objects)
       errors = mock_methods.find_errors()
       fail( errors.join("\n") + "\n" ) unless errors.length == 0
     finally
       mock_methods.restore_original_methods()
-      _uninstall_expects_method(expects_method_name)
+      _uninstall_expects_method(@_expects_method_name)

   # private

-  _check_mock_usage = (args) ->
+  check_mock_usage: (args) ->
     fail(messages.MockUsage) if args.length < 1 or 2 < args.length
     fail(messages.MockUsage) if args.length == 1 and not is_function(args[0])
     fail(messages.MockUsage) if args.length == 2 and not is_function(args[1])
     fail(messages.MockBadUsage) if args.length == 2 and not has_property(args[0], "expects_method_name") and not has_property(args[0], "mock_count")

-  _parse_args = (args) ->
-    test_function = ( if args.length == 1 then args[0] else args[1] )
-    expects_method_name = ( if args.length == 2 then args[0].expects_method_name ) ? "expects"    # TODO: use merge idiom?  what happens if expects_method_name is not a valid method name?
-    mock_count = ( if args.length == 2 then args[0].mock_count ) ? 5                              # TODO: what happens if mock_count is not a number?
-    [ expects_method_name, mock_count, test_function ]
+  parse_args: (args) ->
+    @_test_function = ( if args.length == 1 then args[0] else args[1] )
+    @_expects_method_name = ( if args.length == 2 then args[0].expects_method_name ) ? "expects"    # TODO: use merge idiom?  what happens if expects_method_name is not a valid method name?
+    @_mock_count = ( if args.length == 2 then args[0].mock_count ) ? 5                              # TODO: what happens if mock_count is not a number?

   _install_expects_method = (expects_method_name, mock_methods) ->
     Object.prototype[expects_method_name] = _build_mock_function(expects_method_name, mock_methods)


Flesh out Poller class

Flesh out Poller class

--- assets/javascripts/routes/index_route.js.coffee
+++ assets/javascripts/routes/index_route.js.coffee
@@ -1,6 +1,6 @@
 App.IndexRoute = App.AuthenticatedRoute.extend
   setupController: (controller, model)->
-    stateData   = []
+    stateData   = {}
     stateIds = []
     activeState = null

@@ -10,21 +10,17 @@
     model.forEach (item)=>
       stateId = item.get("id")
       stateIds.push(stateId)
-      console.log stateId

       # This is to make sure the first active room is loaded
       if item.get("joined") == true
         activeState = item if !activeState?
         poller = new App.Poller()
         poller.store  = @store
-        poller.roomId = item.get("room").get("id")
+        poller.setRoom item.get("room")
         poller.start(item.get("room").get("id"))
         stateData[stateId] = {"poller": poller}
       else
         stateData[stateId] = {}
-
-    for stateId in stateIds
-      console.log "stateId: #{stateId}"

     controller.set("stateIds", stateIds)
     controller.set("stateData", stateData)
@@ -36,8 +32,9 @@
     @store.find("room_user_state")

   deactivate: ->
-    states = controller.get("states")
-    console.log states
+    stateData = @controller.get("stateData")
+    for stateId, state of stateData
+      state.poller.stop() if state.poller

 App.Poller = Em.Object.extend
@@ -47,14 +44,17 @@
     @beforeMessageId = null
     @timer = setInterval @onPoll.bind(@), 3000

+  setRoom: (room)->
+    @room = room
+    @roomId = @room.get("id")
+
   stop: ->
+    return true if !@started
     clearInterval(@timer)
     @started = false

   onPoll: ->
-    console.log("polling now #{@roomId}")
     if @afterMessageId
-      console.log "after #{@afterMessageId}"
       url = "/api/messages/#{@roomId}?after=#{@afterMessageId}"
     else
       url = "/api/messages/#{@roomId}"
@@ -62,7 +62,26 @@
     $.getJSON url, (response)=>
       Em.$.each response.messages, (index, message)=>
         if (! @store.recordIsLoaded(App.Message, message.id))
-          console.log message
+          messageParams =
+            id: message.id
+            type: message.type
+            body: message.body
+          messageObj = @store.createRecord("message", messageParams)
+          messageObj.set("room", @room)
           @afterMessageId = message.id

-
+          if(@store.recordIsLoaded("user", message.user.id))
+            @store.find("user", message.user.id).then (user)=>
+              messageObj.set("user", user)
+              #TODO push or shift depending on the query
+              @room.get("messages").pushObject(messageObj)
+          else
+            userParams =
+              id: message.user.id
+              firstName: message.user.first_name
+              lastName: message.user.last_name
+              role: message.user.role
+            user = @store.createRecord("user", userParams)
+            messageObj.set("user", user)
+            #TODO push or shift depending on the query
+            @room.get("messages").pushObject(messageObj)


Escaping spaces to not have urls break

Escaping spaces to not have urls break

--- scripts/card-fetcher.coffee
+++ scripts/card-fetcher.coffee
@@ -22,4 +22,4 @@
         if firstMatch.toUpperCase() != card.toUpperCase()
           msg.send "#{url}?type=card&name=Dismal%20Failure"
         else
-          msg.send "#{url}?type=card&name=#{card}"
+          msg.send "#{url}?type=card&name=#{encodeURIComponent(card)}"


fix(draft): DraftCountStore updates after send now

--- src/flux/stores/draft-store.coffee
+++ src/flux/stores/draft-store.coffee
@@ -50,6 +50,7 @@
     @listenTo Actions.composeReply, @_onComposeReply
     @listenTo Actions.composeForward, @_onComposeForward
     @listenTo Actions.composeReplyAll, @_onComposeReplyAll
+    @listenTo Actions.sendDraftSuccess, => @trigger()
     @listenTo Actions.composePopoutDraft, @_onPopoutDraftClientId
     @listenTo Actions.composeNewBlankDraft, @_onPopoutBlankDraft
     @listenTo Actions.draftSendingFailed, @_onDraftSendingFailed


fix validation

fix validation

--- app/imports/models.coffee
+++ app/imports/models.coffee
@@ -1,10 +1,10 @@
 {buildMeteorCollection} = require './helpers'

 Survey = Parse.Object.extend 'Survey',
-  # validate: (props) ->
-  #   if not props?.title or props.title.length == 0
-  #     return new Parse.Error(Parse.VALIDATION_ERROR, 'The title field cannot be empty')
-  #   return
+  validate: (props) ->
+    if props?.title?.length == 0
+      return new Parse.Error(Parse.VALIDATION_ERROR, 'The title field cannot be empty')
+    Parse.Object.prototype.validate.call(this, props)

   getForms: (returnMeteorCollection, collection) ->
     query = @relation('forms').query()
@@ -51,7 +51,7 @@
     form = @
     @save(props)
       .then ->
-        form.addTrigger(props.trigger, form)
+        form.addTrigger(props.trigger)
       .then ->
         relation = survey.relation 'forms'
         relation.add form


Trap against 0 recents/favorites

Trap against 0 recents/favorites

--- app/routes/profile.coffee
+++ app/routes/profile.coffee
@@ -23,7 +23,7 @@
     @klass.create(item) for item in list

   totalEntries: ->
-    2 * zooniverse.models.User.current.project.classification_count
+    2 * (zooniverse.models.User.current.project?.classification_count or 0)

   model: (params) ->
     @fetch(params).then (list) =>
@@ -43,7 +43,7 @@
   perPage: 8

   totalEntries: ->
-    zooniverse.models.User.current.project.favorite_count
+    zooniverse.models.User.current.project?.favorite_count or 0

 App.ProfileIndexRoute = Ember.Route.extend
   beforeModel: ->


auto focus on url bar

auto focus on url bar

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -162,6 +162,7 @@
 		@status.showToolpad = false
 	openLinkInput: ->
 		$('#toolpad li:first-child').addClass 'toggled'
+		$('#toolpad input').focus()
 	cancelLinkInput: ->
 		$('#toolpad li:first-child').removeClass 'toggled'
 	applyOrCancelUrl: (range, url)->


Fix replacement paths that are absolute paths on Windows

There is a bug where absolute paths have their backslashes replaced as forward slashes.  For relative paths, this is not a problem.  However, especially with joeybaker/remapify, it is possible to have absolute paths passed into aliasify, and getting the backslashes replaced there is problematic.

This fixes the issue by testing for an absolute path on Windows, and then replacing backslashes with double backslashes, which is necessary since the filename is in quotes.  All other paths have their backslashes replaced by forward slashes as currently happens today.

--- src/aliasify.coffee
+++ src/aliasify.coffee
@@ -35,6 +35,11 @@
             if verbose
                 console.error "aliasify - #{opts.file}: replacing #{args[0]} with #{replacement}"

+            if /^[a-zA-Z]:\\/.test(replacement)
+                replacement = replacement.replace(/\\/gi, "\\\\")
+            else
+                replacement = replacement.replace(/\\/gi, "/")
+
             result = "require('#{replacement.replace(/\\/gi,"/")}')"

     done null, result


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -25,6 +25,6 @@
     height: 10

   fastEnemy.include(Follow)
-  fas
+  fastEnemy

 module()


Typo fix

Typo fix

--- homedisplay/display/static/js/clock.coffee
+++ homedisplay/display/static/js/clock.coffee
@@ -37,7 +37,7 @@
   update = ->
     days = new Array("su", "ma", "ti", "ke", "to", "pe", "la")
     currentTime = getMoment()
-    jq(".calendar").html currentTime.Format("dd D.M.")
+    jq(".calendar").html currentTime.format("dd D.M.")
     currentHours = currentTime.getHours()
     currentMinutes = currentTime.getMinutes()
     currentSeconds = currentTime.getSeconds()


Change how we construct ClickEvents to satisfy PhantomJS.

--- test/helper.coffee
+++ test/helper.coffee
@@ -29,6 +29,11 @@
     e = document.createEventObject()
     node.fireEvent 'onclick', e
   else
-    node.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+    e = document.createEvent 'MouseEvent'
+    e.initMouseEvent('click', true, true,
+      window, null, 0, 0, 0, 0, false, false, false, false, 0, null)
+    node.dispatchEvent e
+    # Doesn't work with PhantomJS:
+    #   node.dispatchEvent(new MouseEvent('click', { bubbles: true }))

 module.exports = { assertRendersHtml, click }


Sim: Adjust isImmune priorities

Sim: Adjust isImmune priorities

--- server/bw/priorities.coffee
+++ server/bw/priorities.coffee
@@ -129,13 +129,13 @@

 Priorities.isImmune ?= [
   Attachment.GravityPokemon  # Gravity overrides Ground-type immunities.
+  Attachment.Ingrain
+  Attachment.SmackDown
+  Item.IronBall
+  Attachment.Telekinesis
+  Ability.Levitate
+  Attachment.MagnetRise
+  Item.AirBalloon
   Attachment.Identify
-  Attachment.MagnetRise
-  Attachment.Ingrain
-  Attachment.Telekinesis
-  Attachment.SmackDown
-  Item.AirBalloon
-  Item.IronBall
-  Ability.Levitate
   Ability.Soundproof
 ]


[priorities] define shouldBlockExecution for XY.

--- server/xy/priorities.coffee
+++ server/xy/priorities.coffee
@@ -171,6 +171,33 @@
   # Ability.ZenMode
 ]

+Priorities.shouldBlockExecution ?= [
+  # Type-immunity/Levitate (Move#use)
+  # Wide Guard/Quick Guard
+  Attachment.Protect
+  Attachment.KingsShield
+  Attachment.SpikyShield
+  Attachment.MagicCoat
+  # TODO: Reimplement Magic Bounce as its own thing
+  Ability.DrySkin
+  Ability.FlashFire
+  Ability.Lightningrod
+  Ability.MotorDrive
+  Ability.SapSipper
+  Ability.Soundproof
+  Ability.StormDrain
+  Ability.Telepathy
+  Ability.VoltAbsorb
+  Ability.WaterAbsorb
+  Ability.WonderGuard
+  Ability.Overcoat
+  Item.SafetyGoggles
+  Attachment.Ingrain
+  Attachment.Charging
+  Attachment.SmackDown
+  Attachment.Substitute
+]
+
 Priorities.isImmune ?= [
   Attachment.GravityPokemon  # Gravity overrides Ground-type immunities.
   Attachment.Ingrain


Convert test to single code

Convert test to single code


Fix client tests when you go to /test/views

--- test/app/views/editor/level/EditorLevelView.spec.coffee
+++ test/app/views/editor/level/EditorLevelView.spec.coffee
@@ -8,6 +8,7 @@
       view = new LevelEditView({}, 'something')
       request = jasmine.Ajax.requests.first()
       request.respondWith {status: 200, responseText: JSON.stringify(emptyLevel)}
+      me.set('anonymous', false) # otherwise button may be disabled an not fire
       view.render()
       spyOn(view, 'openModalView')
       view.$el.find('#revert-button').click()


Make test more deterministic

Make test more deterministic

--- test/spec/releaseplanning/ReleasePlanningAppSpec.coffee
+++ test/spec/releaseplanning/ReleasePlanningAppSpec.coffee
@@ -51,7 +51,7 @@
     expect(@getColumn(1).getCards()[0].getRecord().get('Name')).toBe @featureData[0].Name

   it 'should show a progress bar based on preliminary estimate and planned velocity', ->
-    expect(@getProgressBarHtml(1)).toContain '12'
+    expect(@getProgressBarHtml(1)).toStartWith '12'
     expect(@getProgressBarHtml(1)).toContain '20'

   it 'should order the release columns based on end date', ->


Add defensive check for options.plan

Add defensive check for options.plan

--- client/ide/lib/views/collaboration/limitedvideocollaborationmodal.coffee
+++ client/ide/lib/views/collaboration/limitedvideocollaborationmodal.coffee
@@ -18,6 +18,7 @@
     options.cssClass = 'free-plan computeplan-modal env-modal'
     options.width   ?= 336
     options.overlay ?= yes
+    options.plan   or= 'free'

     super options, data


pass property names to getters and setters

--- bokehjs/src/coffee/common/has_properties.coffee
+++ bokehjs/src/coffee/common/has_properties.coffee
@@ -82,7 +82,7 @@
         if _.has(this, 'properties') and
            _.has(@properties, key) and
            @properties[key]['setter']
-          @properties[key]['setter'].call(this, val)
+          @properties[key]['setter'].call(this, val, key)
           toremove.push(key)
       if not _.isEmpty(toremove)
         attrs = _.clone(attrs)
@@ -205,7 +205,7 @@
           return @property_cache[prop_name]
         else
           getter = prop_spec.getter
-          computed = getter.apply(this)
+          computed = getter.apply(this, [prop_name])
           if @properties[prop_name].use_cache
             @add_cache(prop_name, computed)
           return computed


Fixed case for q

Fixed case for q

--- src/api/mods.coffee
+++ src/api/mods.coffee
@@ -5,7 +5,7 @@
 errors = error = require "../error"
 _  = require("lodash")
 parse = require "../parser"
-Q = require "Q"
+Q = require "q"

 ###
 Lists the mods and pass them to the then with a `totalCount` property that counts the mods


Added a new function to load javascripts by using script tags. See #49638

--- src/base/CUI.coffee
+++ src/base/CUI.coffee
@@ -842,6 +842,20 @@
 		data = data.replace(/"/g, """).replace(/\'/g, "'")
 		data

+	@loadScript: (src) ->
+		deferred = new CUI.Deferred
+		script = CUI.dom.element("script", src: src)
+		script.addEventListener("load", (ev) =>
+			deferred.resolve(ev)
+			document.body.removeChild(script)
+		)
+		script.addEventListener("error", (ev) =>
+			deferred.reject(ev)
+			document.body.removeChild(script)
+		)
+		document.body.appendChild(script)
+		return deferred.promise()
+
 # http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
 	@browser: (->
 		map =


added radix to parseInt() call

added radix to parseInt() call

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -213,7 +213,7 @@
 		collaborators: -1
 		dropbox: true
 		versioning: true
-		compileTimeout: parseInt(process.env["COMPILE_TIMEOUT"]) or 180
+		compileTimeout: parseInt(process.env["COMPILE_TIMEOUT"] or 180, 10)
 		compileGroup: "standard"
 		trackChanges: true
 		templates: true


improved the URL expansion/text shortening issue. still has problems with overly long links

--- core/utils.coffee
+++ core/utils.coffee
@@ -137,8 +137,8 @@
     return null unless text
     # @expandWwwDotDomains @expandUrls @expandUsernames @expandTags text
     text = text.replace /
/g, ' '
+    text = __utils.putShowMore text if shorten
     text = @expandUrls @expandUsernames text
-    text = __utils.putShowMore text if shorten
     # @expandWwwDotDomains @expandUrls @expandUsernames text

   expandWwwDotDomains: (text) ->
@@ -232,17 +232,16 @@
       maxLength : l + Math.floor(l/10)
       suffix    : ''

-    log "Length comparison", shortenedText.length-(Encoder.htmlEncode(shortenedText).length)
-
-    text = if text.length > shortenedText.length
-      morePart  = ""
+    # log "[#{text.length}:#{Encoder.htmlEncode(text).length}/#{shortenedText.length}:#{Encoder.htmlEncode(shortenedText).length}]"
+    text = if Encoder.htmlEncode(text).length > Encoder.htmlEncode(shortenedText).length
+      morePart = ""
       morePart += "···"
-      morePart += text.substr shortenedText.length
+      morePart += Encoder.htmlEncode(text).substr Encoder.htmlEncode(shortenedText).length
       morePart += "···"
       morePart += ""
-      shortenedText + morePart
+      Encoder.htmlEncode(shortenedText) + morePart
     else
-      shortenedText
+      Encoder.htmlEncode shortenedText

   shortenText:do ->
     tryToShorten = (longText, optimalBreak = ' ', suffix)->


options: removed unused function for closing infoscreen tabs - ...when disabling infoscreen feature

--- cs/options.coffee
+++ cs/options.coffee
@@ -558,8 +558,6 @@
                   # Create Bigscreen in a new tab
                   Browser.openBackgroundTab url
     else
-      # # Close any open Bigscreen tabs
-      # closeBigscreenTabs()
       # Refresh office status
       if Affiliation.org[ls.affiliationKey1].hw
         Browser.getBackgroundProcess().updateOfficeAndMeetings true
@@ -627,22 +625,6 @@
                 $('#bigscreenPreview').attr 'src', 'about:blank'
   ), 500

-# COMMENTED OUT: This requires 'tabs' permission, which isn't cool.
-# closeInfoscreenTabs = ->
-#   chrome.windows.getAll # OPERA?
-#     populate: true,
-#     (window_list) ->
-#       list = []
-#       for win of window_list
-#         tabs = window_list[win].tabs
-#         for tab of tabs
-#           tab = tabs[tab]
-#           titleIndex = tab.title.indexOf "Infoscreen"
-#           urlIndex = tab.url.indexOf "infoscreen.html"
-#           if titleIndex >= 0
-#             if urlIndex >= 0
-#               chrome.tabs.remove tab.id # OPERA?
-
 restoreChecksToBoxes = ->
   # Restore checks to boxes from localStorage
   $('input:checkbox').each (index, element) ->


Set appended view to 'position: absolute' in ::appendToLinesView

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -45,6 +45,7 @@
     @editor.pixelPositionForScreenPosition(screenPosition)

   appendToLinesView: (view) ->
+    view.css('position', 'absolute')
     @find('.scroll-view-content').prepend(view)

   beforeRemove: ->


Removed old alert message.

Removed old alert message.

--- app/assets/javascripts/models/message.js.coffee
+++ app/assets/javascripts/models/message.js.coffee
@@ -12,9 +12,7 @@

     user = session.get('users').findOrInitialize(args.user)
     @set('user',user)
-
-    # @get('user').on 'change', -> alert("wat")
-
+
   timestamp: ->
     new Date(@get('timestamp'))


ListView: Improved ListViewTreeHeaderNode, make first column resizable.

--- src/elements/ListView/ListViewTreeHeaderNode.coffee
+++ src/elements/ListView/ListViewTreeHeaderNode.coffee
@@ -6,13 +6,9 @@
 				check: (v) ->
 					v.length > 0

-	renderContent: ->
+	setElement: (@element) ->
+
+	render: ->
 		for header, idx in @_headers
-			if idx == 0
-				if header.label instanceof CUI.Label
-					ret = header.label
-				else
-					ret = new CUI.defaults.class.Label(header.label)
-			else
-				@addColumn(new ListViewHeaderColumn(header))
-		ret
+			@addColumn(new ListViewHeaderColumn(header))
+		@


Bubble upload:percentComplete

Bubble upload:percentComplete

--- lib/assets/javascripts/jack_up/processor.coffee
+++ lib/assets/javascripts/jack_up/processor.coffee
@@ -30,7 +30,7 @@
       reader.readAsDataURL(file)

       fileUploader = new JackUp.FileUploader(path: @uploadPath)
-      @bubble 'upload:start', 'upload:success', 'upload:failure', 'upload:sentToServer',
+      @bubble 'upload:start', 'upload:success', 'upload:failure', 'upload:sentToServer', 'upload:percentComplete',
         from: fileUploader

       fileUploader.upload file


Improve stacktrace reporting

Improve stacktrace reporting

--- lib/assets/javascripts/oojspec/runner.js.coffee
+++ lib/assets/javascripts/oojspec/runner.js.coffee
@@ -257,6 +257,10 @@
   handleResult: ->
     (@events.emit 'test:success', name: @description; return) unless @error
     @error.handled = true
+    stack = if traceErr = @error.lastAsyncTraceError then @filterStack traceErr.stack
+    else @filterStack @error.stack
+    @error.originalStack = @error.stack if @error.stack
+    @error.stack = stack if stack
     if @error.name is 'AssertionError'
       @events.emit 'test:failure', name: @description, error: @error
       return
@@ -268,6 +272,16 @@
     @error.name = 'Exception'
     @error.name += " in #{@error.source}" if @error.source
     @events.emit 'test:error', name: @description, error: @error
+
+  filterStack: (stack) ->
+    return unless stack
+    filteredStack = []
+    for l in stack.split("\n")
+      continue if /at ExampleDsl\.(runs|waitsFor)/.test l
+      continue if /at Object\.(referee|assert)/.test l
+      break if /Example\.tryBlock/.test l
+      filteredStack.push l
+    filteredStack.join "\n"

 class ExampleGroupWithoutHooks
   constructor: (@params, @binding, @bare, @blocks, @onFinish, @onError)->
@@ -314,7 +328,7 @@

   runNextAsyncStep: ->
     (@finish(); return) unless @steps.length
-    step = @steps.shift()
+    [step, @lastAsyncTraceError] = @steps.shift()
     if typeof step is 'function'
       @tryBlock step, @runNextAsyncStep
     else
@@ -330,7 +344,8 @@
       (@finish {timeout: true, @description}; return) if new Date().getTime() > @deadline
       setTimeout @keepTryingCondition, TICK

-  finish: ->
+  finish: (error) ->
+    error.lastAsyncTraceError = @lastAsyncTraceError if error and @lastAsyncTraceError
     if @binding and not @bare
       (@binding[m] = b if b = @describeDsl[m]) for m in RESERVED_FOR_DESCRIPTION_DSL
       delete @binding[m] for m in RESERVED_FOR_EXAMPLE_DSL
@@ -339,14 +354,15 @@
 class ExampleDsl
   constructor: (@assert, @expect, @fail, @refute)-> @_asyncQueue_ = []

-  runs: (step)=> @_asyncQueue_.push step
+  runs: (step)=>
+    @_asyncQueue_.push [step, new Error('trace')]

   waitsFor: =>
     for a in arguments
       (condition = a; continue)   if typeof a is "function"
       (timeout = a; continue)     if typeof a is "number"
       (description = a; continue) if typeof a is "string"
-    @_asyncQueue_.push [condition, timeout, description]
+    @_asyncQueue_.push [[condition, timeout, description], new Error('trace')]

 class StepContext
   constructor: (@assert, @expect, @fail)->


Be more generous to server hiccups

Be more generous to server hiccups

--- api.coffee
+++ api.coffee
@@ -21,6 +21,8 @@
 config = require './config'
 winston = require 'winston'
 debug = (require 'debug')('Chatbot:api')
+
+remainingFails = 3

 # attempts to retrieve the current security token
 # calls the callback without parameters afterwards
@@ -104,10 +106,14 @@
 	common.request.get config.host + '/index.php/NewMessages/', (err, res, body) ->
 		try
 			data = JSON.parse body
+			remainingFails = 3
 		catch e
-			common.fatal "Invalid JSON returned by NewMessages #{body}"
-
-		callback data if callback?
+			unless --remainingFails
+				common.fatal "Invalid JSON returned by NewMessages #{body}"
+			else
+				winston.warn "Fetching messages failed. Remaining fails until shutdown: #{remainingFails}"
+
+		callback? data

 # Permanently fetches messages with a delay of half a second between requests
 recursiveFetchMessages = (callback) ->


Renamed temporary field to temporaryFile.

--- server/publication.coffee
+++ server/publication.coffee
@@ -48,7 +48,7 @@
     assert @importing?[0]?.person?._id
     assert.equal @importing[0].person._id, Meteor.personId()

-    Publication._filenamePrefix() + 'tmp' + Storage._path.sep + @importing[0].temporary + '.pdf'
+    Publication._filenamePrefix() + 'tmp' + Storage._path.sep + @importing[0].temporaryFile + '.pdf'

   _verificationSamples: (personId) =>
     _.map _.range(NUMBER_OF_VERIFICATION_SAMPLES), (num) =>
@@ -123,7 +123,7 @@
             person:
               _id: Meteor.personId()
             filename: filename
-            temporary: Random.id()
+            temporaryFile: Random.id()
             uploadProgress: 0
       # If we have the file, ask for verification. Otherwise, ask for upload
       id = existingPublication._id
@@ -139,7 +139,7 @@
           person:
             _id: Meteor.personId()
           filename: filename
-          temporary: Random.id()
+          temporaryFile: Random.id()
           uploadProgress: 0
         ]
         sha256: sha256


compiler: fixes to build from pure json

compiler: fixes to build from pure json

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -2,7 +2,7 @@
 # builds a parser for xml from its inferred structure

 get_inodes = (node) ->
-  return [] if node.isLeaf()
+  return [] if node.children?.length is 0
   inodes = [node]
   for name, child of node.children
     child_inodes = get_inodes child
@@ -44,7 +44,7 @@

 make_builder = (node) ->
   indent = "      "
-  field = node.tag
+  field = node.name
   builder = builderize field
   "#{indent}when '#{field}'\n#{indent}  Builder = #{builder}"


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -7,7 +7,7 @@
   health: 100

 player.cooldown "health",
-
+  target

 # => `velocity is {x: 0, y: 0} and position is {x: 0, y: 0}`


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -6,7 +6,7 @@
 player = GameObject
   health: 100

-player.coo
+player.cooldown

 # => `velocity is {x: 0, y: 0} and position is {x: 0, y: 0}`


refactoring of dataflux

refactoring of dataflux

--- src/dataflux.coffee
+++ src/dataflux.coffee
@@ -78,11 +78,7 @@
     return false

   isFluxFile: (filePath) ->
-    rel = @path.relative @dataFluxFolder, filePath
-    if rel[0...2] is ".."
-      return false
-    else
-      return true
+    @path.isSubfolder @dataFluxFolder, filePath

   isDirectory: (filePath) ->
     @fse.lstatSync(filePath).isDirectory()


Some POSIX functions not available on Windows were being used

--- src/dns-server.iced
+++ src/dns-server.iced
@@ -41,7 +41,7 @@

     secure: ->
-        if process.getuid() is 0
+        if process.getuid and process.getgid and process.getuid() is 0
             if @gid
                 logger.info "setting gid to", @gid
                 process.setgid @gid


Range headers

Range headers

--- src/podroller/core.coffee
+++ src/podroller/core.coffee
@@ -147,22 +147,51 @@
         @loadPreroll stream_key, req, (predata = null) =>
             # compute our final size
             fsize = (id3?.length||0) + (predata?.length||0) + size
+            fstart = 0
+            fend = fsize - 1

             console.log "id3 length is ", id3?.length||0

             @listeners++
-
+
+            # Check if range request
+            # False by default
+            rangeRequest = false
+
+            # Is the range header a string?
+            rangeStr = req.headers.range
+
+            # Is the range header formatted properly?
+            if _u.isString rangeStr
+                rangeVals = rangeStr.match(/bytes=(.+)-(.+)?/)
+
+            # If both of the above are true, then check if the start and end values
+            # are valid and within the file size
+            if !_u.isUndefined rangeStr and !_u.isNull rangeVals
+                rangeStart = _u.isNumber(rangeVals[1]) && rangeVals[1] >= fstart && rangeVals[1] < fend ? rangeVals[1] - 0 : fstart
+                rangeEnd = _u.isNumber(rangeVals[2]) && rangeVals[2] > fstart && rangeVals[2] <= fend ? rangeVals[2] - 0 : fend
+                rangeRequest = true
+
+
+            # What is the actual length of content being sent back?
+            length = rangeRequest ? (rangeEnd - rangeStart) + 1 : fsize
+
             # send out headers
             headers =
                 "Content-Type":         "audio/mpeg",
                 "Connection":           "close",
                 "Transfer-Encoding":    "identity",
-                "Content-Length":       fsize
+                "Content-Length":       length
+
+            if rangeRequest
+                headers["Status"] = "206 Partial Content"
+                headers["Accept-Ranges"] = "bytes"
+                headers["Content-Range"] = "bytes #{rangeStart}-#{rangeEnd}/#{fsize}"
+

             console.log "final size should be ", fsize
-
             console.log "request method is ", req.method
-
+
             res.writeHead 200, headers

             if req.method == "HEAD"


tween with arbitrary easing function

tween with arbitrary easing function

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -161,7 +161,7 @@

     transform = Matrix.translation(center.x.floor(), center.y.floor())

-    transform = transform.concat(Matrix.scale(I.scale * I.s, I.scale * I.scaleY))
+    transform = transform.concat(Matrix.scale(I.scale * I.scaleX, I.scale * I.scaleY))
     transform = transform.concat(Matrix.rotation(I.rotation)) if I.rotation
     transform = transform.concat(Matrix.HORIZONTAL_FLIP) if I.hflip
     transform = transform.concat(Matrix.VERTICAL_FLIP) if I.vflip


Added warning for when sprite has no draw method

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -48,7 +48,10 @@
       I.transform = Matrix.translation(I.x, I.y)

     if I.sprite
-      I.sprite.draw(canvas, 0, 0)
+      if I.sprite.draw?
+        I.sprite.draw(canvas, 0, 0)
+      else
+        warn?("Sprite has no draw method!")
     else
       canvas.fillColor(I.color)
       canvas.fillRect(0, 0, I.width, I.height)


Added a removeAllFiles function.

Added a removeAllFiles function.

Closes #82

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -468,6 +468,13 @@

     @emit "removedfile", file
     @emit "reset" if @files.length == 0
+
+  # Removes all files that aren't currently processed from the list
+  removeAllFiles: ->
+    # Create a copy of files since removeFile() changes the @files array.
+    for file in @files.slice()
+      @removeFile file unless file in @filesProcessing
+    return null

   createThumbnail: (file) ->


testing deployment

testing deployment

--- src/private/server.coffee
+++ src/private/server.coffee
@@ -22,7 +22,7 @@
     console.log req.body
     res.redirect '/thanks'

-app.get '/thanks', (req, res) ->
+app.get '/thanks-buddy', (req, res) ->
     res.sendFile 'thanks.html', {root: path.join(__dirname, "../public")}

 app.get '/login', (req, res) ->


Added comment message method, unified response callbacks

--- src/flowdock.coffee
+++ src/flowdock.coffee
@@ -14,8 +14,10 @@
   uri = url.parse(process.env.FLOWDOCK_API_URL || 'https://api.flowdock.com')

 class Session extends process.EventEmitter
+
   constructor: (@email, @password) ->
     @auth = 'Basic ' + new Buffer(@email + ':' + @password).toString('base64')
+
   flows: (callback) ->
     uri = baseURL()
     uri.path = '/flows?users=1'
@@ -36,11 +38,11 @@
         return

       flows = JSON.parse body.toString("utf8")
-      callback flows
+      callback(flows, res) if callback?

   # Start streaming flows given as argument using authentication credentials
   #
-  # flows - Flow id (String <subdomain>:<flow> or <subdomain>/</flow>) or array of flow ids
+  # flows - Flow id or array of flow ids
   # options - query string hash
   #
   # Returns Stream object
@@ -67,17 +69,26 @@
       else if res.statusCode >= 300
         @emit 'error', res.statusCode
         return
-
-      callback res if callback
+      callback(body, res) if callback?

   # Send a chat message to Flowdock
-  message: (flow, message, tags, callback) ->
+  message: (flowId, message, tags, callback) ->
     data =
-      flow: flow
+      flow: flowId
       event: 'message'
       content: message
       tags: tags || []
     @send "/messages", data, callback
+
+  # Send a chat comment to Flowdock
+  comment: (flowId, parentId, comment, tags, callback) ->
+    data =
+      flow: flowId
+      message: parentId
+      event: 'comment'
+      content: comment
+      tags: tags || []
+    @send "/comments", data, callback

   # Send a private message to Flowdock
   privateMessage: (userId, message, tags, callback) ->
@@ -88,10 +99,11 @@
     @send "/private/#{userId}/messages", data, callback

   # Change status on Flowdock
-  status: (flow, status) ->
+  status: (flowId, status, callback) ->
     data =
       event: 'status'
       content: status
-    @send flow, data
+      flow: flowId
+    @send "/messages", data, callback

 exports.Session = Session


fix mime.lookup()

fix mime.lookup()

--- src/fotolife.coffee
+++ src/fotolife.coffee
@@ -33,7 +33,7 @@
     method = 'post'
     path = '/atom/post'
     title = options.title ? ''
-    type = options.type ? mime(options.file)
+    type = options.type ? mime.lookup(options.file)
     encoded = fs.readFileSync(options.file).toString('base64')
     # TODO: XML encode
     body = """


removes context cuz I no use

removes context cuz I no use

--- src/goldfish.coffee
+++ src/goldfish.coffee
@@ -19,7 +19,6 @@
   constructor: (options)->
     throw new Error("must specify populate function") unless options?.populate and typeof options.populate is "function"
     @_populate = options.populate
-    @_context = if options?.context? then options.context else null
     @_expires = if options?.expires? then options.expires else Infinity
     @_remind = if options?.remind? then options.remind else false
     @_capacity = if options?.capacity? then options.capacity else Infinity
@@ -56,7 +55,7 @@
       delete @_queue[hash]
       for fn in functions
         fn.apply(null, [err].concat(result))
-    @_populate.apply(@_context, args.concat([callback]))
+    @_populate.apply(null, args.concat([callback]))
   clear: =>
     while @_size > 0
       @_evict(@_oldest)


Check roles correctly.

Check roles correctly.

--- src/greeting.coffee
+++ src/greeting.coffee
@@ -5,7 +5,7 @@
 #   None
 #
 # Configuration:
-#   HUBOT_GREETING_FFA - If set, any user can set any other user's greeting.
+#   HUBOT_GREETINGS_FFA - If set, any user can set any other user's greeting.
 #
 # Commands:
 #   hubot greet <name> - Test out <name>'s current greeting; can be "me" or "yourself".
@@ -17,7 +17,7 @@
 # Author:
 #   smashwilson

-freeForAll = process.env.HUBOT_GREETING_FFA?
+freeForAll = process.env.HUBOT_GREETINGS_FFA?

 GREETING_ROLE = 'greeting czar'

@@ -54,8 +54,7 @@

   verifyPermission = (msg, target) ->
     return true if freeForAll or msg.message.user.name is target
-    user = robot.brain.userForName()
-    if robot.auth.hasRole msg.message.user.name, GREETING_ROLE
+    if robot.auth.hasRole(msg.message.user, GREETING_ROLE)
       true
     else
       msg.reply "I can't do that, you're not a #{GREETING_ROLE}!"


modified

modified

--- src/jsonsina.coffee
+++ src/jsonsina.coffee
@@ -70,6 +70,7 @@

       if arr?.length > 0 # 必須這樣寫,不能簡化為: for each in arr?
         for each in arr
+          each.date = each.day
           each.day = new Date each.day
           each.open = Number each.open
           each.low = Number each.low


Address keybase/keybase-issues#256

Address keybase/keybase-issues#256

But this will only fix the ugly crash.  The program is still going to crash

--- src/keymanager.iced
+++ src/keymanager.iced
@@ -61,7 +61,7 @@
       @key = @ring.make_key { key_id_64, secret : true }
       await @key.load esc defer()
     else
-      err = new E.GenerationError "Failed to parse output of key generation"
+      err = new E.GenerateError "Failed to parse output of key generation"
     cb err

   #--------------


MR comment fix

MR comment fix

--- client/Account/views/sshkeys.coffee
+++ client/Account/views/sshkeys.coffee
@@ -137,15 +137,13 @@
       @info.$('span.key').text "#{key.substr(0,45)} . . . #{key.substr(-25)}"
       @swappable.swapViews()
       @getDelegate().emit "UpdatedItems"
-      @form.buttons.save.hideLoader()
     else unless key
       new KDNotificationView
         title : "Key shouldn't be empty."
-      @form.buttons.save.hideLoader()
     else unless title
       new KDNotificationView
         title : "Title required for SSH key."
-      @form.buttons.save.hideLoader()
+    @form.buttons.save.hideLoader()

   partial:(data)->
     """


Terminal: no need to set defaults to localStorage

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -28,10 +28,6 @@
     if @readOnly
       for keyHandler in ['keyDown', 'keyPress', 'keyUp', 'paste']
         @[keyHandler] = noop
-
-    try
-      localStorage?["WebTerm.logRawOutput"] ?= "false"
-      localStorage?["WebTerm.slowDrawing"]  ?= "false"

     @parent               = containerView
     @container            = containerView.$()


Terminal: richard's throttle implementation removed

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -133,9 +133,7 @@
     @measurebox.empty()
     @setSize width, height

-  windowDidResize: ->
-    window.clearTimeout @updateSizeTimer
-    @updateSizeTimer = window.setTimeout (=> @updateSize()), 500
+  windowDidResize: _.throttle (-> @updateSize()), 500

   lineFeed: ->
     if @cursor.y is @screenBuffer.scrollingRegion[1]


Do not consider packages without versions a hard failure

--- app.coffee
+++ app.coffee
@@ -397,23 +397,28 @@
 							async.parallel tasks, (err, data) ->
 								versionsCallback err, data
 					, (err, data) ->
-						# data = [ packageData, hash? ]
-
-						data = data.filter (item) -> item?
-						data.sort (a, b) ->
-							a = a[0].version
-							b = b[0].version
+						# data = [ [ packageData, hash? ], … ]
+						if err?
+							fileCallback err
+						else
+							data = data.filter (item) -> item?
+							data.sort (a, b) ->
+								a = a[0].version
+								b = b[0].version
+
+								if a is b
+									0
+								else if (createComparator "$v > #{b}")(a)
+									1
+								else
+									-1

-							if a is b
-								0
-							else if (createComparator "$v > #{b}")(a)
-								1
+							if data.length is 0
+								warn "Could not find valid versions for #{packageFolder}"
+								# this will be filtered out afterwards
+								fileCallback null, undefined
 							else
-								-1
-						if data.length is 0
-							fileCallback "Could not find valid versions for #{packageFolder}"
-						else
-							fileCallback err, data
+								fileCallback err, data
 		, (err, data) ->
 			if err?
 				error "Error reading package list: #{err}"


Force HTTPS

Force HTTPS

--- app.coffee
+++ app.coffee
@@ -6,8 +6,10 @@
 routes = require("./routes")
 user = require("./routes/user")
 http = require("http")
+https = require("https")
 path = require("path")
 app = express()
+fs = require("fs")

 # all environments
 app.set "port", process.env.PORT or 3000
@@ -30,6 +32,16 @@
 app.get "/javascripts/*", serveAsset
 app.get "/stylesheets/*", serveAsset
 app.get "/", routes.index
-
-http.createServer(app).listen app.get("port"), ->
-  console.log "Express server listening on port " + app.get("port")
+
+if app.get("env") is "production"
+  options =
+    key: fs.readFileSync('/etc/haproxy/certs/cyberia2020-key.pem')
+    cert: fs.readFileSync('/etc/haproxy/certs/cyberia2020-cert.pem')
+    ca: fs.readFileSync('/etc/haproxy/certs/cyberia2020-intermediate.pem')
+
+  https.createServer(options, app).listen app.get("port"), ->
+    console.log "Express server listening on port " + app.get("port")
+
+else
+  http.createServer(app).listen app.get("port"), ->
+    console.log "Express server listening on port " + app.get("port")


LIb updated

LIb updated

--- vendor/assets/javascripts/jquery.awesome-cropper.coffee
+++ vendor/assets/javascripts/jquery.awesome-cropper.coffee
@@ -225,7 +225,7 @@

   if (settings.proxy_path != undefined)
     $urlSelectButton.click ->
-      return unless $urlSelect.val().match(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/)
+      return unless $urlSelect.val().match(/^(https?:\/\/)?/)
       return unless fileAllowed($urlSelect.val())

       setLoading()


Double the search input delay to prevent races

The full story is in #247. Essentially fast typists will type multiple
letters into the search input field in less than half a second.

Since our input delay is 200 ms (.2 second) then it’s possible for
someone to type one letter every .2 second which will fire one
XHR search query to the server for each and every letter.

This is responsive but not ideal because of the asynchronous
nate of these queries. It’s very possible that when searching for
“monday”, the response for “mon” will arrive before the
response for “m”. And when “m” does arrive, it will wipe out the
search results for “mon” (which are probably good) with a useless
result since single-letter queries can’t possibly yield a full-text
match.

Another way around this issue would be to not send any request
to the server until we have three letters in the search input field.

I’m pretty sure I used to have it set up that way too.

--- app/assets/javascripts/components/search.coffee
+++ app/assets/javascripts/components/search.coffee
@@ -24,7 +24,7 @@
     $input      : $( '.js-search-input' )
     $results    : $( '.js-search-results' )
     hiddenClass : 'is-hidden'
-    inputDelay  : 200
+    inputDelay  : 400
     onSubmit    : null
   , options


updated the configuration and added a configuration for how to save content (json or form)

--- app/assets/javascripts/mercury/config.js.coffee
+++ app/assets/javascripts/mercury/config.js.coffee
@@ -1,25 +1,28 @@
 Mercury.config =
+
+  # Saving
+  #
+  # When saving content, it can be sent to the server as JSON, or as normal form variables.
+  #
+  saveStyle: 'json' # or form
+

   # Ignored Links
   #
-  # Links need to be specially handled because the editor loads content into an
-  # iframe, and to ensure that links load outsite of the content iframe they're
-  # "hijacked" to load in the _top window.  This allow for displaying a
-  # confirmation when leaving an edited page.  There are times when you may be
-  # observing clicks on anchor tags and you might not want this behavior, for
-  # instance a link that pops a modal.  You can add classnames to this array
-  # and the special handling won't be attached to anchors that have them.
+  # Links need to be specially handled because the editor loads content into an iframe, and to ensure that links load
+  # outsite of the content iframe they're "hijacked" to load in the _top window.  This allow for displaying a
+  # confirmation when leaving an edited page.  There are times when you may be observing clicks on anchor tags and you
+  # might not want this behavior, for instance a link that pops a modal.  You can add classnames to this array and the
+  # special handling won't be attached to anchors that have them.
   #
   ignoredLinks: ['lightview'],

   # Image Uploading (via editable regions)
   #
-  # If you drag images (while pressing shift) from your desktop into an
-  # editable region it will be uploaded to the server and inserted into the
-  # region.  This configuration allows you to specify if you want to disable
-  # this feature, the accepted mime-types, file size restrictions, and other
-  # things related to uploading.
+  # If you drag images (while pressing shift) from your desktop into an editable region it will be uploaded to the
+  # server and inserted into the region.  This configuration allows you to specify if you want to disable this feature,
+  # the accepted mime-types, file size restrictions, and other things related to uploading.
   #
   uploading:
     enabled: true
@@ -31,22 +34,17 @@

   # Toolbars
   #
-  # This is where you can customize the toolbar by adding new ones, and add or
-  # change buttons and their behaviors.  Any top level object put here will
-  # create a new toolbar.  Buttons are nested inside the toolbars.  Some
-  # toolbars are custom (the snippetable one for instance), and to denote that
-  # a custom toolbar, use _custom: true.
+  # This is where you can customize the toolbar by adding new ones, and add or change buttons and their behaviors.  Any
+  # top level object put here will create a new toolbar.  Buttons are nested inside the toolbars.  Some toolbars are
+  # custom (the snippetable one for instance), and to denote that a custom toolbar, use _custom: true.
   #
-  # Buttons can be grouped.  A button group is simply a way to wrap buttons
-  # for styling, and can also handle enabling or disabling all the buttons
-  # within it by using a context.  The table button group is a good example of
-  # this.
+  # Buttons can be grouped.  A button group is simply a way to wrap buttons for styling, and can also handle enabling or
+  # disabling all the buttons within it by using a context.  The table button group is a good example of this.
   #
-  # It's important to note that each of the names (keys), in this object must
-  # be unique, regardless of if it's in a button group, or nested, etc.
+  # It's important to note that each of the names (keys), in this object must be unique, regardless of if it's in a
+  # button group, or nested, etc.  This is because styling is applied to them by name.
   #
-  # Button format: [label, description, {type: action, type: action, etc}]
-  # The available button types are:
+  # Button format: [label, description, {type: action, type: action, etc}] The available button types are:
   #
   # button:  (default) calls handleCommand and passes the key of the object
   #          (eg. save, preview, undo etc.)
@@ -65,15 +63,12 @@
   #            a function that returns a string url
   # context: calls a callback function, expects the action to be:
   #            a function that returns a boolean to highlight the button
-  #            note: if a function isn't provided, the key will be passed to
-  #                  the contextHandler (eg. backcolor, bold), in which case
-  #                  default context will be used (for more info read the
-  #                  Contexts section below)
+  #            note: if a function isn't provided, the key will be passed to the contextHandler (eg. backcolor, bold),
+  #                  in which case default context will be used (for more info read the Contexts section below)
   # mode:    toggle a given mode in the editor, expects the action to be:
   #            a string, denoting the name of the mode
-  #            note: it's assumed that when a specific mode is turned on, all
-  #                  other modes will be turned off which happens automatically,
-  #                  thus putting the editor into a specific "state"
+  #            note: it's assumed that when a specific mode is turned on, all other modes will be turned off which
+  #                  happens automatically, thus putting the editor into a specific "state"
   # regions: allows buttons to be enabled/disabled based on what region type
   #          has focus, expects the action to be.
   #            an array of region types (eg. ['editable', 'snippetable']
@@ -81,8 +76,8 @@
   # preload: preloads views when the editor is loaded instead of on first open
   #          (only used for panels, selects, and palettes, otherwise ignored)
   #
-  # Separators are any "button" that's not an array, and are expected to be a
-  # string.  You can use two different separator styles: line, and spacer.
+  # Separators are any "button" that's not an array, and are expected to be a string.  You can use two different
+  # separator styles: line, and spacer.
   # '-' = line
   # ' ' = spacer
   #
@@ -170,14 +165,12 @@

   # Behaviors
   #
-  # Behaviors are used to change the default behaviors of a given region type
-  # when a given button is clicked.  For example, you may prefer to add HR tags
-  # using an HR wrapped within a div with a classname (for styling).  To add
-  # your own complex behaviors you can do so here.
+  # Behaviors are used to change the default behaviors of a given region type when a given button is clicked.  For
+  # example, you may prefer to add HR tags using an HR wrapped within a div with a classname (for styling).  You can add
+  # your own complex behaviors here.
   #
-  # You can see how the behavior matches up directly with the button name.
-  # It's also important to note that the callback functions are executed within
-  # the scope of the given region.
+  # You can see how the behavior matches up directly with the button name.  It's also important to note that the
+  # callback functions are executed within the scope of the given region, so you have access to all it's methods.
   #
   behaviors:
     horizontalrule: (selection) -> selection.replace('<hr/>')
@@ -192,10 +185,9 @@

   # Contexts
   #
-  # Contexts are used callback functions used for highlighting and
-  # disabling/enabling buttons and buttongroups.  When the cursor enters an
-  # element within an html region for instance we want to disable or highlight
-  # buttons based on the properties of the given node.
+  # Contexts are used callback functions used for highlighting and disabling/enabling buttons and buttongroups.  When
+  # the cursor enters an element within an html region for instance we want to disable or highlight buttons based on the
+  # properties of the given node.
   #
   # You can see some examples of contexts in:
   #


preventing default tap on header text

preventing default tap on header text

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -22,7 +22,9 @@

 $ ->
     toggle_menu = -> ($ '#menu').toggle()
-    ($ '#menu_button').tap(-> toggle_menu())
+    ($ '#menu_button').tap((e) ->
+        e.preventDefault()
+        toggle_menu())

 window.pluralize_like_count = (like_count) ->
     if like_count is 1 then 'like' else 'likes'


Removes the Rand from currency list

Removes the Rand from currency list

--- app/assets/javascripts/models/expense.js.coffee
+++ app/assets/javascripts/models/expense.js.coffee
@@ -16,7 +16,6 @@
           { id: "CAD", name: "Canada Dollars" },
           { id: "DKK", name: "Denmark Kroner" },
           { id: "NOK", name: "Norway Kroner" },
-          { id: "ZAR", name: "South Africa Rand" },
           { id: "SEK", name: "Sweden Krona" },
           { id: "CHF", name: "Switzerland Francs" },
           { id: "HUF", name: "Hungarian Forint" }


Fix visibility of markers with related popup or updated status.

--- app/assets/javascripts/views/map.view.js.coffee
+++ app/assets/javascripts/views/map.view.js.coffee
@@ -110,7 +110,7 @@
     if poppingNode?
       @_closePopup(poppingNode.get('id'))
       poppingNode.send('closed')
-      @filterLayers()
+      Ember.run.once(@, @filterLayers)
   ).observesBefore('poppingNode.id')

   getLayer: (nodeId)->
@@ -210,10 +210,13 @@
       return

     marker = @getLayer(poppingNode.get('id'))
-    icon = Wheelmap.MarkerLayer.createIcon
+    options =
       wheelchair: poppingNode.get('wheelchair')
       icon: poppingNode.get('icon')

+    icon = Wheelmap.MarkerLayer.createIcon(options)
+
+    $.extend(marker.feature.properties, options)
     marker.setIcon(icon)
   ).observes('poppingNode.wheelchair')


Remove model attribute binding from TextArea view

--- app/assets/javascripts/views/textarea.js.coffee
+++ app/assets/javascripts/views/textarea.js.coffee
@@ -1,4 +1,4 @@
 ETahi.TextArea = Ember.TextArea.extend
-  attributeBindings: [ "name", "type", "value", "model"]
+  attributeBindings: ["name", "type", "value"]
   focusOut: ->
     @get('model').save()


adjusted affixNav method for sticky sidebars

--- app/coffee/drm-sticky-nav/drm-sticky-nav.coffee
+++ app/coffee/drm-sticky-nav/drm-sticky-nav.coffee
@@ -39,7 +39,7 @@

         if _scroll > (top + _contentHeight)
             @nav.removeClass "sticky-#{_position}"
-        else if _scroll > (top - 100) and _navHeight < _winHeight
+        else if _scroll > (top - 50) and _navHeight < _winHeight
             @nav.addClass "sticky-#{_position}"
             @positionRight navPositionLeft
         else


Fix SVG creation in Firefox

Fix SVG creation in Firefox

--- app/controllers/classify-steps/base-step.coffee
+++ app/controllers/classify-steps/base-step.coffee
@@ -34,16 +34,15 @@

     if @hasDrawing
       @svg = new SVG width: 100, height: 100
-      @svg.el.style.display = 'none'
-      @svg.el.style.position = 'absolute'
-      @svg.el.style.left = 0
-      @svg.el.style.top = 0
-      @svg.el.style.width = SUBJECT_WIDTH
-      @svg.el.style.height = SUBJECT_HEIGHT
-      @svg.el.style.left = '50%'
-      @svg.el.style.top = '50%'
-      @svg.el.style.marginLeft = -0.5 * SUBJECT_WIDTH
-      @svg.el.style.marginTop = -0.5 * SUBJECT_HEIGHT
+      @svg.attr 'width', SUBJECT_WIDTH
+      @svg.attr 'height', SUBJECT_HEIGHT
+      @svg.attr 'style', """
+        display: none;
+        margin: #{-0.5 * SUBJECT_HEIGHT}px 0 0 #{-0.5 * SUBJECT_WIDTH}px;
+        left: 50%;
+        position: absolute;
+        top: 50%;
+      """
       @classifier.el.find('.subject').append @svg.el

     setTimeout =>


Fix initializing a public transit stop popup with the correct stop, not the latest in the list (i.e. fixed javascript closure in loop)

--- src/map-view.coffee
+++ src/map-view.coffee
@@ -341,9 +341,10 @@
                 marker = L.circle latLng,
                     clickable: true
                 marker.setRadius 3
-                marker.on 'click', (e) ->
-                    publicTransitStop = new PublicTransitStopView {stop, route}
-                    @.bindPopup(publicTransitStop.render().el).openPopup()
+                do (stop) ->
+                    marker.on 'click', (e) ->
+                        publicTransitStop = new PublicTransitStopView {stop, route}
+                        @.bindPopup(publicTransitStop.render().el).openPopup()
                 marker.addTo @publicTransitStopsLayer

         drawUnit: (unit, units, options) ->


Fix remove collaborator log bug

Fix remove collaborator log bug

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -429,8 +429,8 @@
     ###
     # **THIS IS AN ASYNC FUNCTION!**
     remove_collaborator: (project_id, account_id) =>
+        name = redux.getStore('users').get_name(account_id)
         f = (cb) =>
-            name = redux.getStore('users').get_name(account_id)
             webapp_client.project_remove_collaborator
                 project_id : project_id
                 account_id : account_id


Update projects.cjsx

Update projects.cjsx

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

     is_deleted: (project_id) =>
-        return !!@getIn(['project_map', project_id, 'deleted'])
+        return !!@getIn(['project_map', project_id, 'deleted'])

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


use notebook cells attribute

use notebook cells attribute

--- src/notebook.coffee
+++ src/notebook.coffee
@@ -24,15 +24,14 @@
     $.extend CodeMirror.commands, ed.commands

   create_notebook = ->
-    notebook = []
-    notebook.input_number = 1
-    notebook.default_options = {}
-    notebook.$document = $ '
'
-    notebook
+    cells: []
+    input_number: 1
+    default_options: {}
+    $document: $ '
'

   export_notebook = (current_cell) ->
     lead_js_version: 0
-    cells: current_cell.notebook.filter((cell) -> cell != current_cell).map (cell) ->
+    cells: current_cell.notebook.cells.filter((cell) -> cell != current_cell).map (cell) ->
       type: 'input'
       value: cell.editor.getValue()

@@ -46,23 +45,23 @@

   clear_notebook = (notebook) ->
     notebook.$document.empty()
-    notebook.length = 0
+    notebook.cell.slength = 0

   input_cell_at_offset = (cell, offset) ->
-    index = cell.notebook.indexOf cell
+    index = cell.notebook.cells.indexOf cell
     cell.notebook[index + offset]

   get_available_input_cell = (notebook) ->
-    last = notebook[notebook.length - 1]
+    last = notebook.cells[notebook.cells.length - 1]
     if last?.is_clean()
       return last
     else
       return null

   remove_cell = (cell) ->
-    index = cell.notebook.indexOf cell
+    index = cell.notebook.cells.indexOf cell
     cell.$el.remove()
-    cell.notebook.splice index, 1
+    cell.notebook.cells.splice index, 1

   add_context = (notebook, code='') ->
     cell = get_available_input_cell notebook
@@ -72,7 +71,7 @@
       cell = create_input_cell notebook, code
       notebook.$document.append cell.$el
       cell.rendered()
-      notebook.push cell
+      notebook.cells.push cell

     {editor} = cell
     editor.focus()
@@ -87,9 +86,9 @@
   run_in_info_context = (current_cell, code) ->
     cell = create_input_cell current_cell.notebook, code
     current_cell.$el.before cell.$el
-    index = notebook.indexOf cell
+    index = notebook.cells.indexOf cell
     cell.rendered()
-    current_cell.notebook.splice index, 0, cell
+    current_cell.notebook.cells.splice index, 0, cell
     cell.run code

   create_input_cell = (notebook, code) ->


Add removeGrammarForScopeName

Add removeGrammarForScopeName

--- src/registry.coffee
+++ src/registry.coffee
@@ -28,6 +28,10 @@
     _.remove(@injectionGrammars, grammar)
     @grammarUpdated(grammar.scopeName)

+  removeGrammarForScopeName: (scopeName) ->
+    grammar = @grammarForScopeName(scopeName)
+    @removeGrammar(grammar) if grammar?
+
   grammarUpdated: (scopeName) ->
     for grammar in @grammars when grammar.scopeName isnt scopeName
       @emit 'grammar-updated', grammar if grammar.grammarUpdated(scopeName)


:sake::mailbox_closed: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -8,7 +8,7 @@
 dependencies:
   "jquery-utils": "STRd6/jquery-utils:v0.1.2"
   runtime: "STRd6/runtime:v0.1.1"
-  "touch-canvas": "STRd6/touch-canvas:v0.2.0-alpha.18"
+  "touch-canvas": "STRd6/touch-canvas:v0.2.0-alpha.19"
   "commando": "STRd6/commando:v0.9.0"
 width: 480
 height: 320


Suppression d'un affichage pour les tests

--- js/game/entities/enemies/HoverPoule.coffee
+++ js/game/entities/enemies/HoverPoule.coffee
@@ -96,13 +96,6 @@

             @ctx.restore()

-
-            @ctx.fillStyle = "rgba(255, 59, 0, 0.63)"
-            @ctx.fillRect(@centerX, @centerY, 5, 5)
-            @ctx.fillStyle = "rgba(0, 255, 196, 0.63)"
-            @ctx.fillRect(@x, @y, 5, 5)
-
-

         # Appliquer le déplacement vertical
         updateVelocity: (dt) ->


ordering

ordering

--- src/sentence.coffee
+++ src/sentence.coffee
@@ -29,20 +29,6 @@

 lte = less_or_equal = (y) -> (x) -> x <= y

-be = to = (x) ->
-  if x is empty then return is_empty
-  if x is ok then return is_ok
-  if x is exist then return does_exist
-  if x is error then return should_throw
-  x
-
-nt = (f) ->
-  if f is empty then return negate is_empty
-  if f is ok then return negate is_ok
-  if f is exist then return negate does_exist
-  if f is error then return negate should_throw
-  negate f
-
 negate = (f) -> (x) -> not f x

 contain = include = (x) -> (y) -> y.indexOf(x) isnt -1
@@ -59,6 +45,20 @@
   if x is Object then return y instanceof x
   Object(y) instanceof x

+be = to = (x) ->
+  if x is empty then return is_empty
+  if x is ok then return is_ok
+  if x is exist then return does_exist
+  if x is error then return should_throw
+  x
+
+nt = (f) ->
+  if f is empty then return negate is_empty
+  if f is ok then return negate is_ok
+  if f is exist then return negate does_exist
+  if f is error then return negate should_throw
+  negate f
+
 sentence = module.exports = {exist, empty, ok, error, a, an, have, match, length, contain, include, negate, nt, to, be, than, less, lt, less_than, less_or_equal, lte, greater, gt, greater_than, greater_or_equal, gte, expect, equal, not_equal}

 Object.defineProperty sentence, "globals", value: ->


Added form processing

Added form processing

--- lib/assets/javascripts/wiselinks.js.coffee
+++ lib/assets/javascripts/wiselinks.js.coffee
@@ -1,9 +1,12 @@
 #= require _history
+
+String.prototype.ends_with = (suffix) ->
+  this.indexOf(suffix, this.length - suffix.length) != -1

 class Wiselinks
   constructor: (@$target = $('body'), options = {}) ->
     # check that JQuery or Zepto.js are available
-    throw "Load JQuery or Zepto.js to use Wiselinks" unless window.JQuery? || window.Zepto?
+    throw "Load JQuery to use Wiselinks" unless window.jQuery?

     self = this

@@ -21,7 +24,7 @@

     $(document).on(
       "submit", "form[data-push], form[data-replace]"
-      (event) ->
+      (event) ->
         self._process_form($(this))

         event.preventDefault()
@@ -68,13 +71,14 @@
       dataType: "html"
     )

-  _process_form: ($form) ->
+  _process_form: ($form) ->
     self = this

     $disable = $form.find(':input[value=""]')
     $disable.attr('disabled', true);

     params = {}
+
     for item in $form.serializeArray()
       if item.name != 'utf8'
         name = if item.name.ends_with('[]')
@@ -88,7 +92,7 @@
           params[name] = item.value

     serialized = []
-    for key in params
+    for key of params
       serialized.push("#{params[key]}=key")

     serialized = serialized.join('&').replace(/%|!/g, '')


removed a naughty console.log

removed a naughty console.log

--- app/assets/javascripts/admin/admin_in_place_editor.js.coffee
+++ app/assets/javascripts/admin/admin_in_place_editor.js.coffee
@@ -1,5 +1,4 @@
 $(document).ready ->
-  console.log(window.editorClass)
   if window.editorClass == 'taxon_concepts'
     window.adminEditor = new TaxonConceptsEditor()
   else if window.editorClass == 'taxon_listing_changes' or\


Use turbolinks:before-render in effective_select

--- app/assets/javascripts/effective_select/initialize.js.coffee
+++ app/assets/javascripts/effective_select/initialize.js.coffee
@@ -28,7 +28,11 @@
 $(document).on 'page:change', -> initialize()
 $(document).on 'turbolinks:load', -> initialize()
 $(document).on 'turbolinks:render', -> initialize()
-$(document).on 'turbolinks:before-cache', -> $('select.effective_select.initialized').select2('destroy')
+
+$(document).on 'turbolinks:before-render', ->
+  $input = $('select.effective_select.initialized')
+  $input.select2('destroy') if $input.data('select2')
+
 $(document).on 'cocoon:after-insert', -> initialize()

 # If we're working with a polymorphic select, split out the ID and assign the hidden _type and _id fields


Fix DOM.prependChild when element has no children

--- src/marbles/dom.js.coffee
+++ src/marbles/dom.js.coffee
@@ -49,7 +49,10 @@
     new_el

   prependChild: (el, node) ->
-    el.insertBefore(node, el.firstChild)
+    if el.firstChild
+      el.insertBefore(node, el.firstChild)
+    else
+      el.appendChild(node)

   removeNode: (el) ->
     el.parentNode?.removeChild(el)


Fix bug: every region was black, there was no legend

... and all regions were displaying unknown percentages

--- app/scripts/mapctrl.coffee
+++ app/scripts/mapctrl.coffee
@@ -8,13 +8,13 @@
     # FIXME: "ward" and "region" should be defined elsewhere I think
     # NOTE: Rewritten to avoid too much "ward" / "region" lookup
     getFeatureType = (feature) ->
-      return if feature.id.match /ward/ then "ward" else "region"
+      return if feature.properties.hasOwnProperty "REGNAME" then "region" else "ward"

     getFeaturedItem = (feature) ->
       itemType = getFeatureType(feature)
       nameProperty = if itemType == "region" then "REGNAME" else "Ward_Name"
       name = feature.properties[nameProperty].toLowerCase()
-      item = $scope[itemType + "name"]
+      item = $scope[itemType + "Map"][name]
       [name, item, itemType]

     getItem = (feature) ->
@@ -28,10 +28,10 @@
       ### EVENT HANDLERS ###
       ######################
       mouseOver = (e) ->
-        [name, item] = getFeaturedItem(e.target.feature)
+        [name, item, itemType] = getFeaturedItem(e.target.feature)

         if item
-          hoverText = item[type] + ": " + item[$scope.choroChoice].toPrecision(3) + "%"
+          hoverText = item[itemType] + ": " + item[$scope.choroChoice].toPrecision(3) + "%"
         else
           hoverText = name + ": unknown"

@@ -95,7 +95,7 @@
         })
         deferred.promise

-      makeLegend: (map) ->
+      makeLegend = (map) ->
         legend = L.control(
           position: 'bottomright'
         )
@@ -164,6 +164,7 @@
           fullscreenControl: true
           layers: [satLayer, regionLayer, wardLayer, clusterLayer]
         )
+        makeLegend(map)

         # Add a layer selector
         layerSelector = L.control.layers(baseMaps, overlayControls).addTo(map)


Made only non-empty attributes appear in sidebar

--- app/scripts/widgets.coffee
+++ app/scripts/widgets.coffee
@@ -107,7 +107,7 @@
         return [posItems, structItems]

     renderItem: (key, value, attrs, wordData, sentenceData, tokens) ->
-        if attrs.label
+        if attrs.label and value
             output = $("
:
")
         else
             output = $("

")
@@ -115,8 +115,8 @@
             return output.append(attrs.renderItem key, value, attrs, wordData, sentenceData, tokens)

         output.data("attrs", attrs)
-        if value == "|" or value == ""
-            output.append "${util.getLocaleString('empty')}"
+        if !value or value == "|" or value == ""
+            # output.append "${util.getLocaleString('empty')}"
             return output

         if attrs.type == "set" and attrs.display?.expandList


hook destroy events as well (for collections and models)

--- lib/assets/javascripts/stark/model_binding.js.coffee
+++ lib/assets/javascripts/stark/model_binding.js.coffee
@@ -2,6 +2,7 @@
 window.Stark or= {}
 class Stark.ModelBinding

+  # Bind the given view to 'change' or 'destroy' events triggered on this model
   bind_view: (view) ->

     # check to see if any of our parents have already bound here
@@ -18,7 +19,13 @@
     @bound_views.push(view)
     model = @
     @bind "change", ->
-      @log "redraw handler fired due to model binding on: ", model
+      @log "redraw handler fired (change) due to model binding on: ", model
+      @log "redrawing view: ", @
+      @redraw()
+    , view
+
+    @bind "destroy", ->
+      @log "redraw handler fired (destroy) due to model binding on: ", model
       @log "redrawing view: ", @
       @redraw()
     , view


Refactors default plugin click initalisation

--- lib/generators/js_percolator/templates/plugin.coffee
+++ lib/generators/js_percolator/templates/plugin.coffee
@@ -49,7 +49,7 @@
   # ===================================

   $ ->
-    $('body').on 'click.<%= file_name.camelize(:lower) %>.data-api', '[data-<%= file_name.camelize(:lower) %>Action^=Action]', ( e ) ->
+    $('body').on 'click.<%= file_name.camelize(:lower) %>.data-api', '[data-<%= file_name.camelize(:lower) %>]', ( e ) ->
       $(e.target).<%= file_name.camelize(:lower) %>()

 do ( plugin ) ->


url bug fix

url bug fix

--- lib/generators/webrtc_rails/templates/main.js.coffee
+++ lib/generators/webrtc_rails/templates/main.js.coffee
@@ -7,11 +7,11 @@
   onWebRTCReconnected: ->
   onWebRTCHangedUp: ->

-  constructor: (userToken, @localOutput, @remoteOutput) ->
+  constructor: (url, userToken, @localOutput, @remoteOutput) ->
     @localOutput = @localOutput[0] || @localOutput
     @remoteOutput = @remoteOutput[0] || @remoteOutput
     @_startOutput(@localOutput.tagName.toUpperCase() == 'VIDEO')
-    @_webSocketInitialize(userToken)
+    @_webSocketInitialize(url, userToken)
     @_addNetworkEventListener()

   connect: (remoteUserID) ->
@@ -50,8 +50,8 @@
     'OfferToReceiveAudio': true
     'OfferToReceiveVideo': true

-  _webSocketInitialize: (userToken) ->
-    @_webSocket = new WebSocket('ws://' + location.host + '/websocket')
+  _webSocketInitialize: (url, userToken) ->
+    @_webSocket = new WebSocket(url)
     @_webSocket.onopen = =>
       @_startHeartbeat()
       @_sendValue('setMyToken',


Fix preroll code to queue instead of stream

--- src/podroller/core.coffee
+++ src/podroller/core.coffee
@@ -227,15 +227,20 @@
         req = http.get opts, (rres) =>
             console.log "got preroll response ", count
             if rres.statusCode == 200
-                # stream preroll through to the output
+                # collect preroll and return it so length can be computed
+                pre_data = new Buffer(0)
+
                 rres.on "data", (chunk) =>
-                    res.write(chunk)
+                    buf = new Buffer(pre_data.length + chunk.length)
+                    pre_data.copy(buf,0)
+                    chunk.copy(buf,pre_data.length)
+                    pre_data = buf

                 # when preroll is done, call the output's callback
                 rres.on "end", =>
                     conn.removeListener "close", conn_pre_abort
                     conn.removeListener "end", conn_pre_abort
-                    cb?()
+                    cb?(pre_data)
                     return true

             else


fix id bug

fix id bug

--- app/assets/javascripts/app/spine/spine.js.coffee
+++ app/assets/javascripts/app/spine/spine.js.coffee
@@ -350,8 +350,8 @@

   create: (options) ->
     @trigger('beforeCreate', options)
-    @id          = @constructor.uid() unless @id
-
+    @id          = @cid() unless @id
+
     record       = @dup(false)
     @constructor.records[@id]   = record
     @constructor.crecords[@cid] = record


Test in terms of Reporter.send since this is a unit test

--- spec/reporter-spec.coffee
+++ spec/reporter-spec.coffee
@@ -22,7 +22,8 @@
             at HTMLDivElement.<anonymous> (#{atom.config.resourcePath}/fuzzy-finder.coffee:10:15)
             at HTMLDivElement.jQuery.event.dispatch (#{atom.config.resourcePath}/node_modules/space-pen/vendor/jquery.js:4676:9)
         """
-      window.onerror('message', filePath, 1, 2, error)
+
+      Reporter.send('message', filePath, 1, 2, error)
       expect(Reporter.request).toHaveBeenCalled()

       requestArgs = Reporter.request.calls[0].args[0]
@@ -56,13 +57,13 @@

     it "truncates large backtraces", ->
       largeString = Array(1024*6).join("a")
-      window.onerror(largeString, filePath, 1)
+      Reporter.send(largeString, filePath, 1)

       body = JSON.parse(Reporter.request.calls[0].args[0].body)
-      window.onerror(largeString, 'file.coffee', 1)
+      Reporter.send(largeString, 'file.coffee', 1)
       expect(body.events[0].exceptions[0].message.length).toBeLessThan largeString.length

   describe "when the exception is not from atom core", ->
     it "doesn't send a request with the proper options", ->
-      window.onerror('message', 'file.coffee', 1)
+      Reporter.send('message', 'file.coffee', 1)
       expect(Reporter.request).not.toHaveBeenCalled()


updating paperclip image property

updating paperclip image property

--- app/assets/javascripts/ems/models/article.coffee
+++ app/assets/javascripts/ems/models/article.coffee
@@ -16,7 +16,7 @@
   availableStatus: null

   @encode 'id', 'slug', 'title', 'standfirst', 'content', 'hot', "featured", "toc", "comment", "meta_title",
-    "meta_description", "publish_from", "status", "content_disposition", "created_at", "updated_at", "image564x252_file_name"
+    "meta_description", "publish_from", "status", "content_disposition", "created_at", "updated_at", "image"
   @encode "publish_from", "created_at", "updated_at", Batman.Encoders.railsDate

   @encode 'channel_ids',


fixed unit display in footer

fixed unit display in footer

--- app/assets/javascripts/monitoring/util.js.coffee
+++ app/assets/javascripts/monitoring/util.js.coffee
@@ -19,14 +19,15 @@

   # draw footer
   footer = $(s + " .footer")
-  unit = ""
-  if metric.unit?
-    if metric.unit != "%"
-      unit = " " + metric.unit
+  unit = metric.get("unit")
+  unit_label = ""
+  if unit?
+    if unit == "%"
+      unit_label = "%"
     else
-      unit = "%"
+      unit_label = " " + unit
   last_val = vals[vals.length-1][1]
-  footer_text = sprintf("Last Value: %0.2f%s", last_val, unit)
+  footer_text = sprintf("Last Value: %0.2f%s", last_val, unit_label)
   footer.text(footer_text)

   # draw graph
@@ -44,7 +45,7 @@

   opts.includeZero = true # always incldue zero on y-axis

-  if metric.get("unit") == "%" || metric.get("range") == "1..100"
+  if unit == "%" || metric.get("range") == "1..100"
     # set range if known
     opts.valueRange = [ 0, 100 ]
     opts.yRangePad = 1