Avatar Area : On group change event update the partial

--- client/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
+++ client/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
@@ -61,10 +61,15 @@
         KD.getSingleton('router').handleRoute '/Groups'
         @hide()

+    groupsController = KD.getSingleton("groupsController")
+    groupsController.once 'GroupChanged', () =>
+      group =  groupsController.getCurrentGroup()
+      if group?.slug isnt "koding"
+        backToKodingView.updatePartial "Back to Koding"
+
     backToKodingView = new KDView
       height   : "auto"
       cssClass : "split sublink right"
-      partial  : "Back to Koding"
       click    : =>
         @hide()


either for appearanceObject

either for appearanceObject

--- src/steroids/Converter.coffee
+++ src/steroids/Converter.coffee
@@ -113,31 +113,36 @@
     }

   appearanceObject: (config)->
-    appearanceObject =
-      nav_bar_portrait_background_image: "#{config.navigationBar.portrait.backgroundImage}"
-      nav_bar_landscape_background_image: "#{config.navigationBar.landscape.backgroundImage}"
-      nav_bar_tint_color: "#{config.navigationBar.tintColor}"
-      nav_bar_title_text_color: "#{config.navigationBar.titleColor}"
-      nav_bar_title_shadow_color: "#{config.navigationBar.titleShadowColor}"
-      nav_bar_button_tint_color: "#{config.navigationBar.buttonTintColor}"
-      nav_bar_button_title_text_color: "#{config.navigationBar.buttonTitleColor}"
-      nav_bar_button_title_shadow_color: "#{config.navigationBar.buttonShadowColor}"
-      tab_bar_background_image: "#{config.tabBar.backgroundImage}"
-      tab_bar_tint_color: "#{config.tabBar.tintColor}"
-      tab_bar_button_title_text_color: "#{config.tabBar.tabTitleColor}"
-      tab_bar_button_title_shadow_color: "#{config.tabBar.tabTitleShadowColor}"
-      tab_bar_selected_icon_tint_color: "#{config.tabBar.selectedTabTintColor}"
-      tab_bar_selected_indicator_background_image: "#{config.tabBar.selectedTabBackgroundImage}"
-      loading_screen_color: "#{config.loadingScreen.tintColor}"
+    @config.eitherSupersonicOrLegacy().fold(
+      ->
+        {}
+      ->
+        appearanceObject =
+          nav_bar_portrait_background_image: "#{config.navigationBar.portrait.backgroundImage}"
+          nav_bar_landscape_background_image: "#{config.navigationBar.landscape.backgroundImage}"
+          nav_bar_tint_color: "#{config.navigationBar.tintColor}"
+          nav_bar_title_text_color: "#{config.navigationBar.titleColor}"
+          nav_bar_title_shadow_color: "#{config.navigationBar.titleShadowColor}"
+          nav_bar_button_tint_color: "#{config.navigationBar.buttonTintColor}"
+          nav_bar_button_title_text_color: "#{config.navigationBar.buttonTitleColor}"
+          nav_bar_button_title_shadow_color: "#{config.navigationBar.buttonShadowColor}"
+          tab_bar_background_image: "#{config.tabBar.backgroundImage}"
+          tab_bar_tint_color: "#{config.tabBar.tintColor}"
+          tab_bar_button_title_text_color: "#{config.tabBar.tabTitleColor}"
+          tab_bar_button_title_shadow_color: "#{config.tabBar.tabTitleShadowColor}"
+          tab_bar_selected_icon_tint_color: "#{config.tabBar.selectedTabTintColor}"
+          tab_bar_selected_indicator_background_image: "#{config.tabBar.selectedTabBackgroundImage}"
+          loading_screen_color: "#{config.loadingScreen.tintColor}"

-    # legacy support: bug in 3.1.5 client causes empty strings for these values to crash
-    unless config.navigationBar.borderSize is null or config.navigationBar.borderSize is ""
-      appearanceObject.nav_bar_border_size = "#{config.navigationBar.borderSize}"
+        # legacy support: bug in 3.1.5 client causes empty strings for these values to crash
+        unless config.navigationBar.borderSize is null or config.navigationBar.borderSize is ""
+          appearanceObject.nav_bar_border_size = "#{config.navigationBar.borderSize}"

-    unless config.navigationBar.borderColor is null or config.navigationBar.borderColor is ""
-      appearanceObject.nav_bar_border_color = "#{config.navigationBar.borderColor}"
+        unless config.navigationBar.borderColor is null or config.navigationBar.borderColor is ""
+          appearanceObject.nav_bar_border_color = "#{config.navigationBar.borderColor}"

-    return appearanceObject
+        appearanceObject
+    )

   preloadsObject: (config)->
     return [] unless config.preloads?.length


Adding command for demoing mood poll on-demand.

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -253,3 +253,40 @@
             value: "sad"
           }]
   , null, true, 'America/New_York'
+
+  robot.respond /demo mood poll/i, (msg) ->
+    buttonName = "mood_" + robot.brain.data._private.team[msg.message.room]._id
+    robot.emit 'slack.attachment',
+      message: msg.message
+      content:
+        text: "How was your day today?"
+        fallback: "Oh no! Something went horribly wrong!"
+        callback_id: "dab_mood"
+        color: "#3AA3E3"
+        attachment_type: "default"
+        actions: [{
+          name: buttonName
+          text: ":bolt-ecstatic:"
+          type: "button"
+          value: "ecstatic"
+        },{
+          name: buttonName
+          text: ":bolt-happy:"
+          type: "button"
+          value: "happy"
+        },{
+          name: buttonName
+          text: ":bolt-indifferent:"
+          type: "button"
+          value: "indifferent"
+        },{
+          name: buttonName
+          text: ":bolt-disappointed:"
+          type: "button"
+          value: "disappointed"
+        },{
+          name: buttonName
+          text: ":bolt-sad:"
+          type: "button"
+          value: "sad"
+        }]


Made the ach hans run fix extensible for other replacements in the future

--- scripts/card-fetcher.coffee
+++ scripts/card-fetcher.coffee
@@ -13,13 +13,18 @@

 cardDatabaseUrl = "http://mtgjson.com/json/AllCards.json"
 urlBase = "http://gatherer.wizards.com/Handlers/Image.ashx?type=card&name="
-uniQuotes = /“|”/
+replacements = {
+  uniQuotes : {
+      regex : /“|”/,
+      str : "\""
+    }
+}

 doReplacement = (str) ->
-  return str.replace uniQuotes, "\"", "g"
+  return str.replace replacements.uniQuotes.regex, replacements.uniQuotes.str, "g"

 verifyCard = (cardName, cardDB) ->
-  while uniQuotes.test cardName
+  while replacements.uniQuotes.regex.test cardName
     cardName = doReplacement cardName
   if cardName.search(" // ") isnt -1
     splitCards = cardName.split " // "


Fixed incorrect working with sourcemaps. See https://github.com/sindresorhus/gulp-autoprefixer/pull/29

--- gulp/tasks/styles.coffee
+++ gulp/tasks/styles.coffee
@@ -9,6 +9,7 @@
   gulp.src config.style
   .pipe $.plumber(errorHandler: utils.onError)
   .pipe do $.less
+  .pipe $.autoprefixer {cascade: false}
   .pipe do $.minifyCss
   .pipe $.size {title: 'Minified styles'}
   .pipe gulp.dest "#{paths.static.min}/style"
@@ -19,5 +20,6 @@
   .pipe $.plumber(errorHandler: utils.onError)
   .pipe do $.sourcemaps.init
   .pipe do $.less
+  .pipe $.autoprefixer {map: true}
   .pipe do $.sourcemaps.write
   .pipe gulp.dest "#{paths.static.dev}/style"


activity: simplify `CommentViewHeader::viewAppended` method

--- client/Social/Activity/views/comments/commentviewheader.coffee
+++ client/Social/Activity/views/comments/commentviewheader.coffee
@@ -114,9 +114,7 @@

     {repliesCount} = @getData()

-    repliesCount
-    unless repliesCount and repliesCount > @maxCount
-      @hide()
+    @hide()  unless repliesCount and repliesCount > @maxCount

   render: ->


different link repr

different link repr

--- scripts/imaginaria.coffee
+++ scripts/imaginaria.coffee
@@ -28,7 +28,7 @@
           feed.init()
           count = msg.match[1] || 5
           items = feed.getItems(0, count)
-          msg.send "<" + item.getPermalink() + "|" + item.getTitle() + "> [" + item.getCategories() + "]" for item in items
+          msg.send "" + item.getTitle() + " [" + item.getCategories() + "]" for item in items
         catch e
           console.log(e)
           msg.send "Something's gone awry"


another fix for missing accepts param on find response of new API code

--- noddy/service/oabutton/find.coffee
+++ noddy/service/oabutton/find.coffee
@@ -50,6 +50,9 @@
     opts.url = 'https://doi.org/' + (if opts.doi.indexOf('doi.org/') isnt -1 then opts.doi.split('doi.org/')[1] else opts.doi) if opts.doi
   return {} if not opts.url?

+  # ret.accepts is not currently properly handled - it is leftover from when we merged articles and data, and had an idea to do software and other types too
+  # for now it should stay, so that the plugin can know how to handle them if necessary, but it just stays at accepting articles
+  # unless there is an availability or a request found, in which case it is just set to empty list
   ret = {match:opts.url,availability:[],requests:[],accepts:[{type:'article'}],meta:{article:{},data:{}}}
   ret.library = API.service.oab.library(opts) if opts.library
   ret.libraries = API.service.oab.libraries(opts) if opts.libraries
@@ -88,6 +91,7 @@
       opts.source.article = ret.meta.article.source
       opts.discovered.article = if typeof ret.meta.article.redirect is 'string' then ret.meta.article.redirect else ret.meta.article.url
       ret.availability.push {type:'article',url:opts.discovered.article}
+      ret.accepts = [];
       already.push 'article'

   opts.url = 'https://doi.org/' + ret.meta.article.doi if opts.url.indexOf('http') isnt 0 and ret.meta.article.doi
@@ -100,6 +104,7 @@
     rq.ucreated = if opts.uid and request.user?.id is opts.uid then true else false
     rq.usupport = if opts.uid then API.service.oab.supports(request._id, opts.uid)? else false
     ret.requests.push rq
+    ret.accepts = []
     already.push request.type

   delete opts.dom


fixing erroneous read of some availability data

--- noddy/service/oabutton/find.coffee
+++ noddy/service/oabutton/find.coffee
@@ -78,7 +78,7 @@
     finder += ')'
     if opts.refresh isnt 0 and 'oabutton' in opts.sources
       avail = oab_availability.find finder + ' AND discovered.article:* AND NOT discovered.article:false', true
-      if avail?.discovered?.article and ret.meta.article.redirect = API.service.oab.redirect(avail.discovered.article) isnt false
+      if avail?.discovered?.article and ret.meta.article.redirect = API.service.oab.redirect(avail.discovered.article)
         ret.meta.article.url = avail.discovered.article
         ret.meta.article.source = avail.source?.article
         ret.meta.cache = true


Added comment

Added comment

--- core/app/backbone/views/buttons/react_sliding_share_button.coffee
+++ core/app/backbone/views/buttons/react_sliding_share_button.coffee
@@ -59,10 +59,10 @@
   render: ->
     delete_button =
       _span [],
-        "\u00a0"
+        "\u00a0" # nbsp
         ReactCommentFacebookShare
           model: @model()
-        "\u00a0"
+        "\u00a0" # nbsp
         ReactCommentTwitterShare
           model: @model()


bugfix: button label for first task Should toggle between 'Continue' and 'Finish'.

--- overrides.coffee
+++ overrides.coffee
@@ -98,10 +98,12 @@

 classify_page.el.on decisionTree.CHANGE, ({originalEvent: {detail}})->
   {key, value} = detail
-
-  label = 'Finish' unless decisionTree.currentTask.getNext()
+
+  if key is 'illustrations'
+    label = decisionTree.currentTask.confirmButtonLabel
+    label = 'Finish' unless decisionTree.currentTask.getNext()

-  decisionTree.currentTask.confirmButton.innerHTML = label if label?
+    decisionTree.currentTask.confirmButton.innerHTML = label if label?

   current_tool?.mark.details = value if key is 'details'
   current_tool?.mark.parts = value if key is 'parts'


misc cleanup and commenting

misc cleanup and commenting

