change instances on responsive.breadcrumbs to ezBreadcrumbs on grunt build tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,7 +6,7 @@
         coffee:
             common:
                 files:
-                    'assets/js/responsive.breadcrumbs.js': 'src/coffee/responsive.breadcrumbs.coffee'
+                    'assets/js/ezBreadcrumbs.js': 'src/coffee/ezBreadcrumbs.coffee'
                     'assets/js/site.js': 'src/coffee/site.coffee'
                 options:
                     bare: true
@@ -14,13 +14,13 @@
             common:
                 files:
                     'assets/css/site.css': 'src/less/site.less'
-                    'assets/css/responsive.breadcrumbs.css': 'src/less/responsive.breadcrumbs.less'
+                    'assets/css/ezBreadcrumbs.css': 'src/less/ezBreadcrumbs.less'
         watch:
             coffee:
-                files: ['src/coffee/site.coffee', 'src/coffee/responsive.breadcrumbs.coffee']
+                files: ['src/coffee/site.coffee', 'src/coffee/ezBreadcrumbs.coffee']
                 tasks: ['coffee']
             less:
-                files: ['src/less/site.less', 'src/less/responsive.breadcrumbs.less']
+                files: ['src/less/site.less', 'src/less/ezBreadcrumbs.less']
                 tasks: ['less']
         concat:
             rwd:
@@ -59,6 +59,6 @@
     grunt.loadNpmTasks 'grunt-notify'

     # Default Tasks
-    grunt.registerTask 'default', ['concat', 'watch']
+    grunt.registerTask 'default', ['concat', 'concat_css', 'coffee', 'less', 'watch']

     grunt.task.run 'notify_hooks'


fix(Grunt): Updated grunt uglify task to report minification result

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -177,6 +177,9 @@
     clean: ["tmp"]

     uglify:
+      options:
+        report:           "gzip"
+        preserveComments: false
       dist:
         files: [
           {


Remove unused imageim grunt task

Remove unused imageim grunt task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -169,21 +169,6 @@
         files:
           "<%= core.dist %>/assets/css/main.css": ["<%= core.dist %>/assets/css/main.css"]

-    imagemin:
-      server:
-        options:
-          optimizationLevel: 0
-
-        files:
-          ".tmp/assets/img/icon.png": "<%= core.app %>/assets/img/icon.png"
-
-      dist:
-        options:
-          optimizationLevel: 7
-
-        files:
-          "<%= core.dist %>/assets/img/icon.png": "<%= core.app %>/assets/img/icon.png"
-
     uglify:
       dist:
         options:
@@ -223,10 +208,10 @@
         logConcurrentOutput: true

       server:
-        tasks: ["less:server", "coffee:server", "imagemin:server"]
-
-      dist:
-        tasks: ["htmlmin", "cssmin", "imagemin:dist", "uglify"]
+        tasks: ["less:server", "coffee:server"]
+
+      dist:
+        tasks: ["htmlmin", "cssmin", "uglify"]

   grunt.registerTask "server", ["connect:livereload", "concurrent:server", "autoprefixer:server", "watch"]
   grunt.registerTask "test", ["coffeelint", "recess"]


Fix source map URL.

Fix source map URL.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -16,17 +16,6 @@

   grunt.initConfig
     pkg: pkg_data
-    coffee:
-      source:
-        options:
-          sourceMap: true
-          bare: false
-
-        cwd: "."
-        expand: true
-        src: ["src/**/*.coffee"]
-        dest: "tmp/"
-        ext: ".js"

     browserify:
       options:
@@ -39,6 +28,7 @@
     uglify:
       options:
         sourceMap: 'dist/airbrake.min.map'
+        sourceMappingURL: 'airbrake.min.map'
         sourceMapPrefix: 1

       dist:


Added Jasmine

Added Jasmine

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,9 +22,11 @@
       output      : 'source/quo.output.coffee'
       query       : 'source/quo.query.coffee'
       style       : 'source/quo.style.coffee'
+      spec        : 'spec/*.coffee'

     concat:
       core        : files: '<%=meta.build%>/<%=meta.file%>.coffee': '<%= source.core %>'
+      spec        : files: '<%=meta.build%>/<%=meta.file%>.spec.coffee': '<%= source.spec %>'

     coffee:
       core        : files: '<%= meta.build %>/<%= meta.file %>.js': '<%=meta.build%>/<%=meta.file%>.coffee'
@@ -36,6 +38,7 @@
       output      : files: '<%= meta.build %>/<%= meta.file %>.output.js': '<%=source.output%>'
       query       : files: '<%= meta.build %>/<%= meta.file %>.query.js': '<%=source.query%>'
       style       : files: '<%= meta.build %>/<%= meta.file %>.style.js': '<%=source.style%>'
+      spec        : files: '<%= meta.build %>/<%= meta.file %>.spec.js': '<%=meta.build%>/<%=meta.file%>.spec.coffee'

     uglify:
       options: mangle: false, banner: "<%= meta.banner %>"#, report: "gzip"
@@ -50,10 +53,20 @@
       style       : files: '<%= meta.bower %>/<%= meta.file %>.style.js': '<%= meta.build %>/<%= meta.file %>.style.js'

+    jasmine:
+      pivotal:
+        src: [
+          '<%=meta.bower%>/<%= meta.file %>.js']
+        options:
+          # vendor: 'spec/components/jquery/jquery.min.js'
+          specs: '<%=meta.build%>/<%=meta.file%>.spec.js',
+          # outfile: 'spec.html'
+          # keepRunner: true
+
     watch:
       core:
         files: ['<%= source.core %>']
-        tasks: ["concat:core", "coffee:core", "uglify:core"]
+        tasks: ["concat:core", "coffee:core", "uglify:core", "jasmine"]
       ajax:
         files: ['<%= source.ajax %>']
         tasks: ["coffee:ajax", "uglify:ajax"]
@@ -78,11 +91,17 @@
       style:
         files: ['<%= source.style %>']
         tasks: ["coffee:style", "uglify:style"]
+      spec:
+        files: ['<%= source.spec %>']
+        tasks: ["concat:spec", "coffee:spec", "jasmine"]
+
+

   grunt.loadNpmTasks "grunt-contrib-concat"
   grunt.loadNpmTasks "grunt-contrib-coffee"
   grunt.loadNpmTasks "grunt-contrib-uglify"
+  grunt.loadNpmTasks "grunt-contrib-jasmine"
   grunt.loadNpmTasks "grunt-contrib-watch"

   # Default task.
-  grunt.registerTask 'default', ["concat", "coffee", "uglify"]
+  grunt.registerTask 'default', ["concat", "coffee", "uglify", "jasmine"]


Add support for transpiling noflo-runtime-xx to ES5, refs https://github.com/noflo/grunt-noflo-browser/commit/18c133c0c56ebf42309bff388eac4d994f6610c9


Lint empty param lists as errors

Lint empty param lists as errors

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,6 +12,8 @@

     coffeelint:
       options:
+        no_empty_param_list:
+          level: 'error'
         max_line_length:
           level: 'ignore'


Trying mocha-cov + travis-ci

Trying mocha-cov + travis-ci

Signed-off-by: Jean-Denis Vauguet <ff9dedefe9ee78b3339d23163d8b29a3c83c6016@vauguet.fr>

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,18 +21,23 @@
           'build/circular.js': 'src/circular.coffee'

     mochacov:
+      coverage:
+        options:
+          coveralls:
+            serviceName: 'travis-ci'
+      test:
+        options:
+          reporter: 'spec'
       options:
-        #reporter: 'html-cov',
         compilers: ['coffee:coffee-script']
-        coverage: true
-      all: ['test/**/*.coffee']
+        files: ['test/**/*.coffee']

     watch:
       files: ['src/circular.coffee', 'test/**/*.coffee']
       tasks: ['check', 'test']

   grunt.registerTask('check', ['coffee', 'jshint'])
-  grunt.registerTask('test', 'mochacov')
+  grunt.registerTask('test', 'mochacov:test')

   grunt.registerTask('default', 'watch')
-  grunt.registerTask('travis', ['check', 'test'])
+  grunt.registerTask('travis', ['check', 'mochacov:coverage'])


Always use default lint task

Always use default lint task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -38,6 +38,6 @@
     grunt.file.delete('bin/node_darwin_x64') if grunt.file.exists('bin/node_darwin_x64')

   grunt.registerTask('lint', ['coffeelint'])
-  grunt.registerTask('default', ['coffee', 'coffeelint:src'])
-  grunt.registerTask('test', ['clean', 'default', 'coffeelint:test', 'shell:test'])
+  grunt.registerTask('default', ['coffee', 'lint'])
+  grunt.registerTask('test', ['clean', 'default', 'shell:test'])
   grunt.registerTask('prepublish', ['clean', 'coffee', 'lint'])


added npm deployment flag

added npm deployment flag

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -49,6 +49,7 @@
     # Deployment
     bumper:
       options:
+        npm: true
         tasks: ["default"]
         files: ["package.json"]
         updateConfigs: ["pkg"]


remove some dead code from Gruntfile, improve Rule.symlink*

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -16,8 +16,10 @@
 fs = require 'fs'
 path = require 'path'

-uproxyLibPath = Path.dirname(require.resolve('uproxy-lib/package.json'))
-uproxyNetworkingPath = Path.dirname(require.resolve('uproxy-networking/package.json'))
+getNodePath = (module) =>
+  Path.dirname(require.resolve(module + '/package.json'))
+uproxyLibPath = getNodePath 'uproxy-lib'
+uproxyNetworkingPath = getNodePath 'uproxy-networking'

 # TODO: Move this into common-grunt-rules in uproxy-lib.
 Rule.symlink = (dir, dest='') =>
@@ -27,9 +29,12 @@
     cwd: dir,
     src: ['*'],
     dest: 'build/typescript-src/' + dest} ] }
-Rule.symlinkSrc = (dir) => Rule.symlink Path.join(dir, 'src')
-Rule.symlinkThirdParty = (dir) =>
-  Rule.symlink(Path.join(dir, 'third_party'), 'third_party')
+
+# Use symlinkSrc with the name of the module, and it will automatically symlink
+# the path to its src/ directory.
+Rule.symlinkSrc = (module) => Rule.symlink Path.join(getNodePath(module), 'src')
+Rule.symlinkThirdParty = (module) =>
+  Rule.symlink(Path.join(getNodePath(module), 'third_party'), 'third_party')

 # Temporary wrapper which allows implicit any.
 # TODO: Remove once implicit anys are fixed. (This is actually happening in some
@@ -48,21 +53,19 @@
       # merge `third_party` from different places as well.
       typescriptSrc: Rule.symlinkSrc '.'
       thirdPartyTypescriptSrc: Rule.symlinkThirdParty '.'
+
       uproxyNetworkingThirdPartyTypescriptSrc: Rule.symlinkThirdParty uproxyNetworkingPath
