No swipe, just click.

No swipe, just click.

--- assets/javascripts/behavior.js.coffee
+++ assets/javascripts/behavior.js.coffee
@@ -1,9 +1,9 @@
-if navigator.userAgent.match(/iPhone/i) or navigator.userAgent.match(/iPod/i) or navigator.userAgent.match(/iPad/i)
-  $(".item").bind "touchmove", (event) ->
-    $(this).find(".actions").fadeIn('fast')
-else
-  $(".item").click ->
-    $(this).find(".actions").fadeIn('fast')
+# if navigator.userAgent.match(/iPhone/i) or navigator.userAgent.match(/iPod/i) or navigator.userAgent.match(/iPad/i)
+#   $(".item").bind "touchmove", (event) ->
+#     $(this).find(".actions").fadeIn('fast')
+# else
+$(".item").click ->
+  $(this).find(".actions").fadeIn('fast')

 $(".item .actions button").click (event) ->
   event.stopPropagation()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tiled_background.coffee
+++ src/tiled_background.coffee
@@ -1,11 +0,0 @@
-TiledBackground = (I={}) ->
-  Object.reverseMerge I,
-    sprite: Sprite.EMPTY
-
-  self = GameObject(I)
-
-  self.unbind 'draw'
-  self.bind 'draw', (canvas) ->
-    I.sprite.fill(canvas, -16, -16, App.width, App.height)
-
-  return self


fix bug in Gruntfile, esteDeps needs target

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -148,12 +148,12 @@
         js:
           files: appJsFiles
           tasks: if grunt.option('stage') then [
-            'esteDeps'
+            'esteDeps:all'
             'esteUnitTests:app'
             'esteBuilder:app'
           ]
           else [
-            'esteDeps'
+            'esteDeps:all'
             'esteUnitTests:app'
           ]

@@ -188,7 +188,7 @@
   grunt.registerTask 'run', 'Build app and run watchers.', (app) ->
     tasks = [
       "build:#{app}"
-      "esteWatch"
+      "esteWatch:#{app}"
     ]
     grunt.task.run tasks


watch app files

watch app files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -18,6 +18,12 @@
     watch:
       options:
         livereload: true
+      app:
+        files: ['app/*',
+                'app/classes/*',
+                'app/models/*',
+                'app/views/*',
+                'app/collections/*']
       css:
         files: ['app/assets/css/*']
         tasks: ['stylus']
@@ -96,5 +102,6 @@

   grunt.registerTask('work', ['jade:debug', 'handlebars', 'stylus', 'connect:debug', 'watch'])
   grunt.registerTask('production', ['jade:production', 'handlebars', 'requirejs:production', 'stylus', 'connect:production:keepalive'])
+  grunt.registerTask('noop',->)
   grunt.registerTask('default', ['work'])


fixed bad merge

fixed bad merge

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -153,83 +153,6 @@
         expand: true, cwd: 'build/taskmanager/'
         src: ['taskmanager.js']
         dest: '.' } ] }