--- src/giphy.coffee
+++ src/giphy.coffee
@@ -30,16 +30,17 @@

 DEBUG = process.env.DEBUG

+# utility method for extending an object definition
 extend = (object, properties) ->
   for key, val of properties
     object[key] = val if val
   object

+# utility method for merging two objects
 merge = (options, overrides) ->
   extend (extend {}, options), overrides

 class Giphy
-
   @SearchEndpointName = 'search'
   @IdEndpointName = 'id'
   @TranslateEndpointName = 'translate'


pass index to get_value, get_timestamp

pass index to get_value, get_timestamp

--- src/graph.coffee
+++ src/graph.coffee
@@ -127,9 +127,9 @@
       value_min = null
       value_max = null
       targets = for s in data
-        values = for datapoint in s.datapoints
-          value = get_value datapoint
-          timestamp = get_timestamp datapoint
+        values = for datapoint, i in s.datapoints
+          value = get_value datapoint, i
+          timestamp = get_timestamp datapoint, i
           time_min = Math.min timestamp, time_min ? timestamp
           time_max = Math.max timestamp, time_max
           value = transform_value value


big_handler now check ID with/without quotes

--- big_handler.coffee
+++ big_handler.coffee
@@ -106,10 +106,12 @@
     ########################################
     terminal:
         id: (value)->
+            if value.match /^['`\[]/
+                value = ssubstring value, 1, -1
             $$ =
                 node: 'ID'
                 raw: value
-                value: ssubstring value, 1, -1
+                value: value
         string: (value)->
             $$ =
                 node: 'STRING'


Add default options when using wintersmith programmatically

--- src/index.coffee
+++ src/index.coffee
@@ -34,6 +34,11 @@
     module wintersmith, callback
   , callback

+defaultOptions =
+  plugins: []
+  ignore: []
+  locals: {}
+
 module.exports = (options, callback) ->
   ### build all contents and templates
       *options*:
@@ -43,6 +48,10 @@
         templates: path to templates
         output: path to output directory
         locals: optional extra data to send to templates ###
+
+  # default options
+  for key of defaultOptions
+    options[key] ?= defaultOptions[key]

   logger.verbose 'running with options:', {options: options}


Support pipes returning thenables

Support pipes returning thenables

--- src/index.coffee
+++ src/index.coffee
@@ -82,7 +82,7 @@
       return if ++index is length
       startTime = now()
       result = pipes[index].call context, req, res
-      if isType result, Promise
+      if result and isType result.then, Function
       then result.then done
       else done result


revert export require path to Release dir

--- src/index.coffee
+++ src/index.coffee
@@ -2,7 +2,7 @@
   _Geometry
   WKTWriter
   GeoJSONWriter
-} = exports = module.exports = require("../build/Debug/geos")
+} = exports = module.exports = require("../build/Release/geos")

 _Geometry = exports.Geometry


Stray console.log

Stray console.log

--- src/index.coffee
+++ src/index.coffee
@@ -8,7 +8,6 @@
     @captured = false

   start: (url) ->
-    console.log @
     @ios = iosctrl "#{url}?id=#{@id}"
   kill: (done) ->
     @ios.close() unless @ios.state is 'ready'


Fix weird merge

Fix weird merge

--- src/index.coffee
+++ src/index.coffee
@@ -178,14 +178,12 @@

   data = @data.get()
   for k, v of opts
-    if opts[k]
-      if !data[k]?
-        data[k] = opts[k]
-      else
-        for k2, v2 of data[k]
-          if opts[k]? && opts[k][k2]?
-            objectAssign data[k][k2], opts[k][k2]
-
+    unless data[k]?
+      data[k] = opts[k]
+    else
+      for k2, v2 of data[k]
+        unless v2?
+          data[k][k2] = opts[k]?[k2]

   @data.set data


Removed console.log statement.

Removed console.log statement.

--- src/index.coffee
+++ src/index.coffee
@@ -119,7 +119,6 @@

       options.matches ?= '**/*.' + name
       options.hints ?= {}
-      console.log type, output, options.hints

       nextFilter = filter options.matches
       steps.push nextFilter


group bundle: rough UI for bundle editor

--- client/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupsbundleview.coffee
@@ -3,7 +3,7 @@
   constructor:(options, data)->
     super

-    group = @getData()
+    {group} = @getData()

     @createButton = new KDButtonView
       style     : "clean-gray"
@@ -23,13 +23,69 @@

 class GroupsBundleEditView extends JView

+  computeUnitMap =
+    vms   : 1
+    cpu   : 1
+    ram   : 0.25
+    disk  : 0.5
+
+  computeUnitVisibility =
+    users : yes
+    vms   : yes
+    cpu   : no
+    ram   : no
+    disk  : no
+
   constructor: (options, data) ->
     super

-    group = @getData()
+    {group, bundle} = @getData()
+
+    {JLimit} = KD.remote.api
+
+    computeLimit = new JLimit { usage: 0, quota: 0, unit: 'VM', title: 'vms' }
+
+    bundle.fetchLimits (err, limits) =>
+      return error err      if err?
+      limits.splice 1, 0, computeLimit
+      @renderLimits limits  if limits?
+
+    @usersLabel = new KDLabelView
+      title       : 'Users'
+
+    @usersSlider = new KDInputView
+      label       : @usersLabel
+      type        : 'range'
+      change      : (event) =>
+        userData = @limitsData.users
+        userData.quota = @usersSlider.getValue()
+        userData.emit 'update'
+      attributes  :
+        min       : 0
+        max       : 50000
+
+    @computeLabel = new KDLabelView
+      title       : 'Computing resources'
+
+    @computeSlider = new KDInputView
+      label       : @computeLabel
+      type        : 'range'
+      change      : (event) =>
+        computeValue = @computeSlider.getValue()
+        ['cpu','ram','disk','vms'].forEach (resource) =>
+          resourceData = @limitsData[resource]
+          resourceData.quota = computeValue * computeUnitMap[resource]
+          resourceData.emit 'update'
+      attributes  :
+        min       : 0
+        max       : 50000
+
+    @limits = new KDView
+      tagName   : 'span'
+      cssClass  : 'group-bundle-limits clearfix'

     @destroyButton = new KDButtonView
-      style     : "clean-gray"
+      style     : "modal-clean-red"
       title     : "Destroy bundle"
       callback  : =>
         group.destroyBundle (err, bundle) =>
@@ -37,14 +93,83 @@

           @emit 'BundleDestroyed', bundle

-  pistachio:->
+    @saveButton = new KDButtonView
+      style     : "modal-clean-green"
+      title     : "Save bundle"
+      callback  : =>
+        overagePolicy = @overagePolicy.getValue()
+        group.updateBundle { overagePolicy }
+
+    @overageLabel = new KDLabelView
+      title     : 'Overage policy'
+
+    @overagePolicy = new KDSelectBox
+      label       : @overageLabel
+      name        : "overagePolicy"
+      defaultValue: bundle.overagePolicy ? "not allowed"
+      selectOptions : [
+        { value : "not allowed",    title : "Not allowed" }
+        { value : "by permission",  title : "Allowed only with admin approval" }
+        { value : "allowed",        title : "Allowed for all members" }
+      ]
+
+  renderLimits: (limits) ->
+    @limits.destroySubViews()
+    @limitsData = {}
+    for limit in limits
+      @limitsData[limit.title] = limit
+      cssClass = 'group-bundle-limit'
+      cssClass += ' hidden'  unless computeUnitVisibility[limit.title]
+      limitView = new GroupsBundleLimitView {cssClass}, limit
+      @addSubView limitView
+
+  pistachio: ->
     """

Bundle details

There will be some cool details here.

-
For now you can only destroy it.

-    {{> @destroyButton}}
+

+
{{> @usersLabel}} {{> @usersSlider}}

+
{{> @computeLabel}}  {{> @computeSlider}}

+
{{> @overageLabel}} {div#overage{> @overagePolicy}}

+

+    {{> @limits}}
+
{{> @saveButton}} {{> @destroyButton}}

     """

+# class GroupsBundleLimitWrapper extends KDObject
+#   constructor: (@limit, @usersLimit)->
+#     super()
+#     @calculate()
+#     @limit.on 'update', @bound 'calculate'
+#     @usersLimit.on 'update', @bound 'calculate'
+
+#   calculate:->
+#     {@unit, @title, @quota, @usage} = @limit
+#     @perUserQuota = @quota / @usersLimit.quota
+#     console.log this
+#     @emit 'update'
+
+class GroupsBundleLimitPerUserView extends JView
+
+  pistachio: ->
+    """
+    {h2{#(title)}}
+    {h3{#(usage)}}
+
/

+
{.per-user{#(perUserQuota)}} - {.total{#(quota)}}

+    {h4{#(unit)}}
+    """
+
+class GroupsBundleLimitView extends JView
+
+  pistachio: ->
+    """
+    {h2{#(title)}}
+    {h3{#(usage)}}
+
/

+    {h3{#(quota)}}
+    {h4{#(unit)}}
+    """

 class GroupsBundleView extends KDView

@@ -62,10 +187,10 @@
       return error err  if err?

       unless bundle?
-        @createBundleView = new GroupsBundleCreateView {}, group
+        @createBundleView = new GroupsBundleCreateView {}, { group }
         @createBundleView.once 'BundleCreated', @bound 'resetBundleView'
         @addSubView @createBundleView
       else
-        @editBundleView = new GroupsBundleEditView {}, group
+        @editBundleView = new GroupsBundleEditView {}, { group, bundle }
         @editBundleView.once 'BundleDestroyed', @bound 'resetBundleView'
         @addSubView @editBundleView


:memo: doc PackageManager as public

:memo: doc PackageManager as public

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -4,21 +4,21 @@
 Package = require './package'
 path = require 'path'

-###
-Packages have a lifecycle
-
-* The paths to all non-disabled packages and themes are found on disk (these are available packages)
-* Every package (except those in core.disabledPackages) is 'loaded', meaning
-  `Package` objects are created, and their metadata loaded. This includes themes,
-  as themes are packages
-* The ThemeManager.activateThemes() is called 'activating' all the themes, meaning
-  their stylesheets are loaded into the window.
-* The PackageManager.activatePackages() function is called 'activating' non-theme
-  package, meaning its resources -- keymaps, classes, etc. -- are loaded, and
-  the package's activate() method is called.
-* Packages and themes can then be enabled and disabled via the public
-  .enablePackage(name) and .disablePackage(name) functions.
-###
+# Public: Package manager for coordinating the lifecycle of Atom packages.
+#
+# Packages can be loaded, activated, and deactivated, and unloaded:
+#  * Loading a package reads and parses the package's metadata and resources
+#    such as keymaps, menus, stylesheets, etc.
+#  * Activating a package registers the loaded resources and calls `activate()`
+#    on the package's main module.
+#  * Deactivating a package unregisters the package's resources  and calls
+#    `deactivate()` on the package's main module.
+#  * Unloading a package removes it completely from the package manager.
+#
+# Packages can also be enabled/disabled via the `core.disabledPackages` config
+# settings and also by calling `enablePackage()/disablePackage()`.
+#
+# An instance of this class is globally available via `atom.packages`.
 module.exports =
 class PackageManager
   Emitter.includeInto(this)


:lipstick: Use trailing if

:lipstick: Use trailing if

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -44,8 +44,7 @@
   # Public: Get the path to the apm command
   getApmPath: ->
     commandName = 'apm'
-    if process.platform == 'win32'
-      commandName += '.cmd'
+    commandName += '.cmd' if process.platform is 'win32'
     @apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)

   # Public: Get the paths being used to look for packages.


Build Test To Reproduce #196

Build Test To Reproduce #196

- #196

--- spec/fuzzy-provider-spec.coffee
+++ spec/fuzzy-provider-spec.coffee
@@ -3,7 +3,7 @@
 TestProvider = require "./lib/test-provider"

 describe "Autocomplete", ->
-  [completionDelay, editorView, editor, autocomplete, mainModule] = []
+  [completionDelay, editorView, editor, autocompleteManager] = []

   beforeEach ->
     runs ->
@@ -27,16 +27,30 @@

       # Activate the package
       waitsForPromise -> atom.packages.activatePackage("autocomplete-plus").then (a) ->
-        mainModule = a.mainModule
-        autocomplete = mainModule.autocompleteManager
+        autocompleteManager = a.mainModule.autocompleteManager

       runs ->
         editorView = atom.views.getView(editor)

