chore(Gruntfile): Indents are 2 spaces

chore(Gruntfile): Indents are 2 spaces

Totally subjective, but I think it makes the Gruntfile
easier to read.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,746 +1,657 @@
 # Gruntfile
-#   for
+# for
 # fineuploader

 # the 'wrapper' function
 module.exports = (grunt) ->

-    require('time-grunt')(grunt)
-    fs = require 'fs'
-
-    # Utilities
-    # ==========
-    path = require 'path'
-
-    # Package
-    # ==========
-    pkg = require './package.json'
-
-    # Paths
-    # ==========
-    paths =
-        'dist': './_dist'
-        'build': './_build'
-        'src': './client'
-        'docs': './docs'
-        'test': './test'
-
-    # Browsers
-    # ==========
-    browsers = require "#{paths.test}/browsers"
-
-    # Modules
-    # ==========
-    fineUploaderModules = require './lib/fineuploader.modules'
-
-    # Configuration
-    # ==========
-    grunt.initConfig
-
-        # Package
-        # --------
-        pkg: pkg
-
-
-        # Modules
-        # ----------
-        # works
-        bower:
-            install:
-                options:
-                    targetDir: "#{paths.test}/vendor"
-                    install: true
-                    cleanTargetDir: true
-                    cleanBowerDir: true
-                    layout: 'byComponent'
-
-        # Clean
-        # --------
-        clean:
-            build:
-                files:
-                    src: paths.build
-            dist:
-                files:
-                    src: paths.dist
-            test:
-                files:
-                    src: ["#{paths.test}/temp*", "#{paths.test}/coverage"]
-            vendor:
-                files:
-                    src: "#{paths.test}/vendor/*"
-
-        # Banner
-        # ----------
-        usebanner:
-            header:
-                src: ["#{paths.build}/*.{js,css}"]
-                options:
-                    position: 'top'
-                    banner: '''
-                            /*!
-                             * <%= pkg.title %>
-                             *
-                             * Copyright 2013, <%= pkg.author %> info@fineuploader.com
-                             *
-                             * Version: <%= pkg.version %>
-                             *
-                             * Homepage: http://fineuploader.com
-                             *
-                             * Repository: <%= pkg.repository.url %>
-                             *
-                             * Licensed under GNU GPL v3, see LICENSE
-                             */ \n\n'''
-            footer:
-                src: ["#{paths.build}/*.{js,css}"]
-                options:
-                    position: 'bottom'
-                    banner: '/*! <%= grunt.template.today("yyyy-mm-dd") %> */\n'
-
-        # Complexity
-        # ----------
-        complexity:
-                src:
-                    files:
-                        src: ["#{paths.src}/js/*.js"]
-                    options:
-                        errorsOnly: false # show only maintainability errors
-                        cyclomatic: 5
-                        halstead: 8
-                        maintainability: 100
-                        #jsLintXML: 'report.xml' # create XML JSLint-like report
-
-        # Concatenate
-        # --------
-        concat:
-            core:
-                src: fineUploaderModules.mergeModules 'core', 'traditional'
-                dest: "#{paths.build}/<%= pkg.name %>.js"
-            coreS3:
-                src: fineUploaderModules.mergeModules 'core', 's3'
-                dest: "#{paths.build}/s3.<%= pkg.name %>.js"
-            jquery:
-                src: fineUploaderModules.mergeModules 'jquery', 'traditional'
-                dest: "#{paths.build}/jquery.<%= pkg.name %>.js"
-            jqueryS3:
-                src: fineUploaderModules.mergeModules 'jquery', 's3'
-                dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.js"
-            all:
-                src: fineUploaderModules.mergeModules 'all'
-                dest: paths.build + "/all.<%= pkg.name %>.js"
-            css:
-                src: ["#{paths.src}/*.css"]
-                dest: "#{paths.build}/<%= pkg.name %>.css"
-
-
-        # Uglify
-        # --------
-        uglify:
-            options:
-                mangle: true
-                compress: true
-                report: 'min'
-                preserveComments: 'some'
-            core:
-                src: ['<%= concat.core.dest %>']
-                dest: "#{paths.build}/<%= pkg.name %>.min.js"
-            jquery:
-                src: ['<%= concat.jquery.dest %>']
-                dest: "#{paths.build}/jquery.<%= pkg.name %>.min.js"
-            coreS3:
-                src: ['<%= concat.coreS3.dest %>']
-                dest: "#{paths.build}/s3.<%= pkg.name %>.min.js"
-            jqueryS3:
-                src: ['<%= concat.jqueryS3.dest %>']
-                dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.min.js"
-            all:
-                src: ['<%= concat.all.dest %>']
-                dest: "#{paths.build}/all.<%= pkg.name %>.min.js"
-
-        # Copy
-        # ----------
-        # @noworks
-        copy:
-            dist:
-                files: [
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.js', '!all.*', '!s3.*', '!*.min.js', '!jquery*', '!*iframe*']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: [ '!all.*', 's3.*.js', '!*.min.js', '!s3.jquery*', '!*iframe*']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.<%= pkg.name %>-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.min.js',  '!all.*', '!s3.*', '!jquery*']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '-<%= pkg.version %>.min.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['s3.*.min.js', '!s3.jquery*']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['jquery*js', '!s3.*', '!*.min.js']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.<%= pkg.name %>-<%= pkg.version %>.js'
-                    },
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['s3.jquery*js', '!*.min.js']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.js'
-                    },
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['jquery*min.js']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['s3.jquery*min.js']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.min.js'
-                    }
-                    {
-                        expand: true
-                        cwd: "./#{paths.src}/js/"
-                        src: ['iframe.xss.response.js']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.xss.response-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: "./#{paths.src}/js/"
-                        src: ['iframe.xss.response.js']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.xss.response-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: "./#{paths.src}/js/"
-                        src: ['iframe.xss.response.js']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.xss.response-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: "./#{paths.src}/js/"
-                        src: ['iframe.xss.response.js']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                        ext: '.xss.response-<%= pkg.version %>.js'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.src
-                        src: ['*.gif']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: paths.src
-                        src: ['*.gif']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: paths.src
-                        src: ['*.gif']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: paths.src
-                        src: ['*.gif']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: './'
-                        src: ['LICENSE']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: './'
-                        src: ['LICENSE']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: './'
-                        src: ['LICENSE']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: './'
-                        src: ['LICENSE']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.min.css']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.min.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.min.css']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.min.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.css', '!*.min.css']
-                        dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.css', '!*.min.css']
-                        dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.min.css']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.min.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.min.css']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.min.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.css', '!*.min.css']
-                        dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.css'
-                    }
-                    {
-                        expand: true
-                        cwd: paths.build
-                        src: ['*.css', '!*.min.css']
-                        dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>"
-                        ext: '-<%= pkg.version %>.css'
-                    }
-                ]
-            build:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{paths.src}/js/"
-                        src: ['iframe.xss.response.js']
-                        dest: paths.build
-                    },
-                    {
-                        expand: true
-                        cwd: paths.src
-                        src: ['*.gif']
-                        dest: paths.build
-                    }
-                ]
-            test:
-                expand: true
-                flatten: true
-                src: ["#{paths.build}/*"]
-                dest: "#{paths.test}/temp"
-            images:
-                files: [
-                    expand: true
-                    cwd: paths.src
-                    src: ['*.gif']
-                    dest: paths.build
-                ]
-
-
-        # Compress
-        # ----------
-        compress:
-            jquery:
-                 options:
-                     archive: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>.zip"
-                 files: [
-                     {
-                         expand: true
-                         cwd: paths.dist
-                         src: './jquery.<%= pkg.name %>-<%= pkg.version %>/*'
-                     }
-                 ]
-             jqueryS3:
-                  options:
-                      archive: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>.zip"
-                  files: [
-                      {
-                          expand: true
-                          cwd: paths.dist
-                          src: './s3.jquery.<%= pkg.name %>-<%= pkg.version %>/*'
-                      }
-                  ]
-            core:
-                options:
-                    archive: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>.zip"
-                files: [
-                    {
-                        expand: true
-                        cwd: paths.dist
-                        src: './<%= pkg.name %>-<%= pkg.version %>/*'
-                    }
-                ]
-            coreS3:
-                options:
-                    archive: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>.zip"
-                files: [
-                    {
-                        expand: true
-                        cwd: paths.dist
-                        src: './s3.<%= pkg.name %>-<%= pkg.version %>/*'
-                    }
-                ]
-
-        # cssmin
-        # ---------
-        # @works
-        cssmin:
-            options:
-                banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
-                report: 'min'
-            files:
-                src: '<%= concat.css.dest %>'
-                dest: "#{paths.build}/<%= pkg.name %>.min.css"
-
-        # Lint
-        # --------
-        # @nowork
-        jshint:
-            source: ["#{paths.src}/js/*.js"]
-            tests: ["#{paths.test}unit/*.js"]
-            options:
-                validthis: true
-                laxcomma: true
-                laxbreak: true
-                browser: true
-                eqnull: true
-                debug: true
-                devel: true
-                boss: true
-                expr: true
-                asi: true
-
-        # Run linter on coffeescript files
-        # ----------
-        # @works
-        coffeelint:
-            options:
-                indentation:
-                    level: 'ignore'
-                no_trailing_whitespace:
-                    level: 'ignore'
-                max_line_length:
-                    level: 'ignore'
-            grunt: './Gruntfile.coffee'
-
-
-        # Server to run tests against and host static files
-        # ----------
-        # @works
-        connect:
-            root_server:
-                options:
-                    base: "."
-                    hostname: "0.0.0.0"
-                    port: 9000
-                    keepalive: true
-            test_server:
-                options:
-                    base: "test"
-                    hostname: "0.0.0.0"
-                    port: 9001
-
-        version:
-            options:
-                pkg: pkg,
-                prefix: '[^\\-][Vv]ersion[\'"]?\\s*[:=]\\s*[\'"]?'
-            major:
-                options:
-                    release: 'major'
-                src: fineUploaderModules.modules.versioned
-            minor:
-                options:
-                    release: 'minor'
-                src: fineUploaderModules.modules.versioned
-            hotfix:
-                options:
-                    release: 'patch'
-                src: fineUploaderModules.modules.versioned
-            build:
-                options:
-                    release: 'build'
-                src: fineUploaderModules.modules.versioned
-
-        # Watching for changes
-        # ----------
-        # @works
-        watch:
-            options:
-                interrupt: true
-                debounceDelay: 250
-            js:
-                files: ["#{paths.src}/js/*.js", "#{paths.src}/js/s3/*.js"]
-                tasks: [
-                    'build'
-                    'copy:test'
-                    'connect:test_server'
-                    'mocha'
-                ]
-            test:
-                files: ["#{paths.test}/unit/*.js", "#{paths.test}/unit/s3/*.js"]
-                tasks: [
-                    'jshint:tests'
-                    'test'
-                ]
-            grunt:
-                files: ['./Gruntfile.coffee']
-                tasks: [
-                    'coffeelint:grunt'
-                    'build'
-                    'test'
-                ]
-            images:
-                files: ["#{paths.src}/*.gif"]
-                tasks: [
-                    'copy:images'
-                ]
-
-        # Increment version with semver
-        # ----------
-        version:
-            options:
-                pkg: pkg,
-                prefix: '[^\\-][Vv]ersion[\'"]?\\s*[:=]\\s*[\'"]?'
-            major:
-                options:
-                    release: 'major'
-                src: versioned
-            minor:
-                options:
-                    release: 'minor'
-                src: versioned
-            hotfix:
-                options:
-                    release: 'patch'
-                src: versioned
-            build:
-                options:
-                    release: 'build'
-                src: versioned
-
-        # Test
-        # ----------
-        mocha:
-            all:
-                options:
-                    urls: ['http://localhost:9001/index.html']
-                    log: true
-                    mocha:
-                        ignoreLeaks: false
-                    reporter: 'Spec'
-                    run: false
-
-        # Saucelas + Mocha
-        # ---------
-        'saucelabs-mocha':
-            all:
-                options:
-                    urls: ['http://localhost:9001/index.html']
-                    tunneled: true
-                    concurrency: 3
-                    identifier: process.env.TRAVIS_JOB_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
-                    tags: [ process.env.SAUCE_USERNAME+"@"+process.env.TRAVIS_BRANCH || process.env.SAUCE_USERNAME+"@local"]
-                    testname: 'Unit Tests'
-                    detailedError: false
-                    build: process.env.TRAVIS_BUILD_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
-                    browsers: browsers.unit
-                    ## onTestComplete: (status, page, config, browser) ->
-                    ##     done = @async()
-                    ##     browser.eval 'JSON.stringify(window.mochaResults)', (err, res) ->
-                    ##         done(err) if err
-
-                    ##         res = JSON.parse res
-                    ##         res.browser = config
-
-                    ##         grunt.log.debug '[%s] Results: %j', config.prefix, res
-
-                    ##         data =
-                    ##             'custom-data':
-                    ##                 mocha: res.jsonReport
-                    ##             'passed': !res.failures
-
-                    ##         request
-                    ##             method: 'PUT'
-                    ##             uri: ["https://", process.env.SAUCE_USERNAME, ":", process.env.SAUCE_ACCESS_KEY, "@saucelabs.com/rest", "/v1/", process.env.SAUCE_USERNAME, "/jobs/", browser.sessionID].join('')
-                    ##             headers:
-                    ##                 'Content-Type': 'application/json'
-                    ##             body: JSON.stringify data
-                    ##         , (error, response, body) ->
-                    ##             done(error) if error
-                    ##             done res
-
-    # Dependencies
-    # ==========
-    for name of pkg.devDependencies when name.substring(0, 6) is 'grunt-'
-        grunt.loadNpmTasks name
-
-    # Tasks
-    # ==========
-
-    # Lint
-    # ----------
-    grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', [
-        'coffeelint:grunt',
-        'jshint:source',
-        'jshint:tests'
-    ]
-
-    # Minify
-    # ----------
-    grunt.registerTask 'minify', 'Minify the source javascript and css', [
-        'uglify'
-        'cssmin'
-    ]
-
-    # Docs
-    # ----------
-    #grunt.registerTask 'docs', 'IN THE WORKS: Generate documentation', []
-
-
-    # Watcher
-    # ----------
-    grunt.registerTask 'test-watch', 'Run headless unit-tests and re-run on file changes', [
-        'rebuild',
-        'copy:test'
-        'watch'
-    ]
-    # Coverage
-    # ----------
-    # @todo
-    grunt.registerTask 'coverage', 'IN THE WORKS: Generate a code coverage report', []
-
-    # Travis
-    # ---------
-    grunt.registerTask 'check_for_pull_request_from_master', 'Fails if we are testing a pull request against master', ->
-        if (process.env.TRAVIS_BRANCH == 'master' and process.env.TRAVIS_PULL_REQUEST != 'false')
-            grunt.fail.fatal '''Woah there, buddy! Pull requests should be
-            branched from develop!\n
-            Details on contributing pull requests found here: \n
-            https://github.com/Widen/fine-uploader/blob/master/CONTRIBUTING.md\n
-            '''
-
-    # Travis' own test
-    # ----------
-    grunt.registerTask 'travis-sauce', 'Run tests on Saucelabs', [
-        'copy:test'
-        'connect:test_server'
-        'saucelabs-mocha'
-    ]
-
-    grunt.registerTask 'travis', [
-        'check_for_pull_request_from_master'
-        'travis-sauce'
-    ]
-
-    # Test
-    # ----------
-    grunt.registerTask 'test', 'Run headless unit tests', [
-        'rebuild'
-        'copy:test'
-        'connect:test_server'
-        'mocha'
-    ]
-
-    # Test on Saucelabs
-    # ----------
-    grunt.registerTask 'test-sauce', 'Run tests on Saucelabs', [
-        'rebuild'
-        'copy:test'
-        'connect:test_server'
-        'saucelabs-mocha'
-    ]
-
-    # Local tests (indefinite)
-    # ----------
-    grunt.registerTask 'test-local', 'Run a local server indefinitely for testing', [
-        'copy:test'
-        'connect:root_server'
-    ]
-
-
-    # Build
-    # ----------
-    # @verify
-    grunt.registerTask 'build', 'build from latest source', [
-        'concat'
-        'minify'
-        'usebanner'
-        'copy:images'
-    ]
-
-    # Prepare
-    # ----------
-    # @verify
-    grunt.registerTask 'prepare', 'Prepare the environment for FineUploader development', [
-        'clean'
-        'bower'
-    ]
-
-    # Rebuild
-    # ----------
-    grunt.registerTask 'rebuild', "Rebuild the environment and source", [
-        'prepare',
-        'build'
-    ]
-
-    # Dist
-    # ---------
-    # @todo
-    grunt.registerTask 'dist', 'build a zipped distribution-worthy version', [
-        'build'
-        'copy:dist'
-        'compress'
-    ]
-
-    # Default
-    # ----------
-    grunt.registerTask 'default', 'Default task: clean, bower, lint, build, & test', [
-        'clean'
-        #'lint'
-        'build'
-        'test'
-    ]
+  require('time-grunt')(grunt)
+  fs = require 'fs'
+
+  # Utilities
+  # ==========
+  path = require 'path'
+
+  # Package
+  # ==========
+  pkg = require './package.json'
+
+  # Paths
+  # ==========
+  paths =
+    'dist': './_dist'
+    'build': './_build'
+    'src': './client'
+    'docs': './docs'
+    'test': './test'
+
+  # Browsers
+  # ==========
+  browsers = require "#{paths.test}/browsers"
+
+  # Modules
+  # ==========
+  fineUploaderModules = require './lib/fineuploader.modules'
+
+  # Configuration
+  # ==========
+  grunt.initConfig
+
+    pkg: pkg
+
+
+    bower:
+      install:
+        options:
+          targetDir: "#{paths.test}/_vendor"
+          install: true
+          cleanTargetDir: true
+          cleanBowerDir: true
+          layout: 'byComponent'
+
+    clean:
+      build:
+        files:
+          src: paths.build
+      dist:
+        files:
+          src: paths.dist
+      test:
+        files:
+          src: ["#{paths.test}/_temp*"]
+      vendor:
+        files:
+          src: "#{paths.test}/_vendor"
+
+    coffeelint:
+      options:
+        indentation:
+          level: 'ignore'
+        no_trailing_whitespace:
+          level: 'ignore'
+        max_line_length:
+          level: 'ignore'
+      grunt: './Gruntfile.coffee'
+
+    compress:
+      jquery:
+        options:
+          archive: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>.zip"
+        files: [
+          {
+            expand: true
+            cwd: paths.dist
+            src: './jquery.<%= pkg.name %>-<%= pkg.version %>/*'
+          }
+        ]
+        jqueryS3:
+          options:
+            archive: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>.zip"
+          files: [
+            {
+              expand: true
+              cwd: paths.dist
+              src: './s3.jquery.<%= pkg.name %>-<%= pkg.version %>/*'
+            }
+          ]
+      core:
+        options:
+          archive: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>.zip"
+        files: [
+          {
+            expand: true
+            cwd: paths.dist
+            src: './<%= pkg.name %>-<%= pkg.version %>/*'
+          }
+        ]
+      coreS3:
+        options:
+          archive: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>.zip"
+        files: [
+          {
+            expand: true
+            cwd: paths.dist
+            src: './s3.<%= pkg.name %>-<%= pkg.version %>/*'
+          }
+        ]
+
+    concat:
+      core:
+        src: fineUploaderModules.mergeModules 'core', 'traditional'
+        dest: "#{paths.build}/<%= pkg.name %>.js"
+      coreS3:
+        src: fineUploaderModules.mergeModules 'core', 's3'
+        dest: "#{paths.build}/s3.<%= pkg.name %>.js"
+      jquery:
+        src: fineUploaderModules.mergeModules 'jquery', 'traditional'
+        dest: "#{paths.build}/jquery.<%= pkg.name %>.js"
+      jqueryS3:
+        src: fineUploaderModules.mergeModules 'jquery', 's3'
+        dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.js"
+      all:
+        src: fineUploaderModules.mergeModules 'all'
+        dest: paths.build + "/all.<%= pkg.name %>.js"
+      css:
+        src: ["#{paths.src}/*.css"]
+        dest: "#{paths.build}/<%= pkg.name %>.css"
+
+    concurrent:
+      minify: ['cssmin', 'uglify']
+      lint: ['jshint', 'coffeelint']
+
+    connect:
+      root_server:
+        options:
+          base: "."
+          hostname: "0.0.0.0"
+          port: 9000
+          keepalive: true
+      test_server:
+        options:
+          base: "test"
+          hostname: "0.0.0.0"
+          port: 9000
+
+    copy:
+      dist:
+        files: [
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.js', '!all.*', '!s3.*', '!*.min.js', '!jquery*', '!*iframe*']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: [ '!all.*', 's3.*.js', '!*.min.js', '!s3.jquery*', '!*iframe*']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.<%= pkg.name %>-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.min.js',  '!all.*', '!s3.*', '!jquery*']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '-<%= pkg.version %>.min.js'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['s3.*.min.js', '!s3.jquery*']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['jquery*js', '!s3.*', '!*.min.js']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.<%= pkg.name %>-<%= pkg.version %>.js'
+          },
+          {
+            expand: true
+            cwd: paths.build
+            src: ['s3.jquery*js', '!*.min.js']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.js'
+          },
+          {
+            expand: true
+            cwd: paths.build
+            src: ['jquery*min.js']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['s3.jquery*min.js']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.min.js'
+          }
+          {
+            expand: true
+            cwd: "./#{paths.src}/js/"
+            src: ['iframe.xss.response.js']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.xss.response-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: "./#{paths.src}/js/"
+            src: ['iframe.xss.response.js']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.xss.response-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: "./#{paths.src}/js/"
+            src: ['iframe.xss.response.js']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.xss.response-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: "./#{paths.src}/js/"
+            src: ['iframe.xss.response.js']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
+            ext: '.xss.response-<%= pkg.version %>.js'
+          }
+          {
+            expand: true
+            cwd: paths.src
+            src: ['*.gif']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: paths.src
+            src: ['*.gif']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: paths.src
+            src: ['*.gif']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: paths.src
+            src: ['*.gif']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: './'
+            src: ['LICENSE']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: './'
+            src: ['LICENSE']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: './'
+            src: ['LICENSE']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: './'
+            src: ['LICENSE']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/"
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.min.css']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.min.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.min.css']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.min.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.css', '!*.min.css']
+            dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.css', '!*.min.css']
+            dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.min.css']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.min.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.min.css']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.min.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.css', '!*.min.css']
+            dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.css'
+          }
+          {
+            expand: true
+            cwd: paths.build
+            src: ['*.css', '!*.min.css']
+            dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>"
+            ext: '-<%= pkg.version %>.css'
+          }
+        ]
+      build:
+        files: [
+          {
+            expand: true
+            cwd: "#{paths.src}/js/"
+            src: ['iframe.xss.response.js']
+            dest: paths.build
+          },
+          {
+            expand: true
+            cwd: paths.src
+            src: ['*.gif']
+            dest: paths.build
+          }
+        ]
+      test:
+        expand: true
+        flatten: true
+        src: ["#{paths.build}/*"]
+        dest: "#{paths.test}/_temp"
+      images:
+        files: [
+          expand: true
+          cwd: paths.src
+          src: ['*.gif']
+          dest: paths.build
+        ]
+
+    cssmin:
+      options:
+        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+        report: 'min'
+      files:
+        src: '<%= concat.css.dest %>'
+        dest: "#{paths.build}/<%= pkg.name %>.min.css"
+
+
+    jshint:
+      source: ["#{paths.src}/js/*.js"]
+      tests: ["#{paths.test}unit/*.js"]
+      options:
+        validthis: true
+        laxcomma: true
+        laxbreak: true
+        browser: true
+        eqnull: true
+        debug: true
+        devel: true
+        boss: true
+        expr: true
+        asi: true
+
+    open:
+      test:
+        path: 'http://localhost:9000'
+        app: 'Google Chrome'
+
+    uglify:
+      options:
+        mangle: true
+        compress: true
+        report: 'min'
+        preserveComments: 'some'
+      core:
+        src: ['<%= concat.core.dest %>']
+        dest: "#{paths.build}/<%= pkg.name %>.min.js"
+      jquery:
+        src: ['<%= concat.jquery.dest %>']
+        dest: "#{paths.build}/jquery.<%= pkg.name %>.min.js"
+      coreS3:
+        src: ['<%= concat.coreS3.dest %>']
+        dest: "#{paths.build}/s3.<%= pkg.name %>.min.js"
+      jqueryS3:
+        src: ['<%= concat.jqueryS3.dest %>']
+        dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.min.js"
+      all:
+        src: ['<%= concat.all.dest %>']
+        dest: "#{paths.build}/all.<%= pkg.name %>.min.js"
+
+    usebanner:
+      header:
+        src: ["#{paths.build}/*.{js,css}"]
+        options:
+          position: 'top'
+          banner: '''
+              /*!
+               * <%= pkg.title %>
+               *
+               * Copyright 2013, <%= pkg.author %> info@fineuploader.com
+               *
+               * Version: <%= pkg.version %>
+               *
+               * Homepage: http://fineuploader.com
+               *
+               * Repository: <%= pkg.repository.url %>
+               *
+               * Licensed under GNU GPL v3, see LICENSE
+               */ \n\n'''
+      footer:
+        src: ["#{paths.build}/*.{js,css}"]
+        options:
+          position: 'bottom'
+          banner: '/*! <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+
+    version:
+      options:
+        pkg: pkg,
+        prefix: '[^\\-][Vv]ersion[\'"]?\\s*[:=]\\s*[\'"]?'
+      major:
+        options:
+          release: 'major'
+        src: fineUploaderModules.modules.versioned
+      minor:
+        options:
+          release: 'minor'
+        src: fineUploaderModules.modules.versioned
+      hotfix:
+        options:
+          release: 'patch'
+        src: fineUploaderModules.modules.versioned
+      build:
+        options:
+          release: 'build'
+        src: fineUploaderModules.modules.versioned
+
+    watch:
+      options:
+        interrupt: true
+        debounceDelay: 250
+      js:
+        files: ["#{paths.src}/js/*.js", "#{paths.src}/js/s3/*.js"]
+        tasks: [
+          'build'
+          'copy:test'
+          'test-unit'
+        ]
+      test:
+        files: ["#{paths.test}/unit/*.js", "#{paths.test}/unit/s3/*.js"]
+        tasks: [
+          'jshint:tests'
+          'test-unit'
+        ]
+      grunt:
+        files: ['./Gruntfile.coffee']
+        tasks: [
+          'coffeelint:grunt'
+          'build'
+        ]
+      images:
+        files: ["#{paths.src}/*.gif"]
+        tasks: [
+          'copy:images'
+        ]
+      karma:
+        files: ["#{paths.src}/js/**/*.js"]
+        tasks: ["karma:unit:run"]
+
+    mochaWebdriver:
+      options:
+        timeout: 1000 * 60
+        reporter: 'spec'
+        browsers: browsers
+      sauce:
+        src: ['./test/functional/*.coffee']
+        options:
+          concurrency: 3
+          testTags: [ process.env.SAUCE_USERNAME+"@"+process.env.TRAVIS_BRANCH || process.env.SAUCE_USERNAME+"@local"]
+          build: process.env.TRAVIS_BUILD_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
+          identifer: process.env.TRAVIS_JOB_ID || Math.floor((new Date).getTime() / 1000 - 1230768000).toString()
+          tunnelTimeout: 120
+          testName: 'Sauce Mocha Test'
+
+    karma:
+      options:
+        configFile: './karma.conf.coffee'
+      dev:
+        configFile: './karma-local.conf.coffee'
+        autoWatch: true
+        background: false
+        reporters: 'dots'
+      unit:
+        configFile: './karma-local.conf.coffee'
+        autoWatch: false
+        background: true
+        reporters: 'dots'
+      sauce:
+        configFile: './karma-sauce.conf.coffee'
+
+    shell:
+      sauce_connect:
+        command: 'cat ./lib/sauce/sauce_connect_setup.sh | bash'
+        options:
+          stdout: true
+          stderr: true
+      karma:
+        command: 'karma start & sleep 5 && karma run karma-modules.conf.coffee '
+
+  # Dependencies
+  # ==========
+  for name of pkg.devDependencies when name.substring(0, 6) is 'grunt-'
+    grunt.loadNpmTasks name
+
+  grunt.loadTasks './lib/tasks'
+
+  # Tasks
+  # ==========
+
+  # General Tasks
+  # ----------
+  grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', [
+    'concurrent:lint'
+  ]
+
+  grunt.registerTask 'minify', 'Minify the source javascript and css', [
+    'concurrent:minify'
+  ]
+
+  grunt.registerTask 'sauce', 'Run tests on SauceLabs', [
+    #'shell:sauce_connect'
+    'karma:sauce'
+  ]
+
+  # Testing Tasks
+  # ----------
+  grunt.registerTask 'check_for_pull_request_from_master', 'Fails if we are testing a pull request against master', ->
+    if (process.env.TRAVIS_BRANCH == 'master' and process.env.TRAVIS_PULL_REQUEST != 'false')
+      grunt.fail.fatal '''Woah there, buddy! Pull requests should be
+      branched from develop!\n
+      Details on contributing pull requests found here: \n
+      https://github.com/Widen/fine-uploader/blob/master/CONTRIBUTING.md\n
+      '''
+
+  grunt.registerTask 'travis', [
+    'check_for_pull_request_from_master'
+    'travis-sauce'
+  ]
+
+  grunt.registerTask 'test-watch', 'Run headless unit-tests and re-run on file changes', [
+    'prepare-test'
+    'connect:test_server'
+    'open:test'
+    'watch'
+  ]
+
+  grunt.registerTask 'test-unit', 'Run headless unit tests', [
+    'prepare-test'
+    'connect:test_server'
+    'karma:unit'
+  ]
+
+  grunt.registerTask 'test-unit-sauce', 'Run tests on SauceLabs', [
+    'prepare-test'
+    'connect:test_server'
+    'karma:sauce_0'
+    'karma:sauce_1'
+    'karma:sauce_2'
+    'karma:sauce_3'
+  ]
+
+  grunt.registerTask 'test-unit-forever', 'Run a local server for indefinite unit testing', [
+    'prepare-test'
+    'connect:root_server'
+    'karma:dev'
+  ]
+
+  grunt.registerTask 'test-functional', 'ITW: Run functional tests', [
+    'prepare-test'
+    'connect:test_server'
+    'mocha:functional'
+  ]
+
+  grunt.registerTask 'test-functional-sauce', 'ITW: Run functional tests (SauceLabs)', [
+    'prepare-test'
+    'connect:test_server'
+    'mocha-sauce:functional'
+  ]
+
+  # Building Tasks
+  # ----------
+
+  grunt.registerTask 'prepare-test', 'Prepare code for testing', [
+    'rebuild'
+    'copy:test'
+  ]
+
+  grunt.registerTask 'prepare', 'Prepare the environment for development', [
+    'clean'
+    'bower'
+  ]
+
+  grunt.registerTask 'rebuild', "Rebuild the environment and source", [
+    'prepare',
+    'build'
+  ]
+
+  grunt.registerTask 'build', 'Build from latest source', [
+    'concat'
+    'minify'
+    'usebanner'
+    'copy:images'
+  ]
+
+  grunt.registerTask 'dist', 'Build a zipped distribution-worthy version', [
+    'build'
+    'copy:dist'
+    'compress'
+  ]
+
+  grunt.registerTask 'default', 'Default task: clean, bower, lint, build, & test', [
+    'dist'
+  ]