-<<<<<<< HEAD
-
-      logger: { files: [ {
-        expand: true, cwd: 'src/logger'
-        src: ['*.json']
-        dest: 'build/logger/' } ] }
-
-      peerconnection: { files: [ {
-        expand: true, cwd: 'src/peerconnection'
-        src: ['*.json']
-        dest: 'build/peerconnection/' } ] }
-
-      chat: {
-        files: [ {
-          expand: true, cwd: 'src/samples/chat/'
-          src: ['*.html']
-          dest: 'build/samples/chat/'
-        }, {
-          expand: true, cwd: 'build/peerconnection/'
-          src: ['**/*']
-          dest: 'build/samples/chat/peerconnection/'
-        }, {
-          expand: true, cwd: 'build/handler/'
-          src: ['**/*']
-          dest: 'build/samples/chat/handler/'
-        }, {
-          expand: true, cwd: 'third_party/webrtc-adapter/'
-          src: ['**/*']
-          dest: 'build/samples/chat/webrtc-adapter/'
-        } ]
-      }
-
-      chat2: {
-        files: [ {
-          expand: true, cwd: 'src/samples/chat2/'
-          src: ['*.html']
-          dest: 'build/samples/chat2/'
-        }, {
-          expand: true, cwd: 'build/peerconnection/'
-          src: ['**/*']
-          dest: 'build/samples/chat2/peerconnection/'
-        }, {
-          expand: true, cwd: 'build/handler/'
-          src: ['**/*']
-          dest: 'build/samples/chat2/handler/'
-        }, {
-          expand: true, cwd: 'third_party/angular/'
-          src: ['**/*']
-          dest: 'build/samples/chat2/angular/'
-        }, {
-          expand: true, cwd: 'third_party/webrtc-adapter/'
-          src: ['**/*']
-          dest: 'build/samples/chat2/webrtc-adapter/'
-        } ]
-      }
-
-      # Throwaway app to verify freedom-for-uproxy works.
-      freedomchat: {
-        files: [ {
-          expand: true, cwd: 'src/samples/freedomchat/'
-          src: ['**/*']
-          dest: 'build/samples/freedomchat/'
-        }, {
-          expand: true, cwd: 'build/'
-          src: ['freedom-for-chrome-for-uproxy.js']
-          dest: 'build/samples/freedomchat/chrome/lib/'
-        }, {
-          expand: true, cwd: 'build/'
-          src: ['coreproviders/**']
-          dest: 'build/samples/freedomchat/chrome/lib/'
-        }, {
-          expand: true, cwd: 'third_party/webrtc-adapter/'
-          src: ['**/*']
-          dest: 'build/samples/freedomchat/chrome/webrtc-adapter/'
-        } ]
-      }
-=======
       # Copy any JavaScript from the third_party directory
       thirdPartyJavaScript: { files: [ {
           expand: true,
@@ -242,20 +165,14 @@
       sampleChat: Rule.copySampleFiles 'peerconnection/samples/chat-webpage', 'lib'
       sampleChat2: Rule.copySampleFiles 'peerconnection/samples/chat2-webpage', 'lib'
       sampleFreedomchat: Rule.copySampleFiles 'coreproviders/samples/freedomchat-chromeapp', 'lib'
->>>>>>> dev

     typescript:
       # For bootstrapping of this Gruntfile
       taskmanager: Rule.typescriptSrc 'taskmanager'
       taskmanagerSpecDecl: Rule.typescriptSpecDecl 'taskmanager'
       # Freedom interfaces (no real spec, only for typescript checking)
-<<<<<<< HEAD
-      freedomDeclarations: Rule.typeScriptSrc 'freedom-declarations'
-      freedomDeclarationsSpecDecl: Rule.typeScriptSpecDecl 'freedom-declarations'
-=======
       freedomDeclarations: Rule.typescriptSrc 'freedom-declarations'
       freedomDeclarationsSpecDecl: Rule.typescriptSpecDecl 'freedom-declarations'
->>>>>>> dev
       # The uProxy modules library
       arraybuffers: Rule.typescriptSrc 'arraybuffers'
       arraybuffersSpecDecl: Rule.typescriptSpecDecl 'arraybuffers'
@@ -405,11 +322,7 @@
   # This is the target run by Travis. Targets in here should run locally
   # and on Travis/Sauce Labs.
   taskManager.add 'test', [
-<<<<<<< HEAD
-    'copyTypeScriptBase'
-=======
-    'base'
->>>>>>> dev
+    'base'
     'typescript:freedomDeclarations'
     'typescript:freedomDeclarationsSpecDecl'
     'build'


Reordered Grunt tasks

Reordered Grunt tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -10,56 +10,48 @@
       specs  : 'specs'

     source:
-      init    : '<%= meta.source %>/<%= meta.name %>.init.coffee'
-      utils   : '<%= meta.source %>/<%= meta.name %>.utils.coffee'
-      specs   : '<%= meta.specs %>/<%= meta.name %>.spec.coffee'
+      micro  : '<%= meta.source %>/<%= meta.name %>.coffee'
+      utils  : '<%= meta.source %>/<%= meta.name %>.utils.coffee'
+      ajax   : '<%= meta.source %>/<%= meta.name %>.ajax.coffee'
+      specs  : '<%= meta.specs %>/*.spec.coffee'

     coffee:
-      init:
-        files: '<%= meta.build %>/<%= meta.name %>.init.js' : '<%= source.init %>'
-      utils:
-        files: '<%= meta.build %>/<%= meta.name %>.utils.js' : '<%= source.utils %>'
-      specs:
-        files: '<%= meta.build %>/<%= meta.name %>.spec.js': '<%= source.specs %>'
+      micro  : files: '<%= meta.build %>/<%= meta.name %>.js'       : '<%= source.micro %>'
+      utils  : files: '<%= meta.build %>/<%= meta.name %>.utils.js' : '<%= source.utils %>'
+      ajax   : files: '<%= meta.build %>/<%= meta.name %>.ajax.js'  : '<%= source.ajax %>'
+      specs  : files: '<%= meta.build %>/<%= meta.name %>.spec.js'  : '<%= source.specs %>'

-    concat:
-      dist:
-        src: [
-          '<%= meta.dist %>/<%= meta.name %>.init.js'
-          '<%= meta.dist %>/<%= meta.name %>.utils.js'
-        ]
-        dest: '<%= meta.dist %>/<%= meta.name %>.js'
+    clean:
+      build  : src: ['build']
+      dist   : src: ['dist']

     uglify:
-      options:
-        mangle: true
-        report: 'gzip'
-      init:
-        files: '<%= meta.dist %>/<%= meta.name %>.init.js' : '<%= meta.build %>/<%= meta.name %>.init.js'
-      utils:
-        files: '<%= meta.dist %>/<%= meta.name %>.utils.js' : '<%= meta.build %>/<%= meta.name %>.utils.js'
+      options: mangle: true, report: 'gzip'
+      micro  : files: '<%= meta.dist %>/<%= meta.name %>.js'       : '<%= meta.build %>/<%= meta.name %>.js'
+      utils  : files: '<%= meta.dist %>/<%= meta.name %>.utils.js' : '<%= meta.build %>/<%= meta.name %>.utils.js'
+      ajax   : files: '<%= meta.dist %>/<%= meta.name %>.ajax.js'  : '<%= meta.build %>/<%= meta.name %>.ajax.js'

     jasmine:
-      pivotal:
-        src: [
-          '<%= meta.build %>/<%= meta.name %>.init.js'
-          '<%= meta.build %>/<%= meta.name %>.utils.js'
-        ]
-        options:
-          specs: '<%= meta.build %>/<%= meta.name %>.spec.js'
+      pivotal: src: [
+        '<%= meta.build %>/<%= meta.name %>.js'
+        '<%= meta.build %>/<%= meta.name %>.utils.js'
+        '<%= meta.build %>/<%= meta.name %>.ajax.js'
+      ]
+      options: specs: '<%= meta.build %>/<%= meta.name %>.spec.js'

     watch:
-      specs:
-        files: [
-          '<%= meta.source %>/*.coffee',
-          '<%= meta.specs %>/*.coffee'
-        ]
-        tasks: ['coffee', 'jasmine']
+      specs: files: [
+        '<%= meta.source %>/*.coffee',
+        '<%= meta.specs %>/*.coffee'
+      ]
+      tasks: ['coffee', 'jasmine']

     grunt.loadNpmTasks 'grunt-contrib-coffee'
+    grunt.loadNpmTasks 'grunt-contrib-clean'
     grunt.loadNpmTasks 'grunt-contrib-concat'
     grunt.loadNpmTasks 'grunt-contrib-jasmine'
     grunt.loadNpmTasks 'grunt-contrib-uglify'
     grunt.loadNpmTasks 'grunt-contrib-watch'

-    grunt.registerTask 'default', ['coffee', 'uglify', 'concat', 'jasmine']+    grunt.registerTask 'test', ['clean', 'coffee', 'jasmine']
+    grunt.registerTask 'default', ['clean', 'coffee', 'uglify', 'jasmine']


clean up

clean up

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -24,7 +24,7 @@
           paths: ['path/to/import', 'another/to/import']
           urlfunc: 'embedurl' # use embedurl('test.png') in our code to trigger Data URI embedding
           use: [
-            require('fluidity') # use stylus plugin at compile time
+            require 'fluidity' # use stylus plugin at compile time
           ]
           import: [      #  @import 'foo', 'bar/moo', etc. into every .styl file
             'foo'       #  that is compiled. These might be findable based on values you gave


grunt stuff

grunt stuff

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,4 +1,22 @@
 module.exports = (grunt) =>
-  require("load-grunt-tasks")(grunt)
+  require("load-grunt-tasks")(grunt)
   grunt.initConfig
-  +    pkg: grunt.file.readJSON('package.json') #
+    vulcanize:
+      components:
+        files:
+          "assets/components.html": "components.html"
+          "assets/components-min.html": "assets/components.html"
+    sass:
+      default:
+        files: "assets/css/app.min.css": "sass/app.scss"
+    watch:
+      components:
+        files: "components.html"
+        tasks: ["vulcanize"]
+      sass:
+        files: "sass/*.scss"
+        tasks: ["sass"]
+
+  # Tasks
+  grunt.registerTask "build", ["vulcanize", "sass"]


Whitespace commit to kick ALM in the balls and deploy properly

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,3 +1,4 @@
+# ALM has trouble deploying wars, unlike Russia
 module.exports = (grunt) ->
   serverPort = grunt.option('port') || 8892
   inlinePort = grunt.option('port') || 8893


Compile CoffeeScript always before tests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -132,11 +132,11 @@

   @registerTask 'test', 'Build NoFlo and run automated tests', (target = 'all') =>
     @task.run 'coffeelint'
+    @task.run 'coffee'
     if target is 'all' or target is 'nodejs'
       @task.run 'cafemocha'
     if target is 'all' or target is 'browser'
       @task.run 'connect'
-      @task.run 'coffee'
       @task.run 'noflo_browser'
       @task.run 'mocha_phantomjs'


Set max cyclomatic complexity to 10

Set max cyclomatic complexity to 10

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -37,6 +37,7 @@
         trailing: true
         maxparams: 3
         maxlen: 120
+        maxcomplexity: 10
         # Relaxing options
         asi: false
         boss: false
@@ -86,6 +87,9 @@
         max_line_length:
           level: 'error'
           value: 120
+        cyclomatic_complexity:
+          level: 'error'
+          value: 10

       source: 'site/scripts/**/*.coffee'
       grunt: 'Gruntfile.coffee'


Doesn't use phantomjs

Doesn't use phantomjs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -258,11 +258,6 @@
           dest: ""
         ]

-    # Excec test.
-    exec:
-      test: "./node_modules/.bin/mocha-phantomjs -p ./node_modules/mocha-phantomjs/node_modules/phantomjs2/bin/phantomjs test/test.html"
-
-
   # tasks
   grunt.registerTask 'watch', ['esteWatch']
   grunt.registerTask 'minify', ['ngmin', 'uglify', 'cssmin']


Reorganize Gruntfile.

Reorganize Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -37,14 +37,14 @@
         'docs/*'
         '!docs/.gitignore'
       ]
-      lib: [
-        'lib/*'
-        '!lib/.gitignore'
-      ]
       'gh-pages': [
         'gh-pages/*'
         '!gh-pages/.gitignore'
         '!gh-pages/template.html'
+      ]
+      lib: [
+        'lib/*'
+        '!lib/.gitignore'
       ]
     coffee:
       src:
@@ -124,11 +124,6 @@
     'watch'
   ]

-  grunt.registerTask 'lib', [
-    'clean:lib'
-    'bower'
-  ]
-
   grunt.registerTask 'dist', [
     'clean:dist'
     'coffee'
@@ -136,7 +131,15 @@
     'autoprefixer'
   ]

-  grunt.registerTask 'docs', ['clean:docs', 'groc']
+  grunt.registerTask 'docs', [
+    'clean:docs'
+    'groc'
+  ]
+
+  grunt.registerTask 'lib', [
+    'clean:lib'
+    'bower'
+  ]

   grunt.registerTask 'pages', [
     'dist'


Revert "gruntfile: ensure css files are built when testing"

This reverts commit d4260b7eefe5b1be0ec1762138a1368cfdf63e3a.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -149,7 +149,7 @@
     # task to watch sources for changes and recompile during development
     watch:
       files: [paths.coffee_src, paths.less_src]
-      tasks: 'build' # or 'test', or 'testserver' :)
+      tasks: 'deps' # or 'test', or 'testserver' :)

     # task to run shell commands
     exec:
@@ -179,11 +179,6 @@
         src: paths.less_src
         dest: paths.css_dest

-    # construct build (no closure compilation)
-    build:
-      js: 'deps'
-      css: 'less:dev'
-

   # Load tasks
@@ -196,8 +191,9 @@
   grunt.loadNpmTasks 'grunt-jasmine-spec-server'

   # Register tasks
-  grunt.registerTask 'compile', ['exec:mkbuild', 'build', 'closureCompiler']
+  grunt.registerTask 'compile', ['exec:mkbuild', 'coffee', 'less:dev',
+                                 'closureCompiler']
   grunt.registerTask 'deps', ['coffee', 'closureDepsWriter']
-  grunt.registerTask 'test', ['clean', 'build', 'jasmine', 'clean:test']
-  grunt.registerTask 'server', ['build', 'jasmineSpecServer', 'watch']
+  grunt.registerTask 'test', ['clean', 'deps', 'jasmine', 'clean:test']
+  grunt.registerTask 'server', ['deps', 'jasmineSpecServer', 'watch']
   grunt.registerTask 'default', ['compile']


sorted rjs paths

sorted rjs paths

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -141,10 +141,13 @@
           name: 'app'
           out: 'build/assets/js/app-<%= grunt.config.get("git-commit") %>.js'
           include: [
-            'controller/app-controller'
+            'startup'
           ],
           paths:
             'angular': 'empty:'
+            'angular-resource': 'empty:'
+            'angular-route': 'empty:'
+            'lodash': 'empty:'
     uglify:
       prod:
         files:
@@ -200,6 +203,7 @@
   grunt.loadNpmTasks 'grunt-regex-replace'

   grunt.registerTask 'default', 'run the server and watch for changes', [
+    'install'
     'compass:dev'
     'coffee:dev'
     'jade:dev'
@@ -208,6 +212,7 @@
   ]

   grunt.registerTask 'test', 'compile the app and run the tests', [
+    'install'
     'compass:dev'
     'coffeelint'
     'coffee:dev'
@@ -216,12 +221,14 @@
   ]

   grunt.registerTask 'unit', 'run unit tests', [
+    'install'
     'coffeelint'
     'coffee:dev'
     'karma:unit'
   ]

   grunt.registerTask 'client', 'run client tests', [
+    'install'
     'compass:dev'
     'coffeelint'
     'coffee:dev'
@@ -243,7 +250,7 @@
     child.stdout.on 'data', (data) ->
       grunt.log.write data

-  grunt.registerTask 'package', 'package the app', () ->
+  grunt.registerTask 'build', 'build and package the app', () ->
     done = @async()

     tasks = [


No need for marker file, simply use the build folder as a marker

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -147,13 +147,10 @@

 	# Deploy - creates deploy folder structure
 	grunt.registerTask 'deploy', ->
-		console.log 'Entering deploy task...'
 		env = this.args[0] or 'dev'
 		commit = grunt.config('gitCommit')
-		console.log 'Commit set to', commit
 		deployDir = path.resolve grunt.config('deployDirectory'), commit
 		deployExists = false
-		console.log 'Deploy dir set to', deployDir
 		grunt.log.writeln 'Version deploy dir set to: '.cyan + deployDir.green
 		try
 			deployExists = fs.existsSync deployDir
@@ -162,17 +159,9 @@
 			console.log e

 		if deployExists
-			console.log 'Deploy dir exists'
-			skipFilePath = path.resolve '.', 'build/skip_version_upload'
-			try
-				fs.writeFileSync skipFilePath, 'true'
-			catch e
-				grunt.log.writeln 'Error creating marker file'.red
-				console.log e
 			grunt.log.writeln 'Folder '.cyan + deployDir.green + ' already exists.'.cyan
-			grunt.log.writeln 'Created marker file: '.cyan + 'build/skip_version_upload'.green
 			grunt.log.writeln 'Skipping build process and generating environment folder.'.cyan
-			grunt.task.run ['gen-version:' + env]
+			grunt.task.run ['clean', 'gen-version:' + env]
 		else
 			grunt.task.run ['prod', 'copy:deploy', 'gen-version:' + env]


more stripe and grunt updates

more stripe and grunt updates

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -171,8 +171,9 @@
   grunt.loadNpmTasks 'grunt-bump'

   # Default task.
-  grunt.registerTask 'default', ['clean', 'exec:generate_docs', 'coffee', 'concat', 'copy', 'exec:start_server', 'connect:test', 'qunit', 'exec:kill_server', 'uglify']
-  grunt.registerTask 'notest', ['clean', 'exec:generate_docs', 'coffee', 'concat', 'copy', 'uglify']
+  grunt.registerTask 'default', ['test']
+  grunt.registerTask 'test', ['clean', 'exec:generate_docs', 'coffee', 'concat', 'copy', 'exec:start_server', 'connect:test', 'qunit', 'exec:kill_server', 'uglify']
+  grunt.registerTask 'build', ['clean', 'exec:generate_docs', 'coffee', 'concat', 'copy', 'uglify']
   grunt.registerTask 'debug', ['clean', 'coffee', 'concat', 'copy', 'exec:start_server', 'exec:open_browser', 'connect:debug']
   # grunt.registerTask 'noserver', ['clean', 'coffee', 'concat', 'copy', 'connect:test', 'qunit' ]
   grunt.registerTask 'noserver', ['clean', 'coffee', 'concat', 'copy', 'exec:open_browser', 'connect:debug' ]


Fix the grunt deploy

Fix the grunt deploy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,7 +31,7 @@
             jshint:
                 files: ['js/*.js']
                 tasks: ['jshint']
-
+
         connect:

             livereload:
@@ -71,6 +71,8 @@
                         'bower_components/**'
                         'js/**'
                         'images/**'
+                        'css/**'
+                        'video/**'
                     ]
                     dest: 'dist/'
                 },{
@@ -80,7 +82,7 @@
                     filter: 'isFile'
                 }]

-
+
         manifest:

             generate:
@@ -145,7 +147,7 @@
                 options:
                     remote: 'git@github.com:ukmadlz/cloud-replication-equals-offline-awesome.git'
                     branch: 'gh-pages'
-
+

     # Load all grunt tasks.
@@ -187,13 +189,13 @@
             'copy'
         ]

-
+
     grunt.registerTask 'deploy',
         'Deploy to Github Pages', [
             'dist'
             'buildcontrol'
         ]
-
+

     # Define default task.
     grunt.registerTask 'default', [


Add Grunt task ‘clean:package’

Add Grunt task ‘clean:package’

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,5 +1,7 @@
 module.exports = (grunt) ->
 	grunt.initConfig
+		clean:
+			package: ['lib']
 		coffee:
 			options:
 				bare: true
@@ -10,6 +12,7 @@
 				dest: 'lib'
 				ext: '.js'

+	grunt.loadNpmTasks 'grunt-contrib-clean'
 	grunt.loadNpmTasks 'grunt-contrib-coffee'

 	grunt.registerTask 'default', []


Remobe unnecessary 'open' instruction for serve:dist

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -460,7 +460,6 @@
         'env:prod'
         'express:prod'
         'wait'
-        'open'
         'express-keepalive'
       ]

@@ -519,4 +518,3 @@
     'test'
     'build'
   ])
-


Stricter rules for compress so we don't get any cruft from Bower

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -126,16 +126,28 @@
           dest: '/'
         ,
           src: [
-            'bower_components/**/*.js'
-            'bower_components/**/*.css'
-            'bower_components/**/*.woff'
-            'bower_components/**/*.ttf'
-            'bower_components/**/*.svg'
-          ]
-          expand: true
-          dest: '/'
-        ,
-          src: ['bower_components/**/*.map']
+            'bower_components/codemirror/**/*.js'
+            'bower_components/codemirror/lib/*.css'
+            'bower_components/codemirror/theme/mdn-like.css'
+            'bower_components/coffee-script/extras/*.js'
+            'bower_components/font-awesome/css/*.css'
+            'bower_components/font-awesome/**/*.woff'
+            'bower_components/font-awesome/**/*.ttf'
+            'bower_components/font-awesome/**/*.svg'
+            'bower_components/klay-js/klay/*.js'
+            'bower_components/platform/*.js'
+            'bower_components/polymer/*.js'
+            'bower_components/react/*.js'
+            'bower_components/the-graph/**/*.js'
+            'bower_components/the-graph/**/*.css'
+          ]
+          expand: true
+          dest: '/'
+        ,
+          src: [
+            'bower_components/platform/*.map'
+            'bower_components/polymer/*.map'
+          ]
           expand: true
           flatten: true
           dest: '/'


Watch Gruntfile.

Watch Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -93,6 +93,11 @@
         options:
           cwd: {files: featureDir}
         tasks: 'protractor:cucumber'
+      grunt:
+        files: 'Gruntfile.coffee'
+        options:
+          cwd: {files: '.'}
+        tasks: []
       haml:
         files: hamlFiles
         options:


Remove useless codes

Remove useless codes

--- index.cjsx
+++ index.cjsx
@@ -2,7 +2,6 @@
 Promise = require 'bluebird'
 path = require 'path'
 fs = Promise.promisifyAll require 'fs-extra'
-webview = $('kan-game webview')
 shipgraph = {}
 voiceMap = {}
 voiceKey = [604825,607300,613847,615318,624009,631856,635451,637218,640529,643036,652687,658008,662481,669598,675545,685034,687703,696444,702593,703894,711191,714166,720579,728970,738675,740918,743009,747240,750347,759846,764051,770064,773457,779858,786843,790526,799973,803260,808441,816028,825381,827516,832463,837868,843091,852548,858315,867580,875771,879698,882759,885564,888837,896168]
@@ -19,14 +18,11 @@
   console.error err.code if err?.code isnt 'ENOENT' and err?.code

 handleGameResponse = (e) ->