-    # TODO: Move this to a separate fuzzyprovider spec
     it "adds words to the wordlist after they have been written", ->
-      editor.insertText "somethingNew"
-      editor.insertText " "
+      provider = autocompleteManager.providerManager.fuzzyProvider

-      provider = autocomplete.providerManager.fuzzyProvider;
+      expect(provider.wordList.indexOf("somethingNew")).toEqual(-1)
+      editor.insertText("somethingNew")
+      editor.insertText(" ")
       expect(provider.wordList.indexOf("somethingNew")).not.toEqual(-1)
+
+    xit "removes words from the wordlist when they no longer exist in any open buffers", ->
+      # Not sure we should fix this; could have a significant performance impacts
+      provider = autocompleteManager.providerManager.fuzzyProvider
+
+      expect(provider.wordList.indexOf("bogos")).toEqual(-1)
+      editor.insertText("bogos = 1")
+      editor.insertText(" ")
+      expect(provider.wordList.indexOf("bogos")).not.toEqual(-1)
+      expect(provider.wordList.indexOf("bogus")).toEqual(-1)
+      editor.backspace() for [1..7]
+      editor.insertText("us = 1")
+      editor.insertText(" ")
+      expect(provider.wordList.indexOf("bogus")).not.toEqual(-1)
+      expect(provider.wordList.indexOf("bogos")).toEqual(-1)


Fix Specs

Fix Specs

--- spec/highlight-line-spec.coffee
+++ spec/highlight-line-spec.coffee
@@ -1,21 +1,22 @@
 path = require 'path'
-{WorkspaceView, Range, Point} = require 'atom'
-HighlightSelected = require '../lib/highlight-line'
+{Range, Point} = require 'atom'
+HighlightLine = require '../lib/highlight-line'

-describe "DecorationExample", ->
-  [activationPromise, editor, editorView, highlightLine] = []
+describe "Higlight line", ->
+  [activationPromise, workspaceElement,
+    editor, editorElement, highlightSelected] = []

   beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    atom.project.setPath(path.join(__dirname, 'fixtures'))
+    workspaceElement = atom.views.getView(atom.workspace)
+    atom.project.setPaths([path.join(__dirname, 'fixtures')])

     waitsForPromise ->
       atom.workspace.open('sample.coffee')

     runs ->
-      atom.workspaceView.attachToDom()
-      editorView = atom.workspaceView.getActiveView()
-      editor = editorView.getEditor()
+      jasmine.attachToDOM(workspaceElement)
+      editor = atom.workspace.getActiveTextEditor()
+      editorElement = atom.views.getView(editor)

       activationPromise = atom.packages
         .activatePackage('highlight-line').then ({mainModule}) ->
@@ -26,7 +27,8 @@

   describe "when the view is loaded", ->
     it "attaches the view", ->
-      expect(atom.workspaceView.find('.highlight-line')).toExist()
+      expect(workspaceElement.querySelectorAll('.highlight-view'))
+        .toHaveLength(1)

   describe "when the background color is enabled", ->
     beforeEach ->
@@ -38,22 +40,25 @@
         editor.setSelectedBufferRange(range)

       it "adds the background class to the cursor line", ->
-        expect(atom.workspaceView.find('.cursor-line.highlight-line'))
-          .toHaveLength(1)
+        expect(editorElement.shadowRoot
+          .querySelectorAll('.cursor-line.highlight-line')
+        ).toHaveLength(1)

       describe "when hide highlight on select is enabled", ->
         beforeEach ->
           atom.config.set('highlight-line.hideHighlightOnSelect', true)

         it "will have a highlight when there is no text selected", ->
-          expect(atom.workspaceView.find('.cursor-line.highlight-line'))
-            .toHaveLength(1)
+          expect(editorElement.shadowRoot
+            .querySelectorAll('.cursor-line.highlight-line')
+          ).toHaveLength(1)

         it "won`t have a highlight when there is text selected", ->
           range = new Range(new Point(8, 2), new Point(8, 5))
           editor.setSelectedBufferRange(range)
-          expect(atom.workspaceView.find('.cursor-line.highlight-line'))
-            .toHaveLength(0)
+          expect(editorElement.shadowRoot
+            .querySelectorAll('.cursor-line.highlight-line')
+          ).toHaveLength(0)

     describe "when underline is enabled", ->
       beforeEach ->
@@ -66,9 +71,11 @@
           editor.setSelectedBufferRange(range)

         it "adds an underline to the current line", ->
-          expect(atom.workspaceView
-            .find('.cursor-line.highlight-line-multi-line-solid-bottom'))
-            .toHaveLength(1)
+          expect(
+            editorElement.shadowRoot.querySelectorAll(
+              '.cursor-line.highlight-line-multi-line-solid-bottom'
+            )
+          ).toHaveLength(1)

         describe "when hide highlight on select is enabled", ->
           beforeEach ->
@@ -77,9 +84,11 @@
           it "will still have a line", ->
             range = new Range(new Point(8, 2), new Point(8, 5))
             editor.setSelectedBufferRange(range)
-            expect(atom.workspaceView
-              .find('.line.highlight-line-multi-line-solid-bottom'))
-              .toHaveLength(1)
+            expect(
+              editorElement.shadowRoot.querySelectorAll(
+                '.line.highlight-line-multi-line-solid-bottom'
+              )
+            ).toHaveLength(1)

       describe "when dashed settings has been set", ->
         beforeEach ->
@@ -88,9 +97,11 @@
           editor.setSelectedBufferRange(range)

         it "adds an underline to the current line", ->
-          expect(atom.workspaceView
-            .find('.cursor-line.highlight-line-multi-line-dashed-bottom'))
-            .toHaveLength(1)
+          expect(
+            editorElement.shadowRoot.querySelectorAll(
+              '.cursor-line.highlight-line-multi-line-dashed-bottom'
+            )
+          ).toHaveLength(1)

       describe "when dotted settings has been set", ->
         beforeEach ->
@@ -99,9 +110,11 @@
           editor.setSelectedBufferRange(range)

         it "adds an underline to the current line", ->
-          expect(atom.workspaceView
-            .find('.cursor-line.highlight-line-multi-line-dotted-bottom'))
-            .toHaveLength(1)
+          expect(
+            editorElement.shadowRoot.querySelectorAll(
+              '.cursor-line.highlight-line-multi-line-dotted-bottom'
+            )
+          ).toHaveLength(1)

     describe "when there are two cursors", ->
       beforeEach ->
@@ -110,8 +123,9 @@
         editor.setSelectedBufferRanges([range1, range2])

       it 'adds the background class to the cursor line', ->
-        expect(atom.workspaceView.find('.cursor-line.highlight-line'))
-          .toHaveLength(2)
+        expect(editorElement.shadowRoot
+          .querySelectorAll('.cursor-line.highlight-line')
+        ).toHaveLength(2)

     describe "when there is a multi row selection", ->
       beforeEach ->
@@ -119,8 +133,9 @@
         editor.setSelectedBufferRange(range)

       it "won`t add a highlight line class", ->
-        expect(atom.workspaceView.find('.cursor-line.highlight-line'))
-          .toHaveLength(0)
+        expect(editorElement.shadowRoot
+          .querySelectorAll('.cursor-line.highlight-line')
+        ).toHaveLength(0)

       describe "when selection border is enabled", ->
         beforeEach ->
@@ -130,7 +145,7 @@
           editor.setSelectedBufferRange(range)

         it "will add highlights to the top and bottom", ->
