apply aligner again

apply aligner again

--- spec/motion-general-spec.coffee
+++ spec/motion-general-spec.coffee
@@ -161,23 +161,23 @@
           """
           cursor: [1, 2]

-      it "by `j`", -> ensureBigCountMotion 'j', cursor: [2, 2]
-      it "by `k`", -> ensureBigCountMotion 'k', cursor: [0, 2]
-      it "by `h`", -> ensureBigCountMotion 'h', cursor: [1, 0]
-      it "by `l`", -> ensureBigCountMotion 'l', cursor: [1, 3]
-      it "by `[`", -> ensureBigCountMotion '[', cursor: [0, 2]
-      it "by `]`", -> ensureBigCountMotion ']', cursor: [2, 2]
-      it "by `w`", -> ensureBigCountMotion 'w', cursor: [2, 3]
-      it "by `W`", -> ensureBigCountMotion 'W', cursor: [2, 3]
-      it "by `b`", -> ensureBigCountMotion 'b', cursor: [0, 0]
-      it "by `B`", -> ensureBigCountMotion 'B', cursor: [0, 0]
-      it "by `e`", -> ensureBigCountMotion 'e', cursor: [2, 3]
-      it "by `(`", -> ensureBigCountMotion '(', cursor: [0, 0]
-      it "by `)`", -> ensureBigCountMotion ')', cursor: [2, 3]
-      it "by `{`", -> ensureBigCountMotion '{', cursor: [0, 0]
-      it "by `}`", -> ensureBigCountMotion '}', cursor: [2, 3]
-      it "by `-`", -> ensureBigCountMotion '-', cursor: [0, 0]
-      it "by `_`", -> ensureBigCountMotion '_', cursor: [2, 0]
+      it "by `j`",   -> ensureBigCountMotion 'j',   cursor: [2, 2]
+      it "by `k`",   -> ensureBigCountMotion 'k',   cursor: [0, 2]
+      it "by `h`",   -> ensureBigCountMotion 'h',   cursor: [1, 0]
+      it "by `l`",   -> ensureBigCountMotion 'l',   cursor: [1, 3]
+      it "by `[`",   -> ensureBigCountMotion '[',   cursor: [0, 2]
+      it "by `]`",   -> ensureBigCountMotion ']',   cursor: [2, 2]
+      it "by `w`",   -> ensureBigCountMotion 'w',   cursor: [2, 3]
+      it "by `W`",   -> ensureBigCountMotion 'W',   cursor: [2, 3]
+      it "by `b`",   -> ensureBigCountMotion 'b',   cursor: [0, 0]
+      it "by `B`",   -> ensureBigCountMotion 'B',   cursor: [0, 0]
+      it "by `e`",   -> ensureBigCountMotion 'e',   cursor: [2, 3]
+      it "by `(`",   -> ensureBigCountMotion '(',   cursor: [0, 0]
+      it "by `)`",   -> ensureBigCountMotion ')',   cursor: [2, 3]
+      it "by `{`",   -> ensureBigCountMotion '{',   cursor: [0, 0]
+      it "by `}`",   -> ensureBigCountMotion '}',   cursor: [2, 3]
+      it "by `-`",   -> ensureBigCountMotion '-',   cursor: [0, 0]
+      it "by `_`",   -> ensureBigCountMotion '_',   cursor: [2, 0]
       it "by `g {`", -> ensureBigCountMotion 'g {', cursor: [1, 2] # No fold no move but won't freeze.
       it "by `g }`", -> ensureBigCountMotion 'g }', cursor: [1, 2] # No fold no move but won't freeze.
       it "by `, N`", -> ensureBigCountMotion ', N', cursor: [1, 2] # No grammar, no move but won't freeze.
@@ -421,46 +421,46 @@
       describe "when it can move", ->
         beforeEach -> set cursor: [1, 0]
         it "delete by j", -> ensure "d j", text: "000\n", mode: 'normal'
-        it "yank by j", -> ensure "y j", text: originalText, register: {'"': text: "111\n222\n"}, mode: 'normal'
+        it "yank by j", ->   ensure "y j", text: originalText, register: {'"': text: "111\n222\n"}, mode: 'normal'
         it "change by j", -> ensure "c j", textC: "000\n|\n", register: {'"': text: "111\n222\n"}, mode: 'insert'

         it "delete by k", -> ensure "d k", text: "222\n", mode: 'normal'
-        it "yank by k", -> ensure "y k", text: originalText, register: {'"': text: "000\n111\n"}, mode: 'normal'
+        it "yank by k", ->   ensure "y k", text: originalText, register: {'"': text: "000\n111\n"}, mode: 'normal'
         it "change by k", -> ensure "c k", textC: "|\n222\n", register: {'"': text: "000\n111\n"}, mode: 'insert'

       describe "when it can not move-up", ->
         beforeEach -> set cursor: [0, 0]
         it "delete by dk", -> ensure "d k", text: originalText, mode: 'normal'
-        it "yank by yk", -> ensure "y k", text: originalText, register: {'"': text: undefined}, mode: 'normal'
+        it "yank by yk", ->   ensure "y k", text: originalText, register: {'"': text: undefined}, mode: 'normal'
         it "change by ck", -> ensure "c k", textC: "|000\n111\n222\n", register: {'"': text: "\n"}, mode: 'insert' # FIXME, incompatible: shoud remain in normal.

       describe "when it can not move-down", ->
         beforeEach -> set cursor: [2, 0]
         it "delete by dj", -> ensure "d j", text: originalText, mode: 'normal'
-        it "yank by yj", -> ensure "y j", text: originalText, register: {'"': text: undefined}, mode: 'normal'
+        it "yank by yj", ->   ensure "y j", text: originalText, register: {'"': text: undefined}, mode: 'normal'
         it "change by cj", -> ensure "c j", textC: "000\n111\n|222\n", register: {'"': text: "\n"}, mode: 'insert' # FIXME, incompatible: shoud remain in normal.

     describe "moveSuccessOnLinewise=true motion", ->
       describe "when it can move", ->
         beforeEach -> set cursor: [1, 0]
         it "delete by G", -> ensure "d G", text: "000\n", mode: 'normal'
-        it "yank by G", -> ensure "y G", text: originalText, register: {'"': text: "111\n222\n"}, mode: 'normal'
+        it "yank by G", ->   ensure "y G", text: originalText, register: {'"': text: "111\n222\n"}, mode: 'normal'
         it "change by G", -> ensure "c G", textC: "000\n|\n", register: {'"': text: "111\n222\n"}, mode: 'insert'

         it "delete by gg", -> ensure "d g g", text: "222\n", mode: 'normal'
-        it "yank by gg", -> ensure "y g g", text: originalText, register: {'"': text: "000\n111\n"}, mode: 'normal'
+        it "yank by gg", ->   ensure "y g g", text: originalText, register: {'"': text: "000\n111\n"}, mode: 'normal'
         it "change by gg", -> ensure "c g g", textC: "|\n222\n", register: {'"': text: "000\n111\n"}, mode: 'insert'

       describe "when it can not move-up", ->
         beforeEach -> set cursor: [0, 0]
         it "delete by gg", -> ensure "d g g", text: "111\n222\n", mode: 'normal'
-        it "yank by gg", -> ensure "y g g", text: originalText, register: {'"': text: "000\n"}, mode: 'normal'
+        it "yank by gg", ->   ensure "y g g", text: originalText, register: {'"': text: "000\n"}, mode: 'normal'
         it "change by gg", -> ensure "c g g", textC: "|\n111\n222\n", register: {'"': text: "000\n"}, mode: 'insert'
       describe "when it can not move-down", ->
         beforeEach -> set cursor: [2, 0]
-        it "delete by G", -> ensure "d G", text: "000\n111\n", mode: 'normal'
-        it "yank by G", -> ensure "y G", text: originalText, register: {'"': text: "222\n"}, mode: 'normal'
-        it "change by G", -> ensure "c G", textC: "000\n111\n|\n", register: {'"': text: "222\n"}, mode: 'insert'
+        it "delete by G", ->  ensure "d G", text: "000\n111\n", mode: 'normal'
+        it "yank by G", ->    ensure "y G", text: originalText, register: {'"': text: "222\n"}, mode: 'normal'
+        it "change by G", ->  ensure "c G", textC: "000\n111\n|\n", register: {'"': text: "222\n"}, mode: 'insert'

   describe "the w keybinding", ->
     baseText = """
@@ -728,35 +728,30 @@
   describe "the ge keybinding", ->
     describe "as a motion", ->
       it "moves the cursor to the end of the previous word", ->
-        set text: "1234 5678 wordword"
-        set cursor: [0, 16]
+        set text: "1234 5678 wordword", cursor: [0, 16]
         ensure 'g e', cursor: [0, 8]
         ensure 'g e', cursor: [0, 3]
         ensure 'g e', cursor: [0, 0]
         ensure 'g e', cursor: [0, 0]

       it "moves corrently when starting between words", ->
-        set text: "1 leading     end"
-        set cursor: [0, 12]
+        set text: "1 leading     end", cursor: [0, 12]
         ensure 'g e', cursor: [0, 8]

       it "takes a count", ->
-        set text: "vim mode plus is getting there"
-        set cursor: [0, 28]
+        set text: "vim mode plus is getting there", cursor: [0, 28]
         ensure '5 g e', cursor: [0, 2]

       # test will fail until the code is fixed
       xit "handles non-words inside words like vim", ->
-        set text: "1234 5678 word-word"
-        set cursor: [0, 18]
+        set text: "1234 5678 word-word", cursor: [0, 18]
         ensure 'g e', cursor: [0, 14]
         ensure 'g e', cursor: [0, 13]
         ensure 'g e', cursor: [0, 8]

       # test will fail until the code is fixed
       xit "handles newlines like vim", ->
-        set text: "1234\n\n\n\n5678"
-        set cursor: [5, 2]
+        set text: "1234\n\n\n\n5678", cursor: [5, 2]
         # vim seems to think an end-of-word is at every blank line
         ensure 'g e', cursor: [4, 0]
         ensure 'g e', cursor: [3, 0]
@@ -768,21 +763,18 @@

     describe "when used by Change operator", ->
       it "changes word fragments", ->
-        set text: "cet document"
-        set cursor: [0, 7]
+        set text: "cet document", cursor: [0, 7]
         ensure 'c g e', cursor: [0, 2], text: "cement", mode: 'insert'
         # TODO: I'm not sure how to check the register after checking the document
         # ensure register: '"', text: 't docu'

       it "changes whitespace properly", ->
-        set text: "ce    doc"
-        set cursor: [0, 4]
+        set text: "ce    doc", cursor: [0, 4]
         ensure 'c g e', cursor: [0, 1], text: "c doc", mode: 'insert'

     describe "in characterwise visual mode", ->
       it "selects word fragments", ->
-        set text: "cet document"
-        set cursor: [0, 7]
+        set text: "cet document", cursor: [0, 7]
         ensure 'v g e', cursor: [0, 2], selectedText: "t docu"

   describe "the E keybinding", ->
@@ -824,8 +816,7 @@
   describe "the gE keybinding", ->
     describe "as a motion", ->
       it "moves the cursor to the end of the previous word", ->
-        set text: "12.4 5~7- word-word"
-        set cursor: [0, 16]
+        set text: "12.4 5~7- word-word", cursor: [0, 16]
         ensure 'g E', cursor: [0, 8]
         ensure 'g E', cursor: [0, 3]
         ensure 'g E', cursor: [0, 0]
@@ -882,16 +873,16 @@
         ensure '(', cursor: [0, 0]

       it "skips to beginning of sentence", ->
-        set cursor: [4, 15]
+        set         cursor: [4, 15]
         ensure '(', cursor: [4, 3]

       it "supports a count", ->
-        set cursor: [0, 0]
+        set           cursor: [0, 0]
         ensure '3 )', cursor: [1, 7]
         ensure '3 (', cursor: [0, 0]

       it "can move start of buffer or end of buffer at maximum", ->
-        set cursor: [0, 0]
+        set             cursor: [0, 0]
         ensure '2 0 )', cursor: [12, 13]
         ensure '2 0 (', cursor: [0, 0]