-    {method, path, body, postBody} = e.detail
-    {_ships, _decks, _teitokuLv} = window
-    switch path
-      when '/kcsapi/api_start2'
-        shipgraph[ship.api_filename] = ship.api_id for ship in body.api_mst_shipgraph
-        # Adjust animation duation
-        $('poi-alert #alert-area')?.style?.animationDuration="20s"
-    return
+  {method, path, body, postBody} = e.detail
+  {_ships, _decks, _teitokuLv} = window
+  switch path
+    when '/kcsapi/api_start2'
+      shipgraph[ship.api_filename] = ship.api_id for ship in body.api_mst_shipgraph

 handleGetResponseDetails = (e) ->
   prior = 5
@@ -49,13 +45,12 @@
     window.log "本【#{$ships[apiId].api_name}】的台词字幕缺失的说，来舰娘百科（http://zh.kcwiki.moe/）帮助我们补全台词吧！",
       priority : prior,
       stickyFor: 5000
-  return

 module.exports =
   show: false
   pluginDidLoad: (e) ->
     window.addEventListener 'game.response', handleGameResponse
-    webview.addEventListener 'did-get-response-details', handleGetResponseDetails
+    $('kan-game webview').addEventListener 'did-get-response-details', handleGetResponseDetails
   pluginWillUnload: (e) ->
     window.removeEventListener 'game.response', handleGameResponse
-    webview.removeEventListener 'did-get-response-details', handleGetResponseDetails
+    $('kan-game webview').removeEventListener 'did-get-response-details', handleGetResponseDetails


Adjust animation(overflow scroll) duration

--- index.cjsx
+++ index.cjsx
@@ -29,6 +29,8 @@
     switch path
       when '/kcsapi/api_start2'
         shipgraph[ship.api_filename] = ship.api_id for ship in body.api_mst_shipgraph
+        # Adjust animation duation
+        $('poi-alert #alert-area')?.style?.animationDuration="20s"
     return
   webview.addEventListener 'did-get-response-details', (e) ->
     match = /kcs\/sound\/kc(.*?)\/(.*?).mp3/.exec(e.newURL)


Fix build process

Fix build process

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

       dist:
         files:
-          src: ["src/**/*.js", "test/**/*.js"]
+          src: ["src/**/*.js"]

     # File concatenation, copying and templating
     concat:
@@ -89,7 +89,7 @@
       done(!error?)

   # Release meta-task
-  grunt.registerTask "release", ["jshint", "uglify", "git-tag", "s3"]
+  grunt.registerTask "release", ["jshint", "concat", "uglify", "git-tag", "s3"]

   # Default meta-task
-  grunt.registerTask "default", ["jshint", "uglify"]+  grunt.registerTask "default", ["jshint", "concat", "uglify"]


update default task

update default task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -183,7 +183,7 @@
   grunt.config.set "cssmin", cssmin

   # set the default task.
-  grunt.registerTask "default", "coffeelint"
+  grunt.registerTask "default", ["coffeelint","js","css","qunit"]

   grunt.registerTask "sass", "compile sass files into css file", ->
     grunt.log.writeln "sass --update css"


updated Gruntfile with new folder system

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,5 +1,13 @@
 module.exports = (grunt) ->
+
+	appConfig =
+		src:	'src',
+		dist:	'dist',
+		docs:	'docs'
+
 	grunt.initConfig
+
+		config : appConfig

 		jade:
 			options:
@@ -17,7 +25,8 @@
 				sourcemap: "none"
 			app:
 				files:
-					['docs/assets/css/stones.css': 'src/sass/stones.sass','build/stones.css': 'src/sass/stones.sass']
+					['<%= config.docs %>/assets/css/stones.css': '<%= config.src %>/sass/stones.sass','<%= config.dist %>/css/stones.css': '<%= config.src %>/sass/stones.sass']
+				# Maybe unnecessary
 				require: ['sass-json-vars']
 				noCache: true
 				debugInfo: true
@@ -25,55 +34,67 @@
 		coffeelint:
 			app:
 				files:
-					src: ['src/js/**/*.coffee']
+					src: ['<%= config.src %>/coffee/**/*.coffee']

 		coffee:
 			options:
 				sourceMap: false
 			app:
 				files:
-					'docs/assets/js/main.js': ['src/js/**/*.coffee']
+					['<%= config.docs %>/assets/js/stones.js': '<%= config.src %>/coffee/**/*.coffee','<%= config.dist %>/js/stones.js': '<%= config.src %>/coffee/**/*.coffee']

 		copy:
 			main:
 				files: [
 					{
 						expand: true
-						cwd: 'src/imgs'
+						cwd: '<%= config.src %>/imgs'
 						src: '**/*'
-						dest: 'docs/assets/imgs'
+						dest: '<%= config.dist %>/imgs'
 					},
 					{
 						expand: true
-						cwd: 'src/fonts'
+						cwd: '<%= config.src %>/imgs'
 						src: '**/*'
-						dest: 'docs/assets/fonts'
+						dest: '<%= config.docs %>/assets/imgs'
+					},
+					{
+						expand: true
+						cwd: '<%= config.src %>/fonts'
+						src: '**/*'
+						dest: '<%= config.dist %>/fonts'
+					},
+					{
+						expand: true
+						cwd: '<%= config.src %>/fonts'
+						src: '**/*'
+						dest: '<%= config.docs %>/assets/fonts'
 					}
 				]

 		watch:
 			jade:
-				files: ['src/views/**/*.jade']
+				files: ['<%= config.src %>/views/**/*.jade']
 				tasks: ['jade', 'notify:watch']

 			coffee:
-				files: ['src/js/**/*.coffee']
+				files: ['<%= config.src %>/coffee/**/*.coffee']
 				tasks: ['coffeelint', 'coffee', 'notify:watch']

 			sass:
-				files: ['src/sass/**/*.sass']
+				files: ['<%= config.src %>/sass/**/*.sass']
 				tasks: ['sass', 'notify:watch']

 			imgs:
-				files: ['src/imgs/*']
+				files: ['<%= config.src %>/imgs/*']
 				tasks: ['copy', 'notify:watch']

 			fonts:
-				files: ['src/fonts/*']
+				files: ['<%= config.src %>/fonts/*']
 				tasks: ['copy', 'notify:watch']

 			build:
-				files: ['docs/assets/stylesheets/stone.css', 'docs/*.html', 'docs/assets/javascript/**/*.js']
+				files: ['<%= config.docs %>/assets/css/stone.css', '<%= config.docs %>/*.html', '<%= config.docs %>/assets/js/stones.js']
 				options:
 					livereload: true


Don't delete and rebuild the qirest env in the reinstall task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,7 +3,7 @@
     pkg: grunt.file.readJSON('package.json')

     clean:
-      derived: ['public', 'build']
+      derived: ['public', 'build', 'typings']
       options:
         force: true

@@ -159,20 +159,33 @@
         configFile: 'test/conf/karma-conf.coffee'

     exec:
+      # # Delete the qirest Anaconda environment.
+      # # Note: conda env remove hangs when executed from a script.
+      # # It does not run when executed from a script in background.
+      # # There is no known mechanism to make this work.
+      # cleanqirest:
+      #   command:
+      #     '(source deactivate 2>/dev/null);' +
+      #     '(yes | conda env remove -n qirest)'
+
       # Clean all installation artifacts, including the qirest Anaconda
       # environment.
-      cleanall:
+      cleannpm:
         command:
-          'rm -rf node_modules/ jspm_packages/ build/ public/ typings/; ' +
-          'source deactivate 2>/dev/null; ' +
-          'conda env remove -n qirest 2>/dev/null'
+          'rm -rf node_modules/ jspm_packages/'

       # Build the qirest Anaconda environment and install the application.
-      buildall:
+      buildqirest:
         command:
-          'conda env create -n qirest pip 2>/dev/null; ' +
-          'source activate qirest; ' +
-          'pip install -r requirements.txt; ' +
+          # Note: conda env remove hangs when executed from a script.
+          # There is no known mechanism to make this work.
+          # '(yes | conda create -n qirest pip) && ' +
+          'source activate qirest && ' +
+          'pip install --upgrade -r requirements.txt'
+
+      # Build the qirest Anaconda environment and install the application.
+      buildnpm:
+        command:
           'npm install'

       # Kill any existing selenium server and install the drivers.
@@ -267,7 +280,13 @@
                                      'exec:updatewebdriver', 'build']

   # Reinstall from scratch.
-  grunt.registerTask 'reinstall', ['exec:cleanall', 'exec:buildall']
+  grunt.registerTask 'cleanall', ['clean', 'exec:cleannpm']
+
+  # Reinstall from scratch.
+  grunt.registerTask 'buildall', ['exec:buildqirest', 'exec:buildnpm']
+
+  # Reinstall from scratch.
+  grunt.registerTask 'reinstall', ['cleanall', 'buildall']

   # Start the server with debug turned on.
   grunt.registerTask 'start:dev', ['express:dev', 'watch']


Convert to single quote.

Convert to single quote.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,13 +11,13 @@
     watch:
       all:
         files: [
-          "Gruntfile.coffee"
-          "lib/**/*.coffee"
-          "test/**/*.coffee"
+          'Gruntfile.coffee'
+          'lib/**/*.coffee'
+          'test/**/*.coffee'
         ]
         tasks: [
-          "coffeecov"
-          "mochaTest"
+          'coffeecov'
+          'mochaTest'
         ]
         options:
           nospawn: true
@@ -57,15 +57,16 @@
   grunt.registerTask 'uploadCoverage', ->
     grunt.task.run 'shell:coveralls'

