add support to parsers #15

add support to parsers #15

--- lib/linter-js-standard.coffee
+++ lib/linter-js-standard.coffee
@@ -13,10 +13,6 @@
   # The syntax that the linter handles. May be a string or
   # list/tuple of strings. Names should be all lowercase.
   @syntax: ['source.js', 'source.js.jsx', 'source.js.jquery']
-
-  # A string, list, tuple or callable that returns a string, list or tuple,
-  # containing the command line (with arguments) used to lint.
-  cmd: ['cmd', '--verbose']

   linterName: 'js-standard'

@@ -35,6 +31,10 @@
   constructor: (editor) ->
     super(editor)

+    # A string, list, tuple or callable that returns a string, list or tuple,
+    # containing the command line (with arguments) used to lint.
+    @cmd = ['cmd', '--verbose']
+
     file = editor?.buffer.file
     @filePath = file?.path

@@ -52,9 +52,16 @@
     if typeof config == 'undefined'
       return

+    styleSettings =
+      standard: pkgConfig(null, { cwd: @filePath, root: 'standard' })
+      semistandard: pkgConfig(null, { cwd: @filePath, root: 'semistandard' })
+
+    # see if setting checkStyleDevDependencies is true
+    # if not fallback to style value to decide which bin
+    # we should use
     if config.checkStyleDevDependencies
       devDeps = pkgConfig(null, { cwd: @filePath, root: 'devDependencies' })
-      @executablePath = @formatDevDepsExecPath(devDeps)
+      @executablePath = @formatDevDepsExecPath(devDeps, styleSettings)
     else
       if config.style == 'standard'
         @linterName = 'js-standard'
@@ -62,6 +69,15 @@
       else
         @linterName = 'js-semistandard'
         @executablePath = binPath.semiStandard
+
+    # see if setting honorStyleSettings is true
+    if config.honorStyleSettings
+      # If linter is standard and parser is present
+      # else if linter is semiStandard and parser is present
+      if @linterName == 'js-standard' and styleSettings.standard.parser
+        @cmd.push '--parser', styleSettings.standard.parser
+      else if @linterName == 'js-standard' and styleSettings.semistandard.parser
+        @cmd.push '--parser', styleSettings.semistandard.parser

     # Check if executablePath is defined,
     # if isn't defined it means the file
@@ -74,7 +90,7 @@
           installed properly with linter-js-standard,
           please re-install the plugin.'

-  formatDevDepsExecPath: (devDeps) ->
+  formatDevDepsExecPath: (devDeps, styleSettings) ->
     # Default value
     execPath = false

@@ -96,24 +112,16 @@
         @linterName = 'js-standard'
         execPath = binPath.standard

-        # Get standard property from package.json
-        options = { cwd: @filePath, root: 'standard' }
-        standardOpts = pkgConfig(null, options) or {}
-
         # If ignore glob patterns are present
-        if standardOpts.ignore
+        if styleSettings.standard.ignore
           ignoreGlobPatterns = []
-          ignoreGlobPatterns = ignoreGlobPatterns.concat semistandardOpts.ignore
+          ignoreGlobPatterns = ignoreGlobPatterns.concat styleSettings.standard.ignore

           testGlobPatterns = ignoreGlobPatterns.some (pattern) ->
             return minimatch(relativeFilePath, pattern)

           if testGlobPatterns
             execPath = false
-
-        # If parser is present
-        if standardOpts.parser
-          @cmd.push '--parser', standardOpts.parser

       else
         # Set execPath to semistandard bin
@@ -123,24 +131,16 @@
         @linterName = 'js-semistandard'
         execPath = binPath.semiStandard

-        # Get semistandard property from package.json
-        options = { cwd: @filePath, root: 'semistandard' }
-        semistandardOpts = pkgConfig(null, options) or {}
-
         # If ignore glob patterns are present
-        if semistandardOpts.ignore
+        if styleSettings.semistandard.ignore
           ignoreGlobPatterns = []
-          ignoreGlobPatterns = ignoreGlobPatterns.concat semistandardOpts.ignore
+          ignoreGlobPatterns = ignoreGlobPatterns.concat styleSettings.semistandard.ignore

           testGlobPatterns = ignoreGlobPatterns.some (pattern) ->
             return minimatch(relativeFilePath, pattern)

           if testGlobPatterns
             execPath = false
-
-        # If parser is present
-        if semistandardOpts.parser
-          @cmd.push '--parser', semistandardOpts.parser

     execPath


add try-catch block to fix issue #17

add try-catch block to fix issue #17

--- lib/linter-js-standard.coffee
+++ lib/linter-js-standard.coffee
@@ -110,7 +110,10 @@
     # This is the absolute path
     # of the project path relative
     # to the package.json
-    projectPath = findRoot(@filePath)
+    try
+      projectPath = findRoot(@filePath)
+    catch
+      return

     # Get relative path of the filePath
     relativeFilePath = @filePath.replace(projectPath, '')


Improve model primaryKey attribute accessor

* Add a check to ensure we can parseInt the pkey value without degrading it
* This resolves issues where the pkey is a string such as '00000' getting parseInt to '0'

Closes #442.

--- src/model/model.coffee
+++ src/model/model.coffee
@@ -264,7 +264,7 @@
         @get(primaryKey)
     set: (k, v) ->
       # naively coerce string ids into integers
-      if typeof v is "string" and v.match(/[^0-9]/) is null
+      if (typeof v is "string") and (v.match(/[^0-9]/) is null) and ("#{parseInt(v, 10)}" is v)
         v = parseInt(v, 10)

       primaryKey = @constructor.primaryKey


minor change

minor change

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -178,7 +178,7 @@
         this.interceptExceptions.call this, initializer
         return asciify branding..., (error, banner) =>
             util.puts banner.toString().blue unless error
-            identify = "Running ver %s, codename: %s"
+            identify = "Running version %s, codename: %s"
             using = "Using %s class as the kernel type"
             logger.info identify.underline, types...
             logger.info using, @constructor.name.bold


Concatenate @rootPath with filepath before passing to git-utils `relativize`

--- src/path-filter.coffee
+++ src/path-filter.coffee
@@ -115,7 +115,7 @@
     return result

   isPathExcludedByGit: (filepath) ->
-    @repo?.isIgnored(@repo.relativize(filepath))
+    @repo?.isIgnored(@repo.relativize(path.join(@rootPath, filepath)))

   # Given an array of `globalExclusions`, filter out any which have an
   # `inclusion` defined for a subdirectory


fix parser to handle new commands

fix parser to handle new commands

--- src/path-parser.coffee
+++ src/path-parser.coffee
@@ -50,6 +50,26 @@
         currentPoint = slicePoint(params, 0)
         result.nodes.push(new Curve.Node(currentPoint))

+      when 'H', 'h'
+        moveNode = makeMoveNode()
+        firstNode = moveNode if moveNode
+
+        params = command.parameters
+        params = makeAbsolute(params) if command.type == 'h'
+
+        currentPoint = [params[0], currentPoint[1]]
+        result.nodes.push(new Curve.Node(currentPoint))
+
+      when 'V', 'v'
+        moveNode = makeMoveNode()
+        firstNode = moveNode if moveNode
+
+        params = command.parameters
+        params = makeAbsolute(params) if command.type == 'v'
+
+        currentPoint = [currentPoint[0], params[0]]
+        result.nodes.push(new Curve.Node(currentPoint))
+
       when 'C', 'c'
         moveNode = makeMoveNode()
         firstNode = moveNode if moveNode
@@ -83,6 +103,7 @@

 # Returns a list of svg commands with their parameters.
 groupCommands = (pathTokens) ->
+  console.log 'grouping tokens', pathTokens
   commands = []
   for i in [0...pathTokens.length]
     token = pathTokens[i]
@@ -100,7 +121,7 @@
       else
         break

-    #console.log command.type, command
+    console.log command.type, command
     commands.push(command)

   commands
@@ -108,7 +129,7 @@
 # Breaks pathString into tokens
 lexPath = (pathString) ->
   numberMatch = '-0123456789.'
-  separatorMatch = ' ,'
+  separatorMatch = ' ,\n\t'

   tokens = []
   currentToken = null
@@ -125,6 +146,7 @@
   for ch in pathString
     if numberMatch.indexOf(ch) > -1
       saveCurrentTokenWhenDifferentThan(NUMBER)
+      saveCurrentToken() if ch == '-'

       currentToken = {type: NUMBER, string: []} unless currentToken
       currentToken.string.push(ch)


Working on functions

Working on functions

--- grammars/d.cson
+++ grammars/d.cson
@@ -306,9 +306,363 @@
         'name': 'keyword.other.special.d'
       ]
     }
