<commit_before>import { HTMLElementAssembler } from './element'

const { HTMLLabelElement } = window

export class Label extends HTMLElementAssembler {
    /**
     * @param control
     */
    set control(control) {
        this.htmlFor = control.id
    }

    /**
     * @returns {*}
     */
    get control() {
        return this.getInstanceOf(this.node.control)
    }

    /**
     * @returns {Form|HTMLElementAssembler|null}
     */
    get form() {
        return this.getInstanceOf(this.node.form)
    }

    /**
     * @param {*} htmlFor
     */
    set htmlFor(htmlFor) {
        this.node.htmlFor = htmlFor
    }

    /**
     * @returns {NodeAssembler|null}
     */
    get htmlFor() {
        return this.node.htmlFor
    }

    /**
     * @returns {window.HTMLLabelElement}
     */
    static get interface() {
        return HTMLLabelElement
    }
}

/**
 * @param {*} [init]
 * @returns {Label}
 */
export function label(init) {
    return new Label(init)
}

Label.register()
<commit_msg>Label.control: generate unique ID on setting control without ID<commit_after>import { HTMLElementAssembler } from './element'

const { HTMLLabelElement } = window

export class Label extends HTMLElementAssembler {
    /**
     * @param {*} control
     */
    set control(control) {
        this.htmlFor = control.id || (control.id = HTMLElementAssembler.generateId())
    }

    /**
     * @returns {Button|Input|Meter|Output|Progress|Select|TextArea|null}
     */
    get control() {
        return this.getInstanceOf(this.node.control)
    }

    /**
     * @returns {Form|null}
     */
    get form() {
        return this.getInstanceOf(this.node.form)
    }

    /**
     * @param {string} htmlFor
     */
    set htmlFor(htmlFor) {
        this.node.htmlFor = htmlFor
    }

    /**
     * @returns {string}
     */
    get htmlFor() {
        return this.node.htmlFor
    }

    /**
     * @returns {interface} HTMLLabelElement
     */
    static get interface() {
        return HTMLLabelElement
    }
}

/**
 * @param {*} [init]
 * @returns {Label}
 */
export function label(init) {
    return new Label(init)
}

Label.register()
<|endoftext|>


<commit_before>/**
 * Main entry point for the scenario editor
 * @author mattwigway
 */

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import bootstrap from './bootstrap'
import ScenarioEditor from './scenario-editor'
import store from './store'
// browserify-css packs the css using this file
import './styles.css'

bootstrap(store)

// initialize the application
ReactDOM.render(
  <Provider store={store}>
    <ScenarioEditor />
  </Provider>,
  document.getElementById('root'))
<commit_msg>Load bootstrap.css<commit_after>/**
 * Main entry point for the scenario editor
 * @author mattwigway
 */

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import bootstrap from './bootstrap'
import ScenarioEditor from './scenario-editor'
import store from './store'
// browserify-css packs the css using this file
import 'bootstrap/dist/css/bootstrap.css'
import './styles.css'

bootstrap(store)

// initialize the application
ReactDOM.render(
  <Provider store={store}>
    <ScenarioEditor />
  </Provider>,
  document.getElementById('root'))
<|endoftext|>


<commit_before>// Install deps:
// sudo npm install -g gulp bower less
// npm install

var gulp = require('gulp');

var plugins = require("gulp-load-plugins")({lazy: false});
var gconf = require('./gulpconfig.json');

var default_browser = 'Google Chrome';
if (process.platform === 'win32') {
    default_browser = 'chrome';
}

// Download all bower dependencies and 3rd party components
gulp.task('bower', function() {
    return plugins.bower();
});

/**
coming soon
gulp.task('lint', function() {
    return gulp.src(gconf.scripts)
        .pipe(plugins.jshint({
            eqnull: true,
            sub: true
        }))
        .pipe(plugins.jshint.reporter('default'));
});
**/

/**
 * Concat and uglify all scripts into build path.
 *
 */
gulp.task('scripts', ['bower'], function() {
    // Minify and copy all JavaScript (except vendor script)
    for (var outputFile in gconf.scripts) {
        console.log('creating '+outputFile+'...');
        gulp.src(gconf.scripts[outputFile].files)
        .pipe(plugins.concat(outputFile).on('error', plugins.util.log))
        .pipe(plugins.uglify().on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+gconf.scripts[outputFile].output_path));
    }
    return;
});

/**
 * Process LESS files and copy into build directory
 *
 */
gulp.task('styles', ['bower'], function() {
    // Minify and copy all Styles
    for (var outputFile in gconf.stylesheets) {
        console.log('creating '+outputFile+'...');
        gulp.src(gconf.stylesheets[outputFile].files)
        .pipe(plugins.less({
            gconf: ".",
            sourceMap: true,
            compress: true
        }).on('error', plugins.util.log))
        //.pipe(plugins.minifyCss())
        //.pipe(plugins.concat(outputFile))
        .pipe(gulp.dest(gconf.build_path+gconf.stylesheets[outputFile].output_path))
        .pipe(plugins.connect.reload());
    }

    return;
});

/**
 * Copy and compress static images
 *
 */
gulp.task('images', function() {

    for (var outputPath in gconf.images) {
        console.log('compressing images to '+outputPath+'...');
        gulp.src(gconf.images[outputPath].files)
        .pipe(plugins.imagemin(gconf.images[outputPath].options).on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+outputPath))
        .pipe(plugins.connect.reload());
    }

    return;
});

// Copy files (ussually for fonts or other assets being copied)
// from bower_components into the build directory
gulp.task('copy', function() {

    for (var outputPath in gconf.copy) {
        gulp.src(gconf.copy[outputPath].files)
        .pipe(gulp.dest(gconf.build_path+outputPath));
    }

    return;
});

/**
 * Compile templates
 *
 */

// first set some additional dynamic context
//

gulp.task('views', function () {

    for (var outputPath in gconf.views) {
        console.log('creating views in '+outputPath+'...');
        gulp.src(gconf.views[outputPath].files)
        .pipe(plugins.nunjucksRender(gconf.views[outputPath].context).on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+outputPath))
        .pipe(plugins.connect.reload());
    }

    return;
});

/**
 * Start LiveReload Server
 *
 */
gulp.task('server', plugins.connect.server({
    root: [gconf.build_path],
    port: 8080,
    livereload: true,
    open: {
        browser: default_browser
    }
}) );

/**
 * Watch for file changes
 * But first, process all the files and start the livereload server
 *
 */
gulp.task('watch', ['default', 'server'], function () {

    for (var o in gconf.scripts) {
        if (gconf.scripts[o].watch !== false) {
            gulp.watch(gconf.scripts[o].watch, ['scripts']);
        }
    }
    for (o in gconf.stylesheets) {
        if (gconf.stylesheets[o].watch !== false) {
            gulp.watch(gconf.stylesheets[o].watch, ['styles']);
        }
    }
    for (o in gconf.views) {
        if (gconf.views[o].watch !== false) {
            gulp.watch(gconf.views[o].watch, ['views']);
        }
    }
    for (o in gconf.images) {
        gulp.watch(gconf.images[o].files, ['images']);
    }

});

/**
 * Runs all the processes except for watch and livereload
 */
gulp.task('default', [
    'scripts',
    'styles',
    'images',
    'copy',
    'views'
]);
<commit_msg>* Also added error handling so gulp will no longer exit on an error, it will continue to run and just display the error message in the console.<commit_after>// Install deps:
// sudo npm install -g gulp bower less
// npm install

var gulp = require('gulp');

var plugins = require("gulp-load-plugins")({lazy: false});
var gconf = require('./gulpconfig.json');

var default_browser = 'Google Chrome';
if (process.platform === 'win32') {
    default_browser = 'chrome';
}

// Download all bower dependencies and 3rd party components
gulp.task('bower', function() {
    return plugins.bower();
});

/**
coming soon
gulp.task('lint', function() {
    return gulp.src(gconf.scripts)
        .pipe(plugins.jshint({
            eqnull: true,
            sub: true
        }))
        .pipe(plugins.jshint.reporter('default'));
});
**/

/**
 * Concat and uglify all scripts into build path.
 *
 */
gulp.task('scripts', ['bower'], function() {
    // Minify and copy all JavaScript (except vendor script)
    for (var outputFile in gconf.scripts) {
        console.log('creating '+outputFile+'...');
        gulp.src(gconf.scripts[outputFile].files)
        .pipe(plugins.concat(outputFile).on('error', plugins.util.log))
        .pipe(plugins.uglify().on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+gconf.scripts[outputFile].output_path));
    }
    return;
});

/**
 * Process LESS files and copy into build directory
 *
 */
gulp.task('styles', ['bower'], function() {
    // Minify and copy all Styles
    for (var outputFile in gconf.stylesheets) {
        console.log('creating '+outputFile+'...');
        gulp.src(gconf.stylesheets[outputFile].files)
        .pipe(plugins.less({
            gconf: ".",
            sourceMap: true,
            compress: true
        }).on('error', plugins.util.log))
        //.pipe(plugins.minifyCss())
        //.pipe(plugins.concat(outputFile))
        .pipe(gulp.dest(gconf.build_path+gconf.stylesheets[outputFile].output_path))
        .pipe(plugins.connect.reload());
    }

    return;
});

/**
 * Copy and compress static images
 *
 */
gulp.task('images', function() {

    for (var outputPath in gconf.images) {
        console.log('compressing images to '+outputPath+'...');
        gulp.src(gconf.images[outputPath].files)
        .pipe(plugins.imagemin(gconf.images[outputPath].options).on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+outputPath))
        .pipe(plugins.connect.reload());
    }

    return;
});

// Copy files (ussually for fonts or other assets being copied)
// from bower_components into the build directory
gulp.task('copy', function() {

    for (var outputPath in gconf.copy) {
        gulp.src(gconf.copy[outputPath].files)
        .pipe(gulp.dest(gconf.build_path+outputPath));
    }

    return;
});

/**
 * Compile templates
 *
 */
