Fix typo in the last pull request

Fix typo in the last pull request

--- src/bot.coffee
+++ src/bot.coffee
@@ -652,7 +652,7 @@
     rq = api: 'user.modify_profile'
     rq.name       = profile.name if profile.name
     rq.twitter    = profile.twitter if profile.twitter
-    rq.soundcloud    = profile.soundcloud if profile.soundcloud
+    rq.soundcloud = profile.soundcloud if profile.soundcloud
     rq.facebook   = profile.facebook if profile.facebook
     rq.website    = profile.website if profile.website
     rq.about      = profile.about if profile.about


Store a global reference to File-Icons package

--- .config/init.coffee
+++ .config/init.coffee
@@ -33,3 +33,6 @@

 # Disable that useless pending item feature
 atom.workspace.onDidAddPaneItem ({pane}) -> pane.setPendingItem(null)
+
+
+global.fi = atom.packages.loadedPackages["file-icons"]


Revert "adding in footer tests"

Revert "adding in footer tests"

This reverts commit 1fb940d58d5d73703317828d3ec70b2fb5af6813.

--- test/all-tests.coffee
+++ test/all-tests.coffee
@@ -7,9 +7,9 @@
 # TODO figure out why.
 require './components/course-listing.spec'
 require './components/navbar.spec'
-require './components/task-plan/homework/homework-plan.spec'
-require './components/task-plan/homework/exercise-summary.spec'
-require './components/task-plan/footer.spec'
+require './components/homework/homework-plan.spec'
+require './components/homework/exercise-summary.spec'
+require './components/reading-plan.spec'

 require './components/task.spec'
 require './components/task-homework.spec'


Added the Set class for handling rule locals

--- src/lib.coffee
+++ src/lib.coffee
@@ -59,7 +59,19 @@

   # some reflective stuff
   ownPropertyNames = (x) ->
-    key for own key of x
+    key for own key of x
+
+  class Set
+    constructor: ->
+      @data = {}
+
+    add: (what...) ->
+      for one in what
+        @data[one] = true
+      this
+
+    values: ->
+      key for own key of @data

   # some functional programming stuff - never used


Fixed a bug, where the selected dates would be half a day off

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -90,7 +90,6 @@
             @root.select('g.brush').remove()
             @root.append('g')
                 .attr('class', 'brush')
-                .attr('transform', "translate(#{@options.pixelPerDay / 2},0)")
                 .call(@brush)
                 .selectAll('rect')
                     # TODO remove hardcoded height


chore: minify html templates

chore: minify html templates

--- build/config/ngtemplates.coffee
+++ build/config/ngtemplates.coffee
@@ -5,6 +5,9 @@
     module:
       name: "myApp.templates"
       define: true
+    htmlmin:
+      collapseWhitespace: true,
+      collapseBooleanAttributes: true

   myApp:
     src: [


Prefer constraints to where, and use service convention

--- resources/tools/templates/controller.coffee
+++ resources/tools/templates/controller.coffee
@@ -58,8 +58,8 @@
         thing: "#{ filters('templateTitle')(q) } template query"
         tool: 'show-table',
         data:
-          url: scope.connection.root,
-          token: scope.connection.token,
+          service:
+            root: scope.connection.root,
           query: q

     scope.classes = []


Change Grunt tasks

Change Grunt tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -86,13 +86,13 @@
   tasks =
     # Building block tasks
     build: ['clean', 'webpack:main', 'copy:main', 'copy:dev', 'copy:pkg']
-    test: ['mochaTest']
+    test: ['build', 'mochaTest']
     # Deploy tasks
-    dist: ['build', 'test', 'coffee:dist'] # Dist
-    publish: ['build', 'test', 'gh-pages'] # Publish to Github Pages
+    dist: ['build', 'mochaTest', 'coffee:dist'] # Dist
+    publish: ['build', 'mochaTest', 'gh-pages'] # Publish to Github Pages
     # Development tasks
-    dev: ['nolr', 'build', 'test', 'watch']
-    default: ['build', 'connect', 'test', 'watch']
+    dev: ['nolr', 'build', 'mochaTest', 'watch']
+    default: ['build', 'connect', 'mochaTest', 'watch']

   # Project configuration.
   grunt.config.init defaultConfig


Fix build system

Fix build system

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -58,6 +58,7 @@
             'ice-draw': 'draw'
             'ice-coffee': 'coffee'
             'ice-parser': 'parser'
+            'ice-helper': 'helper'
             'ice': 'main'
           name: 'ice'
           optimize: 'none'


Fixed a syntax error.

Fixed a syntax error.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -116,7 +116,6 @@
             cwd: 'build'
             src: 'app/**/*.html'
             dest: 'build/app.templates.js'
-            ]

     # Load plugins
     grunt.loadNpmTasks 'grunt-contrib-jade'


Revert "Compile nu.nl javascript and css"

This reverts commit b93b8c974f1a637f93def717535aaad0b4e1bfc4.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -69,14 +69,10 @@
           'build/js/loader/loader_bookmarklet.js'
         ]
         dest: 'build/factlink_loader_bookmarklet.js'
-      publisher:
-        src:  'build/js/publisher_poc/nu.js'
-        dest: 'build/nu.js'
     less:
       build:
         files:
           'build/css/basic.css': 'app/css/basic.less'
-          'build/css/publisher_poc/nu.nl.css': 'app/css/publisher_poc/nu.nl.less'
     cssmin:
       build:
         options:


copy:ymls in build task

copy:ymls in build task

Don't copy production.yml.sample in copy:ymls grunt task

It's symlinked on deployment.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -218,7 +218,7 @@
       ymls:
         expand: true
         cwd: 'config/settings'
-        src: ['*.yml.sample']
+        src: ['{testing,development}.yml.sample']
         dest: 'config/settings'
         ext: '.yml'

@@ -281,6 +281,7 @@

   #ON DEPLOY
   grunt.registerTask 'build', [
+    'copy:ymls'
     'create_env_settings'
     'bower_install'
     'build_dist'
@@ -299,4 +300,4 @@
     'karma:single'
   ]

-  return+  return


#170: tasks to link npm module and run example

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,3 +1,5 @@
+_ = require 'underscore'
+
 module.exports = (grunt) ->
   grunt.initConfig
     pkg: grunt.file.readJSON("package.json")
@@ -69,3 +71,33 @@
     grammar = grunt.file.read 'app/graphite_grammar.peg'
     parser = PEG.buildParser grammar
     grunt.file.write 'app/graphite_parser.js', "module.exports = #{parser.toSource()};"
+
+  grunt.registerTask 'npm-link', ->
+    done = @async()
+    spawned = grunt.util.spawn cmd: "npm", args: ['link'], opts: {cwd: "#{__dirname}/dist/node", env: _.extend({npm_config_prefix: "#{__dirname}/build/node/npm"}, process.env)}, (err, result, code) ->
+      if err?
+        grunt.log.error 'npm link failed'
+        done false
+      else
+        done()
+    spawned.stdout.pipe(process.stdout)
+    spawned.stderr.pipe(process.stderr)
+
+  grunt.registerTask 'run-node', ->
+    script = grunt.option 'script'
+    unless script?
+      grunt.fail.warn 'script option is required.'
+
+    if /\.coffee$/.test script
+      cmd = 'node_modules/coffee-script/bin/coffee'
+    else
+      cmd = 'node'
+    done = @async()
+    spawned = grunt.util.spawn cmd: cmd, args: [script], opts: {env: _.extend({NODE_PATH: "#{__dirname}/build/node/npm/lib/node_modules"}, process.env)}, (err, result, code) ->
+      if err?
+        grunt.log.error 'script failed'
+        done false
+      else
+        done()
+    spawned.stdout.pipe(process.stdout)
+    spawned.stderr.pipe(process.stderr)


Adjust grunt clean path

Adjust grunt clean path

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,7 +6,7 @@

     # Task configuration
     clean:
-      dist: 'dist'
+      dist: 'public/assets/dist'

     coffee:
       dist:


Reduce duplication of filenames in the dist build

I have verified that this change has no effect on the resulting
build file tree.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -155,6 +155,7 @@
   'generic_ui/polymer/vulcanized*.{html,js}'
   'generic_ui/fonts/*'
   'generic_ui/icons/*'
+  'generic_ui/scripts/get_logs.js'
 ]

 coreDistFiles = [
@@ -182,6 +183,8 @@
   'freedom-pgp-e2e/hex2words.js'
   'freedom-pgp-e2e/e2e.js'
   'freedom-pgp-e2e/pgpapi.json'
+
+  'forge-min/forge.min.js'  # for cloud social provider

   '**/freedom-module.json'
   '**/*.static.js'
@@ -387,7 +390,6 @@
             '_locales/**'

             'generic_ui/scripts/copypaste.js'
-            'generic_ui/scripts/get_logs.js'
             'scripts/context.static.js'
             'scripts/background.static.js'
           ].concat(uiDistFiles, universalDistFiles)
@@ -405,9 +407,6 @@
             'polymer/vulcanized.{html,js}'

             'freedom-for-chrome/freedom-for-chrome.js'
-
-            # for cloud social provider
-            'forge-min/forge.min.js'
           ].concat(coreDistFiles, universalDistFiles)
           dest: 'build/dist/chrome/app'
         }
@@ -426,13 +425,9 @@
             # addon sdk scripts
             'lib/**/*.js'

-            'data/generic_ui/scripts/get_logs.js'
             'data/scripts/content-proxy.js'

             'data/freedom-for-firefox/freedom-for-firefox.jsm'
-
-            # for cloud social provider
-            'data/forge-min/forge.min.js'
           ].concat(
             getWithBasePath(uiDistFiles, 'data'),
             getWithBasePath(coreDistFiles, 'data'),
@@ -455,17 +450,7 @@
             # This is not browserified so we use .js instead of .static.js
             'polymer/vulcanized.{html,js}'

-            'generic_ui/scripts/copypaste.js'
-            'generic_ui/scripts/get_logs.js'
-
-            # TODO move to universalDistFiles once supported on other platforms
-            'freedom-social-quiver/socketio.quiver.json'
-            'freedom-social-quiver/socketio.quiver.js'
-
             'freedom-for-chrome/freedom-for-chrome.js'
-
-            # for cloud social provider
-            'forge-min/forge.min.js'
           ].concat(uiDistFiles, coreDistFiles, universalDistFiles)
           dest: ccaDistPath
         }


Add git user for Travis autodeploy

Add git user for Travis autodeploy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -67,6 +67,9 @@
         clone: 'gh-pages'
         message: 'Updating'
         repo: 'https://' + process.env.GH_TOKEN + '@github.com/noflo/noflo-browser-app.git'
+        user:
+          name: 'NoFlo bot'
+          email: 'bot@noflo.org'
         silent: false
       src: '**/*'


Fix Grunt release configuration.

Fix Grunt release configuration.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -43,7 +43,7 @@
       dist:
         files:
           'dist/compiler/hamlcoffee.min.js': ['dist/compiler/hamlcoffee.js']
-    release
+    release:
       bump: false
       file: 'package.json CHANGELOG.md dist/compiler/hamlcoffee.js dist/compiler/hamlcoffee.min.js'


Don't gzip css files, they're not served anyhow

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -99,8 +99,6 @@
     shell:
       gzip_js_files:
         command: ' find build/ -iname \'*.js\'  -exec bash -c \' gzip -9 -f < "{}" > "{}.gz" \' \\; '
-      gzip_css_files:
-        command: ' find build/ -iname \'*.css\'  -exec bash -c \' gzip -9 -f < "{}" > "{}.gz" \' \\; '

     copy:
       build:
@@ -171,7 +169,7 @@
     'clean', 'copy:build',  'copy:start_stop_files', 'coffee','copy:postFactlinkObject',
     'sass', 'cssUrlEmbed', 'cssmin',
     'concat', 'uglify', 'code_inliner',
-    'shell:gzip_css_files', 'shell:gzip_js_files', 'copy:dist'
+    'shell:gzip_js_files', 'copy:dist'
   ]
   grunt.registerTask 'test',    ['qunit']


New Prod test

New Prod test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -102,20 +102,6 @@
 					dest: '<%= settings.distDirectory %>'
 					expand: true
 				]