@@ -934,7 +925,7 @@
           """

       it "moves without crashing", ->
-        set cursor: [0, 0]
+        set         cursor: [0, 0]
         ensure ')', cursor: [1, 4]
         ensure ')', cursor: [1, 4]
         ensure '(', cursor: [0, 0]
@@ -978,7 +969,7 @@

     describe "as a motion", ->
       it "moves the cursor to the end of the paragraph", ->
-        set cursor: [0, 0]
+        set         cursor: [0, 0]
         ensure '}', cursor: [5, 0]
         ensure '}', cursor: [9, 0]
         ensure '}', cursor: [14, 0]
@@ -987,12 +978,12 @@
         ensure '{', cursor: [2, 0]

       it "support count", ->
-        set cursor: [0, 0]
+        set           cursor: [0, 0]
         ensure '3 }', cursor: [14, 0]
         ensure '3 {', cursor: [2, 0]

       it "can move start of buffer or end of buffer at maximum", ->
-        set cursor: [0, 0]
+        set             cursor: [0, 0]
         ensure '1 0 }', cursor: [16, 14]
         ensure '1 0 {', cursor: [0, 0]

@@ -1080,9 +1071,7 @@

       describe "as a selection", ->
         it 'selects to the first character of the line', ->
-          ensure 'd ^',
-            text: 'abcde'
-            cursor: [0, 0]
+          ensure 'd ^', text: 'abcde', cursor: [0, 0]
         it 'selects to the first character of the line', ->
           ensure 'd I', text: 'abcde', cursor: [0, 0]

@@ -1262,7 +1251,7 @@

     describe "as a motion", ->
       it "moves the cursor to the number column", ->
-        ensure '|', cursor: [0, 0]
+        ensure '|',   cursor: [0, 0]
         ensure '1 |', cursor: [0, 0]
         ensure '3 |', cursor: [0, 2]
         ensure '4 |', cursor: [0, 3]
@@ -1280,7 +1269,7 @@

     describe "as a motion from empty line", ->
       it "moves the cursor to the end of the line", ->
-        set cursor: [1, 0]
+        set         cursor: [1, 0]
         ensure '$', cursor: [1, 0]

     describe "as a motion", ->
@@ -1295,7 +1284,7 @@

       it "should remain in the last column when moving down", ->
         ensure '$ j', cursor: [1, 0]
-        ensure 'j', cursor: [2, 9]
+        ensure 'j',   cursor: [2, 9]

       it "support count", ->
         ensure '3 $', cursor: [2, 9]
@@ -1520,7 +1509,7 @@
     describe "as a motion", ->
       describe "in normal mode", ->
         it "moves the cursor to the beginning of the first line", ->
-          set cursor: [2, 0]
+          set           cursor: [2, 0]
           ensure 'g g', cursor: [0, 1]

         it "move to same position if its on first line and first char", ->
@@ -1571,16 +1560,16 @@

     describe "as a motion", ->
       it "moves the cursor to the last nonblank character", ->
-        set cursor: [1, 0]
+        set           cursor: [1, 0]
         ensure 'g _', cursor: [1, 4]

       it "will move the cursor to the beginning of the line if necessary", ->
-        set cursor: [0, 2]
+        set           cursor: [0, 2]
         ensure 'g _', cursor: [0, 0]

     describe "as a repeated motion", ->
       it "moves the cursor downward and outward", ->
-        set cursor: [0, 0]
+        set             cursor: [0, 0]
         ensure '2 g _', cursor: [1, 4]

     describe "as a selection", ->
@@ -1623,8 +1612,8 @@
         cursor: [0, 0]

     describe "put cursor on line specified by percent", ->
-      it "50%", -> ensure '5 0 %', cursor: [499, 0]
-      it "30%", -> ensure '3 0 %', cursor: [299, 0]
+      it "50%", ->  ensure '5 0 %',   cursor: [499, 0]
+      it "30%", ->  ensure '3 0 %',   cursor: [299, 0]
       it "100%", -> ensure '1 0 0 %', cursor: [999, 0]
       it "120%", -> ensure '1 2 0 %', cursor: [999, 0]

@@ -1696,14 +1685,14 @@

     describe "gg, G, N%", ->
       it "go to row with keep column and respect cursor.goalColum", ->
-        ensure 'g g', cursor: [0, 10]
-        ensure '$', cursor: [0, 15]
-        ensure 'G', cursor: [2, 13]
+        ensure 'g g',     cursor: [0, 10]
+        ensure '$',       cursor: [0, 15]
+        ensure 'G',       cursor: [2, 13]
         expect(editor.getLastCursor().goalColumn).toBe(Infinity)
-        ensure '1 %', cursor: [0, 15]
+        ensure '1 %',     cursor: [0, 15]
         expect(editor.getLastCursor().goalColumn).toBe(Infinity)
-        ensure '1 0 h', cursor: [0, 5]
-        ensure '5 0 %', cursor: [1, 5]
+        ensure '1 0 h',   cursor: [0, 5]
+        ensure '5 0 %',   cursor: [1, 5]
         ensure '1 0 0 %', cursor: [2, 5]

     describe "H, M, L", ->


Only show pull requests that are open

Only show pull requests that are open

--- src/jira.coffee
+++ src/jira.coffee
@@ -136,7 +136,7 @@
                     json = JSON.parse body
                     if json.detail?[0]?.pullRequests
                       for pr in json.detail[0].pullRequests
-                        message += "PR: #{pr.url}\n"
+                        message += "PR: #{pr.url}\n" if pr.status is "OPEN"
                   finally
                     msg.send message


rewrite compiler in coffeescript

rewrite compiler in coffeescript

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -1,703 +1,646 @@
-'use strict';
-
-var nodes = require('./nodes');
-var filters = require('./filters');
-var doctypes = require('./doctypes');
-var runtime = require('./runtime');
-var utils = require('./utils');
-var selfClosing = require('./self-closing');
-var parseJSExpression = require('character-parser').parseMax;
-var constantinople = require('constantinople');
-
-function isConstant(src) {
-  return constantinople(src, {jade: runtime, 'jade_interp': undefined});
-}
-function toConstant(src) {
-  return constantinople.toConstant(src, {jade: runtime, 'jade_interp': undefined});
-}
-function errorAtNode(node, error) {
-  error.line = node.line;
-  error.filename = node.filename;
-  return error;
-}
-
-/**
+nodes = require './nodes'
+filters = require './filters'
+doctypes = require './doctypes'
+runtime = require './runtime'
+utils = require './utils'
+selfClosing = require './self-closing'
+parseJSExpression = require('character-parser').parseMax
+constantinople = require 'constantinople'
+
+isConstant = (src) ->
+  constantinople src,
+    jade: runtime
+    jade_interp: undefined
+
+toConstant = (src) ->
+  constantinople.toConstant src,
+    jade: runtime
+    jade_interp: undefined
+
+errorAtNode = (node, error) ->
+  error.line = node.line
+  error.filename = node.filename
+  error
+
+###
  * Initialize `Compiler` with the given `node`.
- *
  * @param {Node} node
  * @param {Object} options
  * @api public
- */
-
-var Compiler = module.exports = function Compiler(node, options) {
-  this.options = options = options || {};
-  this.node = node;
-  this.hasCompiledDoctype = false;
-  this.hasCompiledTag = false;
-  this.pp = options.pretty || false;
-  this.debug = false !== options.compileDebug;
-  this.indents = 0;
-  this.parentIndents = 0;
-  this.terse = false;
-  this.mixins = {};
-  this.dynamicMixins = false;
-  if (options.doctype) this.setDoctype(options.doctype);
-};
-
-/**
+###
+Compiler = module.exports = Compiler = (node, options) ->
+  @options = options = options or {}
+  @node = node
+  @hasCompiledDoctype = false
+  @hasCompiledTag = false
+  @pp = options.pretty or false
+  @debug = false isnt options.compileDebug
+  @indents = 0
+  @parentIndents = 0
+  @terse = false
+  @mixins = {}
+  @dynamicMixins = false
+  @setDoctype options.doctype if options.doctype
+  return
+
+###
  * Compiler prototype.
- */
-
-Compiler.prototype = {
-
-  /**
+###
+Compiler:: =
+  ###
    * Compile parse tree to JavaScript.
-   *
-   * @api public
-   */
-
-  compile: function(){
-    this.buf = [];
-    if (this.pp) this.buf.push("var jade_indent = [];");
-    this.lastBufferedIdx = -1;
-    this.visit(this.node);
-    if (!this.dynamicMixins) {
-      // if there are no dynamic mixins we can remove any un-used mixins
-      var mixinNames = Object.keys(this.mixins);
-      for (var i = 0; i < mixinNames.length; i++) {
-        var mixin = this.mixins[mixinNames[i]];
-        if (!mixin.used) {
-          for (var x = 0; x < mixin.instances.length; x++) {
-            for (var y = mixin.instances[x].start; y < mixin.instances[x].end; y++) {
-              this.buf[y] = '';
-            }
-          }
-        }
-      }
-    }
-    return this.buf.join('\n');
-  },
-
-  /**
-   * Sets the default doctype `name`. Sets terse mode to `true` when
-   * html 5 is used, causing self-closing tags to end with ">" vs "/>",
-   * and boolean attributes are not mirrored.
-   *
+   * @api public
+  ###
+  compile: ->
+    @buf = []
+    @buf.push 'var jade_indent = [];'  if @pp
+    @lastBufferedIdx = -1
+    @visit @node
+    unless @dynamicMixins
+
+      # if there are no dynamic mixins we can remove any un-used mixins
+      mixinNames = Object.keys(@mixins)
+      i = 0
+
+      while i < mixinNames.length
+        mixin = @mixins[mixinNames[i]]
+        unless mixin.used
+          x = 0
+
+          while x < mixin.instances.length
+            y = mixin.instances[x].start
+
+            while y < mixin.instances[x].end
+              @buf[y] = ''
+              y++
+            x++
+        i++
+    @buf.join '\n'
+
+
+  ###
+  Sets the default doctype `name`. Sets terse mode to `true` when
+  html 5 is used, causing self-closing tags to end with ">" vs "/>",
+  and boolean attributes are not mirrored.
+
    * @param {string} name
    * @api public
-   */
-
-  setDoctype: function(name){
-    this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>';
-    this.terse = this.doctype.toLowerCase() == '<!doctype html>';
-    this.xml = 0 == this.doctype.indexOf('<?xml');
-  },
-
-  /**
-   * Buffer the given `str` exactly as is or with interpolation
-   *
+  ###
+  setDoctype: (name) ->
+    @doctype = doctypes[name.toLowerCase()] or "<!DOCTYPE #{name}>"
+    @terse = @doctype.toLowerCase() is '<!doctype html>'
+    @xml = 0 is @doctype.indexOf('<?xml')
+    return
+
+
+  ###
+  Buffer the given `str` exactly as is or with interpolation
+
    * @param {String} str
    * @param {Boolean} interpolate
    * @api public
-   */
-
-  buffer: function (str, interpolate) {
-    var self = this;
-    if (interpolate) {
-      var match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str);
-      if (match) {
-        this.buffer(str.substr(0, match.index), false);
-        if (match[1]) { // escape
-          this.buffer(match[2] + '{', false);
-          this.buffer(match[3], true);
-          return;
-        } else {
-          var rest = match[3];
-          var range = parseJSExpression(rest);
-          var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade_interp = " + range.src + ") == null ? '' : jade_interp)";
-          this.bufferExpression(code);
-          this.buffer(rest.substr(range.end + 1), true);
-          return;
-        }
-      }
-    }
-
-    str = JSON.stringify(str);
-    str = str.substr(1, str.length - 2);
-
-    if (this.lastBufferedIdx == this.buf.length) {
-      if (this.lastBufferedType === 'code') this.lastBuffered += ' + "';
-      this.lastBufferedType = 'text';
-      this.lastBuffered += str;
-      this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + '");'
-    } else {
-      this.buf.push('buf.push("' + str + '");');
-      this.lastBufferedType = 'text';
-      this.bufferStartChar = '"';
-      this.lastBuffered = str;
-      this.lastBufferedIdx = this.buf.length;
-    }
-  },
-
-  /**
+  ###
+  buffer: (str, interpolate) ->
+    self = this
+    if interpolate
+      match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str)
+      if match
+        @buffer str.substr(0, match.index), false
+        if match[1] # escape
+          @buffer match[2] + '{', false
+          @buffer match[3], true
+          return
+        else
+          rest = match[3]
+          range = parseJSExpression(rest)
+          code = (if '!' is match[2] then '' else 'jade.escape') + "((jade_interp = #{range.src}) == null ? '' : jade_interp)"
+          @bufferExpression code
+          @buffer rest.substr(range.end + 1), true
+          return
+    str = JSON.stringify(str)
+    str = str.substr(1, str.length - 2)
+    if @lastBufferedIdx is @buf.length
+      @lastBuffered += " + \""  if @lastBufferedType is 'code'
+      @lastBufferedType = 'text'
+      @lastBuffered += str
+      @buf[@lastBufferedIdx - 1] = "buf.push(#{@bufferStartChar}#{@lastBuffered}\");"
+    else
+      @buf.push "buf.push(\"#{str}\");"
+      @lastBufferedType = 'text'
+      @bufferStartChar = "\""
+      @lastBuffered = str
+      @lastBufferedIdx = @buf.length
+    return
+
+
+  ###
    * Buffer the given `src` so it is evaluated at run time
-   *
    * @param {String} src
    * @api public
-   */
-
-  bufferExpression: function (src) {
-    if (isConstant(src)) {
-      return this.buffer(toConstant(src) + '', false)
-    }
-    if (this.lastBufferedIdx == this.buf.length) {
-      if (this.lastBufferedType === 'text') this.lastBuffered += '"';
-      this.lastBufferedType = 'code';
-      this.lastBuffered += ' + (' + src + ')';
-      this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + ');'
-    } else {
-      this.buf.push('buf.push(' + src + ');');
-      this.lastBufferedType = 'code';
-      this.bufferStartChar = '';
-      this.lastBuffered = '(' + src + ')';
-      this.lastBufferedIdx = this.buf.length;
-    }
-  },
-
-  /**
-   * Buffer an indent based on the current `indent`
-   * property and an additional `offset`.
-   *
+  ###
+  bufferExpression: (src) ->
+    if isConstant(src)
+      return @buffer(toConstant(src) + '', false)
+    if @lastBufferedIdx is @buf.length
+      @lastBuffered += "\"" if @lastBufferedType is 'text'
+      @lastBufferedType = 'code'
+      @lastBuffered += " + (#{src})"
+      @buf[@lastBufferedIdx - 1] = "buf.push(#{@bufferStartChar}#{@lastBuffered});"
+    else
+      @buf.push "buf.push(#{src});"
+      @lastBufferedType = 'code'
+      @bufferStartChar = ''
+      @lastBuffered = "(#{src})"
+      @lastBufferedIdx = @buf.length
+    return
+
+
+  ###
+   * Buffer an indent based on the current `indent` property and an additional `offset`.
    * @param {Number} offset
    * @param {Boolean} newline
    * @api public
-   */
-
-  prettyIndent: function(offset, newline){
-    offset = offset || 0;
-    newline = newline ? '\n' : '';
-    this.buffer(newline + Array(this.indents + offset).join('  '));
-    if (this.parentIndents)
-      this.buf.push("buf.push.apply(buf, jade_indent);");
-  },
-
-  /**
+  ###
+  prettyIndent: (offset, newline) ->
+    offset = offset or 0
+    newline = (if newline then '\n' else '')
+    @buffer newline + Array(@indents + offset).join("  ")
+    @buf.push "buf.push.apply(buf, jade_indent);"  if @parentIndents
+    return
+
+
+  ###
    * Visit `node`.
-   *
    * @param {Node} node
    * @api public
-   */
-
-  visit: function(node){
-    var debug = this.debug;
-
-    if (debug) {
-      this.buf.push('jade_debug.unshift({ lineno: ' + node.line
-        + ', filename: ' + (node.filename
-          ? JSON.stringify(node.filename)
-          : 'jade_debug[0].filename')
-        + ' });');
-    }
-
-    // Massive hack to fix our context
-    // stack for - else[ if] etc
-    if (false === node.debug && this.debug) {
-      this.buf.pop();
-      this.buf.pop();
-    }
-
-    this.visitNode(node);
-
-    if (debug) this.buf.push('jade_debug.shift();');
-  },
-
-  /**
+  ###
+  visit: (node) ->
+    debug = @debug
+    if debug
+      @buf.push """
+        jade_debug.unshift({
+          lineno: #{node.line},
+          filename: #{
+            if node.filename
+              JSON.stringify(node.filename)
+            else
+              'jade_debug[0].filename'
+          }
+        });
+      """
+
+    # Massive hack to fix our context
+    # stack for - else[ if] etc
+    if false is node.debug and @debug
+      @buf.pop()
+      @buf.pop()
+    @visitNode node
+    @buf.push "jade_debug.shift();"  if debug
+    return
+
+
+  ###
    * Visit `node`.
-   *
    * @param {Node} node
    * @api public
-   */
-
-  visitNode: function(node){
-    return this['visit' + node.type](node);
-  },
-
-  /**
+  ###
+  visitNode: (node) ->
+    this['visit' + node.type] node
+
+
+  ###
    * Visit case `node`.
-   *
    * @param {Literal} node
    * @api public
-   */
-
-  visitCase: function(node){
-    var _ = this.withinCase;
-    this.withinCase = true;
-    this.buf.push('switch (' + node.expr + '){');
-    this.visit(node.block);
-    this.buf.push('}');
-    this.withinCase = _;
-  },
-
-  /**
+  ###
+  visitCase: (node) ->
+    _ = @withinCase
+    @withinCase = true
+    @buf.push "switch (#{node.expr}){"
+    @visit node.block
+    @buf.push '}'
+    @withinCase = _
+    return
+
+
+  ###
    * Visit when `node`.
-   *
    * @param {Literal} node
    * @api public
-   */
-
-  visitWhen: function(node){
-    if ('default' == node.expr) {
-      this.buf.push('default:');
-    } else {
-      this.buf.push('case ' + node.expr + ':');
-    }
-    if (node.block) {
-      this.visit(node.block);
-      this.buf.push('  break;');
-    }
-  },
-
-  /**
+  ###
+  visitWhen: (node) ->
+    if 'default' is node.expr
+      @buf.push 'default:'
+    else
+      @buf.push "case #{node.expr}:"
+    if node.block
+      @visit node.block
+      @buf.push "  break;"
+    return
+
+
+  ###
    * Visit literal `node`.
-   *
    * @param {Literal} node
    * @api public
-   */
-
-  visitLiteral: function(node){
-    this.buffer(node.str);
-  },
-
-  /**
+  ###
+  visitLiteral: (node) ->
+    @buffer node.str
+    return
+
+
+  ###
    * Visit all nodes in `block`.
-   *
    * @param {Block} block
    * @api public
-   */
-
-  visitBlock: function(block){
-    var len = block.nodes.length
-      , escape = this.escape
-      , pp = this.pp
-
-    // Pretty print multi-line text
-    if (pp && len > 1 && !escape && block.nodes[0].isText && block.nodes[1].isText)
-      this.prettyIndent(1, true);
-
-    for (var i = 0; i < len; ++i) {
-      // Pretty print text
-      if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText)
-        this.prettyIndent(1, false);
-
-      this.visit(block.nodes[i]);
-      // Multiple text nodes are separated by newlines
-      if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText)
-        this.buffer('\n');
-    }
-  },
-
-  /**
+  ###
+  visitBlock: (block) ->
+    len = block.nodes.length
+
+    # Pretty print multi-line text
+    if @pp and len > 1 and not @escape and block.nodes[0].isText and block.nodes[1].isText
+      @prettyIndent 1, true
+
+    for i in [0...len]
+      # Pretty print text
+      if @pp and i > 0 and not @escape and block.nodes[i].isText and block.nodes[i - 1].isText
+        @prettyIndent 1, false
+      @visit block.nodes[i]
+
+      # Multiple text nodes are separated by newlines
+      if block.nodes[i + 1] and block.nodes[i].isText and block.nodes[i + 1].isText
+        @buffer '\n'
+    return
+
+
+  ###
    * Visit a mixin's `block` keyword.
-   *
    * @param {MixinBlock} block
    * @api public
-   */
-
-  visitMixinBlock: function(block){
-    if (this.pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join('  ') + "');");
-    this.buf.push('block && block();');
-    if (this.pp) this.buf.push("jade_indent.pop();");
-  },
-
-  /**
-   * Visit `doctype`. Sets terse mode to `true` when html 5
-   * is used, causing self-closing tags to end with ">" vs "/>",
-   * and boolean attributes are not mirrored.
-   *
+  ###
+  visitMixinBlock: (block) ->
+    if @pp
+      @buf.push "jade_indent.push('#{Array(@indents + 1).join('  ')}');"
+    @buf.push 'block && block();'
+    if @pp
+      @buf.push 'jade_indent.pop();'
+    return
+
+
+  ###
+   * Visit `doctype`. Sets terse mode to `true` when html 5 is used, causing self-closing tags to end with ">" vs "/>", and boolean attributes are not mirrored.
    * @param {Doctype} doctype
    * @api public
-   */
-
-  visitDoctype: function(doctype){
-    if (doctype && (doctype.val || !this.doctype)) {
-      this.setDoctype(doctype.val || 'default');
-    }
-
-    if (this.doctype) this.buffer(this.doctype);
-    this.hasCompiledDoctype = true;
-  },
-
-  /**
-   * Visit `mixin`, generating a function that
-   * may be called within the template.
-   *
+  ###
+  visitDoctype: (doctype) ->
+    @setDoctype doctype.val or 'default'  if doctype and (doctype.val or not @doctype)
+    @buffer @doctype if @doctype
+    @hasCompiledDoctype = true
+    return
+
+
+  ###
+   * Visit `mixin`, generating a function that may be called within the template.
    * @param {Mixin} mixin
    * @api public
-   */
-
-  visitMixin: function(mixin){
-    var name = 'jade_mixins[';
-    var args = mixin.args || '';
-    var block = mixin.block;
-    var attrs = mixin.attrs;
-    var attrsBlocks = mixin.attributeBlocks;
-    var pp = this.pp;
-    var dynamic = mixin.name[0]==='#';
-    var key = mixin.name;
-    if (dynamic) this.dynamicMixins = true;
-    name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';
-
-    this.mixins[key] = this.mixins[key] || {used: false, instances: []};
-    if (mixin.call) {
-      this.mixins[key].used = true;
-      if (pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join('  ') + "');")
-      if (block || attrs.length || attrsBlocks.length) {
-
-        this.buf.push(name + '.call({');
-
-        if (block) {
-          this.buf.push('block: function(){');
-
-          // Render block with no indents, dynamically added when rendered
-          this.parentIndents++;
-          var _indents = this.indents;
-          this.indents = 0;
-          this.visit(mixin.block);
-          this.indents = _indents;
-          this.parentIndents--;
-
-          if (attrs.length || attrsBlocks.length) {
-            this.buf.push('},');
-          } else {
-            this.buf.push('}');
-          }
-        }
-
-        if (attrsBlocks.length) {
-          if (attrs.length) {
-            var val = this.attrs(attrs);
-            attrsBlocks.unshift(val);
-          }
-          this.buf.push('attributes: jade.merge([' + attrsBlocks.join(',') + '])');
-        } else if (attrs.length) {
-          var val = this.attrs(attrs);
-          this.buf.push('attributes: ' + val);
-        }
-
-        if (args) {
-          this.buf.push('}, ' + args + ');');
+  ###
+  visitMixin: (mixin) ->
+    name = 'jade_mixins['
+    args = mixin.args or ''
+    block = mixin.block
+    attrs = mixin.attrs
+    attrsBlocks = mixin.attributeBlocks
+    dynamic = mixin.name[0] is '#'
+    key = mixin.name
+    @dynamicMixins = true if dynamic
+    name += (
+      if dynamic
+        mixin.name.substr(2, mixin.name.length - 3)
+      else
+        "\"#{mixin.name}\""
+    ) + ']'
+    @mixins[key] = @mixins[key] or
+      used: false
+      instances: []
+
+    if mixin.call
+      @mixins[key].used = true
+      if @pp
+        @buf.push "jade_indent.push('#{Array(@indents + 1).join('  ')}');"
+      if block or attrs.length or attrsBlocks.length
+        @buf.push name + '.call({'
+        if block
+          @buf.push 'block: function(){'
+
+          # Render block with no indents, dynamically added when rendered
+          @parentIndents++
+          _indents = @indents
+          @indents = 0
+          @visit mixin.block
+          @indents = _indents
+          @parentIndents--
+          if attrs.length or attrsBlocks.length
+            @buf.push '},'
+          else
+            @buf.push '}'
+        if attrsBlocks.length
+          if attrs.length
+            val = @attrs(attrs)
+            attrsBlocks.unshift val
+          @buf.push "attributes: jade.merge([#{attrsBlocks.join(",")}])"
+        else if attrs.length
+          val = @attrs(attrs)
+          @buf.push "attributes: #{val}"
+        if args
+          @buf.push "}, #{args});"
+        else
+          @buf.push '});'
+      else
+        @buf.push name + "(#{args});"
+      if @pp
+        @buf.push 'jade_indent.pop();'
+    else
+      mixin_start = @buf.length
+      @buf.push "#{name} = function(#{args}){"
+      @buf.push 'var block = (this && this.block), attributes = (this && this.attributes) || {};'
+      @parentIndents++
+      @visit block
+      @parentIndents--
+      @buf.push "};"
+      mixin_end = @buf.length
+      @mixins[key].instances.push
+        start: mixin_start
+        end: mixin_end
+
+    return
+
+
+  ###
+   * Visit `tag` buffering tag markup, generating attributes, visiting the `tag`'s code and block.
+   * @param {Tag} tag
+   * @api public
+  ###
+  visitTag: (tag) ->
+    bufferName = ->
+      if tag.buffer
+        self.bufferExpression name
+      else
+        self.buffer name
+      return
+    @indents++
+    name = tag.name
+    pp = @pp
+    self = this
+    @escape = true if 'pre' is tag.name
+    unless @hasCompiledTag
+      @visitDoctype()  if not @hasCompiledDoctype and 'html' is name
+      @hasCompiledTag = true
+
+    # pretty print
+    @prettyIndent 0, true if pp and not tag.isInline()
+    if tag.selfClosing or (not @xml and selfClosing.indexOf(tag.name) isnt -1)
+      @buffer '<'
+      bufferName()
+      @visitAttributes tag.attrs, tag.attributeBlocks
+      (if @terse then @buffer('>') else @buffer('/>'))
+
+      # if it is non-empty throw an error
+      if tag.block and
+         not (tag.block.type is 'Block' and tag.block.nodes.length is 0) and
+         tag.block.nodes.some((tag) ->
+          tag.type isnt 'Text' or not /^\s*$/.test(tag.val)
+         )
+        throw errorAtNode(tag, new Error("#{name} is self closing and should not have content."))
+    else
+
+      # Optimize attributes buffering
+      @buffer '<'
+      bufferName()
+      @visitAttributes tag.attrs, tag.attributeBlocks
+      @buffer '>'
+      @visitCode tag.code if tag.code
+      @visit tag.block
+
+      # pretty print
+      @prettyIndent 0, true if pp and not tag.isInline() and 'pre' isnt tag.name and not tag.canInline()
+      @buffer '</'
+      bufferName()
+      @buffer '>'
+    @escape = false if 'pre' is tag.name
+    @indents--
+    return
+
+
+  ###
+  Visit `filter`, throwing when the filter does not exist.
+
+   * @param {Filter} filter
+   * @api public
+  ###
+  visitFilter: (filter) ->
+    text = filter.block.nodes.map((node) ->
+      node.val
+    ).join('\n')
+    filter.attrs.filename = @options.filename
+    try
+      @buffer filters(filter.name, text, filter.attrs), true
+    catch err
+      throw errorAtNode(filter, err)
+    return
+
+
+  ###
+  Visit `text` node.
+
+   * @param {Text} text
+   * @api public
+  ###
+  visitText: (text) ->
+    @buffer text.val, true
+    return
+
+
+  ###
+  Visit a `comment`, only buffering when the buffer flag is set.
+
+   * @param {Comment} comment
+   * @api public
+  ###
+  visitComment: (comment) ->
+    return  unless comment.buffer
+    @prettyIndent 1, true if @pp
+    @buffer "<!--#{comment.val}-->"
+    return
+
+
+  ###
+  Visit a `BlockComment`.
+
+   * @param {Comment} comment
+   * @api public
+  ###
+  visitBlockComment: (comment) ->
+    return  unless comment.buffer
+    @prettyIndent 1, true if @pp
+    @buffer "<!--#{comment.val}"
+    @visit comment.block
+    @prettyIndent 1, true if @pp
+    @buffer '-->'
+    return
+
+
+  ###
+   * Visit `code`, respecting buffer / escape flags. If the code is followed by
+     a block, wrap it in a self-calling function.
+   * @param {Code} code
+   * @api public
+  ###
+  visitCode: (code) ->
+
+    # Wrap code blocks with {}.
+    # we only wrap unbuffered code blocks ATM
+    # since they are usually flow control
+
+    # Buffer code
+    if code.buffer
+      val = code.val.trimLeft()
+      val = "null == (jade_interp = #{val}) ? \"\" : jade_interp"
+      if code.escape
+        val = "jade.escape(#{val})"
+      @bufferExpression val
+    else
+      @buf.push code.val
+
+    # Block support
+    if code.block
+      unless code.buffer
+        @buf.push '{'
+      @visit code.block
+      unless code.buffer
+        @buf.push '}'
+    return
+
+
+  ###
+   * Visit `each` block.
+   * @param {Each} each
+   * @api public
+  ###
+  visitEach: (each) ->
+    @buf.push """
+      // iterate #{each.obj}
+      ;(function(){
+        var $$obj = #{each.obj};
+        if ('number' == typeof $$obj.length) {
+      """
+    if each.alternative
+      @buf.push '  if ($$obj.length) {'
+    @buf.push """
+          for (var #{each.key} = 0, $$l = $$obj.length; #{each.key} < $$l; #{each.key}++) {
+            var #{each.val} = $$obj[#{each.key}];
+      """
+    @visit each.block
+    @buf.push '    }\n'
+    if each.alternative
+      @buf.push '  } else {'
+      @visit each.alternative
+      @buf.push '  }'
+    @buf.push """
         } else {
-          this.buf.push('});');
-        }
-
-      } else {
-        this.buf.push(name + '(' + args + ');');
-      }
-      if (pp) this.buf.push("jade_indent.pop();")
-    } else {
-      var mixin_start = this.buf.length;
-      this.buf.push(name + ' = function(' + args + '){');
-      this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};');
-      this.parentIndents++;
-      this.visit(block);
-      this.parentIndents--;
-      this.buf.push('};');
-      var mixin_end = this.buf.length;
-      this.mixins[key].instances.push({start: mixin_start, end: mixin_end});
-    }
-  },
-
-  /**
-   * Visit `tag` buffering tag markup, generating
-   * attributes, visiting the `tag`'s code and block.
-   *
-   * @param {Tag} tag
-   * @api public
-   */
-
-  visitTag: function(tag){
-    this.indents++;
-    var name = tag.name
-      , pp = this.pp
-      , self = this;
-
-    function bufferName() {
-      if (tag.buffer) self.bufferExpression(name);
-      else self.buffer(name);
-    }
-
-    if ('pre' == tag.name) this.escape = true;
-
-    if (!this.hasCompiledTag) {
-      if (!this.hasCompiledDoctype && 'html' == name) {
-        this.visitDoctype();
-      }
-      this.hasCompiledTag = true;
-    }
-
-    // pretty print
-    if (pp && !tag.isInline())
-      this.prettyIndent(0, true);
-
-    if (tag.selfClosing || (!this.xml && selfClosing.indexOf(tag.name) !== -1)) {
-      this.buffer('<');
-      bufferName();
-      this.visitAttributes(tag.attrs, tag.attributeBlocks);
-      this.terse
-        ? this.buffer('>')
-        : this.buffer('/>');
-      // if it is non-empty throw an error
-      if (tag.block &&
-          !(tag.block.type === 'Block' && tag.block.nodes.length === 0) &&
-          tag.block.nodes.some(function (tag) {
-            return tag.type !== 'Text' || !/^\s*$/.test(tag.val)
-          })) {
-        throw errorAtNode(tag, new Error(name + ' is self closing and should not have content.'));
-      }
-    } else {
-      // Optimize attributes buffering
-      this.buffer('<');
-      bufferName();
-      this.visitAttributes(tag.attrs, tag.attributeBlocks);
-      this.buffer('>');
-      if (tag.code) this.visitCode(tag.code);
-      this.visit(tag.block);
-
-      // pretty print
-      if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline())
-        this.prettyIndent(0, true);
-
-      this.buffer('</');
-      bufferName();
-      this.buffer('>');
-    }
-
-    if ('pre' == tag.name) this.escape = false;
-
-    this.indents--;
-  },
-
-  /**
-   * Visit `filter`, throwing when the filter does not exist.
-   *
-   * @param {Filter} filter
-   * @api public
-   */
-
-  visitFilter: function(filter){
-    var text = filter.block.nodes.map(
-      function(node){ return node.val; }
-    ).join('\n');
-    filter.attrs.filename = this.options.filename;
-    try {
-      this.buffer(filters(filter.name, text, filter.attrs), true);
-    } catch (err) {
-      throw errorAtNode(filter, err);
-    }
-  },
-
-  /**
-   * Visit `text` node.
-   *
-   * @param {Text} text
-   * @api public
-   */
-
-  visitText: function(text){
-    this.buffer(text.val, true);
-  },
-
-  /**
-   * Visit a `comment`, only buffering when the buffer flag is set.
-   *
-   * @param {Comment} comment
-   * @api public
-   */
-
-  visitComment: function(comment){
-    if (!comment.buffer) return;
-    if (this.pp) this.prettyIndent(1, true);
-    this.buffer('<!--' + comment.val + '-->');
-  },
-
-  /**
-   * Visit a `BlockComment`.
-   *
-   * @param {Comment} comment
-   * @api public
-   */
-
-  visitBlockComment: function(comment){
-    if (!comment.buffer) return;
-    if (this.pp) this.prettyIndent(1, true);
-    this.buffer('<!--' + comment.val);
-    this.visit(comment.block);
-    if (this.pp) this.prettyIndent(1, true);
-    this.buffer('-->');
-  },
-
-  /**
-   * Visit `code`, respecting buffer / escape flags.
-   * If the code is followed by a block, wrap it in
-   * a self-calling function.
-   *
-   * @param {Code} code
-   * @api public
-   */
-
-  visitCode: function(code){
-    // Wrap code blocks with {}.
-    // we only wrap unbuffered code blocks ATM
-    // since they are usually flow control
-
-    // Buffer code
-    if (code.buffer) {
-      var val = code.val.trimLeft();
-      val = 'null == (jade_interp = '+val+') ? "" : jade_interp';
-      if (code.escape) val = 'jade.escape(' + val + ')';
-      this.bufferExpression(val);
-    } else {
-      this.buf.push(code.val);
-    }
-
-    // Block support
-    if (code.block) {
-      if (!code.buffer) this.buf.push('{');
-      this.visit(code.block);
-      if (!code.buffer) this.buf.push('}');
-    }
-  },
-
-  /**
-   * Visit `each` block.
-   *
-   * @param {Each} each
-   * @api public
-   */
-
-  visitEach: function(each){
-    this.buf.push(''
-      + '// iterate ' + each.obj + '\n'
-      + ';(function(){\n'
-      + '  var $$obj = ' + each.obj + ';\n'
-      + '  if (\'number\' == typeof $$obj.length) {\n');
-
-    if (each.alternative) {
-      this.buf.push('  if ($$obj.length) {');
-    }
-
-    this.buf.push(''
-      + '    for (var ' + each.key + ' = 0, $$l = $$obj.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
-      + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');
-
-    this.visit(each.block);
-
-    this.buf.push('    }\n');
-
-    if (each.alternative) {
-      this.buf.push('  } else {');
-      this.visit(each.alternative);
-      this.buf.push('  }');
-    }
-
-    this.buf.push(''
-      + '  } else {\n'
-      + '    var $$l = 0;\n'
-      + '    for (var ' + each.key + ' in $$obj) {\n'
-      + '      $$l++;'
-      + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');
-
-    this.visit(each.block);
-
-    this.buf.push('    }\n');
-    if (each.alternative) {
-      this.buf.push('    if ($$l === 0) {');
-      this.visit(each.alternative);
-      this.buf.push('    }');
-    }
-    this.buf.push('  }\n}).call(this);\n');
-  },
-
-  /**
+          var $$l = 0;
+          for (var #{each.key} in $$obj) {
+            $$l++;
+            var #{each.val} = $$obj[#{each.key}];
+      """
+    @visit each.block
+    @buf.push '    }\n'
+    if each.alternative
+      @buf.push '    if ($$l === 0) {'
+      @visit each.alternative
+      @buf.push '    }'
+    @buf.push '  }\n}).call(this);\n'
+    return
+
+
+  ###
    * Visit `attrs`.
-   *
    * @param {Array} attrs
    * @api public
-   */
-
-  visitAttributes: function(attrs, attributeBlocks){
-    if (attributeBlocks.length) {
-      if (attrs.length) {
-        var val = this.attrs(attrs);
-        attributeBlocks.unshift(val);
-      }
-      this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + JSON.stringify(this.terse) + ')');
-    } else if (attrs.length) {
-      this.attrs(attrs, true);
-    }
-  },
-
-  /**
+  ###
+  visitAttributes: (attrs, attributeBlocks) ->
+    if attributeBlocks.length
+      if attrs.length
+        val = @attrs(attrs)
+        attributeBlocks.unshift val
+      @bufferExpression "jade.attrs(jade.merge([#{attributeBlocks.join(",")}]), #{JSON.stringify(@terse)})"
+    else @attrs attrs, true if attrs.length
+    return
+
+
+  ###
    * Compile attributes.
-   */
-
-  attrs: function(attrs, buffer){
-    var buf = [];
-    var classes = [];
-    var classEscaping = [];
-
-    attrs.forEach(function(attr){
-      var key = attr.name;
-      var escaped = attr.escaped;
-
-      if (key === 'class') {
-        classes.push(attr.val);
-        classEscaping.push(attr.escaped);
-      } else if (isConstant(attr.val)) {
-        if (buffer) {
-          this.buffer(runtime.attr(key, toConstant(attr.val), escaped, this.terse));
-        } else {
-          var val = toConstant(attr.val);
-          if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) {
-            val = runtime.escape(val);
-          }
-          buf.push(JSON.stringify(key) + ': ' + JSON.stringify(val));
-        }
-      } else {
-        if (buffer) {
-          this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + JSON.stringify(escaped) + ', ' + JSON.stringify(this.terse) + ')');
-        } else {
-          var val = attr.val;
-          if (escaped && !(key.indexOf('data') === 0)) {
-            val = 'jade.escape(' + val + ')';
-          } else if (escaped) {
-            val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)';
-          }
-          buf.push(JSON.stringify(key) + ': ' + val);
-        }
-      }
-    }.bind(this));
-    if (buffer) {
-      if (classes.every(isConstant)) {
-        this.buffer(runtime.cls(classes.map(toConstant), classEscaping));
-      } else {
-        this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + JSON.stringify(classEscaping) + ')');
-      }
-    } else if (classes.length) {
-      if (classes.every(isConstant)) {
-        classes = JSON.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) {
-          return classEscaping[i] ? runtime.escape(cls) : cls;
-        })));
-      } else {
-        classes = '(jade_interp = ' + JSON.stringify(classEscaping) + ',' +
-          ' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' +
-          '   return jade_interp[i] ? jade.escape(cls) : cls' +
-          ' }))' +
-          ')';
-      }
-      if (classes.length)
-        buf.push('"class": ' + classes);
-    }
-    return '{' + buf.join(',') + '}';
-  }
-};
+  ###
+  attrs: (attrs, buffer) ->
+    buf = []
+    classes = []
+    classEscaping = []
+    attrs.forEach ((attr) ->
+      key = attr.name
+      escaped = attr.escaped
+      if key is 'class'
+        classes.push attr.val
+        classEscaping.push attr.escaped
+      else if isConstant(attr.val)
+        if buffer
+          @buffer runtime.attr(key, toConstant(attr.val), escaped, @terse)
+        else
+          val = toConstant(attr.val)
+          if escaped and not (key.indexOf('data') is 0 and typeof val isnt 'string')
+            val = runtime.escape(val)
+          buf.push JSON.stringify(key) + ": " + JSON.stringify(val)
+      else
+        if buffer
+          @bufferExpression """
+            jade.attr(
+              \"#{key}\",
+              #{attr.val},
+              #{JSON.stringify(escaped)},
+              #{JSON.stringify(@terse)}
+            )
+          """
+        else
+          val = attr.val
+          if escaped and (key.indexOf('data') isnt 0)
+            val = "jade.escape(#{val})"
+          else if escaped
+            val = """(
+              typeof (jade_interp = #{val}) == \"string\" ? jade.escape(jade_interp) : jade_interp
+            )"""
+          buf.push "#{JSON.stringify(key)}: #{val}"
+      return
+    ).bind(this)
+    if buffer
+      if classes.every(isConstant)
+        @buffer runtime.cls(classes.map(toConstant), classEscaping)
+      else
+        @bufferExpression """
+          jade.cls([#{classes.join(",")}], #{JSON.stringify(classEscaping)})
+        """
+    else if classes.length
+      if classes.every(isConstant)
+        classes = JSON.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map((cls, i) ->
+          (if classEscaping[i] then runtime.escape(cls) else cls)
+        )))
+      else
+        classes = """(
+          jade_interp = #{JSON.stringify(classEscaping)},
+          jade.joinClasses([#{classes.join(",")}]
+            .map(jade.joinClasses)
+            .map(function (cls, i) {
+              return jade_interp[i] ? jade.escape(cls) : cls
+            })
+          )
+        )"""
+      if classes.length
+        buf.push "\"class\": #{classes}"
+    return "{#{buf.join(',')}}"