Fix bug in Gruntfile

Fix bug in Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -147,7 +147,7 @@
     freedomfirefox: readJSONFile('node_modules/freedom-for-firefox/package.json')
     freedomxmpp: readJSONFile('node_modules/freedom-social-xmpp/package.json')
     freedomfirebase: readJSONFile('node_modules/freedom-social-firebase/package.json')
-    freedomGitHub: grunt.file.readJSON('node_modules/freedom-social-github/package.json')
+    freedomGitHub: readJSONFile('node_modules/freedom-social-github/package.json')
     freedomwechat: readJSONFile('node_modules/freedom-social-wechat/package.json')

   clean: ['build/dev', 'build/dist', '.tscache']


Add demos compilation and minimap markers


if port is already is use if will now use a different port

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -324,6 +324,7 @@
           port: 4040
           base: '_site'
           livereload: false
+          useAvailablePort: true

   grunt.registerTask 'build', [


refactor: no need to compile beforehand as `brunch watch` will do that anyway

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -107,7 +107,6 @@
   grunt.registerTask 'default', [
     'init'
     'exec:harpKill'
-    'exec:brunchCompile'
     'concurrent:develop'
   ]


Fix new build file

Fix new build file

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -35,9 +35,10 @@
         files: [
           {
             expand: true
-            cwd: '.'
-            src: ['src/*.coffee']
+            cwd: 'src/'
+            src: ['*.coffee']
             dest: 'js/'
+            ext: '.js'
           }

           {dest: 'test/js/parserTests.js', src: 'test/coffee/parserTests.coffee'}


Offline mode is false by default

Offline mode is false by default

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -124,7 +124,7 @@
             replacement: process.env.NOFLO_USER_LOGIN_ENABLED or true
           ,
             pattern: /\$NOFLO_OFFLINE_MODE/ig
-            replacement: process.env.NOFLO_OFFLINE_MODE or true
+            replacement: process.env.NOFLO_OFFLINE_MODE or false
           ]
       analytics:
         files:


Copy assets to build

Copy assets to build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -70,7 +70,7 @@
         coffeeTest: ["!#{sourceDir}/**/*.spec.coffee"]
         css: ["#{sourceDir}/**/*.{css,styl,less}"]
         # Non-script/style assets
-        assets: ["#{sourceDir}/**/*.!{js,coffee,css,styl,less}"]
+        assets: ["**/*.!(js|coffee|css|styl|less|jade|ejs)"]
       compile:
         js: ["#{compileDir}/**/*.js", "!#{compileDir}/**/*.spec.js"]
         jsTest: ["!#{compileDir}/**/*.spec.js"]
@@ -203,12 +203,6 @@
         cwd: vendorDir
         src: ['<%= files.vendor.scripts.local %>']
         dest: "#{compileDir}/vendor/"
-      # Copy over the assets from source to compile
-      assetsToCompile:
-        expand: true
-        cwd: sourceDir
-        src: '<%= files.source.assets %>'
-        dest: compileDir
       # Copy over the assets from source to build
       assetsToBuild:
         expand: true
@@ -339,7 +333,6 @@
     'jshint' # Check JS compliance
     'copy:vendorToCompile' # Copy over vendor files
     'copy:vendorToSource' # Copy over vendor files back to source because of `clean:source`
-    'copy:assetsToCompile' # As name
   ]

   # The ultimate deployment package