-  grunt.registerTask "default", [
-    "watch"
-    "mochaTest"
+  grunt.registerTask 'default', [
+    'watch'
+    'mochaTest'
   ]

-  grunt.registerTask "test", [
-    "coffeelint"
-    "coffeecov"
-    "mochaTest"
+  grunt.registerTask 'test', [
+    'coffeelint'
+    'coffeecov'
+    'mochaTest'
   ]

   return
+


Remove verbose from coffeeify.

Remove verbose from coffeeify.

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

     coffeeify:
       options:
-        verbose: true
         requires: ['tandem-core']
       files:
         { dest: 'bin/src/scribe.js', src: ['src/scribe.coffee'] }


grunt task for travis

grunt task for travis

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -37,7 +37,7 @@
     karma:
       options:
         frameworks: ['jasmine']
-        browsers: ['PhantomJS', 'Chrome', 'Firefox']
+        browsers: ['PhantomJS']
         reporters: ['progress', ]
         files: [
           'bower_components/angular/angular.js'
@@ -67,6 +67,9 @@
           singleRun: true
           autoWatch: false

+      travis:
+        singleRun: true
+
     bump:
       options:
         files: ['./package.json', './bower.json']
@@ -93,4 +96,10 @@

   grunt.registerTask 'test', [
     'karma:continuous'
-  ]+  ]
+
+  grunt.registerTask 'travis', [
+    'karma:travis'
+  ]
+
+  grunt.registerTask 'default', [ 'test']


Also bump version in picplus.jquery.json

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -4,7 +4,7 @@
     pkg: grunt.file.readJSON 'package.json'
     bump:
       options:
-        files: ['package.json', 'bower.json']
+        files: ['package.json', 'bower.json', 'picplus.jquery.json']
         commit: true
         commitFiles: ['-a']
         createTag: true


refactored added support for more customized environments

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -13,6 +13,10 @@
   grunt.loadNpmTasks 'grunt-contrib-cssmin'
   grunt.loadNpmTasks 'grunt-exec'

+  currentEnvironment =
+    grunt.config 'env',
+    grunt.option('env') or process.env.GRUNT_ENV or 'development'
+
   env =
     development:
       baseurl: '"\'\'"'
@@ -20,14 +24,20 @@
     staging:
       baseurl: '/open-source-club-website'

+    production:
+      baseurl: '"\'\'"'
+
   grunt.initConfig
     envBaseUrl: null

-    csslint: test:
-      options: import: 2
-      expand: true
-      cwd: '_site/dist/css/'
-      src: '*.css'
+    csslint:
+      development:
+        options: [ {
+          import: 2
+          expand: true
+          cwd: '_site/dist/css/'
+          src: '*.css'
+        } ]

     htmllint:
       options:
@@ -36,7 +46,27 @@
         src: ['_site/**/*.html']

     coffee:
-      dist:
+      production:
+        options: [ {
+          sourceMap: false
+          join: true
+          joinExt: '.coffee'
+        } ]
+        files: [ {
+          '_site/dist/js/app.js': '_assets/js/*.coffee'
+        } ]
+
+      staging:
+        options: [ {
+          sourceMap: false
+          join: true
+          joinExt: '.coffee'
+        } ]
+        files: [ {
+          '_site/dist/js/app.js': '_assets/js/*.coffee'
+        } ]
+
+      development:
         options: [ {
           sourceMap: false
           join: true
@@ -74,7 +104,7 @@
         } ]

     sass:
-      dist:
+      production:
         options: [ {
           sourcemap: 'none'
           style: 'compressed'
@@ -88,8 +118,37 @@
           ext: '.css'
         } ]

+      staging:
+        options: [ {
+          sourcemap: 'none'
+          style: 'compressed'
+          bundleExec: true
+        } ]
+        files: [ {
+          expand: true
+          cwd: '_assets/css/'
+          src: ['*.sass']
+          dest: '_site/dist/css/'
+          ext: '.css'
+        } ]
+
+      development:
+        options: [ {
+          sourcemap: 'none'
+          style: 'expanded'
+          bundleExec: true
+        } ]
+        files: [ {
+          expand: true
+          cwd: '_assets/css/'
+          src: ['*.sass']
+          dest: '_site/dist/css/'
+          ext: '.css'
+        } ]
+
+
     uglify:
-      my_target:
+      production:
         files: [ {
           expand: true
           cwd: '_site/'
@@ -98,8 +157,31 @@
           ext: '.min.js'
         } ]

+      staging:
+        files: [ {
+          expand: true
+          cwd: '_site/'
+          src: ['**/*.js']
+          dest: '_site/'
+          ext: '.min.js'
+        } ]
+
+      development:
+        options: [ {
+          compress: false
+          beautify: true
+          mangle: false
+        } ]
+        files: [ {
+          expand: true
+          cwd: '_site/'
+          src: ['**/*.js']
+          dest: '_site/'
+          ext: '.min.js'
+        } ]
+
     cssmin:
-      target:
+      production:
         files: [ {
           expand: true
           cwd: '_site/'
@@ -108,69 +190,104 @@
           ext: '.min.css'
         } ]

+      staging:
+        files: [ {
+          expand: true
+          cwd: '_site/'
+          src: ['**/*.css']
+          dest: '_site/'
+          ext: '.min.css'
+        } ]
+
+      development:
+        files: [ {
+          expand: true
+          cwd: '_site/'
+          src: ['**/*.css']
+          dest: '_site/'
+          ext: '.min.css'
+        } ]
+
     copy:
-      image: files: [ {
+      image:
+        files: [ {
+          stdout: false
+          expand: true
+          cwd: '_assets/img/'
+          src: '*'
+          dest: '_site/dist/img/'
+        } ]
+
+      js:
+        files: [ {
+          stdout: false
+          expand: true
+          filter: 'isFile'
+          flatten: true
+          cwd: 'bower_components/'
+          src: [
+            'jquery/dist/*.js'
+            'simple-jekyll-search/dest/*.js'
+            'mousetrap/mousetrap.js'
+            '!**/*.min.js'
+          ]
+          dest: '_site/vendor/js/'
+        } ]
+
+      css:
+        files: [ {
+          stdout: false
+          expand: true
+          filter: 'isFile'
+          flatten: true
+          cwd: 'bower_components/'
+          src: [ '**/*.css', '!**/*.min.css' ]
+          dest: '_site/vendor/css/'
+        } ]
+
+      fonts:
+        files: [ {
+          stdout: false
+          expand: true
+          filter: 'isFile'
+          flatten: true
+          cwd: 'bower_components/'
+          src: [ '**/*.woff', '**/*.woff2', '**/*.ttf' ]
+          dest: '_site/vendor/fonts/'
+        } ]
+
+
+    exec:
+
+      options: [ {
         stdout: false
-        expand: true
-        cwd: '_assets/img/'
-        src: '*'
-        dest: '_site/dist/img/'
       } ]

-      js: files: [ {
-        stdout: false
-        expand: true
-        filter: 'isFile'
-        flatten: true
-        cwd: 'bower_components/'
-        src: [
-          'jquery/dist/*.js'
-          'simple-jekyll-search/dest/*.js'
-          'mousetrap/mousetrap.js'
-          '!**/*.min.js'
-        ]
-        dest: '_site/vendor/js/'
-      } ]
-
-      css: files: [ {
-        stdout: false
-        expand: true
-        filter: 'isFile'
-        flatten: true
-        cwd: 'bower_components/'
-        src: [
-          '**/*.css'
-          '!**/*.min.css'
-        ]
-        dest: '_site/vendor/css/'
-      } ]
-
-      fonts: files: [ {
-        stdout: false
-        expand: true
-        filter: 'isFile'
-        flatten: true
-        cwd: 'bower_components/'
-        src: [
-          '**/*.woff'
-          '**/*.woff2'
-          '**/*.ttf'
-        ]
-        dest: '_site/vendor/fonts/'
-      } ]
-
-
-    exec:
-      bundler: cmd: 'bundle install --quiet'
-      bower: cmd: 'bower install --quiet'
-      purge: cmd: 'rm -rf _site/*'
-      jekyll: cmd: 'bundle exec jekyll build --quiet'
-      new_post: cmd: 'bash _helpers/new-post.sh'
-      status: cmd: "clear && echo the site is now accessible at http://localhost:<%= connect.server.options.port %>"
-      baseurl: cmd: 'bash _helpers/set-baseurl.sh' + ' ' + "<%= envBaseUrl %>"
+      bundler:
+        cmd: 'bundle install'
+
+      bower:
+        cmd: 'bower install'
+
+      purge:
+        cmd: 'rm -rf _site/*'
+
+      jekyll:
+        cmd: 'bundle exec jekyll build'
+
+      new_post:
+        cmd: 'bash _helpers/new-post.sh'
+
+      status:
+        cmd: "clear && echo the site is now accessible at http://localhost:<%= connect.server.options.port %>"
+
+      baseurl:
+        cmd: 'bash _helpers/set-baseurl.sh' + ' ' + "<%= envBaseUrl %>"
+

     watch:
-      options: livereload: true
+      options:
+        livereload: true

       # TODO
       # add image
@@ -198,14 +315,17 @@
           '*.html'
           '*.md'
         ]
-        tasks: [
-          'exec:purge'
-          'exec:jekyll'
-        ]
-    connect: server: options:
-      port: 4040
-      base: '_site'
-      livereload: false
+
+        tasks: [ 'exec:purge', 'build' ]
+
+    connect:
+      server:
+        options:
+          port: 4040
+          base: '_site'
+          livereload: false
+
+
   grunt.registerTask 'build', [
     'set_environment'
     'exec:baseurl'
@@ -214,10 +334,10 @@
     'jade'
     'exec:jekyll'
     'copy'
-    'sass'
-    'coffee'
-    'uglify'
-    'cssmin'
+    'sass:' + currentEnvironment
+    'coffee:' + currentEnvironment
+    'uglify:' + currentEnvironment
+    'cssmin:' + currentEnvironment
   ]
   grunt.registerTask 'serve', [
     'build'
@@ -225,27 +345,31 @@
     'exec:status'
     'watch'
   ]
-  grunt.registerTask 'test', [
-    'csslint'
-    'htmllint'
-  ]
+  grunt.registerTask 'test', [ 'csslint', 'htmllint' ]
   grunt.registerTask 'new', [ 'exec:new_post' ]
   grunt.registerTask 'default', [ 'serve' ]

   grunt.registerTask 'set_environment', ->
-    currentEnvironment = grunt.config 'env', grunt.option('env') or process.env.GRUNT_ENV or 'development'
-
-    development = currentEnvironment == 'development'
-    staging = currentEnvironment == 'staging'
-
-    if development == true
-      u = env.development.baseurl
-    else if staging == true
-      u = env.staging.baseurl
-
-    grunt.config.set 'envBaseUrl', u
+    isDevelopment =
+      currentEnvironment == 'development'
+
+    isStaging =
+      currentEnvironment == 'staging'
+
+    isProduction =
+      currentEnvironment == 'production'
+
+    if isDevelopment == true
+      baseurl = env.development.baseurl
+
+    else if isStaging == true
+      baseurl = env.staging.baseurl
+
+    else if isProduction == true
+      baseurl = env.production.baseurl
+
+    grunt.config.set 'envBaseUrl', baseurl

     console.log 'environment: ' + currentEnvironment
-    console.log 'baseurl: ' + u
-
+    console.log 'baseurl: ' + baseurl
     return


Grunt: Use ignoreMissing: true

Grunt: Use ignoreMissing: true

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -24,8 +24,8 @@
           ['coffeeify', {global: true}]
         ]
         browserifyOptions:
-          extensions: ['.coffee']
-          fullPaths: false
+          extensions: ['.coffee', '.js']
+          ignoreMissing: true
           standalone: 'fbpspec'
       lib:
         files:


added notification for grunt

added notification for grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -17,16 +17,24 @@
                     'assets/css/responsive.breadcrumbs.css': 'src/less/responsive.breadcrumbs.less'
         watch:
             coffee:
-                files: ['src/coffee/responsive.breadcrumbs.coffee']
+                files: ['src/coffee/site.coffee', 'src/coffee/responsive.breadcrumbs.coffee']
                 tasks: ['coffee']
             less:
                 files: ['src/less/site.less', 'src/less/responsive.breadcrumbs.less']
                 tasks: ['less']
+        notify_hooks:
+            options:
+                enabled: true
+                max_jshint_notifications: 5
+                title: "Responsive Breadcrumbs"

     # Load the plugin that provides the "coffee", "less", "watch" task.
     grunt.loadNpmTasks 'grunt-contrib-coffee'
     grunt.loadNpmTasks 'grunt-contrib-less'
     grunt.loadNpmTasks 'grunt-contrib-watch'
+    grunt.loadNpmTasks 'grunt-notify'

     # Default Tasks
     grunt.registerTask 'default', ['coffee']
+
+    grunt.task.run 'notify_hooks'


Update the grunt config to share updated version in the same grunt process

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,6 +80,7 @@
     bump:
       options:
         files: ["package.json"]
+        updateConfigs: ["pkg"]
         commit: true
         commitMessage: "Bump version to %VERSION%"
         commitFiles: ["-a"]


Add in detectGlobals and noParse into browserify config... Seems to speed up about 1-2s

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -20,9 +20,16 @@
         browserifyOptions:
           fullPaths: true
           basedir: './client/source/'
-          commondir: './client/source/'
+          commondir: "#{__dirname}/client/source/"
           extensions: ['.coffee', '.hbs']
           paths: ['./client/source', 'node_modules']
+          detectGlobals: no # Disable "detect-globals" for faster build
+          noParse: [
+            './bower_components/backbone/backbone.js'
+            './bower_components/chaplin/chaplin.js'
+            './bower_components/cocktail/Cocktail.js'
+            './bower_components/underscore/underscore.js'
+          ]
         alias: [
           './bower_components/backbone/backbone.js:backbone'
           './bower_components/chaplin/chaplin.js:chaplin'


fix heroku deployment

fix heroku deployment

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

             livereload:
                 options:
-                    port: grunt.option('port') || 9000
+                    port: grunt.option('port') || 5000
                     # Change hostname to '0.0.0.0' to access
                     # the server from outside.
                     hostname: '0.0.0.0'


fix(Gruntfile.coffee): Fix packaging tasks running twice for travis

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -559,7 +559,7 @@
   grunt.registerTask 'test:func', 'Run functional tests locally', ['dev', 'mochaWebdriver:local']
   grunt.registerTask 'test:func:sauce', 'Run functional tests on SauceLabs', ['dev', 'mochaWebdriver:sauce']

-  grunt.registerTask 'travis', 'Test with Travis CI', ['check_pull_req', 'test:unit:sauce']
+  grunt.registerTask 'travis', 'Test with Travis CI', ['check_pull_req', 'saucetests:default']

   grunt.registerTask 'dev', 'Prepare code for testing', ['concurrent:clean', 'shell:npm_install', 'bower', 'package', 'copy:test']
   grunt.registerTask 'build', 'Build from latest source', ['concurrent:concat', 'concurrent:minify', 'usebanner', 'copy:images']


Test commit, ensure coffeelint fails Travis build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,4 +62,4 @@
       "test:#{target}"
     ]

-  grunt.registerTask 'default', ['ci:firefox']
+  grunt.registerTask('default', ['ci:firefox']);


Add Megadestroyer event and Karka Queen "Beta"

--- script.coffee
+++ script.coffee
@@ -37,6 +37,7 @@
     { id: "33F76E9E-0BB6-46D0-A3A9-BE4CDFC4A3A4", name: "Fire elemental"},
     { id: "C5972F64-B894-45B4-BC31-2DEEA6B7C033", name: "Jungle wurm"},
     { id: "9AA133DC-F630-4A0E-BB5D-EE34A2B306C2", name: "Golem Mark II"},
+    { id: "9AA133DC-F630-4A0E-BB5D-EE34A2B306C2", name: "Megadestroyer"},

     { id: "2555EFCB-2927-4589-AB61-1957D9CC70C8", name: "Balthazar"},
     { id: "A5B5C2AF-22B1-4619-884D-F231A0EE0877", name: "Melandru"},
@@ -50,7 +51,13 @@
     { id: "95CA969B-0CC6-4604-B166-DBCCE125864F", name: "Dredge Commissar"},
     { id: "242BD241-E360-48F1-A8D9-57180E146789", name: "Taidha Covington"},
     { id: "295E8D3B-8823-4960-A627-23E07575ED96", name: "Fire Shaman"},
-    { id: "A0796EC5-191D-4389-9C09-E48829D1FDB2", name: "Eye of Zaithan"}
+    { id: "A0796EC5-191D-4389-9C09-E48829D1FDB2", name: "Eye of Zaithan"},
+
+    # Four events for the Karka Queen?
+    # { id: "E1CC6E63-EFFE-4986-A321-95C89EA58C07", name: "Defeat the Karka Queen threatening the settlements."},
+    # { id: "F479B4CF-2E11-457A-B279-90822511B53B", name: "Defeat the Karka Queen threatening the settlements."},
+    # { id: "5282B66A-126F-4DA4-8E9D-0D9802227B6D", name: "Defeat the Karka Queen threatening the settlements."},
+    { id: "4CF7AA6E-4D84-48A6-A3D1-A91B94CCAD56", name: "Karka Queen (Beta)"}
   ]

   eventNotificationsEnabled = false


Update webgrabplus-siteini.cson

Update webgrabplus-siteini.cson

debug accidently removed.re-added.

--- grammars/webgrabplus-siteini.cson
+++ grammars/webgrabplus-siteini.cson
@@ -108,14 +108,14 @@
         'name': 'entity.name.function.siteini'
   }
   {
-    # specials xmltv attributes
+    # xmltv attributes
     'match': '\\((lang|system|role|src|width|height|start|channel)\\s*='
     'captures':
       '1':
         'name': 'entity.name.tag.siteini'
   }
   {
-    # specials style=
+    # style=
     'match': '[\\s+\\(](style)=(sentence|name|upper|lower|regex|urlencode|urldecode|unidecode|uniencode|htmlencodespecialchar|htmldecodespecialchar|jsondecode|base64encode|base64decode(,decimal)?|sha256encode|md5encode|roman2arabicnumeral)'
     'captures':
       '1':
@@ -124,7 +124,7 @@
         'name': 'entity.name.function.siteini'
   }
   {
-    # specials type=
+    # type=
     'match': '[\\s+\\(](type)=(string|char|word|sentence|paragraph|element|regex|run)'
     'captures':
       '1':
@@ -151,7 +151,7 @@
         'name': 'entity.name.function.siteini'
   }
   {
-    # specials include/exlcude=
+    # include/exlcude=
     'match': "(include|exclude)\\s*=\\s*(\\d+|first|last|\".+?\")(\\d+)?"
     'captures':
       '1':
@@ -162,7 +162,7 @@
         'name': 'entity.name.tag.siteini'
   }
   {
-    # specials (include/exlcude)block=
+    # (include/exlcude)block=
     'match': "(includeblock|excludeblock)\\s*=\\s*((\\d+|'.+?')(/\\d+|/'.+?')?|\".+?\")"
     'captures':
       '1':
@@ -173,14 +173,14 @@
         'name': 'entity.name.tag.siteini'
   }
   {
-    # specials pattern(episodesystem)=
+    # pattern(episodesystem)=
     'match': "('(S|St|E|Et|P|Pt)[01]')"
     'captures':
       '1':
         'name': 'entity.name.function.siteini'
   }
   {
-    # specials scope= as argument
+    # scope= as argument
     'match': '(scope)\\s*=\\s*(urlindex|datelogo|splitindex|indexshowdetails|showdetails|showsubdetails|channellist)'
     'captures':
       '1':
@@ -189,7 +189,7 @@
         'name': 'entity.name.function.siteini'
   }
   {
-    # specials scope.range
+    # scope.range
     'match': '(scope\\.range)\\s*\{\\s*\\((urlindex|datelogo|splitindex|indexshowdetails|showdetails|showsubdetails|channellist)\\s*\\)\\s*\\|\\s*(end|\\d+)\\s*\}'
     'captures':
       '1':
@@ -200,10 +200,26 @@
         'name': 'entity.name.tag.siteini'
   }
   {
-    # specials end_scope or end_scope.range
+    # end_scope or end_scope.range
     'match': '(end_scope(\\.range)?)'
     'captures':
       '1':
+        'name': 'entity.name.tag.siteini'
+  }
+  {
+    # debug as argument
+    'match': '(debug)'
+    'captures':
+      '1':
+        'name': 'entity.name.tag.siteini'
+  }
+  {
+    # debug stand alone
+    'match': '\\.(modify)(\\s*\{\\s*\\((debug)\\))'
+    'captures':
+      '1':
+        'name': 'entity.name.type.siteini'
+      '3':
         'name': 'entity.name.tag.siteini'
   }
 ]


