Guard against no atom.__workspaceView

Guard against no atom.__workspaceView

This makes it easier for packages to use status-bar in their specs
without having to shim this.

--- lib/main.coffee
+++ lib/main.coffee
@@ -27,20 +27,20 @@
     wrappedStatusBar.getActiveItem     = => @statusBar.getActiveItem()
     wrappedStatusBar.subscribeToBuffer = (event, callback) => @statusBar.subscribeToBuffer(event, callback)

-    Object.defineProperty atom.__workspaceView, 'statusBar',
-      get: ->
-        Grim.deprecate """
-          The atom.workspaceView.statusBar global is deprecated. The global was
-          previously being assigned by the status-bar package, but Atom packages
-          should never assign globals.
+    if atom.__workspaceView?
+      Object.defineProperty atom.__workspaceView, 'statusBar',
+        get: ->
+          Grim.deprecate """
+            The atom.workspaceView.statusBar global is deprecated. The global was
+            previously being assigned by the status-bar package, but Atom packages
+            should never assign globals.

-          In the future, this problem will be solved by an inter-package communication
-          API available on `atom.services`. For now, you can get a reference to the
-          `status-bar` element via `document.querySelector('status-bar')`.
-        """
-        wrappedStatusBar
-      configurable: true
-
+            In the future, this problem will be solved by an inter-package communication
+            API available on `atom.services`. For now, you can get a reference to the
+            `status-bar` element via `document.querySelector('status-bar')`.
+          """
+          wrappedStatusBar
+        configurable: true

     atom.commands.add 'atom-workspace', 'status-bar:toggle', =>
       if @statusBarPanel.isVisible()


Uninstall themes/packages commands point to themes/packages settings

--- lib/main.coffee
+++ lib/main.coffee
@@ -39,10 +39,10 @@
       openPanel('Packages')

     atom.workspaceView.command 'settings-view:uninstall-themes', ->
-      atom.workspaceView.open(configUri)
+      openPanel('Themes')

     atom.workspaceView.command 'settings-view:uninstall-packages', ->
-      atom.workspaceView.open(configUri)
+      openPanel('Packages')

     atom.workspaceView.on 'pane-container:active-pane-item-changed', ->
       if settingsView is atom.workspace.getActivePaneItem()


whitespace

whitespace

--- lib/main.coffee
+++ lib/main.coffee
@@ -65,7 +65,6 @@
           if error or stderr
             detail = unless stderr then error else stderr
             atom.notifications.addError("Transpile failed", detail: stderr, dismissable: true)
-
             return

           atom.notifications.addSuccess("Transpiled successfully", detail: stdout, dismissable: false)


Import Fonts CJK as global variable

Import Fonts CJK as global variable

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,3 +1,5 @@
+fontsCJK = require('./fonts-cjk')
+
 module.exports =
   config:
     editorFont:
@@ -60,9 +62,7 @@
       ]

   activate: ->
-    fonts_cjk = require('./fonts-cjk')
-    fonts_cjk.run()
+    fontsCJK.run()

   deactivate: ->
-    fonts_cjk = require('./fonts-cjk')
-    fonts_cjk.stop()
+    fontsCJK.stop()


:art: use builtin feature

:art: use builtin feature

--- lib/main.coffee
+++ lib/main.coffee
@@ -41,7 +41,7 @@
   kw2color = Object.create(null)
   add:      (keyword) -> kw2color[keyword] = colorProvider.getNext()
   delete:   (keyword) -> delete kw2color[keyword]
-  has:      (keyword) -> kw2color[keyword]?
+  has:      (keyword) -> keyword of kw2color
   reset:    (keyword) ->
     kw2color = Object.create(null)
     colorProvider.reset()


lazyload deps

lazyload deps

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,8 +1,4 @@
 meta = require "../package.json"
-
-# Dependencies
-{ spawn } = require "child_process"
-{ platform } = require "os"

 module.exports = InnoSetupCore =
   config:
@@ -72,6 +68,9 @@
   consumeConsolePanel: (@consolePanel) ->

   buildScript: (consolePanel) ->
+    { spawn } = require "child_process"
+    { platform } = require "os"
+
     editor = atom.workspace.getActiveTextEditor()

     unless editor?
@@ -132,6 +131,9 @@
       atom.beep()

   getPath: (callback) ->
+    { spawn } = require "child_process"
+    { platform } = require "os"
+
     # If stored, return pathToISCC
     pathToISCC = atom.config.get("language-innosetup.pathToISCC")
     if pathToISCC.length > 0
@@ -152,6 +154,8 @@
         atom.notifications.addError("**#{meta.name}**: `ISCC.exe` is not in your PATH [environmental variable](http://superuser.com/a/284351/195953)", dismissable: true)

   which: ->
+    { platform } = require "os"
+
     if platform() is "win32"
       return "where"


Add CP932

Add CP932

--- lib/main.coffee
+++ lib/main.coffee
@@ -137,6 +137,9 @@
   shiftjis:
     list: 'Japanese (Shift JIS)'
     status: 'Shift JIS'
+  cp932:
+    list: 'Japanese (CP 932)'
+    status: 'CP 932'
   eucjp:
     list: 'Japanese (EUC-JP)'
     status: 'EUC-JP'


Use atom.workspace.addOpener

Use atom.workspace.addOpener

--- lib/main.coffee
+++ lib/main.coffee
@@ -4,11 +4,11 @@

 module.exports =
   activate: ->
-    atom.workspace.registerOpener(openUri)
+    @openerDisposable = atom.workspace.addOpener(openUri)
     atom.packages.once('activated', createImageStatusView)

   deactivate: ->
-    atom.workspace.unregisterOpener(openUri)
+    @openerDisposable.dispose()

 createImageStatusView = ->
   {statusBar} = atom.workspaceView


remove trailing semicolons

remove trailing semicolons

--- lib/main.coffee
+++ lib/main.coffee
@@ -86,14 +86,14 @@
             {
               text: 'Open Settings'
               onDidClick: ->
-                atom.workspace.open("atom://config/packages/#{meta.name}");
-                notification.dismiss();
+                atom.workspace.open("atom://config/packages/#{meta.name}")
+                notification.dismiss()
             },
             {
               text: 'Ignore',
               onDidClick: ->
-                atom.config.set("#{meta.name}.mutePathWarning", true);
-                notification.dismiss();
+                atom.config.set("#{meta.name}.mutePathWarning", true)
+                notification.dismiss()
             }
           ]
         )


Persist config

Persist config

--- example/example.coffee
+++ example/example.coffee
@@ -64,6 +64,7 @@

 # Stuff for testing convenience
 $('#update').on 'click', ->
+  localStorage.setItem 'config', dropletConfig.getValue()
   createEditor JSON.parse dropletConfig.getValue()

 configCurrentlyOut = localStorage.getItem('configOut') is 'yes'


ComputeProvider: use revive yes where needed to cleanup the code

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -13,8 +13,6 @@
   engineyard   : require './engineyard'

 checkCredential = (client, pubKey, callback)->
-
-  console.log pubKey

   JCredential = require './credential'
   JCredential.fetchByPublicKey client, pubKey, (err, credential)->
@@ -137,7 +135,7 @@

   @ping = permit 'ping machines',

-    success: revive (client, options, callback)->
+    success: revive yes, (client, options, callback)->

       {provider} = options
       provider.ping client, options, callback
@@ -145,7 +143,7 @@

   @create = permit 'create machines',

-    success: revive (client, options, callback)->
+    success: revive yes, (client, options, callback)->

       { provider, stack, label } = options

@@ -153,12 +151,12 @@

         return callback err  if err

-        { connection: { delegate:account }, context: { group } } = client
+        { connection: { delegate:account }, r: { group, user } } = client
         { meta, postCreateOptions } = machineData

         @createMachine {
           vendor : provider.slug
-          label, meta, group, account
+          label, meta, group, user
         }, (err, machine)->

           # TODO if any error occurs here which means user paid for
@@ -188,37 +186,25 @@
     provider.remove client, options, callback

-  @fetchExisting = secure revive (client, options, callback)->
+  @fetchExisting = secure revive yes, (client, options, callback)->

     { provider } = options
-
-    { connection: { delegate:account }, context: { group } } = client
-
-    JGroup = require '../group'
-    JGroup.one { slug: group }, (err, groupObj)=>
-
+    { r: { group, user } } = client
+
+    selector =
+      vendor : provider.slug
+      users  : $elemMatch: id: user.getId()
+      groups : $elemMatch: id: group.getId()
+
+    fieldsToFetch = label:1, meta:1, groups:1
+
+    JMachine.someData selector, fieldsToFetch, { }, (err, cursor)->
       return callback err  if err
-      return callback new Error "Group not found"  unless groupObj
-
-      account.fetchUser (err, user)=>
-
+      cursor.toArray (err, arr) ->
         return callback err  if err
-        return callback new Error "user is not defined"  unless user
-
-        selector =
-          vendor : provider.slug
-          users  : $elemMatch: id: user.getId()
-          groups : $elemMatch: id: groupObj.getId()
-
-        fieldsToFetch = label:1, meta:1, groups:1
-
-        JMachine.someData selector, fieldsToFetch, { }, (err, cursor)->
-          return callback err  if err
-          cursor.toArray (err, arr) ->
-            return callback err  if err
-
-            options.machines = arr
-            provider.fetchExisting client, options, callback
+
+        options.machines = arr
+        provider.fetchExisting client, options, callback

   @fetchAvailable = secure revive (client, options, callback)->
@@ -229,31 +215,20 @@

   @createMachine = (options, callback)->

-    { vendor, label, meta, group, account } = options
-
-    JGroup = require '../group'
-    JGroup.one { slug: group }, (err, groupObj)=>
-
-      return callback err  if err
-      return callback new Error "Group not found"  unless groupObj
-
-      account.fetchUser (err, user)=>
-
-        return callback err  if err
-        return callback new Error "user is not defined"  unless user
-
-        users  = [{ id: user.getId(), sudo: yes, owner: yes }]
-        groups = [{ id: groupObj.getId() }]
-
-        machine = new JMachine {
-          vendor, users, groups, meta, label
-        }
-
-        machine.save (err)=>
-
-          if err
-            callback err
-            return console.warn \
-              "Failed to create Machine for ", {users, groups}
-
-          callback null, machine
+    { vendor, label, meta, group, user } = options
+
+    users  = [{ id: user.getId(), sudo: yes, owner: yes }]
+    groups = [{ id: group.getId() }]
+
+    machine = new JMachine {
+      vendor, users, groups, meta, label
+    }
+
+    machine.save (err)=>
+
+      if err
+        callback err
+        return console.warn \
+          "Failed to create Machine for ", {users, groups}
+
+      callback null, machine


Null out file when unwatching

Null out file when unwatching

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -110,6 +110,7 @@
   #Private:
   unwatchUserStylesheet: ->
     @userStylesheetFile?.off()
+    @userStylesheetFile = null
     @removeStylesheet(@userStylesheetPath) if @userStylesheetPath?

   # Private:


Add missing @ before lessCache lazy initialization

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -146,7 +146,7 @@

   # Internal-only:
   loadLessStylesheet: (lessStylesheetPath) ->
-    unless lessCache?
+    unless @lessCache?
       LessCompileCache = require './less-compile-cache'
       @lessCache = new LessCompileCache({@resourcePath})


Show notifications for theme activate errors

--- src/theme-package.coffee
+++ src/theme-package.coffee
@@ -26,7 +26,10 @@

     @activationDeferred = Q.defer()
     @measure 'activateTime', =>
-      @loadStylesheets()
-      @activateNow()
+      try
+        @loadStylesheets()
+        @activateNow()
+      catch error
+        @handleError("Failed to activate the #{@name} theme", error)

     @activationDeferred.promise


Keep track of branched too

Keep track of branched too

--- lib/SubtreeResults.coffee
+++ lib/SubtreeResults.coffee
@@ -8,7 +8,7 @@
     @tasks = 1 unless @tasks
     @finished = false
     @started = []
-    @branches = []
+    @branched = []
     @aborted = []
     @fulfilled = []
     @rejected = []
@@ -44,9 +44,10 @@
       rejected = if @rejected[i] then Object.keys(@rejected[i]).length else 0
       started = if @started[i] then Object.keys(@started[i]).length else 0
       aborted = if @aborted[i] then Object.keys(@aborted[i]).length else 0
+      branched = if @branched[i] then Object.keys(@branched[i]).length else 0
       complete = false if started is 0

-      log.collection "#{@choice?.treeId} #{@choice} ##{i} #{fulfilled + rejected >= started - aborted} (#{fulfilled} fulfilled + #{rejected} rejected / #{started} started - #{aborted} aborted)"
+      log.collection "#{@choice?.treeId} #{@choice} ##{i} #{fulfilled + rejected >= started - aborted} (#{fulfilled} fulfilled + #{rejected} rejected / #{started} started - #{aborted} aborted, #{branched} branched)"
       if fulfilled + rejected < started - aborted
         complete = false
       i++