gulp.task('views', function () {

    for (var outputPath in gconf.views) {
        console.log('creating views in '+outputPath+'...');
        gulp.src(gconf.views[outputPath].files)
        .pipe(plugins.nunjucksRender(gconf.views[outputPath].context).on('error', plugins.util.log))
        .pipe(gulp.dest(gconf.build_path+outputPath))
        .pipe(plugins.connect.reload());
    }

    return;
});

/**
 * Start LiveReload Server
 *
 */
gulp.task('server', plugins.connect.server({
    root: [gconf.build_path],
    port: 8080,
    livereload: true,
    open: {
        browser: default_browser
    }
}) );

/**
 * Watch for file changes
 * But first, process all the files and start the livereload server
 *
 */
gulp.task('watch', ['default', 'server'], function () {

    for (var o in gconf.scripts) {
        if (gconf.scripts[o].watch !== false) {
            gulp.watch(gconf.scripts[o].watch, ['scripts']);
        }
    }
    for (o in gconf.stylesheets) {
        if (gconf.stylesheets[o].watch !== false) {
            gulp.watch(gconf.stylesheets[o].watch, ['styles']);
        }
    }
    for (o in gconf.views) {
        if (gconf.views[o].watch !== false) {
            gulp.watch(gconf.views[o].watch, ['views']);
        }
    }
    for (o in gconf.images) {
        gulp.watch(gconf.images[o].files, ['images']);
    }

});

/**
 * Runs all the processes except for watch and livereload
 */
gulp.task('default', [
    'scripts',
    'styles',
    'images',
    'copy',
    'views'
]);
<|endoftext|>


<commit_before>var vows         = require('vows')
  , assert       = require('assert')
  , xmlrpcParser = require('../lib/xmlrpc-parser.js')

vows.describe('XML-RPC Parser').addBatch({
  // Test parseResponseXml functionality
  'A parseResponseXml call' : {
    // Test Array
    'with an Array param' : { topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><array><data><value><int>178</int></value><value><string>testString</string></value></data></array></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testString']])
      }
    }
  , 'with a nested Array param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value></data></array></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64]]])
      }
    }
  , 'with a nested Array param and values after the nested array' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value><value><string>testLevel1StringAfter</string></value></data></array></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64], 'testLevel1StringAfter']])
      }
    }
  , 'with multiple params containing Arrays and other values mixed in' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value><value><string>testLevel1StringAfter</string></value></data></array></value></param>'
          + '<param><value><array><data><value><int>19</int></value><value><boolean>1</boolean></value><value><array><data><value><string>testSomeString</string></value><value><int>60</int></value></data></array></value><value><string>pleasework</string></value><value><double>7.11</double></value></data></array></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.typeOf(params[1], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64], 'testLevel1StringAfter'], [19, true, ['testSomeString', 60], 'pleasework', 7.11]])
      }
    }
    // Test Boolean
  , 'with a true Boolean param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><boolean>1</boolean></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array with a true value' : function (err, params) {
        assert.typeOf(params[0], 'boolean')
        assert.deepEqual(params, [true])
      }
    }
  , 'with a false Boolean param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><boolean>0</boolean></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array with a false value' : function (err, params) {
        assert.typeOf(params[0], 'boolean')
        assert.deepEqual(params, [false])
      }
    }
    // Test DateTime
  , 'with a Datetime param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><dateTime.iso8601>20120608T11:35:10</dateTime.iso8601></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the Date object' : function (err, params) {
        assert.typeOf(params[0], 'date')
        assert.deepEqual(params, [new Date(2012, 05, 08, 11, 35, 10)])
      }
    }
    // Test Double
  , 'with a positive Double param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><double>4.11</double></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the positive double' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [4.11])
      }
    }
  , 'with a negative Double param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><double>-4.2221</double></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the positive double' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-4.2221])
      }
    }
    // Test Integer
  , 'with a positive Int param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><int>4</int></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the positive integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [4])
      }
    }
  , 'with a positive I4 param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><i4>6</i4></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the positive integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [6])
      }
    }
  , 'with a negative Int param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><int>-14</int></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the negative integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-14])
      }
    }
  , 'with a negative I4 param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><i4>-26</i4></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the negative integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-26])
      }
    }
  , 'with a Int param of 0' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><int>0</int></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the value 0' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [0])
      }
    }
  , 'with a I4 param of 0' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><i4>0</i4></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the value 0' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [0])
      }
    }
    // Test String
  , 'with a String param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><string>testString</string></value></param></params></methodResponse>', this.callback)
      }
    , 'contains an array with the string' : function (err, params) {
        assert.typeOf(params[0], 'string')
        assert.deepEqual(params, ['testString'])
      }
    }
    // Test Struct
  , 'with a Struct param' : {
      topic: function() {
        xmlrpcParser.parseResponseXml('<methodResponse><params><param><value><struct><member><name>theName</name><value><string>testValue</string></value></member></struct></value></param></params></methodResponse>', this.callback)
      }
    , 'contains the object' : function (err, params) {
        assert.isObject(params[0])
        assert.deepEqual(params, [{ theName: 'testValue'}])
      }
    }
  , 'with a nested Struct param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><struct><member><name>theName</name><value><string>testValue</string></value></member><member><name>anotherName</name><value><struct><member><name>nestedName</name><value><string>nestedValue</string></value></member></struct></value></member><member><name>lastName</name><value><string>Smith</string></value></member></struct></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the objects' : function (err, params) {
        assert.isObject(params[0])
        assert.deepEqual(params, [{ theName: 'testValue', anotherName: {nestedName: 'nestedValue' }, lastName: 'Smith'}])
      }
    }
  }

}).export(module)
<commit_msg>Minor refactoring and beefing up of the parser test cases.<commit_after>var vows         = require('vows')
  , assert       = require('assert')
  , xmlrpcParser = require('../lib/xmlrpc-parser.js')

vows.describe('XML-RPC Parser').addBatch({
  // Test parseResponseXml functionality
  'A parseResponseXml call' : {
    // Test Array
    'with an Array param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><array><data><value><int>178</int></value><value><string>testString</string></value></data></array></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testString']])
      }
    }
  , 'with a nested Array param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value></data></array></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64]]])
      }
    }
  , 'with a nested Array param and values after the nested array' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value><value><string>testLevel1StringAfter</string></value></data></array></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64], 'testLevel1StringAfter']])
      }
    }
  , 'with multiple params containing Arrays and other values mixed in' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><array><data><value><int>178</int></value><value><string>testLevel1String</string></value><value><array><data><value><string>testString</string></value><value><int>64</int></value></data></array></value><value><string>testLevel1StringAfter</string></value></data></array></value></param>'
          + '<param><value><array><data><value><int>19</int></value><value><boolean>1</boolean></value><value><array><data><value><string>testSomeString</string></value><value><int>60</int></value></data></array></value><value><string>pleasework</string></value><value><double>7.11</double></value></data></array></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array of arrays' : function (err, params) {
        assert.typeOf(params[0], 'array')
        assert.typeOf(params[1], 'array')
        assert.deepEqual(params, [[178, 'testLevel1String', ['testString', 64], 'testLevel1StringAfter'], [19, true, ['testSomeString', 60], 'pleasework', 7.11]])
      }
    }
    // Test Boolean
  , 'with a true Boolean param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><boolean>1</boolean></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array with a true value' : function (err, params) {
        assert.typeOf(params[0], 'boolean')
        assert.deepEqual(params, [true])
      }
    }
  , 'with a false Boolean param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><boolean>0</boolean></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array with a false value' : function (err, params) {
        assert.typeOf(params[0], 'boolean')
        assert.deepEqual(params, [false])
      }
    }
    // Test DateTime
  , 'with a Datetime param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><dateTime.iso8601>20120608T11:35:10</dateTime.iso8601></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the Date object' : function (err, params) {
        assert.typeOf(params[0], 'date')
        assert.deepEqual(params, [new Date(2012, 05, 08, 11, 35, 10)])
      }
    }
    // Test Double
  , 'with a positive Double param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><double>4.11</double></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the positive double' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [4.11])
      }
    }
  , 'with a negative Double param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><double>-4.2221</double></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the positive double' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-4.2221])
      }
    }
    // Test Integer
  , 'with a positive Int param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><int>4</int></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the positive integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [4])
      }
    }
  , 'with a positive I4 param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><i4>6</i4></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the positive integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [6])
      }
    }
  , 'with a negative Int param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><int>-14</int></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the negative integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-14])
      }
    }
  , 'with a negative I4 param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><i4>-26</i4></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the negative integer' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [-26])
      }
    }
  , 'with a Int param of 0' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><int>0</int></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the value 0' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [0])
      }
    }
  , 'with a I4 param of 0' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><i4>0</i4></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the value 0' : function (err, params) {
        assert.typeOf(params[0], 'number')
        assert.deepEqual(params, [0])
      }
    }
    // Test String
  , 'with a String param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><string>testString</string></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains an array with the string' : function (err, params) {
        assert.typeOf(params[0], 'string')
        assert.deepEqual(params, ['testString'])
      }
    }
    // Test Struct
  , 'with a Struct param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><struct><member><name>theName</name><value><string>testValue</string></value></member></struct></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the object' : function (err, params) {
        assert.isObject(params[0])
        assert.deepEqual(params, [{ theName: 'testValue'}])
      }
    }
  , 'with a nested Struct param' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><struct><member><name>theName</name><value><string>testValue</string></value></member><member><name>anotherName</name><value><struct><member><name>nestedName</name><value><string>nestedValue</string></value></member></struct></value></member><member><name>lastName</name><value><string>Smith</string></value></member></struct></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the objects' : function (err, params) {
        assert.isObject(params[0])
        assert.deepEqual(params, [{ theName: 'testValue', anotherName: {nestedName: 'nestedValue' }, lastName: 'Smith'}])
      }
    }
  // The grinder
  , 'with a mix of everything' : {
      topic: function() {
        var xml = '<methodResponse><params>'
          + '<param><value><struct><member><name>theName</name><value><string>testValue2</string></value></member><member><name>anotherName2</name><value><struct><member><name>somenestedName</name><value><string>nestedValue2k</string></value></member></struct></value></member><member><name>lastName</name><value><string>Smith</string></value></member></struct></value></param>'
          + '<param><value><array><data>'
            + '<value><struct><member><name>theName</name><value><string>testValue</string></value></member><member><name>anotherName</name><value><struct><member><name>nestedName</name><value><string>nestedValue</string></value></member></struct></value></member><member><name>lastName</name><value><string>Smith</string></value></member></struct></value>'
            + '<value><array><data>'
              + '<value><struct><member><name>yetAnotherName</name><value><double>1999.26</double></value></member></struct></value>'
              + '<value><string>moreNested</string></value>'
            + '</data></array></value>'
          + '</data></array></value></param>'
          + '<param><value><dateTime.iso8601>19850608T14:35:10</dateTime.iso8601></value></param>'
          + '</params></methodResponse>'
        xmlrpcParser.parseResponseXml(xml, this.callback)
      }
    , 'contains the objects' : function (err, params) {
        assert.isObject(params[0])
          var expected = [
            { theName: 'testValue2', anotherName2: {somenestedName: 'nestedValue2k' }, lastName: 'Smith'}
          , [
              { theName: 'testValue', anotherName: {nestedName: 'nestedValue' }, lastName: 'Smith' }
            , [
                { yetAnotherName: 1999.26}
              , 'moreNested'
              ]
            ]
          , new Date(1985, 05, 08, 14, 35, 10)
          ]
        assert.deepEqual(params, expected)
      }
    }
  }

}).export(module)
<|endoftext|>