-      uproxyNetworkingTypescriptSrc: Rule.symlinkSrc uproxyNetworkingPath
-      uproxyLibThirdPartyTypescriptSrc: Rule.symlinkThirdParty uproxyLibPath
-      uproxyLibTypescriptSrc: Rule.symlinkSrc uproxyLibPath
+      uproxyNetworkingTypescriptSrc: Rule.symlinkSrc 'uproxy-networking'
+
+      uproxyLibThirdPartyTypescriptSrc: Rule.symlinkThirdParty 'uproxy-lib'
+      uproxyLibTypescriptSrc: Rule.symlinkSrc 'uproxy-lib'

     shell: {
-
-      # TODO: Get rid of this step once socks-rtc has this automatically done.
-      socks_rtc_setup: {
-        command: 'npm install;grunt',
-        options: {stdout: true, stderr: true, failOnError: true, execOptions: {cwd: 'node_modules/socks-rtc'}}
-      }

       # Once compiled, take all .spec files out of the chrome extension and app
       # directories and into the chrome/test directory, to keep a clean distro.
+      # TODO: Change to have a separate task that creates a distribution, and
+      # remove this hacky test extraction.
       extract_chrome_tests: {
         command: 'mkdir -p test; mv extension/scripts/*.spec.js test/',
         options: { failOnError: true, execOptions: {cwd: 'build/chrome/' }}


Enable Grunt bump

Enable Grunt bump

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -40,6 +40,7 @@
   grunt.registerTask 'build', ['coffee']

   # Load grunt modules
+  grunt.loadNpmTasks('grunt-bump')
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-contrib-watch')
   grunt.loadTasks('tasks')


Add extension.

Add extension.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -44,6 +44,7 @@
             directory: './locales/'
             syncFiles: true
             updateFiles: true
+            extension: ".yml"
     nodeunit:
       tests:
         [


Add "copy" as Grunt task

Add "copy" as Grunt task

Now copies over all files that are not .coffee to dist/ folder upon
compilation. Can be for example fixtures that are .json files.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -26,13 +26,13 @@
     coffee:
       lib:
         expand: true
-        cwd: 'src/lib'
+        cwd: 'lib/'
         src: ['**/*.coffee']
         dest: 'dist/lib'
         ext: '.js'
       test:
         expand: true
-        cwd: 'src/test/'
+        cwd: 'test/'
         src: ['**/*.coffee']
         dest: 'dist/test/'
         ext: '.js'
@@ -43,7 +43,18 @@
           require: 'coffee-script/register'
           reporter: 'spec'
         src: [
-          'src/test/**/*.coffee'
+          'test/**/*.coffee'
+        ]
+
+    copy:
+      dist:
+        files: [
+          expand: true,
+          cwd: 'test',
+          dest: 'dist/test/',
+          src: [
+            '**/*.json'
+          ]
         ]

     watch:
@@ -53,7 +64,7 @@
         files: 'Gruntfile.coffee'
         tasks: ['coffeelint:gruntfile']
       lib:
-        files: ['src/**/*.coffee']
+        files: ['lib/**/*.coffee']
         tasks: ['coffeelint:lib', 'coffee:lib', 'mochaTest']
       test:
         files: ['test/**/*.coffee']
@@ -61,24 +72,11 @@

     clean: ['dist/']

-  grunt.event.on 'watch', (action, files, target)->
-    grunt.log.writeln "#{target}: #{files} has #{action}"
-
-    # coffeelint
-    grunt.config ['coffeelint', target], src: files
-
-    # coffee
-    coffeeData = grunt.config ['coffee', target]
-    files = [files] if _.isString files
-    files = files.map (file)-> path.relative coffeeData.cwd, file
-    coffeeData.src = files
-
-    grunt.config ['coffee', target], coffeeData
-
   # tasks.
   grunt.registerTask 'compile', [
     'coffeelint'
     'coffee'
+    'copy'
   ]

   grunt.registerTask 'test', [


make sure .d.ts files are in dist directory

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -115,7 +115,7 @@
               nonull: true,
               expand: true,
               cwd: devBuildDir,
-              src: ['**/*.html', '**/*.css',  '**/*.js', '**/*.json',
+              src: ['**/*.html', '**/*.css',  '**/*.js', '**/*.d.ts', '**/*.json',
                     '!**/*.spec.js', '!**/*.spec.static.js'],
               dest: 'build/dist/',
               onlyIf: 'modified'


Update Grunt to build new JS files

Update Grunt to build new JS files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,7 +31,8 @@
           'js/vendor/underscore.js',
           'js/vendor/handlebars.runtime.js',
           'js/templates/*.js',
-          'js/src/isvalid.js'
+          'js/src/concon.js'
+          'js/src/app.js'
         ]
         dest : 'static/js/scripts.js'


Removed old Replace Task

Removed old Replace Task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -47,7 +47,7 @@
   grunt.loadNpmTasks 'grunt-contrib-copy'

   # Register our tasks. For this project, it's only prepublish.
-  grunt.registerTask 'build',       ['clean', 'coffee', 'copy', 'replace']
+  grunt.registerTask 'build',       ['clean', 'coffee', 'copy']
   grunt.registerTask 'prepublish',  ['build']
   grunt.registerTask 'default',     ['build']


Load grunt-release

Load grunt-release

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,4 +12,6 @@

   grunt.loadTasks('tasks')

+  grunt.loadNpmTasks('grunt-release')
+
   grunt.registerTask('default', ['mocha_debug'])


Add `parallel` option to run tests not in series

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -92,6 +92,9 @@

     casperjs:
       files: ['tests/casperjs/**/*.coffee']
+      options:
+        async:
+          parallel: true

     watch:
       scripts:


Prevent sending empty message to chat

Prevent sending empty message to chat

--- app/assets/javascripts/interaction_web_tools/chat.coffee
+++ app/assets/javascripts/interaction_web_tools/chat.coffee
@@ -19,6 +19,7 @@
         setTimeout InteractionWebTools.chat.pollMessages, 1000

   sendMessage: (message) ->
+    return false unless message
     that = @
     $.post @constructor.EVENTS_PATH, { event: { content: message } }, (data) ->
       that.renderMessages data.events


feat(changeloG): generate changelog on release

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -27,7 +27,7 @@
         files: ['package.json', 'bower.json']
         commit: true
         commitMessage: 'chore(release): v%VERSION%'
-        commitFiles: ['package.json', 'bower.json']
+        commitFiles: ['package.json', 'bower.json', 'CHANGELOG.md']
         createTag: true
         tagName: 'v%VERSION%'
         tagMessage: 'Version %VERSION%'
@@ -125,3 +125,11 @@
     'copy:404'
     'docker'
   ]
+
+  # Release -- new version!
+  grunt.registerTask 'release', (type) ->
+    grunt.task.run [
+      "bump-only:#{type or 'patch'}"
+      'changelog'
+      'bump-commit'
+    ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -9,9 +9,6 @@
 @see Camera.Flash
 ###
 Camera.Fade = (I, self) ->
-  Object.reverseMerge I,
-    flashTargetAlpha: 0
-
   fadeInDefaults =
     color: 'black'
     duration: 30


Account for different y-axis orientation

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -703,8 +703,10 @@
         # Check if any annotation over lens
         for index, annotation of @annotations
           if trainingType is 'lensed_galaxy'
-            r2 = (annotation.x - 220)*(annotation.x - 220) + (annotation.y - 220)*(annotation.y - 220)
-            @isLensMarked = if r2 < 900 then true else false
+            @isLensMarked = if Math.abs(training.x - annotation.x) < 30 && Math.abs(@subjectDimension - training.y - annotation.y) < 30
+               true
+             else
+               false
           else
             @isLensMarked = @checkImageMask(annotation.x, annotation.y)


pull live subjects once again

pull live subjects once again

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -101,7 +101,7 @@
     jsonFile = @subject.location['14-1'] # read actual subject

     # DEBUG CODE
-    jsonFile = 'http://demo.zooniverse.org.s3.amazonaws.com/planet_hunter/subjects/APHE100025p.json'
+    # jsonFile = 'http://demo.zooniverse.org.s3.amazonaws.com/planet_hunter/subjects/APHE100025p.json'
     # jsonFile = 'http://demo.zooniverse.org.s3.amazonaws.com/planet_hunter/subjects/APHE1000232.json'
     # jsonFile = './offline/subject.json' # for debug only
     console.log 'json_file: ', jsonFile


Add spec for price actions. #1

Add spec for price actions. #1

--- src/spec/utils/product-utils.spec.coffee
+++ src/spec/utils/product-utils.spec.coffee
@@ -15,29 +15,31 @@
   masterVariant:
     id: 1
     prices: [
-      {value: {currencyCode: "EUR", centAmount: 100}},
-      {value: {currencyCode: "EUR", centAmount: 1000}},
-      {value: {currencyCode: "EUR", centAmount: 1100}, country: "DE"},
-      {value: {currencyCode: "EUR", centAmount: 1200}, customerGroup: {id: "984a64de-24a4-42c0-868b-da7abfe1c5f6", typeId: "customer-group"}}
+      { value: { currencyCode: "EUR", centAmount: 1 } }
+      { value: { currencyCode: "EUR", centAmount: 7 } }
     ]
   variants: [
     {
       id: 2
       prices: [
-        {value: {currencyCode: "EUR", centAmount: 100}},
-        {value: {currencyCode: "EUR", centAmount: 2000}},
-        {value: {currencyCode: "EUR", centAmount: 2100}, country: "US"},
-        {value: {currencyCode: "EUR", centAmount: 2200}, customerGroup: {id: "59c64f80-6472-474e-b5be-dc57b45b2faf", typeId: "customer-group"}}
+        { value: { currencyCode: "USD", centAmount: 3 } }
       ]
     },
     {
       id: 3
       prices: [
-        {value: {currencyCode: "YEN", centAmount: 7777}},
-      ]
-    },
-    {
-      id: 4
+        { value: { currencyCode: "EUR", centAmount: 2100 }, country: 'DE' }
+        { value: { currencyCode: "EUR", centAmount: 2200 }, customerGroup: { id: "123", typeId: "customer-group" } }
+      ]
+    },
+    {
+      id: 4
+      prices: [
+        { value: { currencyCode: "YEN", centAmount: 7777 } }
+      ]
+    },
+    {
+      id: 5
       prices: []
     }
   ]
@@ -52,27 +54,31 @@
   masterVariant:
     id: 1
     prices: [
-      {value: {currencyCode: "EUR", centAmount: 100}},
-      {value: {currencyCode: "EUR", centAmount: 3800}}, # change
-      {value: {currencyCode: "EUR", centAmount: 1100}, country: "IT"} # change
+      { value: { currencyCode: "EUR", centAmount: 2 } }
+      { value: { currencyCode: "USD", centAmount: 7 } }
     ]
   variants: [
     {
       id: 2
       prices: [
-        {value: {currencyCode: "EUR", centAmount: 100}},
-        {value: {currencyCode: "EUR", centAmount: 2000}},
-        {value: {currencyCode: "EUR", centAmount: 2200}, customerGroup: {id: "59c64f80-6472-474e-b5be-dc57b45b2faf", typeId: "customer-group"}}
-      ]
-    },
-    {
-      id: 3
+        { value: { currencyCode: "USD", centAmount: 3 } }
+      ]
+    },
+    {
+      id: 3
+      prices: [
+        { value: { currencyCode: "EUR", centAmount: 2100 }, country: 'CH' }
+        { value: { currencyCode: "EUR", centAmount: 2200 }, customerGroup: { id: "987", typeId: "customer-group" } }
+      ]
+    },
+    {
+      id: 4
       prices: []
     },
     {
-      id: 4
+      id: 5
       prices: [
-        {value: {currencyCode: "EUR", centAmount: 999}},
+        { value: { currencyCode: "EUR", centAmount: 999 } }
       ]
     }
   ]
@@ -431,24 +437,27 @@
       description: [en: "Sample description", 0, 0] # deleted
       masterVariant:
         prices:
-          1: { value: { centAmount: [ 1000, 3800 ] } }
-          2: { country: ['DE', 'IT'] }
+          0:
+            value:
+              centAmount: [ 1, 2 ]
+          1:
+            value:
+              currencyCode: ['EUR', 'USD']
           _t: 'a'
-          _3: [{value: {currencyCode: 'EUR', centAmount: 1200 }, customerGroup: {id: '984a64de-24a4-42c0-868b-da7abfe1c5f6', typeId: 'customer-group'}, _id: 3}, 0, 0]
       variants:
-        0:
+        1:
           prices:
-            2:
-              value: { centAmount: [ 2100, 2200 ] },
-              customerGroup: [{id: '59c64f80-6472-474e-b5be-dc57b45b2faf', typeId: 'customer-group'}],
-              country: ['US', 0, 0]
+            0:
+              country: [ 'DE', 'CH' ]
+            1:
+              customerGroup:
+                id: ['123', '987']
             _t: 'a'
-            _3: [{value: {currencyCode: 'EUR', centAmount: 2200 }, customerGroup: {id: '59c64f80-6472-474e-b5be-dc57b45b2faf', typeId: 'customer-group'}, _id: 3}, 0, 0]
-        1:
+        2:
           prices:
             _t: 'a'
             _0: [ { value: { currencyCode: 'YEN', centAmount: 7777 }, _id: 0 }, 0, 0 ]
-        2:
+        3:
           prices:
             0: [ { value: { currencyCode: 'EUR', centAmount: 999 }, _id: 0 } ]
             _t: 'a'
@@ -492,7 +501,21 @@

   it 'should build prices actions', ->
-    # TODO!!!
+    delta = @utils.diff OLD_PRODUCT, NEW_PRODUCT
+    update = @utils.actionsMapPrices delta, OLD_PRODUCT, NEW_PRODUCT
+    expected_update = [
+      { action: 'removePrice', variantId: 1, price: { value: { currencyCode: 'EUR', centAmount: 1 } } }
+      { action: 'removePrice', variantId: 1, price: { value: { currencyCode: 'EUR', centAmount: 7 } } }
+      { action: 'removePrice', variantId: 3, price: { value: { currencyCode: 'EUR', centAmount: 2100 }, country: 'DE' } }
+      { action: 'removePrice', variantId: 3, price: { value: { currencyCode: 'EUR', centAmount: 2200 }, customerGroup: { id: '123', typeId: 'customer-group' } } }
+      { action: 'removePrice', variantId: 4, price: { value: { currencyCode: 'YEN', centAmount: 7777 } } }
+      { action: 'addPrice', variantId: 1, price: { value: { currencyCode: 'EUR', centAmount: 2 } } }
+      { action: 'addPrice', variantId: 1, price: { value: { currencyCode: 'USD', centAmount: 7 } } }
+      { action: 'addPrice', variantId: 3, price: { value: { currencyCode: 'EUR', centAmount: 2100 }, country: 'CH' } }
+      { action: 'addPrice', variantId: 3, price: { value: { currencyCode: 'EUR', centAmount: 2200 }, customerGroup: { id: '987', typeId: 'customer-group' } } }
+      { action: 'addPrice', variantId: 5, price: { value: { currencyCode: 'EUR', centAmount: 999 } } }
+    ]
+    expect(update).toEqual expected_update

   it "should build attribute actions", ->
     delta = @utils.diff(OLD_ATTRIBUTES, NEW_ATTRIBUTES)


fixes issue #304: feeds marked as expired before they should be

--- app/js/filters/pretty_date.coffee
+++ app/js/filters/pretty_date.coffee
@@ -42,7 +42,7 @@
             date = Utils.toDate(date)
         diff = Date.now() - date

-        diff = Math.round(diff/1000/60/24)
+        diff = Math.round(diff/1000/3600)
         return diff

 angular.module("app").filter "secondsAgo", (Utils)->


fixed a error name

fixed a error name

--- src/coffee/util.coffee
+++ src/coffee/util.coffee
@@ -156,8 +156,8 @@
       rName = new RegExp "^#{key}", "i"
       if rName.test name
         dataName = name.replace( rName, "" )
-        names = dataName.split(",")
+        names = dataName.split("")
         names[0] = names[0].toLowerCase()
-        data[names.join('')] = val
+        data[names.join('')] = if !isNaN val then Number val else val
     data


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -42,7 +42,7 @@

     if arguments.length == 2
       if argments[0].length == 3
-        parsedColor = [parseInt(argments[0]), parseInt(argments[1]), parseInt(argments[2]), parseFloat(arguments[1])]
+        parsedColor = [parseInt(argments[0][0]), parseInt(argments[0][1]), parseInt(argments[0][2]), parseFloat(arguments[1])]
     if arguments.length > 2
       alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [parseInt(arguments[0]), parseInt(arguments[1]), parseInt(arguments[2]), parseFloat(alpha)]


LMS-1646: The “Assignment” tab is not visible for teachers when they enter the “Calendar” tool

--- app/coffeescripts/calendar/EditEventDetailsDialog.coffee
+++ app/coffeescripts/calendar/EditEventDetailsDialog.coffee
@@ -59,11 +59,13 @@
       dialog.addClass("group_#{newContext}").data('group_class', "group_#{newContext}")
       @calendarEventForm.setContext(newContext) if @calendarEventForm
       @assignmentDetailsForm.setContext(newContext) if @assignmentDetailsForm
+      $('.edit_assignment_option').removeAttr('style')

     closeCB: () =>
       dialog.dialog('close')

     dialogClose: () =>
+      dialog.removeClass dialog.data('group_class')
       if @oldFocus?
         @oldFocus.focus()
         @oldFocus = null
@@ -86,6 +88,10 @@

         @setupTabs()

+        # Avoid tab with 'color: white' on a white background
+        has_colored_bg = _.any($('#edit_event').attr('class').split(' '), (c) -> c.startsWith('group_'))
+        $('.edit_assignment_option').css('color', 'black') unless has_colored_bg
+
         # TODO: select the tab that should be active

         @oldFocus = document.activeElement


Add a login time tag to the session, and a lastLogin time to the user profile.

--- user_status.coffee
+++ user_status.coffee
@@ -28,7 +28,8 @@
   userId = @_session.userId
   return unless @_session.socket?
   sessionId = @_session.id
-
+  dateMs = new Date().getTime()
+
   # Untrack connection on logout
   unless userId?
     # TODO: this could be replaced with a findAndModify once it's supported on Collections
@@ -46,11 +47,12 @@
     $set:
       userId: userId
       ipAddr: ipAddr
+      loginTime: dateMs

-  UserStatus.emit("sessionLogin", userId, sessionId, ipAddr)
+  UserStatus.emit("sessionLogin", userId, sessionId, ipAddr, dateMs)

   Meteor.users.update userId,
-    $set: {'profile.online': true}
+    $set: {'profile.online': true, 'lastLogin': dateMs}

   # Remove socket on close
   @_session.socket.on "close", Meteor.bindEnvironment ->


coffeelint component loader

coffeelint component loader

--- app/_src/component-loader.coffee
+++ app/_src/component-loader.coffee
@@ -53,7 +53,7 @@
     componentsToCleanUp = []
     for id, instance of @registeredComponents
       exists = currentComponents[id] == true
-      unless exists
+      unless exists
         instance.destroy?()
         componentsToCleanUp.push(id)
     delete @registeredComponents[id] for id in componentsToCleanUp


Allow actions to indicate that they don't want magic scope.

To do so, they simply define at least one argument to the function.

--- application/chrome/content/wesabe/download/Player.coffee
+++ application/chrome/content/wesabe/download/Player.coffee
@@ -234,7 +234,10 @@

       logger.info 'History is ', (hi.name for hi in @history).join(' -> ')

-      @callWithMagicScope fn, browser, page, extend({log}, scope or {})
+      if func.argNames(fn).length > 0
+        fn.call @, browser, page
+      else
+        @callWithMagicScope fn, browser, page, extend({log}, scope or {})

     return retval


Changing to no location range should be considered a change

Fixes closing the attachment editor when focusing outside of the <trix-editor>

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -86,7 +86,6 @@

   updateCurrentLocationRange: (locationRange) ->
     locationRange ?= @createLocationRangeFromDOMRange(getDOMRange())
-    return unless locationRange
     if (@currentLocationRange and not locationRange) or not locationRange?.isEqualTo(@currentLocationRange)
       @currentLocationRange = locationRange
       @delegate?.locationRangeDidChange?(@currentLocationRange)


Refactor, and log when we have a 'range' header.

--- app/coffee/FileController.coffee
+++ app/coffee/FileController.coffee
@@ -13,19 +13,20 @@
 	getFile: (req, res)->
 		{key, bucket} = req
 		{format, style} = req.query
-		opts = {
+		options = {
 			key: key,
 			bucket: bucket,
 			format: format,
 			style: style,
 		}
 		metrics.inc "getFile"
+		logger.log key:key, bucket:bucket, format:format, style: style, "reciving request to get file"
 		if req.headers.range?
 			range = FileController._get_range(req.headers.range)
-			opts.start = range.start
-			opts.end = range.end
-		logger.log key:key, bucket:bucket, format:format, style:style, "reciving request to get file"
-		FileHandler.getFile bucket, key, opts, (err, fileStream)->
+			options.start = range.start
+			options.end = range.end
+			logger.log start: range.start, end: range.end, "getting range of bytes from file"
+		FileHandler.getFile bucket, key, options, (err, fileStream)->
 			if err?
 				logger.err err:err, key:key, bucket:bucket, format:format, style:style, "problem getting file"
 				if !res.finished and res?.send?


fixing bedsheet selector to update either the user OR the entry..not both

--- app/coffee/metamenu.coffee
+++ app/coffee/metamenu.coffee
@@ -129,7 +129,9 @@
         bedsheetUrl = 'url("/images/uploads/originals/' + $(event.currentTarget).data('id') + '.jpg")'
         $('#body').css('background-image', bedsheetUrl)

-        if $('#view_preference_image_id').attr('name')?
+        alert $('#entry_view_preference_image_id').attr('name')?
+
+        if $('#entry_view_preference_image_id').attr('name')?
           @updateEntryBedsheet($(event.currentTarget).data('id'))
         else
           @updateUserBedsheet($(event.currentTarget).data('id'))


fixing the bedsheets not saving on the right page

--- app/coffee/metamenu.coffee
+++ app/coffee/metamenu.coffee
@@ -146,7 +146,8 @@

           if $('#entry_view_preference_attributes_image_id').attr('name')?
             $('#entry_view_preference_attributes_image_id').val($(event.currentTarget).data('id'))
-
+          else
+            @updateUserBedsheet($(event.currentTarget).data('id'))
           if $('#show_entry_mode').attr('name')?
             @updateEntryBedsheet($('#showEntry').data('id'),$(event.currentTarget).data('id'))
         )


step fix

step fix

--- cakes.coffee
+++ cakes.coffee
@@ -60,7 +60,7 @@

 exports.step = ->
-	dic 'it', '', arguments
+	dic 'it', '%s', arguments

 exports.Spec = ->  # describe() start of spec file


workers/social: refactor participant handler to accept optional callback

--- workers/social/lib/social/models/socialapi/socialaccount.coffee
+++ workers/social/lib/social/models/socialapi/socialaccount.coffee
@@ -4,11 +4,11 @@
 # dont want it to be listed first item while searching for account.coffe in
 # sublime ~ CS

-removeParticipantHandler = (data) ->
-  participantHandler 'removeParticipants', data
+removeParticipantHandler = (rest...) ->
+  participantHandler 'removeParticipants', rest...

-addParticipantHandler = (data) ->
-  participantHandler 'addParticipants', data
+addParticipantHandler = (rest...) ->
+  participantHandler 'addParticipants', rest...

 # SocialAccount
 module.exports = class SocialAccount extends Base
@@ -68,20 +68,30 @@

-participantHandler = (funcName, data) ->
+participantHandler = (funcName, data, callback = ->) ->

   JSession          = require '../session'
   SocialChannel     = require './channel'
   { group, member } = data

   group.fetchAdmin (err, admin) ->
-    return console.error 'err while fetching admin', err  if err
-    return console.error 'couldnt find admin'  unless admin
+    if err
+      console.error 'err while fetching admin', err
+      return callback err
+
+    unless admin
+      console.error 'couldnt find admin'
+      return callback new Error 'couldnt find admin'

     sessionData = { username: admin.profile.nickname, groupName: group.slug }
     JSession.fetchSessionByData sessionData, (err, session) ->
-      return console.error 'err while fetching session', err  if err
-      return console.error 'couldnt find a session'  unless session
+      if err
+        console.error 'err while fetching session', err
+        return callback err
+
+      unless session
+        console.error 'couldnt find a session'
+        return callback new Error 'couldnt find a session'

       client = {}
       client.sessionToken = session.clientId
@@ -93,12 +103,17 @@
       client.connection.groupName = group.slug

       group.createSocialApiChannels client, (err, socialApiChannels) ->
-        return console.error 'couldnt create socialapi channels', err  if err
+        if err
+          console.error 'couldnt create socialapi channels', err
+          return callback err
+
         { socialApiChannelId, socialApiAnnouncementChannelId } = socialApiChannels

         # ensure member has socialapi id
         member.createSocialApiId (err, socialApiId) ->
-          return console.error 'couldnt create socialapi id', err  if err
+          if err
+            console.error 'couldnt create socialapi id', err
+            return callback new Error 'couldnt create socialapi id'

           options = {
             channelId  : socialApiChannelId
@@ -106,11 +121,12 @@
           }

           SocialChannel[funcName] client, options, (err, participants) ->
-
-            return console.error "couldnt #{funcName} user into group socialapi chan", err, options  if err
+            if err
+              console.error "couldnt #{funcName} user into group socialapi chan", err, options
+              return new Error "couldnt #{funcName} user into group socialapi chan"

             # only add koding's members to announcement channel
-            return if group.slug isnt 'koding'
+            return callback null if group.slug isnt 'koding'

             options = {
               channelId  : socialApiAnnouncementChannelId
@@ -118,4 +134,5 @@
             }

             SocialChannel[funcName] client, options, (err) ->
-              return console.error "couldnt #{funcName} user into group socialapi chan", err, options  if err
+              console.error "couldnt #{funcName} user into group socialapi chan", err, options  if err
+              return callback err


Disable setting other owners

Disable setting other owners

--- app/pages/lab/collaborators.cjsx
+++ app/pages/lab/collaborators.cjsx
@@ -39,7 +39,7 @@

-            {for role in POSSIBLE_ROLES
+            {for role in POSSIBLE_ROLES when role isnt 'owner'

                   <input type="checkbox" name="role" value={role} />{' '}
@@ -126,7 +126,7 @@
           toggleThisRole = @toggleRole.bind this, projectRoleSet, role
           # TODO: Translate this.

-            <input type="checkbox" name={role} checked={role in projectRoleSet.roles} disabled={projectRoleSet.id in @state.saving} onChange={toggleThisRole} />{' '}
+            <input type="checkbox" name={role} checked={role in projectRoleSet.roles} disabled={role is 'owner' or projectRoleSet.id in @state.saving} onChange={toggleThisRole} />{' '}
             {role[...1].toUpperCase()}{role[1...]}
           }


Trade Route added.

Trade Route added.

--- cards.coffee
+++ cards.coffee
@@ -780,6 +780,13 @@
       if not opposingProvince
         state.current.coins += 1
         state.current.drawCards(1)
+}
+
+makeCard "Trade Route", action {
+  buys: 1
+  trash: 1
+  getCoins: (state) ->
+    state.tradeRouteValue
 }

 makeCard "Trusty Steed", c["Bag of Gold"], {


Page parent page association field autocompletes in page form.

--- app/assets/javascripts/admin/pages.js.coffee
+++ app/assets/javascripts/admin/pages.js.coffee
@@ -21,3 +21,6 @@
   $('#page_author_name').typeahead(
     name: 'page_author_name',
     local: $('#page_author_name').data('source') )
+  $('#page_parent_page_title').typeahead(
+    name: 'page_parent_page_title',
+    local: $('#page_parent_page_title').data('source') )


Save collapsibles state so that changing countries for a new location keeps the collapsible expanded

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -15,6 +15,8 @@
     $('select', select_wrapper).attr('disabled', true)

     subregion_code = $(this).val()
+
+    save_collapsible_states()

     $.get(
       "/api/subregions?regionstr=#{subregion_code}",


Tidied up JS

Tidied up JS

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -552,7 +552,7 @@
     has_filename = !!wrapper.data('filename')
     is_link = !!$el.data('add-link')

-    govuk_button = $(el).closest('.govuk-button--secondary')
+    govuk_button = $(el).closest('.govuk-button')

     #  Searching for inputs only excludes 'Add website address' button
     if $(el).is("input")


Upload in progress style

Upload in progress style

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -297,9 +297,22 @@
       # TODO

     upload_started = (e, data) ->
-      button.addClass('visuallyhidden') #TODO: show progressbar
+      # Show `Uploading...`
+      button.addClass("visuallyhidden")
+      new_el = $("<li class='js-uploading'>")
+      div = $("<div>")
+      label = $("<label>").text("Uploading...")
+      div.append(label)
+      new_el.append(div)
+      list.append(new_el)
+      list.removeClass("visuallyhidden")

     upload_done = (e, data, link) ->
+      # Remove `Uploading...`
+      list.find(".js-uploading").remove()
+      list.addClass("visuallyhidden")
+
+      # Show new upload
       new_el = $("<li>")

       if link


Fix question wont remove bug

Fix question wont remove bug

--- app/assets/javascripts/instruments.js.coffee
+++ app/assets/javascripts/instruments.js.coffee
@@ -1,8 +1,7 @@
 jQuery ->
   $('form').on 'click', '.remove_fields', (event) ->
     $(this).prev('input[type=hidden]').val('1')
-    $(this).closest('div.question').hide()
-    $(this).closest('fieldset').hide()
+    $(this).closest('div.question').remove()
     event.preventDefault()

   $('form').on 'click', '.add_fields', (event) ->


added bind to make the settlment create with the POWER OF AJAX

--- app/assets/javascripts/settlements.js.coffee
+++ app/assets/javascripts/settlements.js.coffee
@@ -4,8 +4,10 @@

 $ = jQuery
+debug = false

 $(document).ready ->
+  # random name
   $('#random-settlement-name').click ->
     $sn = $('#settlement_name')
     $sp = $('#settlement_population')
@@ -22,9 +24,21 @@
       $sp.unstash()
     true

+  # force the entry in the population to be an int
   $('#settlement_population').keyup ->
     parsed = parseInt($(this).val())
     if (isNaN(parsed))
       $(this).val('')
     else
       $(this).val(parseInt($(this).val()))
+
+  # bind the 'create' link to a post ajax call with a callback
+  $('#create-settlement').click ->
+    options =
+      'settlement[name]': $('#settlement_name').val(),
+      'settlement[population]': $('#settlement_population').val()
+    $.post '/settlements', options,   (data, textStatus) ->
+        $('#info-settlement dd.name').html( '' + data.name + '')
+        $('#info-settlement dd.population').text(data.population)
+      ,"json"
+    false


Expose Config.defaults

Expose Config.defaults

--- src/core/config.coffee
+++ src/core/config.coffee
@@ -8,7 +8,7 @@

 class Config

-  defaults =
+  @defaults =
     # path to the directory containing content's to be scanned
     contents: './contents'
     # list of glob patterns to ignore
@@ -33,11 +33,10 @@
     # options prefixed with _ are undocumented and should generally not be modified
     _fileLimit: 40 # max files to keep open at once

-
   constructor: (options) ->
     for option, value of options
       this[option] = value
-    for option, defaultValue of defaults
+    for option, defaultValue of @constructor.defaults
       this[option] ?= defaultValue

 Config.fromFile = (path, callback) ->


Update sidebar_profile_view.coffee

Update sidebar_profile_view.coffee

Disabling to fix acceptence test

--- core/app/backbone/views/sidebar_profile_view.coffee
+++ core/app/backbone/views/sidebar_profile_view.coffee
@@ -72,4 +72,4 @@
   onRender: ->
     @profilePictureRegion.show   new UserLargeView(model: @model)
     @socialStatisticsRegion.show new SocialStatisticsView(model: @model)
-    @followUserButtonRegion.show new FollowUserButtonView(model: @model)
+    #@followUserButtonRegion.show new FollowUserButtonView(model: @model)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -13,6 +13,8 @@
   root = exports ? this

   root.Core = (I={}) ->
+    Object.reverseMerge I,
+      includedModules: [
     self =
       ###*
       External access to instance variables. Use of this property should be avoided


Add default charset to Content-Type headers for text types.

--- src/core/server.coffee
+++ src/core/server.coffee
@@ -222,11 +222,17 @@
           renderView env, content, locals, tree, templates, (error, result) ->
             if error then callback error
             else if result?
+              mimeType = mime.lookup(content.filename)
+              charset = mime.charsets.lookup(mimeType)
+              if charset
+                contentType = "#{mimeType}; charset=#{charset}"
+              else
+                contentType = mimeType
               if result instanceof Stream
-                response.writeHead 200, 'Content-Type': mime.lookup(content.filename)
+                response.writeHead 200, 'Content-Type': contentType
                 pump result, response, (error) -> callback error, 200
               else if result instanceof Buffer
-                response.writeHead 200, 'Content-Type': mime.lookup(content.filename)
+                response.writeHead 200, 'Content-Type': contentType
                 response.write result
                 response.end()
                 callback null, 200


Client: Activity: Created focusInputOnEdit method

--- client/activity/lib/components/chatlistitem/index.coffee
+++ client/activity/lib/components/chatlistitem/index.coffee
@@ -41,7 +41,6 @@
     isMarkUserAsTrollModalVisible : no
     showItemMenu                  : yes

-
   constructor: (props) ->

     super props
@@ -66,6 +65,7 @@
   componentDidUpdate: ->

     @setState editMode: @props.message.get '__isEditing'
+    @focusInputOnEdit()

   getAccountInfo: ->
@@ -186,15 +186,21 @@
     @setState isDeleting: no

-  editPost: ->
-
-    messageId = @props.message.get '_id'
-
-    ActivityFlux.actions.message.setMessageEditMode messageId
+  focusInputOnEdit: ->

     domNode = @refs.EditMessageTextarea.getDOMNode()
+
     kd.utils.wait 100, ->
       kd.utils.moveCaretToEnd domNode
+
+
+  editPost: ->
+
+    messageId = @props.message.get '_id'
+
+    ActivityFlux.actions.message.setMessageEditMode messageId
+    @focusInputOnEdit()
+

   showDeletePostPromptModal: ->


dashboard: itemview name is changed

dashboard: itemview name is changed

--- client/admin/lib/views/moderation/similaritemview.coffee
+++ client/admin/lib/views/moderation/similaritemview.coffee
@@ -4,7 +4,7 @@
 KDCheckBox = kd.CheckBox
 KDLabelView = kd.LabelView

-module.exports = class SimilarItemView extends KDListItemView
+module.exports = class SelectableItemView extends KDListItemView

   JView.mixin @prototype


payment/redux: add plan amount selector

payment/redux: add plan amount selector

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/redux/modules/payment/subscription.coffee
+++ client/app/lib/redux/modules/payment/subscription.coffee
@@ -114,6 +114,14 @@
   (end) -> dateDiffInDays(new Date(Number end), new Date)
 )

+planAmount = (state) ->
+  amount = if p = plan(state) then p.amount else 0
+
+  return {
+    dollars: amount / 100
+    cents: amount
+  }
+

 module.exports = {
   namespace: withNamespace()
@@ -122,5 +130,6 @@
   LOAD, CREATE, REMOVE

   # Selectors
-  plan, isTrial, endsAt, pricePerSeat, trialDays, daysLeft
+  plan, isTrial, endsAt, pricePerSeat
+  trialDays, daysLeft, planAmount
 }


include Bindable by default in Core

include Bindable by default in Core

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -170,6 +170,8 @@

       send: (name, args...) ->
         self[name](args...)
+
+    self.include "Bi"

     # Initial module inclue, for reconstructing objects from JSON
     for moduleName in I.includedModules


home: minor fixed dates

home: minor fixed dates

--- client/landing/site.hackathon/coffee/home/AppView.coffee
+++ client/landing/site.hackathon/coffee/home/AppView.coffee
@@ -306,19 +306,19 @@

Now - Registration open

Nov 20th - Applications are closed. Last notifications are sent to teams/individuals who were accepted into the hackathon

-
Nov 26th - Day 1
+
Nov 29th - Day 1

0900 PDT  Announcement of topics on #WFGH

0901 PDT  Let the hacking begin!

-
Nov 27th - Day 2
+
Nov 30th - Day 2

0000 – 2200 PDT Hacking continues

-
2200 – 2230 PDT Teams submit their projects

+
2200 – 2230 PDT Teams submit their projects

-
Nov 28th - Winners are notified via email and winning projects are listed

+
December 3rd - Winners are notified via email and winning projects are listed


check if included in avgs

check if included in avgs

--- src/flux/scores.coffee
+++ src/flux/scores.coffee
@@ -103,7 +103,8 @@

     task.accepted_late_at = TimeStore.getNow().toISOString()

-    adjustTaskAverages(@_local, taskInfo)
+    if task.is_included_in_averages
+      adjustTaskAverages(@_local, taskInfo)

     @emitChange()

@@ -121,7 +122,8 @@
     task.completed_accepted_late_step_count = 0
     delete task.accepted_late_at

-    adjustTaskAverages(@_local, taskInfo)
+    if task.is_included_in_averages
+      adjustTaskAverages(@_local, taskInfo)

     @emitChange()


Use Game width and height for canvas size

--- src/game_canvas.coffee
+++ src/game_canvas.coffee
@@ -1,4 +1,4 @@
-define ['fabric', 'underscore'], (fabric, _) ->
+define ['game', 'fabric', 'underscore'], (Game, fabric, _) ->

   fabric = fabric.fabric if fabric.fabric?

@@ -9,7 +9,7 @@
         @canvas = new fabric.Canvas(canvasId)
       else
         console.log "Node!"
-        @canvas = fabric.createCanvasForNode(800, 800)
+        @canvas = fabric.createCanvasForNode(Game.WIDTH, Game.HEIGHT)
       options = {strokeWidth: 5, fill: 'none'}
       @lines = [new fabric.Polyline([{'x': @canvas.getWidth() / 2, 'y': 0}], _.extend(options, {'stroke': 'green'}), true),
         new fabric.Polyline([{'x': @canvas.getWidth(), y: @canvas.getHeight() / 2}], _.extend(options, {'stroke': 'blue'}), true),


Make linter happy

Make linter happy

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -23,7 +23,7 @@
 # doing asynchronous processing. In fact, `sendStreams` is enabled by default
 # on all outports when `config.async` is `true`.
 #
-# WirePattern supports async `proc` handlers. Set `config.async = true` and
+# WirePattern supports async `proc` handlers. Set `config.async = true` and
 # make sure that `proc` accepts callback as 4th parameter and calls it when
 # async operation completes or fails.
 exports.WirePattern = (component, config, proc) ->


don’t remove non-existing timeout

don’t remove non-existing timeout

--- src/event.coffee
+++ src/event.coffee
@@ -37,7 +37,7 @@

   resolve: ->
     cancelAnimationFrame(@queue.frame) if @queue.frame
-    clearTimeout(@queue.timeout)
+    clearTimeout(@queue.timeout) if @queue.timeout
     if @queue.length
       perform = (args) =>
         if @listeners


add edit to games list

add edit to games list

--- src/games.coffee
+++ src/games.coffee
@@ -5,7 +5,8 @@
 #

 exports.games = {
+  tutorial: require("../games/tutorial").game
+  edit: require("../games/edit").game
   example: require("../games/example").game
-  tutorial: require("../games/tutorial").game
   sampler: require("../games/sampler").game
 }


주석 정리

주석 정리

--- src/index.coffee
+++ src/index.coffee
@@ -224,13 +224,10 @@

     _tosser.setArgs = (args)->
       _tosser.argv = args
-
-    # _tosser.vars = ()->
+
     _tosser.items = ()->
       context.vars
-
-    # _tosser.setVar = # DEPRECATED
-    # _tosser.storeArgs =
+
     _tosser.toItems = (names...)->
       return _tosser.err (err, args...)->
         for value, inx in args
@@ -238,20 +235,12 @@
           if names[inx]
             _tosser.setItem names[inx], value
         _tosser err, args...
-    # _tosser.getVar = (args...)->
-    #   _tosser.var args...
+
     _tosser.setItem = (key, value)->
       context.vars[key] = value

     _tosser.getItem = (key)->
-      return context.vars[key]
-
-    # _tosser.var = (args...)->
-    #   if args.length is 2
-    #     [var_name, value] = args
-    #     context.vars[var_name] = value
-    #   else
-    #     return context.vars[args[0]]
+      return context.vars[key]

     return _tosser
   do_cancel = ()->


force reindexing in background, allow app to wait for it

--- src/index.coffee
+++ src/index.coffee
@@ -97,6 +97,42 @@
             log.info "succeeded"
             defineRequests requestsToSave, callback, i + 1

+requestsIndexingProgress = 0
+requestsIndexingTotal = 1
+requestsIndexingCallbacks = []
+
+module.exports.getRequestsReindexingProgress = ->
+    log.warn "#{requestsIndexingProgress} / #{requestsIndexingTotal}"
+    requestsIndexingProgress / requestsIndexingTotal
+
+module.exports.waitReindexing = (callback) ->
+    if requestsIndexingTotal is requestsIndexingProgress
+        callback null
+    else
+        requestsIndexingCallbacks.push callback
+
+forceIndexRequests = (requestsToSave, callback, i = 0) ->
+    {model, requestName} = requestsToSave[i]
+    requestsIndexingTotal = requestsToSave.length
+    log.info """
+        #{model.getDocType()} - #{requestName} reindexing #{requestsIndexingProgress}/#{requestsIndexingTotal}"""
+    model.request requestName, limit: 1, (err) ->
+        if err and err.code is 'ECONNRESET'
+            log.info " Timedout"
+            setTimeout ->
+                forceIndexRequests requestsToSave, callback, i
+            , 4000 # wait 4s, the request is timing out, probably reindex
+
+        else if i + 1 >= requestsToSave.length
+            requestsIndexingProgress++
+            log.info " requests reindexing complete"
+            callback null
+            cb null for cb in requestsIndexingCallbacks
+        else
+            requestsIndexingProgress++
+            log.info " succeeded"
+            forceIndexRequests requestsToSave, callback, i + 1
+

 # to use cozydb as an americano module
 # Plugin configuration: run through models/requests.(coffee|js) and save
@@ -123,3 +159,7 @@
     defineRequests requestsToSave, (err) ->
         return callback err if err
         callback null
+
+        # in the background
+        forceIndexRequests requestsToSave, ->
+            log.info "Requests reindexing complete"


Ignore package spec folder

Ignore package spec folder

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -26,6 +26,7 @@

     cp 'package.json', path.join(appDir, 'package.json')

+    packageNames = []
     packageDirectories = []
     nonPackageDirectories = [
       'benchmark'
@@ -39,6 +40,7 @@
       directory = path.join('node_modules', child)
       if isAtomPackage(directory)
         packageDirectories.push(directory)
+        packageNames.push(child)
       else
         nonPackageDirectories.push(directory)

@@ -92,6 +94,9 @@
       '.pairs'
       '.travis.yml'
     ]
+
+    packageNames.forEach (packageName) -> ignoredPaths.push(path.join(packageName, 'spec'))
+
     ignoredPaths = ignoredPaths.map (ignoredPath) -> _.escapeRegExp(ignoredPath)

     # Add .* to avoid matching hunspell_dictionaries.


Have atom.desktop use PATH to find executable

Currently for RPM build only.

--- build/tasks/mkrpm-task.coffee
+++ build/tasks/mkrpm-task.coffee
@@ -33,7 +33,7 @@
     installDir = grunt.config.get('atom.installDir')
     shareDir = path.join(installDir, 'share', 'atom')
     iconName = path.join(shareDir, 'resources', 'app', 'resources', 'atom.png')
-    executable = path.join(shareDir, 'atom')
+    executable = 'atom'

     data = {name, version, description, installDir, iconName, executable}
     specFilePath = fillTemplate(path.join('resources', 'linux', 'redhat', 'atom.spec'), data)


removed test previously added to fail Travis CI on purpose

--- spec/admin.spec.coffee
+++ spec/admin.spec.coffee
@@ -26,9 +26,6 @@
          end : jasmine.createSpy()

    describe 'urlValid', ->
-      it 'should fail', ->
-         expect(true).toBe false
-
       it 'should accept the root url', ->
          url = '/'


:art: Cleanup

:art: Cleanup

--- spec/jumpy-spec.coffee
+++ spec/jumpy-spec.coffee
@@ -49,10 +49,10 @@
         # He found it in the wrap-guide.
         workspaceElement.style.height = "5000px" # big enough
         workspaceElement.style.width = "5000px"
+        # TODO: Abstract the following out, (DRY) --------------
         jumpyPromise = atom.packages.activatePackage 'jumpy'
         statusBarPromise = atom.packages.activatePackage 'status-bar'
         jasmine.attachToDOM(workspaceElement)
-        # TODO: Abstract the following out, (DRY) --------------

         waitsForPromise ->
             atom.workspace.open 'test_text.md'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -8,7 +8,7 @@

     # => called with no arguments, flicker will
     # toggle the player's alpha value between 0.9 (value provided above)
-    # and 0.5 (flickerable default) every 0.1 second, lasting 2 seconds
+    # and 0.5 (flickerable default) every 0.1 second, lasting 2 seconds total
     player.flicker()

 @name Flickerable


Cleanup extra spaces

Cleanup extra spaces

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -5,7 +5,7 @@

 # Keep track of what environment we're running in, Node.js or a browser.

-NODEJS    = typeof exports isnt 'undefined'
+NODEJS = typeof exports isnt 'undefined'

 # If we're in Node, require the `https` module for our network requests.


Removed storage.coffee (no real use, and might be better off in hubot-diagnostics)

--- scripts/storage.coffee
+++ scripts/storage.coffee
@@ -1,25 +0,0 @@
-# Description:
-#   Inspect the data in redis easily
-#
-# Commands:
-#   hubot show users - Display all users that hubot knows about
-#   hubot show storage - Display the contents that are persisted in the brain
-
-
-Util = require "util"
-
-module.exports = (robot) ->
-  robot.respond /show storage$/i, (msg) ->
-    output = Util.inspect(robot.brain.data, false, 4)
-    msg.send output
-
-  robot.respond /show users$/i, (msg) ->
-    response = ""
-
-    for own key, user of robot.brain.data.users
-      response += "#{user.id} #{user.name}"
-      response += " <#{user.email_address}>" if user.email_address
-      response += "\n"
-
-    msg.send response
-


Defaults set before resets.

Defaults set before resets.

--- scripts/tickets.coffee
+++ scripts/tickets.coffee
@@ -28,6 +28,8 @@
     robot.brain.on 'loaded', ->
         points = robot.brain.data.points or {}
         tickets = robot.brain.data.tickets or {}
+        raffle = off
+        entered[username] = false

     # Start raffle with chosen cost.
     robot.respond /raffle start (\d+)$/i, (msg) ->
@@ -74,5 +76,5 @@
     robot.respond /entrants$/i, (msg) ->
         if robot.auth.hasRole(msg.envelope.user, ['admin'])
             tickets = [entered[username] for username in entrants]
-            msg.send "Entrants for this raffle: " + tickets
+            msg.send "Entrants for this raffle: " + entrants
             return


fix event filtration location loading bug

--- client/controllers/events/eventFiltration.coffee
+++ client/controllers/events/eventFiltration.coffee
@@ -116,13 +116,11 @@

 Template.eventFiltration.onRendered ->
   @autorun =>
-    @incidentLocations = _.uniq(
+    incidentLocations = _.uniq(
       _.flatten(EventIncidents.find({}, field: locations: 1).map (incident) ->
         incident.locations
       )
     )
-
-  @autorun =>
     countryLevel = @countryLevel.get()
     @locations.remove({})
     @selectedLocations.remove({})
@@ -131,7 +129,7 @@
       if regionData.continentCode
         regionData.countryISOs.forEach (iso) ->
           countryToRegion[iso] = regionData
-    @incidentLocations.forEach (location) =>
+    incidentLocations.forEach (location) =>
       return unless location.countryCode
       location.region = countryToRegion[location.countryCode].name
       return unless location[countryLevel]


Calling `getFromTextLocation` at location 0 should return a block

Before this change it returned the DocumentStartToken.

--- src/model.coffee
+++ src/model.coffee
@@ -699,6 +699,9 @@
     head = @start
     best = head

+    if head instanceof DocumentStartToken
+      head = head.next
+
     row = 0

     until not head? or row is location.row


Fix for tests

Fix for tests

--- src/model.coffee
+++ src/model.coffee
@@ -771,9 +771,11 @@
         @handwritten
       }\" classes=\"#{
         @classes?.join?(' ') ? ''
-      }\" dropdown=\"#{
-        @dropdown?.join?(' ') ? ''
-      }\">"
+      }\"#{
+        if @dropdown?
+          " dropdown=\"#{@dropdown?.join?(' ') ? ''}\""
+        else ''
+      }>"

     _serialize_footer: -> "</socket>"


Move credible interval

Move credible interval

--- site/app.coffee
+++ site/app.coffee
@@ -846,28 +846,30 @@
     ctx.setLineDash([1, 0])
     ctx.strokeStyle = 'green'
     # credible interval
-    current = @chartData[numWeeks-1]
-    centerX = i2x(numWeeks-1)
-    centerY = ili2y(current.value)
-    [upY, downY] = [ili2y(current.value+current.std), ili2y(current.value-current.std)]
-    ctx.beginPath()
-    ctx.moveTo(centerX, upY)
-    ctx.lineTo(centerX, downY)
-    ctx.stroke()
-    ctx.beginPath()
-    ctx.moveTo(i2x(numWeeks-3), upY)
-    ctx.lineTo(2*centerX-i2x(numWeeks-3), upY)
-    ctx.stroke()
-    ctx.beginPath()
-    ctx.moveTo(i2x(numWeeks-3), downY)
-    ctx.lineTo(2*centerX-i2x(numWeeks-3), downY)
-    ctx.stroke()
-    ctx.beginPath()
-    ctx.arc(centerX, centerY, 2, 0, 2 * Math.PI, false)
-    ctx.fillStyle = 'green'
-    ctx.fill()
-    ctx.strokeStyle = '#FF0000'
-    ctx.fillStyle = '#000'
+    for i in [0..numWeeks]
+      if @chartData[i].epiweek == @currentEpweek
+        current = @chartData[i]
+        centerX = i2x(i)
+        centerY = ili2y(current.value)
+        [upY, downY] = [ili2y(current.value+current.std), ili2y(current.value-current.std)]
+        ctx.beginPath()
+        ctx.moveTo(centerX, upY)
+        ctx.lineTo(centerX, downY)
+        ctx.stroke()
+        ctx.beginPath()
+        ctx.moveTo(i2x(numWeeks-3), upY)
+        ctx.lineTo(2*centerX-i2x(numWeeks-3), upY)
+        ctx.stroke()
+        ctx.beginPath()
+        ctx.moveTo(i2x(numWeeks-3), downY)
+        ctx.lineTo(2*centerX-i2x(numWeeks-3), downY)
+        ctx.stroke()
+        ctx.beginPath()
+        ctx.arc(centerX, centerY, 2, 0, 2 * Math.PI, false)
+        ctx.fillStyle = 'green'
+        ctx.fill()
+        ctx.strokeStyle = '#FF0000'
+        ctx.fillStyle = '#000'
     # title and legend
     ctx.font = 12 * Math.min(1, w / 500) + 'px sans-serif'
     ctx.lineWidth = ctx.lineWidth/2


Iphone pinch final

Iphone pinch final

--- site/app.coffee
+++ site/app.coffee
@@ -223,39 +223,33 @@
         $("#button_view_#{name} i").removeClass('fa-circle-o')
         $("#button_view_#{name} i").addClass('fa-dot-circle-o')
         @setLocations(locations)
+    # TODO: Move the following into pointerinput
     [xs0,xs1,xe2,xe3,ys0,ys1,ye2,ye3] = [-1,-1,-1,-1,-1,-1,-1,-1]
     isPinching = false
     pinchZoom = () =>
       d1 = ((xs0-xs1)**2+(ys0-ys1)**2)**0.5
       d2 = ((xe2-xe3)**2+(ye2-ye3)**2)**0.5
-      #alert(xs0+"|"+xs1)
-      #alert(xe2+"|"+xe3)
-      #alert(d1+"|"+d2)
       if d1 > d2
         @zoomOut()
       else
         @zoomIn()
     pinchend = (e) =>
       if isPinching
-        if e.originalEvent.changedTouches.length == 2
-          #alert("2")
+        if e.originalEvent.changedTouches.length == 2
           xe2 = e.originalEvent.changedTouches[0].pageX
           xe3 = e.originalEvent.changedTouches[1].pageX
           ye2 = e.originalEvent.changedTouches[0].pageY
           ye3 = e.originalEvent.changedTouches[1].pageY
           isPinching = false
           pinchZoom()
-        if e.originalEvent.changedTouches.length == 1
-          #alert("4"+"|"+xe2+"|"+xe3)
+        if e.originalEvent.changedTouches.length == 1
           if xe2 < 0
-            #alert("3")
             xe2 = e.originalEvent.changedTouches[0].pageX
             ye2 = e.originalEvent.changedTouches[0].pageY
           else
             xe3 = e.originalEvent.changedTouches[0].pageX
             ye3 = e.originalEvent.changedTouches[0].pageY
             isPinching = false
-            #alert("1")
             pinchZoom()
     pinchstart = (e) =>
       if e.originalEvent.targetTouches.length == 2


Make it clearer that we're adding a property to an object

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -288,7 +288,8 @@
     # Mark AST nodes that correspond to expressions that (implicitly) return.
     # We can’t do this as part of `astNode` because we need to assemble child
     # nodes first before marking the parent being returned.
-    astNode.returns = yes if @astNode? and @canBeReturned
+    if @astNode? and @canBeReturned
+      Object.assign astNode, {returns: yes}
     astNode

   astInitialize: (o, level) ->


Fixed boolean on allowed actions

Fixed boolean on allowed actions

--- snippets/php.cson
+++ snippets/php.cson
@@ -225,7 +225,7 @@
      * @var array
      */
     private static \\$allowed_actions = array(
-    \t\'${1}\' => \'${2:true}\'
+    \t\'${1}\' => ${2:true}
     );
     """
   'hide_ancestor':


fix pascals-triangle tests

fix pascals-triangle tests

--- exercises/pascals-triangle/pascals-triangle.spec.coffee
+++ exercises/pascals-triangle/pascals-triangle.spec.coffee
@@ -1,26 +1,34 @@
 Pascal = require './pascals-triangle'

 describe 'Pascal', ->
-  it 'with one row', ->
-    arr = new PascalsTriangle(1)
-    expect(arr.array).toEqual [[1]]
+  it 'one row', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(0)).toEqual []

-  xit 'with two rows', ->
-    arr = new PascalsTriangle(2)
-    expect(arr.array).toEqual [[1], [1,1]]
+  xit 'two rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(1)).toEqual [[1]]

-  xit 'with three rows', ->
-    arr = new PascalsTriangle(3)
-    expect(arr.array).toEqual [[1], [1,1], [1,2,1]]
+  xit 'three rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(2)).toEqual [[1], [1,1]]

-  xit 'with four rows', ->
-    arr = new PascalsTriangle(4)
-    expect(arr.array).toEqual [[1], [1,1], [1,2,1], [1,3,3,1]]
+  xit 'four rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(3)).toEqual [[1], [1,1], [1,2,1]]

-  xit 'with five rows', ->
-    arr = new PascalsTriangle(5)
-    expect(arr.array).toEqual [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]]
+  xit 'five rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(4)).toEqual [[1], [1,1], [1,2,1], [1,3,3,1]]

-  xit 'should equal 20th row', ->
-    arr = new PascalsTriangle(20)
-    expect(arr.array[19]).toEqual [1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171, 19, 1]
+  xit 'five rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows(5)).toEqual [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1]]
+
+  xit 'six rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows[6]).toEqual [[1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1], [1, 5, 10, 10, 5, 1]]
+
+  xit 'ten rows', ->
+    pascal = new PascalsTriangle()
+    expect(pascal.rows[6]).toEqual [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1], [1, 6, 15, 20, 15, 6, 1], [1, 7, 21, 35, 35, 21, 7, 1], [1, 8, 28, 56, 70, 56, 28, 8, 1], [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -46,7 +46,7 @@
     ###*
     @name clamp
     @methodOf Point#
-    @r
+    @returns {Point} A new point
     ###
     clamp: (n) ->
       @copy().clamp$()


Display backup log if current doesn't exist, refs #428

--- server/lib/logs.coffee
+++ server/lib/logs.coffee
@@ -14,7 +14,12 @@
     # Return the log path for a given app slug.
     getLogPath: (slug) ->
         filename = "#{slug}.log"
-        return path.join '/', 'usr', 'local', 'var', 'log', 'cozy', filename
+        filepath = path.join '/', 'usr', 'local', 'var', 'log', 'cozy', filename
+        backuppath = "#{filepath}-backup"
+        if (not fs.existsSync filepath) and fs.existsSync backuppath
+            return backuppath
+        else
+            return filepath

     # Returns log content for given app.


added index to mongodb

added index to mongodb

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -16,12 +16,19 @@
 module.exports =
   open: (callback) ->
     MongoClient.connect 'mongodb://localhost:27017/popular_convention', (err, db) ->
-      return callback(err) if err
+      return callback(err) if err?
       dbserver = db
       worklogs = dbserver.collection 'worklogs'
       conventions = dbserver.collection 'conventions'
       score = dbserver.collection 'score'
-      callback()
+
+      # ensure index
+      dbserver.ensureIndex 'conventions', {timestamp: 1}, (err) ->
+        return callback(err) if err?
+        dbserver.ensureIndex 'score', {shortfile: 1, lang: 1}, (err) ->
+          return callback(err) if err?
+
+          callback()

   insertWorklogs: (doc, callback) ->
     worklogs.insert doc, callback


Support for ignoring set identifiers

Support for ignoring set identifiers

--- src/phabricator.coffee
+++ src/phabricator.coffee
@@ -8,6 +8,7 @@
 #   HUBOT_PHABRICATOR_USER=username
 #   HUBOT_PHABRICATOR_API=api-uri
 #   HUBOT_PHABRICATOR_CERT=certificate
+#   HUBOT_PHABRICATOR_IGNORE=[T1000,D999,etc]
 #
 # Commands:
 #   [TDPQFV]12345 - respond with a description of the phabricator object referenced
@@ -27,9 +28,14 @@
   conduit = createCanduit config,
     (error, conduit) ->

+  ignore = process.env.HUBOT_PHABRICATOR_IGNORE.replace(/\s+/g, '').split(',')
+
   # object (the TDPQFV bit is the things we recognize as prefixes)
   robot.hear /(?:^|[\[\s])([TDPQFV][0-9]+|r[A-Z]+[a-f0-9]+)(?:\s*(-v))?(?=\W|$)/g, (msg) ->
-    conduit.exec 'phid.lookup', {names: (match.trim() for match in msg.match)}, (error, result) ->
+    names = (match.trim() for match in msg.match when match.trim() in ignore)
+    if names.length == 0
+      return
+    conduit.exec 'phid.lookup', {names: names}, (error, result) ->
       if error
         return
       hits = ("^ #{info.fullName} - #{info.uri}" for phid, info of result).join("\n")


Add support for es6-module-loader.

Add support for es6-module-loader.

--- src/pipes/bower.coffee
+++ src/pipes/bower.coffee
@@ -58,13 +58,20 @@
             p.then -> func i
         , RSVP.Promise.resolve()

+    nameReq: "requirejs/require.js"
+    nameEML: "es6-module-loader/dist/es6-module-loader-sans-promises.js"
+
     getFileContent: (name) ->
         readFile name
         .then (content) =>
-            if name is "#{@state.directory}/requirejs/require.js"
+            if name is "#{@state.directory}/#{@nameReq}"
                 config = JSON.stringify {"packages":@bower.pkgConfig()}, 0, 4
                 content =
                     new Buffer """#{content}\nrequirejs.config(#{config});"""
+            else if name is "#{@state.directory}/#{@nameEML}"
+                mods = for p in @bower.pkgConfig()
+                    "System.paths['#{p.name}']='#{p.location}/#{p.main}.js';"
+                content = new Buffer content + "\n" + mods.join "\n"
             content
         .catch (err) =>
             @log "error", "Cannot read file #{name} #{err}"


Use multiple dues

Use multiple dues

--- src/Avatar.coffee
+++ src/Avatar.coffee
@@ -3,8 +3,8 @@
 class Avatar extends Entity
   constructor: ->
     super
-
-    @sprite = @host.spriteGroup.create(-100,-100, 'nigel')
+    @skin = @host.generator.pick(['nigel','bruce', 'julie', 'rachel'])
+    @sprite = @host.spriteGroup.create(-100,-100, @skin)
     @sprite.animations.add("down", [0, 1, 2, 1], 10, true)
     @sprite.animations.add("left", [4, 5, 6, 5], 10, true)
     @sprite.animations.add("right", [8, 9, 10, 9], 10, true)
@@ -19,10 +19,14 @@
     @sprite.position.y = state.y
     if @sprite.animations.currentAnim.name != state.anim
       @sprite.animations.play(state.anim)
+    if state.skin && @skin != state.skin
+      @skin = state.skin
+      @sprite.loadTexture(@skin)

   getState:(state)->
     x: @sprite.position.x,
-    y: @sprite.position.y
+    y: @sprite.position.y,
+    skin: @skin,
     anim: @sprite.animations.currentAnim.name

   despawn:->


Fix tyops

Fix tyops

--- src/Carrot.coffee
+++ src/Carrot.coffee
@@ -171,7 +171,6 @@
         'object_type' : opts['object_type']
         'object_instance_id' : opts['object_id']
       }