-    #
-    # Parameters
-    #
+
+
+
+    #
+    # Expressions
+    #
+    # We cheat a bit here, since highlighting expressions is overkill, and in
+    # any case we want lexical entities like strings and numbers to be
+    # clearly visible.  So we group expressions into a meta class for the
+    # purposes of other entities.  Expression entities themselves just highlight
+    # lexical elements as well as the base expression operators, and a few
+    # others.
+    #
+    'expression': {
+      'patterns': [
+          { 'include': '#operator' },
+          {
+            'match': ',',
+            'name': 'keyword.operator.comma.d'
+          }
+          { 'include': '#lexical' },
+      ]
+    },
+    'conditional-expression': {
+      'patterns': [
+        {
+          # ternary
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          # logical
+          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
+          'name': 'keyword.operator.logical.d'
+        },
+        { 'include': '#lexical' }
+      ]
+    }
+    'operator': {
+      'patterns': [
+        {
+          # assign
+          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
+          'name': 'keyword.operator.assign.d'
+        },
+        {
+          # ternary
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          # logical
+          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
+          'name': 'keyword.operator.logical.d'
+        },
+        {
+          # bitwise
+          'match': '\\||\\^|&|<<|>>|>>>',
+          'name': 'keyword.operator.bitwise.d'
+        },
+        {
+          # arithmetic
+          'match': '\\+|-|\\*|/|%',
+          'name': 'keyword.operator.arithmetic.d'
+        },
+        {
+          # unary
+          'match': '[=~]\s*(&|\\+\\+|\\--|\\*|-|\\+|!)',
+          'name': 'keyword.operator.unary.d'
+        },
+        {
+          # misc
+          'match': '~|(\\s+!in\\s+)|(\\s+in\\s+)',
+          'name': 'keyword.operator.misc.d'
+        },
+        {
+          # primary expressions
+          'match': 'this|super|null|true|false|$',
+          'name': 'constant.language.primary.d'
+        }
+
+      ]
+    },
+    'assign-operator': {
+      'patterns': [
+        {
+          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
+          'name': 'keyword.operator.assign.d'
+        },
+        { 'include': '#expression' },
+      ]
+    },
+    'ternary-operator': {
+      'patterns:': [
+        {
+          'match': '(\\?|:)',
+          'name': 'keyword.operator.ternary.d'
+        },
+        {
+          'include': '#expression',
+        }
+      ]
+    },
+    #
+    # Template
+    #
+
+
+
+
+
+    #
+    # Declarations
+    #
+    'declaration': {
+      'patterns': [
+        { 'include': '#alias-declaration' },
+        { 'include': '#aggregate-declaration' },
+        { 'include': '#enum-declaration' },
+        { 'include': '#import-declaration' },
+        { 'include': '#decl' }
+      ]
+    },
+    'alias-declaration': {
+      'patterns': [
+        {
+          'match': 'alias',
+          'name': 'keyword.alias.d'
+        },
+        { 'include': '#storage-class' },
+        { 'include': '#basic-type' },
+        { 'include': '#declarator' },
+        { 'include': '#alias-declaration-x' }
+      ]
+    },
+    #
+    # Initializers
+    #
+    'initializer': {
+      'patterns': [
+        { 'include': '#void-initializer' },
+        { 'include': '#non-void-initializer' }
+      ]
+    },
+    'void-initializer': {
+      'patterns': [
+        'match': 'void',
+        'name': 'support.type.void.d'
+      ]
+    },
+    'non-void-initializer': {
+      'patterns': [
+        { 'include': '#exp-initializer' },
+        { 'include': '#array-initializer' },
+        { 'include': '#struct-initializer' }
+      ]
+    },
+    'exp-initializer': {
+      'patterns': [
+        { 'include': '#expression' }
+      ]
+    },
+    'array-initializer': {
+      'patterns': [
+        'begin': '\\[',
+        'beginCaptures': { '0': { 'name': 'support.type.array.initializer.begin.d' } },
+        'end': '\\]',
+        'endCaptures': {'0': { 'name': 'support.type.array.initializer.end.d' } },
+        'patterns': [
+          {
+            'match': ',',
+            'name': 'keyword.operator.comma.d'
+          },
+          { 'include': '#array-member-initialization' },
+        ]
+      ]
+    },
+    'array-member-initializations': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#array-member-initialization' },
+      ]
+    },
+    'array-member-initialization': {
+      'patterns': [
+        { 'include': '#non-void-initializer' },
+        { 'include': '#expression' },
+        {
+          'match': ':',
+          'name': 'support.type.tag.d'
+        }
+      ]
+    },
+    'struct-initializer': {
+      'patterns': [
+        'begin': '\\{',
+        'beginCaptures': { '0': { 'name': 'support.type.struct.initializer.begin.d' } },
+        'end': '\\}',
+        'endCaptures': { '0': { 'name': 'support.type.struct.initializer.end.d' } },
+        'patterns': [
+          { 'include': '#struct-member-initializers' }
+        ]
+      ]
+    }
+    'struct-member-initializers': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.d'
+        },
+        { 'include': '#struct-member-initializer' },
+      ]
+    },
+    'struct-member-initializer': {
+      'patterns': [
+        { 'include': '#non-void-initializer' },
+        { 'include': '#identifier' },
+        {
+          'match': ':',
+          'name': 'support.type.tag.d'
+        },
+      ]
+    },
+
+    #
+    # Assembly (Iasm)
+    #
+
+
+    ###########################################################################
+    # Type
+    ###########################################################################
+
+
+
+    ###########################################################################
+    # Expression
+    ###########################################################################
+
+    ###########################################################################
+    # Statement
+    ###########################################################################
+
+
+
+
+    ###########################################################################
+    # Iasm
+    ###########################################################################
+
+    'asm-statement': {
+      'patterns': [
+        'begin': 'asm\\s*\\{',
+        'end': '\\}',
+        'beginCaptures': { '0': { 'name': 'keyword.control.asm.begin.d' } },
+        'endCaptures': { '0': { 'name': 'keyword.control.asm.end.d' } },
+        'patterns': [
+          { 'include': '#asm-instruction' },
+        ],
+      ]
+    },
+    'asm-instruction': {
+      'patterns': [
+        {
+          'match': 'align|even|naked|db|ds|di|dl|df|dd|de|:',
+          'name': 'keyword.asm-instruction.d',
+        },
+        { 'include': '#operands' },
+        { 'include': '#integer-expression' }
+      ]
+    },
+    'operands': {
+      'patterns': [
+        {
+          'match': ',',
+          'name': 'keyword.operator.comma.assembly.d'
+        }
+        { 'include': '#asm-exp' },
+      ]
+    },
+    'asm-exp': {
+      'patterns': [
+        {
+          'match': '\\?|:',
+          'name': 'keyword.operator.ternary.assembly.d',
+        },
+        {
+          'match': '\\]|\\[',
+          'name': 'keyword.operator.bracket.assembly.d',
+        },
+        {
+          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%|~|!',
+          'name': 'keyword.operator.assembly.d'
+        },
+        { 'include': '#asm-una-exp' },
+      ]
+    }
+    'integer-expression': {
+      'patterns': [
+        { 'include': '#integer-literal' },
+        { 'include': '#identifier' },
+      ]
+    },
+    'asm-una-exp': {
+      'patterns': [
+        { 'include': '#asm-type-prefix' },
+        {
+          'match': 'offsetof|seg',
+          'name': 'support.type.assembly.d'
+        },
+        { 'include': '#asm-primary-expression' },
+      ]
+    },
+    'asm-primary-expression': {
+      'patterns': [
+        { 'include': '#integer-literal' },
+        { 'include': '#float-literal' },
+        {
+          'match': '__LOCAL_SIZE',
+          'name': 'constant.language.assembly.d'
+        },
+        { 'include': '#register' },
+        { 'include': '#register-64' },
+        { 'include': '#identifier' }
+      ]
+    },
+    'asm-type-prefix': {
+      'patterns': [
+        'match': '(near\\s+ptr)|(far\\s+ptr)|(byte\\s+ptr)|(short\\s+ptr)|(int\\s+ptr)|(word\\s+ptr)|(dword\\s+ptr)|(qword\\s+ptr)|(float\\s+ptr)|(double\\s+ptr)|(real\\s+ptr)',
+        'name': 'support.type.asm-type-prefix.d',
+      ]
+    },
+    'register': {
+      'patterns': [
+        'match': 'XMM0|XMM1|XMM2|XMM3|XMM4|XMM5|XMM6|XMM7|MM0|MM1|MM2|MM3|MM4|MM5|MM6|MM7|ST\\(0\\)|ST\\(1\\)|ST\\(2\\)|ST\\(3\\)|ST\\(4\\)|ST\\(5\\)|ST\\(6\\)|ST\\(7\\)|ST|TR1|TR2|TR3|TR4|TR5|TR6|TR7|DR0|DR1|DR2|DR3|DR4|DR5|DR6|DR7|CR0|CR2|CR3|CR4|EAX|EBX|ECX|EDX|EBP|ESP|EDI|ESI|AL|AH|AX|BL|BH|BX|CL|CH|CX|DL|DH|DX|BP|SP|DI|SI|ES|CS|SS|DS|GS|FS',
+        'name': 'storage.type.assembly.register.d',
+      ]
+    },
+    'register-64': {
+      'patterns': [
+        'match': 'RAX|RBX|RCX|RDX|BPL|RBP|SPL|RSP|DIL|RDI|SIL|RSI|R8B|R8W|R8D|R8|R9B|R9W|R9D|R9|R10B|R10W|R10D|R10|R11B|R11W|R11D|R11|R12B|R12W|R12D|R12|R13B|R13W|R13D|R13|R14B|R14W|R14D|R14|R15B|R15W|R15D|R15|XMM8|XMM9|XMM10|XMM11|XMM12|XMM13|XMM14|XMM15|YMM0|YMM1|YMM2|YMM3|YMM4|YMM5|YMM6|YMM7|YMM8|YMM9|YMM10|YMM11|YMM12|YMM13|YMM14|YMM15',
+        'name': 'storage.type.assembly.register-64.d'
+      ]
+    },
+    ###########################################################################
+    # Declaration
+    ###########################################################################
+
+
+
+
+    ###########################################################################
+    # Function
+    ###########################################################################
     'parameters': {
       'patterns': [
         {
@@ -344,6 +698,11 @@
     },
     'in-out': {
       'patterns': [
+        { 'include': '#in-out-x' }
+      ]
+    },
+    'in-out-x': {
+      'patterns': [
         {
           'match': 'auto|final|in|out|lazy|ref|scope',
           'name': 'storage.type.modifier.inout.d'
@@ -351,228 +710,24 @@
         { 'include': '#type-ctor' }
       ]
     }
-
-    #
-    # Expressions
-    #
-    # We cheat a bit here, since highlighting expressions is overkill, and in
-    # any case we want lexical entities like strings and numbers to be
-    # clearly visible.  So we group expressions into a meta class for the
-    # purposes of other entities.  Expression entities themselves just highlight
-    # lexical elements as well as the base expression operators, and a few
-    # others.
-    #
-    'expression': {
-      'patterns': [
-          { 'include': '#operator' },
-          {
-            'match': ',',
-            'name': 'keyword.operator.comma.d'
-          }
-          { 'include': '#lexical' },
-      ]
-    },
-    'conditional-expression': {
-      'patterns': [
-        {
-          # ternary
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          # logical
-          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
-          'name': 'keyword.operator.logical.d'
-        },
-        { 'include': '#lexical' }
-      ]
-    }
-    'operator': {
-      'patterns': [
-        {
-          # assign
-          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
-          'name': 'keyword.operator.assign.d'
-        },
-        {
-          # ternary
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          # logical
-          'match': '!<>=|!<=|!>=|<>=|!<>|<=|>=|<>|!>|!<|==|!=|&&|\\|\\||<|>',
-          'name': 'keyword.operator.logical.d'
-        },
-        {
-          # bitwise
-          'match': '\\||\\^|&|<<|>>|>>>',
-          'name': 'keyword.operator.bitwise.d'
-        },
-        {
-          # arithmetic
-          'match': '\\+|-|\\*|/|%',
-          'name': 'keyword.operator.arithmetic.d'
-        },
-        {
-          # unary
-          'match': '[=~]\s*(&|\\+\\+|\\--|\\*|-|\\+|!)',
-          'name': 'keyword.operator.unary.d'
-        },
-        {
-          # misc
-          'match': '~|(\\s+!in\\s+)|(\\s+in\\s+)',
-          'name': 'keyword.operator.misc.d'
-        },
-        {
-          # primary expressions
-          'match': 'this|super|null|true|false|$',
-          'name': 'constant.language.primary.d'
-        }
-
-      ]
-    },
-    'assign-operator': {
-      'patterns': [
-        {
-          'match': '=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|~=|<<=|>>=|>>>=|^^=',
-          'name': 'keyword.operator.assign.d'
-        },
-        { 'include': '#expression' },
-      ]
-    },
-    'ternary-operator': {
-      'patterns:': [
-        {
-          'match': '(\\?|:)',
-          'name': 'keyword.operator.ternary.d'
-        },
-        {
-          'include': '#expression',
-        }
-      ]
-    },
-    #
-    # Template
-    #
-
-
-
-
-
-    #
-    # Declarations
-    #
-    'declaration': {
-      'patterns': [
-        { 'include': '#alias-declaration' },
-        { 'include': '#aggregate-declaration' },
-        { 'include': '#enum-declaration' },
-        { 'include': '#import-declaration' },
-        { 'include': '#decl' }
-      ]
-    },
-    'alias-declaration': {
-      'patterns': [
-        {
-          'match': 'alias',
-          'name': 'keyword.alias.d'
-        },
-        { 'include': '#storage-class' },
-        { 'include': '#basic-type' },
-        { 'include': '#declarator' },
-        { 'include': '#alias-declaration-x' }
-      ]
-    },
-    #
-    # Initializers
-    #
-    'initializer': {
-      'patterns': [
-        { 'include': '#void-initializer' },
-        { 'include': '#non-void-initializer' }
-      ]
-    },
-    'void-initializer': {
-      'patterns': [
-        'match': 'void',
-        'name': 'support.type.void.d'
-      ]
-    },
-    'non-void-initializer': {
-      'patterns': [
-        { 'include': '#exp-initializer' },
-        { 'include': '#array-initializer' },
-        { 'include': '#struct-initializer' }
-      ]
-    },
-    'exp-initializer': {
-      'patterns': [
-        { 'include': '#expression' }
-      ]
-    },
-    'array-initializer': {
-      'patterns': [
-        'begin': '\\[',
-        'beginCaptures': { '0': { 'name': 'support.type.array.initializer.begin.d' } },
-        'end': '\\]',
-        'endCaptures': {'0': { 'name': 'support.type.array.initializer.end.d' } },
-        'patterns': [
-          {
-            'match': ',',
-            'name': 'keyword.operator.comma.d'
-          },
-          { 'include': '#array-member-initialization' },
-        ]
-      ]
-    },
-    'array-member-initializations': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#array-member-initialization' },
-      ]
-    },
-    'array-member-initialization': {
-      'patterns': [
-        { 'include': '#non-void-initializer' },
-        { 'include': '#expression' },
-        {
-          'match': ':',
-          'name': 'support.type.tag.d'
-        }
-      ]
-    },
-    'struct-initializer': {
-      'patterns': [
-        'begin': '\\{',
-        'beginCaptures': { '0': { 'name': 'support.type.struct.initializer.begin.d' } },
-        'end': '\\}',
-        'endCaptures': { '0': { 'name': 'support.type.struct.initializer.end.d' } },
-        'patterns': [
-          { 'include': '#struct-member-initializers' }
-        ]
-      ]
-    }
-    'struct-member-initializers': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.d'
-        },
-        { 'include': '#struct-member-initializer' },
-      ]
-    },
-    'struct-member-initializer': {
-      'patterns': [
-        { 'include': '#non-void-initializer' },
-        { 'include': '#identifier' },
-        {
-          'match': ':',
-          'name': 'support.type.tag.d'
-        },
+    'function-attributes': {
+      'patterns': [
+
+      ]
+    },
+    'function-attribute': {
+      'patterns': [
+
+      ]
+    },
+    'member-function-attributes': {
+      'patterns': [
+
+      ]
+    },
+    'member-function-attribute': {
+      'patterns': [
+
       ]
     },
     'default-initializer-expression': {
@@ -581,139 +736,94 @@
         { 'include': '#special-keyword' }
       ]
     },
-    #
-    # Assembly (Iasm)
-    #
-
-
-    ###########################################################################
-    # Type
-    ###########################################################################
-
-
-
-    ###########################################################################
-    # Expression
-    ###########################################################################
-
-    ###########################################################################
-    # Statement
-    ###########################################################################
-
-
-
-
-    ###########################################################################
-    # Iasm
-    ###########################################################################
-
-    'asm-statement': {
-      'patterns': [
-        'begin': 'asm\\s*\\{',
-        'end': '\\}',
-        'beginCaptures': { '0': { 'name': 'keyword.control.asm.begin.d' } },
-        'endCaptures': { '0': { 'name': 'keyword.control.asm.end.d' } },
-        'patterns': [
-          { 'include': '#asm-instruction' },
-        ],
-      ]
-    },
-    'asm-instruction': {
-      'patterns': [
-        {
-          'match': 'align|even|naked|db|ds|di|dl|df|dd|de|:',
-          'name': 'keyword.asm-instruction.d',
-        },
-        { 'include': '#operands' },
-        { 'include': '#integer-expression' }
-      ]
-    },
-    'operands': {
-      'patterns': [
-        {
-          'match': ',',
-          'name': 'keyword.operator.comma.assembly.d'
-        }
-        { 'include': '#asm-exp' },
-      ]
-    },
-    'asm-exp': {
-      'patterns': [
-        {
-          'match': '\\?|:',
-          'name': 'keyword.operator.ternary.assembly.d',
-        },
-        {
-          'match': '\\]|\\[',
-          'name': 'keyword.operator.bracket.assembly.d',
-        },
-        {
-          'match': '>>>|\\|\\||&&|==|!=|<=|>=|<<|>>|\\||\\^|&|<|>|\\+|-|\\*|/|%|~|!',
-          'name': 'keyword.operator.assembly.d'
-        },
-        { 'include': '#asm-una-exp' },
-      ]
-    }
-    'integer-expression': {
-      'patterns': [
-        { 'include': '#integer-literal' },
-        { 'include': '#identifier' },
-      ]
-    },
-    'asm-una-exp': {
-      'patterns': [
-        { 'include': '#asm-type-prefix' },
-        {
-          'match': 'offsetof|seg',
-          'name': 'support.type.assembly.d'
-        },
-        { 'include': '#asm-primary-expression' },
-      ]
-    },
-    'asm-primary-expression': {
-      'patterns': [
-        { 'include': '#integer-literal' },
-        { 'include': '#float-literal' },
-        {
-          'match': '__LOCAL_SIZE',
-          'name': 'constant.language.assembly.d'
-        },
-        { 'include': '#register' },
-        { 'include': '#register-64' },
-        { 'include': '#identifier' }
-      ]
-    },
-    'asm-type-prefix': {
-      'patterns': [
-        'match': '(near\\s+ptr)|(far\\s+ptr)|(byte\\s+ptr)|(short\\s+ptr)|(int\\s+ptr)|(word\\s+ptr)|(dword\\s+ptr)|(qword\\s+ptr)|(float\\s+ptr)|(double\\s+ptr)|(real\\s+ptr)',
-        'name': 'support.type.asm-type-prefix.d',
-      ]
-    },
-    'register': {
-      'patterns': [
-        'match': 'XMM0|XMM1|XMM2|XMM3|XMM4|XMM5|XMM6|XMM7|MM0|MM1|MM2|MM3|MM4|MM5|MM6|MM7|ST\\(0\\)|ST\\(1\\)|ST\\(2\\)|ST\\(3\\)|ST\\(4\\)|ST\\(5\\)|ST\\(6\\)|ST\\(7\\)|ST|TR1|TR2|TR3|TR4|TR5|TR6|TR7|DR0|DR1|DR2|DR3|DR4|DR5|DR6|DR7|CR0|CR2|CR3|CR4|EAX|EBX|ECX|EDX|EBP|ESP|EDI|ESI|AL|AH|AX|BL|BH|BX|CL|CH|CX|DL|DH|DX|BP|SP|DI|SI|ES|CS|SS|DS|GS|FS',
-        'name': 'storage.type.assembly.register.d',
-      ]
-    },
-    'register-64': {
-      'patterns': [
-        'match': 'RAX|RBX|RCX|RDX|BPL|RBP|SPL|RSP|DIL|RDI|SIL|RSI|R8B|R8W|R8D|R8|R9B|R9W|R9D|R9|R10B|R10W|R10D|R10|R11B|R11W|R11D|R11|R12B|R12W|R12D|R12|R13B|R13W|R13D|R13|R14B|R14W|R14D|R14|R15B|R15W|R15D|R15|XMM8|XMM9|XMM10|XMM11|XMM12|XMM13|XMM14|XMM15|YMM0|YMM1|YMM2|YMM3|YMM4|YMM5|YMM6|YMM7|YMM8|YMM9|YMM10|YMM11|YMM12|YMM13|YMM14|YMM15',
-        'name': 'storage.type.assembly.register-64.d'
-      ]
-    },
-    ###########################################################################
-    # Declaration
-    ###########################################################################
-
-
-
-
-    ###########################################################################
-    # Function
-    ###########################################################################
-
-
-
+    'function-body': {
+      'patterns': [
+
+      ]
+    },
+    'in-statement': {
+      'patterns': [
+
+      ]
+    },
+    'out-statement': {
+      'patterns': [
+
+      ]
+    },
+    'body-statement': {
+      'patterns': [
+
+      ]
+    },
+    'constructor': {
+      'patterns': [
+
+      ]
+    },
+    'constructor-template': {
+      'patterns': [
+
+      ]
+    },
+    'destructor': {
+      'patterns': [
+        {
+          'match': '~this\\s*\\(\\s*\\)',
+          'name': '#entity.name.class.destructor.d'
+        },
+        { 'include': '#member-function-attributes' },
+        { 'include': '#function-body' },
+      ]
+    },
+    'postblit': {
+      'patterns': [
+        {
+          'match': 'this\\s*\\(\\s*this\\s*\\)\\s',
+          'name': '#entity.name.class.postblit.d'
+        },
+        { 'include': '#member-function-attributes' },
+        { 'include': '#function-body'}
+      ]
+    },
+    'allocator': {
+      'patterns': [
+        {
+          'match': 'new',
+          'name': 'keyword.other.new.d'
+        },
+        { 'include': '#parameters' },
+        { 'include': '#function-body' }
+      ]
+    },
+    'deallocator': {
+      'patterns': [
+        {
+          'match': 'delete',
+          'name': 'keyword.other.delete.d'
+        },
+        { 'include': '#parameters' },
+        { 'include': '#function-body' }
+      ]
+    },
+    'invariant': {
+      'patterns': [
+        {
+          'match': 'invariant\\s*\\(\\s*\\)',
+          'name': 'entity.name.class.invariant.d'
+        },
+        { 'include': '#block-statement' }
+      ]
+    },
+    'unit-test': {
+      'patterns': [
+        {
+          'match': 'unittest',
+          'name': 'entity.name.function.unit-test.d'
+        },
+        { 'include': '#function-body' }
+      ]
+    },
     ###########################################################################
     # Aggregate
     ###########################################################################


a reasonable color compromise?

a reasonable color compromise?

--- graphics.coffee
+++ graphics.coffee
@@ -21,7 +21,8 @@
       width: @block*10
       height: @block*10

-    @program_stroke_style = '#8000c0'
+    @program_fill_style = '#404040'
+    @program_stroke_style = '#000000'
     @grid_stroke_style = '#404040'

     @computeOutlines()
@@ -49,7 +50,7 @@
     s: '#c00000'  # red
     c: '#00c000'  # green
     d: '#0000c0'  # blue
-    h: '#00c0c0'  # light blue
+    h: '#c000c0'  # purple

   computeOutlines: () ->
     @outline_links = []
@@ -206,17 +207,13 @@
           when 'empty'
             @ctx.strokeRect(x*@block-.5, y*@block-.5, @block, @block)
     @ctx.strokeStyle = @program_stroke_style
-    @ctx.lineWidth = 2
+    @ctx.fillStyle = @program_fill_style
     for x in [0...@game_state.Wi]
       for y in [0...@game_state.Hi]
         switch @game_state.level[x][y].type
           when 'program'
-            #@ctx.fillRect(x*@block-.5, y*@block-.5, @block, @block)
-            #@ctx.strokeRect(x*@block-.5, y*@block-.5, @block, @block)
-            @ctx.save()
-            @ctx.translate(x*@block-.5, y*@block-.5)
-            @renderShape('octagon', @block*.5)
-            @ctx.restore()
+            @ctx.fillRect(x*@block-.5, y*@block-.5, @block, @block)
+            @ctx.strokeRect(x*@block-.5, y*@block-.5, @block, @block)
     @ctx.restore()

   renderBot: (sprite) =>


changing binding to be less invasive

changing binding to be less invasive

--- src/mixins/detail.coffee
+++ src/mixins/detail.coffee
@@ -2,7 +2,7 @@

 class DetailMixin
-  bindings: {}
+  bindings: null
   detailSelector: null
   autoBind: (name) -> "[data-" + name + "]"
   dataActions:
@@ -75,22 +75,26 @@
       else
         attrs = []

-    for name in attrs
-      @bindAttribute name
+    if @bindings
+      for name in Object.keys(@bindings)
+        @bindAttribute name
+    else
+      for name in attrs
+        @bindAttribute name

   bindAttribute: (name) ->
-    selector = @bindings[name]
+    selector = @bindings?[name]
     if not selector and @autoBind
       selector = @autoBind name

     @getSubPanel().find(selector).each (idx, e) =>
       el = @.$(e)
       action = el.attr("data-#{name}") or "default"
-      args = action.split(":")
-      action = args[0]
-      args = args[1..]
+      # args = action.split(":")
+      # action = args[0]
+      # args = args[1..]
       func = @dataActions[action]
-      opts = {el: el, name: name, value: @model.get(name), args: args}
+      opts = {el: el, name: name, value: @model.get(name)}#, args: args}
       if func
         func.bind(@)(opts)
       else


Fix certain keypresses triggering a re-render when the popover was open

--- app/assets/javascripts/exo/views/autocomplete_token_field.js.coffee
+++ app/assets/javascripts/exo/views/autocomplete_token_field.js.coffee
@@ -45,10 +45,14 @@

     handleKeyUp: (key, e) ->
       switch key
+        when "up"
+          return false # Prevent re-rendering when navigating auto-completer
         when "down"
           # Buggy..
           if @showAllOnDownArrow && @input.value == "" && !@resultsPopover.visible
             @_showResultsPopover()
+          else
+            return false
         else
           @_queryEntered(@input.value)

@@ -57,8 +61,7 @@
     handleKeyDown: (key, e) ->
       switch key
         when "up", "down"
-          # Prevent re-rendering when navigating auto-completer
-          return false
+          return false # Prevent re-rendering when navigating auto-completer
         when "enter"
           if @selectedIndex > -1
             # Don't add tokens on 'enter', this happens by watching for


Inject a missed dependencie

Inject a missed dependencie

--- app/modules/history/history-lightbox/history-entry.directive.coffee
+++ app/modules/history/history-lightbox/history-entry.directive.coffee
@@ -17,10 +17,8 @@
 # File: comment.directive.coffee
 ###

-module = angular.module('taigaHistory')

 HistoryEntryDirective = (lightboxService) ->
-
     return {
         scope: {
             entry: "<"
@@ -28,4 +26,8 @@
         templateUrl:"history/history-lightbox/history-entry.html",
     }

-module.directive("tgHistoryEntry", HistoryEntryDirective)
+HistoryEntryDirective.$inject = [
+    "lightboxService"
+]
+
+angular.module('taigaHistory').directive("tgHistoryEntry", HistoryEntryDirective)


add select2 call after loading new images Fixes #5385

--- backend/app/assets/javascripts/spree/backend/images/index.js.coffee
+++ backend/app/assets/javascripts/spree/backend/images/index.js.coffee
@@ -13,3 +13,4 @@
       )
       success: (r) ->
         ($ '#images').html r
+        ($ '.select2').select2()


Update nl-NL.coffee

Update nl-NL.coffee

--- app/locale/nl-NL.coffee
+++ app/locale/nl-NL.coffee
@@ -167,9 +167,9 @@
     version_history: "Versie geschiedenis"
     version_history_for: "Versie geschiedenis voor: "
     select_changes: "Selecteer hieronder twee veranderingen om het verschil te zien."
-#    undo_prefix: "Undo"
+    undo_prefix: "Ongedaan maken"
     undo_shortcut: "(Ctrl+Z)"
-#    redo_prefix: "Redo"
+    redo_prefix: "Terugzetten"
     redo_shortcut: "(Ctrl+Shift+Z)"
     play_preview: "Speel voorproefje van dit level"
     result: "Resultaat"
@@ -224,11 +224,11 @@
     restart: "Herstarten"
     goals: "Doelen"
     goal: "Doel"
-#    running: "Running..."
+    running: "Afspelen..."
     success: "Gelukt!"
     incomplete: "Incompleet"
     timed_out: "De tijd is op"
-#    failing: "Failing"
+    failing: "Mislukken"
     action_timeline: "Actie tijdlijn"
     click_to_select: "Klik op een eenheid om deze te selecteren."
     control_bar_multiplayer: "Multiplayer"
@@ -254,16 +254,16 @@
     victory_experience_gained: "XP verdient"
     victory_gems_gained: "Edelstenen verdient"
     victory_new_item: "Nieuw Item"
-#    victory_viking_code_school: "Holy smokes, that was a hard level you just beat! If you aren't already a software developer, you should be. You just got fast-tracked for acceptance with Viking Code School, where you can take your skills to the next level and become a professional web developer in 14 weeks."
+    victory_viking_code_school: "Wow, jij hebt net een moeilijk level gehaald! Als je nog geen programmeur was, zou je er een moeten worden! Je hebt net versneld toegang gekregen voor de Viking programmeerSchool, waar je je vaardigheden kan opkrikken en binnen 14 weken een professionele webontwikkelaar kunt worden."
     victory_become_a_viking: "Word een Viking"
-#    victory_bloc: "Great work! Your skills are improving, and someone's taking notice. If you've considered becoming a software developer, this may be your lucky day. Bloc is an online bootcamp that pairs you 1-on-1 with an expert mentor who will help train you into a professional developer! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES"
+#   victory_bloc: "Goed werk! Je wordt steeds beter en het valt op! Als je een programmeur wil worden, kan dit je geluksdag zijn. Bloc is een online bootcamp waarbij je een eigen mentor krijgt, die je helpt een echte programmeur te worden! By beating A Mayhem of Munchkins, you're now eligible for a $500 price reduction with the code: CCRULES"
     victory_bloc_cta: "Ontmoet je mentor – leer over Bloc"
     guide_title: "Handleiding"
     tome_minion_spells: "Jouw Minions' Spreuken" # Only in old-style levels.
     tome_read_only_spells: "Read-Only Spreuken" # Only in old-style levels.
     tome_other_units: "Andere Eenheden" # Only in old-style levels.
     tome_cast_button_run: "Uitvoeren"
-#    tome_cast_button_running: "Running"
+    tome_cast_button_running: "Afspelen"
     tome_cast_button_ran: "Uitgevoerd"
     tome_submit_button: "Indienen"
     tome_reload_method: "Oorspronkelijke code voor deze methode herladen" # Title text for individual method reload button.
@@ -289,7 +289,7 @@
     infinite_loop_title: "Oneindige Loop gedetecteerd"
 #    infinite_loop_description: "The initial code to build the world never finished running. It's probably either really slow or has an infinite loop. Or there might be a bug. You can either try running this code again or reset the code to the default state. If that doesn't fix it, please let us know."
 #    check_dev_console: "You can also open the developer console to see what might be going wrong."
-#    check_dev_console_link: "(instructions)"
+    check_dev_console_link: "(instructies)"
     infinite_loop_try_again: "Probeer opnieuw"
     infinite_loop_reset_level: "Level resetten"
     infinite_loop_comment_out: "Mijn code weg commentariëren"
@@ -323,25 +323,25 @@
     tip_hofstadters_law: "De Wet van Hofstadter: Het duurt altijd langer dan je verwacht, zelfs wanneer je rekening houdt met de Wet van Hofstadter."
     tip_premature_optimization: "vroegtijdig optimaliseren is de wortel van al het kwaad. - Donald Knuth"
     tip_brute_force: "Wanneer je twijfelt, gebruik brute force. - Ken Thompson"
-#    tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
+    tip_extrapolation: "Er zijn twee soorten mensen: Zij die iets kunnen afleiden van onvolledige gegevens..."
 #    tip_superpower: "Coding is the closest thing we have to a superpower."
 #    tip_control_destiny: "In real open source, you have the right to control your own destiny. - Linus Torvalds"
-#    tip_no_code: "No code is faster than no code."
+    tip_no_code: "Geen code is sneller dan geen code."
     tip_code_never_lies: "Code liegt niet, commentaar soms wel. — Ron Jeffries"
-#    tip_reusable_software: "Before software can be reusable it first has to be usable."
+    tip_reusable_software: "voor code herbruikbaar kan zijn, moet het bruikbaar zijn."
 #    tip_optimization_operator: "Every language has an optimization operator. In most languages that operator is ‘//’"
-#    tip_lines_of_code: "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates"
-#    tip_source_code: "I want to change the world but they would not give me the source code."
+    tip_lines_of_code: "programmeervoortgang meten met lijnen code, is als vliegtuigbouwvoortgang meten bij gewicht. — Bill Gates"
+    tip_source_code: "Ik wil de wereld veranderen, maar ze geven me de broncode niet."
 #    tip_javascript_java: "Java is to JavaScript what Car is to Carpet. - Chris Heilmann"
     tip_move_forward: "Wat je ook doet, ga vooruit. - Martin Luther King Jr."
     tip_google: "Heb je een probleem die je niet kan oplossen? Googel het!"
-#    tip_adding_evil: "Adding a pinch of evil."
-#    tip_hate_computers: "That's the thing about people who think they hate computers. What they really hate is lousy programmers. - Larry Niven"
+    tip_adding_evil: "Een snufje kwaad toevoegen."
+    tip_hate_computers: "Mensen die denken dat ze de computer haten, haten eigenlijk slechte programmeurs. - Larry Niven"
     tip_open_source_contribute: "Je kunt helpen CodeCombat te verbeteren!"
 #    tip_recurse: "To iterate is human, to recurse divine. - L. Peter Deutsch"
-#    tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus"
+    tip_free_your_mind: "You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. - Morpheus"
     tip_strong_opponents: "Zelf de sterkste vijand heeft een zwakheid. - Itachi Uchiha"
-#    tip_paper_and_pen: "Before you start coding, you can always plan with a sheet of paper and a pen."
+    tip_paper_and_pen: "Voor je begint te programmeren, kan je altijd met pen en papier een plan maken."

   game_menu:
     inventory_tab: "Inventaris"
@@ -406,14 +406,14 @@
     feature2: "10 sterke nieuw helden met unieke vaardigheden!"
     feature3: "70+ bonuslevels"
 #    feature4: "3500 bonus gems every month!"
-#    feature5: "Video tutorials"
+    feature5: "Video cursussen"
 #    feature6: "Premium email support"
 #    feature7: "Private Clans"
     free: "Gratis"
     month: "maand"
     must_be_logged: "Je moet eerst ingelogd zijn. Maak een account aan of log in in het menu hierboven."
     subscribe_title: "Abonneren"
-    unsubscribe: "Abonnement pzeggen"
+    unsubscribe: "Abonnement opzeggen"
     confirm_unsubscribe: "Opzegging Bevestigen"
     never_mind: "Laat Maar, Ik Hou Nog Steeds Van Je"
     thank_you_months_prefix: "Bedankt voor je steun in de laatste"
@@ -471,21 +471,21 @@
 #    managed_subs_desc_2: "Recipients must have a CodeCombat account associated with the email address you provide."
     group_discounts: "Groepskorting"
     group_discounts_1: "We bieden groepskortingen aan voor grote groepen."
-#    group_discounts_1st: "1st subscription"
-#    group_discounts_full: "Full price"
-#    group_discounts_2nd: "Subscriptions 2-11"
-#    group_discounts_20: "20% off"
-#    group_discounts_12th: "Subscriptions 12+"
-#    group_discounts_40: "40% off"
-#    subscribing: "Subscribing..."
-#    recipient_emails_placeholder: "Enter email address to subscribe, one per line."
-#    subscribe_users: "Subscribe Users"
-#    users_subscribed: "Users subscribed:"
-#    no_users_subscribed: "No users subscribed, please double check your email addresses."
-#    current_recipients: "Current Recipients"
-#    unsubscribing: "Unsubscribing..."
-#    subscribe_prepaid: "Click Subscribe to use prepaid code"
-#    using_prepaid: "Using prepaid code for monthly subscription"
+    group_discounts_1st: "eerste abonnement"
+    group_discounts_full: "hele prijs"
+    group_discounts_2nd: "Abonnementen 2-11"
+    group_discounts_20: "20% korting"
+    group_discounts_12th: "Abonnementen boven 11"
+    group_discounts_40: "40% korting"
+    subscribing: "Inschrijven..."
+    recipient_emails_placeholder: "Voer emailadres in om in te schrijven, een per regel."
+    subscribe_users: "Gebruikers inschrijven"
+    users_subscribed: "Gebruikers ingschreven:"
+    no_users_subscribed: "Geen gebruikers ingeschreven, Controleer je emailadres nog een keer."
+    current_recipients: "Huidige ontvangers"
+    unsubscribing: "Uitschrijven..."
+    subscribe_prepaid: "Klik op inschrijven om een prepaidcode te gebruiken."
+    using_prepaid: "Prepaidcode gebruiken voor maandelijkse inschrijving."

   choose_hero:
     choose_hero: "Kies je held"
@@ -519,15 +519,15 @@
 #    health_1: "Gains"
 #    health_2: "of listed"
 #    health_3: "armor health."
-#    speed_1: "Moves at"
+    speed_1: "Beweegt met"
     speed_2: "meter per seconde."
-#    available_for_purchase: "Available for Purchase" # Shows up when you have unlocked, but not purchased, a hero in the hero store
-#    level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
-    restricted_to_certain_heroes: "Only certain heroes can play this level."
+    available_for_purchase: "Aanschafbaar" # Shows up when you have unlocked, but not purchased, a hero in the hero store
+    level_to_unlock: "Beschikbaar na level:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
+    restricted_to_certain_heroes: "Alleen bepaalde helden kunnen dit level spelen."

   skill_docs:
-#    writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
-#    read_only: "read-only"
+    writable: "beschrijfbaar" # Hover over "attack" in Your Skills while playing a level to see most of this
+    read_only: "read-only"
     action: "Actie"
     spell: "toverspreuk"
     action_name: "naam"
@@ -550,7 +550,7 @@
     granularity_change_history: "Geschiedenis"

   options:
-#    general_options: "General Options" # Check out the Options tab in the Game Menu while playing a level
+    general_options: "Algemene Opties" # Check out the Options tab in the Game Menu while playing a level
     volume_label: "Volume"
     music_label: "Muziek"
     music_description: "zet achtergrondmuziek aan/uit."
@@ -563,7 +563,7 @@
     editor_config_keybindings_label: "Toets instellingen"
     editor_config_keybindings_default: "Standaard (Ace)"
     editor_config_keybindings_description: "Voeg extra shortcuts toe van de gebruikelijke editors."
-#    editor_config_livecompletion_label: "Live Autocompletion"
+    editor_config_livecompletion_label: "Automatisch aanvullen"
     editor_config_livecompletion_description: "Toont suggesties tijdens het typen."
     editor_config_invisibles_label: "Toon onzichtbare"
     editor_config_invisibles_description: "Toon onzichtbare (spatie) karakters."
@@ -629,19 +629,19 @@
     monitor_progress_title: "Hoe bekijk ik de voortgang van de leerlingen?"
     monitor_progress_1: "De voortgang van leerlingen kunnen worden gevolg door het maken van een"
     monitor_progress_2: "voor je klas."
-#    monitor_progress_3: "Om een leerling toe te voegen, stuur hem een uitnodiging voor je Clan, which is on the"
+#    monitor_progress_3: "Om een leerling toe te voegen, stuur hem een uitnodiging voor je Clan,welke te vinden is op"
     monitor_progress_4: "pagina."
-#    monitor_progress_5: "After they join, you will see a summary of the student's progress on your Clan's page."
-#    private_clans_1: "Private Clans provide increased privacy and detailed progress information for each student."
+    monitor_progress_5: "Nadat ze zijn aangesloten, ziet u een samenvatting van de leerlingen op uw Clan's pagina."
+#    private_clans_1: "Privé Clans hebben meer privacy en gedetailleerdere voortgang voor elke leeerlingen."
 #    private_clans_2: "To create a private Clan, check the 'Make clan private' checkbox when creating a"
     private_clans_3: "."
     who_for_title: "Voor wie is CodeCombat?"
     who_for_1: "We raden CodeCombat aan voor leerlingen vanaf 9 jaar. Er is geen programmeerevaring nodig."
-#    who_for_2: "We've designed CodeCombat to appeal to both boys and girls."
+    who_for_2: "We hebben CodeCombat zo gemaakt dat het leuk is vooi jongens en meiden."
     material_title: "Hoeveel materiaal is er?"
 #    material_china: "Approximately 40 hours of gameplay spread over 180+ subscriber-only levels so far."
-#    material_1: "Approximately 25 hours of free content and an additional 15 hours of subscriber content."
-#    concepts_title: "What concepts are covered?"
+    material_1: "ongeveer 25 uur gratis en 15 uur betaalde inhoud."
+    concepts_title: "Welke onderwerpen worden behandeld?"
     how_much_title: "Hoeveel kost een maadelijkse inschrijving?"
     how_much_1: "Een"
     how_much_2: "maandelijkse inschrijving"
@@ -652,7 +652,7 @@
     more_info_title: "Waar kan ik meer informatie vinden?"
     more_info_1: "ons"
     more_info_2: "lerarenforum"
-#    more_info_3: "is a good place to connect with fellow educators who are using CodeCombat."
+    more_info_3: "Het is een goede plek om contact te leggen met andere leerkrachten, die CodeCombat gebruiken."
     sys_requirements_title: "Systeem Requirements"
     sys_requirements_1: "Een moderne webbrowser. Een nieuwe versie van Chrome, Firefox, of Safari. Internet Explorer 9 of later."
     sys_requirements_2: "CodeCombat ondersteund de IPad nog niet."
@@ -670,11 +670,11 @@
     denied_2: "afgewezen."
     contact_1: "Neem contact op"
     contact_2: "als u meer vragen heeft."
-#    description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our"
+    description_1: "We bieden gratis abbonnementen aan, aan leerkrachten voor evaluatiedoeleinden. Meer informatie kan worden gevonden op onze"
     description_2: "leerkrachten"
     description_3: "pagina."
-#    description_4: "Please fill out this quick survey and we’ll email you setup instructions."
-    email: "Emailaddress"
+    description_4: "Vul alstublieft onze korte vragenlijst in en we zullen u instructies sturen om te beginnen."
+    email: "Emailadres"
     school: "schoolnaam"
     location: "stad of dorp"
     age_students: "Hoe oud zijn uw leerlingen?"
@@ -766,10 +766,10 @@
     escape: "Escape"
     shift: "Shift"
     run_code: "Voer huidige code uit."
-#    run_real_time: "Run in real time."
-#    continue_script: "Continue past current script."
-#    skip_scripts: "Skip past all skippable scripts."
-#    toggle_playback: "Toggle play/pause."
+    run_real_time: "Voer code direct uit."
+    continue_script: "Sla huidige script over."
+    skip_scripts: "Sla alle scripts over die overgeslagen kunnen worden."
+    toggle_playback: "switch speel/pause."
 #    scrub_playback: "Scrub back and forward through time."
 #    single_scrub_playback: "Scrub back and forward through time by a single frame."
 #    scrub_execution: "Scrub through current spell execution."
@@ -781,24 +781,24 @@

   community:
     main_title: "CodeCombat Gemeenschap"
-#    introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!"
-#    level_editor_prefix: "Use the CodeCombat"
-#    level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!"
+    introduction: "`Kijk hieronder hoe je mee kunt doen en kies wat je leuk lijkt. Wek kijken er naar uit om samen te werken!"
+    level_editor_prefix: "Gebruik CodeCombat"
+    level_editor_suffix: "om levels te bekijken en te bewerken. Gebruikers hebben levels gemaakt voor hun klas, vrienden, hackathons, leerlingen en familie. Als een nieuw level maken te moeilijk lijkt,  kun je beginnen met het forken van een van onze levels!"
 #    thang_editor_prefix: "We call units within the game 'thangs'. Use the"
 #    thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites."
-#    article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the"
-#    article_editor_suffix: "and help CodeCombat players get the most out of their playtime."
-#    find_us: "Find us on these sites"
-#    social_github: "Check out all our code on GitHub"
-#    social_blog: "Read the CodeCombat blog on Sett"
-#    social_discource: "Join the discussion on our Discourse forum"
-#    social_facebook: "Like CodeCombat op Facebook"
-#    social_twitter: "Follow CodeCombat on Twitter"
-#    social_gplus: "Join CodeCombat on Google+"
-#    social_hipchat: "Chat with us in the public CodeCombat HipChat room"
-#    contribute_to_the_project: "Contribute to the project"
-
-#  clans:
+    article_editor_prefix: "Zie je een fout in een van onze documenten? Wil je instruicties maken voor je eigen creatie? Ga naar de"
+    article_editor_suffix: "en help CodeCombat spelers meer uit hun speeltijd te halen."
+    find_us: "vind ons op de volgende sites:"
+    social_github: "Bekijk al onze code op GitHub"
+    social_blog: "Lees onze blog op Sett"
+    social_discource: "Doe mee met discussies op onze Discourse forum"
+    social_facebook: "Like CodeCombat op Facebook"
+    social_twitter: "Volg CodeCombat op Twitter"
+    social_gplus: "Join CodeCombat op Google+"
+    social_hipchat: "Chat met ons in de publieke CodeCombat HipChat room"
+    contribute_to_the_project: "Help mee met het project"
+
+  clans:
 #    clan: "Clan"
 #    clans: "Clans"
 #    new_name: "New clan name"
@@ -806,41 +806,41 @@
 #    make_private: "Make clan private"
 #    subs_only: "subscribers only"
 #    create_clan: "Create New Clan"
-#    private_preview: "Preview"
+    private_preview: "Preview"
 #    public_clans: "Public Clans"
 #    my_clans: "My Clans"
 #    clan_name: "Clan Name"
-#    name: "Name"
+    name: "Naam"
 #    chieftain: "Chieftain"
-#    type: "Type"
+    type: "Type"
 #    edit_clan_name: "Edit Clan Name"
 #    edit_clan_description: "Edit Clan Description"
-#    edit_name: "edit name"
-#    edit_description: "edit description"
-#    private: "(private)"
-#    summary: "Summary"
-#    average_level: "Average Level"
+    edit_name: "bewerk naam"
+    edit_description: "bewerk beschrijving"
+    private: "(privé)"
+    summary: "Samenvatting"
+    average_level: "Gemiddelde Level"
 #    average_achievements: "Average Achievements"
 #    delete_clan: "Delete Clan"
 #    leave_clan: "Leave Clan"
 #    join_clan: "Join Clan"
-#    invite_1: "Invite:"
-#    invite_2: "*Invite players to this Clan by sending them this link."
-#    members: "Members"
-#    progress: "Progress"
-#    not_started_1: "not started"
-#    started_1: "started"
-#    complete_1: "complete"
-#    exp_levels: "Expand levels"
-#    rem_hero: "Remove Hero"
-#    status: "Status"
-#    complete_2: "Complete"
-#    started_2: "Started"
-#    not_started_2: "Not Started"
-#    view_solution: "Click to view solution."
+    invite_1: "Uitnodigen:"
+#    invite_2: "*Nodig spelers uit voor deze Clan door ze deze link te sturen."
+    members: "Leden"
+    progress: "Voortgang"
+    not_started_1: "Niet gestard"
+    started_1: "gestart"
+    complete_1: "Klaar"
+    exp_levels: "Breid levels uit"
+    rem_hero: "Verwijder held"
+    status: "Status"
+    complete_2: "Klaar"
+    started_2: "Gestart"
+    not_started_2: "Niet gestart"
+    view_solution: "Klik om oplossing te bekijken."
 #    latest_achievement: "Latest Achievement"
-#    playtime: "Playtime"
-#    last_played: "Last played"
+    playtime: "Speeltijd"
+    last_played: "Laatst gespeeld"
 #    leagues_explanation: "Play in a league against other clan members in these multiplayer arena instances."

   courses:
@@ -897,9 +897,9 @@
     creating_for: "U maakt een klas voor"
     for: "voor" # Like in 'for 30 students'
 #    receive_code: "Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class."
-#    free_trial: "Free trial for teachers!"
-#    get_access: "to get individual access to all courses for evalutaion purposes."
-    questions: "Vragdn?"
+    free_trial: "Gratis uitproberen voor leerkrachten!"
+    get_access: "Individueel toegang krijgen voor evaluatiedoeleinden."
+    questions: "Vragen?"
     faq: "Cursus FAQ"
     question: "Vraag:" # Like in 'Question'
     question1: "Wat is het verschil tussen deze cursussen en de single player game?"
@@ -917,14 +917,14 @@
     see_the: "Bekijk de"
     more_info: "voor meer informatie."
     choose_course: "Kies je cursus:"
-#    enter_code: "Enter an unlock code"
-#    enter_code1: "Enter unlock code"
+    enter_code: "voer een ontgrendelcode in"
+    enter_code1: "Voer ontgrendelcode in"
     enroll: "Inschrijven"
-#    pick_from_classes: "Pick from your current classes"
+    pick_from_classes: "Kies uit je hidige klas"
 #    enter: "Enter"
     or: "Of"
     topics: "Onderwerpen"
-#    hours_content: "Hours of content:"
+    hours_content: "uren inhoud:"
     get_free: "Ontvang een GRATIS cursus"

   classes:
@@ -1017,13 +1017,13 @@
     new_level_title_login: "Log In om een New Level te maken"
     new_achievement_title: "Maak een Nieuwe Prestatie"
     new_achievement_title_login: "Log In om een Nieuwe Prestatie te maken"
-#    new_poll_title: "Create a New Poll"
-#    new_poll_title_login: "Log In to Create a New Poll"
+    new_poll_title: "Maak een nieuwe stemming"
+    new_poll_title_login: "Log in om een nieuwe stemming te maken"
     article_search_title: "Zoek Artikelen Hier"
     thang_search_title: "Zoek Thang Types Hier"
     level_search_title: "Zoek Levels Hier"
     achievement_search_title: "Zoek Prestaties"
-#    poll_search_title: "Search Polls"
+    poll_search_title: "Zoek stemmingen"
     read_only_warning2: "Pas op, je kunt geen aanpassingen opslaan hier, want je bent niet ingelogd."
     no_achievements: "Er zijn nog geen achievements toegevoegd voor dit level"
 #    achievement_query_misc: "Key achievement off of miscellanea"
@@ -1176,8 +1176,8 @@

   user:
     stats: "Statistieken"
-#    singleplayer_title: "Singleplayer Levels"
-#    multiplayer_title: "Multiplayer Levels"
+    singleplayer_title: "Singleplayer Levels"
+    multiplayer_title: "Multiplayer Levels"
     achievements_title: "Prestaties"
     last_played: "Laatst Gespeeld"
     status: "Status"
@@ -1194,8 +1194,8 @@
     last_earned: "Laatst verdiend"
     amount_achieved: "Hoeveelheid"
     achievement: "Prestatie"
-#    category_contributor: "Contributor"
-#    category_ladder: "Ladder"
+    category_contributor: "Meehelper"
+    category_ladder: "Stand"
     category_level: "Level"
     category_miscellaneous: "Diversen"
     category_levels: "Levels"
@@ -1311,7 +1311,7 @@
     clas: "klas"
 #    play_counts: "Play Counts"
     feedback: "Feedback"
-    payment_info: "Betalings Informatie"
+    payment_info: "Betalingsinformatie"
     campaigns: "Campagnes"
     poll: "Stemming"
     user_polls_record: "Stemgeschiedenis"
@@ -1354,8 +1354,8 @@
   guide:
     temp: "Temp"

-#  temp:
-#    ace_of_coders_tournament: "New: play in the Ace of Coders tournament now!"
+  temp:
+    ace_of_coders_tournament: "Nieuw: Speel nu mee in het Ace of Coders toernament!"

   multiplayer:
     multiplayer_title: "Multiplayer Instellingen" # We'll be changing this around significantly soon. Until then, it's not important to translate.
@@ -1422,25 +1422,25 @@
     nutshell_description: "Alle middelen die wij aanbieden in de Level Editor zijn gratis te gebruiken om levels aan te maken. Wij behouden ons echter het recht voor om levels die gemaakt zijn op codecombat.com te beperken, en hier in de toekomst geld voor te vragen, moest dat ooit gebeuren."
     canonical: "De Engelse versie van dit document is de definitieve en kanonieke versie. Bij verschillen tussen vertalingen heeft de Engelse versie voorrang."

-#  ladder_prizes:
-#    title: "Tournament Prizes" # This section was for an old tournament and doesn't need new translations now.
-#    blurb_1: "These prizes will be awarded according to"
-#    blurb_2: "the tournament rules"
-#    blurb_3: "to the top human and ogre players."
-#    blurb_4: "Two teams means double the prizes!"
-#    blurb_5: "(There will be two first place winners, two second-place winners, etc.)"
-#    rank: "Rank"
-#    prizes: "Prizes"
-#    total_value: "Total Value"
-#    in_cash: "in cash"
-#    custom_wizard: "Custom CodeCombat Wizard"
-#    custom_avatar: "Custom CodeCombat avatar"
-#    heap: "for six months of \"Startup\" access"
-#    credits: "credits"
-#    one_month_coupon: "coupon: choose either Rails or HTML"
-#    one_month_discount: "discount, 30% off: choose either Rails or HTML"
-#    license: "license"
-#    oreilly: "ebook of your choice"
+  ladder_prizes:
+    title: "Tournamentprijzen" # This section was for an old tournament and doesn't need new translations now.
+    blurb_1: "Deze prijzen worden uitgereikt volgens de toernamentregels"
+    blurb_2: "De toernamentregels"
+    blurb_3: "voor de beste mensen en boemannen."
+    blurb_4: "Twee teams, dus dubbele prijzen!"
+    blurb_5: "(Er is een gedeelde eerste plek, een gedeelde tweede plek, enz.)"
+    rank: "Rang"
+    prizes: "Prijzen"
+    total_value: "Totale waarde"
+    in_cash: "contant"
+    custom_wizard: "Aangepaste CodeCombat Wizard"
+    custom_avatar: "Aangepaste CodeCombat avatar"
+    heap: "voor zes maanden \"opsstart\" toegang"
+    credits: "credits"
+    one_month_coupon: "coupon: Kies Rails of HTML"
+    one_month_discount: "30% korting: Kies Rails of HTML"
+    license: "licentie"
+    oreilly: "ebook van je keuze"

   account_profile:
 #    settings: "Settings" # We are not actively recruiting right now, so there's no need to add new translations for this section.
@@ -1611,4 +1611,4 @@
     u_title: "Gebruikerslijst"
 #    ucp_title: "User Code Problems"
     lg_title: "Laatste Spelletjes"
-    clas: "CLAs"
+    clas: "klas"


Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -768,17 +768,17 @@
     ambassador_title_description: "(Suporte)"

   ladder:
-    please_login: "Por favor, faz log in antes de jogar um jogo para o campeonato."
-    my_matches: "Os meus jogos"
+    please_login: "Por favor inicie sessão antes de jogar um jogo do campeonato."
+    my_matches: "Os Meus Jogos"
     simulate: "Simular"
-    simulation_explanation: "Simulando jogos podes fazer com que o teu jogo seja classificado mais rapidamente!"
+    simulation_explanation: "Ao simular jogos pode ter o seu jogo classificado mais rapidamente!"
     simulate_games: "Simular Jogos!"
 #    simulate_all: "RESET AND SIMULATE GAMES"
-#    games_simulated_by: "Games simulated by you:"
-#    games_simulated_for: "Games simulated for you:"
-#    games_simulated: "Games simulated"
-#    games_played: "Games played"
-#    ratio: "Ratio"
+    games_simulated_by: "Jogos simulados por si:"
+    games_simulated_for: "Jogos simulados para si:"
+    games_simulated: "Jogos simulados"
+    games_played: "Jogos jogados"
+    ratio: "Rácio"
     leaderboard: "Tabela de Classificação"
     battle_as: "Lutar como "
     summary_your: "As tuas "
@@ -868,29 +868,29 @@
 #    server_error: "Server error."
 #    unknown: "Unknown error."

-#  resources:
-#    your_sessions: "Your Sessions"
-#    level: "Level"
+  resources:
+    your_sessions: "As Suas Sessões"
+    level: "Nível"
 #    social_network_apis: "Social Network APIs"
 #    facebook_status: "Facebook Status"
-#    facebook_friends: "Facebook Friends"
+    facebook_friends: "Amigos do Facebook"
 #    facebook_friend_sessions: "Facebook Friend Sessions"
-#    gplus_friends: "G+ Friends"
+    gplus_friends: "Amigos do Google+"
 #    gplus_friend_sessions: "G+ Friend Sessions"
-#    leaderboard: "Leaderboard"
+    leaderboard: "Tabela de Classificação"
 #    user_schema: "User Schema"
 #    user_profile: "User Profile"
 #    patches: "Patches"
 #    patched_model: "Source Document"
 #    model: "Model"
-#    system: "System"
-#    component: "Component"
-#    components: "Components"
-#    thang: "Thang"
-#    thangs: "Thangs"
-#    level_session: "Your Session"
-#    opponent_session: "Opponent Session"
-#    article: "Article"
+    system: "Sistema"
+    component: "Componente"
+    components: "Componentes"
+    thang: "Thang"
+    thangs: "Thangs"
+    level_session: "A Sua Sessão"
+    opponent_session: "Sessão Do Oponente"
+    article: "Artigo"
 #    user_names: "User Names"
 #    thang_names: "Thang Names"
 #    files: "Files"
@@ -900,7 +900,7 @@
 #    sprite_sheet: "Sprite Sheet"
 #    candidate_sessions: "Candidate Sessions"
 #    user_remark: "User Remark"
-#    versions: "Versions"
+    versions: "Versões"

 #  delta:
 #    added: "Added"


port oneClass from jQuery to Zepto hook

port oneClass from jQuery to Zepto hook

--- src/synapse/hooks/zepto.coffee
+++ src/synapse/hooks/zepto.coffee
@@ -203,6 +203,16 @@
             get: (key) -> @data(key)
             set: (key, value) -> @data(key, value)

+
+        # ### _oneClass.FOO_
+        # Reads or adds/removes a single class on the target element using
+        # Zepto hasClass for get and toggleClass for set
+        # (based on the truthiness of the set value)
+        interfaces.register
+            name: 'oneClass'
+            get: (key) -> @hasClass(key)
+            set: (key, value) -> @toggleClass(key, value)
+

     # Default DOM events. When a DOM element is declared the subject of a
     # binding and no event is specified, the element will be compared to


hide #comix when failed

hide #comix when failed

--- app/scripts/boot.coffee
+++ app/scripts/boot.coffee
@@ -51,6 +51,7 @@

   fail = (event, xhr) ->
     spinner.stop()
+    $("#comix").css "display", "none"
     $("#error").css "display", "block"
     $("#error-response").text xhr?.responseText
     $("#error-gist-number").text "#" + gistId


Build: minor fix on livereload trigger

Build: minor fix on livereload trigger

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -121,9 +121,8 @@
     .pipe(concat "main.js")
     .pipe(gulp.dest "playground/js")

-  stream.pipe(livereload())  if useLiveReload
-
   if useLiveReload
+    stream.pipe(livereload())
     gulp.src('./playground/index.html').pipe(livereload())

 gulp.task 'live', ->


Add gulpfile task

Add gulpfile task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -9,6 +9,7 @@
 rename = require 'gulp-rename'
 uglify = require 'gulp-uglify'
 insert = require 'gulp-insert'
+acorn = require 'acorn'

 gulp.task 'default', ['coffee']

@@ -28,3 +29,19 @@
     .on('error', gutil.log))
     .pipe(insert.prepend('#!/usr/bin/env node\n\n'))
     .pipe gulp.dest('./dist/bin/')
