Updated method deleteUser.

Updated method deleteUser.

--- server/methods/deleteUser.coffee
+++ server/methods/deleteUser.coffee
@@ -4,7 +4,7 @@
 		#	console.log(Meteor.userId())
 		#	throw new Meteor.Error('invalid-user', "[methods] deleteUser -> Invalid user")

-		user = RocketChat.models.Users.findOneById Meteor.userId()
+		# user = RocketChat.models.Users.findOneById Meteor.userId()

 		#unless RocketChat.authz.hasPermission(Meteor.userId(), 'delete-user') is true
 		#	throw new Meteor.Error 'not-authorized', '[methods] deleteUser -> Not authorized'


Adding navlinks data and page visit path routing

--- widow-analyze/src/main/coffee/app.coffee
+++ widow-analyze/src/main/coffee/app.coffee
@@ -1,4 +1,11 @@
 app = angular.module 'widow-analyze', ['wa.controllers', 'ui.router']
+
+app.run ($rootScope) ->
+
+  $rootScope.navlinks = [
+    {sref: 'home' , text: 'Home' }
+    {sref: 'about', text: 'About'}
+  ]

 app.config ($stateProvider, $urlRouterProvider) ->

@@ -12,4 +19,9 @@
     templateUrl: 'templates/pageSummary.html',
     controller: 'PageSummaryController'

+  $stateProvider.state 'pageVisit',
+    url: '/page/:id/:time',
+    templateUrl: 'templates/pageVisit.html',
+    controller: 'PageVisitController'
+
   $urlRouterProvider.otherwise '/home'


correct erroneous appearance of list literal

--- src/MKTS.coffee
+++ src/MKTS.coffee
@@ -756,13 +756,11 @@
   index 3) and plain objects. The value returned will be a shallow copy in the case of objects and
   lists, but if a list has a value at index 3, that object will also be copied. Not guaranteed to
   work for general values. ###
-  R = if ( isa_list = CND.isa_list x )
-    []
-  else if CND.isa_pod x
-    {}
+  if ( isa_list = CND.isa_list x ) then R = []
+  else if         CND.isa_pod  x   then R = {}
   else throw new Error "unable to cpy a #{CND.type_of x}"
   R       = Object.assign R, x, updates...
-  R[ 3 ]  = Object.assign [], meta if isa_list and ( meta = R[ 3 ] )?
+  R[ 3 ]  = Object.assign {}, meta if isa_list and ( meta = R[ 3 ] )?
   return R

 #-----------------------------------------------------------------------------------------------------------


Correctly passthrough call to deactivatePackages

--- src/atom.coffee
+++ src/atom.coffee
@@ -92,7 +92,7 @@
   setPackageState: (args...) -> @packages.setPackageState(args...)
   activatePackages: (args...) -> @packages.activatePackages(args...)
   activatePackage: (args...) -> @packages.activatePackage(args...)
-  deactivatePackages: (args...) -> @packages.deactivatePackage(args...)
+  deactivatePackages: (args...) -> @packages.deactivatePackages(args...)
   deactivatePackage: (args...) -> @packages.deactivatePackage(args...)
   getActivePackage: (args...) -> @packages.getActivePackage(args...)
   isPackageActive: (args...) -> @packages.isPackageActive(args...)


GruntCommand being subclassed from codo/command

--- src/codo.coffee
+++ src/codo.coffee
@@ -1,15 +1,79 @@
 'use strict'

+require 'coffee-script'
+
+Codo = require 'codo/lib/codo'
+Command = require 'codo/lib/command'
+Table = require 'cli-table'
+
 module.exports = (grunt) ->
-
-  codo = require 'codo'
+
+  class GruntCommand extends Command
+    generate: ->
+      environment = Codo.parseProject(@input, @options)
+      sections    = @collectStats(environment)
+
+      @theme.compile(environment)
+
+      if @options.undocumented
+        for section, data of sections when data.undocumented.length != 0
+          table = new Table
+            head: [section, 'Path']
+
+          table.push(entry) for entry in data.undocumented
+          grunt.log.writeln table.toString()
+          grunt.log.writeln ''
+      else
+        overall      = 0
+        undocumented = 0
+
+        for section, data of sections
+          overall      += data.total
+          undocumented += data.undocumented.length
+
+        table = new Table
+          head: ['', 'Total', 'Undocumented']
+
+        table.push(
+          ['Files', environment.allFiles().length, ''],
+          ['Extras', environment.allExtras().length, ''],
+          ['Classes', sections['Classes'].total, sections['Classes'].undocumented.length],
+          ['Mixins', sections['Mixins'].total, sections['Mixins'].undocumented.length],
+          ['Methods', sections['Methods'].total, sections['Methods'].undocumented.length]
+        )
+
+        grunt.log.writeln table.toString()
+        grunt.log.writeln ''
+        grunt.log.writeln "  Totally documented: #{(100 - 100/overall*undocumented).toFixed(2)}%"
+        grunt.log.writeln ''
+

   grunt.registerTask 'codo', 'Generates Codo documentation', ->

     options = @options
       source: 'src'
       output: 'doc'
+      theme: 'default'
+      quiet: false
+      verbose: false
+      undocumented: false
+      closure: false
+      debug: true

-    done = @async()
-
-    done()
+    GruntCommand::options = [
+      {name: 'help', alias: 'h', describe: 'Show this help'}
+      {name: 'version', describe: 'Show version'}
+      {name: 'output', alias: 'o', describe: 'The output directory', default: options.output}
+      {name: 'output-dir'}
+      {name: 'theme', describe: 'The theme to be used', default: options.theme}
+      {name: 'name', alias: 'n', describe: 'The project name used'}
+      #{name: 'readme', alias: 'r', describe: 'The readme file used'}
+      {name: 'quiet', alias: 'q', describe: 'Supress warnings', boolean: true, default: options.quiet}
+      {name: 'verbose', alias: 'v', describe: 'Show parsing errors', boolean: true, default: options.verbose}
+      {name: 'undocumented', alias: 'u', describe: 'List undocumented objects', boolean: true, default: options.undocumented}
+      {name: 'closure', describe: 'Try to parse closure-like block comments', boolean: true, default: options.closure}
+      {name: 'debug', alias: 'd', boolean: options.debug}
+    ]
+
+    GruntCommand.run()
+


Trigger first update immediately after start

--- gitcommander.coffee
+++ gitcommander.coffee
@@ -57,6 +57,8 @@
   console.log "* Starting timer with timout #{TIMEOUT}."
   timer = setInterval timerCallback, TIMEOUT
   do timer.unref
+  # trigger first update immediately
+  do timerCallback

 events = []
 handleEvents = (err, res) ->


"body-only-if" and "w-config" attribute highlighting

--- grammars/marko.cson
+++ grammars/marko.cson
@@ -470,7 +470,7 @@
     'match': '(?<=[^=])\\b([a-zA-Z0-9:-]+)'
     'name': 'entity.other.attribute-name.html'
   'tag-marko-attribute':
-    'match': '(?<=[^=])\\b((if|for|else-if|attrs|w-bind|w-el-id|w-id)\\s*=|(else)\\b)'
+    'match': '(?<=[^=])\\b((if|for|else-if|body-only-if|attrs|w-bind|w-config|w-el-id|w-id)\\s*=|(else)\\b)'
     'captures':
       '2':
         'name': 'support.function.marko_attribute'


Remove properties that are later defined


[grunt] move requires in proper position in nunjucks task

--- tasks/grunt-nunjucks-2-html.coffee
+++ tasks/grunt-nunjucks-2-html.coffee
@@ -3,6 +3,17 @@
 https://github.com/vitkarpov/grunt-nunjucks-2-html
 Render nunjucks templates
 ###
+
+_            = require('lodash')
+path         = require('path')
+numbro       = require('numbro')
+moment       = require('moment')
+smartPlurals = require('smart-plurals')
+printf       = require('../modules/printf')
+marked       = require('marked')
+markdown     = require('nunjucks-markdown')
+nunjucks     = require('nunjucks')
+urlify       = require('urlify')

 module.exports = (grunt) ->
   # ======
@@ -35,20 +46,7 @@
       trim              : true
       failureOutput     : 'non-printable-url'

