<commit_before>/**
 * Copyright (C) 2016 panto.xyz
 * test.js
 *
 * changelog
 * 2016-06-21[19:03:41]:revised
 *
 * @author yanni4night@gmail.com
 * @version 0.1.0-alpha.1
 * @since 0.0.1
 */
'use strict';

const Panto = require('../src/panto');
const panto = require('../');
const fs = require('fs');
const assert = require('assert');
const Transformer = require('panto-transformer');
const isFunction = require('lodash/isFunction');

/*global describe,it*/
/*eslint no-console: ["error", { allow: ["error"] }] */
describe('panto', () => {
    describe('#constructor', () => {
        it('should define frozen "options"', () => {
            assert.ok('Stream' in panto, '"Stream" in panto');
            assert.ok('options' in panto, '"options" in panto');
            assert.ok(!!panto.options.get('output'), '"output" in panto.options');
            assert.deepEqual(panto.options.get('cwd'), process.cwd(), '"cwd" in panto.options');
            assert.ok('' === panto.options.get('binary_resource'),
                '"binary_resource" in panto.options');
            assert.throws(() => {
                panto.options = 1;
            }, 'set "panto.options"');
            assert.throws(() => {
                delete panto.options;
            }, 'delete "panto.options"');
        });
        it('should define frozen "file"', () => {
            assert.ok('file' in panto);
            assert.throws(() => {
                delete panto.file;
            }, '"panto.file" is frozen');
        });
        it('should define frozen "util"', () => {
            assert.ok('util' in panto, '"util" in panto');
            assert.ok('_' in panto, '"_" in panto');
            assert.throws(() => {
                delete panto._;
            }, '"panto._" is frozen');
        });
    });
    describe('#setOptions#getOption', () => {
        it('should set to the options', () => {
            const panto = new Panto();
            panto.setOptions({
                cwd: 'xyz'
            });
            assert.deepEqual(panto.getOption('cwd'), 'xyz', '"panto.options.cwd" equals "xyz"');
            assert.deepEqual(panto.getOption('noexist', 'default'), 'default', 'get default');
        });
    });
    describe('#getFiles', () => {
        it('should get the files', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.getFiles().then(filenames => {
                assert.ok(filenames.indexOf('javascripts/a.js') > -1,
                    'found "javascripts/a.js"');
                assert.ok(filenames.indexOf('javascripts/b.js') > -1,
                    'found "javascripts/b.js"');
                done();
            });
        });
        it('should throw if src and output are same', () => {
            const panto = new Panto();
            panto.setOptions({
                cwd: '.',
                src: 'foo',
                output: './foo'
            });
            assert.throws(() => {
                panto.getFiles();
            }, 'throws error');
        });
    });

    describe('#build', function () {
        this.timeout(5e3);
        it('should get all the files', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick(['**/*.js']).tag('js');
            panto.pick(['**/*.css']).tag('css');

            panto.build().then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/a.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'javascripts/b.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'stylesheets/a.css'),
                    'match "stylesheets/a.css"');
                assert.ok(files.some(file => file.filename === 'stylesheets/b.css'),
                    'match "stylesheets/b.css"');
            }).then(() => done());
        });
        it('should support equative src & output', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname,
                src: '/fixtures/',
                output: 'out'
            });

            const filename = `r/r-${Date.now()}.txt`;

            panto.file.write(filename, 'out').then(() => {
                assert.ok(fs.existsSync(__dirname + '/out/' + filename));
                return panto.file.rimraf('r', {
                    force: true
                });
            }).then(() => done()).catch(e => console.error(e));
        });
        it('should support serial src&output', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname,
                src: '/fixtures/',
                output: 'fixtures/out'
            });

            const filename = `r/r-${Date.now()}.txt`;

            panto.file.write(filename, 'out').then(() => {
                assert.ok(fs.existsSync(__dirname + '/fixtures/out/' + filename));
                return panto.file.rimraf('r', {
                    force: true
                });
            }).then(() => done()).catch(e => console.error(e));
        });
        it('should throw if src and output are same', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: '.',
                src: 'foo',
                output: './foo'
            });
            panto.build().catch(() => {
                done();
            });
        });
        it('should emit start event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('start', id => {
                assert.ok(id);
                done();
            });
            panto.build();
        });
        it('should emit complete event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('complete', (files, id) => {
                assert.ok(files);
                assert.ok(id);
                done();
            });
            panto.build();
        });
        it('should emit flowstart event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('flowstart', ({
                tag
            }, fid, bid) => {
                assert.ok(tag);
                assert.ok(fid);
                assert.ok(bid);
                done();
            });
            panto.build();
        });
        it('should emit flowend event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('flowend', ({
                tag
            }, fid, bid) => {
                assert.ok(tag);
                assert.ok(fid);
                assert.ok(bid);
                done();
            });
            panto.build();
        });
        it('should emit error event', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            class ErrorTransformer extends Transformer {
                _transform() {
                    return Promise.reject(new Error('normal error'));
                }
            }

            panto.pick('**/*.js').tag('js').pipe(new ErrorTransformer());

            panto.on('error', (err, id) => {
                assert.ok(err);
                assert.ok(id);
                done();
            });

            panto.build();
        });
        it('should operate only once when multiple reading on a file', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let readInvoked = 0;

            class ReadTransformer extends Transformer {
                _transform(file) {
                    const {
                        filename,
                        content
                    } = file;
                    // use cache if possible
                    if (!panto._.isNil(content)) {
                        return Promise.resolve(file);
                    } else {
                        readInvoked += 1;
                        return panto.file.read(filename).then(content => {
                            file.content = content;
                            return file;
                        });
                    }
                }
                isCacheable() {
                    return false;
                }
            }

            panto.loadTransformer('read', ReadTransformer);

            panto.$('**/a.js').read();
            panto.$('**/a.js').read();
            panto.build().then(() => {
                assert.deepEqual(readInvoked, 1, 'reading a.js only once');
            }).then(() => done());
        });
        it('should skip dormant streams', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let invoked = 0;

            class TestTransformer extends Transformer {
                transformAll(files) {
                    invoked += 1;
                    return super.transformAll(files);
                }
                isTorrential() {
                    return true;
                }
                isCacheable() {
                    return false;
                }
            }

            panto.$('**/a.js');
            panto.$('**/b.js', true).pipe(new TestTransformer());
            panto.build().then(() => {
                return panto.build();
            }).then(() => {
                assert.deepEqual(invoked, 1, 'skip dormant streams');
            }).then(() => done()).catch(e => console.error(e));
        });
    });

    describe('#clear', function () {
        this.timeout(2e3);
        it('should clear streams', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick('**/*.js').tag('js');

            panto.build().then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/a.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'javascripts/b.js'),
                    'match "javascripts/a.js"');
            }).then(() => {
                assert.deepEqual(panto.clear(), panto, 'clear() return self');
                return panto.build();
            }).then(files => {
                assert.deepEqual(files, [], 'no files due to no streams');
            }).then(() => done());
        });
    });

    describe('#rest', function () {
        this.timeout(3e3);
        it('should pick the rest', done => {
            const restFiles = [];

            class FinalTransformer extends Transformer {
                _transform(file) {
                    this.options.collection.push(file);
                    return super._transform(file);
                }
            }

            const panto = new Panto();
            const Stream = panto.Stream;

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.rest().tag('rest').connect(new Stream(new FinalTransformer({
                collection: restFiles
            })));

            panto.pick('**/*.js').tag('js');
            panto.pick('**/*.css').tag('css');

            panto.build().then(() => {
                assert.ok(restFiles.some(file => file.filename === 'README.md'),
                    '"README.md" rested');
            }).then(() => done()).catch(e => console.error(e));
        });
    });
    describe('#onFileDiff', function () {
        this.timeout(5e3);
        it('add and remove', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick('**/*.js').tag('js');
            panto.rest().tag('rest');

            panto.build().then(() => {
                return panto._onFileDiff({
                    filename: 'javascripts/c.js',
                    cmd: 'add'
                });
            }).then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/c.js'),
                    '"javascripts/c.js" added');
                return panto._onFileDiff({
                    filename: 'javascripts/c.js',
                    cmd: 'remove'
                });
            }).then(files => {
                assert.ok(!files.some(file => file.filename === 'javascripts/c.js'),
                    '"javascripts/c.js" removed');
            }).then(() => {
                return panto._onFileDiff({
                    filename: 'rest.txt',
                    cmd: 'add'
                });
            }).then(files => {
                assert.ok(files.some(file => file.filename === 'rest.txt'),
                    '"rest.txt" added');
                return panto._onFileDiff({
                    filename: 'rest.txt',
                    cmd: 'remove'
                });
            }).then(files => {
                assert.ok(!files.some(file => file.filename === 'rest.txt'),
                    '"rest.txt" remove');
            }).then(() => done()).catch(e => console.error(e));

        });
    });

    describe('#reportDependencies', function () {
        it('should transform dependencies too', done => {
            const panto = new Panto();
            const Stream = panto.Stream;

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let readInvoked = 0;

            class ReadTransformer extends Transformer {
                _transform(file) {
                    const {
                        filename,
                        content
                    } = file;
                    // use cache if possible
                    if (!panto._.isNil(content)) {
                        return Promise.resolve(file);
                    } else {
                        readInvoked += 1;
                        panto.log.debug(file);
                        return panto.file.read(filename).then(content => {
                            file.content = content;
                            return file;
                        });
                    }
                }
                isCacheable() {
                    return false;
                }
            }

            panto.loadTransformer('read', ReadTransformer);

            let jsInvoked = 0;

            class ModifyTransformer extends Transformer {
                _transform(file) {
                    if (file.filename !== 'javascripts/a.js') {
                        return super._transform(file);
                    }
                    return Promise.resolve(panto._.extend(file, {
                        content: Date.now()
                    }));
                }
                isCacheable() {
                    return false;
                }
            }

            class JsTransformer extends Transformer {
                _transform(file) {
                    jsInvoked += 1;
                    return super._transform(file);
                }
                isTorrential() {
                    return false;
                }
                isCacheable() {
                    return true;
                }
            }

            panto.pick('**/*.css').tag('css');
            panto.pick('**/*.js').tag('js').read().connect(new Stream(new ModifyTransformer()))
                .connect(new Stream(new JsTransformer()));

            panto.build().then(() => {
                panto.reportDependencies('javascripts/a.js', 'stylesheets/a.css');
                return panto._onFileDiff({
                    filename: 'stylesheets/a.css',
                    cmd: 'change'
                });
            }).then(() => {
                assert.deepEqual(readInvoked, 3, 'read three times');
                assert.deepEqual(jsInvoked, 3);
            }).then(() => done()).catch(e => console.error(e));
        });
    });
    describe('#loadTransformer', () => {
        it('should load the Transformer', () => {
            const panto = new Panto();
            const Stream = panto.Stream;
            class Foo {}
            panto.loadTransformer('foo', Foo);
            assert.ok(isFunction(new Stream().foo), '"new Stream().foo" is function');
            assert.ok(new Stream().foo() instanceof Stream,
                '"new Stream().foo()" is an instance of "Foo"');
        });
    });
});<commit_msg>Promise for mocha<commit_after>/**
 * Copyright (C) 2016 panto.xyz
 * test.js
 *
 * changelog
 * 2016-06-21[19:03:41]:revised
 *
 * @author yanni4night@gmail.com
 * @version 0.1.0-alpha.1
 * @since 0.0.1
 */