added model listener for active pane change

--- bokehjs/src/coffee/widget/tabs.coffee
+++ bokehjs/src/coffee/widget/tabs.coffee
@@ -15,6 +15,8 @@
     super(options)
     @views = {}
     @render()
+    @listenTo @model, 'change:active', (model, value) =>
+      @$el.find('li:eq(' + value + ') a').tab('show')

   render: () ->
     for own key, val of @views


Removed unused class

Removed unused class

--- models/topicDataInMemory.coffee
+++ models/topicDataInMemory.coffee
@@ -1,113 +0,0 @@
-{TopicMeta}  = require './topicMeta'
-
-# This version of TopicData stores data in memory and
-# it's only good as a mock for unit tests. This class
-# is NOT meant to be used in production.
-#
-# This class handles saving and retrieving of topic
-# data. Notice that this class does NOT perform any
-# validation, it trusts that the caller passes only
-# good data.
-
-class TopicDataInMemory
-
-  @topics = null
-  @contents = null
-  @nextId = null
-
-  constructor: ->
-    @topics = []
-    @contents = []
-    @nextId = null
-
-
-  _loadAll: =>
-    console.log "-- In memory version --"
-    if @topics.length is 0
-      for i in [1..3]
-        topic = new TopicMeta()
-        topic.id = i
-        topic.title = "topic #{i}"
-        topic.url = "topic-#{i}"
-        topic.summary = "topic #{i} summary"
-        topic.createdOn = new Date("Jan #{i}, 2012")
-        topic.updatedOn = new Date("Feb #{i}, 2012")
-        topic.postedOn = new Date("March #{i}, 2012")
-        @topics.push topic
-        @contents.push "content for topic #{i}"
-      @nextId = 4
-    @topics
-
-
-  getAll: =>
-    return @_loadAll()
-
-
-  getRecent: =>
-    return @_loadAll().slice(0,2)
-
-
-  getNew: =>
-    meta = new TopicMeta()
-    return {meta: meta, content: ""}
-
-
-  findMeta: (id) =>
-    topics = @_loadAll()
-    for topic in topics
-      if topic.id is id
-        return topic
-    return null
-
-
-  findMetaByUrl: (url) =>
-    topics = @_loadAll()
-    for topic in topics
-      if topic.url is url
-        return topic
-    return null
-
-
-  loadContent: (meta, callback) =>
-    process.nextTick =>
-      content = @contents[meta.id-1]
-      if content
-        callback null, {meta: meta, content: content}
-      else
-        callback "Invalid ID #{meta.id}"
-
-
-  updateMeta: (id, newMeta) =>
-    topic = @findMeta(id)
-    return null if topic is null
-    topic.title = newMeta.title
-    topic.url = newMeta.url
-    topic.summary = newMeta.summary
-    topic.createdOn = newMeta.createdOn
-    topic.updatedOn = newMeta.updatedOn
-    topic.postedOn = newMeta.postedOn
-    return topic
-
-
-  updateContent: (meta, content, callback) =>
-    maxId = @nextId-1
-    if meta.id? and meta.id in [1..maxId]
-      process.nextTick =>
-        @contents[meta.id] = content
-        callback null, {meta: meta, content: content}
-    else
-      process.nextTick =>
-        callback "Invalid id #{meta.id}"
-
-
-  addNew: (meta, content, callback) =>
-    @_loadAll() # Forces @nextId to be initialized
-
-    meta.id = @nextId
-    @topics.push meta
-    process.nextTick =>
-      @contents.push content
-      @nextId++
-      callback null, {meta: meta, content: content}
-
-exports.TopicData = TopicDataInMemory


Improving validation for Direct Message creation

--- server/methods/createDirectMessage.coffee
+++ server/methods/createDirectMessage.coffee
@@ -8,7 +8,7 @@
 		me = Meteor.user()

 		if me.username is username
-			return
+			throw new Meteor.Error('invalid-user', "[methods] createDirectMessage -> Invalid target user")

 		to = RocketChat.models.Users.findOneByUsername username


Update HoC survey mail end date to a string

--- server/trial_requests/TrialRequest.coffee
+++ server/trial_requests/TrialRequest.coffee
@@ -49,7 +49,7 @@
         address: doc.get('properties')?.email
       email_id: sendwithus.templates.teacher_free_trial_hoc
       email_data:
-        endDate: endDate
+        endDate: endDate.toDateString()
     sendwithus.api.send emailParams, (err, result) =>
       log.error "sendwithus trial request approved error: #{err}, result: #{result}" if err


Refactor hints; no need to guard against no rect.

- The check for whether a rect is defined is only used in one of the
  three cases.  So we don't need it.
- Also, better veriable name.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -393,7 +393,7 @@
     else if userMightOverType
       # Block keyboard events while the user is still typing.  The intention is to prevent the user from
       # inadvertently launching Vimium commands when (over-)typing the link text.
-      new TypingProtector 200, linkMatched?.rect, linkActivator
+      new TypingProtector 200, linkMatched.rect, linkActivator
     else
       DomUtils.flashRect linkMatched.rect
       linkActivator()
@@ -633,24 +633,22 @@
   constructor: (delay, rect, callback) ->
     @timer = Utils.setTimeout delay, => @exit()

-    handler = (event) =>
+    resetExitTimer = (event) =>
       clearTimeout @timer
       @timer = Utils.setTimeout delay, => @exit()

     super
       name: "hint/typing-protector"
       suppressAllKeyboardEvents: true
-      keydown: handler
-      keypress: handler
-
-    if rect
-      # We keep a "flash" overlay active while the user is typing; this provides visual feeback that something
-      # has been selected.
-      flashEl = DomUtils.addFlashRect rect
-      @onExit -> DomUtils.removeElement flashEl
+      keydown: resetExitTimer
+      keypress: resetExitTimer

     @onExit callback

+    # We keep a "flash" overlay active while the user is typing; this provides visual feeback that something
+    # has been selected.
+    flashEl = DomUtils.addFlashRect rect
+    @onExit -> DomUtils.removeElement flashEl

 class WaitForEnter extends Mode
   constructor: (rect, callback) ->


modify default options

modify default options

--- src/core.coffee
+++ src/core.coffee
@@ -12,9 +12,9 @@

   opts:
     textarea: null
-    placeholder: false
+    placeholder: ''
     defaultImage: 'images/image.png'
-    params: null
+    params: {}
     upload: false
     tabIndent: true


Change format of audio/video packet

Change format of audio/video packet

Insert packet type (1 byte).

--- server.coffee
+++ server.coffee
@@ -263,30 +263,58 @@
 lastAudioStatsTime = null

 onReceiveVideoBuffer = (buf) ->
+  packetType = buf[0]
   # Using own format
   # pts (6 bytes)
   # NAL unit (remaining)
-  pts = buf[0] * 0x010000000000 + \
-        buf[1] * 0x0100000000   + \
-        buf[2] * 0x01000000     + \
-        buf[3] * 0x010000       + \
-        buf[4] * 0x0100         + \
-        buf[5]
-  nalUnit = buf.slice 6
-  onReceiveVideoPacket nalUnit, pts
+  pts = buf[1] * 0x010000000000 + \
+        buf[2] * 0x0100000000   + \
+        buf[3] * 0x01000000     + \
+        buf[4] * 0x010000       + \
+        buf[5] * 0x0100         + \
+        buf[6]
+  if packetType is 0x01  # PTS == DTS
+    nalUnit = buf[7..]
+    dts = pts
+  else if packetType is 0x02
+    dts = buf[7]  * 0x010000000000 + \
+          buf[8]  * 0x0100000000   + \
+          buf[9]  * 0x01000000     + \
+          buf[10] * 0x010000       + \
+          buf[11] * 0x0100         + \
+          buf[12]
+    nalUnit = buf[13..]
+  else
+    console.error "video data error: unknown packet type: #{packetType}"
+    return
+  onReceiveVideoPacket nalUnit, pts, dts

 onReceiveAudioBuffer = (buf) ->
+  packetType = buf[0]
   # Using own format
   # pts (6 bytes)
   # Access Unit == raw_data_block (remaining)
-  pts = buf[0] * 0x010000000000 + \
-        buf[1] * 0x0100000000   + \
-        buf[2] * 0x01000000     + \
-        buf[3] * 0x010000       + \
-        buf[4] * 0x0100         + \
-        buf[5]
-  adtsFrame = buf.slice 6
-  onReceiveAudioPacket adtsFrame, pts
+  pts = buf[1] * 0x010000000000 + \
+        buf[2] * 0x0100000000   + \
+        buf[3] * 0x01000000     + \
+        buf[4] * 0x010000       + \
+        buf[5] * 0x0100         + \
+        buf[6]
+  if packetType is 0x01  # PTS == DTS
+    adtsFrame = buf[7..]
+    dts = pts
+  else if packetType is 0x02
+    dts = buf[7]  * 0x010000000000 + \
+          buf[8]  * 0x0100000000   + \
+          buf[9]  * 0x01000000     + \
+          buf[10] * 0x010000       + \
+          buf[11] * 0x0100         + \
+          buf[12]
+    adtsFrame = buf[13..]
+  else
+    console.error "audio data error: unknown packet type: #{packetType}"
+    return
+  onReceiveAudioPacket adtsFrame, pts, dts

 if config.receiverType in ['unix', 'tcp']
   videoReceiveServer = net.createServer (c) ->