-      $.extend(params, opts)
       @postSignedRequest("/me/request.json", params, (jqXHR) =>
         carrotResponse = $.parseJSON(jqXHR.responseText)
         postMethod(carrotResponse.fb_data,
@@ -180,7 +179,7 @@
               for receivingUser in fbResponse.to
                 @ajaxPost("#{@scheme}://parsnip.gocarrot.com/request_send", {platform_id: carrotResponse.request_id, posting_user_id: @udid, user_id: receivingUser})

-            callback(carrotResopnse, fbResponse) if callback
+            callback(carrotResponse, fbResponse) if callback
         )
       )


Add a way to disable printing of @table

Add a way to disable printing of @table

--- src/quest.coffee
+++ src/quest.coffee
@@ -185,6 +185,7 @@
       try
         return cb()
       catch e
+        console.trace e
         if not okErrors or okErrors.some((regex) -> e.message.match regex)
           if times == 'forever' or times > 0
             console.error "Error occurred: #{e.message}".red.underline
@@ -217,7 +218,8 @@
   # ```coffee
   # @table @sql(file: 'foo.sql')
   # ```
-  table: (sqlResult, {trimWhitespace}={}) ->
+  table: (sqlResult, {trimWhitespace, print}={}) ->
+    print ?= true
     trimWhitespace ?= true
     if sqlResult.rows.length > 0
       columns = (field.name for field in sqlResult.fields)