+
+gulp.task 'requires', ->
+  fs.readFile './dist/spinner.js', 'utf-8', (err, data) ->
+    ast = acorn.parse(data)
+    walk = require('acorn/util/walk')
+    walkall = require('walkall')
+
+    walk.simple(ast, walkall.makeVisitors((node) ->
+      return unless node.type == 'CallExpression'
+      return unless node.callee.name == 'require'
+
+      module_name = node.arguments[0].value
+      return unless module_name.match(/^[a-zA-Z]/)
+
+      console.log module_name
+    ), walkall.traversers)


Remove unnecessary newline.

Remove unnecessary newline.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -34,4 +34,3 @@
 		'src/validateSSH.coffee'
 	], (event) ->
 		gulp.run 'build'
-


remove declare

remove declare

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,7 +1,6 @@
 browserify = require 'gulp-browserify'
 coffee = require 'gulp-coffee'
 coffeelint = require 'gulp-coffeelint'
-declare = require 'gulp-declare'
 gulp = require 'gulp'
 gutil = require 'gulp-util'
 plumber = require 'gulp-plumber'


Fix browserify bundles.

Fix browserify bundles.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -22,11 +22,17 @@

 gulp.task 'bundle', ['compile'], ->
-  browserify entries: './lib/transparency.js'
+  bundle = browserify entries: './lib/transparency.js'
     .bundle()