Throw error if config.receiverType is invalid

--- server.coffee
+++ server.coffee
@@ -347,6 +347,8 @@
   videoReceiveServer.name = 'VideoData'
   videoReceiveServer.on 'packet', (buf) ->
     onReceiveVideoBuffer buf[3..]
+else
+  throw new Error "unknown receiverType in config: #{config.receiverType}"

 if config.receiverType is 'unix'
   videoReceiveServer.listen config.videoReceiverPath, ->
@@ -419,6 +421,8 @@
       rtmpServer.startStream timeForVideoRTPZero
     else
       console.log "[videoControlServer] unknown data: #{buf.length} bytes"
+else
+  throw new Error "unknown receiverType in config: #{config.receiverType}"

 if config.receiverType is 'unix'
   videoControlServer.listen config.videoControlPath, ->
@@ -465,6 +469,8 @@
   audioReceiveServer.name = 'AudioData'
   audioReceiveServer.on 'packet', (buf) ->
     onReceiveAudioBuffer buf[3..]
+else
+  throw new Error "unknown receiverType in config: #{config.receiverType}"

 if config.receiverType is 'unix'
   audioReceiveServer.listen config.audioReceiverPath, ->
@@ -535,6 +541,8 @@
       rtmpServer.startStream timeForAudioRTPZero
     else
       console.log "[audioControlServer] unknown data: #{buf.length} bytes"
+else
+  throw new Error "unknown receiverType in config: #{config.receiverType}"

 if config.receiverType is 'unix'
   audioControlServer.listen config.audioControlPath, ->


Fixed server.coffee syntax error

Fixed server.coffee syntax error

--- server.coffee
+++ server.coffee
@@ -40,4 +40,4 @@
     .sendFile("proxy.liquid", root: '.')

-require('http').createServer(app).listen(process.env.PORT.process.env.IP)
+require('http').createServer(app).listen(process.env.PORT,process.env.IP)


trampoline invocation be wild (directly call [setTimeout / process.nextTick])

--- src/trampoline.coffee
+++ src/trampoline.coffee
@@ -5,25 +5,14 @@
       slices["FPS_#{rate}"] = ~~(1000 / rate)
     slices

-  procStack = []
-  timeStack = []
-
   getUnixTime = Date.now or -> +new Date()

-  invoke = ->
-    return unless procStack.length
-    current   = procStack.shift()
-    timeSlice = timeStack.shift()
+  invoke = (proc, timeSlice) ->
+    return if typeof proc isnt 'function'
     timeLimit = getUnixTime() + timeSlice
-
-    while typeof current is 'function' and getUnixTime() < timeLimit
-      current = current()
-
-    if typeof current is 'function'
-      procStack.push current
-      timeStack.push timeSlice
-
-    pp.defer invoke
+    while typeof proc is 'function' and getUnixTime() < timeLimit
+      proc = proc()
+    pp.defer invoke, proc, timeSlice
     return

   limitTimeSlice = (timeSlice) ->
@@ -37,14 +26,12 @@
     requireLength = fn.length
     partialized = (args...) ->
       if requireLength <= args.length
-        procStack.push ->
-          fn.apply null, args.slice 0, requireLength
-        timeStack.push limitTimeSlice(
-          if requireLength < args.length
-            args[requireLength]
-          else 0
-        )
-        pp.defer invoke if procStack.length is 1
+        proc = fn.apply null, args.slice 0, requireLength
+        timeSlice =
+          limitTimeSlice if requireLength < args.length
+          then args[requireLength]
+          else null
+        pp.defer invoke, proc, timeSlice
         return

       apply = (adds...) ->


If a rule gives an error, remove it and try again

--- src/py-wtf.coffee
+++ src/py-wtf.coffee
@@ -27,24 +27,44 @@
   rules = [
     "find %s -type f -name '*.py' -exec grep -i 'fixme' -- {} +"
     "find %s -type f -name '*.py' -exec grep 'lambda' -- {} + | grep reduce"
+    "find %s -type f -name '*.py' -exec grep 'lambda' -- {} + | grep filter"
+    "find %s -type f -name '*.py' -exec grep 'lambda' -- {} + | grep collect"
+    "find %s -type f -name '*.py' -exec grep 'partial' -- {} + | grep reduce"
+    "find %s -type f -name '*.py' -exec grep 'partial' -- {} + | grep filter"
+    "find %s -type f -name '*.py' -exec grep 'partial' -- {} + | grep collect"
     "find %s -type f -name '*.py' -exec grep '<<' -- {} + | grep '&'"
     "find %s -type f -name '*.py' -exec grep '<<' -- {} + | grep '|'"
     "find %s -type f -name '*.py' -exec grep '>>' -- {} + | grep '&'"
     "find %s -type f -name '*.py' -exec grep '>>' -- {} + | grep '|'"
   ]

+  remove_rule = (index) ->
+    # Remove the rule at the given index and return the array
+    if index == 0?
+      return rules[1..]
+    else if index == rules.length?
+      return rules[0..index-1]
+    return rules[0...index].concat(rules[index+1..])
+
+  random_wtf = (res) ->
+    if not rules?
+      return res.reply "Good job - the code looks clean"
+
+    index = Math.floor(Math.random() * rules.length)
+    cmd = util.format(rules[index], working_dir)
+    exec cmd, (err, stdout, stderr) ->
+      if err?
+        rules = remove_rule index
+        random_wtf res
+      else
+        items = (item for item in stdout.split("\n") when item.length > 0)
+        res.reply res.random items
+
   robot.respond /pywtf random/i, (res) ->
     if not working_dir?
       return res.reply "You need to set the working directory!"
+    random_wtf res

-    rule = rules[ Math.floor(Math.random() * rules.length) ]
-    cmd = util.format(rule, working_dir)
-    robot.logger.info(cmd)
-    exec cmd, (err, stdout, stderr) ->
-      if err?
-        return res.reply util.format("Error executing command: %s", err)
-      else
-        return res.reply res.random stdout.split("\n")

   robot.respond /pywtf bomb( (\d+))?/i, (res) ->
     count = res.match[2] || 5


Add empty callback to fs.writeFile calls

Closes atom/atom#4676

--- src/cson.coffee
+++ src/cson.coffee
@@ -16,7 +16,7 @@
     fs.writeFileSync(cachePath, JSON.stringify(object))

 writeCacheFile = (cachePath, object) ->
-  fs.writeFile(cachePath, JSON.stringify(object))
+  fs.writeFile cachePath, JSON.stringify(object), ->

 parseObject = (objectPath, contents) ->
   if path.extname(objectPath) is '.cson'
@@ -93,6 +93,8 @@
         parseContents(objectPath, null, contents, callback)

   writeFile: (objectPath, object, callback) ->
+    callback ?= ->
+
     try
       contents = @stringifyPath(objectPath, object)
     catch error


jcarouth gets it

jcarouth gets it

--- src/rabbot.coffee
+++ src/rabbot.coffee
@@ -21,6 +21,7 @@
   'dcousineau': 'He is one with the brisket.'
   'MANCHUCK': 'As the The secretary of WurstCon, he gets it '
   'GeeH': 'Are you freaking kidding me?'