@@ -108,8 +109,8 @@
   registerTree: (idx, tree, onResult) ->
     tree.branched (c) =>
       path = if c then c.toString() else ''
-      @branches[idx] = {} unless @branches[idx]
-      @branches[idx][path] = c
+      @branched[idx] = {} unless @branched[idx]
+      @branched[idx][path] = c
     tree.started (c) =>
       path = if c then c.toString() else ''
       @started[idx] = {} unless @started[idx]
@@ -126,7 +127,8 @@
     state =
       tasks: @tasks
       finished: @finished
-      branches: @branches.slice 0
+      branched: @branched.slice 0
+      started: @started.slice 0
       aborted: @aborted.slice 0
       fulfilled: @fulfilled.slice 0
       rejected: @rejected.slice 0


Post logs to /api/logging

Post logs to /api/logging

--- scripts/helpers/logging.coffee
+++ scripts/helpers/logging.coffee
@@ -6,7 +6,8 @@

   Backbone.Events.trigger = _.wrap Backbone.Events.trigger, (originalTrigger) ->
     args = _.toArray(arguments).slice(1)
-    console.log args
-    console.log console, _.flatten(['trigger', args])
+
+    log = {'msg': _.flatten(['trigger', args, console])}
+    $.post('/api/logging', log)

     originalTrigger.apply(@, args)


Fix HUD removeItem

Fix HUD removeItem

--- modules/player/hud.coffee
+++ modules/player/hud.coffee
@@ -114,9 +114,6 @@
         icon_url: @util.getIcon item.sauce
       thumbnail:
         url: item.thumbnail
-      fields: [
-        { name: 'Length:', value: "#{@util.displayTime item.duration}\n ", inline: true }
-      ]
     if removedBy
       reply.footer =
         icon_url: removedBy.staticAvatarURL


changed thumbnail and preview to be smaller

--- services/filestore/app/coffee/FileConverter.coffee
+++ services/filestore/app/coffee/FileConverter.coffee
@@ -1,4 +1,3 @@
-easyimage = require("easyimage")
 _ = require("underscore")
 metrics = require("./metrics")
 logger = require("logger-sharelatex")
@@ -29,7 +28,7 @@
 			dst: destPath
 			width: 424
 			height: 300
-		args = "nice convert -flatten -background white -resize 300x -density 300 #{sourcePath} #{destPath}"
+		args = "nice convert -flatten -background white -resize 260x -density 300 #{sourcePath} #{destPath}"
 		exec args, (err, stdout, stderr)->
 			callback(err, destPath)

@@ -42,6 +41,6 @@
 			dst: destPath
 			width: 600
 			height: 849
-		args = "nice convert -flatten -background white -resize 600x -density 300 #{sourcePath} #{destPath}"
+		args = "nice convert -flatten -background white -resize 548x -density 300 #{sourcePath} #{destPath}"
 		exec args, (err, stdout, stderr)->
 			callback(err, destPath)


left-over

left-over

--- keymaps/narrow.cson
+++ keymaps/narrow.cson
@@ -18,7 +18,7 @@
   'o': 'narrow-ui:confirm-keep-open'
   # 'u': 'narrow-ui:preview-item'
   'r': 'narrow-ui:toggle-auto-preview'
-  'ctrl-r': 'narrow-ui:force-refresh'
+  'ctrl-r': 'narrow-ui:refresh-force'

 'atom-text-editor.narrow.narrow-editor.vim-mode-plus.insert-mode':
   'enter': 'core:confirm'


Autosave implemented.

Autosave implemented.

--- assets/coffeescripts/firepad.coffee
+++ assets/coffeescripts/firepad.coffee
@@ -34,6 +34,9 @@
   shortcut.add("Meta+S", () -> save_file(); return false)
   shortcut.add("Meta+Z", () -> undo(); return false)
   shortcut.add("Meta+Shift+Z", () -> redo(); return false)
+
+  # Set autosave.
+  setInterval(save_file, 30000)

   # Destroy the Firepad connection.
   dispose_fp = () ->
@@ -85,14 +88,14 @@
           # from disk.
           firepad.on('ready', () ->
             current_file = file
-            if firepad.isHistoryEmpty()
-              $.post(
-                '/project/load_file/' + project,
-                { file: file },
-                (content) ->
-                  json = $.parseJSON(content)
-                  if json['success']
-                    firepad.setText(json['content'])
-              )
+            # if firepad.isHistoryEmpty()
+            $.post(
+              '/project/load_file/' + project,
+              { file: file },
+              (content) ->
+                json = $.parseJSON(content)
+                if json['success']
+                  firepad.setText(json['content'])
+            )
           )
     )


Tersified function

Tersified function

--- attitude/display/assets/math.coffee
+++ attitude/display/assets/math.coffee
@@ -31,20 +31,16 @@
   c = if degrees then 180/Math.PI else 1

   c1 = if axes[2][2] > 0 then 1 else -1
+  if sheet == 'upper'
+    c1 *= -1
+  if upperHemisphere
+    c1 *= -1

   stepFunc = (angle)->

-    if sheet == 'upper'
-      e2 = -s[2]*c1
-    else if sheet == 'lower'
-      e2 = s[2]*c1
-
     e = [Math.cos(angle)*s[0],
          Math.sin(angle)*s[1],
-         e2]
-
-    if upperHemisphere
-      e[2] *= -1
+         s[2]*c1]

     d = (sdot(e,i) for i in axes)


Separated cartesian transform into own function

--- attitude/display/assets/math.coffee
+++ attitude/display/assets/math.coffee
@@ -29,13 +29,13 @@
   ([Math.cos(a),Math.sin(a)] for a in angles)

 cart2sph = (opts)->
-  opts.degrees ?= true
+  opts.degrees ?= false
   c = if opts.degrees then 180/Math.PI else 1
   (d)->
     r = norm(d)
     [x,y,z] = d
     # Converts xyz to lat lon
-    [c*Math.atan2(y,x),c*Math.asin z/r]
+    [c*Math.atan2(y,x),c*Math.asin(z/r)]

 planeErrors = (axesCovariance, axes, opts={})->
   # Get a single level of planar errors (or the
@@ -43,7 +43,6 @@
   opts.n ?= 100
   upperHemisphere = opts.upperHemisphere or true
   sheet = opts.sheet or 'nominal'
-  degrees = opts.degrees or false
   axes = identity unless axes?
   opts.traditionalLayout ?= true

@@ -82,15 +81,11 @@
     if not upperHemisphere
       z *= -1

-    c = if degrees then 180/Math.PI else 1
-    d = [x,y,z]
-    r = norm(d)
-    return [
-      c*Math.atan2(y,x),
-      c*Math.asin z/r]
+    [x,y,z]

   return ell
     .map stepFunc
+    .map cart2sph(opts)

 normalErrors = (axesCovariance, axes, opts={})->
   # Get a single level of planar errors (or the
@@ -101,7 +96,6 @@
   opts.n ?= 1000
   upperHemisphere = opts.upperHemisphere or true
   sheet = opts.sheet or 'nominal'
-  degrees = opts.degrees or false
   axes = identity unless axes?
   opts.traditionalLayout ?= true

@@ -137,15 +131,11 @@
     if not upperHemisphere
       z *= -1

-    c = if degrees then 180/Math.PI else 1
-    d = [x,y,z]
-    r = norm(d)
-    return [
-      c*Math.atan2(y,x),
-      c*Math.asin z/r]
+    return [x,y,z]

   return ell
     .map stepFunc
+    .map cart2sph(opts)

 combinedErrors = (sv, ax, opts={})->
   func = (type)->


fix repeating bug

fix repeating bug

--- src/plugins/faction/fallback.coffee
+++ src/plugins/faction/fallback.coffee
@@ -76,20 +76,20 @@
         else if /呵呵/.test r.body

             # count words
-            repeatTimes = r.body.length - r.body.replace(/呵/g, '')
+            repeatTimes = r.body.length - r.body.replace(/呵/g, '').length

             # repeat * 2
             send = ''
             send += '呵呵' for i in [1..repeatTimes]

             template = [
-                ['@{player} ⬅_⬅ ']
+                ['@{player} ←_← ']
                 [send]
             ]

         else if /哈/.test r.body

-            repeatTimes = r.body.length - r.body.replace(/哈/g, '')
+            repeatTimes = r.body.length - r.body.replace(/哈/g, '').length

             send = ''
             send += '哈哈' for i in [1..repeatTimes]


Fixed syntax errors

Fixed syntax errors

--- lib/TemplateConfig.coffee
+++ lib/TemplateConfig.coffee
@@ -1,6 +1,6 @@
 # handle CommonJS/Node.js or browser

-sysmo ||= (require?) ? require('sysmo') : window.Sysmo
+sysmo = require?('sysmo') || window?.Sysmo

 # class definition


Removed console.log

Removed console.log

--- lib/class-complete.coffee
+++ lib/class-complete.coffee
@@ -16,7 +16,6 @@
         if split[1]
             for param in split[1].split ","
                 parameters.push param.trim() if param.trim()
-        console.log split

         cursor.moveToEndOfLine()
         editor.selectToBeginningOfLine()


[fix] Incorporated today's data into the regression if today is the last day of a sprint (5m)

--- app/assets/javascripts/houston-scheduler/views/show_milestone_view.coffee
+++ app/assets/javascripts/houston-scheduler/views/show_milestone_view.coffee
@@ -60,7 +60,7 @@
     # If the most recent data point is for an incomplete
     # sprint, disregard it when calculating the regressions
     lastCompleteSprint = @getEndOfSprint(1.week().before(new Date()))
-    if mostRecentDataPoint > lastCompleteSprint
+    if @truncateDate(mostRecentDataPoint) > lastCompleteSprint
       regAll   = @computeRegression(data.slice( 0, -1)) if data.length >= 6  # all time
       regLast3 = @computeRegression(data.slice(-5, -1)) if data.length >= 5  # last 3 weeks only
       regLast2 = @computeRegression(data.slice(-4, -1)) if data.length >= 4  # last 2 weeks only
@@ -207,6 +207,14 @@
     daysUntilFriday += 7 if daysUntilFriday < 0
     daysUntilFriday.days().after(date)

+  truncateDate: (date)->
+    date = new Date(date)
+    date.setHours(0)
+    date.setMinutes(0)
+    date.setSeconds(0)
+    date.setMilliseconds(0)
+    +date
+
   computeRegression: (data)->
     # Compute the linear regression of the points
     # http://trentrichardson.com/2010/04/06/compute-linear-regressions-in-javascript/


payment: implement billing history

payment: implement billing history

--- client/app/Applications/Account.kdapplication/views/paymenthistory.coffee
+++ client/app/Applications/Account.kdapplication/views/paymenthistory.coffee
@@ -14,20 +14,31 @@
     @removeAllItems()
     @showLazyLoader no

-    transactions = []
-    JPayment.fetchTransactions (err, trans=[]) =>
+    items = []
+    JPayment.fetchTransactions (err, transactions) =>
       warn err  if err

-      for t in trans when t.amount + t.tax > 0
-        {status, createdAt, card, cardType, cardNumber, owner, refundable} = t
-        amount = @utils.formatMoney (t.amount + t.tax) / 100
-        transactions.push {
-          status, cardType, cardNumber, owner, refundable, amount
-          currency : 'USD'
-          paidVia  : card or ''
-        }
+      for own paymentMethodId, transactionList of transactions
+        for transaction in transactionList when transaction.amount > 0

-      @instantiateListItems transactions
+          { status, createdAt, card, cardType, invoice
+            cardNumber, owner, refundable, type } = transaction
+
+          amount = @utils.formatMoney (transaction.amount + transaction.tax) / 100
+
+          items.push {
+            status
+            cardType
+            cardNumber
+            owner
+            refundable
+            amount
+            invoice
+            currency : 'USD'
+            paidVia  : card or ''
+          }
+
+      @instantiateListItems items
       @hideLazyLoader()

   loadView:->
@@ -67,6 +78,7 @@

   viewAppended:->
     super
+
     @addSubView editLink = new KDCustomHTMLView
       tagName      : 'a'
       partial      : 'View invoice'
@@ -91,6 +103,6 @@
       #{data.status.toUpperCase()}