Clearer name

Clearer name

--- core/app/backbone/views/share/share_new_fact_view.coffee
+++ core/app/backbone/views/share/share_new_fact_view.coffee
@@ -5,7 +5,7 @@
     text: ''

   events:
-    'click': 'onClick'
+    'click': 'toggleChecked'

   modelEvents:
     'change': 'render'
@@ -14,7 +14,7 @@
     @$el.addClass "factlink-icon-#{@options.name}"
     @$el.toggleClass 'share-button-checked', @model.get(@options.name)

-  onClick: ->
+  toggleChecked: ->
     @model.set @options.name, !@model.get(@options.name)


friendly error

friendly error

--- src/main.coffee
+++ src/main.coffee
@@ -31,20 +31,20 @@
     negations.forEach (m) ->
         if m[1] == ""
             if m[3] == ""
-                assert(relativeFilePath != m[2], "Negation violated.")
+                assert(relativeFilePath != m[2], "NV: Negation violated.")
             else
-                assert(not relativeFilePath.startsWith(m[2]), "Negation violated.")
+                assert(not relativeFilePath.startsWith(m[2]), "NV: Negation violated.")
         else
             if m[3] == ""
                 assert(not (
                     relativeFilePath.endsWith("/" + m[2]) or
                     relativeFilePath == m[2]
-                ), "Negation violated.")
+                ), "NV: Negation violated.")
             else
                 assert(not (
                     relativeFilePath.includes("/" + m[2]) or
                     relativeFilePath.startsWith(m[2])
-                ), "Negation violated.")
+                ), "NV: Negation violated.")
     rawCodeType = path.extname(filePath).substr(1) # strip the leading "."
     rawCode = fs.readFileSync(filePath, {encoding: "utf8"})
     baseDirectory = path.dirname(filePath)