'use strict';

const Panto = require('../src/panto');
const panto = require('../');
const fs = require('fs');
const assert = require('assert');
const Transformer = require('panto-transformer');
const isFunction = require('lodash/isFunction');

/*global describe,it*/
/*eslint no-console: ["error", { allow: ["error"] }] */
describe('panto', () => {
    describe('#constructor', () => {
        it('should define frozen "options"', () => {
            assert.ok('Stream' in panto, '"Stream" in panto');
            assert.ok('options' in panto, '"options" in panto');
            assert.ok(!!panto.options.get('output'), '"output" in panto.options');
            assert.deepEqual(panto.options.get('cwd'), process.cwd(), '"cwd" in panto.options');
            assert.ok('' === panto.options.get('binary_resource'),
                '"binary_resource" in panto.options');
            assert.throws(() => {
                panto.options = 1;
            }, 'set "panto.options"');
            assert.throws(() => {
                delete panto.options;
            }, 'delete "panto.options"');
        });
        it('should define frozen "file"', () => {
            assert.ok('file' in panto);
            assert.throws(() => {
                delete panto.file;
            }, '"panto.file" is frozen');
        });
        it('should define frozen "util"', () => {
            assert.ok('util' in panto, '"util" in panto');
            assert.ok('_' in panto, '"_" in panto');
            assert.throws(() => {
                delete panto._;
            }, '"panto._" is frozen');
        });
    });
    describe('#setOptions#getOption', () => {
        it('should set to the options', () => {
            const panto = new Panto();
            panto.setOptions({
                cwd: 'xyz'
            });
            assert.deepEqual(panto.getOption('cwd'), 'xyz', '"panto.options.cwd" equals "xyz"');
            assert.deepEqual(panto.getOption('noexist', 'default'), 'default', 'get default');
        });
    });
    describe('#getFiles', () => {
        it('should get the files', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.getFiles().then(filenames => {
                assert.ok(filenames.indexOf('javascripts/a.js') > -1,
                    'found "javascripts/a.js"');
                assert.ok(filenames.indexOf('javascripts/b.js') > -1,
                    'found "javascripts/b.js"');
                done();
            });
        });
        it('should throw if src and output are same', () => {
            const panto = new Panto();
            panto.setOptions({
                cwd: '.',
                src: 'foo',
                output: './foo'
            });
            assert.throws(() => {
                panto.getFiles();
            }, 'throws error');
        });
    });

    describe('#build', function () {
        this.timeout(5e3);
        it('should get all the files', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick(['**/*.js']).tag('js');
            panto.pick(['**/*.css']).tag('css');

            return panto.build().then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/a.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'javascripts/b.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'stylesheets/a.css'),
                    'match "stylesheets/a.css"');
                assert.ok(files.some(file => file.filename === 'stylesheets/b.css'),
                    'match "stylesheets/b.css"');
            });
        });
        it('should support equative src & output', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname,
                src: '/fixtures/',
                output: 'out'
            });

            const filename = `r/r-${Date.now()}.txt`;

            return panto.file.write(filename, 'out').then(() => {
                assert.ok(fs.existsSync(__dirname + '/out/' + filename));
                return panto.file.rimraf('r', {
                    force: true
                });
            });
        });
        it('should support serial src&output', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname,
                src: '/fixtures/',
                output: 'fixtures/out'
            });

            const filename = `r/r-${Date.now()}.txt`;

            return panto.file.write(filename, 'out').then(() => {
                assert.ok(fs.existsSync(__dirname + '/fixtures/out/' + filename));
                return panto.file.rimraf('r', {
                    force: true
                });
            });
        });
        it('should throw if src and output are same', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: '.',
                src: 'foo',
                output: './foo'
            });
            panto.build().catch(() => {
                done();
            });
        });
        it('should emit start event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('start', id => {
                assert.ok(id);
                done();
            });
            panto.build();
        });
        it('should emit complete event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('complete', (files, id) => {
                assert.ok(files);
                assert.ok(id);
                done();
            });
            panto.build();
        });
        it('should emit flowstart event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('flowstart', ({
                tag
            }, fid, bid) => {
                assert.ok(tag);
                assert.ok(fid);
                assert.ok(bid);
                done();
            });
            panto.build();
        });
        it('should emit flowend event', done => {
            const panto = new Panto();
            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });
            panto.$('**/*').tag('ALL');
            panto.on('flowend', ({
                tag
            }, fid, bid) => {
                assert.ok(tag);
                assert.ok(fid);
                assert.ok(bid);
                done();
            });
            panto.build();
        });
        it('should emit error event', done => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            class ErrorTransformer extends Transformer {
                _transform() {
                    return Promise.reject(new Error('normal error'));
                }
            }

            panto.pick('**/*.js').tag('js').pipe(new ErrorTransformer());

            panto.on('error', (err, id) => {
                assert.ok(err);
                assert.ok(id);
                done();
            });

            panto.build();
        });
        it('should operate only once when multiple reading on a file', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let readInvoked = 0;

            class ReadTransformer extends Transformer {
                _transform(file) {
                    const {
                        filename,
                        content
                    } = file;
                    // use cache if possible
                    if (!panto._.isNil(content)) {
                        return Promise.resolve(file);
                    } else {
                        readInvoked += 1;
                        return panto.file.read(filename).then(content => {
                            file.content = content;
                            return file;
                        });
                    }
                }
                isCacheable() {
                    return false;
                }
            }

            panto.loadTransformer('read', ReadTransformer);

            panto.$('**/a.js').read();
            panto.$('**/a.js').read();
            return panto.build().then(() => {
                assert.deepEqual(readInvoked, 1, 'reading a.js only once');
            });
        });
        it('should skip dormant streams', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let invoked = 0;

            class TestTransformer extends Transformer {
                transformAll(files) {
                    invoked += 1;
                    return super.transformAll(files);
                }
                isTorrential() {
                    return true;
                }
                isCacheable() {
                    return false;
                }
            }

            panto.$('**/a.js');
            panto.$('**/b.js', true).pipe(new TestTransformer());
            return panto.build().then(() => {
                return panto.build();
            }).then(() => {
                assert.deepEqual(invoked, 1, 'skip dormant streams');
            });
        });
    });

    describe('#clear', function () {
        this.timeout(2e3);
        it('should clear streams', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick('**/*.js').tag('js');

            return panto.build().then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/a.js'),
                    'match "javascripts/a.js"');
                assert.ok(files.some(file => file.filename === 'javascripts/b.js'),
                    'match "javascripts/a.js"');
            }).then(() => {
                assert.deepEqual(panto.clear(), panto, 'clear() return self');
                return panto.build();
            }).then(files => {
                assert.deepEqual(files, [], 'no files due to no streams');
            });
        });
    });

    describe('#rest', function () {
        this.timeout(3e3);
        it('should pick the rest', () => {
            const restFiles = [];

            class FinalTransformer extends Transformer {
                _transform(file) {
                    this.options.collection.push(file);
                    return super._transform(file);
                }
            }

            const panto = new Panto();
            const Stream = panto.Stream;

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.rest().tag('rest').connect(new Stream(new FinalTransformer({
                collection: restFiles
            })));

            panto.pick('**/*.js').tag('js');
            panto.pick('**/*.css').tag('css');

            return panto.build().then(() => {
                assert.ok(restFiles.some(file => file.filename === 'README.md'),
                    '"README.md" rested');
            });
        });
    });
    describe('#onFileDiff', function () {
        this.timeout(5e3);
        it('add and remove', () => {
            const panto = new Panto();

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            panto.pick('**/*.js').tag('js');
            panto.rest().tag('rest');

            return panto.build().then(() => {
                return panto._onFileDiff({
                    filename: 'javascripts/c.js',
                    cmd: 'add'
                });
            }).then(files => {
                assert.ok(files.some(file => file.filename === 'javascripts/c.js'),
                    '"javascripts/c.js" added');
                return panto._onFileDiff({
                    filename: 'javascripts/c.js',
                    cmd: 'remove'
                });
            }).then(files => {
                assert.ok(!files.some(file => file.filename === 'javascripts/c.js'),
                    '"javascripts/c.js" removed');
            }).then(() => {
                return panto._onFileDiff({
                    filename: 'rest.txt',
                    cmd: 'add'
                });
            }).then(files => {
                assert.ok(files.some(file => file.filename === 'rest.txt'),
                    '"rest.txt" added');
                return panto._onFileDiff({
                    filename: 'rest.txt',
                    cmd: 'remove'
                });
            }).then(files => {
                assert.ok(!files.some(file => file.filename === 'rest.txt'),
                    '"rest.txt" remove');
            });

        });
    });

    describe('#reportDependencies', function () {
        it('should transform dependencies too', done => {
            const panto = new Panto();
            const Stream = panto.Stream;

            panto.setOptions({
                cwd: __dirname + '/fixtures/'
            });

            let readInvoked = 0;

            class ReadTransformer extends Transformer {
                _transform(file) {
                    const {
                        filename,
                        content
                    } = file;
                    // use cache if possible
                    if (!panto._.isNil(content)) {
                        return Promise.resolve(file);
                    } else {
                        readInvoked += 1;
                        panto.log.debug(file);
                        return panto.file.read(filename).then(content => {
                            file.content = content;
                            return file;
                        });
                    }
                }
                isCacheable() {
                    return false;
                }
            }

            panto.loadTransformer('read', ReadTransformer);

            let jsInvoked = 0;

            class ModifyTransformer extends Transformer {
                _transform(file) {
                    if (file.filename !== 'javascripts/a.js') {
                        return super._transform(file);
                    }
                    return Promise.resolve(panto._.extend(file, {
                        content: Date.now()
                    }));
                }
                isCacheable() {
                    return false;
                }
            }

            class JsTransformer extends Transformer {
                _transform(file) {
                    jsInvoked += 1;
                    return super._transform(file);
                }
                isTorrential() {
                    return false;
                }
                isCacheable() {
                    return true;
                }
            }

            panto.pick('**/*.css').tag('css');
            panto.pick('**/*.js').tag('js').read().connect(new Stream(new ModifyTransformer()))
                .connect(new Stream(new JsTransformer()));

            panto.build().then(() => {
                panto.reportDependencies('javascripts/a.js', 'stylesheets/a.css');
                return panto._onFileDiff({
                    filename: 'stylesheets/a.css',
                    cmd: 'change'
                });
            }).then(() => {
                assert.deepEqual(readInvoked, 3, 'read three times');
                assert.deepEqual(jsInvoked, 3);
            }).then(() => done()).catch(e => console.error(e));
        });
    });
    describe('#loadTransformer', () => {
        it('should load the Transformer', () => {
            const panto = new Panto();
            const Stream = panto.Stream;
            class Foo {}
            panto.loadTransformer('foo', Foo);
            assert.ok(isFunction(new Stream().foo), '"new Stream().foo" is function');
            assert.ok(new Stream().foo() instanceof Stream,
                '"new Stream().foo()" is an instance of "Foo"');
        });
    });
});<|endoftext|>