<commit_before>// Latte supports Mocha syntax for now, but we'd like to evolve.
// It needs to support two different modes: headless and debug
// * headless runs in the background, and is optimized for speed.
// * debug is optimized for an engineer with devtools open.
(function() {
  window.Latte = {}

  // create the root describe block
  var current = Latte.root = {
    children: [], tests: [],
    before: [], beforeEach: [], after: [], afterEach: [], helpers: {}
  }

  var mode // headless or debug
  var currentContext = null // The context of the currently running test.
  var abort = false // stop tests mid-run. Usually when code changes
  var currentTimeout = null
  var otherError = null // stores any other error that occurred during the test run

  var whenCurrentTestFinished = null // promise that's resolved when the currently running test finishes
  var currentTestResolve = null

  // Save some globals in case test code overrides it (ie sinon, bluebird, etc)
  var realSetTimeout = window.setTimeout
  var realClearTimeout = window.clearTimeout
  var RealPromise = window.Promise
  var realReload = window.location.reload.bind(window.location)

  window.describe = function(name, fn) {
    var previous = current
    current = {
      parent: previous, name: name, children: [], tests: [],
      fullName: [previous.fullName, name].filter(s => !!s).join(' '),
      before: [], beforeEach: [], after: [], afterEach: [], helpers: {},
      timeout: (val) => {} // TODO
    }

    // Replace ourselves in the heirarchy. This happens when a test file is hot-reloaded.
    // TODO: if the name changes, this breaks. Maybe use filename instead?
    let oldIndex = previous.children.findIndex(d => d.name == name)
    if (~oldIndex)
      previous.children[oldIndex] = current
    else
      previous.children.push(current)

    fn.call(current)
    current = previous
  }

  window.context = window.describe
  describe.skip = function(name) {}
  describe.only = function(name) {} // TODO
  window.timeout = function(val) {} // TODO
  window.beforeEach = function(fn) { current.beforeEach.push({fn, stack: getStack()}) }
  window.afterEach = function(fn) { current.afterEach.push({fn, stack: getStack()}) }
  window.before = function(fn) { current.before.push({fn, stack: getStack()}) }
  window.after = function(fn) { current.after.push({fn, stack: getStack()}) }

  // Helpers add methods to the current scope that aid writing clean tests.
  // For example, you might have a `login` or `goOffline` helper that you can call at the top of a
  // test to get things in the right state. Helpers are usually preferred over `beforeEach`, because
  // having setup code in the test makes it easier to understand.
  //
  // Helpers can be defined as a single function: `helper('login', function() {...})`
  // Or as a group: `helper('Factory', {user: function() {...}, hats: function() {...}})`
  // Groups are a great way to organize related helpers.
  window.helper = function(name, fnOrGroup) { current.helpers[name || 'default'] = fnOrGroup }

  window.it = function(name, fn) {
    if (!fn) return
    fullName = current.fullName + ' ' + name
    current.tests.push({fn, name, fullName, suite: current, stack: getStack()})
  }
  it.skip = function() {}
  it.only = function() {}

  function getStack() {
    return new Error().stack.split('\n')[3].trim()
  }

  Latte.setup = function latteSetup(md) {
    mode = md

    let oldWarn = console.warn, oldError = console.error
    // console.warn = function() { otherError = arguments; oldWarn.apply(console, arguments) }
    // console.error = function() { otherError = arguments; oldError.apply(console, arguments) }

    // These seem to break Chrome's "pause on uncaught exception", so don't do it in debug
    if (mode == 'headless') {
      window.onunhandledrejection = e => { otherError = e }
      window.onerror = e => { otherError = e }
    }
  }

  // Cancel any remaining tests and clean up.
  // TODO: this should let the current test finish, though that's tricky in debug mode (where uncaught exceptions are the norm)
  Latte.abort = async function latteClean() {
    abort = true

    whenCurrentTestFinished && await whenCurrentTestFinished

    if (currentContext) {
      await applyCallbacks('afterEach', currentContext)
      await changeSuite(currentContext, null)
      currentContext = null
    }
  }

  // Get all tests (optionally of a specific suite) flattened into an array
  Latte.flatten = function latteFlatten(suite) {
    suite = suite || Latte.root

    var allTests = [], queue = [Latte.root]
    while (step = queue.pop()) {
      allTests = allTests.concat(step.tests)
      queue = queue.concat(step.children)
    }
    return allTests
  }

  Latte.run = async function latteRun(tests) {
    abort = false

    for (test of tests) {
      if (!test.fn) continue
      console.log(`${test.fullName}`)
      otherError = null
      whenCurrentTestFinished = new RealPromise(res => currentTestResolve = res)

      // apply afterEach from the inside out
      if (currentContext && mode == 'debug')
        await applyCallbacks('afterEach', currentContext)

      // each test gets a context that inherits from the suite-level context.
      // This isolates each test but lets us do some setup at the suite-level for speed.
      previousSuiteContext = currentContext && Object.getPrototypeOf(currentContext)
      var suiteContext

      if (mode == 'headless') {
        try { suiteContext = await changeSuite(previousSuiteContext, test.suite) }
        catch(e) { fatalError(e) }
      }

      if (mode == 'debug') {
        suiteContext = await changeSuite(previousSuiteContext, test.suite)
      }

      currentContext = Object.create(suiteContext)

      if (mode == 'headless') {
        try {
          await applyCallbacks('beforeEach', currentContext, {topDown: true})
          await runWithTimeout(test, currentContext)
          Latte.onTest(test, otherError)
        }
        catch (e) {
          realClearTimeout(currentTimeout)
          Latte.onTest(test, e || 'undefined error')
        }

        try { await applyCallbacks('afterEach', currentContext) }
        catch (e) { fatalError(e) }
      }

      if (mode == 'debug') {
        await applyCallbacks('beforeEach', currentContext, {topDown: true})
        await runWithTimeout(test, currentContext)
      }

      // signal that we've finished this test, in case we're aborting
      currentTestResolve()
      whenCurrentTestFinished = currentTestResolve = null
      if (abort) break
    }

    console.log('Done!')
  }

  function fatalError(e) {
    console.error('Fatal', e)
    realReload()
  }

  // Change our context from one suite to another by running the minimal set of before/after callbacks.
  // Any common ancestors between the two suites don't need to run.
  async function changeSuite(context, nextSuite) {
    let currentSuite = context && context._suite

    // short circuit when the suite doesn't actually change
    if (context && context._suite === nextSuite)
      return context

    // figure out what the common ancestor is between both suites. If either suite is null
    // (happens at beginning and end of run) then commonAncestor will be undefined, and our
    // slice calls below will yield the full lineage.
    let currLineage = lineage(currentSuite)
    let nextLineage = lineage(nextSuite)
    let commonAncestor = currLineage.filter(x => nextLineage.indexOf(x) >= 0)[0]

    // walk the lineage up to (but not including) the common ancestor, running after callbacks
    let currTop = currLineage.indexOf(commonAncestor)
    currTop = currTop == -1 ? undefined : currTop
    let chain = currLineage.slice(0, currTop)
    for (suite of chain) {
      for (cb of suite.after)
        await runWithTimeout(cb, context)
      context = Object.getPrototypeOf(context)
    }

    // now walk down the lineage from right below the common ancestor to the new suite, running before callbacks
    let nextTop = nextLineage.indexOf(commonAncestor)
    nextTop = nextTop == -1 ? undefined : nextTop
    chain = nextLineage.slice(0, nextTop).reverse()
    for (suite of chain) {
      context = Object.create(context)
      context._suite = suite
      context.timeout = function() {}
      attachHelpers(suite, context)
      for (cb of suite.before)
        await runWithTimeout(cb, context)
    }

    return context
  }

  // Run user code with a timeout
  async function runWithTimeout(cbOrTest, context) {
    let timeoutPromise = new RealPromise((res, rej) => {
      currentTimeout = realSetTimeout(() => {
        if (mode == 'debug') {
          abort = true
          console.error('Timeout', cbOrTest.stack)
        }
        else {
          console.error('Timeout', cbOrTest.stack)
          rej(`Timeout ${cbOrTest.stack}`)
        }
      }, 10000)
    })

    return RealPromise.race([timeoutPromise, cbOrTest.fn.call(context)])
      .then(() => realClearTimeout(currentTimeout))
  }

  // run all the callbacks (before, after, beforeEach, or afterEach) for each suite.
  async function applyCallbacks(type, context, {topDown}={}) {
    let suites = lineage(context._suite)
    if (topDown) suites = suites.reverse()

    for (suite of suites)
      for (cb of suite[type])
        await runWithTimeout(cb, context)
  }

  // Get all the suites between the current one and the root.
  function lineage(suite) {
    if (!suite) return []

    let arr = []
    while (suite) {
      arr.push(suite)
      suite = suite.parent
    }
    return arr
  }

  function attachHelpers(suite, context) {
    for (name in suite.helpers) {
      let obj = suite.helpers[name]

      if (typeof(obj) == 'function') {
        context[name] = obj
        continue
      }

      let dest = name == 'default' ? context : (context[name] = {})
      for (prop of Object.keys(obj)) {
        let val = obj[prop]
        let isFunction = typeof(val) == 'function'
        Object.defineProperty(dest, prop, { get: () => isFunction ? val.bind(currentContext) : val})
      }
    }
  }

})()
<commit_msg>Catch errors during abort<commit_after>// Latte supports Mocha syntax for now, but we'd like to evolve.
// It needs to support two different modes: headless and debug
// * headless runs in the background, and is optimized for speed.
// * debug is optimized for an engineer with devtools open.
(function() {
  window.Latte = {}

  // create the root describe block
  var current = Latte.root = {
    children: [], tests: [],
    before: [], beforeEach: [], after: [], afterEach: [], helpers: {}
  }

  var mode // headless or debug
  var currentContext = null // The context of the currently running test.
  var abort = false // stop tests mid-run. Usually when code changes
  var currentTimeout = null
  var otherError = null // stores any other error that occurred during the test run

  var whenCurrentTestFinished = null // promise that's resolved when the currently running test finishes
  var currentTestResolve = null

  // Save some globals in case test code overrides it (ie sinon, bluebird, etc)
  var realSetTimeout = window.setTimeout
  var realClearTimeout = window.clearTimeout
  var RealPromise = window.Promise
  var realReload = window.location.reload.bind(window.location)

  window.describe = function(name, fn) {
    var previous = current
    current = {
      parent: previous, name: name, children: [], tests: [],
      fullName: [previous.fullName, name].filter(s => !!s).join(' '),
      before: [], beforeEach: [], after: [], afterEach: [], helpers: {},
      timeout: (val) => {} // TODO
    }

    // Replace ourselves in the heirarchy. This happens when a test file is hot-reloaded.
    // TODO: if the name changes, this breaks. Maybe use filename instead?
    let oldIndex = previous.children.findIndex(d => d.name == name)
    if (~oldIndex)
      previous.children[oldIndex] = current
    else
      previous.children.push(current)

    fn.call(current)
    current = previous
  }

  window.context = window.describe
  describe.skip = function(name) {}
  describe.only = function(name) {} // TODO
  window.timeout = function(val) {} // TODO
  window.beforeEach = function(fn) { current.beforeEach.push({fn, stack: getStack()}) }
  window.afterEach = function(fn) { current.afterEach.push({fn, stack: getStack()}) }
  window.before = function(fn) { current.before.push({fn, stack: getStack()}) }
  window.after = function(fn) { current.after.push({fn, stack: getStack()}) }

  // Helpers add methods to the current scope that aid writing clean tests.
  // For example, you might have a `login` or `goOffline` helper that you can call at the top of a
  // test to get things in the right state. Helpers are usually preferred over `beforeEach`, because
  // having setup code in the test makes it easier to understand.
  //
  // Helpers can be defined as a single function: `helper('login', function() {...})`
  // Or as a group: `helper('Factory', {user: function() {...}, hats: function() {...}})`
  // Groups are a great way to organize related helpers.
  window.helper = function(name, fnOrGroup) { current.helpers[name || 'default'] = fnOrGroup }

  window.it = function(name, fn) {
    if (!fn) return
    fullName = current.fullName + ' ' + name
    current.tests.push({fn, name, fullName, suite: current, stack: getStack()})
  }
  it.skip = function() {}
  it.only = function() {}

  function getStack() {
    return new Error().stack.split('\n')[3].trim()
  }

  Latte.setup = function latteSetup(md) {
    mode = md

    let oldWarn = console.warn, oldError = console.error
    // console.warn = function() { otherError = arguments; oldWarn.apply(console, arguments) }
    // console.error = function() { otherError = arguments; oldError.apply(console, arguments) }

    // These seem to break Chrome's "pause on uncaught exception", so don't do it in debug
    if (mode == 'headless') {
      window.onunhandledrejection = e => { otherError = e }
      window.onerror = e => { otherError = e }
    }
  }

  // Cancel any remaining tests and clean up.
  // TODO: this should let the current test finish, though that's tricky in debug mode (where uncaught exceptions are the norm)
  Latte.abort = async function latteClean() {
    abort = true

    whenCurrentTestFinished && await whenCurrentTestFinished

    if (currentContext) {
      try {
        await applyCallbacks('afterEach', currentContext)
        await changeSuite(currentContext, null)
        currentContext = null
      }
      catch (e) { fatalError(e) }
    }
  }

  // Get all tests (optionally of a specific suite) flattened into an array
  Latte.flatten = function latteFlatten(suite) {
    suite = suite || Latte.root

    var allTests = [], queue = [Latte.root]
    while (step = queue.pop()) {
      allTests = allTests.concat(step.tests)
      queue = queue.concat(step.children)
    }
    return allTests
  }

  Latte.run = async function latteRun(tests) {
    abort = false

    for (test of tests) {
      if (!test.fn) continue
      console.log(`${test.fullName}`)
      otherError = null
      whenCurrentTestFinished = new RealPromise(res => currentTestResolve = res)

      // apply afterEach from the inside out
      if (currentContext && mode == 'debug')
        await applyCallbacks('afterEach', currentContext)

      // each test gets a context that inherits from the suite-level context.
      // This isolates each test but lets us do some setup at the suite-level for speed.
      previousSuiteContext = currentContext && Object.getPrototypeOf(currentContext)
      var suiteContext

      if (mode == 'headless') {
        try { suiteContext = await changeSuite(previousSuiteContext, test.suite) }
        catch(e) { fatalError(e) }
      }

      if (mode == 'debug') {
        suiteContext = await changeSuite(previousSuiteContext, test.suite)
      }

      currentContext = Object.create(suiteContext)

      if (mode == 'headless') {
        try {
          await applyCallbacks('beforeEach', currentContext, {topDown: true})
          await runWithTimeout(test, currentContext)
          Latte.onTest(test, otherError)
        }
        catch (e) {
          realClearTimeout(currentTimeout)
          Latte.onTest(test, e || 'undefined error')
        }

        try { await applyCallbacks('afterEach', currentContext) }
        catch (e) { fatalError(e) }
      }

      if (mode == 'debug') {
        await applyCallbacks('beforeEach', currentContext, {topDown: true})
        await runWithTimeout(test, currentContext)
      }

      // signal that we've finished this test, in case we're aborting
      currentTestResolve()
      whenCurrentTestFinished = currentTestResolve = null
      if (abort) break
    }

    console.log('Done!')
  }

  function fatalError(e) {
    // TODO surface this to the user in a better way
    console.error('Fatal', e)
    realReload()
  }

  // Change our context from one suite to another by running the minimal set of before/after callbacks.
  // Any common ancestors between the two suites don't need to run.
  async function changeSuite(context, nextSuite) {
    let currentSuite = context && context._suite

    // short circuit when the suite doesn't actually change
    if (context && context._suite === nextSuite)
      return context

    // figure out what the common ancestor is between both suites. If either suite is null
    // (happens at beginning and end of run) then commonAncestor will be undefined, and our
    // slice calls below will yield the full lineage.
    let currLineage = lineage(currentSuite)
    let nextLineage = lineage(nextSuite)
    let commonAncestor = currLineage.filter(x => nextLineage.indexOf(x) >= 0)[0]

    // walk the lineage up to (but not including) the common ancestor, running after callbacks
    let currTop = currLineage.indexOf(commonAncestor)
    currTop = currTop == -1 ? undefined : currTop
    let chain = currLineage.slice(0, currTop)
    for (suite of chain) {
      for (cb of suite.after)
        await runWithTimeout(cb, context)
      context = Object.getPrototypeOf(context)
    }

    // now walk down the lineage from right below the common ancestor to the new suite, running before callbacks
    let nextTop = nextLineage.indexOf(commonAncestor)
    nextTop = nextTop == -1 ? undefined : nextTop
    chain = nextLineage.slice(0, nextTop).reverse()
    for (suite of chain) {
      context = Object.create(context)
      context._suite = suite
      context.timeout = function() {}
      attachHelpers(suite, context)
      for (cb of suite.before)
        await runWithTimeout(cb, context)
    }

    return context
  }

  // Run user code with a timeout
  async function runWithTimeout(cbOrTest, context) {
    let timeoutPromise = new RealPromise((res, rej) => {
      currentTimeout = realSetTimeout(() => {
        if (mode == 'debug') {
          abort = true
          console.error('Timeout', cbOrTest.stack)
        }
        else {
          console.error('Timeout', cbOrTest.stack)
          rej(`Timeout ${cbOrTest.stack}`)
        }
      }, 10000)
    })

    return RealPromise.race([timeoutPromise, cbOrTest.fn.call(context)])
      .then(() => realClearTimeout(currentTimeout))
  }

  // run all the callbacks (before, after, beforeEach, or afterEach) for each suite.
  async function applyCallbacks(type, context, {topDown}={}) {
    let suites = lineage(context._suite)
    if (topDown) suites = suites.reverse()

    for (suite of suites)
      for (cb of suite[type])
        await runWithTimeout(cb, context)
  }

  // Get all the suites between the current one and the root.
  function lineage(suite) {
    if (!suite) return []

    let arr = []
    while (suite) {
      arr.push(suite)
      suite = suite.parent
    }
    return arr
  }

  function attachHelpers(suite, context) {
    for (name in suite.helpers) {
      let obj = suite.helpers[name]

      if (typeof(obj) == 'function') {
        context[name] = obj
        continue
      }

      let dest = name == 'default' ? context : (context[name] = {})
      for (prop of Object.keys(obj)) {
        let val = obj[prop]
        let isFunction = typeof(val) == 'function'
        Object.defineProperty(dest, prop, { get: () => isFunction ? val.bind(currentContext) : val})
      }
    }
  }

})()
<|endoftext|>


