0.0.33 + important bug fix

0.0.33 + important bug fix

--- lib/server.coffee
+++ lib/server.coffee
@@ -119,7 +119,7 @@

   $SS.redis.pubsub.on 'message', (channel, message) =>
     channel = channel.split(':')
-    if channel && channel[0] == 'socketstream'
+    if channel && channel[0] == $SS.config.redis.key_prefix
       switch channel[1]
         when 'user'
           client = $SS.users.connected[channel[2]]


dropping the fetching of a job

dropping the fetching of a job

--- lib/server.coffee
+++ lib/server.coffee
@@ -480,7 +480,12 @@
 										doc._retries = workque[index]._retries if workque[index]._retries?
 										workque[index] = doc
 								else
-									console.log "invalid doc",item?.id
+									if item?.id?
+										# dropping invalid jobs (most probably a deleted job)
+										console.log "invalid doc",item.id
+										index = findplace(item.id)
+										workque[index].status = "dropped"
+
 						callback()
 			), config.readBatchsize)
 			syncJobs = (host, jobs) ->


Add menu items for pane management

Add menu items for pane management

Closes #1897

--- menus/darwin.cson
+++ menus/darwin.cson
@@ -135,6 +135,25 @@
       { label: 'Reload', command: 'window:reload' }
       { label: 'Toggle Full Screen', command: 'window:toggle-full-screen' }
       {
+        label: 'Panes'
+        submenu: [
+          { label: 'Split Up', command: 'pane:split-up' }
+          { label: 'Split Down', command: 'pane:split-down' }
+          { label: 'Split Left', command: 'pane:split-left' }
+          { label: 'Split Right', command: 'pane:split-right' }
+          { type: 'separator' }
+          { label: 'Focus Next Pane', command: 'window:focus-next-pane' }
+          { label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
+          { type: 'separator' }
+          { label: 'Focus Pane Above', command: 'window:focus-pane-above' }
+          { label: 'Focus Pane Below', command: 'window:focus-pane-below' }
+          { label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
+          { label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
+          { type: 'separator' }
+          { label: 'Close pane', command: 'pane:close' }
+        ]
+      }
+      {
         label: 'Developer'
         submenu: [
           { label: 'Open In Dev Mode...', command: 'application:open-dev' }


header font customization

header font customization

--- @client/franklin.coffee
+++ @client/franklin.coffee
@@ -2014,6 +2014,7 @@
     current_user = fetch('/current_user')

     fonts = customization('font') or "'Avenir Next W01', 'Avenir Next', 'Lucida Grande', 'Lucida Sans Unicode', 'Helvetica Neue', Helvetica, Verdana, sans-serif"
+    header_fonts = customization('header_font') or fonts
     DIV

       # Track whether the user is currently swipping. Used to determine whether
@@ -2044,7 +2045,11 @@
       STYLE
         dangerouslySetInnerHTML: __html: """
           #content, #content input, #content button, #content textarea {
-            font-family: #{fonts}; }
+            font-family: #{fonts};
+          }
+          #content h1, #content h2, #content h3, #content h4 {
+            font-family: #{header_fonts};
+          }
         """

       if !subdomain.name


JDomain: cross-check domain if it may valid or not, before doing any other check.

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -175,16 +175,30 @@

   parseDomain = (domain)->

+    # Custom error
+    err = (message = "Invalid domain: #{domain}")->
+      err: new KodingError message, "INVALIDDOMAIN"
+
+    # Domain check ~
+    # ^                          start of the line
+    #   [A-Za-z0-9-]+            start with the string in the brackets [ ], must contain one or more (+)
+    #       (                    start of group #1 - For sub-domain.
+    #        \\.[A-Za-z0-9-]+    follow by a dot "." and string in the brackets [ ], must contain one or more (+)
+    #       )*                   end of group #1, this group is optional (*)
+    #       (                    start of group #2 - check the extension
+    #        \\.[A-Za-z]{2,}     follow by a dot "." and string in the brackets [ ], with minimum length of 2
+    #       )                    end of group #2
+    # $                          end of the line
+
+    return err()  unless \
+      /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/i.test domain
+
     # Return domain type as custom and keep the domain as is
     return {type: 'custom', domain}  unless /\.kd\.io$/.test domain

-    # Custom error
-    err = (message)->
-      err: new KodingError message, "INVALIDDOMAIN"
-
     # Basic check
     unless /([a-z0-9\-]+)\.kd\.io$/.test domain
-      return err "Invalid domain: #{domain}."
+      return err()

     # Check for shared|vm prefix
     if /^shared|vm[\-]?([0-9]+)?/.test prefix


 Merge branch 'master.environments-customDomainFilter' into 'master'

Master.Environments Custom Domain Filter

*Custom Domains are not staying in the proper Group -> https://app.asana.com/0/5526870156482/10969203653260

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -146,6 +146,9 @@
   filterDomains = (domains, account, group)->
     domainList = []
     domainList = domains.filter (domain)->
+      if domain.group? # we filter domains per group
+        return no  unless domain.group is group
+
       {domain} = domain
       return yes  unless /\.kd\.io$/.test domain


Remove the request once it's been completed.

--- src/extras/testing/xhr_mocking.coffee
+++ src/extras/testing/xhr_mocking.coffee
@@ -9,7 +9,7 @@
       data ?= @get('data')
       @fire 'loading'

-      fn = self.getRequestCb(@get('url'), @get('method'))
+      fn = self.getAndRemoveRequestCb(@get('url'), @get('method'))

       return if not fn
       {status, response, before} = fn(data)
@@ -30,8 +30,12 @@
   xhrTeardown: ->
     Batman.Request::send = @_savedSend

-  getRequestCb: (url, method) ->
-    return @_requests["#{method}::#{url}"]
+  getAndRemoveRequestCb: (url, method) ->
+    id = "#{method}::#{url}"
+    request = @_requests[id]
+
+    delete @_requests[id]
+    return request

   setRequestCb: (url, method, cb) ->
     @_requests["#{method}::#{url}"] = cb


Battle: Use the player name when showing the rating update message

--- server/conditions.coffee
+++ server/conditions.coffee
@@ -134,12 +134,17 @@
       ratings.getRatings [ winnerId, loserId ], (err, oldRatings) =>
         ratings.updatePlayers winnerId, loserId, ratings.results.WIN, (err, result) =>
           return @message "An error occurred updating rankings :("  if err
+
+          name = @players.find((p) -> p.id == winnerId).name
           oldRating = Math.floor(oldRatings[0])
           newRating = Math.floor(result[0].rating)
-          @message "#{winnerId}'s rating: #{oldRating} -> #{newRating}"
+          @message "#{name}'s rating: #{oldRating} -> #{newRating}"
+
+          name = @players.find((p) -> p.id == loserId).name
           oldRating = Math.floor(oldRatings[1])
           newRating = Math.floor(result[1].rating)
-          @message "#{loserId}: #{oldRating} -> #{newRating}"
+          @message "#{name}: #{oldRating} -> #{newRating}"
+
           @emit('ratingsUpdated')
           @sendUpdates()


Always show accuracy in play-details

Always show accuracy in play-details

--- resources/assets/coffee/react/_components/play-detail.coffee
+++ resources/assets/coffee/react/_components/play-detail.coffee
@@ -73,14 +73,17 @@
                   osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.pp))
                 else
                   score.score.toLocaleString()
+            if score.weight
+              div
+                className: 'detail-row__detail-row detail-row__detail-row--bottom'
+                span
+                  className: 'detail-row__text-score'
+                  osu.trans 'users.show.extra.top_ranks.weighted_pp',
+                    percentage: "#{Math.round(score.weight.percentage)}%"
+                    pp: osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.weight.pp))
             div
               className: 'detail-row__detail-row detail-row__detail-row--bottom'
               span
                 className: 'detail-row__text-score'
-                if score.weight
-                  osu.trans 'users.show.extra.top_ranks.weighted_pp',
-                    percentage: "#{Math.round(score.weight.percentage)}%"
-                    pp: osu.trans('users.show.extra.top_ranks.pp', amount: Math.round(score.weight.pp))
-                else if !score.pp
-                  osu.trans 'users.show.extra.historical.recent_plays.accuracy',
-                    percentage: "#{(score.accuracy * 100).toFixed(2)}%"
+                osu.trans 'users.show.extra.historical.recent_plays.accuracy',
+                  percentage: "#{(score.accuracy * 100).toFixed(2)}%"


lint fix

lint fix

--- server/src/Component/Builtin/DataSource/ElasticSearch.coffee
+++ server/src/Component/Builtin/DataSource/ElasticSearch.coffee
@@ -2,7 +2,7 @@
 _ = require 'underscore'

 class ElasticSearch
-    constructor: ->
+    constructor: ->
         @_elasticsearch = require 'elasticsearch'
         @_ejs = require 'elastic.js'

@@ -33,6 +33,6 @@

         es = @client datasource
         es.search options
-        .then success, error
+        .then success, error

 module.exports = ElasticSearch


Check whether id is valid when adding to weak map.

It's possible that the object has been added to weak map before and then
erased, so having an id property doesn't mean it's added in the weak map.

--- browser/atom/objects_registry.coffee
+++ browser/atom/objects_registry.coffee
@@ -59,7 +59,7 @@
 exports.add = (processId, routingId, obj) ->
   # Some native objects may already been added to objectsWeakMap, be care not
   # to add it twice.
-  objectsWeakMap.add obj unless obj.id?
+  objectsWeakMap.add obj unless obj.id? and objectsWeakMap.has obj.id

   # Store and reference the object, then return the storeId which points to
   # where the object is stored. The caller can later dereference the object


Adds an `href` method

Adds an `href` method

--- models/article.coffee
+++ models/article.coffee
@@ -46,3 +46,6 @@

   isTopTier: ->
     @get('tier') is 1
+
+  href: ->
+    "/article/#{@get('slug')}"


webapp: codemirror rtex offers \rinline{} and rnw \Sexpr{} just like sagetex offers \sage{}

--- src/smc-webapp/codemirror/custom-modes.coffee
+++ src/smc-webapp/codemirror/custom-modes.coffee
@@ -73,7 +73,11 @@
         open       : /^<<.+?>>=/
         close      : /^@/
         mode       : CodeMirror.getMode(config, 'r')
-    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "stex2"), block)
+    inline =
+        open  : "\\Sexpr{"
+        close : "}"
+        mode  : CodeMirror.getMode(config, 'r')
+    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "stex2"), block, inline)

 CodeMirror.defineMode "rtex", (config) ->
     block =
@@ -81,7 +85,11 @@
         close      : /^%%\s+end\.rcode/
         indent     : '% '
         mode       : CodeMirror.getMode(config, 'r')
-    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "stex2"), block)
+    inline =
+        open  : "\\rinline{"
+        close : "}"
+        mode  : CodeMirror.getMode(config, 'r')
+    return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "stex2"), block, inline)

 CodeMirror.defineMode "cython", (config) ->
     # FUTURE: need to figure out how to do this so that the name


updating gulp dev to also watch for lint errors

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -113,6 +113,9 @@
   server = new karma.Server(KARMA_COVERAGE_CONFIG)
   server.start()

+gulp.task 'watch-lint', ->
+  gulp.watch(['./src/**/*.{cjsx,coffee}', './*.coffee', './test/**/*.{cjsx,coffee}'], ['lint'])
+
 # clean out the dist directory before running since otherwise stale files might be served from there.
 # The _webserver task builds and serves from memory with a fallback to files in dist
-gulp.task 'dev', ['_cleanDist', '_webserver']
+gulp.task 'dev', ['_cleanDist', '_webserver', 'watch-lint']


Agentpie: changed label layout radius

Agentpie: changed label layout radius

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -240,7 +240,7 @@
             bbox = @getBBox()
             #bbox.width and bbox.height will
             #describe the size of the label text
-            labelRadius = radiuso + (radiuso - radiusi)
+            labelRadius = radiuso + 2*(radiuso - radiusi)
             d.x = Math.sin(a) * labelRadius
             d.l = d.x - bbox.width / 2 - 1#2
             d.r = d.x + bbox.width / 2 + 1#2


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -23,22 +23,27 @@
   pieTween: (d, i) ->
     `var i`
     that = this
-    theOldDataInPie = @oldPieData
+    #theOldDataInPie = @oldPieData
     # Interpolate the arcs in data space