@@ -351,7 +344,7 @@
     'ngmin:build' # Apply minification protection
     'concat' # Concatenate all files
     'uglify' # Uglify the files for production
-    'copy:assetsToBuild' # As name
+    'copy:assetsToBuild' # Copy assets to the build directory

     # We then need to compile the HTML with references to the packaged assets (only the script and the style)
     'clean:source' # Clean the source directory because we need to compile from source


kick travis

kick travis

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,6 +32,14 @@
           ide: 'http://localhost:8000/index.html'
         files:
           "browser/<%=pkg.name%>.js": ['component.json']
+
+    # Generate runner.html
+    # noflo_browser_mocha:
+    #   all:
+    #     options:
+    #       scripts: ["../browser/<%=pkg.name%>.js"]
+    #     files:
+    #       'test/runner.html': ['test/*.js']

     # JavaScript minification for the browser
     uglify:


Fix default Grunt task

Fix default Grunt task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,5 +62,5 @@
     grunt.loadNpmTasks 'grunt-contrib-coffee'

     # Grunt tasks.
-    grunt.registerTask 'default', ['less', 'coffee']
+    grunt.registerTask 'default', ['compass', 'less', 'coffee', 'uglify']
     grunt.registerTask 'ninja', ['reloadr', 'watch']


Copy images to dist