-    .pipe source 'dist/transparency.js'
+
+  bundle
+    .pipe source 'transparency.js'
+    .pipe gulp.dest './dist/'
+
+  bundle
+    .pipe source 'transparency.min.js'
     .pipe streamify uglify()
-    .pipe gulp.dest 'dist/transparency.min.js'
+    .pipe gulp.dest './dist/'

 gulp.task 'test:functional', ['compile:tests'], ->


Fixed test-quick

Fixed test-quick

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -45,7 +45,7 @@
 testNodeFn = (options={}) -> (callback) ->
   (require './test/lib/test_server').retain()

-  gutil.log 'Running Node.js tests'
+  gutil.log "Running Node.js tests #{if options.quick then '(quick)' else ''}"
   require './test/lib/node_jquery_xhr' # ensure that globals for the target backend are loaded
   global.test_parameters = require './test/parameters'
   mocha_options = if options.quick then {grep: '@no_options'} else {}
@@ -59,7 +59,7 @@
 testBrowsersFn = (options={}) -> (callback) ->
   (require './test/lib/test_server').retain()

-  gutil.log 'Running Browser tests'
+  gutil.log "Running Browser tests #{if options.quick then '(quick)' else ''}"
   karma_options = if options.quick then {client: {args: ['--grep', '@no_options']}} else {}
   (require './config/karma/run') karma_options, (err) ->
     (require './test/lib/test_server').release(err); callback(err)