@@ -239,7 +241,10 @@
               value ? ''

       table.push.apply table, values
-      console.log table.toString()
+      if print
+        console.log table.toString()
+      else
+        table
     else
       console.log "Nothing to output.".gray


TMonad::print -> TMonad::log

TMonad::print -> TMonad::log

--- src/skuld.coffee
+++ src/skuld.coffee
@@ -16,9 +16,9 @@
       switch Object.getOwnPropertyDescriptor(value, 'toString')
         when undefined then util.inspect value
         else value.toString()
-  print : () ->
+  log : () ->
     @bind (value) ->
-      console.log "[LOG: HH:MM:SS] #{value}"
+      util.log "#{value}"
       value.unit value

 #Actual!


[fix] redis values from multi...exec are unparsed JSON

--- src/redis-model.coffee
+++ src/redis-model.coffee
@@ -258,7 +258,7 @@
       ["hget", "bull:#{job.queue}:#{job.id}", "progress"]

     await (@redis.multi multi).exec ideally defer results
-    job.progress = results[i] for job, i in jobs
+    job.progress = JSON.parse results[i] for job, i in jobs
     callback null, jobs

   stacktraceForJobs: (jobs, callback) ->
@@ -268,7 +268,7 @@
       ["hget", "bull:#{job.queue}:#{job.id}", "stacktrace"]

     await (@redis.multi multi).exec ideally defer results