+  'jcarouth': 'It is decidedly so.'
 }

 maxims = [


Adiciona username no cliente para login

Adiciona username no cliente para login

--- Operador-Interno/server/methods/cliente.coffee
+++ Operador-Interno/server/methods/cliente.coffee
@@ -11,6 +11,7 @@

 		Meteor.users.insert
 			_id: data.cpf
+			username: data.cpf
 			role: 'client'
 			profile:
 				nome: data.nome


Fix size bug as the svg is created on flame graph construction

--- src/d3-flame-graph.coffee
+++ src/d3-flame-graph.coffee
@@ -89,7 +89,6 @@
     constructor: (selector, root) ->
       @_selector = selector
       @_generateAccessors([
-        'size',
         'margin',
         'cellHeight',
         'zoomEnabled',
@@ -99,7 +98,7 @@
       @_ancestors = []

       # defaults
-      @_size        = [1200, 600]
+      @_size        = [1200, 800]
       @_cellHeight  = 10
       @_margin      = { top: 0, right: 0, bottom: 0, left: 0 }
       @_color       = (d) ->
@@ -127,6 +126,14 @@
       @original = d3.flameGraphUtils.augment(root, '0')
       console.timeEnd('augment')
       @root(@original)
+
+    size: (size) ->
+      return @_size if not size
+      @_size = size
+      d3.select(@_selector).select('.flame-graph')
+        .attr('width', @_size[0])
+        .attr('height', @_size[1])
+      @

     root: (root) ->
       return @_root if not root


use upper case to make letter in word read easily

--- script.coffee
+++ script.coffee
@@ -52,10 +52,8 @@
         @repr.css('left', @left)

     update: ->
-        if @remain == @full
-            @repr.html($('<b>').html(@full))
-        else
-            @repr.html([$('<u>').html(@done()), $('<b>').html(@remain)])
+        @repr.html([ $('<u>').html(@done().toUpperCase()),
+                     $('<b>').html(@remain.toUpperCase()) ])

     reset: ->
         @remain = @full


controller pin read/write refactor

controller pin read/write refactor

--- grovepi.coffee
+++ grovepi.coffee
@@ -27,25 +27,49 @@
 		wire = new i2c @address, device: "/dev/i2c-1"
 		@wire = wire  if debug.mode

-	analog:
-		write: (byte, callback) ->
-			wire.writeByte CMD.analog.write, byte, callback
-		read: (callback) ->
-			wire.writeBytes CMD.analog.read, 0, ->
-				wire.readByte (err, byte) -> callback byte
+	write: ( type, data, callback ) ->
+		switch type
+			when 'digital'
+				writeCmd = if Array.isArray data then 'writeBytes' else 'writeByte'
+				wire[writeCmd] CMD.digital.write, data, ( error ) ->
+					callback()
+					debug.log "Digital write", CMD.digital.write, data
+					debug.log "ERROR:", error if error
+			when 'analog'
+				wire.writeByte CMD.analog.write, data,  ( error ) ->
+					callback()
+					debug.log "Analog write", CMD.analog.write, data
+					debug.log "ERROR:", error if error
+	read: ( type, callback ) ->
+		switch type
+			when 'digital'
+				isBlock = true
+				readCmd = if isBlock then 'readBytes' else 'readByte'
+				wire.writeBytes CMD.digital.read, 0, ->
+					wire[readCmd] (error, data) ->
+						callback data
+						debug.log "Digital read", CMD.digital.read, data
+						debug.log "ERROR:", error if error
+			when 'analog'
+				wire.writeBytes CMD.analog.read, 0, ->
+					wire.readByte (err, data) ->
+						callback data
+						debug.log "Analog read", CMD.analog.read, data
+						debug.log "ERROR:", error if error
+
+

 	digital:
 		write: (bytes, callback) ->
-			writeCmd = if Array.isArray bytes then 'writeBytes' else 'writeByte'
-			wire[writeCmd] CMD.digital.write, bytes, ( error ) ->
-				callback()
-				debug.log "Digital write", CMD.digital.write, bytes
-				debug.log "ERROR:", error if error
+			@write 'digital', bytes, callback
 		read: (isBlock, callback) ->
-			readCmd = if isBlock then 'readBytes' else 'readByte'
-			wire.writeBytes CMD.digital.read, 0, ->
-				wire[readCmd] (err, byte) -> callback byte
-				debug.log "Digital read", CMD.digital.read, byte
+			@read 'digital', callback
+
+	analog:
+		write: (byte, callback) ->
+			@write 'analog', byte, callback
+		read: (callback) ->
+			@read 'analog', callback

 	input: (callback) ->
 		wire.writeByte CMD.mode, 0, -> # Switch to input


auto pin mode

auto pin mode

--- grovepi.coffee
+++ grovepi.coffee
@@ -27,7 +27,10 @@
 		output: 1
 	}

-	getCmd = (id) -> cmd  if cmdId is id  for cmdId, cmd  of CMD
+	getCmd = (id) ->
+		for cmd, cmdId of CMD
+			if cmdId is id
+				return cmd

 	constructor: (@address, @id) ->


auto pin mode

auto pin mode

--- grovepi.coffee
+++ grovepi.coffee
@@ -75,25 +75,24 @@

 	receive: ( port, args... ) ->
-		@mode port, MODES.input, ->
-			readArgs = []
-			if args[1]
-				length = args[0]
-				callback = args[1]
-				readArgs.push port
-				readArgs.push length
-				readCmd = 'readBytes'
-			else
-				callback = args[0]
-				readCmd = 'readByte'
+		readArgs = []
+		if args[1]
+			length = args[0]
+			callback = args[1]
+			readArgs.push port
+			readArgs.push length
+			readCmd = 'readBytes'
+		else
+			callback = args[0]
+			readCmd = 'readByte'

-			debug.log 'Receive from port', port
-			readArgs.push (error, data) ->
-				callback data
-				debug.log 'Received', (Array.prototype.slice.call data, 0)
-				debug.log 'ERROR:', error  if error
+		debug.log 'Receive from port', port
+		readArgs.push (error, data) ->
+			callback data
+			debug.log 'Received', (Array.prototype.slice.call data, 0)
+			debug.log 'ERROR:', error  if error

-			wire[readCmd] readArgs...
+		wire[readCmd] readArgs...


Remove low-value controller tests

Remove low-value controller tests

--- server/test/controllers/deploy.coffee
+++ server/test/controllers/deploy.coffee
@@ -1,94 +0,0 @@
-assert = require('chai').assert
-helpers = require '../helpers'
-request = require('request')
-fs = require('fs')
-sinon = require('sinon')
-Q = require('q')
-
-CommandRunner = require('../../bin/command-runner')
-AppConfig = require('../../initializers/config')
-DeployController = require('../../controllers/deploy')
-Deploy = require('../../lib/deploy')
-GITHUB_IP = "192.30.252.0"
-
-suite('Deploy Controller')
-
-test('.index calls Deploy.deploy when receiving a request with an
- x_real_ip from github, it deploys', (done) ->
-  sandbox = sinon.sandbox.create()
-
-  ref = 'some-tag'
-
-  updateCodeStub = sandbox.stub(Deploy, 'deploy', ->
-    then: ->
-      catch: ->
-  )
-  sandbox.stub(AppConfig, 'get', ->
-    ref
-  )
-
-  fakeReq =
-    headers:
-      "x-real-ip": GITHUB_IP
-    body:
-      ref: ref
-
-  fakeRes =
-    send: (responseCode, message) ->
-      console.log "In fake res.send"
-      try
-        assert.strictEqual(responseCode, 200,
-          "Expected the request to succeed")
-
-        assert.strictEqual 1, updateCodeStub.callCount,
-          "Expected Deploy.deploy to be called once"
-
-        done()
-      catch err
-        done(err)
-      finally
-        sandbox.restore()
-
-  DeployController.index(fakeReq, fakeRes)
-)
-
-test('.index calls Deploy.deploy when receiving a request without
- the x_real_ip (nginx) header, but a github IP in
- req.connection.remoteAddress', (done) ->
-  sandbox = sinon.sandbox.create()
-
-  ref = 'some-tag'
-
-  updateCodeStub = sandbox.stub(Deploy, 'deploy', ->
-    then: ->
-      catch: ->
-  )
-  sandbox.stub(AppConfig, 'get', ->
-    ref
-  )
-
-  fakeReq =
-    body:
-      ref: ref
-    headers: {}
-    connection:
-      remoteAddress: GITHUB_IP
-
-  fakeRes =
-    send: (responseCode, message) ->
-      console.log "In fake res.send"
-      try
-        assert.strictEqual(responseCode, 200,
-          "Expected the request to succeed")
-
-        assert.strictEqual 1, updateCodeStub.callCount,
-          "Expected Deploy.deploy to be called once"
-
-        done()
-      catch err
-        done(err)
-      finally
-        sandbox.restore()
-
-  DeployController.index(fakeReq, fakeRes)
-)


Remove unused var

Remove unused var

--- select.coffee
+++ select.coffee
@@ -5,8 +5,6 @@
 SPACE = 32
 UP = 38
 DOWN = 40
-
-currentFocusedSelect = undefined

 lastKeysPressed = ''
 lastKeysTimeout = undefined


exceptional naming

exceptional naming

--- server.coffee
+++ server.coffee
@@ -23,7 +23,7 @@
   else
     url = Url.parse req.url

-    four_oh_hour = (msg) ->
+    four_oh_four = (msg) ->
       log msg
       resp.writeHead 404, { }
       resp.write "Not Found"
@@ -54,7 +54,7 @@
           src = Http.createClient url.port || 80, url.hostname

           src.on 'error', (error) ->
-            four_oh_hour("Client Request error #{error.stack}")
+            four_oh_four("Client Request error #{error.stack}")

           query_path = url.pathname
           if url.query?
@@ -72,7 +72,7 @@
             content_length  = srcResp.headers['content-length']

             if(content_length > 5242880)
-              four_oh_hour("Content-Length exceeded")
+              four_oh_four("Content-Length exceeded")
             else
               newHeaders =
                 'expires'                : srcResp.headers['expires']
@@ -90,7 +90,7 @@
               switch srcResp.statusCode
                 when 200
                   if srcResp.statusCode == 200 && newHeaders['content-type'].slice(0, 5) != 'image'
-                    four_oh_hour("Non-Image content-type returned")
+                    four_oh_four("Non-Image content-type returned")

                   log newHeaders

@@ -102,7 +102,7 @@
                   resp.writeHead srcResp.statusCode, newHeaders

                 else
-                  four_oh_hour("Responded with #{srcResp.statusCode}")
+                  four_oh_four("Responded with #{srcResp.statusCode}")

           srcReq.on 'error', ->
             resp.end()
@@ -110,11 +110,11 @@
           srcReq.end()

         else
-          four_oh_hour("No host found")
+          four_oh_four("No host found")
       else
-        four_oh_hour("checksum mismatch")
+        four_oh_four("checksum mismatch")
     else
-      four_oh_hour("No pathname provided on the server")
+      four_oh_four("No pathname provided on the server")

 console.log "SSL-Proxy running on #{port} with pid:#{process.pid}."
 console.log "Using the secret key #{shared_key}"


Install indexes for subdocument IDs.

Install indexes for subdocument IDs.

--- server.coffee
+++ server.coffee
@@ -66,6 +66,16 @@
 # We have to modify prototype directly because there are classes which already inherit from the class
 # and we cannot just override the class as we are doing for other server-side only methods.
 globals.Document._TargetedFieldsObservingField::_setupTargetObservers = (updateAll) ->
+  unless updateAll
+    index = {}
+    index["#{ @sourcePath }._id"] = 1
+    @sourceCollection._ensureIndex index
+
+    if @reverseName
+      index = {}
+      index["#{ @reverseName }._id"] = 1
+      @targetCollection._ensureIndex index
+
   initializing = true

   observers =
@@ -87,7 +97,7 @@
   handle.stop() if updateAll

 # Cannot use => here because we are not in the globals.Document._Trigger context.
-# We are mofiying prototype directly to match code style of
+# We are modifying prototype directly to match code style of
 # _TargetedFieldsObservingField::_setupTargetObservers but in this case it is not
 # really needed, because there are no already existing classes which would inherit
 # from globals.Document._Trigger.
@@ -423,6 +433,7 @@
   @_setupSourceObservers: (updateAll) ->
     return if _.isEmpty @Meta.fields

+    indexes = []
     sourceFields =
       _id: 1 # To make sure we do not pass empty set of fields

@@ -430,10 +441,17 @@
       for name, field of obj
         if field instanceof globals.Document._ObservingField
           sourceFields[field.sourcePath] = 1
+          index = {}
+          index["#{ field.sourcePath }._id"] = 1
+          indexes.push index
         else if field not instanceof globals.Document._Field
           sourceFieldsWalker field

     sourceFieldsWalker @Meta.fields
+
+    unless updateAll
+      for index in indexes
+        @Meta.collection._ensureIndex index

     initializing = true


Simplified code for reverse fields.

Simplified code for reverse fields.

--- server.coffee
+++ server.coffee
@@ -247,7 +247,7 @@

       unless target
         Log.error "Document's '#{ id }' field '#{ @sourcePath }' is referencing a nonexistent document '#{ value._id }'"
-        # TODO: Should we call reference.removeSource here?
+        # TODO: Should we call reference.removeSource here? And remove from reverse fields?
         return

       # We omit _id because that field cannot be changed, or even $set to the same value, but is in target
@@ -255,15 +255,10 @@

     return unless @reverseName

-    # TODO: Current code is run too many times, for any update of source collection reverse field is updated, which is big overkill for the most common case of maintaining only the list of IDs
-    # TODO: Probably only existence (adding) of IDs in the reverse field should be maintained here and the content of any additional fields will be taken care by Meta._reverseFields anyway
-
-    reverseFields = fieldsToProjection @reverseFields
-    source = @sourceCollection.findOne id,
-      fields: reverseFields
-      transform: null
-
-    # TODO: What if returned source is null, document does not exist (anymore)? Should we then call try to remove the ID?
+    # TODO: Current code is run too many times, for any update of source collection reverse field is updated
+
+    # We just add the ID to the reverse field array and leave for any additional fields
+    # (@reverseFields) to be added by reference fields configured through Meta._reverseFields

     selector =
       _id: value._id
@@ -271,7 +266,8 @@
       $ne: id

     update = {}
-    update[@reverseName] = source
+    update[@reverseName] =
+      _id: id

     @targetCollection.update selector,
       $addToSet: update


fix issue with chat message retrieval

fix issue with chat message retrieval

--- server.coffee
+++ server.coffee
@@ -204,7 +204,7 @@
       res.send {message: 'OK'}, 200

 app.get '/messages/:channel', (req, res) ->
-  db.collection('messages').find({channel: req.params.channel}).sort(date: 1).limit(50).toArray (err, data) ->
+  db.collection('messages').find({channel: req.params.channel}).sort(date: -1).limit(100).toArray (err, data) ->
     throw err if err
     res.json(200, data)


Add sh-bang to server.coffee and chmod +x

--- server.coffee
+++ server.coffee
@@ -1,3 +1,4 @@
+#!env coffee
 require 'coffee-script/register'
 {full: version} = require './lib/version'


added output for new TCP connection event

--- src/zombie/resources.coffee
+++ src/zombie/resources.coffee
@@ -584,6 +584,7 @@
       httpRequest.port = prxy.split(':')[1]

     #console.log JSON.stringify(httpRequest,null,'\t')
+    #console.log request.url
     req = HTTP.request httpRequest
     req.on "response", (response)->
       #console.log response.statusCode
@@ -605,6 +606,9 @@
     req.on "push", (push)=>
       console.log 'PUSH REQUEST FOR '+push.url

+    req.on "newConnection", ()=>
+      console.log 'NEW TCP CONNECTION'
+
     req.on "error", (error)=>
       #console.log error
       if error


Set width to 'auto' explicitly

Set width to 'auto' explicitly

--- app/assets/javascripts/intro.coffee
+++ app/assets/javascripts/intro.coffee
@@ -56,7 +56,7 @@
     dropdownParent: $('#area-select-options'),
     dropdownAutoWidth : true
   )
-  sYearSelect.select2(minimumResultsForSearch: -1, dropdownAutoWidth : true)
+  sYearSelect.select2(minimumResultsForSearch: -1, dropdownAutoWidth : true, width: 'auto')

   areaOnChange = ->
     earliest = areaSelect.find(':selected').data('earliest')


fixed weird bug in add_item

fixed weird bug in add_item

--- commands/add_item.coffee
+++ commands/add_item.coffee
@@ -3,15 +3,20 @@

 module.exports = (character, item, count, cb) ->
   has_some = _.some character.items, (i) ->
+    console.log "#{i.item} is #{item.id}"
     i.item is item.id
+  console.log has_some
   if has_some
+    console.log 1
     query =
       'items.item': item.id
     update =
       $inc:
         'items.$.count': count
         weight: item.weight * count
+    db.characters.update query, update, false, true, cb
   else
+    console.log 2
     query =
       _id: character._id
     update =
@@ -21,4 +26,4 @@
         items:
           item: item.id
           count: count
-  db.characters.update query, update, cb
+    db.characters.update query, update, false, true, cb


Remove duplicate coffeescript

Remove duplicate coffeescript

--- app/assets/javascripts/users.coffee
+++ app/assets/javascripts/users.coffee
@@ -1,25 +1,3 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
-###
-# Welcome to the new js2coffee 2.0, now
-# rewritten to use the esprima parser.
-# try it out!
-###
-$('#formValidate').validate
-  rules:
-    username:
-      required: "true"
-      minlength: "5"
-    email:
-      required: "true"
-      email: "true"
-    password:
-      required: "true"
-      minlength: "5"
-    confirmpassword:
-      required: "true"
-      minlength: "5"
-      equalTo: '#password'
-# ---
-# generated by js2coffee 2.2.0


logging. bugfix

logging. bugfix

--- app.coffee
+++ app.coffee
@@ -43,6 +43,7 @@

 platformMap = {
+  "darwin_x64": "darwin_x64"
   "darwin": "darwin_x64"
   "osx": "darwin_x64"
   "windows_64": "windows_x64"
@@ -72,8 +73,6 @@
         return

       latest = versions[0]
-      console.log latest
-      console.log req.query
       if ! semver.valid(req.query.version)
         res.status(400)
         cleanVersion = semver.clean(req.query.version)
@@ -83,7 +82,6 @@
         res.send "Invalid version: #{req.query.version}." + suggestion
       else if semver.lt(req.query.version, latest.version)
         data = formatVersion latest, req.query.platform
-        console.log data
         res.json data
       else
         res.status(204)


Use parallel, not series

Use parallel, not series

--- app.coffee
+++ app.coffee
@@ -152,7 +152,7 @@
       user.send('register error', errors)
       return

-    async.series
+    async.parallel
       emailExists: (done) ->
         db.sismember("users", email, done)
       usernameExists: (done) ->


Turn off memory monitoring.

Turn off memory monitoring.

--- app.coffee
+++ app.coffee
@@ -10,7 +10,7 @@
 Metrics = require "metrics-sharelatex"
 Metrics.initialize("clsi")
 Metrics.open_sockets.monitor(logger)
-Metrics.memory.monitor(logger)
+#Metrics.memory.monitor(logger)

 ProjectPersistenceManager = require "./app/js/ProjectPersistenceManager"
 OutputCacheManager = require "./app/js/OutputCacheManager"


fixbug in csrf token

fixbug in csrf token

--- app.coffee
+++ app.coffee
@@ -90,13 +90,13 @@

       resave: true
       saveUninitialized: true
-      secret: fs.readFileSync path.join __dirname, 'session.key'
+      secret: fs.readFileSync(path.join __dirname, 'session.key').toString()

     app.use (req, res, next) ->
       unless req.session.csrf_secret
         csrf.secret (err, secret) ->
           req.session.csrf_secret = secret
-          req.session.csrf_token = csrf.token secret
+          req.session.csrf_token = csrf.create secret
           next()

       next()


improve logging by avoiding string escapes

--- app.coffee
+++ app.coffee
@@ -7,16 +7,18 @@

 # log updates as truncated strings
 truncateFn = (updates) ->
-		JSON.stringify updates, (key, value) ->
-			if typeof value == 'string' && (len = value.length) > 80
-				return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32)
-			else
-				return value
+		JSON.parse(
+			JSON.stringify updates, (key, value) ->
+				if typeof value == 'string' && (len = value.length) > 80
+					return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32)
+				else
+					return value
+		)

 TrackChangesLogger.addSerializers {
+	rawUpdate: truncateFn
 	rawUpdates: truncateFn
 	newUpdates: truncateFn
-	rawUpdate: truncateFn
 	lastUpdate: truncateFn
 }