Copy images to dist

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

             livereload:
@@ -67,7 +67,8 @@
                     src: [
                         'slides/**'
                         'bower_components/**'
-                        'js/**'
+                        'js/**',
+                        'image/**'
                     ]
                     dest: 'dist/'
                 },{
@@ -77,7 +78,7 @@
                     filter: 'isFile'
                 }]

-
+
         buildcontrol:

             options:
@@ -89,7 +90,7 @@
                 options:
                     remote: 'git@github.com:dvberkel/polyglot-programming-on-jvm.git'
                     branch: 'gh-pages'
-
+

     # Load all grunt tasks.
@@ -131,13 +132,13 @@
             'copy'
         ]

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

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


Test on Safari 10 instead

Test on Safari 10 instead

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -332,7 +332,7 @@
             version: '39'
           ,
             browserName: 'safari'
-            version: '9'
+            version: '10'
           ,
             browserName: 'internet explorer'
             version: '11'


Build src before publishing

Build src before publishing

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -98,4 +98,4 @@
   grunt.registerTask "test", ["coffeelint", "shell:jasmine"]
   grunt.registerTask 'release', 'Release a new version, push it and publish it', (target) ->
     target = 'patch' unless target
-    grunt.task.run "bump-only:#{target}", 'test', 'bump-commit', 'shell:publish'
+    grunt.task.run "bump-only:#{target}", 'build', 'test', 'bump-commit', 'shell:publish'


Use 'core.log' in tests instead of direct require of 'core/log'

--- test/log.coffee
+++ test/log.coffee
@@ -1,7 +1,6 @@
 should = require 'should'

 bus = require '../lib/bus'
