apply api changes

apply api changes

--- src/components/widgets/accounts-cash-balance/accounts-cash-balance.directive.coffee
+++ src/components/widgets/accounts-cash-balance/accounts-cash-balance.directive.coffee
@@ -30,7 +30,7 @@
       # TODO: theming config for positive/negative hex codes (or move to API)
       # chartColors = ImpacTheming.get().chartColors

-      # Set chart accounts series colors by account sub-type ('positive' / 'negative')
+      # Set chart accounts series colors by account bias ('positive' / 'negative')
       setSeriesColors(w.content.chart.series, {'positive': '#3FC4FF', 'negative': '#e50228'})

       # Wait for the next digest cycle to ensure the chart parent (.data-container) is shown.
@@ -61,17 +61,17 @@
     w.metadata? && w.metadata.hist_parameters? && w.metadata.hist_parameters.period || 'MONTHLY'

   getSerieByAccount = (series, account)->
-    _.find(series, (serie)-> (serie.uid || serie.options && serie.options.uid) == account.uid)
+    _.find(series, (serie)-> (serie.id || serie.options && serie.options.id) == account.id)

   getTodayMarker = ->
     projection_date = _.find(w.content.chart.labels, (label)-> moment(label) >= moment().startOf('day'))
     _.indexOf(w.content.chart.labels, projection_date)

   setSeriesColors = (series, chartColors) ->
-    groupedSeries = _.groupBy(series, (serie)-> serie.sub_type)
-    for subType, series of groupedSeries
-      continue unless chartColors[subType]
-      palette = ImpacTheming.color.generateShadesPalette(chartColors[subType], series.length)
+    groupedSeries = _.groupBy(series, (serie)-> serie.bias)
+    for bias, series of groupedSeries
+      continue unless chartColors[bias]
+      palette = ImpacTheming.color.generateShadesPalette(chartColors[bias], series.length)
       for serie, i in series
         serie.color = palette[i]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -151,5 +151,9 @@
   equal color.b, 100
   equal color.a, 0.32

+test "invalid color throws error", ->
+  raises ->
+    Color2("A Fake Color")
+
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -39,4 +39,14 @@
   equal color.b, 90
   equal color.a, 0.6

+test "accepts an rgb string as the first argument and an alpha value as the second", ->
+  color = Color2('rgb(255, 40, 30)', 0.4)
+
+  equal color.r, 255
+  equal color.g, 40
+  equal color.b, 90
+  equal color.a, 0.4
+
+test "accepts an rgba string as the first argument and an alpha value as the second. The alpha value takes precedence over the alpha from the rgba string", ->
+
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -66,12 +66,11 @@
     test: true

   ok engine.first("GameObject")
-
+
 test "#add class name only", ->
   engine = Engine()

-  engine.add "GameObject",
-    test: true
+  engine.add "GameObject"

   ok engine.first("GameObject")


Update test desctiption

Update test desctiption

--- test/errors.coffee
+++ test/errors.coffee
@@ -114,7 +114,7 @@
     new Understream(stream).run done

   describe 'maxListeners', ->
-    describe 'increases the maxListeners limit to account for error handlers it adds', ->
+    describe 'increases the limit to account for error handlers it adds', ->
       num_listeners = (stream) -> stream.listeners('error').length + stream.listeners('end').length

       stream = new Understream([]).stream()
@@ -130,7 +130,7 @@
             " but found that it increased by #{increase_in_max_listeners}" +
             " (from #{starting_max_listeners} to #{stream._maxListeners})"
           done()
-    describe 'it does not crash if there are no maxListeners', ->
+    describe 'does not crash if there are no maxListeners', ->
       stream = new Understream([]).stream()
       delete stream._maxListeners
       new_stream = new Understream(stream).each(-> ).run (err) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

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

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

   rightEnemy = GameObject
     x: 100
@@ -28,6 +28,6 @@
   rightEnemy.include(Follow)
   rightEnemy.follow(player)

-  ok rightEnemy.I.direction.equal(Point(-1, 0)), 'rightEnemy should head toward player with a direction Point(-1, 0)'
+  ok rightEnemy.I.direction.equal(Point(-1, 0)), 'rightEnemy should head toward player with a velocit Point(-1, 0)'

 module()


show chart all the way to now

show chart all the way to now

--- src/modules/graph.coffee
+++ src/modules/graph.coffee
@@ -70,6 +70,9 @@
             day.points = total
             total -= velocity if days[i] and not days[i].off_day
             day
+
+        # Do we need to make a link to right now?
+        days.push { date: now, points: 0 } if (now = new Date()) > cutoff

         cb null, days


fix(sidebar): Display correct keyboard shortcuts in account switcher

- Fixes #1203

--- internal_packages/account-sidebar/lib/components/account-switcher.cjsx
+++ internal_packages/account-sidebar/lib/components/account-switcher.cjsx
@@ -44,7 +44,7 @@
   _toggleDropdown: =>
     @setState showing: !@state.showing

-  _makeMenuItem: (item, idx = 0) =>
+  _makeMenuItem: (item, idx) =>
     menuItem = {
       label: item.label,
       click: @_onSwitchAccount.bind(@, item)
@@ -64,11 +64,11 @@
     if @props.accounts.length > 1
       unifiedItem = @_makeUnifiedItem()
       template = [
-        @_makeMenuItem(unifiedItem)
+        @_makeMenuItem(unifiedItem, 1)
         {type: 'separator'}
       ]

-    items.forEach (item, idx) => template.push(@_makeMenuItem(item, idx + 1))
+    items.forEach (item, idx) => template.push(@_makeMenuItem(item, idx + 2))

     template = template.concat [
       {type: 'separator'}


Level 9 added

Level 9 added


adding rq command

adding rq command

--- src/scripts/drush.coffee
+++ src/scripts/drush.coffee
@@ -20,7 +20,7 @@

 drush_interface = ->
   site_aliases = []
-  #allowed_commands = ["sa", "cc", "pml", "unif", "pmi", "ws", "vget", "rq"]
+  #allowed_commands = ["sa", "cc", "pml", "uinf", "pmi", "ws", "vget", "rq"]

   # helper method to propigate the site aliases in memory
   update_aliases = (msg) ->
@@ -57,10 +57,23 @@
       clearcache.stdout.on "data", (data) ->
         output += data
       clearcache.stderr.on "data", (data) ->
-        output = "We've experienced and error clearing cache."
+        output += data
       clearcache.on "exit", (code) ->
         if code is 0
           output += "Cache clear complete."
+        msg.send output
+
+    drush_rq: (msg, command) ->
+      output = ''
+      msg.send "This may take a moment..."
+      clearcache = spawn("drush", [command.alias, "rq", "--severity=1"])
+      clearcache.stdout.on "data", (data) ->
+        output += data
+      clearcache.stderr.on "data", (data) ->
+        output += data
+      clearcache.on "exit", (code) ->
+        if code is 0
+          output += "Core Requirements complete."
         msg.send output

     drush_pml: (msg, command) ->
@@ -74,8 +87,7 @@

       msg.send "This may take a moment..."

-      # @TODO loop through command.args and append args that appear in allowed_options
-      spawn_options = [command.alias, "pml", "--pipe", modules_state, modules_core]
+      spawn_options = [command.alias, "pml", modules_state, modules_core]
       pml = spawn("drush", spawn_options)
       pml.stdout.on "data", (data) ->
         output += data


Typo fix

Typo fix

--- src/snappystreams.coffee
+++ src/snappystreams.coffee
@@ -100,7 +100,7 @@
     chunks = []

     data = new Buffer data, encoding if encoding
-    data = Buffer.concact [@frameBuffer, data] if @frameBuffer
+    data = Buffer.concat [@frameBuffer, data] if @frameBuffer
     @frameBuffer = null

     unless @identifierFound or data.readUInt8(0) is CHUNKS.streamIdentifier


Add licenses

Add licenses

--- shared/licenses.coffee
+++ shared/licenses.coffee
@@ -1,29 +1,41 @@
 @licenses = {
   'cc-by-4.0': {
-    name: 'Creative Commons - Attribution 4.0',
+    name: 'Creative Commons - Attribution 4.0'
     url: "http://creativecommons.org/licenses/by/4.0/"
     icons: ["creative-commons", "creative-commons-attribution",]
   },
   'cc-by-sa-4.0': {
-    name: 'Creative Commons - Attribution - ShareAlike 4.0',
+    name: 'Creative Commons - Attribution - ShareAlike 4.0'
     url: "http://creativecommons.org/licenses/by-sa/4.0/"
     icons: ["creative-commons", "creative-commons-attribution", "creative-commons-sharealike",]
   },
   'cc-by-nc-sa-4.0': {
-    name: 'Creative Commons - Attribution - NonCommercial - ShareAlike 4.0',
+    name: 'Creative Commons - Attribution - NonCommercial - ShareAlike 4.0'
     url: "http://creativecommons.org/licenses/by-nc-sa/4.0/"
     icons: ["creative-commons", "creative-commons-attribution", "creative-commons-noncommercial-us",
-      "creative-commons-sharealike"],
+      "creative-commons-sharealike"]
+  },
+  'cc-by-nc-4.0': {
+    name: 'Creative Commons - Attribution - NonCommercial 4.0'
+    url: "http://creativecommons.org/licenses/by-nc/4.0/"
+    icons: ["creative-commons", "creative-commons-attribution",
+      "creative-commons-noncommercial-us",],
   },
   'cc-by-sa-3.0': {
-    name: 'Creative Commons - Attribution - ShareAlike 3.0',
+    name: 'Creative Commons - Attribution - ShareAlike 3.0'
     url: "http://creativecommons.org/licenses/by-a-sa/3.0/"
     icons: ["creative-commons", "creative-commons-attribution", "creative-commons-sharealike",]
   },
   'cc-by-nc-sa-3.0': {
-    name: 'Creative Commons - Attribution - NonCommercial - ShareAlike 3.0',
+    name: 'Creative Commons - Attribution - NonCommercial - ShareAlike 3.0'
     url: "http://creativecommons.org/licenses/by-nc-sa/3.0/"
-    icons: ["creative-commons", "creative-commons-attribution", "creative-commons-noncommercial-us",
-      "creative-commons-sharealike"],
+    icons: ["creative-commons", "creative-commons-attribution",
+      "creative-commons-noncommercial-us", "creative-commons-sharealike"],
+  },
+  'cc-by-nc-3.0': {
+    name: 'Creative Commons - Attribution - NonCommercial 3.0'
+    url: "http://creativecommons.org/licenses/by-nc/3.0/"
+    icons: ["creative-commons", "creative-commons-attribution",
+      "creative-commons-noncommercial-us",],
   },
 }


Implement #bootstrap spec coverage

Implement #bootstrap spec coverage

--- spec/storage-manager-spec.coffee
+++ spec/storage-manager-spec.coffee
@@ -781,6 +781,21 @@
           expect(funct).not.toBeUndefined()
           expect(funct.toString()).toEqual(baseXhr[functionName].toString())

+  describe 'bootstrap', ->
+    it 'loads models into the storage manager', ->
+      task = buildTask()
+      manager.bootstrap 'tasks',
+        count: 1
+        results: [{ key: 'tasks', id: task.id }]
+        tasks:
+          "#{task.id}": task.attributes
+
+      cachedTask = base.data.storage('tasks').get(task.id)
+      expect(cachedTask).toBeDefined()
+
+      for attribute, value of task.attributes
+        expect(cachedTask.get(attribute)).toEqual value
+
   describe "error handling", ->
     describe "passing in a custom error handler when loading a collection", ->
       it "gets called when there is an error", ->


Social: add multiple account id support into participant functions

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -155,21 +155,33 @@
   url = "/activity/pin/glance"
   post url, data, callback

-followTopic = (data, callback)->
-  if not data.accountId or not data.channelId
-    return callback { message: "Request is not valid"}
-
-  url = "/channel/#{data.channelId}/\
-        participant/#{data.accountId}/add"
-  post url, data, callback
-
-unfollowTopic = (data, callback)->
-  if not data.accountId or not data.channelId
-    return callback { message: "Request is not valid"}
-
-  url = "/channel/#{data.channelId}/\
-        participant/#{data.accountId}/delete"
-  post url, data, callback
+listParticipants = (data, callback)->
+  return callback { message: "Request is not valid" } unless data.channelId
+  url = "/channel/#{data.channelId}/participants"
+  get url, data, callback
+
+addParticipants = (data, callback)->
+  url = "/channel/#{data.channelId}/participants/add"
+  doChannelParticipantOperation data, url, callback
+
+removeParticipants = (data, callback)->
+  url = "/channel/#{data.channelId}/participants/remove"
+  doChannelParticipantOperation data, url, callback
+
+doChannelParticipantOperation = (data, url, callback)->
+  return callback { message: "Request is not valid" } unless data.channelId
+
+  # if accountIds is not set and also accountId is not set
+  # return error
+  if not data.accountIds
+    return callback { message: "Request is not valid" } if not data.accountId
+    data.accountIds = [data.accountId]
+
+  # make the object according to channel participant data
+  req = ({accountId} for accountId in data.accountIds)
+
+  url = "#{url}?accountId=#{accountId}"
+  post url, req, callback

 updateLastSeenTime = (data, callback)->
   unless data.channelId and data.accountId
@@ -337,8 +349,9 @@
   fetchPrivateMessages
   sendPrivateMessage
   fetchFollowedChannels
-  followTopic
-  unfollowTopic
+  listParticipants
+  addParticipants
+  removeParticipants
   fetchPinnedMessages
   pinMessage
   unpinMessage


ok, progress. fix one bug

ok, progress. fix one bug

--- src/packet/signature.iced
+++ src/packet/signature.iced
@@ -29,6 +29,8 @@
       uint_to_buffer(16, flatsp.length),
       flatsp
     ]