-  # ====================
-  # Requires and caching
-  # ====================
-
-  _            = require('lodash')
-  path         = require('path')
-  numbro       = require('numbro')
-  moment       = require('moment')
-  smartPlurals = require('smart-plurals')
-  printf       = require('../modules/printf')
-  marked       = require('marked')
-  markdown     = require('nunjucks-markdown')
-  nunjucks     = require('nunjucks')
-  urlify       = require('urlify').create({
+  urlify = urlify.create({
     addEToUmlauts : taskConfig.urlify.addEToUmlauts
     szToSs        : taskConfig.urlify.szToSs
     spaces        : taskConfig.urlify.spaces
@@ -58,13 +56,13 @@
     failureOutput : taskConfig.urlify.failureOutput
   })

-  gettext       = taskConfig.i18n.gettext
-
-  locales       = _.map(taskConfig.i18n.locales, 'locale')
-  baseLocale    = taskConfig.i18n.baseLocale
-
-  buildDir      = taskConfig.files.dest
-  templatesDir  = taskConfig.files.cwd
+  gettext      = taskConfig.i18n.gettext
+
+  locales      = _.map(taskConfig.i18n.locales, 'locale')
+  baseLocale   = taskConfig.i18n.baseLocale
+
+  buildDir     = taskConfig.files.dest
+  templatesDir = taskConfig.files.cwd

   # =======
   # Helpers


SocialWorker: remove appstorage relate stuff

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -19,7 +19,6 @@
   JName               = require './name'
   JKite               = require './kite'
   JStorage            = require './storage'
-  JAppStorage         = require './appstorage'
   JCombinedAppStorage = require './combinedappstorage'

   @getFlagRole            = 'content'
@@ -273,15 +272,10 @@

       # bongo doesn't wait models to be loaded and this causes errors
       # in node.js tests so synchronously requiring them
-      JAppStorage      = require './appstorage'
       JCredential      = require './computeproviders/credential'
       JStackTemplate   = require './computeproviders/stacktemplate'

       return {
-        appStorage    :
-          as          : 'appStorage'
-          targetType  : JAppStorage
-
         storage       :
           as          : 'storage'
           targetType  : 'JStorage'
@@ -954,68 +948,6 @@
       return callback err, storage

-  migrateOldAppStorageIfExists: (options, callback) ->
-
-    { appId, version } = options
-
-    unless appId and version
-      return callback new KodingError 'version and appId is not set!'
-
-    accountId    = @getId()
-    newStorage   = null
-    oldStorages  = null
-    luckyStorage = null
-
-    queue = [
-
-      =>
-        # trying to fetch old app storages, there were more than one
-        # appStorages for the same appId and version, probably due to some bug
-        # so we are trying to pick the one that contains data in it
-        query = { 'data.appId':appId, 'data.version':version }
-        @fetchAppStorages query, (err, storages) ->
-          return calback err  if err
-          # returning storage null bcs there was no old storage to be migrated
-          return callback null, null  unless storages.length > 0
-
-          storages.forEach (storage) ->
-            # if storage bucket is not empty pick it as the lucky one
-            luckyStorage = storage  unless _.isEmpty storage?.bucket
-
-          # if there was no lucky storage, pick the first one
-          luckyStorage ?= storages[0]
-          oldStorages   = storages
-          queue.next()
-
-      =>
-        # creating new app storage document
-        options.data = luckyStorage?.bucket or {}
-        @createAppStorage options, (err, storage) ->
-          return callback err  if err
-          newStorage = storage
-          queue.next()
-
-      ->
-        _queue = []
-        # removing old storages
-        oldStorages.forEach (oldStorage) ->
-          _queue.push ->
-            oldStorage.remove (err) ->
-              return _queue.fin err  if err
-              _queue.fin()
-
-        dash _queue, (err) ->
-          return callback err  if err
-          queue.next()
-
-      ->
-        callback null, newStorage
-
-    ]
-
-    daisy queue
-
-
   fetchAppStorage$: secure (client, options, callback) ->

     unless client?.connection?.delegate


general: added my local dev account to dummyAdmins. may it be short-lived.

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -333,7 +333,7 @@
     , (err, count)=>
       @update ($set: 'counts.topics': count), ->

-  dummyAdmins = ["sinan", "devrim", "aleksey-m", "gokmen", "chris", "arvidkahl"]
+  dummyAdmins = ["sinan", "devrim", "aleksey-m", "gokmen", "chris", "arvidkahl", "testdude"]

   flagAccount: secure (client, flag, callback)->
     {delegate} = client.connection


account: bugfix: support queries by status

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -1290,22 +1290,30 @@
     if delegate is this or delegate.can 'administer accounts'
       @fetchDecoratedPaymentMethods callback

-  fetchSubscriptions$: secure ({ connection:{ delegate }}, tags, callback) ->
+  fetchSubscriptions$: secure ({ connection:{ delegate }}, options, callback) ->
     return callback { message: 'Access denied!' }  unless @equals delegate

-    [callback, tags] = [tags, callback]  unless callback
+    [callback, options] = [options, callback]  unless callback
+
+    { tags, status } = options

-    options =
-      if tags?
-      then targetOptions: selector: tags: $in: tags
-      else {}
-
-    @fetchSubscriptions {}, options, callback
-
-  fetchPlansAndSubscriptions: secure (client, tags, callback) ->
+    selector = {}
+    queryOptions = targetOptions: { selector }
+
+    selector.tags = $in: tags  if tags
+
+    selector.status = status ? $in: [
+      'active'
+      'past_due'
+      'future'
+    ]
+
+    @fetchSubscriptions {}, queryOptions, callback
+
+  fetchPlansAndSubscriptions: secure (client, options, callback) ->
     JPaymentPlan = require './payment/plan'

-    @fetchSubscriptions$ client, tags, (err, subscriptions) ->
+    @fetchSubscriptions$ client, options, (err, subscriptions) ->
       return callback err  if err

       planCodes = (s.planCode for s in subscriptions)


JSession add index for clientIP

JSession add index for clientIP

--- workers/social/lib/social/models/session.coffee
+++ workers/social/lib/social/models/session.coffee
@@ -18,6 +18,7 @@
     indexes         :
       clientId      : 'unique'
       username      : 'descending'
+      clientIP      : 'sparse'
     schema          :
       clientId      : String
       clientIP      : String


Fix query coalescing and cache type validators

--- src/Route.coffee
+++ src/Route.coffee
@@ -52,17 +52,10 @@

     if isValid @query, "object"
       queryTypes = @query
-
       Object.keys(queryTypes).forEach (key) ->
-        type = queryTypes[key]
-        if type is Number
-          queryTypes[key] = stringToNumber
-        else if type is Boolean
-          queryTypes[key] = stringToBoolean
-        return
-
+        queryTypes[key] = valido.get queryTypes[key]
       return (req, res) -> Promise.try ->
-        return error if error = validateTypes req.query, queryTypes
+        return error if error = validateQuery req.query, queryTypes
         return responder.call req, req.query, res

     if @body is yes
@@ -73,6 +66,8 @@

     if isValid @body, "object"
       bodyTypes = @body
+      Object.keys(bodyTypes).forEach (key) ->
+        bodyTypes[key] = valido.get bodyTypes[key]
       return (req, res) ->
         readBody(req).then ->
           return Error "Missing body" unless req.body
@@ -89,30 +84,6 @@
 #
 # Helpers
 #
-
-stringToNumber = (query, key) ->
-  value = parseInt query[key]
-
-  if isNaN value
-    return Error "Expected '#{key}' to be a Number"
-
-  query[key] = value
-  return
-
-stringToBoolean = (query, key) ->
-  value = query[key]
-
-  if (value is "") or (value is "true")
-    value = yes
-
-  else if (value is undefined) or (value is "false")
-    value = no
-
-  if isValid value, "boolean"
-    query[key] = value
-    return
-
-  return Error "Expected '#{key}' to be a Boolean"

 createRegex = (pattern) ->
   paramRE = /:[^\/]+/gi
@@ -136,7 +107,32 @@

 validateTypes = (obj, types) ->
   for key, type of types
-    type = valido.get type
-    if error = type.assert obj[key]
-      return error key
+    error = type.assert obj[key]
+    return error key if error
   return
+
+validateQuery = (obj, types) ->
+  for key, type of types
+    coalesce obj, key, type.name
+    error = type.assert obj[key]
+    return error key if error
+  return
+
+coalesce = (obj, key, type) ->
+  value = obj[key]
+  switch type
+
+    when "number"
+      value = parseInt value
+      unless isNaN value
+        obj[key] = value
+
+    when "boolean"
+
+      if (value is undefined) or (value is "false")
+        obj[key] = false
+
+      else if (value is "") or (value is "true")
+        obj[key] = true
+
+  return


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -18,14 +18,17 @@
     switch hexString.length
       when 3, 4 then
         return [
-          parseInt(hexString.substr(0, 1), 16) * 0x11
+          parseInt(hexString.substr(0, 1), 16) * 0x11
+          parseInt(hexString.substr(1, 1), 16) * 0x11
+          parseInt(hexString.substr(2, 1), 16) * 0x11
+          if hexString.substr(3, 1).length then (parseInt(hexString.substr(3, 1), 16) * 0x11) / 255.0)
         ]
       else
         return [
           parseInt(hexString.substr(0, 2), 16)
           parseInt(hexString.substr(2, 2), 16)
           parseInt(hexString.substr(4, 2), 16)
-          if hexString.substr(6, 2).length then parseInt(hexString.substr(6, 2) / 255.0) else 1.0
+          if hexString.substr(6, 2).length then parseInt(hexString.substr(6, 2), 16) / 255.0 else 1.0
         ]

   parseRGB = (colorString) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -2826,6 +2826,14 @@
   names.each (element) ->
     lookup[normalizeKey(element[1])] = parseHex(element[0])

+  Color.fromHue = (hue) ->
+    color = Color(0, 0, 0, 1)
+    color.hue(hue)
+    color.saturation(1)
+    color.lightness(0.5)
+
+    return color
+
   Color.random = ->
     Color(rand(256), rand(256), rand(256))


Support hints for rotated layers

Support hints for rotated layers

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -1212,12 +1212,6 @@
 		return if not @visible
 		return if @opacity is 0

-		# We do not support rotated layers
-		return if @rotation isnt 0
-		return if @rotationX isnt 0
-		return if @rotationY isnt 0
-		return if @rotationZ isnt 0
-
 		# If we don't need to show a hint exit but pass to children
 		unless @shouldShowHint(targetLayer)
 			layer._showHint(targetLayer) for layer in @children


Show oldest events first

Show oldest events first

--- gitcommander.coffee
+++ gitcommander.coffee
@@ -12,6 +12,7 @@
 nconf.file CONFIG_FILE

 IRC_OPTS = nconf.get "irc"
+CHANNELS = IRC_OPTS.options.channels
 GIT_OPTS = nconf.get "git"
 RECENCY = nconf.get "recency"
 TIMEOUT = nconf.get "timeout"
@@ -37,31 +38,31 @@
   timer = setInterval timerCallback, TIMEOUT
   timer.unref

-events = []
 eventTeller =
   "PushEvent": (channel, event) ->
     ircClient.say channel, "[#{event.created_at}] #{event.actor.login} pushed
       #{event.payload.size} commit(s) to #{event.repo.name}"

+events = []
 handleEvents = (err, res) ->
   if err? or not res?
     console.log "! Error when handling events: #{err}."
     return

   nofEvents = 0
-  res.reverse
-  now = Date.now()
+  do res.reverse
+  now = do Date.now

   for event in res
     # ignore events we've already seen and those older than one hour
     eventDate = new Date event.created_at
-    age = now - eventDate.getTime()
+    age = now - do eventDate.getTime
     if age >= RECENCY or event.id in events
       continue
     events.push event.id
     nofEvents++
     if event.type of eventTeller
-      for channel in IRC_OPTS.options.channels
+      for channel in CHANNELS
         eventTeller[event.type] channel, event

   console.log "* Seen #{nofEvents} new events (#{events.length} total)."


Reformat spec

Reformat spec

--- spec/language-csound-spec.coffee
+++ spec/language-csound-spec.coffee
@@ -22,36 +22,151 @@

       tokens = lines[0]
       expect(tokens.length).toBe 15
-      expect(tokens[0]).toEqual value: 'instr', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'keyword.function.csound']
-      expect(tokens[1]).toEqual value: '/*', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[2]).toEqual value: '*/', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[3]).toEqual value: '1', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'entity.name.function.csound']
-      expect(tokens[4]).toEqual value: ',', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound']
-      expect(tokens[5]).toEqual value: '/*', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[6]).toEqual value: '*/', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[7]).toEqual value: 'N_a_M_e_', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'entity.name.function.csound']
-      expect(tokens[8]).toEqual value: ',', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound']
-      expect(tokens[9]).toEqual value: '/*', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[10]).toEqual value: '*/', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[11]).toEqual value: '+', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound']
-      expect(tokens[12]).toEqual value: 'Name', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'entity.name.function.csound']
-      expect(tokens[13]).toEqual value: '//', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound', 'comment.line.csound', 'punctuation.definition.comment.line.csound']
-      expect(tokens[14]).toEqual value: '', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.instrument-declaration.csound']
+      expect(tokens[0]).toEqual value: 'instr', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'keyword.function.csound'
+      ]
+      expect(tokens[1]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[2]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[3]).toEqual value: '1', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'entity.name.function.csound'
+      ]
+      expect(tokens[4]).toEqual value: ',', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+      ]
+      expect(tokens[5]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[6]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[7]).toEqual value: 'N_a_M_e_', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'entity.name.function.csound'
+      ]
+      expect(tokens[8]).toEqual value: ',', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+      ]
+      expect(tokens[9]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[10]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[11]).toEqual value: '+', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+      ]
+      expect(tokens[12]).toEqual value: 'Name', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'entity.name.function.csound'
+      ]
+      expect(tokens[13]).toEqual value: '//', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+        'comment.line.csound'
+        'punctuation.definition.comment.line.csound'
+      ]
+      expect(tokens[14]).toEqual value: '', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.instrument-declaration.csound'
+      ]

       tokens = lines[1]