-    job.stacktrace = results[i] for job, i in jobs
+    job.stacktrace = JSON.parse results[i] for job, i in jobs
     callback null, jobs

   delayTimeForJobs: (jobs, callback) ->


add make.button

add make.button

--- src/render/make.coffee
+++ src/render/make.coffee
@@ -47,4 +47,11 @@
       _CHILDREN: children
       stroke: my.color.line
     }
+
+  button: (name, action, options = {}) ->
+    _.extend {
+      tag_name: 'button'
+      name:     name
+      click:    action
+    }, options
 }


Fix Token::isEqual() to accept scopes for now.

--- src/token.coffee
+++ src/token.coffee
@@ -26,7 +26,9 @@
     @hasPairedCharacter = textUtils.hasPairedCharacter(@value)

   isEqual: (other) ->
-    @value == other.value and _.isEqual(@scopeDescriptor, other.scopeDescriptor) and !!@isAtomic == !!other.isAtomic
+    # TODO: scopes is deprecated. This is here for the sake of lang package tests
+    scopeDescriptor = other.scopeDescriptor ? other.scopes
+    @value == other.value and _.isEqual(@scopeDescriptor, scopeDescriptor) and !!@isAtomic == !!other.isAtomic

   isBracket: ->
     /^meta\.brace\b/.test(_.last(@scopeDescriptor))