<commit_before><commit_msg>added doc description for tts() param onReadyCallback<commit_after><|endoftext|>


<commit_before>/* Constant Strings */
var AJAX_LOADER_BIG = '';
var AJAX_LOADER_SMALL = '';

/* QUALITY CONSTANTS */
const BAD_QUALITY = 3;
const OK_QUALITY = 2;
const GOOD_QUALITY = 1;

/* CONSTANTS FOR BUTTON TITLES */
const CLEAN_GEN   = "Generate Cleaned Tier";
const CLEAN_REGEN = "Regenerate Cleaned Tier";

const NORM_GEN    = "Generate Normalized Tier";
const NORM_REGEN  = "Regenerate Normalized Tier";

/* CLASS CONSTANTS */
const RATING_DISABLED = "rating-disabled";
const RATING_ENABLED  = "rating-enabled";

const FEEDBACK_WARN   = "feedback-warn";
const FEEDBACK_OK     = "feedback-ok";

const ICON_CLICKED    = "iconclicked";

const CURRENT_ROW     = "current-row";
// -------------------------------------------

/* Login the User */
function login() {
    var userid = $('#userid').val();
    console.log(userid);
    window.location.href = '/user/'+userid;
}

/* Populate the IGT pane */
function populateIGTs(corpId, async) {

    if (typeof async === 'undefined') {async = true;}

    $('#fine-list').html('
'+AJAX_LOADER_SMALL+'
');
    $.ajax({
        url:'/populate/'+corpId,
        error: populateError,
        success: populateSuccess,
        async: async
    })
}

function populateSuccess(r, stat, jqXHR) {
    $('#fine-list').html(r);
    nexts = {};
    $('.igtrow').each(function(i, elt) {
        igtid = $(elt).attr('igtid');
        nexts[igtid] = $(elt).attr('next');
    });
    localStorage.setItem('nexts', JSON.stringify(nexts));
}

function populateError() {
    $('#fine-list').text("An error occurred.");
}

/* display a single IGT instance */
function displayIGT(corp_id, igt_id) {
    $('#editor-panel').html(AJAX_LOADER_BIG);
    url = '/display/'+corp_id+'/'+igt_id
    $.ajax({
        url: url,
        success: displaySuccess,
        error: displayError,
        contentType : "json"
    });
}

function displaySuccess(r, stat, jqXHR) {
    // Get the data out of the response.
    data = JSON.parse(r);
    content = data['content'];
    $('#editor-panel').html(content);

    // createCombos
    createCombos();

    // Assign the tooltips to the interface elements.
    assign_tooltips();

    // Stash the current versions of the clean lines for undo-ing.
    stashCleanLines();
    $('.igtrow').removeClass(CURRENT_ROW);
    $('#igtrow-'+igtId()).addClass(CURRENT_ROW);
}

/* COMBO FUNCTIONS */
function createCombos() {
    $('.taglabel-combo').each(function(i, comboElt) {
        if (!$(comboElt).hasClass('combo-f')) {
            var labels = $(comboElt).next('.taglabels');
            $(comboElt).combo({
                multiple: true,
                editable: false,
                checkbox: true
            });
            var panel = $(comboElt).combo('panel');
            labels.appendTo(panel);

            labels.find('input').each(function (i, elt) {

                $(elt).click(function () {
                    updateCombo(comboElt, labels);
                });
            });

            // Begin an array of the marked items
            updateCombo(comboElt, labels);
        }
    });
}

function updateCombo(comboElt, labels) {
    var labelArr = [];
    $(labels).find('input').each(function(i, labelElt) {
        var v = $(labelElt).val();
        if ($(labelElt).is(':checked')) {
            labelArr.push(v);
        }
    });
    $(comboElt).combo('setText', labelArr.join('+'));
}

function displayError(r, stat, jqXHR) {
    $('#editor-panel').text("An error occurred.");
}

function get_raw_lines() {
    lines = [];
    $('.raw-tier .textrow').each(function(i, el){
        linedata = {};
        linedata['tag'] = $(el).find('.tags').text();
        linedata['text'] = $(el).find('.text').text();
        lines.push(linedata);
    });
    return lines;
}

/* Retrieve Clean Tiers */
function get_tier_lines(rowSelector) {

    var lines = [];

    /* Iterate over all the clean items, and add them to the data */
    $(rowSelector).each(function(i, el) {

        linedata = {};

        linedata['tag'] = $(el).find('.tags option:selected').val();

        li = $(el).find('.line-input');
        linedata['text'] = li.val();
        linedata['id'] = li.attr('id');
        linedata['num'] = i+1;

        /* Skip the line if it's disabled */
        if (!li.attr('disabled')) {
            lines.push(linedata);
        }

    });

    return lines;
}

function get_clean_lines() {
    return get_tier_lines('.cleanrow');
}

function get_normal_lines() {
    return get_tier_lines('.normalrow');
}

/* Cleaning */
function cleanIGT(corp_id, igt_id, alreadyGenerated) {

    cleanExists = $('#clean-contents').html().trim();

    var regenerate = true;
    if (cleanExists) {
        regenerate = confirm("This will replace the data on the clean tier. Is this ok?");
    }

    if (regenerate) {
        $('#clean-contents').html('Loading...');
        $('#generate-normalized').val(NORM_GEN);
        $('#normalized-tier').hide();
        $('#normalized-contents').html('');
        disableYellowGreen();

        $.ajax({
            url: '/clean/' + corp_id + '/' + igt_id,
            type: 'GET',
            dataType: 'html',
            success: cleanSuccess,
            error: cleanError
        });
    }
}

function cleanSuccess(r, stat, jqXHR) {

    $('#clean-contents').html(r);
    $('#clean-tier').show();

    $('#generate-clean').val(CLEAN_REGEN);

    createCombos();
    assign_tooltips();
    stashCleanLines();
}

function cleanError(r) {
    $('#clean-tier').show();
    $('#clean-contents').text("An error occurred.");
    console.error(r);
}

/* Normalization */
function normalizeIGT(corp_id, igt_id, alreadyGenerated) {

    normExists = $('#normalized-contents').html().trim();

    var regenerate = true;

    if (normExists) {
        regenerate = confirm("This will replace the existing normalized tier. Are you sure? ");
    }

    if (regenerate) {

        $('#normalized-contents').html('Loading...');

        cleanData = {lines: get_clean_lines()};

        $.ajax({
            url: '/normalize/' + corp_id + '/' + igt_id,
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json',
            data: JSON.stringify(cleanData),
            success: normalizeSuccess,
            error: normalizeError
        });
    }
}

function disableYellowGreen() {
    rg = $('#rating-green');
    ry = $('#rating-yellow');

    rg.removeClass(RATING_ENABLED);
    ry.removeClass(RATING_ENABLED);
    rg.addClass(RATING_DISABLED);
    ry.addClass(RATING_DISABLED);

    rg.attr('onclick', '');
    ry.attr('onclick', '');
}

function enableYellowGreen() {
    $('.rating-button').removeClass(RATING_DISABLED);
    $('.rating-button').addClass(RATING_ENABLED);
    $('#rating-green').click(function() {saveIGT(GOOD_QUALITY)});
    $('#rating-yellow').click(function() {saveIGT(OK_QUALITY)});
}

function normalizeSuccess(r, stat, jqXHR) {

    $('#normalized-tier').show();
    $('#generate-normalized').val(NORM_REGEN);
    $('#normalized-contents').html(r['content']);

    assign_tooltips();
    stashNormLines();
    createCombos();

    // Once the normalized lines are shown, it's okay for
    // the user to use the green/yellow buttons.
    enableYellowGreen();
}

function normalizeError(r) {
    $('#normalized-contents').text("An error occured while normalizing.");
    console.error(r);
}

/* INTENT-ification */
function generateFromNormalized(corp_id, igt_id) {
    normalData = get_normal_lines();
    cleanData  = get_clean_lines();
    rawData    = get_raw_lines();

    data = {raw: rawData,
            clean: cleanData,
            normal: normalData};

    analyzeUnmark('glm');
    analyzeUnmark('glw');
    analyzeUnmark('tag');
    analyzeUnmark('col');

    $.ajax({
        url: '/intentify/'+corp_id+'/'+igt_id,
        dataType: 'json',
        contentType: 'application/json',
        type: 'POST',
        data: JSON.stringify(data),
        success: intentifySuccess,
        error: intentifyError
    });
}

function analyzeUnmark(id) {
    $('#'+id).removeClass(FEEDBACK_WARN);
    $('#'+id).removeClass(FEEDBACK_OK);
}

function analyzeWarn(id) {
    $('#'+id).addClass(FEEDBACK_WARN);
}

function analyzeOK(id) {
    $('#'+id).addClass(FEEDBACK_OK);
}

function analysisNotifier(r, id) {
    if (r[id] == 0) {
        analyzeWarn(id);
    } else {
        analyzeOK(id);
    }
}

function intentifySuccess(r, stat, jqXHR) {
    analysisNotifier(r, 'glw');
    analysisNotifier(r, 'glm');
    analysisNotifier(r, 'tag');
    analysisNotifier(r, 'col');
}

function intentifyError() {
    $('#remaining-content').text("An error occurred producing the remaining tiers.")
}

/* Edit/Delete Scripts */
function deleteItem(elt, itemId) {

    var identifier = '#'+itemId;

    /* If this button has been clicked already,
     * restore (as a toggle) */
    if ($(elt).hasClass(ICON_CLICKED)) {
        $(elt).removeClass(ICON_CLICKED);

        // Unmark the item for deletion.
        $(identifier).find('input').prop('disabled',false);
        $(identifier).removeClass('for-deletion');

    } else {

    /* Otherwise, set it clicked, and delete it. */
        $(elt).addClass(ICON_CLICKED);

        // If neither of the boxes are filled out, delete the
        // element.
        if ($(identifier).find('.line-input').val().trim() == '') {
            $(identifier).remove();

        // Otherwise, simply mark it for deletion.
        } else {
            $(identifier).find('input').prop('disabled', true);
            $(identifier).addClass('for-deletion');
        }
    }
}

function restoreItem(itemId) {
    var identifier = '#'+itemId;

    /* Set the item to its initial value. */
    $(identifier).find('.line-input').val(localStorage.getItem(itemId));
}

function addItem(prefix, jqAfter, rowtype) {
    numitems = $('.'+rowtype).length;

    id = prefix+(numitems+1).toString();

    var lineHTML = $($('#row_template').html());
    lineHTML.attr('id', id);    // set the id to the new id
    lineHTML.addClass(rowtype); // Add the row class

    /* Change the functions */
    lineHTML.find('.undo').attr("onclick", "restoreItem('"+id+'")');
    lineHTML.find('.delete').attr("onclick", "deleteItem(this, '"+id+"')");
    lineHTML.find('.add').attr("onclick", "addItem('"+prefix+"',$(this).closest('tr'),'"+rowtype+"')");

    jqAfter.after(lineHTML.get(0));
}

/* Retrieve the IGT id and Corp ID */
function igtId() {
    return $('#igt-instance').attr('igtid');
}

function corpId() {
    return $('#igt-instance').attr('corpid');
}

/* Save the edited tier! */
function saveIGT(rating) {

    var data = {rating: rating,
                norm : get_normal_lines(),
                clean: get_clean_lines(),
                raw:   get_raw_lines()
    };

    console.log(igtId() + ' ' + corpId());
    $.ajax({
        url: '/save/'+corpId()+'/'+igtId(),
        type: 'PUT',
        data: JSON.stringify(data),
        success: saveSuccess,
        contentType: 'application/json',
        error: saveError
    });
}

function saveSuccess(r, stat, jqXHR) {
    populateIGTs(corpId(), false);
    nexts = JSON.parse(localStorage.getItem('nexts'));
    nextId = nexts[igtId()];

    // scroll the igt list to the appropriate position
    var scrollPos = $('#igtrow-'+igtId()).position().top + $('#fine-list').scrollTop()-52;
    $('#fine-list').animate({scrollTop : scrollPos}, 0);

    // Now display the current IGT.
    displayIGT(corpId(), nextId);
}

function saveError(r, stat, jqXHR) {
    console.error("An error occurred saving the instance.");
    console.error(jqXHR.toString());
}<commit_msg>Fix the newly created lines not getting the combo dropdowns correctly created.<commit_after>/* Constant Strings */
var AJAX_LOADER_BIG = '';
var AJAX_LOADER_SMALL = '';

/* QUALITY CONSTANTS */
const BAD_QUALITY = 3;
const OK_QUALITY = 2;
const GOOD_QUALITY = 1;

/* CONSTANTS FOR BUTTON TITLES */
const CLEAN_GEN   = "Generate Cleaned Tier";
const CLEAN_REGEN = "Regenerate Cleaned Tier";

const NORM_GEN    = "Generate Normalized Tier";
const NORM_REGEN  = "Regenerate Normalized Tier";

/* CLASS CONSTANTS */
const RATING_DISABLED = "rating-disabled";
const RATING_ENABLED  = "rating-enabled";

const FEEDBACK_WARN   = "feedback-warn";
const FEEDBACK_OK     = "feedback-ok";

const ICON_CLICKED    = "iconclicked";

const CURRENT_ROW     = "current-row";
// -------------------------------------------

/* Login the User */
function login() {
    var userid = $('#userid').val();
    console.log(userid);
    window.location.href = '/user/'+userid;
}

/* Populate the IGT pane */
function populateIGTs(corpId, async) {

    if (typeof async === 'undefined') {async = true;}

    $('#fine-list').html('
'+AJAX_LOADER_SMALL+'
');
    $.ajax({
        url:'/populate/'+corpId,
        error: populateError,
        success: populateSuccess,
        async: async
    })
}

function populateSuccess(r, stat, jqXHR) {
    $('#fine-list').html(r);
    nexts = {};
    $('.igtrow').each(function(i, elt) {
        igtid = $(elt).attr('igtid');
        nexts[igtid] = $(elt).attr('next');
    });
    localStorage.setItem('nexts', JSON.stringify(nexts));
}

function populateError() {
    $('#fine-list').text("An error occurred.");
}

/* display a single IGT instance */
function displayIGT(corp_id, igt_id) {
    $('#editor-panel').html(AJAX_LOADER_BIG);
    url = '/display/'+corp_id+'/'+igt_id
    $.ajax({
        url: url,
        success: displaySuccess,
        error: displayError,
        contentType : "json"
    });
}

function displaySuccess(r, stat, jqXHR) {
    // Get the data out of the response.
    data = JSON.parse(r);
    content = data['content'];
    $('#editor-panel').html(content);

    // createCombos
    createCombos();

    // Assign the tooltips to the interface elements.
    assign_tooltips();

    // Stash the current versions of the clean lines for undo-ing.
    stashCleanLines();
    $('.igtrow').removeClass(CURRENT_ROW);
    $('#igtrow-'+igtId()).addClass(CURRENT_ROW);
}

/* COMBO FUNCTIONS */
function createCombos() {
    $('.taglabel-combo').each(function(i, comboElt) {
        if (!$(comboElt).hasClass('combo-f')) {
            var labels = $(comboElt).next('.taglabels');
            $(comboElt).combo({
                multiple: true,
                editable: false,
                checkbox: true
            });
            var panel = $(comboElt).combo('panel');
            labels.appendTo(panel);

            labels.find('input').each(function (i, elt) {

                $(elt).click(function () {
                    updateCombo(comboElt, labels);
                });
            });

            // Begin an array of the marked items
            updateCombo(comboElt, labels);
        }
    });
}

function updateCombo(comboElt, labels) {
    var labelArr = [];
    $(labels).find('input').each(function(i, labelElt) {
        var v = $(labelElt).val();
        if ($(labelElt).is(':checked')) {
            labelArr.push(v);
        }
    });
    $(comboElt).combo('setText', labelArr.join('+'));
}

function displayError(r, stat, jqXHR) {
    $('#editor-panel').text("An error occurred.");
}

function get_raw_lines() {
    lines = [];
    $('.raw-tier .textrow').each(function(i, el){
        linedata = {};
        linedata['tag'] = $(el).find('.tags').text();
        linedata['text'] = $(el).find('.text').text();
        lines.push(linedata);
    });
    return lines;
}

/* Retrieve Clean Tiers */
function get_tier_lines(rowSelector) {

    var lines = [];

    /* Iterate over all the clean items, and add them to the data */
    $(rowSelector).each(function(i, el) {

        linedata = {};

        linedata['tag'] = $(el).find('.tags option:selected').val();

        li = $(el).find('.line-input');
        linedata['text'] = li.val();
        linedata['id'] = li.attr('id');
        linedata['num'] = i+1;

        /* Skip the line if it's disabled */
        if (!li.attr('disabled')) {
            lines.push(linedata);
        }

    });

    return lines;
}

function get_clean_lines() {
    return get_tier_lines('.cleanrow');
}

function get_normal_lines() {
    return get_tier_lines('.normalrow');
}

/* Cleaning */
function cleanIGT(corp_id, igt_id, alreadyGenerated) {

    cleanExists = $('#clean-contents').html().trim();

    var regenerate = true;
    if (cleanExists) {
        regenerate = confirm("This will replace the data on the clean tier. Is this ok?");
    }

    if (regenerate) {
        $('#clean-contents').html('Loading...');
        $('#generate-normalized').val(NORM_GEN);
        $('#normalized-tier').hide();
        $('#normalized-contents').html('');
        disableYellowGreen();

        $.ajax({
            url: '/clean/' + corp_id + '/' + igt_id,
            type: 'GET',
            dataType: 'html',
            success: cleanSuccess,
            error: cleanError
        });
    }
}

function cleanSuccess(r, stat, jqXHR) {

    $('#clean-contents').html(r);
    $('#clean-tier').show();

    $('#generate-clean').val(CLEAN_REGEN);

    createCombos();
    assign_tooltips();
    stashCleanLines();
}

function cleanError(r) {
    $('#clean-tier').show();
    $('#clean-contents').text("An error occurred.");
    console.error(r);
}

/* Normalization */
function normalizeIGT(corp_id, igt_id, alreadyGenerated) {

    normExists = $('#normalized-contents').html().trim();

    var regenerate = true;

    if (normExists) {
        regenerate = confirm("This will replace the existing normalized tier. Are you sure? ");
    }

    if (regenerate) {

        $('#normalized-contents').html('Loading...');

        cleanData = {lines: get_clean_lines()};

        $.ajax({
            url: '/normalize/' + corp_id + '/' + igt_id,
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json',
            data: JSON.stringify(cleanData),
            success: normalizeSuccess,
            error: normalizeError
        });
    }
}

function disableYellowGreen() {
    rg = $('#rating-green');
    ry = $('#rating-yellow');

    rg.removeClass(RATING_ENABLED);
    ry.removeClass(RATING_ENABLED);
    rg.addClass(RATING_DISABLED);
    ry.addClass(RATING_DISABLED);

    rg.attr('onclick', '');
    ry.attr('onclick', '');
}

function enableYellowGreen() {
    $('.rating-button').removeClass(RATING_DISABLED);
    $('.rating-button').addClass(RATING_ENABLED);
    $('#rating-green').click(function() {saveIGT(GOOD_QUALITY)});
    $('#rating-yellow').click(function() {saveIGT(OK_QUALITY)});
}

function normalizeSuccess(r, stat, jqXHR) {

    $('#normalized-tier').show();
    $('#generate-normalized').val(NORM_REGEN);
    $('#normalized-contents').html(r['content']);

    assign_tooltips();
    stashNormLines();
    createCombos();

    // Once the normalized lines are shown, it's okay for
    // the user to use the green/yellow buttons.
    enableYellowGreen();
}

function normalizeError(r) {
    $('#normalized-contents').text("An error occured while normalizing.");
    console.error(r);
}

/* INTENT-ification */
function generateFromNormalized(corp_id, igt_id) {
    normalData = get_normal_lines();
    cleanData  = get_clean_lines();
    rawData    = get_raw_lines();

    data = {raw: rawData,
            clean: cleanData,
            normal: normalData};

    analyzeUnmark('glm');
    analyzeUnmark('glw');
    analyzeUnmark('tag');
    analyzeUnmark('col');

    $.ajax({
        url: '/intentify/'+corp_id+'/'+igt_id,
        dataType: 'json',
        contentType: 'application/json',
        type: 'POST',
        data: JSON.stringify(data),
        success: intentifySuccess,
        error: intentifyError
    });
}

function analyzeUnmark(id) {
    $('#'+id).removeClass(FEEDBACK_WARN);
    $('#'+id).removeClass(FEEDBACK_OK);
}

function analyzeWarn(id) {
    $('#'+id).addClass(FEEDBACK_WARN);
}

function analyzeOK(id) {
    $('#'+id).addClass(FEEDBACK_OK);
}

function analysisNotifier(r, id) {
    if (r[id] == 0) {
        analyzeWarn(id);
    } else {
        analyzeOK(id);
    }
}

function intentifySuccess(r, stat, jqXHR) {
    analysisNotifier(r, 'glw');
    analysisNotifier(r, 'glm');
    analysisNotifier(r, 'tag');
    analysisNotifier(r, 'col');
}

function intentifyError() {
    $('#remaining-content').text("An error occurred producing the remaining tiers.")
}

/* Edit/Delete Scripts */
function deleteItem(elt, itemId) {

    var identifier = '#'+itemId;

    /* If this button has been clicked already,
     * restore (as a toggle) */
    if ($(elt).hasClass(ICON_CLICKED)) {
        $(elt).removeClass(ICON_CLICKED);

        // Unmark the item for deletion.
        $(identifier).find('input').prop('disabled',false);
        $(identifier).removeClass('for-deletion');

    } else {

    /* Otherwise, set it clicked, and delete it. */
        $(elt).addClass(ICON_CLICKED);

        // If neither of the boxes are filled out, delete the
        // element.
        if ($(identifier).find('.line-input').val().trim() == '') {
            $(identifier).remove();

        // Otherwise, simply mark it for deletion.
        } else {
            $(identifier).find('input').prop('disabled', true);
            $(identifier).addClass('for-deletion');
        }
    }
}

function restoreItem(itemId) {
    var identifier = '#'+itemId;

    /* Set the item to its initial value. */
    $(identifier).find('.line-input').val(localStorage.getItem(itemId));
}

function addItem(prefix, jqAfter, rowtype) {
    numitems = $('.'+rowtype).length;

    id = prefix+(numitems+1).toString();

    var lineHTML = $($('#row_template').html());
    lineHTML.attr('id', id);    // set the id to the new id
    lineHTML.addClass(rowtype); // Add the row class

    /* Change the functions */
    lineHTML.find('.undo').attr("onclick", "restoreItem('"+id+'")');
    lineHTML.find('.delete').attr("onclick", "deleteItem(this, '"+id+"')");
    lineHTML.find('.add').attr("onclick", "addItem('"+prefix+"',$(this).closest('tr'),'"+rowtype+"')");

    jqAfter.after(lineHTML.get(0));
    createCombos();
}

/* Retrieve the IGT id and Corp ID */
function igtId() {
    return $('#igt-instance').attr('igtid');
}

function corpId() {
    return $('#igt-instance').attr('corpid');
}

/* Save the edited tier! */
function saveIGT(rating) {

    var data = {rating: rating,
                norm : get_normal_lines(),
                clean: get_clean_lines(),
                raw:   get_raw_lines()
    };

    console.log(igtId() + ' ' + corpId());
    $.ajax({
        url: '/save/'+corpId()+'/'+igtId(),
        type: 'PUT',
        data: JSON.stringify(data),
        success: saveSuccess,
        contentType: 'application/json',
        error: saveError
    });
}

function saveSuccess(r, stat, jqXHR) {
    populateIGTs(corpId(), false);
    nexts = JSON.parse(localStorage.getItem('nexts'));
    nextId = nexts[igtId()];

    // scroll the igt list to the appropriate position
    var scrollPos = $('#igtrow-'+igtId()).position().top + $('#fine-list').scrollTop()-52;
    $('#fine-list').animate({scrollTop : scrollPos}, 0);

    // Now display the current IGT.
    displayIGT(corpId(), nextId);
}

function saveError(r, stat, jqXHR) {
    console.error("An error occurred saving the instance.");
    console.error(jqXHR.toString());
}<|endoftext|>


<commit_before>#!/usr/bin/env node

const Ajv = require('ajv');
const fs = require("fs");
const path = require("path");
const shell = require("shelljs");
const Main = require("../output/Main");
const Samples = require("../output/Samples");

// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
function deepEquals(x, y) {
    var i;
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
        return true;
    }

    // Compare primitives and functions.
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
        return true;
    }

    if ((x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number)) {
        if (x.toString() !== y.toString()) {
            console.log("Number or string not equal.");
            return false;
        }
        return true;
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
        console.log("One is not an object.")
        return false;
    }

    if (x.constructor !== y.constructor) {
        console.log("Not the same constructor.");
        return false;
    }

    if (x.prototype !== y.prototype) {
        console.log("Not the same prototype.");
        return false;
    }

    if (Array.isArray(x)) {
        if (x.length !== y.length){
            console.log("Arrays don't have the same length.");
            return false;
        }
        for (i = 0; i < x.length; i++) {
            if (!deepEquals(x[i], y[i]))
                return false;
        }
        return true;
    }

    for (p in y) {
        // We allow properties in y that aren't present in x
        // so long as they're null.
        if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
            if (y[p] !== null) {
                console.log(`Non-null property ${p} is not expected.`);
                return false;
            }
            continue;
        }
        if (typeof y[p] !== typeof x[p]) {
            console.log(`Properties ${p} don't have the same types.`);
            return false;
        }
    }

    for (p in x) {
        if (x.hasOwnProperty(p) && !y.hasOwnProperty(p)) {
            console.log(`Expected property ${p} not found.`);
            return false;
        }
        if (typeof x[p] !== typeof y[p]) {
            console.log(`Properties ${p} don't have the same types.`);
            return false;
        }

        switch (typeof(x[p])) {
        case 'object':
            if (!deepEquals(x[p], y[p])) {
                return false;
            }
            break;

        default:
            if (x[p] !== y[p]) {
                console.log(`Non-object properties ${p} are not equal.`)
                return false;
            }
            break;
        }
    }

    return true;
}