@@ -208,7 +208,10 @@
             compileCommands[arg.split(":")[1]] = args[i + 1]
             i += 2
         else if arg == "-d"
-            assert(args[i + 1].search(/^(\/|\.\/|\.\.\/)/) == -1)
+            assert(
+                args[i + 1].search(/^(\/|\.\/|\.\.\/)/) == -1,
+                "Dummy can't start with `/`, `./` or `../`."
+            )
             dummies.push(args[i + 1])
             i += 2
         else if arg == "-i"
@@ -216,7 +219,7 @@
             i++
         else if arg == "-n"
             negation = args[i + 1].match(/^((?:\*\/)?)([^*]+)(\*?)$/)
-            assert(negation != null)
+            assert(negation != null, "Invalid negation pattern.")
             negations.push(negation)
             i += 2
         else if arg in ["--version", "-v"]
@@ -233,4 +236,9 @@
         writeOutput()
 catch ex
     console.error("Error: " + ex.message)
-    process.exit(1)
+    process.exit(
+        if ex.message.startsWith("NV: ")
+            64
+        else
+            1
+    )


tweak horizontal orientation

tweak horizontal orientation

--- bokehjs/src/coffee/models/annotations/legend.coffee
+++ bokehjs/src/coffee/models/annotations/legend.coffee
@@ -47,8 +47,7 @@
     else
       @legend_width = 0
       for name, width of @text_widths