-    s0 = undefined
-    e0 = undefined
-    if theOldDataInPie[i]
-      s0 = theOldDataInPie[i].startAngle
-      e0 = theOldDataInPie[i].endAngle
-    else if !theOldDataInPie[i] and theOldDataInPie[i - 1]
-      s0 = theOldDataInPie[i - 1].endAngle
-      e0 = theOldDataInPie[i - 1].endAngle
-    else if !theOldDataInPie[i - 1] and theOldDataInPie.length > 0
-      s0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
-      e0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
-    else
-      s0 = 0
-      e0 = 0
+    #s0 = undefined
+    #e0 = undefined
+    #if theOldDataInPie[i]
+    #  s0 = theOldDataInPie[i].startAngle
+    #  e0 = theOldDataInPie[i].endAngle
+    #else if !theOldDataInPie[i] and theOldDataInPie[i - 1]
+    #  s0 = theOldDataInPie[i - 1].endAngle
+    #  e0 = theOldDataInPie[i - 1].endAngle
+    #else if !theOldDataInPie[i - 1] and theOldDataInPie.length > 0
+    #  s0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
+    #  e0 = theOldDataInPie[theOldDataInPie.length - 1].endAngle
+    #else
+    #  s0 = 0
+    #  e0 = 0
+
+    # TS
+    s0 = 0
+    e0 = 0
+    #
     i = d3.interpolate({
       startAngle: 0
       endAngle: 0


Fix column hider on pjax

Fix column hider on pjax

--- app/assets/javascripts/ab_admin/core/columns_hider.js.coffee
+++ app/assets/javascripts/ab_admin/core/columns_hider.js.coffee
@@ -1,8 +1,6 @@
 class window.ColumnsHider
   constructor: ->
     @store_key = 'cols'
-    @wrap = $('#columns_hider_wrap')
-    @menu = @wrap.find('.dropdown-menu')
     @column_names = @columnNames()
     @collection_name = window.location.href.match(/admin\/(\w+)/)?[1]
     return unless @collection_name
@@ -12,26 +10,27 @@
     @refreshColumns()

   initHandlers: ->
-    @menu.on 'click', 'label', (e) =>
+    $(document).on 'pjax:end admin:list_init', =>
+      @initButtons()
+      @refreshColumns()
+
+    $(document).on 'click', '#columns_hider_wrap label', (e) =>
       e.preventDefault()
       e.stopPropagation()
       $input = $(e.currentTarget).find('input')
       $input.prop('checked', !$input.is(':checked'))
-      colIds = _.map @menu.find('input:not(:checked)'), (el) -> parseInt($(el).val())
+      colIds = _.map $('#columns_hider_wrap .dropdown-menu input:not(:checked)'), (el) -> parseInt($(el).val())
       @data[@collection_name] = colIds
       @refreshColumns()
       @setData()

-    $(document).on 'pjax:end admin:list_init', =>
-      @refreshColumns()
-
   initButtons: ->
-    log @data[@collection_name]
-    @menu.empty()
+    $menu = $('#columns_hider_wrap .dropdown-menu')
+    $menu.empty()
     for col, i in @column_names
       isActive = !_.include(@data[@collection_name], i)
       html = "<label class='checkbox'><input type='checkbox' #{'checked' if isActive} value='#{i}'>#{col.name}</label>"
-      @menu.append(html)
+      $menu.append(html)

   setData: ->
     res = {}


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -26,8 +26,8 @@
         $(@node).children("svg").remove();

         width = @width
-        height = 400
-        radius = Math.min(width, height) / 2
+        height = @height
+        radius = @radius
         radiuso = @radiuso
         radiusi = @radiusi
         color = @color


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -6,6 +6,8 @@
     ready: ->
         @testData = 123
         @oldPieData = ''
+        @instanceDataId = $(@node).parent().data-id
+        console.log('@instanceDataId',@instanceDataId)

     onData: (data) ->
         console.log('testData',@testData)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -129,7 +129,8 @@
         labels = labelGroup.selectAll('text').data(piedata.sort((p1, p2) ->
             p1.startAngle - p2.startAngle
         ))
-        labels.enter().append('text').attr 'text-anchor', 'middle'
+        #labels.enter().append('text').attr 'text-anchor', 'middle'
+        labels.enter().append('text').attr('text-anchor', 'middle').attr("filter","url(#dropshadow)")
         labels.exit().remove()
         labelLayout = d3.geom.quadtree().extent([
             [


Only log aborts that are non-branching

Only log aborts that are non-branching

--- lib/BehaviorTree.coffee
+++ lib/BehaviorTree.coffee
@@ -68,7 +68,7 @@

   onAbort: (choice, reason, value, branched = false) =>
     log.tree "#{@name or @id} Non-collection #{choice} aborted with reason '%s'", reason
-    log.abort "#{@name or @id} Non-collection #{choice} aborted with reason '%s'", reason
+    log.abort "#{@name or @id} Non-collection #{choice} aborted with reason '%s'", reason unless branched
     value = new Error reason unless value
     aborted =
       choice: choice


fix(my-videos-by-default): fix link element selection

--- src/my-videos-by-default/index.coffee
+++ src/my-videos-by-default/index.coffee
@@ -8,6 +8,6 @@
   run: ->
     utils = require "utils"

-    videosLink = $ "#l_vid > a"
+    videosLink = $( "#l_vid > a" )[ 0 ]
     utils.setConditional "videos.myVideosByDefault", videosLink, "href",
       true: "/videos#{window.vk.id}"


Merging with upstream

Merging with upstream

--- source/coffee/index.coffee
+++ source/coffee/index.coffee
@@ -97,9 +97,10 @@
       foreach: @wsItems

     @getPlans = ko.computed =>
-      if not @wsPlans()?.length
+      if not @wsPlans()?.plan?.length
         return null
-      @wsPlans()
+
+      return ntrapy.toArray n?.args for n in @wsPlans()?.plan

     @getActions = (node) =>
       if ntrapy.poller? then ntrapy.stopTree() else ntrapy.pollTree()
@@ -114,10 +115,32 @@
       , (jqXHR, textStatus, errorThrown) => # error handler
         switch jqXHR.status
           when 409 # Need more data
-            @wsPlans ntrapy.toArray n?.args for n in JSON.parse(jqXHR.responseText).plan
+            @wsPlans JSON.parse jqXHR.responseText
+            @wsPlans().node = object.id()
             $("#indexInputModal").modal "show"
           else
             console.log "Error (#{jqXHR.status}): ", errorThrown
+
+    $('#inputForm').validate
+      debug: true
+      highlight: (element) ->
+        $(element).closest('.control-group').removeClass('success').addClass('error')
+      success: (element) ->
+        $(element).addClass('valid').closest('.control-group').removeClass('error').addClass('success')
+      submitHandler: (form) =>
+        $(form).find('.control-group').each (index, element) =>
+          key = $(element).find('label').first().text()
+          val = $(element).find('input').val()
+          @wsPlans().plan[0].args[key].value = val
+        ntrapy.post "/roush/plan/",
+          JSON.stringify
+            node: @wsPlans().node
+            plan: @wsPlans().plan
+        , (data) ->
+          $("#indexInputModal").modal "hide"
+        , (jqXHR, textStatus, errorThrown) ->
+          $("#indexInputModal").modal "hide"
+          console.log "Error: ", jqXHR.status, textStatus, errorThrown

     @ # Return ourself

@@ -178,12 +201,3 @@
   ko.applyBindings ntrapy.indexModel

   $(document).on "click.dropdown.data-api", ntrapy.pollTree
-
-  $('#inputForm').validate
-    debug: true
-    highlight: (element) ->
-      $(element).closest('.control-group').removeClass('success').addClass('error')
-    success: (element) ->
-      $(element).text('OK').addClass('valid').closest('.control-group').removeClass('error').addClass('success')
-    submitHandler: (form) ->
-      console.log "Submitting form: ", form


Added error handling for JSON and YAML parsing.

--- SwaggerImporter.coffee
+++ SwaggerImporter.coffee
@@ -144,9 +144,14 @@
         try
           # Try JSON parse
           swaggerCollection = JSON.parse string
-        catch error
-          # Try YAML parse
-          swaggerCollection = yaml.load string
+        catch jsonParseError
+          try
+            # Try YAML parse
+            swaggerCollection = yaml.load string
+          catch yamlParseError
+            console.log jsonParseError
+            console.log yamlParseError
+            throw new Error "Invalid Swagger file format (invalid JSON or YAML file)"

         schema = readFile "schema.json"
         valid = tv4.validate swaggerCollection, JSON.parse(schema)


enable packs by default for new docs

enable packs by default for new docs

--- services/track-changes/app/coffee/UpdatesManager.coffee
+++ services/track-changes/app/coffee/UpdatesManager.coffee
@@ -47,10 +47,9 @@
 			if rawUpdates.length == 0
 				return callback()

-			if temporary and (not lastCompressedUpdate? or lastCompressedUpdate.pack?) # handle pack append as a special case
+			if (not lastCompressedUpdate?) or lastCompressedUpdate.pack? # handle pack append as a special case
 				UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback
 			else #use the existing op code
-				lastCompressedUpdate = null if lastCompressedUpdate?.pack? # can't handle packs with op code
 				UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback

 	_updatePack: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) ->


Code cleanup.

Code cleanup.

--- client/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -23,39 +23,6 @@
       @container.$().scrollLeft 0
     @addSubView @container

-    @sessionBox = new KDView
-      cssClass: "kddialogview"
-      position:
-        top: 10
-        left: 10
-    @sessionBox.hide()
-    @addSubView @sessionBox
-
-    label = new KDLabelView
-      title: "Select session:"
-    @sessionBox.addSubView label
-
-    @sessionList = new KDListView
-      itemClass: WebTermSessionItem
-    @sessionBox.addSubView @sessionList
-
-    label = new KDLabelView
-      title: "Create session with name:"
-    @sessionBox.addSubView label
-
-    sessionNameInput = new KDInputView
-      label: label
-      defaultValue: (new Date).format "yyyy-mm-dd HH:MM:ss"
-    @sessionBox.addSubView sessionNameInput
-
-    createSessionButton = new KDButtonView
-      title: "Create"
-      callback: =>
-        @sessionBox.hide()
-        @terminal.createSession sessionNameInput.getValue()
-        @setKeyView()
-    @sessionBox.addSubView createSessionButton
-
     @terminal = new WebTerm.Terminal @container.$()

     if @options.advancedSettings
@@ -74,24 +41,7 @@

     @terminal.sessionEndedCallback = (sessions) =>
       @emit "WebTerm.terminated"
-      return

-      @server.getSessions (sessions) =>
-        keys = Object.keys sessions
-        keys.sort (a, b) ->
-          if sessions[a] < sessions[b]
-            -1
-          else if sessions[a] > sessions[b]
-            1
-          else
-            0
-        @sessionList.empty()
-        for key in keys
-          @sessionList.addItem
-            id: parseInt(key)
-            name: sessions[key]
-            mainView: this
-        @sessionBox.show()
     @terminal.setTitleCallback = (title) =>
       #@tabPane.setTitle title

@@ -228,23 +178,7 @@
     @terminal.windowDidResize()

   getAdvancedSettingsMenuItems:->
-
     settings      :
       type        : 'customView'
       view        : new WebtermSettingsView
         delegate  : @
-
-
-class WebTermSessionItem extends KDListItemView
-  constructor: (options = {},data) ->
-    super options, data
-
-  partial: (data) ->
-    link = $(document.createElement("a"))
-    link.text data.name
-    link.attr "href", "#"
-    link.bind "click", (event) =>
-      data.mainView.sessionBox.hide()
-      data.mainView.terminal.joinSession data.id
-      event.preventDefault()
-    link


Version 2014-10-28

Version 2014-10-28

--- coffee/hatemile/implementation/AccessibleShortcutImpl.coffee
+++ coffee/hatemile/implementation/AccessibleShortcutImpl.coffee
@@ -198,7 +198,7 @@
 		if isEmpty(items)
 			alert(@textWithoutShortcut)
 			return
-		text = @textShortcut
+		text = @textShortcuts
 		for item in items
 			text += "\n*#{item.getTextContent()}"
 		alert(text)
@@ -232,4 +232,4 @@
 		for element in elements
 			if not element.hasAttribute(@dataIgnore)
 				@fixShortcut(element)
-		return+		return


fix danbooru timestamps

fix danbooru timestamps

--- scripts/preview/danbooru.coffee
+++ scripts/preview/danbooru.coffee
@@ -28,7 +28,7 @@
           dimensions: [post.height, post.width]
           size: post.file_size
           score: post.score
-          timestamp: new Date(post.created_at.s)
+          timestamp: new Date(post.created_at.s.seconds() + post.created_at.n.nanoseconds())
           author: {name: post.author, uri: "#{booru}/user/show/#{post.creator_id}"}
           source: {name: post.source?.replace(///^ \w+ :// ///, ''), uri: post.source}
     else


[BUGFIX] use includes

[BUGFIX] use includes

--- addon/services/tooltip-box-manager.coffee
+++ addon/services/tooltip-box-manager.coffee
@@ -136,7 +136,7 @@
     return pop

   addFromView: (view, type, object) ->
-    unless view.attributeBindings.contains(instance.attribute)
+    unless view.attributeBindings.includes(instance.attribute)
       console.warn('TooltipBoxManager.addFromView: You need to add "TooltipBoxManager.attribute" to the attributeBindings!')
       return


Use CommandRegistry::dispatch

Use CommandRegistry::dispatch

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -939,7 +939,7 @@

       describe "when nothing is selected", ->
         it "does nothing", ->
-          root.trigger 'tree-view:open-selected-entry'
+          atom.commands.dispatch(treeView.element, 'tree-view:open-selected-entry')
           expect(atom.workspace.getActivePaneItem()).toBeUndefined()

   describe "file modification", ->


Changed block execution so it will find a block directly before or after the cursor first before looking to see if it is in a block.

--- lib/editor-utils.coffee
+++ lib/editor-utils.coffee
@@ -54,18 +54,45 @@
           result.stop()
     endPos

+  # Determines if the cursor is directly after a closed block. If it is returns
+  # the text of that block
+  directlyAfterBlockText: (editor)->
+    pos = editor.getCursorBufferPosition()
+    if pos.column > 0
+      previousPos = new Point(pos.row, pos.column-1)
+      previousChar = editor.getTextInBufferRange(new Range(previousPos, pos))
+      if [")","}","]"].indexOf(previousChar) >= 0
+        if startPos = @findBlockStartPosition(editor, previousPos)
+          editor.getTextInBufferRange(new Range(startPos, pos))
+
+  # Determines if the cursor is directly before a block opening. If it is returns
+  # the text of that block
+  directlyBeforeBlockText: (editor)->
+    pos = editor.getCursorBufferPosition()
+    subsequentPos = pos.translate(new Point(0, 1))
+    afterChar = editor.getTextInBufferRange(new Range(pos, subsequentPos))
+    if ["(","{","["].indexOf(afterChar) >= 0
+      if endPos = @findBlockEndPosition(editor, subsequentPos)
+        closingPos = endPos.translate(new Point(0, 1))
+        editor.getTextInBufferRange(new Range(pos, closingPos))
+
   # If the cursor is located in a Clojure block (in parentheses, brackets, or
   # braces) or next to one returns the text of that block.
   getCursorInBlockText: (editor)->
-    pos = editor.getCursorBufferPosition()
-    startPos = @findBlockStartPosition(editor, pos)
-    endPos = @findBlockEndPosition(editor, pos)
+    if text = @directlyAfterBlockText(editor)
+      text
+    else if text = @directlyBeforeBlockText(editor)
+      text
+    else
+      pos = editor.getCursorBufferPosition()
+      startPos = @findBlockStartPosition(editor, pos)
+      endPos = @findBlockEndPosition(editor, pos)

-    if startPos && endPos
-      closingPos = endPos.translate(new Point(0, 1))
+      if startPos && endPos
+        closingPos = endPos.translate(new Point(0, 1))

-      # Note that this will if used instead of executing the code implement expand selection on
-      # repeated executions
-      #editor.setSelectedBufferRange(new Range(startPos, closingPos))
+        # Note that this will if used instead of executing the code implement expand selection on
+        # repeated executions
+        #editor.setSelectedBufferRange(new Range(startPos, closingPos))

-      editor.getTextInBufferRange(new Range(startPos, closingPos))
+        editor.getTextInBufferRange(new Range(startPos, closingPos))


Push the changes I never pushed.

Push the changes I never pushed.

--- lib/linter-clang.coffee
+++ lib/linter-clang.coffee
@@ -39,29 +39,23 @@
   provideLinter: ->
     {
       scopes: ['source.c', 'source.cpp', 'source.objc', 'source.objcpp']
-      scope: 'file'
       lint: @lint
-      lintOnFly: false
     }

-  lint: (TextEditor, TextBuffer) ->
-    CP = require 'child_process'
-    Path = require 'path'
+  lint: (TextEditor) ->
+    cp = require 'child_process'
+    path = require 'path'
     XRegExp = require('xregexp').XRegExp

-    regex = XRegExp('(?<file>.+):(?<line>\\d+):(?<col>\\d+):(\{(?<lineStart>\\d+):(?<colStart>\\d+)\\-(?<lineEnd>\\d+):(?<colEnd>\\d+)\}.*:)? ((?<error>(?:fatal )?error)|(?<warning>warning)): (?<message>.*)')
+    regex = XRegExp('(?<file>.+):(?<line>\\d+):(?<col>\\d+):(\{(?<lineStart>\\d+):(?<colStart>\\d+)\\-(?<lineEnd>\\d+):(?<colEnd>\\d+)\}.*:)? (?<type>\\w+): (?<message>.*)')

     return new Promise (Resolve) ->
       filePath = TextEditor.getPath()
       if filePath # Files that have not be saved
-        if TextEditor.getGrammar().name == 'C++'
-          @language = 'c++'
-        if TextEditor.getGrammar().name == 'Objective-C++'
-          @language = 'objective-c++'
-        if TextEditor.getGrammar().name == 'C'
-          @language = 'c'
-        if TextEditor.getGrammar().name == 'Objective-C'
-          @language = 'objective-c'
+        @language = 'c++' if TextEditor.getGrammar().name == 'C++'
+        @language = 'objective-c++' if TextEditor.getGrammar().name == 'Objective-C++'
+        @language = 'c' if TextEditor.getGrammar().name == 'C'
+        @language = 'objective-c' if TextEditor.getGrammar().name == 'Objective-C'

         cmd = atom.config.get 'linter-clang.command'
         cmd = "#{cmd} -fsyntax-only"
@@ -70,12 +64,12 @@
         cmd = "#{cmd} -fdiagnostics-print-source-range-info"
         cmd = "#{cmd} -fexceptions"
         cmd = "#{cmd} -x#{@language}"
-        cmd = "#{cmd} #{Path.basename(filePath)}"
+        cmd = "#{cmd} #{path.basename(filePath)}"
         Data = []

-        Process = CP.exec(cmd, {cwd: Path.dirname(filePath)})
-        Process.stderr.on 'data', (data) -> Data.push(data.toString())
-        Process.on 'close', ->
+        process = cp.exec(cmd, {cwd: path.dirname(filePath)})
+        process.stderr.on 'data', (data) -> Data.push(data.toString())
+        process.on 'close', ->
           Content = []
           for line in Data
             Content.push XRegExp.exec(line, regex)
@@ -83,18 +77,12 @@
           ToReturn = []
           Content.forEach (regex) ->
             if regex
-              if regex.error
-                ToReturn.push(
-                  type: 'error',
-                  message: regex.message,
-                  file: filePath
-                  position: [[regex.line, regex.column], [regex.line, regex.column]]
-                )
-              if regex.warning
-                ToReturn.push(
-                  type: 'warning',
-                  message: regex.message,
-                  file: filePath
-                  position: [[regex.line, regex.col], [regex.line, regex.col]]
-                )
+              console.log "linter-clang file: #{regex.file}" if atom.inDevMode()
+              console.log "linter-clang type: #{regex.type}" if atom.inDevMode()
+              ToReturn.push(
+                type: regex.type,
+                message: regex.message,
+                file: path.join(path.dirname(filePath), regex.file).normalize()
+                position: [[regex.line, regex.column], [regex.line, regex.column]]
+              )
           Resolve(ToReturn)


:art: Make linter-views work with latest bottom-container API

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -26,7 +26,8 @@
       @renderBubble()
       @renderCount()
       @panel.refresh(@state.scope)
-    @subscriptions.add @bottomContainer.onDidChangeTab =>
+    @subscriptions.add @bottomContainer.onDidChangeTab (scope) =>
+      @state.scope = scope
       atom.config.set('linter.showErrorPanel', true)
       @panel.refresh(@state.scope)
     @subscriptions.add @bottomContainer.onShouldTogglePanel ->


:art: Use array instead of ES6 Map for Tabs

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -12,33 +12,26 @@
     @markers = []
     @statusTiles = []

-    @tabs = new Map
-    @tabs.set 'line', new BottomTab()
-    @tabs.set 'file', new BottomTab()
-    @tabs.set 'project', new BottomTab()
+    @tabs = [] # Array has methods that we need to perform certain operations, map won't be a good fit
+    @tabs['line'] = new BottomTab()
+    @tabs['file'] = new BottomTab()
+    @tabs['project'] = new BottomTab()

     @panel = document.createElement 'div'
     @bubble = null
     @bottomStatus = new BottomStatus()

-    @tabs.get('line').initialize 'Line', => @changeTab('line')
-    @tabs.get('file').initialize 'File', => @changeTab('file')
-    @tabs.get('project').initialize 'Project', => @changeTab('project')
+    @tabs['line'].initialize 'Line', => @changeTab('line')
+    @tabs['file'].initialize 'File', => @changeTab('file')
+    @tabs['project'].initialize 'Project', => @changeTab('project')

     @bottomStatus.initialize()
     @bottomStatus.addEventListener 'click', ->
       atom.commands.dispatch atom.views.getView(atom.workspace), 'linter:next-error'
     @panelWorkspace = atom.workspace.addBottomPanel item: @panel, visible: false

-    # Set default tab
-    visibleTabs = @getVisibleTabs()
-
-    @scope = atom.config.get('linter.defaultErrorTab', 'File')?.toLowerCase()
-    if not visibleTabs.has(@scope)
-      @scope = visibleTabs[0]
-
+    @scope = atom.config.get('linter.defaultErrorTab').toLowerCase()
     @tabs.forEach (tab, key) =>
-      tab.visible = false
       tab.active = @scope is key

     @panel.id = 'linter-panel'
@@ -93,8 +86,8 @@
     @updateLineMessages()

     @renderPanel()
-    @tabs.get('file').count = counts.file
-    @tabs.get('project').count = counts.project
+    @tabs['file'].count = counts.file
+    @tabs['project'].count = counts.project
     @bottomStatus.count = counts.project

   updateTabs: ->
@@ -142,18 +135,18 @@
       @messages.forEach (message) =>
         if message.currentFile and message.range?.intersectsRow @currentLine
           @lineMessages.push message
-      @tabs.get('line').count = @lineMessages.length
+      @tabs['line'].count = @lineMessages.length

   # This method is called when we get the status-bar service
   attachBottom: (statusBar) ->
     @statusTiles.push statusBar.addLeftTile
-      item: @tabs.get('line'),
+      item: @tabs['line'],
       priority: -1002
     @statusTiles.push statusBar.addLeftTile
-      item: @tabs.get('file'),
+      item: @tabs['file'],
       priority: -1001
     @statusTiles.push statusBar.addLeftTile
-      item: @tabs.get('project'),
+      item: @tabs['project'],
       priority: -1000
     statusIconPosition = atom.config.get('linter.statusIconPosition')
     @statusTiles.push statusBar["add#{statusIconPosition}Tile"]
@@ -170,9 +163,9 @@
       statusTile.destroy()

   changeTab: (Tab) ->
-    if @getActiveTabKey() is Tab
+    if @scope is Tab.toLowerCase()
       @showPanel = not @showPanel
-      @tabs.forEach (tab, key) -> tab.active = false
+      @tabs.forEach (tab)-> tab.active = false
     else
       @showPanel = true
       @scope = Tab
@@ -180,13 +173,15 @@
       @renderPanel()
     @setShowPanel @showPanel

+  # TODO: Remove me and use @scope instead
   getActiveTabKey: ->
     activeKey = null
     @tabs.forEach (tab, key) -> activeKey = key if tab.active
     return activeKey

+  # TODO: Remove me and use @scope instead
   getActiveTab: ->
-    @tabs.entries().find (tab) -> tab.active
+    @tabs.find (tab) -> tab.active

   getVisibleTabs: ->
     toReturn = new Set


server: add an endpoint to create crsf token

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -17,6 +17,7 @@
 { error_500 }         = require './helpers'
 { generateHumanstxt } = require './humanstxt'
 csrf                  = require './csrf'
+setCrsfToken          = require './setcsrftoken'
 { NodejsProfiler }    = require 'koding-datadog'

 do ->
@@ -60,6 +61,8 @@
 # routes ordered as before no particular structure

 app.post '/-/teams/validate-token'               , require './handlers/checktoken'
+app.post '/-/teams/allow'                        , setCrsfToken, (req, res) ->
+  res.json { token: req.pendingCookies._csrf }
 app.post '/-/teams/create'                       , csrf,   require './handlers/createteam'
 app.post '/-/teams/join'                         , csrf,   require './handlers/jointeam'
 app.post '/-/teams/early-access'                 , require './handlers/earlyaccess'


server: ping socialpi, newkontrol, socialworker, broker in healthcheck

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -19,6 +19,7 @@
 webPort = argv.p ? webserver.port
 koding  = require './bongo'
 Crawler = require '../crawler'
+{dash}  = require 'bongo'

 _          = require 'underscore'
 async      = require 'async'
@@ -252,7 +253,28 @@
   res.redirect 301, '/Activity'

 app.get "/-/healthCheck", (req, res) ->
-  res.jsonp(result:1)
+  {socialapi, newkontrol} = KONFIG
+  {socialApiUri, broker} = KONFIG.client.runtimeOptions
+
+  errs = []
+  urls = [
+    socialapi.proxyUrl
+    newkontrol.url
+    socialApiUri
+    broker.uri
+  ]
+
+  urlFns = urls.map (url)->->
+    request url, (err, resp, body)->
+      errs.push({ url, err })  if err?
+      urlFns.fin()
+
+  dash urlFns, ->
+    if Object.keys(errs).length > 0
+      result = errs
+    else result = 200
+
+    res.jsonp({result})

 app.get "/-/version", (req, res) ->
   res.jsonp(version:KONFIG.version)


Update score model

Update score model

--- lib/models/score.coffee
+++ lib/models/score.coffee
@@ -4,6 +4,7 @@
 scoreSchema = mongoose.Schema
     team: { type: Number, ref: 'Team' }
     score: Number
+    updatedAt: Date

 scoreSchema.plugin autoIncrement.plugin, model: 'Score', startAt: 1
 module.exports = mongoose.model 'Score', scoreSchema


Server: setting cookies by forcing the path to root

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -313,7 +313,7 @@

         return

-      res.cookie 'clientId', info.replacementToken
+      res.cookie 'clientId', info.replacementToken, path : '/'
       return res.status(200).send 'User is logged in!'

@@ -345,7 +345,7 @@
     JUser.convert client, req.body, (err, result) ->
       return res.status(400).send err.message  if err?

-      res.cookie 'clientId', result.newToken
+      res.cookie 'clientId', result.newToken, path : '/'
       # handle the request as an XHR response:
       return res.status(200).end() if req.xhr
       # handle the request with an HTTP redirect:
@@ -361,7 +361,7 @@

   JUser.login clientId, { username, password }, (err, info) ->
     return res.status(403).send err.message  if err?
-    res.cookie 'clientId', info.replacementToken
+    res.cookie 'clientId', info.replacementToken, path : '/'
     res.status(200).end()

 app.post "/:name?/Recover", (req, res) ->


update looks

update looks

--- lib/module-basic.coffee
+++ lib/module-basic.coffee
@@ -3,38 +3,63 @@
   @

 ModuleBasic::build = () ->
-  $box = $('
');
-  $('
')
+  profile = @module.profile
+  $inner = $('
');
+  $section = $('
').append($inner)
+
+  $('
')
+    .addClass('.wanigui-module-name')
     .text @profile.name
-    .appendTo $box
-  $box.append @instrument.build() if @instrument
+    .appendTo $inner
+  if profile.type == 'synth'
+    $inner.addClass('wanigui-synth')
+  if @instrument
+    $inst = @instrument.build()
+    $inst.addClass 'wanigui-inst'
+    $inner.append $inst
   for audioParam in @audioParams
-    $box.append audioParam.build()
+    $inner.append audioParam.build()
   for param in @params
-    $box.append param.build()
-  $box
+    $inner.append param.build()
+  $section

 Wanigui.registerModule
   name: 'module-basic'
   create: ModuleBasic
   stylesheet: '''
-.wani-module {
+
+.wanigui-module-wrapper {
+  width: 260px;
+}
+
+.wanigui-module {
   border: 1px solid #888;
   background-color: #808a90;
   color: #fff;
   position: relative;
-  width: 256px;
+  width: 100%;
   margin: 2px;
 }

-.wani-module.synth {
+.wanigui-module-name {
+  display: block;
+  width: 100%;
+  text-align: center;
+}
+
+.wanigui-module.wanigui-synth {
   background-color: #80908a;
 }

-.wani-module h1 {
+.wanigui-module h1 {
   color: #fff;
   padding: 3px 3px 3px 20px;
-  margin: 2px;
+  margin: 8px 2px;;
+  font-size: 16px;
+}
+
+.wanigui-inst {
+  margin: 5px 0;
 }

 .wani-module .js-remove-module {


Fixed typo in plugin creation

Fixed typo in plugin creation

--- cli/plugins/create.coffee
+++ cli/plugins/create.coffee
@@ -24,7 +24,8 @@
 					if not obj?
 						obj = {}
 					obj.name = name
-					jf.writeFile path + 'plugin.json', obj, (err) ->
+					console.log obj
+					jf.writeFile path + '/plugin.json', obj, (err) ->
 						return defer.reject err if err
 						exec 'cd ' + path + '&& git init', (error, stdout, stderr) ->
 							if not error


Fixed corners on fields and buttons in masthead search.

--- app/assets/javascripts/masthead.js.coffee
+++ app/assets/javascripts/masthead.js.coffee
@@ -77,13 +77,13 @@
   hideSearch = ->
     $mastheadSearch.removeClass("expanded")
     $mastheadSearch.find("input").blur()
-    $mastheadSearch.find("div").removeClass("input-append, input-prepend")
+    $mastheadSearch.find("div").removeClass("input-append input-prepend")
     $(document).off 'click.searchForm'

   showSearch = ->
     hideNav() if isNarrow()
     $mastheadSearch.addClass("expanded")
-    $mastheadSearch.find("div").addClass("input-append, input-prepend")
+    $mastheadSearch.find("div").addClass("input-append input-prepend")

     # Close on click outside the searchbox
     $(document).on 'click.searchForm', (event) ->


OX-438 increased comment dom update performance

--- app/assets/javascripts/oxalis/view.coffee
+++ app/assets/javascripts/oxalis/view.coffee
@@ -96,9 +96,14 @@
     commentTab = $("#comment-container")
     commentTab.empty()

+    # DOM container to append all elements at once for increased performance
+    newContent = document.createDocumentFragment()
+
     for comment in comments
-      commentTab.append($('', {"href": "#", "data-nodeid": comment.node, "text": comment.content}))
-      commentTab.append($('
'))
+      newContent.appendChild($('', {"href": "#", "data-nodeid": comment.node, "text": comment.content})[0])
+      newContent.appendChild($('
')[0])
+
+    commentTab.append(newContent)

   updateActiveComment : ->


Add support for block quoted strings

Add support for block quoted strings

--- lib/python-tools.coffee
+++ lib/python-tools.coffee
@@ -90,31 +90,56 @@

     scopeDescriptor = editor.scopeDescriptorForBufferPosition(bufferPosition)
     scopes = scopeDescriptor.getScopesArray()
-    debugger;
-
+
+    block = false
     if "string.quoted.single.single-line.python" in scopes
       delimiter = '\''
-    else if  'string.quoted.double.single-line.python' in scopes
+    else if 'string.quoted.double.single-line.python' in scopes
       delimiter = '"'
+    else if 'string.quoted.double.block.python' in scopes
+      delimiter = '"""'
+      block = true
+    else if 'string.quoted.single.block.python' in scopes
+      delimiter = '\'\'\''
+      block = true
     else
       return

-    start = end = bufferPosition.column
-
-    while line[start] != delimiter
-      start = start - 1
-      if start < 0
-        return
-
-    while line[end] != delimiter
-      end = end + 1
-      if end == line.length
-        return
-
-    editor.setSelectedBufferRange(new Range(
-      new Point(bufferPosition.row, start + 1),
-      new Point(bufferPosition.row, end),
-    ))
+    if not block
+      start = end = bufferPosition.column
+
+      while line[start] != delimiter
+        start = start - 1
+        if start < 0
+          return
+
+      while line[end] != delimiter
+        end = end + 1
+        if end == line.length
+          return
+
+      editor.setSelectedBufferRange(new Range(
+        new Point(bufferPosition.row, start + 1),
+        new Point(bufferPosition.row, end),
+      ))
+    else
+      start = end = bufferPosition.row
+      start_index = end_index = -1
+
+      while start_index == -1
+        start = start - 1
+        line = editor.lineTextForBufferRow(start)
+        start_index = line.indexOf(delimiter)
+
+      while end_index == -1
+        end = end + 1
+        line = editor.lineTextForBufferRow(end)
+        end_index = line.indexOf(delimiter)
+
+      editor.setSelectedBufferRange(new Range(
+        new Point(start, start_index + delimiter.length),
+        new Point(end, end_index),
+      ))

   handleJsonResponse: (response) ->
     console.log "tools.py => #{response}"


payment: transition operations are optional for "nosync" plan subscriptions

--- workers/social/lib/social/models/payment/subscription.coffee
+++ workers/social/lib/social/models/payment/subscription.coffee
@@ -273,7 +273,7 @@
       =>
         newPlan.checkQuota {@usage}, (err) -> queue.next err
       =>
-        operation.call this, (err) -> queue.next err
+        operation?.call this, (err) -> queue.next err
       ->
         newPlan.subscribe paymentMethodId, subOptions, (err, newSub) ->
           newSubscription = newSub
@@ -296,14 +296,16 @@
     options.subOptions =
       startsAt: @getEndDate()

-    options.operation = (continuation) =>
-      @cancel continuation
+    if "nosync" not in @tags
+      options.operation = (continuation) =>
+        @cancel continuation

     @applyTransition options, callback

   upgrade: (options, callback) ->
-    options.operation = (continuation) =>
-      @terminate options.oldPlan, continuation
+    if "nosync" not in @tags
+      options.operation = (continuation) =>
+        @terminate options.oldPlan, continuation

     @applyTransition options, callback


Animation: Fix lone strips not being evaluated outside their bounds.

--- engine/animation.coffee
+++ engine/animation.coffee
@@ -177,8 +177,6 @@
                 strip_pos_reverse = frame_end - @pos
                 blend_factor = clamp(strip_pos/strip.blend_in, 0, 1) * \
                               clamp(strip_pos_reverse/strip.blend_out, 0, 1)
-                if blend_factor < 0.0000000000001
-                    continue
                 if strip.reversed
                     [strip_pos, strip_pos_reverse] = [strip_pos_reverse, strip_pos]
                 switch strip.extrapolation


main_loop.stop enables pause and main_loop.run disables pause.

--- engine/main_loop.coffee
+++ engine/main_loop.coffee
@@ -26,10 +26,12 @@
         requestAnimationFrame @_bound_tick
         not @enabled and @timeout? and console.log 'Main loop running: ' + Math.floor(performance.now())
         @enabled = true
+        @pause_time = 0
         @last_time = performance.now()

     stop: ->
         cancelAnimationFrame @_bound_tick
+        @pause_time = Infinity
         @enabled = false

     pause: (pause_time)->
@@ -108,5 +110,4 @@
         @timeout_timer =  @timeout
         @enabled = true

-
 module.exports = {MainLoop}


#fix 2029 - preventDefault when click milestone edit icon

--- app/coffee/modules/backlog/sprints.coffee
+++ app/coffee/modules/backlog/sprints.coffee
@@ -69,6 +69,8 @@
             $el.find(".sprint-table").slideToggle(slideOptions)

         $el.on "click", ".sprint-name > .icon-edit", (event) ->
+            event.preventDefault()
+
             sprint = $scope.$eval($attrs.tgBacklogSprint)
             $rootscope.$broadcast("sprintform:edit", sprint)


:racehorse: Speed up buffer row adjustment

Changes to a single line in the buffer do not require buffer
row adjustment. Before this change, we were still looping over
all the symbols and adding `0` to the buffer rows. This means

* Every keystroke, the buffer rows were being ‘adjusted’
* When a newline was entered, the buffer rows were being
  adjusted twice! Once for the newline, and once when
  auto-indent adjusted the indentation.

Running through all these symbols produced was problematic when several large files were open.

--- lib/symbol-store.coffee
+++ lib/symbol-store.coffee
@@ -145,6 +145,7 @@
   adjustBufferRows: (editor, oldRange, newRange) ->
     adjustmentStartRow = oldRange.end.row + 1
     adjustmentDelta = newRange.getRowCount() - oldRange.getRowCount()
+    return if adjustmentDelta is 0
     for key, symbol of @symbolMap
       symbol.adjustBufferRows(editor.getBuffer(), adjustmentStartRow, adjustmentDelta)
     return


Add missing the

Add missing the

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -232,7 +232,7 @@
           activeItemIndex = activePane.getActiveItemIndex()
           @moveItemBetweenPanes(activePane, activeItemIndex, toPane, toIndex, item)

-          # Let window where the drag started know that the tab was dropped
+          # Let the window where the drag started know that the tab was dropped
           BrowserIpc.sendChannel(fromProcessId, fromRoutingId, 'tab:dropped', fromIndex, fromPaneIndex)

         atom.focus()


linking project issues

linking project issues

--- src/Models/Projects.coffee
+++ src/Models/Projects.coffee
@@ -4,7 +4,7 @@
   init: =>
     @members = @load 'ProjectMembers'
     @hooks =   @load 'ProjectHooks'
-    #@issues =  @load 'ProjectIssues'
+    @issues =  @load 'ProjectIssues'

   all: (params = {}, fn = null) =>
     if 'function' is typeof params


better behavior for adding modules with the same name - i.e. when the modules are re-loaded. (behavor: replaces the old module) this specifically makes mocha testing work with the "-w" option

--- src/Runtime/Neptune.coffee
+++ src/Runtime/Neptune.coffee
@@ -84,8 +84,12 @@
   @addModules: (map) ->

     for name, module of map
-      @moduleNames.push name
-      @modules.push module
+      if @[name]
+        @modules[@modules.indexOf @[name]] = module
+      else
+        @moduleNames.push name
+        @modules.push module
+
       @_setChildNamespaceProps name, module
       @[name] = module unless name.match /^-/
     @


Typo fix

Typo fix

--- components/Recurse.coffee
+++ components/Recurse.coffee
@@ -12,7 +12,7 @@
   c.outPorts.add 'recurse',
     datatype: 'object'
   c.recurses = {}
-  c.tearDown (callback) ->
+  c.tearDown = (callback) ->
     c.recurses = {}
     do callback
   c.process (input, output) ->


Inline block section

Inline block section

--- lib/ui-demo-view.coffee
+++ lib/ui-demo-view.coffee
@@ -67,6 +67,32 @@
             @div class: 'editor mini editor-colors', 'Something else you typed...'
           @div class: 'block', =>
             @button class: 'btn', 'Do it'
+        '''
+
+        @h2 '.inline-block'
+        @p 'Sometimes you need to separate components horizontally.'
+        @exampleCode '''
+          @div class: 'block', =>
+            @label 'This is a thing with stuff in it'
+            @div =>
+              @div class: 'inline-block editor mini editor-colors', 'Something you typed...'
+              @div class: 'inline-block btn-group', =>
+                @button class: 'btn', 'One'
+                @button class: 'btn', 'Two'
+
+          @div class: 'block', =>
+            @button class: 'inline-block btn', 'Do it'
+            @button class: 'inline-block btn', 'Another'
+            @button class: 'inline-block btn', 'OMG again'
+        '''
+
+        @h2 '.inline-block-tight'
+        @p 'You might want things to be a little closer to each other.'
+        @exampleCode '''
+          @div class: 'block', =>
+            @button class: 'inline-block-tight btn', 'Do it'
+            @button class: 'inline-block-tight btn', 'Another'
+            @button class: 'inline-block-tight btn', 'OMG again'
         '''

       @section class: 'bordered', 'data-name': 'git', =>


Call new GET /db/user/:id/course-instances route

Previously, was using a similar, older GET /db/user/:id/course_instances
route that was missing new logic.

--- app/views/courses/CoursesView.coco.coffee
+++ app/views/courses/CoursesView.coco.coffee
@@ -58,7 +58,7 @@
     super()

     @classCodeQueryVar = utils.getQueryVariable('_cc', false)
-    @courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
+    @courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course-instances", model: CourseInstance})
     @courseInstances.comparator = (ci) -> return parseInt(ci.get('classroomID'), 16) + utils.orderedCourseIDs.indexOf ci.get('courseID')
     @listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
     @supermodel.loadCollection(@courseInstances, { cache: false })


Clear listeners on process object when unloading.

The process object is created under node context, and it will live
through the reloading, so we are responsible for clearing the listeners
to make sure resources are not leaked.

--- renderer/api/lib/ipc.coffee
+++ renderer/api/lib/ipc.coffee
@@ -5,6 +5,9 @@
   constructor: ->
     process.on 'ATOM_INTERNAL_MESSAGE', (args...) =>
       @emit(args...)
+
+    window.addEventListener 'unload', (event) ->
+      process.removeAllListeners 'ATOM_INTERNAL_MESSAGE'

   send: (args...) ->
     ipc.send('ATOM_INTERNAL_MESSAGE', 'message', args...)


test if vent is defened

test if vent is defened

--- BackofficeBundle/Resources/public/coffee/setUpCallAjax.coffee
+++ BackofficeBundle/Resources/public/coffee/setUpCallAjax.coffee
@@ -1,10 +1,11 @@
 $(document).ready ->
   $.ajaxSetup
     beforeSend: ->
-      if event.type == "submit"
-        displayLoader($('.submit_form', event.currentTarget).parent())
-      else
-        displayLoader($('.submit_form').parent()) if event.target.type == "submit"
+      if event != undefined && event.type != undefined
+        if event.type == "submit"
+          displayLoader($('.submit_form', event.currentTarget).parent())
+        else
+          displayLoader($('.submit_form').parent()) if event.target.type == "submit"
   $(document).ajaxSuccess (event, xhr, settings) ->
     if isLoginForm(xhr.responseText)
       redirectToLogin()


add support for behaviors

add support for behaviors

--- vendor/assets/javascripts/nifty/dialog.coffee
+++ vendor/assets/javascripts/nifty/dialog.coffee
@@ -1,26 +1,14 @@
 window.Nifty ||= {}
 window.Nifty.Dialog =

-  # The zindex to start showing dialogs from
+  # The numerical ID to start showing dialogs from
   startingID: 1

   # A callback reference which is run on content set if set.
   onSetContent: null

-  closeTopDialog: ->
-    if $('div.niftyDialog').length
-      $('div.niftyDialog:last').data('closeAction').call()
-
-  # Closes the any overlay which is in place
-  closeOverlay: -> this.closeTopDialog()
-
-  # Complete the opening of a dialog with the given HTML
-  displayDialog: (dialog, content, afterDisplay)->
-    dialog.html(content)
-    dialog.fadeIn('fast')
-    dialog.removeClass 'loading'
-    afterDisplay.call(null, dialog) if afterDisplay?
-    this.onSetContent(null, dialog) if this.onSetContent?
+  # Stores all behaviors
+  behaviors: {}

   # Open a new dialog which will accept a number of possible options.
   #
@@ -40,6 +28,9 @@
   #   offset     => specifies a vertical offset (in px)
   #
   #   class      => the CSS class to assign to this dialog
+  #
+  #   behavior   => the name of a behavior set to be invoked on dialog open/close.
+  #                 Behaviors can be setup using the Nifty.Dialog.addBehavior method.
   #
   open: (options={})->

@@ -70,9 +61,10 @@

     # Set the closing action for the inserted dialog to close dialog
     # and fade out the appropriate overlay
-    insertedDialog.data 'closeAction', ->
-      closeMethod = insertedDialog.data('onClose')
-      closeMethod.call(insertedDialog) if closeMethod
+    insertedDialog.data 'closeAction', =>
+      options.onClose.call(null, insertedDialog, options) if options.onClose?
+      if options.behavior? && behavior = this.behaviors[options.behavior]
+        behavior.onClose.call(null, insertedDialog, options) if behavior.onClose?
       insertedDialog.fadeOut 'fast', -> insertedDialog.remove()
       theOverlay.fadeOut 'fast', -> theOverlay.remove()

@@ -86,18 +78,36 @@
       insertedDialog.addClass 'loading'
       $.ajax
         url: options.url
-        success: (data)=> this.displayDialog(insertedDialog, data, options.afterLoad)
+        success: (data)=> this.displayDialog(insertedDialog, data, options)

     else if options.html?
-      this.displayDialog(insertedDialog, options.html, options.afterLoad)
+      this.displayDialog(insertedDialog, options.html, options)

     else
       # anything else won't work
       console.log "Dialog could not be displayed. Invalid options passed."
       console.log options
       return false
+
+  # Add a behaviour callback which will be executed
+  addBehavior: (options)->
+    if options.name?
+      this.behaviors[options.name] = options
+      true
+    else
+      console.log "Must pass a 'name' option to the addBehavior method."
+      false

-
+  # Complete the opening of a dialog with the given HTML
+  displayDialog: (dialog, content, options={})->
+    dialog.html(content)
+    dialog.fadeIn('fast')
+    dialog.removeClass 'loading'
+    if options.behavior? && behavior = this.behaviors[options.behavior]
+      behavior.onLoad.call(null, dialog, options) if behavior.onLoad?
+    options.afterLoad.call(null, dialog) if options.afterLoad?
+    this.onSetContent(null, dialog) if this.onSetContent?
+
   # This method will replace the contents of the nearest dialog (or the one with the
   # given ID if one is given).
   setContent: (content, id = null)->
@@ -115,4 +125,10 @@
       overlay.fadeOut 'fast', ->
         overlay.remove()
     overlay.fadeIn('fast')
-    +
+  # Closes the top dialgo in the dialog stack
+  closeTopDialog: ->
+    if $('div.niftyDialog').length
+      $('div.niftyDialog:last').data('closeAction').call()
+
+


Material: Default model view matrix var name for custom vertex shaders.

--- engine/material.coffee
+++ engine/material.coffee
@@ -99,6 +99,7 @@

         if @data.vertex
             vs = @data.vertex
+            var_model_view_matrix = 'model_view_matrix'
         else
             {has_normal=true} = @data
             var_model_view_matrix = generator.get_model_view_matrix_name()


include color data

include color data

--- lib/assets/javascripts/jax/mesh.js.coffee
+++ lib/assets/javascripts/jax/mesh.js.coffee
@@ -89,12 +89,13 @@
   draw_mode: GL_POINTS

   toJSON: ->
-    vertices:      (i for i in @data.vertexBuffer)
-    normals:       (i for i in @data.normalBuffer)
-    tangents:      (i for i in @data.tangentBuffer)
-    bitangents:    (i for i in @data.bitangentBuffer)
-    textureCoords: (i for i in @data.textureCoordsBuffer)
-    indices:       (i for i in @data.indexBuffer)
+    vertices:      (parseFloat i.toFixed(6) for i in @data.vertexBuffer)
+    colors:        (parseFloat i.toFixed(6) for i in @data.colorBuffer)
+    textureCoords: (parseFloat i.toFixed(6) for i in @data.textureCoordsBuffer)
+    normals:       (parseFloat i.toFixed(6) for i in @data.normalBuffer)
+    tangents:      (parseFloat i.toFixed(6) for i in @data.tangentBuffer)
+    bitangents:    (parseFloat i.toFixed(6) for i in @data.bitangentBuffer)
+    indices:       (           i            for i in @data.indexBuffer)

   ###


Avoid get user for notifications when is anonymous

--- app/coffee/modules/events.coffee
+++ app/coffee/modules/events.coffee
@@ -88,6 +88,8 @@
             @rootScope.$digest()

     liveNotifications: ->
+        if not @.auth.userData?
+            return
         userId = @.auth.userData.get('id')

         subscribe = () =>


socialworker: remove badge class

socialworker: remove badge class

--- workers/social/lib/social/models/badge.coffee
+++ workers/social/lib/social/models/badge.coffee
@@ -1,236 +0,0 @@
-jraphical = require 'jraphical'
-module.exports = class JBadge extends jraphical.Module
-  {permit}          = require './group/permissionset'
-  {daisy, secure, signature}   = require 'bongo'
-  KodingError       = require '../error'
-
-  @trait __dirname, '../traits/filterable'
-  @trait __dirname, '../traits/protected'
-
-  @share()
-  @set
-    permissions           :
-      'create badge'      : []
-      'delete badge'      : []
-      'edit badge'        : []
-      'assign badge'      : ['moderator']
-      'list badges'       : ['member','moderator']
-      'remove user badge' : ['moderator']
-    schema                :
-      title               : String
-      description         : String
-      rule                : String
-      invisible           :
-        type              : Boolean
-        default           : false
-      iconURL             :
-        type              : String
-      reward              : String
-      role                : String # storing as string because we already use permission title as a relation identifier in relationship modal
-      createdAt           :
-        type              : Date
-        default           : -> new Date
-    sharedEvents          :
-      instance            : []
-      static              : []
-    sharedMethods         :
-      static :
-        listBadges :
-          (signature Object, Object, Function)
-        create :
-          (signature Object, Function)
-        fetchBadgeUsers :
-          (signature String, Object, Function)
-        checkEligibleBadges :
-          (signature Object, Function)
-      instance :
-        modify :
-          (signature Object, Function)
-        deleteBadge :
-          (signature Function)
-        removeBadgeFromUser :
-          (signature Object, Function)
-        assignBadgeBatch :
-          (signature Object, Function)
-
-# We dont assign current roles, because on badge remove, it deletes
-# roles and its users also. If role has users before badge assignment
-# they also will be removed.
-  @create: permit 'create badge',
-    success:(client, badgeData, callback=->)->
-      protectedRoles = ["admin","owner","guest","member"]
-      unless badgeData.role in protectedRoles
-        badge = new JBadge badgeData
-        badge.role = null if badgeData.role is "none"
-        badge.save (err)=>
-          callback err, badge
-      else
-        callback new KodingError 'That role cannot be given from UI', null
-
-  @listBadges: permit 'list badges',
-    success: (client, selector, options, callback=->)->
-      JBadge.some selector, options, callback
-
-  modify: permit 'edit badge',
-    success: (client, formData, callback)->
-      updatedFields =
-        title       : formData.title
-        iconURL     : formData.iconURL
-        description : formData.description
-
-      @update $set  :updatedFields, callback
-
-  deleteBadge : permit 'delete badge',
-    success: (client, callback=->)->
-      # get badge users
-      JBadge.fetchBadgeUsers client, @getId(), {}, (err, accounts)=>
-        return err if err
-        ids = (account.getId() for account in accounts)
-        removeKey = "$in" : ids
-        # remove role that given with that badge
-        jraphical.Relationship.remove {
-          sourceName : "JGroup"
-          as         : @role
-          targetId   : removeKey
-        },(err)=>
-          return err if err
-          # remove badge
-          @remove (err)->
-            return callback err, null
-
-  assignBadgeBatch : permit 'assign badge',
-    success: (client, accountIds, callback)->
-      if accountIds.length > 0
-        JAccount = require './account'
-        queue    = accountIds.map (id) =>=>
-          JAccount.one "_id" : id, (err, account)=>
-            return queue.next() if err or not account
-            jraphical.Relationship.one
-              as         : "badge"
-              targetId   : @getId()
-              sourceId   : account.getId()
-            , (err, rel) =>
-                return queue.next() if err or rel
-                account.addBadge this, (err, badge)=>
-                  return queue.next() if err or not badge
-                  groupName = client.context.group
-                  # find the group id
-                  JGroup = require './group'
-                  JGroup.one {slug : groupName}, (err, group)=>
-                    return queue.next() if err or @role is undefined
-                    new jraphical.Relationship
-                      targetName  : 'JAccount'
-                      targetId    : account.getId()
-                      sourceName  : 'JGroup'
-                      sourceId    : group.getId()
-                      as          : @role
-                    .save (err)=>
-                      queue.next()
-        queue.push -> callback null
-        daisy queue
-
-  removeBadgeFromUser : permit 'remove user badge',
-    success: (client, user, callback)->
-      {Relationship} = jraphical
-      Relationship.remove {
-        targetId   : @getId()
-        targetName : 'JBadge'
-        sourceId   : user._id
-        sourceName : 'JAccount'
-        as         : "badge"
-      }, (err)=>
-        return callback err if err
-        Relationship.remove {
-          targetId   : user._id
-          targetName : 'JAccount'
-          sourceName : "JGroup"
-          as         : @role
-        }, callback
-
-  @findAccounts : (cursor, items, callback)->
-    cursor.nextObject (err, rel) =>
-      if err
-        callback items
-      else if rel
-        items.push rel.sourceId
-        @findAccounts cursor, items, callback
-      else
-        callback items
-
-  @fetchBadgeUsers:permit 'list badges',
-    success: (client, badgeId, options, callback)->
-      selector =
-        targetName : "JBadge"
-        targetId   : badgeId
-        as         : "badge"
-        sourceName : "JAccount"
-
-      jraphical.Relationship.cursor selector, options, (err, cursor)=>
-        return callback err if err
-        @findAccounts cursor, [], (items) ->
-          JAccount = require './account'
-          JAccount.some { "_id": { "$in": items } }, {}, callback
-
-  @ruleSplit:(rule, userCounts)->
-    operators =
-        '>': (badgeValue, userValue)-> userValue > badgeValue
-        '<': (badgeValue, userValue)-> userValue < badgeValue
-    #TODO : should use regular expressions
-    actionPos = rule.search /[\<\>\=]/ # find the position of "<,>,=" in rule
-    action    = rule.substr actionPos, 1
-    property  = rule.substr 0, actionPos
-    propVal   = rule.substr actionPos + 1
-
-    operators[action] propVal, userCounts[property]
-
-  @checkRules : (rules, userCounts)->
-    ruleArray = rules.split "+"
-    if ruleArray.length is 1
-      return @ruleSplit rules, userCounts
-    else
-      for rule in ruleArray
-        return no  unless @ruleSplit rule, userCounts
-      return yes
-
-
-  @checkEligibleBadges:secure (client, options, callback) ->
-    @account  = client.connection.delegate
-    #get user badges
-    @account.fetchBadges (err, userBadges)=>
-      return err if err
-      badgeIds = []
-      if userBadges then badgeIds = (userBadge.getId() for userBadge in userBadges)
-      # find badges that users can gain and not already gained
-      ruleSelector =
-        "rule"     :
-          "$regex" : options.badgeItem
-        "_id"      : $nin:badgeIds
-      # 50 is hardcoded, we may need to get this value from client.
-      JBadge.some ruleSelector,{limit:50}, (err, badges)=>
-        return err if err
-        badgesGained  = []
-        queue = badges.map (badge) =>=>
-          return queue.next() if not @checkRules badge.rule, @account.counts
-          @account.addBadge badge, (err, o)->
-            return queue.next() if err
-            JGroup = require './group'
-            {context:{group:groupName}} = client
-            JGroup.one {slug : groupName}, (err, group)=>
-              return queue.next() if err or not group
-              user = client.connection.delegate
-              if badge.role
-                new jraphical.Relationship
-                  targetName  : 'JAccount'
-                  targetId    : user.getId()
-                  sourceName  : 'JGroup'
-                  sourceId    : group.getId()
-                  as          : badge.role
-                .save (err)=>
-                  return queue.next() if err
-                  badgesGained.push badge
-                  queue.next()
-              else
-                badgesGained.push badge
-                queue.next()
-        queue.push -> callback null, badgesGained
-        daisy queue


added kite call limit count into database model

--- workers/social/lib/social/models/kites.coffee
+++ workers/social/lib/social/models/kites.coffee
@@ -34,12 +34,13 @@
       kiteName      :
         type        : String
         required    : yes
-      secret        :
+      key           :
         type        : String
         required    : no
-      key          :
-        type        : String
+      count         :
+        type        : Number
         required    : no
+
     relationships   :->
       JAccount = require './account'
       creator       :
@@ -51,24 +52,20 @@

     crypto = require 'crypto'

-    crypto.randomBytes 12, (ex1, key) ->
-      crypto.randomBytes 12, (ex2, secret) ->
-        #todo remove prefixes
-        apiKey    = "key-" + key.toString 'hex'
-        apiSecret = "secret-" + secret.toString 'hex'
-        data.key = apiKey
-        data.secret = apiSecret
+    crypto.randomBytes 32, (ex1, key) ->
+      apiKey   = key.toString 'hex'
+      data.key = apiKey

-        kite = new JKite data
-        kite.save (err)=>
-          if err
-            callback err
-          else
-            kite.addCreator delegate, (err)=>
-              if err
-                callback err
-              else
-                callback null, kite
+      kite = new JKite data
+      kite.save (err)=>
+        if err
+          callback err
+        else
+          kite.addCreator delegate, (err)=>
+            if err
+              callback err
+            else
+              callback null, kite

   @get = secure ({connection:{delegate}}, data, callback)->
@@ -90,8 +87,8 @@
     {limit, skip, sort}  = data

     @one {
-      key    : data.key
-      secret : data.secret
+      key      : data.key
+      kiteName : data.kiteName
     }, (err, data)=>
       if err
         callback err
@@ -119,9 +116,6 @@
             if err
               callback err
               fin()
-            else if not kite
-              console.warn "Source does not exists:", root.sourceName, root.sourceId
-              fin()
             else
               teasers.push(kite)
               fin()


Fix field type

Fix field type

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -73,7 +73,7 @@
       stackRevision      : String

       machines           :
-        type             : [ ObjectId ]
+        type             : Object
         default          : -> []

       config             : Object
@@ -370,5 +370,3 @@
             stackRevisionErrors.TEMPLATEDIFFERENT

         callback null, { status, machineCount: template.machines.length }
-
-


update controller to fetch the new partner city collections and sharify them

--- apps/galleries_institutions/routes.coffee
+++ apps/galleries_institutions/routes.coffee
@@ -4,8 +4,9 @@
 qs = require 'qs'
 metaphysics = require '../../lib/metaphysics'
 { API_URL } = require('sharify').data
-{ FeaturedCities } = require 'places'
 Partners = require '../../collections/partners'
+PartnerCities = require '../../collections/partner_cities'
+PartnerFeaturedCities = require '../../collections/partner_featured_cities'
 Profiles = require '../../collections/profiles'
 ViewHelpers = require './components/partner_cell/view_helpers'
 query = require './queries/partner_categories_query'
@@ -26,9 +27,13 @@
   type = mapType[req.params.type]
   searchParams = _.pick(req.query, 'location', 'category')
   params = _.extend type: type, searchParams
+  partnerCities = new PartnerCities()
+  partnerFeaturedCities = new PartnerFeaturedCities()

   Q.all([
     fetchPrimaryCarousel(params)
+    partnerCities.fetch()
+    partnerFeaturedCities.fetch()
     metaphysics(
       query: query
       variables: _.extend category_type: type.toUpperCase(), type: partnerTypes[type]
@@ -39,8 +44,10 @@
           partners: mergeBuckets(category.primary, category.secondary),
           facet: 'category'

-  ]).spread (profiles, categories) ->
+  ]).spread (profiles, partnerCities, partnerFeaturedCities, categories) ->
     res.locals.sd.MAIN_PROFILES = profiles.toJSON()
+    res.locals.sd.PARTNER_CITIES = partnerCities
+    res.locals.sd.PARTNER_FEATURED_CITIES = partnerFeaturedCities
     res.locals.sd.CATEGORIES = _.map(categories, (c) -> _.pick c, 'id', 'name')

     res.render 'index',


Fix bug with slow-loading sessions never opening play button

--- app/lib/LevelSetupManager.coffee
+++ app/lib/LevelSetupManager.coffee
@@ -28,24 +28,24 @@
     @level = @supermodel.loadModel(@level, 'level').model
     if @level.loaded then @onLevelSync() else @listenToOnce @level, 'sync', @onLevelSync

+  loadSession: ->
+    sessionURL = "/db/level/#{@options.levelID}/session"
+    #sessionURL += "?team=#{@team}" if @options.team  # TODO: figure out how to get the teams for multiplayer PVP hero style
+    sessionURL += "?course=#{@options.courseID}" if @options.courseID
+    @session = new LevelSession().setURL sessionURL
+    @session = @supermodel.loadModel(@session, 'level_session').model
+    if @session.loaded then @onSessionSync() else @listenToOnce @session, 'sync', @onSessionSync
+
   onLevelSync: ->
     return if @destroyed
     if @waitingToLoadModals
       @waitingToLoadModals = false
       @loadModals()

-  loadSession: ->
-    sessionURL = "/db/level/#{@options.levelID}/session"
-    #sessionURL += "?team=#{@team}" if @options.team  # TODO: figure out how to get the teams for multiplayer PVP hero style
-    sessionURL += "?course=#{@options.courseID}" if @options.courseID
-    @session = new LevelSession().setURL sessionURL
-    onSessionSync = ->
-      return if @destroyed
-      @session.url = -> '/db/level.session/' + @id
-      @fillSessionWithDefaults()
-    @listenToOnce @session, 'sync', onSessionSync
-    @session = @supermodel.loadModel(@session, 'level_session').model
-    onSessionSync.call @ if @session.loaded
+  onSessionSync: ->
+    return if @destroyed
+    @session.url = -> '/db/level.session/' + @id
+    @fillSessionWithDefaults()

   fillSessionWithDefaults: ->
     heroConfig = _.merge {}, me.get('heroConfig'), @session.get('heroConfig')


Fix level borders not being created

Fix level borders not being created

--- app/models/PhysicsManager.coffee
+++ app/models/PhysicsManager.coffee
@@ -144,12 +144,12 @@
     body.CreateFixture fixtureDefinition

   ###
-  @todo does not work
+  @todo movo to TiledMap
   ###
   createLevelBorder: (map) =>
     fixtureDefinition = new FixtureDef()
     fixtureDefinition.shape = new PolygonShape()
-    fixtureDefinition.shape.SetAsBox 0, map.tileheight
+    fixtureDefinition.shape.SetAsBox 0, map.tileheight * map.height
     mapBorder = new BodyDef()
     mapBorder.type = Body.b2_staticBody
     mapBorder.position.x = 0
@@ -159,17 +159,17 @@

     fixtureDefinition = new FixtureDef()
     fixtureDefinition.shape = new PolygonShape()
-    fixtureDefinition.shape.SetAsBox 0, map.tileheight
-    mapBorder = new BodyDef()
-    mapBorder.type = Body.b2_staticBody
-    mapBorder.position.x = map.tilewidth
+    fixtureDefinition.shape.SetAsBox 0, map.tileheight * map.height
+    mapBorder = new BodyDef()
+    mapBorder.type = Body.b2_staticBody
+    mapBorder.position.x = map.tilewidth * map.width
     mapBorder.position.y = 0
     body = @registerBody mapBorder
     body.CreateFixture fixtureDefinition

     fixtureDefinition = new FixtureDef()
     fixtureDefinition.shape = new PolygonShape()
-    fixtureDefinition.shape.SetAsBox map.tilewidth, 0
+    fixtureDefinition.shape.SetAsBox map.tilewidth * map.width, 0
     mapBorder = new BodyDef()
     mapBorder.type = Body.b2_staticBody
     mapBorder.position.x = 0
@@ -179,11 +179,11 @@

     fixtureDefinition = new FixtureDef()
     fixtureDefinition.shape = new PolygonShape()
-    fixtureDefinition.shape.SetAsBox map.tilewidth, 0
+    fixtureDefinition.shape.SetAsBox map.tilewidth * map.width, 0
     mapBorder = new BodyDef()
     mapBorder.type = Body.b2_staticBody
     mapBorder.position.x = 0
-    mapBorder.position.y = map.tileheight
+    mapBorder.position.y = map.tileheight * map.height
     body = @registerBody mapBorder
     body.CreateFixture fixtureDefinition


better scroll sync

better scroll sync

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -256,12 +256,11 @@
       if screenRow == -1
         screenRow = mid

-      if (screenRow >= 0)
-        @scrollToPos(screenRow * @editor.getLineHeightInPixels() - @element.offsetHeight / 2, @editor.getElement())
-        # @editor.getElement().setScrollTop
-
-        # track currnet time to disable onDidChangeScrollTop
-        @editorScrollDelay = Date.now() + 500
+      @scrollToPos(screenRow * @editor.getLineHeightInPixels() - @element.offsetHeight / 2, @editor.getElement())
+      # @editor.getElement().setScrollTop
+
+      # track currnet time to disable onDidChangeScrollTop
+      @editorScrollDelay = Date.now() + 500

   initSettingsEvents: ->
     # settings changed


Recompile

Recompile

--- bin/recipes/config.workshop.template.cson
+++ bin/recipes/config.workshop.template.cson
@@ -1,44 +1,16 @@
-version: "0.1.11"
-notifications:
-  email: {}
-  slack: {}
+version: "0.1.9"
 liquibase:
   working_directory: ""
-  notifications:
-    development:
-      slack: ""
-      email: []
-      events: []
-    staging:
-      slack: ""
-      email: []
-      events: []
-    production:
-      slack: ""
-      email: []
-      events: []
 scriptella:
   working_directory: ""
-  notifications:
-    development:
-      slack: ""
-      email: []
-      events: []
-    staging:
-      slack: ""
-      email: []
-      events: []
-    production:
-      slack: ""
-      email: []
-      events: []
   etl_properties:
-    development:
-      data_working_directory: "{{cwd}}"
-    staging:
-      data_working_directory: "{{cwd}}"
-    production:
-      data_working_directory: "{{cwd}}"
+    environments:
+      development:
+        data_working_directory: "{{cwd}}"
+      staging:
+        data_working_directory: "{{cwd}}"
+      production:
+        data_working_directory: "{{cwd}}"
 cloud:
   asana:
     key: ""


Made test run against empty project

Made test run against empty project

--- spec/atom/root-view-spec.coffee
+++ spec/atom/root-view-spec.coffee
@@ -36,7 +36,7 @@

   describe "focus", ->
     it "can receive focus if there is no active editor, but otherwise hands off focus to the active editor", ->
-      rootView = new RootView(require.resolve 'fixtures')
+      rootView = new RootView({url: require.resolve 'fixtures'})
       rootView.attachToDom()
       expect(rootView).toMatchSelector(':focus')


Don’t assume same token instances in spec now that we use shims

--- spec/tokenized-line-spec.coffee
+++ spec/tokenized-line-spec.coffee
@@ -27,7 +27,7 @@
           for child in scopeTree.children
             ensureValidScopeTree(child, scopeDescriptor.concat([scopeTree.scope]))
         else
-          expect(scopeTree).toBe tokens[tokenIndex++]
+          expect(scopeTree).toEqual tokens[tokenIndex++]
           expect(scopeDescriptor).toEqual scopeTree.scopes

       waitsForPromise ->


Prevent repopulation of already processed markers, leading to duplicated markers when using addMarkers

--- app/assets/javascripts/gmaps4rails/gmaps4rails.base.js.coffee
+++ app/assets/javascripts/gmaps4rails/gmaps4rails.base.js.coffee
@@ -302,38 +302,39 @@
   #create google.maps Markers from data provided by user
   createServiceMarkersFromMarkers : ->
     for marker, index in @markers
-      #extract options, test if value passed or use default
-      Lat = @markers[index].lat
-      Lng = @markers[index].lng
-
-      #alter coordinates if randomize is true
-      if @markers_conf.randomize
-        LatLng = @randomize(Lat, Lng)
-        #retrieve coordinates from the æarray
-        Lat = LatLng[0]
-        Lng = LatLng[1]
-
-      #save object
-      @markers[index].serviceObject = @createMarker
-        "marker_picture":   if @markers[index].picture  then @markers[index].picture else @markers_conf.picture
-        "marker_width":     if @markers[index].width    then @markers[index].width   else @markers_conf.width
-        "marker_height":    if @markers[index].height   then @markers[index].height  else @markers_conf.length
-        "marker_title":     if @markers[index].title    then @markers[index].title   else null
-        "marker_anchor":    if @markers[index].marker_anchor  then @markers[index].marker_anchor  else null
-        "shadow_anchor":    if @markers[index].shadow_anchor  then @markers[index].shadow_anchor  else null
-        "shadow_picture":   if @markers[index].shadow_picture then @markers[index].shadow_picture else null
-        "shadow_width":     if @markers[index].shadow_width   then @markers[index].shadow_width   else null
-        "shadow_height":    if @markers[index].shadow_height  then @markers[index].shadow_height  else null
-        "marker_draggable": if @markers[index].draggable      then @markers[index].draggable      else @markers_conf.draggable
-        "rich_marker":      if @markers[index].rich_marker    then @markers[index].rich_marker    else null
-        "Lat":              Lat
-        "Lng":              Lng
-        "index":            index
-
-      #add infowindowstuff if enabled
-      @createInfoWindow(@markers[index])
-      #create sidebar if enabled
-      @createSidebar(@markers[index])
+      if not @markers[index].serviceObject?
+        #extract options, test if value passed or use default
+        Lat = @markers[index].lat
+        Lng = @markers[index].lng
+
+        #alter coordinates if randomize is true
+        if @markers_conf.randomize
+          LatLng = @randomize(Lat, Lng)
+          #retrieve coordinates from the æarray
+          Lat = LatLng[0]
+          Lng = LatLng[1]
+
+        #save object
+        @markers[index].serviceObject = @createMarker
+          "marker_picture":   if @markers[index].picture  then @markers[index].picture else @markers_conf.picture
+          "marker_width":     if @markers[index].width    then @markers[index].width   else @markers_conf.width
+          "marker_height":    if @markers[index].height   then @markers[index].height  else @markers_conf.length
+          "marker_title":     if @markers[index].title    then @markers[index].title   else null
+          "marker_anchor":    if @markers[index].marker_anchor  then @markers[index].marker_anchor  else null
+          "shadow_anchor":    if @markers[index].shadow_anchor  then @markers[index].shadow_anchor  else null
+          "shadow_picture":   if @markers[index].shadow_picture then @markers[index].shadow_picture else null
+          "shadow_width":     if @markers[index].shadow_width   then @markers[index].shadow_width   else null
+          "shadow_height":    if @markers[index].shadow_height  then @markers[index].shadow_height  else null
+          "marker_draggable": if @markers[index].draggable      then @markers[index].draggable      else @markers_conf.draggable
+          "rich_marker":      if @markers[index].rich_marker    then @markers[index].rich_marker    else null
+          "Lat":              Lat
+          "Lng":              Lng
+          "index":            index
+
+        #add infowindowstuff if enabled
+        @createInfoWindow(@markers[index])
+        #create sidebar if enabled
+        @createSidebar(@markers[index])

     @markers_conf.offset = @markers.length


Fix grouping of contributors by email in graph.

--- app/assets/javascripts/stat_graph_contributors_util.js.coffee
+++ app/assets/javascripts/stat_graph_contributors_util.js.coffee
@@ -6,7 +6,7 @@
     for entry in log
       @add_date(entry.date, total) unless total[entry.date]?

-      data = by_author[entry.author_name] #|| by_email[entry.author_email]
+      data = by_author[entry.author_name] || by_email[entry.author_email]
       data ?= @add_author(entry, by_author, by_email)

       @add_date(entry.date, data) unless data[entry.date]
@@ -95,5 +95,4 @@
     if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
       true
     else
-      false
-
+      false


Save useexttracker in db

Save useexttracker in db

--- src/controllers/torrents.coffee
+++ src/controllers/torrents.coffee
@@ -86,6 +86,8 @@
               }
               if req.session.user
                 torrent.uploader = req.session.user.name
+              if fields.useexternaltracker
+                torrent.external_tracker = torrentInfo.announce

               torrent.generatePermalink (err) ->
                 torrent.save (err) ->


Block: added "maximize" parameters to the block.

--- src/elements/Block/Block.coffee
+++ src/elements/Block/Block.coffee
@@ -45,6 +45,7 @@

 		@addClass("appearance-"+@_appearance)
 		@addClass("cui-block-level-"+@_level)
+		@maximizeAddClasses()

 		if @_padded
 			@addClass("cui-block--padded")
@@ -75,12 +76,23 @@
 			padded:
 				check: Boolean
 				default: true
+			maximize:
+				check: Boolean
+			maximize_horizontal:
+				check: Boolean
+				default: false
+			maximize_vertical:
+				check: Boolean
+				default: false

 	readOpts: ->
 		super()
 		CUI.util.assert(not ((@_text or @_icon) and @_header), "new CUI.Block", "opts.text and opts.header are mutually exclusive.", opts: @opts)
+		CUI.Layout::maximizeReadOpts.call(@)
+		@

-		@
+	maximizeAddClasses: ->
+		CUI.Layout::maximizeAddClasses.call(@)

 	getTemplateName: ->
 		"block"


(Accessibility) Label: Check and add the 'role: status' attribute to the text element (span). See #63237

--- src/elements/Label/Label.coffee
+++ src/elements/Label/Label.coffee
@@ -213,6 +213,14 @@
 		else
 			@replace(content, "content")

+		# This is used for accessibility.
+		# The role status should be on the 'span'.
+		if @_attr?.role == "status"
+			span = CUI.dom.findElement(@DOM, "span")
+			if span
+				CUI.dom.removeAttribute(@DOM, "role")
+				CUI.dom.setAttribute(span, "role", "status")
+
 		if not @_manage_overflow
 			return


Small changes

Small changes

--- activity.client.coffee
+++ activity.client.coffee
@@ -53,7 +53,7 @@
 					# min: '-8px 0px 0px 0px'
 				Dom.div !->
 					Dom.style
-						padding: '2px 8px'
+						padding: '2px 18px'
 						boxSizing: 'border-box'
 						Box: 'bottom right'
 						height: '100%'
@@ -104,14 +104,15 @@
 					# Box: "center"
 					textAlign: 'center'
 					padding: "8px 0px"
-				for id in at
-					Ui.avatar Plugin.userAvatar(id), size: 60, onTap: !->
-						Plugin.userInfo id
-
+				if at.length
+					for id in at
+						Ui.avatar Plugin.userAvatar(id), size: 60, onTap: !->
+							Plugin.userInfo id
+				else
+					Dom.text "No one attending yet..."
+		else
+			Dom.text "No one attending yet..."
 		#Joining in button
-		Dom.css
-			".bar-foot":
-				background: '#111'
 		Page.setFooter label: !->
 			ar = Db.shared.get "attendance", name
 			if !ar?


misc cleanups

misc cleanups

--- browser/node.coffee
+++ browser/node.coffee
@@ -511,18 +511,18 @@
       for pwdCmp in @pwd.split('/') by -1
         components.unshift(pwdCmp)
     for c, idx in components
-      if c == '..'
-        processed = false
-        i = idx-1
-        while !processed
-          if i < 0 then processed = true
-          if components[i] != ''
-            components[i] = ''
-            components[idx] = ''
-            processed = true
-          i--
+      continue unless c == '..'
+      processed = false
+      i = idx-1
+      while !processed
+        if i < 0 then processed = true
+        if components[i] != ''
+          components[i] = ''
+          components[idx] = ''
+          processed = true
+        i--
     # remove repeated //s
-    path = (c for c, idx in components when c != '').join '/'
+    path = (c for c in components when c != '').join '/'
     if path[0] != '/'
       path = '/' + path
     return path
@@ -628,7 +628,7 @@
     if stat?
       cb null, stat
     else
-      cb new Error "Invalid file: #{path}", null
+      cb new Error("Invalid file: #{path}"), null

   fstatSync: (fp) -> new Stat(fp)


put that damn tooltip in the callback function.

--- app/assets/javascripts/pagination.coffee
+++ app/assets/javascripts/pagination.coffee
@@ -12,5 +12,5 @@
         $.getScript more_posts_url, ->
           $this.text('Load more').removeClass('disabled') if $this
           loading_posts = false
-        $('[data-toggle="tooltip"]').tooltip()
+          $('[data-toggle="tooltip"]').tooltip()
       return


:coffee: up the Coffeescript example.

:coffee: up the Coffeescript example.

--- examples/hashie.coffee
+++ examples/hashie.coffee
@@ -1,8 +1,8 @@
-crypto = require('crypto')
-fs = require('fs')
+crypto = require 'crypto'
+fs = require 'fs'

-console.log("Let's hash these bugs out")
+console.log "Let's hash these bugs out"

-shasum = crypto.createHash('sha1')
-shasum.update("I like it when you sum.")
-console.log(shasum.digest('hex'))
+shasum = crypto.createHash 'sha1'
+shasum.update 'I like it when you sum.'
+console.log shasum.digest 'hex'


Fixed appearance settings updating (was assuming everything was a new entry)

--- public/dreamcatcher/controllers/appearance_controller.coffee
+++ public/dreamcatcher/controllers/appearance_controller.coffee
@@ -1,8 +1,8 @@
 $.Controller 'Dreamcatcher.Controllers.Appearance',

   init: ->
-    @newEntry = $("#entry_view_preference_attributes_theme")?
-    @entryId = $('#showEntry').data 'id' if $('#showEntry')?
+    @newEntry = $("#entry_view_preference_attributes_theme").attr("name")?
+    @entryId = $('#showEntry').data 'id' if $('#showEntry').attr("id")?
     @defaultGenre = $('#defaultGenre').val()

   showPanel: ->


[skip] Allowed `Neat.CollectionEditor.modelView` to be a function (2m)

--- vendor/assets/javascripts/neat/collection_editor.coffee
+++ vendor/assets/javascripts/neat/collection_editor.coffee
@@ -155,7 +155,9 @@
     view

   constructModelView: (options) ->
-    new @modelView options
+    viewClass = @modelView
+    viewClass = viewClass(options.model) if _.isFunction(viewClass) and !(viewClass.prototype instanceof Neat.ModelEditor)
+    new viewClass(options)

   beforeEdit: (view)->


CsvWriter.writeRow has problem

CsvWriter.writeRow has problem

CsvWriter.writeRow resolves immediately it doesn't wait for call back of writableStream.write

--- src/mixin/CsvWriterMixin.coffee
+++ src/mixin/CsvWriterMixin.coffee
@@ -54,10 +54,10 @@
   (target) ->

     target.writeRow = (row) ->
-      Promise.resolve target._csv.writer.write(row)
+      target._csv.writer.writeAsync(row)

     target._csv = options
-    target._csv.writer = csv.createWriteStream()
+    target._csv.writer = Promise.promisifyAll(csv.createWriteStream())
     target._csv.writer.pipe fs.createWriteStream target._csv.path, {encoding: 'utf8'}
     if target._csv.headers?
       target.writeRow target._csv.headers


Maintain selection when clicking remove in the link tooltip

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -69,9 +69,9 @@
     # Expand range to the entire leaf
     if range.isCollapsed()
       range = this._expandRange(range)
+    this.hide()
     @quill.formatText(range, 'link', false, 'user')
     @toolbar.setActive('link', false) if @toolbar?
-    this.hide()

   setMode: (url, edit = false) ->
     if edit


HUB-161 : persistent must be false by default

--- lib/actor/hactor.coffee
+++ lib/actor/hactor.coffee
@@ -718,7 +718,7 @@
     if options.persistent isnt null or options.persistent isnt undefined
       hMessage.persistent = options.persistent
     if hMessage.persistent is null or hMessage.persistent is undefined
-      hMessage.persistent = true
+      hMessage.persistent = false
     hMessage.location = options.location  if options.location
     hMessage.author = options.author  if options.author
     hMessage.published = options.published  if options.published


fixe bug with timeout

fixe bug with timeout

--- lib/actor/hactor.coffee
+++ lib/actor/hactor.coffee
@@ -51,7 +51,7 @@
   #Init logger
   logger.exitOnError = false
   logger.remove(logger.transports.Console)
-  logger.add(logger.transports.Console, {handleExceptions: true, level: "INFO"})
+  logger.add(logger.transports.Console, {handleExceptions: true, level: "debug"})
   logger.add(logger.transports.File, {handleExceptions: true, filename: "#{__dirname}/../../log/hActor.log", level: "debug"})

   # Possible running states of an actor
@@ -195,7 +195,8 @@
       if @timerOutAdapter[outboundAdapter.targetActorAid]
         clearTimeout(@timerOutAdapter[outboundAdapter.targetActorAid])
         @timerOutAdapter[outboundAdapter.targetActorAid] = setTimeout(=>
-          @timerOutAdapter[outboundAdapter.targetActorAid] = null
+          delete @timerOutAdapter[outboundAdapter.targetActorAid]
+          @unsubscribe @trackers[0].trackerChannel, outboundAdapter.targetActorAid, () ->
           @removePeer(outboundAdapter.targetActorAid)
         , 90000)
       @h_sending(hMessage, cb, outboundAdapter)
@@ -209,8 +210,9 @@
             if @actor isnt @trackers[0].trackerChannel and hResult.payload.result.targetActorAid isnt @trackers[0].trackerChannel
               @subscribe @trackers[0].trackerChannel, hResult.payload.result.targetActorAid, () ->

-            @timerOutAdapter[outboundAdapter.targetActorAid] = setTimeout(->
-              @timerOutAdapter[outboundAdapter.targetActorAid] = null
+            @timerOutAdapter[outboundAdapter.targetActorAid] = setTimeout(=>
+              delete @timerOutAdapter[outboundAdapter.targetActorAid]
+              @unsubscribe @trackers[0].trackerChannel, outboundAdapter.targetActorAid, () ->
               @removePeer(outboundAdapter.targetActorAid)
             , 90000)


capitalize

capitalize

--- lib/adaptor/ipfs.coffee
+++ lib/adaptor/ipfs.coffee
@@ -6,7 +6,7 @@
 Node = require '../core/node'
 Edge = require '../core/edge'

-class ipfsAdaptor
+class IpfsAdaptor
   @create: (args = {}) ->
     {host, port} = args
     host ?= process?.env?.IPFS_HOST
@@ -15,7 +15,7 @@
       return Promise.reject "host must be defined when running outside of a browser"
     ipfs = Promise.promisifyAll ipfsApi host, port
     ipfs.commandsAsync()
-      .then => return new ipfsAdaptor {ipfs}
+      .then => return new IpfsAdaptor {ipfs}

   constructor: ({@ipfs}) ->
     unless @ipfs instanceof ipfsApi
@@ -55,4 +55,4 @@
   path: (id) ->
     "/ipfs/#{id}"

-module.exports = ipfsAdaptor
+module.exports = IpfsAdaptor


:bug: fix cursorTickness is not defined

:bug: fix cursorTickness is not defined

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -74,7 +74,7 @@
     config = {cursorType, primaryColor, secondaryColor,
       blinkInterval, useHardwareAcceleration, cursorLineFix}
     if pulseDuration > 0 then config.pulseDuration = "#{pulseDuration}ms"
-    if cursorType isnt 'block' then config.cursorThickness = "#{cursorTickness}px"
+    if cursorType isnt 'block' then config.cursorThickness = "#{cursorThickness}px"
     config

   removeCSSRulesForScope: (scopeName) ->


Added function to compare version numbers

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -10,16 +10,25 @@
 innerPath = join 'PepperFlash', platform, filename

 validatePath = (p) ->
-  return false if not p.endsWith filename
+  return false if typeof p isnt 'string' or not p.endsWith filename
   try
     fs.accessSync p
   catch
     return false
   true

-builtInPath = join EXECROOT, innerPath
+builtInPath = join EXECROOT, innerPath if EXECROOT?
 builtInPath = join ROOT, innerPath if not validatePath builtInPath

+reVerNum = /(\d+)\.(\d+)\.(\d+)\.(\d+)/
+getNewerVersion = (ver1, ver2) ->
+  v1 = reVerNum.exec ver1
+  v2 = reVerNum.exec ver2
+  if not v1
+    return if v2 then ver2 else ''
+  for i in [1..4]
+    continue if v1[i] == v2[i]
+    return if parseInt(v1[i]) > parseInt(v2[i]) then ver1 else ver2
 findChromeFlashPath = ->
   switch platform
     when 'darwin'
@@ -28,7 +37,7 @@
         chromeVersionsDir = '/Applications/Google Chrome.app/Contents/Versions'
         chromeVersions = fs.readdirSync chromeVersionsDir
         return '' if chromeVersions.length is 0
-        chromeVer = chromeVersions[1] # TODO
+        chromeVer = chromeVersions.reduce getNewerVersion
         chromeFlashPath = join chromeVersionsDir, chromeVer,
           'Google Chrome Framework.framework/Internet Plug-Ins/PepperFlash/PepperFlashPlayer.plugin'
         fs.accessSync chromeFlashPath


use the correct event in widget

use the correct event in widget

--- vendor/assets/javascripts/dashing/default_widgets/list.coffee
+++ vendor/assets/javascripts/dashing/default_widgets/list.coffee
@@ -1,5 +1,5 @@
 class Dashing.List extends Dashing.Widget
-  @::on 'ready', ->
+  @::on 'data', ->
     if @get('unordered')
       $(@node).find('ol').remove()
     else