-          expect(atom.workspaceView.find('.cursor-line
+          expect(editorElement.shadowRoot.querySelectorAll('.cursor-line
             .highlight-line-multi-line-solid-top
             .highlight-line-multi-line-solid-bottom'))
             .toHaveLength(0)


Camel casing test very close just missing digits.

--- spec/jumpy-settings-spec.coffee
+++ spec/jumpy-settings-spec.coffee
@@ -24,7 +24,6 @@
         atom.config.set 'jumpy.fontSize', .50
         atom.config.set 'jumpy.useHomingBeaconEffectOnJumps', false
         atom.config.set 'jumpy.matchPattern', '([\\w]){2,}' # also the default
-        #atom.config.set 'jumpy.matchPattern', '[A-Z]+[0-9a-z]|([a-z])*' # NOT YET DONE.

         waitsForPromise ->
             atom.workspace.open 'test_text.md'
@@ -70,8 +69,11 @@
             expect(labels[83].innerHTML).toBe 'df'

     # Do not have the regex to make this work yet.
-    xdescribe "when a custom match is used (camel case)", ->
-        it "draws correct labels", ->
+    describe "when a custom match is used (camel case)", ->
+        fit "draws correct labels", ->
+            editorView.trigger 'jumpy:clear'
+            atom.config.set 'jumpy.matchPattern', '([A-Z]+([0-9a-z])*)|[a-z]{2,}' # NOT YET DONE.
+            editorView.trigger 'jumpy:toggle'
             expect(editorView.find('.jumpy.labels')).toExist()
             labels = editorView.find('.jumpy.label')
             expect(labels.length)


debug

debug

--- src/xmpp/pubsub_server.coffee
+++ src/xmpp/pubsub_server.coffee
@@ -79,7 +79,6 @@
         if result?.node?
             queryEl.attrs.node = result.node

-        console.log "DiscoInfoRequest.reply result": result
         for identity in result.identities
             queryEl.c "identity",
                 category: identity.category
@@ -367,7 +366,6 @@
         @node

     reply: (items) ->
-        console.log "PubsubItemsRequest.reply": items
         itemsEl = new xmpp.Element("items", node: items.node)
         for item in items
             itemEl = itemsEl.c("item", id: item.id)


fix for one of the test dois

fix for one of the test dois

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -605,8 +605,7 @@
         _L.show '._oab_oa_deposit'
     else if this.f?.permissions?.permissions?.archiving_allowed
       # can be shared, depending on permissions info
-      # hide #_oab_not_pdf if version_allowed isnt publisher pdf
-      # config ._oab_library by (if this.config.not_a_library then 'We have'
+      _L.hide('#_oab_not_pdf') if this.f?.permissions?.permissions?.version_allowed is 'publisher pdf'
       if typeof this.f?.permissions?.permissions?.licence_required is 'string' and this.permissions.permissions.licence_required.indexOf('other-') is 0
         _L.html '#_oab_licence', 'under the publisher\'s terms.' + refs
       else
@@ -1076,6 +1075,7 @@
       if this.cml()? and el = _L.gebi '_oab_nodoi'
         el.setAttribute 'href', el.getAttribute('href').replace('help@openaccessbutton.org', this.cml())
       if not this.config.not_a_library
+        _L.html '._oab_library', 'We have'
         _L.html '#_oab_lib_info', 'Share your paper with help from the library in ' + (this.config.repo_name ? 'ScholarWorks') + '. Legally, for free, in minutes. '

     else if this.plugin is 'instantill'


embed alteration for permissions contact

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -625,7 +625,7 @@
           else
             this.file = true # no file required for oa deposit...
             _L.show '._oab_oa_deposit'
-        else if this.f?.permissions?.best_permission?.can_archive and not this.f.permissions.best_permission.permissions_contact
+        else if this.f?.permissions?.best_permission?.can_archive
           # can be shared, depending on permissions info
           _L.hide('#_oab_not_pdf') if this.f?.permissions?.best_permission?.version is 'publishedVersion'
           if typeof this.f?.permissions?.best_permission?.licence is 'string' and this.f.permissions.best_permission.licence.indexOf('other-') is 0
@@ -702,7 +702,7 @@
       _L.show '#_oab_findings'
       if this.f.ill?.error
         _L.show '#_oab_error', '
Please note, we encountered errors querying the following subscription services: ' + this.f.ill.error.join(', ') + '
'
-      if this.f.metadata?.title?
+      if this.f.metadata?.title? and (this.f.metadata.journal? or this.data.usermetadata)
         citation = '
' + this.f.metadata.title + '
'
         if this.f.metadata.year or this.f.metadata.journal or this.f.metadata.volume or this.f.metadata.issue
           citation += '
'


Fix login.coffee

Fix login.coffee

--- src/login.coffee
+++ src/login.coffee
@@ -5,6 +5,10 @@
 # Current programmatic login workflow is described here
 # https://github.com/tdryer/hangups/issues/260#issuecomment-246578670
 LOGIN_URL = "https://accounts.google.com/o/oauth2/programmatic_auth?hl=en&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&client_id=936475272427.apps.googleusercontent.com&access_type=offline&delegated_client_id=183697946088-m3jnlsqshjhh5lbvg05k46q1k4qqtrgn.apps.googleusercontent.com&top_level_cookie=1"
+
+# Hack the user agent so this works again
+# Credit to https://github.com/yakyak/yakyak/issues/1087#issuecomment-565170640
+AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/80.0.3904.70 Safari/537.36"

 # promise for one-time oauth token
 module.exports = (mainWindow) -> Q.Promise (rs) ->
@@ -27,4 +31,4 @@

     # redirect to google oauth
     options = {"userAgent": AGENT}
-    mainWindow.loadURL LOGIN_URL
+    mainWindow.loadURL LOGIN_URL, options


moved addCommas to knockout custom bindings

--- js/inner.coffee
+++ js/inner.coffee
@@ -1,11 +1,4 @@
 window.observableCount = 0
-
-window.addCommas = (num) ->
-  num_str = num.toString()
-  i = num_str.length % 3
-  prefix = num_str.substr(0, i) + if i > 0 and num_str.length > 3 then "," else ""
-  suffix = num_str.substr(i).replace(/(\d{3})(?=\d)/g, "$1" + ",")
-  prefix + suffix

 log2 = (v) ->
   Math.log(v) / Math.log(2)


fix(model): fixed callback for get_one

fix(model): fixed callback for get_one

--- src/model.coffee
+++ src/model.coffee
@@ -186,7 +186,7 @@

       callback = callbackOrObservable

-    model.get { id: id }, callbackOrObservable
+    model.get { id: id }, callback

   # Create new object with given params
   #


adding 'modelUtils.getConstructor()'

adding 'modelUtils.getConstructor()'

--- shared/model_utils.coffee
+++ shared/model_utils.coffee
@@ -18,6 +18,13 @@
 utils.getCollectionConstructor = (path) ->
   path = utils.underscorize(path)
   classMap[path] || require(rendr.entryPath + "/collections/#{path}")
+
+utils.getConstructor = (type, path) ->
+  method = if type is 'model'
+    utils.getModelConstructor
+  else
+    utils.getCollectionConstructor
+  method(path)

 utils.isModel = (obj) ->
   obj instanceof BaseModel


Added "period" alias for "scaleTo" option

--- Smooth.coffee
+++ Smooth.coffee
@@ -147,6 +147,9 @@
 		(t) -> f(t*scaleFactor)

 Smooth = (arr, config = {}) ->
+	#Alias 'period' to 'scaleTo'
+	config.scaleTo ?= config.period
+
 	config[k] ?= v for own k,v of defaultConfig #fill in defaults

 	#Get the interpolator class according to the configuration


no longer rely on consolidateSelections to remove cursor :tulip:

--- spec/snippets-spec.coffee
+++ spec/snippets-spec.coffee
@@ -474,7 +474,7 @@
         editor.setCursorScreenPosition([0, 0])
         editor.insertText('t9b')
         simulateTabKeyEvent()
-        editor.consolidateSelections()
+        editor.getCursors()[0].destroy()
         simulateTabKeyEvent()

         expect(editor.lineTextForBufferRow(1)).toEqual("without placeholder   ")
@@ -485,7 +485,7 @@
         simulateTabKeyEvent()
         editor.insertText('test')

-        editor.consolidateSelections()
+        editor.getCursors()[0].destroy()
         editor.moveDown() # this should destroy the previous expansion
         editor.moveToBeginningOfLine()


Fix subscription page for members of a group

For a member of a group LimitationsManager.userHasSubscriptionOrIsGroupMember
doesn't return any subscription, so when the controller tries to
access subscription.recurlySubscription_id there's an error.

SubscriptionViewModelBuilder already knows how to handle the case
in which the user is a member of a group but has not subscription
of their own, so guarding against the crash is enough to fix
the issue.

--- services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
+++ services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
@@ -97,7 +97,7 @@
 				logger.log user: user, "redirecting to plans"
 				res.redirect "/user/subscription/plans"
 			else
-				RecurlyWrapper.getSubscription subscription.recurlySubscription_id,
+				RecurlyWrapper.getSubscription subscription?.recurlySubscription_id,
 					includeAccount: true,
 					(err, usersSubscription)->
 						# always render the page, but skip the recurly link if


Change name and logic of normalizeAuthError method

Signed-off-by: amdomanska <47b3af9da9b3132ba7a10479a787862f50e1928b@resin.io>
Change-type: patch

--- lib/auth.coffee
+++ lib/auth.coffee
@@ -23,10 +23,10 @@
 	twoFactor = require('./2fa')(deps, opts)
 	exports = {}

-	normalizeError = (err) ->
+	normalizeAuthError = (err) ->
 		if err.statusCode == 401
 			return new errors.ResinNotLoggedIn()
-		else if err.code == 'ResinMalformedToken' and not savedToken?
+		else if err.code == 'ResinMalformedToken'
 			return new errors.ResinNotLoggedIn()
 		else
 			return err
@@ -50,7 +50,7 @@
 			.tap (body) ->
 				userDetailsCache = body
 			.catch (err) ->
-				throw normalizeError(err)
+				throw normalizeAuthError(err)

 	###*
 	# @summary Return current logged in username
@@ -250,7 +250,7 @@
 		.then (savedToken) ->
 			return savedToken
 		.catch (err) ->
-			throw normalizeError(err)
+			throw normalizeAuthError(err)
 		.asCallback(callback)

 	###*


Fixed notFound template.

Fixed notFound template.

--- lib/base.coffee
+++ lib/base.coffee
@@ -101,7 +101,7 @@

 notFound = ->
   # TODO: Is there a better/official way?
-  Meteor.Router._page = 'notfound'
+  Meteor.Router._page = 'notFound'
   Meteor.Router._pageDeps.changed()

 redirectHighlightId = (highlightId) ->
@@ -381,4 +381,4 @@

   '*': ->
     setSession()
-    'notfound'
+    'notFound'


Commit file grafts explicitly target child lis, not descendants.

--- lib/blog.coffee
+++ lib/blog.coffee
@@ -147,7 +147,7 @@
             if ! commit.hasParent
               commitGraft['.files'] = ($files) -> $files.remove()
             else
-              commitGraft['.files li'] = commit.files.map (file) ->
+              commitGraft['.files > li'] = commit.files.map (file) ->
                 fileGraft =
                   '.filename': file.filename


Update gruntfile.coffee

Update gruntfile.coffee

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -42,18 +42,18 @@
         stamp: '<%= Math.floor(Date.now() / 1000 / 60) %>'
       normal:
         src: 'src/assets/images/sprites/1x/*.png'
+        cssTemplate: 'src/assets/styles/sprites/1x.mustache'
+        algorithm: 'binary-tree'
+        destCSS: 'src/assets/styles/sprites/1x.styl'
         destImg: '.tmp/assets/images/sprites/1x-<%= sprite.options.stamp %>.png'
-        destCSS: 'src/assets/styles/sprites/1x.styl'
-        cssTemplate: 'src/assets/styles/sprites/1x.mustache'
         imgPath: '../images/sprites/1x-<%= sprite.options.stamp %>.png'
-        algorithm: 'binary-tree'
       retina:
         src: 'src/assets/images/sprites/2x/*.png'
+        cssTemplate: 'src/assets/styles/sprites/2x.mustache'
+        algorithm: 'binary-tree'
+        destCSS: 'src/assets/styles/sprites/2x.styl'
         destImg: '.tmp/assets/images/sprites/2x-<%= sprite.options.stamp %>.png'
-        destCSS: 'src/assets/styles/sprites/2x.styl'
-        cssTemplate: 'src/assets/styles/sprites/2x.mustache'
         imgPath: '../images/sprites/2x-<%= sprite.options.stamp %>.png'
-        algorithm: 'binary-tree'

     image:
       sprites:


Removed pointless arrow (pun intended)

Removed pointless arrow (pun intended)

--- core/app/backbone/views/auto_complete/auto_complete_search_view.coffee
+++ core/app/backbone/views/auto_complete/auto_complete_search_view.coffee
@@ -17,8 +17,8 @@

   initSearchCollection: (collection) ->
     @search_collection = collection
-    @listenTo @search_collection, 'request', -> @setLoading()
-    @listenTo @search_collection, 'reset', -> @unsetLoading()
+    @listenTo @search_collection, 'request', @setLoading
+    @listenTo @search_collection, 'reset', @unsetLoading

   initTextInputView: (placeholder) ->
     @_text_input_view = new Backbone.Factlink.TextInputView


no duplicate users

no duplicate users

--- lib/chat.coffee
+++ lib/chat.coffee
@@ -12,20 +12,19 @@
   io.sockets.on "connection", (socket) ->
     socket.on 'subscribe', (data)->
       socket.handshake.getSession (err, session) ->
-        for user in user_list
-          if user.socket_id is socket.id
-            removeFromArray user_list, user
         if session.passport? and session.passport.user?
-          user = session.passport.user
-          user.socket_id = socket.id
-          user_list.push user
+          newUser = session.passport.user
         else
-          user_list.push {
-            socket_id: socket.id
+          newUser = {
             nickname: socket.handshake.randomNickname
             avatar_url : '/images/anonymous.png'
             role: 'guest'
           }
+        newUser.socket_id = socket.id
+        for user in user_list
+          if user.nickname is newUser.nickname
+            removeFromArray user_list, user
+        user_list.push newUser
         io.sockets.emit('user_list', user_list)
         Message.find({}).sort({'date': 1}).limit(100).exec (err, messages)->
           pojos = []


Add no-coverage pragmas to prevent instrumentation of pouch queries

[ci skip]

--- src/pouch.coffee
+++ src/pouch.coffee
@@ -117,6 +117,7 @@
     # The path for a file/folder in root will be '',
     # not '.' as with node's path.dirname
     addByPathView: (callback) =>
+        ### !pragma no-coverage-next ###
         query = (
             (doc) ->
                 if 'docType' of doc
@@ -128,6 +129,7 @@

     # Create a view to find files by their checksum
     addByChecksumView: (callback) =>
+        ### !pragma no-coverage-next ###
         query = (
             (doc) ->
                 if 'checksum' of doc
@@ -137,6 +139,7 @@

     # Create a view to find file/folder by their _id on a remote cozy
     addByRemoteIdView: (callback) =>
+        ### !pragma no-coverage-next ###
         query = (
             (doc) ->
                 if 'remote' of doc


Allow the document module to be require-d.

--- src/quill.coffee
+++ src/quill.coffee
@@ -3,6 +3,7 @@
 Delta         = require('rich-text/lib/delta')
 EventEmitter2 = require('eventemitter2').EventEmitter2
 dom           = require('./lib/dom')
+Document      = require('./core/document')
 Editor        = require('./core/editor')
 Format        = require('./core/format')
 Normalizer    = require('./core/normalizer')
@@ -52,6 +53,7 @@
       when 'format'     then return Format
       when 'normalizer' then return Normalizer
       when 'dom'        then return dom
+      when 'document'   then return Document
       when 'range'      then return Range
       else return null


Factor out iteration helpers

Factor out iteration helpers

--- src/range.coffee
+++ src/range.coffee
@@ -111,57 +111,35 @@

     # Move the start container to the last leaf before any sibling boundary.
     # All children of the start container will fall within the range.
-    # This step obviates the recursion when iterating over the text.
     while startContainer.childNodes.length and startOffset > 0
       startContainer = startContainer.childNodes[startOffset-1]
       startOffset = startContainer.length or startContainer.childNodes.length

-    # Find the first TextNode.
+    # Find the first TextNode not excluded by the start offset.
     node = startContainer
-    while node?
-      # Skip to the first leaf Node, since all children are included.
-      # This step substitutes for recursion, capitalizing on the preconditions.
-      while node.firstChild?
-        node = node.firstChild
-
-      # Terminate on the first TextNode not excluded by the offset.
+    while node? and node = firstLeaf(node)
       if node.nodeType is Util.NodeTypes.TEXT_NODE
         if not (node is startContainer and startOffset isnt 0)
           start = node
           break

-      # Skip to the next branch, staying within the common ancestor.
-      while not (node.nextSibling? or node is commonAncestor)
-        node = node.parentNode
-
-      node = node.nextSibling
+      node = nextBranch(commonAncestor, node)

     # Move the end container to the first leaf before any sibling boundary.
     # All children of the end container will fall within the range.
-    # This step obviates the recursion when iterating over the text in reverse.
     while endOffset < endContainer.childNodes.length
       endContainer = endContainer.childNodes[endOffset]
       endOffset = 0

-    # Find the last TextNode.
+    # Find the last TextNode not excluded by the end offset.
     node = endContainer
-    while node?
-      # Skip to the last leaf Node, since all children are included.
-      # This step substitutes for recursion, capitalizing on the preconditions.
-      while node.lastChild?
-        node = node.lastChild
-
-      # Terminate on the first TextNode not excluded by the offset.
+    while node? and node = lastLeaf(node)
       if node.nodeType is Util.NodeTypes.TEXT_NODE
         if not (node is endContainer and endOffset is 0)
           end = node
           break

-      # Skip to the previous branch, staying within the common ancestor.
-      while not (node.previousSibling? or node is commonAncestor)
-        node = node.parentNode
-
-      node = node.previousSibling
+      node = previousBranch(commonAncestor, node)

     return new Range.NormalizedRange({commonAncestor, start, end})

@@ -397,5 +375,31 @@
     }

+firstLeaf = (node) ->
+  while node.hasChildNodes()
+    node = node.firstChild
+  return node
+
+
+lastLeaf = (node) ->
+  while node.hasChildNodes()
+    node = node.lastChild
+  return node
+
+
+nextBranch = (root, node) ->
+  while node? and node isnt root
+    if node.nextSibling? then return node.nextSibling
+    node = node.parentNode
+  return null
+
+
+previousBranch = (root, node) ->
+  while node? and node isnt root
+    if node.previousSibling? then return node.previousSibling
+    node = node.parentNode
+  return null
+
+
 # Export Range object.
 module.exports = Range


Reorder reply and messageRoom functions

Reorder reply and messageRoom functions

--- src/robot.coffee
+++ src/robot.coffee
@@ -293,6 +293,16 @@
   send: (user, strings...) ->
     @adapter.send user, strings...

+  # Public: A helper reply function which delegates to the adapter's reply
+  # function.
+  #
+  # user    - A User instance.
+  # strings - One or more Strings for each message to send.
+  #
+  # Returns nothing.
+  reply: (user, strings...) ->
+    @adapter.reply user, strings...
+
   # Public: A helper send function to message a room that the robot is in.
   #
   # room    - String designating the room to message.
@@ -302,16 +312,6 @@
   messageRoom: (room, strings...) ->
     user = { room: room }
     @adapter.send user, strings...
-
-  # Public: A helper reply function which delegates to the adapter's reply
-  # function.
-  #
-  # user    - A User instance.
-  # strings - One or more Strings for each message to send.
-  #
-  # Returns nothing.
-  reply: (user, strings...) ->
-    @adapter.reply user, strings...

   # Public: Get an Array of User objects stored in the brain.
   #


Serialize findOptions rather than `modelState`

--- lib/find.coffee
+++ lib/find.coffee
@@ -29,7 +29,7 @@
       minimum: 0
       description: 'When you type in the buffer find box, you must type this many characters to automatically search'

-  activate: ({@modelState, findHistory, replaceHistory, pathsHistory}={}) ->
+  activate: ({findOptions, findHistory, replaceHistory, pathsHistory}={}) ->
     atom.workspace.addOpener (filePath) ->
       new ResultsPaneView() if filePath is ResultsPaneView.URI

@@ -38,7 +38,7 @@
     @replaceHistory = new History(replaceHistory)
     @pathsHistory = new History(pathsHistory)

-    @findOptions = new FindOptions(@modelState)
+    @findOptions = new FindOptions(findOptions)
     @findModel = new BufferSearch(@findOptions)
     @resultsModel = new ResultsModel(@findOptions)

@@ -179,7 +179,7 @@
     @subscriptions = null

   serialize: ->
-    modelState: @findOptions?.serialize() ? @modelState
+    findOptions: @findOptions.serialize()
     findHistory: @findHistory.serialize()
     replaceHistory: @replaceHistory.serialize()
     pathsHistory: @replaceHistory.serialize()


no-op.

no-op.

--- src/scope.coffee
+++ src/scope.coffee
@@ -59,7 +59,7 @@

   # Gets the type of a variable.
   type: (name) ->
-    for v in @variables when v.name is name then return v.type
+    return v.type for v in @variables when v.name is name
     null

   # If we need to store an intermediate result, find an available name for a


#update #13

#update #13

--- lib/main.coffee
+++ lib/main.coffee
@@ -20,12 +20,18 @@
 # Keep a global reference of the window object, if you don't, the window will
 # be closed automatically when the JavaScript object is garbage collected.
 mainWindow = null
-require "C:\\Users\\wii\\AppData\\Local\\Google\\Chrome\\User\ Data\\PepperFlash\\19.0.0.226\\manifest.json"
+# require "C:\\Users\\wii\\AppData\\Local\\Google\\Chrome\\User\ Data\\PepperFlash\\19.0.0.226\\manifest.json"

-console.log "use ppapi-flash-path: #{packageFile.flash.path}"
-console.log "use ppapi-flash-version: #{packageFile.flash.version}"
-app.commandLine.appendSwitch 'ppapi-flash-path', packageFile.flash.path
-app.commandLine.appendSwitch 'ppapi-flash-version', packageFile.flash.version
+flashPath = packageFile.flash.path
+flashVersion = packageFile.flash.version
+if process.platform != 'win32'
+  flashPath = "C:\\Users\\wii\\AppData\\Local\\Google\\Chrome\\User\ Data\\PepperFlash\\19.0.0.226\\pepflashplayer.dll"
+
+console.log "use ppapi-flash-path: #{flashPath}"
+console.log "use ppapi-flash-version: #{flashVersion}"
+if
+app.commandLine.appendSwitch 'ppapi-flash-path', flashPath
+app.commandLine.appendSwitch 'ppapi-flash-version', flashVersion
 # Quit when all windows are closed.
 app.on 'window-all-closed', ->
   # On OS X it is common for applications and their menu bar


for/of instead of for/in

for/of instead of for/in

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- src/stats.coffee
+++ src/stats.coffee
@@ -17,7 +17,7 @@
   combine: (a) ->
     finalStats = {}

-    for stat, name in lol.stats.names
+    for stat, name of lol.stats.names
       finalStats[stat] = 0

       for el in a


Rename a local variable in "translate" function

ref #2

--- lib/main.coffee
+++ lib/main.coffee
@@ -24,26 +24,26 @@
     editor = atom.workspace.getActiveTextEditor()
     return unless editor?

-    texts = []
+    targets = []

     # Get texts from selected texts
     for range in editor.getSelectedBufferRanges()
       if (range.start.row == range.end.row) && (range.start.column == range.end.column)
         continue
       text = editor.getTextInRange(range)
-      texts.push({
+      targets.push({
         range: range
         text: text
       })

-    if texts.length == 0
+    if targets.length == 0
       # Get words from cursor positions if no text are selected
       for cursor in editor.getCursors()
         beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
         endWord = cursor.getEndOfCurrentWordBufferPosition()
         range = new Range(beginWord, endWord)

-        texts.push({
+        targets.push({
           range: range
           text: editor.getTextInRange(range)
         })


Readded 'Rename' and 'Copy Full Path' command sto palette

--- lib/main.coffee
+++ lib/main.coffee
@@ -21,6 +21,8 @@
     atom.workspaceView.command 'tree-view:add-folder', => @createView().add(false)
     atom.workspaceView.command 'tree-view:duplicate', => @createView().copySelectedEntry()
     atom.workspaceView.command 'tree-view:remove', => @createView().removeSelectedEntries()
+    atom.workspaceView.command 'tree-view:rename', => @createView().moveSelectedEntry()
+    atom.workspaceView.command 'tree-view:copy-full-path', => @createView().copySelectedEntryPath(false)

   deactivate: ->
     @treeView?.deactivate()


:gift: sugar command for "* keystroke to use * register #272

Thanks @bonson

--- lib/main.coffee
+++ lib/main.coffee
@@ -125,6 +125,7 @@
       'reset-normal-mode': -> @resetNormalMode(userInvocation: true)
       'set-register-name': -> @register.setName() # "
       'set-register-name-to-blackhole': -> @register.setName('_')
+      'set-register-name-to-system-clipboard': -> @register.setName('*')
       'operator-modifier-characterwise': -> @emitDidSetOperatorModifier(wise: 'characterwise')
       'operator-modifier-linewise': -> @emitDidSetOperatorModifier(wise: 'linewise')
       'operator-modifier-occurrence': -> @emitDidSetOperatorModifier(occurrence: true)


2015-05-08T12:28:07.194Z

--- lib/main.coffee
+++ lib/main.coffee
@@ -664,6 +664,60 @@

             next null, data

+  create: (command, next) ->
+
+    @shell
+
+    { repo } = command.args
+
+    { github } = @cli.cache.get()
+
+    github = merge github or {},  command.args
+
+    delete github.create
+
+    delete github.repo
+
+    delete github.login
+
+    delete github.init
+
+    delete github.templates
+
+    delete github.force
+
+    delete github.commit
+
+    @api ?= new GitHubApi github
+
+    @authenticate github, (err, github) =>
+
+      if err then return next @error(err, github), null
+
+      payload = { "name": repo }
+
+      @api.repos.create payload, (err, response) =>
+
+        if err then return next @error(err, response), null
+
+        if response.id
+
+          github.repos ?= {}
+
+          github.repos[response.name] = response
+
+          @cli.cache.put "github", github
+
+          @cli.cache.save()
+
+          user = github.username
+
+          @init command, response, (err, res) =>
+
+            @cli.console.info "#{user}/#{repo}"
+
+            next null, response
+
   "license?": (command, next) ->

     if command.args.recursive
@@ -744,57 +798,9 @@

   "create?": (command, next) ->

-    @shell
-
-    { repo } = command.args
-
-    { github } = @cli.cache.get()
-
-    github = merge github or {},  command.args
-
-    delete github.create
-
-    delete github.repo
-
-    delete github.login
-
-    delete github.init
-
-    delete github.templates
-
-    delete github.force
-
-    delete github.commit
-
-    @api ?= new GitHubApi github
-
-    @authenticate github, (err, github) =>
-
-      if err then return next @error(err, github), null
-
-      payload = { "name": repo }
-
-      @api.repos.create payload, (err, response) =>
-
-        if err then return next @error(err, response), null
-
-        if response.id
-
-          github.repos ?= {}
-
-          github.repos[response.name] = response
-
-          @cli.cache.put "github", github
-
-          @cli.cache.save()
-
-          user = github.username
-
-          @init command, response, (err, res) =>
-
-            @cli.console.info "#{user}/#{repo}"
-
-            next null, response
+    @create command, (err, res) ->
+
+      next err, res

   "delete?": (command, next) ->


More type handling for generic logging

More type handling for generic logging

--- src/utils.coffee
+++ src/utils.coffee
@@ -2,6 +2,7 @@
   inspect: (obj, label) ->
     # Log the properties and values of an object for debugging
     # Optional label describes object dump
+    # Also works on arrays and strings, with unsurprising results.
     #
     # Usage:
     #   var car = { color: 'red', drive: function() {} }
@@ -16,18 +17,29 @@
     #       color: red
     #       drive(): <function>

-    defined = (if obj? then '' else '<undefined>')
+    defined_str   = (if obj? then '' else '<undefined>')
+    spacer        = "  " # Indent results
+    is_array      = obj instanceof Array
+    is_string     = typeof obj == 'string'
+    is_number     = typeof obj == 'number'

     if label?
-      Kona.debug "Dumping #{label}: #{defined}"
+      Kona.debug "Dumping #{label}: #{defined_str}"
     else
-      Kona.debug "Dumping object: #{defined}"
+      Kona.debug "Dumping #{ if is_array then 'array' else typeof obj}: #{defined_str}"

-    for own key, value of obj
-      if value instanceof Function
-        Kona.debug "  #{key}(): <function>"
-      else
-        Kona.debug "  #{key}: #{value}"
+    if is_array
+      Kona.debug "#{spacer}[#{obj}]"    # [1,2,3]
+    else if is_string
+      Kona.debug "#{spacer}\"#{obj}\""  # "test"
+    else if is_number
+      Kona.debug "#{spacer}#{obj}"      # 5
+    else
+      for own key, value of obj         # x: 25
+        if value instanceof Function
+          Kona.debug "#{spacer}#{key}(): <function>"
+        else
+          Kona.debug "#{spacer}#{key}: #{value}"

   randomFromTo: (from, to) ->


Add a comment

Add a comment

--- src/smc-webapp/billing.cjsx
+++ src/smc-webapp/billing.cjsx
@@ -34,6 +34,7 @@

 STUDENT_COURSE_PRICE = require('smc-util/upgrade-spec').upgrades.subscription.student_course.price.month4

+# TODO: Upgrade to v3 and Stripe Elements
 load_stripe = (cb) ->
     if Stripe?
         cb()


fix unnamed assert

fix unnamed assert

--- src/test/test_config.coffee
+++ src/test/test_config.coffee
@@ -46,7 +46,7 @@
     t.end()

   test 'config sprite', (t) ->
-    t.ok sprites = layout.find_path('grid.sprites'), "can not find sprites"
+    t.ok sprites = layout.find_path('grid.sprites'), "find sprites"
     t.ok sprite = sprites.find_child(), "missing sprite"

     world.send('reset')


(trivial) clarify a comment

(trivial) clarify a comment

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -970,7 +970,7 @@
             exports.save_BUP_age
                 database                 : db
                 age_m                    : 60*24*14 # 2 weeks: consider all projects edited in the last 2 weeks
-                time_since_last_backup_m : 60*12    # 1 day: ensure they have a bup snapshot that is at most 12 hours old
+                time_since_last_backup_m : 60*12    # 1 day: ensure they have a bup snapshot that is at most 12 hours old if edited since last snapshot
                 threads                  : 2
                 cb                       : cb
     ], (err) ->


Don't use jquery in BindingsView

Don't use jquery in BindingsView

--- lib/bindings-view.coffee
+++ lib/bindings-view.coffee
@@ -1,5 +1,4 @@
 happens = require 'happens'
-{$} = require 'atom-space-pen-views'

 module.exports = class BindingsView
   constructor: ->
@@ -49,7 +48,7 @@
     li.addEventListener('click', @onTextureClick, false)

   removeTexture: (filePath) ->
-    li = $('li[data-file="'+filePath+'"]')[0]
+    li = document.querySelector('li[data-file="'+filePath+'"]')
     @list.removeChild(li) if li

   onTextureClick: (event) =>


fix fullscreen functionality, add check for edge case

--- src/textorum/helper.coffee
+++ src/textorum/helper.coffee
@@ -404,9 +404,7 @@
           depth += 1
           node = node.firstChild
         else
-          while node.nextSibling is null
-            # FIXME: On fullscreen, I believe at some point parentNode is null,
-            # resulting call to node.nextSibling in the while above error.
+          while node.nextSibling is null and node.parentNode isnt null
             node = node.parentNode
             depth -= 1
             if handler.endTag


:art: Remove duplication from `seek` and `seekToSourcePosition`

--- src/transform-layer.coffee
+++ src/transform-layer.coffee
@@ -69,7 +69,25 @@
     else
       {value: undefined, done: true}

-  seek: (position, clip=CLIP_BACKWARD) ->
+  seek: (position, clip) ->
+    calculateOvershoot = (position, lastPosition, lastSourcePosition) =>
+      overshoot = position.traversalFrom(lastPosition)
+      lastSourcePosition = lastSourcePosition.traverse(overshoot)
+      @position = position
+      @sourcePosition = lastSourcePosition
+
+    @_seek (=> @position), position, calculateOvershoot, clip
+
+  seekToSourcePosition: (position, clip) ->
+    calculateOvershoot = (position, lastPosition, lastSourcePosition) =>
+      overshoot = position.traversalFrom(lastSourcePosition)
+      lastPosition = lastPosition.traverse(overshoot)
+      @position = lastPosition
+      @sourcePosition = position
+
+    @_seek (=> @sourcePosition), position, calculateOvershoot, clip
+
+  _seek: (seekPropertyFn, position, calculateOvershoot, clip = CLIP_BACKWARD) ->
     @position = Point.zero()
     @sourcePosition = Point.zero()
     @transformBuffer.reset(@position, @sourcePosition)
@@ -77,13 +95,13 @@

     return if position.isZero()

-    until @position.compare(position) >= 0
+    until seekPropertyFn().compare(position) >= 0
       lastPosition = @position
       lastSourcePosition = @sourcePosition
       {done} = @next()
       return if done

-    if @clipping? and @position.compare(position) > 0
+    if @clipping? and seekPropertyFn().compare(position) > 0
       switch clip
         when CLIP_FORWARD
           return
@@ -92,41 +110,8 @@
           @sourcePosition = lastSourcePosition
           return

-    unless @position.compare(position) is 0
-      overshoot = position.traversalFrom(lastPosition)
-      lastSourcePosition = lastSourcePosition.traverse(overshoot)
-      @position = position
-      @sourcePosition = lastSourcePosition
-    @transformBuffer.reset(@position, @sourcePosition)
-
-  seekToSourcePosition: (position, clip = CLIP_BACKWARD) ->
-    @position = Point.zero()
-    @sourcePosition = Point.zero()
-    @transformBuffer.reset(@position, @sourcePosition)
-    position = position.sanitize()
-
-    return if position.isZero()
-
-    until @sourcePosition.compare(position) >= 0
-      lastPosition = @position
-      lastSourcePosition = @sourcePosition
-      {done} = @next()
-      return if done
-
-    if @clipping? and @sourcePosition.compare(position) > 0
-      switch clip
-        when CLIP_FORWARD
-          return
-        when CLIP_BACKWARD
-          @position = lastPosition
-          @sourcePosition = lastSourcePosition
-          return
-
-    unless @sourcePosition.compare(position) is 0
-      overshoot = position.traversalFrom(lastSourcePosition)
-      lastPosition = lastPosition.traverse(overshoot)
-      @position = lastPosition
-      @sourcePosition = position
+    unless seekPropertyFn().compare(position) is 0
+      calculateOvershoot(position, lastPosition, lastSourcePosition)

     @transformBuffer.reset(@position, @sourcePosition)


more details for events

more details for events

--- src/zombie/resources.coffee
+++ src/zombie/resources.coffee
@@ -651,7 +651,7 @@
            })

         # Report the new connection
-        @resources.browser.emit("newConnection", {})
+        @resources.browser.emit("newConnection", {}, httpRequest.host, httpRequest.port)
         @resources.spdy_agents.push(cagent)

       httpRequest.agent = cagent
@@ -697,10 +697,10 @@
       @resources.browser.emit("push", push)

     req.on "newConnection", (endpoint)=>
-      @resources.browser.emit("newConnection", endpoint)
+      @resources.browser.emit("newConnection", endpoint, httpRequest.host, httpRequest.port)

     req.on "protocolNegotiated", (protocol)=>
-      @resources.browser.emit("protocolNegotiated", protocol)
+      @resources.browser.emit("protocolNegotiated", protocol, httpRequest.host, httpRequest.port)

     req.on "error", (error)=>
       #console.log error


Removal of Content Tabs

Removal of Content Tabs

Part 2; Issue #31

--- resources/assets/scripts/app/app.coffee
+++ resources/assets/scripts/app/app.coffee
@@ -168,7 +168,7 @@

         state.state 'content',
             abstract: true
-            templateUrl: '/templates/content/_layout.html'
+            template: '<ui-view/>'

         state.state 'content.tracks',
             templateUrl: '/templates/tracks/index.html'


Clearer logic for triggering labels-loading

--- services/web/public/coffee/ide/editor/directives/aceEditor/labels/LabelsManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/labels/LabelsManager.coffee
@@ -16,16 +16,16 @@
 			onChange = (change) =>
 				if change.remote
 					return
+				if change.action not in ['remove', 'insert']
+					return
 				cursorPosition = @editor.getCursorPosition()
 				end = change.end
 				range = new Range(end.row, 0, end.row, end.column)
 				lineUpToCursor = @editor.getSession().getTextRange(range)
 				commandFragment = getLastCommandFragment(lineUpToCursor)
-				if (
-					change.action in ['remove', 'insert'] and
-					((_.any(change.lines, (line) -> line.match(/\\label\{[^\}\n\\]{0,80}\}/))) or
-					 (commandFragment?.length > 2 and commandFragment.slice(0,7) == '\\label{'))
-				)
+				linesContainLabel = _.any(change.lines, (line) -> line.match(/\\label\{[^\}\n\\]{0,80}\}/))
+				lastCommandFragmentIsLabel = commandFragment?.slice(0,7) == '\\label{'
+				if linesContainLabel or lastCommandFragmentIsLabel
 					@scheduleLoadCurrentDocLabelsFromServer()

 			@editor.on "changeSession", (e) =>


fix(funnel): prevent matching 'label' in 'unlabel'

--- scripts/gitter-activity-notifier.coffee
+++ scripts/gitter-activity-notifier.coffee
@@ -64,7 +64,8 @@
     # help!
     helper = (items, id, prop, value, parts) ->

-        if items[id] and items[id][prop].indexOf(value) == -1
+        # split to prevent matching 'label' in 'unlabel'
+        if items[id] and items[id][prop].split(',').indexOf(value) == -1
             items[id][prop] += ',' + value
         # add new notification
         else


Fix endpoint error

Fix endpoint error

--- server/middleware/trial-requests.coffee
+++ server/middleware/trial-requests.coffee
@@ -45,7 +45,7 @@
     applicantID = req.query.applicant
     return next() unless applicantID
     throw new errors.UnprocessableEntity('Bad applicant id') unless utils.isID(applicantID)
-    throw new errors.Forbidden('May not fetch for anyone but yourself') unless req.user?.id is applicantID or req.user.isAdmin()
+    throw new errors.Forbidden('May not fetch for anyone but yourself') unless req.user?.id is applicantID or req.user?.isAdmin()
     trialRequests = yield TrialRequest.find({applicant: mongoose.Types.ObjectId(applicantID)})
     trialRequests = (tr.toObject({req: req}) for tr in trialRequests)
     res.status(200).send(trialRequests)


removed log statements

removed log statements

--- interface.coffee
+++ interface.coffee
@@ -14,7 +14,6 @@

     # iterate over all tokens and save them in an array
     this.initTokens()
-    console.log this.tokens

     $(document).keydown (e) ->
       returnStatement = true
@@ -202,7 +201,6 @@
   changeTokenKind: (kind) ->
     return if this.selectedTokenIndex < 0

-    console.log "change token kind to '#{kind}'"
     modifier = (token, kind) ->
       unless token.kind == kind
         token.kind = kind
@@ -239,7 +237,6 @@

   render: (template, data) ->
     window.annotationDocumentPayload = data
-    console.log 'render interface with payload:', data
     super

   saveAnnotation: ->
@@ -275,7 +272,6 @@

           payload['content'][paragraphIndex][sentenceIndex].push(tokenHash)

-    console.log 'save payload:', payload
     this.saveChanges(payload)

 window.ner_complete = new ner_complete()


first step towards flipbook

first step towards flipbook

--- intervals.coffee
+++ intervals.coffee
@@ -429,13 +429,14 @@
     ctx.lineWidth = 1

 draw_chord_diagram = (positions, options={}) ->
-  {barres} = options
+  {barres, dy} = options
+  dy ||= 0
   style = ChordDiagramStyle

   finger_coordinates = ({string, fret}) ->
     return {
       x: style.h_gutter + string * style.string_spacing,
-      y: style.v_gutter + style.above_fretboard + (fret - 1) * style.fret_height + style.fret_height / 2
+      y: style.v_gutter + style.above_fretboard + (fret - 0.5) * style.fret_height + dy
     }

   draw_finger_position = (position, options={}) ->
@@ -606,8 +607,8 @@
     m = ctx.measureText(class_name)
     ctx.fillText class_name, r2 * Math.cos(a) - m.width / 2, r2 * Math.sin(a) + m.emHeightDescent

-chord_page = (chord, options) ->
-  {best_fingering} = options || {}
+chord_page = (chord, options={}) ->
+  {best_fingering, dy} = options

   pitch_fingers = []
   finger_positions_each (finger_position) ->
@@ -653,17 +654,29 @@
         fingering = best_fingering_for(chord, pitch) if best_fingering

         position.color = degree_colors[position.degree_index] for position in fingering.positions
-        draw_chord_diagram fingering.positions, barres: fingering.barres
+        draw_chord_diagram fingering.positions, barres: fingering.barres, dy: dy

 chord_book = (options) ->
-  page_count = options.pages
   title = if options.best_fingering then "Chord Diagrams" else "Combined Chord Diagrams"
   book title, pages: options.pages, (page) ->
     for chord in Chords
       page -> chord_page chord, options

+chord_flipbook = (options) ->
+  chord = Chords[0]
+  title = "Chord Flipbook"
+  book title, (page) ->
+    for dy in [0...ChordDiagramStyle.fret_height]
+      page -> chord_page chord, dy: dy, only: 'E'
+
+
+#
+# Output Commands
+#
+
 # chord_page Chords[0], best_fingering: true
 # intervals_book by_root: true, pages: 1
 # intervals_book by_root: false
-chord_book best_fingering: 1, pages: 0
+chord_book best_fingering: 0, pages: 0
+# chord_flipbook pages: 0
 # chord_fingerings_page Chords[6], 'F'


Increase threshold and stop deep comparisons

--- spec/GetFeaturesYAPE.coffee
+++ spec/GetFeaturesYAPE.coffee
@@ -59,8 +59,8 @@
                 chai.expect(corners[0]).to.have.property 'score'
                 chai.expect(corners[0]).to.have.property 'level'
                 unless noflo.isBrowser()
-                  chai.expect(corners.length).to.be.within expected.length-100, expected.length+100
-                  chai.expect(corners.slice(0,100)).to.deep.equal expected.slice 0, 100
+                  chai.expect(corners.length).to.be.within expected.length-2000, expected.length+2000
+                  #chai.expect(corners.slice(0,100)).to.deep.equal expected.slice 0, 100
                 chai.expect(groups).to.have.length 1
                 chai.expect(groups[0]).to.equal id
                 done()


Fix: `condition` has to be dereferenced

Fix: `condition` has to be dereferenced

--- src/resources-packaged/ops/javascript/orbeon/util/FiniteStateMachine.coffee
+++ src/resources-packaged/ops/javascript/orbeon/util/FiniteStateMachine.coffee
@@ -18,8 +18,8 @@
 ORBEON.util.FiniteStateMachine =
     create: ({ events, elements, conditions, actions, transitions }) ->
         _.each transitions, (transition) ->
-            _.each transition.events, (event) ->
-                events[event] (event) ->
+            _.each transition.events, (eventName) ->
+                events[eventName] (event) ->
                     if transition.elements?                                                                             # State if per-element
                         elementsFromEvent = elements[transition.elements]
                         elts = elements[transition.elements] event                                                      # Get elements from event (e.g. editable inside cell for mouseover)
@@ -36,7 +36,8 @@
                             conditionsMet = true
                             if transition.conditions
                                 _.each transition.conditions, (condition) ->
-                                    conditionsMet = false if not condition(event)
+                                    conditionsMet = false if not conditions[c](event)
                             if conditionsMet == true
-                                globalState = transition.to
-                                _.each transition.actions, (action) -> actions[action] event
+                                globalState = transition.to if transition.to?
+                                _.each transition.actions, (action) ->
+                                    actions[action] event


:green_heart: Fix inconsistency in DOM related spec

--- spec/csv-editor-spec.coffee
+++ spec/csv-editor-spec.coffee
@@ -220,7 +220,7 @@
         expect(csvEditorElement.querySelector('atom-csv-preview .alert')).toExist()

       it 'disables the open table action', ->
-        expect(csvEditorElement.form.openTableEditorButton.disabled).toBeTruthy()
+        waitsFor -> csvEditorElement.form.openTableEditorButton.disabled

     describe 'changing the delimiter settings', ->
       beforeEach ->


Remove unused spies and variables

Remove unused spies and variables

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -3,7 +3,7 @@
 temp = require 'temp'

 describe "Whitespace", ->
-  [editor, buffer, filePath, directory] = []
+  [editor, buffer] = []

   beforeEach ->
     directory = temp.mkdirSync()
@@ -16,12 +16,7 @@

     atom.activatePackage('whitespace')

-  afterEach ->
-    fs.remove(filePath) if fs.exists(filePath)
-
   it "strips trailing whitespace before an editor saves a buffer", ->
-    spyOn(fs, 'write')
-
     config.set("whitespace.ensureSingleTrailingNewline", false)

     # works for buffers that are already open when extension is initialized
@@ -39,7 +34,6 @@

   describe "when the edit session is destroyed", ->
     beforeEach ->
-      spyOn(fs, 'write')
       config.set("whitespace.ensureSingleTrailingNewline", false)

       buffer.retain()


Activate language-gfm asynchronously

Activate language-gfm asynchronously

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -108,9 +108,13 @@

     beforeEach ->
       spyOn(atom.syntax, "addGrammar").andCallThrough()
-      atom.packages.activatePackage("language-gfm", sync: true)
-      expect(atom.syntax.addGrammar).toHaveBeenCalled()
-      grammar = atom.syntax.addGrammar.argsForCall[0][0]
+
+      waitsForPromise ->
+        atom.packages.activatePackage("language-gfm")
+
+      runs ->
+        expect(atom.syntax.addGrammar).toHaveBeenCalled()
+        grammar = atom.syntax.addGrammar.argsForCall[0][0]

     it "trims GFM text with a single space", ->
       editor.setGrammar(grammar)


Add some tests!

Add some tests!

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -51,6 +51,35 @@
     editor.insertText "don't trim me "
     editor.save()
     expect(editor.getText()).toBe "don't trim me \n"
+
+  describe "whitespace.ignoreLeadingWhitespaceOnCurrentLine config", ->
+    [originalConfigValue] = []
+    beforeEach ->
+      originalConfigValue = atom.config.get("whitespace.ignoreLeadingWhitespaceOnCurrentLine")
+      expect(originalConfigValue).toBe true
+
+    afterEach ->
+      atom.config.set("whitespace.ignoreLeadingWhitespaceOnCurrentLine", originalConfigValue)
+
+    it "doesn't trim whitespace on current line", ->
+      editor.insertText "  "
+      editor.setCursorBufferPosition([0,2])
+      editor.save()
+      expect(editor.getText()).toBe "  \n"
+
+    it "does trim whitespace on other lines", ->
+      editor.insertText "  \n  "
+      editor.setCursorBufferPosition([0,2])
+      editor.save()
+      expect(editor.getText()).toBe "  \n"
+
+    it "trims whitespace on current line if ignoreLeadingWhitespaceOnCurrentLine is false", ->
+      atom.config.set("whitespace.ignoreLeadingWhitespaceOnCurrentLine", false)
+
+      editor.insertText "  "
+      editor.setCursorBufferPosition([0,2])
+      editor.save()
+      expect(editor.getText()).toBe "\n"

   describe "whitespace.ensureSingleTrailingNewline config", ->
     [originalConfigValue] = []


collaborative-workspace: do not disconnect on join session event

--- client/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -184,7 +184,6 @@
     options                = @getOptions()
     options.sessionKey     = newOptions.sessionKey.trim()
     options.joinedASession = yes
-    @forceDisconnect()
     @destroy()

     @addSubView new CollaborativeWorkspace options


selective logo onclick route by loggedin state implemented

--- client/app/MainApp/maincontroller/mainviews/mainview.coffee
+++ client/app/MainApp/maincontroller/mainviews/mainview.coffee
@@ -96,7 +96,8 @@
       partial   : ""
       click     : (event)=>
         KD.utils.stopDOMEvent event
-        KD.getSingleton('router').handleRoute "/Activity", {entryPoint}
+        homeRoute = if KD.isLoggedIn() then "/Activity" else "/Home"
+        KD.getSingleton('router').handleRoute homeRoute, {entryPoint}

     loginLink = new CustomLinkView
       domId       : 'header-sign-in'


Workspace: In chat messages multiple links are supported now.

--- client/landing/CollaborativeWorkspace/panes/chatpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/chatpane.coffee
@@ -106,12 +106,19 @@
     @lastChatItem.timeAgo.setData new Date timestamp

   checkEmbeddableContent: ->
-    element = @lastMessage.getElement()
-    content = element.innerHTML
-    isUrl   = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test content
+    element  = @lastMessage.getElement()
+    content  = element.innerHTML
+    words    = content.split " "
+    urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
+    hasUrl   = no

-    if isUrl
-      element.innerHTML = "#{content}"
+    for word, index in words
+      if urlRegex.test word
+        hasUrl = yes
+        words.splice index, 1, "#{word}"
+
+    if hasUrl
+      element.innerHTML = words.join " "
       element.classList.add "tw-chat-media"

   scrollToTop: ->


ChatPane: define `scrollToBottom` method

--- client/landing/CollaborativeWorkspace/panes/chatpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/chatpane.coffee
@@ -99,7 +99,7 @@
     @updateDate details.time
     @lastMessage = @lastChatItem.message
     @emit "NewChatItemCreated"
-    @scrollToTop()
+    @scrollToBottom()

   appendToChatItem: (params) ->
     {details}  = params
@@ -129,9 +129,9 @@
       element.innerHTML = words.join " "
       element.classList.add "tw-chat-media"

-  scrollToTop: ->
-    $messages = @messages.$()
-    $messages.scrollTop $messages[0].scrollHeight
+  scrollToBottom: ->
+    element = @messages.getElement()
+    element.scrollTop = element.scrollHeight

   pistachio: ->
     """


Disable animations in tests

Disable animations in tests

--- core/app/assets/javascripts/test_counter_cookie_storage.coffee
+++ core/app/assets/javascripts/test_counter_cookie_storage.coffee
@@ -1,4 +1,5 @@
 if window.test_counter &&
     /^(::1|127\.0\.0\.1|localhost)$/.test(window.location.hostname)
+  window.$.fx.off = true
   window.addEventListener 'unload', -> #phantomjs doesn't support beforeunload
     document.cookie = "test_counter=#{window.test_counter};path=/"


Omg, it just about works

Omg, it just about works

--- tentacle-transformer.coffee
+++ tentacle-transformer.coffee
@@ -15,7 +15,13 @@
     msgProto = new @MicrobluProto msg
     msgProto.encodeDelimited buffer
     buffer.flip()
-    return Buffer.concat [buffer.buffer, new Buffer([0])]
+    console.log 'message of length', buffer.buffer.length, 'with head of', buffer.readVarint32(), 'and first byte', buffer.buffer[0]
+    byteString = ""
+    for num in [0..buffer.buffer.length-1]
+      byteString += buffer.readInt8(num).toString(16).toUpperCase()
+    console.log 'look at my string, it tastes like lemonade'
+    console.log byteString
+    buffer.buffer
     #return buffer.buffer

     #console.log "msg is: #{JSON.stringify(msgProto)}"


Removed deprecation tests

Removed deprecation tests

--- test/deprecated/test.coffee
+++ test/deprecated/test.coffee
@@ -16,8 +16,5 @@
   )

   test("0.16.0 deprecations", ->
-    kb.observables(new Backbone.Model(), ['hello'], {})
-    kb.utils.release(kb.viewModel(new Backbone.Model()))
-    kb.defaultWrapper(ko.observable(), 'test')
   )
 )


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -11,6 +11,7 @@
   equals engine.find('.findable').length, 1

   engine.add
-    find
+    findable: true
+

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -6,7 +6,9 @@
   engine.add
     class: "Awesome"
     x: 0
-    y:
+    y: 5
+
+

 test "#find", ->
   engine = Engine()


integrationSpec.coffee updated

integrationSpec.coffee updated

--- test/integrationSpec.coffee
+++ test/integrationSpec.coffee
@@ -8,7 +8,7 @@

       JasmineSugar.setup(context)

-    fit 'without dependencies', ->
+    it 'without dependencies', ->
       context.describe 'test', ->

         another   .is 'something else'
@@ -73,7 +73,17 @@
     #   'when collide with context methods names'
     #   'multiple describes (inner and parallel)'

-    xit 'with dependencies'
+    xit 'dependencies in one describe', ->
+      context.describe 'test', ->
+        collection.is 'something else'
+        another.is collection + ' another'
+
+        @it 'two', ->
+          expect(collection).toBeEqual 'something else'
+          expect(another).toBeEqual 'something else another'
+
+      JE.run(context)
+

 ## Planning DSL example:


Update config.cson

Update config.cson

--- atom/config.cson
+++ atom/config.cson
@@ -33,6 +33,8 @@
     forceShow: true
   "file-watcher":
     promptWhenChange: false
+  "line-ending-selector":
+    defaultLineEnding: "LF"
   "highlight-selected":
     hideHighlightOnSelectedWord: true
   linter:


Add configuration for the Overrides package

--- atom/config.cson
+++ atom/config.cson
@@ -43,7 +43,6 @@
     'text.plain'
   ]
 'tree-view':
-  'hideVcsIgnoredFiles': true
   'hideIgnoredNames': true
 'autosave': {}
 'remember-window':
@@ -60,3 +59,16 @@
   'fileBlacklist': '.*'
 'autocomplete-emojis':
   'fileWhitelist': '*.md, COMMIT_EDITMSG'
+'overrides':
+  'scopes':
+    'source':
+      'gfm':
+        'showInvisibles': false
+        'softWrap': true
+        'tabLength': 4
+      'java':
+        'tabLength': 4
+      'python':
+        'tabLength': 4
+      'shell':
+        'tabLength': 4


Disable React editor for now

Disable React editor for now

--- atom/config.cson
+++ atom/config.cson
@@ -31,7 +31,6 @@
     'lifted-dark-ui'
     'ya-railscasts-syntax'
   ]
-  'useReactEditor': true
 'whitespace':
   'ensureSingleTrailingNewline': true
 'auto-copyright':
@@ -52,6 +51,7 @@
   'width': 1509
   'height': 1237
   'x': 570
+  'treeWidth': 338
 'tabs-to-spaces':
   'onSave': 'untabify'
 'git-diff':


keyup with shiftKey reported the wrong .which

and made my "Show keys" mode look ridiculous.

I suspect it's a recently-introduced bug in WebKit...
I added a workaround.

--- client/demo.coffee
+++ client/demo.coffee
@@ -14,11 +14,11 @@
 $p = null # $p: DOM element for the pending key or null if key display mode is off
 keyInfo = (e) -> # returns a pair of the key name and an "is complete" flag
   s = '' # key name
-  e.shiftKey && s += 'Shift-'
+  e.shiftKey && e.which && s += 'Shift-'
   e.ctrlKey  && s += 'Ctrl-'
   e.altKey   && s += 'Alt-'
   k = CodeMirror.keyNames[e.which] || "[#{e.which}]"
-  c = k !in ['Shift', 'Ctrl', 'Alt'] # c: is the key combination complete?
+  c = !!e.which && k !in ['Shift', 'Ctrl', 'Alt'] # c: is the key combination complete?
   c && s += k
   [s, c]
 keyDownHandler = (e) ->


Fix issue with @overlay.removeEventListener

--- app/annotation/annotation.cjsx
+++ app/annotation/annotation.cjsx
@@ -109,7 +109,7 @@

       @refs.imageZoom.hide()
       @setState zoomImageSrc: null
-      @overlay.removeEventListener 'click'
+      @overlay.removeEventListener 'click', @closeZoom
     ), 250

   onImageLoad: (i, event) ->


add logging for ASpell worker errors

add logging for ASpell worker errors

--- app/coffee/ASpellWorker.coffee
+++ app/coffee/ASpellWorker.coffee
@@ -17,10 +17,14 @@
 			metrics.inc "aspellWorker-exit-" + @language
 		@pipe.on 'error', (err) =>
 			@state = 'error'
-			@callback err, []
+			logger.log process: @pipe.pid, error: err, stdout: output, stderr: error, lang: @language, "aspell worker error"
+			if @callback?
+				@callback err, []
 		@pipe.stdin.on 'error', (err) =>
 			@state = 'error'
-			@callback err, []
+			logger.log process: @pipe.pid, error: err, stdout: output, stderr: error, lang: @language, "aspell worker error on stdin"
+			if @callback?
+				@callback err, []

 		output = ""
 		@pipe.stdout.on "data", (chunk) =>


add null byte check for ops

add null byte check for ops

--- app/coffee/RedisManager.coffee
+++ app/coffee/RedisManager.coffee
@@ -162,6 +162,11 @@
 				return callback(error)

 			jsonOps = appliedOps.map (op) -> JSON.stringify op
+			if jsonOps.indexOf("\u0000") != -1
+				error = new Error("null bytes found in jsonOps")
+				logger.error err: error, doc_id: doc_id, jsonOps: jsonOps, error.message
+				return callback(error)
+
 			newDocLines = JSON.stringify(docLines)
 			if newDocLines.indexOf("\u0000") != -1
 				error = new Error("null bytes found in doc lines")


Specific Segwit fee management

Specific Segwit fee management

--- app/src/bitcoin/bitcoin.coffee
+++ app/src/bitcoin/bitcoin.coffee
@@ -9,5 +9,21 @@
   verifyAddress: (address) -> ledger.bitcoin.checkAddress(address)

   estimateTransactionSize: (inputsCount, outputsCount) ->
-    median = 148 * inputsCount + outputsCount * 34 + 10
-    min: median - inputsCount, max: median + inputsCount
+  	if inputsCount < 0xfd
+  		varintLength = 1
+  	else if inputsCount < 0xffff
+  		varintLength = 3
+  	else
+  		varintLength = 5
+  	if ledger.config.network.handleSegwit
+  		minNoWitness = varintLength + 4 + 2 + (59 * inputsCount) + 1 + (31 * outputsCount) + 4
+  		maxNoWitness = varintLength + 4 + 2 + (59 * inputsCount) + 1 + (33 * outputsCount) + 4
+  		minWitness = varintLength + 4 + 2 + (59 * inputsCount) + 1 + (31 * outputsCount) + 4 + (106 * inputsCount)
+  		maxWitness = varintLength + 4 + 2 + (59 * inputsCount) + 1 + (33 * outputsCount) + 4 + (108 * inputsCount)
+  		minSize = (minNoWitness * 3 + minWitness) / 4
+  		maxSize = (maxNoWitness * 3 + maxWitness) / 4
+  	else
+  	  minSize = varintLength + 4 + (145 * inputsCount) + 1 + (31 * outputsCount) + 4
+  	  maxSize = varintLength + 4 + (147 * inputsCount) + 1 + (33 * outputsCount) + 4
+
+    min: minSize, max: maxSize


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -113,7 +113,7 @@
   points = [
     Point()
   ]
-  4.circularPoints (p) ->
+  4.circularPoints (p, i) ->

 module undefined


Remove sidebar button works

Remove sidebar button works

--- app/views/x3d_page_view.coffee
+++ app/views/x3d_page_view.coffee
@@ -28,7 +28,10 @@
     @offset += 3

   removeObject: (event) =>
-    alert('remove')
+    if @currentObject
+      pa = @currentObject.parentElement
+      pa.removeChild(@currentObject)
+      @currentObject = null

   updateObject: (event) =>
     alert('update')


Use overlay to show slotitems

Use overlay to show slotitems

--- views/components/ship/parts/slotitems.cjsx
+++ views/components/ship/parts/slotitems.cjsx
@@ -1,5 +1,6 @@
 path = require 'path-extra'
 {$, $$, _, React, ReactBootstrap, ROOT} = window
+{OverlayTrigger, Tooltip} = ReactBootstrap

 Slotitems = React.createClass
   render: ->
@@ -9,7 +10,9 @@
         for itemId in @props.data
           continue if itemId == -1
           item = _slotitems[itemId]
-           0 then "★+#{item.api_level}" else '' } />
+          <OverlayTrigger placement='left' overlay={<Tooltip>{item.api_name}{if item.api_level > 0 then ★+{item.api_level} else ''}</Tooltip>}>
+
+          </OverlayTrigger>
       }


Don't duplicate amendUrl tests.

Don't duplicate amendUrl tests.

--- tests/html/src/tests.coffee
+++ tests/html/src/tests.coffee
@@ -365,31 +365,6 @@

 module('other')

-test "amending url", ->
-    dl = document.location
-
-    r = new SockJS('//blah:1/abc', [])
-    equal(r._base_url, dl.protocol + '//blah:1/abc')
-
-    r = new SockJS('/abc', [])
-    equal(r._base_url, dl.protocol + '//' + dl.host + '/abc')
-
-    r = new SockJS('http://a:1/abc', [])
-    equal(r._base_url, 'http://a:1/abc')
-
-    r = new SockJS('http://a:1/abc/', [])
-    equal(r._base_url, 'http://a:1/abc')
-
-    r = new SockJS('http://a:1/abc//', [])
-    equal(r._base_url, 'http://a:1/abc')
-
-    t = ->
-        new SockJS('', [])
-    raises(t, 'Wrong url')
-    t = ->
-        new SockJS(false, [])
-    raises(t, 'Wrong url')
-
 test "EventEmitter", ->
     expect(4)
     r = new SockJS('//blah/abc', [])


Tests for chunking detection.

Tests for chunking detection.

--- tests/html/src/tests.coffee
+++ tests/html/src/tests.coffee
@@ -34,6 +34,7 @@
 factor_echo_rich = (protocol) ->
     messages = [ [1,2,3,'data'], null, "data", 1, 12.0, {a:1, b:2} ]
     return echo_factory_factory(protocol, messages)
+

 factor_echo_unicode = (protocol) ->
     messages = [
@@ -301,3 +302,39 @@
     r.onmessage = -> ok(true)
     r.removeEventListener 'message', bluff
     r.dispatchEvent({type:'message'})
+
+asyncTest "chunking test", ->
+    expect(25)
+    a = new Array(25)
+    go = ->
+        SockJS.chunkingTest client_opts.url + '/echo', (r) ->
+            if $.browser.msie and $.browser.version < 8
+                # on browsers with no streaming support...
+                equal(r, false)
+            else
+                equal(r, true)
+            a.shift()
+            if a.length isnt 0
+                go()
+            else
+                start()
+    go()
+
+asyncTest "chunking test, invalid url 404", ->
+        expect(1)
+        SockJS.chunkingTest client_opts.url + '/invalid_url', (r) ->
+            equal(r, false)
+            start()
+
+asyncTest "chunking test, invalid url 500", ->
+        expect(1)
+        SockJS.chunkingTest client_opts.url + '/500_error', (r) ->
+            equal(r, false)
+            start()
+
+asyncTest "chunking test, invalid url port", ->
+        expect(1)
+        dl = document.location
+        SockJS.chunkingTest dl.protocol + '//' + dl.hostname + ':1079', (r) ->
+            equal(r, false)
+            start()


Remove extensions. Not useful for now

Remove extensions. Not useful for now

--- lib/written/app/assets/javascripts/written/core/extensions.coffee
+++ lib/written/app/assets/javascripts/written/core/extensions.coffee
@@ -1,2 +0,0 @@
-Written.Extensions = []
-


Converted octal escapes to hexadecimal.

Converted octal escapes to hexadecimal.

CoffeeScript 1.3.3 strict mode forbids octal literals.

--- tests/support/tester.coffee
+++ tests/support/tester.coffee
@@ -7,12 +7,12 @@
 print = -> process.stdout.write.apply process.stdout, arguments
 timeout = (func, time) -> setTimeout time, func

-grey    = "\033[0;90m"
-red     = "\033[0;31m"
-green   = "\033[0;32m"
-yellow  = "\033[0;33m"
-blue    = "\033[0;34m"
-reset   = "\033[0m"
+grey    = "\x1b[0;90m"
+red     = "\x1b[0;31m"
+green   = "\x1b[0;32m"
+yellow  = "\x1b[0;33m"
+blue    = "\x1b[0;34m"
+reset   = "\x1b[0m"

 tests = {}
 done = {}
@@ -178,4 +178,4 @@

 process.on 'uncaughtException', (err) ->
   uncaught.push err
-  throw err+  throw err


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -272,12 +272,12 @@
         if labelGroup.empty()
             labelGroup = svg.append("g").attr("class", "labels")

-        #path = pathGroup.selectAll("path.pie")
-        #    .data(piedata)
-
-        #path.enter().append("path")
-        #    .attr("class", "pie")
-        #    .attr("fill",(d, i) -> return color i)
+        path = pathGroup.selectAll("path.pie")
+            .data(piedata)
+
+        path.enter().append("path")
+            .attr("class", "pie")
+            .attr("fill",(d, i) -> return color i)
         #    .data(piedata)

         #arcs = svg.selectAll("g.slice")