-      ...#{data.cardNumber.split("...")[1]}
+      ...#{data.cardNumber}

     """


payment: show a sublist of the products belonging to a given plan

--- client/app/Applications/Dashboard.kdapplication/views/planlistitem.coffee
+++ client/app/Applications/Dashboard.kdapplication/views/planlistitem.coffee
@@ -8,11 +8,18 @@
       title    : "Add products"
       callback : => @emit 'AddProductsRequested', plan

-    @quantitiesView = new KDView
-      cssClass : 'formline button-field clearfix'
+    @childProducts = new KDListViewController
+      itemClass : GroupChildProductListItem

-    for own planCode, qty of plan.quantities
-      @quantitiesView.addSubView new GroupPlanProduct {}, { planCode, qty }
+    if plan.childProducts?
+
+      @childProducts.instantiateListItems(
+        plan.childProducts.map (product) ->
+          {
+            product
+            quantity: plan.quantities[product.planCode]
+          }
+      )

     super()

@@ -24,6 +31,6 @@
     {{> @clientsButton}}
     {{> @editButton}}
     {{> @addProductsButton}}
-    {{> @quantitiesView}}
+    {{> @childProducts.getView()}}
     {{> @embedView}}
     """


do not close group created modal if clicked on group link (not buttons)

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -836,8 +836,6 @@
       modal = new KDModalView
         title        : "#{group.title} has been created!"
         content      : body
-        click        : (event)->
-          if $(event.target).is 'a.group-link' then modal.destroy()
         buttons      :
           dashboard  :
             title    : 'Go to Dashboard'


Update -> replaced for with map.

Update -> replaced for with map.

--- lib/enum-validator.coffee
+++ lib/enum-validator.coffee
@@ -59,13 +59,11 @@
       @_generateEnumSetUpdateAction(enumAttribute, refEnum)

   _generateMultipleValueEnumSetUpdateAction: (enumAttribute, refEnum) =>
-    updateActions = []
-    for attributeValue in enumAttribute.value
+    _.map enumAttribute.value, (attributeValue) =>
       ea =
         name: enumAttribute.name
         value: attributeValue
-      updateActions.push(@_generateEnumSetUpdateAction(ea, refEnum))
-    updateActions
+      @_generateEnumSetUpdateAction(ea, refEnum)

   _generateEnumSetUpdateAction: (enumAttribute, refEnum) =>
     switch refEnum.type.elementType.name


Updated debugging

Updated debugging

--- lib/fold-functions.coffee
+++ lib/fold-functions.coffee
@@ -161,11 +161,14 @@
     @debugMessage('fold functions: action=', action)
     @indentLevel = @indentLevel || null
     hasFoldableLines = false
-    lines = fold = unfold = toggle = 0
+    lines = foldableLines = fold = unfold = toggle = 0
+    bufferHasFoldableLines = false
     for row in [0..editor.getLastBufferRow()]
       foldable = editor.isFoldableAtBufferRow(row)
       isFolded = editor.isFoldedAtBufferRow(row)
       isCommented = editor.isBufferRowCommented(row)
+
+      bufferHasFoldableLines = true if foldable

       lines++

@@ -199,6 +202,7 @@
       if isFunction and not (foldable and isFunction and not isCommented)
         @debugMessage('fold functions: line is a function, but cannot be folded', foldable, isCommented)
       if foldable and isFunction and not isCommented
+        foldableLines++
         if @indentLevel == null
           @indentLevel = thisIndentLevel
         if action == 'toggle'
@@ -211,6 +215,7 @@
           editor.foldBufferRow(row)
           fold++
     @debugMessage('fold functions: done scanning ' + lines + ' lines (' + fold + ':' + unfold + ':' + toggle + ')')
+    @debugMessage('foldable lines: ' + foldableLines)

   toggle: ->
     @fold('toggle')


KernelManager: Remove method `languageHasKernel`

--- lib/kernel-manager.coffee
+++ lib/kernel-manager.coffee
@@ -90,9 +90,6 @@

         return _.assign kernelInfo, grammarLanguage: grammarLanguage

-    languageHasKernel: (language) ->
-        return @getKernelInfoForLanguage(language)?
-
     getRunningKernelForLanguage: (language) ->
         runningKernel = null

@@ -129,7 +126,8 @@
     startKernelIfNeeded: (language, onStarted) ->
         console.log "startKernelIfNeeded:", language

-        unless @languageHasKernel(language)
+        kernelInfo = @getKernelInfoForLanguage language
+        unless kernelInfo?
             message = "No kernel for language `#{language}` found"
             options =
                 detail: "Check that the language for this file is set in Atom
@@ -142,7 +140,6 @@
             onStarted?(runningKernel)
             return

-        kernelInfo = @getKernelInfoForLanguage language
         @startKernel kernelInfo, onStarted

     destroyRunningKernelForLanguage: (language) ->


Revert "Don't resolve modifiers as keys"

This reverts commit 2bb2d314f13368e125018932aec4e7d80f5f2cfb.

--- lib/keyboard_utils.coffee
+++ lib/keyboard_utils.coffee
@@ -36,8 +36,6 @@
       ""
     else if key of @keyNames
       @keyNames[key]
-    else if @isModifier event
-      "" # Don't resolve modifier keys.
     else if key.length == 1
       key
     else


Workspace: Added tab change support between clients.

--- client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -12,6 +12,7 @@
     @workspaceRef     = @workspace.firepadRef.child @sessionKey
     @isJoinedASession = @getOptions().sessionKey
     @openedFiles      = []
+    @activeTabIndex   = 0

     log "joined an old session again, creating new tabbed editor" if @isJoinedASession

@@ -22,7 +23,11 @@
       val  = snapshot.val()
       return unless val

-      {tabs} = snapshot.val()
+      if val.ActiveTabIndex?
+        @tabView.showPaneByIndex val.ActiveTabIndex
+        return @workspaceRef.child("ActiveTabIndex").remove()
+
+      {tabs} = val
       return unless tabs

       for key, data of tabs
@@ -40,6 +45,15 @@
     @tabView = new ApplicationTabView
       delegate           : @
       tabHandleContainer : @tabHandleContainer
+
+    @tabView.on "PaneAdded", (pane) =>
+      {tabHandle} = pane
+      tabHandle.on "click", =>
+        newIndex = @tabView.getPaneIndex @tabView.getActivePane()
+        return  if newIndex is @activeTabIndex
+
+        @workspaceRef.child("ActiveTabIndex").set newIndex
+        @activeTabIndex = newIndex

   createEditorInstance: (file, content, sessionKey) ->
     if file
@@ -62,6 +76,7 @@

     pane.addSubView editor
     @tabView.addPane pane
+    @activeTabIndex = @tabView.panes.length

     pane.on "KDTabPaneDestroy", =>
       @workspaceRef.once "value", (snapshot) =>


Less noise, more meat

Less noise, more meat

--- src/ThreeCSG.coffee
+++ src/ThreeCSG.coffee
@@ -4,17 +4,15 @@
 class ThreeBSP.Vertex extends THREE.Vector3
   constructor: (x, y, z, @normal=V3.clone(), @uv=V2.clone()) ->
     super x, y, z
-
-    # Method aliases
     @subtract = @sub

   clone: ->
     new ThreeBSP.Vertex @x, @y, @z, @normal.clone(), @uv.clone()

-  lerp: (v, alpha) =>
-    @normal.add (v.normal ? V3).clone().sub(@normal).multiplyScalar(alpha)
-    @uv.add (v.uv ? V2).clone().sub(@uv).multiplyScalar(alpha)
-    super v, alpha
+  lerp: (args...) =>
+    @uv.lerp args...
+    @normal.lerp args...
+    super args...

   interpolate: (args...) =>
     @clone().lerp args...


First stab at reducing a group's properties from its children.

--- framer/SVGGroup.coffee
+++ framer/SVGGroup.coffee
@@ -32,7 +32,19 @@

 		return @define propertyName,
 			get: ->
-				return @[privateProp]
+				return @[privateProp] if @[privateProp]?
+
+				# When not set, try to reduce the value from our children:
+				value = undefined
+				for child in @_children
+					childPropertyValue = child[propertyName]
+					# The equality check is naive - needs more work when comparing Color instances, etc.:
+					if value is undefined
+						value = childPropertyValue
+					else
+						if childPropertyValue isnt value
+							return @[privateProp]
+				return value

 			set: (value) ->
 				value = converter(value) unless validator(value)


Remove extension in require

Remove extension in require

--- spec/results-model-spec.coffee
+++ spec/results-model-spec.coffee
@@ -2,7 +2,7 @@

 {$, WorkspaceView} = require 'atom'

-ResultsModel = require '../lib/project/results-model.coffee'
+ResultsModel = require '../lib/project/results-model'

 # Default to 30 second promises
 waitsForPromise = (fn) -> window.waitsForPromise timeout: 30000, fn


Use log instead console.log

Use log instead console.log

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -411,7 +411,7 @@
         serviceName   : "github"
         userId        : KD.whoami().getId()
       ,
-    (err, status)-> console.log "Status of fetching stuff from external: #{status}"
+    (err, status)-> log "Status of fetching stuff from external: #{status}"

   afterLoginCallback: (err, params={})->


Add msg for dev 'app.Storage onStoreChange called'

To manually check loops.

--- app/client/js/storage.coffee
+++ app/client/js/storage.coffee
@@ -32,8 +32,8 @@
     @localStorage.load @stores
     @listenStores()

-    # TODO(steida): Do it on server side. It takes seconds on client.
-    @firebase.simpleLogin @userStore
+    # # TODO(steida): Do it on server side. It takes seconds on client.
+    # @firebase.simpleLogin @userStore

   @THROTTLE_MS: 1000

@@ -65,6 +65,8 @@
     @param {app.Store} store
   ###
   onStoreChange: (store) ->
+    if goog.DEBUG
+      console.log 'app.Storage onStoreChange called'
     @pendingStores.add store
     @savePendingStoresThrottle.fire()
     @notify()


Fix async bug in repos.coffee

Fix async bug in repos.coffee

--- app/controllers/repos.coffee
+++ app/controllers/repos.coffee
@@ -9,13 +9,13 @@
       ActiveRepo.findOne {repoId, sourceProviderName, userId}, (err, activeRepo) ->
         return callback(err) if err?
         return callback(new Error "Repo #{activeRepo.repoId} is already active.") if activeRepo?
-      callback null
+        callback null

     checkRepoActive = ({repoId, sourceProviderName, userId}) -> (callback) ->
       ActiveRepo.findOne {repoId, sourceProviderName, userId}, (err, activeRepo) ->
         return callback(err) if err?
         return callback(new Error "Repo #{activeRepo.repoId} repo is not active.") if not activeRepo?
-      callback null
+        callback null

     getSourceProviderSync = (sourceProviderName) -> _.findWhere initPlugins.sourceProviders, {NAME: sourceProviderName}


added user object to data relay object

added user object to data relay object

--- app/fermentable/index.coffee
+++ app/fermentable/index.coffee
@@ -11,7 +11,15 @@
 		{
 			method: 'GET'
 			path: '/fermentables/{id}'
-			handler: show
+			config:
+            	handler: show
+            	auth:
+                	mode: 'try',
+                	strategy: 'session'
+
+           	 	plugins:
+                	'hapi-auth-cookie':
+                    	redirectTo: false
 		}
 		{
 			method: 'GET'
@@ -52,10 +60,13 @@
 	]

 list = (req, reply) =>
+	if(req.auth.isAuthenticated)
+		user = req.auth.credentials.user
 	@get req.url.path, (status, response) =>
 		reply @renderer.page
 			title: " Fermentables - Ingredients"
 			navigationState: 'ingredients'
+			user: user
 			html: @renderer.render
 				template: "public/templates/gridList.jade"
 				data:
@@ -64,10 +75,13 @@
 					results: response.fermentables

 show = (req, reply) =>
+	if(req.auth.isAuthenticated)
+		user = req.auth.credentials.user
 	@get req.url.path, (status, response) =>
 		reply @renderer.page
 			title: "#{response.fermentables[0].name} - Fermentables - Ingredients"
 			navigationState: 'ingredients'
+			user: user
 			html: @renderer.render
 				template: "public/templates/fermentables/index.jade"
 				data:
@@ -81,6 +95,7 @@
 		reply @renderer.page
 			title: "#{response.fermentables[0].name} - Fermentables - Ingredients"
 			navigationState: 'ingredients'
+			user: req.auth.credentials.user
 			html: @renderer.render
 				template: "public/templates/fermentables/index.jade"
 				data:
@@ -92,6 +107,7 @@
 	reply @renderer.page
 			title: "Add - Fermentables - Ingredients"
 			navigationState: 'ingredients'
+			user: req.auth.credentials.user
 			html: @renderer.render
 				template: "public/templates/fermentables/index.jade"
 				data:


Minor cleanup of PolygonalBody.aabb

Minor cleanup of PolygonalBody.aabb

--- app/js/polygonal_body.coffee
+++ app/js/polygonal_body.coffee
@@ -83,12 +83,10 @@
   # Public: return an axis-aligned bounding box: [[xmin, ymin], [xmax, ymax]]
   aabb: ->
     return @cachedAABB if @cachedAABB
