Remove measureScrollView helper

Remove measureScrollView helper

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -47,19 +47,15 @@

       node.style.height = editor.getLineCount() * lineHeightInPixels + 'px'
       node.style.width = '1000px'
-      measureScrollView()
+      component.measureScrollView()

   afterEach ->
     contentNode.style.width = ''
-
-  measureScrollView = ->
-    component.measureScrollView()
-    component.forceUpdate()

   describe "line rendering", ->
     it "renders the currently-visible lines plus the overdraw margin", ->
       node.style.height = 4.5 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()

       linesNode = node.querySelector('.lines')
       expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
@@ -126,7 +122,7 @@
     it "renders the .lines div at the full height of the editor if there aren't enough lines to scroll vertically", ->
       editor.setText('')
       node.style.height = '300px'
-      measureScrollView()
+      component.measureScrollView()

       linesNode = node.querySelector('.lines')
       expect(linesNode.offsetHeight).toBe 300
@@ -170,7 +166,7 @@
           editor.setText "a line that wraps "
           editor.setSoftWrap(true)
           node.style.width = 16 * charWidth + 'px'
-          measureScrollView()
+          component.measureScrollView()

         it "doesn't show end of line invisibles at the end of wrapped lines", ->
           expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that "
@@ -246,7 +242,7 @@

     it "renders the currently-visible line numbers", ->
       node.style.height = 4.5 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(node.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number
       expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1"
@@ -286,7 +282,7 @@
       editor.setSoftWrap(true)
       node.style.height = 4.5 * lineHeightInPixels + 'px'
       node.style.width = 30 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(node.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line node
       expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1"
@@ -432,7 +428,7 @@
       describe "when decorations are applied to buffer rows", ->
         it "renders line number classes based on the decorations on their buffer row", ->
           node.style.height = 4.5 * lineHeightInPixels + 'px'
-          measureScrollView()
+          component.measureScrollView()

           expect(component.lineNumberNodeForScreenRow(9)).not.toBeDefined()

@@ -469,7 +465,7 @@
           editor.setSoftWrap(true)
           node.style.height = 4.5 * lineHeightInPixels + 'px'
           node.style.width = 30 * charWidth + 'px'
-          measureScrollView()
+          component.measureScrollView()

           expect(lineNumberHasClass(2, 'no-wrap')).toBe true
           expect(lineNumberHasClass(2, 'wrap-me')).toBe true
@@ -586,7 +582,7 @@

       node.style.height = 4.5 * lineHeightInPixels + 'px'
       node.style.width = 20 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()

       cursorNodes = node.querySelectorAll('.cursor')
       expect(cursorNodes.length).toBe 1
@@ -785,7 +781,7 @@
       inputNode = node.querySelector('.hidden-input')
       node.style.height = 5 * lineHeightInPixels + 'px'
       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(editor.getCursorScreenPosition()).toEqual [0, 0]
       editor.setScrollTop(3 * lineHeightInPixels)
@@ -831,7 +827,7 @@
         it "moves the cursor to the nearest screen position", ->
           node.style.height = 4.5 * lineHeightInPixels + 'px'
           node.style.width = 10 * charWidth + 'px'
-          measureScrollView()
+          component.measureScrollView()
           editor.setScrollTop(3.5 * lineHeightInPixels)
           editor.setScrollLeft(2 * charWidth)

@@ -956,7 +952,7 @@
   describe "scrolling", ->
     it "updates the vertical scrollbar when the scrollTop is changed in the model", ->
       node.style.height = 4.5 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(verticalScrollbarNode.scrollTop).toBe 0

@@ -965,7 +961,7 @@

     it "updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model", ->
       node.style.width = 30 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       linesNode = node.querySelector('.lines')
       expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
@@ -977,7 +973,7 @@

     it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", ->
       node.style.width = 30 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(editor.getScrollLeft()).toBe 0
       horizontalScrollbarNode.scrollLeft = 100
@@ -988,7 +984,7 @@
     it "does not obscure the last line with the horizontal scrollbar", ->
       node.style.height = 4.5 * lineHeightInPixels + 'px'
       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()
       editor.setScrollBottom(editor.getScrollHeight())
       lastLineNode = component.lineNodeForScreenRow(editor.getLastScreenRow())
       bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
@@ -997,7 +993,7 @@

       # Scroll so there's no space below the last line when the horizontal scrollbar disappears
       node.style.width = 100 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()
       bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
       bottomOfEditor = node.getBoundingClientRect().bottom
       expect(bottomOfLastLine).toBe bottomOfEditor
@@ -1005,7 +1001,7 @@
     it "does not obscure the last character of the longest line with the vertical scrollbar", ->
       node.style.height = 7 * lineHeightInPixels + 'px'
       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       editor.setScrollLeft(Infinity)

@@ -1019,19 +1015,19 @@

       node.style.height = 4.5 * lineHeightInPixels + 'px'
       node.style.width = '1000px'
-      measureScrollView()
+      component.measureScrollView()

       expect(verticalScrollbarNode.style.display).toBe ''
       expect(horizontalScrollbarNode.style.display).toBe 'none'

       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(verticalScrollbarNode.style.display).toBe ''
       expect(horizontalScrollbarNode.style.display).toBe ''

       node.style.height = 20 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(verticalScrollbarNode.style.display).toBe 'none'
       expect(horizontalScrollbarNode.style.display).toBe ''
@@ -1039,7 +1035,7 @@
     it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", ->
       node.style.height = 4 * lineHeightInPixels + 'px'
       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       atom.themes.applyStylesheet "test", """
         ::-webkit-scrollbar {
@@ -1062,19 +1058,19 @@

       node.style.height = 4.5 * lineHeightInPixels + 'px'
       node.style.width = '1000px'
-      measureScrollView()
+      component.measureScrollView()
       expect(verticalScrollbarNode.style.bottom).toBe ''
       expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
       expect(scrollbarCornerNode.style.display).toBe 'none'

       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()
       expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
       expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
       expect(scrollbarCornerNode.style.display).toBe ''

       node.style.height = 20 * lineHeightInPixels + 'px'
-      measureScrollView()
+      component.measureScrollView()
       expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
       expect(horizontalScrollbarNode.style.right).toBe ''
       expect(scrollbarCornerNode.style.display).toBe 'none'
@@ -1082,7 +1078,7 @@
     it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
       gutterNode = node.querySelector('.gutter')
       node.style.width = 10 * charWidth + 'px'
-      measureScrollView()
+      component.measureScrollView()

       expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth()

@@ -1094,7 +1090,7 @@
       beforeEach ->
         node.style.height = 4.5 * lineHeightInPixels + 'px'
         node.style.width = 20 * charWidth + 'px'
-        measureScrollView()
+        component.measureScrollView()

       it "updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y)", ->
         expect(verticalScrollbarNode.scrollTop).toBe 0
@@ -1132,7 +1128,7 @@
       it "keeps the line on the DOM if it is scrolled off-screen", ->
         node.style.height = 4.5 * lineHeightInPixels + 'px'
         node.style.width = 20 * charWidth + 'px'
-        measureScrollView()
+        component.measureScrollView()

         lineNode = node.querySelector('.line')
         wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
@@ -1144,7 +1140,7 @@
       it "does not set the mouseWheelScreenRow if scrolling horizontally", ->
         node.style.height = 4.5 * lineHeightInPixels + 'px'
         node.style.width = 20 * charWidth + 'px'
-        measureScrollView()
+        component.measureScrollView()

         lineNode = node.querySelector('.line')
         wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0)
@@ -1188,7 +1184,7 @@
       it "keeps the line number on the DOM if it is scrolled off-screen", ->
         node.style.height = 4.5 * lineHeightInPixels + 'px'
         node.style.width = 20 * charWidth + 'px'
-        measureScrollView()
+        component.measureScrollView()

         lineNumberNode = node.querySelectorAll('.line-number')[1]
         wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)


Show project id.

Show project id.

--- src/coffee/controllers/timerCtrl.coffee
+++ src/coffee/controllers/timerCtrl.coffee
@@ -73,7 +73,7 @@
                         #{n.text}

"
           if n.groupTop
-            template = template.insert("
#{n.project.name}
", 0)
+            template = template.insert("
#{n.project.id} #{n.project.name}
", 0)
           return template
     $scope.activityData =
       displayKey: 'name'


Don't Use Suggestion In Tests

Don't Use Suggestion In Tests

--- spec/provider-manager-spec.coffee
+++ spec/provider-manager-spec.coffee
@@ -11,13 +11,13 @@
       providerManager = new ProviderManager()
       testProvider =
         requestHandler: (options) ->
-          [new Suggestion(this,
+          [{
             word: "ohai",
             prefix: "ohai",
             label: "ohai",
             renderLabelAsHtml: true,
             className: 'ohai'
-          )]
+          }]
         selector: '.source.js'
         dispose: ->
           # No-op
@@ -218,13 +218,13 @@
     it "registers a provider with a blacklist", ->
       testProvider =
         requestHandler: (options) ->
-          [new Suggestion(this,
+          [{
             word: "ohai",
             prefix: "ohai",
             label: "ohai",
             renderLabelAsHtml: true,
             className: 'ohai'
-          )]
+          }]
         selector: '.source.js'
         blacklist: '.source.js .comment'
         dispose: ->
@@ -260,26 +260,26 @@
         providerManager = new ProviderManager()
         testProvider1 =
           requestHandler: (options) ->
-            [new Suggestion(this,
+            [{
               word: "ohai2",
               prefix: "ohai2",
               label: "ohai2",
               renderLabelAsHtml: true,
               className: 'ohai2'
-            )]
+            }]
           selector: '.source.js'
           dispose: ->
             # No-op

         testProvider2 =
           requestHandler: (options) ->
-            [new Suggestion(this,
+            [{
               word: "ohai2",
               prefix: "ohai2",
               label: "ohai2",
               renderLabelAsHtml: true,
               className: 'ohai2'
-            )]
+            }]
           selector: '.source.js .variable.js'
           blacklist: '.source.js .variable.js .comment2'
           providerblacklist:
@@ -289,26 +289,26 @@

         testProvider3 =
           requestHandler: (options) ->
-            [new Suggestion(this,
+            [{
               word: "ohai3",
               prefix: "ohai3",
               label: "ohai3",
               renderLabelAsHtml: true,
               className: 'ohai3'
-            )]
+            }]
           selector: '*'
           dispose: ->
             # No-op

         testProvider4 =
           requestHandler: (options) ->
-            [new Suggestion(this,
+            [{
               word: "ohai4",
               prefix: "ohai4",
               label: "ohai4",
               renderLabelAsHtml: true,
               className: 'ohai4'
-            )]
+            }]
           selector: '.source.js .comment'
           dispose: ->
             # No-op


Use event methods in TokenizedBuffer spec

--- spec/tokenized-buffer-spec.coffee
+++ spec/tokenized-buffer-spec.coffee
@@ -41,7 +41,7 @@
       buffer = atom.project.bufferForPathSync('sample.js')
       tokenizedBuffer = new TokenizedBuffer({buffer})
       startTokenizing(tokenizedBuffer)
-      tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
+      tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')

     afterEach ->
       tokenizedBuffer.destroy()
@@ -753,7 +753,7 @@
         expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3
         expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2

-        tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
+        tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')

         buffer.setTextInRange([[7, 0], [8, 65]], '        one\n        two\n        three\n        four')

@@ -771,7 +771,7 @@
         buffer.insert([7, 0], '\n\n')
         buffer.insert([5, 0], '\n\n')

-        tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
+        tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')

         buffer.setTextInRange([[7, 0], [8, 65]], '    ok')


Enable commands only when a file pane is active

If there are no file panes open in Atom, it doesn't make sense to show the
github:open and github:blame commands in the command palette. So, instead of
_always_ making these commands available, let's make them available only when
a file pane is active.

--- lib/to-the-hubs.coffee
+++ lib/to-the-hubs.coffee
@@ -4,10 +4,11 @@
   activate: ->
     return unless project.getRepo()?

-    rootView.command 'github:open', ->
-      if itemPath = rootView.getActivePaneItem()?.getPath?()
-        GitHubFile.fromPath(itemPath).open()
+    rootView.eachPane (pane) ->
+      pane.command 'github:open', ->
+        if itemPath = rootView.getActivePaneItem()?.getPath?()
+          GitHubFile.fromPath(itemPath).open()

-    rootView.command 'github:blame', ->
-      if itemPath = rootView.getActivePaneItem()?.getPath?()
-        GitHubFile.fromPath(itemPath).blame()
+      pane.command 'github:blame', ->
+        if itemPath = rootView.getActivePaneItem()?.getPath?()
+          GitHubFile.fromPath(itemPath).blame()


Fix core:cancel handling event bug

Fix core:cancel handling event bug

--- lib/views/input.coffee
+++ lib/views/input.coffee
@@ -27,7 +27,7 @@
     @editorInput.element.addEventListener "blur", @remove
     @subscriptions.add atom.commands.add @editorInput.element,
       "core:confirm": => @confirm()
-      "core:cancel": => @remove()
+      "core:cancel": => @goBack()
       "core:page-up": => @oRefTextEditor.trigger "core:page-up"
       "core:page-down": => @oRefTextEditor.trigger "core:page-down"
     @subscriptions.add @oRefTextEditor.onDidChangeScrollTop @goBack


Updated publication schema.

Updated publication schema.

--- lib/documents/publication.coffee
+++ lib/documents/publication.coffee
@@ -29,6 +29,9 @@
   # cached: timestamp when the publication was cached
   # metadata: do we have metadata?
   # processed: has PDF been processed (file checked, text extracted, thumbnails generated, etc.)
+  # processError:
+  #   error: description of the publication processing error
+  #   stack: stack trace of the error
   # numberOfPages
   # searchResult (client only): the last search query this publication is a result for, if any
   #   _id: id of the query, an _id of the SearchResult object for the query


Remove debug string

Remove debug string

--- lib/ghc-modi-process-base.coffee
+++ lib/ghc-modi-process-base.coffee
@@ -104,7 +104,6 @@
     savedLines = []
     parseData = ->
       data = process.stdout.read()
-      console.log "#{data}"
       unless data?
         atom.notifications.addError "Haskell-ghc-mod: ghc-modi crashed
             on #{command} with message #{savedLines.join('\n')}",


cleaner get cnx id

cleaner get cnx id

--- src/components/task-step/all-steps.cjsx
+++ src/components/task-step/all-steps.cjsx
@@ -20,12 +20,9 @@
   console.error(msgs...)
   throw new Error(JSON.stringify(msgs...))

-# TODO make this logic more failsafe
 getCnxId = (id) ->
-  {content_url} = TaskStepStore.get(id)
-  if content_url.search('contents/') > -1
-    cnxId = _.last(content_url.split('contents/'))
-  cnxId
+  parts = TaskStepStore.get(id)?.content_url?.split('contents/')
+  if parts.length > 1 then _.last(parts) else undefined

 Reading = React.createClass
   displayName: 'Reading'


bug fix with tree item view

bug fix with tree item view

--- src/components/tree/treeitemview.coffee
+++ src/components/tree/treeitemview.coffee
@@ -19,14 +19,14 @@
     if childClass
       @child = new childClass childOptions, @getData()

-  viewAppendeed:->
+  viewAppended:->

     if @getOptions().childClass
       @addSubView @child
     else
       @updatePartial """

-        #{ title }
+        #{ @getData().title }
         """

   toggle:(callback)->


remove semicolon

remove semicolon

--- bootstrap-tour.coffee
+++ bootstrap-tour.coffee
@@ -265,7 +265,7 @@
       if (step.next >= 0)
         nav.push "#{options.labels.next}"

-      content = nav.join(" | ");
+      content = nav.join(" | ")
       content += "#{options.labels.end}"

     # Show step popover


JMachine::unshare: allow participants to remove their accounts from participants

[completes #84962740]

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -485,8 +485,16 @@
     JMachine::shareWith$.call this, client, options, callback

   unshare: secure (client, users, callback)->
+
     options = target: users, asUser: no
-    JMachine::shareWith$.call this, client, options, callback
+
+    {connection:{delegate}} = client
+    {profile:{nickname}}    = delegate
+
+    if users.length is 1 and users[0] is nickname
+      JMachine::shareWith.call this, options, callback
+    else
+      JMachine::shareWith$.call this, client, options, callback

   setAsOwner: secure (client, users, callback)->
     options = target: users, asUser: yes, asOwner: yes


Removed the dependency on underscore.

Removed the dependency on underscore.

--- lib/collections.coffee
+++ lib/collections.coffee
@@ -24,16 +24,23 @@
 # You must not remove this notice, or any other, from this software.
 # --------------------------------------------------------------------

-# TODO - Eliminate the dependency on underscore.
-require 'underscore' if typeof require == 'function'
-
-
 # --------------------------------------------------------------------
 # Nodes and support functions used by several collections.
 # --------------------------------------------------------------------

 # A collection of utility functions used by interior nodes.
 util = {
+  find: (a, test) ->
+    for x in a
+      return x if test(x) == true
+    ()
+
+  reduce: (a, step, init) ->
+    result = init
+    for x in a
+      result = step(result, x)
+    result
+
   arrayWith: (a, i, x) -> a[...i].concat([x], a[i+1..])

   arrayWithInsertion: (a, i, x) -> a[...i].concat([x], a[i..])
@@ -163,7 +170,7 @@
     else
       remaining = j for j in [1...@table.length] when j != i and @table[j]
       if remaining.length <= 4
-        bitmap = _.reduce(remaining, ((b, j) -> b | (1 << j)), 0)
+        bitmap = util.reduce(remaining, ((b, j) -> b | (1 << j)), 0)
         array  = @table[j] for j in remaining
         new BitmapIndexedNode(bitmap, array, @size - 1)
       else
@@ -195,7 +202,7 @@
       catch ex
         Object.prototype.toString.call(obj)

-  _.reduce(stringVal, ((code, c) -> code * 37 + c.charCodeAt(0)), 0)
+  util.reduce(stringVal, ((code, c) -> code * 37 + c.charCodeAt(0)), 0)

 areEqual = (obj1, obj2) ->
   if obj1? and typeof(obj1.equals) == "function"
@@ -218,7 +225,7 @@
       node.each(func)

   get: (shift, hash, key) ->
-    leaf = _.detect @bucket, (v) -> areEqual(v.key, key)
+    leaf = util.find @bucket, (v) -> areEqual(v.key, key)
     leaf.get(shift, hash, key) if leaf?

   with: (shift, hash, leaf) ->
@@ -230,7 +237,7 @@
   without: (shift, hash, key) ->
     switch @bucket.length
       when 0, 1 then null
-      when 2    then _.detect @bucket, (v) -> not areEqual(v.key, key)
+      when 2    then util.find @bucket, (v) -> not areEqual(v.key, key)
       else           new CollisionNode(hash, this.bucketWithout(key))

   toString: -> "CollisionNode(#{@bucket.join(", ")})"


Improve statics module

Improve statics module

--- modules/statics.coffee
+++ modules/statics.coffee
@@ -6,6 +6,22 @@
     @registerCommand 'stats', { allowDM: true }, (msg, args)=>
       mem = Math.floor process.memoryUsage().heapTotal / 1024000
       memfree = Math.floor os.freemem() / 1024000
+      loadAvg = os.loadavg().map((c)=> c.toFixed(2))
+      totalCommands = Object.keys(Core.commands.plain).length
+      excludingAliases = Object.keys(Core.commands.registered).length
+      totalGuilds = 0
+      totalVoice = 0
+
+      # Get statics from the other shards
+      stats = await Core.data.get('Stats')
+      if stats?
+        stats.forEach (shard)=>
+          totalGuilds += shard.guilds
+          totalVoice += shard.voice
+      else
+        # Fallback
+        totalGuilds = @bot.Guilds.length
+        totalVoice = @bot.VoiceConnections.length

       msg.channel.sendMessage '', false, {
         author:
@@ -18,20 +34,60 @@
           {
             name: "Shard #{(Core.settings.shardIndex or 0)+1}/#{Core.settings.shardCount or 1}"
             value: """
-            Uptime: #{Core.bootDate.fromNow(true)}
-            Guilds: #{@bot.Guilds.length}
-            Voice Connections: #{@bot.VoiceConnections.length}
-            Memory: #{mem}MB (#{memfree}MB free)
+            **Uptime**: #{Core.bootDate.fromNow(true)}
+            **Guilds**: #{@bot.Guilds.length}
+            **Voice Connections**: #{@bot.VoiceConnections.length}
+            **Memory Usage**: #{mem}MB
+            """
+          }
+          {
+            name: "Overall"
+            value: """
+            **Guilds**: #{totalGuilds}
+            **Voice Connections**: #{totalVoice}
+            """
+          }
+          {
+            name: "System"
+            value: """
+            **Platform**: #{os.platform()} #{os.release()} #{os.arch()}
+            **Load Average**: #{loadAvg}
+            **Free Memory**: #{memfree}MB
             """
           }
           {
             name: 'FocaBotCore'
             value: """
-            Version: #{Core.version}
-            #{Object.keys(Core.modules.loaded).length} modules loaded.
+            **Version**: #{Core.version}
+            **Modules**: #{Object.keys(Core.modules.loaded).length} modules loaded.
+            **Commands**: #{totalCommands} commands registered. (#{totalCommands - excludingAliases} are aliases)
             """
           }
         ]
       }

+  updateStats:->
+    stats = (await Core.data.get('Stats')) or []
+    stats[Core.settings.shardIndex or 0] = {
+      guilds: Core.bot.Guilds.length
+      voice: Core.bot.VoiceConnections.length
+    }
+    Core.data.set('Stats', stats)
+
+  ready:=>
+    @updateStats()
+    Core.bot.Dispatcher.on('VOICE_CONNECTED', @updateStats)
+    Core.bot.Dispatcher.on('VOICE_DISCONNECTED', @updateStats)
+    Core.bot.Dispatcher.on('GUILD_CREATE', @updateStats)
+    Core.bot.Dispatcher.on('GUILD_DELETE', @updateStats)
+
+
+  unload:=>
+    # Remove all listeners
+    Core.bot.Dispatcher.removeListener('VOICE_CONNECTED', @updateStats)
+    Core.bot.Dispatcher.removeListener('VOICE_DISCONNECTED', @updateStats)
+    Core.bot.Dispatcher.removeListener('GUILD_CREATE', @updateStats)
+    Core.bot.Dispatcher.removeListener('GUILD_DELETE', @updateStats)
+
+
 module.exports = StatsModule


bugfix: no reaction of first search

bugfix: no reaction of first search

--- lib/sumatra-forwardsearch.coffee
+++ lib/sumatra-forwardsearch.coffee
@@ -17,6 +17,9 @@
     # Register command that toggles this view
     @subscriptions.add atom.commands.add 'atom-workspace', 'sumatra-forwardsearch:forwardseach': => @forwardsearch()

+    this.forwardsearch()
+
+
   deactivate: ->
     @modalPanel.destroy()
     @subscriptions.dispose()


modified

modified

--- src/jsonsina.coffee
+++ src/jsonsina.coffee
@@ -33,22 +33,25 @@
   request.get options, (err, res, string)->
     if err?
       callback error, null
+
     else
+
+      arr = null
       try
         arr = eval string
-        if arr
-          for each in arr
-            each.day = new Date each.day
-            each.open = Number each.open
-            each.low = Number each.low
-            each.high = Number each.high
-            each.close = Number each.close
-            each.volume = Number each.volume
-            #console.log each
-          callback err, arr
       catch error
         callback error, null

+      for each in arr?
+        each.day = new Date each.day
+        each.open = Number each.open
+        each.low = Number each.low
+        each.high = Number each.high
+        each.close = Number each.close
+        each.volume = Number each.volume
+        #console.log each
+
+      callback err, arr


Avoid binding some functions we won't need on instances

--- src/createClass.coffee
+++ src/createClass.coffee
@@ -3,6 +3,10 @@
 applyMixin = require './applyMixin'
 DefaultMixin = require './DefaultMixin'
 ReduxComponent = require './ReduxComponent'
+
+dontBindThese = {
+	applyMixin: true
+}

 createClass = (spec) ->
 	# Apply default mixin, then setup the spec
@@ -14,7 +18,7 @@
 		# Allow Class() instead of new Class() if desired.
 		if not (@ instanceof Constructor) then return new Constructor()
 		# Magic bind all the functions on the prototype
-		(@[k] = f.bind(@)) for k,f of Constructor.prototype when typeof(f) is 'function'
+		(@[k] = f.bind(@)) for k,f of Constructor.prototype when typeof(f) is 'function' and (not dontBindThese[k])
 		# Constructor must return this.
 		@


Fix broken test because of language change

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -1,7 +1,7 @@
 if typeof process isnt 'undefined' and process.execPath and process.execPath.match /node|iojs/
   chai = require 'chai' unless chai
   noflo = require '../src/lib/NoFlo.coffee'
-  shippingLanguage = 'coffeescript'
+  shippingLanguage = 'javascript'
   path = require 'path'
   root = path.resolve __dirname, '../'
   urlPrefix = './'


No need for this test

No need for this test

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -44,8 +44,6 @@

   it 'should initially know of no components', ->
     chai.expect(l.components).to.be.null
-  it 'should not initially require revalidation', ->
-    chai.expect(l.revalidate).to.be.false
   it 'should not initially be ready', ->
     chai.expect(l.ready).to.be.false
   it 'should not initially be processing', ->


actually run the set component

actually run the set component

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -260,39 +260,43 @@

   describe 'writing sources', ->
     describe 'with working code', ->
-      workingSource = """
-      var noflo = require('noflo');
-
-      exports.getComponent = function() {
-        var c = new noflo.Component();
-
-        c.inPorts.add('in', function(packet, outPorts) {
-          if (packet.event !== 'data') {
-            return;
-          }
-          // Do something with the packet, then
-          c.outPorts.out.send(packet.data);
-        });
-
-        c.outPorts.add('out');
-
-        return c;
-      };"""
-
-      it 'should be able to set the source', (done) ->
-        @timeout 10000
-        unless noflo.isBrowser()
-          workingSource = workingSource.replace "'noflo'", "'../src/lib/NoFlo'"
-        l.setSource 'foo', 'RepeatData', workingSource, 'js', (err) ->
-          return done err if err
-          done()
-      it 'should be a loadable component', (done) ->
-        l.load 'foo/RepeatData', (err, inst) ->
-          return done err if err
-          chai.expect(inst).to.be.an 'object'
-          chai.expect(inst.inPorts).to.contain.keys ['in']
-          chai.expect(inst.outPorts).to.contain.keys ['out']
-          done()
+      describe 'with ES5', ->
+        workingSource = """
+        var noflo = require('noflo');
+
+        exports.getComponent = function() {
+          var c = new noflo.Component();
+          c.inPorts.add('in');
+          c.outPorts.add('out');
+          c.process(function (input, output) {
+            output.sendDone(input.get('in'));
+          });
+          return c;
+        };"""
+
+        it 'should be able to set the source', (done) ->
+          @timeout 10000
+          unless noflo.isBrowser()
+            workingSource = workingSource.replace "'noflo'", "'../src/lib/NoFlo'"
+          l.setSource 'foo', 'RepeatData', workingSource, 'js', (err) ->
+            return done err if err
+            done()
+        it 'should be a loadable component', (done) ->
+          l.load 'foo/RepeatData', (err, inst) ->
+            return done err if err
+            chai.expect(inst).to.be.an 'object'
+            chai.expect(inst.inPorts).to.contain.keys ['in']
+            chai.expect(inst.outPorts).to.contain.keys ['out']
+            ins = new noflo.internalSocket.InternalSocket
+            out = new noflo.internalSocket.InternalSocket
+            inst.inPorts.in.attach ins
+            inst.outPorts.out.attach out
+            out.on 'ip', (ip) ->
+              chai.expect(ip.type).to.equal 'data'
+              chai.expect(ip.data).to.equal 'ES5'
+              done()
+            ins.send 'ES5'
+
     describe 'with non-working code', ->
       before ->
         # setSource not implemented in webpack loader yet


Fix message outport attach

Fix message outport attach

--- spec/ReceiveResponse.coffee
+++ spec/ReceiveResponse.coffee
@@ -10,11 +10,11 @@
     c = ReceiveResponse.getComponent()
     request = noflo.internalSocket.createSocket()
     response = noflo.internalSocket.createSocket()
-    request = noflo.internalSocket.createSocket()
+    message = noflo.internalSocket.createSocket()

     c.inPorts.request.attach request
     c.outPorts.response.attach response
-    c.outPorts.message.attach request
+    c.outPorts.message.attach message

   describe 'when instantiated', ->
     it 'should have a "request" inport', ->


:lipstick:

:lipstick:

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -307,6 +307,7 @@
             editor.moveCursorUp()
             expect(editor.getCursorScreenPosition().column).toBe 0

+      describe "horizontal movement", ->
         describe "when left is pressed on the first column", ->
           describe "when there is a previous line", ->
             it "wraps to the end of the previous line", ->


change to structured data to pagerduty

change to structured data to pagerduty

  * move content to details field instead of description
  * change to a structured json object instead of a text blob

--- plugins/pagerduty/index.coffee
+++ plugins/pagerduty/index.coffee
@@ -4,27 +4,27 @@
   stacktraceLines = (stacktrace) ->
     ("#{line.file}:#{line.lineNumber} - #{line.method}" for line in stacktrace when line.inProject)

-  pagerDutyDescription = (event) ->
-    """
-    #{event.trigger.message}</b> in #{event.project.name}
+  metaDataLines = (metaData) ->
+    ("  * #{k} - #{v}" for k,v of metaData)

-    #{event.error.exceptionClass} in #{event.error.context}
-    #{event.error.message if event.error.message}

-    View on bugsnag.com: #{event.error.url}
-
-    #{stacktraceLines(event.error.stacktrace).join("\n")}
-
-    View full stacktrace: #{event.error.url}
-
-    """
+  pagerDutyDetails = (event) ->
+    details =
+      message : event.trigger.message
+      project : event.project.name
+      class : event.error.exceptionClass
+      url : event.error.url
+      stackTrace : stacktraceLines(event.error.stacktrace)
+      metaData : event.error.metaData
+    details

   @receiveEvent: (config, event, callback) ->
     payload =
       service_key: config.serviceKey
       event_type: 'trigger'
       incident_key: event.error.url
-      description: pagerDutyDescription(event)
+      description: "#{event.error.exceptionClass} in #{event.error.context}"
+      details: pagerDutyDetails(event)

     # Send the request
     @request


'optimizing' the request with less datas

--- netatmo_librato.coffee
+++ netatmo_librato.coffee
@@ -53,13 +53,13 @@
     device_name[device._id]=norm(device.station_name)
     device_end_time[device._id]=device.last_status_store
     end_time = device.last_status_store
-    time_to_retrieve = 2*ten_min_sec
+    time_to_retrieve = 3*ten_min_sec/2
     if device.type == 'NAMain'
       console.log "Weather Station detected"
       device_thermostat[device._id] = false
     else
       console.log "Thermostat detected"
-      time_to_retrieve = 2*one_hour_sec
+      time_to_retrieve = 3*one_hour_sec/2
       device_thermostat[device._id] = true
       device.data_type = ['Temperature', 'Sp_Temperature', 'BoilerOn', 'BoilerOff']


Hide the invitations prompt when there are no users to display.

Mentioned in #1973, fixes #2013.

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -372,19 +372,20 @@
     @first_green_build_invitations = @komp
       deferEvaluation: true
       read: =>
-        new CI.inner.Invitations VM.project().github_users_not_following(), (sending, users) =>
-          node = $ ".first-green"
-          node.addClass "animation-fadeout-collapse"
-          if sending
-            node.addClass "success"
-            for user in users
-              mixpanel.track "Sent invitation",
-                project: VM.project().project_name()
-                login: user.login()
-                id: user.id()
-                email: user.email()
-            VM.project().invite_team_members users
-          window.setTimeout (=> @dismiss_first_green_build_invitations true), 2000
+        if VM.project().github_users_not_following()
+          new CI.inner.Invitations VM.project().github_users_not_following(), (sending, users) =>
+            node = $ ".first-green"
+            node.addClass "animation-fadeout-collapse"
+            if sending
+              node.addClass "success"
+              for user in users
+                mixpanel.track "Sent invitation",
+                  project: VM.project().project_name()
+                  login: user.login()
+                  id: user.id()
+                  email: user.email()
+              VM.project().invite_team_members users
+            window.setTimeout (=> @dismiss_first_green_build_invitations true), 2000

   feature_enabled: (feature_name) =>
     @feature_flags()[feature_name]


Sanitize metric keys for Prometheus

Sanitize metric keys for Prometheus

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -32,11 +32,15 @@
 			res.end(Register.metrics())
 		)

+	sanitizeKey: (key) ->
+		key.replace /[^a-zA-Z0-9]/g, "_"
+
 	set : (key, value, sampleRate = 1)->
 		statsd.set buildKey(key), value, sampleRate

 	inc : (key, sampleRate = 1)->
 		statsd.increment buildKey(key), sampleRate
+		key = this.sanitizeKey(key)
 		if !counters[key]
 			counters[key] = new prom.Counter({
 		  		name: key,
@@ -63,6 +67,7 @@

 	gauge : (key, value, sampleRate = 1)->
 		statsd.gauge buildKey(key), value, sampleRate
+		key = this.sanitizeKey(key)
 		if !gauges[key]
 			gauges[key] = new prom.Gauge({
 		  		name: key,
@@ -73,6 +78,7 @@

 	globalGauge: (key, value, sampleRate = 1)->
 		statsd.gauge buildGlobalKey(key), value, sampleRate
+		key = this.sanitizeKey(key)
 		if !gauges[key]
 			gauges[key] = new prom.Gauge({
 		  		name: key,


pubsub_client: rm debug

pubsub_client: rm debug

--- src/xmpp/pubsub_client.coffee
+++ src/xmpp/pubsub_client.coffee
@@ -85,7 +85,6 @@
             @results.features.push el.attrs.var
         else if el.is('x', NS.DATA)
             form = forms.fromXml(el)
-            logger.debug form: form, formType: form.getFormType(), is: (form.getFormType() is NS.PUBSUB_META_DATA)
             if form.getFormType() is NS.PUBSUB_META_DATA
                 @results.config = forms.formToConfig(form)


fixed a very sneaky bug

fixed a very sneaky bug

--- library/nucleus/archetype.coffee
+++ library/nucleus/archetype.coffee
@@ -67,7 +67,7 @@
     # vectors from the compound that is being composed right into the
     # destination components. There are certain rules on what is merged
     # and how it is merged. Please refer to the implementation for it.
-    @composition: (destination, hierarchy) ->
+    @composition: (destination, local, implanted) ->
         return if this.STOP_COMPOSITION_MERGING or no
         return if destination.STOP_COMPOSITION_MERGING
         assert from = try @identify().underline or null
@@ -75,8 +75,8 @@
         merge = "Merging vector context from %s into %s"
         exact = "Vector %s of %s is merged into %s".grey
         gs = (h) -> h.COMPOSITION_EXPORTS or new Object
-        rd = (acc, table) -> return _.merge acc, table
-        symbols = _.reduce _.map(hierarchy, gs), rd, {}
+        rd = (acc, table) -> return _.merge acc, table; acc
+        symbols = _.reduce _.map(implanted, gs), rd, {}
         logger.debug merge.toString().grey, from, into
         _.forIn this, (value, name, sourcing) -> do ->
             return unless symbols and name of symbols


nitrogen8mm-dwe: Switch to non-flasher image

Switch from flasher image to a standard, non-flasher
one in the coffee file.

Update dashboard instructions for this usecase too.

Changelog-entry: nitrogen8mm-dwe: Switch to non-flasher image
Signed-off-by: Alexandru Costache <2d2dfcfad7b9832058fd41884a8955d042a85275@balena.io>

--- nitrogen8mm-dwe.coffee
+++ nitrogen8mm-dwe.coffee
@@ -1,14 +1,17 @@
 deviceTypesCommon = require '@resin.io/device-types/common'
 { networkOptions, commonImg, instructions } = deviceTypesCommon

-BOARD_FLASHING = 'The device internal storage is now being flashed with BalenaOS, please wait until completed.'
+UNPACK_IMAGE = 'Unzip the image downloaded from the dashboard.'
+CONNECT_USB = 'Power on the board, stop booting in u-boot cmdline and connect the USB OTG port to the PC. Issue the following command in u-boot: ums 0 mmc 0'
+FLASH_IMAGE = 'Use Balena Etcher to write the unzipped image to the internal storage of device, which is exposed as Mass Storage on the PC.'
 CAPACITOR_DRAIN = 'Remove power from the board and drain the super capacitor completely.'
 BOARD_POWERON = 'Connect power to the board.'
+WRITE_BALENA_UBOOT = 'If board is using a standard uboot, overwrite it with BalenaOS imx-boot in the mmcblk0boot0 partition.'

 postProvisioningInstructions = [
 	CAPACITOR_DRAIN
-	instructions.REMOVE_INSTALL_MEDIA
 	BOARD_POWERON
+	WRITE_BALENA_UBOOT
 ]

 module.exports =
@@ -22,11 +25,9 @@
 		postProvisioning: postProvisioningInstructions

 	instructions: [
-		instructions.ETCHER_SD
-		instructions.EJECT_SD
-		instructions.FLASHER_WARNING
-		instructions.BOARD_REPOWER
-		BOARD_FLASHING
+		UNPACK_IMAGE
+		CONNECT_USB
+		FLASH_IMAGE
 	].concat(postProvisioningInstructions)

 	gettingStartedLink:
@@ -38,10 +39,10 @@

 	yocto:
 		machine: 'nitrogen8mm-dwe'
-		image: 'resin-image-flasher'
+		image: 'resin-image'
 		fstype: 'resinos-img'
 		version: 'yocto-thud'
-		deployArtifact: 'resin-image-flasher-nitrogen8mm-dwe.resinos-img'
+		deployArtifact: 'resin-image-nitrogen8mm-dwe.resinos-img'
 		compressed: true

 	options: [ networkOptions.group ]


Refactor and skip automatic variable tests

We'll skip these tests while waiting for discussions on the parent
grammar to settle out. See SublimeText/PowerShell#103 for more
information.

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -253,11 +253,8 @@
           expect(tokens[1]).not.toHaveScopes ["source.powershell", "keyword.operator.comparison.powershell"]

   # Automatic variables. Ref: https://technet.microsoft.com/en-us/library/hh847768.aspx
-  describe "Automatic variables", ->
-    automaticConstants = [
-      "$null", "$true", "$false"
-    ]
-
+  # Skip while things settle out on the parent grammar
+  xdescribe "Automatic variables", ->
     automaticVariables = [
       "$$", "$?", "$^", "$_",
       "$Args",
@@ -277,22 +274,14 @@
       "$This", "$true"
     ]

-    it "tokenizes automatic language constants", ->
-      for automaticConstant in automaticConstants
-        {tokens} = grammar.tokenizeLine automaticConstant
-        expect(tokens[0].value).toEqual "$"
-        expect(tokens[0]).toHaveScopes ["source.powershell", "keyword.other.powershell"]
-        expect(tokens[1].value).toEqual automaticConstant.substr(1)
-        expect(tokens[1]).toHaveScopes ["source.powershell", "constant.language.powershell"]
-
     it "tokenizes automatic variables", ->
       for automaticVariable in automaticVariables
         {tokens} = grammar.tokenizeLine automaticVariable
         expect(tokens[0].value).toEqual "$"
         expect(tokens[0]).toHaveScopes ["source.powershell", "keyword.other.powershell"]
-        expect(tokens[1].value).toEqual automaticVariable.substr(1)
-        expect(tokens[1]).toHaveScopes ["source.powershell", "variable.language.powershell"]
-        expect(tokens[1]).not.toHaveScopes ["source.powershell", "punctuation.variable.begin.powershell"]
+        if tokens[1]?
+          expect(tokens[1].value).toEqual automaticVariable.substr(1)
+          expect(tokens[1]).toHaveScopes ["source.powershell", "support.constant.automatic.powershell"]

   describe "Escaped characters", ->
     escapedCharacters = [


unit test for case-insensitive sort

unit test for case-insensitive sort

--- spec/sort-lines-spec.coffee
+++ spec/sort-lines-spec.coffee
@@ -178,3 +178,23 @@
           Helium
           Lithium
         """
+  describe "case-insensitive sorting", ->
+    it "sorts all lines, ignoring case", ->
+      editor.setText """
+        Hydrogen
+        lithium
+        helium
+        Helium
+        Lithium
+      """
+
+      editor.setCursorBufferPosition([0, 0])
+
+      sortLinesInsensitive ->
+        expect(editor.getText()).toBe """
+          Helium
+          helium
+          Hydrogen
+          Lithium
+          lithium
+        """


Remove spec related to natural sorting of movie titles

If we _happen_ to support sorting movie titles in this way, I suppose
that's fine, but it's not something that makes sense (to me) for us to
officially support and maintain.

--- spec/sort-lines-spec.coffee
+++ spec/sort-lines-spec.coffee
@@ -469,19 +469,3 @@
           $10001.02
           $10002.00
         """
-
-    it "properly handles movie titles", ->
-      editor.setText """
-        1 Title - The Big Lebowski
-        1 Title - Gattaca
-        1 Title - Last Picture Show
-      """
-
-      editor.setCursorBufferPosition([0, 0])
-
-      sortLinesNatural ->
-        expect(editor.getText()).toBe """
-          1 Title - Gattaca
-          1 Title - Last Picture Show
-          1 Title - The Big Lebowski
-        """


Update spec to maintain for selections when appending a newline

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -117,13 +117,14 @@
       expect(editor.getText()).toBe "foo\nboo\n"
       expect(editor.getCursorBufferPosition()).toEqual([0,3])

-    it "preserves the cursor position when saving on last line", ->
+    it "preserves selections when saving on last line", ->
       editor.insertText "foo"
-      editor.moveCursorToEndOfLine()
-      originalCursorPosition = editor.getCursor().getBufferPosition()
+      editor.setCursorBufferPosition([0,0])
+      editor.selectToEndOfLine()
+      originalSelectionRange = editor.getSelection().getBufferRange()
       editor.save()
-      newCursorPosition = editor.getCursor().getBufferPosition()
-      expect(originalCursorPosition.isEqual(newCursorPosition))
+      newSelectionRange = editor.getSelection().getBufferRange()
+      expect(originalSelectionRange).toEqual(newSelectionRange)

   describe "when 'whitespace.ensureSingleTrailingNewline' is false", ->
     beforeEach ->


Added preprocessing progress updates

Added preprocessing progress updates

--- client/import.coffee
+++ client/import.coffee
@@ -1,8 +1,9 @@
 # Local (client-only) document of importing files
 class ImportingFile extends Document
   # name: user's file name
+  # readProgress: progress of reading from file, in %
+  # uploadProgress: progress of uploading file, in %
   # preprocessingProgress: progress of file preprocessing, in %
-  # uploadProgress: progress of uploading file, in %
   # status: current status or error message
   # finished: true when importing has finished
   # errored: true when there was an error
@@ -124,11 +125,8 @@
       ImportingFile.documents.update file._id,
       $set:
         # TODO: Remove uploadProgress update when progressbar frontend is updated (#513)
-        uploadProgress: progress * 100 #%
-        # When file reading in chunks is implemented, it will be the first part
-        # of preprocessing progress. Checksum computation should then continue
-        # preprocessing progress from that point on.
-        preprocessingProgress: progress * 100 #%
+        uploadProgress: progress * 100 # %
+        preprocessingProgress: progress * 100 # %

   hash.update file.content, (error, result) ->
     if error
@@ -192,8 +190,9 @@
     _id: id
     name: file.name
     status: "In preprocessing queue"
+    readProgress: 0
+    uploadProgress: 0
     preprocessingProgress: 0
-    uploadProgress: 0
     finished: false
     errored: false
     canceled: false


Changed meta-description strings.

Changed meta-description strings.

--- workers/social/lib/social/render/graphmeta.coffee
+++ workers/social/lib/social/render/graphmeta.coffee
@@ -1,11 +1,11 @@
 module.exports = ->
   """
   <meta name="title" content="Koding - A new way for developers to work.">
-  <meta name="description" content="Koding is the best software development environment on cloud. It's now social, in the browser and free.">
+  <meta name="description" content="Koding is a developer community and cloud development environment where developers come together and code in the browser – with a real development server to run their code. Developers can work, collaborate, write and run apps without jumping through hoops and spending unnecessary money.">
   <meta name="keywords" content="Online IDE, Collaborative IDE, Free VM, Browser-based terminal,free virtual machine, online compiler, Javascript, nodejs, golang, Python, ">
   <meta name="author" content="Koding">
   <meta property="og:site_name" content="Koding"/>
-  <meta property="og:description" content="Koding is the best cloud development environment. Free virtual machine, best online IDE, amazing community, collaborative IDE, real terminal on browser and more.. "/>
+  <meta property="og:description" content="Koding is a developer community and cloud development environment where developers come together and code in the browser – with a real development server to run their code. Developers can work, collaborate, write and run apps without jumping through hoops and spending unnecessary money."/>
   <meta property="og:title" content="Koding - A new way for developers to work."/>
   <meta property="og:url" content="http://www.koding.com"/>
   <meta property="og:type" content="website" />


Fixed indentation

Fixed indentation

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -135,17 +135,17 @@
                 delete data.cardYear
                 delete data.cardCV

-              paymentModal = paymentController.createPaymentMethodModal data, (newData, onError, onSuccess)->
-                newData.plan = planCode
-                newData.type = type
-                group.makePayment newData, (err, subscription)->
-                  if err
-                    onError err
-                  else
-                    vmController.createGroupVM type, planCode, vmCreateCallback
-                    onSuccess()
-                    callback()
-              paymentModal.on "KDModalViewDestroyed", -> vmController.emit "PaymentModalDestroyed"
+                paymentModal = paymentController.createPaymentMethodModal data, (newData, onError, onSuccess)->
+                  newData.plan = planCode
+                  newData.type = type
+                  group.makePayment newData, (err, subscription)->
+                    if err
+                      onError err
+                    else
+                      vmController.createGroupVM type, planCode, vmCreateCallback
+                      onSuccess()
+                      callback()
+                paymentModal.on "KDModalViewDestroyed", -> vmController.emit "PaymentModalDestroyed"
           else
             group.updatePayment {plan: planCode, type: type}, (err, subscription)->
               vmController.createGroupVM type, planCode, vmCreateCallback


Has default vm should look at personal vms in koding group.

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -183,9 +183,18 @@
       @info vm, callback? rest...

   hasDefaultVM:(callback)->
-    @fetchDefaultVmName (defaultVmName)=>
-      @fetchVMs (err, vms)->
-        callback defaultVmName in vms
+    # Default VM should be the personal vm in Koding group
+    @fetchVMs (err, vms)->
+      if err
+        warn "An error occured while fetching VMs:", err
+        return callback yes
+
+      # Check if there is at least one personal vm from koding group
+      for vm in vms
+        if (vm.indexOf 'vm-') is 0 and (vm.indexOf 'koding.kd.io') > -1
+          return callback yes
+
+      callback no

   createDefaultVM:->
     @hasDefaultVM (state)->


Fix role selection when click event clears hidden inputs instead of filling them in, addresses #727.

--- app/assets/javascripts/components/invites.coffee
+++ app/assets/javascripts/components/invites.coffee
@@ -12,10 +12,17 @@
     $("[name=\"invite[site_id]\"]").prop("checked", false)

     if role_level == "site" and $("[name=\"invite[site_id]\"]").length == 1
-      $("[name=\"invite[site_id]\"]").click()
+      input = $("[name=\"invite[site_id]\"]")
+      parent = $(input).closest("[data-object~=role-selection]")
+      $(input).prop("checked", true)
+      $("#invite_subgroup_type").val($(parent).data("subgroup-type"))
+      $("#invite_subgroup_id").val($(parent).find("input").val())
     else if role_level == "ae_team" and $("[name=\"invite[team_id]\"]").length == 1
-      $("[name=\"invite[team_id]\"]").click()
-
+      input = $("[name=\"invite[team_id]\"]")
+      parent = $(input).closest("[data-object~=role-selection]")
+      $(input).prop("checked", true)
+      $("#invite_subgroup_type").val($(parent).data("subgroup-type"))
+      $("#invite_subgroup_id").val($(parent).find("input").val())
   )
   .on("click", "[data-object~=role-selection]", ->
     if $(this).find("input").prop("checked") == true


Add method to completely remove model from page

--- app/assets/javascripts/models/active_page.coffee
+++ app/assets/javascripts/models/active_page.coffee
@@ -36,6 +36,10 @@

     error_tag = $("[data-error][data-k-uuid='" + @uuid + "']")
     error_tag.hide()
+
+  remove_from_page: ->
+    $("[data-wrapper][data-k-uuid='" + @uuid + "']").remove()
+    $("[data-kindred-model]").find("div[data-k-uuid='" + @uuid + "']").remove()

   update_vals_on_page: ->
     $.each @attributes, (attr, val) =>


Modified tweet text.

Modified tweet text.

--- app/assets/javascripts/modules/twitter.js.coffee
+++ app/assets/javascripts/modules/twitter.js.coffee
@@ -1,5 +1,5 @@
 jQuery.fn.twitterButton = (el,options) ->
   $('#twitter-wjs').remove()
   $(@)
-  .append('Tweet')
+  .append('Tweet')
   .append('')


fixed compilation in latest coffeescript

--- app/assets/javascripts/monitoring/util.js.coffee
+++ app/assets/javascripts/monitoring/util.js.coffee
@@ -85,7 +85,6 @@

 # just for reference, rendering using rickshaw
 Bixby.monitoring.render_with_rickshaw = (s, metric) ->
-  super() # render()

   # display graphs
   @resources.each (res) ->


Switch template name to register

Switch template name to register

--- components/inquiry_questionnaire/test/views/account.coffee
+++ components/inquiry_questionnaire/test/views/account.coffee
@@ -4,7 +4,7 @@
 setup = require './setup'
 Account = benv.requireWithJadeify require.resolve('../../views/account'), [
   'templates.login'
-  'templates.signup'
+  'templates.register'
   'templates.forgot'
 ]


Clustering enabled when using cluster control

--- app/scripts/alchemy/clustering/clustering.coffee
+++ app/scripts/alchemy/clustering/clustering.coffee
@@ -74,5 +74,6 @@
         d3.select("#cluster-key")
             .on "keydown", ->
                 if d3.event.keyIdentifier == "Enter"
+                    alchemy.conf.cluster = true
                     alchemy.conf.clusterKey = this.value
                     alchemy.generateLayout()


found a race condition

found a race condition

--- commands/move.coffee
+++ commands/move.coffee
@@ -104,6 +104,7 @@
             return cb(err) if err?
             db.tiles.update coords, update_newtile, cb
       , (cb) ->
+        #BUG: race condition due to HP
         update_oldtile =
           $pull:
             people:


all horizontal wordclouldrs

all horizontal wordclouldrs

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -47,14 +47,24 @@
     # open ended, use a cloud
     if results[0].open_ended
       $("#chart-" + questionID).highcharts({
+        chart: {
+          marginTop: 0
+          marginBottom: 0
+          style: {
+            fontFamily: "Montserrat"
+          }
+        }
         series: [{
-          type: 'wordcloud',
-          data: data,
+          type: 'wordcloud'
+          data: data
           name: 'Occurrences'
-        }],
+        }]
         plotOptions: {
           wordcloud: {
             colors: orgColors
+            rotation: {
+              orientations: 1
+            }
           }
         }
         credits: { enabled: false }


embed default config setting change for #1702

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -165,7 +165,7 @@
     this.plugin ?= 'instantill' # has to be defined at startup, as either instantill or shareyourpaper
     this.element ?= '#' + this.plugin
     this.pushstate ?= true # if true, the embed will try to add page state changes to the browser state manager
-    this.local ?= true # local storage of config turned off by default for now
+    this.local ?= false # local storage of config turned off by default for now
     this.config ?= {}
     this.data ?= {} # the data obj to send to backend
     this.f ?= {} # the result of the find/ill/permission request to the backend
@@ -180,10 +180,10 @@

     _L.loaded = this.loaded if this.loaded? # if this is set to a function, it will be passed to _leviathan loaded, which gets run after every ajax call completes. It is also called directly after every configure

-    if window.location.search.indexOf('clear=') isnt -1
-      localStorage.removeItem '_oab_config_' + this.plugin
     if window.location.search.indexOf('local=') isnt -1
       this.local = if window.location.search.indexOf('local=true') isnt -1 then true else false
+    if window.location.search.indexOf('clear=') isnt -1 or this.local is false
+      try localStorage.removeItem '_oab_config_' + this.plugin
     if window.location.search.indexOf('config=') isnt -1
       try this.config = JSON.parse window.location.search.split('config=')[1].split('&')[0].split('#')[0]
     if window.location.search.indexOf('config.') isnt -1


woops, search_tag :/

woops, search_tag :/


[Fix] Fix channel list difference to last selected channel

--- src/renderer/scripts/views/shell/view.coffee
+++ src/renderer/scripts/views/shell/view.coffee
@@ -1,11 +1,9 @@
 _           = require "underscore"
 Marionette  = require "marionette"

-# NcoAPI          = require "nco/nco"
-# ChannelManager  = require "nco/ChannelManager"
-# NcoConfigure    = require "nco/config"
+NsenChannels = require "app/NsenChannels"

-NsenChannels = require "app/NsenChannels"
+CONFIG_LAST_SELECT_CHANNEL = "nco.nsen.lastSelectChannel"

 module.exports =
 class ShellView extends Marionette.ItemView
@@ -30,15 +28,12 @@
         app.onDidChangeChannel =>
             @_didChangeChannel()

-        # @listenTo ChannelManager, "channelChanged", @_didChangeChannel
-        # @listenTo NcoAPI, "changeAlwaysOnTop", @_onPinStateChanged
-
     onRender : ->
         # チャンネル一覧をセレクトボックスへ設定
         template = _.template "<option value='<%- id %>'><%- name %>"
         @ui.channel.html _.map(NsenChannels, (obj, index) -> template(obj)).join("")

-        channelId = app.config.get "nco.lastSelectChannel"
+        channelId = app.config.get CONFIG_LAST_SELECT_CHANNEL
         @ui.channel.find("option").each (i, el) =>
             console.log el.value, channelId
             if el.value is channelId


Support accessing any author property from a template

--- src/scripts/helpers/handlebars/author.coffee
+++ src/scripts/helpers/handlebars/author.coffee
@@ -5,5 +5,5 @@
 define (require) ->
   Handlebars = require('hbs/handlebars')

-  Handlebars.registerHelper 'author', (list, index) ->
-    return new Handlebars.SafeString(list[index].fullname)
+  Handlebars.registerHelper 'author', (list, index, property) ->
+    return new Handlebars.SafeString(list[index][property])


Hotfix alerts-config indentation mistake from merge

--- src/components/alerts-config/alerts-config.directive.coffee
+++ src/components/alerts-config/alerts-config.directive.coffee
@@ -82,7 +82,7 @@
         for alert in $scope.kpi.alerts
           $scope.alerts[alert.service].active = true

-      $scope.modal = $uibModal.open(alertsSettingsModal.options)
+        $scope.modal = $uibModal.open(alertsSettingsModal.options)

       $scope.showRecipientList = (alert) ->
         alert.active && alert.service == 'email' && $scope.members


fix filter json

fix filter json

--- src/filter/json.coffee
+++ src/filter/json.coffee
@@ -1,5 +1,6 @@

-alight.filters.json = class J
-    watchMode: 'deep'
-    onChange: (value) ->
-        @.setValue JSON.stringify alight.utils.clone(value), null, 4
+alight.filters.json =
+    init: (scope, expression, env) ->
+        watchMode: 'deep'
+        onChange: (value) ->
+            env.setValue JSON.stringify alight.utils.clone(value), null, 4


Extend specs for all log levels

Extend specs for all log levels

--- src/spec/helpers/extended-logger.spec.coffee
+++ src/spec/helpers/extended-logger.spec.coffee
@@ -1,3 +1,4 @@
+_ = require 'underscore'
 ExtendedLogger = require '../../lib/helpers/extended-logger'

 describe 'ExtendedLogger', ->
@@ -22,24 +23,26 @@

   describe ':: wrapper', ->

-    it 'should log with extended object and message', ->
-      spyOn(@log.bunyanLogger, 'info')
-      @log.info {id: 123}, 'Hello'
-      expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar'}, 'Hello'
+    _.each ['trace', 'debug', 'info', 'warn', 'error', 'fatal'], (level) ->
+      it "should log (#{level}) with extended object and message", ->
+        spyOn(@log.bunyanLogger, 'info')
+        @log.info {id: 123}, 'Hello'
+        expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar'}, 'Hello'

-    it 'should log with additional fields and message', ->
-      spyOn(@log.bunyanLogger, 'info')
-      @log.info 'Hello'
-      expect(@log.bunyanLogger.info).toHaveBeenCalledWith {project_key: 'foo', another_field: 'bar'}, 'Hello'
+      it "should log (#{level}) with additional fields and message", ->
+        spyOn(@log.bunyanLogger, 'info')
+        @log.info 'Hello'
+        expect(@log.bunyanLogger.info).toHaveBeenCalledWith {project_key: 'foo', another_field: 'bar'}, 'Hello'

-    it 'should log with extended object and no message', ->
-      spyOn(@log.bunyanLogger, 'info')
-      @log.info {id: 123}
-      expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar'}, undefined
+      it "should log (#{level}) with extended object and no message", ->
+        spyOn(@log.bunyanLogger, 'info')
+        @log.info {id: 123}
+        expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar'}, undefined

   describe ':: withField', ->

-    it 'should chain field for extended object', ->
-      spyOn(@log.bunyanLogger, 'info')
-      @log.withField(token: 'qwerty').info {id: 123}, 'Hello'
-      expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar', token: 'qwerty'}, 'Hello'
+    _.each ['trace', 'debug', 'info', 'warn', 'error', 'fatal'], (level) ->
+      it "should chain field (#{level}) for extended object", ->
+        spyOn(@log.bunyanLogger, 'info')
+        @log.withField(token: 'qwerty').info {id: 123}, 'Hello'
+        expect(@log.bunyanLogger.info).toHaveBeenCalledWith {id: 123, project_key: 'foo', another_field: 'bar', token: 'qwerty'}, 'Hello'


requiring mixins

requiring mixins

--- app/assets/javascripts/bandango.js.coffee
+++ app/assets/javascripts/bandango.js.coffee
@@ -1,5 +1,6 @@
 #= require ./store
 #= require_tree ./classes
+#= require_tree ./mixins
 #= require_tree ./helpers
 #= require_tree ./models
 #= require_tree ./controllers


Extract functions for Google Maps and jquery-datatables initialization code

--- app/assets/javascripts/bikeways.js.coffee
+++ app/assets/javascripts/bikeways.js.coffee
@@ -12,32 +12,38 @@
   index: ->
     $ ->
       console.log "enter segments#index"
-      $('#segments').dataTable
-        sDom: 'ilrptf'  # http://www.datatables.net/ref#sDom
-        bProcessing: true
-        bServerSide: true
-        sAjaxSource: $('#segments').data('source')
-        bStateSave: true
-        bDeferRender: true
+      APP._initGoogleMaps()
+      APP._initDataTables()
       console.log "exit segments#index"

   show: ->
     $ ->
       console.log "enter segments#show"
-      kmlUrl = $('#map').data('kml-url')
-      handler = Gmaps.build('Google')
-      opts =
-        # see https://developers.google.com/maps/documentation/javascript/reference?hl=fr#MapOptions
-        provider: {
-          scrollwheel: false,
-          zoom: 16
-        }
-        internal: { id: 'map' }
-      handler.buildMap(opts, ->
-        kmls = handler.addKml(
-          { url: kmlUrl }
-        )
-        # handler.bounds.extendWith(markers)
-        # handler.fitMapToBounds()
-      )
       console.log "exit segments#show"
+
+APP._initGoogleMaps = ->
+  kmlUrl = $('#map').data('kml-url')
+  handler = Gmaps.build('Google')
+  opts =
+    # see https://developers.google.com/maps/documentation/javascript/reference?hl=fr#MapOptions
+    provider: {
+      scrollwheel: false,
+      zoom: 16
+    }
+    internal: { id: 'map' }
+  handler.buildMap(opts, ->
+    kmls = handler.addKml(
+      { url: kmlUrl }
+    )
+    # handler.bounds.extendWith(markers)
+    # handler.fitMapToBounds()
+  )
+
+APP._initDataTables = ->
+  $('#segments').dataTable
+    sDom: 'ilrptf'  # http://www.datatables.net/ref#sDom
+    bProcessing: true
+    bServerSide: true
+    sAjaxSource: $('#segments').data('source')
+    bStateSave: true
+    bDeferRender: true


removed empty coffee file

removed empty coffee file

--- app/assets/javascripts/filterpanel.coffee
+++ app/assets/javascripts/filterpanel.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -26,6 +26,28 @@
     draw: (canvas) ->
       I.alpha = originalAlpha

+  ###*
+  A convenient way to set the flicker instance variables on a sprite. You can modify the
+  instance variables by hand but the suggested way to do it is through this method.
+
+

+  player = GameObject()
+
+  player.include(Flickerable)
+
+  player.flicker()
+  # => This causes the sprite to flicker between full opacity and 50% opacity every 3 frames for 30 frames
+
+  player.flicker(90, 5, 0.3)
+  # => This causes the sprite to flicker between full opacity and 30% opacity every 5 frames for 90 frames
+

+
+  @name flicker
+  @methodOf Flickerable#
+  @param {Number} [duration=30] How long the effect lasts
+  @param {Number} [frequency=3] The number of frames in between opacity changes
+  @param {Number} [alpha=0.5] The alpha value to flicker to
+  ###
   flicker: (duration = 30, frequency = 3, alpha = 0.5) ->
     I.flickerDuration = duration
     I.flickerFrequency = frequency


Add space between username and comment in JavaScript

--- app/assets/javascripts/undersky.js.coffee
+++ app/assets/javascripts/undersky.js.coffee
@@ -195,6 +195,7 @@
         for c in data
           comment = $('

')
           comment.append('' + c.from.username + '');
+          comment.append(' ')
           comment.append('' + c.text + '')
           container.append(comment)
       error: (e, data) ->


Check for dropped files first to avoid error reading dataTransfer in IE

--- src/trix/controllers/input_controller.coffee
+++ src/trix/controllers/input_controller.coffee
@@ -163,7 +163,10 @@
       point = [event.clientX, event.clientY]
       @responder?.setLocationRangeFromPoint(point)

-      if @draggedRange
+      if files = event.dataTransfer?.files
+        @attachFiles(files)
+
+      else if @draggedRange
         @delegate?.inputControllerWillMoveText()
         @responder?.moveTextFromRange(@draggedRange)
         @draggedRange = null
@@ -173,9 +176,6 @@
         document = Trix.Document.fromJSONString(documentJSON)
         @responder?.insertDocument(document)
         @requestRender()
-
-      else if files = event.dataTransfer.files
-        @attachFiles(event.dataTransfer.files)

       @draggedRange = null
       @draggingPoint = null


refactor: minor change

refactor: minor change

--- app/components/project-preferences.coffee
+++ app/components/project-preferences.coffee
@@ -41,11 +41,9 @@

     selectDeviceType: ->
       @set "selectedDeviceType", parseInt @$('#project-device-preference').val()
-      @send "versionSelected"

     selectVersion: ->
       @set "selectVersion", @$('#project-version-preference').val()
-      @send "versionSelected"

     versionSelected: ->
       selectVersion = @get "selectVersion"


Remove #loadJSON/#toJSON from <trix-editor>

--- src/trix/elements/trix_editor_element.coffee
+++ src/trix/elements/trix_editor_element.coffee
@@ -159,11 +159,3 @@

   reset: ->
     @value = @defaultValue
-
-  # JSON serialization
-
-  loadJSON: (json) ->
-    @editor?.loadJSON(json)
-
-  toJSON: ->
-    @editor?.toJSON()


Fixing some intial problems

Fixing some intial problems

--- src/backside/updates_graph.coffee
+++ src/backside/updates_graph.coffee
@@ -1,8 +1,15 @@
-FileSystemChizel = require ...../backside/filesystem_chizelin
+"""
+  Parses update files and creates graph based on time.
+"""

-
+FileSystemChizel = require '../backside/filesystem_chizelin'

 fs = require 'fs'
 path = require 'path'
+
+
+module.exports =
+    graphicData = (FileSys) ->
+      console.log(FileSys.files)


Fix a bit

Fix a bit

--- src/haml-coffee.coffee
+++ src/haml-coffee.coffee
@@ -296,9 +296,17 @@
       when 'amd'
         @renderAmd()
       when 'standalone'
-        @precompile()
+        @renderStandalone()
       else
         @renderGlobal templateName, namespace
+
+  # Render a standalone version
+  renderStandalone: ->
+    template = """
+    return ->
+      (context) ->
+    #{indent(@precompile(), 2)}
+    """

   # Render the parsed source code as CoffeeScript template wrapped in a
   # define() statement for AMD. If the global modules list contains a module


Fix bug with @engine instead of env.utilities.oauth for refresh token method.

--- src/presentationLayer/auth/index.coffee
+++ src/presentationLayer/auth/index.coffee
@@ -24,7 +24,7 @@
 						return next e if e
 						if not provider.oauth2?.refresh
 							return next new env.utilities.check.Error "refresh token not supported for " + req.params.provider
-						oa = new @engine.oauth.oauth2(provider, keyset.parameters)
+						oa = new env.utilities.oauth.oauth2(provider, keyset.parameters)
 						oa.refresh req.body.token, keyset, send(res,next)

 		# iframe injection for IE


bind new resize event to the window object for utility callbacks

--- src/coffee/breadcrumbsEatr.coffee
+++ src/coffee/breadcrumbsEatr.coffee
@@ -353,6 +353,13 @@
             # bind main resize logic to the resize event of the window object
             o.browserWindow.on o.resizeKey, o.resize

+            # bind resize utility logic to the resize event of the window object
+            o.browserWindow.on o.resizeUtilKey, ->
+                unless o.resizeStarted
+                    o.resizeStarted = true
+
+                    o.resizeStart()
+
             # create deferred for triggering onAfterLoad callback
             afterLoadDfd = new $.Deferred()


Fix grid issue #9.

Fix grid issue #9.

--- src/coffee/components/grid.coffee
+++ src/coffee/components/grid.coffee
@@ -51,9 +51,9 @@
           tryTile = tiles.get rowNo, i
           if not tryTile
             if i is colNo
-              tile = row.tile colNo, opts
+              tile = row.make 'tile', colNo, opts
             else if not tile
-              row.tile i
+              row.make 'tile', i

       fillMissing()
       tile


retain LICENSE text when license detected from package metadata

--- src/legal-eagle.coffee
+++ src/legal-eagle.coffee
@@ -41,9 +41,9 @@

 extractLicense = ({license, licenses, readme}, path) ->
   license ?= licenses[0] if licenses?.length > 0
-
-  if result = extractLicenseFromDirectory(path)
-    result
+  result_dir = extractLicenseFromDirectory(path)
+  if result_dir && result_dir['license']
+    result_dir
   else if license?
     unless typeof license is 'string'
       license = license.type ? 'UNKNOWN'
@@ -57,7 +57,10 @@
     license = 'Public Domain' if license.match /[\s(]*Public Domain/i
     license = 'LGPL' if license.match /[\s(]*LGPL(-.+)*/
     license = 'GPL' if license.match /[\s(]*[^L]GPL(-.+)*/
-    {license, source: 'package.json'}
+    result = {license, source: 'package.json'}
+    if result_dir && result_dir['sourceText']
+      result['sourceText'] = result_dir['sourceText']
+    result
   else if readme and readme isnt 'ERROR: No README data found!'
     extractLicenseFromReadme(readme) ? {license: 'UNKNOWN'}
   else
@@ -151,8 +154,7 @@
     else if licenseText.toLocaleLowerCase().indexOf('public domain')  > -1
       'Public Domain'

-  if license?
-    {license, source: licenseFileName, sourceText: licenseText}
+  {license, source: licenseFileName, sourceText: licenseText}

 readIfExists = (path) ->
   readFileSync(path, 'utf8') if existsSync(path)


Fall back to legacy WirePattern implementation when given group or field options

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -35,6 +35,10 @@
 # Takes WirePattern configuration of a component and sets up
 # Process API to handle it.
 setupProcess = (component, config, func) ->
+  if checkDeprecation config
+    # If we're using deprecated WirePattern features we provide warnings on them
+    # and fall back to the legacy implementation of WirePattern
+    return legacyWirePattern component, config, func
   # Make param ports control ports
   setupControlPorts component, config
   # Set up sendDefaults function
@@ -61,9 +65,22 @@

     # Fake postpone and resume methods
     postpone = ->
+      platform.deprecated 'noflo.helpers.WirePattern postpone is deprecated.'
     resume = ->
+      platform.deprecated 'noflo.helpers.WirePattern resume is deprecated.'
     # Async WirePattern will call the output.done callback itself
     func.call component, data, groups, outProxy, output.done.bind(output), postpone, resume, context.scope
+
+# Provide deprecation warnings on certain more esoteric WirePattern features
+checkDeprecation = (config) ->
+  needsFallback = false
+  if config.group
+    platform.deprecated 'noflo.helpers.WirePattern group option is deprecated. Please port Process API'
+    needsFallback = true
+  if config.field
+    platform.deprecated 'noflo.helpers.WirePattern field option is deprecated. Please port Process API'
+    needsFallback = true
+  return needsFallback

 # Updates component port definitions to control prots for WirePattern
 # -style params array
@@ -253,6 +270,7 @@
   config.outPorts = outPorts
   return setupProcess component, config, proc

+legacyWirePattern = (component, config, proc) ->
   # Firing policy for addressable ports
   unless 'arrayPolicy' of config
     config.arrayPolicy =
@@ -266,7 +284,7 @@
   collectGroups = config.forwardGroups
   # Collect groups from each port?
   if typeof collectGroups is 'boolean' and not config.group
-    collectGroups = inPorts
+    collectGroups = config.inPorts
   # Collect groups from one and only port?
   if typeof collectGroups is 'string' and not config.group
     collectGroups = [collectGroups]
@@ -274,24 +292,24 @@
   if collectGroups isnt false and config.group
     collectGroups = true

-  for name in inPorts
+  for name in config.inPorts
     unless component.inPorts[name]
       throw new Error "no inPort named '#{name}'"
-  for name in outPorts
+  for name in config.outPorts
     unless component.outPorts[name]
       throw new Error "no outPort named '#{name}'"

   disconnectOuts = ->
     # Manual disconnect forwarding
-    for p in outPorts
+    for p in config.outPorts
       component.outPorts[p].disconnect() if component.outPorts[p].isConnected()

   sendGroupToOuts = (grp) ->
-    for p in outPorts
+    for p in config.outPorts
       component.outPorts[p].beginGroup grp

   closeGroupOnOuts = (grp) ->
-    for p in outPorts
+    for p in config.outPorts
       component.outPorts[p].endGroup grp

   # Declarations
@@ -340,9 +358,9 @@
       else
         # At least one of the outputs has to be resolved
         # for output streams to be flushed.
-        if outPorts.length is 1
+        if config.outPorts.length is 1
           tmp = {}
-          tmp[outPorts[0]] = streams
+          tmp[config.outPorts[0]] = streams
           streams = tmp
         for key, stream of streams
           if stream.resolved
@@ -438,7 +456,7 @@
             delete _wp(scope).gcTimestamps[key]

   # Grouped ports
-  for port in inPorts
+  for port in config.inPorts
     do (port) ->
       # Support for StreamReceiver ports
       # if config.receiveStreams and config.receiveStreams.indexOf(port) isnt -1
@@ -467,7 +485,7 @@
               closeGroupOnOuts payload
             # Disconnect
             if _wp(scope).groupBuffers[port].length is 0
-              if inPorts.length is 1
+              if config.inPorts.length is 1
                 if config.async or config.StreamSender
                   if config.ordered
                     _wp(scope).outputQ.push null
@@ -484,7 +502,7 @@
                   unless port of _wp(scope).disconnectData[key][i]
                     foundGroup = true
                     _wp(scope).disconnectData[key][i][port] = true
-                    if Object.keys(_wp(scope).disconnectData[key][i]).length is inPorts.length
+                    if Object.keys(_wp(scope).disconnectData[key][i]).length is config.inPorts.length
                       _wp(scope).disconnectData[key].shift()
                       if config.async or config.StreamSender
                         if config.ordered
@@ -502,7 +520,7 @@
                   _wp(scope).disconnectData[key].push obj

           when 'data'
-            if inPorts.length is 1 and not inPort.isAddressable()
+            if config.inPorts.length is 1 and not inPort.isAddressable()
               data = payload
               groups = _wp(scope).groupBuffers[port]
             else
@@ -523,7 +541,7 @@
               _wp(scope).groupedData[key] = [] unless key of _wp(scope).groupedData
               _wp(scope).groupedGroups[key] = [] unless key of _wp(scope).groupedGroups
               foundGroup = false
-              requiredLength = inPorts.length
+              requiredLength = config.inPorts.length
               ++requiredLength if config.field
               # Check buffered tuples awaiting completion
               for i in [0..._wp(scope).groupedData[key].length]
@@ -558,7 +576,7 @@
                   if groupLength is requiredLength
                     data = (_wp(scope).groupedData[key].splice i, 1)[0]
                     # Strip port name if there's only one inport
-                    if inPorts.length is 1 and inPort.isAddressable()
+                    if config.inPorts.length is 1 and inPort.isAddressable()
                       data = data[port]
                     groups = (_wp(scope).groupedGroups[key].splice i, 1)[0]
                     if collectGroups is true
@@ -578,7 +596,7 @@
                   obj[port] = {} ; obj[port][index] = payload
                 else
                   obj[port] = payload
-                if inPorts.length is 1 and
+                if config.inPorts.length is 1 and
                 component.inPorts[port].isAddressable() and
                 (config.arrayPolicy.in is 'any' or
                 component.inPorts[port].listAttached().length is 1)
@@ -604,7 +622,7 @@

             # Prepare outputs
             outs = {}
-            for name in outPorts
+            for name in config.outPorts
               wrp = new OutPortWrapper component.outPorts[name], scope
               if config.async or config.sendStreams and
               config.sendStreams.indexOf(name) isnt -1
@@ -613,7 +631,7 @@
               else
                 outs[name] = wrp

-            outs = outs[outPorts[0]] if outPorts.length is 1 # for simplicity
+            outs = outs[config.outPorts[0]] if config.outPorts.length is 1 # for simplicity
             groups = [] unless groups
             # Filter empty connect/disconnect groups
             groups = (g for g in groups when g isnt null)
@@ -627,7 +645,7 @@
               # Disconnect outputs if still connected,
               # this also indicates them as resolved if pending
               outputs = outs
-              if outPorts.length is 1
+              if config.outPorts.length is 1
                 outputs = {}
                 outputs[port] = outs
               disconnect = false
@@ -647,7 +665,7 @@

             # Group forwarding
             if config.forwardGroups and config.async
-              if outPorts.length is 1
+              if config.outPorts.length is 1
                 outs.beginGroup g for g in groups
               else
                 for name, out of outs


Remove unused array

Remove unused array

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -484,7 +484,6 @@

   startComponents: ->
     # Perform any startup routines necessary for every component.
-    rest = []
     for id, process of @processes
       process.component.start()


Fixed progress bar being spammed on the initial load

--- progress.coffee
+++ progress.coffee
@@ -35,19 +35,17 @@
 	@done : (progress = 10) ->
 		count = @count
 		if @element
+			# Set width to 100% to indicate we're done loading
+			@element.removeClass 'loading'
+			@element.addClass 'done'
+			@element.css 'width', '100%'
+
+			# XX Use the event `animationEnd` instead
 			setTimeout =>
-				# Set width to 100% to indicate we're done loading
-				@element.removeClass 'loading'
-				@element.addClass 'done'
-				@element.css 'width', '100%'
-
-				@alive = false
-				setTimeout =>
-					# Count each load, so our end timeout won't be called multiple times, when it's loading something else
-					return if count isnt @count
-					@reset()
-				, 1000 * (Router.options.progressSpeed or 1.5)
-			, 1
+				# Count each load, so our end timeout won't be called multiple times, when it's loading something else
+				return if count isnt @count
+				@reset()
+			, 1000 * (Router.options.progressSpeed or 1.5)

 # Our callbacks, we'll be calling on all routes
 callbacks =


Revert "Merge pull request #686 from sheldonkwok/master"

This reverts commit 42825e990cc345cb977877d074ec12eec690818a, reversing
changes made to 2d096f9726a88d73297609fc35f3459c916acacd.

--- keymaps/vim-mode.cson
+++ keymaps/vim-mode.cson
@@ -400,7 +400,3 @@
   '" +': 'vim-mode:register-prefix'
   '" %': 'vim-mode:register-prefix'
   '" _': 'vim-mode:register-prefix'
-
-'.tree-view':
-  'ctrl-w v': 'tree-view:open-selected-entry-right'
-  'ctrl-w s': 'tree-view:open-selected-entry-down'


Dont declare underscore, like in Haskell

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2894,7 +2894,8 @@
         (ctx.currentDeclarationId symbol) ? ctx.freshId()
       type = toConstrained withOrigin (ctx.freshTypeVariable star), atom
       # ctx.bindTypeVariables [type.type.name]
-      ctx.addToDefinedNames {name: symbol, id: id, type: type}
+      if symbol isnt '_' # Don't declare underscore
+        ctx.addToDefinedNames {name: symbol, id: id, type: type}
       type: type
       id: id
       pattern:


Fixed predclaring when functions already predeclare

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1275,11 +1275,10 @@
   if ctx.shouldDefer()
     shouldDeclareFinally = ctx.isDeclared ctx.deferReason()[1]
     for {name, id} in definedNames
-      if not ctx.isFinallyCurrentlyDeclared name
-        if shouldDeclareFinally
-          ctx.declare name, id: id
-        else
-          ctx.preDeclare name, id: id
+      if shouldDeclareFinally and (not ctx.isFinallyCurrentlyDeclared name)
+        ctx.declare name, id: id
+      else if not ctx.isCurrentlyDeclared name
+        ctx.preDeclare name, id: id
     #log "exiting pattern early", pattern, "for", ctx.shouldDefer()
     return {}


get a debug message on control (too big output)

--- src/smc-hub/local_hub_connection.coffee
+++ src/smc-hub/local_hub_connection.coffee
@@ -186,7 +186,7 @@
     # Project query support code
     #
     mesg_query: (mesg, write_mesg) =>
-        dbg = (m) => winston.debug("mesg_query(project_id='#{@project_id}'): #{m}")
+        dbg = (m) => winston.debug("mesg_query(project_id='#{@project_id}'): #{misc.trunc(m,200)}")
         dbg(misc.to_json(mesg))
         query = mesg.query
         if not query?


Guard controller.route

Guard controller.route

--- client/helpers.coffee
+++ client/helpers.coffee
@@ -36,7 +36,7 @@
     pattern = controller.location.get().path

   else
-    pattern = controller.route.getName()
+    pattern = controller.route?.getName()

   re = new RegExp exp, 'i'


(feature) launch chrome debug endpoint

(feature) launch chrome debug endpoint

--- src/steroids/servers/BuildServer.coffee
+++ src/steroids/servers/BuildServer.coffee
@@ -278,6 +278,16 @@
         else
           res.status(500).json { error: "Invalid request"}

+      else if tool is "chrome"
+        ChromeDebug = require "../debug/chrome"
+        chromeDebug = new ChromeDebug
+        chromeDebug.run().then ->
+          res.status(200).json { message: "Chrome Web Inspecter launched"}
+        .catch ->
+          res.status(500).json { error: "Could not launch Chrome Web Inspector" }
+      else
+        res.status(500).json { error: "Invalid request"}
+
     @app.options "/__appgyver/logger", (req, res) =>
       res.header "Access-Control-Allow-Origin", "*"
       res.header "Access-Control-Allow-Headers", "Content-Type"


Expose d3

Expose d3

--- client/src/app.coffee
+++ client/src/app.coffee
@@ -6,6 +6,8 @@
 window._ = lodash
 moment = require 'moment'
 window.moment = moment
+d3 = require 'd3'
+window.d3 = d3

 require 'angular-material/angular-material.min.css'
 require 'angular-material-data-table/dist/md-data-table.min.css'
@@ -31,7 +33,6 @@

 require 'angular-aria'

-require 'd3'
 require 'd3-bdc'
 require 'c3'
 require 'MimeJS/dist/mime-js.js'


rm needless comments

rm needless comments

--- public/assets/coffee/order-id-payment.coffee
+++ public/assets/coffee/order-id-payment.coffee
@@ -128,22 +128,3 @@
       error: (jqXHR, textStatus, errorThrown) ->
       complete: (jqXHR, textStatus) ->
         $this.removeClass('disabled')
-
-  ###
-  STATUS =
-    choose_address: 49
-    payment_done: 50
-
-  $('#btn-payment').click (e) ->
-    e.preventDefault()
-    $this = $(@)
-    $this.addClass('disabled')
-    $.ajax location.href,
-      type: 'PUT'
-      data: { status_id: STATUS.payment_done }
-      success: (data, textStatus, jqXHR) ->
-        location.reload()
-      error: (jqXHR, textStatus, errorThrown) ->
-      complete: (jqXHR, textStatus) ->
-        $this.removeClass('disabled')
-  ###


What am I fixing? D:

What am I fixing? D:

--- base/assets/coffeescript/search.coffee
+++ base/assets/coffeescript/search.coffee
@@ -9,7 +9,7 @@
     @$field.on 'keydown.results', @onFieldKeyDown
     @$field.on 'input.results', @onFieldInput
     @$field.on 'focusout.results', @teardown
-    @$field.on 'submit.results', @teardown
+    @$form.on 'submit.results', @teardown

     @$template = $ '#js-search-results-template'
     @$results = $ '.js-search-results'
@@ -33,3 +33,7 @@
       success: (data) =>
         template = Handlebars.templates["autocomplete"]
         @$results.html(template(data))
+
+$(document).on 'click', '.result-item', ->
+  $('.js-search').val("")
+  return


Broken sentence selection commit

Broken sentence selection commit

--- annotran/static/scripts/annotator/plugin/sentenceselection.coffee
+++ annotran/static/scripts/annotator/plugin/sentenceselection.coffee
@@ -9,74 +9,39 @@
   pluginInit: ->
     # Register the event handlers required for creating a selection
     $(document).bind({
-      "click": @makeSentenceSelection
+      "click": @findASentence
     })

     this.operational = false
     this.currentIndex = 0
+    this.currentSentence = 0
     this.storedEvent = null
+    this.extentElement = null

     null

   destroy: ->
     $(document).unbind({
-      "click": @makeSentenceSelection,
+      "click": @findASentence
     })
     super

   toggleOperation: () ->
     this.operational = not this.operational

-  prepareData: (target) ->
-    desiredText = $(target).text()
-    desiredText = desiredText.split(/[.!?]/)
-
-    offset_to_use = 0
-    counter = 0
-
-    if desiredText.length == 1
-      desiredText = []
-      desiredText.push $(target).text()
-
-    for sentence in desiredText
-      if counter == this.currentIndex
-        break
-      offset_to_use = offset_to_use + sentence.length
-      counter = counter + 1
-
-    if desiredText.length == 1
-      desiredText = $(target).text()
-    else
-      desiredText = desiredText[this.currentIndex]
-
-    full_xpath = Util.xpathFromNode($(target), document)
-
-    data = {
-      start: full_xpath
-      startOffset: offset_to_use + this.currentIndex
-      end: full_xpath
-      endOffset: offset_to_use + desiredText.length + this.currentIndex + 1
-    }
-
-    return data
-
-  secondaryJump: (target) ->
-    this.currentIndex = 0
-    nextSibling = $(target).next()
-
-    if nextSibling != undefined and nextSibling.length != 0
-      this.selectSentence nextSibling
-    else
-      nextSibling = $($(target).parent()).next()
-      this.selectSentence nextSibling
-
-  selectSentence: (target) ->
-
-    if tagName = $(target).prop("tagName") == undefined
-      window.getSelection().removeAllRanges()
-      return
-
-     # Get the currently selected ranges.
+  anchorToPage: (data) ->
+    anchor = new xpathRange.SerializedRange(data).normalize(document)
+
+    window.getSelection().removeAllRanges()
+    window.getSelection().addRange(anchor.toRange())
+
+    return null
+
+  normalizeStyleTags: (target) ->
+
+    return target
+
+    # Get the currently selected ranges.
     tagName = $(target).prop("tagName").toLowerCase()

     # this loop checks that we are not within a formatting cell and that we should recurse upwards through parent elements
@@ -84,37 +49,90 @@
       target = $(target).parent()
       tagName = $(target).prop("tagName").toLowerCase()

-    data = this.prepareData target
-
-    try
-      anchor = new xpathRange.SerializedRange(data).normalize(document)
-
-      window.getSelection().removeAllRanges()
-      window.getSelection().addRange(anchor.toRange())
-
-    catch error
-      # if we get here the most likely thing is that the next sentence is beyond the range, so we can just pass a call
-      # to the first sentence selection function with the target set to the next element
-      this.currentIndex = 0
-      nextSibling = $(window.getSelection().focusNode.parentElement).next()
-
-      # now try again
-      data = this.prepareData nextSibling
-
-      if data.start.length == 0
-        this.secondaryJump(target)
-        return
-
-      try
-        anchor = new xpathRange.SerializedRange(data).normalize(document)
-
-        window.getSelection().removeAllRanges()
-        window.getSelection().addRange(anchor.toRange())
-      catch error
-        this.secondaryJump(target)
-
-    return null
-
+    return target
+
+  packageData: (initialTarget, endTarget, endIndex) ->
+    full_xpath = Util.xpathFromNode($(initialTarget), document)
+    end_xpath = Util.xpathFromNode($(endTarget), document)
+
+    data = {
+      start: full_xpath
+      startOffset: this.currentIndex
+      end: end_xpath
+      endOffset: endIndex + 1
+    }
+
+    return data
+
+  selectSentence: (initialTarget, currentTarget = undefined, force = false) ->
+
+    if currentTarget == undefined
+        currentTarget = initialTarget
+
+    desiredText = $(currentTarget).text()
+    match = /[.!?]/.test(desiredText)
+    desiredText = desiredText.split(/[.!?]/)
+
+    offset_to_use = 0
+    counter = 0
+
+    if desiredText.length == 1
+      desiredText = []
+      desiredText.push $(currentTarget).text()
+
+    for sentence in desiredText
+      if counter == this.currentSentence
+        break
+      offset_to_use = offset_to_use + sentence.length
+      counter = counter + 1
+
+    if desiredText.length == 1
+      desiredText = $(currentTarget).text()
+    else
+      desiredText = desiredText[this.currentSentence]
+
+    endIndex = offset_to_use + desiredText.length + this.currentSentence + 1
+
+    if endIndex > $(currentTarget).text().length - 1
+      endIndex = $(currentTarget).text().length - 1
+
+    # test if we have found a sentence marker or if we are forcing this through anyway
+    if match or force
+      data = this.packageData(initialTarget, currentTarget, endIndex)
+      this.anchorToPage(data)
+    else
+      # here want to test:
+      # 1. is there a sibling element?
+      # 2. is there a parent element with a next sibling?
+      nextSibling = $(currentTarget).next()
+
+      if nextSibling != undefined and nextSibling.length != 0
+        this.selectSentence initialTarget, nextSibling
+      else
+        nextSibling = this.findNextJumpNode(currentTarget)
+
+        if nextSibling != undefined and nextSibling.length != 0
+          this.selectSentence initialTarget, currentTarget, true
+        else
+          this.selectSentence initialTarget, nextSibling
+
+  findASentence: (event = {}) =>
+    this.storedEvent = event
+
+    this.currentIndex = 0
+    this.selectSentence event.target
+
+    selection = Annotator.Util.getGlobal().getSelection()
+    ranges = for i in [0...selection.rangeCount]
+      r = selection.getRangeAt(0)
+      if r.collapsed then continue else r
+
+    if ranges.length
+      event.ranges = ranges
+      @annotator.onSuccessfulSelection event
+      @annotator.createAnnotation()
+
+    return null

   # This is called when the mouse is clicked on a DOM element.
   # Checks to see if there is a sentence that we can select, if so
@@ -131,6 +149,7 @@
     this.storedEvent = event

     this.currentIndex = 0
+    this.currentSentence = 0
     this.selectSentence event.target

     selection = Annotator.Util.getGlobal().getSelection()
@@ -145,16 +164,60 @@

     return null

+  findNextJumpNode: (target) ->
+    parent = $(target).parent()
+
+    nextSibling = $(parent).next()
+
+    if nextSibling == undefined
+      return this.findNextJumpNode parent
+
+    else if nextSibling.length == 0
+
+      tagName = $(nextSibling).prop("tagName")
+
+      if tagName != undefined
+        tagName = tagName.toLowerCase()
+
+        if tagName == 'body' or tagName == 'html'
+          return nextSibling
+        else
+          return this.findNextJumpNode parent
+      else
+        return this.findNextJumpNode parent
+
+    return nextSibling

   moveToNextSentence: (event) ->
     if this.operational == false
       # we are not in sentence selection mode
       return

-    this.currentIndex = this.currentIndex + 1
     currentSelection = window.getSelection()

-    this.selectSentence(currentSelection.anchorNode.parentElement)
+    this.currentIndex = currentSelection.extentOffset
+    this.currentSentence = this.currentSentence + 1
+
+    elementToUse = currentSelection.extentNode.parentElement
+
+    if elementToUse.textContent.length <= (this.currentIndex)
+      nextSibling = $(elementToUse).next()
+
+      this.currentIndex = 0
+      this.currentSentence = 0
+
+      # algorithm here is:
+      # 1. check if there is a sibling
+      # 2. if there is no sibling, check the parent
+      # 3. if the parent has a sibling, use that
+      # 4. if the parent has no sibling, pass parent as element to 2
+
+      if nextSibling == undefined or nextSibling.length == 0
+        nextSibling = this.findNextJumpNode(elementToUse)
+
+      elementToUse = nextSibling
+
+    this.selectSentence elementToUse

     selection = Annotator.Util.getGlobal().getSelection()
     ranges = for i in [0...selection.rangeCount]


Added more tests

Added more tests

--- test/browser/animframe.coffee
+++ test/browser/animframe.coffee
@@ -10,4 +10,16 @@
     expect(requestAnimationFrame).to.be.a('function')

   it 'cancelAnimationFrame is available', ->
-    expect(cancelAnimationFrame).to.be.a('function')+    expect(cancelAnimationFrame).to.be.a('function')
+
+  it 'requestAnimationFrame takes a function', (done) ->
+    requestAnimationFrame -> done()
+
+  it 'requestAnimationFrame return value is a number', ->
+    ret = requestAnimationFrame ->
+    expect(ret).to.be.a('number')
+
+  it 'requestAnimationFrame callback parameter is a number', (done) ->
+    requestAnimationFrame (time) ->
+      expect(time).to.be.a('number')
+      done()


switch to chai leads to passing tests in ie. continuing switch

--- test/drill-reader_test.coffee
+++ test/drill-reader_test.coffee
@@ -1,5 +1,6 @@
 # test suit for the NC drill file parser
 Reader = require '../src/drill-reader'
+expect = require('chai').expect

 # twenty line test file
 TEST_DRILL = '''
@@ -31,31 +32,31 @@
     r = new Reader TEST_DRILL

   it 'should get the next data block', ->
-    r.nextBlock().should.eql 'M48'
-    r.nextBlock().should.eql ';FORMAT={2:4/ absolute / inch / keep zeros}'
-    r.nextBlock().should.eql 'FMAT,2'
-    r.nextBlock().should.eql 'INCH,TZ'
+    expect( r.nextBlock() ).to.eql 'M48'
+    expect( r.nextBlock() ).to.eql ';FORMAT={2:4/ absolute / inch / keep zeros}'
+    expect( r.nextBlock() ).to.eql 'FMAT,2'
+    expect( r.nextBlock() ).to.eql 'INCH,TZ'

   it 'should return false when there are no more data blocks', ->
     r.nextBlock() for i in [0...19]
-    r.nextBlock().should.eql 'M30'
-    r.nextBlock().should.be.false
+    expect( r.nextBlock() ).to.eql 'M30'
+    expect( r.nextBlock() ).to.be.false

   it 'should keep track of the line number', ->
-    r.line.should.equal 0
+    expect( r.line ).to.equal 0
     r.nextBlock()
-    r.line.should.equal 1
+    expect( r.line ).to.equal 1
     r.nextBlock()
-    r.line.should.equal 2
+    expect( r.line ).to.equal 2
     r.nextBlock()
-    r.line.should.equal 3
+    expect( r.line ).to.equal 3

   it 'should ignore CR to work with LF (unix) or CRLF (windows)', ->
     rWindows = new Reader 'M48\r\nFMAT,2\r\nINCH,TZ\r\nT1C0.015\r\n'
-    rWindows.line.should.equal 0
-    rWindows.nextBlock().should.eql 'M48'
-    rWindows.line.should.equal 1
-    rWindows.nextBlock().should.eql 'FMAT,2'
-    rWindows.line.should.equal 2
-    rWindows.nextBlock().should.eql 'INCH,TZ'
-    rWindows.line.should.equal 3
+    expect( rWindows.line ).to.equal 0
+    expect( rWindows.nextBlock() ).to.eql 'M48'
+    expect( rWindows.line ).to.equal 1
+    expect( rWindows.nextBlock() ).to.eql 'FMAT,2'
+    expect( rWindows.line ).to.equal 2
+    expect( rWindows.nextBlock() ).to.eql 'INCH,TZ'
+    expect( rWindows.line ).to.equal 3


Call the right function

Call the right function

--- src/lib/loader/ComponentIo.coffee
+++ src/lib/loader/ComponentIo.coffee
@@ -59,7 +59,7 @@

 exports.register = (loader, callback) ->
   # Start discovery from baseDir
-  customLoader loader, loader.baseDir, callback
+  customLoader.getModuleComponents loader, loader.baseDir, callback

 exports.dynamicLoad = (name, cPath, metadata, callback) ->
   try