@@ -71,12 +71,12 @@
 gulp.task 'test', ['minify', 'start-test-server'], (callback) ->
   (require './test/lib/test_server').retain()

-  Async.series [testNodeFn(), testBrowsersFn()], (err) -> (require './test/lib/test_server').release(err)
+  Async.series [testNodeFn(), testBrowsersFn()], (err) -> (require './test/lib/test_server').release(err); callback(err)
   return # promises workaround: https://github.com/gulpjs/gulp/issues/455

-gulp.task 'test-quick', ['test'], (callback) ->
+gulp.task 'test-quick', ['start-test-server'], (callback) ->
   (require './test/lib/test_server').retain()
-  Async.series [testNodeFn({quick: true}), testBrowsersFn({quick: true})], (err) -> (require './test/lib/test_server').release(err)
+  Async.series [testNodeFn({quick: true}), testBrowsersFn({quick: true})], (err) -> (require './test/lib/test_server').release(err); callback(err)
   return # promises workaround: https://github.com/gulpjs/gulp/issues/455

 gulp.task 'test-node-quick', ['build', 'start-test-server'], testNodeFn({quick: true})


gulp: edit sass task

gulp: edit sass task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -20,7 +20,11 @@

 gulp.task "main.css/sass", ["bower"], ->
   sass = require("gulp-sass")