<commit_before>"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var debug = require("debug");
var fs = require("fs");
var stream = require("stream");
var streamify_1 = require("./streamify");
var d = debug('TP:');
var Parser = (function (_super) {
    __extends(Parser, _super);
    function Parser(options) {
        var _this = _super.call(this, { objectMode: true, highWaterMark: 16 }) || this;
        _this.parserOptions = _this.mergeOptions(options);
        _this.quote = new Buffer(_this.parserOptions.quote)[0];
        _this.escapedQuotes = new RegExp("" + _this.parserOptions.quote + _this.parserOptions.quote, 'g');
        return _this;
    }
    Parser.prototype.text2json = function (data, cb) {
        var _this = this;
        var dataStream;
        if (data instanceof Buffer) {
            dataStream = streamify_1.createReadStream(data, {});
        }
        else if (fs.existsSync(data)) {
            dataStream = fs.createReadStream(data);
        }
        else if (typeof data === 'string' || data instanceof String) {
            dataStream = streamify_1.createReadStream(data, {});
        }
        var hashtable = [];
        var headers = [];
        var elements = [];
        var bufEnd = 0;
        var colStart = 0;
        var balancedQuotes = true;
        var i = 0;
        var separator = new Buffer(this.parserOptions.separator)[0];
        var newline = new Buffer(this.parserOptions.newline)[0];
        var crlf = this.parserOptions.newline === '\n' ? false : true;
        dataStream.on('data', function (buf) {
            bufEnd = buf.length;
            for (i = 0; i < bufEnd; i++) {
                if (buf[i] === separator || buf[i] === newline) {
                    var _parsed = _this._value(buf, colStart, i, elements);
                    elements = _parsed.values;
                    balancedQuotes = _parsed.parsed;
                    if (balancedQuotes) {
                        colStart = i + 1;
                    }
                }
                if (balancedQuotes && buf[i] === newline) {
                    if (crlf) {
                        colStart = colStart + 1;
                    }
                    if (headers.length === 0) {
                        if (_this.parserOptions.hasHeader) {
                            headers = elements.slice(0);
                        }
                        else if (!_this.isEmpty(_this.parserOptions.headers)) {
                            headers = _this.parserOptions.headers;
                        }
                        headers = _this.fillHeaders(headers, elements.length);
                        _this.emit('headers', headers);
                        if (!_this.parserOptions.hasHeader) {
                            hashtable[hashtable.length] = _this.createHash(headers, elements);
                        }
                    }
                    else {
                        if (elements.length)
                            hashtable[hashtable.length] = _this.createHash(headers, elements);
                    }
                    elements.length = 0;
                }
            }
            if (!balancedQuotes && i === bufEnd && colStart < bufEnd) {
                var err = 'Unmatched quotes around ' + buf.toString('utf8', colStart, colStart + 20 > bufEnd ? bufEnd : colStart + 20);
                dataStream.emit('error', new Error(err));
            }
            else if (i === bufEnd && colStart < bufEnd) {
                elements = _this._value(buf, colStart, bufEnd, elements).values;
                hashtable[hashtable.length] = _this.createHash(headers, elements);
                colStart = bufEnd;
            }
        });
        dataStream.on('end', function () {
            if (cb) {
                cb(null, hashtable);
            }
            else {
                _this.emit('end', null);
            }
        });
        dataStream.on('error', function (err) {
            if (cb) {
                cb(err, hashtable);
            }
            else {
                _this.emit('error', err);
            }
        });
        return this;
    };
    Parser.prototype._value = function (buf, start, end, values) {
        var balancedQuotes = true;
        var hasQuote = false;
        var parsedValue = '';
        if (start === end) {
            return { parsed: true, values: values };
        }
        for (var i = start; i < end; i++) {
            if (buf[i] === this.quote) {
                balancedQuotes = !balancedQuotes;
                hasQuote = true;
            }
        }
        if (balancedQuotes) {
            parsedValue = hasQuote
                ? buf.toString('utf8', start + 1, end - 1).replace(this.escapedQuotes, this.parserOptions.quote)
                : buf.toString('utf8', start, end);
            values[values.length] = parsedValue;
        }
        return { parsed: balancedQuotes, values: values };
    };
    Parser.prototype.createHash = function (headers, line) {
        var _hash = {};
        for (var i = 0; i < line.length; i++) {
            _hash[headers[i]] = line[i];
        }
        this.emit('row', _hash);
        return _hash;
    };
    Parser.prototype.fillHeaders = function (headers, numElements) {
        headers = headers || [];
        if (headers.length === numElements) {
            return headers;
        }
        else if (headers.length === 0) {
            for (var i = 0; i < numElements; i++) {
                headers.push('_' + i);
            }
        }
        else if (headers.length < numElements) {
            for (var i = headers.length - 1; i < numElements; i++) {
                headers.push('_' + i);
            }
        }
        return headers;
    };
    Parser.prototype.defaultOptions = function () {
        return {
            hasHeader: false,
            headers: [],
            newline: '\n',
            separator: ',',
            quote: '"',
            escape: '"',
            encoding: 'utf8'
        };
    };
    Parser.prototype.mergeOptions = function (options) {
        var defaultOpt = this.defaultOptions();
        var opt = options || defaultOpt;
        opt.hasHeader = options.hasHeader || defaultOpt.hasHeader;
        opt.headers = options.headers || defaultOpt.headers;
        opt.newline = options.newline || defaultOpt.newline;
        opt.separator = options.separator || defaultOpt.separator;
        opt.quote = options.quote || defaultOpt.quote;
        opt.escape = options.escape || defaultOpt.escape;
        opt.encoding = options.encoding || defaultOpt.encoding;
        return opt;
    };
    Parser.prototype.isEmpty = function (obj) {
        if (Array.isArray(obj)) {
            return obj.length > 0 ? false : true;
        }
        else {
            return true;
        }
    };
    return Parser;
}(stream.Transform));
exports.Parser = Parser;
<commit_msg>bump version to 2.1.0<commit_after>"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var debug = require("debug");
var fs = require("fs");
var stream = require("stream");
var streamify_1 = require("./streamify");
var d = debug('TP:');
var Parser = (function (_super) {
    __extends(Parser, _super);
    function Parser(options) {
        var _this = _super.call(this, { objectMode: true, highWaterMark: 16 }) || this;
        _this.parserOptions = _this.mergeOptions(options);
        _this.quote = new Buffer(_this.parserOptions.quote)[0];
        _this.escapedQuotes = new RegExp("" + _this.parserOptions.quote + _this.parserOptions.quote, 'g');
        return _this;
    }
    Parser.prototype.text2json = function (data, cb) {
        var _this = this;
        var dataStream;
        if (data instanceof Buffer) {
            dataStream = streamify_1.createReadStream(data, {});
        }
        else if (fs.existsSync(data)) {
            dataStream = fs.createReadStream(data);
        }
        else if (typeof data === 'string' || data instanceof String) {
            dataStream = streamify_1.createReadStream(data, {});
        }
        var hashtable = [];
        var headers = [];
        var elements = [];
        var rowsSkipped = 0;
        var skipThisRow = this.parserOptions.skipRows > 0;
        var bufEnd = 0;
        var colStart = 0;
        var balancedQuotes = true;
        var i = 0;
        var separator = new Buffer(this.parserOptions.separator)[0];
        var newline = new Buffer(this.parserOptions.newline)[0];
        var crlf = this.parserOptions.newline === '\n' ? false : true;
        dataStream.on('data', function (buf) {
            bufEnd = buf.length;
            for (i = 0; i < bufEnd; i++) {
                if (buf[i] === separator || buf[i] === newline) {
                    var _parsed = _this._value(buf, colStart, i, elements);
                    elements = _parsed.values;
                    balancedQuotes = _parsed.parsed;
                    if (balancedQuotes) {
                        colStart = i + 1;
                    }
                }
                if (balancedQuotes && buf[i] === newline) {
                    if (crlf) {
                        colStart = colStart + 1;
                    }
                    if (headers.length === 0) {
                        if (_this.parserOptions.hasHeader) {
                            headers = elements.slice(0);
                        }
                        else if (!_this.isEmpty(_this.parserOptions.headers)) {
                            headers = _this.parserOptions.headers;
                        }
                        headers = _this.fillHeaders(headers, elements.length);
                        _this.emit('headers', headers);
                        if (!_this.parserOptions.hasHeader) {
                            hashtable[hashtable.length] = _this.createHash(headers, elements);
                        }
                    }
                    else {
                        if (!skipThisRow && elements.length) {
                            hashtable[hashtable.length] = _this.createHash(headers, elements);
                        }
                        else {
                            rowsSkipped++;
                        }
                        skipThisRow = !(rowsSkipped >= _this.parserOptions.skipRows);
                    }
                    elements.length = 0;
                }
            }
            if (!balancedQuotes && i === bufEnd && colStart < bufEnd) {
                var err = 'Unmatched quotes around ' + buf.toString('utf8', colStart, colStart + 20 > bufEnd ? bufEnd : colStart + 20);
                dataStream.emit('error', new Error(err));
            }
            else if (!skipThisRow && i === bufEnd && colStart < bufEnd) {
                elements = _this._value(buf, colStart, bufEnd, elements).values;
                hashtable[hashtable.length] = _this.createHash(headers, elements);
                colStart = bufEnd;
            }
        });
        dataStream.on('end', function () {
            if (cb) {
                cb(null, hashtable);
            }
            else {
                _this.emit('end', null);
            }
        });
        dataStream.on('error', function (err) {
            if (cb) {
                cb(err, hashtable);
            }
            else {
                _this.emit('error', err);
            }
        });
        return this;
    };
    Parser.prototype._value = function (buf, start, end, values) {
        var balancedQuotes = true;
        var hasQuote = false;
        var parsedValue = '';
        if (start === end) {
            return { parsed: true, values: values };
        }
        for (var i = start; i < end; i++) {
            if (buf[i] === this.quote) {
                balancedQuotes = !balancedQuotes;
                hasQuote = true;
            }
        }
        if (balancedQuotes) {
            parsedValue = hasQuote
                ? buf.toString('utf8', start + 1, end - 1).replace(this.escapedQuotes, this.parserOptions.quote)
                : buf.toString('utf8', start, end);
            values[values.length] = parsedValue;
        }
        return { parsed: balancedQuotes, values: values };
    };
    Parser.prototype.createHash = function (headers, line) {
        var _hash = {};
        for (var i = 0; i < line.length; i++) {
            _hash[headers[i]] = line[i];
        }
        this.emit('row', _hash);
        return _hash;
    };
    Parser.prototype.fillHeaders = function (headers, numElements) {
        headers = headers || [];
        if (headers.length === numElements) {
            return headers;
        }
        else if (headers.length === 0) {
            for (var i = 0; i < numElements; i++) {
                headers.push('_' + i);
            }
        }
        else if (headers.length < numElements) {
            for (var i = headers.length - 1; i < numElements; i++) {
                headers.push('_' + i);
            }
        }
        return headers;
    };
    Parser.prototype.defaultOptions = function () {
        return {
            hasHeader: false,
            headers: [],
            newline: '\n',
            separator: ',',
            quote: '"',
            encoding: 'utf8',
            skipRows: 0
        };
    };
    Parser.prototype.mergeOptions = function (options) {
        var defaultOpt = this.defaultOptions();
        var opt = options || defaultOpt;
        opt.hasHeader = options.hasHeader || defaultOpt.hasHeader;
        opt.headers = options.headers || defaultOpt.headers;
        opt.newline = options.newline || defaultOpt.newline;
        opt.separator = options.separator || defaultOpt.separator;
        opt.quote = options.quote || defaultOpt.quote;
        opt.encoding = options.encoding || defaultOpt.encoding;
        opt.skipRows = options.skipRows || defaultOpt.skipRows;
        return opt;
    };
    Parser.prototype.isEmpty = function (obj) {
        if (Array.isArray(obj)) {
            return obj.length > 0 ? false : true;
        }
        else {
            return true;
        }
    };
    return Parser;
}(stream.Transform));
exports.Parser = Parser;
<|endoftext|>