+    console.log "prepare payload ->"
+    console.log flatsp.toString('hex')

     trailer = Buffer.concat [
       new Buffer([ C.versions.signature.V4, 0xff ]),
@@ -87,7 +89,7 @@
   to_buffer : () ->
     inner = @_v_to_buffer()
     Buffer.concat [
-      encode_length(inner.length),
+      encode_length(inner.length + 1),
       uint_to_buffer(8, @type),
       inner
     ]
@@ -120,10 +122,10 @@

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

-class SigExpirationTime extends Time
+class ExpirationTime extends Time
   constructor : (t) ->
     super S.expiration_time, t
-  @parse : (slice) -> Time.parse slice, SigExpirationTime
+  @parse : (slice) -> Time.parse slice, ExpirationTime

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

@@ -281,6 +283,7 @@
 class KeyFlags extends Preference
   constructor : (v) ->
     super S.key_flags, v
+    console.log "key flags -> #{v}"
   @parse : (slice) -> Preference.parse slice, KeyFlags

 #------------
@@ -358,6 +361,7 @@
     hashed_subpacket_count = @slice.read_uint16()
     end = @slice.i + hashed_subpacket_count
     o.sig_data = @slice.peek_to_buffer hashed_subpacket_count
+    console.log o.sig_data.toString 'hex'
     o.hashed_subpackets = (@parse_subpacket() while @slice.i < end)
     unhashed_subpacket_count = @slice.read_uint16()
     end = @slice.i + unhashed_subpacket_count


payments: better UI for account screen

payments: better UI for account screen

--- client/landing/app/Applications/Account.kdapplication/views/paymentmethods.coffee
+++ client/landing/app/Applications/Account.kdapplication/views/paymentmethods.coffee
@@ -25,7 +25,7 @@
             </div>
             """
           buttons:
-            Yes:
+            Remove:
               style: "modal-clean-red"
               callback: =>
                 modal.destroy()
@@ -34,6 +34,7 @@
               style: "modal-cancel"
               callback: ->
                 modal.destroy()
+        modal.addSubView new BillingMethodView {}, data.billing

     list.on 'reload', (data) => @loadItems()

@@ -71,9 +72,12 @@
       modal.setBillingInfo initialBillingInfo.billing  if initialBillingInfo?

       modal.on 'PaymentInfoSubmitted', (updatedBillingInfo) =>
-        paymentController.updateBillingInfo updatedBillingInfo, (err, res)->
-          console.log arguments
-        # @addItem updatedBillingInfo
+        paymentController.updateBillingInfo updatedBillingInfo, (err, res) =>
+          if err
+            new KDNotificationView title: err.message
+          else
+            modal.destroy()
+            @loadItems()


weekend work

weekend work

--- client/landing/app/Applications/Topics.kdapplication/ContentDisplays/Topic.coffee
+++ client/landing/app/Applications/Topics.kdapplication/ContentDisplays/Topic.coffee
@@ -122,7 +122,7 @@
     """

-        {{#(image) || "upload an image"}}
+        {{#(image) or "upload an image"}}

       {{> @followButton}}


scroll to the top if it feels like you need to

--- public/javascripts/application.coffee
+++ public/javascripts/application.coffee
@@ -3,7 +3,14 @@
   href = $(this).attr('href')
   if !href.match(new RegExp('^/(auth|login/|logout)')) # login, logout
     e.preventDefault()
-    $.pjax url: href, container: '#inner', fragment: '#inner', timeout: 2222
+    $.pjax
+      url: href
+      container: '#inner'
+      fragment: '#inner'
+      timeout: 2222
+      success: ->
+        if $('#inner nav').offset().top < $(window).scrollTop()
+          $(window).scrollTop 0

 $(document).bind 'pjax', (e, xhr, options) ->
   $('#inner').addClass('pjax')


adds comment

adds comment

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -226,6 +226,7 @@
                   enabled: viewstate.loggedin && viewstate.showPopUpNotifications
                   click: (it) -> action 'mutesoundnotification', it.checked
                 }
+                # Only show option if notifier backend supports sound, otherwise custom sound is always used
                 {
                   type: 'checkbox'
                   label: i18n.__('menu.view.notification.custom_sound:Use YakYak custom sound for notifications')


fixed config to work with s3 correctly

fixed config to work with s3 correctly

--- services/docstore/config/settings.defaults.coffee
+++ services/docstore/config/settings.defaults.coffee
@@ -10,11 +10,8 @@
 	mongo:
 		url: 'mongodb://127.0.0.1/sharelatex'

-	#filestore:
-	#	backend: "s3"
-	#	stores:
-	#		user_files: ""
+	#docstore:
 	#	s3:
 	#		key: ""
 	#		secret: ""
-	#	fail: false+	#		bucket: "something"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


re-enabled mathml ui element rendering

re-enabled mathml ui element rendering

--- javascripts/widgets/ui_elements.coffee
+++ javascripts/widgets/ui_elements.coffee
@@ -42,10 +42,8 @@
       @figure

     update: (mathml)->
-      #@math_element.html(mathml)
-
+      @math_element.html(mathml)
       @mathml_renderer.render(@math_element[0])
-

   class_mixer(MathMLDisplay)


Shorten intro duration

Shorten intro duration


windows line endings are gross

windows line endings are gross

--- test/ace/dot_command.coffee
+++ test/ace/dot_command.coffee
@@ -72,4 +72,4 @@
   eq @adaptor.lastInsert().string, 'bc'

   @press 'iabc\nbc', @backspace, @backspace, 'de', @esc
-  eq @adaptor.lastInsert().string, 'abc\nde'
+  eq endings(@adaptor.lastInsert().string), 'abc\nde'


:art: cleanup further

:art: cleanup further


Leave todo

Leave todo

--- src/Baseamp.coffee
+++ src/Baseamp.coffee
@@ -150,6 +150,11 @@
       part     = "###{part}"
       todoLists.push new TodoList part

+    # @todo update todos whos ID is known.
+    # create others
+    # * what about delete? <-- we cannot do delete as we cannot know
+    # if someone else added an item to Basecamp. Let's just only check
+    # items off. (I think Basecamp also doesn't let you easily delete items)
     debug util.inspect
       todoList: todoLists[0].todos


let the browser Download PDFs (#1876)

let the browser Download PDFs (#1876)

* let the browser Download PDFs

this was causing Safari & Edge browsers to fail

* do not preventDefault for PDF links

* :bug: fix one more place where preventDefault used to run

--- src/scripts/loader.coffee
+++ src/scripts/loader.coffee
@@ -62,23 +62,25 @@
       # Only handle links intended to be processed by Backbone
       if e.isDefaultPrevented() or href.charAt(0) is '#' or mailto.test(href) then return

-      e.preventDefault()
-
       if external.test(href)
+        e.preventDefault()
         if legacy.test(href)
           location.href = href
         else
           window.open(href, '_blank')
       else if download.test(href)
         if document.cookie.indexOf('donation') is -1
+          e.preventDefault()
           content = href.match(exports)
           router.navigate("/donate/download/#{content[1]}/#{content[2]}", {trigger: true})
         else
-          downloadUrl(href)
+          return # Let the browser download the file # downloadUrl(href)

       else if resources.test(href)
+        e.preventDefault()
         window.open(href, '_blank')
       else
+        e.preventDefault()
         if $this.data('trigger') is false then trigger = false else trigger = true
         router.navigate(href, {trigger: trigger})


Set the  when we set a timeout

Set the  when we set a timeout

--- src/scripts/remind.coffee
+++ src/scripts/remind.coffee
@@ -47,12 +47,12 @@
         # setTimeout uses a 32-bit INT
         extendTimeout = (timeout, callback) ->
           if timeout > 0x7FFFFFFF
-            setTimeout ->
+            @current_timeout = setTimeout ->
               extendTimeout (timeout - 0x7FFFFFFF), callback
             , 0x7FFFFFFF
           else
-            setTimeout callback, timeout
-        @current_timeout = extendTimeout @cache[0].due - now, trigger
+            @current_timeout = setTimeout callback, timeout
+        extendTimeout @cache[0].due - now, trigger

 class Reminder
   constructor: (@msg_envelope, @time, @action) ->


Old calling synatx is old, removed unnecessary commented code

--- src/scripts/shodan.coffee
+++ src/scripts/shodan.coffee
@@ -26,7 +26,6 @@
       request_url = api_url + "/api/host?key=#{SHODAN_KEY}&ip=#{shodan_term}"

       robot.http(request_url)
-        #.query('ip': shodan_term, 'key': SHODAN_KEY)
         .get() (err, res, body) ->

           if res.statusCode is 200


Add coupons to updating subscriptions

Add coupons to updating subscriptions

--- src/smc-hub/client.coffee
+++ src/smc-hub/client.coffee
@@ -2095,6 +2095,16 @@
             subscription = undefined
             async.series([
                 (cb) =>
+                    if mesg.coupon_id
+                        @validate_coupon mesg.coupon_id, (err, coupon, coupon_history) =>
+                            if err
+                                cb(err)
+                            coupon_history[coupon.id] += 1
+                            @database.update_coupon_history
+                                account_id     : @account_id
+                                coupon_history : coupon_history
+                                cb             : cb
+                (cb) =>
                     dbg("Update the subscription.")
                     changes =
                         quantity : mesg.quantity


AnalysisDecorator diposes its children.

AnalysisDecorator diposes its children.

--- lib/analysis/analysis_decorator.coffee
+++ lib/analysis/analysis_decorator.coffee
@@ -1,12 +1,14 @@
+{CompositeDisposable} = require 'atom'
 _ = require 'lodash'

 class AnalysisDecorator
   constructor: (@errors) ->
+    @subscriptions = new CompositeDisposable()
     @listen()

   listen: =>
-    atom.workspace.observeTextEditors @handleEditors
-    @errors.onChange @handleErrors
+    @subscriptions.add atom.workspace.observeTextEditors @handleEditors
+    @subscriptions.add @errors.onChange @handleErrors

   clearMarkers: (editor, problems) =>
     markers = editor.findMarkers
@@ -19,10 +21,10 @@
         problem = m.getProperties().problem
         _.any(problems, (p) => _.isEqual(p, problem))
       ).value()
+    # console.log "destroying #{ms.length} markers for #{problems.length} problems"
     _.invoke ms, 'destroy'

   decorateEditor: (editor, problems) =>
-    # @clearMarkers(editor, problems)
     for problem in problems
       location = problem.location

@@ -60,4 +62,8 @@
     return unless problems?.length > 0
     @decorateEditor(editor, problems)

+  dispose: =>
+    @subscriptions.dispose()
+
+
 module.exports = AnalysisDecorator


Delete obsolete comment

Delete obsolete comment

--- lib/models/_canvas.coffee
+++ lib/models/_canvas.coffee
@@ -5,9 +5,6 @@
 server = require('../server/server')
 promisifiedServerRequest = Promise.promisify(server.request, server)

-# TODO: I've copy and pasted request.coffee that comes with
-# resin-platform-api. In package.json, the main is set as
-# simply canvas.js, so I have no way of access this file.
 class CanvasRequestService extends Canvas(_, Promise)
 	_request: (params) ->
 		params.json = true


Do not test if sections are loaded since they don't load

--- src/scripts/models/contents/node.coffee
+++ src/scripts/models/contents/node.coffee
@@ -223,7 +223,7 @@
     isSaveable: () -> !!@get('mediaType')

     isEditable: () ->
-      if not @get('loaded')
+      if not @get('loaded') and not @isSection()
         editable = false
       else if @get('editable')
         editable = true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -23,6 +23,6 @@
     x: 4
     y: 20

-  equals e
+  equals engine.first

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -20,7 +20,8 @@

   obj4 = engine.add
     rad: "Awesome"
-    x:
+    x: 40
+    y:

   indexSum = 0


re-render manually

re-render manually


* minor changes

* minor changes

--- src/ability.coffee
+++ src/ability.coffee
@@ -4,7 +4,7 @@
 isUndefined     = require 'util-ex/lib/is/type/undefined'
 defineProperty  = require 'util-ex/lib/defineProperty'

-class RefCountAble
+class RefCountable
   # initialization method
   initialize: ->
     #@RefCount = 0
@@ -25,4 +25,4 @@
   free: @::release

-module.exports = customAbility RefCountAble, 'addRef'
+module.exports = customAbility RefCountable, 'addRef'


Better wording.

Better wording.

--- src/spec/stockxmlimport.spec.coffee
+++ src/spec/stockxmlimport.spec.coffee
@@ -33,7 +33,7 @@
   beforeEach ->
     @import = new StockXmlImport Config

-  it 'single attachment - one entry', (done) ->
+  it 'one entry', (done) ->
     rawXml = '
 <row>
   123


Call process.exit when callback is called

This ensures that apm exits when the command is done and that any
spawned processes don't keep the main apm process active.

Refs #138

--- src/apm-cli.coffee
+++ src/apm-cli.coffee
@@ -175,6 +175,7 @@
         process.exit(1)
       else
         callback?()
+        process.exit(0)

     args = options.argv
     command = options.command


Return undefined from focus/blur event handlers


Fix absolute position when resizing

Fix absolute position when resizing

--- src/coffee/bootstrap-tour.coffee
+++ src/coffee/bootstrap-tour.coffee
@@ -345,7 +345,7 @@
       $tip.attr("id", step.id)
       @_scrollIntoView($tip)
       @_reposition($tip, step)
-
+
       if isOrphan
         @_center($tip)

@@ -372,6 +372,9 @@
         @_replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, "left") if originalLeft != tipOffset.left
       else
         @_replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top") if originalTop != tipOffset.top
+
+      element = @_getStepElement(step)
+      if element? then @_positionOverlay($(element))

     # Center popover in the page
     _center: ($tip) ->
@@ -441,20 +444,36 @@
       @backdrop = $("<div/>",
         class: "tour-backdrop"
       )
-      $("body").append(@backdrop)
+      body = $("body")
+      if $(".tour-backdrop", body).length is 0 then return body.append(@backdrop) else return body

     _hideOverlay: ->
       @backdrop.remove()
       @backdrop.overlay = null

+    _positionOverlay: ($element) ->
+      return unless $element? and @backdrop.$background?
+      offset = $element.offset()
+      offset.top = offset.top
+      offset.left = offset.left
+
+      @backdrop.$background
+        .width($element.innerWidth())
+        .height($element.innerHeight())
+        .addClass("tour-step-background")
+        .offset(offset)
+
+    _getStepElement: (step) ->
+      return step.element unless @_isOrphan(step)
+
     _showOverlayElement: (step) ->
-      element = step.element unless @_isOrphan(step)
+      element = @_getStepElement(step);

       return unless element

       $element = $(element)
       $background = $("<div/>")
-
+      ###
       offset = $element.offset()
       offset.top = offset.top
       offset.left = offset.left
@@ -464,7 +483,8 @@
         .height($element.innerHeight())
         .addClass("tour-step-background")
         .offset(offset)
-
+      ###
+
       $element.addClass("tour-step-backdrop")

       $("body").append($background)
@@ -481,6 +501,7 @@

       @backdrop.$element = $element
       @backdrop.$background = $background
+      @_positionOverlay($element)

     _removeElementMask: ->
       if @backdrop.$element


log presenter changes

log presenter changes

--- lib/server.storage.coffee
+++ lib/server.storage.coffee
@@ -40,6 +40,7 @@
 subscribe(channels.clientAuthenticated, subscribeToUserSlideEvents)

 publishEventsFromPresenterToSlaves = (stateChange) ->
+  console.log("presenter state change: ", stateChange)
   publish(channels.slaveEvents, stateChange)

 PRESENTER_USER = {id: "236886", provider: "github"}


:memo: Mention why the debounce

:memo: Mention why the debounce

--- lib/settings-panel.coffee
+++ lib/settings-panel.coffee
@@ -86,6 +86,8 @@

         setNewValue = => @set(name, value)
         if type is 'color'
+          # This is debounced since the color wheel fires lots of events
+          # as you are dragging it around
           clearTimeout(@colorDebounceTimeout)
           @colorDebounceTimeout = setTimeout(setNewValue, 100)
         else


v3 sigs don't work yet, so comment out the test


Padding on Graph widget to avoid cropping labels

Fixes #484

--- templates/project/widgets/graph/graph.coffee
+++ templates/project/widgets/graph/graph.coffee
@@ -22,6 +22,7 @@
         data: [{x:0, y:0}]
         }
       ]
+      padding: {top: 0.02, left: 0.02, right: 0.02, bottom: 0.02}
     )

     @graph.series[0].data = @get('points') if @get('points')


Indentation fix

Indentation fix

--- lib/tintan/compile.coffee
+++ lib/tintan/compile.coffee
@@ -73,8 +73,8 @@
   invokeTask: ->
     jake.Task[@options.name].invoke()

-   invokeClean: ->
-     jake.Task[@options.name + ':clean'].invoke()
+  invokeClean: ->
+    jake.Task[@options.name + ':clean'].invoke()

 Compilers =


Improve 6de335ae's snippet to include current item

--- user/snippets.cson
+++ user/snippets.cson
@@ -84,7 +84,7 @@
 		body: 'const ${1:module} = require("${1:module}");'
 	For:
 		prefix: "for"
-		body: "for(let i = 0, l = ${1:array}.length; i < l; ++i){\n\t$2\n}"
+		body: "for(let i = 0, l = ${1:array}.length; i < l; ++i){\n\tlet ${2:item} = ${1:array}[i];\n\t$3\n}"

 # CoffeeScript/CSON


pass name as arg

pass name as arg

--- main.coffee
+++ main.coffee
@@ -1,3 +1,9 @@
+# Natal
+# Bootstrap ClojureScript React Native apps
+# Dan Motzenbecker
+# http://oxism.com
+# MIT License
+
 fs         = require 'fs'
 crypto     = require 'crypto'
 {execSync} = require 'child_process'
@@ -124,6 +130,8 @@
     log 'Try this command as an example:', 'yellow'
     log '(swap! app-state assoc :text "Hello Native World")', 'inverse'
     log ''
+    log '✔ Done', 'bgMagenta'
+    log ''

   catch e
@@ -137,3 +145,13 @@
       logErr e.message

     process.exit 1
+
+
+[_, _, name] = process.argv
+
+unless name
+  logErr 'You must pass a project name as the first argument.'
+  logErr 'e.g. natal HelloWorld'
+  process.exit 1
+
+init name


Fix up tukey issue

Fix up tukey issue

--- main.coffee
+++ main.coffee
@@ -226,10 +226,9 @@

                 sort_context.median_stack.pop() if swap_count >= 2

-                # If we swapped then (potentially) defer.
-                if swap_count <= 2
-                        do_next()
-                        return
+                sort_context.count-- if swap_count > 2 # If we didn't swap then doesn't count
+                do_next()
+                return

         if in_partition
                 # We would have swapped pivot to end at start.


display filename and line in log.error

display filename and line in log.error

--- main.coffee
+++ main.coffee
@@ -52,6 +52,18 @@
         console.warn @format 'warn', texts if process.env.NODE_ENV isnt 'test'

     error: (texts...) ->
+        stacklist = (new Error()).stack.split('\n').slice(2);
+        stackReg = /at\s+(.*)\s+\((.*):(\d*):(\d*)\)/gi;
+        stackReg2 = /at\s+()(.*):(\d*):(\d*)/gi;
+
+        s = stacklist[0]
+        sp = stackReg.exec(s) or stackReg2.exec(s)
+
+        filePath = sp[2].substr(process.cwd().length)
+        line = sp[3]
+        errDetails = ".#{filePath}:#{line} |"
+        texts.unshift(errDetails)
+
         console.error @format 'error', texts if process.env.NODE_ENV isnt 'test'

     debug: (texts...) ->


Move collection specs

Move collection specs

--- test/properties.spec.coffee
+++ test/properties.spec.coffee
@@ -45,6 +45,34 @@
       oldAuthor = @object.get('author')
       @object.set(author: extended())
       expect(-> oldAuthor.set(name: 'test')).not.to.triggerEvent(@object, 'change:name')
+    it 'does not bleed over between objects with same prototype', ->
+      @ctor = ->
+      @inst1 = new @ctor()
+      @inst2 = new @ctor()
+      extend(@ctor.prototype, Serenade.Properties)
+      @ctor.prototype.property 'name', serialize: true
+      @inst1.property 'age', serialize: true
+      @inst2.property 'height', serialize: true
+      expect(Object.keys(@inst2.serialize())).not.to.include('age')
+
+  describe '.collection', ->
+    it 'is initialized to a collection', ->
+      @object.collection 'numbers'
+      expect(@object.numbers.get(0)).to.not.exist
+      @object.numbers.push(5)
+      expect(@object.numbers.get(0)).to.eql(5)
+    it 'updates the collection on set', ->
+      @object.collection 'numbers'
+      @object.set('numbers', [1,2,3])
+      expect(@object.get('numbers').get(0)).to.eql(1)
+    it 'triggers a change event when collection is changed', ->
+      @object.collection 'numbers'
+      collection = @object.numbers
+      expect(-> collection.push(4)).to.triggerEvent(@object, 'change:numbers', with: [@object.numbers])
+    it 'passes on the serialize option', ->
+      @object.collection 'numbers', serialize: true
+      @object.set('numbers', [1,2,3])
+      expect(@object.serialize()).to.eql(numbers: [1,2,3])
     it 'can reach into collections and observe changes to the entire collection', ->
       @object.property 'authorNames', dependsOn: ['authors', 'authors:name']
       @object.collection 'authors'
@@ -64,34 +92,6 @@
       oldAuthor.schmoo = true
       @object.authors.deleteAt(0)
       expect(-> oldAuthor.set(name: 'test')).not.to.triggerEvent(@object, 'change:authorNames')
-    it 'does not bleed over between objects with same prototype', ->
-      @ctor = ->
-      @inst1 = new @ctor()
-      @inst2 = new @ctor()
-      extend(@ctor.prototype, Serenade.Properties)
-      @ctor.prototype.property 'name', serialize: true
-      @inst1.property 'age', serialize: true
-      @inst2.property 'height', serialize: true
-      expect(Object.keys(@inst2.serialize())).not.to.include('age')
-
-  describe '.collection', ->
-    it 'is initialized to a collection', ->
-      @object.collection 'numbers'
-      expect(@object.numbers.get(0)).to.not.exist
-      @object.numbers.push(5)
-      expect(@object.numbers.get(0)).to.eql(5)
-    it 'updates the collection on set', ->
-      @object.collection 'numbers'
-      @object.set('numbers', [1,2,3])
-      expect(@object.get('numbers').get(0)).to.eql(1)
-    it 'triggers a change event when collection is changed', ->
-      @object.collection 'numbers'
-      collection = @object.numbers
-      expect(-> collection.push(4)).to.triggerEvent(@object, 'change:numbers', with: [@object.numbers])
-    it 'passes on the serialize option', ->
-      @object.collection 'numbers', serialize: true
-      @object.set('numbers', [1,2,3])
-      expect(@object.serialize()).to.eql(numbers: [1,2,3])

   describe '.set', ->
     describe 'with a single property', ->


peillis/helpapp#3455 setSearchParam test

--- test/test_controller.coffee
+++ test/test_controller.coffee
@@ -252,3 +252,33 @@

       # first search
       controller.search 'silla'
+
+
+  context ' parameter setting methods ', ->
+
+    beforeEach ()->
+      client_mock =
+        search: ()->
+        hit: ()->
+        hashid: mock.request.hashid
+
+
+    it 'setSearchParam adds parameter to every search', (done) ->
+      client_mock.search = (query, params) ->
+        params.should.have.keys 'query_counter', 'query', 'filters', 'page', 'rpp', 'transformer'
+        params.transformer.should.eql 'testTransformer'
+        params.rpp.should.be.equal 23
+        # when we search again, the search parameters  must remain
+        client_mock.search = (query, params) ->
+          params.should.have.keys 'query_counter', 'query', 'filters', 'page', 'rpp', 'transformer'
+          params.transformer.should.eql 'testTransformer'
+          params.rpp.should.be.equal 23
+          done()
+        controller.search 'silla2'
+
+      controller = new doofinder.Controller client_mock, [widget_mock]
+      controller.setSearchParam 'rpp', 23
+      controller.setSearchParam 'transformer', 'testTransformer'
+
+      # first search
+      controller.search 'silla'


stop posthog autocapture within app

stop posthog autocapture within app

--- vue/src/main.coffee
+++ vue/src/main.coffee
@@ -44,7 +44,7 @@
   Session.apply(data)

   if AppConfig.posthog_key
-    posthog.init(AppConfig.posthog_key, {api_host: AppConfig.posthog_host});
+    posthog.init(AppConfig.posthog_key, {api_host: AppConfig.posthog_host, autocapture: false});
     posthog.identify Session.user().id, pick(Session.user(), ['name', 'email', 'username'])

   if AppConfig.sentry_dsn


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


spec fixes for AssignmentListItemView

spec fixes for AssignmentListItemView

Test should have been setting a submission model for the test
assignment, but wasn't.

test plan:
  - specs pass

Change-Id: Iac221fb15762b55ad3c0391c0e14c97616e57e84
Reviewed-on: https://gerrit.instructure.com/25614
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
Product-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>

--- spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee
+++ spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee
@@ -84,11 +84,12 @@
   module 'AssignmentListItemViewSpec',
     setup: ->
       ENV = window.ENV ||= {}
-      ENV.PERMISSIONS = {manage: true}
+      ENV.PERMISSIONS = {manage: false}
       window.ENV = ENV

       @model = assignment1()
-      @view = createView(@model, canManage: true)
+      @submission = new Backbone.Model
+      @view = createView(@model, canManage: false)
       screenreaderText = =>
         $.trim @view.$('.js-score .screenreader-only').text()
       nonScreenreaderText = =>
@@ -133,30 +134,32 @@
     ok view.model.destroy.called
     view.model.destroy.restore()

-    ENV.context_asset_string = old_asset_string
+    window.ENV = oldENV
   test "updating grades from model change", ->
-    @model.set 'grade', 1.5555
+    @submission.set 'grade', 1.5555
+    @model.set 'submission', @submission
+    @model.trigger 'change:submission'

     equal screenreaderText(), 'Score: 1.56 out of 2 points', 'sets screenreader text'
     equal nonScreenreaderText(), '1.56/2 pts', 'sets non-screenreader text'

-    @model.set 'grade', null
-    @model.set 'noSubmission', true
-
+    @model.set 'submission', null
     equal screenreaderText(), 'No submission for this assignment. 2 points possible.',
       'sets screenreader text for null points'
     equal nonScreenreaderText(), '-/2 pts',
       'sets non-screenreader text for null points'

-    @model.set 'noSubmission', false
-    @model.set 'grade', 0
+    @submission.set 'grade', 0
+    @model.set 'submission', @submission

     equal screenreaderText(), 'Score: 0 out of 2 points',
       'sets screenreader text for 0 points'
     equal nonScreenreaderText(), '0/2 pts',
       'sets non-screenreader text for 0 points'

-    @model.set 'notYetGraded', true
+    @submission.set 'notYetGraded', true
+    @model.set 'submission', @submission
+    @model.trigger 'change:submission'
     equal screenreaderText(), 'Assignment not yet graded. 2 points possible.',
       'sets correct screenreader text for not yet graded'
     equal nonScreenreaderText(), 'Not Yet Graded/2 pts',


Added /ws, :port/ and ajax long poling auto detection.

--- app/assets/javascripts/app/lib/app_post/websocket.js.coffee
+++ app/assets/javascripts/app/lib/app_post/websocket.js.coffee
@@ -171,7 +171,6 @@
     App.Delay.set @ping, 60000, 'websocket-pong', 'ws'

   connect: =>
-    return if @backend is 'ajax'

     if !window.WebSocket
       @backend = 'ajax'
@@ -183,13 +182,18 @@
     if window.location.protocol is 'https:'
       protocol = 'wss://'

-    @ws = new window.WebSocket( protocol + window.location.hostname + ":6042/" )
+    if @backend is 'websocket'
+      @ws = new window.WebSocket( protocol + window.location.hostname + '/ws' )
+    else if @backend is 'websocketPort'
+      @ws = new window.WebSocket( protocol + window.location.hostname + ':6042/' )
+    else
+      @_ajaxInit()

     # Set event handlers.
     @ws.onopen = =>
-      @log 'notice', 'new websocket connection open'
-
-      @connectionEstablished = true
+      @log 'notice', "new websocket (#{@channel()}) connection open"
+
+      @connectionEstablished    = true
       @connectionWasEstablished = true

       # close error message show up (because try so connect again) if exists
@@ -224,18 +228,26 @@
       if @connectionEstablished
         @connectionEstablished = false

-      return if @backend is 'ajax'
+      # if connection was not possible
+      if !@connectionWasEstablished
+
+        # use ws dedicated port fallback if no connection was possible
+        if @backend is 'websocket'
+          @log 'notice', 'no websocket connection on /ws, use :port/'
+          @backend = 'websocketPort'
+          @connect()
+          return
+
+        # use ajax fallback if no connection was possible
+        if @backend is 'websocketPort'
+          @log 'notice', 'no websocket connection on :port/, use ajax long polling as fallback'
+          @backend = 'ajax'
+          @connect()
+          return

       # show error message, first try to reconnect
       if !@error
         message = =>
-
-          # use fallback if no connection was possible
-          if !@connectionWasEstablished
-            @backend = 'ajax'
-            @log 'notice', 'No connection to websocket, use ajax long polling as fallback'
-            @_ajaxInit()
-            return

           # show reconnect message
           @error = new App.ControllerModal(


[ModalView] Automatically clean modal views up upon hiding.

--- app/assets/javascripts/cartilage/views/modal_view.js.coffee
+++ app/assets/javascripts/cartilage/views/modal_view.js.coffee
@@ -16,6 +16,11 @@
   show: ->
     ($ document.body).append @render().el
     ($ @el).modal('show')
+    ($ @el).on 'hidden', @dismiss

   hide: =>
     ($ @el).modal('hide')
+
+  dismiss: =>
+    ($ @el).off 'hidden'
+    super()


updated to add fix if nested isnt already in the dom

--- app/assets/javascripts/cohesive_admin/inputs/_nested.coffee
+++ app/assets/javascripts/cohesive_admin/inputs/_nested.coffee
@@ -5,7 +5,12 @@
     object_id = new Date().getTime()
     source = $('[data-nested-template]').html();
     template = Handlebars.compile(source);
-    index = parseInt($('[data-nested] .collapsible-body').last().find("label").attr('for').match(/\d+/)[0] ) + 1
+    index_value = $('[data-nested] .collapsible-body').last().find("label").attr('for')
+    if index_value != undefined
+      index = parseInt(index_value.match(/\d+/)[0] ) + 1
+    else
+      index = 0
+
     context = { index: index, object_id: object_id }
     html = template(context)
     $('[data-nested]').append(html)


OX-248 performance testing

OX-248 performance testing

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -194,8 +194,8 @@

     @socket4Bit = new ArrayBufferSocket(
       senders : [
-        #new ArrayBufferSocket.WebWorker("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=1&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
-        #new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=1&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
+        new ArrayBufferSocket.WebWorker("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=1&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
+        new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=1&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
         new ArrayBufferSocket.XmlHttpRequest("/binary/ajax?dataSetId=#{@dataSetId}&halfByte=1&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
       ]
       requestBufferType : Float32Array
@@ -204,8 +204,8 @@

     @socket8Bit = new ArrayBufferSocket(
       senders : [
-        #new ArrayBufferSocket.WebWorker("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=0&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
-        #new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=0&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
+        new ArrayBufferSocket.WebWorker("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=0&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
+        <new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetId=#{@dataSetId}&halfByte=0&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
         new ArrayBufferSocket.XmlHttpRequest("/binary/ajax?dataSetId=#{@dataSetId}&halfByte=0&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
       ]
       requestBufferType : Float32Array


make elements clickable from keyboard

make elements clickable from keyboard

--- app/assets/javascripts/uploadcare/widget/template.js.coffee
+++ app/assets/javascripts/uploadcare/widget/template.js.coffee
@@ -10,6 +10,15 @@
 } = uploadcare

 namespace 'uploadcare.widget', (ns) ->
+
+  keyClickable = (el) ->
+    el.on 'click', ->
+        this.blur()
+      .on 'keypress', (e) ->
+        # 13 = Return, 32 = Space
+        if e.which == 13 or e.which == 32
+          $(this).click()
+
   class ns.Template
     constructor: (@settings, @element)->
       @content = $(tpl('widget'))
@@ -18,7 +27,11 @@
       @statusText = @content.find('@uploadcare-widget-text')

     addButton: (name, caption='') ->
-      $(tpl('widget-button', {name, caption})).appendTo(@content)
+      keyClickable(
+        $(tpl('widget-button', {name, caption}))
+          .appendTo(@content)
+      )
+

     setStatus: (status) ->
       prefix = 'uploadcare-widget-status-'
@@ -53,6 +66,8 @@
       @setStatus 'error'

     setFileInfo: (info) ->
-      @statusText.html(tpl('widget-file-name', info))
+      name = @statusText.html(tpl('widget-file-name', info))
         .find('.uploadcare-widget-file-name')
-        .toggleClass('needsclick', @settings.systemDialog)
+      keyClickable(
+        name.toggleClass('needsclick', @settings.systemDialog)
+      )


Add quotes for error

Add quotes for error

--- src/SchemaUtils.coffee
+++ src/SchemaUtils.coffee
@@ -206,7 +206,7 @@
   _queryAncestry: (collection, id, callback, options) ->
     options = @getAncestryOptions(options)
     doc = collection.findOne(_id: id, options)
-    unless doc then throw new Meteor.Error(404, "Entity with ID #{id} not found.")
+    unless doc then throw new Meteor.Error(404, "Entity with ID '#{id}' not found.")

     queue = [doc]
     ancestry = []


Use instanceof instead of type in computed_renderers

--- bokehjs/src/coffee/models/tools/gestures/select_tool.coffee
+++ bokehjs/src/coffee/models/tools/gestures/select_tool.coffee
@@ -1,6 +1,7 @@
 _ = require "underscore"

 GestureTool = require "./gesture_tool"
+GlyphRenderer = require "../../renderers/glyph_renderer"
 {logger} = require "../../../core/logging"
 p = require "../../../core/properties"

@@ -71,7 +72,7 @@

         if renderers.length == 0
           all_renderers = @get('plot').get('renderers')
-          renderers = (r for r in all_renderers when r.type == "GlyphRenderer")
+          renderers = (r for r in all_renderers when r instanceof GlyphRenderer.Model)

         if names.length > 0
           renderers = (r for r in renderers when names.indexOf(r.get('name')) >= 0)


Get rid of logging.

Get rid of logging.

--- js-library/app/js/jail_iframe/classes/ready_promise.coffee
+++ js-library/app/js/jail_iframe/classes/ready_promise.coffee
@@ -9,18 +9,8 @@
 if 'complete' == document.readyState
   complete.resolve()
 else
-  console.log "queuing handler since we're in state #{document.readyState}"
   window.addEventListener('load', -> complete.resolve())
-
-console.log document.readyState

 FactlinkJailRoot.ready_promise = ready.promise()
 FactlinkJailRoot.load_promise = complete.promise()

-FactlinkJailRoot.ready_promise.done -> console.log 'AAAAAAAAAAAAAAAA'
-FactlinkJailRoot.load_promise.done -> console.log 'BBBBBBBBBBBBBBBB'
-
-
-FactlinkJailRoot.ready_promise
-  .then( -> FactlinkJailRoot.delay 4000)
-  .then( -> console.log("final state #{document.readyState}"))


Added a new behaviour in parse method of DateTime: when a value of 3 digits is entered, a 0 digit will be prepended. See #46174

--- src/elements/DateTime/DateTime.coffee
+++ src/elements/DateTime/DateTime.coffee
@@ -618,25 +618,29 @@
 	#              matched is not among them, init to the first check format.
 	#              these formats are the "allowed" formats, this is used in __checkInput

-	parse: (s, formats = @__input_formats, use_formats = formats) ->
-		if not (s?.trim?().length > 0)
+	parse: (stringValue, formats = @__input_formats, use_formats = formats) ->
+		if not (stringValue?.trim?().length > 0)
 			return moment.invalid()

 		bc = false
-		if s.startsWith("-")
+		if stringValue.startsWith("-")
 			bc = true
-			s = s.substring(1)
+			stringValue = stringValue.substring(1)
 		else
-			us = s.toLocaleUpperCase()
+			us = stringValue.toLocaleUpperCase()
 			for appendix in CUI.DateTime.defaults.bc_appendix
 				ua = appendix.toLocaleUpperCase()
 				if us.endsWith(" "+ua)
-					s = s.substring(0, s.length - ua.length).trim()
+					stringValue = stringValue.substring(0, stringValue.length - ua.length).trim()
 					bc = true
 					break

+		# In case that stringValue is a year of 3 digits, prepend a 0 digit.
+		if stringValue.length == 3
+			stringValue = 0 + stringValue
+
 		for format in formats
-			mom = @__parseFormat(format, s)
+			mom = @__parseFormat(format, stringValue)
 			if mom
 				if format in use_formats
 					@__input_format = @initFormat(format)


Fix cyclic buildModel() behavior PUBDEV-272 PUBDEV-188

--- src/ext/components/model-input.coffee
+++ src/ext/components/model-input.coffee
@@ -337,7 +337,8 @@
 H2O.ModelInput = (_, _algo, _opts) ->
   _exception = signal null
   _algorithms = signal []
-  _algorithm = signal _algo
+  _algorithm = signal null
+  lift _algorithm, (a) -> debug "ALGO CHANGED TO " + a
   _canCreateModel = lift _algorithm, (algorithm) -> if algorithm then yes else no

   _modelForm = signal null
@@ -373,6 +374,7 @@
     _.requestModelBuilders (error, result) ->
       modelBuilders = if error then [] else result.model_builders
       _algorithms (key for key in keys modelBuilders when key isnt 'example')
+      _algorithm _algo
       frameKey = _opts?.training_frame
       act _algorithm, (algorithm) ->
         if algorithm


operations: all nodes in defaultConfiguration accessModel=authorize

See Github issue #47

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -25,25 +25,25 @@
     status:
         title: "#{user} Status Updates"
         description: "M000D"
-        accessModel: "open"
+        accessModel: "authorize"
         publishModel: "publishers"
         defaultAffiliation: "member"
     'geo/previous':
         title: "#{user} Previous Location"
         description: "Where #{user} has been before"
-        accessModel: "open"
+        accessModel: "authorize"
         publishModel: "publishers"
         defaultAffiliation: "member"
     'geo/current':
         title: "#{user} Current Location"
         description: "Where #{user} is at now"
-        accessModel: "open"
+        accessModel: "authorize"
         publishModel: "publishers"
         defaultAffiliation: "member"
     'geo/next':
         title: "#{user} Next Location"
         description: "Where #{user} intends to go"
-        accessModel: "open"
+        accessModel: "authorize"
         publishModel: "publishers"
         defaultAffiliation: "member"
     subscriptions:


PR #176: Typos

PR #176: Typos


Additional stat helpers

Additional stat helpers


Add halmuruk.

Add halmuruk.


Change the Free tier plan title to Personal

Connects-To: #374
Change-Type: patch

--- tests/integration/models/billing.spec.coffee
+++ tests/integration/models/billing.spec.coffee
@@ -25,8 +25,8 @@
 			it 'should return a free tier billing plan object', ->
 				promise = resin.models.billing.getPlan()
 				m.chai.expect(promise).to.become({
-					title: 'Free'
-					name: 'Free plan'
+					title: 'Personal'
+					name: 'Personal plan'
 					code: 'free'
 					tier: 'free'
 					billing:
@@ -34,7 +34,7 @@
 						charges: [
 							{
 								itemType: 'plan'
-								name: 'Free plan'
+								name: 'Personal plan'
 								code: 'free'
 								unitCostCents: '0'
 								quantity: '1'


fixing encoding problem for statusupdate widget

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -4,11 +4,14 @@

     super

-    name = KD.utils.getFullnameFromAccount KD.whoami(), yes
+    name        = KD.utils.getFullnameFromAccount KD.whoami(), yes
+
+    # seyPlaceholer already has encoder. No need to decode again.
+    decodedName = Encoder.htmlDecode name

     @smallInput = new KDInputView
       cssClass      : "status-update-input warn-on-unsaved-data"
-      placeholder   : "What's new #{name}?"
+      placeholder   : "What's new #{decodedName}?"
       name          : 'dummy'
       style         : 'input-with-extras'
       focus         : @bound 'switchToLargeView'
@@ -30,7 +33,7 @@
     @largeInput = new KDInputView
       cssClass      : "status-update-input warn-on-unsaved-data"
       type          : "textarea"
-      placeholder   : "What's new #{name}?"
+      placeholder   : "What's new #{decodedName}?"
       name          : 'body'
       style         : 'input-with-extras'
       autogrow      : yes


Fix scan permissions error message

Fix scan permissions error message

--- app/controllers/index.coffee
+++ app/controllers/index.coffee
@@ -33,7 +33,10 @@
           that.set "storeURL", null
           Notify.success "Hang in there while we process your URL"
         .fail (xhr, message, status) ->
-          Notify.error "A network error occured! Please try again later"
+          if xhr.status is 401
+            Notify.error xhr.responseJSON.message
+          else
+            Notify.error "A network error occured! Please try again later"

 `export default IndexController;`


detect Teams objects

detect Teams objects

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -434,6 +434,10 @@
     /orgs/ [^/]+
     $
   ///
+  'teams': /// ^ (https?://[^/]+)? (/api/v3)?
+    /teams/ [^/]+
+    $
+  ///
   'repos.comments': /// ^
     (https?://[^/]+)? # Optional protocol, host, and port
     (/api/v3)?        # Optional API root for enterprise GitHub users


fix lint error

fix lint error

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -246,7 +246,7 @@
 exports.isModifierKeyup = (keystroke) -> isKeyup(keystroke) and ENDS_IN_MODIFIER_REGEX.test(keystroke)

 exports.isKeyup = isKeyup = (keystroke) ->
-  keystroke isnt '^' and keystroke.startsWith('^')
+  keystroke isnt '^' and keystroke.startsWith('^')

 exports.keydownEvent = (key, options) ->
   return buildKeyboardEvent(key, 'keydown', options)


Use basename of original cache path

Use basename of original cache path

--- src/install.coffee
+++ src/install.coffee
@@ -246,7 +246,7 @@
     cacheDir = config.getPackageCacheDirectory()
     cachePath = path.join(cacheDir, packageName, packageVersion, 'package.tgz')
     if fs.isFileSync(cachePath)
-      tempPath = path.join(temp.mkdirSync(), 'package.tgz')
+      tempPath = path.join(temp.mkdirSync(), path.basename(cachePath))
       fs.cp cachePath, tempPath, (error) ->
         if error?
           callback(error)


Use log helpers in Install command

Use log helpers in Install command

--- src/install.coffee
+++ src/install.coffee
@@ -109,13 +109,13 @@
             source = path.join(nodeModulesDirectory, child)
             destination = path.join(@atomPackagesDirectory, child)
             fs.cp(source, destination, forceDelete: true)
-          process.stdout.write '\u2713\n'.green
+          @logSuccess()

         callback()
       else
         if installGlobally
           fs.removeSync(installDirectory)
-          process.stdout.write '\u2717\n'.red
+          @logFailure()

         callback("#{stdout}\n#{stderr}")

@@ -130,13 +130,8 @@
   installModules: (options, callback) =>
     process.stdout.write 'Installing modules '

-    @forkInstallCommand options, (code, stderr='', stdout='') =>
-      if code is 0
-        process.stdout.write '\u2713\n'.green
-        callback()
-      else
-        process.stdout.write '\u2717\n'.red
-        callback("#{stdout}\n#{stderr}")
+    @forkInstallCommand options, (args...) =>
+      @logCommandResults(callback, args...)

   forkInstallCommand: (options, callback) ->
     installArgs = ['--globalconfig', config.getGlobalConfigPath(), '--userconfig', config.getUserConfigPath(), 'install']
@@ -186,10 +181,10 @@
   #            as the second.
   downloadPackage: (packageUrl, installGlobally, callback) ->
     requestSettings = url: packageUrl
-    request.createReadStream requestSettings, (readStream) ->
+    request.createReadStream requestSettings, (readStream) =>
       readStream.on 'error', (error) ->
         callback("Unable to download #{packageUrl}: #{error.message}")
-      readStream.on 'response', (response) ->
+      readStream.on 'response', (response) =>
         if response.statusCode is 200
           filePath = path.join(temp.mkdirSync(), 'package.tgz')
           writeStream = fs.createWriteStream(filePath)
@@ -200,14 +195,14 @@
         else
           chunks = []
           response.on 'data', (chunk) -> chunks.push(chunk)
-          response.on 'end', ->
+          response.on 'end', =>
             try
               error = JSON.parse(Buffer.concat(chunks))
               message = error.message ? error.error ? error
-              process.stdout.write('\u2717\n'.red) if installGlobally
+              @logFailure() if installGlobally
               callback("Unable to download #{packageUrl}: #{response.headers.status ? response.statusCode} #{message}")
             catch parseError
-              process.stdout.write('\u2717\n'.red) if installGlobally
+              @logFailure() if installGlobally
               callback("Unable to download #{packageUrl}: #{response.headers.status ? response.statusCode}")

   # Get the path to the package from the local cache.
@@ -258,14 +253,14 @@

     @requestPackage packageName, (error, pack) =>
       if error?
-        process.stdout.write '\u2717\n'.red
+        @logFailure()
         callback(error)
       else
         commands = []
         packageVersion ?= pack.releases.latest
         {tarball} = pack.versions[packageVersion]?.dist ? {}
         unless tarball
-          process.stdout.write '\u2717\n'.red
+          @logFailure()
           callback("Package version: #{packageVersion} not found")
           return

@@ -281,12 +276,12 @@
         commands.push (packagePath, callback) =>
           @installModule(options, pack, packagePath, callback)

-        async.waterfall commands, (error) ->
+        async.waterfall commands, (error) =>
           unless installGlobally
             if error?
-              process.stdout.write '\u2717\n'.red
+              @logSuccess()
             else
-              process.stdout.write '\u2713\n'.green
+              @logFailure()
           callback(error)

   # Install all the package dependencies found in the package.json file.


Support for standard chosen options

Support for standard chosen options

--- src/ajax-chosen.coffee
+++ src/ajax-chosen.coffee
@@ -1,6 +1,6 @@
 do ($ = jQuery) ->

-  $.fn.ajaxChosen = (settings = {}, callback = ->) ->
+  $.fn.ajaxChosen = (settings = {}, callback = {}, chosenOptions = ->) ->
     defaultOptions =
       minTermLength: 3
       afterTypeDelay: 500
@@ -17,7 +17,7 @@
     # Load chosen. To make things clear, I have taken the liberty
     # of using the .chzn-autoselect class to specify input elements
     # we want to use with ajax autocomplete.
-    @chosen()
+    @chosen(if chosenOptions then chosenOptions else {})

     @each ->
       # Now that chosen is loaded normally, we can bootstrap it with


On update failure schedule another attempt briefly in the future.

--- src/application.coffee
+++ src/application.coffee
@@ -143,7 +143,8 @@
 # 1 - Updating
 # 2 - Update required
 currentlyUpdating = 0
-exports.update = ->
+failedUpdates = 0
+exports.update = update = ->
 	if currentlyUpdating isnt 0
 		# Mark an update required after the current.
 		currentlyUpdating = 2
@@ -229,11 +230,22 @@
 					.then ->
 						knex('app').update(app).where(imageId: app.imageId)
 				Promise.all(installingPromises.concat(updatingPromises))
+	.then ->
+		failedUpdates = 0
+	.catch (err) ->
+		failedUpdates++
+		if currentlyUpdating is 2
+			console.log('Updating failed, but there is already another update scheduled immediately: ', err)
+			return
+		delayTime = Math.min(failedUpdates * 500, 30000)
+		# If there was an error then schedule another attempt briefly in the future.
+		console.log('Scheduling another update attempt due to failure: ', delayTime, err)
+		setTimeout(update, delayTime)
 	.finally ->
 		updateDeviceInfo(status: 'Idle')
 		if currentlyUpdating is 2
 			# If an update is required then schedule it
-			setTimeout(exports.update)
+			setTimeout(update)
 		# Set the updating as finished
 		currentlyUpdating = 0


Remove unused event methods.

Remove unused event methods.

--- src/atom/window.coffee
+++ src/atom/window.coffee
@@ -25,10 +25,10 @@
     @showConsole true

   handleKeyEvent: ->
-    atom.keybinder.handleEvent arguments...
+    null

   triggerEvent: ->
-    atom.trigger arguments...
+    null

 for key, value of windowAdditions
   console.warn "DOMWindow already has a key named `#{key}`" if window[key]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -45,7 +45,7 @@
   fadeIn: (options={}) ->
     {alpha, color, duration} = Object.reverseMerge(options, fadeInDefaults)

-    configureFade(duration, color, aloh)
+    configureFade(duration, color, alpha)

   ###*
   A convenient way to set the flash effect instance variables. This provides a shorthand for fading


Add type-test to getDownloadUrl(); handles saving share info correctly

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -487,10 +487,14 @@
       @_ui.downloadDialog @state.metadata?.name, envelopedContent, callback

   getDownloadUrl: (content, includeShareInfo) ->
-    contentToSave = content
-
-    if not includeShareInfo
-      # clone the document so we can delete the share info if needed and not effect the original
+    if typeof content is "string"
+      contentToSave = content
+
+    else if includeShareInfo
+      contentToSave = JSON.stringify(content.getContent())
+
+    else # not includeShareInfo
+      # clone the document so we can delete the share info and not affect the original
       json = content.clone().getContent()
       delete json.sharedDocumentId
       delete json.shareEditKey


Play With EDSL Formatting

Play With EDSL Formatting

--- src/coffee/game.coffee
+++ src/coffee/game.coffee
@@ -2,25 +2,30 @@
   .run (qbnEdsl) ->
     {quality} = qbnEdsl

-    quality 'punchiness', 'Punchiness', 'One\'s capability for punching.',
-        defaultValue: 7
+    quality 'punchiness',
+      'Punchiness'
+      'One\'s capability for punching.'
+      defaultValue: 7

-    quality 'luck', 'Luck', 'This text should never display.',
-        defaultValue: -2
-        visible: false
+    quality 'luck',
+      'Luck'
+      'This text should never display.'
+      defaultValue: -2
+      visible: false

     return
-
+
   .run (qbnEdsl) ->
     {storylet, choice, front} = qbnEdsl

-    storylet 'harrowing-carriage', 'A Harrowing Carriage-Voyage',
+    storylet 'harrowing-carriage',
+      'A Harrowing Carriage-Voyage'
       '''
       Insert purple prose here.
       '''

     front choice 'A Harrowing Carriage-Voyage',
-      'Well, this is unpleasant…',
+      'Well, this is unpleasant…'
       'harrowing-carriage'

     return


throw error if `--inCsv` provided, but does not exist

--- src/coffee/util.coffee
+++ src/coffee/util.coffee
@@ -21,6 +21,8 @@
     fs.exists(filePath).then (exists) ->
       if exists
         [stdFs.createReadStream(filePath), false]
+      else if filePath and filePath != ''
+        throw new Error("Input CSV file '#{filePath}' does not exist!")
       else
         [process.stdin, true]


passport: change from showing email address to showing name in Account Settings button; since not all passports have an email address now.

--- salvus/page/account.coffee
+++ salvus/page/account.coffee
@@ -38,10 +38,10 @@
 defaults = misc.defaults
 required = defaults.required

-set_account_tab_label = (signed_in, email_address) ->
+set_account_tab_label = (signed_in, label) ->
     if signed_in
         top_navbar.pages['account'].icon = 'fa-cog'
-        top_navbar.set_button_label("account", email_address)
+        top_navbar.set_button_label("account", label)
     else
         # nothing
         top_navbar.set_button_label("account", "Sign in", "", false)
@@ -390,8 +390,8 @@
             account_settings.set_view()
             # change the view in the account page to the settings/sign out view
             show_page("account-settings")
-            # change the navbar title from "Sign in" to their email address -- don't use the one from mesg, which may be out of date
-            set_account_tab_label(true, account_settings.settings.email_address)
+            # change the navbar title from "Sign in" to their name
+            set_account_tab_label(true, account_settings.fullname())
             $("#account-forgot_password-email_address").val(account_settings.settings.email_address)

@@ -621,6 +621,7 @@

             @settings[prop] = val

+        set_account_tab_label(true, @fullname())

     set_view: () ->
         if not @settings?
@@ -689,7 +690,7 @@
                 else
                     set(element, value)

-        set_account_tab_label(true, @settings.email_address)
+        set_account_tab_label(true, @fullname())

     # Store the properties that user can freely change to the backend database.
     # The other properties only get saved by direct api calls that require additional
@@ -815,7 +816,6 @@
                 # success
                 $("#account-settings-email_address").html(new_email_address)
                 account_settings.settings.email_address = new_email_address
-                set_account_tab_label(true, new_email_address)
                 close_change_email_address()
                 alert_message(type:"success", message:"Email address successfully changed.")
     return false


extend translations

extend translations

--- template/base/config/translation.coffee
+++ template/base/config/translation.coffee
@@ -1,5 +1,8 @@
-module.exports.en = Model:
-  NotFound: 'resource doesn\'t found'
+module.exports.translations =
+  en:
+    Model:
+      NotFound: "resource doesn't found"

-module.exports.es = Model:
-  NotFound: 'recurso no encontrado'+  es:
+    Model:
+      NotFound: "recurso no encontrado"


comment out unimplemented test

comment out unimplemented test


add tests for comment format option

add tests for comment format option

--- test/comment-test.coffee
+++ test/comment-test.coffee
@@ -2,16 +2,19 @@
 bHtml = require './'

 describe 'Comment (<!--)', ->
-  it 'works', ->
-    source = '''
-      <!--xyz
-    '''
-    assert bHtml(source, format: 'demo') is '<!--xyz-->'
-
-    source = '''
-      <!--xyz
-    '''
-    assert bHtml(source) is '<!--xyz-->'
+  context '1', ->
+    it 'works', ->
+      source = '''
+        <!--xyz
+      '''
+      format = (nodes, options) ->
+        assert nodes.length is 1
+        assert nodes[0].type is 'comment'
+        assert nodes[0].value is 'xyz'
+        assert.deepEqual options, { format }
+      bHtml source, { format }
+      assert bHtml(source) is '<!--xyz-->'
+      assert bHtml(source, format: 'demo') is '<!--xyz-->'

   context '(errors)', ->
     it 'works', ->


test-ified control_flow

test-ified control_flow

--- test/control_flow.coffee
+++ test/control_flow.coffee
@@ -198,38 +198,38 @@
   eq nonce, result

-# For / While / Until / Loop
-
-# TODO: refactor while tests
-
-# While
-
-i = 5
-list = while i -= 1
-  i * 2
-ok list.join(' ') is "8 6 4 2"
-
-i = 5
-list = (i * 3 while i -= 1)
-ok list.join(' ') is "12 9 6 3"
-
-i = 5
-func   = (num) -> i -= num
-assert = -> ok i < 5 > 0
-results = while func 1
-  assert()
-  i
-ok results.join(' ') is '4 3 2 1'
-
-i = 10
-results = while i -= 1 when i % 2 is 0
-  i * 2
-ok results.join(' ') is '16 12 8 4'
-
-#759: `if` within `while` condition
-2 while if 1 then 0
+test "basic `while` loops", ->
+
+  i = 5
+  list = while i -= 1
+    i * 2
+  ok list.join(' ') is "8 6 4 2"
+
+  i = 5
+  list = (i * 3 while i -= 1)
+  ok list.join(' ') is "12 9 6 3"
+
+  i = 5
+  func   = (num) -> i -= num
+  assert = -> ok i < 5 > 0
+  results = while func 1
+    assert()
+    i
+  ok results.join(' ') is '4 3 2 1'
+
+  i = 10
+  results = while i -= 1 when i % 2 is 0
+    i * 2
+  ok results.join(' ') is '16 12 8 4'
+
+
+test "Issue 759: `if` within `while` condition", ->
+
+  2 while if 1 then 0
+

 test "assignment inside the condition of a `while` loop", ->
+
   nonce = {}
   count = 1
   a = nonce while count--
@@ -239,47 +239,45 @@
     b = nonce
   eq nonce, b

-# While over break.
-i = 0
-result = while i < 10
-  i++
-  break
-arrayEq result, []
-
-# While over continue.
-i = 0
-result = while i < 10
-  i++
-  continue
-arrayEq result, []
-
-# Until
-
-# TODO: refactor until tests
-# TODO: add until tests
-
-value = false
-i = 0
-results = until value
-  value = true if i is 5
-  i++
-ok i is 6
-
-# Loop
-
-# TODO: refactor loop tests
-# TODO: add loop tests
-
-i = 5
-list = []
-loop
-  i -= 1
-  break if i is 0
-  list.push i * 2
-ok list.join(' ') is '8 6 4 2'
-
-# TODO: refactor for tests
-# TODO: add for tests
+
+test "While over break.", ->
+
+  i = 0
+  result = while i < 10
+    i++
+    break
+  arrayEq result, []
+
+
+test "While over continue.", ->
+
+  i = 0
+  result = while i < 10
+    i++
+    continue
+  arrayEq result, []
+
+
+test "Basic `until`", ->
+
+  value = false
+  i = 0
+  results = until value
+    value = true if i is 5
+    i++
+  ok i is 6
+
+
+test "Basic `loop`", ->
+
+  i = 5
+  list = []
+  loop
+    i -= 1
+    break if i is 0
+    list.push i * 2
+  ok list.join(' ') is '8 6 4 2'
+

 test "break at the top level", ->
   for i in [1,2,3]
@@ -289,7 +287,7 @@
   eq 2, result

 test "break *not* at the top level", ->
-  someFunc = () ->
+  someFunc = ->
     i = 0
     while ++i < 3
       result = i
@@ -298,121 +296,125 @@
   eq 2, someFunc()

-# Switch
-
-# TODO: refactor switch tests
-
-num = 10
-result = switch num
-  when 5 then false
-  when 'a'
-    true
-    true
-    false
-  when 10 then true
-
-
-  # Mid-switch comment with whitespace
-  # and multi line
-  when 11 then false
-  else false
-
-ok result
-
-
-func = (num) ->
-  switch num
-    when 2, 4, 6
+test "basic `switch`", ->
+
+  num = 10
+  result = switch num
+    when 5 then false
+    when 'a'
       true
-    when 1, 3, 5
+      true
       false
-
-ok func(2)
-ok func(6)
-ok !func(3)
-eq func(8), undefined
-
-
-# Ensure that trailing switch elses don't get rewritten.
-result = false
-switch "word"
-  when "one thing"
-    doSomething()
-  else
-    result = true unless false
-
-ok result
-
-result = false
-switch "word"
-  when "one thing"
-    doSomething()
-  when "other thing"
-    doSomething()
-  else
-    result = true unless false
-
-ok result
-
-
-# Should be able to handle switches sans-condition.
-result = switch
-  when null                     then 0
-  when !1                       then 1
-  when '' not of {''}           then 2
-  when [] not instanceof Array  then 3
-  when true is false            then 4
-  when 'x' < 'y' > 'z'          then 5
-  when 'a' in ['b', 'c']        then 6
-  when 'd' in (['e', 'f'])      then 7
-  else ok
-
-eq result, ok
-
-
-# Should be able to use "@properties" within the switch clause.
-obj = {
-  num: 101
-  func: ->
-    switch @num
-      when 101 then '101!'
-      else 'other'
-}
-
-ok obj.func() is '101!'
-
-
-# Should be able to use "@properties" within the switch cases.
-obj = {
-  num: 101
-  func: (yesOrNo) ->
-    result = switch yesOrNo
-      when yes then @num
-      else 'other'
-    result
-}
-
-ok obj.func(yes) is 101
-
-
-# Switch with break as the return value of a loop.
-i = 10
-results = while i > 0
-  i--
-  switch i % 2
-    when 1 then i
-    when 0 then break
-
-eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
-
-
-# Issue #997. Switch doesn't fallthrough.
-val = 1
-switch true
-  when true
-    if false
-      return 5
-  else
-    val = 2
-
-eq val, 1
+    when 10 then true
+
+
+    # Mid-switch comment with whitespace
+    # and multi line
+    when 11 then false
+    else false
+
+  ok result
+
+
+  func = (num) ->
+    switch num
+      when 2, 4, 6
+        true
+      when 1, 3, 5
+        false
+
+  ok func(2)
+  ok func(6)
+  ok !func(3)
+  eq func(8), undefined
+
+
+test "Ensure that trailing switch elses don't get rewritten.", ->
+
+  result = false
+  switch "word"
+    when "one thing"
+      doSomething()
+    else
+      result = true unless false
+
+  ok result
+
+  result = false
+  switch "word"
+    when "one thing"
+      doSomething()
+    when "other thing"
+      doSomething()
+    else
+      result = true unless false
+
+  ok result
+
+
+test "Should be able to handle switches sans-condition.", ->
+
+  result = switch
+    when null                     then 0
+    when !1                       then 1
+    when '' not of {''}           then 2
+    when [] not instanceof Array  then 3
+    when true is false            then 4
+    when 'x' < 'y' > 'z'          then 5
+    when 'a' in ['b', 'c']        then 6
+    when 'd' in (['e', 'f'])      then 7
+    else ok
+
+  eq result, ok
+
+
+test "Should be able to use `@properties` within the switch clause.", ->
+
+  obj = {
+    num: 101
+    func: ->
+      switch @num
+        when 101 then '101!'
+        else 'other'
+  }
+
+  ok obj.func() is '101!'
+
+
+test "Should be able to use `@properties` within the switch cases.", ->
+
+  obj = {
+    num: 101
+    func: (yesOrNo) ->
+      result = switch yesOrNo
+        when yes then @num
+        else 'other'
+      result
+  }
+
+  ok obj.func(yes) is 101
+
+
+test "Switch with break as the return value of a loop.", ->
+
+  i = 10
+  results = while i > 0
+    i--
+    switch i % 2
+      when 1 then i
+      when 0 then break
+
+  eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
+
+
+test "Issue #997. Switch doesn't fallthrough.", ->
+
+  val = 1
+  switch true
+    when true
+      if false
+        return 5
+    else
+      val = 2
+
+  eq val, 1


Bumping Moment.js version; and renaming methods

--- scripts/dweet-brain.coffee
+++ scripts/dweet-brain.coffee
@@ -2,7 +2,7 @@
 #   Dweet.io and MsgPack powered Hubot Brain
 #
 # Dependencies:
-#   "moment": "^2.6.0"
+#   "moment": "^2.8.3"
 #   "msgpack": "^0.2.3"
 #
 # Commands:
@@ -24,23 +24,23 @@
 DWEET_KEY = process.env.DWEET_KEY
 DWEET_AUTOSAVE = process.env.DWEET_AUTOSAVE or 1800

-toBase64 = (object) ->
+Serialize = (object) ->
     msgpack.pack(object).toString('base64')

-fromBase64 = (encodedObject) ->
+Unserialize = (encodedObject) ->
     msgpack.unpack new Buffer(encodedObject, 'base64')

-encodeChildren = (object, ignoreUsers = false) ->
+EncodeChildren = (object, ignoreUsers = false) ->
     encodedObject = {}
     for key, value of object
         continue if ignoreUsers and key is 'users'
-        encodedObject[key] = toBase64 value
+        encodedObject[key] = Serialize value
     encodedObject

-decodeChildren = (object) ->
+DecodeChildren = (object) ->
     decodedObject = {}
     for key, value of object
-        decodedObject[key] = fromBase64 value
+        decodedObject[key] = Unserialize value
     decodedObject

 module.exports = (robot) ->
@@ -63,7 +63,7 @@
             else if dweet.this is 'failed'
                 return robot.logger.error dweet.because
             else
-                content = decodeChildren dweet.with[0].content
+                content = DecodeChildren dweet.with[0].content
                 robot.brain.mergeData content
                 robot.logger.info 'Data for brain retrieved from Dweet.io.'
                 status.lastSaved = dweet.with[0].created
@@ -74,7 +74,7 @@

     robot.brain.on 'save', (data = {}) ->
         isIRC = robot.adapterName is 'irc'
-        dweetRequest robot, '/dweet', encodeChildren(data, isIRC), (dweet) ->
+        dweetRequest robot, '/dweet', EncodeChildren(data, isIRC), (dweet) ->
             return robot.logger.error dweet.because if dweet.this is 'failed'

             status.lastSaved = dweet.with.created


+ maxLevel option to limit the max nested role level to avoid recusive


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -1,6 +1,6 @@
 ###*
 The Metered module provides a simple drop-in
-
+meter ui

 player = Core


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -146,7 +146,7 @@
       text: null
       width: 100

-    if not I[I.name]
+    if options.name
       I[I.name] = 100

     if not I["max#{I.name.capitalize()}"]


fixing rhino on Apple java

fixing rhino on Apple java

don't ask me why we have to use the bootstrap loader, but we do

--- src/methods.coffee
+++ src/methods.coffee
@@ -125,7 +125,7 @@
         j++
         if j < exceptions.length
           e_desc = exceptions[j]
-          @cls.loader.resolve_class(rs, e_desc,
+          rs.bcl.resolve_class(rs, e_desc,
             ((cls)=>etype_objs[j]=cls.get_class_object(rs);fetch_etype()), failure_fn)
         else
           # XXX: missing parameterAnnotations
@@ -144,7 +144,7 @@
       fetch_ptype = () =>
         i++
         if i < @param_types.length
-          @cls.loader.resolve_class(rs, @param_types[i],
+          rs.bcl.resolve_class(rs, @param_types[i],
             ((cls)=>param_type_objs[i]=cls.get_class_object(rs);fetch_ptype()), failure_fn)
         else
           fetch_etype()


small typo fixes in commits

small typo fixes in commits

--- src/motions.coffee
+++ src/motions.coffee
@@ -18,7 +18,7 @@
   rightOfCursor = line.substring column

   # If the item on the cursor isn't a word it goes to the next word or
-  # the nest group of special character if there isn't a word.
+  # the next group of special characters if there isn't a word.
   if /\W/.test line[column]
     leftMatch = ['']
     nextWord = /\w+/.exec rightOfCursor


Add emit prototypes for all ast node types

--- src/opcodes.coffee
+++ src/opcodes.coffee
@@ -4,9 +4,10 @@
   id = 0
   classFactory = (name, fn) ->
     OpcodeClass = (->
-      # this is ugly but its the only way to get meaningful opcode
+      # this is ugly but its the only way to get nicde opcode
       # names when inspecting with google chrome
       constructor = eval "(function #{name}(args) { this.args = args; })"
+      # constructor = (@args) ->
       constructor::id = id++
       constructor::name = name
       constructor::exec = fn
@@ -41,36 +42,277 @@
     do (opcode) -> opcodes[opcode::name] = (args...) -> new opcode(args)
 )()

+unaryOp =
+  '-': null
+  '+': null
+  '!': null
+  '~': null
+  'typeof': null
+  'void': null
+  'delete': null
+
 binaryOp =
+  '==': null
+  '!=': null
+  '===': null
+  '!==': null
+  '<': null
+  '<=': null
+  '>': null
+  '>=': null
+  '<<': null
+  '>>': null
+  '>>>': null
   '+': opcodes.add
   '-': opcodes.sub
   '*': opcodes.mul
   '/': opcodes.div
-
+  '%': null
+  '|': null
+  '^': null
+  'in': null
+  'instanceof': null
+  '..': null
+
+logicalOp =
+  '||': null
+  '&&': null
+
+assignOp =
+  '=': null
+  '+=': null
+  '-=': null
+  '*=': null
+  '/=': null
+  '%=': null
+  '<<=': null
+  '>>=': null
+  '>>>=': null
+  '|=': null
+  '^=': null
+  '&=': null
+
+updateOp =
+  '++': null
+  '--': null
+
+# AST node types. Source:
+# https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
 emit =
-  Literal: (node, script) -> script.push opcodes.literal(node.value)
-
+  # Programs:
+  Program: (node, script) ->
+    # A complete program source tree.
+    for child in node.body
+      emit[child.type](child, script)
+  Function: (node, script) ->
+    # A function declaration or expression. The body of the function may be a
+    # block statement, or in the case of an expression closure, an expression.
+    throw new Error('not implemented')
+
+  # Statements:
+  EmptyStatement: (node, script) ->
+    # An empty statement, i.e., a solitary semicolon.
+    throw new Error('not implemented')
+  BlockStatement: (node, script) ->
+    # A block statement, i.e., a sequence of statements surrounded by braces.
+    throw new Error('not implemented')
   ExpressionStatement: (node, script) ->
+    # An expression statement, i.e., a statement consisting of a single
+    # expression.
     emit[node.expression.type](node.expression, script)
-
-  BinaryExpression: (node, script) ->
-    emit[node.left.type](node.left, script)      # emit left expression
-    emit[node.right.type](node.right, script)    # emit right expression
-    script.push binaryOp[node.operator]()        # emit binary opcode
-
+  IfStatement: (node, script) ->
+    # An if statement.
+    throw new Error('not implemented')
+  LabeledStatement: (node, script) ->
+    # A labeled statement, i.e., a statement prefixed by a break/continue labe
+    throw new Error('not implemented')
+  BreakStatement: (node, script) ->
+    # A break statement
+    throw new Error('not implemented')
+  ContinueStatement: (node, script) ->
+    # A continue statement
+    throw new Error('not implemented')
+  WithStatement: (node, script) ->
+    # A with statement
+    throw new Error('not implemented')
+  SwitchStatement: (node, script) ->
+    # A switch statement. The lexical flag is metadata indicating whether
+    # the switch statement contains any unnested let declarations
+    # (and therefore introduces a new lexical scope)
+    throw new Error('not implemented')
+  ReturnStatement: (node, script) ->
+    # A return statement
+    throw new Error('not implemented')
+  ThrowStatement: (node, script) ->
+    # A throw statement
+    throw new Error('not implemented')
+  TryStatement: (node, script) ->
+    # A try statement
+    throw new Error('not implemented')
+  WhileStatement: (node, script) ->
+    # A while statement
+    throw new Error('not implemented')
+  DoWhileStatement: (node, script) ->
+    # A do/while statement
+    throw new Error('not implemented')
+  ForStatement: (node, script) ->
+    # A for statement
+    throw new Error('not implemented')
+  ForInStatement: (node, script) ->
+    # A for/in statement, or, if each is true, a for each/in statement
+    throw new Error('not implemented')
+  ForOfStatement: (node, script) ->
+    # A for/of statement
+    throw new Error('not implemented')
+  LetStatement: (node, script) ->
+    # A let statement
+    throw new Error('not implemented')
+  DebuggerStatement: (node, script) ->
+    # A debugger statement
+    throw new Error('not implemented')
+
+  # Declarations:
+  FunctionDeclaration: (node, script) ->
+    # A function declaration
+    throw new Error('not implemented')
   VariableDeclaraction: (node, script) ->
+    # A variable declaration, via one of var, let, or const.
+    # TODO incomplete
     for child in node.declarations
       emit[child.type](child.init, script)       # declare each variable
-
   VariableDeclarator: (node, script) ->
+    # A variable declarator
     emit[node.init.type](node.init, script)      # emit init expression
     script.push opcodes.store(node.name)         # emit store opcode

+  # Expressions
+  ThisExpression: (node, script) ->
+    # A this expression
+    throw new Error('not implemented')
+  ArrayExpression: (node, script) ->
+    # An array expression
+    throw new Error('not implemented')
+  ObjectExpression: (node, script) ->
+    # An object expression. A literal property in an object expression
+    # can have either a string or number as its value. Ordinary property
+    # initializers have a kind value "init"; getters and setters have the
+    # kind values "get" and "set", respectively.An object expression
+    throw new Error('not implemented')
+  FunctionExpression: (node, script) ->
+    # A function expression
+    throw new Error('not implemented')
+  ArrowExpression: (node, script) ->
+    # A fat arrow function expression, i.e.,`let foo = (bar) => { /* body */ }`
+    throw new Error('not implemented')
+  SequenceExpression: (node, script) ->
+    # A sequence expression, i.e., a comma-separated sequence of expressions.
+    throw new Error('not implemented')
+  UnaryExpression: (node, script) ->
+    # A unary operator expression.
+    throw new Error('not implemented')
+  BinaryExpression: (node, script) ->
+    # A binary operator expression.
+    emit[node.left.type](node.left, script)
+    emit[node.right.type](node.right, script)
+    script.push binaryOp[node.operator]()
+  AssignmentExpression: (node, script) ->
+    # An assignment operator expression.
+    throw new Error('not implemented')
+  UpdateExpression: (node, script) ->
+    # An update (increment or decrement) operator expression.
+    throw new Error('not implemented')
+  LogicalExpression: (node, script) ->
+    # A logical operator expression.
+    throw new Error('not implemented')
+  ConditionalExpression: (node, script) ->
+    # A conditional expression, i.e., a ternary ?/: expression
+    throw new Error('not implemented')
+  NewExpression: (node, script) ->
+    # A new expression.
+    throw new Error('not implemented')
+  CallExpression: (node, script) ->
+    # A function or method call expression.
+    throw new Error('not implemented')
+  MemberExpression: (node, script) ->
+    # A member expression. If computed === true, the node corresponds to
+    # a computed e1[e2] expression and property is an Expression. If
+    # computed === false, the node corresponds to a static e1.x
+    # expression and property is an Identifier.
+    throw new Error('not implemented')
+  YieldExpression: (node, script) ->
+    # A yield expression
+    throw new Error('not implemented')
+  ComprehensionExpression: (node, script) ->
+    # An array comprehension. The blocks array corresponds to the sequence
+    # of for and for each blocks. The optional filter expression corresponds
+    # to the final if clause, if present
+    throw new Error('not implemented')
+  GeneratorExpression: (node, script) ->
+    # A generator expression. As with array comprehensions, the blocks
+    # array corresponds to the sequence of for and for each blocks, and
+    # the optional filter expression corresponds to the final if clause,
+    # if present.
+    throw new Error('not implemented')
+  GraphExpression: (node, script) ->
+    # A graph expression, aka "sharp literal," such as #1={ self: #1# }.
+    throw new Error('not implemented')
+  GraphIndexExpression: (node, script) ->
+    # A graph index expression, aka "sharp variable," such as #1#
+    throw new Error('not implemented')
+  LetExpression: (node, script) ->
+    # A let expression
+    throw new Error('not implemented')
+
+  # Patterns:
+
+  # JavaScript 1.7 introduced destructuring assignment and binding
+  # forms. All binding forms (such as function parameters, variable
+  # declarations, and catch block headers), accept array and object
+  # destructuring patterns in addition to plain identifiers. The left-hand
+  # sides of assignment expressions can be arbitrary expressions, but in the
+  # case where the expression is an object or array literal, it is interpreted
+  # by SpiderMonkey as a destructuring pattern.
+
+  # Since the left-hand side of an assignment can in general be any expression,
+  # in an assignment context, a pattern can be any expression. In binding
+  # positions (such as function parameters, variable declarations, and catch
+  # headers), patterns can only be identifiers in the base case, not arbitrary
+  # expressions
+  ObjectPattern: (node, script) ->
+    # An object-destructuring pattern. A literal property in an object pattern
+    # can have either a string or number as its value.
+    throw new Error('not implemented')
+  ArrayPattern: (node, script) ->
+    # An array-destructuring pattern.
+    throw new Error('not implemented')
+
+  # Clauses
+  SwitchCase: (node, script) ->
+    # A case (if test is an Expression) or default (if test === null) clause in
+    # the body of a switch statement.
+    throw new Error('not implemented')
+  CatchClause: (node, script) ->
+    # A catch clause following a try block. The optional guard property
+    # corresponds to the optional expression guard on the bound variable.
+    throw new Error('not implemented')
+  ComprehensionBlock: (node, script) ->
+    # A for or for each block in an array comprehension or generator expression
+    throw new Error('not implemented')
+
+  # Miscellaneous
+  Identifier: (node, script) ->
+    # An identifier. Note that an identifier may be an expression or a
+    # destructuring pattern.
+    throw new Error('not implemented')
+  Literal: (node, script) ->
+    # A literal token. Note that a literal can be an expression.
+    script.push opcodes.literal(node.value)
+
+
 compile = (node) ->
   script = new Script()
-  for child in node.body
-    emit[child.type](child, script)
-  script
+  emit[node.type](node, script)
+  return script

 module.exports = compile


Some things should stay this way.

Some things should stay this way.

--- src/pattern.coffee
+++ src/pattern.coffee
@@ -7,7 +7,7 @@
   try
     string.split "."
   catch error
-    throw (toError "invalid-pattern") #(string))
+    throw (toError "invalid-pattern")(string)

 _match = (pattern, target) ->
   pl = pattern.length


:memo: Tweak comment

:memo: Tweak comment

--- src/pattern.coffee
+++ src/pattern.coffee
@@ -156,10 +156,11 @@

   # Get the tokens for the capture indices.
   #
-  # line - The string line being tokenized.
-  # currentCaptureIndices - The current array capture indices being processed.
-  #                         This method is called recursively and this array
-  #                         will be modified inside this method.
+  # line - The string being tokenized.
+  # currentCaptureIndices - The current array of capture indices being
+  #                         processed into tokens. This method is called
+  #                         recursively and this array will be modified inside
+  #                         this method.
   # allCaptureIndices - The array of all capture indices, this array will not
   #                     be modified.
   # scopes - An array of scopes.


added trim()

added trim()

--- src/recurry.coffee
+++ src/recurry.coffee
@@ -68,9 +68,9 @@
     args = msg.match[1].split(" ")
     return msg.send( recurry.usage.add ) if args.length != 3
     data = {}
-    data.id     = args.pop()
-    data.url    = args.pop()
-    data.method = args.pop()
+    data.id     = args.pop().trim()
+    data.url    = args.pop().trim()
+    data.method = args.pop().trim()
     data.scheduler = "?"
     console.dir data
     robot.http( recurry.url+"/scheduler"  )


Use className instead of class

Use className instead of class

React uses className.

--- views/navigation-bar.cjsx
+++ views/navigation-bar.cjsx
@@ -78,7 +78,7 @@
           <Button bsSize='small' bsStyle='info' disabled={!webview.canGoForward()} onClick={@goForward} title='Forward'><FontAwesome name='arrow-right' /></Button>
         </ButtonGroup>

-        {@getIcon()}
+        {@getIcon()}

         <Button bsSize='small' bsStyle='warning' onClick={@refreshPage} title='Refresh'><FontAwesome name='refresh' /></Button>


fox for spinning wheel of death; fixes #50

--- app/assets/javascripts/models/users_collection.coffee
+++ app/assets/javascripts/models/users_collection.coffee
@@ -19,4 +19,5 @@
     if user = @findWhere(id : parseInt(id))
       return user.get("githubLogin")
     else
-      throw new Error("Couldn't find user with id: #{id}")+      #in rare cases user could be delete from the server
+      "<Deleted User>"


compare9


activity: define `NewCommentForm::mention` method

--- client/Social/Activity/views/comments/newcommentform.coffee
+++ client/Social/Activity/views/comments/newcommentform.coffee
@@ -49,6 +49,18 @@
       groupName : KD.getGroup().slug

+  mention: (username) ->
+
+    value = @getValue()
+
+    @setValue \
+      if value.indexOf("@#{username}") >= 0 then value
+      else if value.length is 0 then "@#{username} "
+      else "#{value} @#{username} "
+
+    @setFocus()
+
+
   setFocus: ->

     @input.setFocus()


Viewer: Fixed empty path bug.

Viewer: Fixed empty path bug.

--- client/app/Applications/Viewer.kdapplication/AppView.coffee
+++ client/app/Applications/Viewer.kdapplication/AppView.coffee
@@ -1,7 +1,9 @@
 class PreviewerView extends KDView

   constructor:(options = {},data)->
+
     options.cssClass = 'previewer-body'
+
     super options, data

   openPath:(path)->
@@ -21,6 +23,7 @@
     @path = path
     @iframe.$().attr 'src', path
     @viewerHeader.setPath initialPath
+    @emit "ready"

   refreshIFrame:->
     @iframe.$().attr 'src', "#{@path}"
@@ -29,9 +32,9 @@
     @clean

   viewAppended:->
-    @addSubView @viewerHeader = new ViewerTopBar {}, @path
-    @addSubView @iframe = new KDCustomHTMLView
-      tagName : 'iframe'
+    @addSubView @viewerHeader = new ViewerTopBar     { delegate: this }, @path
+    @addSubView @iframe       = new KDCustomHTMLView { tagName : "iframe" }

-    {path} = @getOption 'params'
+    {params} = @getOptions()
+    path     = params?.path
     if path then @utils.defer => @openPath path


docs: switch to learn.koding.com

docs: switch to learn.koding.com

--- client/app/MainApp/navigation/navigationdocsjobslink.coffee
+++ client/app/MainApp/navigation/navigationdocsjobslink.coffee
@@ -16,7 +16,7 @@
       partial    : "Docs"
       cssClass   : "ext"
       attributes :
-        href     : "http://koding.github.io/docs/"
+        href     : "http://learn.koding.com"
         target   : "_blank"

     @jobsLink  = new KDCustomHTMLView


home: remove unnecessarry whitespace

home: remove unnecessarry whitespace

--- client/home/lib/utilities/components/desktopapp/view.coffee
+++ client/home/lib/utilities/components/desktopapp/view.coffee
@@ -6,19 +6,19 @@

   renderGuideButton: ->
-
+

       VIEW GUIDE

-
+
   renderDownloadButton: ->
-
+

       DOWNLOAD

   render: ->
-
+

         Koding Collaborative Development Environment (CDE),
@@ -35,4 +35,4 @@
         {@renderGuideButton()}

-    +


IDE: Don't show "Workspace from here" link for team users

--- client/ide/lib/finder/idefindercontextmenucontroller.coffee
+++ client/ide/lib/finder/idefindercontextmenucontroller.coffee
@@ -1,4 +1,5 @@
-kd = require 'kd'
+kd        = require 'kd'
+isKoding  = require 'app/util/isKoding'

 NFinderContextMenuController = require 'finder/filetree/controllers/nfindercontextmenucontroller'
 NSetPermissionsView = require 'finder/filetree/itemsubviews/nsetpermissionsview'
@@ -57,7 +58,7 @@
     else
       delete items.Collapse

-    unless fileData.machine.isMine()
+    if not fileData.machine.isMine() or not isKoding()
       delete items['Workspace from here']

     return items


simplified description of symbols

simplified description of symbols

--- src/symbols.coffee
+++ src/symbols.coffee
@@ -2,15 +2,15 @@

 {Symbol} = require './es6-support'

-exports.bName = Symbol 'various names for the objects'
-exports.bType = Symbol 'contains type of the object'
-exports.bDispose = Symbol 'method name for disposing objects'
+exports.bName = Symbol 'name of the object'
+exports.bType = Symbol 'type of the object'
+exports.bDispose = Symbol 'method to dispose object'
 exports.bChanged = Symbol 'timestamp of last change'

 exports.bIdentity = Symbol 'identifier of the object'
 exports.bFields = Symbol 'fields defined for the component'

-exports.bNodes = Symbol 'nodes that owns current entity'
+exports.bNodes = Symbol 'nodes that owns entity'
 exports.bEntity = Symbol 'reference to entity instance'

 exports.Symbol = Symbol


adding elapsedTime

adding elapsedTime