-        @legend_width += (_.max([width, label_width]) + glyph_width + legend_spacing)
-      @legend_width += (1 + legend_names.length) * legend_spacing
+        @legend_width += (_.max([width, label_width]) + glyph_width + 3 * legend_spacing)
       @legend_height = @max_label_height + 2 * legend_spacing

     location = @mget('location')
@@ -131,7 +130,7 @@
         x2 = x1 + glyph_width
         y1 = @box_coords[1] + legend_spacing
         y2 = y1 + glyph_height
-        xoffset += @text_widths[legend_name] + legend_spacing + glyph_width
+        xoffset += @text_widths[legend_name] + 3*legend_spacing + glyph_width

       tx = x2 + legend_spacing
       ty = y1 + @max_label_height / 2.0


Add option to show dropdown while in palette (default to false)

--- src/controller.coffee
+++ src/controller.coffee
@@ -224,7 +224,10 @@

     # Instantiate an ICE editor view
     @view = new view.View extend_ @standardViewSettings, respectEphemeral: true
-    @paletteView = new view.View extend_ @standardViewSettings, {showDropdowns: false, respectEphemeral: true}
+    @paletteView = new view.View extend_ @standardViewSettings, {
+      showDropdowns: @options.showDropdownInPalette ? false
+      respectEphemeral: true
+    }
     @dragView = new view.View extend_ @standardViewSettings, respectEphemeral: false

     boundListeners = []


Add missing helper back in, closes #30

Add missing helper back in, closes #30

--- shared/start.coffee
+++ shared/start.coffee
@@ -16,5 +16,9 @@
   if Accounts.oauth && Accounts.oauth.serviceNames().length > 0
     "collapse"

+Handlebars.registerHelper 'otherLoginServices', ->
+  Accounts.oauth &&
+  Accounts.oauth.serviceNames().length > 0
+
 Handlebars.registerHelper 'loginServices', ->
   Accounts.oauth.serviceNames()


ability to scroll cursor to end of document

--- src/controller.coffee
+++ src/controller.coffee
@@ -2866,6 +2866,17 @@

   @mainScroller.scrollLeft = 0

+# Moves the cursor to the end of the document and scrolls it into position
+# (in block and text mode)
+Editor::scrollCursorToEndOfDocument = ->
+  pos = @tree.end
+  while pos && !@validCursorPosition(pos)
+    pos = pos.prev
+  @setCursor(pos)
+  @scrollCursorIntoPosition()
+  @aceEditor.scrollToLine @aceEditor.session.getLength()
+
+
 # Pressing the up-arrow moves the cursor up.
 hook 'keydown', 0, (event, state) ->
   if event.which is UP_ARROW_KEY


First cleaning up touch

First cleaning up touch

--- src/touch.coffee
+++ src/touch.coffee
@@ -91,27 +91,23 @@
     return unless t

     t.dx = (t.x - finger.x) || 0