<commit_before>/**
 * Variables
 */
var gulp           = require('gulp'),
    autoprefixer   = require('gulp-autoprefixer'),
    browserSync    = require('browser-sync'),
    changed        = require('gulp-changed'),
    concat         = require('gulp-concat'),
    del            = require('del'),
    foreach        = require('gulp-foreach'),
    ghPages        = require('gulp-gh-pages'),
    notify         = require('gulp-notify'),
    plumber        = require('gulp-plumber'),
    reload         = browserSync.reload,
    runSequence    = require('run-sequence'),
    sass           = require('gulp-sass'),
    sassLint       = require('gulp-sass-lint'),
    sourcemaps     = require('gulp-sourcemaps'),
    twig           = require('gulp-twig'),
    uglify         = require('gulp-uglify'),
    watch          = require('gulp-watch');

/**
 * Paths
 */
var paths = {
  dist: 'dist/',
  src: 'src/',
  deploy: 'dist/**/*',
  html: 'dist/*.html',
  browserSync: './dist',
  sass: {
    inputAll: 'src/sass/**/*.scss',
    input: 'src/sass/main.scss',
    output: 'dist/css',
    lint: 'src/sass/**/*.s+(a|c)ss'
  },
  jsVendor: {
    input: 'src/js/vendor/**/*.js',
    output: 'dist/js'
  },
  jsComponents: {
    input: 'src/js/components/**/*.js',
  },
  js: {
    input: 'src/js/main.js',
    output: 'dist/js'
  },
  img: {
    input: 'src/img/**/*',
    output: 'dist/img'
  },
  twig: {
    inputAll: 'src/templates/**/*.{twig,html}',
    inputLayouts: '!src/templates/layouts/**/*.{twig,html}',
    inputComponents: '!src/templates/components/**/*.{twig,html}',
    inputPartials: '!src/templates/partials/**/*.{twig,html}'
  },
  misc: {
    xml: 'src/*.xml',
    txt: 'src/*.txt'
  }
};