GameObject.Effect

GameObject.Effect

--- src/tween.coffee
+++ src/tween.coffee
@@ -22,7 +22,7 @@
       else
         f = Easing[data.easing](data.start, data.end)
         t = (I.age + elapsedTime
-        I[property] = f()
+        I[property] = f((t- data.startTime) / data.duration)

   ###*
   Modify the object's properties over time.


Bug using an existent context

Bug using an existent context

--- src/watch.coffee
+++ src/watch.coffee
@@ -331,8 +331,8 @@

   # Load config, get brunch packages from package.json.
   helpers.loadConfig options.config, configParams, (error, config) ->
-    logger.notifications = @config.notifications
-    logger.notificationsTitle = @config.notificationsTitle or 'Brunch'
+    logger.notifications = config.notifications
+    logger.notificationsTitle = config.notificationsTitle or 'Brunch'
     joinConfig = config._normalized.join
     plugins    = getPlugins packages, config


Don't bundle .jshintignore files

Don't bundle .jshintignore files

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -98,6 +98,7 @@
       '.lint'
       '.lintignore'
       '.eslintrc'
+      '.jshintignore'
     ]

     packageNames.forEach (packageName) -> ignoredPaths.push(path.join(packageName, 'spec'))


Reload member statistics on bulkDeleteStatisticData

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -344,7 +344,9 @@

       linking.unlinkItems toRemove, lookup