-      expect(tokens[0]).toEqual value: 'aLabel', scopes: ['source.csound', 'meta.instrument-block.csound', 'entity.name.label.csound']
-      expect(tokens[1]).toEqual value: ':', scopes: ['source.csound', 'meta.instrument-block.csound', 'entity.punctuation.label.csound']
+      expect(tokens.length).toBe 2
+      expect(tokens[0]).toEqual value: 'aLabel', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'entity.name.label.csound'
+      ]
+      expect(tokens[1]).toEqual value: ':', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'entity.punctuation.label.csound'
+      ]

       tokens = lines[2]
-      expect(tokens[1]).toEqual value: 'i', scopes: ['source.csound', 'meta.instrument-block.csound', 'storage.type.csound']
-      expect(tokens[2]).toEqual value: 'Duration', scopes: ['source.csound', 'meta.instrument-block.csound', 'meta.other.csound']
-      expect(tokens[3]).toEqual value: ' ', scopes: ['source.csound', 'meta.instrument-block.csound']
-      expect(tokens[4]).toEqual value: '=', scopes: ['source.csound', 'meta.instrument-block.csound', 'keyword.operator.csound']
-      expect(tokens[5]).toEqual value: ' ', scopes: ['source.csound', 'meta.instrument-block.csound']
-      expect(tokens[6]).toEqual value: 'p3', scopes: ['source.csound', 'meta.instrument-block.csound', 'support.variable.csound']
+      expect(tokens.length).toBe 7
+      expect(tokens[1]).toEqual value: 'i', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'storage.type.csound'
+      ]
+      expect(tokens[2]).toEqual value: 'Duration', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'meta.other.csound'
+      ]
+      expect(tokens[3]).toEqual value: ' ', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+      ]
+      expect(tokens[4]).toEqual value: '=', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'keyword.operator.csound'
+      ]
+      expect(tokens[5]).toEqual value: ' ', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+      ]
+      expect(tokens[6]).toEqual value: 'p3', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'support.variable.csound'
+      ]

       tokens = lines[3]
-      expect(tokens[0]).toEqual value: 'endin', scopes: ['source.csound', 'meta.instrument-block.csound', 'keyword.other.csound']
+      expect(tokens.length).toBe 1
+      expect(tokens[0]).toEqual value: 'endin', scopes: [
+        'source.csound'
+        'meta.instrument-block.csound'
+        'keyword.other.csound'
+      ]

     it 'tokenizes user-defined opcodes', ->
       # The Csound Orchestra grammar relies on the existence of an active text
@@ -66,26 +181,123 @@

           tokens = lines[0]
           expect(tokens.length).toBe 14
-          expect(tokens[0]).toEqual value: 'opcode', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'keyword.function.csound']
-          expect(tokens[1]).toEqual value: '/*', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-          expect(tokens[2]).toEqual value: '*/', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-          expect(tokens[3]).toEqual value: 'aUDO', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'entity.name.function.opcode.csound']
-          expect(tokens[4]).toEqual value: ',', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound']
-          expect(tokens[5]).toEqual value: '/*', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-          expect(tokens[6]).toEqual value: '*/', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-          expect(tokens[7]).toEqual value: '0', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'meta.opcode-argument-types.csound', 'storage.type.csound']
-          expect(tokens[8]).toEqual value: ',', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'meta.opcode-argument-types.csound']
-          expect(tokens[9]).toEqual value: '/*', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-          expect(tokens[10]).toEqual value: '*/', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-          expect(tokens[11]).toEqual value: '0', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'meta.opcode-argument-types.csound', 'storage.type.csound']
-          expect(tokens[12]).toEqual value: '//', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound', 'meta.opcode-details.csound', 'meta.opcode-argument-types.csound', 'comment.line.csound', 'punctuation.definition.comment.line.csound']
-          expect(tokens[13]).toEqual value: '', scopes: ['source.csound', 'meta.opcode-definition.csound', 'meta.opcode-declaration.csound']
+          expect(tokens[0]).toEqual value: 'opcode', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'keyword.function.csound'
+          ]
+          expect(tokens[1]).toEqual value: '/*', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.begin.csound'
+          ]
+          expect(tokens[2]).toEqual value: '*/', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.end.csound'
+          ]
+          expect(tokens[3]).toEqual value: 'aUDO', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'entity.name.function.opcode.csound'
+          ]
+          expect(tokens[4]).toEqual value: ',', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+          ]
+          expect(tokens[5]).toEqual value: '/*', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.begin.csound'
+          ]
+          expect(tokens[6]).toEqual value: '*/', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.end.csound'
+          ]
+          expect(tokens[7]).toEqual value: '0', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'meta.opcode-argument-types.csound'
+            'storage.type.csound'
+          ]
+          expect(tokens[8]).toEqual value: ',', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'meta.opcode-argument-types.csound'
+          ]
+          expect(tokens[9]).toEqual value: '/*', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.begin.csound'
+          ]
+          expect(tokens[10]).toEqual value: '*/', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'comment.block.csound'
+            'punctuation.definition.comment.end.csound'
+          ]
+          expect(tokens[11]).toEqual value: '0', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'meta.opcode-argument-types.csound'
+            'storage.type.csound'
+          ]
+          expect(tokens[12]).toEqual value: '//', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound'
+            'meta.opcode-details.csound'
+            'meta.opcode-argument-types.csound'
+            'comment.line.csound'
+            'punctuation.definition.comment.line.csound'
+          ]
+          expect(tokens[13]).toEqual value: '', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'meta.opcode-declaration.csound']

           tokens = lines[1]
-          expect(tokens[0]).toEqual value: 'aUDO', scopes: ['source.csound', 'meta.opcode-definition.csound', 'entity.name.function.opcode.csound']
+          expect(tokens.length).toBe 1
+          expect(tokens[0]).toEqual value: 'aUDO', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'entity.name.function.opcode.csound'
+          ]

           tokens = lines[2]
-          expect(tokens[0]).toEqual value: 'endop', scopes: ['source.csound', 'meta.opcode-definition.csound', 'keyword.other.csound']
+          expect(tokens.length).toBe 1
+          expect(tokens[0]).toEqual value: 'endop', scopes: [
+            'source.csound'
+            'meta.opcode-definition.csound'
+            'keyword.other.csound'
+          ]

     it 'tokenizes preprocessor directives', ->
       preprocessorDirectives = [
@@ -99,38 +311,101 @@
         '@ \t0'
         '@@ \t0'
       ]
-      lines = grammar.tokenizeLines preprocessorDirectives.join('\n')
+      lines = grammar.tokenizeLines(preprocessorDirectives.join '\n')
       for i in [0...lines.length]
-        expect(lines[i][0]).toEqual value: preprocessorDirectives[i], scopes: ['source.csound', 'keyword.preprocessor.csound']
+        expect(lines[i][0]).toEqual value: preprocessorDirectives[i], scopes: [
+          'source.csound'
+          'keyword.preprocessor.csound'
+        ]

     it 'tokenizes includes', ->
       lines = grammar.tokenizeLines '#include/**/"file.udo"'
       tokens = lines[0]
-      expect(tokens[0]).toEqual value: '#include', scopes: ['source.csound', 'keyword.include.preprocessor.csound']
-      expect(tokens[1]).toEqual value: '/*', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[2]).toEqual value: '*/', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[3]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.include.csound', 'punctuation.definition.string.begin.csound']
-      expect(tokens[4]).toEqual value: 'file.udo', scopes: ['source.csound', 'string.quoted.include.csound']
-      expect(tokens[5]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.include.csound', 'punctuation.definition.string.end.csound']
+      expect(tokens[0]).toEqual value: '#include', scopes: [
+        'source.csound'
+        'keyword.include.preprocessor.csound'
+      ]
+      expect(tokens[1]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[2]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[3]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.include.csound'
+        'punctuation.definition.string.begin.csound'
+      ]
+      expect(tokens[4]).toEqual value: 'file.udo', scopes: [
+        'source.csound'
+        'string.quoted.include.csound'
+      ]
+      expect(tokens[5]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.include.csound'
+        'punctuation.definition.string.end.csound'
+      ]

     it 'tokenizes macro definitions', ->
       lines = grammar.tokenizeLines '# \tdefine/**/MACRO(ARGUMENT)/**/#$ARGUMENT#'
       tokens = lines[0]
-      expect(tokens[0]).toEqual value: '# \tdefine', scopes: ['source.csound', 'keyword.define.preprocessor.csound']
-      expect(tokens[1]).toEqual value: '/*', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[2]).toEqual value: '*/', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[3]).toEqual value: 'MACRO', scopes: ['source.csound', 'entity.name.function.preprocessor.csound']
-      expect(tokens[4]).toEqual value: '(', scopes: ['source.csound']
-      expect(tokens[5]).toEqual value: 'ARGUMENT', scopes: ['source.csound', 'variable.parameter.preprocessor.csound']
-      expect(tokens[6]).toEqual value: ')', scopes: ['source.csound']
-      expect(tokens[7]).toEqual value: '/*', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.begin.csound']
-      expect(tokens[8]).toEqual value: '*/', scopes: ['source.csound', 'comment.block.csound', 'punctuation.definition.comment.end.csound']
-      expect(tokens[9]).toEqual value: '#', scopes: ['source.csound', 'punctuation.definition.macro.begin.csound']
-      expect(tokens[10]).toEqual value: '$ARGUMENT', scopes: ['source.csound', 'entity.name.function.preprocessor.csound']
-      expect(tokens[11]).toEqual value: '#', scopes: ['source.csound', 'punctuation.definition.macro.end.csound']
-
-    it 'tokenizes header global variables', ->
-      headerGlobalVariables = [
+      expect(tokens[0]).toEqual value: '# \tdefine', scopes: [
+        'source.csound'
+        'keyword.define.preprocessor.csound'
+      ]
+      expect(tokens[1]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[2]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[3]).toEqual value: 'MACRO', scopes: [
+        'source.csound'
+        'entity.name.function.preprocessor.csound'
+      ]
+      expect(tokens[4]).toEqual value: '(', scopes: [
+        'source.csound'
+      ]
+      expect(tokens[5]).toEqual value: 'ARGUMENT', scopes: [
+        'source.csound'
+        'variable.parameter.preprocessor.csound'
+      ]
+      expect(tokens[6]).toEqual value: ')', scopes: [
+        'source.csound'
+      ]
+      expect(tokens[7]).toEqual value: '/*', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.begin.csound'
+      ]
+      expect(tokens[8]).toEqual value: '*/', scopes: [
+        'source.csound'
+        'comment.block.csound'
+        'punctuation.definition.comment.end.csound'
+      ]
+      expect(tokens[9]).toEqual value: '#', scopes: [
+        'source.csound'
+        'punctuation.definition.macro.begin.csound'
+      ]
+      expect(tokens[10]).toEqual value: '$ARGUMENT', scopes: [
+        'source.csound'
+        'entity.name.function.preprocessor.csound'
+      ]
+      expect(tokens[11]).toEqual value: '#', scopes: [
+        'source.csound'
+        'punctuation.definition.macro.end.csound'
+      ]
+
+    it 'tokenizes global values', ->
+      globalValues = [
         '0dbfs'
         'kr'
         'ksmps'
@@ -138,17 +413,35 @@
         'nchnls_i'
         'sr'
       ]
-      lines = grammar.tokenizeLines headerGlobalVariables.join('\n')
+      lines = grammar.tokenizeLines(globalValues.join '\n')
       for i in [0...lines.length]