-  gulp.src ["./bower_components/social-snippet-registry-assets/src/sass/**/*.sass"]
+  sassSrc = [
+    "./bower_components/social-snippet-registry-assets/src/sass/**/*.sass"
+    "./bower_components/social-snippet-registry-assets/src/sass/**/*.scss"
+  ]
+  gulp.src sassSrc
     .pipe sass()
     .pipe gulp.dest("tmp/css/")


remove unused function 16796

remove unused function 16796

--- src/options_model.coffee
+++ src/options_model.coffee
@@ -4,7 +4,6 @@
 # helpers
 #
 $ = document.querySelector.bind document
-$$ = document.querySelectorAll.bind document
 val = (obj) -> obj.value
 last = (arr) -> arr[arr.length-1] if arr.length > 0


Added background to TabGroup

Added background to TabGroup

--- src/views/ui/tabs/group.coffee
+++ src/views/ui/tabs/group.coffee
@@ -9,6 +9,8 @@
 module.exports = class TabGroup extends CollectionView

   viewName: 'TabGroup'
+
+  attributes: styles.window.view

   events:
     close: 'destroy'


fixed touch response issue

fixed touch response issue

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -81,15 +81,19 @@
       delta:  0
       last:   0
       el:     document.createElement 'div'
+      holder: document.createElement 'div'

     cube.el.className = "#{ cssClass }-cube #{ cssClass }-cube-#{ set }"
     cube.el.style.margin = "0 #{ @margin }px"
-    cube.el.style.width = cube.el.style.height = @size + 'px'
-    cube.el.style[css.transform] = @_getTransform 0
+    cube.el.style.width = cube.el.style.height =
+      cube.holder.style.width = cube.holder.style.height = @size + 'px'
+    cube.holder.style[css.transform] = @_getTransform 0
+    sideProto = document.createElement 'div'
+    sideProto.classList.add cssClass + '-side'

     for side in faceNames
-      cube[side] = document.createElement 'div'
-      cube[side].className = "#{ cssClass }-#{ side }"
+      cube[side] = sideProto.cloneNode false
+      cube[side].classList.add "#{ cssClass }-side-#{ side }"
       rotation = do ->
         switch side
           when 'front'
@@ -108,7 +112,9 @@
       cube[side].style[css.transform] = "#{ rotation } translate3d(0, 0, #{ @size / 2 }px)" +
         (if @horizontalFlip then 'rotateZ(90deg)' else '')
       cube[side].style.fontSize = @fontSize
-      cube.el.appendChild cube[side]
+      cube.holder.appendChild cube[side]
+
+    cube.el.appendChild cube.holder

     eventPairs = [['TouchStart', 'MouseDown'], ['TouchMove', 'MouseMove'],
       ['TouchEnd', 'MouseUp'], ['TouchLeave', 'MouseLeave']]
@@ -146,7 +152,7 @@

   _setSides: (cube) ->