-    t.dy = (t.y - finger.y) || 0
     finger.x = t.x
     finger.y = t.y
-    t.x0 = finger.x0
-    t.y0 = finger.y0

-    s = getOffset slider
+    sliderOffset = getOffset slider

-    dx = Math.abs(t.x0 - t.x)
-    dy = Math.abs(t.y0 - t.y)
+    dx = Math.abs(finger.x0 - t.x)
+    dy = Math.abs(finger.y0 - t.y)
+    isThumbing = dx > dy
+    isClosing = not isThumbing

-    if dx > dy
-      isThumbing = true
-      isClosing = false
+    if isThumbing
       transparent 1
-      translate slider, s + t.dx
+      translate slider, sliderOffset + t.dx
     else
-      isClosing = true
-      isThumbing = false
       transparent Math.round((1 - dy / env.h) * 100) / 100
     stop event
+    return

   end = (event) =>
     t = captureFinger getTouch event.changedTouches, finger.id
@@ -123,9 +119,9 @@
     if isThumbing
       addClass slider, choco_animated
       max = @storage.length() - 1 if max is -1
-      s = getOffset slider
-      s = touchShift t, (s / env.w)
-      @select squeeze s, 0, max
+      sliderOffset = getOffset slider
+      sliderOffset = touchShift t, (sliderOffset / env.w)
+      @select squeeze sliderOffset, 0, max
     if isClosing
       addClass overlay, choco_animated
       if opacity < 0.7
@@ -133,6 +129,8 @@
       else
         transparent 1
         isClosing = false
+    finger = {}
+    return

   addEvent slider, 'click', (event) ->
     stop event
@@ -149,7 +147,6 @@
   addEvent slider, 'webkitTransitionEnd', ->
     removeClass slider, choco_animated
     return
-

   addEvent overlay, 'transitionend', ->
     finish()


Added require function

Added require function

--- src/molo.coffee
+++ src/molo.coffee
@@ -47,6 +47,10 @@
       firstScriptElem.parentNode.insertBefore scriptElem, firstScriptElem

   moloFunc = (name, defines) ->
+    # TODO: Add module name always to queue and remove it if all dependencies have been met
+    # Currently, the module will only be added if it has any depedencies and cannot be
+    # resolved directly
+
     # Skip flag to skip the rest of the function
     skipFunc = false

@@ -177,16 +181,16 @@
   root.molo.plugins = {}

   # Shorthand function to load script files
-  root.molo.main = (dependencies) ->
+  root.molo.main = root.require = (dependencies, callback) ->
     dependencies = [dependencies] if typeof dependencies is 'string'

-    moloFunc(dependencies) if Array.isArray dependencies
+    moloFunc(dependencies, callback) if Array.isArray dependencies

   # Overwrite module only if it hasn't been defined
   # TODO: Reflect if this is necessary or even a good idea (ES 6 problems?)
   root.module or= moloFunc

-  # Compability to official definition ()
+  # Compability to official definition (https://github.com/amdjs/amdjs-api/wiki/AMD)
   root.define = (name, deps, defines) ->
     unless Array.isArray deps
       defines = deps


Model: added default title & thumbnail properties

--- coffee/src/model.coffee
+++ coffee/src/model.coffee
@@ -6,7 +6,9 @@
 class acorn.Model extends athena.lib.Model

   properties: => _.extend super,
-    'acornid': 'new'
+    'acornid':   'new'
+    'title':     'New Acorn'
+    'thumbnail': '/img/acorn.png'

   initialize: =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -19,5 +19,5 @@
     for property, data of I.activeTweens
       I[property] = 10

-  # We must always return self as the last line
-  return self
+
+


DataForm: use trash icon

DataForm: use trash icon

--- src/elements/DataForm/DataForm.coffee
+++ src/elements/DataForm/DataForm.coffee
@@ -202,7 +202,7 @@

 		if @_new_rows != "none"
 			trash = new CUI.defaults.class.Button
-				icon: "svg-close"
+				icon: "fa-trash-o"
 				appearance: "flat"
 				onMouseenter: =>
 					CUI.dom.addClass(hl, "cui-data-form-row--trash")


:lipstick:

:lipstick:

--- lib/git-plus.coffee
+++ lib/git-plus.coffee
@@ -221,11 +221,10 @@
     statusBar.getRightTiles().some ({item}) =>
       if item?.classList?.contains? 'git-view'
         $(item).find('.git-branch').on 'click', (e) =>
-          keys = atom.config.getSchema('git-plus').properties.general?.properties.newBranchKey?.enum
           {newBranchKey} = atom.config.get('git-plus.general')
           pressed = (key) -> e["#{key}Key"]
           if pressed newBranchKey
             atom.commands.dispatch(@workspace, 'git-plus:new-branch')
-          else unless(keys.some pressed)
+          else
             atom.commands.dispatch(@workspace, 'git-plus:checkout')
         return true


Added comments and updated formatting.

Added comments and updated formatting.

--- src/chalkboard.coffee
+++ src/chalkboard.coffee
@@ -21,11 +21,20 @@
 ##

 ##
+## @name Supported Tags
+## @description
+## [Wiki Page](https://github.com/adrianlee44/chalkboard/wiki/Supported-Tags)
+##
+
+##
 ## @name Getting Started
 ## @description
 ## The easiest way to use chalkboard will probably be to install it globally.
 ##
-## To do so, install the module with: `npm install -g chalkboard`
+## To do so, install the module with:
+## ```
+## npm install -g chalkboard
+## ```
 ##

 ##
@@ -110,7 +119,7 @@
 # Run through code and parse out all the comments
 # @param {String} code Source code to be parsed
 # @param {Object} lang Language settings for the file
-# @param {Object} options User settings
+# @param {Object} options User settings (default {})
 # @returns {Array} List of objects with all the comment block
 #
 parse = (code, lang, options = {})->
@@ -197,10 +206,12 @@
               type: argsMatch[1] or "undefined"
             switch key
               when "param"
-                argObject.description = argsMatch[3] or ""
-                argObject.name        = argsMatch[2]
+                argObject.description  = argsMatch[3] or ""
+                argObject.name         = argsMatch[2]
               when "returns"
                 argObject.description = argsMatch[2] or ""
+
+            argObject.description += "  \n" if argObject.description

             value = null

@@ -217,9 +228,10 @@
         do (argObject, multiLineKey, value, currentSection, definitions) ->
           object = if _(argObject).isEmpty() then currentSection else argObject
           keys   = multiLineKey.split(".")
+          value += "  \n" if value
           _setAttribute(object,
             keys[keys.length - 1],
-            "#{value}  \n",
+            value,
             definitions[keys[keys.length - 1]]
           )

@@ -255,7 +267,8 @@
 # @param {String} key Tag name
 # @param {String} value User input for the tag
 # @param {Boolean} newLine If a new line should be appended to the end
-# @param {Integer} headerLevel The header level of the section name
+#  (default true)
+# @param {Integer} headerLevel The header level of the section name (default 3)
 # @returns {String} Formatted markdown string based on key and value
 #
 _formatKeyValue = (key, value, newLine = true, headerLevel = 3) ->
@@ -280,8 +293,6 @@
       else
         output += "-   #{element}  \n"

-      output += "\n"
-
   else if _(value).isString()
     output += "#{value}"

@@ -328,7 +339,7 @@
       omitList.push "name"

     if section.description?
-      output += "#{section.description}\n"
+      output += "#{section.description}  \n"
       omitList.push "description"

     if section.type?


paragraph fix

paragraph fix

--- coffee/nodes.coffee
+++ coffee/nodes.coffee
@@ -34,7 +34,7 @@
     return node

    template: ->
-    @$.elem = @div ",node", null
+    @$.elem = @div ".node", null

    render: (options) ->
     Weya elem: options.elem, context: this, @template
@@ -78,7 +78,10 @@
     @_add new Text text: text

    template: ->
-    @$.elem = @span ".block", null
+    if @$.paragraph
+     @$.elem = @p ".paragraph", null
+    else
+     @$.elem = @span ".block", null

   class Article extends Node


added 3 more unicode chars

added 3 more unicode chars


Use infix.

Use infix.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -347,7 +347,7 @@
             res.send code
           if req.method.toUpperCase() isnt 'GET' then next()
           else
-            zappa_prefix = app.settings['zappa_prefix']
+            zappa_prefix = app.settings.zappa_prefix
             switch req.url
               when zappa_prefix+'/Zappa.js' then send client_bundled
               when zappa_prefix+'/Zappa-simple.js' then send client_bundle_simple
@@ -656,7 +656,7 @@
           body @body

   if io?
-    zappa_prefix = app.settings['zappa_prefix']
+    zappa_prefix = app.settings.zappa_prefix
     context.get zappa_prefix+'/socket/:channel_name/:socket_id', ->
       if @session?
         channel_name = @params.channel_name


use new api for poller

use new api for poller

handleTimeout was renamed add

--- server/controllers/konnectors.coffee
+++ server/controllers/konnectors.coffee
@@ -48,7 +48,7 @@
                 else
                     res.send 200
                     poller = require "../lib/konnector_poller"
-                    poller.handleTimeout date, req.konnector
+                    poller.add date, req.konnector

                     # Don't import data if a start date is defined
                     unless date?


Fix migration number

Fix migration number

--- server/startup/migrations/v36.coffee
+++ server/startup/migrations/v36.coffee
@@ -1,6 +1,6 @@
 url = Npm.require 'url'
 RocketChat.Migrations.add
-	version: 35
+	version: 36
 	up: ->
 		loginHeader = RocketChat.models.Settings.findOne _id: 'Layout_Login_Header'


:lipstick: Fix doc string

:lipstick: Fix doc string

--- src/decoration.coffee
+++ src/decoration.coffee
@@ -61,13 +61,16 @@

   # Essential: When the {Decoration} is updated via {Decoration::update}.
   #
-  # * `event` {Object}
-  #   * `oldProperties` {Object} the old parameters the decoration used to have
-  #   * `newProperties` {Object} the new parameters the decoration now has
+  # * `callback` {Function}
+  #   * `event` {Object}
+  #     * `oldProperties` {Object} the old parameters the decoration used to have
+  #     * `newProperties` {Object} the new parameters the decoration now has
   onDidChangeProperties: (callback) ->
     @emitter.on 'did-change-properties', callback

   # Essential: Invoke the given callback when the {Decoration} is destroyed
+  #
+  # * `callback` {Function}
   onDidDestroy: (callback) ->
     @emitter.on 'did-destroy', callback


Prepare for acplus update

Prepare for acplus update

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -52,7 +52,7 @@
       blacklist: ''
       # inclusionPriority: 1 #FIXME hack to prevent default provider in MD file
       # excludeLowerPriority: true
-      providerblacklist: '' #TODO Give the user the option to configure this.
+      providerblacklist: '' # Give the user the option to configure this.
       requestHandler: (options) =>
         prefix = @prefixForCursor(options.cursor, options.buffer)

@@ -61,19 +61,14 @@
         character before the part in which we're interested. Since this is the
         only case in which an `@` could be the second character, that's a simple
         way to test for it.
-
         (I put this here, and not in the `prefixForCursor` method because I want
         to keep that method as similar to the `AutocompleteManager` method of
         the same name as I can.)
         ###
         prefix = prefix[1..] if prefix[1] is '@'
-
         return if not prefix.length or prefix[0] is not '@'
-
         normalizedPrefix = prefix.normalize().replace(/^@/, '')
-
         words = fuzzaldrin.filter @possibleWords, normalizedPrefix, { key: 'author' }