function exec(s, opts, cb) {
    let result = shell.exec(s, opts, cb);
    if (result.code !== 0) {
        shell.exit(result.code);
    }
    return result;
}

function execAndCompare(cmd, p, knownFails) {
    let outputString = exec(cmd, {silent:true}).stdout;
    if (knownFails.indexOf(path.basename(p)) < 0) {
        let outputJSON = JSON.parse(outputString);
        let inputJSON = JSON.parse(fs.readFileSync(p));
        if (!deepEquals(inputJSON, outputJSON)) {
            console.log("Error: Output is not equivalent to input.");
            process.exit(1);
        }
    } else {
        console.log("Known to fail - not checking output.");
    }
}

function absolutize(p) {
    if (path.isAbsolute(p))
        return p;
    return path.join(process.cwd(), p);
}

function runTests(description, samples, dir, prepareCmd, filename, testFn) {
    shell.cd(dir);
    if (prepareCmd)
        shell.exec(prepareCmd, { silent: true });

    samples.forEach((sample) => {
        console.error(`* Building ${description} for ${sample}`);
        exec(`node ../../cli/quicktype.js -o "${filename}" "${sample}"`);
        testFn(sample);
    });

    shell.cd("../..");
}

function testCSharp(samples, knownFails) {
    runTests("C# code", samples, "test/csharp", "dotnet restore", "QuickType.cs",
        function (p) {
            execAndCompare(`dotnet run "${p}"`, p, knownFails);
        }
    );
}