-      bulkDeleteStatisticData.call(this, member, event).fail((err) ->
+      bulkDeleteStatisticData.call(this, member, event).then(->
+        sdk.loadMemberStatistics(memberId: member.id)
+      ).fail((err) ->
         linking.linkItems toRemove, lookup
         err
       ).callback callback


Invincible blob honors its parent

Invincible blob honors its parent

--- src/Blob.coffee
+++ src/Blob.coffee
@@ -52,7 +52,6 @@
       if entBlob.invincible
         entBlob.invincible -= delta
         entBlob.invincible = null if entBlob.invincible < 0
-        continue
       entObj =
         x: entPos.x - entPos.radius
         y: entPos.y - entPos.radius
@@ -73,6 +72,7 @@
           pushOther entity, other
           return
         return if otherBlob.weight <= 0.1
+        return if entBlob.invincible
         return if otherBlob.invincible
         if entPos.collides otherPos
           # Bigger one eats smaller one


chore($core): all accessors are configurable

All the accessors created through the Function extensions
are now configurable.

--- src/spectacular.coffee
+++ src/spectacular.coffee
@@ -32,17 +32,20 @@
 Function::getter = (name, block) ->
   Object.defineProperty @prototype, name, {
     get: block
+    configurable: true
     enumerable: true
   }
 Function::setter = (name, block) ->
   Object.defineProperty @prototype, name, {
     set: block
+    configurable: true
     enumerable: true
   }
 Function::accessor = (name, options) ->
   Object.defineProperty @prototype, name, {
     get: options.get
     get: options.set
+    configurable: true
     enumerable: true
   }


introducing new raw, command escaping

introducing new raw, command escaping

--- src/MKTS.coffee
+++ src/MKTS.coffee
@@ -681,10 +681,12 @@
   return $ ( old_text, send ) ->
     new_text = old_text.replace /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\ufffd-\uffff]/g, ( $0 ) ->
       cid_hex = ( $0.codePointAt 0 ).toString 16
+      pre     = '█'
+      post    = '█'
       ### TAINT use mkts command ###
+      warn "detected (probably) illegal character U+#{cid_hex}" # if old_text isnt new_text
       return """{\\mktsStyleBold\\color{red}{%
         \\mktsStyleSymbol#{pre}}U+#{cid_hex}{\\mktsStyleSymbol#{post}}}"""
-    warn "detected (probably) illegal character U+#{cid_hex}" if old_text isnt new_text
     send new_text

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


Add a widget support

Add a widget support

--- vendor/assets/javascripts/dashing/dashing-src.coffee
+++ vendor/assets/javascripts/dashing/dashing-src.coffee
@@ -22,32 +22,62 @@
 		num

 class window.Dashing extends Batman.App
-# 	@root ->
+#		@root ->
 # Dashing.params = Batman.URI.paramsFromQuery(window.location.search.slice(1));

 class Dashing.Widget extends Batman.View
 	constructor: ->
 		# Set the view path
 		@constructor::source = Batman.Filters.underscore(@constructor.name)
-
 		super

 		@observe 'node', (newValue, oldValue) ->
-
+
 			if !oldValue && !@_registeredAtDashing
 				@_registeredAtDashing = true
+				@_nodeData = $(@node).data() if $(@node).data().id
+
+				@_renderNode()
+
+
+	attachTo: (@el_id) =>
+		@_nodeData = $(@el_id).data()
+
+		if @node
+			@_renderNode()
+
+	_renderNode: () =>
+
+		if @_nodeData
+
+			console.log @node
+			console.log @_nodeData
+
+			$(@el_id).replaceWith($(@node)) if @el_id
+			$(@node).data(@_nodeData)
+			$(@node).attr('id', @el_id.slice(1)) if @el_id
+			#@insertIntoDOM(document.getElementById(@el_id)) if @el_id
+
+
+			@mixin(@_nodeData)
+
+			Dashing.widgets[@id] ||= []
+			Dashing.widgets[@id].push(@)

-				@mixin($(@node).data())
-				Dashing.widgets[@id] ||= []
-				Dashing.widgets[@id].push(@)
+			# in case the events from the server came
+			# before the widget was rendered
+			@mixin(Dashing.lastEvents[@id])

-				# in case the events from the server came
-				# before the widget was rendered
-				@mixin(Dashing.lastEvents[@id])
-
-				type = Batman.Filters.dashize(@constructor.name)
-				$(@node).addClass("widget widget-#{type} #{@id}")
-
+			type = Batman.Filters.dashize(@constructor.name)
+			$(@node).addClass("widget widget-#{type} #{@id}")
+
+			@initializeBindings() if @el_id
+
+
+
+
+
+
 	@accessor 'updatedAtMessage', ->
 		if updatedAt = @get('updatedAt')
 			timestamp = new Date(updatedAt * 1000)


check if bufferstream is enabled

check if bufferstream is enabled

--- src/test/stream.coffee
+++ src/test/stream.coffee
@@ -86,6 +86,7 @@
         buffer.on 'data', (data) -> æ.equal data.toString(), readme.shift()
         buffer.on 'end', ->
             æ.equal buffer.length, 0
+            æ.equal buffer.enabled, yes # size none desnt mean we cant split
             æ.equal buffer.toString(), ""
             æ.deepEqual readme, [ "END" ]
             æ.done()


Note the default value of ::rangeForRow includeNewline

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -1544,7 +1544,7 @@
   # * `row` A {Number} representing a 0-indexed row.
   # * `includeNewline` A {Boolean} indicating whether or not to include the
   #   newline, which results in a range that extends to the start
-  #   of the next line.
+  #   of the next line. (default: `false`)
   #
   # Returns a {Range}.
   rangeForRow: (row, includeNewline) ->


Append line ending if it isn’t present

Append line ending if it isn’t present

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -856,8 +856,7 @@
           insertPosition = Point.fromObject([startRow - insertDelta])
           endPosition = Point.min([endRow + 1], @buffer.getEndPosition())
           lines = @buffer.getTextInRange([[startRow], endPosition])
-          if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row)
-            lines = "#{lines}\n"
+          lines += @buffer.lineEndingForRow(endPosition.row - 1) unless lines[lines.length - 1] is '\n'

           @buffer.deleteRows(startRow, endRow)


Testing

Testing

--- widgets/cusi_progress_bars/cusi_progress_bars.coffee
+++ widgets/cusi_progress_bars/cusi_progress_bars.coffee
@@ -119,7 +119,8 @@
       currentValue = from

       # Every x milliseconds, the function should run.
-      stepInterval = 16.667
+#      stepInterval = 16.667
+      stepInterval = 1.667

       # Change the duration based on the distance between points.
       duration = baseDuration + Math.abs(endpointDifference) * 25


Fix issue vatesfr/xo-web/issues/643

Fix issue vatesfr/xo-web/issues/643

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -108,7 +108,7 @@
         # UUID of the network to create the interface in.
         network: { type: 'string' }

-        MAC: {
+        mac: {
           optional: true # Auto-generated per default.
           type: 'string'
         }


vm.create(): fix setting name/description for disks (vatesfr/xo-web#774).

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -85,12 +85,12 @@
     objectIds.push(sr.id)
     limits.disk += size

-    return {
-      device: vdi.device ? device.position,
+    return $assign({}, vdi, {
+      device: vdi.device ? vdi.position,
       size,
       SR: sr._xapiId,
       type: vdi.type
-    }
+    })
   )

   xapi = @getXapi(template)


prevent change to navbar url in pdf viewer

return false from pdf <a> click handler

--- services/web/public/coffee/ide/pdfng/directives/pdfAnnotations.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfAnnotations.coffee
@@ -41,4 +41,5 @@
 					element.href = "#" + link.dest;
 					element.onclick = (e) =>
 						@navigateFn link
+						return false
 	]


[nj] fix not retrieved `this.orig.cwd` when `grunt-newer` used

Updated by `grunt-newer` files will have `cwd` available under deeper path: `this.cwd.orig.orig`

--- modules/nunjucks-task.coffee
+++ modules/nunjucks-task.coffee
@@ -51,7 +51,7 @@
             configureEnvironment.call(@, env, nunjucks)

         preprocessData: (data) ->
-          pagepath   = humanReadableUrl(@src[0].replace(@orig.cwd, ''), humanReadableUrlsExclude)
+          pagepath   = humanReadableUrl(@src[0].replace((@orig.cwd or @orig.orig.cwd), ''), humanReadableUrlsExclude)
           breadcrumb = crumble(pagepath)
           matter     = if typeof matter == 'function' then matter() else matter
           pageProps  = (get(matter, breadcrumb) or {}).props


Fix table to rehighlight selected after sort

--- src/ubret/table.coffee
+++ src/ubret/table.coffee
@@ -32,6 +32,9 @@
       .enter().append('td')
         .text( (d) -> return d)

+    if @selected?
+      @highlightRow()
+
   compare: (a, b) ->
     if typeof a is 'string'
       return a.localeCompare b
@@ -52,9 +55,12 @@
     @createRows key

   selectRow: (datum) =>
-    @selected.attr('class', '') unless typeof @selected is 'undefined'
-    @selected = @tbody.select("[data-id=#{datum.id}]")
-      .attr('class', 'selected')
+    @tbody.select("[data-id=#{@selected}]").attr('class', '') unless typeof @selected is 'undefined'
+    @selected = datum.id
+    @highlightRow()
+
+  highlightRow: =>
+    @tbody.select("[data-id=#{@selected}]").attr('class', 'selected')

   changeData: (data) =>
     @data = data


Typo (thanks bae)

Typo (thanks bae)

--- packages/shrub-core/client/index.coffee
+++ packages/shrub-core/client/index.coffee
@@ -9,8 +9,8 @@

 exports.pkgmanRegister = (registrar) ->