-
         suggestions = for word in words
           {
             word: @resultTemplate.replace('[key]', word.key)
@@ -188,10 +183,12 @@

     # make title into titlecaps, trim length to 70 chars(ish) and add elipsis
     title = titlecaps(title)
-    l = if title.length > 70 then 70 else title.length
-    title = title.slice(0, l)
-    n = title.lastIndexOf(" ")
-    title = title.slice(0, n) + "..."
+    cutoff = 45
+    if title.length > cutoff
+      title = title.slice(0, cutoff)
+      n = title.lastIndexOf(" ")
+      title = title.slice(0, n) + "..."
+    return title

     # sugar function alternative
     # title.titleize().truncateOnWord 30, 'middle'


Match resource path case insensitively

Match resource path case insensitively

Sometimes on Windows the drive letter case doesn't seem to be consistent
depending on where the path was obtained from so maybe resource path pattern
case insensitively.

--- lib/reporter.coffee
+++ lib/reporter.coffee
@@ -35,11 +35,11 @@
       for line in coffeestack.convertStackTrace(error.stack).split('\n')
         if match = atLinePattern.exec(line)
           stacktrace.push
-            file: match[3].replace(///^#{atom.getLoadSettings().resourcePath}(\/|\\)///, '')
+            file: match[3].replace(///^#{atom.getLoadSettings().resourcePath}[\/\\]///i, '')
             method: match[2].replace(/^(HTMLDocument|HTML[^\.]*Element|Object)\./, '')
             columnNumber: parseInt(match[5])
             lineNumber: parseInt(match[4])
-            inProject: !/node_modules/.test(match[3])
+            inProject: not /node_modules/.test(match[3])
     else
       stacktrace.push
         file: source


Re-enabled tests

Re-enabled tests

--- config/run_tests.coffee
+++ config/run_tests.coffee
@@ -13,15 +13,15 @@
   queue.defer (callback) ->
     gutil.log 'Running Node.js tests'
     global.test_parameters = require '../test/parameters' # ensure that globals for the target backend are loaded
-    gulp.src('test/spec/sync/relational/schema.tests.coffee')
+    gulp.src('test/spec/**/*.tests.coffee')
       .pipe(mocha({}))
       .pipe es.writeArray (err, array) ->
         delete global.test_parameters # cleanup globals
         callback(err)

-  # # run browser tests
-  # queue.defer (callback) ->
-  #   gutil.log 'Running Browser tests'
-  #   karma(callback)
+  # run browser tests
+  queue.defer (callback) ->
+    gutil.log 'Running Browser tests'
+    karma(callback)

   queue.await callback


Updated Foundation JS to v5.5.0

Updated Foundation JS to v5.5.0

--- config/variables.coffee
+++ config/variables.coffee
@@ -20,7 +20,7 @@
         ]
         scripts: [
             '//cdn.jsdelivr.net/jquery/2.1.1/jquery.min.js'
-            '//cdn.jsdelivr.net/foundation/5.2.2/js/foundation.min.js'
+            '//cdn.jsdelivr.net/foundation/5.5.0/js/foundation.min.js'
             "#{cdn}/js/enactus.js"
         ]
         contacts: [


Make lazyConcat() a method of Sequence.

Make lazyConcat() a method of Sequence.

--- lib/sequence.coffee
+++ lib/sequence.coffee
@@ -185,9 +185,9 @@
     else
       Sequence.conj seq.first(), => @interleave__ other, seq.rest()

-  lazyConcat = (seq, next) ->
+  @operator 'lazyConcat', (seq, next) ->
     if seq
-      Sequence.conj seq.first(), -> lazyConcat seq.rest(), next
+      Sequence.conj seq.first(), => @lazyConcat__ seq.rest(), next
     else
       next()

@@ -195,13 +195,13 @@
     if @empty__ seq
       other
     else
-      lazyConcat seq, -> other
+      @lazyConcat__ seq, -> other

   @method 'flatten', (seq) ->
     if @empty__ seq
       null
     else if seq.first()
-      lazyConcat make(seq.first()), => @flatten__ seq.rest()
+      @lazyConcat__ make(seq.first()), => @flatten__ seq.rest()
     else if seq.rest()
       @flatten__ @dropWhile__(seq.rest(), (x) -> not make(x)?.first())
     else


consummate hit

consummate hit

--- lib/simulate.coffee
+++ lib/simulate.coffee
@@ -61,7 +61,7 @@
       dmg = []
       dmg.push damage
       critical = []
-      critical.push if kouku.api_ecl_flag[i] == 1 then HitType.Critical else HitType.Hit
+      critical.push if kouku.api_ecl_flag[i] == 1 then HitType.Critical else if damage == 0 then HitType.Miss else HitType.Hit
       list.push new Attack AttackType[AttackTypeMap[0]], null, enemyShip[i - 1], enemyShip[i - 1].maxHP, enemyShip[i - 1].nowHP, dmg, critical
       enemyShip[i - 1].nowHP -= damage
   if kouku.api_fdam?
@@ -71,7 +71,7 @@
       dmg = []
       dmg.push damage
       critical = []
-      critical.push if kouku.api_fcl_flag[i] == 1 then HitType.Critical else HitType.Hit
+      critical.push if kouku.api_fcl_flag[i] == 1 then HitType.Critical else if damage == 0 then HitType.Miss else HitType.Hit
       list.push new Attack AttackType[AttackTypeMap[0]], null, sortieShip[i - 1], sortieShip[i - 1].maxHP, sortieShip[i - 1].nowHP, dmg, critical
       sortieShip[i - 1].nowHP -= damage
       checkRepairItem sortieShip[i - 1]
@@ -81,15 +81,36 @@

 simulateSupportFire = (enemyShip, support) ->
   list = []
-  for damage, i in support
-    continue unless 1 <= i <= 6
-    damage = Math.floor(damage)
-    dmg = []
-    dmg.push damage
-    critical = []
-    critical.push HitType.Hit   # TODO: Aerial support may be cirtical attack.
-    list.push new Attack AttackType[AttackTypeMap[0]], null, enemyShip[i - 1], enemyShip[i - 1].maxHP, enemyShip[i - 1].nowHP, dmg, critical
-    enemyShip[i - 1].nowHP -= damage
+  if support.api_support_airatack?
+    for damage, i in support.api_support_airatack.api_stage3.api_edam
+      continue unless 1 <= i <= 6
+      damage = Math.floor(damage)
+      dmg = []
+      dmg.push damage
+      critical = []
+      if support.api_support_airatack.api_stage3.api_ecl_flag[i] == 2
+        critical.push HitType.Critical
+      else if support.api_support_airatack.api_stage3.api_ecl_flag[i] == 1
+        critical.push HitType.Hit
+      else
+        critical.push HitType.Miss
+      list.push new Attack AttackType[AttackTypeMap[0]], null, enemyShip[i - 1], enemyShip[i - 1].maxHP, enemyShip[i - 1].nowHP, dmg, critical
+      enemyShip[i - 1].nowHP -= damage
+  else if support.api_support_hourai?
+    for damage, i in support.api_support_hourai.api_damage
+      continue unless 1 <= i <= 6
+      damage = Math.floor(damage)
+      dmg = []
+      dmg.push damage
+      critical = []
+      if support.api_support_hourai.api_cl_list[i] == 2
+        critical.push HitType.Critical
+      else if support.api_support_hourai.api_cl_list[i] == 1
+        critical.push HitType.Hit
+      else
+        critical.push HitType.Miss
+      list.push new Attack AttackType[AttackTypeMap[0]], null, enemyShip[i - 1], enemyShip[i - 1].maxHP, enemyShip[i - 1].nowHP, dmg, critical
+      enemyShip[i - 1].nowHP -= damage
   list

 #api_opening_atack	：開幕雷撃戦 *スペルミスあり、注意
@@ -113,7 +134,12 @@
     # クリティカルフラグ 0=ミス, 1=命中, 2=クリティカル
     critical = []
     for crt, j in raigeki.api_fcl[i]
-      critical.push if crt == 2 then HitType.Critical else HitType.Hit
+      if crt == 2
+        critical.push HitType.Critical
+      else if crt == 1
+        critical.push HitType.Hit
+      else
+        critical.push HitType.Miss
     list.push new Attack AttackType[AttackTypeMap[0]], sortieShip[i - 1], enemyShip[target - 1], enemyShip[target - 1].maxHP, enemyShip[target - 1].nowHP, dmg, critical
     enemyShip[target - 1].nowHP -= damage
   # 雷撃ターゲット
@@ -125,7 +151,12 @@
     # api_cl_list		：クリティカルフラグ 0=ミス, 1=命中, 2=クリティカル
     critical = []
     for crt, j in raigeki.api_ecl[i]
-      critical.push if crt == 2 then HitType.Critical else HitType.Hit
+      if crt == 2
+        critical.push HitType.Critical
+      else if crt == 1
+        critical.push HitType.Hit
+      else
+        critical.push HitType.Miss
     list.push new Attack AttackType[AttackTypeMap[0]], enemyShip[i - 1], sortieShip[target - 1], sortieShip[target - 1].maxHP, sortieShip[target - 1].nowHP, dmg, critical
     sortieShip[target - 1].nowHP -= damage
     checkRepairItem sortieShip[target - 1]
@@ -147,7 +178,12 @@
       totalDamage += damage
     critical = []
     for crt, j in hougeki.api_cl_list[i]
-      critical.push if crt == 2 then HitType.Critical else HitType.Hit
+      if crt == 2
+        critical.push HitType.Critical
+      else if crt == 1
+        critical.push HitType.Hit
+      else
+        critical.push HitType.Miss
     target = hougeki.api_df_list[i][0] - 1
     attackType = 0
     if !isNight
@@ -200,12 +236,7 @@

     # Support battle
     if req.api_support_info?
-      if req.api_support_info.api_support_airatack?
-        sortieProgress.push new Stage StageType.Support, simulateSupportFire enemyShip, req.api_support_info.api_support_airatack.api_stage3.api_edam
-      else if req.api_support_info.api_support_hourai?
-        sortieProgress.push new Stage StageType.Support, simulateSupportFire enemyShip, req.api_support_info.api_support_hourai.api_damage
-      else
-        sortieProgress.push new Stage StageType.Support, simulateSupportFire enemyShip, req.api_support_info.api_damage
+      sortieProgress.push new Stage StageType.Support, simulateSupportFire enemyShip, req.api_support_info

     # Opening battle
     if req.api_opening_atack?


upgrade wakatime cli to v4.1.0

upgrade wakatime cli to v4.1.0

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -14,7 +14,7 @@
 request = require 'request'
 rimraf = require 'rimraf'

-latestCLIVersion = '4.0.16'
+latestCLIVersion = '4.1.0'

 module.exports =


find latest wakatime-cli version from GitHub instead of hard coding

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -13,8 +13,6 @@
 process = require 'child_process'
 request = require 'request'
 rimraf = require 'rimraf'
-
-latestCLIVersion = '4.1.8'

 packageVersion = null
 unloadHandler = null
@@ -207,13 +205,37 @@
       args = [cliLocation(), '--version']
       process.execFile(python, args, (error, stdout, stderr) ->
         if not error?
-          if stderr.trim() == latestCLIVersion
-            callback(true)
-          else
+          currentVersion = stderr.trim()
+          console.log 'Current wakatime-cli version is ' + currentVersion
+          console.log 'Checking for updates to wakatime-cli...'
+          getLatestCliVersion((latestVersion) ->
+            if currentVersion == latestVersion
+              console.log 'wakatime-cli is up to date.'
+              if callback?
+                callback(true)
+            else
+              console.log 'Found an updated wakatime-cli v' + latestVersion
+              if callback?
+                callback(false)
+          )
+        else
+          if callback?
             callback(false)
-        else
-          callback(false)
-      )
+      )
+  )
+
+getLatestCliVersion = (callback) ->
+  url = 'https://raw.githubusercontent.com/wakatime/wakatime/master/wakatime/__about__.py'
+  request.get(url, (error, response, body) ->
+    version = null
+    if !error and response.statusCode == 200
+      re = new RegExp(/__version_info__ = \('([0-9]+)', '([0-9]+)', '([0-9]+)'\)/g)
+      for line in body.split('\n')
+        match = re.exec(line)
+        if match?
+          version = match[1] + '.' + match[2] + '.' + match[3]
+    if callback?
+      callback(version)
   )

 cliLocation = () ->
@@ -309,10 +331,10 @@
 fileIsIgnored = (file) ->
   if endsWith(file, 'COMMIT_EDITMSG') or endsWith(file, 'PULLREQ_EDITMSG') or endsWith(file, 'MERGE_MSG') or endsWith(file, 'TAG_EDITMSG')
     return true
-  patterns = atom.config.get("wakatime.ignore")
+  patterns = atom.config.get('wakatime.ignore')
   ignore = false
   for pattern in patterns
-    re = new RegExp(pattern, "gi")
+    re = new RegExp(pattern, 'gi')
     if re.test(file)
       ignore = true
       break


:bug: Fix typo

:bug: Fix typo

--- src/application-delegate.coffee
+++ src/application-delegate.coffee
@@ -81,7 +81,7 @@

   setRepresentedDirectoryPaths: (paths) ->
     loadSettings = getWindowLoadSettings()
-    loadSettings['initialPaths'] = value
+    loadSettings['initialPaths'] = paths
     setWindowLoadSettings(loadSettings)

   setAutoHideWindowMenuBar: (autoHide) ->


Repo#sync restructured to make sure a 'stash pop' is never attempted if there was nothing to stash in the first place

--- src/repo.coffee
+++ src/repo.coffee
@@ -270,12 +270,17 @@
   sync: (remote_name, branch, callback) ->
     [remote_name, callback, branch] = ['origin', branch, remote_name] if !callback
     [remote_name, callback, branch] = ['origin', remote_name, []]     if !branch
-    @git "stash", {}, ["save"], (err) =>
+    @status (err, status) =>
       return callback err if err
-      @git "pull", {}, [remote_name, branch], (err) =>
+      @git "stash", {}, ["save"], (err) =>
         return callback err if err
-        @git "push", {}, [remote_name, branch], (err) =>
+        @git "pull", {}, [remote_name, branch], (err) =>
           return callback err if err
-          @git "stash", {}, "pop", (err) =>
+          @git "push", {}, [remote_name, branch], (err) =>
             return callback err if err
-            return callback null
+            if not status?.clean
+              @git "stash", {}, ["pop"], (err) =>
+                return callback err if err
+                return callback null
+            else
+              return callback null


Removed some redundant functions from the glsl library

--- src/compile.glsl.library.coffee
+++ src/compile.glsl.library.coffee
@@ -1,15 +1,15 @@
 glslLibrary =
   distanceFunctions:
-    sphereDist:
-      id: '_sphereDist'
-      returnType: 'float'
-      arguments: ['vec3', 'float']
-      code: do () ->
-        position = 'a'
-        radius = 'b'
-        [
-          "return length(#{position}) - #{radius};"
-        ]
+    #sphereDist:
+    #  id: '_sphereDist'
+    #  returnType: 'float'
+    #  arguments: ['vec3', 'float']
+    #  code: do () ->
+    #    position = 'a'
+    #    radius = 'b'
+    #    [
+    #      "return length(#{position}) - #{radius};"
+    #    ]
     #cornerDist:
     #  id: '_cornerDist'
     #  returnType: 'float'
@@ -74,18 +74,18 @@
           "}"
           "return min(#{chamferDistLength} - #{chamferRadius}, 0.0);"
         ]