-    vertices = @vertices()
-    [x, y] = vertices[0]
-    xMin = xMax = x
-    yMin = yMax = y
-
-    for [x, y] in vertices
+    xMin = yMin = Infinity
+    xMax = yMax = -Infinity
+
+    for [x, y] in @vertices()
       xMin = x if x < xMin
       xMax = x if x > xMax
       yMin = y if y < yMin


Got rid of an extraneous log.

Got rid of an extraneous log.

--- app/models/SuperModel.coffee
+++ app/models/SuperModel.coffee
@@ -80,7 +80,6 @@
     return _.values @models

   registerModel: (model) ->
-    console.log 'register model?', model, model.getURL
     @models[model.getURL()] = model

   getCollection: (collection) ->


the "loose stuff on reconnect" is caused by "scrollIntoView(pos)" not being wrapped in an exception; this in setValueNoJump.

--- salvus/page/misc_page.coffee
+++ salvus/page/misc_page.coffee
@@ -270,13 +270,21 @@
 # to maintain the view, e.g., cursor position and scroll position.
 # This function is very, very naive now, but will get better using better algorithms.
 CodeMirror.defineExtension 'setValueNoJump', (value) ->
-    scroll = @getScrollInfo()
-    pos = @getCursor()
-    #console.log("setValueNoJump: cursor pos = ", pos)
+    try
+        scroll = @getScrollInfo()
+        pos = @getCursor()
+    catch e
+        # nothing
     @setValue(value)
-    @setCursor(pos)
-    @scrollTo(scroll.left, scroll.top)
-    @scrollIntoView(pos)
+    try
+        @setCursor(pos)
+        @scrollTo(scroll.left, scroll.top)
+        @scrollIntoView(pos)   #I've seen tracebacks from this saying "cannot call method chunckSize of undefined"
+                               #which cause havoc on the reset of sync, which assumes setValueNoJump works, and
+                               # leads to user data loss.  I consider this a codemirror bug, but of course
+                               # just not moving the view in such cases is a reasonable workaround.
+    catch e
+        # nothing


Ensure to process all categories on export.

--- src/coffee/csv/exporter.coffee
+++ src/coffee/csv/exporter.coffee
@@ -38,7 +38,9 @@
   export: (templateFileName, outPutFileName) ->
     new Promise (resolve, reject) =>
       @stream = fs.createWriteStream outPutFileName
-      @stream.on 'error', (error) =>
+      @stream.on 'finish', =>
+        resolve 'OK'
+      @stream.on 'error', (error) ->
         reject error

       @loadTemplate templateFileName
@@ -47,18 +49,22 @@
       .then () =>

         processChunk = (payload) =>
+          @logger.info "Processing #{_.size payload.body.results} categories"
           new Promise (resolve, reject) =>
             rows = _.map payload.body.results, (category) =>
               row = @mapping.toCSV category
             @write rows
+            .then (result) ->
+              resolve result

-        @client.categories.process(processChunk)
-        .then (result) ->
+        @client.categories.all().process(processChunk)
+        .then (result) =>
           @stream.end()
-          resolve result

   write: (output) ->
-    stringify output, (err, out) =>
-      @stream.write out
+    new Promise (resolve, reject) =>
+      stringify output, (err, out) =>
+        @stream.write out
+        resolve 'OK'

 module.exports = Exporter


return index of the used bucket for increment commands

--- src/celerity.coffee
+++ src/celerity.coffee
@@ -59,11 +59,15 @@
         unless 'function' is typeof cb
             throw new Error 'cb argument must be a function'

+        now = Date.now()
+
         config.redis.eval lua.increment, 1,
-            getBucketKey(config, name, Date.now()),
+            getBucketKey(config, name, now),
             increment,
             getExpire(config),
-            cb
+            (err) ->
+                return cb err if err?
+                cb null, getBucketIndex(config, now)

     incrementAndRead: (config, name, increment, cb) ->
         checkConfig config
@@ -74,13 +78,17 @@
         unless 'function' is typeof cb
             throw new Error 'cb argument must be a function'

+        now = Date.now()
+
         config.redis.eval lua.incrementAndRead, 2,
             getKey(config, name),
-            getBucketKey(config, name, Date.now()),
+            getBucketKey(config, name, now),
             increment,
             getExpire(config),
             config.bucketCount,
-            cb
+            (err, result) ->
+                return cb err if err?
+                cb null, result, getBucketIndex(config, now)

     read: (config, name, cb) ->
         checkConfig config


Add snippet to insert a cross-platform @font-face

--- user/snippets.cson
+++ user/snippets.cson
@@ -56,6 +56,21 @@
 	"Comment":
 		prefix: "/"
 		body: "/** $1 */"
+
+	"Font-face":
+		prefix: "font"
+		body: """
+			@font-face {
+				font-family: ${1:FontName};
+				font-weight: normal;
+				font-style: normal;
+				src: url("${2:fonts/font-name}.eot");
+				src: url("${2:fonts/font-name}.eot#iefix") format("embedded-opentype"),
+					url("${2:fonts/font-name}.woff2") format("woff2"),
+					url("${2:fonts/font-name}.ttf") format("truetype"),
+					url("${2:fonts/font-name}.woff") format("woff"),
+					url("${2:fonts/font-name}.svg#FontName") format("svg");
+			}$3"""

 # JavaScript
 ".source.js":


fixed bugs with view switching

fixed bugs with view switching

--- src/components/life_app.coffee
+++ src/components/life_app.coffee
@@ -16,7 +16,7 @@
     @initializeEvents(props.events)
     {events, headers} = @processEvents(props.events)
     view_type = "day"
-    objects = @getAllTimelineObjects(events, headers, view_type)
+    objects = @getAllTimelineObjects(events, headers, @getViewTimeRange(view_type))

     return {
       events
@@ -162,12 +162,15 @@
   getAllTimelineObjects: (events, headers, view_time_range) ->
     if not view_time_range?
       view_time_range = @getViewTimeRange @state.view_type
-
+    debugger
     # Reads the events and headers off of state, orders them, and returns them
     objects = []
     i = 0
     for header, j in headers
       if header.moment.unix() < view_time_range.start
+        # Skip over all the events for this header that are out of the window
+        while i < events.length and events[i].rendered_date == header.date
+          i++
         continue
       if header.moment.unix() >= view_time_range.end
         break
@@ -185,7 +188,7 @@
     if view_type == @state.view_type
       return
     view_time_range = @getViewTimeRange(view_type)
-    {objects} = @getAllTimelineObjects @state.events, @state.headers, view_time_range
+    objects = @getAllTimelineObjects @state.events, @state.headers, view_time_range
     @setState({view_type, objects})

   getViewTimeRange: (view_type) ->
@@ -198,7 +201,6 @@
     else if view_type == 'month'
       start = moment(moment().format("MM/1/YYYY"))
     end = moment(start).add(1, view_type)
-
     return {start: start.unix(), end: end.unix()}

   render: () ->
@@ -211,29 +213,37 @@

     return React.createElement("div", {className: "col-sm-offset-2 col-sm-8"},
       React.createElement(AppNavigation, {switchView: @switchView})
-      React.createElement(TimelineBar, {y: @state.timeline_hover_y})
-      React.createElement("div", null, timeline_list)
+      React.createElement("div", null,
+        React.createElement(TimelineBar, {y: @state.timeline_hover_y})
+        React.createElement("div", null, timeline_list)
+      )
     )

 AppNavigation = React.createClass
   displayName: 'AppNavigation'

-  switchAppView: (e) ->
-    @props.switchView e.target.dataset['view']
+  switchView: (e) ->
+    @props.switchView $(e.target).data('view')

   render: () ->
     # View changes
     React.createElement("div", {className: "btn-group"},
       React.createElement("a", {
-        className: "btn btn-default btn-warning btn-material-indigo dropdown-toggle"
+        className: "btn btn-material-indigo dropdown-toggle"
         'data-toggle': "dropdown"
-      }, "View",
+      }, "View ",
         React.createElement("span", {className: "caret"})
       )
       React.createElement("ul", className: "dropdown-menu",
-        React.createElement("li", {onClick: @switchAppView, 'data-view': 'day'}, 'Day')
-        React.createElement("li", {onClick: @switchAppView, 'data-view': 'week'}, 'Week')
-        React.createElement("li", {onClick: @switchAppView, 'data-view': 'month'}, 'Month')
+        React.createElement("li", null,
+          React.createElement("a", {href: "#", onClick: @switchView, 'data-view': 'day'}, 'Day')
+        )
+        React.createElement("li", null,
+          React.createElement("a", {href: "#", onClick: @switchView, 'data-view': 'week'}, 'Week')
+        )
+        React.createElement("li", null,
+          React.createElement("a", {href: "#", onClick: @switchView, 'data-view': 'month'}, 'Month')
+        )
       )
     )

@@ -259,7 +269,7 @@
     return initial

   getEventDetail: (event, show_all) ->
-    if show_all or true
+    if show_all
       return event.detail
     else
       element = $('<div>').html(event.detail)


fix(RetinaImg): shoudn't throw an exception if you forget to provide an image name

--- src/components/retina-img.cjsx
+++ src/components/retina-img.cjsx
@@ -125,6 +125,8 @@
     <img className={className} src={path} style={style} {...otherProps} />

   _pathFor: (name) ->
+    return null unless name and _.isString(name)
+
     [basename, ext] = name.split('.')
     if @props.active is true
       name = "#{basename}-active.#{ext}"


Fix hdfs_client wait

Fix hdfs_client wait

--- hdp/hdfs_client.coffee
+++ hdp/hdfs_client.coffee
@@ -39,7 +39,7 @@
   # ctx.waitForConnection datanodes, port, (err) ->
   #   return next err if err
   # User "test" should be created