-	# ## Implements hook `aHrefSanitizationWhilelist`
-	registrar.registerHook 'aHrefSanitizationWhilelist', -> [
+	# ## Implements hook `aHrefSanitizationWhitelist`
+	registrar.registerHook 'aHrefSanitizationWhitelist', -> [
 		'(?:https?|ftp|mailto|tel|file):'
 		'javascript:void(?:%20)*\\((?:%20)*0(?:%20)*\\)'
 	]
@@ -25,7 +25,7 @@
 			# attributes.
 			regexes = []
 			for regexes_ in pkgmanProvider.invokeFlat(
-				'aHrefSanitizationWhilelist'
+				'aHrefSanitizationWhitelist'
 			)
 				regexes.push regex for regex in regexes_


add "locked" as a color

add "locked" as a color

--- src/badges.coffee
+++ src/badges.coffee
@@ -103,6 +103,7 @@

 colorList= {
   '': '',
+  'locked': 0,
   'bronze': 1,
   'silver': 2,
   'gold': 3,


Tidy up rescale code Set sensible limits on the SVG bounding box

--- overrides.coffee
+++ overrides.coffee
@@ -12,18 +12,18 @@
 ms.rescale = (x, y, width, height) ->
   return if @root.el.getBoundingClientRect().width is 0 # don't rescale when surface isn't visible
   currentViewBox = @svg.attr('viewBox')?.split /\s+/
-  x ?= currentViewBox?[0] ? 0
-  y ?= currentViewBox?[1] ? 0
-  width ?= currentViewBox?[2] ? 0
-  height ?= currentViewBox?[3] ? 0
-  x = parseInt x
-  y = parseInt y
-  width = parseInt width
-  height = parseInt height
+  x ?= parseInt currentViewBox?[0] ? 0
+  y ?= parseInt currentViewBox?[1] ? 0
+  width ?= parseInt currentViewBox?[2] ? 0
+  height ?= parseInt currentViewBox?[3] ? 0
   @svg.attr 'viewBox', [x, y, width, height].join ' '
-  @scaleX = @root.el.getBoundingClientRect().width / subjectViewer.maxWidth
-  @scaleY = @root.el.getBoundingClientRect().height / subjectViewer.maxHeight
-  @magnification = @root.el.getBoundingClientRect().width / subjectViewer.maxWidth
+
+  root = @root.el.getBoundingClientRect()
+  @scaleX = root.width / subjectViewer.maxWidth
+  @scaleY = root.height / subjectViewer.maxHeight
+  @magnification = root.width / subjectViewer.maxWidth
+
+  # recalculate the viewbox so that the aspect ratio matches the SVG element
   w = parseInt @svg.attr 'width'
   h = parseInt @svg.attr 'height'
   w = w / @magnification
@@ -32,7 +32,8 @@
   y = y + .5 * (height - h)
   x = Math.max x, 0
   y = Math.max y, 0
-
+  x = Math.min x, subjectViewer.maxWidth - w
+  y = Math.min y, subjectViewer.maxHeight - h
   @svg.attr 'viewBox', [x, y, w, h].join ' '
   @renderTools()


Check loaded models before doing a find in belongsTo

--- src/batman.coffee
+++ src/batman.coffee
@@ -2321,9 +2321,13 @@
   getAccessor: (model, label, relatedModel) ->
     if relatedRecord = @_batman.attributes?[label]
       relatedRecord
-    else if relatedID = @get(label + "_id")
-      relatedModel.find relatedID, (error, loadedRecord) ->
-        throw error if error
+    else if relatedID = @get("#{label}_id")
+      loadedRecord = relatedModel.get('loaded').indexedBy('id').get(relatedID)
+      unless loadedRecord.isEmpty()
+        return loadedRecord.toArray()[0]
+      else
+        relatedModel.find relatedID, (error, loadedRecord) ->
+          throw error if error

   save: (base) ->
     # saveEvent is allowed but not fired, because base's storage op still needs to happen


remove bespoke decision tree focus management

--- readymade/overrides.coffee
+++ readymade/overrides.coffee
@@ -11,20 +11,5 @@
   if classify_page.tutorial.el.getAttribute('aria-hidden') is 'false'
     setTimeout => classify_page.tutorial.active_button?.focus()

-# focus management for the decision tree.
-decisionTree.autofocus = false
-
-classify_page.el.on decisionTree.LOAD_TASK, ({originalEvent: detail: {index}})->
-  unless decisionTree.autofocus
-    decisionTree.autofocus = true
-    return
-
-  if index > 0
-    decisionTree.backButton.focus()
-  else
-    decisionTree.el.querySelector('input').focus()
-
-classify_page.el.on decisionTree.COMPLETE, (e)->
-  document.querySelector('button[name=readymade-dont-talk]').focus()


Fixed bug in oauth error management (missing origin or referer)

--- src/presentationLayer/auth/index.coffee
+++ src/presentationLayer/auth/index.coffee
@@ -1,6 +1,7 @@
 Url = require 'url'
 async = require 'async'
 UAParser = require 'ua-parser-js'
+restify = require 'restify'

 module.exports = (env) ->
 	init: () ->


Restore cloning of ::loadSettings in ::getLoadSettings

It's a deprecated method, but if you call it you'll get a clone.

--- src/atom.coffee
+++ src/atom.coffee
@@ -217,7 +217,7 @@
   # Deprecated: Just access the loadSettings property directly. Eventually I want
   # both to go away and just store the relevant info on the atom global itself.
   getLoadSettings: ->
-    @loadSettings
+    _.deepClone(@loadSettings)

   # Private:
   deserializeProject: ->


Make Atom::initialize private

Make Atom::initialize private

--- src/atom.coffee
+++ src/atom.coffee
@@ -144,7 +144,7 @@
     DeserializerManager = require './deserializer-manager'
     @deserializers = new DeserializerManager()

-  # Public: Sets up the basic services that should be available in all modes
+  # Sets up the basic services that should be available in all modes
   # (both spec and application).
   #
   # Call after this instance has been assigned to the `atom` global.


Add checking before date casting

Add checking before date casting

--- src/cell.coffee
+++ src/cell.coffee
@@ -70,7 +70,8 @@
             if Utils.isNumber(val)
               val = Number(val)
         if @_.styleMixin.style.numberFormat.isDate()
-          val = Utils.offsetToDate(val)
+          if Utils.isNumber(val)
+            val = Utils.offsetToDate(val)
         val

       set: (val) ->


Change precompilation directive to match espy's style.

--- src/bundle.coffee
+++ src/bundle.coffee
@@ -148,9 +148,9 @@
             $ = window.$
             scripts = getScripts window, @root
             scripts.forEach (script) =>
-                if script.type and tilt.getHandlerByMime script.type
-                    s = @find '/' + script.src
-                    s.compilerType = 'precompiler'
+                if ($ script).data 'precompile'
+                    s = @find '/' + normalize script.src
+                    s.compilerType = 'precompiler'

             callback()


remove block comment, add eof --- comment

--- editor-support/atom/language-unison/grammars/unison.cson
+++ editor-support/atom/language-unison/grammars/unison.cson
@@ -93,7 +93,8 @@

   comment:
     patterns: [
-      {include: "#block_comment"}
+      # {include: "#block_comment"}
+      {include: "#eof_comment"}
       {include: "#line_comment"}
     ]
   block_comment:
@@ -110,3 +111,7 @@
   line_comment:
     name: 'comment.line.double-dash.unison'
     match: '--.*$'
+  eof_comment:
+    name: 'comment.block.eof.unison'
+    begin: '^---'
+    end: '\\z'


add an install step to install freedom-for-node for zork-node (#2595)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -314,6 +314,11 @@
       }
       androidReplaceXwalkDist: {
         command: './replace_xwalk_in_apk.sh release'
+      }
+      installFreedomForNodeForZork: {
+        # This allows our Docker containers, which do not have access to the
+        # git repo's "top-level" node_modules/ folder find freedom-for-node.
+        command: 'npm install --prefix build/dev/uproxy/lib/samples/zork-node freedom-for-node'
       }
     }

@@ -620,7 +625,6 @@
           localDestPath: 'lib/samples/zork-firefoxapp/data/'
       libsForZorkNode:
         Rule.copyLibs
-          npmLibNames: ['freedom-for-node']
           pathsFromDevBuild: ['lib/churn-pipe', 'lib/loggingprovider', 'lib/zork']
           pathsFromThirdPartyBuild: ['freedom-port-control']
           localDestPath: 'lib/samples/zork-node/'
@@ -1095,6 +1099,7 @@
     'copy:libsForZorkChromeApp'
     'copy:libsForZorkFirefoxApp'
     'copy:libsForZorkNode'
+    'exec:installFreedomForNodeForZork'
   ]

   grunt.registerTask 'version_file', [


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -39,7 +39,6 @@
       I.scroll.x = I.scroll.x.approach(centerRect.right, I.maxVelocity.x)
     else if objectCenter.x < centerRect.left
       I.scroll.x = I.scroll.x.approach(centerRect.right, I.maxVelocity.x)
-

     I.scroll = Point(


Updated comment

Updated comment

--- src/checkt.coffee
+++ src/checkt.coffee
@@ -66,7 +66,10 @@
     checkWrapper = ->
       if udefine.env.browser then bindCheckToGlobal() else check

+    # Register checkt module
+    udefine 'checkt', checkWrapper
+
+    # Register alternative module name
     udefine 'check', checkWrapper
-    udefine 'checkt', checkWrapper
   else
     bindCheckToGlobal()


hub: add back file_read_from_project event

--- src/smc-hub/local_hub_connection.coffee
+++ src/smc-hub/local_hub_connection.coffee
@@ -297,6 +297,10 @@
                         @mesg_query_get_changefeed_ids(mesg, write_mesg)
                     when 'file_written_to_project'
                         # ignore -- don't care; this is going away
+                        return
+                    when 'file_read_from_project'
+                        # handle elsewhere by the code that requests the file
+                        return
                     else
                         write_mesg(message.error(error:"unknown event '#{mesg.event}'"))
             return


removed old sharing code

removed old sharing code

--- src/taxonomyViz.coffee
+++ src/taxonomyViz.coffee
@@ -1785,97 +1785,7 @@
 		if data.status is 'ok'
 			src = document.location.origin + document.location.pathname + "?shareID=" + data.urlHash
 			d3.select('#sharingInfo .results').append('a').attr('href',src).attr('target','_blank').text(src)
-	###
-
-	shareViz: () =>
-		biomData = JSON.stringify(biom)
-		# console.log 'share'
-		# console.log CryptoJS.SHA1(biomData).toString()
-		$('#sharingInfo').show()
-		if shareFlag
-			$('#sharingInfo .shareForm input, #sharingInfo .shareForm label, #sharingInfo .shareForm textarea').show();
-			$('#sharingInfo #shareToEmail').val("")
-			$('#sharingInfo #shareToName').val("")
-			$('#sharingInfo .results').hide();
-		else
-			$('#sharingInfo .loadingText').text('Preparing data ... ')
-
-		w = new Worker('scripts/hashWorker.js')
-		w.addEventListener('message', (e) =>
-			console.log 'worker message'
-			# console.log e
-			hashValue = e.data
-			@shareHashExists(hashValue)
-		)
-		w.postMessage(biomData)

-	shareHashExists: (hash) ->
-		@shareHash = hash
-		hashExistsEndpoint = backendServer + "hashExists.php"
-		$.get(hashExistsEndpoint, {hash: hash}, @shareHashExistsCallback)
-
-	shareHashExistsCallback: (data, textStatus, xhr) =>
-		# console.log data
-		@shareHashExists = data
-		$('#sharingInfo .loading').hide()
-		$('#sharingInfo .shareForm').show()
-		hideShare = (e) -> $('#sharingInfo').fadeOut(200);
-		$('#sharingInfo .icon-remove').off('click', hideShare).on('click', hideShare)
-		$('#sharingInfo .shareButton').off('click', @submitShare).on('click', @submitShare)
-
-	submitShare: () =>
-		console.log 'submit share'
-		console.log @shareHash
-		console.log LayerID + " " + VizID
-		layerName = layerNameArr[LayerID - 1]
-		vizName = vizNameArr[VizID-1]
-
-		if @validateEmail($('#sharingInfo #shareFromEmail').val()) and @validateEmail($('#sharingInfo #shareToEmail').val())
-			shareFlag = true
-			$('#sharingInfo .shareForm input, #sharingInfo .shareForm label, #sharingInfo .shareForm textarea').hide();
-			$('#sharingInfo .results').remove();
-			$('#sharingInfo .results').show();
-			results = d3.select('#sharingInfo').append('div').attr('class','results')
-			results.append('div').html('Your visualization has been shared. <br/> Wait until the link is generated below: ')
-			@shareData = {
-				from_email: $('#sharingInfo #shareFromEmail').val(),
-				to_email: $('#sharingInfo #shareToEmail').val(),
-				from_name: $('#sharingInfo #shareFromName').val(),
-				to_name: $('#sharingInfo #shareToName').val(),
-				notes: $('#sharingInfo #shareNotes').val(),
-				biom_file_hash: @shareHash,
-				layer_name: layerName,
-				filter_options_json: JSON.stringify(filterOptionJSON),
-				viz_name: vizName
-			}
-			if @shareHashExists is 'true'
-				@shareRequest()
-			else
-				@generateBiomZip()
-		else
-			alert("Invalid email address ... ")
-
-	generateBiomZip: () =>
-		biomData = JSON.stringify(biom)
-		console.log biomData.length
-		w = new Worker('scripts/zipWorker.js')
-		w.addEventListener('message', (e) =>
-			# console.log e.data
-			@shareData.biomFile = e.data
-			@shareRequest()
-		)
-		w.postMessage(biomData)
-
-	shareRequest: () =>
-		shareEndpoint = backendServer + "shareViz.php"
-		$.post(shareEndpoint, @shareData, @shareCallback, 'json')
-
-	shareCallback: (data, textStatus, xhr) =>
-		console.log(data)
-		if data.status is 'ok'
-			src = document.location.origin + document.location.pathname + "?shareID=" + data.urlHash
-			d3.select('#sharingInfo .results').append('a').attr('href',src).attr('target','_blank').text(src)
-	###
 	validateEmail: (email) ->
 		re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
 		return re.test(email);


:art: Rename to this.didChangeTextPatch

:art: Rename to this.didChangeTextPatch

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -741,8 +741,8 @@

     @changeCount++
     @stoppedChangingPatch.splice(oldRange.start, oldRange.getExtent(), newRange.getExtent(), text: newText)
-    @transactionPatch ?= new Patch
-    @transactionPatch.splice(oldRange.start, oldRange.getExtent(), newRange.getExtent(), text: newText)
+    @didChangeTextPatch ?= new Patch
+    @didChangeTextPatch.splice(oldRange.start, oldRange.getExtent(), newRange.getExtent(), text: newText)
     @emitter.emit 'did-change', changeEvent

   # Public: Delete the text in the given range.
@@ -1469,10 +1469,10 @@
       markerLayer.emitChangeEvents(snapshot?[markerLayerId])

   emitChangeTextEvent: ->
-    return unless @transactionPatch?
-
-    @emitter.emit 'did-change-text', Object.freeze(@transactionPatch.getChanges())
-    @transactionPatch = null
+    return unless @didChangeTextPatch?
+
+    @emitter.emit 'did-change-text', Object.freeze(@didChangeTextPatch.getChanges())
+    @didChangeTextPatch = null

   # Identifies if the buffer belongs to multiple editors.
   #