-    cube.el.style[css.transform] = @_getTransform cube.delta
+    cube.holder.style[css.transform] = @_getTransform cube.delta
     cube.offset = offset = Math.floor cube.delta / 90
     return if offset is cube.lastOffset
     cube.lastOffset = faceOffset = setOffset = offset
@@ -180,7 +186,7 @@
   _onTouchStart: (e, cube) ->
     e.preventDefault()
     cube.touchStarted = true
-    e.currentTarget.classList.add 'no-tween'
+    cube.holder.classList.add 'no-tween'
     if e.type is 'mousedown'
       cube.start = e[@eProp]
     else
@@ -209,8 +215,8 @@
     if cube.last % 90 isnt 0
       cube.last -= cube.last % 90

-    cube.el.classList.remove 'no-tween'
-    cube.el.style[css.transform] = @_getTransform cube.last
+    cube.holder.classList.remove 'no-tween'
+    cube.holder.style[css.transform] = @_getTransform cube.last

   _onTouchLeave: (e, cube) ->


applyDelta already records now

applyDelta already records now

--- src/paste-manager.coffee
+++ src/paste-manager.coffee
@@ -33,7 +33,6 @@
         delta.startLength = docLength
         oldDelta = @editor.doc.toDelta()
         @editor.applyDelta(delta)
-        @editor.undoManager.record(delta, oldDelta)
         @editor.root.focus()
         lengthAdded = Math.max(0, @editor.doc.length - docLength)
         @editor.setSelection(new Scribe.Range(@editor, selection.start.index + lengthAdded, selection.start.index + lengthAdded))


Screen: Fix pixel ratio in resize_to_canvas()

--- engine/screen.coffee
+++ engine/screen.coffee
@@ -67,12 +67,12 @@
             if not @context.vr_screen? and @auto_resize_to_canvas
                 @resize_to_canvas()

-    resize_to_canvas: (pixel_ratio_x=1, pixel_ratio_y=1) ->
+    resize_to_canvas: (ratio_x=@pixel_ratio_x, ratio_y=@pixel_ratio_y) ->
         {clientWidth, clientHeight} = @canvas
         if clientWidth == @width and clientHeight == @height and
-            pixel_ratio_x == @pixel_ratio_x and pixel_ratio_y == @pixel_ratio_y
+            ratio_x == @pixel_ratio_x and ratio_y == @pixel_ratio_y
                 return
-        @resize(clientWidth, clientHeight, pixel_ratio_x, pixel_ratio_y)
+        @resize(clientWidth, clientHeight, ratio_x, ratio_y)

     # Changes the resolution of the canvas and aspect ratio of viewports.
     # It doesn't handle the final size (that's done through HTML styles).


more logic

more logic

Signed-off-by: Kalman Olah <9c6f1d21f830c70c1a1dae4ae81fa22e2b8bc9d4@multatronic.com>

--- src/server/game.coffee
+++ src/server/game.coffee
@@ -1,5 +1,6 @@
 uuid = Npm.require 'node-uuid'
 md5 = Npm.require 'md5'
+

 ###
 Main DotD server-side logic, unrelated to general
@@ -49,6 +50,24 @@
         @_id = md5 uuid.v1()

+class Game.Event extends Game.Identifiable
+
+    ###
+    Game base event.
+    ###
+
+    @type: 'base'
+
+
+class Game.TickEvent extends Game.Event
+
+    ###
+    Game tick event.
+    ###
+
+    @type: 'tick'
+
+
 class Game.Loggable extends Game.Identifiable

     ###
@@ -56,11 +75,21 @@
     instance-specific logs.
     ###

+    ###
+    Log a message.
+    ###
     log: (args...) ->
         args[0] = "[#{@constructor.name}:#{@_id.substr(0, 8)}] " + args[0]
         console.log.apply console.log, args

     ###
+    Log a message.
+    ###
+    @log: (args...) ->
+        args[0] = "[#{@name}] " + args[0]
+        console.log.apply console.log, args
+
+    ###
     Constructor.
     ###
     constructor: () ->
@@ -118,7 +147,7 @@
     height: null

-class Game.Room
+class Game.Room extends Game.Loggable

     ###
     Room. A room contains a map, several players and so on
@@ -165,7 +194,9 @@
     Constructor.
     ###
     constructor: (@id, @name) ->
-        console.log "Creating room '#{@name}' with ID '#{@id}'"
+        super
+
+        @log "Creating room '#{@name}' with ID '#{@id}'"
         Game.rooms[@id] = @

     ###
@@ -193,35 +224,35 @@
     Add a player.
     ###
     addPlayer: (player) ->
-        console.log "Adding player '#{player.name}' to room '#{@name}'"
+        @log "Adding player '#{player.name}'"
         @players[player.id] = player

     ###
     Find a player by its primary identifier.
     ###
     findPlayer: (playerId) ->
-        console.log "Looking up player '#{playerId}'"
+        @log "Looking up player '#{playerId}'"
         @players[playerId]

     ###
     Remove a player.
     ###
     removePlayer: (player) ->
-        console.log "Removing player '#{player.name}' from room '#{@name}'"
+        @log "Removing player '#{player.name}'"
         delete @players[player.id]

     ###
     Find a room by its primary identifier.
     ###
     @find: (roomId) ->
-        console.log "Looking up room '#{roomId}'"
+        @log "Looking up room '#{roomId}'"
         Game.rooms[roomId]

     ###
     Attempt to remove a room.
     ###
     @remove: (room) ->
-        console.log "Removing room '#{room.name}'"
+        @log "Removing room '#{room.name}'"
         delete Game.rooms[room.name]

@@ -267,7 +298,9 @@
     Constructor.
     ###
     constructor: (@id, @name) ->
-        console.log "Creating player '#{@name}' with ID '#{@id}'"
+        super
+
+        @log "Creating player '#{@name}' with ID '#{@id}'"

 class Game.EventManager extends Game.StartStoppable
@@ -294,7 +327,7 @@
     @type integer
     @see `loop_forever`
     ###
-    loop_delay: 5
+    loop_delay: 2

     ###
     {@inheritDoc}
@@ -331,8 +364,8 @@
     Handle a single event.
     ###
     handle: (event) ->
-        if @handlers[event.type]
-            for handler in @handlers[event.type]
+        if @handlers[event.constructor.type]
+            for handler in @handlers[event.constructor.type]
                 result = handler event

                 if result == false
@@ -349,9 +382,28 @@
     ###
     add_handler: (event, handler) ->
         if not @handlers[event.type]
-            @handlers[event.type]
+            @handlers[event.type] = []

         @handlers[event.type].push handler
+
+
+class Game.PhysicsEngine extends Game.StartStoppable
+
+    ###
+    Game physics engine. In charge of processing
+    positional updates, detecting collisions, etc.
+    ###
+
+    ###
+    Handle a tick event.
+    ###
+    on_tick: (event) =>
+        # For each room, increment the current server tick counter
+        # Once we reach the required amount of server ticks for a room
+        # tick, we reset the counter.
+        for id, room of Game.rooms
+            continue
+

 ###
 When a player joins a room,
@@ -411,10 +463,14 @@
 Game.events = new Game.EventManager
 Game.events.start()

+Game.physics = new Game.PhysicsEngine
+Game.events.add_handler Game.TickEvent, Game.physics.on_tick
+Game.physics.start()
+

 ###
 Trigger a 'tick' periodically.
 ###
 setInterval () ->
-    Game.events.dispatch 'tick'
-, 1000
+    Game.events.dispatch new Game.TickEvent
+, 200


Avoid deprecation warnings for methods that are now on TextEditor

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -676,9 +676,8 @@

   # Returns the editor width in characters.
   getEditorWidthInChars: ->
-    width = @getWidth()
-    if width? and @defaultCharWidth > 0
-      Math.max(0, Math.floor(width / @defaultCharWidth))
+    if @width? and @defaultCharWidth > 0
+      Math.max(0, Math.floor(@width / @defaultCharWidth))
     else
       @editorWidthInChars

@@ -3337,7 +3336,7 @@

   setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin

-  getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@getWidth() / @getDefaultCharWidth()) - 1) / 2))
+  getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@width / @getDefaultCharWidth()) - 1) / 2))
   setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin

   getLineHeightInPixels: -> @lineHeightInPixels


Use the new `lineText` property

Use the new `lineText` property

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -776,7 +776,7 @@
   #
   # * `screenRow` A {Number} representing a zero-indexed screen row.
   lineTextForScreenRow: (screenRow) ->
-    @screenLineForScreenRow(screenRow)?.tokens.map((t) -> t.text).join('')
+    @screenLineForScreenRow(screenRow)?.lineText

   screenLineForScreenRow: (screenRow) ->
     @displayLayer.getScreenLines(screenRow, screenRow + 1)[0]


bugfix: using definition of this

bugfix: using definition of this

Definition is not defined and will throw an error, as it surely is meant to be the property definition of this.
This resolves https://github.com/noflo/noflo-ui/issues/436.

--- src/runtimes/base.coffee
+++ src/runtimes/base.coffee
@@ -2,7 +2,7 @@

 class BaseRuntime extends platform.EventEmitter
   constructor: (@definition) ->
-    @definition.capabilities = [] unless definition.capabilities
+    @definition.capabilities = [] unless @definition.capabilities
     @graph = null

   setMain: (@graph) ->


Bind the vote key

Bind the vote key

--- assets/js/nav.js.coffee
+++ assets/js/nav.js.coffee
@@ -10,6 +10,7 @@
   k: 75
   p: 80
   n: 78
+  v: 86

 $ ->

@@ -38,5 +39,7 @@
         $('#modal-container').hide()
       when KeyCodes.slash
         $('#modal-container').toggle() if e.shiftKey == true
+      when KeyCodes.v
+        $('.vote').click()

     window.location = url if url


comments since

comments since

--- iced2/main.iced
+++ iced2/main.iced
@@ -167,7 +167,8 @@
                     for milestone in repo.milestones
                         for issue in milestone.issues
                             {number} = issue
-                            github.comments {repo:full_name, number}, defer err["#{login} #{full_name} #{number}"], issue.comments
+                            since = '2001-01-01T00:00:00Z'
+                            github.comments {repo:full_name, number, since}, defer err["#{login} #{full_name} #{number}"], issue.comments
     authorize_uri = github.authorize_uri req
     res.render 'index', {inspect:util.inspect, authorize_uri, token, repos, orgs, err}


let rethink monkey patch wrapper allow for possibility that cb is undefined

--- salvus/rethink.coffee
+++ salvus/rethink.coffee
@@ -351,7 +351,7 @@
                     start_delay : 3000
                     factor      : 1.4
                     max_tries   : 15
-                    cb          : -> cb(error, result)
+                    cb          : -> cb?(error, result)

     table: (name, opts={readMode:'outdated'}) =>
         @db.table(name, opts)


update

update

--- test/basic-query-select.coffee
+++ test/basic-query-select.coffee
@@ -41,7 +41,7 @@
         done()
   it "should select multiple field with blank space", (done) ->
     request(app)
-      .get("/odata/books?$select=price,     title")
+      .get("/odata/books?$select=price,   title")
       .expect(200)
       .expect('Content-Type', /json/)
       .end (err, res) ->


added i to the regex

added i to the regex

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -48,7 +48,7 @@
     trelloIt subject, msg

   robot.hear /^(i|we) (sh|c)ould (.*)/i, (msg) ->
-    subject = msg.match[2]
+    subject = msg.match[3]
     unless process.env.TRELLO_API_KEY?
       msg.send "Please specify the Trello API key in TRELLO_API_KEY"
       return


fix(onboarding): Do not load themes in the onboarding window

Do not load user themes into the onboarding window, because it uses a wide range of hard-coded colors and assets and should always be on-brand.

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -200,6 +200,11 @@
         return true if NylasEnv.packages.resolvePackagePath(themeName)
         console.warn("Enabled theme '#{themeName}' is not installed.")
       false
+
+    # Do not load user themes into the onboarding window, because it uses
+    # a wide range of hard-coded colors and assets and should always be on-brand.
+    if NylasEnv.getWindowType() is 'onboarding'
+      themeNames = []

     # Use a built-in theme any time the configured themes are not
     # available.


Fix worker hashing edge case on chrome

Fix worker hashing edge case on chrome

--- scripts/worker.coffee
+++ scripts/worker.coffee
@@ -9,7 +9,9 @@
 , false)

 computeHash = (e) ->
-    hash = btoa(rstr_md5(e.data)).substr(0, 12).replace('+', '-', 'g').replace('/', '_', 'g')
+    hash = btoa(rstr_md5(e.data)).substr(0, 12)
+    hash = hash.replace(/\+/g, '-')
+    hash = hash.replace(/\//g, '_')
     self.postMessage({
         execute: "#{ e.callback }('#{ e.id }', '#{ hash }')"
     })


adds roles

adds roles

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -87,11 +87,13 @@
           label: 'Undo'
           accelerator: acceleratorMap['undo'][platform]
           selector: 'undo:' if platform == 'darwin'
+          role: 'undo' if platform == 'others'
         }
         {
           label: 'Redo'
           accelerator: acceleratorMap['redo'][platform]
           selector: 'redo:' if platform == 'darwin'
+          role: 'redo' if platform == 'others'
         }
     ]
     if platform == 'darwin'