-  ctx.waitForExecution "hdfs dfs -test -d /user/test", (err) ->
+  ctx.waitForExecution mkcmd.test(ctx, "hdfs dfs -test -d /user/test"), (err) ->
     return next err if err
     ctx.execute
       cmd: mkcmd.test ctx, """


hacky (but working!) support for top-level exceptions

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -22,6 +22,12 @@
       if oc.offset?
         targets.push idx + oc.byte_count + 1, idx + oc.offset

+    # organize exception handlers, adding targets as needed
+    try_locations = {}
+    for handler in method.code.exception_handlers
+      try_locations[handler.start_pc] = {name: 'try', byte_count: -1, handler: handler}
+      targets.push handler.start_pc, handler.handler_pc
+
     targets.sort((a,b) -> a - b)
     labels = _.uniq(targets)

@@ -38,6 +44,9 @@
     method.code.each_opcode (idx, oc) =>
       current_block++ if idx in labels
       block = @blocks[current_block]
+      if try_locations[idx]?
+        block.opcodes.push try_locations[idx]
+        block.has_try = try_locations[idx].handler
       block.opcodes.push oc

     @param_names = ['rs']
@@ -140,16 +149,17 @@

     rv.concat postordered.reverse()

-  compile: (prev_stack, prev_locals) ->
+  compile: (prev_stack, prev_locals, exc_catcher=false) ->
     return if @visited

     @visited = true

     # the first block has its local vars set from the function params
     unless @start_idx == 0
-      @stack =
-        for s,i in prev_stack
-          if s? then new StackVar i else null
+      unless exc_catcher
+        @stack =
+          for s,i in prev_stack
+            if s? then new StackVar i else null
       @locals =
         for l,i in prev_locals
           if l? then new LocalVar i else null
@@ -168,6 +178,26 @@
     # instructions obviate the need for one
     unless op.offset? or (op.name.indexOf 'return') != -1
       @add_stmt => @compile_epilogue()
+
+    # catch any try blocks
+    if (handler = @has_try)?
+      next_block = @block_chain.get_block_from_instr handler.handler_pc
+      @add_stmt """
+        } catch (e) {
+          if (!(e instanceof util.JavaException)) throw e
+      """
+      if handler.catch_type == "<any>"
+        @add_stmt "rs.push(e.exception); label = #{handler.handler_pc}; continue\n}"
+      else
+        @add_stmt """
+          if (types.is_castable(rs, e.exception.type, types.c2t(#{JSON.stringify handler.catch_type}))) {
+            rs.push(e.exception); label = #{handler.handler_pc}; continue;
+          } else {
+            throw e;
+          }\n}
+        """
+      next_block.stack = ["rs.pop()"]
+      next_block.compile @stack, @locals, true

     for next_block in @next
       # java bytecode verification ensures that the stack height and stack /
@@ -343,6 +373,8 @@
         b.push temp

 compile_obj_handlers = {
+  # pseudo-opcode, generates the beginning of a try block
+  'try': { compile: (b) -> b.add_stmt "try { //" }
   aconst_null: { compile: (b) -> b.push new Primitive "null"; }
   iconst_m1: { compile: (b) -> b.push new Primitive "-1"; }
   iconst_0: { compile: (b) -> b.push new Primitive "0"; }


Rewrite compiler to use while-switch structure.

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -1,128 +1,83 @@
 _ = require '../third_party/_.js'
 util = require './util'

-root = exports ? window.opcodes = {}
+class BasicBlock
+  constructor: (@start_idx) ->
+    @opcodes = []
+    @stack = []
+    @external_stack_count = 0

-INDENT_SIZE = 4
-
-class RuntimeState
-  constructor: ->
-    @_cs = []
-    @locals = []
-    @var_count = 0
-
-    @blocks = []
-    @block_start_idxs = []
-    @new_block 0
-
-  push: (sf) -> @_cs.push sf
-  pop: -> @_cs.pop()
-  cl: (idx) -> @locals[idx]
-  put_cl: (idx, val) -> @locals[idx] = val
-  put_cl2: (idx,val) -> @put_cl(idx,val); @put_cl(idx+1,null)
-
-  add_cond: (condFn, expr, dest1, dest2) ->
-    @current_block().cond = { condFn: condFn, expr: expr, dest1: dest1, dest2: dest2 }
-    @new_block dest1
-
-  new_block: (start_idx) ->
-    @blocks.push new BasicBlock
-    @block_start_idxs.push(start_idx)
-
-  current_block: -> _.last @blocks
-
-  instr_idx2block: (idx) ->
-    for blockStartIdx, i in @block_start_idxs
-      return i - 1 if idx < blockStartIdx
-    return @blocks.length - 1
+  push: (args...) -> @stack.push.apply @stack, args
+  pop: ->
+    if @stack.length > 0
+      @stack.pop()
+    else
+      # not correct; I think it needs some liveness analysis
+      "S#{@external_stack_count++}"

   compile: ->
-    for block, idx in @blocks
-      if block.cond?
-        d1 = @instr_idx2block block.cond.dest1
-        d2 = @instr_idx2block block.cond.dest2
-        block.out.push d1, d2
-        @blocks[d1].in.push idx
-        @blocks[d2].in.push idx
+    instr_idx = @start_idx
+    body = ""
+    for op in @opcodes
+      compiled_str = compile_fns[op.name]?.compile.call(op, @, instr_idx)
+      body += "#{compiled_str};\n" if compiled_str?
+      instr_idx += op.byte_count + 1
+    """
+    case #{@start_idx}:
+    // #{op.name for op in @opcodes}
+    #{body}
+    """

-    @compile_block(@blocks[0], 1)
+compile_fns = {
+  nop: { compile: -> }
+  aconst_null: { compile: (b) -> b.push "null"; null }
+  iconst_m1: { compile: (b) -> b.push "-1"; null }
+  iconst_0: { compile: (b) -> b.push "0"; null }
+  iconst_1: { compile: (b) -> b.push "1"; null }
+  iconst_2: { compile: (b) -> b.push "2"; null }
+  iconst_3: { compile: (b) -> b.push "3"; null }
+  iconst_4: { compile: (b) -> b.push "4"; null }
+  iconst_5: { compile: (b) -> b.push "5"; null }
+  lconst_0: { compile: (b) -> b.push "gLong.ZERO", null; null }
+  istore_0: { compile: (b) -> "L0 = #{b.pop()}" }
+  istore_1: { compile: (b) -> "L1 = #{b.pop()}" }
+  istore_2: { compile: (b) -> "L2 = #{b.pop()}" }
+  istore_3: { compile: (b) -> "L3 = #{b.pop()}" }
+  iload_0: { compile: (b) -> b.push "L0"; null }
+  iload_1: { compile: (b) -> b.push "L1"; null }
+  iload_2: { compile: (b) -> b.push "L2"; null }
+  iload_3: { compile: (b) -> b.push "L3"; null }
+  iadd: { compile: (b) -> b.push "wrap_int(#{b.pop()}+#{b.pop()})"; null }
+  imul: { compile: (b) -> b.push "gLong.fromInt(#{b.pop()}).multiply(gLong.fromInt(#{b.pop()})).toInt()"; null }
+  ifge: { compile: (b, idx) -> "if (#{b.pop()} >= 0) { label = #{@offset + idx}; continue }" }
+  ireturn: { compile: (b) -> "return #{b.pop()}" }
+  'return': { compile: (b) -> "return"; }
+}

-  compile_block: (block, indent) ->
-    indentation = (" " for i in [0...INDENT_SIZE * indent] by 1).join ''
-    if block.in.length <= 1 && block.out.length == 2
-      "#{indentation}if (#{block.cond.condFn block.cond.expr}) {\n#{
-        @compile_block @blocks[block.out[1]], indent + 1
-      }\n#{indentation}} else {\n#{
-        @compile_block @blocks[block.out[0]], indent + 1
-      }\n#{indentation}}"
-    else
-      indentation + block
+# partition the opcodes into basic blocks
+get_blocks_for_method = (m) ->
+  targets = [0]
+  m.code.each_opcode (idx, oc) ->
+    # ret is the only instruction that does not have an 'offset' field.
+    # however, it will only jump to locations that follow jsr, so we do
+    # not need to worry about it
+    if oc.offset?
+      targets.push idx + oc.byte_count + 1, idx + oc.offset

-class BasicBlock
-  constructor: ->
-    @in = []
-    @out = []
-    @lines = []
-    @cond = null
+  targets.sort()
+  # dedup
+  labels = []
+  for target, i in targets
+    if i == 0 or targets[i-1] != target
+      labels.push target

-  add_line: (line) -> @lines.push line
+  blocks = (new BasicBlock idx for idx in labels)
+  current_block = -1
+  m.code.each_opcode (idx, oc) ->
+    current_block++ if idx in labels
+    blocks[current_block].opcodes.push oc

-  toString: -> @lines.join "\n"
-
-class Expr
-
-class Primitive extends Expr
-  constructor: (@val) ->
-
-  toString: -> @val
-
-class Variable extends Expr
-
-  constructor: (id) ->
-    @strId = ""
-    while id >= 0
-      @strId = String.fromCharCode((id % 26) + 97) # 97 = 'a'
-      id -= 26
-
-  toString: -> @strId
-
-class BinaryOp extends Expr
-  constructor: (@op_func, @left, @right) ->
-
-  toString: -> @op_func(@left, @right)
-
-opcodes = {
-  nop: { execute: -> }
-  aconst_null: { execute: (rs) -> rs.push new Primitive null }
-  iconst_m1: { execute: (rs) -> rs.push new Primitive -1 }
-  iconst_0: { execute: (rs) -> rs.push new Primitive 0 }
-  iconst_1: { execute: (rs) -> rs.push new Primitive 1 }
-  iconst_2: { execute: (rs) -> rs.push new Primitive 2 }
-  iconst_3: { execute: (rs) -> rs.push new Primitive 3 }
-  iconst_4: { execute: (rs) -> rs.push new Primitive 4 }
-  iconst_5: { execute: (rs) -> rs.push new Primitive 5 }
-  lconst_0: { execute: (rs) -> rs.push new Primitive("gLong.ZERO"), null }
-  istore_0: { execute: (rs) -> rs.put_cl 0, rs.pop() }
-  istore_1: { execute: (rs) -> rs.put_cl 1, rs.pop() }
-  istore_2: { execute: (rs) -> rs.put_cl 2, rs.pop() }
-  istore_3: { execute: (rs) -> rs.put_cl 3, rs.pop() }
-  iload_0: { execute: (rs) -> rs.push rs.cl(0) }
-  iload_1: { execute: (rs) -> rs.push rs.cl(1) }
-  iload_2: { execute: (rs) -> rs.push rs.cl(2) }
-  iload_3: { execute: (rs) -> rs.push rs.cl(3) }
-  iadd: { execute: (rs) -> rs.push new BinaryOp ((a,b) -> "wrap_int(#{a}+#{b})"), rs.pop(), rs.pop() }
-  imul: { execute: (rs) -> rs.push new BinaryOp ((a,b) ->
-    "gLong.fromInt(#{a}).multiply(gLong.fromInt(#{b})).toInt()"), rs.pop(), rs.pop()
-  }
-  ifge: { execute: (rs, op, idx) -> rs.add_cond(
-    ((a) -> "#{a} >= 0"), rs.pop(), idx + op.byte_count + 1, op.offset + idx) }
-  'ireturn': { execute: (rs, op, idx) ->
-    rv = rs.pop()
-    rs.current_block().add_line "return #{rv};"
-    rs.new_block(idx + op.byte_count)
-  }
-  'return': { execute: (rs, op, idx) -> rs.current_block().add_line 'return;'; }
-}
+  blocks

 root.compile = (class_file) ->
   class_name = class_file.this_class.toExternalString()
@@ -134,22 +89,25 @@
           else if m.name is '<clinit>' then '__clinit__'
           else m.name

-        rs = new RuntimeState
-
-        vars = []
+        param_names = []
         params_size = 0
         for p in m.param_types
-          vars.push new Variable rs.var_count++
+          param_names.push "L#{params_size}"
           if p.toString() in ['D','J']
-            rs.put_cl2 params_size, _.last vars
             params_size += 2
           else
-            rs.put_cl params_size, _.last vars
             params_size++

-        m.code.each_opcode (idx, oc) ->
-          opcodes[oc.name]?.execute rs, oc, idx
-        "#{name}: function(#{vars.join ", "}) {\n#{rs.compile()}\n},"
+        """
+        #{name}: function(#{param_names.join ", "}) {
+          var label = 0;
+          while (true) {
+            switch (label) {
+#{(block.compile() for block in get_blocks_for_method m).join ""}
+            };
+          };
+        },
+        """

   "var #{class_name} = {\n#{methods.join "\n"}\n};\n"


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -13,7 +13,7 @@

     target ||= 0
     approachBy = 1 unless approachBy?
-    value = op
+    value =

     unless I.cooldowns[name]
       I.cooldowns[name] = {


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -10,7 +10,7 @@

   cooldown: (name, options={target: 0, approachBy: 1, value: null}) ->
     options.target ||= 0
-    unless options.
+    unless options.approachBy

     unless I.cooldowns[name]
       I.cooldowns[name] = options


Searches now use projections, way faster.

--- server/handlers/Handler.coffee
+++ server/handlers/Handler.coffee
@@ -94,6 +94,7 @@
     unless @modelClass.schema.uses_coco_search
       return @sendNotFoundError(res)

+    project = {original:1, name:1, version:1}
     term = req.query.term
     matchedObjects = []
     filters = [{filter: {index: true}}]
@@ -111,9 +112,10 @@
           res.send matchedObjects
           res.end()
       if term
+        filter.project = project
         @modelClass.textSearch term, filter, callback
       else
-        @modelClass.find(filter.filter).limit(100).exec callback
+        @modelClass.find(filter.filter, project).limit(100).exec callback

   versions: (req, res, id) ->
     # TODO: a flexible system for doing GAE-like cursors for these sort of paginating queries


KiteSubscription:added token to social endpoint

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -135,16 +135,19 @@
     if err then console.log err
     next()

-app.get "/-/subscription/check/:username", (req, res)->
-  {username} = req.params
-  {JAccount} = koding.models
-  JAccount.one {"profile.nickname": username}, (err, account)->
-    account.fetchSubscriptions (err, subs)->
-      unless err
-        subsdata = subs.map (sub)->sub.data
-        res.send 200, {subsdata}
-      else
-        res.send 401, authTemplate "Kite Subscription Error - 1"
+app.get "/-/subscription/check/:kiteToken/:username", (req, res)->
+  {username, kiteToken} = req.params
+  {JAccount, JKite} = koding.models
+  JKite.one kiteCode: kiteToken, (err, kite)->
+    return res.send 401, "Kite Subscription Error - 1" if err
+    JAccount.one {"profile.nickname": username}, (err, account)->
+      account.fetchSubscriptions (err, subs)->
+        return res.send 401, "Kite Subscription Error - 2" if err
+        kite.fetchPlans (err, plans)->
+          subsIds = subs.map (sub)-> sub.planCode
+          for plan in plans
+            if plan.planCode in subsIds
+              return res.send 200, plan.data

 app.get "/-/8a51a0a07e3d456c0b00dc6ec12ad85c", require './__notify-users'


Added humans.txt to koding.

Added humans.txt to koding.

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -199,6 +199,47 @@
 app.get "/Logout", (req, res)->
   res.clearCookie 'clientId'
   res.redirect 302, '/'
+
+app.get "/humans.txt", (req, res)->
+  {JAccount}       = koding.models
+  JAccount.some {'globalFlags': 'staff'}, {}, (err, accounts)->
+    if err or not accounts
+      return res.send 500
+    else
+      body = ""
+      for acc in accounts
+
+        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 */\n
+        """
+
+      footer =
+        """/* SITE */
+            Last update:2013/12/02
+            Language: English
+            Doctype: HTML5
+            IDE: Coffeescript, NodeJS, Golang, Sublime Text, MongoDB, RabbitMQ
+        """
+      content = header + body + footer
+      res.setHeader 'Content-Type', 'text/plain'
+      return res.send 200, content

 app.get "/sitemap:sitemapName", (req, res)->
   {JSitemap}       = koding.models


Fix creating and viewing licenses in /licensor page

--- server/middleware/users.coffee
+++ server/middleware/users.coffee
@@ -88,7 +88,7 @@
   fetchByEmail: wrap (req, res, next) ->
     email = req.query.email
     return next() unless email
-    if req.user?.isAdmin()
+    if req.user?.isAdmin() or req.user?.isLicensor()
       user = yield User.findOne({ emailLower: email.toLowerCase() })
       throw new errors.NotFound('No user with that email', { errorID: 'no-user-with-that-email' }) unless user
       res.status(200).send(user.toObject({req}))


refactor bound aggregators to allow re-use

--- server/models/indicator.coffee
+++ server/models/indicator.coffee
@@ -110,26 +110,20 @@
   return deferred.promise

 # Functions to aggregate the data bounds of different types of fields
-boundAggregators =
-  integer: (data, fieldName) ->
-    bounds = {}
-    bounds.min = _.min(data, (row) ->
-      row[fieldName]
-    )[fieldName]
-    bounds.max = _.max(data, (row) ->
-      row[fieldName]
-    )[fieldName]
-    return bounds
-  date: (data, fieldName) ->
-    bounds = {}
-    bounds.min = _.min(data, (row) ->
-      row[fieldName]
-    )[fieldName]
-    bounds.max = _.max(data, (row) ->
-      row[fieldName]
-    )[fieldName]
-    return bounds
-  text: -> null
+boundAggregators = {}
+boundAggregators.integer = (data, fieldName) ->
+  bounds = {}
+  bounds.min = _.min(data, (row) ->
+    row[fieldName]
+  )[fieldName]
+  bounds.max = _.max(data, (row) ->
+    row[fieldName]
+  )[fieldName]
+
+  return bounds
+
+boundAggregators.date = boundAggregators.integer
+boundAggregators.text = -> null

 indicatorSchema.statics.calculateBoundsForType = (fieldType, data, fieldName) ->
   if boundAggregators[fieldType]?


do not slugify room names at renaming - fixes #1571

--- packages/rocketchat-channel-settings/server/functions/saveRoomName.coffee
+++ packages/rocketchat-channel-settings/server/functions/saveRoomName.coffee
@@ -19,7 +19,7 @@
 	if not nameValidation.test name
 		throw new Meteor.Error 'name-invalid', 'Invalid_room_name', { channelName: name }

-	name = _.slugify name
+	# name = _.slugify name

 	if name is room.name
 		return


Add fetched attribute to reports

Add fetched attribute to reports

--- app/js/lib/models/report.coffee
+++ app/js/lib/models/report.coffee
@@ -1,3 +1,8 @@
 class LedgerWeb.Models.Report extends Backbone.Model
+  defaults:
+    fetched: false
+
   initialize: ->
-    @set 'reports', new LedgerWeb.Collections.Reports(@get('reports'))
+    @set
+      fetched: !_.isEmpty(@get('reports'))
+      reports: new LedgerWeb.Collections.Reports(@get('reports'))


Improving error handling

Improving error handling

--- src/hubot-archive-today.coffee
+++ src/hubot-archive-today.coffee
@@ -28,7 +28,11 @@
             .post(data) (err, httpRes, body) ->
                 robot.logger.debug "hubot-archive-today: #{body}"

-                if err or body.match(PATTERNS.NOT_FOUND)
+                if err or httpRes.statusCode isnt 200
+                    res.reply "I'm unable to process your request at this time due to a server error. Please try again later."
+                    return robot.logger.error "hubot-archive-today: #{err}"
+
+                if body.match(PATTERNS.NOT_FOUND)
                     return res.reply "#{res.match[1]} is not a valid URL."

                 links = body.match PATTERNS.FOUND


Getting the cursors to move the sprite.

Getting the cursors to move the sprite.

--- src/game/states/game.coffee
+++ src/game/states/game.coffee
@@ -3,28 +3,27 @@
   @player = null

   create: ->
-    x = @game.width / 2
+    x = @game.width / 3
     y = @game.height / 2
     @player = @add.sprite x, y, 'player'
-    @player.anchor.setTo 0.5, 0.5
     @input.onDown.add @onInputDown, this
+    @cursors = @game.input.keyboard.createCursorKeys()
+
+    @game.physics.startSystem(Phaser.Physics.P2JS);
+    @game.physics.p2.enable(@player)
+    @player.body.setZeroDamping();
+    @player.body.fixedRotation = true;

   update: ->
-    x = @input.position.x
-    y = @input.position.y
-    cx = @world.centerX
-    cy = @world.centerY
-
-    angle = Math.atan2(y - cy, x - cx) * (180 / Math.PI)
-    @player.angle = angle
-
-    dx = x - cx
-    dy = y - cy
-    scale = Math.sqrt(dx * dx + dy * dy) / 100
-
-    @player.scale.x = scale * 0.6
-    @player.scale.y = scale * 0.6
-
+     if @cursors.left.isDown
+     	@player.body.moveLeft(400)
+     if @cursors.right.isDown
+     	@player.body.moveRight(400)
+     if @cursors.up.isDown
+     	@player.body.moveUp(400)
+     if @cursors.down.isDown
+     	@player.body.moveDown(400)
+
   onInputDown: ->
     @game.state.start 'menu'


UI/core making the Modal unit set the size restrictions in the #show method

--- ui/core/src/modal.coffee
+++ ui/core/src/modal.coffee
@@ -31,7 +31,7 @@
     @_inner = @dialog = @first('.lui-inner')
     @_inner.insert(html)

-    @limit_size(dom_window.size())
+    return @

   #
   # Bypassing the {Element#html} calls to the inner element
@@ -86,6 +86,7 @@
   show: ()->
     hide_all_modals()

+    @limit_size(dom_window.size())
     @insertTo(document.body)
     @$super.apply(@, arguments)


updates /fairs route to pass currentFairRows and featuredFairs for fallback

--- apps/fairs_new/routes.coffee
+++ apps/fairs_new/routes.coffee
@@ -1,9 +1,10 @@
 _ = require 'underscore'
 Q = require 'q'
+moment = require 'moment'
 Fairs = require '../../collections/fairs'
-Fair = require '../../models/fair'
-OrderedSets = require '../../collections/ordered_sets'
+Items = require '../../collections/items'
 Profile = require '../../models/profile'
+fair_fixtures = require './fixtures/fairs.js'

 representation = (fair) ->
   dfd = Q.defer()
@@ -22,46 +23,42 @@
     if fair.get('has_full_feature') is true
       fair.related().profile.fetch(cache: true)

-parseGroups = (fairs) ->
-  currentFairs: fairs.filter((fair) -> fair.isCurrent())
+parseGroups = (fairs, date) ->
+  currentFairs: fairs.chain().filter((fair) -> fair.isCurrent(date)).sortBy((fair) -> fair.get('tier')).value()
   pastFairs: fairs.chain()
-    .filter((fair) -> fair.isPast())
-    .take(6)
+    .filter((fair) -> fair.isPast(date))
+    .sortBy((fair) -> Date.parse(fair.get 'start_at'))
     .value()
   upcomingFairs: fairs.chain()
-    .filter((fair) -> fair.isUpcoming())
+    .filter((fair) -> fair.isUpcoming(date))
     .sortBy((fair) -> Date.parse(fair.get 'start_at'))
     .value()

 @index = (req, res) ->
-  fairs = new Fairs
-  fairs.fetch
-    cache: true
-    data: sort: '-start_at', size: 50
-    success: ->
-      Q.allSettled(profiles(fairs)).then(->
-        { currentFairs, pastFairs, upcomingFairs } = parseGroups(fairs)
+  allFairs = new Fairs fair_fixtures, parse: true
+  date = new Date(req.query.date) || new Date
+  fairs = allFairs.aroundDate date
+  featuredFairs = new Items [], id: '55a4204d72616970e40000f9'

-        featuredFairs = _.flatten [currentFairs, pastFairs]
-        allFairs = _.flatten [featuredFairs, upcomingFairs]
+  Q.allSettled(profiles(fairs)).then(->
+    { currentFairs, pastFairs, upcomingFairs } = parseGroups(fairs, date)

-        promises = _.compact _.flatten [
-          # Fetch all displayable fairs (so that we can get their location)
-          _.map(allFairs, (fair) -> fair.fetch(cache: true))
+    allFairs = _.flatten [currentFairs, pastFairs, upcomingFairs]

-          # Get the two smaller images to use via the fair 'explore' sets
-          # (This is a pretty ineffcient way to go about it though)
-          _.map(featuredFairs, representation)
-        ]
+    promises = _.compact _.flatten [
+      _.map(allFairs, (fair) -> fair.fetch(cache: true))
+      featuredFairs.fetch(cache: true)
+    ]

-        Q.allSettled(promises).then(->
+    Q.allSettled(promises).then(->

-          res.locals.sd.FAIRS = currentFairs
-          res.render 'index',
-            featuredFairs: featuredFairs
-            currentFairs: currentFairs
-            upcomingFairs: upcomingFairs
-            pastFairs: pastFairs
+      res.locals.sd.FAIRS = fairs
+
+      res.render 'index',
+        featuredFairs: featuredFairs.models
+        currentFairRows: fairs.currentRows(date)
+        upcomingFairs: upcomingFairs
+        pastFairs: pastFairs

-        ).done()
-      ).done()
+    ).done()
+  ).done()


Update name-expander.coffee

Update name-expander.coffee

--- scripts/name-expander.coffee
+++ scripts/name-expander.coffee
@@ -72,7 +72,13 @@
   "nmathrani"
 ]