/**
 * Catch stream errors
 */
var onError = function (err) {
  notify.onError({
    title: "Gulp error in " + err.plugin,
    message: err.toString()
  })(err);
};

/**
 * Browser Sync
 */
gulp.task('browser-sync', function() {
  browserSync.init(null, {
    files: [paths.html],
    server: {
      baseDir: paths.browserSync
    },
    notify: false
  });
});

/**
 * Clean dist
 */
gulp.task('clean:dist', function() {
  return del.sync(paths.dist);
})

/**
 * CSS
 */
gulp.task('css', function (done) {
  return gulp.src(paths.sass.input)
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(sass({ outputStyle: 'compressed' }))
    .pipe(autoprefixer('last 2 versions'))
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.sass.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Sass Lint
 */
gulp.task('sass-lint', function () {
  return gulp.src(paths.sass.lint)
    .pipe(sassLint())
    .pipe(sassLint.format())
    .pipe(sassLint.failOnError())
});

/**
 * JS Vendor
 */
gulp.task('js:vendor', function (done) {
  return gulp.src(paths.jsVendor.input)
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(concat('vendor.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.jsVendor.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * JS Main
 */
gulp.task('js:main', function (done) {
  return gulp.src([
    paths.js.input,
    paths.jsComponents.input
  ])
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(concat('main.min.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.js.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Images
 */
gulp.task('images', function (done) {
  return gulp.src([
    paths.img.input
  ], {
    'dot': true // include hidden files
  })
    .pipe(changed(paths.img.output))
    .pipe(gulp.dest(paths.img.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Twig
 */
gulp.task('twig', function (done) {
  return gulp.src([
    paths.twig.inputAll,
    paths.twig.inputLayouts,
    paths.twig.inputComponents,
    paths.twig.inputPartials
  ])
    .pipe(plumber({
      errorHandler: function (error) {
        console.log(error.message);
        this.emit('end');
    }}))
    .pipe(foreach(function(stream,file){
      return stream
        .pipe(twig())
    }))
    .pipe(changed(paths.dist))
    .pipe(gulp.dest(paths.dist))
    done();
});

/**
 * Copy miscellaneous files
 */
gulp.task('copy:misc', function (done) {
  return gulp.src([
    paths.misc.xml,
    paths.misc.txt
  ])
    .pipe(changed(paths.dist))
    .pipe(gulp.dest(paths.dist))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Task: Gulp Watch Sequence
 */
gulp.task('watch-files', function () {
  watch(paths.img.input, function () {
    gulp.start('images');
  });
  watch(paths.sass.inputAll, function () {
    gulp.start('css');
  });
  watch(paths.js.input, function () {
    gulp.start('js:main');
  });
  watch(paths.jsComponents.input, function () {
    gulp.start('js:main');
  });
  watch(paths.jsVendor.input, function () {
    gulp.start('js:vendor');
  });
  watch(paths.twig.inputAll, function () {
    gulp.start('twig');
  });
  watch([paths.misc.xml, paths.misc.txt], function () {
    gulp.start('copy:misc');
  });
});

/**
 * Task: Gulp Default
 */
gulp.task('default', function(done) {
  runSequence('build', [
    'watch-files',
    'browser-sync'
  ], done )
});

/**
 * Task: Gulp Build
 */
gulp.task('build', function (done) {
  runSequence('clean:dist', [
    'css',
    'js:vendor',
    'js:main',
    'images',
    'twig',
    'copy:misc'
  ], done )
})

/**
 * Task: Gulp Watch
 */
gulp.task('watch', function(done) {
  runSequence('watch-files', [
    'browser-sync'
  ], done )
});

/**
 * Task: Gulp Deploy
 */
gulp.task('deploy', function() {
  return gulp.src(paths.deploy)
    .pipe(ghPages());
});
<commit_msg>Fix Twig function so it launches everytime when files from /layout /components and /partials are compiled<commit_after>/**
 * Variables
 */
var gulp           = require('gulp'),
    autoprefixer   = require('gulp-autoprefixer'),
    browserSync    = require('browser-sync'),
    changed        = require('gulp-changed'),
    concat         = require('gulp-concat'),
    del            = require('del'),
    foreach        = require('gulp-foreach'),
    ghPages        = require('gulp-gh-pages'),
    notify         = require('gulp-notify'),
    plumber        = require('gulp-plumber'),
    reload         = browserSync.reload,
    runSequence    = require('run-sequence'),
    sass           = require('gulp-sass'),
    sassLint       = require('gulp-sass-lint'),
    sourcemaps     = require('gulp-sourcemaps'),
    twig           = require('gulp-twig'),
    uglify         = require('gulp-uglify'),
    watch          = require('gulp-watch');

/**
 * Paths
 */
var paths = {
  dist: 'dist/',
  src: 'src/',
  deploy: 'dist/**/*',
  html: 'dist/*.html',
  browserSync: './dist',
  sass: {
    inputAll: 'src/sass/**/*.scss',
    input: 'src/sass/main.scss',
    output: 'dist/css',
    lint: 'src/sass/**/*.s+(a|c)ss'
  },
  jsVendor: {
    input: 'src/js/vendor/**/*.js',
    output: 'dist/js'
  },
  jsComponents: {
    input: 'src/js/components/**/*.js',
  },
  js: {
    input: 'src/js/main.js',
    output: 'dist/js'
  },
  img: {
    input: 'src/img/**/*',
    output: 'dist/img'
  },
  twig: {
    src: 'src/templates/*.{twig,html}',
    watch: 'src/templates/**/*.{twig,html}'
  },
  misc: {
    xml: 'src/*.xml',
    txt: 'src/*.txt'
  }
};

/**
 * Catch stream errors
 */
var onError = function (err) {
  notify.onError({
    title: "Gulp error in " + err.plugin,
    message: err.toString()
  })(err);
};

/**
 * Browser Sync
 */
gulp.task('browser-sync', function() {
  browserSync.init(null, {
    files: [paths.html],
    server: {
      baseDir: paths.browserSync
    },
    notify: false
  });
});

/**
 * Clean dist
 */
gulp.task('clean:dist', function() {
  return del.sync(paths.dist);
})

/**
 * CSS
 */
gulp.task('css', function (done) {
  return gulp.src(paths.sass.input)
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(sass({ outputStyle: 'compressed' }))
    .pipe(autoprefixer('last 2 versions'))
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.sass.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Sass Lint
 */
gulp.task('sass-lint', function () {
  return gulp.src(paths.sass.lint)
    .pipe(sassLint())
    .pipe(sassLint.format())
    .pipe(sassLint.failOnError())
});

/**
 * JS Vendor
 */
gulp.task('js:vendor', function (done) {
  return gulp.src(paths.jsVendor.input)
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(concat('vendor.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.jsVendor.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * JS Main
 */
gulp.task('js:main', function (done) {
  return gulp.src([
    paths.js.input,
    paths.jsComponents.input
  ])
    .pipe(plumber({ errorHandler: onError }))
    .pipe(sourcemaps.init())
    .pipe(concat('main.min.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('./', {addComment: false}))
    .pipe(gulp.dest(paths.js.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Images
 */
gulp.task('images', function (done) {
  return gulp.src([
    paths.img.input
  ], {
    'dot': true // include hidden files
  })
    .pipe(changed(paths.img.output))
    .pipe(gulp.dest(paths.img.output))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Twig
 */
gulp.task('twig', function (done) {
  return gulp.src(paths.twig.src)
    .pipe(plumber({
      errorHandler: function (error) {
        console.log(error.message);
        this.emit('end');
    }}))
    .pipe(foreach(function(stream,file){
      return stream
        .pipe(twig())
    }))
    .pipe(gulp.dest(paths.dist))
    done();
});

/**
 * Copy miscellaneous files
 */
gulp.task('copy:misc', function (done) {
  return gulp.src([
    paths.misc.xml,
    paths.misc.txt
  ])
    .pipe(changed(paths.dist))
    .pipe(gulp.dest(paths.dist))
    .pipe(browserSync.reload({stream:true}))
    done();
});

/**
 * Task: Gulp Watch Sequence
 */
gulp.task('watch-files', function () {
  watch(paths.img.input, function () {
    gulp.start('images');
  });
  watch(paths.sass.inputAll, function () {
    gulp.start('css');
  });
  watch(paths.js.input, function () {
    gulp.start('js:main');
  });
  watch(paths.jsComponents.input, function () {
    gulp.start('js:main');
  });
  watch(paths.jsVendor.input, function () {
    gulp.start('js:vendor');
  });
  watch(paths.twig.watch, function () {
    gulp.start('twig');
  });
  watch([paths.misc.xml, paths.misc.txt], function () {
    gulp.start('copy:misc');
  });
});

/**
 * Task: Gulp Default
 */
gulp.task('default', function(done) {
  runSequence('build', [
    'watch-files',
    'browser-sync'
  ], done )
});

/**
 * Task: Gulp Build
 */
gulp.task('build', function (done) {
  runSequence('clean:dist', [
    'css',
    'js:vendor',
    'js:main',
    'images',
    'twig',
    'copy:misc'
  ], done )
})

/**
 * Task: Gulp Watch
 */
gulp.task('watch', function(done) {
  runSequence('watch-files', [
    'browser-sync'
  ], done )
});

/**
 * Task: Gulp Deploy
 */
gulp.task('deploy', function() {
  return gulp.src(paths.deploy)
    .pipe(ghPages());
});
<|endoftext|>


<commit_before>/**
 * @author supereggbert / http://www.paulbrunt.co.uk/
 * @author philogb / http://blog.thejit.org/
 * @author mikael emtinger / http://gomo.se/
 * @author egraether / http://egraether.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Vector4 = function ( x, y, z, w ) {

	this.x = x || 0;
	this.y = y || 0;
	this.z = z || 0;
	this.w = ( w !== undefined ) ? w : 1;

};

THREE.Vector4.prototype = {

	constructor: THREE.Vector4,

	set: function ( x, y, z, w ) {

		this.x = x;
		this.y = y;
		this.z = z;
		this.w = w;

		return this;

	},

	setX: function ( x ) {

		this.x = x;

		return this;

	},

	setY: function ( y ) {

		this.y = y;

		return this;

	},

	setZ: function ( z ) {

		this.z = z;

		return this;

	},

	setW: function ( w ) {

		this.w = w;

		return this;

	},

    setComponent: function ( index, value ) {

        switch( index ) {

            case 0: this.x = value; break;
            case 1: this.y = value; break;
            case 2: this.z = value; break;
            case 3: this.w = value; break;
            default: throw new Error( "index is out of range: " + index );

        }

    },

    getComponent: function ( index ) {

        switch( index ) {

            case 0: return this.x;
            case 1: return this.y;
            case 2: return this.z;
            case 3: return this.w;
            default: throw new Error( "index is out of range: " + index );

        }

    },

	copy: function ( v ) {

		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		this.w = ( v.w !== undefined ) ? v.w : 1;

		return this;

	},

	addScalar: function ( s ) {

		this.x += s;
		this.y += s;
		this.z += s;
		this.w += s;

		return this;

	},

	add: function ( a, b ) {

		this.x = a.x + b.x;
		this.y = a.y + b.y;
		this.z = a.z + b.z;
		this.w = a.w + b.w;

		return this;

	},

	addSelf: function ( v ) {

		this.x += v.x;
		this.y += v.y;
		this.z += v.z;
		this.w += v.w;

		return this;

	},

	sub: function ( a, b ) {

		this.x = a.x - b.x;
		this.y = a.y - b.y;
		this.z = a.z - b.z;
		this.w = a.w - b.w;

		return this;

	},

	subSelf: function ( v ) {

		this.x -= v.x;
		this.y -= v.y;
		this.z -= v.z;
		this.w -= v.w;

		return this;

	},

	multiplyScalar: function ( s ) {

		this.x *= s;
		this.y *= s;
		this.z *= s;
		this.w *= s;

		return this;

	},

	divideScalar: function ( s ) {

		if ( s !== 0 ) {

			this.x /= s;
			this.y /= s;
			this.z /= s;
			this.w /= s;

		} else {

			this.x = 0;
			this.y = 0;
			this.z = 0;
			this.w = 1;

		}

		return this;

	},

	minSelf: function ( v ) {

		if ( this.x > v.x ) {

			this.x = v.x;

		}

		if ( this.y > v.y ) {

			this.y = v.y;

		}

		if ( this.z > v.z ) {

			this.z = v.z;

		}

		if ( this.w > v.w ) {

			this.w = v.w;

		}

		return this;

	},

	maxSelf: function ( v ) {

		if ( this.x < v.x ) {

			this.x = v.x;

		}

		if ( this.y < v.y ) {

			this.y = v.y;

		}

		if ( this.z < v.z ) {

			this.z = v.z;

		}

		if ( this.w < v.w ) {

			this.w = v.w;

		}

		return this;

	},

	clampSelf: function ( min, max ) {

		// This function assumes min < max, if this assumption isn't true it will not operate correctly

		if ( this.x < min.x ) {

			this.x = min.x;

		} else if ( this.x > max.x ) {

			this.x = max.x;

		}

		if ( this.y < min.y ) {

			this.y = min.y;

		} else if ( this.y > max.y ) {

			this.y = max.y;

		}

		if ( this.z < min.z ) {

			this.z = min.z;

		} else if ( this.z > max.z ) {

			this.z = max.z;

		}

		if ( this.w < min.w ) {

			this.w = min.w;

		} else if ( this.w > max.w ) {

			this.w = max.w;

		}

		return this;

	},

	negate: function() {

		return this.multiplyScalar( -1 );

	},

	dot: function ( v ) {

		return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;

	},

	lengthSq: function () {

		return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;

	},

	length: function () {

		return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );

	},

	lengthManhattan: function () {

		return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );

	},

	normalize: function () {

		return this.divideScalar( this.length() );

	},

	setLength: function ( l ) {

		var oldLength = this.length();

		if ( oldLength !== 0 && l !== oldLength  ) {

			this.multiplyScalar( l / oldLength );
		}

		return this;

	},

	lerpSelf: function ( v, alpha ) {

		this.x += ( v.x - this.x ) * alpha;
		this.y += ( v.y - this.y ) * alpha;
		this.z += ( v.z - this.z ) * alpha;
		this.w += ( v.w - this.w ) * alpha;

		return this;

	},

	equals: function ( v ) {

		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );

	},

	clone: function () {

		return new THREE.Vector4( this.x, this.y, this.z, this.w );

	},

	setAxisAngleFromQuaternion: function ( q ) {

		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm

		// q is assumed to be normalized

		this.w = 2 * Math.acos( q.w );

		var s = Math.sqrt( 1 - q.w * q.w );

		if ( s < 0.0001 ) {

			 this.x = 1;
			 this.y = 0;
			 this.z = 0;

		} else {

			 this.x = q.x / s;
			 this.y = q.y / s;
			 this.z = q.z / s;

		}

		return this;

	},

	setAxisAngleFromRotationMatrix: function ( m ) {

		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm

		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

		var angle, x, y, z,		// variables for result
			epsilon = 0.01,		// margin to allow for rounding errors
			epsilon2 = 0.1,		// margin to distinguish between 0 and 180 degrees

			te = m.elements,

			m11 = te[0], m12 = te[4], m13 = te[8],
			m21 = te[1], m22 = te[5], m23 = te[9],
			m31 = te[2], m32 = te[6], m33 = te[10];

		if ( ( Math.abs( m12 - m21 ) < epsilon )
		  && ( Math.abs( m13 - m31 ) < epsilon )
		  && ( Math.abs( m23 - m32 ) < epsilon ) ) {

			// singularity found
			// first check for identity matrix which must have +1 for all terms
			// in leading diagonal and zero in other terms

			if ( ( Math.abs( m12 + m21 ) < epsilon2 )
			  && ( Math.abs( m13 + m31 ) < epsilon2 )
			  && ( Math.abs( m23 + m32 ) < epsilon2 )
			  && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {

				// this singularity is identity matrix so angle = 0

				this.set( 1, 0, 0, 0 );

				return this; // zero angle, arbitrary axis

			}

			// otherwise this singularity is angle = 180

			angle = Math.PI;

			var xx = ( m11 + 1 ) / 2;
			var yy = ( m22 + 1 ) / 2;
			var zz = ( m33 + 1 ) / 2;
			var xy = ( m12 + m21 ) / 4;
			var xz = ( m13 + m31 ) / 4;
			var yz = ( m23 + m32 ) / 4;

			if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term

				if ( xx < epsilon ) {

					x = 0;
					y = 0.707106781;
					z = 0.707106781;

				} else {

					x = Math.sqrt( xx );
					y = xy / x;
					z = xz / x;

				}

			} else if ( yy > zz ) { // m22 is the largest diagonal term

				if ( yy < epsilon ) {

					x = 0.707106781;
					y = 0;
					z = 0.707106781;

				} else {

					y = Math.sqrt( yy );
					x = xy / y;
					z = yz / y;

				}

			} else { // m33 is the largest diagonal term so base result on this

				if ( zz < epsilon ) {

					x = 0.707106781;
					y = 0.707106781;
					z = 0;

				} else {

					z = Math.sqrt( zz );
					x = xz / z;
					y = yz / z;

				}

			}

			this.set( x, y, z, angle );

			return this; // return 180 deg rotation

		}

		// as we have reached here there are no singularities so we can handle normally

		var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
						 + ( m13 - m31 ) * ( m13 - m31 )
						 + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize

		if ( Math.abs( s ) < 0.001 ) s = 1;

		// prevent divide by zero, should not happen if matrix is orthogonal and should be
		// caught by singularity test above, but I've left it in just in case

		this.x = ( m32 - m23 ) / s;
		this.y = ( m13 - m31 ) / s;
		this.z = ( m21 - m12 ) / s;
		this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );

		return this;

	}

};<commit_msg>More space/tabs clean up.<commit_after>/**
 * @author supereggbert / http://www.paulbrunt.co.uk/
 * @author philogb / http://blog.thejit.org/
 * @author mikael emtinger / http://gomo.se/
 * @author egraether / http://egraether.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Vector4 = function ( x, y, z, w ) {

	this.x = x || 0;
	this.y = y || 0;
	this.z = z || 0;
	this.w = ( w !== undefined ) ? w : 1;

};

THREE.Vector4.prototype = {

	constructor: THREE.Vector4,

	set: function ( x, y, z, w ) {

		this.x = x;
		this.y = y;
		this.z = z;
		this.w = w;

		return this;

	},

	setX: function ( x ) {

		this.x = x;

		return this;

	},

	setY: function ( y ) {

		this.y = y;

		return this;

	},

	setZ: function ( z ) {

		this.z = z;

		return this;

	},

	setW: function ( w ) {

		this.w = w;

		return this;

	},

	setComponent: function ( index, value ) {

		switch ( index ) {

			case 0: this.x = value; break;
			case 1: this.y = value; break;
			case 2: this.z = value; break;
			case 3: this.w = value; break;
			default: throw new Error( "index is out of range: " + index );

		}

	},

	getComponent: function ( index ) {

		switch ( index ) {

			case 0: return this.x;
			case 1: return this.y;
			case 2: return this.z;
			case 3: return this.w;
			default: throw new Error( "index is out of range: " + index );

		}

	},

	copy: function ( v ) {

		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		this.w = ( v.w !== undefined ) ? v.w : 1;

		return this;

	},

	addScalar: function ( s ) {

		this.x += s;
		this.y += s;
		this.z += s;
		this.w += s;

		return this;

	},

	add: function ( a, b ) {

		this.x = a.x + b.x;
		this.y = a.y + b.y;
		this.z = a.z + b.z;
		this.w = a.w + b.w;

		return this;

	},

	addSelf: function ( v ) {

		this.x += v.x;
		this.y += v.y;
		this.z += v.z;
		this.w += v.w;

		return this;

	},

	sub: function ( a, b ) {

		this.x = a.x - b.x;
		this.y = a.y - b.y;
		this.z = a.z - b.z;
		this.w = a.w - b.w;

		return this;

	},

	subSelf: function ( v ) {

		this.x -= v.x;
		this.y -= v.y;
		this.z -= v.z;
		this.w -= v.w;

		return this;

	},

	multiplyScalar: function ( s ) {

		this.x *= s;
		this.y *= s;
		this.z *= s;
		this.w *= s;

		return this;

	},

	divideScalar: function ( s ) {

		if ( s !== 0 ) {

			this.x /= s;
			this.y /= s;
			this.z /= s;
			this.w /= s;

		} else {

			this.x = 0;
			this.y = 0;
			this.z = 0;
			this.w = 1;

		}

		return this;

	},

	minSelf: function ( v ) {

		if ( this.x > v.x ) {

			this.x = v.x;

		}

		if ( this.y > v.y ) {

			this.y = v.y;

		}

		if ( this.z > v.z ) {

			this.z = v.z;

		}

		if ( this.w > v.w ) {

			this.w = v.w;

		}

		return this;

	},

	maxSelf: function ( v ) {

		if ( this.x < v.x ) {

			this.x = v.x;

		}

		if ( this.y < v.y ) {

			this.y = v.y;

		}

		if ( this.z < v.z ) {

			this.z = v.z;

		}

		if ( this.w < v.w ) {

			this.w = v.w;

		}

		return this;

	},

	clampSelf: function ( min, max ) {

		// This function assumes min < max, if this assumption isn't true it will not operate correctly

		if ( this.x < min.x ) {

			this.x = min.x;

		} else if ( this.x > max.x ) {

			this.x = max.x;

		}

		if ( this.y < min.y ) {

			this.y = min.y;

		} else if ( this.y > max.y ) {

			this.y = max.y;

		}

		if ( this.z < min.z ) {

			this.z = min.z;

		} else if ( this.z > max.z ) {

			this.z = max.z;

		}

		if ( this.w < min.w ) {

			this.w = min.w;

		} else if ( this.w > max.w ) {

			this.w = max.w;

		}

		return this;

	},

	negate: function() {

		return this.multiplyScalar( -1 );

	},

	dot: function ( v ) {

		return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;

	},

	lengthSq: function () {

		return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;

	},

	length: function () {

		return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );

	},

	lengthManhattan: function () {

		return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );

	},

	normalize: function () {

		return this.divideScalar( this.length() );

	},

	setLength: function ( l ) {

		var oldLength = this.length();

		if ( oldLength !== 0 && l !== oldLength ) {

			this.multiplyScalar( l / oldLength );
		}

		return this;

	},

	lerpSelf: function ( v, alpha ) {

		this.x += ( v.x - this.x ) * alpha;
		this.y += ( v.y - this.y ) * alpha;
		this.z += ( v.z - this.z ) * alpha;
		this.w += ( v.w - this.w ) * alpha;

		return this;

	},

	equals: function ( v ) {

		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );

	},

	clone: function () {

		return new THREE.Vector4( this.x, this.y, this.z, this.w );

	},

	setAxisAngleFromQuaternion: function ( q ) {

		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm

		// q is assumed to be normalized

		this.w = 2 * Math.acos( q.w );

		var s = Math.sqrt( 1 - q.w * q.w );

		if ( s < 0.0001 ) {

			 this.x = 1;
			 this.y = 0;
			 this.z = 0;

		} else {

			 this.x = q.x / s;
			 this.y = q.y / s;
			 this.z = q.z / s;

		}

		return this;

	},

	setAxisAngleFromRotationMatrix: function ( m ) {

		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm

		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

		var angle, x, y, z,		// variables for result
			epsilon = 0.01,		// margin to allow for rounding errors
			epsilon2 = 0.1,		// margin to distinguish between 0 and 180 degrees

			te = m.elements,

			m11 = te[0], m12 = te[4], m13 = te[8],
			m21 = te[1], m22 = te[5], m23 = te[9],
			m31 = te[2], m32 = te[6], m33 = te[10];

		if ( ( Math.abs( m12 - m21 ) < epsilon )
		  && ( Math.abs( m13 - m31 ) < epsilon )
		  && ( Math.abs( m23 - m32 ) < epsilon ) ) {

			// singularity found
			// first check for identity matrix which must have +1 for all terms
			// in leading diagonal and zero in other terms

			if ( ( Math.abs( m12 + m21 ) < epsilon2 )
			  && ( Math.abs( m13 + m31 ) < epsilon2 )
			  && ( Math.abs( m23 + m32 ) < epsilon2 )
			  && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {

				// this singularity is identity matrix so angle = 0

				this.set( 1, 0, 0, 0 );

				return this; // zero angle, arbitrary axis

			}

			// otherwise this singularity is angle = 180

			angle = Math.PI;

			var xx = ( m11 + 1 ) / 2;
			var yy = ( m22 + 1 ) / 2;
			var zz = ( m33 + 1 ) / 2;
			var xy = ( m12 + m21 ) / 4;
			var xz = ( m13 + m31 ) / 4;
			var yz = ( m23 + m32 ) / 4;

			if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term

				if ( xx < epsilon ) {

					x = 0;
					y = 0.707106781;
					z = 0.707106781;

				} else {

					x = Math.sqrt( xx );
					y = xy / x;
					z = xz / x;

				}

			} else if ( yy > zz ) { // m22 is the largest diagonal term

				if ( yy < epsilon ) {

					x = 0.707106781;
					y = 0;
					z = 0.707106781;

				} else {

					y = Math.sqrt( yy );
					x = xy / y;
					z = yz / y;

				}

			} else { // m33 is the largest diagonal term so base result on this

				if ( zz < epsilon ) {

					x = 0.707106781;
					y = 0.707106781;
					z = 0;

				} else {

					z = Math.sqrt( zz );
					x = xz / z;
					y = yz / z;

				}

			}

			this.set( x, y, z, angle );

			return this; // return 180 deg rotation

		}

		// as we have reached here there are no singularities so we can handle normally

		var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
						 + ( m13 - m31 ) * ( m13 - m31 )
						 + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize

		if ( Math.abs( s ) < 0.001 ) s = 1;

		// prevent divide by zero, should not happen if matrix is orthogonal and should be
		// caught by singularity test above, but I've left it in just in case

		this.x = ( m32 - m23 ) / s;
		this.y = ( m13 - m31 ) / s;
		this.z = ( m21 - m12 ) / s;
		this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );

		return this;

	}

};
<|endoftext|>