-
-		# Renames files based on their hashed content
-		# When the files contents change, the hash value changes
-		# Used as a cache buster, ensuring browsers load the correct static resources
-		#
-		# glyphicons-halflings.png -> glyphicons-halflings.6c8829cc6f.png
-		# scripts.min.js -> scripts.min.6c355e03ee.js
-		hash:
-			images: '.temp/**/*.{gif,jpeg,jpg,png,svg,webp}'
-			scripts:
-				cwd: '.temp'
-				src: ['scripts/scripts.min.js']
-				expand: true
-			styles: '.temp/styles/styles.min.css'

 		# Compresses image files
 		imagemin:


Added grunt bump task for version management

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,10 +1,11 @@
-shell = require 'shelljs'
 module.exports = (grunt) ->
+  pkgFile = 'sieve.jquery.json'
+  pkg = grunt.file.readJSON pkgFile

   # Project configuration.
   grunt.initConfig
     # Metadata.
-    pkg: grunt.file.readJSON 'sieve.jquery.json'
+    pkg: pkg
     basename: 'jquery.<%= pkg.name %>'
     headline: '<%= pkg.title || pkg.name %> v<%= pkg.version %>'
     copyright: '<%= grunt.template.today("yyyy") %> <%= pkg.author.name %>'
@@ -76,7 +77,20 @@
   grunt.registerTask 'test', ['qunit']
   grunt.registerTask 'default', ['clean', 'build', 'test']

-  grunt.registerTask 'release', (type) ->
+  grunt.registerTask 'bump', (type) ->
+    oldVersion = pkg.version
+    pkg.version = versionBump(oldVersion, type)
+    grunt.log.write('Bumping version to ' + pkg.version);
+    grunt.file.write(pkgFile, JSON.stringify(pkg, null, '  ') + '\n')
+    grunt.log.ok()
+    grunt.task.run('default', 'commit');
+
+  grunt.registerTask "commit", ->
+    run "git add ."
+    run "git commit -m 'Bumped version to v#{pkg.version}'"
+    grunt.log.subhead("DON'T FORGET TO `grunt release`")
+
+  grunt.registerTask 'release', 'Tag and publish a new release.', ->
     version = grunt.config("pkg.version")
     run "git rebase master gh-pages"
     run "git checkout master"
@@ -85,6 +99,7 @@
     run "git push origin gh-pages"
     run "git push --tags"

+  shell = require 'shelljs'
   run = (command) ->
     grunt.log.write "Running `#{command}`..."
     p = shell.exec(command, silent: true)
@@ -94,3 +109,13 @@
       grunt.log.error()
       grunt.log.error(p.output)
       grunt.warn "Non-zero exit code for `#{command}`."
+
+  versionBump = (version, versionType) ->
+    type = major: 0, minor: 1, patch: 2
+    parts = version.split('.')
+    idx = type[versionType || 'patch']
+
+    parts[idx] = parseInt(parts[idx], 10) + 1
+    while ++idx < parts.length
+      parts[idx] = 0
+    return parts.join('.')


sets precision to one digit

sets precision to one digit

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -254,8 +254,9 @@
     dictFileMBUnitFormat: "MB"
     dictFileKBUnitFormat: "KB"
     dictFileBUnitFormat: "b"
-    dictFileThousandsSeparator: ","
-    dictFileDecimalSeparator: "."
+    dictNumberPrecision: 1
+    dictNumberDelimiter: ","
+    dictNumberSeparator: "."

     # If `done()` is called without argument the file is accepted
@@ -813,13 +814,13 @@

   # Returns a nicely formatted filesize
   filesize: (size) ->
-    formatNumber = (n, dp, thousandsSep, decimalSep) ->
+    numberWithDelimiter = (n, dp, delimiter, separator) ->
       s = "" + (Math.floor(n))
       d = n % 1
       i = s.length
       r = ""