-log = require '../lib/core/log'
 core = require '..'

 tests = [
@@ -19,7 +18,7 @@
 # channel without making any API calls to 'core'.
 listen = (topic, callback) ->

-  {channel} = log
+  {channel} = core.log
   sub = bus.subscribe {channel, topic, callback}

   # Unsubscribe after being fired once
@@ -42,7 +41,7 @@
           done()

         # When we log it via the given topic
-        log[test.topic] test.data
+        core.log[test.topic] test.data

 # Test 'info' coverage for public 'core' API
 describe "log, 'core' API", ->


:lipstick:

:lipstick:

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -25,7 +25,6 @@
   initializeContent: (attributes) ->
     @classList.add('editor')
     @setAttribute('tabindex', -1)
-

     if atom.config.get('editor.useShadowDOM')
       @createShadowRoot()


:memo: TextEditorElement::invalidateBlockDecorationDimensions

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -347,6 +347,12 @@
   getHeight: ->
     @offsetHeight

+  # Experimental: Invalidate the passed block {Decoration} dimensions, forcing
+  # them to be recalculated and the surrounding content to be adjusted on the
+  # next animation frame.
+  #
+  # * {blockDecoration} A {Decoration} representing the block decoration you
+  # want to update the dimensions of.
   invalidateBlockDecorationDimensions: ->
     @component.invalidateBlockDecorationDimensions(arguments...)


Added rudimentary tweening, wut!

Added rudimentary tweening, wut!

--- src/tween.coffee
+++ src/tween.coffee
@@ -37,7 +37,7 @@
   @name tween
   @methodOf Tween#
   @param {Number} duration How long (in frames) until the object's properties reach their final values.
-  @param {Object} properties
+  @param {Object} properties Which properties to tween, plun
   ###
   tween: (duration, properties) ->
     properties = Object.extend({}, properties) # Make a local copy


GameObject.Effect

GameObject.Effect

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

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


Add comment about account values used in tests

--- test/test.coffee
+++ test/test.coffee
@@ -1,3 +1,4 @@
+# The API key and IDs used in this file belong to the official Ghost Inspector API testing account.
 should = require('should')
 GhostInspector = require('../index')('53cf58be350c6c41029a11bd', 'ff586dcaaa9b781163dbae48a230ea1947f894ff')


Built isimage tests and stub function passes

--- test/test.coffee
+++ test/test.coffee
@@ -1,5 +1,7 @@

 cachedir = __dirname + '/testcache'
+testimage = 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Large_format_camera_lens.jpg'
+notimage = 'http://www.phirephly.com/'

 imgcache = require('../lib/imgcache.js')({ "cachedir": cachedir })
 testCase  = require('nodeunit').testCase
@@ -20,8 +22,17 @@
   test.equal typeof imgcache.isimage, 'function'
   test.done()

+exports.imgcacheIsImage = (test) ->
+  imgcache.isimage testimage, (err, isimage) ->
+    test.ok(! err,"No error")
+    test.ok(isimage, "Test if image is correctly identified")
+    imgcache.isimage notimage, (err, isimage) ->
+      test.ok(! err,"No error")
+      test.ok(!isimage, "Test if non image URL resolves to false")
+      test.done()
+
 exports.imgcacheDownloads = (test) ->
-  imgcache.get 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Large_format_camera_lens.jpg', (err,image,info) ->
+  imgcache.get testimage, (err,image,info) ->
     test.ok(! err,"No error")
     test.ok(image,"Image Returned")
     stats = fs.lstatSync(cachedir);


Player will now be in sync with Play/Pause

--- src/videojs.chromecastComponent.coffee
+++ src/videojs.chromecastComponent.coffee
@@ -97,11 +97,23 @@
   onMediaStatusUpdate: (event) ->
     return unless @apiMedia

+    console.log @player_
+    console.log "##############"
+
     @currentMediaTime = @apiMedia.currentTime
+
     if @apiMedia.playerState is "IDLE"
       @currentMediaTime = 0
       @trigger "timeupdate"
       @onStopAppSuccess()
+    else if @apiMedia.playerState is "PAUSED"
+      return if @paused
+      @player_.pause()
+      @paused = true
+    else if @apiMedia.playerState is "PLAYING"
+      return unless @paused
+      @player_.play()
+      @paused = false

   startProgressTimer: (callback) ->
     if @timer


修复 context.messages 为undefined的问题

修复 context.messages 为undefined的问题

--- src/widget/edit/abstract-editor.coffee
+++ src/widget/edit/abstract-editor.coffee
@@ -56,12 +56,17 @@
 	_setValue: (value) ->
 		return false if @_value is value
 		arg = {oldValue: @_value, value: value}
-		return if @fire("beforeChange", @, arg) is false
-		@_value = value
+
+		if not @_modelSetValue
+			return if @fire("beforeChange", @, arg) is false
+			@_value = value
+			@fire("change", @, arg)
+
 		if value isnt @_modelValue and @_rendered
 			@post()
+
 		@onSetValue?(value)
-		@fire("change", @, arg)
+
 		return true

 	post: ()->
@@ -91,8 +96,14 @@
 					@set("state", keyMessage?.type)

 		if type isnt cola.constants.MESSAGE_VALIDATION_STATE_CHANGE and type < cola.constants.MESSAGE_LOADING_START
+
+			@_modelSetValue = true
+
 			if @refreshValue()
 				cola.util.delay(@, "refreshDom", 0, @_refreshDom)
+
+			@_modelSetValue = false
+
 		return

 	refreshValue: () ->


JDomain: Update domain filter, accept all custom domains

--- workers/social/lib/social/models/domain.coffee
+++ workers/social/lib/social/models/domain.coffee
@@ -152,8 +152,13 @@
     domainList = []
     domainList = domains.filter (domain)->
       {domain} = domain
-      re = if group is "koding" then new RegExp(account.profile.nickname + '\.kd\.io$') \
-           else new RegExp('(.*)\.' + group + '\.kd\.io$')
+      return yes  unless /\.kd\.io$/.test domain
+
+      re = if group is "koding"
+             new RegExp(account.profile.nickname + '\.kd\.io$')
+           else
+             new RegExp('(.*)\.' + group + '\.kd\.io$')
+
       isVmAlias         = (/^shared|vm[\-]?([0-9]+)?/.test domain)
       isKodingSubdomain = (/(.*)\.(koding|guests)\.kd\.io$/.test domain)
       isGroupAlias      = re.test domain


Added missing viewstate property

Added missing viewstate property

--- src/ui/models/viewstate.coffee
+++ src/ui/models/viewstate.coffee
@@ -26,6 +26,7 @@
     convertEmoji: tryparse(localStorage.convertEmoji) ? true
     showPopUpNotifications: tryparse(localStorage.showPopUpNotifications) ? true
     showMessageInNotification: tryparse(localStorage.showMessageInNotification) ? true
+    showUsernameInNotification: tryparse(localStorage.showUsernameInNotification) ? true
     colorScheme: localStorage.colorScheme or 'default'
     fontSize: localStorage.fontSize or 'medium'
     zoom: tryparse(localStorage.zoom ? "1.0")


Cleaned up views/ui/form

Cleaned up views/ui/form

--- src/views/ui/form/index.coffee
+++ src/views/ui/form/index.coffee
@@ -21,10 +21,8 @@

         listViewClass = if section.group then FormTable else FieldListView

-        listView = new listViewClass
+        @add new listViewClass
           controller: @controller
           collection: new FieldList fields

-        @view.add listView.render().view
-
     @


Add comment mentioning chocoloatey

Add comment mentioning chocoloatey

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -90,6 +90,7 @@
       cp path.join('resources', 'win', 'msvcp100.dll'), path.join(shellAppDir, 'msvcp100.dll')
       cp path.join('resources', 'win', 'msvcr100.dll'), path.join(shellAppDir, 'msvcr100.dll')

+      # Set up chocolatey ignore and gui files
       fs.writeFileSync path.join(appDir, 'apm', 'node_modules', 'atom-package-manager', 'bin', 'node.exe.ignore'), ''
       fs.writeFileSync path.join(appDir, 'node_modules', 'symbols-view', 'vendor', 'ctags-win32.exe.ignore'), ''
       fs.writeFileSync path.join(shellAppDir, 'atom.exe.gui'), ''


Fixes T1272: Package keyboard commands work in prod build

CSON files were not being copied over and therefore couldn't be parsed
into the main package.json file. All package-specific keymaps (like the
composer's ctrl+enter send) were not being activated on production builds.

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -147,7 +147,7 @@
       pathToCopy = path.resolve(pathToCopy)
       nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy)

-    packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee|cjsx|jsx)$)")
+    packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(coffee|cjsx|jsx)$)")
     filterPackage = (pathToCopy) ->
       return true if benchmarkFolderPattern.test(pathToCopy)


installSize -> installedSize

installSize -> installedSize

--- build/tasks/mkdeb-task.coffee
+++ build/tasks/mkdeb-task.coffee
@@ -18,7 +18,7 @@
     args = ['-sk', path.join(buildDir, 'Atom')]
     spawn {cmd, args}, (error, {stdout}) ->
       installedSize = stdout.split(' ')?[0] or '200000' # default to 200MB
-      callback(null, installSize)
+      callback(null, installedSize)

   grunt.registerTask 'mkdeb', 'Create debian package', ->
     done = @async()


clenup

clenup

