:minibus: LinterViews::changeTab -> LinterViews::_changeTab

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -15,10 +15,10 @@
     @_bottomStatus = new BottomStatus()

     @_bottomTabFile.initialize("Current File", =>
-      @changeTab('file')
+      @_changeTab('file')
     )
     @_bottomTabProject.initialize("Project", =>
-      @changeTab('project')
+      @_changeTab('project')
     )
     @_bottomStatus.initialize()
     @_panelWorkspace = atom.workspace.addBottomPanel item: @_panel, visible: false
@@ -67,13 +67,6 @@
       )
       break

-  # consumed in views/bottom-tab-{file, project}
-  changeTab: (Tab)->
-    @_scope = Tab
-    @_bottomTabProject.active = Tab is 'project'
-    @_bottomTabFile.active = Tab is 'file'
-    @_renderPanel()
-
   # consumed in views/panel
   setPanelVisibility: (Status)->
     if Status
@@ -98,6 +91,12 @@
     @_panel.removeDecorations()
     @_panelWorkspace.destroy()
     @_removeBubble()
+
+  _changeTab: (Tab)->
+    @_scope = Tab
+    @_bottomTabProject.active = Tab is 'project'
+    @_bottomTabFile.active = Tab is 'file'
+    @_renderPanel()

   _removeBubble: ->
     return unless @_bubble


Add isPreviewableItem helper

Add isPreviewableItem helper

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -50,7 +50,7 @@
       @removeTabForItem(item)

     @subscriptions.add @pane.onDidChangeActiveItem (item) =>
-      if atom.config.get('tabs.usePreviewTabs') and item instanceof TextEditor
+      if @isPreviewableItem(item)
         if @getTabs().length > 1 and @tab?.item isnt item and @tab?.isPreviewTab
           @pane.destroyItem(@tab.item)
         @tab = @tabForItem(item)
@@ -92,11 +92,14 @@
     RendererIpc.removeListener('tab:dropped', @onDropOnOtherWindow)
     @subscriptions.dispose()

+  isPreviewableItem: (item) ->
+    atom.config.get('tabs.usePreviewTabs') and item instanceof TextEditor
+
   addTabForItem: (item, index) ->
     tabView = new TabView()
     tabView.initialize(item)
-    if atom.config.get('tabs.usePreviewTabs') and not @tab and item instanceof TextEditor
-      @tab = tabView
+    @tab ?= tabView if @isPreviewableItem(item)
+
     @insertTabAtIndex(tabView, index)

   moveItemTabToIndex: (item, index) ->


those properties are not needed to make the example work