-sysadmin = ["ehays", "fnenz2", "fnenz", "lbernat", "Stephen Couratier", "kying", "hvellanki", "bnessler"]
+
+sysadmin_members = ["ehays", "fnenz2", "fnenz", "lbernat", "Stephen Couratier", "kying", "hvellanki", "bnessler"]
+
+neteng_members = ["Paul Davis", "amills", "jhorstman", "sean cline", "CheechandMChong"]
+
+dba_members = ["awhitwham", "agentile", "dminor"]
+

 module.exports = (robot) ->
   robot.hear /^(merlern|dert)( team| dudes| guys)*:(.+)/i, (msg) ->
@@ -106,7 +112,13 @@
     msg.send devinf_members.join(", ") + ": ^^"

   robot.hear /^@?(sysadmin)( team| dudes| guys)*(:| )(.+)/i, (msg) ->
-    msg.send sysadmin.join(", ") + ": ^^"
+    msg.send sysadmin_members.join(", ") + ": ^^"

   robot.hear /^@?(ops)( team| dudes| guys)*(:| )(.+)/i, (msg) ->
-    msg.send sysadmin.join(", ") + ": ^^"
+    msg.send sysadmin_members.join(", ") + ": ^^"
+
+  robot.hear /^@?(neteng)( team| dudes| guys)*(:| )(.+)/i, (msg) ->
+    msg.send neteng_members.join(", ") + ": ^^"
+
+  robot.hear /^@?(dba)( team| dudes| guys)*(:| )(.+)/i, (msg) ->
+    msg.send dba_members.join(", ") + ": ^^"


update btn title as blog post

update btn title as blog post

--- client/landing/app/Applications/Activity.kdapplication/widgets/blogpostwidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/blogpostwidget.coffee
@@ -108,7 +108,7 @@
   switchToEditView:(activity,fake=no)->

     unless fake
-      @submitBtn.setTitle "Edit Discussion"
+      @submitBtn.setTitle "Edit Blog Post"
       @addCustomData "activity", activity
     else
       @submitBtn.setTitle 'Submit again'


Remove unused $window injection

Remove unused $window injection

--- assets/coffee/menubar.coffee
+++ assets/coffee/menubar.coffee
@@ -13,9 +13,9 @@
 # limitations under the License.

 angular.module 'eveindy'