-        expect(lines[i][0]).toEqual value: headerGlobalVariables[i], scopes: ['source.csound', 'variable.other.readwrite.global.csound']
+        expect(lines[i][0]).toEqual value: globalValues[i], scopes: [
+          'source.csound'
+          'variable.other.readwrite.global.csound'
+        ]

     it 'tokenizes quoted strings', ->
       lines = grammar.tokenizeLines '"characters$MACRO."'
       tokens = lines[0]
-      expect(tokens[0]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.csound', 'punctuation.definition.string.begin.csound']
-      expect(tokens[1]).toEqual value: 'characters', scopes: ['source.csound', 'string.quoted.csound']
-      expect(tokens[2]).toEqual value: '$MACRO.', scopes: ['source.csound', 'string.quoted.csound', 'entity.name.function.preprocessor.csound']
-      expect(tokens[3]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.csound', 'punctuation.definition.string.end.csound']
+      expect(tokens[0]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+        'punctuation.definition.string.begin.csound'
+      ]
+      expect(tokens[1]).toEqual value: 'characters', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+      ]
+      expect(tokens[2]).toEqual value: '$MACRO.', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+        'entity.name.function.preprocessor.csound'
+      ]
+      expect(tokens[3]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+        'punctuation.definition.string.end.csound'
+      ]

     it 'tokenizes escaped characters', ->
       escapedCharacters = [
@@ -176,19 +469,40 @@
         '\\345'
         '\\67'
       ]
-      lines = grammar.tokenizeLines '"' + escapedCharacters.join('') + '"'
+      lines = grammar.tokenizeLines "\"#{escapedCharacters.join ''}\""
       tokens = lines[0]
-      expect(tokens[0]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.csound', 'punctuation.definition.string.begin.csound']
+      expect(tokens[0]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+        'punctuation.definition.string.begin.csound'
+      ]
       for i in [1...tokens.length - 1]
-        expect(tokens[i]).toEqual value: escapedCharacters[i - 1], scopes: ['source.csound', 'string.quoted.csound', 'constant.character.escape.csound']
-      expect(tokens[tokens.length - 1]).toEqual value: '"', scopes: ['source.csound', 'string.quoted.csound', 'punctuation.definition.string.end.csound']
+        expect(tokens[i]).toEqual value: escapedCharacters[i - 1], scopes: [
+          'source.csound'
+          'string.quoted.csound'
+          'constant.character.escape.csound'
+        ]
+      expect(tokens[tokens.length - 1]).toEqual value: '"', scopes: [
+        'source.csound'
+        'string.quoted.csound'
+        'punctuation.definition.string.end.csound'
+      ]

     it 'tokenizes braced strings', ->
       lines = grammar.tokenizeLines '{{characters}}'
       tokens = lines[0]
-      expect(tokens[0]).toEqual value: '{{', scopes: ['source.csound', 'string.braced.csound']
-      expect(tokens[1]).toEqual value: 'characters', scopes: ['source.csound', 'string.braced.csound']
-      expect(tokens[2]).toEqual value: '}}', scopes: ['source.csound', 'string.braced.csound']
+      expect(tokens[0]).toEqual value: '{{', scopes: [
+        'source.csound'
+        'string.braced.csound'
+      ]
+      expect(tokens[1]).toEqual value: 'characters', scopes: [
+        'source.csound'
+        'string.braced.csound'
+      ]
+      expect(tokens[2]).toEqual value: '}}', scopes: [
+        'source.csound'
+        'string.braced.csound'
+      ]

     it 'tokenizes keywords', ->
       keywords = [
@@ -208,9 +522,12 @@
         'until'
         'while'
       ]
-      lines = grammar.tokenizeLines keywords.join('\n')
+      lines = grammar.tokenizeLines(keywords.join '\n')
       for i in [0...lines.length]
-        expect(lines[i][0]).toEqual value: keywords[i], scopes: ['source.csound', 'keyword.control.csound']
+        expect(lines[i][0]).toEqual value: keywords[i], scopes: [
+          'source.csound'
+          'keyword.control.csound'
+        ]

     it 'tokenizes goto statements', ->
       keywordsAndOpcodes = [
@@ -233,14 +550,28 @@
         ''
       ]
       # Putting a label after each string is enough to test the grammar, but
-      # it is not always valid Csound syntax. In particular, loop_ge, loop_gt,
+      # it’s not always valid Csound syntax. In particular, loop_ge, loop_gt,
       # loop_le, and loop_lt all take four arguments, the last of which is a
       # label.
-      lines = grammar.tokenizeLines keywordsAndOpcodes.join ' aLabel //\n'
+      lines = grammar.tokenizeLines(keywordsAndOpcodes.join ' aLabel //\n')
       for i in [0...lines.length - 1]
         tokens = lines[i]
-        expect(tokens[0]).toEqual value: keywordsAndOpcodes[i], scopes: ['source.csound', 'keyword.control.csound']
-        expect(tokens[1]).toEqual value: ' ', scopes: ['source.csound']
-        expect(tokens[2]).toEqual value: 'aLabel', scopes: ['source.csound', 'entity.name.label.csound']
-        expect(tokens[3]).toEqual value: ' ', scopes: ['source.csound']
-        expect(tokens[4]).toEqual value: '//', scopes: ['source.csound', 'comment.line.csound', 'punctuation.definition.comment.line.csound']
+        expect(tokens[0]).toEqual value: keywordsAndOpcodes[i], scopes: [
+          'source.csound'
+          'keyword.control.csound'
+        ]
+        expect(tokens[1]).toEqual value: ' ', scopes: [
+          'source.csound'
+        ]
+        expect(tokens[2]).toEqual value: 'aLabel', scopes: [
+          'source.csound'
+          'entity.name.label.csound'
+        ]
+        expect(tokens[3]).toEqual value: ' ', scopes: [
+          'source.csound'
+        ]
+        expect(tokens[4]).toEqual value: '//', scopes: [
+          'source.csound'
+          'comment.line.csound'
+          'punctuation.definition.comment.line.csound'
+        ]


add delegator property to the event context

--- src/event.coffee
+++ src/event.coffee
@@ -18,8 +18,8 @@
         on: (name, callback, context) ->
             return this if (!eventsApi(this, 'on', name, [callback, context]) || !callback)

-            this._events || (this._events = {});
-            events = this._events[name] || (this._events[name] = []);
+            this._listeners || (this._listeners = {});
+            events = this._listeners[name] || (this._listeners[name] = []);
             events.push
                 callback: callback
                 context: context
@@ -46,23 +46,23 @@
         # callbacks for the event. If `name` is null, removes all bound
         # callbacks for all events.
         off: (name, callback, context) ->
-            return this if (!this._events || !eventsApi(this, 'off', name, [callback, context]))
+            return this if (!this._listeners || !eventsApi(this, 'off', name, [callback, context]))

             if (!name && !callback && !context)
-                this._events = undefined;
+                this._listeners = undefined;
                 return this;

-            names = if name then [name] else Object.keys(this._events);
+            names = if name then [name] else Object.keys(this._listeners);

             for name, i in names
-                if (events = this._events[name])
-                    this._events[name] = retain = []
+                if (events = this._listeners[name])
+                    this._listeners[name] = retain = []
                     if (callback || context)
                         for ev, j in events
                             if ((callback && callback isnt ev.callback && callback isnt ev.callback._callback) || (context && context isnt ev.context))
                                 retain.push ev

-                    delete this._events[name] if (!retain.length)
+                    delete this._listeners[name] if (!retain.length)

             this

@@ -81,15 +81,16 @@
         delegate: ->
             delegateEvents(this.delegators, arguments) if this.delegators and this.delegators.size() > 0

-            return this if (!this._events)
+            return this if (!this._listeners)

             event = arguments[arguments.length - 1]
+            event.delegator = this

-            events = this._events[event.name]
-            allEvents = this._events.all
+            listeners = this._listeners[event.name]
+            listeners_for_all = this._listeners.all

-            triggerEvents(events, arguments) if (events)
-            triggerEvents(allEvents, arguments) if (allEvents)
+            triggerEvents(listeners, arguments) if (listeners)
+            triggerEvents(listeners_for_all, arguments) if (listeners_for_all)

             this

@@ -107,15 +108,15 @@

             delegateEvents(this.delegators, args) if this.delegators and this.delegators.size() > 0

-            return this if not this._events
+            return this if not this._listeners

             return this if (!eventsApi(this, 'trigger', name, args))

-            events = this._events[name]
-            allEvents = this._events.all
+            listeners = this._listeners[name]
+            listeners_for_all = this._listeners.all

-            triggerEvents(events, args) if (events)
-            triggerEvents(allEvents, args) if (allEvents)
+            triggerEvents(listeners, args) if (listeners)
+            triggerEvents(listeners_for_all, args) if (listeners_for_all)

             this


Remove incorrect scope-names from memorandum macros

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -628,7 +628,7 @@
 	# Memorandum macros
 	mm:
 		patterns: [{
-			name: "meta.function.request.${2:/downcase}.mm.macro.roff"
+			name: "meta.function.${2:/downcase}.mm.macro.roff"
 			begin: "(?x) ^([.'])[ \t]*
 				(1C|2C|AE|AF|AL|APP|APPSK|AS|AST|AT|AU|AV|AVL|B1|B2|BE|BL|BS|BVL
 				|COVER|COVEND|DE|DF|DL|DS|EC|EF|EH|EN|EOP|EPIC|EQ|EX|FC|FD|FE|FG
@@ -639,7 +639,7 @@
 				|VERBOFF|VL|VM|WA|WE|WC|\\)E)\\b"
 			end: "(?<!\\\\)(?=\n)"
 			beginCaptures:
-				1: {name: "punctuation.definition.request.roff"}
+				1: {name: "punctuation.definition.macro.roff"}
 				2: {name: "entity.name.roff"}
 			patterns: [{include: "#params"}]
 		}]


Reset dark-mode threashold.

Reset dark-mode threashold.

--- atom/atom/config.cson
+++ atom/atom/config.cson
@@ -79,7 +79,6 @@
     titleBar: "custom-inset"
   "dark-mode":
     ambientLight: true
-    ambientLightThreshold: 5000000
     darkProfile: "pristine-ui one-dark-syntax"
   docblockr: {}
   editor:


Added image optima watch task and removed PHPDoc watch task

--- grunt/watch.coffee
+++ grunt/watch.coffee
@@ -53,7 +53,7 @@

   imageoptim:
     files: [
-      "**/*.{png,jpg,gif}"
+      "assets/**/*.{png,jpg,gif}"
       "!assets/img/sprite/**/*.{png,jpg,gif}"
       "!assets/img/sprite.png"
     ]
@@ -72,7 +72,7 @@
       "lib/**/*.php",
       "classes/**/*.php"
     ]
-    tasks: ["phpdoc"]
+    tasks: []
     options:
       spawn: false


bump version to 1.0.3

bump version to 1.0.3