function testGolang(samples, knownFails) {
    runTests("Go code", samples, "test/golang", null, "quicktype.go",
        function (p) {
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownFails);
        }
    );
}

function testJsonSchema(samples, knownFails) {
    runTests("JSON Schema", samples, "test/golang", null, "schema.json",
        function (p) {
            let input = JSON.parse(fs.readFileSync(p));
            let schema = JSON.parse(fs.readFileSync("schema.json"));
            let ajv = new Ajv();
            let valid = ajv.validate(schema, input);
            if (!valid) {
                console.log("Error: Generated schema does not validate input JSON.");
                process.exit(1);
            }
        }
    );
}

function testAll(samples, goFails, csFails, jsonSchemaFails) {
    testJsonSchema(samples, jsonSchemaFails);
    testGolang(samples, goFails);
    testCSharp(samples, csFails);
}

function testAllInDir(dir, goFails, csFails, jsonSchemaFails) {
    let samples =
        fs.readdirSync(dir)
            .filter((name) => name.endsWith(".json") && !name.startsWith("."))
            .map((name) => absolutize(path.join(dir, name)));
    testAll(samples, goFails, csFails, jsonSchemaFails);
}

function main(sources) {
    if (sources.length == 0) {
        let samples = Samples.samples.map((name) => path.join("..", "..", "app", "public", "sample", "json", name));
        testAll(samples, [], [], []);
        testAllInDir(path.join("test", "inputs", "json"), ["identifiers.json"], [], []);
    } else {
        sources.forEach((source) => {
            if (fs.lstatSync(source).isDirectory()) {
                testAllInDir(source, [], [], []);
            } else {
                testAll([absolutize(arg)], [], [], []);
            }
        });
    }
}