-      r = thousandsSep + s.substr(i, 3) + r  while (i -= 3) > 0
-      s.substr(0, i + 3) + r + ((if d then decimalSep + Math.round(d * Math.pow(10, dp)) else ""))
+      r = delimiter + s.substr(i, 3) + r  while (i -= 3) > 0
+      s.substr(0, i + 3) + r + ((if d then separator + Math.round(d * Math.pow(10, dp)) else ""))

     units = [
       @options.dictFileTBUnitFormat,
@@ -841,7 +842,7 @@

     selectedSize = Math.round(10 * selectedSize) / 10 # Cutting of digits

-    "#{formatNumber(selectedSize, 2, @options.dictFileThousandsSeparator, @options.dictFileDecimalSeparator)} #{selectedUnit}"
+    '' + numberWithDelimiter(selectedSize, @options.dictNumberPrecision, @options.dictNumberDelimiter, @options.dictNumberSeparator) + ' ' + selectedUnit

   # Adds or removes the `dz-max-files-reached` class from the form.


Dropzones didn't add themselves to the instances array.

Fixes #58

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -245,6 +245,8 @@

     throw new Error "Dropzone already attached." if Dropzone.forElement @element

+    # Now add this dropzone to the instances.
+    Dropzone.instances.push @

     # Get the `Dropzone.options.elementId` for this element if it exists
     elementId = @element.id


Updating mount resource snippet

Updating mount resource snippet

--- snippets/mount.cson
+++ snippets/mount.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe mount':
     'prefix': 'mount'
-    'body': "# http://inspec.io/docs/reference/resources/mount/\ndescribe mount('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/mount/\ndescribe mount('${1:path}') do\n  it { should${2:_not} be_mounted }\n  its('${3:setting}') { should eq '${4:value}' }${5}\nend"


Pass options to AttributeTemplateParser

Pass options to AttributeTemplateParser

--- src/lib.coffee
+++ src/lib.coffee
@@ -18,6 +18,9 @@
     finishedClass: String
     loadedCallback: () -> void
     loadingClass: String
+    displayAttribute: String
+    hidableAttribute: String
+    zeroPadOverrides: String => bool
 countdown.StateWindow: (Copied from component/countdown)
     getUpdatedPeriod: () -> Period
     getCountdownPeriod: () -> Period
@@ -46,7 +49,7 @@
             ClassList(elem).remove(options['loadingClass']) for elem in elements
             oldLoadedCallback?()

-    baseCountdown endDate, new AttributeTemplateParser().build(elements), options
+    baseCountdown endDate, new AttributeTemplateParser(options).build(elements), options

 module.exports =


Make a shortcut for large bounding boxes

Needed for certain embedding contexts

--- src/map.coffee
+++ src/map.coffee
@@ -180,6 +180,15 @@
             min = x: ne.x, y: sw.y
             max = y: ne.y, x: sw.x

+            if ((Math.abs(min.x - max.x) / METER_GRID) > 4 or
+               (Math.abs(max.y - min.y) / METER_GRID) > 4)
+                # The idea of the grids is to maximize cache hits. But
+                # if the original bounding box spans too many grid
+                # elements, it defeats the purpose, so disable
+                # gridding in that case.
+                return [[[min.x, min.y],[max.x, max.y]]]
+
+
             snapToGrid = (coord) ->
                 parseInt(coord / METER_GRID) * METER_GRID
             coordinates = {}


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -54,6 +54,6 @@
     width: 10
     height: 10

-  Collision.rayRectangle ""
+  Collision.rayRectangle
 module()


factor out a formatCaption method

factor out a formatCaption method

--- src/frinkiac.coffee
+++ src/frinkiac.coffee
@@ -32,8 +32,8 @@
   encodeURIComponent(str).replace /[!'()*]/g, (c) ->
     '%' + c.charCodeAt(0).toString(16)

-trimWhitespace = (string) ->
-  string.replace /^\s*|\s*$/g, ''
+getImageUrl = (episode, timestamp, caption) ->
+  "https://frinkiac.com/meme/#{episode}/#{timestamp}.jpg?lines=#{encode(caption)}"

 addLineBreaks = (str) ->
   wordCount = 4
@@ -44,15 +44,17 @@
     newString += word + delimiter
   newString

-getImageUrl = (episode, timestamp, caption) ->
-  encodedUrl = encode(addLineBreaks(trimWhitespace(caption)))
-  "https://frinkiac.com/meme/#{episode}/#{timestamp}.jpg?lines=#{encodedUrl}"
+trimWhitespace = (string) ->
+  string.replace /^\s*|\s*$/g, ''
+
+formatCaption = (caption) ->
+  addLineBreaks(trimWhitespace(caption))

 combineCaptions = (captions) ->
-  if captions.length <= 3
+  if captions.length <= 4
     newCaption = ''
     captions.forEach (caption, i) ->
-      newCaption += caption.Content
+      newCaption += formatCaption(caption.Content)
       unless i == (captions.length - 1)
         newCaption += '\n'
     newCaption
@@ -71,7 +73,7 @@
           timestamp = response.data[0].Timestamp

           if customCaption
-            msg.send getImageUrl(episode, timestamp, trimWhitespace(customCaption))
+            msg.send getImageUrl(episode, timestamp, formatCaption(customCaption))

           else
             axios(getRequestConfig('caption', {e: episode, t: timestamp}))


Add helper methods to JS validation spec for invalid branch names

--- spec/javascripts/new_branch_spec.js.coffee
+++ spec/javascripts/new_branch_spec.js.coffee
@@ -5,151 +5,156 @@
   describe 'create a new branch', ->
     fixture.preload('new_branch.html')

+    fillNameWith = (value) ->
+      $('.js-branch-name').val(value).trigger('blur')
+
+    expectToHaveError = (error) ->
+      expect($('.js-branch-name-error span').text()).toEqual(error)
+
     beforeEach ->
       fixture.load('new_branch.html')
       $('form').on 'submit', (e) -> e.preventDefault()

       @form = new NewBranchForm($('.js-create-branch-form'), [])
-      @name = $('.js-branch-name')

     it "can't start with a dot", ->
-      @name.val('.foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't start with '.'")
+      fillNameWith '.foo'
+      expectToHaveError "can't start with '.'"

     it "can't start with a slash", ->
-      @name.val('/foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't start with '/'")
+      fillNameWith '/foo'
+      expectToHaveError "can't start with '/'"

     it "can't have two consecutive dots", ->
-      @name.val('foo..bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '..'")
+      fillNameWith 'foo..bar'
+      expectToHaveError "can't contain '..'"

     it "can't have spaces anywhere", ->
-      @name.val(' foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain spaces")
-      @name.val('foo bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain spaces")
-      @name.val('foo ').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain spaces")
+      fillNameWith ' foo'
+      expectToHaveError "can't contain spaces"
+      fillNameWith 'foo bar'
+      expectToHaveError "can't contain spaces"
+      fillNameWith 'foo '
+      expectToHaveError "can't contain spaces"

     it "can't have ~ anywhere", ->
-      @name.val('~foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
-      @name.val('foo~bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
-      @name.val('foo~').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
+      fillNameWith '~foo'
+      expectToHaveError "can't contain '~'"
+      fillNameWith 'foo~bar'
+      expectToHaveError "can't contain '~'"
+      fillNameWith 'foo~'
+      expectToHaveError "can't contain '~'"

     it "can't have tilde anwhere", ->
-      @name.val('~foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
-      @name.val('foo~bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
-      @name.val('foo~').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '~'")
+      fillNameWith '~foo'
+      expectToHaveError "can't contain '~'"
+      fillNameWith 'foo~bar'
+      expectToHaveError "can't contain '~'"
+      fillNameWith 'foo~'
+      expectToHaveError "can't contain '~'"

     it "can't have caret anywhere", ->
-      @name.val('^foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '^'")
-      @name.val('foo^bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '^'")
-      @name.val('foo^').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '^'")
+      fillNameWith '^foo'
+      expectToHaveError "can't contain '^'"
+      fillNameWith 'foo^bar'
+      expectToHaveError "can't contain '^'"
+      fillNameWith 'foo^'
+      expectToHaveError "can't contain '^'"

     it "can't have : anywhere", ->
-      @name.val(':foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain ':'")
-      @name.val('foo:bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain ':'")
-      @name.val(':foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain ':'")
+      fillNameWith ':foo'
+      expectToHaveError "can't contain ':'"
+      fillNameWith 'foo:bar'
+      expectToHaveError "can't contain ':'"
+      fillNameWith ':foo'
+      expectToHaveError "can't contain ':'"

     it "can't have question mark anywhere", ->
-      @name.val('?foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '?'")
-      @name.val('foo?bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '?'")
-      @name.val('foo?').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '?'")
+      fillNameWith '?foo'
+      expectToHaveError "can't contain '?'"
+      fillNameWith 'foo?bar'
+      expectToHaveError "can't contain '?'"
+      fillNameWith 'foo?'
+      expectToHaveError "can't contain '?'"

     it "can't have asterisk anywhere", ->
-      @name.val('*foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '*'")
-      @name.val('foo*bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '*'")
-      @name.val('foo*').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '*'")
+      fillNameWith '*foo'
+      expectToHaveError "can't contain '*'"
+      fillNameWith 'foo*bar'
+      expectToHaveError "can't contain '*'"
+      fillNameWith 'foo*'
+      expectToHaveError "can't contain '*'"

     it "can't have open bracket anywhere", ->
-      @name.val('[foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '['")
-      @name.val('foo[bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '['")
-      @name.val('foo[').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '['")
+      fillNameWith '[foo'
+      expectToHaveError "can't contain '['"
+      fillNameWith 'foo[bar'
+      expectToHaveError "can't contain '['"
+      fillNameWith 'foo['
+      expectToHaveError "can't contain '['"

     it "can't have a backslash anywhere", ->
-      @name.val('\\foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '\\'")
-      @name.val('foo\\bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '\\'")
-      @name.val('foo\\').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '\\'")
+      fillNameWith '\\foo'
+      expectToHaveError "can't contain '\\'"
+      fillNameWith 'foo\\bar'
+      expectToHaveError "can't contain '\\'"
+      fillNameWith 'foo\\'
+      expectToHaveError "can't contain '\\'"

     it "can't contain a sequence @{ anywhere", ->
-      @name.val('@{foo').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '@{'")
-      @name.val('foo@{bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '@{'")
-      @name.val('foo@{').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '@{'")
+      fillNameWith '@{foo'
+      expectToHaveError "can't contain '@{'"
+      fillNameWith 'foo@{bar'
+      expectToHaveError "can't contain '@{'"
+      fillNameWith 'foo@{'
+      expectToHaveError "can't contain '@{'"

     it "can't have consecutive slashes", ->
-      @name.val('foo//bar').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain consecutive slashes")
+      fillNameWith 'foo//bar'
+      expectToHaveError "can't contain consecutive slashes"

     it "can't end with a slash", ->
-      @name.val('foo/').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't end in '/'")
+      fillNameWith 'foo/'
+      expectToHaveError "can't end in '/'"

     it "can't end with a dot", ->
-      @name.val('foo.').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't end in '.'")
+      fillNameWith 'foo.'
+      expectToHaveError "can't end in '.'"

     it "can't end with .lock", ->
-      @name.val('foo.lock').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't end in '.lock'")
+      fillNameWith 'foo.lock'
+      expectToHaveError "can't end in '.lock'"

     it "can't be the single character @", ->
-      @name.val('@').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't be '@'")
+      fillNameWith '@'
+      expectToHaveError "can't be '@'"

     it "concatenates all error messages", ->
-      @name.val('/foo bar?~.').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't start with '/', can't contain spaces, '?', '~', can't end in '.'")
+      fillNameWith '/foo bar?~.'
+      expectToHaveError "can't start with '/', can't contain spaces, '?', '~', can't end in '.'"

     it "doesn't duplicate error messages", ->
-      @name.val('?foo?bar?zoo?').trigger('blur')
-      expect($('.js-branch-name-error span').text()).toEqual("can't contain '?'")
+      fillNameWith '?foo?bar?zoo?'
+      expectToHaveError "can't contain '?'"

     it "removes the error message when is a valid name", ->
-      @name.val('foo?bar').trigger('blur')
+      fillNameWith 'foo?bar'
       expect($('.js-branch-name-error span').length).toEqual(1)
-      @name.val('foobar').trigger('blur')
+      fillNameWith 'foobar'
       expect($('.js-branch-name-error span').length).toEqual(0)

     it "can have dashes anywhere", ->
-      @name.val('-foo-bar-zoo-').trigger('blur')
+      fillNameWith '-foo-bar-zoo-'
       expect($('.js-branch-name-error span').length).toEqual(0)

     it "can have underscores anywhere", ->
-      @name.val('_foo_bar_zoo_').trigger('blur')
+      fillNameWith '_foo_bar_zoo_'
       expect($('.js-branch-name-error span').length).toEqual(0)

     it "can have numbers anywhere", ->
-      @name.val('1foo2bar3zoo4').trigger('blur')
+      fillNameWith '1foo2bar3zoo4'
       expect($('.js-branch-name-error span').length).toEqual(0)

     it "can be only letters", ->
-      @name.val('foo').trigger('blur')
+      fillNameWith 'foo'
       expect($('.js-branch-name-error span').length).toEqual(0)


:lipstick: Add missing whitespace in matcher message

--- specs/support/geom/rectangle_helper.coffee
+++ specs/support/geom/rectangle_helper.coffee
@@ -7,7 +7,7 @@
     toBeRectangle: ->
       compare: (actual,x=0, y=0, width=0, height=0, rotation=0) ->
         result = {}
-        result.message = "Expect #{actual}to be a rectangle equals to (#{x},#{y},#{width},#{height},#{rotation})"
+        result.message = "Expect #{actual} to be a rectangle equals to (#{x},#{y},#{width},#{height},#{rotation})"

         result.pass = actual.x is x and
         actual.y is y and


Use package name and version in default title

--- src/cli.coffee
+++ src/cli.coffee
@@ -21,7 +21,7 @@
 class Cli
   # Public: Parses the command-line arguments.
   constructor: ->
-    @version = require('../package').version
+    @packageInfo = require('../package')

   # Public: Parses the command-line arguments.
   parseArguments: (argv = process.argv) ->
@@ -36,10 +36,10 @@
         default: false
         describe: 'Dump metadata to a file or api.json if no filename given'
       .options 'title',
-        default: path.basename process.cwd()
+        default: "#{@packageInfo.name} v#{@packageInfo.version}"
         describe: 'Title of index page'
       .help('help').alias('help', '?')
-      .version("v#{@version}")
+      .version("v#{@packageInfo.version}")
       .parse(argv)

   # Public: Executes the program.
@@ -149,7 +149,7 @@
                           content: content
                           title: @args.title
                           navigation: @navigation
-                          version: @version
+                          version: @packageInfo.version
     fs.writeFileSync(filePath, doc)

   renderFile: (file) ->


Set flag to indicate changeset is being applied

--- client/lanes/models/Base.coffee
+++ client/lanes/models/Base.coffee
@@ -13,6 +13,7 @@
         lastServerMessage: { type: 'string' }
         isDirty:   { type: 'boolean', default: false }
         invalidAttributes: 'array'
+        updatingFromChangeset: { type: 'boolean', default: false }

     derived:
         isSavable:
@@ -47,10 +48,12 @@
     # used by PubSub to record a remote change to the model
     addChangeSet: (change) ->
         change = new Lanes.Models.ChangeSet(change)
+        this.set('updatingFromChangeset', true, silent: true)
         this.set( change.value() )
         for name, value of change.value()
-            this.trigger("remote-update:#{name}", "changeset", this, change)
+            this.trigger("remote-update:#{name}", this, change)
         this.triggerChangeSet(this, change)
+        this.set('updatingFromChangeset', false, silent: true)

     triggerChangeSet: (triggered, change) ->
         this.changes ||= new Lanes.Models.ChangeSet.Collection( parent: this )


fixed erroneous merge

fixed erroneous merge

--- core/app/backbone/views/facts/fact_bottom_view.coffee
+++ core/app/backbone/views/facts/fact_bottom_view.coffee
@@ -36,8 +36,6 @@
         @$('.popup-content .' + popup + '-container').show()
         @$('.transparent-layer').show()

-        @startConversationRegion.show new StartConversationView(model: @model)
-
         collection = @model.getOwnContainingChannels()
         collection.on "add", (channel) =>
           @model.addToChannel channel, {}


Log high-fives with no dollars

Log high-fives with no dollars

--- src/highfive.coffee
+++ src/highfive.coffee
@@ -146,6 +146,16 @@
             roomid = process.env.HUBOT_HIGHFIVE_ROOM || msg.envelope.room
             chatService.message roomid, from_obj, to_obj, reason

+            if amt == 0
+                logToSheet robot, [
+                    moment(),       # date
+                    from_obj.email, # from
+                    to_obj.email,   # to
+                    amt,            # amount
+                    reason,         # reason
+                    '',             # gift card code
+                    # TODO: link to transcript?
+                  ]
             if awardLimit != 0 and amt > 0
                 return tango(robot).order msg, from_obj, to_obj, amt, reason
                 , (order) ->


:memo: Merge example with return doc

:memo: Merge example with return doc

--- src/git.coffee
+++ src/git.coffee
@@ -250,12 +250,7 @@
   # Public: Returns the upstream branch for the current HEAD, or null if there
   # is no upstream branch for the current HEAD.
   #
-  # Examples
-  #
-  #   getUpstreamBranch()
-  #   # => "refs/remotes/origin/master"
-  #
-  # Returns a String.
+  # Returns a String branch name such as `refs/remotes/origin/master`
   getUpstreamBranch: -> @getRepo().getUpstreamBranch()

   # Public: Returns the current SHA for the given reference.


Updated the usage

Updated the usage

--- src/jira-cli.coffee
+++ src/jira-cli.coffee
@@ -161,7 +161,7 @@
             alias:'help'
             describe:'Shows this help message'
             default:false
-        }).usage('Usage: $0 -f "EG-143"\n $0 -r "EG-143"')
+        }).usage('Usage: jira -f EG-143 -- jira -r EG-143')
         .boolean('h')
         .boolean('l')
         .string('f')


one of everything

one of everything

--- src/games/sampler.coffee
+++ src/games/sampler.coffee
@@ -13,7 +13,12 @@
   levels: [
     {
       name: 'One of Everything'
-      sprites: [me]
+      sprites: [
+        {kind: 'turtle'}
+        {kind: 'wall'}
+        {kind: 'egg'}
+        {kind: 'gate'}
+      ]
       bricks: 7
       goal: {
         clicks: 1


Fixed check of updateHash setting.

Fixed check of updateHash setting.

--- src/jqt.coffee
+++ src/jqt.coffee
@@ -434,7 +434,7 @@
         .bind((if support.touch then "touchstart" else "mousedown"), touchStartHandler)
         .trigger "orientationchange"

-      $(window).bind("hashchange", hashChangeHandler) if @updateHash
+      $(window).bind("hashchange", hashChangeHandler) if @settings.updateHash
       startHash = location.hash

       # Determine what the initial view should be
@@ -447,7 +447,7 @@

       addPageToHistory $currentPage

-      @goTo(startHash) if @updateHash and $(startHash).length
+      @goTo(startHash) if @settings.updateHash and $(startHash).length

     orientationChangeHandler = ->
       # Scroll to top if we change orientation


Changing 'not found' message

Changing 'not found' message

--- src/scripts/httpcat.coffee
+++ src/scripts/httpcat.coffee
@@ -31,7 +31,7 @@
         if (codes.indexOf( status ) > -1)
             msg.send 'http://httpcats.herokuapp.com/' + status + '.jpg'
         else
-            msg.send 'I only recognize the following status codes: ' + codes
+            msg.send "That's not a valid HTTP status code, sorry amigo!"

     robot.respond /httpcat help/i, (msg) ->


Changes on License

Changes on License

--- src/quo.coffee
+++ src/quo.coffee
@@ -1,7 +1,26 @@
 ###
-  QuoJS 2.0
-  (c) 2011, 2012 Javi Jiménez Villar (@soyjavi)
-  http://quojs.tapquo.com
+    QuoJS 2.0
+    http://quojs.tapquo.com
+
+    Copyright (C) 2011,2012 Javi Jiménez Villar (@soyjavi)
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
 ###

 Quo = (->


ui: scroll to top on screen change

ui: scroll to top on screen change

--- ui/index.coffee
+++ ui/index.coffee
@@ -425,12 +425,12 @@
   events:
     'click a': 'onClick'
     'touchstart a': 'onClick'
-
+
   onClick: (e) ->
-      href = e.currentTarget.attributes?.href?.value
-      if href? and not /https?:/.exec href
-        e.preventDefault()
-        this.router.navigate href, trigger: true
+    href = e.currentTarget.attributes?.href?.value
+    if href? and not /https?:/.exec href
+      e.preventDefault()
+      this.router.navigate href, trigger: true

   getInitialState: ->
     {user: this.getUser()}
@@ -447,6 +447,7 @@
       Backbone.history.history.back()

   show: (screen, options = {}) ->
+    window.scrollTo(0)
     this.setState {screen}
     screenURL = screen.url()
     this.router.navigate screenURL, {trigger: options.trigger} if screenURL?


webapp: be graceful with mathjax errors

webapp: be graceful with mathjax errors

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -62,7 +62,8 @@
 window.MathJax = exports.MathJaxConfig =
     skipStartupTypeset: true
     extensions: ["tex2jax.js","asciimath2jax.js"]  # "static/mathjax_extensions/xypic.js"
-    jax: ["input/TeX","input/AsciiMath", "output/SVG"]
+    # common html is the output default: http://docs.mathjax.org/en/latest/output.html
+    jax: ["input/TeX","input/AsciiMath", "output/CommonHTML"]
     # http://docs.mathjax.org/en/latest/options/tex2jax.html
     tex2jax:
         inlineMath: [ ['$','$'], ["\\(","\\)"] ]
@@ -72,7 +73,7 @@
         skipTags: ["script","noscript","style","textarea","pre","code"]

     TeX:
-        extensions: ["autoload-all.js"]
+        extensions: ["autoload-all.js", "noUndefined.js", "noErrors.js"]
         Macros:  # get these from sage/misc/latex.py
             Bold:  ["\\mathbb{#1}",1]
             ZZ:    ["\\Bold{Z}",0]
@@ -93,6 +94,22 @@
             Zp:    ["\\ZZ_{#1}",1]
             Qp:    ["\\QQ_{#1}",1]
             Zmod:  ["\\ZZ/#1\\ZZ",1]
+        noErrors:  # http://docs.mathjax.org/en/latest/tex.html#noerrors
+            inlineDelimiters : ["$","$"]
+            multiLine        : true
+            style:
+                "font-size"      : "85%"
+                "text-align"     : "left"
+                "color"          : "red"
+                "padding"        : "1px 3px"
+                "background"     : "#FFEEEE"
+                "border"         : "none"
+        noUndefined:  # http://docs.mathjax.org/en/latest/tex.html#noundefined
+            attributes:
+                mathcolor        : "red"
+                mathbackground   : "#FFEEEE"
+                mathsize         : "90%"
+

     # do not use "xypic.js", frequently causes crash!
     "HTML-CSS":


Remove unused functions

Remove unused functions

--- admin/static/coffee/app.coffee
+++ admin/static/coffee/app.coffee
@@ -8,32 +8,11 @@
     modal_registry = []
 register_modal = (modal) -> modal_registry.push(modal)

-#TODO Just for development, CHANGE IT BACK TO 5000
 updateInterval = 5000
 statUpdateInterval = 1000
 progress_interval_default_value = 5000
 progress_interval_value = 5000
 progress_short_interval = 1000
-
-# Check if new_data is included in old_data and if the values are equals. (Note: We don't check if the objects are equals)
-need_update_objects = (new_data, old_data) ->
-    for key of new_data
-        if key of old_data is false
-            return true
-
-    for key of new_data
-        if typeof new_data[key] is object and typeof old_data[key] is object
-            need_update = compare_object(new_data[key], old_data[key])
-            if need_update is true
-                return need_update
-        else if typeof new_data[key] isnt typeof old_data[key]
-            return true
-        else if new_data[key] isnt old_data[key]
-            return true
-
-    return false
-
-

 apply_to_collection = (collection, collection_data) ->
     for id, data of collection_data
@@ -44,13 +23,7 @@
                         if !machines.get(machine_uuid)?
                             delete collection_data[id].blueprint.peers_roles[machine_uuid]
             if collection.get(id)
-                # We update only if something changed so we don't trigger to much 'update'
-                ###
-                need_update = need_update_objects(data, collection.get(id))
-                if need_update is true
-                ###
                 collection.get(id).set(data)
-                collection.trigger('change')
             else
                 data.id = id
                 collection.add(new collection.model(data))


Update example-js.coffee

Update example-js.coffee

Incorrect descriptions in example-js

--- example/example-js.coffee
+++ example/example-js.coffee
@@ -88,8 +88,8 @@
           {block: 'random(1, 100)', title: 'Get a random number in a range'}
           {block: 'round(__)', title: 'Round to the nearest integer'}
           {block: 'abs(__)', title: 'Absolute value'}
-          {block: 'max(__, __)', title: 'Absolute value'}
-          {block: 'min(__, __)', title: 'Absolute value'}
+          {block: 'max(__, __)', title: 'Get the larger of two numbers'}
+          {block: 'min(__, __)', title: 'Get the smaller on two numbers'}
         ]
       }
       {


CTRL+A

CTRL+A

--- lib/views/winning-selectionmodel.coffee
+++ lib/views/winning-selectionmodel.coffee
@@ -46,10 +46,17 @@
         @onMouseDown(e, args)

     onKeyDown: (e, args) =>
+        data = @grid.getData()
+        columns = @grid.getColumns()
         if e.keyCode == 27
             @ranges = []
             @activeRange = null
             @onSelectedRangesChanged.notify @ranges
+        if e.keyCode == 65 and e.metaKey and data.length > 0
+            @ranges = []
+            @activeRange = new Slick.Range 0, 0, data.length - 1, columns.length - 1
+            console.log @activeRange, data.length, data[0]
+            @onSelectedRangesChanged.notify [ @activeRange ]
         if (e.metaKey or e.ctrlKey) and e.keyCode == 67
             selectedColumns = []
             output = []
@@ -60,7 +67,7 @@
                     for y in [range.fromRow..range.toRow]
                         selectedColumns.push({x: x, y:y})
             for cell in selectedColumns
-                output.push(data[cell.y][columns[cell.x]["field"]].toString())
+                output.push(data[cell.y][columns[cell.x]["field"]]?.toString())
             atom.clipboard.write(output.join(", ").toString())
             #atom.clipboard.write(@data[args.row][@columns[args.cell]["field"]].toString())


added game to #66

added game to #66

--- tests/TestGame.coffee
+++ tests/TestGame.coffee
@@ -806,7 +806,100 @@
   ]
 }

+game_13_03_5 = {
+  "referee": "Озб",
+  "date": "2014-02-13",
+  "result": "mafia_win",
+  "firstKilledAtNight": "Женька-Печенька",
+  "firstKilledByDay": "Капоне",
+  "bestMoveAuthor": "Женька-Печенька",
+  "bestMoveAccuracy": 2,
+  "players": [
+    {
+      "name": "Манчестер",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 3,
+      "isBest": true,
+      "extraScores": 0.5
+    },
+    {
+      "name": "Sam",
+      "role": "sheriff",
+      "fouls": 1,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Агата",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 1,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "kors",
+      "role": "don",
+      "fouls": 2,
+      "likes": 2,
+      "isBest": true,
+      "extraScores": 0.5
+    },
+    {
+      "name": "Женька-Печенька",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 2,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Енот",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Марвел",
+      "role": "mafia",
+      "fouls": 3,
+      "likes": 2,
+      "isBest": true,
+      "extraScores": 0.5
+    },
+    {
+      "name": "Капоне",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Фельдшер",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Олифер",
+      "role": "mafia",
+      "fouls": 2,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    }
+  ]
+}
+
 games = [game_10_03_3, game_10_03_3_1,
+         game_13_03_5,
          game_16_03_2, game_16_03_3, game_16_03_1,
          game_20_03_1, game_20_03_2, game_20_03_3, game_20_03_4]


Fixed broken tests

Fixed broken tests

--- test/unit/controllers/backlogControllerSpec.coffee
+++ test/unit/controllers/backlogControllerSpec.coffee
@@ -250,11 +250,11 @@
         it "should allow move the selected user stories to the current sprint", inject ($model) ->
             scope.milestones = [{id: 1, user_stories: []}, {id: 2, user_stories: []}]
             ctrl.scope.unassignedUs = _.map(
-                [{id: 1, order: 2, selected: true}, {id: 2, order: 1, selected: true}, {id: 3, order: 3, selected: false}],
+                [{id: 1, selected: true}, {id: 2, selected: true}, {id: 3, selected: false}],
                 (us) -> $model.make_model("userstories", us)
             )
             ctrl.moveSelectedUserStoriesToCurrentSprint()
-            expect(_.map(ctrl.scope.milestones[0].user_stories, (x) -> x.getAttrs())).to.be.deep.equal([{id: 1, order: 2, selected: true}, {id: 2, order: 1, selected: true}])
+            expect(_.map(ctrl.scope.milestones[0].user_stories, (x) -> x.getAttrs())).to.be.deep.equal([{id: 1, selected: true}, {id: 2, selected: true}])
             expect(ctrl.scope.milestones[0].user_stories[0].milestone).to.be.equal(1)
             expect(ctrl.scope.milestones[0].user_stories[1].milestone).to.be.equal(1)
             expect(ctrl.scope.unassignedUs).to.have.length(1)


Removed obsolete interface

Removed obsolete interface

--- app/coffee/services/rpc.coffee
+++ app/coffee/services/rpc.coffee
@@ -3,13 +3,9 @@
 	'$q', 'require', 'socket'
 	($q, require, socket) ->

-		@call = (route, data, fn) ->
+		@call = (route, data) ->

 			deferred = $q.defer()
-
-			unless fn?
-				fn = data
-				data = null

 			socket.emit "rpc://#{route}", data, ({errors, result}) ->
 				return deferred.reject new Error(


Comment out cluster config (again...)

Comment out cluster config (again...)

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -32,18 +32,18 @@
 				docVersion: ({doc_id}) -> "DocVersion:#{doc_id}"
 				projectKey: ({doc_id}) -> "ProjectId:#{doc_id}"
 				docsInProject: ({project_id}) -> "DocsIn:#{project_id}"
-		}, {
-			cluster: [{
-				port: "7000"
-				host: "localhost"
-			}]
-			key_schema:
-				blockingKey: ({doc_id}) -> "Blocking:{#{doc_id}}"
-				docLines: ({doc_id}) -> "doclines:{#{doc_id}}"
-				docOps: ({doc_id}) -> "DocOps:{#{doc_id}}"
-				docVersion: ({doc_id}) -> "DocVersion:{#{doc_id}}"
-				projectKey: ({doc_id}) -> "ProjectId:{#{doc_id}}"
-				docsInProject: ({project_id}) -> "DocsIn:{#{project_id}}"
+		# }, {
+		# 	cluster: [{
+		# 		port: "7000"
+		# 		host: "localhost"
+		# 	}]
+		# 	key_schema:
+		# 		blockingKey: ({doc_id}) -> "Blocking:{#{doc_id}}"
+		# 		docLines: ({doc_id}) -> "doclines:{#{doc_id}}"
+		# 		docOps: ({doc_id}) -> "DocOps:{#{doc_id}}"
+		# 		docVersion: ({doc_id}) -> "DocVersion:{#{doc_id}}"
+		# 		projectKey: ({doc_id}) -> "ProjectId:{#{doc_id}}"
+		# 		docsInProject: ({project_id}) -> "DocsIn:{#{project_id}}"
 		}]

 	max_doc_length: 2 * 1024 * 1024 # 2mb


add debug line

add debug line

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -27,5 +27,7 @@
 		key: process.env['AWS_ACCESS_KEY_ID']
 		secret: process.env['AWS_SECRET_ACCESS_KEY']
 		bucket: process.env['AWS_BUCKET']
+
+console.log Settings, process.env['HEALTH_CHECK_PROJECT_ID']

 module.exports = Settings


Added Growl notifications.

Added Growl notifications.

--- src/pie.coffee
+++ src/pie.coffee
@@ -6,6 +6,7 @@
 minimatch     = require "minimatch"
 fsWatchTree   = require "fs-watch-tree"
 nStore        = require "nstore"
+growl         = require "growl"
 _             = require "underscore"

@@ -47,6 +48,7 @@
   async.forEachSeries _mappings, ((m, innerCb) -> m.run(innerCb)), (err) ->
     return printErr(err) if err
     console.log "Build complete"
+    growl "Build complete"

 startWatcher = (cb = noop) ->
   console.log "Starting watcher"
@@ -116,6 +118,7 @@
       if @options.batch
         if changedFiles.length > 0
           @execFunc changedFiles, (err) =>
+            growl("#{@name}\n#{err}") if err
             return cb(err) if err
             async.forEach changedFiles, _.bind(@updateMtime, @), cb
         else
@@ -124,6 +127,7 @@
         if changedFiles.length > 0
           x = (f, innerCb) =>
             @execFunc f, (err) =>
+              growl("#{f}\n#{err}") if err
               return innerCb(err) if err
               @updateMtime(f, innerCb)
           async.forEach changedFiles, x, cb


oops, doesn't belong here

oops, doesn't belong here

--- src/tab.coffee
+++ src/tab.coffee
@@ -4,7 +4,6 @@
 class Tab extends View
   @content: (editSession) ->
     @div class: 'tab', =>
-      console.log(editSession)
       @span class: 'file-name', outlet: 'fileName'
       @span class: 'close-icon'


Add associate function.

Add associate function.

--- app/javascripts/helpers.coffee
+++ app/javascripts/helpers.coffee
@@ -42,9 +42,16 @@
       else
         []

+    # @param key the property name
+    # @param value the property value
+    # @returns the {key, value} object
+    associate: (key, value) ->
+      obj = {}
+      obj[key] = value
+      obj
+
     # @param objects the delegate objects
-    # @returns a new object with properties from the given
-    #   objects
+    # @returns a new object with properties from the given objects
     delegate: (objects...) ->
       _.reduce(objects, _.defaults, {})

@@ -63,7 +70,8 @@
       replace = (key, val) ->
         elideCycles = (val) ->
           if _.isObject(val)
-            if val in visited
+
+            if  _.isFunction(val) or val in visited
               '...'
             else
               visited.push(val)
@@ -73,7 +81,7 @@

         elideCycles(val)

-      JSON.stringify(obj, replace, 4)
+      JSON.stringify(obj, replace, 2)

     # @param obj the associative object
     # @returns the concatenated values array


Fix fs util tests

Fix fs util tests

--- spec/fs-util-spec.coffee
+++ spec/fs-util-spec.coffee
@@ -3,7 +3,7 @@
 path = require 'path'

 describe "fs util", ->
-  testPath = "/home/test/github/coffee-compile/lib/fs-util.coffee"
+  testPath = __dirname + "/lib/fs-util.coffee"

   beforeEach ->
     atom.project.setPaths([__dirname])
@@ -11,11 +11,11 @@
   describe "toExt", ->
     it "should convert to js extension", ->
       output = fsUtil.toExt testPath, 'js'
-      expect(output).toBe "/home/test/github/coffee-compile/lib/fs-util.js"
+      expect(output).toBe __dirname + "/lib/fs-util.js"

   describe "resolvePath", ->
     beforeEach ->
-      atom.project.setPaths(["/home/test/github/coffee-compile"])
+      atom.project.setPaths([__dirname])

     afterEach ->
       atom.config.unset('coffee-compile')
@@ -28,18 +28,12 @@
       atom.config.set("coffee-compile.destination", "test/folder")

       output = fsUtil.resolvePath testPath
-      expect(output).toBe "/home/test/github/coffee-compile/test/folder/lib/fs-util.coffee"
+      expect(output).toBe __dirname + "/test/folder/lib/fs-util.coffee"

     it "should flatten path", ->
       atom.config.set("coffee-compile.flatten", true)
       output = fsUtil.resolvePath testPath
-      expect(output).toBe "/home/test/github/coffee-compile/fs-util.coffee"
-
-    it "should join cwd path", ->
-      atom.config.set("coffee-compile.cwd", "lib")
-      cwdTestPath = "/home/test/github/coffee-compile/lib/more/folder/fs-util.coffee"
-      output = fsUtil.resolvePath cwdTestPath
-      expect(output).toBe "/home/test/github/coffee-compile/more/folder/fs-util.coffee"
+      expect(output).toBe __dirname + "/fs-util.coffee"

   describe "writeFile", ->
     editor = null
@@ -71,7 +65,7 @@

   describe "isPathInSrc", ->
     beforeEach ->
-      atom.project.setPaths(["/home/test/github/coffee-compile"])
+      atom.project.setPaths([__dirname])

     afterEach ->
       atom.config.unset('coffee-compile')
@@ -109,7 +103,7 @@
     it "should be relative to cwd and return true", ->
       atom.config.set("coffee-compile.cwd", "lib/")
       atom.config.set("coffee-compile.source", ["more/"])
-      cwdTestPath = "/home/test/github/coffee-compile/lib/more/folder/fs-util.coffee"
+      cwdTestPath = __dirname + "/lib/more/folder/fs-util.coffee"

       output = fsUtil.isPathInSrc cwdTestPath
       expect(output).toBe true
@@ -117,7 +111,7 @@
     it "should not be relative to cwd and return false", ->
       atom.config.set("coffee-compile.cwd", "spec")
       atom.config.set("coffee-compile.source", ["."])
-      cwdTestPath = "/home/test/github/coffee-compile/lib/more/folder/fs-util.coffee"
+      cwdTestPath = __dirname + "/lib/more/folder/fs-util.coffee"

       output = fsUtil.isPathInSrc cwdTestPath
       expect(output).toBe false


Paths for non-admins. Making sure paths are showing when level first loads and playback ends (not sure why they are not showing up at first otherwise).

--- app/lib/surface/Surface.coffee
+++ app/lib/surface/Surface.coffee
@@ -342,6 +342,7 @@
       @ended = true
       @setPaused true
       Backbone.Mediator.publish 'surface:playback-ended', {}
+      @updatePaths()  # TODO: this is a hack to make sure paths are on the first time the level loads
     else if @currentFrame < @world.totalFrames and @ended
       @ended = false
       @setPaused false
@@ -586,7 +587,6 @@

   updatePaths: ->
     return unless @options.paths and @heroLank
-    return unless me.isAdmin() # TODO: Fix world thang points, targets, then remove this
     @hidePaths()
     return if @world.showPaths is 'never'
     layerAdapter = @lankBoss.layerAdapters['Path']


Adjust wrapper-size strategy to take into account page size

Fixes canvas sizing when testing game dev levels on wide screen sizes

--- app/lib/surface/Surface.coffee
+++ app/lib/surface/Surface.coffee
@@ -573,7 +573,9 @@
       newWidth = 1024
       newHeight = newWidth / aspectRatio
     else if @options.resizeStrategy is 'wrapper-size'
-      newWidth = $('#canvas-wrapper').width()
+      canvasWrapperWidth = $('#canvas-wrapper').width()
+      pageHeight = window.innerHeight - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight()
+      newWidth = Math.min(pageWidth, pageHeight * aspectRatio, canvasWrapperWidth)
       newHeight = newWidth / aspectRatio
     else if @realTime or @options.spectateGame
       pageHeight = window.innerHeight - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight()
@@ -663,6 +665,8 @@

   # Force sizing based on width for game-dev levels, so that the instructions panel doesn't obscure the game
   onManualCast: ->
+    console.log '???'
+    console.trace()
     if @options.levelType is 'game-dev'
       console.log "Force resize strategy"
       @options.originalResizeStrategy = @options.resizeStrategy


Made component.attach to work with objects and ES6 classes (#602)

--- app/lib/world/component.coffee
+++ app/lib/world/component.coffee
@@ -11,8 +11,10 @@

   attach: (thang) ->
     # Optimize; this is much of the World constructor time
-    for key, value of @ when key not in componentKeywords and key[0] isnt '_'
+    keys = (Object.getOwnPropertyNames(@.__proto__) or []).concat(Object.getOwnPropertyNames(@))
+    for key in keys when key not in componentKeywords and key[0] isnt '_'
       oldValue = thang[key]
+      value = @[key]
       if typeof oldValue is 'function'
         thang.appendMethod key, value
       else


Add support for ES6 JSX modules

Add support for ES6 JSX modules

--- webpack.config.coffee
+++ webpack.config.coffee
@@ -7,7 +7,7 @@

 module.exports =
   cache         : true
-  resolve       : extensions: ['', '.cjsx', '.coffee', '.js', '.json', '.styl']
+  resolve       : extensions: ['', '.jsx', '.cjsx', '.coffee', '.js', '.json', '.styl']
   context       : __dirname

   entry:
@@ -34,7 +34,7 @@
     noParse     : [node_modules + '/react/dist/*.js']

     loaders: [
-      test      : /\.js$/,      exclude:/(node_modules)/, loader: 'babel'
+      test      : /(\.js|\.jsx)$/,      exclude:/(node_modules)/, loader: 'babel'
     ,
       test      : /\.cjsx$/,    loader: 'coffee-jsx-loader'
     ,


Bump subject file size limit

Bump subject file size limit

--- app/pages/lab/subject-set.cjsx
+++ app/pages/lab/subject-set.cjsx
@@ -17,7 +17,7 @@

 VALID_SUBJECT_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.svg']
 INVALID_FILENAME_CHARS = ['/', '\\', ':', ',']
-MAX_FILE_SIZE = 600000
+MAX_FILE_SIZE = 600 * 1024

 announceSetChange = ->
   apiClient.type('subject_sets').emit 'add-or-remove'
@@ -164,7 +164,7 @@
           Drag-and-drop or click to upload manifests and subject images here.

           Manifests must be .csv or .tsv. The first row should define metadata headers. All other rows should include at least one reference to an image filename in the same directory as the manifest.

           Headers that begin with "#" or "//" denote private fields that will not be visible to classifiers in the main classification interface or in the Talk discussion tool.

-          Subject images can be up to {MAX_FILE_SIZE/1000}KB and any of: {{ext}{', ' if VALID_SUBJECT_EXTENSIONS[i + 1]?} for ext, i in VALID_SUBJECT_EXTENSIONS}{' '}
+          Subject images can be up to {MAX_FILE_SIZE / 1024}KB and any of: {{ext}{', ' if VALID_SUBJECT_EXTENSIONS[i + 1]?} for ext, i in VALID_SUBJECT_EXTENSIONS}{' '}
           and may not contain {{char}{', ' if INVALID_FILENAME_CHARS[i + 1]?} for char, i in INVALID_FILENAME_CHARS}

         </UploadDropTarget>


Fix double scenery glitch

Fix double scenery glitch

--- app/scripts/scenes/Game.coffee
+++ app/scripts/scenes/Game.coffee
@@ -1,3 +1,4 @@
+level = null
 Crafty.defineScene 'Game', (data = {}) ->
   # constructor
   #
@@ -143,6 +144,7 @@

 , ->
   # destructor
+  level.stop()
   Crafty('Player').each -> @removeComponent('ShipSpawnable')
   Crafty.unbind('GameOver')
   Crafty.unbind('GamePause')


Builder::remove

Builder::remove

--- src/xml.coffee
+++ src/xml.coffee
@@ -260,7 +260,7 @@
     remove: (opts = {}) =>
         @closed = 'removed' unless @closed or opts.soft
         @emit 'remove', this, opts
-        @builder = null
+        @builder = null unless this is @builder
         @parent = null
         @emit 'removed', opts.soft # internal
         @removeAllListeners() unless opts.soft
@@ -292,6 +292,7 @@

     show: Tag::show
     hide: Tag::hide
+    remove: Tag::remove

     toString: ->
         "[object AsyncXMLBuilder]"


fix pretty print and indentions for sub templates

--- src/xml.coffee
+++ src/xml.coffee
@@ -67,6 +67,8 @@
     wire_tag = (_, tag) =>
         tag.builder ?= @builder
         tag.parent  ?= this
+        tag.builder.opts.pretty = @builder.opts.pretty
+        tag.builder.level = @level

         connect_tags(this, tag)


update indentation

update indentation

--- app/templates/_gulpfile.coffee
+++ app/templates/_gulpfile.coffee
@@ -1,34 +1,34 @@
 # Gulp
 gulp						= require "gulp"
-coffee 					= require "gulp-coffee"
-concat      		= require "gulp-concat"
-exec 						= require "gulp-exec"
-minifyCSS 			= require "gulp-minify-css"
-plumber 				= require "gulp-plumber"
-rename 					= require "gulp-rename"
-sass 						= require "gulp-sass"
-uglifyJs 				= require "gulp-uglify"
-gutil       		= require "gulp-util"
-watch       		= require "gulp-watch"
+coffee					= require "gulp-coffee"
+concat					= require "gulp-concat"
+exec						= require "gulp-exec"
+minifyCSS				= require "gulp-minify-css"
+plumber					= require "gulp-plumber"
+rename					= require "gulp-rename"
+sass						= require "gulp-sass"
+uglifyJs				= require "gulp-uglify"
+gutil						= require "gulp-util"
+watch						= require "gulp-watch"
 cache						= require "gulp-cache"
-remember      	= require "gulp-remember"
-shell       		= require "gulp-shell"
-browserSync 		= require "browser-sync"
-reload      		= browserSync.reload
-imagemin    		= require "gulp-imagemin"
-pngquant    		= require "imagemin-pngquant"
+remember				= require "gulp-remember"
+shell						= require "gulp-shell"
+browserSync			= require "browser-sync"
+reload					= browserSync.reload
+imagemin				= require "gulp-imagemin"
+pngquant				= require "imagemin-pngquant"
 rsyncwrapper		= require "rsyncwrapper"
-rsync 					= rsyncwrapper.rsync
-cp 							= require 'child_process'
-spawn 					= cp.spawn
-rev 						= require 'gulp-rev'
+rsync						= rsyncwrapper.rsync
+cp							= require 'child_process'
+spawn						= cp.spawn
+rev							= require 'gulp-rev'
 replace					= require 'gulp-replace'

 # Extra
-extend 					= require "extend"
-
-pkg   = require './package.json'
-conf  = require './gulpconfig.json'
+extend					= require "extend"
+
+pkg		= require './package.json'
+conf	= require './gulpconfig.json'
 try
 	pvt = require './private.json'
 catch err
@@ -36,10 +36,10 @@

 #*------------------------------------*\
-#   $SASS
+#		$SASS
 #*------------------------------------*/
 gulp.task "sass", () ->
-  gulp.src([conf.path.pvt.scss + "/style.scss"])
+	gulp.src([conf.path.pvt.scss + "/style.scss"])
 		.pipe plumber(conf.plumber)
 		.pipe sass({errLogToConsole: true})
 		.pipe gulp.dest(conf.path.pvt.css)
@@ -47,7 +47,7 @@

 #*------------------------------------*\
-#   $PIXEL &
+#		$PIXEL &
 #		$VECTOR OPTIM
 #*------------------------------------*/
 gulp.task 'imagemin', () ->
@@ -71,8 +71,8 @@

 #*------------------------------------*\
-#   $AUTO RELOAD GULPFILE ON SAVE
-# 	noxoc.de/2014/06/25/reload-gulpfile-js-on-change/
+#		$AUTO RELOAD GULPFILE ON SAVE
+#		noxoc.de/2014/06/25/reload-gulpfile-js-on-change/
 #*------------------------------------*/
 gulp.task "auto_reload", () ->
 	process = undefined
@@ -95,7 +95,7 @@

 #*------------------------------------*\
-#   $COFFEE
+#		$COFFEE
 #*------------------------------------*/
 gulp.task "coffee", () ->
 	gulp.src [conf.path.pvt.coffee+"/**/*.coffee"]
@@ -107,7 +107,7 @@

 #*------------------------------------*\
-#   $WATCH
+#		$WATCH
 #*------------------------------------*/
 gulp.task "watch", ["browser-sync"], () ->
 	gulp.watch conf.path.pvt.scss + "/**/*.scss", ["sass"]
@@ -117,7 +117,7 @@

 #*------------------------------------*\
-#   $UGLIFY
+#		$UGLIFY
 #*------------------------------------*/
 gulp.task "uglify", () ->
 	gulp.src [conf.path.pvt.js + "/main.js"]
@@ -130,7 +130,7 @@

 #*------------------------------------*\
-#   $SASS
+#		$SASS
 #*------------------------------------*/
 gulp.task "minify", () ->
 	gulp.src(["#{conf.path.pvt.css}/style.css"])
@@ -143,7 +143,7 @@

 #*------------------------------------*\
-#   $FONT REV
+#		$FONT REV
 #*------------------------------------*/
 gulp.task "font", () ->
 	gulp.src([conf.path.pvt.fnt+'/**/*'])
@@ -155,8 +155,8 @@

 #*------------------------------------*\
-#   $REV REPLACE
-# 	github.com/jamesknelson/gulp-rev-replace/issues/23
+#		$REV REPLACE
+#		github.com/jamesknelson/gulp-rev-replace/issues/23
 #*------------------------------------*/
 gulp.task 'rev_replace', ["uglify", "minify", "font", "imagemin"], () ->
 	manifest = require "./#{conf.path.pvt.assets}/rev-manifest.json"
@@ -169,7 +169,7 @@

 #*------------------------------------*\
-#   $MYSQL-DUMP
+#		$MYSQL-DUMP
 #*------------------------------------*/
 gulp.task "db_dump:dev", shell.task [
 	"mysqldump --host=#{pvt.db_dev.host}
@@ -187,7 +187,7 @@

 #*------------------------------------*\
-#   $RSYNC
+#		$RSYNC
 #*------------------------------------*/
 # rsync files to and from production

@@ -287,13 +287,13 @@

 #*------------------------------------*\
-#   $DEV UPDATE
+#		$DEV UPDATE
 #*------------------------------------*/
 # No exsisting gulp task implemented

 #*------------------------------------*\
-#   $TASKS
+#		$TASKS
 #*------------------------------------*/
 gulp.task 'default', ['watch']
 gulp.task "build", ["uglify", "minify", "font", "imagemin", "rev_replace"]


Added check for old player_id cookie

Added check for old player_id cookie

--- client/lobby/lobby.coffee
+++ client/lobby/lobby.coffee
@@ -4,12 +4,14 @@
   if current_player() and current_player().name is '' then 'disabled="disabled"'

 Template.players.waiting = ->
-  if player_count() == 0
+  count = online_players().length
+
+  if count == 0
     "Ingen spillere der venter"
-  else if player_count() == 1
+  else if count == 1
     "1 spiller der venter:"
   else
-    player_count() + " spillere der venter:"
+    count + " spillere der venter:"

 Template.lobby.rendered = ->
   #$('#myname').focus() # TODO: Fix
@@ -21,7 +23,9 @@
     else
       # get name and remove ws
       name = $('input#myname').val().replace /^\s+|\s+$/g, ""
-      @Players.update Session.get('player_id'), {$set: {name: name}}
+      @Players.update Session.get('player_id'), { $set: { name: name } }
+
+    console.log current_player()

   'click button#startgame': ->
     Meteor.call 'start_new_game', current_player()._id


update dispImageStrings.coffee

update dispImageStrings.coffee

--- lib/_dispImageStrings.coffee
+++ lib/_dispImageStrings.coffee
@@ -24,6 +24,7 @@
         switch @_processnumber
             when 0
                 for obj in @stringslist
+                    JSLog("opacity=%@", @opacity)
                     obj.opacity = @opacity
                     obj.x = @x
                     obj.y = @y


Fix tag close suggestions displaying trailing unclosed tags on same line.
e.g.

<div>suggested tags   <trainlingTag></trailingTag>
the sugested tags displayed after <div> displayed </trailingTag> as the first suggestion!

--- lib/auto-complete-jsx.coffee
+++ lib/auto-complete-jsx.coffee
@@ -136,6 +136,8 @@
     row = range.start.row
     while row <= range.end.row
       line = editor.lineTextForBufferRow row
+      if row is range.end.row
+        line = line.substr 0, range.end.column
       while (( match = JSXREGEXP.exec(line)) isnt null )
         matchColumn = match.index
         matchPointStart = new Point(row, matchColumn)


Use atom.config for scoped properties

Use atom.config for scoped properties

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -56,9 +56,8 @@
     false

   getCompletionsForCursorScope: ->
-    cursorScope = @editor.scopesForBufferPosition(@editor.getCursorBufferPosition())
-    completions = atom.syntax.propertiesForScope(cursorScope, 'editor.completions')
-    completions = completions.map (properties) -> _.valueForKeyPath(properties, 'editor.completions')
+    cursorScope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
+    completions = atom.config.get(cursorScope, 'editor.completions')
     _.uniq(_.flatten(completions))

   buildWordList: ->


More cases for cancellation

More cases for cancellation

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -35,15 +35,23 @@
    * Handles editor events
   ###
   handleEvents: ->
+    # Make sure we don't scroll in the editor view when scrolling
+    # in the list
     @list.on 'mousewheel', (event) -> event.stopPropagation()

-    @editorView.on 'editor:path-changed', => @setCurrentBuffer(@editor.getBuffer())
-
+    # Listen to `contents-modified` event when live completion is disabled
     unless atom.config.get('autocomplete-plus.liveCompletion')
       @editor.on 'contents-modified', => @contentsModified()

-    @editorView.command 'autocomplete:next', => @selectNextItemView()
-    @editorView.command 'autocomplete:previous', => @selectPreviousItemView()
+    # Is this the event for switching tabs? Dunno...
+    @editor.on 'title-changed-subscription-removed', =>
+      @cancel()
+
+    # Close the overlay when the cursor moved without
+    # changing any text
+    @editor.on 'cursor-moved', (data) =>
+      unless data.textChanged
+        @cancel()

   ###
    * Return false so that the events don't bubble up to the editor


don't make a redundant call

don't make a redundant call

--- lib/connection/client.coffee
+++ lib/connection/client.coffee
@@ -41,7 +41,9 @@
       atom.beep()

     @onAttached =>
-      @import('args') atom.config.get 'julia-client.juliaOptions.arguments'
+      args = atom.config.get 'julia-client.juliaOptions.arguments'
+      if args.length > 0
+        @import('args') args

   deactivate: ->
     @emitter.dispose()


Updated executor to not kill a non-existing process

--- lib/debugger/executor.coffee
+++ lib/debugger/executor.coffee
@@ -16,8 +16,9 @@
     return

   stopDebugger: ->
-    @process.kill()
-    @handleExit null
+    if @process?
+      @process.kill()
+      @handleExit undefined
     return

   handleExit: (code) ->


Update domain store configuration structure

--- lib/domain_repository.coffee
+++ lib/domain_repository.coffee
@@ -9,15 +9,15 @@
   @eventHandlers = {}

   @initializeStoreWithConfiguration: (configuration) =>
-    switch configuration.domain.store
+    switch configuration.store
       when COUCHDB_STORE
         CouchDbEventStore = require "./event_store/couchdb"
-        @store = new CouchDbEventStore configuration.domain.uri
+        @store = new CouchDbEventStore configuration.uri
       when REDIS_STORE
         RedisEventStore = require "./event_store/redis"
         @store = new RedisEventStore
       else
-        throw new Error "Unknown domain repository store \"#{configuration.domain.store}\"."
+        throw new Error "Unknown domain repository store \"#{configuration.store}\"."

   @setup: (callback) =>
     @store.setup callback


refactor(cli): DRY

refactor(cli): DRY

--- src/cli.coffee
+++ src/cli.coffee
@@ -35,6 +35,17 @@
   outputPath = cli.argv.output
   outputPath = path.resolve(outputPath) if outputPath

+  textlex = new Textlex()
+
+  stringify = (tokens, tab) ->
+    JSON.stringify tokens, null, tab
+
+  output = (outputPath, tokens, tab) ->
+    if outputPath
+      fs.writeFileSync(outputPath, stringify tokens, tab)
+    else
+      console.log(stringify tokens, tab)
+
   if filePath
     filePath = path.resolve(filePath)
     unless fs.isFileSync(filePath)
@@ -42,11 +53,8 @@
       process.exit(1)
       return

-    tokens = new Textlex().lexSync({filePath, scopeName: cli.argv.scope})
-    if outputPath
-      fs.writeFileSync(outputPath, JSON.stringify tokens, null, cli.argv.t)
-    else
-      console.log(JSON.stringify tokens, null, cli.argv.t)
+    tokens = textlex.lexSync({filePath, scopeName: cli.argv.scope})
+    output outputPath, tokens, cli.argv.tab
   else
     filePath = cli.argv.f
     process.stdin.resume()
@@ -54,8 +62,5 @@
     fileContents = ''
     process.stdin.on 'data', (chunk) -> fileContents += chunk.toString()
     process.stdin.on 'end', ->
-      tokens = new Textlex().lexSync({filePath, fileContents, scopeName: cli.argv.scope})
-      if outputPath
-        fs.writeFileSync(outputPath, JSON.stringify tokens, null, cli.argv.t)
-      else
-        console.log(JSON.stringify tokens, null, cli.argv.t)
+      tokens = textlex.lexSync({filePath, fileContents, scopeName: cli.argv.scope})
+      output outputPath, tokens, cli.argv.tab


added variable resolver

added variable resolver

--- src/mistfile.coffee
+++ src/mistfile.coffee
@@ -174,9 +174,11 @@
   #
   # root:
   #   Absolute path for this mistfile (the folder to resolve against)
-  ###
-  resolve: (root)->
-    new MistResolver root, @
+  # resolver:
+  #   The resolver to use
+  ###
+  resolve: (root, resolver = MistResolver)->
+    new resolver root, @

   ###
   # Mount another Mistfile at a specific relative path


Fix checks for socket listening

Fix checks for socket listening

--- lib/engines/dbgp/dbgp.coffee
+++ lib/engines/dbgp/dbgp.coffee
@@ -16,24 +16,27 @@
     @serverAddress = params.serverAddress

   setPort: (port) ->
-    @serverPort = port
-    if @listening
-      @close()
-      @listen()
+    if port != @serverPort
+      @serverPort = port
+      if @listening()
+        @close()
+        @listen()

   setAddress: (address) ->
-    @serverAddress = address
-    if @listening
-      @close()
-      @listen()
+    if address != @serverAddress
+      @serverAddress = address
+      if @listening()
+        @close()
+        @listen()

   setAddressPort: (address,port) ->
-    @serverPort = port
-    @serverAddress = address
-    if @listening
-      @close()
-      @listen()
+    if port != @serverPort || address != @serverAddress
+      @serverPort = port
+      @serverAddress = address
+      if @listening()
+        @close()
+        @listen()

   listening: ->
     return @server != undefined
@@ -47,11 +50,10 @@
     net = require "net"
     buffer = ''
     try
-      @close()
-      @GlobalContext?.notifyConsoleMessage "Listening on Address:Port " + @serverAddress + ":" + @serverPort
-      console.log "Listening on Address:Port " + @serverAddress + ":" + @serverPort
+      console.log "Attempting to setup server"
+      if @listening()
+        @close()
       @server = net.createServer( (socket) =>
-
         socket.setEncoding('ascii');
         if !@GlobalContext.getCurrentDebugContext()
           @GlobalContext?.notifyConsoleMessage "Session initiated"
@@ -69,10 +71,14 @@
         @close()
         @GlobalContext.notifySocketError()
         return false
-      if @serverAddress == "*"
-        @server?.listen @serverPort
-      else
-        @server?.listen @serverPort, @serverAddress
+      serverOptions = {}
+      serverOptions.port = @serverPort
+      if @serverAddress != "*"
+        serverOptions.host = @serverAddress
+
+      @server?.listen serverOptions, () =>
+          @GlobalContext?.notifyConsoleMessage "Listening on Address:Port " + @serverAddress + ":" + @serverPort
+          console.log "Listening on Address:Port " + @serverAddress + ":" + @serverPort
       return true
     catch e
       @GlobalContext?.notifyConsoleMessage "Error: " + "Socket Error:", e
@@ -91,5 +97,5 @@
     unless !@server
       @server.close()
       delete @server
-    @GlobalContext?.notifyConsoleMessage "Closed"
-    console.log("closed")
+      @GlobalContext?.notifyConsoleMessage "Closed"
+      console.log("closed")


ActionCable: Fix missing time formatting on receiving update

--- app/assets/javascripts/channels/page_update.coffee
+++ app/assets/javascripts/channels/page_update.coffee
@@ -15,3 +15,4 @@
         # Called when there's incoming data on the websocket for this channel
         console.log "Received data from " + App.updates.identifier
         $('.content').html(data.html)
+        new Init()


WIP: add data-src attribute to attributes whitelist in angularJS html sanitization.

This is intended to allow the user of jquery-unveil library for lazy image loading; this plugin needs a data-src attribute to be added to images, and this attribute is currently being removed by angularJS anti-xss sanitization for the ng-bind-html directive.

--- app/assets/javascripts/ng-modules/ng-app.js.coffee
+++ app/assets/javascripts/ng-modules/ng-app.js.coffee
@@ -17,4 +17,8 @@
       else
         return $q.reject rejection
   ]
+]
+
+module.config ["$compileProvider", ($compileProvider)->
+  #alert $compileProvider.imgSrcSanitizationWhitelist()
 ]


Add debt and mental to tools.js

Add debt and mental to tools.js

* This enables the syndication of debt and mental health on partner-tools

--- app/assets/javascripts/syndication/tools.js.coffee
+++ app/assets/javascripts/syndication/tools.js.coffee
@@ -262,6 +262,13 @@
         path: "cy/tools/cyfrifiannell-benthyciadau/loan"
       title: "Loan calculator"

+    debt_and_mental_health:
+      en:
+        path: "en/debt-and-mental-health/preview"
+      title: "Debt and mental health"
+      width: "100%"
+      height: "800px"
+
     debt_advice_locator:
       en:
         path: "en/tools/debt-advice-locator"


Use dummy stdin stream on Windows.

Use dummy stdin stream on Windows.

--- browser/atom/atom.coffee
+++ browser/atom/atom.coffee
@@ -1,11 +1,17 @@
 fs = require 'fs'
 path = require 'path'

-# Redirect node's console to use our own implementations, since node can not
-# handle output when running as GUI program.
 if process.platform is 'win32'
+  # Redirect node's console to use our own implementations, since node can not
+  # handle output when running as GUI program.
   console.log = console.error = console.warn = process.log
   process.stdout.write = process.stderr.write = process.log
+
+  # Always returns EOF for stdin stream.
+  Readable = require('stream').Readable
+  stdin = new Readable
+  stdin.push null
+  process.__defineGetter__ 'stdin', -> stdin

 # Provide default Content API implementations.
 atom = {}


Utility function for nicer specification

--- browser/statproxy.coffee
+++ browser/statproxy.coffee
@@ -5,6 +5,25 @@

 	specification: =>
 		return $.getJSON @url
+
+	@specification_to_object: (spec) =>
+		dictspec = _.clone spec
+		dictspec.dimensions = {}
+		dictspec.dimension_order = []
+		for dim in spec.dimensions
+			dictdim = dictspec.dimensions[dim.id] = _.clone dim
+			dictspec.dimensions[dim.id] = dictdim
+			dictspec.dimension_order.push dim.id
+			dictdim.categories = {}
+			dictdim.category_order = []
+			if 'categories' not of dim
+				continue
+			for cat in dim.categories
+				dictdim.categories[cat.id] = _.clone cat
+				dictdim.category_order.push cat.id
+		return dictspec
+
+

 	filter: (categories) =>
 		if _.isEmpty(categories)


:memo:

:memo:

--- lib/image-editor-view.coffee
+++ lib/image-editor-view.coffee
@@ -110,7 +110,7 @@
     @zoomToFitButton.addClass 'selected'
     @image.width('')
     @image.height('')
-    @resetZoomButton.text('auto')
+    @resetZoomButton.text('Auto')

   # Adjust the size of the image by the given multiplying factor.
   #


refactoring by dev-rke

refactoring by dev-rke

--- alight/core.coffee
+++ alight/core.coffee
@@ -731,7 +731,9 @@
             func = alight.utilits.filterBuilder scope, func, filters
             if cfg.string
                 f1 = func
-                `func = function() { var __ = f1.apply(this, arguments); return '' + (__ || (__ == null?'':__)) }`
+                func = ->
+                    __ = f1.apply this, arguments
+                    "" + (__ or (__ ? ''))

         if cfg.noBind
             resp.fn = func


Update git-branch.coffee to atom api 1.0.0

--- lib/models/git-branch.coffee
+++ lib/models/git-branch.coffee
@@ -1,4 +1,4 @@
-{$, EditorView, View} = require 'atom'
+{$, TextEditorView, View} = require 'atom-space-pen-views'

 git = require '../git'
 StatusView = require '../views/status-view'
@@ -12,21 +12,25 @@

 class InputView extends View
   @content: ->
-    @div class: 'overlay from-top', =>
-      @subview 'branchEditor', new EditorView(mini: true, placeholderText: 'New branch name')
+    @div =>
+      @subview 'branchEditor', new TextEditorView(mini: true, placeholderText: 'New branch name')

   initialize: ->
     @currentPane = atom.workspace.getActivePane()
-    atom.workspaceView.append this
+    panel = atom.workspace.addModalPanel(item: this)
+    panel.show()
+
+    destroy = =>
+      panel.destroy()
+      @currentPane.activate()
+
     @branchEditor.focus()
-    @on 'core:cancel', => @detach()
-
+    @branchEditor.on 'core:cancel', => destroy()
     @branchEditor.on 'core:confirm', =>
       editor = @branchEditor.getEditor()
       name = editor.getWordUnderCursor()
       if name.length > 0
-        @createBranch name
-        @detach()
+        destroy()

   createBranch: (name) ->
     git.cmd


Close JSON modal on lost focus

Close JSON modal on lost focus

--- lib/modals/json-modal.coffee
+++ lib/modals/json-modal.coffee
@@ -32,4 +32,6 @@
                     event.stopPropagation()
             })
             jsonElement.focus()
+            jsonElement.addEventListener 'blur', () ->
+                modal.destroy()
         )


Clear on confirm when there is nothing in the find box

--- lib/project-find-view.coffee
+++ lib/project-find-view.coffee
@@ -92,7 +92,6 @@
     @subscribe @model, 'replacement-state-cleared', (results) => @generateResultsMessage(results)
     @subscribe @model, 'finished-searching', (results) => @generateResultsMessage(results)

-    # @findEditor.getBuffer().on 'changed', => @runSearch()
     @findEditor.editor.on 'contents-modified', =>
       @runRealtimeSearch() if @findEditor.getText().length > 2

@@ -155,7 +154,9 @@
     elements[focusedIndex].selectAll() if elements[focusedIndex].selectAll

   confirm: ->
-    return if @findEditor.getText().length == 0
+    if @findEditor.getText().length == 0
+      @model.clear()
+      return

     @findHistory.store()
     @replaceHistory.store()


added 'filename' option for includes

added 'filename' option for includes

--- build/tasks/jaded.coffee
+++ build/tasks/jaded.coffee
@@ -7,6 +7,7 @@
     for file in files
       base = path.basename file, path.extname file
       dest = path.join @file.dest, "#{base}.js"
+      # for includes
       @data.options['filename'] = file
       templ = jaded.compile grunt.file.read(file), @data.options
       grunt.file.write dest, templ


Ehh, don’t enable dragging in the pen tool

--- src/pen-tool.coffee
+++ src/pen-tool.coffee
@@ -22,7 +22,6 @@
     @objectEditor.activate()
     @subscriptions = new CompositeDisposable
     @subscriptions.add @objectEditor.editors.Path.on('mousedown:node', @onMouseDownNode.bind(this))
-    @subscriptions.add @selectionModel.on('change:selected', @onChangedSelectedObject.bind(this))
     svg = @svgDocument.getSVGRoot()
     svg.on 'mousedown', @onMouseDown
     svg.on 'mousemove', @onMouseMove
@@ -38,9 +37,6 @@
     svg.off 'mousemove', @onMouseMove
     svg.off 'mouseup', @onMouseUp
     @active = false
-
-  onChangedSelectedObject: ({object, old}) =>
-    old.disableDragging() if old?

   onMouseDownNode: (event) ->
     {node} = event
@@ -77,7 +73,6 @@

   _unsetCurrentObject: ->
     @currentObjectSubscriptions?.dispose()
-    @currentObject?.enableDragging()
     @currentObject = null

   _unsetCurrentNode: ->


Fix regex escaping

Fix regex escaping

--- lib/skeleton/template.coffee
+++ lib/skeleton/template.coffee
@@ -229,7 +229,7 @@
       # Capitalize a string
       # string => String
       String::capitalize = () ->
-          this.replace /(?:^|s)S/g, (a) -> a.toUpperCase()
+          this.replace /(?:^|\\s)\\S/g, (a) -> a.toUpperCase()

       # Classify a string
       # application_controller => ApplicationController


add comments to polynomial

add comments to polynomial

--- src/polynomial.cjsx
+++ src/polynomial.cjsx
@@ -5,6 +5,7 @@
     render: ->
         coefficient = @props.k

+        # all terms but the first one have a plus or minus operator
         if !@props.initial
             if coefficient < 0
                 coefficient = -coefficient
@@ -12,10 +13,12 @@
             else
                 op = '+'

-        if coefficient is 1 and @props.exp isnt 0
-            coefficient = ''
-        if coefficient is -1
-            coefficient = '-'
+        # all terms but the constant have optional coefficients
+        if @props.exp isnt 0
+            if coefficient is 1
+                coefficient = ''
+            if coefficient is -1
+                coefficient = '-'

         if @props.exp > 0
             variable = @props.variable or 'x'


Component icon should be string

Component icon should be string

--- src/protocol.coffee
+++ src/protocol.coffee
@@ -22,7 +22,7 @@
     info =
       name: name
       description: component.definition.label or component.cmd or ""
-      icon: component.definition.icon
+      icon: component.definition.icon or ''
       subgraph: false
       inPorts: component.definition.inports.map fbpPort
       outPorts: component.definition.outports.map fbpPort
@@ -31,7 +31,7 @@
     info =
       name: name
       description: component.cmd
-      icon: null
+      icon: ''
       subgraph: false
       inPorts: []
       outPorts: []


:art: The braces are not necessary!

:art: The braces are not necessary!

--- lib/snippets-provider.coffee
+++ lib/snippets-provider.coffee
@@ -14,11 +14,9 @@
     return [] unless snippets?

     for __, snippet of snippets when snippet.prefix.lastIndexOf(prefix, 0) isnt -1
-      {
-        text: snippet.prefix
-        replacementPrefix: prefix
-        rightLabel: snippet.name
-      }
+      text: snippet.prefix
+      replacementPrefix: prefix
+      rightLabel: snippet.name

   onDidInsertSuggestion: ({editor}) ->
     atom.commands.dispatch(atom.views.getView(editor), 'snippets:expand')


Format Validator: Throw exception if regexp is invalid.

--- lib/validators/format.coffee
+++ lib/validators/format.coffee
@@ -12,10 +12,15 @@
     if !options.message?
         options.message = m.messages.invalid

+    #Ensure regexp is valid
+    regexpTest = options['with'].test
+    if regexpTest is undefined
+      throw {code: 500, message: "The specified regexp #{options['with']} is invalid.", status: "failure"}
+
     if u.isBlank model[property]()
         if not options.allowBlank
             model.addError property, options.message
-    else if options['with'] && !options['with'].test model[property]()
+    else if options['with'] && not options['with'].test model[property]()
         model.addError property, options.message
     else if options.without && options.without.test model[property]()
         model.addError property, options.message


Unify the syntax in the tools module

Unify the syntax in the tools module

--- library/nucleus/tools.coffee
+++ library/nucleus/tools.coffee
@@ -42,10 +42,10 @@
 module.exports.urlWithHost = (ssl = no, parts, params, segment) ->
     params = query.stringify params if params?
     parts = parts.join "/" if _.isArray parts
-    server = nconf.get("server:port")
-    secure = nconf.get("secure:port")
+    server = nconf.get "server:port"
+    secure = nconf.get "secure:port"
     port = if ssl then secure else server
-    hostname = nconf.get("server:hostname")
+    hostname = nconf.get "server:hostname"
     hostname = "#{hostname}:#{port}"
     hostname += "/#{parts}" if parts?
     hostname += "?#{params}" if params?


fix bootstrapping

fix bootstrapping

--- chrome/src/gmailr.coffee
+++ chrome/src/gmailr.coffee
@@ -123,8 +123,10 @@
       # Here we do delayed loading until success. This is in the case
       # that our script loads after Gmail has already loaded.
       load = =>
-        @elements.canvas = $("[style*='min-height: 100%;']")
+        @elements.canvas = if (canvas_frame = $("#canvas_frame").get 0) then $ canvas_frame.contentDocument else $ document
+        #dbg @elements.canvas
         @elements.body = @elements.canvas.find(".nH").first()
+        #dbg @elements.body
         if @loaded
           clearInterval @delayedLoader
           dbg "Delayed loader success."
@@ -144,32 +146,30 @@
     and then setting up some basic hooks.
     ###
     bootstrap: (cb) ->
-      if @inBootstrap
-        return
-      @inBootstrap = true
-      if @elements.body
-        el = $(@elements.body)
-
-        # get handle on the left menu
-        if not @leftMenu or @leftMenu.length is 0
-
-          #                  this.leftMenu = el.find('.no .nM .TK').first().closest('.nn');
-          inboxLink = @getInboxLink()
-          v = el.find("a[href$='#mbox']")
-          @priorityInboxLink = v.first()  if v.length > 0
-          if inboxLink
-            @leftMenu = inboxLink.closest(".TO").closest("div")
-          else @leftMenu = @priorityInboxLink.closest(".TO").closest("div")  if @priorityInboxLink
-          if @leftMenu and @leftMenu.length > 0
-            @leftMenuItems = @leftMenu.find(".TO")
-            dbg "Fully loaded."
-            @loaded = true
-            @currentNumUnread = @numUnread()
-            @currentInboxCount = @toolbarCount()  if @inboxTabHighlighted()
-            @xhrWatcher = new XHRWatcher @detectXHREvents
-            cb? this
+      unless @inBootstrap
+
+        @inBootstrap = true
+        if @elements.body
+          # get handle on the left menu
+          if not @leftMenu or @leftMenu.length is 0
+            #                  this.leftMenu = el.find('.no .nM .TK').first().closest('.nn');
+            if (inboxLink = @getInboxLink())
+              @leftMenu = inboxLink.closest(".TO").closest "div"
+            else
+              v = @elements.body.find("a[href$='#mbox']")
+              @priorityInboxLink = v.first() if v.length > 0
+              @leftMenu = @priorityInboxLink.closest(".TO").closest "div" if @priorityInboxLink
+
+            if @leftMenu and @leftMenu.length > 0
+              @leftMenuItems = @leftMenu.find ".TO"
+              dbg "Fully loaded."
+              @loaded = true
+              @currentNumUnread = @numUnread()
+              @currentInboxCount = @toolbarCount()  if @inboxTabHighlighted()
+              @xhrWatcher = new XHRWatcher @detectXHREvents
+              cb? @
         @inBootstrap = false
-        return
+      return

     intercept: ->
       throw "Call to method before Gmail has loaded" unless @loaded


Routes: Machine label vs. slug

Routes: Machine label vs. slug

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -74,7 +74,7 @@
         m = new Machine machine: KD.remote.revive m

       sameUser  = m.getOwner() is username
-      sameLabel = (m.label or m.slug) is label
+      sameLabel = (m.slug or m.label) is label

       return m  if sameLabel and sameUser

@@ -150,7 +150,7 @@

     workspaceSlug ?= 'my-workspace'

-    KD.getSingleton('router').handleRoute "/IDE/#{machine.label}/#{workspaceSlug}"
+    KD.getSingleton('router').handleRoute "/IDE/#{machine.slug}/#{workspaceSlug}"

   routeToLatestWorkspace = ->
@@ -247,7 +247,7 @@
           if workspace
             return loadWorkspace workspace, params
           else
-            for machine in KD.userMachines when machine.label is machineLabel
+            for machine in KD.userMachines when machine.slug is machineLabel
               break

             if machine


Set correct zoom level when selecting a place from select box

--- client/index_page.coffee
+++ client/index_page.coffee
@@ -138,7 +138,8 @@
       {sw, ne, zoom} = BoxParam.decode($select.val())
       rectangleEditor.setNorthEast(crs.projection.unproject(ne))
       rectangleEditor.setSouthWest(crs.projection.unproject(sw))
-      map.fitBounds(rectangleEditor.getBounds())
+      map.panTo(rectangleEditor.getCenter())
+      map.setZoom(zoom)

   do ->
     # Set up place search autocompleter


Added Hunting Grounds

Added Hunting Grounds

--- cards.coffee
+++ cards.coffee
@@ -2500,6 +2500,30 @@
       state.current.hand.push(card)

   ai_playValue: (state, my) -> 790
+}
+
+
+makeCard 'Hunting Grounds', action, {
+  cost: 6
+  cards: 4
+
+  trashEffect: (state, player) ->
+    state.log("Starting trashEffect HG")
+    choice = ai.chooseHuntingGroundsGain(state, player)
+    if choice == "Estates"
+      state.gainCard(player, c.Estate)
+      state.gainCard(player, c.Estate)
+      state.gainCard(player, c.Estate)
+    else if choice == "Duchy"
+      state.gainCard(player, c.Duchy)
+    else
+      state.log("Invalid choice for HuntingGroundsGain: #{choice}!")
+      state.gainCard(player, c.Duchy)
+
+  ai_playValue: (state, my) ->
+    if my.actions > 1 then 666 else 201
+  ai_multipliedValue: (state, my) ->
+    if my.actions > 0 then 1542 else -1
 }

 makeCard 'Ironworks', action, {


Demo host attachment management

Demo host attachment management

--- assets/demo.coffee
+++ assets/demo.coffee
@@ -13,6 +13,7 @@

       didAddAttachment: (attachment) ->
         console.log "Host received attachment:", attachment
+        saveAttachment(attachment)

         if file = attachment.file
           setTimeout ->
@@ -28,5 +29,26 @@

       didRemoveAttachment: (attachment) ->
         console.log "Host received removed attachment:", attachment
+        removeAttachment(attachment)

   window.controller = Trix.install(config)
+
+
+saveAttachment = (attachment) ->
+  item = document.createElement("li")
+  item.setAttribute("id", "attachment_#{attachment.id}")
+  item.textContent = "#{attachment.attributes.filename ? attachment.attributes.url} "
+
+  link = document.createElement("a")
+  link.setAttribute("href", "#")
+  link.textContent = "(remove)"
+  link.addEventListener "click", ->
+    attachment.remove()
+    removeAttachment(attachment)
+
+  item.appendChild(link)
+  document.getElementById("attachments").appendChild(item)
+
+removeAttachment = (attachment) ->
+  if item = document.getElementById("attachment_#{attachment.id}")
+    item.parentElement.removeChild(item)


Update installed Atom packages

Update installed Atom packages

--- atom/packages.cson
+++ atom/packages.cson
@@ -4,6 +4,7 @@
   "atom-wrap-in-tag"
   "atomic-chrome"
   "auto-update-packages"
+  "busy-signal"
   "close-tags"
   "date"
   "elmjutsu"
@@ -12,11 +13,14 @@
   "git-plus"
   "git-time-machine"
   "highlight-selected"
+  "intentions"
   "language-elixir"
   "language-elm"
   "language-haml"
+  "language-pug"
   "language-rspec"
   "linter"
+  "linter-ui-default"
   "linter-write-good"
   "merge-conflicts"
   "multi-cursor-plus"
@@ -28,5 +32,5 @@
   "react"
   "ruby-test"
   "split-diff"
-  "stylus"
+  "Stylus"
 ]


Add front matter snippet

Add front matter snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -831,6 +831,10 @@
 '.text.html.liquid,
  .source.gfm':

+  'front matter':
+    'prefix': 'fm'
+    'body': '---\n$1\n---'
+
   'expression':
     'prefix': 'exp'
     'body': '{{ $1 }}'


Add another Redux snippet

Add another Redux snippet

`rrit` generates a context for testing a reducer action

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -69,6 +69,20 @@
             })

             """
+  'Redux reducer action spec':
+    'prefix': 'rrit'
+    'body': """
+            context('$1', () => {
+              const state = initialState
+              const action = $2
+              const newState = reducer(state, action)
+
+              it('$3', () => {
+                expect($4).$5
+              })
+            })
+
+            """
   'Redux selectors':
     'prefix': 'rsel'
     'body': """


Safe insert

Safe insert

--- coffee/collection.coffee
+++ coffee/collection.coffee
@@ -30,7 +30,7 @@

 exports.post = (req, res, next) ->
   return res.send 400, 'Missing Request Payload' if Object.keys(req.body).length is 0
-  req.col.insert req.body, (err, doc) ->
+  req.col.insert req.body, safe: true, (err, doc) ->
     return next(err) if err
     return res.json 201, documents: doc, count: doc.length


Server: use component error method

Server: use component error method

--- components/Server.coffee
+++ components/Server.coffee
@@ -21,9 +21,7 @@
       @closeServer(parseInt(port))

   sendError: (msg) ->
-    return unless @outPorts.error.isAttached()
-    @outPorts.error.send(new Error(msg))
-    @outPorts.error.disconnect()
+    @error new Error msg

   createServer: (port) ->
     if @servers[port]


for failures, output first file in stack trace

--- testify.coffee
+++ testify.coffee
@@ -123,6 +123,12 @@
       else
         line = indent + "#{test.name} ( #{test.failed} )".yellow
       console.log(line)
+      if test.failed?.stack
+        where = test.failed.stack.split("\n")[1]
+        regex = /\((.*)\)/
+        match = regex.exec(where)
+        console.log "#{indent}    #{match[1]}"
+
     console.log()
     if suite.failed
       process.exit(1)


Fixed bug where target variable was named incorrectly

--- server/methods/box/buildFromTemplate.coffee
+++ server/methods/box/buildFromTemplate.coffee
@@ -11,7 +11,7 @@
     #PICK FOLDER TO COPY
     if Meteor.settings.public.environment == "dev"
       targetFolder = "0" #Root level for Dev
-    else if targetFolder
+    else if target
       console.log("Found target folder:")
       console.log(" - " + targetFolder.folderId)
       console.log(" - " + targetFolder.folderName)