--- gulp/config.coffee
+++ gulp/config.coffee
@@ -8,7 +8,7 @@
   constructor: ->
     @_GLOBALS_DEFAULTS = {
       defaults: {
-        BUNDLE_VERSION: "1.0.2"
+        BUNDLE_VERSION: "1.0.3"

         AVAILABLE_PLATFORMS: ["ios", "android"]


Rename 'requiresToken()' to 'requireToken()'.

--- backend/server.coffee
+++ backend/server.coffee
@@ -31,7 +31,7 @@
   Token.findOne { value: value }, (err, token) ->
     cb err || !token? || token.expiration < Date.now(), token

-requiresToken = (func) ->
+requireToken = (func) ->
   return (req, res) ->
     _args = arguments
     _this = this
@@ -54,19 +54,19 @@
 app.get '/', (req, res) ->
   res.render 'index.html', { layout: false }

-app.get '/categories', requiresToken (req, res) ->
+app.get '/categories', requireToken (req, res) ->
   Category.find {}, ['slug', 'title', 'description', '_id'], (err, cats) ->
     res.send cats

-app.get '/category/:slug', requiresToken (req, res) ->
+app.get '/category/:slug', requireToken (req, res) ->
   Category.findOne { slug: req.params.slug }, (err, cat) ->
     res.send cat

-app.get '/thread/:tid', requiresToken (req, res) ->
+app.get '/thread/:tid', requireToken (req, res) ->
   Thread.findById req.params.tid, (err, thread) ->
     res.send thread

-app.post '/new-thread', requiresToken (req, res) ->
+app.post '/new-thread', requireToken (req, res) ->
   thread = new Thread({ username: req.body.username, title: req.body.title })
   post = new Post({
     username: req.body.username
@@ -84,7 +84,7 @@
     user.save()
   res.send { result: 'success', id: thread._id }

-app.post '/post-reply', requiresToken (req, res) ->
+app.post '/post-reply', requireToken (req, res) ->
   Thread.findById req.body.tid, (err, thread) ->
     post = new Post({
       username: req.body.username
@@ -109,7 +109,7 @@
       else
         res.send { result: 'failure', provided: sha1(req.body.password) }

-app.get '/user/:username', requiresToken (req, res) ->
+app.get '/user/:username', requireToken (req, res) ->
   User.findOne { username: req.params.username }, (err, user) ->
     if err
       res.send { result: 'not found' }


Add CORS

Add CORS

--- http-socket.coffee
+++ http-socket.coffee
@@ -10,8 +10,15 @@
       response.end "Not a thing.\n", 'utf-8'
       return

-    allowHeaders = {'Content-Type': 'text/plain', 'Allow': 'GET, HEAD, OPTIONS'}
-    mainHeaders = {'Content-Type': 'text/event-stream'}
+    allowHeaders = {
+      'Content-Type': 'text/plain',
+      'Allow': 'GET, HEAD, OPTIONS',
+      'Access-Control-Allow-Origin': '*'
+    }
+    mainHeaders = {
+      'Content-Type': 'text/event-stream',
+      'Access-Control-Allow-Origin': '*'
+    }

     if request.method is 'OPTIONS'
       response.writeHead 200, 'OK', allowHeaders


Centralise ctrl calls

Centralise ctrl calls

--- spec/javascripts/unit/bulk_product_update_spec.js.coffee
+++ spec/javascripts/unit/bulk_product_update_spec.js.coffee
@@ -257,13 +257,11 @@
     ctrl = $controller
     timeout = $timeout
     httpBackend = $httpBackend
+
+    ctrl "AdminBulkProductsCtrl", {$scope: scope, $timeout: timeout}
   )

   describe "loading data upon initialisation", ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     it "gets a list of suppliers and then resets products with a list of data", ->
       httpBackend.expectGET("/api/users/authorise_api?token=api_key").respond success: "Use of API Authorised"
       httpBackend.expectGET("/api/enterprises/managed?template=bulk_index&q[is_primary_producer_eq]=true").respond "list of suppliers"
@@ -278,8 +276,6 @@

   describe "resetting products", ->
     beforeEach ->
-      ctrl "AdminBulkProductsCtrl", {$scope: scope}
-
       spyOn scope, "unpackProduct"
       scope.products = {}
       scope.resetProducts [
@@ -314,8 +310,6 @@

   describe 'preparing products', ->
     beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
       spyOn scope, "matchSupplier"
       spyOn scope, "loadVariantUnit"

@@ -339,10 +333,6 @@

   describe "matching supplier", ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     it "changes the supplier of the product to the one which matches it from the suppliers list", ->
       s1_s =
         id: 1
@@ -370,10 +360,6 @@

   describe 'loading variant unit', ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     it 'sets variant_unit_with_scale by combining variant_unit and variant_unit_scale', ->
       product =
         variant_unit: "volume"
@@ -393,10 +379,6 @@

   describe "getting on_hand counts when products have variants", ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     p1 = undefined
     p2 = undefined
     p3 = undefined
@@ -458,10 +440,6 @@

   describe "submitting products to be updated", ->
     describe 'packing products', ->
-      beforeEach ->
-        ctrl "AdminBulkProductsCtrl",
-          $scope: scope
-
       it 'extracts variant_unit_with_scale into variant_unit and variant_unit_scale', ->
         testProduct =
           id: 1
@@ -480,9 +458,6 @@

     describe "filtering products", ->
       beforeEach ->
-        ctrl "AdminBulkProductsCtrl",
-          $scope: scope
-
         spyOn scope, "packProduct"
         spyOn(window, "filterSubmitProducts").andReturn [
           {
@@ -533,11 +508,6 @@

     describe "updating products", ->
-      beforeEach ->
-        ctrl "AdminBulkProductsCtrl",
-          $scope: scope
-          $timeout: timeout
-
       it "submits products to be updated with a http post request to /admin/products/bulk_update", ->
         httpBackend.expectPOST("/admin/products/bulk_update").respond "list of products"
         scope.updateProducts "list of products"
@@ -587,10 +557,6 @@

   describe 'fetching products without derived attributes', ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     it 'returns products without the variant_unit_with_scale field', ->
       scope.products = [{id: 123, variant_unit_with_scale: 'weight_1000'}]
       expect(scope.productsWithoutDerivedAttributes()).toEqual([{id: 123}])
@@ -605,10 +571,6 @@

   describe "deleting products", ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
     it "deletes products with a http delete request to /api/products/id", ->
       spyOn(window, "confirm").andReturn true
       scope.products = [
@@ -661,12 +623,6 @@

   describe "deleting variants", ->
-    beforeEach ->
-      scope = {}
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
-
     it "deletes variants with a http delete request to /api/products/product_id/variants/(variant_id)", ->
       spyOn(window, "confirm").andReturn true
       scope.products = [
@@ -748,11 +704,6 @@

   describe "cloning products", ->
-    beforeEach ->
-      ctrl "AdminBulkProductsCtrl",
-        $scope: scope
-
-
     it "clones products using a http get request to /admin/products/(permalink)/clone.json", ->
       scope.products = [
         id: 13


update isAuthenticated when state changes

--- client/security.coffee
+++ client/security.coffee
@@ -36,6 +36,7 @@
       fetch '/logout', myInit
       .then (response) ->
         if response.ok
+          window.isAuthenticated = false
           update_footer ownerName, false
         else
           console.log 'logout failed: ', response
@@ -58,6 +59,7 @@
             response.json().then (json) ->
               ownerName = json.ownerName
               window.isClaimed = true
+              window.isAuthenticated = true
               update_footer ownerName, true
           else
             console.log 'login failed: ', response
@@ -82,6 +84,7 @@
           .then (response) ->
             console.log 'reclaim response', response
             if response.ok
+              window.isAuthenticated = true
               update_footer ownerName, true
             else
               console.log 'reclaim failed: ', response


fixed load loop for mesh buffers

fixed load loop for mesh buffers

--- public/coffee/view/mesh.coffee
+++ public/coffee/view/mesh.coffee
@@ -52,7 +52,7 @@
    draw: (type = @gl.TRIANGLES) ->
       gl = @gl
       # bind all the vbo streams
-      for buffer in @buffers
+      for i,buffer of @buffers
          gl.bindBuffer gl.ARRAY_BUFFER, buffer.vbo
          @setUpAttribPointer(buffer)


Default title, navigationState in render

--- src/handlers/beers/RecipeHandler.coffee
+++ src/handlers/beers/RecipeHandler.coffee
@@ -101,7 +101,7 @@
 		]

 	# Show the recipe app
-	render: (user, title, navigationState) ->
+	render: (user, title="Add new beer", navigationState="recipe") ->
 		return @renderer.page
 			title: title
 			navigationState: navigationState


insights: Run multiple downloads in parallell

Does not seem to affect error rate

--- src/insights-download-timeperiod.coffee
+++ src/insights-download-timeperiod.coffee
@@ -49,7 +49,7 @@
 executeOverDays = (args, callback) ->
   days = daysInPeriod args.start, args.end

-  async.mapSeries days, (d, cb) ->
+  async.mapLimit days, 5, (d, cb) ->
     executeDay d, args, cb
   , callback


Fix user is typing stream

Fix user is typing stream

--- packages/rocketchat-lib/server/Notifications.coffee
+++ packages/rocketchat-lib/server/Notifications.coffee
@@ -46,25 +46,20 @@
 		@streamUser.emit.apply @streamUser, args

-
 ## Permissions for client

 # Enable emit for event typing for rooms and add username to event data
 func = (eventName, username, typing) ->
-	console.log arguments
 	[room, e] = eventName.split('/')

-	if e isnt 'typing'
-		return false
+	if e is 'webrtc'
+		return true

-	user = Meteor.users.findOne(@userId, {fields: {username: 1}})
-	if not user? or user.username isnt username
-		return false
+	if e is 'typing'
+		user = Meteor.users.findOne(@userId, {fields: {username: 1}})
+		if user?.username is username
+			return true

-	return true
+	return false

 RocketChat.Notifications.streamRoom.permissions.write func, false # Prevent Cache
-
-RocketChat.Notifications.streamRoom.permissions.write (eventName) ->
-	[room, e] = eventName.split('/')
-	return e is 'webrtc'


derprag

derprag

--- src/lib/firebase/firebaseManager.coffee
+++ src/lib/firebase/firebaseManager.coffee
@@ -3,7 +3,7 @@
 { config, constants }          = require('../common')

 FirebaseManager =
-  rootRef: new Firebase(constants.FIREBASE)
+  rootRef: new firebase.Firebase(constants.FIREBASE)
   sessionsRef: rootRef.child(constants.CONTEXT_CHILD)
   eventsRef: (session) ->
     sessionsRef.child(session).child(constants.EVENTS_CHILD)


First cut at using the game state world

First cut at using the game state world

--- src/hello.coffee
+++ src/hello.coffee
@@ -9,10 +9,20 @@

   cameraInfo = cameraInfo.skipDuplicates _.isEqual

-  gridDisplay = Bacon.never().toProperty [
-    [['bg_water'], ['bg_water'], ['bg_shipwreck'], ['bg_water']],
-    [['bg_water'], ['bg_plains'], ['bg_plains'], ['bg_water']],
-    [['bg_water'], ['bg_water'], ['bg_water'], ['bg_water']]]
+  gridDisplay = environment.map((env) ->
+    for row in env
+      for cell in row
+        switch cell
+          when 0 then ['bg_water']
+          when 1 then ['bg_plains']
+          when 2 then ['bg_tree']
+          when 3 then ['bg_shipwreck']
+          when 4 then ['bg_stone']
+          when 5 then ['bg_iron']
+          when 6 then ['bg_plains'] # roads
+          else ['unknown'])
+
+  gridDisplay.onValue (x) -> console.log x

   clicks = cameraInfo.flatMapLatest (info) ->
     {camWidth, camHeight} = info
@@ -25,3 +35,13 @@
   clicks.onValue (pos) ->
     [x, y] = pos
     console.log "Click at #{x}, #{y}"
+
+  GameState.start
+    world: [
+      [0, 0, 0, 0, 0, 0],
+      [0, 1, 2, 1, 0, 0],
+      [0, 1, 1, 2, 1, 0],
+      [0, 3, 1, 1, 4, 0],
+      [0, 0, 2, 1, 5, 0],
+      [0, 0, 0, 2, 4, 0],
+      [0, 0, 0, 0, 0, 0]]


Scrolling now works in android browsers! Still though, the scroll bar itself does not appear. Progress on #19

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -128,6 +128,14 @@
             updateMove()
             return
         ed.editor.renderer.onResize = addOurResize
+
+        # Touch Handlers
+        @ongoingTouches = []
+        jQuery('.ace_scroller').bind "touchstart", @handleTouchStart
+        jQuery('.ace_scroller').bind "touchend", @handleTouchEnd
+        jQuery('.ace_scroller').bind "touchcancel", @handleTouchCancel
+        jQuery('.ace_scroller').bind "touchleave", @handleTouchEnd
+        jQuery('.ace_scroller').bind "touchmove", @handleTouchMove
         return

     resetEditor: =>
@@ -322,6 +330,45 @@
         @editor.gotoLine row
         return

+    handleTouchStart: (evt) =>
+        for touch in evt.originalEvent.changedTouches
+            @ongoingTouches.push touch
+        return
+
+    handleTouchMove: (evt) =>
+        evt.preventDefault()
+        touches = evt.originalEvent.changedTouches
+        for touch in touches
+            id = @onGoingTouchIndexByID touch.identifier
+            verticalDistance = @ongoingTouches[id].pageY - touch.pageY
+            horizontalDistance = @ongoingTouches[id].pageX - touch.pageX
+            @editor.editor.renderer.scrollBy horizontalDistance, verticalDistance
+            @ongoingTouches.splice id, 1, touch
+        return
+
+    handleTouchEnd: (evt) =>
+        touches = evt.originalEvent.changedTouches
+        for touch in touches
+            id = @onGoingTouchIndexByID touch.identifier
+            verticalDistance = @ongoingTouches[id].pageY - touch.pageY
+            horizontalDistance = @ongoingTouches[id].pageX - touch.pageX
+            @editor.editor.renderer.scrollBy horizontalDistance, verticalDistance
+            @ongoingTouches.splice id, 1
+        return
+
+    handleTouchCancel: (evt) =>
+        touches = evt.originalEvent.changedTouches
+        for touch in touches
+            id = @onGoingTouchIndexByID touch.identifier
+            @ongoingTouches.splice id, 1
+        return
+
+    onGoingTouchIndexByID: (idToFind) =>
+        for i in [0...@ongoingTouches.length] by 1
+            id = @ongoingTouches[i].identifier
+            if id == i
+                return i
+        return -1 # Not Found

 class window.PlayerCodeEditor
     ###


Update config.cson

Update config.cson

--- etc/atom/config.cson
+++ etc/atom/config.cson
@@ -23,6 +23,7 @@
       "solarized-dark-syntax"
     ]
     projectHome: "/Users/stillers/workspace"
+    audioBeep: false
   "bracket-matcher":
     autocompleteBrackets: false
   "find-and-replace":


Update exports.

Update exports.

--- src/index.coffee
+++ src/index.coffee
@@ -7,9 +7,9 @@

 process.env.PATH = ([binPath].concat process.env.PATH.split ':').join ':'

-global.task    = require './task'
 global.invoke  = require './invoke'
 global.running = require './running'
+global.task    = require './task'
 global.use     = require './use'

 global.exec    = require 'executive'
@@ -20,3 +20,4 @@
   invoke:  global.invoke
   running: global.running
   task:    global.task
+  use:     global.use


Fix the URL the server listen to when printed to the console

Fixes #175

--- src/index.coffee
+++ src/index.coffee
@@ -55,7 +55,7 @@
       if err
         @log "Error on starting server: #{err}"
       else
-        @log "Server started http#{if @https? then 's' else ''}://#{@host}:#{@port}"
+        @log "Server started http#{if @https then 's' else ''}://#{@host}:#{@port}"

         stoped = false;
         sockets = [];


add better check without and only

add better check without and only

--- src/index.coffee
+++ src/index.coffee
@@ -16,12 +16,11 @@

 	folders = (fs.readdirSync path.resolve one for one in search)

-	if valid options.without
-		{without} = options
-
 	if valid options.only
-		without = no
 		{only} = options
+	else if not valid options.only
+		if valid options.without
+			{without} = options

 	modules = zipObject search, folders


+ pass the filename argument to the context processor

--- src/index.coffee
+++ src/index.coffee
@@ -90,7 +90,7 @@
       .then (content)->
         if content
           proc = vConfigurators[path.extname(file)]
-          content = proc stripBom(content), aOptions
+          content = proc stripBom(content), aOptions, file
           defineProperty content, '$cfgPath', file if content
         content
       .catch (err)->
@@ -128,7 +128,7 @@
       catch err
         continue
       try
-        result = proc(result, aOptions)
+        result = proc(result, aOptions, vConfigPath)
       catch err
         err.name = path.basename(vConfigPath) + ':' + err.name
         throw err


Add mkd suffix for Markdown plugin

Add mkd suffix for Markdown plugin

--- src/index.coffee
+++ src/index.coffee
@@ -12,7 +12,7 @@
   JadeTemplate: require('./plugins/jade-template')

 # register default plugins
-registerContentPlugin 'pages', '**/*.*(markdown|md)', defaultPlugins.MarkdownPage
+registerContentPlugin 'pages', '**/*.*(markdown|mkd|md)', defaultPlugins.MarkdownPage
 registerContentPlugin 'pages', '**/*.json', defaultPlugins.JsonPage
 registerTemplatePlugin '**/*.jade', defaultPlugins.JadeTemplate


SPACES!

SPACES!

--- src/index.coffee
+++ src/index.coffee
@@ -53,7 +53,10 @@
     js: 'uglify'
     css: 'minify-css'

+
   config.environment ?= 'development'
+
+
   config.environments ?=
     development:
       minify: no


Refactor plugin

Refactor plugin

--- src/index.coffee
+++ src/index.coffee
@@ -30,8 +30,6 @@
       callback null, new Buffer xml sitemap, declaration: true

   env.registerGenerator 'sitemap', (contents, callback) ->
-    tree =
-      'sitemap.xml': new Sitemap
-    callback null, tree
+    callback null, 'sitemap.xml': new Sitemap

   callback()


fix item check

fix item check

--- src/index.coffee
+++ src/index.coffee
@@ -358,11 +358,11 @@
 Shop.getItem = (id) ->
   item =  @cart.get id
   return {
-    id:         item.id ? ''
-    sku:        item.sku ? ''
-    name:       item.name ? ''
-    quantity:   item.quantity ? 0
-    price:      item.price ? 0
+    id:         item?.id ? ''
+    sku:        item?.sku ? ''
+    name:       item?.name ? ''
+    quantity:   item?.quantity ? 0
+    price:      item?.price ? 0
   }

 export default Shop


Fix: class duplication during initialization without GUI

--- src/index.coffee
+++ src/index.coffee
@@ -78,7 +78,8 @@
   if [' ', ' '].join(el.className).indexOf(' literally ') == -1
     el.className = el.className + ' literally'

-  el.className = el.className + ' toolbar-hidden'
+  if el.className.includes('toolbar-hidden') == false
+    el.className = el.className + ' toolbar-hidden'

   drawingViewElement = document.createElement('div')
   drawingViewElement.className = 'lc-drawing'


Update stylus task to use variables and new naming scheme

--- grunt/stylus.coffee
+++ grunt/stylus.coffee
@@ -4,15 +4,15 @@
     urlfunc: 'embedurl'
     use: [require('rupture')]
     import: ['nib', 'rupture', 'asset']
-  development:
+  debug:
     options:
       compress: false
       linenos: true
     files:
-      'build/debug/css/<%= package.name %>.css': 'stylus/app/**/*.styl'
-  production:
+      '<%= buildDebug %>/css/<%= package.name %>.css': 'stylus/app/**/*.styl'
+  release:
     options:
       compress: true
       linenos: false
     files:
-      'build/debug/css/<%= package.name %>.css': 'stylus/app/**/*.styl'
+      '<%= buildDebug %>/css/<%= package.name %>.css': 'stylus/app/**/*.styl'


fixing avatar uploader UI

fixing avatar uploader UI

--- app/coffee/avatar.coffee
+++ app/coffee/avatar.coffee
@@ -51,11 +51,15 @@
 class AvatarView
   constructor: (containerSelector) ->
     @$container = $(containerSelector)
+    @$contextPanel = $('#contextPanel')
+
   displayUploader: ->
     @displayBodyClick()

-    $('#contextPanel .avatar').hide()
-    $('#contextPanel').prepend("

drag new image here

browse for a file

cancel

browse

")
+
+    @$contextPanel.find('.avatar').hide()
+    @$contextPanel.css('z-index', '1200')
+    @$contextPanel.prepend("

drag new image here

browse for a file

cancel

browse

")

     $.publish 'uploader:init', [@container]

@@ -67,6 +71,7 @@
       $('.rightPanel .user').css('background-image', 'url(' + avatar_thumb_path + ')')

     $('#avatarDrop').remove()
+    @$contextPanel.css('z-index', 'auto')
     $('.uploadAvatarWrap').show()
     @$container.show()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -22,8 +22,7 @@

   obj.include Metered

-  obj.meter
-    name: 'turbo'
+  obj.meter 'turbo'

   equals obj.I.turbo, 100
   equals obj.I.maxTurbo, 100


Change isometric tiles to a top-down view

--- webserver/static/js/renderer.coffee
+++ webserver/static/js/renderer.coffee
@@ -9,24 +9,24 @@

 DrawTileFunc = ?(Num, Num) -> Any

-isoTile :: (Num, Num, Num, Num) -> DrawTileFunc
-isoTile = (bgColor, borderColor, w, h) ->
+makeTile :: (Num, Num, Num, Num) -> DrawTileFunc
+makeTile = (bgColor, borderColor, w, h) ->
     h_2 = h / 2
     tileFunc = (x, y) ->
         console.log ("Drawing at " + x + ", " + y)
         graphics.beginFill bgColor
         graphics.lineStyle(1, borderColor, 1)
         graphics.moveTo(x, y)
-        graphics.lineTo(x + w, y + h_2)
+        graphics.lineTo(x + w, y)
+        graphics.lineTo(x + w, y + h)
         graphics.lineTo(x, y + h)
-        graphics.lineTo(x - w, y + h_2)
         graphics.lineTo(x, y)
         graphics.endFill()

 # Tile Functions
-grass = isoTile(0x80CF5A, 0x339900, tileWidth, tileHeight);
-dirt = isoTile(0x96712F, 0x403014, tileWidth, tileHeight);
-water = isoTile(0x85b9bb, 0x476263, tileWidth, tileHeight);
+grass = makeTile(0x80CF5A, 0x339900, tileWidth, tileHeight);
+dirt = makeTile(0x96712F, 0x403014, tileWidth, tileHeight);
+water = makeTile(0x85b9bb, 0x476263, tileWidth, tileHeight);
 empty = () -> {};
 tileMethods = [grass, dirt, water, empty];

@@ -40,11 +40,9 @@
     drawTile = (i, j) ->
         x = j * tileWidth
         y = i * tileHeight
-        isoX = x - y
-        isoY = (x + y) / 2
         tileType = terrain[i][j]
         drawTileType = tileMethods[tileType]
-        drawTileType(xOffset + isoX, yOffset + isoY)
+        drawTileType(xOffset + x, yOffset + y)
     graphics.clear()
     for row, i in terrain
         for tile, j in row


Fix the width of the performance graph on the dashboard.

--- src/main/webapp/coffeescripts-hidden/weeks-performance-graph.coffee
+++ src/main/webapp/coffeescripts-hidden/weeks-performance-graph.coffee
@@ -4,7 +4,7 @@
       renderTo: 'weeks-performance-graph',
       defaultSeriesType: 'spline',
       backgroundColor: '#f7f7f7',
-      width: 600,
+      width: 700,
       animation: false
     title: [
       text: 'Weekly Performance'
@@ -26,6 +26,7 @@
       title:
         text: null
       min: 0
+      minRange: 10

   requestEventSummary()


Update log handler to use debug

Update log handler to use debug

--- handlers/log.coffee
+++ handlers/log.coffee
@@ -16,12 +16,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ###

-winston = require 'winston'
+debug = (require 'debug')('Chatbot:log')

 handleMessage = (message, callback) ->
-	winston.debug "[Log]", message.message
+	debug message.message
 	callback?()
-

 unload = (callback) -> callback?()
 module.exports =


redundent file reomved

redundent file reomved

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


Fixing line positioning for mekko charts

Fixes: #2186

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -144,15 +144,17 @@
         .attr('class', 'sector_label')
         .attr('transform', "translate(0, #{@label_offset})")

-      sector_group.append("svg:line")
+      @svg.selectAll(".sector_line")
+        .data(@sector_list.models, (d) -> d.get 'key' )
+        .enter().append("svg:line")
         .attr('class', 'sector_line')
         .attr("stroke-width", "1px")
         .attr("fill", "none")
         .attr("stroke", "#000000")
         .attr("x1", 0)
         .attr("x2", 0)
-        .attr("y1", -25)
-        .attr("y2", -5)
+        .attr("y1", @label_offset - 25)
+        .attr("y2", @label_offset - 2)

       sector_label = sector_group.append("svg:text")
         .attr("text-anchor", "end")
@@ -203,7 +205,7 @@
       average_width = @x(total_value / @sector_list.models.length)

       sector_label = @svg.selectAll("g.sector_label")
-        .data(@sector_list.models, (d) -> d.get 'key' )
+        .data(@sector_list.models, (d, i) -> d.get 'key' )
         # .transition().duration(500)
         .attr('transform', (d) => "translate(#{@x(d.total_value() / 2)}, #{@label_offset})")
         .select('text')
@@ -276,7 +278,6 @@

             d3.select(label)
               .attr('transform', 'translate('+ el.translate + ')')
-              .attr('data-moved', el.translate[0])

           # The current label becomes prevLabel in the next iteration.
           return label
@@ -291,15 +292,20 @@
         previous.left - current.right if previous.left < current.right

     moveArrows: =>
-      @svg.selectAll('g.sector').each(() ->
-        scope = d3.select(this)
-        moved = scope.select('g.sector_label').attr('data-moved')
-
-        if moved
-          scope.select('line').attr('x1', () ->
-            -(moved - (scope.select('rect').attr('width') / 2.0))
-          )
-      )
+      x = @x
+      offset = 0
+
+      d3.selectAll('line.sector_line').each (d, i) ->
+        total  = x(d.total_value())
+        moveX2 = offset + d3.transform(d3.select($('g.sector_label')[i])
+          .attr('transform'))
+          .translate[0]
+
+        offset += total
+
+        d3.select(this)
+          .attr('x1', offset - (total / 2))
+          .attr('x2', moveX2)

 # Pseudo-collection of nodes
 #


Fix serialization of selected value

Fix serialization of selected value

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -33,7 +33,7 @@
       putUrl = () ->
         form.attr('action') + '.json'
       putParams = () ->
-        form.serialize() + "&selected_as={#{draggable.data('id')}:#{droppable.data('name')}}"
+        form.serialize() + "&selected_as[#{draggable.data('id')}]=#{droppable.data('name')}"

       superagent.post(putUrl())
         .type('form-data')


Send related member or deputy with post data when dragndropping applicants

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -23,6 +23,7 @@
       return false

   initCallSelectionDragNDrops = () ->
+
     onSameForm = (elem1, elem2) ->
       elem1Form = elem1.parents('form:first').first()
       elem2Form = elem2.parents('form:first').first()
@@ -30,13 +31,26 @@

     saveSelection = (droppable, draggable) ->
       form = droppable.parents('form:first').first()
+      valueOrEmpty = (value) ->
+        if value?
+          value
+        else
+          ''
       putUrl = () ->
         form.attr('action') + '.json'
       putParams = () ->
+        correspondingDeputyOrMember = () ->
+          if droppable.hasClass 'deputy'
+            memberId = valueOrEmpty droppable.prevAll('.member:first').find('.member-card').data('id')
+            "&member=#{memberId}"
+          else if droppable.hasClass 'member'
+            deputyId = valueOrEmpty droppable.nextAll('.deputy:first').find('.member-card').data('id')
+            "&deputy=#{deputyId}"
         id = draggable.data('id')
-        value = droppable.data('name')
-        value = '' unless value?
-        form.serialize() + "&selected_as[#{id}]=#{value}"
+        value = valueOrEmpty droppable.data('name')
+        form.serialize() +
+          "&selected_as[#{id}]=#{value}" +
+          correspondingDeputyOrMember()

       superagent.post(putUrl())
         .type('form-data')


Test for selecting into json data

Test for selecting into json data

--- test/generators/flat/cursor.coffee
+++ test/generators/flat/cursor.coffee
@@ -38,6 +38,7 @@

       queue.defer (callback) -> Fabricator.create(Flat, BASE_COUNT, {
         name: Fabricator.uniqueId('flat_')
+        json_data: {foo: {bar: 'baz'}, fizz: 'buzz'}
         created_at: Fabricator.date
         updated_at: Fabricator.date
         boolean: true
@@ -120,6 +121,23 @@

       Flat.each ((model, callback) -> model.save {name: ALBUM_NAME}, callback), runTest

+    it 'Cursor can select fields from json', (done) ->
+      ALBUM_NAME = 'Test3'
+      FIELD_NAMES = ['json_data.foo.bar']
+
+      runTest = (err) ->
+        assert.ok(!err, "No errors: #{err}")
+
+        Flat.cursor({name: ALBUM_NAME}).select(FIELD_NAMES).toJSON (err, models_json) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(_.isArray(models_json), 'cursor toJSON gives us models')
+          for json in models_json
+            assert.equal(_.size(json), FIELD_NAMES.length, 'gets the requested parent value')
+            assert.equal(_.size(json.json_data), 1, 'gets only the requested value')
+          done()
+
+      Flat.each ((model, callback) -> model.save {name: ALBUM_NAME}, callback), runTest
+
     it 'Cursor can select values', (done) ->
       ALBUM_NAME = 'Test4'
       FIELD_NAMES = ['id', 'name']
@@ -132,7 +150,23 @@
           assert.ok(_.isArray(values), 'cursor values is an array')
           for json in values
             assert.ok(_.isArray(json), 'cursor data values is an array')
-            assert.equal(json.length, FIELD_NAMES.length, 'gets only the requested values')
+            assert.equal(json.length, FIELD_NAMES.length, 'gets the requested value, and only the requested value')
+          done()
+
+      Flat.each ((model, callback) -> model.save {name: ALBUM_NAME}, callback), runTest
+
+    it 'Cursor can select values from json', (done) ->
+      ALBUM_NAME = 'Test3'
+      FIELD_NAMES = ['json_data.foo.bar']
+
+      runTest = (err) ->
+        assert.ok(!err, "No errors: #{err}")
+
+        Flat.cursor({name: ALBUM_NAME}).select(FIELD_NAMES).toJSON (err, models_json) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(_.isArray(models_json), 'cursor toJSON gives us models')
+          for json in models_json
+            assert.equal(_.size(json), FIELD_NAMES.length, 'gets only the requested value')
           done()

       Flat.each ((model, callback) -> model.save {name: ALBUM_NAME}, callback), runTest


Fix 'needs' in paper controller test.

Fix 'needs' in paper controller test.

--- test/javascripts/controllers/paper_manage_test.js.coffee
+++ test/javascripts/controllers/paper_manage_test.js.coffee
@@ -1,4 +1,5 @@
 moduleFor 'controller:paperManage', 'PaperManageController',
+  needs: ['controller:application']
   teardown: -> ETahi.reset()
   setup: ->
     setupApp()


Don't allow adding drawing tasks

Don't allow adding drawing tasks

--- app/classifier/tasks/combo/editor.cjsx
+++ app/classifier/tasks/combo/editor.cjsx
@@ -50,7 +50,7 @@
           {Object.keys(@props.workflow.tasks).map (taskKey) =>
             taskDescription = @props.workflow.tasks[taskKey]
             TaskComponent = tasks[taskDescription.type]
-            <option key={taskKey} value={taskKey}>{TaskComponent.getTaskText taskDescription}</option>}
+            <option key={taskKey} value={taskKey} disabled={taskDescription.type is 'drawing'}>{TaskComponent.getTaskText taskDescription}</option>}
         </select>
       </p>
       <p>


Select multiple nodes with selectBox

Select multiple nodes with selectBox


Fix model dialog positioning when resizing [#129096609]

--- src/code/views/modal-view.coffee
+++ src/code/views/modal-view.coffee
@@ -31,6 +31,12 @@
     else
       dimensions

+  updateStyles: ->
+    dimensions = @getDimensions()
+    @setState
+      backgroundStyle: @getBackgroundStyle dimensions
+      contentStyle: @getContentStyle dimensions
+
   # use bind/unbind for clients using older versions of jQuery
   componentDidMount: ->
     $(window).bind 'keyup', @watchForEscape


empty tests for desired behavior

empty tests for desired behavior

--- src/protomixing/protomixing.spec.coffee
+++ src/protomixing/protomixing.spec.coffee
@@ -76,39 +76,41 @@
       beforeEach ->
         @mixin = MIXINS.default_mixin()

+      it 'should invoke an options pre-mixing hook with the prototype context', ->
+        premixing_hook = ->
+          unless @special_key?
+            throw new errors.NotImplemented "Wanted schema key special_key"
+        mix_opts = {premixing_hook}
+
+        spyOn(mix_opts, 'premixing_hook').and.callThrough()
+
+        expect(=>
+          class Example
+            # special_key is not on the prototype; schema unsatisfied, hook will throw
+          Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
+        ).toThrow new errors.NotImplemented('Wanted schema key special_key')
+
+        class Example
+          # special_key is on the prototype; schema satisfied, hook won't throw
+          special_key: 1
+        Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
+
+        expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
+        expect(mix_opts.premixing_hook.calls.count()).toBe 2
+
+        {object, args} = mix_opts.premixing_hook.calls.mostRecent()
+
+        # test that the right context was used
+        expect(object).toBe(Example::)
+        expect(args...).toEqual(['arg1', 'arg2'])
+
       ###
         This is also a good example of pre-mixin hook usage;
         to validate that the mixing class satisfies a certain schema.
       ###
-      it 'should invoke a pre-mixing hook with the prototype context', ->
-        premixing_hook = ->
-          unless @special_key?
-            throw new errors.NotImplemented "Wanted schema key special_key"
-        mix_opts = {premixing_hook}
-
-        spyOn(mix_opts, 'premixing_hook').and.callThrough()
-
-        expect(=>
-          class Example
-            # special_key is not on the prototype; schema unsatisfied, hook will throw
-          Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
-        ).toThrow new errors.NotImplemented('Wanted schema key special_key')
-
-        class Example
-          # special_key is on the prototype; schema satisfied, hook won't throw
-          special_key: 1
-        Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
-
-        expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
-        expect(mix_opts.premixing_hook.calls.count()).toBe 2
-
-        {object, args} = mix_opts.premixing_hook.calls.mostRecent()
-
-        # test that the right context was used
-        expect(object).toBe(Example::)
-        expect(args...).toEqual(['arg1', 'arg2'])
-
-      it 'should invoke the pre-mixing hook before mixing in properties', ->
+      it 'should invoke a mixin pre-mixing hook with the prototype context', ->
+
+      it 'should invoke an options pre-mixing hook before mixing in', ->
         mix_opts = {premixing_hook: ->}
         error = new Error
         threw = false
@@ -127,12 +129,16 @@
           expect(Example::foo).toBeUndefined() # wasn't mixed in!
           expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])

+      it 'should invoke a mixin pre-mixing hook before mixing in', ->
+
+      it 'should invoke an options pre-mixing hook before a mixin pre-mixing hook', ->
+
     describe 'post-mixing hooks', ->

       beforeEach ->
         @mixin = MIXINS.default_mixin()

-      it 'should invoke a post-mixing hook with the prototype context', ->
+      it 'should invoke an options post-mixing hook with the prototype context', ->
         mix_opts = {postmixing_hook: ->}
         spyOn(mix_opts, 'postmixing_hook').and.callThrough()

@@ -147,7 +153,9 @@
         expect(object).toBe(Example::)
         expect(args...).toEqual(['arg1', 'arg2'])

-      it 'should invoke the post-mixing hook after mixing in properties', ->
+      it 'should invoke a mixin post-mixing hook with the prototype context', ->
+
+      it 'should invoke an options post-mixing hook after mixing in', ->
         mix_opts = {postmixing_hook: ->}
         error = new Error
         threw = false
@@ -164,6 +172,10 @@
         finally
           expect(threw).toBe true
           expect(Example::bar).toBe 1 # was mixed in!
+
+      it 'should invoke a mixin post-mixing hook after mixing in', ->
+
+      it 'should invoke an options post-mixing hook before a mixin post-mixing hook', ->

   describe 'protomixing options', ->


Improving DomainsCtrl

Improving DomainsCtrl

Use NamedRouteService

--- app/scripts/controllers/domains.coffee
+++ app/scripts/controllers/domains.coffee
@@ -1,7 +1,7 @@
 'use strict'

 class DomainsCtrl
-  constructor: (@scope, @window, @interval, @DomainsFcty, @MostCommonViolationsFcty, @WebSocketFcty) ->
+  constructor: (@scope, @window, @interval, @DomainsFcty, @MostCommonViolationsFcty, @WebSocketFcty, @NamedRouteService) ->
     @domainsVisible = false
     @groupsVisible = true
     @mostFrequentVisible = false
@@ -17,7 +17,7 @@

     @scope.$on '$destroy', @_cleanUp

-    @_scrollToViolations() if @scope.$parent.prevHash == '#!/violations'
+    @_scrollToViolations() if @scope.$parent.prevHash == @NamedRouteService.reverse('violations')

   _scrollToElement: (el) =>
     srcollY = el[0].offsetTop + parseInt(el.css('margin-top'), 10) / 2
@@ -95,6 +95,6 @@

 angular.module('holmesApp')
-  .controller 'DomainsCtrl', ($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty) ->
+  .controller 'DomainsCtrl', ($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService) ->

-    $scope.model = new DomainsCtrl($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty)
+    $scope.model = new DomainsCtrl($scope, $window, $interval, DomainsFcty, MostCommonViolationsFcty, WebSocketFcty, $NamedRouteService)


Fixes to AccessTokens model

Fixes to AccessTokens model

- Removed userId:clientId foreign key. If someone accesses a client from
a different device, they should be able to use it.
- isUUID instead of isAlpha because those are UUIDs...
- Removed unique:true / foreign key reference on auth code. I'm deleting
auth codes after they're used. They're very temporary.

--- src/server/database/AccessTokens.coffee
+++ src/server/database/AccessTokens.coffee
@@ -3,7 +3,6 @@
 db = require "./database.js"
 Users = require "./Users.js"
 Clients = require "./Clients.js"
-AuthCodes = require "./AuthCodes.js"

 AccessTokens = db.define "AccessTokens", {
 	id:
@@ -17,9 +16,8 @@
 	userId:
 		type: Seq.UUID
 		allowNull: false
-		unique: "userId:clientId"
 		validate:
-			isAlphanumeric: true
+			isUUID: 4
 			notEmpty: true
 		references:
 			model: Users
@@ -27,9 +25,8 @@
 	clientId:
 		type: Seq.UUID
 		allowNull: false
-		unqiue: "userId:clientId"
 		validate:
-			isAlphanumeric: true
+			isUUID: 4
 			notEmpty: true
 		references:
 			model: Clients
@@ -37,13 +34,9 @@
 	authCode:
 		type: Seq.UUID
 		allowNull: false
-		unique: true
 		validate:
 			isUUID: 4
 			notEmpty: true
-		references:
-			model: AuthCodes
-			key: "id"
 }

 module.exports = AccessTokens


assistant: fix extra setup code based on variables

--- src/smc-webapp/assistant/actions.coffee
+++ src/smc-webapp/assistant/actions.coffee
@@ -183,6 +183,8 @@
         vars  = lang.getIn([lvl1, lvl2, 'variables'])
         code  = doc.getIn([1, 0])

+        # extra setup on top
+        extra = undefined
         # given we have a "variables" dictionary, we check
         if vars?
             # ... each line for variables inside of function calls
@@ -198,11 +200,18 @@
             # TODO syntax needs to be language specific!
             extra = vars
                 .filter(((v,k) -> varincode.includes(k)))
-                .entrySeq().map((([k,v]) -> "#{k} = #{v}"))
-            extra = extra.join('\n')
-        else
-            extra = ''
-        return "#{setup}\n#{extra}"
+                .entrySeq()
+                .map((([k,v]) -> "#{k} = #{v}"))
+                .toJS()
+            if extra.length > 0
+                extra = extra.join('\n')
+
+        ret = ''
+        if setup?
+            ret += "#{setup}\n"
+        if extra?
+            ret += "#{extra}\n"
+        return ret

     # for a specific document, set the code and description box values.
     show_doc: (lang, lvl1, lvl2, doc) ->


Don't try to preview an empty image file

--- sample.coffee
+++ sample.coffee
@@ -136,7 +136,7 @@
             'image/png': true
             'image/gif': true
             'image/tiff': true
-         types[this.contentType]?
+         types[this.contentType]? and this.md5 isnt 'd41d8cd98f00b204e9800998ecf8427e'

       loginToken: () ->
          Meteor.userId()


enable cell recycling

enable cell recycling

--- src/components/scores/index.cjsx
+++ src/components/scores/index.cjsx
@@ -127,6 +127,7 @@
         cellRenderer={-> @cellData}
         width={@state.colSetWidth}
         flexGrow={1}
+        allowCellsRecycling={true}
         isResizable=false
         dataKey={i} />
     </ColumnGroup>
@@ -169,6 +170,8 @@
       <Column
         width={@state.colSetWidth * nameColumns}
         flexGrow={1}
+        allowCellsRecycling={true}
+        isResizable=false
         dataKey='0'
         fixed={true}
         cellRenderer={-> @cellData}


No invite if no children assigned

No invite if no children assigned

--- public/javascripts/app/views/parent/invites.coffee
+++ public/javascripts/app/views/parent/invites.coffee
@@ -81,7 +81,7 @@
       email: $form.find("input[name='#{fieldPrefix}s-email']").val()
       children_ids: $form.find("select[name='children_ids']").val()
       gender: if fieldPrefix is "mother" then "female" else "male"
-    if data.name and data.surname and data.email
+    if data.name and data.surname and data.email and data.children_ids
       return data
     else
       false
@@ -132,7 +132,7 @@
     $inputs.each (index, input)->
       $input = $(input)
       that.cachedFormData[$input.attr("name")] = $input.val()
-
+
   restoreFormData: ()=>
     $form = @$("#send-invite-form")
     for inputName, inputData of @cachedFormData


screenPoint cleanup

screenPoint cleanup

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -275,12 +275,10 @@
 			y: point.y || 0
 			z: point.z || 0
 		point = @matrix3d.point(point)
-		superLayers = @superLayers()
-		for layer in superLayers
-			if layer.flat
-				point.z = 0
+		for layer in @superLayers()
+			point.z = 0 if layer.flat
 			point = layer.matrix3d.point(point)
-		return point
+		point

 	@define "boundingBox",
 		get: ->


Better errors in updater

Better errors in updater

--- lib/autoprefixer/updater.coffee
+++ lib/autoprefixer/updater.coffee
@@ -52,6 +52,11 @@
     @doneCallbacks ||= []
     @doneCallbacks.push(callback)

+  # Print error end exit from process
+  error: (message) ->
+    process.stderr.write("\n" + message + "\n")
+    process.exit(1)
+
   # Execute `callback`, when HTTP request will be finished
   request: (callback) ->
     @requestCallbacks ||= []
@@ -64,7 +69,14 @@
       data = ''
       res.on 'data', (chunk) -> data += chunk
       res.on 'end', =>
-        callback(JSON.parse(data))
+        try
+          callback(JSON.parse(data))
+        catch e
+          title = data.match(/<title>([^<]+)<\/title>/)
+          if title
+            @error("#{ title[1] } on #{ path }")
+          else
+            @error("Parsing error in #{ path }:\n#{ e.message }")

         @requests -= 1
         func() for func in @requestCallbacks


:bug: Fix markers not updated after tokenization and editor styling

--- lib/color-buffer-element.coffee
+++ lib/color-buffer-element.coffee
@@ -52,6 +52,8 @@
       @requestSelectionUpdate()
     @subscriptions.add @editor.onDidChangeSelectionRange =>
       @requestSelectionUpdate()
+    @subscriptions.add @editor.displayBuffer.onDidTokenize =>
+      @editorConfigChanged()

     @subscriptions.add atom.config.observe 'editor.fontSize', =>
       @editorConfigChanged()


:art: give focus back to resultsView

:art: give focus back to resultsView

--- lib/project/results-pane.coffee
+++ lib/project/results-pane.coffee
@@ -155,6 +155,8 @@

   collapseAllResults: =>
     @resultsView.collapseAllResults()
+    @resultsView.focus()

   expandAllResults: =>
     @resultsView.expandAllResults()
+    @resultsView.focus()