-      intersectDist:
-        id: '__intersectDist'
-        arguments: ['float', 'float']
-        code: ["return max(a,b);"]
-      differenceDist:
-        id: '__differenceDist'
-        arguments: ['float','float']
-        code: ["return max(a,-b);"]
-      unionDist:
-        id: '__unionDist'
-        arguments: ['float','float']
-        code: ["return min(a,b);"]
+      #intersectDist:
+      #  id: '__intersectDist'
+      #  arguments: ['float', 'float']
+      #  code: ["return max(a,b);"]
+      #differenceDist:
+      #  id: '__differenceDist'
+      #  arguments: ['float','float']
+      #  code: ["return max(a,-b);"]
+      #unionDist:
+      #  id: '__unionDist'
+      #  arguments: ['float','float']
+      #  code: ["return min(a,b);"]
   compile: (libraryFunctions) ->
     code = ""
     for f,v of libraryFunctions


Export "Grammar" class

Export "Grammar" class

Allows other packages to inherit from Grammar in order to programmatically define language grammars

--- src/first-mate.coffee
+++ src/first-mate.coffee
@@ -1,3 +1,4 @@
 module.exports =
   ScopeSelector: require './scope-selector'
   GrammarRegistry: require './grammar-registry'
+  Grammar: require './grammar'


Fix late night glaring errors

Fix late night glaring errors

--- snippets/ember.cson
+++ snippets/ember.cson
@@ -126,8 +126,8 @@
     'prefix': 'filter'
     'leftLabel': 'Ember'
     'body': """
-      filter(${1:key}, function(#{2:item}${3:, index}) {
-        return #{2:item};
+      filter(${1:key}, function(${2:item}${3:, index}) {
+        return ${2:item};
       }));
     """

@@ -165,8 +165,8 @@
     'prefix': 'map'
     'leftLabel': 'Ember'
     'body': """
-      map(${1:key}, function(#{2:item}${3:, index}) {
-        return #{2:item};
+      map(${1:key}, function(${2:item}${3:, index}) {
+        return ${2:item};
       }));
     """


Fix tyop

Fix tyop

--- src/teak.coffee
+++ src/teak.coffee
@@ -240,7 +240,7 @@
         callback(false) if callback
     )

-  sendRequestOfOpporunity: (callback) ->
+  sendRequestOfOpportunity: (callback) ->
     if @requestOfOpportunity
       @internal_directRequest(@requestOfOpportunity, callback)
     @requestOfOpportunity = undefined


payment: only set current subscription if it exists

--- client/app/MainApp/payment/paymentcontroller.coffee
+++ client/app/MainApp/payment/paymentcontroller.coffee
@@ -106,7 +106,7 @@

       dash queue, ->
         form.setPlans plans
-        form.setCurrentSubscription subscription, forceUpgrade
+        form.setCurrentSubscription subscription, forceUpgrade  if subscription

     return form


Fix feed-related issues with daterange calendar

--- client/controllers/curatorInbox/curatorInbox.coffee
+++ client/controllers/curatorInbox/curatorInbox.coffee
@@ -77,16 +77,16 @@

   @autorun =>
     if @ready.get()
-      if @selectedFeedId.get() in getCustomFeedArray()
-        latestSourceDate = moment().format('L')
-        range = @dateRange.get()
-      else
-        latestSourceDate = @latestSourceDate.get()
-        range = @defaultDateRange
       Meteor.defer =>
-        createNewCalendar(latestSourceDate, range)
+        createNewCalendar(@latestSourceDate.get(), @dateRange.get())
         @$('[data-toggle="tooltip"]').tooltip
           container: 'body'
+
+  @autorun =>
+    if @selectedFeedId.get() in getCustomFeedArray()
+      latestSourceDate = moment().format('L')
+      range = @dateRange.get()
+      createNewCalendar(latestSourceDate, range)

   @autorun =>
     @filtering.set(true)


EditorPane: Use forward event.

EditorPane: Use forward event.

--- client/ide/lib/workspace/panes/ideeditorpane.coffee
+++ client/ide/lib/workspace/panes/ideeditorpane.coffee
@@ -99,7 +99,7 @@

         @file.emit 'FileActionHappened', { eventName, content, cursor, from }

-    ace.on 'FileContentRestored', => @file.emit 'FileContentRestored'
+    ace.forwardEvent @file, 'FileContentRestored'

     @file.on 'FileActionHappened', (data) =>
       { from, eventName, content } = data


no cache from server

no cache from server

--- content/pages/_components/ide/compiler/index.coffee
+++ content/pages/_components/ide/compiler/index.coffee
@@ -15,7 +15,7 @@

 exports.compile = (cache, files, complete) ->

-  if cache
+  if false
     compileServerSide files, (err, content) ->

       if content


Fixed bugs with deselectAll(). Added selectAll(). Fixed bug with setAllChecked().

--- src/tree.coffee
+++ src/tree.coffee
@@ -164,8 +164,14 @@

 deselectAll = (element) ->
   return unless isSelectable(element)
-  selectedIds = getTemplate(element).selection.removeAll()
-  handleSelectionResult(element, {added: selectedIds, removed: []})
+  deselectedIds = getTemplate(element).selection.removeAll()
+  handleSelectionResult(element, {added: [], removed: deselectedIds})
+
+selectAll = (element) ->
+  return unless isSelectable(element)
+  ids = getTemplate(element).collection.find().map (doc) -> doc._id
+  result = getTemplate(element).selection.setIds(ids)
+  handleSelectionResult(element, result)

 toggleSelection = (element, ids) ->
   return unless isSelectable(element)
@@ -274,7 +280,7 @@
   template = getTemplate(element)
   return unless isCheckable(element)
   if check
-    ids = template.collection.find().forEach (doc) -> doc._id
+    ids = template.collection.find().map (doc) -> doc._id
     changedIds = template.check.setIds(ids).added
     handleCheckResult(element, {added: changedIds, removed: []})
   else
@@ -556,6 +562,7 @@
   deselectNode: deselectNode
   setSelectedIds: setSelectedIds
   getSelectedIds: getSelectedIds
+  selectAll: selectAll
   deselectAll: deselectAll
   addSelection: addSelection
   removeSelection: removeSelection


Fixed an issue causing a collision when two trees attempt to load at the same time.

--- src/tree.coffee
+++ src/tree.coffee
@@ -30,7 +30,8 @@
   items = @items
   settings = getSettings()
   template = @
-  refreshTree(@)
+  @refreshTree = _.debounce(loadTree, 100)
+  @refreshTree(@)

 TemplateClass.destory = ->
   _.each @checkboxes, ($checkbox) ->
@@ -70,8 +71,6 @@
   template.readyDf.resolve()
   setUpReactiveUpdates(template)

-refreshTree = _.debounce(loadTree, 100)
-
 setUpReactiveUpdates = (template) ->
   return if template.isReactiveSetup

@@ -95,7 +94,7 @@
           # There is a significant memory leak caused by re-loading the entire tree after each
           # change (e.g. if a node is added to the root node). Hence, we recreate the entire tree
           # just once after all updates are complete.
-          refreshTree(template)
+          template.refreshTree(template)
           return
         sortResults = getSortedIndex(template, newDoc)
         nextSiblingNode = sortResults.nextSiblingNode


Made stroke relative to radius

Made stroke relative to radius

--- core/app/backbone/views/base_fact_wheel_view.coffee
+++ core/app/backbone/views/base_fact_wheel_view.coffee
@@ -35,6 +35,12 @@
     @options = $.extend(true, {}, BaseFactWheelView.prototype.defaults, @defaults, options)
     @opinionTypeRaphaels = {}

+  defaultStrokeWidth: ->
+    3/5 * @defaults.radius
+
+  hoverStrokeWidth: ->
+    @defaultStrokeWidth() + 2
+
   maxStrokeWidth: -> Math.max(@defaultStrokeWidth(), @hoverStrokeWidth())

   onRender: ->
@@ -91,7 +97,7 @@
       # Our custom arc attribute
       arc: arc
       stroke: @options.opinionStyles[opinionType.type].color
-      "stroke-width": @options.defaultStroke.width
+      "stroke-width": @defaultStrokeWidth()
       opacity: opacity

     # Bind Mouse Events on the path
@@ -150,7 +156,7 @@
     destinationOpacity = @options.hoverStroke.opacity
     destinationOpacity = @options.userOpinionStroke.opacity  if opinionType.is_user_opinion
     path.animate(
-      "stroke-width": @options.hoverStroke.width
+      "stroke-width": @hoverStrokeWidth()
       opacity: destinationOpacity
     , 200, "<>")

@@ -158,7 +164,7 @@
     destinationOpacity = @options.defaultStroke.opacity
     destinationOpacity = @options.userOpinionStroke.opacity  if opinionType.is_user_opinion
     path.animate(
-      "stroke-width": @options.defaultStroke.width
+      "stroke-width": @defaultStrokeWidth()
       opacity: destinationOpacity
     , 200, "<>")


namespace the resize event

namespace the resize event

--- modules/plot/main/coffee/Expose.coffee
+++ modules/plot/main/coffee/Expose.coffee
@@ -12,7 +12,7 @@
   selection = new hx.detached 'div'
   graph = new Graph selection.node(), options
   # There is no point rendering it now, the selection is of zero size.
-  selection.on 'resize', -> graph.render()
+  selection.on 'resize', 'hx.plot', -> graph.render()
   selection

 hx.LineSeries = LineSeries