-  .controller 'MenubarCtrl', [ '$scope', 'Server', '$route', '$window'
+  .controller 'MenubarCtrl', [ '$scope', 'Server', '$route',
     class MenubarCtrl
-      constructor: (@$scope, @Server, @$route, @$window) ->
+      constructor: (@$scope, @Server, @$route) ->
         @$scope.$on '$routeChangeSuccess', @updateMenubar
         @$scope.$on '$routeChangeStart', @preventNullRoute
         @$scope.$on 'login-status', @_updateLoginStatus


it actually should overwrite focus to work

--- lib/TermView.coffee
+++ lib/TermView.coffee
@@ -92,14 +92,14 @@

   attachResizeEvents: ->
     setTimeout (=>  @resizeToPane()), 10
-    @on 'focus', @onFocus
+    @on 'focus', @focus
     $(window).on 'resize', => @resizeToPane()

   detachResizeEvents: ->
-    @off 'focus', @onFocus
+    @off 'focus', @focus
     $(window).off 'resize'

-  onFocus: ->
+  focus: ->
     @resizeToPane()
     @focusTerm()


tasks2: don't always make current selected task automatically visible

--- src/smc-webapp/tasks/actions.coffee
+++ src/smc-webapp/tasks/actions.coffee
@@ -93,26 +93,30 @@
         counts =
             done    : 0
             deleted : 0
+        current_is_visible = false
         tasks.forEach (val, id) =>
             if val.get('done')
                 counts.done += 1
             if val.get('deleted')
                 counts.deleted += 1
-            if id != current_task_id
-                if not show_deleted and val.get('deleted') and (val.get('last_edited') ? 0) < cutoff
-                    return
-                if not show_done and val.get('done') and (val.get('last_edited') ? 0) < cutoff
-                    return
-                if not search_matches(search, val.get('desc'))
-                    return
+            if not show_deleted and val.get('deleted') and (val.get('last_edited') ? 0) < cutoff
+                return
+            if not show_done and val.get('done') and (val.get('last_edited') ? 0) < cutoff
+                return
+            if not search_matches(search, val.get('desc'))
+                return
+            if id == current_task_id
+                current_is_visible = true
             # TODO: assuming sorting by position here...
             v.push([val.get('position'), id])
             return
         v.sort (a,b) -> misc.cmp(a[0], b[0])
         visible = immutable.fromJS((x[1] for x in v))

-        if not current_task_id? and visible.size > 0
+        if (not current_task_id? or not current_is_visible) and visible.size > 0
             current_task_id = visible.get(0)
+        else if not current_is_visible and visible.size == 0
+            current_task_id = undefined

         c = @store.get('counts')
         if c.get('done') != counts.done


stop travis from crying

stop travis from crying

--- src/user/accounts-iframe-mixin.cjsx
+++ src/user/accounts-iframe-mixin.cjsx
@@ -1,3 +1,5 @@
+# coffeelint: disable=no_empty_functions
+
 React = require 'react'

 api   = require '../api'


Fix merge?

Fix merge?

--- apps/partnerships/routes.coffee
+++ apps/partnerships/routes.coffee
@@ -52,7 +52,6 @@
     next()

 @edit = (req, res, next) ->
-  @req = req
   getJSON( req.url, (err, data) ->
     return next err if err
     res.locals.sd.DATA = data


Adding custom comment on subscriptions

Adding custom comment on subscriptions

--- src/eventbus.coffee
+++ src/eventbus.coffee
@@ -16,8 +16,8 @@
       callback: callback
       stack: stack
       id: regid
-      description: description ? "calling #{callback.stack} (#{regid})"
-		@log "EventBus: (#{eventName}): new subscription - #{stack} (#{regid})" if @debug
+      description: description ? "calling #{stack} (#{regid})"
+		@log "EventBus: (#{eventName}): new subscription - #{description ? stack} (#{regid})" if @debug
 		regid

 	publish: (eventName, parameter, correlationId = null) =>


personalize factoids route

personalize factoids route

use the bot's name as the route instead of generic hubot

--- src/factoids.coffee
+++ src/factoids.coffee
@@ -25,7 +25,7 @@

 module.exports = (robot) ->
   @factoids = new Factoids robot
-  robot.router.get "/hubot/factoids", (req, res) =>
+  robot.router.get "/#{robot.name}/factoids", (req, res) =>
     res.end JSON.stringify @factoids.data, null, 2

   blip = '!' unless process.env.HUBOT_FACTOID_PREFIX


Update Checkin.coffee

Update Checkin.coffee

--- server/models/Checkin.coffee
+++ server/models/Checkin.coffee
@@ -37,4 +37,4 @@

 export default Checkin

-export MAX_CHECKIN_PER_SESSION = [21, 21]
+export MAX_CHECKIN_PER_SESSION = [10, 10, 10, 10]


Better handling for index.html files

Better handling for index.html files

--- src/generate.coffee
+++ src/generate.coffee
@@ -156,6 +156,15 @@
     page.content = result.content
     newExt = result.ext
     paginator = page.paginator or {}
+
+    # Update page url with new extension
+    unless newExt is ext
+      page.url = (helpers.stripExtension page.url) + newExt
+
+    # Strip out index.html
+    if path.basename(page.url) is 'index.html'
+      page.url = path.dirname page.url
+      console.log page.url

     # Set up correct path / URL
     outpath = path.join config.destination, page.url
@@ -512,6 +521,7 @@
         if config.pretty_urls
           data.url = helpers.stripExtension file

+        console.dir data
         data.content = content

         # Add to collection


Added (some) magic comments

Added (some) magic comments

--- snippets/language-latex2e.cson
+++ snippets/language-latex2e.cson
@@ -20,6 +20,24 @@
     'body': '\\%\n\\\\begin{align$2}\n\t$1$3\n\\\\end{align$2}'
     'description': 'Begin amsmath align env`ironment'
     'descriptionMoreURL': 'https://www.sharelatex.com/learn/Aligning_equations_with_amsmath#!#Aligning_several_equations'
+  '!TEX root':
+    'prefix': '!TEX root'
+    'body': '% !TEX root = $1'
+  '!TEX engine':
+    'prefix': '!TEX engine'
+    'body': '% !TEX engine = $1'
+  '!TEX program':
+    'prefix': '!TEX program'
+    'body': '% !TEX program = $1'
+  '!TEX jobname':
+    'prefix': '!TEX jobname'
+    'body': '% !TEX jobname = $1'
+  '!TEX outputDirectory':
+    'prefix': '!TEX outputDirectory'
+    'body': '% !TEX outputDirectory = $1'
+  '!TEX cleanPatterns':
+    'prefix': '!TEX cleanPatterns'
+    'body': '% !TEX cleanPatterns = $1'

 # These snippets are available in mathmode
 '.text.tex.latex .string.other.math':


find results component

find results component

--- src/graphite.coffee
+++ src/graphite.coffee
@@ -128,32 +128,26 @@
           @run "browser #{JSON.stringify node.path + '*'}"
       @add_renderable finder

+    FindResultsComponent = React.createClass
+      render: ->
+        query_parts = @props.query.split '.'
+        React.DOM.ul {className: 'find-results'}, _.map @props.results, (node) =>
+          text = node.path
+          text += '*' unless node.is_leaf
+          node_parts = text.split '.'
+          React.DOM.li {className: 'cm-string', onClick: => @props.on_click node}, _.map node_parts, (part, i) ->
+            part = '.' + part unless i == 0
+            React.DOM.span {className: if part == query_parts[i] then 'light' else null}, part
+
     fn 'find', 'Finds Graphite metrics', (query) ->
       promise = graphite.find query
       promise.clicks = new Bacon.Bus

       @value @renderable promise, @detached -> @async ->
-        $ul = $ '
'
-        @add_rendered $ul
-        promise.then ({query, result}) =>
-          query_parts = query.split '.'
-          for node in result
-            $li = $ '
'
-            $li.data 'node', node
-            text = node.path
-            text += '*' unless node.is_leaf
-            node_parts = text.split '.'
-            for part, i in node_parts
-              if i > 0
-                $li.append '.'
-              $span = $ ''
-              $span.addClass 'light' if part == query_parts[i]
-              $span.text part
-              $li.append $span
-
-            promise.clicks.plug $li.asEventStream('click').map (e) -> $(e.target).closest('li').data('node')
-
-            $ul.append $li
+        result = React.PropsHolder constructor: FindResultsComponent, props: {results: [], query, on_click: (node) -> promise.clicks.push node}
+        @add_component result
+        promise.then (r) =>
+          result.set_child_props results: r.result
         .fail (reason) =>
           @error 'Find request failed'
           Q.reject reason


Use bluebird only as polyfill

Use bluebird only as polyfill

--- app/assets/main.coffee
+++ app/assets/main.coffee
@@ -1,7 +1,9 @@
 require './shim/phantomjs-bind'

-window.Promise = require 'bluebird'
-if process.env.NODE_ENV == 'development'
-  Promise.longStackTraces()
+unless window.Promise?
+  window.Promise = require 'bluebird'
+
+  if process.env.NODE_ENV == 'development'
+    Promise.longStackTraces()

 window.Rdb = require('rdb/index')


correct prisoner counts when viewing finished game

--- app/coffee/game.coffee
+++ app/coffee/game.coffee
@@ -456,7 +456,7 @@
   accordingly, and return an array of points for all dead stones."
   {moves} = options
   nodes = sgfObject.gameTrees[0].nodes
-  nodeIndex = if moves \
+  nodeIndex = if moves? \
               then nodeNoFromActionNo sgfObject, moves \
               else nodes.length - 1
   node = nodes[nodeIndex]
@@ -464,12 +464,10 @@
   for tag in node.TB or []
     [x, y] = decodeSgfCoord tag
     if state[y][x] == 'white'
-      state[y][x] = 'whitedead'
       deadStones.push [x, y]
   for tag in node.TW or []
     [x, y] = decodeSgfCoord tag
     if state[y][x] == 'black'
-      state[y][x] = 'blackdead'
       deadStones.push [x, y]
   deadStones


remove console.log

remove console.log

--- shared/src/model/user.coffee
+++ shared/src/model/user.coffee
@@ -14,19 +14,17 @@
     _.extend(@, attrs)

   sendVerification: (pin, successCallBack) ->
-    @makeRequest('confirm_by_pin', {pin}).then((resp) =>
+    @makeRequest('confirm_by_pin', {pin}).then (resp) =>
       if resp.status is 204
         delete @error
         @is_verified = true
         successCallBack(@)
       else
         code = _.first(resp.data?.errors)?.code
-        console.log(code)
         @error = ERROR_CODES[code]
         if code is 'no_pin_confirmation_attempts_remaining'
           @verificationFailed = true
       @emit('change')
-    )

   sendConfirmation: ->
@@ -38,7 +36,6 @@
     @requestInProgress = true

     afterRequest = (resp) =>
-
       @requestInProgress = false
       @emit('change')
       resp


- [grunt] fixed glob example in `grunt-responsive-image* task

--- tasks/grunt-responsive-image.coffee
+++ tasks/grunt-responsive-image.coffee
@@ -14,7 +14,7 @@
         expand: true
         cwd: '<%= path.source.images %>/'
         # List folders which have to be resized in glob, e.g.:
-        # {models,blog/posts}
+        # {,models,blog/posts}
         src: ['{,**}/*.{jpg,jpeg,gif,png}']
         custom_dest: '<%= path.build.thumbnails %>/{%= path %}/{%= width %}/'
       ]


aftertabs prop is an element

aftertabs prop is an element

--- tutor/src/components/course-periods-nav.cjsx
+++ tutor/src/components/course-periods-nav.cjsx
@@ -17,7 +17,7 @@
     handleKeyUpdate: React.PropTypes.func
     initialActive: React.PropTypes.number.isRequired
     periods: React.PropTypes.array.isRequired
-    afterTabsItem: React.PropTypes.func
+    afterTabsItem: React.PropTypes.element

   getDefaultProps: ->


i18n: rename iTunes to Apple Podcasts

i18n: rename iTunes to Apple Podcasts

--- src/javascripts/i18n.coffee
+++ src/javascripts/i18n.coffee
@@ -81,7 +81,7 @@
         title: 'Transcription'
       subscribeBar:
         allEpisodes: 'All Episodes'
-        podcastOnItunes: 'View Podcast on iTunes'
+        podcastOnItunes: 'View Podcast on Apple Podcasts'
         podcastOnSpotify: 'View Podcast on Spotify'
         podcastOnDeezer: 'View Podcast on Deezer'
         podcastOnAlexa: 'View Podcast on Alexa'
@@ -120,7 +120,7 @@
         title: 'Transkript'
       subscribeBar:
         allEpisodes: 'Alle Episoden'
-        podcastOnItunes: 'Podcast auf iTunes ansehen'
+        podcastOnItunes: 'Podcast auf Apple Podcasts ansehen'
         podcastOnSpotify: 'Podcast auf Spotify ansehen'
         podcastOnDeezer: 'Podcast auf Deezer ansehen'
         podcastOnAlexa: 'Podcast auf Alexa ansehen'


Removed unnecessary parameter in replaceLoops()

Removed unnecessary parameter ‘aether’ in replaceLoops()

--- src/languages/python.coffee
+++ src/languages/python.coffee
@@ -32,7 +32,7 @@
       return true

   # Replace 'loop:' with 'while True:'
-  replaceLoops: (rawCode, aether) ->
+  replaceLoops: (rawCode) ->
     # rawCode is pre-wrap
     return [rawCode, []] if rawCode.indexOf('loop:') is -1
     convertedCode = ""


fix g++/clang++ issue. Humbug.

fix g++/clang++ issue. Humbug.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -128,7 +128,7 @@
     if GrammarUtils.OperatingSystem.isDarwin()
       "File Based":
         command: "bash"
-        args: (context) -> ['-c', "xcrun g++ -fcolor-diagnostics -std=c++14 -Wall -include stdio.h -include iostream '" + context.filepath + "' -o /tmp/cpp.out && /tmp/cpp.out"]
+        args: (context) -> ['-c', "xcrun clang++ -fcolor-diagnostics -std=c++14 -Wall -include stdio.h -include iostream '" + context.filepath + "' -o /tmp/cpp.out && /tmp/cpp.out"]
     else if GrammarUtils.OperatingSystem.isLinux()
       "Selection Based":
         command: "bash"


add commands to signal load

add commands to signal load

--- client/skr/screens/customer-projects/CustomerProjects.cjsx
+++ client/skr/screens/customer-projects/CustomerProjects.cjsx
@@ -19,7 +19,8 @@
             <Lanes.Screens.CommonComponents activity={@state}
                 commands={@state.commands} model={@project} />
             <BS.Row>
-                <SC.CustomerProjectFinder autoFocus editOnly sm=3 model={@project} />
+                <SC.CustomerProjectFinder commands={@state.commands}
+                model={@project} autoFocus editOnly sm=3 />

                 <SC.CustomerFinder selectField sm=3
                     label='Customer' model={@project} />


Directory spec calls .off() when testing unwatch path

--- spec/app/directory-spec.coffee
+++ spec/app/directory-spec.coffee
@@ -58,7 +58,7 @@

       runs ->
         changeHandler.reset()
-        directory.unsubscribeFromNativeChangeEvents()
+        directory.off()
       waits 20

       runs -> fs.remove(temporaryFilePath)


:art: Fix boolean logic in tests

:art: Fix boolean logic in tests

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -808,13 +808,15 @@
         screenPosition = traverse(tokenScreenStart, Point(0, i))
         bufferPosition = traverse(tokenBufferStart, Point(0, i))

-        if token.metadata?.atomic and not isEqualPoint(screenPosition, tokenScreenStart)
-          expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'backward')).toEqual(tokenScreenStart)
-          expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'forward')).toEqual(tokenScreenEnd)
-          expect(displayLayer.translateScreenPosition(screenPosition, clipDirection: 'backward')).toEqual(tokenBufferStart)
-          expect(displayLayer.translateScreenPosition(screenPosition, clipDirection: 'forward')).toEqual(tokenBufferEnd)
-          expect(displayLayer.translateBufferPosition(bufferPosition, clipDirection: 'backward')).toEqual(tokenScreenStart)
-          expect(displayLayer.translateBufferPosition(bufferPosition, clipDirection: 'forward')).toEqual(tokenScreenEnd)
+        if token.metadata?.atomic
+          unless isEqualPoint(screenPosition, tokenScreenStart)
+            expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'backward')).toEqual(tokenScreenStart)
+            expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'forward')).toEqual(tokenScreenEnd)
+            expect(displayLayer.translateScreenPosition(screenPosition, clipDirection: 'backward')).toEqual(tokenBufferStart)
+            expect(displayLayer.translateScreenPosition(screenPosition, clipDirection: 'forward')).toEqual(tokenBufferEnd)
+            # TODO: converting buffer positions fails for some cases. the above assertions are fine.
+            expect(displayLayer.translateBufferPosition(bufferPosition, clipDirection: 'backward')).toEqual(tokenScreenStart)
+            expect(displayLayer.translateBufferPosition(bufferPosition, clipDirection: 'forward')).toEqual(tokenScreenEnd)
         else unless token.metadata?.void
           expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'backward')).toEqual(screenPosition)
           expect(displayLayer.clipScreenPosition(screenPosition, clipDirection: 'forward')).toEqual(screenPosition)


Spec to ensure the object editor uses selected on activate

--- spec/object-editor-spec.coffee
+++ spec/object-editor-spec.coffee
@@ -24,6 +24,16 @@
     model.setSelected(path)
     expect(editor.isActive()).toBe false
     expect(editor.getActiveObject()).toBe null
+
+  describe "when there are selected objects before the editor becomes active", ->
+    beforeEach ->
+      model.setSelected(path)
+      model.setSelectedNode(path.getNodes()[0])
+
+    it 'activates the editor associated with the selected object', ->
+      editor.activate()
+      expect(editor.isActive()).toBe true
+      expect(editor.getActiveObject()).toBe path.getNodes()[0]

   describe "when the ObjectEditor is active", ->
     beforeEach ->


remove component proxy

remove component proxy

--- app/react_abuse.coffee
+++ app/react_abuse.coffee
@@ -13,23 +13,6 @@
     props ?= {}
     props.key = "#{prefix}_#{component_id++}"
     cls props, args...
-
-ComponentProxy = ->
-  component = null
-  state = null
-  bind_to_component: (c) ->
-    component = c
-    if state != null
-      component.setState state
-  setState: (s) ->
-    if component?
-      component.setState s
-    else
-      state = s
-
-ComponentProxyMixin =
-  componentWillMount: ->
-    @props.component_proxy?.bind_to_component @

 ObservableMixin =
   #get_observable: -> @props.observable
@@ -71,5 +54,5 @@
   get_observable: -> @props.child_props
   render: -> @props.constructor @state.value

-_.extend exports, {PropsModelComponent, ComponentProxy, ComponentProxyMixin, ObservableMixin, component_list, createIdentityClass}, React
+_.extend exports, {PropsModelComponent, ObservableMixin, component_list, createIdentityClass}, React


Use CoffeeScript string templates

Use CoffeeScript string templates

--- src/public/app.nodegraph.coffee
+++ src/public/app.nodegraph.coffee
@@ -118,7 +118,7 @@
 			if (Math.abs(r-g) < 50 && Math.abs(g-b) < 50 && Math.abs(b-r) < 50)
 				return @randomColor()
 			else
-				return 'rgb(' + r + ',' + g + ',' + b + ')'
+				return "rgb(#{r},#{g},#{b})"

 		# Starts the Force Atlas 2 algorithm to draw the graph nicely
 		#


Fix @mention regexp

Fix @mention regexp

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -89,7 +89,7 @@
       ,
         "authors.#{escapeUser user.username}": $exists: true
       ,
-        body: ///@#{user.username}\b///
+        body: ///@#{user.username}(?!\w)///
       ]
     else
       group: group


Bug fix to 662141a4d014fc14df7a6553bd9855def77a04d8

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -422,7 +422,7 @@
   findMessage(message)?.group

 @findMessage = (message, options) ->
-  if message? and not message.id?
+  if message? and not message._id?
     message = Messages.findOne message, options
   message


Use findMessage helper more

Use findMessage helper more

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -34,7 +34,7 @@
   .count()

 _submessageLastUpdate = (root) ->
-  root = Messages.findOne root unless root._id?
+  root = findMessage root
   return null unless root?
   updated = root.updated
   Messages.find
@@ -50,7 +50,7 @@
 @descendantMessageIds = (message) ->
   descendants = []
   recurse = (m) ->
-    m = Messages.findOne m if _.isString m
+    m = findMessage m
     for child in m.children
       descendants.push child
       recurse child
@@ -310,7 +310,7 @@
   ## above, so we don't need to check this in the client.  But this function
   ## is still needed in the server for messages.diff subscription above,
   ## and when simulating non-superuser mode in client/message.coffee.
-  message = Messages.findOne message unless message._id?
+  message = findMessage message
   return false unless message?
   group = message.group #message2group message
   if canSuper group, client, user #groupRoleCheck group, 'super', user
@@ -329,10 +329,10 @@
   Meteor.userId()? and
   groupRoleCheck group, 'post'

-@canEdit = (message) ->
+@canEdit = (msg) ->
   ## Can edit message if an "author" (the creator or edited in the past),
   ## or if we have global edit privileges in this group.
-  msg = Messages.findOne message
+  msg = findMessage msg
   return false unless msg?
   escapeUser(Meteor.user()?.username) of (msg.authors ? {}) or
   groupRoleCheck msg.group, 'edit'
@@ -356,13 +356,13 @@
   canSuper message2group message

 @amAuthor = (message, user = Meteor.user()) ->
-  message = Messages.findOne message unless message._id?
+  message = findMessage message
   return false unless user?.username
   escapeUser(user.username) of (message.authors ? {}) or
   0 <= message.body.search ///@#{user.username}\b///

 @canPrivate = (message) ->
-  message = Messages.findOne message unless message._id?
+  message = findMessage message
   if canSuper message.group
     ## Superuser can always change private flag
     true
@@ -381,7 +381,7 @@
 idle = 1000   ## one second

 @message2group = (message) ->
-  Messages.findOne(message)?.group
+  findMessage(message)?.group

 @findMessage = (message) ->
   message = Messages.findOne message unless message._id?
@@ -403,14 +403,14 @@

 @findMessageRoot = (message) ->
   return message unless message?
-  message = Messages.findOne message unless message._id?
+  message = findMessage message
   if message.root?
     Messages.findOne message.root
   else
     message

 @messageEmpty = (message) ->
-  message = Messages.findOne message unless message._id?
+  message = findMessage message
   message.title.trim().length == 0 and
   message.body.trim().length == 0 and
   not message.file


:art: refactor

:art: refactor

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -53,24 +53,25 @@
     @initialize?()
     @setTarget @new(@target) if _.isString(@target)

+  markSelectedBufferRange: ->
+    @editor.markBufferRange @editor.getSelectedBufferRange(),
+      invalidate: 'never'
+      persistent: false
+
   observeSelectAction: ->
     if @needFlash()
       @onDidSelect =>
         @flash @editor.getSelectedBufferRanges()

     if @needTrackChange()
-      changeMarker = null
+      marker = null
       @onDidSelect =>
-        range = @editor.getSelectedBufferRange()
-        changeMarker = @editor.markBufferRange range,
-          invalidate: 'never'
-          persistent: false
+        marker = @markSelectedBufferRange()

       @onDidOperationFinish =>
-        if range = changeMarker.getBufferRange()
-          @setMarkForChange(range)
-
-  # target - TextObject or Motion to operate on.
+        @setMarkForChange(range) if (range = marker.getBufferRange())
+
+  # @target - TextObject or Motion to operate on.
   setTarget: (@target) ->
     unless _.isFunction(@target.select)
       @vimState.emitter.emit('did-fail-to-set-target')


Quick fix: blindly guard unwanted situation on p, P #802

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -565,7 +565,7 @@
     super

   adjustCursorPosition: ->
-    for selection in @editor.getSelections()
+    for selection in @editor.getSelections() when @mutationsBySelection.has(selection)
       {cursor} = selection
       {start, end} = newRange = @mutationsBySelection.get(selection)
       if @linewisePaste


#350 [revert] Change should not flash. I was wrong

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -1403,7 +1403,6 @@
   @extend()
   requireTarget: true
   trackChange: true
-  flashTarget: true
   supportInsertionCount: false

   execute: ->


astral mode for regex

astral mode for regex

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -51,6 +51,7 @@
           if prefix[0] == "@"
             p = prefix.normalize().replace(/^@/, '')
             p = removeDiacritics(p)
+            console.log p
             suggestions = []
             hits = fuzzaldrin.filter allwords, p, { key: 'author' }
             for h in hits
@@ -60,8 +61,8 @@
             for word in hits
               # We cut the title to 32 chars
               tl = word.title.length
-              if tl > 32
-                word.title = word.title.substr(0, 31) + "\u2026"
+              if tl > 36
+                word.title = word.title.substr(0, 35) + "\u2026"
               # A nifty logo for the different types
               icon = "mortar-board"
               if word.type == "article-journal"
@@ -91,7 +92,7 @@
       getPrefix: (editor, bufferPosition) ->
         # Whatever your prefix regex might be
         regex = /@[\w-]+/
-        wordregex = XRegExp('(?:^|[\\p{WhiteSpace}\\p{Punctuation}])@[\\p{Letter}\\p{Number}\._-]*')
+        wordregex = XRegExp('(?:^|[\\p{WhiteSpace}\\p{Punctuation}])@[\\p{Letter}\\p{Number}\._-]*', 'A')
         cursor = editor.getCursors()[0]
         start = cursor.getBeginningOfCurrentWordBufferPosition({ wordRegex: wordregex, allowPrevious: false })
         end = bufferPosition