--- src/driver/chronology.coffee
+++ src/driver/chronology.coffee
@@ -6,8 +6,6 @@
   return typeof tz is 'string' and tz.indexOf('/') isnt -1

 exports.millisecond = {
-  canonical: 1
-
   floor: (dt, tz) ->
     return new Date(dt)

@@ -19,8 +17,6 @@
 }

 exports.second = {
-  canonical: 1000
-
   floor: (dt, tz) ->
     throw new TypeError("#{tz} is not a valid timezone") unless isTimezone(tz)
     # Seconds do not actually need a timezone because all timezones align on seconds... for now...
@@ -43,8 +39,6 @@
 }

 exports.minute = {
-  canonical: 60000
-
   floor: (dt, tz) ->
     throw new TypeError("#{tz} is not a valid timezone") unless isTimezone(tz)
     # Minutes do not actually need a timezone because all timezones align on minutes... for now...
@@ -67,8 +61,6 @@
 }

 exports.hour = {
-  canonical: 3600000
-
   floor: (dt, tz) ->
     throw new TypeError("#{tz} is not a valid timezone") unless isTimezone(tz)
     # Not all timezones align on hours! (India)
@@ -91,8 +83,6 @@
 }

 exports.day = {
-  canonical: 24 * 3600000
-
   floor: (dt, tz) ->
     wt = WallTime.UTCToWallTime(dt, tz)
     return WallTime.WallTimeToUTC(tz, wt.getFullYear(), wt.getMonth(), wt.getDate(), 0, 0, 0, 0)
@@ -110,8 +100,6 @@
 }

 exports.week = {
-  canonical: 7 * 24 * 3600000
-
   floor: (dt, tz) ->
     wt = WallTime.UTCToWallTime(dt, tz)
     return WallTime.WallTimeToUTC(tz, wt.getFullYear(), wt.getMonth(), wt.getDate() - wt.getUTCDay(), 0, 0, 0, 0)


Fixed search paths for @import directives in LESS.

--- src/watch.coffee
+++ src/watch.coffee
@@ -36,7 +36,7 @@

 setEnv = (env, opts) ->
   settings = {env}
-  for key, value of config
+  for key, value of config.client
     if key in ['development', 'production', 'test']
       if key is env
         _.extend settings, value
@@ -242,13 +242,14 @@
           reload(path)

         when '.less'
-          # Run the source file through template engine
-          sourceData = _.template(fs.readFileSync(source).toString(), {settings})
+          sourceData = fs.readFileSync(source).toString()
           filename = sysPath.basename(source, sysPath.extname(source)) + '.css'
           path = sysPath.join destDir, filename

-          less.render sourceData, (err, data) ->
-            fs.writeFileSync path, data
+          parser = new (less.Parser)
+            paths: [sysPath.dirname(source)]
+          parser.parse sourceData, (err, tree) ->
+            fs.writeFileSync path, tree.toCSS()
             logging.info "compiled #{source}"
             reload(path)


改进layer z-index

改进layer z-index

--- src/widget/layout/layer.coffee
+++ src/widget/layout/layer.coffee
@@ -25,7 +25,7 @@
 		beforeHide: null

 	_onShow: ()->
-		@get$Dom.css({
+		@get$Dom().css({
 			zIndex: cola.floatWidget.zIndex()
 		})
 	_onHide: ()->


payment: return the error if new plan subscription is failed

--- workers/social/lib/social/models/payment/subscription.coffee
+++ workers/social/lib/social/models/payment/subscription.coffee
@@ -296,6 +296,7 @@
         else queue.next()
       ->
         newPlan.subscribe paymentMethodId, subOptions, (err, newSub) ->
+          return callback err  if err
           newSubscription = newSub
           queue.next err
       =>


payment: invoke transition operation after subscription to new plan is made

--- workers/social/lib/social/models/payment/subscription.coffee
+++ workers/social/lib/social/models/payment/subscription.coffee
@@ -294,15 +294,15 @@
     queue = [
       =>
         newPlan.checkQuota {@usage}, (err) -> queue.next err
-      =>
-        if operation
-        then operation.call this, (err) -> queue.next err
-        else queue.next()
       ->
         newPlan.subscribe paymentMethodId, subOptions, (err, newSub) ->
           return callback err  if err
           newSubscription = newSub
           queue.next err
+      =>
+        if operation
+        then operation.call this, (err) -> queue.next err
+        else queue.next()
       =>
         @addLinkedSubscription newSubscription, (err) -> queue.next err
       ->


feat(ctrl): manage empty response in loadHandler

--- cgFileUploadCtrl.coffee
+++ cgFileUploadCtrl.coffee
@@ -27,10 +27,12 @@
             @_input.click()
             return

-        _loadHandler: (response) ->
+        _loadHandler: (response = {}) ->
             # should not happend, just to be sure ;)
             if typeof response is 'string'
-                response = JSON.parse(response)
+                if response.length
+                    response = JSON.parse(response)
+                else response = {}

             # manage old response format
             if response.file


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -19,6 +19,7 @@

   text.update()

+  equals te
   equals text.I.alpha, 1 - 1/40

 module()


clean code

clean code

--- resources/assets/grid/batch.coffee
+++ resources/assets/grid/batch.coffee
@@ -41,4 +41,3 @@

             submitBatch: (action) ->
                 @currentBatchAction = action;
-                console.log $("##{gridContainerId}").find('form')


Add binding tests.

Add binding tests.

--- test/view.coffee
+++ test/view.coffee
@@ -1,8 +1,6 @@
 mvstar = require '../lib'

 describe 'View', ->
-  view = null
-
   describe 'View#render (template)', ->
     view = null

@@ -19,7 +17,6 @@
           template: '#html-template'

         view = new View()
-
         done()

     it 'should use template in DOM', ->
@@ -27,23 +24,58 @@
       view.el.html().should.not.be.null
       view.el.html().should.equal 'Title'

-  describe 'View#render (template)', ->
+  describe 'View#render (string)', ->
     view = null

     before (done) ->
       (require 'jsdom').env '', (err, window) ->
         throw err if err?
         global.$ = (require 'jquery') window
+
+        class View extends mvstar.View
+          html: '''
+
Title

+                '''
+
+        view = new View()
         done()

     it 'should render string template', ->
-      class View extends mvstar.View
-        html: '''
-
Title

-              '''
-
-      view = new View()
       view.render()

       view.el.html().should.not.be.null
       view.el.html().should.equal 'Title'
+
+  describe 'View#bindings', ->
+    view = null
+
+    before (done) ->
+      (require 'jsdom').env '', (err, window) ->
+        throw err if err?
+        global.$ = (require 'jquery') window
+
+        class View extends mvstar.View
+          html: '''
+

+
+
+
+

+                '''
+
+          bindings:
+            textId:    'span#text'
+            textClass: 'span.text'
+            src:       'img#img @src'
+
+        view = new View()
+        done()
+
+    it 'should be used to update DOM declaratively', ->
+      view.render()
+      view.set 'textId', 'foo'
+      view.set 'textClass', 'bar'
+      view.set 'src', 'www.baz.com'
+      view.el.html().should.contain 'foo'
+      view.el.html().should.contain 'bar'
+      view.el.html().should.contain ''


Add scale option to capture().

Add scale option to capture().

--- src/jpeg_camera.coffee
+++ src/jpeg_camera.coffee
@@ -24,6 +24,7 @@
     mirror: false
     timeout: 0
     retry_success: false
+    scale: 1.0

   @_canvas_supported: !!document.createElement('canvas').getContext

@@ -240,6 +241,9 @@
   #   natural orientation - how the front facing camera sees the user.
   #   This option can be set to true to upload images the way the user sees
   #   them. _Cannot_ be overwritten at the time of upload.
+  # @option options scale [Float] By default snapshots are captured and uploaded
+  #   using highest possible resolution. Set this to a number less than 1.0 to
+  #   get smaller snapshots.
   # @option options shutter [Boolean] Whether to play the shutter sound.
   # @option options api_url [String] URL where the snapshots will be uploaded.
   #   Can be overwritten when calling {Snapshot#upload}.
@@ -287,7 +291,10 @@
     if _options.shutter
       @_engine_play_shutter_sound()

-    @_engine_capture snapshot, _options.mirror, _options.quality, 1.0
+    scale = Math.min 1.0, _options.scale
+    scale = Math.max 0.01, scale
+
+    @_engine_capture snapshot, _options.mirror, _options.quality, scale

     snapshot


No unless-else.

No unless-else.

--- src/chaplin/views/view.coffee
+++ src/chaplin/views/view.coffee
@@ -51,11 +51,10 @@
         utils.wrapMethod this, 'initialize'

       # Wrap `render` so `afterRender` is called afterwards
-      unless @render is View::render
+      if @render is View::render
+        @render = _(@render).bind this
+      else
         utils.wrapMethod this, 'render'
-      else
-        # Otherwise just bind the `render` method
-        @render = _(@render).bind this

       # Copy some options to instance properties
       if options


Use duck typing in View#getTemplateData.

--- src/chaplin/views/view.coffee
+++ src/chaplin/views/view.coffee
@@ -227,17 +227,16 @@
     # Uses optimized Chaplin serialization if available.
     getTemplateData: ->
       templateData = if @model
-        if @model instanceof Model
+        if @model.serialize
           @model.serialize()
         else
-          utils.beget @model.attributes
+          @model.toJSON()
       else if @collection
         # Collection: Serialize all models.
-        items = if @collection instanceof Collection
+        items = if @collection.serialize
           @collection.serialize()
         else
-          @collection.map (model) ->
-            utils.beget model.attributes
+          @collection.toJSON()
         {items}
       else
         # Empty object.


Improve error handling for xhr parseerrors scenarios

--- src/ext/modules/proxy.coffee
+++ src/ext/modules/proxy.coffee
@@ -18,6 +18,8 @@

       message = if xhr.responseJSON?.errmsg
         xhr.responseJSON.errmsg
+      else if error?.message
+        error.message
       else if status is 0
         'Could not connect to H2O'
       else


Test that up.reload() does not use the proxy cache

--- spec_app/spec/javascripts/up/fragment_spec.js.coffee
+++ spec_app/spec/javascripts/up/fragment_spec.js.coffee
@@ -3582,6 +3582,13 @@

         next =>
           expect($('.element')).toHaveText('new text')
+
+      it 'does not use a cached response', ->
+        renderSpy = up.fragment.knife.mock('render')
+        element = fixture('.element')
+        up.fragment.setSource(element, '/source')
+        up.reload(element)
+        expect(renderSpy).toHaveBeenCalledWith(jasmine.objectContaining(target: element, cache: false))

       it 'does not reveal by default', asyncSpec (next) ->
         element = fixture('.element', text: 'old text')


fix #43: foodrun route working

fix #43: foodrun route working

use @data in beforeaction
use userId instead of user in globalbeforeaction

--- router.coffee
+++ router.coffee
@@ -19,8 +19,7 @@
       foodrun: share.FoodRuns.findOne(@params._id)
     onBeforeAction: ->
       # if a foodrun is shown that is to old or finished, show home view
-      fr = share.FoodRuns.findOne(@params._id) # @getData()
-      console.log "found: #{fr}"
+      fr = @data()
       if !fr?
         console.log "no such foodrun(#{@params._id}), going home"
         Router.go "home"
@@ -35,7 +34,7 @@
 globalOnBeforeActions =
   # redirect to the homepage if the current user is not logged in.
   loginRequired: (pause) =>
-    if !Meteor.user()?
+    if !Meteor.userId()?
       # render the home page template, but keep the url
       this.render "home"
       # pause rendering


Initial support for collating inputs by field

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -149,7 +149,7 @@
   # Create the processing function
   component.process (input, output, context) ->
     # Abort unless WirePattern-style preconditions don't match
-    return unless checkWirePatternPreconditions component, config, input, output
+    return unless checkWirePatternPreconditions config, input, output
     # Populate component.params from control ports
     component.params = populateParams config, input
     # Read input data
@@ -277,6 +277,34 @@
     params[paramPort] = input.getData paramPort
   return params

+handleInputCollation = (data, config, input, port, idx) ->
+  return if not config.group and not config.field
+  # Note: the collation mechanism as shown below is not a
+  # very nice way to deal with inputs as it messes with
+  # input buffer order. Much better to handle collation
+  # in a specialized component or to separate flows by
+  # scope.
+  if config.field
+    buf = input.ports[port].getBuffer input.scope, idx
+    console.log port, config.field, data[config.field]
+    if data[config.field] is undefined
+      # First port being read, set as the field value
+      datas = buf.filter (ip) -> ip.type is 'data'
+      data[config.field] = datas[0].data[config.field]
+      # We don't need to reorder the first buffer by collation
+      return
+    # Move matching IP packet to be first in buffer
+    matchingAtIdx = null
+    for ip, idx in buf
+      continue unless ip.type is 'data'
+      continue unless ip.data[config.field] is data[config.field]
+      matchingAtIdx = idx
+      break
+    # No need to reorder if matching packet is already first
+    return if matchingAtIdx is 0
+    datas = buf.splice matchingAtIdx, 1
+    buf.unshift datas[0]
+
 getInputData = (config, input) ->
   data = {}
   for port in config.inPorts
@@ -284,8 +312,11 @@
       data[port] = {}
       for idx in input.attached port
         continue unless input.hasData [port, idx]
+        handleInputCollation data, config, input, port, idx
         data[port][idx] = input.getData [port, idx]
       continue
+    continue unless input.hasData port
+    handleInputCollation data, config, input, port
     data[port] = input.getData port
   if config.inPorts.length is 1
     return data[config.inPorts[0]]
@@ -319,50 +350,73 @@
     return outProxy[ports[0]]
   return outProxy

-checkWirePatternPreconditions = (component, config, input, output) ->
+checkWirePatternPreconditions = (config, input, output) ->
   # First check for required params
-  paramsMet = true
-  inputsMet = true
-  droppedPackets = false
+  paramsOk = checkWirePatternPreconditionsParams config, input
+  # Then check actual input ports
+  inputsOk = checkWirePatternPreconditionsInput config, input
+  # If input port has data but param requirements are not met, and we're in dropInput
+  # mode, read the data and call done
+  if config.dropInput and not paramsOk
+    # Drop all received input packets since params are not available
+    packetsDropped = false
+    for port in config.inPorts
+      if input.ports[port].isAddressable()
+        attached = input.attached port
+        continue unless attached.length
+        for idx in attached
+          while input.has [port, idx]
+            packetsDropped = true
+            input.get([port, idx]).drop()
+        continue
+      while input.has port
+        packetsDropped = true
+        input.get(port).drop()
+    # If we ended up dropping inputs because of missing params, we need to
+    # deactivate here
+    output.done() if packetsDropped
+  # Pass precondition check only if both params and inputs are OK
+  return inputsOk and paramsOk
+
+checkWirePatternPreconditionsParams = (config, input) ->
   for param in config.params
     continue unless input.ports[param].isRequired()
     if input.ports[param].isAddressable()
       attached = input.attached param
-      unless attached.length
-        paramsMet = false
-        continue
+      return false unless attached.length
       withData = attached.filter (idx) -> input.hasData [param, idx]
       if config.arrayPolicy.params is 'all'
-        paramsMet = false unless withData.length is attached.length
+        return false unless withData.length is attached.length
         continue
-      paramsMet = false unless withData.length
+      return false unless withData.length
       continue
-    paramsMet = false unless input.hasData param
-  # Then check actual input ports
+    return false unless input.hasData param
+  true
+
+checkWirePatternPreconditionsInput = (config, input) ->
+  collatedBy = undefined if config.group or config.field
+  validate = (ip) ->
+    if ip.type is 'data'
+      if config.field
+        # Use first data packet to define what to collate by
+        collatedBy = ip.data[config.field] if collatedBy is undefined
+        return ip.data[config.field] is collatedBy
+      # Without collation rules any data packet is OK
+      return true
+    false
+
   for port in config.inPorts
     if input.ports[port].isAddressable()
       attached = input.attached port
-      unless attached.length
-        inputsMet = false
+      return false unless attached.length
+      withData = attached.filter (idx) -> input.has [port, idx], validate
+      if config.arrayPolicy['in'] is 'all'
+        return false unless withData.length is attached.length
         continue
-      withData = attached.filter (idx) -> input.hasData [port, idx]
-      if config.arrayPolicy['in'] is 'all'
-        inputsMet = false unless withData.length is attached.length
-        continue
-      inputsMet = false unless withData.length
+      return false unless withData.length
       continue
-    unless input.hasData port
-      inputsMet = false
-      continue
-    # If input port has data but param requirements are not
-    # met, and we're in dropInput mode, read the data and
-    # call done
-    if config.dropInput and not paramsMet
-      input.getData port
-      droppedPackets = true
-
-  output.done() if droppedPackets and not paramsMet
-  return inputsMet and paramsMet
+    return false unless input.has port, validate
+  true

 # Wraps OutPort in WirePattern to add transparent scope support
 class OutPortWrapper


Fix auto-start/end events

Fix auto-start/end events

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -49,7 +49,6 @@
     @graph = graph
     @started = false
     @debug = true
-    @connectionCount = 0

     # On Node.js we default the baseDir for component loading to
     # the current working directory
@@ -81,10 +80,10 @@
     active = []
     return active unless @started
     for name, process of @processes
-      if process.component.load
+      if process.component.load > 0
         # Modern component with load
         active.push name
-      if process.component.__openConnections
+      if process.component.__openConnections > 0
         # Legacy component
         active.push name
     return active
@@ -193,7 +192,6 @@
         # Add either a Node, an Initial, or an Edge and move on to the next one
         # when done
         this["add#{type}"] add, (err) ->
-          console.log err if err
           return done err if err
           callStack++
           if callStack % 100 is 0
@@ -337,11 +335,12 @@
   # Subscribe to events from all connected sockets and re-emit them
   subscribeSocket: (socket, source) ->
     socket.on 'connect', =>
-      if source and not source.component.load
+      if source and typeof source.component.load is 'undefined'
         # Handle activation for legacy components
         source.component.__openConnections = 0 unless source.component.__openConnections
         source.component.__openConnections++
-        @checkIfStarted()
+        if source.component.__openConnections is 1
+          @checkIfStarted()
       @emit 'connect',
         id: socket.getId()
         socket: socket
@@ -387,7 +386,7 @@
       return if load > 1
       @checkIfStarted()
     node.component.on 'deactivate', (load) =>
-      return if load
+      return if load > 0
       @checkIfFinished()
     return unless node.component.getIcon
     node.component.on 'icon', =>
@@ -544,7 +543,7 @@

   isRunning: ->
     return false unless @started
-    @connectionCount > 0
+    return @getActiveProcesses().length > 0

   startComponents: (callback) ->
     unless callback
@@ -560,6 +559,9 @@
     # Perform any startup routines necessary for every component.
     return callback() unless @processes and Object.keys(@processes).length
     for id, process of @processes
+      if process.component.isStarted()
+        onProcessStart()
+        continue
       process.component.on 'start', onProcessStart
       process.component.start()

@@ -606,6 +608,8 @@
     unless callback
       platform.deprecated 'Calling network.stop() without callback is deprecated'
       callback = ->
+
+    return callback null unless @started

     # Disconnect all connections
     for connection in @connections
@@ -624,6 +628,9 @@
       return callback()
     # Tell processes to shut down
     for id, process of @processes
+      unless process.component.isStarted()
+        onProcessEnd()
+        continue
       process.component.on 'end', onProcessEnd
       process.component.shutdown()

@@ -645,17 +652,13 @@
       start: @startupDate

   checkIfStarted: ->
-    return if @started
-    @start ->
+    return if @isRunning()
+    @setStarted true

   checkIfFinished: ->
-    return unless @started
-    active = @getActiveProcesses()
-    console.log @graph.name, active
-    return if active.length
+    return unless @isRunning()
     unless @debouncedEnd
       @debouncedEnd = utils.debounce =>
-        return if @connectionCount
         @setStarted false
       , 50
     do @debouncedEnd


Renamed `startupComponents` to `startComponents`.

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -454,14 +454,14 @@
   isStarted: () ->
     @started

-  startupComponents: ->
+  startComponents: ->
     # Perform any startup routines necessary for every component.
     for id, process of @processes
       process.component.start()

   start: ->
     @started = true
-    @startupComponents()
+    @startComponents()
     @sendInitials()

   stop: ->


payment: improve product model

payment: improve product model

--- workers/social/lib/social/models/payment/product.coffee
+++ workers/social/lib/social/models/payment/product.coffee
@@ -10,40 +10,65 @@
   @share()

   @set
-    sharedMethods :
-      static      : ['create']
-      instance    : ['remove']
-    schema        :
-      title       : String
-      description : String
-      amount      : Number
-      planCode    : String
+    sharedMethods     :
+      static          : ['create']
+      instance        : ['remove']
+    schema            :
+      title           :
+        type          : String
+        required      : yes
+      description     : String
+      subscriptionType: String
+      amount          :
+        type          : Number
+        required      : yes
+      overageEnabled  : Boolean
+      planCode        : String

   @create = (group, formData, callback) ->

     JGroup = require '../group'

-    product = new this formData
-    product.planCode = createId()
+    { type: subscriptionType, overageEnabled, title, description,
+      amount } = formData

-    product.save (err) ->
+    product = new this {
+      title
+      description
+      amount            : amount * 100
+      subscriptionType  : subscriptionType ? 'recurring'
+      planCode          : createId()
+      overageEnabled    : overageEnabled is 'on'
+    }
+
+    product.save (err) =>
       return callback err  if err

-      planData =
-        code        : product.planCode
-        title       : "#{ product.title } - Overage"
-        feeMonthly  : product.amount
-
-      recurly.createPlan planData, (err, plan) ->
+      @savePlanToRecurly product, (err, plan) ->
         return callback err  if err

-        JGroup.one { slug: group }, (err, group) ->
+        JGroup.one slug: group, (err, group) ->
           return callback err  if err

           group.addProduct product, (err) ->
             return callback err  if err

             callback null, product
+
+  @savePlanToRecurly = (product, callback) ->
+    if product.overageEnabled
+
+      planData =
+        code        : product.planCode
+        title       : "#{ product.title } - Overage"
+        feeMonthly  : product.amount
+        feeInterval : switch product.subscriptionType
+          when 'recurring' then 1
+          when 'single'    then 9999 # wat
+
+      recurly.createPlan planData, callback
+
+    else process.nextTick -> callback null

   remove: permit 'manage products',
     success: (client, callback) ->


Revised Image Gallery Script

Revised Image Gallery Script

Improved browser comaptibility, imporved speed and improved how it works. Just generally improved.

--- static/js/image-gallery.coffee
+++ static/js/image-gallery.coffee
@@ -1,31 +1,45 @@
 class TransformGal
-  constructor: ->
+  constructor: (imageSelector, gallerySelector, nextButtonSelector, prevButtonSelector) ->
     @currentIndex = 0
-    @imageContainers = document.querySelectorAll('.image-carousel-thing > .image-container')
-    @paperCard = document.querySelector('article.paper-card')
+    @imageSelector = imageSelector
+    @numberOfImages = document.querySelectorAll(imageSelector).length
+    @nextButton = document.querySelector nextButtonSelector
+    @prevButton = document.querySelector prevButtonSelector
+    # Width of this element is used to calculate translateX value
+    @imageGalleryElement = document.querySelector gallerySelector

-  init: ->
-    nextButton = document.querySelector 'button#next-screenshot'
-    prevButton = document.querySelector 'button#prev-screenshot'
+  init: (nextButton, prevButton) =>
+    # Create the new CSS rule and insert it at the top of the stylesheet
+    if document.styleSheets
+      document.styleSheets[0].insertRule "#{@imageSelector}{}", 0
+      @imageContainerRule = document.styleSheets[0].cssRules[0]

-    nextButton.addEventListener 'click', @moveToNextImage
-    prevButton.addEventListener 'click', @moveToPreviousImage
+    @nextButton.addEventListener 'click', @moveToNextImage
+    @prevButton.addEventListener 'click', @moveToPreviousImage

   moveToNextImage: =>
-    if @imageContainers.length <= @currentIndex + 1
+    if @numberOfImages <= @currentIndex + 1
       return
-    @imageContainers[@currentIndex].style.transform = "translateX(#{-@paperCard.offsetWidth * (@currentIndex + 1)}px"
-    @imageContainers[@currentIndex + 1].style.transform = "translateX(#{-@paperCard.offsetWidth * (@currentIndex + 1)}px"

+    @setTranslateXProperty @imageContainerRule, -@imageGalleryElement.offsetWidth * (@currentIndex + 1)
     @currentIndex++

   moveToPreviousImage: =>
     if @currentIndex - 1 < 0
       return
-    @imageContainers[@currentIndex].style.transform = "translateX(0px)"
-    @imageContainers[@currentIndex - 1].style.transform = "translateX(#{-@paperCard.offsetWidth * (@currentIndex - 1)}px"

+    @setTranslateXProperty @imageContainerRule, -@imageGalleryElement.offsetWidth * (@currentIndex - 1)
     @currentIndex--

-this.transformGal = new TransformGal
-transformGal.init()
+  setTranslateXProperty: (rule, distance) ->
+    rule.style.setProperty 'transform', "translateX(#{distance}px)", 'important'
+    rule.style.setProperty '-ms-transform', "translateX(#{distance}px)", 'important'
+    rule.style.setProperty '-moz-transform', "translateX(#{distance}px)", 'important'
+    rule.style.setProperty '-webkit-transform', "translateX(#{distance}px)", 'important'
+
+
+this.transformGal = new TransformGal('.image-carousel-thing > .image-container',
+                                    '.image-carousel-thing',
+                                    'button#next-screenshot',
+                                    'button#prev-screenshot')
+transformGal.init()


Fixed #19

Fixed #19

--- lib/views/compile-errors-view.coffee
+++ lib/views/compile-errors-view.coffee
@@ -29,8 +29,8 @@
     lines = raw.split "\n"
     errors = []
     for line in lines
-      result = line.match /^([^:]+):(\d+):(\d+):\s(\w+):(.*)$/
-      if result and result[4] == 'error'
+      result = line.match /^([^:]+):(\d+):(\d+):\s(\w+\s*\w*):(.*)$/
+      if result and result[4].indexOf('error') > -1
         errors.push {
           file: result[1],
           row: result[2],


PrivateMessagePane: Emit an event when add new participant button clicked.

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -362,6 +362,8 @@

   showAutoCompleteInput: ->
+
+    @emit 'NewParticipantButtonClicked'

     @autoCompleteForm.toggleClass 'active'
     @newParticipantButton.toggleClass 'active'


PrivateMessage: implement test mode and add hooks for it

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -1,7 +1,8 @@
 class PrivateMessagePane extends MessagePane

+  TEST_MODE = on
+
   constructor: (options = {}, data) ->
-
     options.wrapper     ?= yes
     options.lastToFirst  = yes

@@ -56,6 +57,10 @@
     @prependMessage message, @listController.getItemCount() - 1

     @removeFakeMessage message.requestData  if message.requestData
+
+
+  parse = (args...) -> args.map (item) -> parseInt item
+

   # as soon as the enter key down,
   # we create a fake itemview and put
@@ -64,6 +69,14 @@
   # with the real one.
   handleEnter: (value, timestamp) ->
     return  unless value
+
+    if TEST_MODE
+      if value.match /^\/unleashtheloremipsum/
+        [_, interval, batchCount] = value.split " "
+        [interval, batchCount] = parse interval, batchCount
+        PrivateMessageLoadTest.run this, interval, batchCount
+      else if value.match /^\/analyzetheloremipsum/
+        PrivateMessageLoadTest.analyze this

     @input.reset yes
     @createFakeItemView value, timestamp


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state_camera.coffee
+++ src/game_state_camera.coffee
@@ -1,4 +1,4 @@
-GameState.Camera = (I, self) ->
+GameState.Cameras = (I, self) ->
   cameras = [Camera()]

   self.bind 'afterUpdate', ->


Add a test for default urls based on resource name and urlPrefixes

--- tests/batman/storage_adapter/rest_storage_helper.coffee
+++ tests/batman/storage_adapter/rest_storage_helper.coffee
@@ -391,6 +391,11 @@
     url = @adapter.urlForCollection @Product, {}
     equal url, "/products#{urlSuffix}"

+  test 'absent model urls should have the urlPrefix added if present', 1, ->
+    @Product.urlPrefix = "/admin"
+    url = @adapter.urlForCollection @Product, {}
+    equal url, "/admin/products#{urlSuffix}"
+
   test 'function model urls should be given the options for the storage operation', 1, ->
     opts = {foo: true}
     @Product.url = (passedOpts) ->


Beautify spec.

Beautify spec.

--- src/spec/variants.spec.coffee
+++ src/spec/variants.spec.coffee
@@ -11,28 +11,34 @@
       expect(@variants).toBeDefined()

   describe '#groupVariants', ->
-    it 'should do1', (done) ->
+    it 'should use variantId 1 for each new product', (done) ->
       csv =
         '''
         foo,bar
         xyz,abc
+        123,456
         '''
       Csv().from.string(csv).to.array (data, count) =>
         rows = @variants.groupVariants(_.rest(data), 0)
-        expect(_.size rows).toBe 1
-        expect(rows[0]).toEqual ['xyz', 'abc', 1]
+        expect(_.size rows).toBe 2
+        expect(rows[1]).toEqual ['xyz', 'abc', 1]
+        expect(rows[0]).toEqual ['123', '456', 1]
         done()

-    it 'should do2', (done) ->
+    it 'should set proper variantId for each detected variant', (done) ->
       csv =
         '''
         foo,bar
-        x,1
-        y,1
+        x,same
+        y,same
+        z,same
+        a,differnet
         '''
       Csv().from.string(csv).to.array (data, count) =>
         rows = @variants.groupVariants(_.rest(data), 1)
-        expect(_.size rows).toBe 2
-        expect(rows[0]).toEqual ['x', '1', 1]
-        expect(rows[1]).toEqual ['y', '1', 2]
+        expect(_.size rows).toBe 4
+        expect(rows[0]).toEqual ['x', 'same', 1]
+        expect(rows[1]).toEqual ['y', 'same', 2]
+        expect(rows[2]).toEqual ['z', 'same', 3]
+        expect(rows[3]).toEqual ['a', 'differnet', 1]
         done()


lod_and_effect: Fix some marker clicking weirdness

 - This code is a mess!

--- lod_and_effect/lod_and_effect.coffee
+++ lod_and_effect/lod_and_effect.coffee
@@ -541,7 +541,7 @@
                  d3.select(this).attr("opacity", 1).attr("fill",pink).attr("stroke",purple)
                  effectPlot chr, td
                  revPXG chr, td
-                 unless randomMarker is ""
+                 if randomMarker != "" and randomMarker != td
                    d3.select("#circle#{randomMarker}")
                      .attr("opacity", 0)
                      .attr("fill",purple)
@@ -585,6 +585,7 @@
                botsvg.select("path#detailedLod")
                   .attr("d", botlodcurve(d)(data.lod[d].pos))
                botsvg.selectAll("circle.markercircle").remove()
+               randomMarker = maxLodByChr_marker[d]
                dotsAtMarkers(d)
                d3.select("text#botLtitle").text("Chromosome #{d}")


remove -q from repl

remove -q from repl

--- lib/connection/terminal.coffee
+++ lib/connection/terminal.coffee
@@ -25,7 +25,7 @@

   jlpath: -> atom.config.get("julia-client.juliaPath")

-  repl: -> @term "#{@escpath @jlpath()} -q"
+  repl: -> @term "#{@escpath @jlpath()}"

   client: (port) ->
     client.booting()


Automatically scale the view frustum based upon scene scale.

--- main/bone-hand/leap.bone-hand.coffee
+++ main/bone-hand/leap.bone-hand.coffee
@@ -1,6 +1,6 @@
 scope = null

-initScene = (targetEl)->
+initScene = (targetEl, scale)->
   scope.scene = new THREE.Scene()
   scope.renderer = renderer = new THREE.WebGLRenderer({
     alpha: true
@@ -28,16 +28,20 @@
   directionalLight.position.set( -0.5, 0, -0.2 )
   scope.scene.add(directionalLight)

-  scope.camera = camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000)
+  near = 10 # 1 cm
+  far = 10000 # 10 m
+
+  if scale
+    near *= scale
+    far  *= scale
+
+  scope.camera = camera = new THREE.PerspectiveCamera(45, width / height, near, far)

   camera.position.fromArray([0, 300, 500]);
   camera.lookAt(new THREE.Vector3(0, 160, 0));

   scope.scene.add(camera)
-
-  renderer.render(scope.scene, camera)
-

   window.addEventListener 'resize', ->
     width = window.innerWidth
@@ -365,7 +369,11 @@
   # this allows a null scene to be passed in for delayed-initialization.
   if scope.scene == undefined
     console.assert(scope.targetEl)
-    initScene(scope.targetEl)
+
+    if @plugins.transform && @plugins.transform.scale
+      scale = @plugins.transform.scale.x # we just grab one value, as they're usually all the same.
+
+    initScene(scope.targetEl, scale)

   # Preload two hands
   if scope.scene


Make root div inline-block as well

Make root div inline-block as well

--- lib/grammar-status-view.coffee
+++ lib/grammar-status-view.coffee
@@ -4,7 +4,7 @@
 module.exports =
 class GrammarStatusView extends View
   @content: ->
-    @div class: 'grammar-status', =>
+    @div class: 'grammar-status inline-block', =>
       @a href: '#', class: 'inline-block', outlet: 'grammarLink'

   initialize: (@statusBar) ->


Fix lock not updating DOM

Fix lock not updating DOM

--- app/assets/javascripts/application/stacks.js.coffee
+++ app/assets/javascripts/application/stacks.js.coffee
@@ -68,7 +68,7 @@

   onStackUpdate = (message) ->
     json = JSON.parse(message.data)
-    $('[data-stack-locked]').data('stack-locked', json.locked)
+    $('[data-stack-locked]').attr('data-stack-locked', json.locked)
     $('.lock-reason p').text(json.lock_reason)
     $('.lock-reason').toggle(json.locked)


Fix clicking Boolean/ToggleView labels

Fix clicking Boolean/ToggleView labels

--- app/assets/javascripts/lib/views/toggle_view.coffee
+++ app/assets/javascripts/lib/views/toggle_view.coffee
@@ -31,8 +31,8 @@

     @setLabelActiveStates(@model.get(@attr))

-    @onEl.on  'click', => @setValue(@onValue)
-    @offEl.on 'click', => @setValue(@offValue)
+    @onEl.on  'click', => @quinn.setValue(@quinn.model.maximum)
+    @offEl.on 'click', => @quinn.setValue(@quinn.model.minimum)

     # Changes elsewhere should update the toggle switch.
     @model.on "change:#{ @attr }", (model, status) =>


removed an extra blank line.. ocd.

removed an extra blank line.. ocd.

--- app/assets/javascripts/teaspoon/base/fixture.coffee
+++ app/assets/javascripts/teaspoon/base/fixture.coffee
@@ -72,7 +72,6 @@
     Teaspoon.fixture.el.innerHTML += content

-
   create = =>
     Teaspoon.fixture.el = document.createElement("div")
     Teaspoon.fixture.$el = $(Teaspoon.fixture.el) if typeof(window.$) == 'function'