// skip 2 `node` args
main(process.argv.slice(2));
<commit_msg>Test JSON->Schema->Go<commit_after>#!/usr/bin/env node

const Ajv = require('ajv');
const fs = require("fs");
const path = require("path");
const shell = require("shelljs");
const Main = require("../output/Main");
const Samples = require("../output/Samples");

// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
function deepEquals(x, y) {
    var i;
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
        return true;
    }

    // Compare primitives and functions.
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
        return true;
    }

    if ((x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number)) {
        if (x.toString() !== y.toString()) {
            console.log("Number or string not equal.");
            return false;
        }
        return true;
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
        console.log("One is not an object.")
        return false;
    }

    if (x.constructor !== y.constructor) {
        console.log("Not the same constructor.");
        return false;
    }

    if (x.prototype !== y.prototype) {
        console.log("Not the same prototype.");
        return false;
    }

    if (Array.isArray(x)) {
        if (x.length !== y.length){
            console.log("Arrays don't have the same length.");
            return false;
        }
        for (i = 0; i < x.length; i++) {
            if (!deepEquals(x[i], y[i]))
                return false;
        }
        return true;
    }

    for (p in y) {
        // We allow properties in y that aren't present in x
        // so long as they're null.
        if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
            if (y[p] !== null) {
                console.log(`Non-null property ${p} is not expected.`);
                return false;
            }
            continue;
        }
        if (typeof y[p] !== typeof x[p]) {
            console.log(`Properties ${p} don't have the same types.`);
            return false;
        }
    }

    for (p in x) {
        if (x.hasOwnProperty(p) && !y.hasOwnProperty(p)) {
            console.log(`Expected property ${p} not found.`);
            return false;
        }
        if (typeof x[p] !== typeof y[p]) {
            console.log(`Properties ${p} don't have the same types.`);
            return false;
        }

        switch (typeof(x[p])) {
        case 'object':
            if (!deepEquals(x[p], y[p])) {
                return false;
            }
            break;

        default:
            if (x[p] !== y[p]) {
                console.log(`Non-object properties ${p} are not equal.`)
                return false;
            }
            break;
        }
    }

    return true;
}

function exec(s, opts, cb) {
    let result = shell.exec(s, opts, cb);
    if (result.code !== 0) {
        shell.exit(result.code);
    }
    return result;
}

function execAndCompare(cmd, p, knownFails) {
    let outputString = exec(cmd, {silent:true}).stdout;
    if (knownFails.indexOf(path.basename(p)) < 0) {
        let outputJSON = JSON.parse(outputString);
        let inputJSON = JSON.parse(fs.readFileSync(p));
        if (!deepEquals(inputJSON, outputJSON)) {
            console.log("Error: Output is not equivalent to input.");
            process.exit(1);
        }
    } else {
        console.log("Known to fail - not checking output.");
    }
}

function absolutize(p) {
    if (path.isAbsolute(p))
        return p;
    return path.join(process.cwd(), p);
}

function execQuicktype(source, output, sourceLanguage) {
    exec(`node ../../cli/quicktype.js --srcLang "${sourceLanguage}" -o "${output}" "${source}"`);
}

function runTests(description, samples, dir, prepareCmd, filename, testFn) {
    shell.cd(dir);
    if (prepareCmd)
        shell.exec(prepareCmd, { silent: true });

    samples.forEach((sample) => {
        console.error(`* Building ${description} for ${sample}`);
        execQuicktype(sample, filename, "json");
        testFn(sample);
    });

    shell.cd("../..");
}

function testCSharp(samples, knownFails) {
    runTests("C# code", samples, "test/csharp", "dotnet restore", "QuickType.cs",
        function (p) {
            execAndCompare(`dotnet run "${p}"`, p, knownFails);
        }
    );
}

function testGolang(samples, knownFails) {
    runTests("Go code", samples, "test/golang", null, "quicktype.go",
        function (p) {
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownFails);
        }
    );
}

function testJsonSchema(samples, knownFails, knownGoFails) {
    runTests("JSON Schema", samples, "test/golang", null, "schema.json",
        function (p) {
            let input = JSON.parse(fs.readFileSync(p));
            let schema = JSON.parse(fs.readFileSync("schema.json"));
            let ajv = new Ajv();
            let valid = ajv.validate(schema, input);
            if (!valid) {
                console.log("Error: Generated schema does not validate input JSON.");
                process.exit(1);
            }
            execQuicktype("schema.json", "quicktype.go", "json-schema");
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownGoFails);
        }
    );
}

function testAll(samples, goFails, csFails, jsonSchemaFails) {
    testJsonSchema(samples, jsonSchemaFails, goFails);
    testGolang(samples, goFails);
    testCSharp(samples, csFails);
}

function testAllInDir(dir, goFails, csFails, jsonSchemaFails) {
    let samples =
        fs.readdirSync(dir)
            .filter((name) => name.endsWith(".json") && !name.startsWith("."))
            .map((name) => absolutize(path.join(dir, name)));
    testAll(samples, goFails, csFails, jsonSchemaFails);
}

function main(sources) {
    if (sources.length == 0) {
        let samples = Samples.samples.map((name) => path.join("..", "..", "app", "public", "sample", "json", name));
        testAll(samples, [], [], []);
        testAllInDir(path.join("test", "inputs", "json"), ["identifiers.json"], [], []);
    } else {
        sources.forEach((source) => {
            if (fs.lstatSync(source).isDirectory()) {
                testAllInDir(source, [], [], []);
            } else {
                testAll([absolutize(arg)], [], [], []);
            }
        });
    }
}

// skip 2 `node` args
main(process.argv.slice(2));
<|endoftext|>


<commit_before>'use strict';

require('should');
var anyfetchHydrater = require('anyfetch-hydrater');
var pdfocr = require('../lib/');

var HydrationError = anyfetchHydrater.HydrationError;

describe('Test PDFOCR results', function() {
	it('returns the correct informations', function(done) {
		var document = {
			datas: {}
		};

		var changes = anyfetchHydrater.defaultChanges();

		pdfocr(__dirname + "/samples/test.pdf", document, changes, function(err, changes){
			if (err){
				throw err;
			}
		});
		done();
	});

	it('should return an errored document', function(done) {
    var document = {
      data: {}
    };

    var changes = anyfetchHydrater.defaultChanges();

    pdfocr(__dirname + "/samples/bad.pdf", document, changes, function(err) {
      if(err instanceof HydrationError) {
        done();
      }
      else {
        done(new Error("invalid error"));
      }
    });
  });

});
<commit_msg>Fixing indentation<commit_after>'use strict';

require('should');
var anyfetchHydrater = require('anyfetch-hydrater');
var pdfocr = require('../lib/');

var HydrationError = anyfetchHydrater.HydrationError;

describe('Test PDFOCR results', function() {
  it('returns the correct informations', function(done) {
        var document = {
            datas: {}
        };

        var changes = anyfetchHydrater.defaultChanges();

        pdfocr(__dirname + "/samples/test.pdf", document, changes, function(err, changes){
            if (err){
                throw err;
            }
        });
        done();
    });

    it('should return an errored document', function(done) {
    var document = {
      data: {}
    };

    var changes = anyfetchHydrater.defaultChanges();

    pdfocr(__dirname + "/samples/bad.pdf", document, changes, function(err) {
      if(err instanceof HydrationError) {
        done();
      }
      else {
        done(new Error("invalid error"));
      }
    });
  });

});
<|endoftext|>