Update less-middleware usage

Update less-middleware usage

--- app.coffee
+++ app.coffee
@@ -38,7 +38,7 @@
   app.use(express.bodyParser()); #retrieve json body
   app.use(express.methodOverride());
   app.use(app.router);
-  app.use(require('less-middleware')({ src: __dirname + '/public' }));
+  app.use(require('less-middleware')(path.join(__dirname, 'public')));
   app.use(express.static(path.join(__dirname, 'public')));
   app.use('/shared', express.static(path.join(__dirname, 'shared')));


runner: Use new convenience runAll() function

Still has hardcoded test/runtime setup inside though

--- src/runner.coffee
+++ src/runner.coffee
@@ -119,47 +119,28 @@
 main = () ->
   subprocess = require './subprocess'

-  runSuite = (runner, suite) ->
-    chai = require 'chai'
-
-    runner.setupSuite suite, (err) ->
-      return err if err
-
-      testcase = suite.cases[0]
-      runner.runTest testcase, (err, received) ->
-        return err if err
-
-        console.log "  #{testcase.name}"
-        err = null
-        try
-          chai.expect(received).to.eql testcase.expect
-        catch e
-          err = e
-        console.log "    #{testcase.assertion}:", if not e then '✓' else "✗\n #{e.message}"
-
-        runner.teardownSuite suite, (err) ->
-          return err if err
-
-  suites = testsuite.getSuitesSync './spec/fixtures/ToggleAnimation.yaml'
-  suite = suites[0]
-
   # FIXME: accept commandline arguments for this information
   # - runtime definition. As .json file? Put command in the file too?
   # - list of files with test suites to run. Enumeration of files, or directory?

   # TODO: add options for collecting test suites from FBP protocol component listing

+  suites = testsuite.getSuitesSync './examples/simple-passing.yaml'
+
   def =
     label: "MicroFlo Simulator"
     description: "The first component in the world"
     type: "microflo"
     protocol: "websocket"
-    address: "ws://localhost:3333"
+    address: "ws://localhost:3569"
     secret: "microflo32"
     id: "2ef763ff-1f28-49b8-b58f-5c6a5c23af2d"
     user: "3f3a8187-0931-4611-8963-239c0dff1931"

-  command = '../microflo/microflo.js runtime --port 3333 --file ../microflo/build/emscripten/microflo-runtime.js'
+  onUpdate = () ->
+    console.log 'updated', suites
+
+  command = 'python2 protocol-examples/python/runtime.py'
   options = {}
   subprocess.start command, options, (err) ->
     debug 'started', command, err
@@ -168,11 +149,13 @@
     runner.connect (err) ->
       throw err if err

-      runSuite runner, suite, (err) ->
+      runAll runner, suites, onUpdate, (err) ->
+        throw err if err
+
+        runner.disconnect (err) ->
           throw err if err

-          runner.disconnect (err) ->
-            throw err if err
+          process.exit 0

 exports.main = main


use withConvertedUserAndStackTemplate helper function

--- workers/social/lib/social/models/computeproviders/stacktemplate.test.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.test.coffee
@@ -64,32 +64,15 @@

       it 'should be able to fetch stack template data', (done) ->

-        withConvertedUser ({ client }) ->
-
-          stackTemplate     = null
-          stackTemplateData = generateStackTemplateData client
-
-          queue = [
-
-            ->
-              StackTemplate.create client, stackTemplateData, (err, template) ->
-                expect(err?.message).to.not.exist
-                stackTemplate = template
-                queue.next()
-
-            ->
-              selector = { _id : stackTemplate._id }
-              StackTemplate.some$ client, selector, (err, templates) ->
-                expect(err?.message).to.not.exist
-                expect(templates[0].title).to.be.equal stackTemplateData.title
-                expect(templates[0].template.content).to.be.deep.equal stackTemplateData.template
-                queue.next()
-
-            -> done()
-
-          ]
-
-          daisy queue
+        withConvertedUserAndStackTemplate (data) ->
+          { client, stackTemplate, stackTemplateData } = data
+          selector = { _id : stackTemplate._id }
+
+          StackTemplate.some$ client, selector, (err, templates) ->
+            expect(err?.message).to.not.exist
+            expect(templates[0].title).to.be.equal stackTemplateData.title
+            expect(templates[0].template.content).to.be.deep.equal stackTemplateData.template
+            done()

   describe '#one$()', ->
@@ -105,33 +88,15 @@

       it 'should be able to fetch stack template data', (done) ->

-        withConvertedUser ({ client }) ->
-
-          stackTemplate     = null
-          stackTemplateData = generateStackTemplateData client
-
-          queue = [
-
-            ->
-              StackTemplate.create client, stackTemplateData, (err, template) ->
-                expect(err).to.not.exist
-                stackTemplate = template
-                queue.next()
-
-            ->
-              selector = { '_id' : stackTemplate._id }
-              StackTemplate.one$ client, selector, null, (err, template) ->
-                expect(err?.message).to.not.exist
-                expect(template).to.exist
-                expect(template.title).to.be.equal stackTemplateData.title
-                expect(template.template.content).to.be.deep.equal stackTemplateData.template
-                queue.next()
-
-            -> done()
-
-          ]
-
-          daisy queue
+        withConvertedUserAndStackTemplate (data) ->
+          { client, stackTemplate, stackTemplateData } = data
+          selector = { _id : stackTemplate._id }
+
+          StackTemplate.one$ client, selector, null, (err, templates) ->
+            expect(err?.message).to.not.exist
+            expect(templates[0].title).to.be.equal stackTemplateData.title
+            expect(templates[0].template.content).to.be.deep.equal stackTemplateData.template
+            done()

   describe 'delete()', ->
@@ -148,19 +113,9 @@

       it 'should be able to delete the stack template', (done) ->

-        withConvertedUser ({ client }) ->
-
-          stackTemplate     = null
-          stackTemplateData = null
+        withConvertedUserAndStackTemplate ({ client, stackTemplate }) ->

           queue = [
-
-            ->
-              stackTemplateData = generateStackTemplateData client
-              StackTemplate.create client, stackTemplateData, (err, template_) ->
-                expect(err).to.not.exist
-                stackTemplate = template_
-                queue.next()

             ->
               stackTemplate.delete client, (err) ->
@@ -194,19 +149,9 @@

       it 'should be able to set access level of stack template', (done) ->

-        withConvertedUser ({ client }) ->
-
-          stackTemplate     = null
-          stackTemplateData = null
+        withConvertedUserAndStackTemplate ({ client, stackTemplate }) ->

           queue = [
-
-            ->
-              stackTemplateData = generateStackTemplateData client
-              StackTemplate.create client, stackTemplateData, (err, template_) ->
-                expect(err).to.not.exist
-                stackTemplate = template_
-                queue.next()

             ->
               stackTemplate.setAccess client, 'someInvalidAccessLevel', (err) ->
@@ -246,19 +191,10 @@

       it 'should be able to update the stack template', (done) ->

-        withConvertedUser ({ client }) ->
-
-          stackTemplate     = null
-          stackTemplateData = null
+        withConvertedUserAndStackTemplate (data) ->
+          { client, stackTemplate, stackTemplateData } = data

           queue = [
-
-            ->
-              stackTemplateData = generateStackTemplateData client
-              StackTemplate.create client, stackTemplateData, (err, template_) ->
-                expect(err).to.not.exist
-                stackTemplate = template_
-                queue.next()

             ->
               params = { title : 'title should be updated' }


not sure.

not sure.

--- src/fits.coffee
+++ src/fits.coffee
@@ -7,6 +7,10 @@
   @stringPattern = /'(.*)'\s*\/*(.*)/
   @principalMandatoryKeywords = ['BITPIX', 'NAXIS', 'END']
   @extensionKeywords = ['BITPIX', 'NAXIS', 'PCOUNT', 'GCOUNT']
+  @compressedImageKeywords = ['ZIMAGE', 'ZCMPTYPE', 'ZBITPIX', 'ZNAXIS', 'ZNAXISn']
+  @compressedImageKeywordsOptional =
+    ['ZTILEn', 'ZNAMEn', 'ZVALn', 'ZMASKCMP', 'ZSIMPLE', 'ZTENSION', 'ZEXTEND', 'ZBLOCKED', 'ZPCOUNT', 'ZGCOUNT', 'ZHECKSUM', 'ZDATASUM', 'ZQUANTIZ']
+
   @arrayKeywords = ['BSCALE', 'BZERO', 'BUNIT', 'BLANK', 'CTYPEn', 'CRPIXn', 'CRVALn', 'CDELTn', 'CROTAn', 'DATAMAX', 'DATAMIN']
   @otherReservedKeywords = [
     'DATE', 'ORIGIN', 'BLOCKED',


feat(categoryOrderHint): add more robust handling of orderHints

orderHint in the excel can now also have a leading space, since it gets parsed to a float, which gets casted to a string afterwards

--- src/coffee/mapping.coffee
+++ src/coffee/mapping.coffee
@@ -158,7 +158,7 @@
       else
         if catId
           # orderHint and catId are ensured to be valid
-          catOrderHints[catId] = rawOrderHint
+          catOrderHints[catId] = orderHint.toString()

     catOrderHints


improve user response messages

improve user response messages

--- src/virustotal.coffee
+++ src/virustotal.coffee
@@ -41,7 +41,7 @@
   robot.respond /virustotal url (.+)$/i, (msg) ->
     url = msg.match[1]

-    robot.logger.info "Virus total: requested url report for #{url} [#{msg.envelope.user.name}]"
+    robot.logger.info "Virus total: url report request: #{url} [#{msg.envelope.user.name}]"

     virustotal.getUrlReport url, (err, res) ->
       if err
@@ -57,7 +57,7 @@
       for scanner, obj of res.scans
         unclean.push "`#{scanner}` (#{obj.result})" if obj.detected

-      msgout = "Virus total: `#{res.resource.replace('http:\/\/','')}` rated clean by #{clean.length}.  Rated unclean by #{unclean.length}.  More information: #{res.permalink}"
+      msgout = "Virus total: `#{res.resource.replace('http:\/\/','')}` rated clean by #{clean.length}, unclean by #{unclean.length}.  More info: #{res.permalink}"
       robot.logger.info "#{msgout} [#{msg.envelope.user.name}]"
       return robot.send {room: msg.envelope.user.name}, msgout


Calculate angle correctly

Calculate angle correctly

--- src/config/turtles.coffee
+++ src/config/turtles.coffee
@@ -35,8 +35,10 @@
   v_j: 0
   angle: (world, args) ->
     # TODO: perform real triginometry
-    value = 90*(1-world.get('v_i')) #0, 90, 180, 90, 0
-    value = -1*angle if world.get('v_j') == -1
+    v_i = world.get('v_i')
+    v_j = world.get('v_j')
+    value = 90*(1-v_i) #0, 90, 180, 90, 0
+    value = -90 if world.get('v_j') < 0
     value
   path: (world, args) ->
     scale = world.get('scale') / 10


run: step after timeout

run: step after timeout

--- src/config/turtles.coffee
+++ src/config/turtles.coffee
@@ -63,9 +63,14 @@
     turtles = world.find_parent 'turtles'
     turtles.map_children (child) ->
       child.call(action['do'], action) if action?
+  interval: 1000
+  running: true
   run: (world, args) ->
-    step = world.get('step')
-    step(world, args)
+    interval = world.get('interval')
+    step = world.get_raw('step')
+    #console.log "run", world, interval, step
+    assert _.isFunction(step), "step is not a function"
+    setTimeout((() -> step(world, args)), interval)
   reset: (world, args) ->
     world.put 'program_counter', 0
     turtles = world.find_parent 'turtles'