@@ -101,21 +103,25 @@
             label: 'Cut'
             accelerator: acceleratorMap['cut'][platform]
             selector: 'cut:' if platform == 'darwin'
+            role: 'cut' if platform == 'others'
           }
           {
             label: 'Copy'
             accelerator: acceleratorMap['copy'][platform]
             selector: 'copy:' if platform == 'darwin'
+            role: 'copy' if platform == 'others'
           }
           {
             label: 'Paste'
             accelerator: acceleratorMap['paste'][platform]
             selector: 'paste:' if platform == 'darwin'
+            role: 'paste' if platform == 'others'
           }
           {
             label: 'Select All'
             accelerator: acceleratorMap['selectall'][platform]
             selector: 'selectAll:' if platform == 'darwin'
+            role: 'selectall' if platform == 'others'
           }
         ]
     tmpl


GameObject.Effect

GameObject.Effect

--- test/game_object_effect.coffee
+++ test/game_object_effect.coffee
@@ -8,8 +8,7 @@
   player.fadeOut 1, (player) ->
     fadedOut = true

-  30.times ->
-    player.update()
+  player.update(1)

   ok fadedOut


Remove the underscore from "NOT_RUNNING"

--- js/inner.coffee
+++ js/inner.coffee
@@ -209,6 +209,8 @@
         "timed out"
       when "no_tests"
         "no tests"
+      when "not_running"
+        "not running"
       else
         @status


Include common problems

Include common problems

--- js/outer.coffee
+++ js/outer.coffee
@@ -48,7 +48,7 @@
       # build a table of contents dynamically from all the pages. DRY.
       pages = [
                 "getting-started",
-#                "common-problems",
+                "common-problems",
 #                "faq",
                 "about-circle",
 #                "integrations",


Expose the session

Expose the session

--- framer/Framer.coffee
+++ framer/Framer.coffee
@@ -25,6 +25,7 @@
 Framer.SpringRK4Animator = (require "./Animators/SpringRK4Animator").SpringRK4Animator
 Framer.Importer = (require "./Importer").Importer
 Framer.Debug = (require "./Debug").Debug
+Framer.Session = (require "./Session").Session

 window.Framer = Framer if window


feat: remove schedule from output if not present

--- bin/tourney-time.coffee
+++ bin/tourney-time.coffee
@@ -27,4 +27,4 @@
                #{tourneySchedule.games} tourney games and #{playoffGames} playoff games
                which will take #{totalTime} """

-console.log schedule
+console.log schedule if schedule?.length


@todo's for sort

@todo's for sort

--- source/code/go.coffee
+++ source/code/go.coffee
@@ -167,8 +167,12 @@
           (val, key)->  #console.log '!!! called sort function(val, key, oa[val]): ', val, key, oa[val]
             sort.apply context, (fixForObj val, key)  # uBer sort always called with val, key
         else
+            # @todo : check this part!
+            # @ todo : provide shortcuts
+            #   ['val', 'v'] = (v)->v
+            #   ['key', 'k'] = (v,k)->k
           if _.isString sort #'field' or 'value' # @todo: array of Strings as sort key ?
-            (val)-> val #default sort field is a) the value of an array item and b) the key of an object
+            (val)-> val       #default sort field is a) the value of an array item and b) the key of an object
           else
             if _.isBoolean sort # todo: this is not working probably - check it!
               if sort then ->true else ->false
@@ -204,21 +208,18 @@
         if _.isObject grub
           _.extend grub, oa

-
-
-
   oa # a sorted Array or Object

 module.exports = go

 # inline dev tests - make them into specs!
-obj = ciba: 4, aaa: 7, b: 2, c: -1
-arrInt = [ 4, 7, 2, -1 ]
-arrStr = ['Pikoulas', 'Anodynos', 'Babylon', 'Agelos']
-
-newArr = [667]
-newObj = {"oldKey": "oldValue"}
-#
+#obj = ciba: 4, aaa: 7, b: 2, c: -1
+#arrInt = [ 4, 7, 2, -1 ]
+#arrStr = ['Pikoulas', 'Anodynos', 'Babylon', 'Agelos']
+#
+#newArr = [667]
+#newObj = {"oldKey": "oldValue"}
+##
 #result = go obj ,
 #          #fltr: ['ciba', 'b']
 #          fltr : (val, key)->
@@ -255,9 +256,9 @@
 #console.log "result=", result
 #console.log "newObj=", newObj
 #console.log "newArr=", newArr
-
-console.log go arrInt,
-        sort: (v)-> v #todo: true for value sorting
-        grub: newObj
-
-console.log newObj+#
+#console.log go arrInt,
+#        sort: (v)-> v #todo: true for value sorting
+#        grub: newObj
+#
+#console.log newObj


End group

End group

--- spec/SWTDetect.coffee
+++ spec/SWTDetect.coffee
@@ -33,6 +33,8 @@
     img = null
     beforeEach (done) ->
       fs.readFile __dirname+'/being-d4.jpg', (err, image) ->
+        unless image
+          return done()
         if err
           return done err
         img = new Image
@@ -61,7 +63,7 @@
           done()
         ins.beginGroup 'foo'
         ins.send canvas
-        # ins.endGroup()
+        ins.endGroup()

       it 'should find text regions', ->
         chai.expect(results).to.be.an 'array'


improve log output

improve log output

--- tasks/ftpscript.coffee
+++ tasks/ftpscript.coffee
@@ -77,14 +77,14 @@
           continue
         else if line in [
           '550 Create directory operation failed.'
-          '331 Please specify the password.'
           'Using binary mode to transfer files.'
           'Interactive mode off.'
+          'Passive mode on.'
         ] or
-        /^200 /.test(line) or
-        /^1\d{2} /.test(line) or
+        /^Remote system type is/.test(line) or
         /^\d+ bytes sent in [\d\.]+ secs/.test(line) or
-        /^Remote system type is/.test(line)
+        /^(200|227|230|331) /.test(line) or
+        /^1\d{2} /.test(line)
           grunt.verbose.writeln line
         else if /^5\d{2} /.test(line) or /^Not connected/.test(line)
           grunt.log.error line


Remove all references to vendor/packages

3rd party packages are stored in node_modules

--- src/app/config.coffee
+++ src/app/config.coffee
@@ -11,7 +11,6 @@
 bundledPackagesDirPath = path.join(resourcePath, "src/packages")
 nodeModulesDirPath = path.join(resourcePath, "node_modules")
 bundledThemesDirPath = path.join(resourcePath, "themes")
-vendoredPackagesDirPath = path.join(resourcePath, "vendor/packages")
 userThemesDirPath = path.join(configDirPath, "themes")
 userPackagesDirPath = path.join(configDirPath, "packages")
 userStoragePath = path.join(configDirPath, ".storage")
@@ -24,8 +23,8 @@
 class Config
   configDirPath: configDirPath
   themeDirPaths: [userThemesDirPath, bundledThemesDirPath]
-  bundledPackageDirPaths: [vendoredPackagesDirPath, bundledPackagesDirPath, nodeModulesDirPath]
-  packageDirPaths: [userPackagesDirPath, vendoredPackagesDirPath, bundledPackagesDirPath]
+  bundledPackageDirPaths: [bundledPackagesDirPath, nodeModulesDirPath]
+  packageDirPaths: [userPackagesDirPath, bundledPackagesDirPath]
   userPackagesDirPath: userPackagesDirPath
   userStoragePath: userStoragePath
   lessSearchPaths: [path.join(resourcePath, 'static'), path.join(resourcePath, 'vendor')]


Expand variables in the dependencies part of rules

--- src/coffeemake.coffee
+++ src/coffeemake.coffee
@@ -142,8 +142,19 @@
           rule = (compile x for x in rule)
           ->
             x.call @ for x in rule
+        expandVars = (x)->
+          y = [ ]
+          for z in x
+            if (match = /^[$][(]([^)]*)[)]$/.exec z)?
+              n = match[1]
+              n = @v[n]
+              if n? and (n = n.trim()).length
+                for zz in n.split(/\ +/)
+                  y.push zz
+            y.push z
+          y
         args = [ heading[1] ]
-        args = args.concat(heading[2].trim().split(/\ +/))
+        args = args.concat(expandVars(heading[2].trim().split(/\ +/)))
         args.push rule
         @.apply @, args
       heading = null


Fix undo stack for text input

Fix undo stack for text input

--- src/controller.coffee
+++ src/controller.coffee
@@ -689,15 +689,6 @@
   @dropletElement.focus()
   window.scrollTo(x, y)

-# UNDO STACK SUPPORT
-# ================================
-
-# We must declare a few
-# fields a populate time
-hook 'populate', 0, ->
-  @undoStack = []
-  @changeEventVersion = 0
-
 Editor::removeBlankLines = ->
   # If we have blank lines at the end,
   # get rid of them
@@ -706,6 +697,15 @@
     next = head.prev
     head.remove()
     head = next
+
+# UNDO STACK SUPPORT
+# ================================
+
+# We must declare a few
+# fields a populate time
+hook 'populate', 0, ->
+  @undoStack = []
+  @changeEventVersion = 0

 # Now we hook to ctrl-z to undo.
 hook 'keydown', 0, (event, state) ->
@@ -1886,17 +1886,18 @@
   @redrawMain(); @redrawTextInput()

 Editor::populateSocket = (socket, string) ->
-  lines = string.split '\n'
-
-  unless socket.start.next is socket.end
-    @spliceOut new model.List socket.start.next, socket.end.prev
-
-  first = last = new model.TextToken lines[0]
-  for line, i in lines when line > 0
-    last = helper.connect new model.NewlineToken(), last
-    last = helper.connect last, new model.TextToken line
-
-  @spliceIn (new model.List(first, last)), socket.start
+  unless socket.stringify() is string
+    lines = string.split '\n'
+
+    unless socket.start.next is socket.end
+      @spliceOut new model.List socket.start.next, socket.end.prev
+
+    first = last = new model.TextToken lines[0]
+    for line, i in lines when i > 0
+      last = helper.connect new model.NewlineToken(), last
+      last = helper.connect last, new model.TextToken line
+
+    @spliceIn (new model.List(first, last)), socket.start

 # Convenience hit-testing function
 Editor::hitTestTextInput = (point, block) ->


adding debug

adding debug

--- src/github-tfs.coffee
+++ src/github-tfs.coffee
@@ -215,9 +215,11 @@
     httpntlm.get tfsApiCall, (apiCallErr, apiCallRes) ->
       if apiCallErr
         res.reply "Encountered an error :( #{apiCallErr}"
+        robot.logger.debug apiCallErr
         return
       else if apiCallRes.statusCode isnt 200
         res.reply "Request came back with a problem :( Response code is #{apiCallRes.statusCode}."
+        robot.logger.debug apiCallErr
         return
       else
         result = JSON.parse apiCallRes.body
@@ -249,9 +251,11 @@
     httpntlm.post tfsApiCall, (apiCallErr, apiCallRes) ->
       if apiCallErr
         res.reply "Encountered an error :( #{apiCallErr}"
+        robot.logger.debug apiCallErr
         return
       else if apiCallRes.statusCode isnt 200
         res.reply "Request came back with a problem :( Response code is #{apiCallRes.statusCode}."
+        robot.logger.debug apiCallErr
         return
       else
         #buildTable is expecting an array


switched back to streaming

switched back to streaming

--- test/fakeserver.coffee
+++ test/fakeserver.coffee
@@ -10,7 +10,6 @@
 chai.use require('chai-subset')
 chai.use require('chai-as-promised')

-
 hm = require '../lib/index'

 ccu='localhost:4000'
@@ -18,7 +17,7 @@

 readFile = (fname) ->
 	fpath = path.join __dirname, "resources/#{fname}"
-	fs.readFileSync fpath
+	fs.createReadStream fpath

 fakeSrv = (req, resp) ->
 			urlpath = url.parse(req.url).pathname
@@ -26,11 +25,9 @@
 			if tok[1] is 'config' && tok[2] is 'xmlapi'
 				switch tok[3]
 					when 'programlist.cgi'
-						resp.end readFile('programlist.xml')
-						#readFile('programlist.xml').pipe resp
+						readFile('programlist.xml').pipe resp
 					when 'statelist.cgi'
-						resp.end readFile('statelist.xml')
-						#readFile('statelist.xml').pipe resp
+						readFile('statelist.xml').pipe resp
 					when 'runprogram.cgi'
 						progid =  url.parse(req.url,true).query['program_id']
 						lastprog = progid