--- examples/collection.coffee
+++ examples/collection.coffee
@@ -23,13 +23,10 @@
 '''

 class Post extends Monkey.Model
-  @property 'title'
-  @property 'body'
   @property 'commentCount', dependsOn: 'comments', get: -> @comments.length
   @collection 'comments'

 class Comment extends Monkey.Model
-  @property 'body'

 class PostController
   postComment: ->


dummy nodes during layout for proper tree shape

--- tree2.coffee
+++ tree2.coffee
@@ -660,8 +660,13 @@

       if node.child0?
         children.push(node.child0)
+      else if node.child1?
+        children.push((radius: default_node_radius, dummy: true))
+
       if node.child1?
         children.push(node.child1)
+      else if node.child0?
+        children.push((radius: default_node_radius, dummy: true))

       if children.length > 0
         children_width = 0
@@ -707,10 +712,12 @@
       p = rp.parent
       pos = rp.pos

+      if c.dummy
+        continue
+
       pidx = moved_nodes.indexOf(p)
       ppos = new_positions[pidx]

-      console.log(pos.x, pos.y, ppos.x, ppos.y)
       newpos = (x: ppos.x + pos.x, y: ppos.y + pos.y)
       new_positions.push(newpos)
       moved_nodes.push(c)


animation support in preparation for sorting

--- tree2.coffee
+++ tree2.coffee
@@ -159,22 +159,23 @@
 collection_dropdown_menu = [
   {
     name: 'sort by weight',
-    action: (c) ->
+    action: (c, t) ->
       console.log('sort by weight')
+      c.sortNodes( ((n1, n2) -> n1.value - n2.value), t)
   },
   {
     name: 'sort by alpha',
-    action: (c) ->
+    action: (c, t) ->
       console.log('sort by alpha')
   },
   {
     name: 'Shannon-Fano',
-    action: (c) ->
+    action: (c, t) ->
       console.log('Shannon-Fano')
   },
   {
     name: 'Huffman',
-    action: (c) ->
+    action: (c, t) ->
       console.log('Huffman')
   }
 ]
@@ -182,6 +183,7 @@
 class NodeCollection
   constructor: (@shape) ->
     @nodes = []
+    @animations = []

   addLeaves: (dict, pos) ->
     length = 0
@@ -197,7 +199,12 @@

       i += 1

-  render: (ctx, idx) ->
+  render: (ctx, idx, t) ->
+    if @animations.length > 0
+      @animations[0].setPositions(t)
+      if @animations[0].isFinished(t)
+        @animations = @animations[1..]
+
     if @merging?
       ctx.save()
       setStyle(ctx, light_link_style)
@@ -257,7 +264,7 @@
     return

-  mousedown: (pos, idx) ->
+  mousedown: (pos, idx, t) ->
     for n in @nodes
       if n.isHit(pos)
         if @merging?
@@ -286,7 +293,7 @@
           oy: hit.y
           mx: pos.x # mousedown location
           my: pos.y
-          t: Date.now()
+          t: t
         return true

     if @isHandleHit(pos, idx)
@@ -332,15 +339,15 @@

     return

-  mouseup: (pos) ->
+  mouseup: (pos, t) ->
     if @dropdown_menu?
       if @dropdown_menu.selected >= 0
-        @dropdown_menu.options[@dropdown_menu.selected].action(this)
+        @dropdown_menu.options[@dropdown_menu.selected].action(this, t)
       @dropdown_menu = null
       return

     if @selected?
-      if Date.now() - @selected.t < 200
+      if t - @selected.t < 200
         mdx = pos.x - @selected.mx
         mdy = pos.y - @selected.my
         if mdx*mdx+mdy*mdy < 10*10
@@ -364,11 +371,70 @@
     newnode.y = Math.min(node0.y, node1.y) - Math.abs(node0.x - node1.x)
     @nodes.push(newnode)

+  makeLineupAnim: (yoffset, duration, t) ->
+    midpointy = 0
+    for n in @nodes
+      midpointy += n.y
+    midpointy /= @nodes.length
+
+    anims =
+      (new NodeAnimation(n, (x:n.x, y:midpointy+yoffset), duration)) for n in @nodes
+
+    return new CollectionAnimation(anims, t)
+
+  makeMove1Anim: ->
+
+  sortNodes: (compare, t) ->
+    anims = []
+    anims.push(@makeLineupAnim(default_node_radius, .1, t))
+
+    #oldnodes = @nodes.slice(0) # clone
+    #newnodes = []
+    #while oldnode.length > 0
+
+    @animations = @animations.concat(anims)
+
+
+lerp2d = (t, p0, p1) ->
+  if t < 0
+    t = 0
+  if t > 1
+    t = 1
+  x: (p1.x - p0.x)*t + p0.x
+  y: (p1.y - p0.y)*t + p0.y
+
+class NodeAnimation
+  constructor: (@node, @destpos, @duration) ->
+    @origpos = {x: @node.x, y: @node.y}
+
+  setPosition: (time) ->
+    pos = lerp2d(time/@duration, @origpos, @destpos)
+    @node.x = pos.x
+    @node.y = pos.y
+
+  isFinished: (time) ->
+    time >= @duration
+
+class CollectionAnimation
+  constructor: (@node_animations, @start_time) ->
+
+  setPositions: (time) ->
+    for a in @node_animations
+      a.setPosition(time - @start_time)
+
+  isFinished: (time) ->
+    for a in @node_animations
+      if not a.isFinished(time - @start_time)
+        return false
+    return true
+
 # render loop
 canvas = document.getElementById('cnv')
 context = canvas.getContext('2d')

 render = ->
+  t = Date.now()/1000
+
   context.fillStyle = 'black'
   context.fillRect(0,0, canvas.width,canvas.height)

@@ -378,10 +444,10 @@
     context.fillText(pendingString, 0, canvas.height - 32)

   for collection, idx in collections
-    collection.render(context, idx)
+    collection.render(context, idx, t)

   for collection, idx in collections
-    collection.render_overlay(context, idx)
+    collection.render_overlay(context, idx, t)

   window.requestAnimationFrame(render)

@@ -409,9 +475,10 @@
     e.preventDefault()

 canvas.addEventListener 'mousedown', (e) ->
+  t = Date.now()/1000
   pos = getCursorPosition(canvas, e)
   for idx in [collections.length - 1..0] by -1
-    if collections[idx].mousedown(pos, idx)
+    if collections[idx].mousedown(pos, idx, t)
       break

 canvas.addEventListener 'mousemove', (e) ->
@@ -420,6 +487,7 @@
     collection.mousemove(pos)

 canvas.addEventListener 'mouseup', (e) ->
+  t = Date.now()/1000
   pos = getCursorPosition(canvas, e)
   for collection in collections
-    collection.mouseup(pos)
+    collection.mouseup(pos, t)


Add integration tests for bigint.

Add integration tests for bigint.

--- test/integration/datatypes-in-results-test.coffee
+++ test/integration/datatypes-in-results-test.coffee
@@ -28,6 +28,12 @@

 exports.intNull = (test) ->
   execSql(test, 'select cast(null as int)', null)
+
+exports.bigint = (test) ->
+  execSql(test, 'select cast(8 as bigint)', '8')
+
+exports.bigintNull = (test) ->
+  execSql(test, 'select cast(null as bigint)', null)

 exports.bitFalse = (test) ->
   execSql(test, "select cast('false' as bit)", false)


update

update

--- views/index.coffee
+++ views/index.coffee
@@ -4,10 +4,6 @@
   for name, index in @people
     li ->
       a href: "/people/#{index}", -> name
-div id: 'messages'
-div id: 'chat_input', ->
-  input id: 'text_input', type: 'text'
-  input id: 'send_button', type: 'submit', value: 'Chat'

 div id: 'chessboard', ->
   table cellpadding: 0, cellspacing: 0, ->
@@ -66,6 +62,12 @@
                   img src: 'images/chess_pieces/black_bishop.png', class: "chess_piece #{num+letter}"
                 if letter == 'e'
                   img src: 'images/chess_pieces/black_queen.png', class: "chess_piece #{num+letter}"
+
+div id: 'chat_input', ->
+  input id: 'text_input', type: 'text'
+  input id: 'send_button', type: 'submit', value: 'Chat'
+div id: 'messages'
+
 coffeescript ->
   jQuery(document).ready ->
     now.ready ->
@@ -74,7 +76,6 @@

     setPosition = (className, top, left) ->
       $("#messages").show()
-      now.distributeMessage('has moved')
       now.updateElement(className, top, left)

     if $.cookie('klop_name')
@@ -110,7 +111,7 @@
       $("#text_input").val("")
     )
     now.receiveMessage = (name, message) ->
-      $("#messages").append("
" + name + ": " + message + "
")
+      $("#messages").prepend("
" + name + ": " + message + "
")
     now.receiveElement= (className, top, left) ->
       $(".#{className.replace(" ",  ".").replace(" ui-draggable", "")}").css({"position":"relative","left":"#{left}px","top":"#{top}px"})


Adds check for err to avoid exception in kill task

--- tasks/kill.coffee
+++ tasks/kill.coffee
@@ -26,7 +26,7 @@
       console.log 'killing app' if args.verbose
       lock.killTask.skip[dist] = true
       cb = (err) ->
-        if err.code == 'ENOENT' or err.code == 1 or err.code == 128
+        if err and (err.code == 'ENOENT' or err.code == 1 or err.code == 128)
           console.error err if args.verbose
           done()
         else


make ssl connection option a hash to be inline with other drivers

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -157,9 +157,7 @@
         # Currently, the only support option is '"sslCaCerts"' which
         # is needed to perform host verification and prevent Man in
         # the Middle attacks.
-        @ssl = host.ssl || false
-        if @ssl
-            @caCerts = host.sslCaCerts
+        @ssl = host.ssl || {}

         # The protocol allows for responses to queries on the same
         # connection to be returned interleaved. When a query is run
@@ -922,10 +920,13 @@
             host: @host,
             port: @port,
         }
-        if @ssl
-            options.ca = [ fs.readFileSync(@caCerts) ]
-            options.servername = @host
-            @rawSocket = tls.connect options
+        if Object.keys(@ssl).length > 0
+            if 'caCerts' of @ssl
+                options.ca = [ fs.readFileSync(@ssl.caCerts) ]
+                options.servername = @host
+                @rawSocket = tls.connect options
+            else
+                throw new err.RqlDriverError "SSL options provided but 'caCerts' argument missing"
         else
             @rawSocket = net.connect @port, @host


:fire: unused

:fire: unused

--- lib/persistent-selection-manager.coffee
+++ lib/persistent-selection-manager.coffee
@@ -1,5 +1,5 @@
 _ = require 'underscore-plus'
-{Emitter, CompositeDisposable} = require 'atom'
+{CompositeDisposable} = require 'atom'

 module.exports =
 class PersistentSelectionManager
@@ -9,7 +9,6 @@
     {@editor, @editorElement} = @vimState
     @disposables = new CompositeDisposable
     @disposables.add @vimState.onDidDestroy(@destroy.bind(this))
-    @emitter = new Emitter

     @markerLayer = @editor.addMarkerLayer()
     options = {type: 'highlight', class: 'vim-mode-plus-persistent-selection'}


background quota

background quota

--- server/code/model/box.coffee
+++ server/code/model/box.coffee
@@ -119,9 +119,9 @@
         else if res.statusCode isnt 200
           callback {statusCode: res.statusCode, body: body}, null
         else
-          #TODO: background this
           Plan.setDiskQuota box, user.accountLevel, (err) ->
-            callback null, box
+            console.warn "setDiskQuota on #{box.name} error: #{err}"
+          callback null, box

   @_generateBoxName: ->
     r = Math.random() * Math.pow(10,9)


Remove unused packages in Atom

Remove unused packages in Atom

--- atom/packages.cson
+++ atom/packages.cson
@@ -1,28 +1,9 @@
 packages: [
-  "autocomplete-emojis"
-  "busy-signal"
   "editorconfig"
   "file-icons"
   "highlight-selected"
-  "intentions"
-  "linter"
-  "linter-coffeelint"
-  "linter-csslint"
-  "linter-eslint"
-  "linter-flake8"
-  "linter-htmlhint"
-  "linter-js-yaml"
-  "linter-jsonlint"
-  "linter-less"
-  "linter-php"
-  "linter-scss-lint"
-  "linter-shellcheck"
-  "linter-ui-default"
-  "linter-write-good"
-  "linter-xmllint"
   "minimap"
   "omni-ruler"
-  "package-sync"
   "permanent-delete"
   "pigments"
   "platformio-ide-terminal"


Update to latest packages list

Update to latest packages list

--- atom/packages.cson
+++ atom/packages.cson
@@ -1,23 +1,26 @@
 'packages': [
+  'Sublime-Style-Column-Selection'
+  'atom-color-highlight'
   'atom-lint'
+  'atomatigit'
   'auto-copyright'
   'block-travel'
-  'color-picker'
-  'css-color-highlighting'
-  'editor-stats'
+  'dash'
+  'emacs-flow'
   'fancy-new-file'
-  'gist-it'
+  'highlight-selected'
   'language-haml'
+  'language-less'
   'language-r'
-  'language-rspec'
-  'minimap'
+  'language-treetop'
   'package-sync'
-  'railscasts-syntax'
   'remember-window'
+  'revert-buffer'
   'scroll-past-end'
   'sort-lines'
   'symbol-gen'
   'tabs-to-spaces'
-  'todo-show'
+  'toggle-invisibles'
   'toggle-quotes'
+  'ya-railscasts-syntax'
 ]


Restructure projects

Restructure projects

--- atom/projects.cson
+++ atom/projects.cson
@@ -1,45 +1,48 @@
 [
   {
-    title: "Personal"
+    title: "dev"
     paths: [
       "/Users/rogier/Dropbox/git/blijblijblij.github.io"
       "/Users/rogier/Dropbox/git/blijblijblij.github.io-hugo"
-      "/Users/rogier/Dropbox/git/docker-mesos"
-      "/Users/rogier/Dropbox/git/docker-public"
       "/Users/rogier/git/dotfiles"
       "/Users/rogier/Dropbox/git/learning-ruby"
       "/Users/rogier/Dropbox/git/playground"
-      "/Users/rogier/Dropbox/git/presentations"
-      "/Users/rogier/git/curly-garbanzo"
-      "/Users/rogier/Dropbox/git/learning-python"
-      "/Users/rogier/git"
+      "/Users/rogier/Dropbox/git/meia-ontwerp-rails"
+      "/Users/rogier/Dropbox/git/meia-ontwerp-html"
     ]
   }
   {
-    title: "Work"
+    title: "rm data"
+    paths: [
+      "/Users/rogier/git/rm-ddl"
+      "/Users/rogier/git/rm-etl"
+      "/Users/rogier/git/rm-pentaho"
+      "/Users/rogier/git/rm-pentaho-openid-adapter"
+    ]
+  }
+  {
+    title: "rm rails"
+    paths: [
+      "/Users/rogier/git/rm-content-admin"
+      "/Users/rogier/git/rm-data-admin"
+      "/Users/rogier/git/rm-masterdata-engine"
+      "/Users/rogier/git/rm-rails"
+      "/Users/rogier/git/rm-oidc-engine"
+      "/Users/rogier/git/rm-survey-frontend"
+      "/Users/rogier/git/rm-survey-backend"
+    ]
+  }
+  {
+    title: "rm var"
     paths: [
       "/Users/rogier/git/rm-data-dev"
-      "/Users/rogier/git/rm-ddl"
       "/Users/rogier/git/rm-docker"
-      "/Users/rogier/git/rm-etl"
+      "/Users/rogier/git/rm-documentation"
+      "/Users/rogier/git/rm-documentation.wiki"
       "/Users/rogier/git/rm-mesos"
-      "/Users/rogier/git/rm-pentaho"
       "/Users/rogier/git/rm-scripts"
       "/Users/rogier/git/rm-secrets"
       "/Users/rogier/git/rm-system"
-      "/Users/rogier/git/rm-performance-jmeter"
-    ]
-  }
-  {
-    title: "Work - var"
-    paths: [
-      "/Users/rogier/git/rm-data-admin"
-      "/Users/rogier/git/rm-documentation"
-      "/Users/rogier/git/rm-documentation.wiki"
-      "/Users/rogier/git/rm-masterdata-engine"
-      "/Users/rogier/git/rm-oidc-engine"
-      "/Users/rogier/git/rm-pentaho-openid-adapter"
-      "/Users/rogier/git/rm-rails"
     ]
   }
 ]


Rename 'mediaquery' snippet prefix to 'mediaq'

The 'mediaquery' prefix was conflicting with autocompletion for the
margin* CSS properties.

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -697,7 +697,7 @@
 '.source.css':

   '@media':
-    'prefix': 'mediaquery'
+    'prefix': 'mediaq'
     'body': '@media $1 {\n\t$2\n}$3'

   '@media screen':


return request data for post operation

return request data for post operation

--- src/app/config/restangular.coffee
+++ src/app/config/restangular.coffee
@@ -7,7 +7,7 @@
     Accept: "application/json"

   RestangularProvider.setResponseInterceptor (data, operation, what, url, response, deferred) ->
-    if operation is "get"
+    if operation is "get" or operation is "post"
       # Trim the 's' off so we can reference the singular root note name
       # This is a total hack and won't work for resources like "people"
       return response.data[what.substring(0, what.length-1)]


Modify flow test.

Modify flow test.

--- test/4.flow.coffee
+++ test/4.flow.coffee
@@ -1,6 +1,8 @@
 steps.push(->
     console.log("----- Flow -----")
     new Test(
+    ).set((v, t) ->
+        v.a = 1
     ).add(
         new Test(
         ).after((v) ->
@@ -12,6 +14,9 @@
             t.end()
         , 2500)
     ).after((v) ->
+        console.log(v.a)
+        v.a = 2
+        console.log(v.a)
         console.log("after 2")
     ).run()
 )


nc - delete

nc - delete

--- test/canada.coffee
+++ test/canada.coffee
@@ -1,78 +0,0 @@
-#
-# Test Canadian addresses
-#
-# --- Included at the top of each test file
-_ = require('lodash'); chai = require('chai')
-assert = chai.assert; expect = chai.expect; should = chai.should()
-ap = require("../build/SnailMailAddressParser")
-color = require('mocha').reporters.Base.color
-# ---
-
-# A list of expected results for the given addresses
-expectations = {
-  "100 Bay Street\nToronto, ON M5H 1T1" : {
-    "municipality": "Toronto",
-    "province": "ON",
-    "postal": "M5H 1T1",
-    "country": "Canada",
-    "street_number": "100",
-    "street_name": "Bay Street",
-    'street_name_2': '',
-    "addressee": "",
-    'suite': ''
-  },
-
-  "Mr. Incognito\n100 Bay Street\nToronto, ON M5H 1T1" : {
-    "municipality": "Toronto",
-    "province": "ON",
-    "postal": "M5H 1T1",
-    "country": "Canada",
-    "street_number": "100",
-    "street_name": "Bay Street",
-    "street_name_2": "",
-    "addressee": "Mr. Incognito"
-    'suite': ''
-  },
-}
-
-describe "Unambiguous Canadian addresses", ->
-  _.each expectations, (expect, from_string) ->
-    from_addr = color("pending", JSON.stringify(from_string)) # escape newlines
-
-    describe from_addr, ->
-      expect_string = color("pending", JSON.stringify(expect, null, 2))
-
-      it "is expected to be an object", ->
-        assert.isObject(expect)
-
-      describe "when parsed", ->
-        parsed = ap.parse(from_string, 'Canada')
-        parsed_string = color("error message", JSON.stringify(parsed, null, 2))
-
-        it "parses into an object", ->
-          assert.isObject(parsed)
-
-        it "has the expected keys", ->
-          parsed_only_keys = _.difference(_.keys(parsed), _.keys(expect))
-          expect_only_keys = _.difference(_.keys(expect), _.keys(parsed))
-
-          assert(_.isEmpty(parsed_only_keys),
-            "Expected \"#{parsed_only_keys}\" attribute(s), "+
-            "but 'twas not in the parsed result")
-          assert(_.isEmpty(expect_only_keys),
-            "Parsed result had \"#{parsed_only_keys}\" attribute(s), "+
-            "but 'twas not expected")
-
-        it "has identical values", ->
-          # all the keys are identical
-          assert(_.isEqual(expect, parsed),
-            "Parsed result is not what was expected." +
-            " Expected:\n#{expect_string}" +
-            " Got \n#{parsed_string}")
-          return
-
-      return # describe address
-
-    return # describe good address
-
-


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -212,10 +212,10 @@

   color2 = color.grayscale()

-  equal color.r, 35.7
-  equal color.g, 35.7
-  equal color.b, 35.7
-  equal color.a, 1.0
+  equal color2.r, 35.7
+  equal color2.g, 35.7
+  equal color2.b, 35.7
+  equal color2.a, 1.0

 module()


display warning message when course isn't found

--- tutor/src/helpers/conditional-handlers.cjsx
+++ tutor/src/helpers/conditional-handlers.cjsx
@@ -7,6 +7,7 @@

 {default: CCStudentRedirect}       = require '../components/cc-student-redirect'
 {default: StudentDashboardShell} = require '../components/student-dashboard'
+{default: WarningModal} = require '../components/warning-modal'
 CCDashboard = require '../components/cc-dashboard'
 {CourseStore} = require '../flux/course'

@@ -29,7 +30,12 @@
     extend(props, {courseId})
     course = CourseStore.get(courseId)
     unless course
-      return <InvalidPage message="course was not found" />
+      return (
+        <WarningModal
+          title="Sorry, you can’t access this course"
+          message="You are no longer a student in this course. Please contact your instructor if you are still enrolled in this course and need to be re-added."
+        />
+      )
     unless props.match.isExact
       return <MatchForTutor {...props} />


don't generate a new slug if one already exists

--- models/team.coffee
+++ models/team.coffee
@@ -271,6 +271,8 @@
       uniquifySlug s, attempt + 1, next

 TeamSchema.pre 'save', (next) ->
+  return next() if @slug?
+
   s = @name
     .toLowerCase()
     .replace(/[^-a-z0-9]+/g, '-')


Add full badge img object to the EarnedBadge

--- models/user.coffee
+++ models/user.coffee
@@ -14,6 +14,8 @@
   name:          String
   image:
     type: String,
+  imageObj:
+    type: Schema.Types.Mixed,
   description:   String
   criteria:      String
   version:       String
@@ -36,6 +38,7 @@
   {
     name: @name,
     image: @image,
+    imageObj: @imageObj,
     description: @description,
     criteria: @criteria,
     version: @version,
@@ -102,6 +105,7 @@
     b.issued_on = new Date()
     b.badge_id = badge.id
     b.image = badge.imageUrl
+    b.imageObj = badge.image
     b.issued_count = undefined
     @badges.push b
     @save (err, user)=>


added status table

added status table

--- _source/lib/collections/status.coffee
+++ _source/lib/collections/status.coffee
@@ -3,13 +3,13 @@
 status = Apollos.generateSchema
   name:
     type: String
-    required: true
+    optional: false
   status:
     type: Boolean
-    required: true
+    optional: false
   message:
     type: String
-    required: false
+    optional: true

 Apollos.people.attachSchema status


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -1,6 +1,6 @@
 module "Follow"

 test "testing for equality", ->
-  ok true
+

 module()


add test for the module interface

add test for the module interface

--- test/index.coffee
+++ test/index.coffee
@@ -1 +1,30 @@
+chai = require 'chai'
+
+easter = require '../src/'
+
+
 describe 'easter', ->
+
+    describe '.factory()', ->
+
+        it 'should throw for an unsupported protocol', ->
+            chai.assert.throws ->
+                easter.factory 'websockets'
+            , Error, /Protocol websockets not defined/
+
+    describe '.singleton()', ->
+
+        it 'should cache the instance of a protocol client', ->
+            amqpClient1 = easter.singleton 'amqp'
+            amqpClient2 = easter.singleton 'amqp'
+            chai.assert.equal amqpClient1, amqpClient2,
+                'should return the same client instance'
+            chai.assert.instanceOf amqpClient1, easter.RabbitAmqpClient,
+                'should be an instance of amqp protocol client'
+
+            amqpClient3 = easter.singleton 'http'
+            amqpClient4 = easter.singleton 'http'
+            chai.assert.equal amqpClient3, amqpClient4,
+                'should return the same client instance'
+            chai.assert.instanceOf amqpClient3, easter.RabbitHttpClient,
+                'should be an instance of amqp protocol client'


test #first() and #last()

test #first() and #last()

--- test/index.coffee
+++ test/index.coffee
@@ -38,7 +38,7 @@
     #   prior_last = instance.last()
     #   instance.push 'ghi', 'jkl'
     #   assert.equal instance.tail().prev, prior_tail
-
+
     it "adds the pair at the head if no head existed previously", ->
       instance.push 'abc', 'def'
       assert.equal instance.first(), instance.last()
@@ -98,7 +98,7 @@
       instance.unshift 'ghi', 'jkl'
       assert.equal instance.size(), 2
       assert.equal instance.first(), 'jkl'
-
+
     # it 'wires up .next properly', ->
     #   instance.unshift 'abc', 'def'
     #   prior_head = instance.head()
@@ -121,7 +121,7 @@
       assert.equal instance.get('c'), 'd'

   describe "#insert", ->
-
+

   describe "#update", ->
     it "updates the value of an existing key", ->
@@ -186,7 +186,7 @@
       results = []
       instance.forEach [].push.bind(results)
       assert.deepEqual results, ['a', 1, 'b', 2, 'c', 3, 'd', 4]
-
+
   describe "#has", ->
     it "returns whether or not key is in the map", ->
       instance.push 'a', 1
@@ -203,9 +203,6 @@
       assert.equal instance.has('hasOwnProperty'), false

   describe "#size", ->
-    it "is a function", ->
-      assert.equal typeof instance.size, "function"
-
     it "returns the number of items in the map", ->
       assert.equal instance.size(), 0
       instance.push 'a', 'b'
@@ -218,12 +215,22 @@
       assert.equal instance.size(), 0

   describe "#first", ->
-    it "is a function", ->
-      assert.equal typeof instance.first, "function"
+    it "returns the first item in the map", ->
+      instance.push('a', 'b')
+      assert.equal instance.first(), 'b'
+      instance.push('c', 'd')
+      assert.equal instance.first(), 'b'
+      instance.shift()
+      assert.equal instance.first(), 'd'

   describe "#last", ->
-    it "is a function", ->
-      assert.equal typeof instance.last, "function"
+    it "retursn the last item in the map", ->
+      instance.push('a', 'b')
+      assert.equal instance.last(), 'b'
+      instance.push('c', 'd')
+      assert.equal instance.last(), 'd'
+      instance.pop()
+      assert.equal instance.last(), 'b'

   describe "#at", ->
     it "returns the value for a given index", ->
@@ -256,7 +263,7 @@
       instance.push 'c', 3
       assert.equal instance.size(), 3
       assert.equal instance.first(), 1
-
+
       instance.clear()

       assert.equal instance.size(), 0


windows backslash fix

windows backslash fix


tableverse/dice: Cleanup ownership check.

--- oghma/tableverse/dice.coffee
+++ oghma/tableverse/dice.coffee
@@ -87,7 +87,7 @@

         for name, k of @__k
           k.on( 'click', ( event )=>
-            if O.me().gets( 'name' ) == @__.owner || event.shiftKey
+            if O.i_own( this ) || event.shiftKey
               @remove()
           )


changing the test format for cleaner code, and faster tests

--- test/sample.coffee
+++ test/sample.coffee
@@ -1,27 +1,25 @@
 expect     = require('expect.js')
 Browser    = require('zombie')
-browser    = new Browser()

 url = 'http://localhost:4567'

 describe 'Load page: http://localhost:4567 and', ->
+  browser  = null
+  testOpts =
+    title: 'AcmeCredit'

-  testOpts =
-    title: 'MyWay!'
-    bodyBackgroundColor: 'white'
+  before ->
+    browser = new Browser()
+    return browser.visit url

   it "is expected to have a title of '#{testOpts.title}'", (done) ->
-    browser.visit url, ->
-      expect(browser.text('title')).to.equal(testOpts.title)
-      done()
+    expect(browser.text("title")).to.equal(testOpts.title)
+    done()

   it "should have a title of '#{testOpts.title}'", (done) ->
-    browser.visit url, ->
-      browser.text("title").should.equal(testOpts.title)
-      done()
+    browser.text("title").should.equal(testOpts.title)
+    done()

-  it "should have a white background", (done) ->
-      browser.visit url, ->
-        console.log browser.assert.style('body', 'background')
-        browser.assert.style('body', 'background', testOpts.bodyBackgroundColor)
-        done()
+  it "should have a white background on the body", (done) ->
+    browser.assert.style('#main', 'background', 'white')
+    done()


Finish up wrap links options for the accountButtons helper.  Also added a config option for showUserName to decide if we want to show the username or not in the header.

--- shared/accountButtons.coffee
+++ shared/accountButtons.coffee
@@ -5,7 +5,14 @@

 Template.differentialAccountButtons.helpers
   signedInAs: ->
-    Meteor.user().username || Meteor.user().emails[0].address
+    if Meteor.user().username
+      Meteor.user().username
+    else if Meteor.user().profile.name
+      Meteor.user().profile.name
+    else if Meteor.user().emails and Meteor.user().emails[0]
+      Meteor.user().emails[0].address
+    else
+      "Logged In"

   profileUrl: ->
     Router.path(AccountsEntry.config.profileRoute)


bump max sockets in api

bump max sockets in api

--- web.coffee
+++ web.coffee
@@ -7,6 +7,8 @@
 manifest = require("manifest")
 storage  = require("storage").init()
 util     = require("util")
+
+require("http").globalAgent.maxSockets = 50

 express.logger.format "method", (req, res) ->
   req.method.toLowerCase()


Removed debug outputs.

Removed debug outputs.

--- lib/atom-html-preview.coffee
+++ lib/atom-html-preview.coffee
@@ -18,8 +18,6 @@

     @subscriptions.add atom.workspace.observeTextEditors (editor) =>
       @subscriptions.add editor.onDidSave =>
-        console.log "onDidSave"
-        console.log htmlPreviewView?
         if htmlPreviewView? and htmlPreviewView instanceof HtmlPreviewView
           htmlPreviewView.renderHTML()


:art: Add brackets in front of functions with no args

--- lib/autocomplete-hack.coffee
+++ lib/autocomplete-hack.coffee
@@ -80,7 +80,7 @@
                   Snippet = Text + "(" + Snippet.join(", ") + ")"
                   EntryToReturn.snippet = Snippet
                 else
-                  EntryToReturn.text = Text
+                  EntryToReturn.text = Text + "()"
               else
                 EntryToReturn.text = Text
               return EntryToReturn


fix history

fix history

--- lib/browser-plus-view.coffee
+++ lib/browser-plus-view.coffee
@@ -35,7 +35,7 @@
         params.src = "data:text/html,#{params.src}"
       url = params.src
     if params.url.startsWith "browser-plus://"
-      url = params.browserPlus.getBrowserPlusUrl(url)
+      url = params.browserPlus?.getBrowserPlusUrl?(url)

     @div class:'browser-plus', =>
       @div class:"url native-key-bindings #{hideURLBar}",outlet:'urlbar', =>


OX-362 Simplify flycam.setSpaceDirection()

--- app/assets/javascripts/oxalis/model/flycam2d.coffee
+++ app/assets/javascripts/oxalis/model/flycam2d.coffee
@@ -119,8 +119,7 @@
     if @direction[ind[1]] <= 0 then @spaceDirection[ind[1]] = -1 else @spaceDirection[ind[1]] = 1

   setSpaceDirection : (direction) ->
-    indices = Dimensions.getIndices @activePlane
-    for index in indices
+    for index in [0..2]
       if direction[index] <= 0 then @spaceDirection[index] = -1 else @spaceDirection[index] = 1

   getSpaceDirection : ->


auto scrolling in outcomes should not jump around.
fixes #11499, #11501, #11506

Change-Id: I6c04de369ff2e2af67ceea1b8f5753609c9b3f51
Reviewed-on: https://gerrit.instructure.com/14845
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>

--- app/coffeescripts/views/outcomes/SidebarView.coffee
+++ app/coffeescripts/views/outcomes/SidebarView.coffee
@@ -55,10 +55,7 @@
       clickedOutside = e.target is e.currentTarget
       return unless clickedOutside
       dir = $(e.target).data 'view'
-      if dir.parent
-        dir.parent.selectedModel.trigger 'select'
-      else
-        @selectDir dir
+      @selectDir dir

     # Adds a directory view for an outcome group.
     # Returns the directory view.
@@ -105,29 +102,20 @@

     # Select the directory view and optionally select an Outcome or Group.
     selectDir: (dir, selectedModel) =>
-      dfd = $.Deferred()
       # don't re-select the same model
-      if selectedModel and dir is @selectedDir() and selectedModel is @selectedDir()?.prevSelectedModel
-        dfd.resolve()
-        return dfd.promise()
-
-      complete = =>
-        # remove all directories after the selected dir
-        _.each @directories.splice(i + 1), (d) -> d.remove()
-        @addDirFor selectedModel if selectedModel instanceof OutcomeGroup and !selectedModel.isNew()
-        @updateSidebarWidth()
-        @trigger 'select', selectedModel, @directories
-        dfd.resolve()
+      return if selectedModel and dir is @selectedDir() and selectedModel is @selectedDir()?.prevSelectedModel

       dir.clearSelection() unless selectedModel
+
+      # remove all directories after the selected dir
       i = _.indexOf @directories, dir
-      # only scroll to dir if the selection is not the last dir
-      if @directories.length is i + 1
-        complete()
-      else
-        @_scrollToDir i, selectedModel, complete
-
-      dfd.promise()
+      _.each @directories.splice(i + 1), (d) -> d.remove()
+      isAddingDir = selectedModel instanceof OutcomeGroup and !selectedModel.isNew()
+      @addDirFor selectedModel if isAddingDir
+      @updateSidebarWidth()
+      scrollIndex = if isAddingDir then i + 1 else i
+      @_scrollToDir scrollIndex, selectedModel
+      @trigger 'select', selectedModel, @directories

     refreshSelection: (model) =>
       dir = @selectedDir()
@@ -153,14 +141,11 @@

     # Go up a directory.
     goBack: =>
-      i = _.indexOf @directories, @selectedDir()
-      @goingBack = true
-      if i < 1
-        @directories[0].clearSelection()
-        @selectDir @directories[0]
+      if @selectedModel() instanceof OutcomeGroup
+        @selectDir @selectedDir()
       else
-        prevDir = @directories[i - 1]
-        @selectDir prevDir, prevDir.selectedModel
+        i = _.indexOf @directories, @selectedDir()
+        @selectDir @directories[i - 1]

     updateSidebarWidth: ->
       sidebarWidth = if @directories.length is 1 then @directoryWidth + 1 else (@directoryWidth * 2) + 2
@@ -169,10 +154,6 @@

     renderDir: (dir) =>
       @$el.append dir.render().el
-      @$sidebar.animate scrollLeft: @scrollNextPosition
-
-    scrollNextPosition: ->
-      @$sidebar.get(0).scrollWidth - @$sidebar.get(0).clientWidth

     render: ->
       @$el.empty()
@@ -194,11 +175,11 @@
     dirForGroup: (outcomeGroup) ->
       _.find(@directories, (d) -> d.outcomeGroup is outcomeGroup) || @addDirFor(outcomeGroup)

-    _scrollToDir: (dirIndex, model, complete) ->
+    _scrollToDir: (dirIndex, model) ->
       scrollLeft = (@directoryWidth + 1) * (if model instanceof Outcome then dirIndex - 1 else dirIndex)
-      @$sidebar.animate {scrollLeft: scrollLeft}, duration: @directoryWidth, complete: complete
+      @$sidebar.animate {scrollLeft: scrollLeft}, duration: 200
       scrollTop = (@entryHeight + 1) * _.indexOf(@directories[dirIndex].views(), _.find(@directories[dirIndex].views(), (v) -> v.model is model))
-      @directories[dirIndex].$el.animate {scrollTop: scrollTop}, duration: @directoryWidth
+      @directories[dirIndex].$el.animate {scrollTop: scrollTop}, duration: 200

     _findLastDir: (f) ->
       _.find(_.clone(@directories).reverse(), f) || _.last @directories


:bug: Fix deprecated method call

:bug: Fix deprecated method call

--- lib/find-results-view.coffee
+++ lib/find-results-view.coffee
@@ -57,8 +57,6 @@
         delete @markerViews[id]
         markerView.remove()

-      editor.requestDisplayUpdate()
-
   destroyAllViews: ->
     @empty()
     @markerViews = {}


Corrects toString methods on expression components

--- javascripts/lib/math/expression_components.coffee
+++ javascripts/lib/math/expression_components.coffee
@@ -162,17 +162,15 @@
     class_mixer(Exponentiation)

     class Pi extends ExpressionComponent
-      toDisplay: -> "π"
       toString: -> "PI()"
     class_mixer(Pi)

     class Addition extends ExpressionComponent
-      toDisplay: -> "Add()"
+      toString: -> "Add()"
       isOperator: -> true
     class_mixer(Addition)

     class Subtraction extends ExpressionComponent
-      toDisplay: -> "-"
       isOperator: -> true
       toString: -> "Sub()"
     class_mixer(Subtraction)


Fix PostsFeed [append/prepend]Render methods

--- lib/assets/javascripts/views/posts_feed.js.coffee
+++ lib/assets/javascripts/views/posts_feed.js.coffee
@@ -91,16 +91,16 @@
     for post in posts
       Marbles.DOM.appendHTML(fragment, @renderPostHTML(post))

+    @bindViews(fragment)
     @el.appendChild(fragment)
-    @bindViews(fragment)

   prependRender: (posts) =>
     fragment = document.createDocumentFragment()
     for post in posts
       Marbles.DOM.prependHTML(fragment, @renderPostHTML(post))

+    @bindViews(fragment)
     Marbles.DOM.prependChild(@el, fragment)
-    @bindViews(fragment)

   initAutoPaginate: =>
     TentStatus.on 'window:scroll', @windowScrolled


Use new scandal result syntax

Use new scandal result syntax

--- lib/project-find-view.coffee
+++ lib/project-find-view.coffee
@@ -136,7 +136,7 @@
     paths = (path.trim() for path in @pathsEditor.getText().trim().split(',') when path)

     deferred = $.Deferred()
-    promise = project.scan regex, {paths}, ({path: filePath, matches}) =>
+    promise = project.scan regex, {paths}, ({filePath, matches}) =>
       @results.push({filePath, matches})

     promise.done ->


fixed #131.

fixed #131.

--- lib/view/emp-log-view.coffee
+++ lib/view/emp-log-view.coffee
@@ -564,7 +564,7 @@
   #close and clear the log pane
   close_log_view: ->
     # console.log "close1"
-    if @panel()
+    if @panel
       @clear_log()
       @detach()
       @show_state = false


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -28,11 +28,12 @@

     alpha = parsedColor[4]

-    channels =
-      r: parseInt(parsedColor[1])
-      g: parseInt(parsedColor[2])
-      b: parseInt(parsedColor[3])
-      a: if alpha? then parseFloat(alpha) else 0.0
+    channels = [
+      parseInt(parsedColor[1])
+      parseInt(parsedColor[2])
+      parseInt(parsedColor[3])
+      if alpha? then parseFloat(alpha) else 0.0
+    ]

     self =
       channels: channels


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -813,6 +813,16 @@

     hslColor.saturation()
     # => 0.5
+
+    hslColor.saturation(0.8)
+
+    # to see what it looks like
+    canvas.drawRect
+      color: hslColor
+      x: 50
+      y: 30
+      width: 80
+      height: 80
     </pre></code>

     @name saturation


Update en-US: Add zeroCent, and more:

Update en-US: Add zeroCent, and more:

- Add defaultOptions to include type and zeroCent.
- When type is amount, no zeroCent

--- src/en-us.coffee
+++ src/en-us.coffee
@@ -17,6 +17,10 @@
 import splitInt from './utils/split-int.coffee'
 import speakByDigit from './utils/speak-by-digit.coffee'
 import parse99 from './utils/parse-99.coffee'
+
+defaultOptions =
+	type: 'number'
+	zeroCent: false

 n1 = [
 	'' #zero
@@ -86,11 +90,17 @@
 		(int + dec).toLowerCase()

 # Main function 2 of 2
-speakAmt = (str, options = 'amount', zeroCent) ->
-	# convert amount `str` to written text
-	# accept options as 'amount' or 'cheque'
-	# - amount: some dollars and some cents
-	# - cheque: Some Dollars and Some Cents Only
+###
+Convert numerical amount to written text.
+@param {string} str - The input in string.
+@param {type} type - Either 'amount' or 'cheque':
+	- 'amount': some dollars and some cents.
+	- 'cheque': Some Dollars and Some Cents Only.
+@param {boolean} zeroCent - When zero cent:
+	- true: "and no cent"
+	- false: "and zero cent"
+###
+speakAmt = (str, type, zeroCent) ->
 	if str is '0' then output = 'Null'
 	else
 		dollarUnit = ['Dollar', 'Dollars']
@@ -119,14 +129,14 @@
 			if dollars is '' then ''
 			else ' and '
 		output = int + dollars + andWord + dec + cents
-	# adjust the output according to `options`
+	# adjust the output according to `type`
 	output =
-		if options is 'cheque'
+		if type is 'cheque'
 			if output is 'Null'
 				'Null'
 			else
 				output + ' Only'
-		else if options is 'amount'
+		else if type is 'amount'
 			output
 				.replace(' and No Cent', '')
 				.toLowerCase()
@@ -142,7 +152,7 @@
 @param {Object|null} options - The Numbo options object.
 @return {string|null} - e.g. 'one hundred point two three', or null if detects error.
 ###
-export default (input, options = { type: 'number' }) ->
+export default (input, options = defaultOptions) ->
 	if input is '' then return null
 	if input is '1e+100' then return 'Ding! One Google... Oops... One Googol!!'
 	if check(input) is false
@@ -159,7 +169,7 @@
 			zeroCent = if options.zeroCent is true then true else false
 			return speakAmt(input, 'cheque', zeroCent)
 		when 'amount', 'amt'
-			return speakAmt(input, 'amount')
+			return speakAmt(input, 'amount', false)
 		else
 			console.log 'Error: option.type in enUS is not valid'
 			return null


- Fixed problem with lose of HTML code

- Fixed problem with lose of HTML code

HTML code would get lost when having something like this:

```
<p data-t data-t-interpolate>
  {{Test}}
    <br />Whatever<br />here<br /><br /><br /><br />
</p>
```

This solves the issue.

--- src/index.coffee
+++ src/index.coffee
@@ -130,7 +130,11 @@
       trans = trans.replace /{{([^{}]*)}}/g, (aa, bb) ->
         return t(bb)
     if options.allowHtml
-      $elem.html(trans)
+      if interpolate
+        $elem.html($elem.html().trans.replace /{{([^{}]*)}}/g, (aa, bb) ->
+          return t(bb))
+      else
+        $elem.html(trans)
     else
       $elem.text(trans)
     $elem.attr(interpolateAttr, null) if options.removeAttr && interpolate


setup: Allow specifying extra bindings

setup: Allow specifying extra bindings


Document normalizePath

Document normalizePath


Support just `autoplay` attribute

Support just `autoplay` attribute

Because prop can be a string, `true` wasn’t enabling the autoplay attribute


fixes split test check

fixes split test check

--- apps/artwork/client/view.coffee
+++ apps/artwork/client/view.coffee
@@ -41,7 +41,7 @@
     'click .artwork-buy-button': 'buy'

   initialize: ({ @artwork }) ->
-    @showRails = splitTest('merchandized_rails').outcome()
+    @showRails = if splitTest('merchandized_rails').outcome() is 'true' then true else false
     @location = window.location
     @artist = @artwork.related().artist
     @artists = @artwork.related().artists


failing specs for looping contest

failing specs for looping contest

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -640,6 +640,69 @@
         chai.expect(res).to.eql [15]
         done()

+  describe 'with looping contest', ->
+    it "should resolve in then", (done) ->
+      count = 0
+      contestant = (c, d) ->
+        c.tree 'contestant'
+        .deliver {}
+        .then (c, d) ->
+          count++
+          "contestant-#{count}"
+      Root()
+      .deliver()
+      .contest [contestant]
+        , (c, contestants) -> # scoring
+          contestants[0]
+        , (n, chosen) -> # until
+          return false if count is 4
+          true
+      .then (c, results) ->
+        chai.expect(results).to.eql ["contestant-1","contestant-2","contestant-3","contestant-4"]
+        done()
+
+    it "should resolve in finally", (done) ->
+      count = 0
+      contestant = (c, d) ->
+        c.tree 'contestant'
+        .deliver {}
+        .then (c, d) ->
+          count++
+          "contestant-#{count}"
+      Root()
+      .deliver()
+      .contest [contestant]
+        , (c, contestants) -> # scoring
+          contestants[0]
+        , (n, chosen) -> # until
+          return false if count is 4
+          true
+      .finally (c, results) ->
+        chai.expect(results).to.eql ["contestant-1","contestant-2","contestant-3","contestant-4"]
+        done()
+
+  describe 'branching in contest', ->
+    it 'should resolve', (done) ->
+      multiply = (multiplier, c, data) ->
+        c.tree 'a'
+        .deliver data
+        .then "#{multiplier}", (c, d) ->
+          c.branch 'doubled', (b, data) ->
+            data * 4
+          c.branch 'tripled', (b, data) ->
+            data * 3
+      Root()
+      .deliver 5
+      .contest "contest-multiply", [
+        multiply.bind @, 2
+      ], (c, results) ->
+        paths = results.map (r) -> r.path
+        idx = paths.indexOf 'root-tripled-then'
+        results[idx]
+      .finally 'enfin-fini',   (c, res) ->
+        chai.expect(res).to.eql [15]
+        done()
+
   describe 'handling a multi-dimensional template branch', ->
     it 'should produce the expected path', (done) ->
       Root()
@@ -763,26 +826,3 @@
         chai.expect(val).to.be.a 'null'
         chai.expect(choice.get('non-existant2')).to.equal null
         done()
-
-
-  describe 'branching in contest', ->
-    it 'should resolve', (done) ->
-      multiply = (multiplier, c, data) ->
-        c.tree 'a'
-        .deliver data
-        .then "#{multiplier}", (c, d) ->
-          c.branch 'doubled', (b, data) ->
-            data * 4
-          c.branch 'tripled', (b, data) ->
-            data * 3
-      Root()
-      .deliver 5
-      .contest "contest-multiply", [
-        multiply.bind @, 2
-      ], (c, results) ->
-        paths = results.map (r) -> r.path
-        idx = paths.indexOf 'root-tripled-then'
-        results[idx]
-      .finally 'enfin-fini',   (c, res) ->
-        chai.expect(res).to.eql [15]
-        done()


fixed failing test

fixed failing test

--- spec/zip-spec.coffee
+++ spec/zip-spec.coffee
@@ -124,7 +124,7 @@
         archive.list(path.join(fixturesRoot, 'word.docx'), forceZip: true, callback)
         waitsFor -> zipPaths?
         runs ->
-          expect(zipPaths.length).toBe 1
+          expect(zipPaths.length).toBe 13
           expect(zipPaths[0].path).toBe '[Content_Types].xml'
           expect(zipPaths[0].isDirectory()).toBe false
           expect(zipPaths[0].isFile()).toBe true


Refactoring, more aggressive caching.

Refactoring, more aggressive caching.

--- lib/atom-symbol-navigation.coffee
+++ lib/atom-symbol-navigation.coffee
@@ -41,10 +41,10 @@
   invalidateEditorCache: (editor) ->
     symNavParserCache[editor.getPath()] = null

-  #get parsed buffer data - from cache, if possible, else
+  #parse identifiers out of scopes - from cache, if possible, else
   #we calculate it and cache it.
   parseEditor: (editor) ->
-    if symNavParserCache[editor.getPath()]?
+    if symNavParserCache[editor.getPath()]
       return symNavParserCache[editor.getPath()]

     esprima = require('esprima-fb')
@@ -60,8 +60,16 @@
       console.error "atom-symbol-navigation: problem parsing  #{editor.getTitle()}"
       return null

-    symNavParserCache[editor.getPath()] = parsedBuffer
-    return parsedBuffer
+    syntaxTree = parsedBuffer.syntaxTree
+    scopes = parsedBuffer.scopes
+
+    parsedScopes = scopes.map (scope) =>
+      {
+        scope: scope,
+        identifiers: @getIdentifiersInScope scope
+      }
+    symNavParserCache[editor.getPath()] = parsedScopes
+    return parsedScopes

   #Create status bar view element. Have to wait for packages to load first
   createStatusBarView: ->
@@ -136,19 +144,17 @@
   #works top down from global scope
   getIdentifierAtCursor: ->
     editor = @util.getActiveEditor()
+
     if editor
       cursorPos = editor.getCursorBufferPosition()
-      parsedBuffer = @parseEditor editor
-      if !parsedBuffer?  then return null
-
-      syntaxTree = parsedBuffer.syntaxTree
-      scopes = parsedBuffer.scopes
+      parsedScopes = @parseEditor editor
+      if !parsedScopes then return null

       #run through scopes, get identifiers in each
       #if we find one at the cursor position, look
       #for the next identifier
-      for scope in scopes
-        identifiers = @getIdentifiersInScope scope
+      for parsedScope in parsedScopes
+        identifiers = parsedScope.identifiers
         cursorIds = identifiers.filter (node) =>
           @util.positionIsInsideLocation(cursorPos, node.loc)

@@ -161,7 +167,7 @@
             id: cursorIds[0],
             pos: cursorPos.column - cursorIds[0].loc.start.column,
             usages: usages,
-            scope: scope
+            scope: parsedScope.scope
           }
     return null


Add check icon to installed packages/themes

--- lib/available-package-view.coffee
+++ lib/available-package-view.coffee
@@ -36,6 +36,7 @@
     if atom.packages.isPackageLoaded(@pack.name)
       @installButton.prop('disabled', true)
       @installButton.text('Installed')
+      @setStatusIcon('check')
     else if atom.packages.isPackageDisabled(@pack.name)
       @installButton.prop('disabled', true)


Simplify fake package loading in spec

Simplify fake package loading in spec

--- spec/deprecation-cop-view-spec.coffee
+++ spec/deprecation-cop-view-spec.coffee
@@ -27,10 +27,7 @@
   it "displays deprecated selectors", ->
     fakePackageDir = path.join(__dirname, "..", "spec", "fixtures", "package-with-deprecated-selectors")

-    # TODO - do this better?
-    Package = atom.packages.getActivePackages()[0].constructor
-    pack = new Package(fakePackageDir)
-    pack.load()
+    pack = atom.packages.loadPackage(fakePackageDir)
     spyOn(atom.packages, 'getActivePackages').andReturn([pack])
     deprecationCopView.find("button.refresh-selectors").click()


Make crash-reporter spec more reliable

Make crash-reporter spec more reliable

--- spec/api-crash-reporter-spec.coffee
+++ spec/api-crash-reporter-spec.coffee
@@ -19,8 +19,9 @@
   return if process.platform is 'win32' and process.arch is 'x64'

   it 'should send minidump when renderer crashes', (done) ->
-    @timeout 60000
+    @timeout 120000
     server = http.createServer (req, res) ->
+      server.close()
       form = new formidable.IncomingForm()
       process.throwDeprecation = false
       form.parse req, (error, fields, files) ->
@@ -37,7 +38,6 @@
         assert files['upload_file_minidump']['name']?

         res.end('abc-123-def')
-        server.close()
         done()
     # Server port is generated randomly for the first run, it will be reused
     # when page is refreshed.


Fix getting classrooms by CamelCase codes

--- server/classrooms/classroom_handler.coffee
+++ server/classrooms/classroom_handler.coffee
@@ -104,7 +104,7 @@
       Classroom.find {members: mongoose.Types.ObjectId(memberID)}, (err, classrooms) =>
         return @sendDatabaseError(res, err) if err
         return @sendSuccess(res, (@formatEntity(req, classroom) for classroom in classrooms))
-    else if code = req.query.code
+    else if code = req.query.code.toLowerCase()
       Classroom.findOne {code: code}, (err, classroom) =>
         return @sendDatabaseError(res, err) if err
         return @sendNotFoundError(res) unless classroom


Nodes, in progress...

Nodes, in progress...

--- neo4jdriver.coffee
+++ neo4jdriver.coffee
@@ -486,7 +486,7 @@
     for task in tasks
       check task.method, Match.OneOf 'POST', 'GET', 'PUT', 'DELETE', 'HEAD'
       check task.to, String
-      check task.body, Match.Optional Object
+      check task.body, Match.Optional Match.OneOf Object, String, Number

       task.id ?= Math.floor(Math.random()*(999999999-1+1)+1)
       ids.push task.id
@@ -522,4 +522,142 @@
   @param {Object} opts - [Optional] Map of cypher query(ies) parameters
   @returns {Neo4jTransaction} - Neo4jTransaction instance
   ###
-  transaction: (settings, opts = {}) -> new Neo4jTransaction @, settings, opts+  transaction: (settings, opts = {}) -> new Neo4jTransaction @, settings, opts
+
+
+  nodes: (id, reactive) -> new Neo4jNode @, id, reactive
+
+class Neo4jNode extends Neo4jData
+  _.extend @::, events.EventEmitter.prototype
+  constructor: (@_db, @_id, @_isReactive = false) ->
+    events.EventEmitter.call @
+
+    @_ready = false
+    @on 'ready', (node, fut) =>
+      if node and not _.isEmpty node
+        console.log "[onReady]", {@_ready}
+        @_id = node.metadata.id
+        super @_db.__parseNode(node), @_isReactive
+        @_ready = true
+        fut.return @ if fut
+        console.log "[onReady]", {@_ready}
+      else
+        fut.return undefined if fut
+
+    @on 'create', (properties, fut) =>
+      unless @_ready
+        @_db.__batch
+          method: 'POST'
+          to: @_db.__service.node.endpoint
+          body: properties
+        ,
+          (error, node) =>
+            console.error error if error
+            if node?.metadata
+              @emit 'ready', node, fut
+            else
+              console.error "Node is not crated or created wrongly, metadata is not returned!"
+        , @_isReactive, true
+        return
+      else
+        console.error "You already in node instance, create new one by calling, `db.nodes().create()`"
+        fut.return @
+
+    @on 'apply', =>
+      cb = arguments[arguments.length - 1]
+      if @_ready then cb.apply @, arguments else @once 'ready', => cb.apply @, arguments
+      return
+
+    if _.isObject @_id
+      if _.has @_id, 'metadata'
+        console.log "Initiate FROM OBJECT"
+        @emit 'ready', @_id
+      else
+        console.log "CREATE FROM OBJECT"
+        properties = @_id
+        @_id = undefined
+        @emit 'create'
+    else if _.isNumber @_id
+        console.log "GET by ID"
+        @_db.__batch method: 'GET', to: @_db.__service.node.endpoint + '/' + @_id, (error, node) =>
+          @emit 'ready', node
+        , @_isReactive, true
+    else
+      @emit 'create'
+
+
+  get: -> __wait (fut) => @emit 'apply', fut, (fut) ->
+    console.log fut
+    fut.return super
+
+
+  delete: -> __wait (fut) => @emit 'apply', fut, (fut) ->
+    @_db.__batch
+      method: 'DELETE'
+      to: @_node._service.self.endpoint
+    ,
+      =>
+        @node = undefined
+        fut.return undefined
+    , undefined, true
+
+  properties: -> _.omit @node, ['_service', 'id', 'labels', 'metadata']
+
+  setProperty: (name, value) ->
+    if _.isString name
+      check name, String
+      check value, Match.OneOf String, Number, Boolean, [String], [Number], [Boolean]
+    else if _.isObject name
+      k = Object.keys(name)[0]
+      value = name[k]
+      name = k
+
+    __wait (fut) => @emit 'apply', name, value, fut, (name, value, fut) ->
+      @_node[name] = value
+      @_db.__batch
+        method: 'PUT'
+        to: @_node._service.property.endpoint.replace '{key}', name
+        body: value
+      ,
+        => fut.return @
+      , undefined, true
+
+  setProperties: (nameValue) ->
+    check nameValue, Object
+    __wait (fut) => @emit 'apply', nameValue, fut, (nameValue, fut) ->
+      tasks = []
+      for name, value of nameValue
+        @_node[name] = value
+
+        tasks.push
+          method: 'PUT'
+          to: @_node._service.property.endpoint.replace '{key}', name
+          body: value
+
+      @_db.batch tasks, =>
+        fut.return @
+      , false, true
+
+  updateProperties: (nameValue) ->
+    check nameValue, Object
+    __wait (fut) => @emit 'apply', nameValue, fut, (nameValue, fut) ->
+      delete @_node[k] for k, v of _.omit @_node, ['_service', 'id', 'labels', 'metadata']
+
+      for k, v of nameValue
+        @_node[k] = v
+
+      @_db.__batch
+        method: 'PUT'
+        to: @_node._service.properties.endpoint
+        body: nameValue
+      ,
+        => fut.return @
+      , undefined, true
+
+  property: (name, value) ->
+    check name, String
+    return @node[name] if not value
+    check value, Match.Optional Match.OneOf String, Number, Boolean, [String], [Number], [Boolean]
+    return @setProperty name, value
+
+  getProperty: (name) -> @node[name]


array

array

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -61,7 +61,7 @@
         image: { url: "https://#{host + r[0].file_url}" }
       }

-    @registerCommand 'setwaifu', { allowDM: true, aliases: 'sw' }, (msg, args, d)=>
+    @registerCommand 'setwaifu', { allowDM: true, aliases: ['sw'] }, (msg, args, d)=>
       return if not d.data.allowWaifus
       waifu = (args.match(/\S+/) or [''])[0]
       return msg.reply "Usage: ```#{@prefix}setWaifu <safebooru_tag>```" if not waifu


config: Unnecessary double quotes are forbidden.

--- config/workers.coffee
+++ config/workers.coffee
@@ -77,7 +77,7 @@
       versionURL        : "http://localhost:#{KONFIG.kontrol.port}/version"

     countly             :
-      group             : "webserver"
+      group             : 'webserver'
       nginx             :
         websocket       : yes
         locations       : [


Removing breaks from the last switch case, and just disabling the lint warning instead.

--- nodes.coffee
+++ nodes.coffee
@@ -1367,10 +1367,11 @@
         cond  = cond.invert() unless @subject
         code += idt1 + "case #{ cond.compile o, LEVEL_PAREN }:\n"
       code += block.compile(o, LEVEL_TOP) + '\n'
+      break if i is @cases.length - 1 and not @otherwise
       for expr in block.expressions by -1 when expr not instanceof Comment
         code += idt2 + 'break;\n' unless expr instanceof Return
         break
-    code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL_TOP }\n#{idt2}break;\n" if @otherwise
+    code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL_TOP }\n" if @otherwise
     code +  @tab + '}'

 #### If


trailing comment.

trailing comment.

--- nodes.coffee
+++ nodes.coffee
@@ -147,7 +147,7 @@

 #### Block

-# The expressions body is the list of expressions that forms the body of an
+# The block is the list of expressions that forms the body of an
 # indented block of code -- the implementation of a function, a clause in an
 # `if`, `switch`, or `try`, and so on...
 exports.Block = class Block extends Base


Removing debugging source location output from --nodes. It shouldn't have snuck in there.

--- nodes.coffee
+++ nodes.coffee
@@ -134,8 +134,7 @@
   # `toString` representation of the node, for inspecting the parse tree.
   # This is what `coffee --nodes` prints out.
   toString: (idt = '', name = @constructor.name) ->
-    location = if @locationData then locationDataToString @locationData else "??"
-    tree = '\n' + idt + location + ": " + name
+    tree = '\n' + idt + name
     tree += '?' if @soak
     @eachChild (node) -> tree += node.toString idt + TAB
     tree


fixes issue #1464 ... bound class methods now keep context

--- nodes.coffee
+++ nodes.coffee
@@ -858,7 +858,7 @@
         else
           unless assign.variable.this
             assign.variable = new Value(new Literal(name), [new Access(base, 'proto')])
-          if func instanceof Code and func.bound
+          if func instanceof Code and func.bound and not assign.variable.this
             @boundFuncs.push base
             func.bound = no
       assign


defend against adding multiple row selection columns


(fix) Catch all exceptions and pass them as string.

--- src/flavors/it_style_tests.coffee
+++ src/flavors/it_style_tests.coffee
@@ -20,9 +20,13 @@
             fn();
             pass(__it_index);
           } catch (e) {
-            fail(__it_index, {isException: true, exception: e});
+            if(typeof e == "string")
+              fail(__it_index, {isException: true, exception: e});
+            else if (typeof(e.message) !== 'undefined')
+              fail(__it_index, {isException: true, exception: e.message});
+            else
+              fail(__it_index, {isException: true, exception: "unkown error"});
           }
           __it_index++;
         }
       })()""";
-


Add support for handler reference attribute

--- layout.coffee
+++ layout.coffee
@@ -265,10 +265,16 @@
         delete attributes.$methods

       if attributes.$handlers
-        for {name, script, args, type} in attributes.$handlers
+        for {name, script, args, reference} in attributes.$handlers
           name = name.substr(2)
+
           # console.log 'installing handler', name, args, type, script, @
-          @bind(name, eventCallback(name, script, @, args))
+          callback = eventCallback(name, script, @, args)
+          if reference?
+            @listenTo(eval(reference), name, callback)
+          else
+            @bind(name, callback)
+
           if name in mouseEvents
             attributes.clickable = true unless attributes.clickable == false
             # console.log 'registered for clickable', attributes.clickable
@@ -627,11 +633,12 @@
         when 'handler'
           args = (attributes.args ? '').split()
           script = htmlDecode(child.innerHTML)
-          type = attributes.type or defaulttype
+          type = attributes.type ? defaulttype
           handler =
             name: attributes.name
             script: transformScript(script, type)
             args: args
+            reference: attributes.reference

           classattributes.$handlers.push(handler)
           # console.log 'added handler', attributes.name, script, attributes


servers: remove staff account listing

servers: remove staff account listing

--- servers/lib/server/humanstxt.coffee
+++ servers/lib/server/humanstxt.coffee
@@ -1,63 +1,38 @@
-bongo = require './bongo'
+generateHumanstxt = (req, res) ->

-generateHumanstxt = (req, res) ->
-  { JAccount }       = bongo.models
-  JAccount.some { 'globalFlags': 'staff' }, {}, (err, accounts) ->
-    if err or not accounts
-      return res.status(500).end()
-    else
-      body = ''
-      for acc in accounts
+  header =
+    '''
+    /* TEAM */
+    Koding, Inc.
+    Contact     : hello@koding.com
+    Twitter     : twitter.com/koding
+    Facebook    : facebook.com/koding
+    GitHub      : github.com/koding
+    Location    : San Francisco
+    \n
+    '''

-        if acc?.profile?.nickname?
-          { firstName, lastName, nickname } = acc.profile
-          person = ''
-          if firstName
-            person = "#{firstName} "
-            person += "#{lastName}\n" if lastName
-          else
-            person = "#{nickname}\n"
-          person += "Site: https://koding.com/#{nickname}\n"
-
-          if acc?.locationTags?
-            person +=  "Location: #{acc.locationTags}\n"
-          person += '\n'
-
-          body += person
-
-      header =
-        '''
-        /* TEAM */
-        Koding, Inc.
-        Contact     : hello@koding.com
-        Twitter     : twitter.com/koding
-        Facebook    : facebook.com/koding
-        GitHub      : github.com/koding
-        Location    : San Francisco
-        \n
-        '''
-
-      footer =
-        """
-        /* SITE */
-        Last update : 2015/4/16
-        Language    : English
-        Standards   : HTML5, CSS3
-        Software    : Golang, NodeJS, Coffeescript, KD, MongoDB, PostgreSql, http://github.com/koding for more.
+  footer =
+    """
+    /* SITE */
+    Last update : 2016/11/11
+    Language    : English
+    Standards   : HTML5, CSS3
+    Software    : Golang, NodeJS, Coffeescript, KD, MongoDB, PostgreSql, http://github.com/koding for more.

-        /\\ \\                /\\ \\  __
-        \\ \\ \\/'\\     ___    \\_\\ \\/\\_\\    ___      __
-         \\ \\ , <    / __`\\  /'_` \\/\\ \\ /' _ `\\  /'_ `\\
-          \\ \\ \\\\`\\ /\\ \\L\\ \\/\\ \\L\\ \\ \\ \\/\\ \\/\\ \\/\\ \\L\\ \\
-           \\ \\_\\ \\_\\ \\____/\\ \\___,_\\ \\_\\ \\_\\ \\_\\ \\____ \\
-            \\/_/\\/_/\\/___/  \\/__,_ /\\/_/\\/_/\\/_/\\/___L\\ \\
-                                                  /\\____/
-                                                  \\_/__/
-        """
+    /\\ \\                /\\ \\  __
+    \\ \\ \\/'\\     ___    \\_\\ \\/\\_\\    ___      __
+     \\ \\ , <    / __`\\  /'_` \\/\\ \\ /' _ `\\  /'_ `\\
+      \\ \\ \\\\`\\ /\\ \\L\\ \\/\\ \\L\\ \\ \\ \\/\\ \\/\\ \\/\\ \\L\\ \\
+       \\ \\_\\ \\_\\ \\____/\\ \\___,_\\ \\_\\ \\_\\ \\_\\ \\____ \\
+        \\/_/\\/_/\\/___/  \\/__,_ /\\/_/\\/_/\\/_/\\/___L\\ \\
+                                              /\\____/
+                                              \\_/__/
+    """

-      content = header + body + footer
-      res.setHeader 'Content-Type', 'text/plain'
-      return res.status(200).send content
+  content = header + footer
+  res.setHeader 'Content-Type', 'text/plain'
+  return res.status(200).send content

 module.exports = { generateHumanstxt }


Add spec for excluding ignored files from Project.scan()

--- spec/app/project-spec.coffee
+++ spec/app/project-spec.coffee
@@ -259,6 +259,32 @@
           match: 'aa'
           range: [[1, 3], [1, 5]]

+      describe "when the core.excludeVcsIgnoredPaths config is truthy", ->
+        [projectPath, ignoredPath] = []
+
+        beforeEach ->
+          projectPath = fsUtils.resolveOnLoadPath('fixtures/git/working-dir')
+          ignoredPath = fsUtils.join(projectPath, 'ignored.txt')
+          fsUtils.write(ignoredPath, 'this match should not be included')
+
+        afterEach ->
+          fsUtils.remove(ignoredPath) if fsUtils.exists(ignoredPath)
+
+        it "excludes ignored files", ->
+          project.setPath(projectPath)
+          config.set('core.excludeVcsIgnoredPaths', true)
+          paths = []
+          matches = []
+          waitsForPromise ->
+            project.scan /match/, ({path, match, range}) ->
+              paths.push(path)
+              matches.push(match)
+
+          runs ->
+            expect(paths.length).toBe 0
+            expect(matches.length).toBe 0
+
+
   describe "serialization", ->
     it "restores the project path", ->
       newProject = Project.deserialize(project.serialize())


Navigate to classify on participation

Navigate to classify on participation

--- app/controllers/interactive/group.coffee
+++ app/controllers/interactive/group.coffee
@@ -90,6 +90,7 @@
     e.preventDefault()
     UserGroup.participate @groupId
     User.current['user_group_id'] = @groupId
+    @navigate '/classify'

   redirectHome: (e) =>
     e.preventDefault()


test steroids log

test steroids log

--- spec/core/usages.spec.coffee
+++ spec/core/usages.spec.coffee
@@ -52,3 +52,17 @@

       runs =>
         expect( session.stdout ).toMatch(/Usage: steroids generate module <moduleName>/)
+
+  describe 'generate', ->
+
+    beforeEach =>
+      @testHelper = new TestHelper
+      @testHelper.prepare()
+
+    it "gives usage information when no params are given", =>
+
+      session = @testHelper.runInProject
+        args: ["log"]
+
+      runs =>
+        expect( session.stdout ).toMatch("Shows Android log messages")


Fix specs to not rely on buffer ordering.

--- spec/select-next-spec.coffee
+++ spec/select-next-spec.coffee
@@ -4,6 +4,10 @@

 describe "SelectNext", ->
   [editorView, editor, promise] = []
+
+  getSelectedBufferRanges = ->
+    # TODO: This is a shim. replace with editor.getSelectedBufferRanges() when upgrading to the new cursor / selection APIs.
+    selection.getBufferRange() for selection in editor.getSelections()

   beforeEach ->
     atom.workspaceView = new WorkspaceView()
@@ -27,7 +31,7 @@
       it "selects the word under the cursor", ->
         editor.setCursorBufferPosition([1, 3])
         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [[[1, 2], [1, 5]]]
+        expect(getSelectedBufferRanges()).toEqual [[[1, 2], [1, 5]]]

     describe "when a word is selected", ->
       it "selects the next occurrence of the selected word skipping any non-word matches", ->
@@ -43,20 +47,20 @@
         editor.setSelectedBufferRange([[0, 0], [0, 3]])

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
-          [[3, 8], [3, 11]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
-          [[3, 8], [3, 11]]
-          [[5, 6], [5, 9]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
+          [[0, 0], [0, 3]]
+          [[3, 8], [3, 11]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-next'
+        expect(getSelectedBufferRanges()).toEqual [
+          [[0, 0], [0, 3]]
+          [[3, 8], [3, 11]]
+          [[5, 6], [5, 9]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-next'
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 3]]
           [[3, 8], [3, 11]]
           [[5, 6], [5, 9]]
@@ -66,12 +70,12 @@
         editor.setCursorBufferPosition([0, 0])

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 7]]
         ]

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 7]]
         ]

@@ -89,20 +93,20 @@
         editor.setSelectedBufferRange([[1, 2], [1, 5]])

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[1, 2], [1, 5]]
-          [[2, 0], [2, 3]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[1, 2], [1, 5]]
-          [[2, 0], [2, 3]]
-          [[3, 8], [3, 11]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
+          [[1, 2], [1, 5]]
+          [[2, 0], [2, 3]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-next'
+        expect(getSelectedBufferRanges()).toEqual [
+          [[1, 2], [1, 5]]
+          [[2, 0], [2, 3]]
+          [[3, 8], [3, 11]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-next'
+        expect(getSelectedBufferRanges()).toEqual [
           [[1, 2], [1, 5]]
           [[2, 0], [2, 3]]
           [[3, 8], [3, 11]]
@@ -110,7 +114,7 @@
         ]

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[1, 2], [1, 5]]
           [[2, 0], [2, 3]]
           [[3, 8], [3, 11]]
@@ -119,13 +123,13 @@
         ]

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
+        expect(getSelectedBufferRanges()).toEqual [
           [[1, 2], [1, 5]]
           [[2, 0], [2, 3]]
           [[3, 8], [3, 11]]
           [[4, 0], [4, 3]]
           [[5, 6], [5, 9]]
+          [[0, 0], [0, 3]]
         ]

     describe "when a non-word is selected", ->
@@ -136,7 +140,7 @@
         """
         editor.setSelectedBufferRange([[0, 0], [0, 1]])
         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 1]]
           [[1, 0], [1, 1]]
         ]
@@ -150,18 +154,18 @@
         """

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 1]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
+          [[0, 0], [0, 1]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-next'
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 1]]
           [[2, 0], [2, 1]]
         ]

         editorView.trigger 'find-and-replace:select-next'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 1]]
           [[2, 0], [2, 1]]
         ]
@@ -179,14 +183,14 @@
         """

         editorView.trigger 'find-and-replace:select-all'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
-          [[3, 8], [3, 11]]
-          [[5, 6], [5, 9]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-all'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
+          [[0, 0], [0, 3]]
+          [[3, 8], [3, 11]]
+          [[5, 6], [5, 9]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-all'
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 3]]
           [[3, 8], [3, 11]]
           [[5, 6], [5, 9]]
@@ -206,16 +210,16 @@
         editor.setSelectedBufferRange([[3, 8], [3, 11]])

         editorView.trigger 'find-and-replace:select-all'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
-          [[3, 8], [3, 11]]
-          [[5, 6], [5, 9]]
-        ]
-
-        editorView.trigger 'find-and-replace:select-all'
-        expect(editor.getSelectedBufferRanges()).toEqual [
-          [[0, 0], [0, 3]]
-          [[3, 8], [3, 11]]
+        expect(getSelectedBufferRanges()).toEqual [
+          [[3, 8], [3, 11]]
+          [[0, 0], [0, 3]]
+          [[5, 6], [5, 9]]
+        ]
+
+        editorView.trigger 'find-and-replace:select-all'
+        expect(getSelectedBufferRanges()).toEqual [
+          [[3, 8], [3, 11]]
+          [[0, 0], [0, 3]]
           [[5, 6], [5, 9]]
         ]

@@ -227,7 +231,7 @@
         """
         editor.setSelectedBufferRange([[0, 0], [0, 1]])
         editorView.trigger 'find-and-replace:select-all'
-        expect(editor.getSelectedBufferRanges()).toEqual [
+        expect(getSelectedBufferRanges()).toEqual [
           [[0, 0], [0, 1]]
           [[1, 0], [1, 1]]
         ]