<commit_before>
var TEST_MAP = {
  globalize: {
    init: function() {
      this.superInit();
      phina.globalize();
      phina.pixi.globalize();
      var layer = DisplayElement().addChildTo(this);
      Sprite('tomapiko').addChildTo(this)
      .setPosition(this.gridX.center(), this.gridY.center());

      Sprite(Texture.fromShape(TriangleShape())).addChildTo(layer)
      .setPosition(this.gridX.center(), this.gridY.center());
    },

    see: true,
  },

  Element_defaults: {
    init: function(){
      this.superInit();
      var PixiElement = phina.pixi.PixiElement;
      var defaults = PixiElement.defaults;
      var elm = PixiElement();
      var flag = false;
      var temp = false;
      for(var k in defaults) {
        console.assert(temp = defaults[k] === elm[k], k);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Element_changes: {
    init: function(){
      this.superInit();
      var changeMap = {
        width: 100,
        scaleX: 5,
        scaleY: -5,
        height: 50,
        rotation: 100,
        originX: 0,
        originY: -5,
        x: 10,
        y: -100,

        top: 1,
        left: 10,
        bottom: -10,
        right: 1,

        blendMode: 'lighter',
      };

      var flag = false;
      var temp = false;
      for(var k in changeMap) {

        var elm = phina.pixi.PixiElement();
        var currentValues = {};
        for(var k2 in changeMap) {
          currentValues[k2] = elm[k2];
        }
        elm[k] = currentValues[k] = changeMap[k];
        console.assert(temp = elm[k] === currentValues[k], k + ':' + elm[k]);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Sprite_init: {
    init:function() {
      this.superInit();
      phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    see: true,
  },

  hitTestRect: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  hitTestCircle: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
      this.sp.boundingType = 'circle';
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  Sprite_interactive: {
    init:function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);

      sprite.setInteractive(true);
      sprite.fx = sprite.fy = 0;
      sprite.onpointstart = function() {
        this.fx = this.fy = 0;
        this.setScale(0.5);
      };
      sprite.onpointend = function(e) {
        this.fx = e.pointer.fx;
        this.fy = e.pointer.fy;
        this.setScale(1);
      };
      sprite.onpointmove = function(e) {
        var p = e.pointer;
        if(p.getPointing()) {
          this.x += p.dx;
          this.y += p.dy;
        }
      };
      var self = this;
      sprite.update = function() {
        var x = this.x += this.fx;
        var y = this.y += this.fy;

        if(x < 0 || x > self.width) {
          this.x = Math.clamp(x, 0, self.width);
          this.fx *= -1;
          this.scaleX *= -1;
        }

        if(y < 0 || y > self.height) {
          this.y = Math.clamp(y, 0, self.height);
          this.fy *= -1;
          this.scaleY *= -1;
        }

        this.fx *= 0.95;
        this.fy *= 0.95;
      };
    },

    controll: true,
  },

  Sprite_origin: {
    init: function (){
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var topleft = Sprite('tomapiko').addChildTo(this).setOrigin(0, 0);
      var bottomright = Sprite('tomapiko').addChildTo(this).setOrigin(1, 1).setPosition(this.width, this.height);
      var temp = false;
      var flag = false;
      console.assert(temp = this.width === bottomright.right, 'right');
      flag = flag || !temp;
      console.assert(temp = this.height === bottomright.bottom, 'bottom');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.top, 'top');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.left, 'left');
      assertMessage(flag);
    }
  },

  Sprite_rect: {
    init: function() {
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var sp = Sprite('tomapiko').addChildTo(this);
      var originStep = 0.5;
      var originStart = -5;
      var originEnd = 5;
      var xStep = this.width / 10;
      var xStart = -this.width;
      var xEnd = this.width * 2;

      var yStep = this.height / 10;
      var yStart = -this.height;
      var yEnd = this.height * 2;

      var temp = false;
      var flag = false;

      var rect = phina.geom.Rect();
      rect.width = sp.width;
      rect.height = sp.height;
      var props = 'left,right,top,bottom,centerX,centerY'.split(',');
      Array.range(originStart, originEnd, originStep).forEach(function(i) {
        sp.setOrigin(i);
        Array.range(xStart, xEnd, xStep).forEach(function(x) {
          sp.x = x;
          rect.x = x - rect.width * i;
          Array.range(yStart, yEnd, yStep).forEach(function(y){
            sp.y = y;
            rect.y = y - rect.height * i;
            props.forEach(function(p){
              // 誤差調整
              var rp = rect[p];
              var spp = sp[p];
              rp = Math.round(rp);
              spp = Math.round(spp);
              console.assert(temp = rp === spp, p + ':' + rp + ', ' + spp);
              flag = flag || !temp;
            });
          });
        });
      });

      assertMessage(flag);
      if(!flag) sp.setPosition(320, 480).setOrigin(0.5);
    }
  },

  visualProperties: {
    init: function() {
      this.superInit();
      var sp = phina.pixi.PixiSprite('tomapiko')
        .addChildTo(this)
        .setPosition(this.gridX.center(1), this.gridY.center(1));
      sp.tweener.to({
        rotation: 720,
        alpha: 0,
        width: 100,
        height: 100,
      }, 2000, 'swing').to({
        alpha: 1,
        scaleX: -2,
        scaleY: 2,
      }, 2000, 'easeInBack').to({
        rotation: 0,
      }).to({
        scaleX: 1,
        scaleY: -1
      }).setLoop(true);
    },
    see: true,
  },

  TextureFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.StarShape();
      var texture = phina.pixi.PixiTexture.fromShape(shape);
      var sprite = phina.pixi.PixiSprite(texture).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.HeartShape();
      var sprite = phina.pixi.PixiSprite().fromShape(shape).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFrame: {
    init: function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.setFrame(0, 0, sprite.width, sprite.height / 2);
      sprite.setOrigin(0.5, 1);

      var sprite2 = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite2.setPosition(this.gridX.center(), this.gridY.center());
      sprite2.setFrame(0, sprite2.height / 2, sprite2.width, sprite2.height / 2);
      sprite2.setOrigin(0.5, 0);

      var self = this;

      sprite.tweener
      .by({
        y: -100,
      }, 1000, 'swing')
      .by({
        y: 100
      }, 500, 'easeInBack')
      .wait(500)
      .setLoop(true);

      sprite2.tweener
      .by({
        y: 100,
      }, 1000, 'swing')
      .by({
        y: -100
      }, 500, 'easeInBack')
      .call(function(){
        phina.pixi.PixiSprite()
        .fromShape(phina.display.StarShape())
        .addChildTo(self)
        .setPosition(sprite2.x, sprite2.y)
        .tweener.to({
          scaleX: 5,
          scaleY: 5,
          alpha: 0,
        }, 500, 'easeOutCirc').call(function() {
          this.target.remove();
        });
      })
      .wait(500)
      .setLoop(true);
    },
    see: true
  }

};

var ASSETS = {
  pixi: {
    tomapiko: 'https://raw.githubusercontent.com/phinajs/phina.js/develop/assets/images/tomapiko.png',
  }
};

if(location.href.indexOf('index.html') === -1) {
  TEST_MAP.forIn(function(className, properties){
    properties.superClass = properties.superClass || phina.pixi.PixiScene;
    phina.define('TEST_' + className, properties);
  });
  var app;
  var isLoading = true;
  phina.main(function(){
    app = phina.pixi.PixiApp({}).run().enableStats();
    app.pushScene(phina.game.LoadingScene({assets: ASSETS}).on('loaded', function(){
      isLoading = false;
      if(window !== parent) {
        post('loaded');
      }
    }));
  });

  onerror = function(e) {
    console.error(e);
    alert('エラーが発生しました。\n' + e);
  };
}
else {

  var button = document.createElement('button');
  button.textContent = 'ALL';
  button.id = 'b_ALL';
  button.style.width = '100%';
  button.onclick = function() {
    var f = document.getElementById('frame');
    var w = f.contentWindow;
    w.testAll();
  };

  document.getElementById('side').appendChild(button);
  for(var k in TEST_MAP) {
    var button = document.createElement('button');
    button.textContent = k;
    button.id = 'b_' + k;
    button.dataset.hash = k;
    button.style.width = '100%';
    if(TEST_MAP[k].see) {
      button.style.backgroundColor = 'navy';
      button.style.color = 'white';
      button.textContent = 'see: ' + k;
    }
    if(TEST_MAP[k].controll) {
      button.style.backgroundColor = 'orange';
      button.textContent = 'controll: ' + k;
    }
    button.onclick = function() {
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      location.hash = w.location.hash = this.dataset.hash;
      w.replaceSceneByHash();
    };

    document.getElementById('side').appendChild(button);
  }

  onmessage = function(e) {
    var data = e.data;
    var type = data.type;
    var id = data.id;
    var b = document.getElementById('b_' + id);
    if(!b) {
      b = {style: {}, dataset: {}};
    }
    var style = b.style;

    var beforeText = '';
    var afterText = '';

    if(data === 'loaded'){
      hideLoading();
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      document.getElementById('b_ALL').click();
      w.alertFlag = true;

      w.location.hash = location.hash;
      w.replaceSceneByHash();
    }
    else if (type === 'assert'){
      style.backgroundColor = data.success ? 'rgb(180, 245, 240)' : 'pink';
      afterText = data.success ? ' (success)' : '(failure)';
    }

    if(beforeText) {
      b.textContent = beforeText + b.dataset.hash;
    }

    if(afterText) {
      b.textContent = b.dataset.hash + afterText;
    }
  };

  showLoading();
  function hideLoading() {
    document.getElementById('loading').style.display = 'none';
  }
  function showLoading(){
    document.getElementById('loading').style.display = 'flex';
  }
}

function testAll(){
  console.log('全てのシーンをテストします。テストとして保証できるのは init, onenter のみです。');
  TEST_MAP.forIn(function(k, v){
    location.hash = k;
    replaceSceneByHash();
  });
}

function replaceSceneByHash(){
  if(getId() in TEST_MAP) {
    app.replaceScene(phina.using('TEST_' + getId())());
  }
}

var alertFlag = false;

function assertMessage(flag) {
  var title = getId();
  post({
    id: title,
    success: !flag,
    type: 'assert',
  });
  if(flag && alertFlag) {
    alert(title + '\nテストに失敗しました。\n開発者ツールを確認して下さい。');
  }
  else {
    console.log(location.hash + ': OK');
  }
}

function getId() {
  return location.hash.slice(1);
}

function seeMessage() {
  console.log(location.hash + ': このテストは目視で確認してください。');
  post({
    id: getId(),
    type: 'see'
  });
}
function controllMessage() {
  console.log(location.hash + ': このテストは操作して確認してください。');
  post({
    id: getId(),
    type: 'controll'
  });
}

function post(data) {
  return parent.postMessage(data, '*');
}<commit_msg>add hitTestElement test<commit_after>
var TEST_MAP = {
  globalize: {
    init: function() {
      this.superInit();
      phina.globalize();
      phina.pixi.globalize();
      var layer = DisplayElement().addChildTo(this);
      Sprite('tomapiko').addChildTo(this)
      .setPosition(this.gridX.center(), this.gridY.center());

      Sprite(Texture.fromShape(TriangleShape())).addChildTo(layer)
      .setPosition(this.gridX.center(), this.gridY.center());
    },

    see: true,
  },

  Element_defaults: {
    init: function(){
      this.superInit();
      var PixiElement = phina.pixi.PixiElement;
      var defaults = PixiElement.defaults;
      var elm = PixiElement();
      var flag = false;
      var temp = false;
      for(var k in defaults) {
        console.assert(temp = defaults[k] === elm[k], k);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Element_changes: {
    init: function(){
      this.superInit();
      var changeMap = {
        width: 100,
        scaleX: 5,
        scaleY: -5,
        height: 50,
        rotation: 100,
        originX: 0,
        originY: -5,
        x: 10,
        y: -100,

        top: 1,
        left: 10,
        bottom: -10,
        right: 1,

        blendMode: 'lighter',
      };

      var flag = false;
      var temp = false;
      for(var k in changeMap) {

        var elm = phina.pixi.PixiElement();
        var currentValues = {};
        for(var k2 in changeMap) {
          currentValues[k2] = elm[k2];
        }
        elm[k] = currentValues[k] = changeMap[k];
        console.assert(temp = elm[k] === currentValues[k], k + ':' + elm[k]);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Sprite_init: {
    init:function() {
      this.superInit();
      phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    see: true,
  },

  hitTestRect: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  hitTestCircle: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
      this.sp.boundingType = 'circle';
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  hitTestElement: {
    init: function() {
      this.superInit();
      var sprite = this.sprite = Sprite().fromShape(RectangleShape({
        fill: 'orange',
        padding: 0
      })).addChildTo(this);
      Draggable().attachTo(sprite);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      var shape = this.shape = RectangleShape({
        padding: 0,
      });
      var rect = this.rect = Sprite().fromShape(shape).addChildTo(this);
      rect.setPosition(this.gridX.center(2), this.gridY.center(2));
      Draggable().attachTo(rect);
    },
    controll: true,
    update: function(app) {
      if(this.sprite.hitTestElement(this.rect)) {
        this.shape.fill = 'red';
      }
      else {
        this.shape.fill = 'blue';
      }

      if(this.shape._dirtyDraw) {
        this.shape.render(this.shape.canvas);
        this.rect.updateTexture();
      }
    }
  },

  Sprite_interactive: {
    init:function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);

      sprite.setInteractive(true);
      sprite.fx = sprite.fy = 0;
      sprite.onpointstart = function() {
        this.fx = this.fy = 0;
        this.setScale(0.5);
      };
      sprite.onpointend = function(e) {
        this.fx = e.pointer.fx;
        this.fy = e.pointer.fy;
        this.setScale(1);
      };
      sprite.onpointmove = function(e) {
        var p = e.pointer;
        if(p.getPointing()) {
          this.x += p.dx;
          this.y += p.dy;
        }
      };
      var self = this;
      sprite.update = function() {
        var x = this.x += this.fx;
        var y = this.y += this.fy;

        if(x < 0 || x > self.width) {
          this.x = Math.clamp(x, 0, self.width);
          this.fx *= -1;
          this.scaleX *= -1;
        }

        if(y < 0 || y > self.height) {
          this.y = Math.clamp(y, 0, self.height);
          this.fy *= -1;
          this.scaleY *= -1;
        }

        this.fx *= 0.95;
        this.fy *= 0.95;
      };
    },

    controll: true,
  },

  Sprite_origin: {
    init: function (){
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var topleft = Sprite('tomapiko').addChildTo(this).setOrigin(0, 0);
      var bottomright = Sprite('tomapiko').addChildTo(this).setOrigin(1, 1).setPosition(this.width, this.height);
      var temp = false;
      var flag = false;
      console.assert(temp = this.width === bottomright.right, 'right');
      flag = flag || !temp;
      console.assert(temp = this.height === bottomright.bottom, 'bottom');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.top, 'top');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.left, 'left');
      assertMessage(flag);
    }
  },

  Sprite_rect: {
    init: function() {
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var sp = Sprite('tomapiko').addChildTo(this);
      var originStep = 0.5;
      var originStart = -5;
      var originEnd = 5;
      var xStep = this.width / 10;
      var xStart = -this.width;
      var xEnd = this.width * 2;

      var yStep = this.height / 10;
      var yStart = -this.height;
      var yEnd = this.height * 2;

      var temp = false;
      var flag = false;

      var rect = phina.geom.Rect();
      rect.width = sp.width;
      rect.height = sp.height;
      var props = 'left,right,top,bottom,centerX,centerY'.split(',');
      Array.range(originStart, originEnd, originStep).forEach(function(i) {
        sp.setOrigin(i);
        Array.range(xStart, xEnd, xStep).forEach(function(x) {
          sp.x = x;
          rect.x = x - rect.width * i;
          Array.range(yStart, yEnd, yStep).forEach(function(y){
            sp.y = y;
            rect.y = y - rect.height * i;
            props.forEach(function(p){
              // 誤差調整
              var rp = rect[p];
              var spp = sp[p];
              rp = Math.round(rp);
              spp = Math.round(spp);
              console.assert(temp = rp === spp, p + ':' + rp + ', ' + spp);
              flag = flag || !temp;
            });
          });
        });
      });

      assertMessage(flag);
      if(!flag) sp.setPosition(320, 480).setOrigin(0.5);
    }
  },

  visualProperties: {
    init: function() {
      this.superInit();
      var sp = phina.pixi.PixiSprite('tomapiko')
        .addChildTo(this)
        .setPosition(this.gridX.center(1), this.gridY.center(1));
      sp.tweener.to({
        rotation: 720,
        alpha: 0,
        width: 100,
        height: 100,
      }, 2000, 'swing').to({
        alpha: 1,
        scaleX: -2,
        scaleY: 2,
      }, 2000, 'easeInBack').to({
        rotation: 0,
      }).to({
        scaleX: 1,
        scaleY: -1
      }).setLoop(true);
    },
    see: true,
  },

  TextureFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.StarShape();
      var texture = phina.pixi.PixiTexture.fromShape(shape);
      var sprite = phina.pixi.PixiSprite(texture).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.HeartShape();
      var sprite = phina.pixi.PixiSprite().fromShape(shape).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFrame: {
    init: function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.setFrame(0, 0, sprite.width, sprite.height / 2);
      sprite.setOrigin(0.5, 1);

      var sprite2 = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite2.setPosition(this.gridX.center(), this.gridY.center());
      sprite2.setFrame(0, sprite2.height / 2, sprite2.width, sprite2.height / 2);
      sprite2.setOrigin(0.5, 0);

      var self = this;

      sprite.tweener
      .by({
        y: -100,
      }, 1000, 'swing')
      .by({
        y: 100
      }, 500, 'easeInBack')
      .wait(500)
      .setLoop(true);

      sprite2.tweener
      .by({
        y: 100,
      }, 1000, 'swing')
      .by({
        y: -100
      }, 500, 'easeInBack')
      .call(function(){
        phina.pixi.PixiSprite()
        .fromShape(phina.display.StarShape())
        .addChildTo(self)
        .setPosition(sprite2.x, sprite2.y)
        .tweener.to({
          scaleX: 5,
          scaleY: 5,
          alpha: 0,
        }, 500, 'easeOutCirc').call(function() {
          this.target.remove();
        });
      })
      .wait(500)
      .setLoop(true);
    },
    see: true
  }

};

var ASSETS = {
  pixi: {
    tomapiko: 'https://raw.githubusercontent.com/phinajs/phina.js/develop/assets/images/tomapiko.png',
  }
};

if(location.href.indexOf('index.html') === -1) {
  TEST_MAP.forIn(function(className, properties){
    properties.superClass = properties.superClass || phina.pixi.PixiScene;
    phina.define('TEST_' + className, properties);
  });
  var app;
  var isLoading = true;
  phina.main(function(){
    app = phina.pixi.PixiApp({}).run().enableStats();
    app.pushScene(phina.game.LoadingScene({assets: ASSETS}).on('loaded', function(){
      isLoading = false;
      if(window !== parent) {
        post('loaded');
      }
    }));
  });

  onerror = function(e) {
    console.error(e);
    alert('エラーが発生しました。\n' + e);
  };
}
else {

  var button = document.createElement('button');
  button.textContent = 'ALL';
  button.id = 'b_ALL';
  button.style.width = '100%';
  button.onclick = function() {
    var f = document.getElementById('frame');
    var w = f.contentWindow;
    w.testAll();
  };

  document.getElementById('side').appendChild(button);
  for(var k in TEST_MAP) {
    var button = document.createElement('button');
    button.textContent = k;
    button.id = 'b_' + k;
    button.dataset.hash = k;
    button.style.width = '100%';
    if(TEST_MAP[k].see) {
      button.style.backgroundColor = 'navy';
      button.style.color = 'white';
      button.textContent = 'see: ' + k;
    }
    if(TEST_MAP[k].controll) {
      button.style.backgroundColor = 'orange';
      button.textContent = 'controll: ' + k;
    }
    button.onclick = function() {
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      location.hash = w.location.hash = this.dataset.hash;
      w.replaceSceneByHash();
    };

    document.getElementById('side').appendChild(button);
  }

  onmessage = function(e) {
    var data = e.data;
    var type = data.type;
    var id = data.id;
    var b = document.getElementById('b_' + id);
    if(!b) {
      b = {style: {}, dataset: {}};
    }
    var style = b.style;

    var beforeText = '';
    var afterText = '';

    if(data === 'loaded'){
      hideLoading();
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      document.getElementById('b_ALL').click();
      w.alertFlag = true;

      w.location.hash = location.hash;
      w.replaceSceneByHash();
    }
    else if (type === 'assert'){
      style.backgroundColor = data.success ? 'rgb(180, 245, 240)' : 'pink';
      afterText = data.success ? ' (success)' : '(failure)';
    }

    if(beforeText) {
      b.textContent = beforeText + b.dataset.hash;
    }

    if(afterText) {
      b.textContent = b.dataset.hash + afterText;
    }
  };

  showLoading();
  function hideLoading() {
    document.getElementById('loading').style.display = 'none';
  }
  function showLoading(){
    document.getElementById('loading').style.display = 'flex';
  }
}

function testAll(){
  console.log('全てのシーンをテストします。テストとして保証できるのは init, onenter のみです。');
  TEST_MAP.forIn(function(k, v){
    location.hash = k;
    replaceSceneByHash();
  });
}

function replaceSceneByHash(){
  if(getId() in TEST_MAP) {
    app.replaceScene(phina.using('TEST_' + getId())());
  }
}

var alertFlag = false;

function assertMessage(flag) {
  var title = getId();
  post({
    id: title,
    success: !flag,
    type: 'assert',
  });
  if(flag && alertFlag) {
    alert(title + '\nテストに失敗しました。\n開発者ツールを確認して下さい。');
  }
  else {
    console.log(location.hash + ': OK');
  }
}

function getId() {
  return location.hash.slice(1);
}

function seeMessage() {
  console.log(location.hash + ': このテストは目視で確認してください。');
  post({
    id: getId(),
    type: 'see'
  });
}
function controllMessage() {
  console.log(location.hash + ': このテストは操作して確認してください。');
  post({
    id: getId(),
    type: 'controll'
  });
}

function post(data) {
  return parent.postMessage(data, '*');
}<|endoftext|>