<commit_before>'use strict';

var gulp = require('gulp');

var path = require('path');
var util = require('util');

var Q = require('q');
var openApp = require('open');
var shell = require('shelljs');
var nodemon = require('nodemon');
var strftime = require('strftime');

var $ = require('gulp-load-plugins')();

var gitRevision = shell.exec('git rev-parse --short HEAD', { silent:true }).output;

var environments = ['dev', 'www'];

var config = {
    pub: 'public',
    pkg: require('./package.json'),
    src: path.resolve('app'),
    app: path.resolve('app', 'public'),
    models: path.resolve('app', 'models'),
    dist: path.resolve('dist'),
    cordova: path.resolve('cordova/www/'),
    buildEnvironment: environments[0],
    temp: path.resolve('.tmp'),
    test: path.resolve('test'),
    server: path.resolve('app', 'app.js'),
    hostname: 'localhost',
    port: 3000,
    baseUrl: '/',
    today: Date.now(),
    revision: (gitRevision.indexOf('fatal') > -1)?null:gitRevision.replace(/(\r\n|\n|\r)/gm, ''),
    version: require('./package.json').version
};

if (!config.revision) {
    $.util.log($.util.colors.yellow('Git repository missing.'), 'Consider running', $.util.colors.cyan('git init'));
}

// task names with underscore (_) prefix will be treated as private
// they will not be exposed as gulp tasks a user can directly run
var tasks = {
    clean: function (isBuild) {
        var glob = [config.temp];
        if (isBuild) {
            glob.push(path.join(config.dist, config.buildEnvironment));
        }
        $.util.log('Cleaning ', $.util.colors.magenta(glob));
        return gulp.src(glob, { read: false })
            .pipe($.rimraf({ force: true }));
    },
    jshint: function () {
        $.util.log('Linting javascript files...');
        var glob = [
            'gulpfile.js',
            path.join(config.src, 'app.js'),
            path.join(config.src, 'routes.js'),
            path.join(config.models, '**/*.js'),
            path.join(config.app, 'components/**/*.js'),
            path.join(config.app, 'controllers/**/*.js'),
            path.join(config.app, 'directives/**/*.js'),
            path.join(config.app, 'scripts/**/*.js'),
            // exclude loopback services file, it fails miserably
            '!' + path.join(config.app, 'scripts/services/lbServices.js')
        ];
        return gulp.src(glob)
            .pipe($.jshint('.jshintrc'))
            .pipe($.jshint.reporter('jshint-stylish'));
    },
    jsonlint: function () {
        $.util.log('Linting JSON files...');
        var glob = [
            '*.json'
        ];
        return gulp.src(glob)
            .pipe($.jsonlint())
            .pipe($.jsonlint.reporter());
    },
    sass: function (isBuild) {
        $.util.log('Compiling SASS files...');
        var glob = path.join(config.app, 'styles/app.scss');
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub, 'styles'):path.join(config.temp, 'styles');
        return gulp.src(glob)
            // as the css is not 1-to-1, this doesn't work as expected
            // .pipe($.changed(dest, { extension: '.css' }))
            .pipe($.rubySass({
                sourcemap: true,
                style: (isBuild && config.buildEnvironment !== 'dev')?'compressed':'expanded',
                precision: 10,
                lineNumbers: (isBuild)?false:true,
                debugInfo: (isBuild)?false:true
            }))
            .pipe(gulp.dest(dest));
    },
    jade: function (isBuild) {
        $.util.log('Compiling Jade files...');
        var glob = [path.join(config.app, '**/*.jade'), path.join('!', config.app, '**/_*.jade')];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        var LOCALS = {
            DEBUG: (isBuild)?false:true,
            LOCAL: (isBuild)?false:true,
            ENV: (isBuild)?config.buildEnvironment:'local',
            GIT_REVISION: config.revision,
            VERSION: 'v' + config.pkg.version,
            DATE_STAMP: strftime('%B %d, %Y %H:%M:%S', new Date(config.today)),
            YEAR: new Date(config.today).getFullYear()
        };
        return gulp.src(glob)
            // as the jade files are not 1-to-1 this doesn't work as expected
            // .pipe($.changed(dest, { extension: '.html' }))
            .pipe($.jade({
                locals: LOCALS,
                pretty: (isBuild === 'dist' && config.buildEnvironment !== 'dev')?false:true
            }))
            .pipe(gulp.dest(dest));
    },
    image: function (isBuild) {
        $.util.log('Minifing image files...');
        var glob = [
            path.join(config.app, 'images/**/*.{png,jpg,jpeg}'),
            path.join(config.app, 'styles/images/**/*.{png,jpg,jpeg}'),
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob, { base: config.app })
            .pipe($.imagemin())
            .pipe(gulp.dest(dest));
    },
    _usemin: function (isBuild) {
        $.util.log('Processing usemin js blocks in HTML files...');
        var glob = path.join(config.dist, config.buildEnvironment, config.pub, '*.html');
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob)
            .pipe($.usemin({
                css: ['concat', $.rev()],
                angularJs: ['concat', $.ngAnnotate(), $.uglify(), $.rev()],
                js: ['concat', $.uglify(), $.rev()]
            }))
            .pipe(gulp.dest(dest));
    },
    _copyAssets: function (isBuild) {
        $.util.log('Copying front end assets into build destination...');
        // copy all assets relative to config.app
        var glob = [
            // copy gif files manually as they are not compressed
            path.join(config.app, 'images/**/*.gif'),
            path.join(config.app, 'styles/**/*.gif'),
            path.join(config.app, 'styles/fonts/**/*'),
            path.join(config.app, 'favicon.ico')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob, { base: config.app })
            .pipe(gulp.dest(dest));
    },
    _copyServer: function (isBuild) {
        $.util.log('Copying node assets into build destination...');
        // copy all assets relative to config.src
        var glob = [
            path.join(config.models, '**/*'),
            path.join(config.src, 'app.js'),
            path.join(config.src, 'app.json'),
            path.join(config.src, 'datasources.json'),
            path.join(config.src, 'models.json'),
            path.join(config.src, 'views/**/*')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment):config.temp;
        return gulp.src(glob, { base: config.src })
            .pipe(gulp.dest(dest));
    },
    _copyPackage: function (isBuild) {
        $.util.log('Copying package.json into build destination...');
        // copy all assets relative to root
        var glob = 'package.json';
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment):config.temp;
        return gulp.src(glob)
            .pipe(gulp.dest(dest));
    },
    _addBanner: function (isBuild) {
        $.util.log('Add debug header to files...');
        var glob = [
            path.join(config.dist, config.buildEnvironment, config.pub, '**/*.css'),
            path.join(config.dist, config.buildEnvironment, config.pub, '**/*.js')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        var banner = [
            '/**',
            ' * <%= pkg.title %> - <%= pkg.description %>',
            ' * @version v<%= pkg.version %>',
            ' * @link <%= pkg.homepage %>',
            ' * @license <%= pkg.license %>',
            ' * @revision <%= revision %>',
            ' * @build <%= build %>',
            ' */',
            ''
        ].join('\n');

        return gulp.src(glob)
            .pipe($.header(banner, {
                pkg : config.pkg,
                revision: config.revision,
                build: strftime('%B %d, %Y %H:%M:%S', new Date(config.today))
            }))
            .pipe(gulp.dest(dest));
    }
};

// taskify individual build and server tasks
var taskList = Object.keys(tasks);

taskList.forEach(function (taskName) {
    if (taskName.indexOf('_') === 0) {
        return;
    }
    gulp.task(taskName, function() {
        return tasks[taskName]();
    });
    environments.forEach(function (environment) {
        gulp.task(taskName + ':' + environment, function() {
            config.buildEnvironment = environment;
            return tasks[taskName](true);
        });
    });
});

function buildProject () {
    var deferred = Q.defer();
    // List of build task function names
    var taskList = [
        'clean',
        'jshint',
        'jsonlint',
        'sass',
        'jade',
        'image',
        '_usemin',
        '_copyAssets',
        '_copyServer',
        '_copyPackage',
        '_addBanner'
    ];
    var queue = new Q();
    taskList.forEach(function (task) {
        queue = queue.then(function () {
            // cheap conversion of task stream to promise
            var taskDeferred = Q.defer();
            // call the task name with the task type dist|server
            tasks[task](true)
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    taskDeferred.resolve();
                }));
            return taskDeferred.promise;
        });
    });
    queue = queue.then(function explode() {
        deferred.resolve();
    });
    return deferred.promise;
}

function startServer () {
    var deferred = Q.defer();
    // List of build task function names
    var taskList = [
        'clean',
        'jshint',
        'jsonlint',
        'sass',
        'jade'
    ];
    var queue = new Q();
    taskList.forEach(function (task) {
        queue = queue.then(function () {
            // cheap conversion of task stream to promise
            var taskDeferred = Q.defer();
            // call the task name with the task type dist|server
            tasks[task]()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    taskDeferred.resolve();
                }));
            return taskDeferred.promise;
        });
    });
    queue = queue.then(function explode() {
        deferred.resolve();
    });
    return deferred.promise;
}

// Gulp Tasks

// use first environment as default build alias, should be dev
gulp.task('build', ['build:' + environments[0]]);
environments.forEach(function (environment) {
    gulp.task('build:' + environment, function() {
        config.buildEnvironment = environment;
        return buildProject(environment);
    });
});

// serve alias for server
gulp.task('serve', ['server']);
gulp.task('server', function() {
    var deferred = Q.defer();
    startServer().then(function () {
        var server = $.livereload();
        $.util.log('Starting local server...');
        gulp.watch(path.join(config.app, '**/*.{scss,sass}'), function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            tasks.sass()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    server.changed(file.path);
                }));

        });
        gulp.watch(path.join(config.app, '**/*.jade'), function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            tasks.jade()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    server.changed(file.path);
                }));
        });

        var livereloadGlob = [
            path.join(config.app, 'images/**/*.{gif,png,jpg,jpeg}'),
            path.join(config.app, 'styles/images/**/*.{gif,png,jpg,jpeg}'),
            path.join(config.app, 'components/**/*.js'),
            path.join(config.app, 'controllers/**/*.js'),
            path.join(config.app, 'directives/**/*.js'),
            path.join(config.app, 'scripts/**/*.js')
        ];
        gulp.watch(livereloadGlob, function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            server.changed(file.path);
        });

        nodemon({
            script: config.server,
            args: [
                '--port=' + config.port,
                '--hostname=' + config.hostname,
                '--baseurl=' + config.baseUrl,
                '--local'
            ],
            stdout: false,
            watch: [
                config.server,
                config.models
            ]
        })
            .once('start', function () {
                // delay a second, express is not always immediately ready
                setTimeout(function() {
                    openApp('http://' + config.hostname + ':' + config.port + config.baseUrl);
                }, 1000);
            })
            .on('exit', function () {
                deferred.resolve();
            })
            .on('stdout', function (msg) {
                $.util.log('Server:', $.util.colors.magenta(msg));
            });
    });
    return deferred.promise;
});

gulp.task('config.sh', function () {
    var deferred = Q.defer();
    //make .config.sh from package.json for deploy.sh, remote.sh and tail-log.sh
    var fs = require('fs');
    var configFile = [
        '#Auto-generated by grunt build from values in package.json',
        util.format('BASEDOMAIN="%s";', config.pkg.edgeplate.domain),
        util.format('HOST="%s";', config.pkg.edgeplate.host),
        util.format('SSH_PORT="%d";', config.pkg.edgeplate.ports.ssh),
        util.format('DEV_PORT="%d";', config.pkg.edgeplate.ports.dev),
        util.format('WWW_PORT="%d";', config.pkg.edgeplate.ports.www),
        util.format('DIR="%s";', config.pkg.name)
    ].join('\n');
    fs.writeFile('.config.sh', configFile, function (err) {
        if(err) {
            $.util.beep();
            $.util.log($.util.colors.yellow('Config file creation failed.', err));
        } else {
            $.util.log($.util.colors.cyan('Config file created successfully.'));
        }
        deferred.resolve();
    });
    return deferred.promise;
});
<commit_msg>Force option for HTML 5. This prevents the expansion of boolean attributes<commit_after>'use strict';

var gulp = require('gulp');

var path = require('path');
var util = require('util');

var Q = require('q');
var openApp = require('open');
var shell = require('shelljs');
var nodemon = require('nodemon');
var strftime = require('strftime');

var $ = require('gulp-load-plugins')();

var gitRevision = shell.exec('git rev-parse --short HEAD', { silent:true }).output;

var environments = ['dev', 'www'];

var config = {
    pub: 'public',
    pkg: require('./package.json'),
    src: path.resolve('app'),
    app: path.resolve('app', 'public'),
    models: path.resolve('app', 'models'),
    dist: path.resolve('dist'),
    cordova: path.resolve('cordova/www/'),
    buildEnvironment: environments[0],
    temp: path.resolve('.tmp'),
    test: path.resolve('test'),
    server: path.resolve('app', 'app.js'),
    hostname: 'localhost',
    port: 3000,
    baseUrl: '/',
    today: Date.now(),
    revision: (gitRevision.indexOf('fatal') > -1)?null:gitRevision.replace(/(\r\n|\n|\r)/gm, ''),
    version: require('./package.json').version
};

if (!config.revision) {
    $.util.log($.util.colors.yellow('Git repository missing.'), 'Consider running', $.util.colors.cyan('git init'));
}

// task names with underscore (_) prefix will be treated as private
// they will not be exposed as gulp tasks a user can directly run
var tasks = {
    clean: function (isBuild) {
        var glob = [config.temp];
        if (isBuild) {
            glob.push(path.join(config.dist, config.buildEnvironment));
        }
        $.util.log('Cleaning ', $.util.colors.magenta(glob));
        return gulp.src(glob, { read: false })
            .pipe($.rimraf({ force: true }));
    },
    jshint: function () {
        $.util.log('Linting javascript files...');
        var glob = [
            'gulpfile.js',
            path.join(config.src, 'app.js'),
            path.join(config.src, 'routes.js'),
            path.join(config.models, '**/*.js'),
            path.join(config.app, 'components/**/*.js'),
            path.join(config.app, 'controllers/**/*.js'),
            path.join(config.app, 'directives/**/*.js'),
            path.join(config.app, 'scripts/**/*.js'),
            // exclude loopback services file, it fails miserably
            '!' + path.join(config.app, 'scripts/services/lbServices.js')
        ];
        return gulp.src(glob)
            .pipe($.jshint('.jshintrc'))
            .pipe($.jshint.reporter('jshint-stylish'));
    },
    jsonlint: function () {
        $.util.log('Linting JSON files...');
        var glob = [
            '*.json'
        ];
        return gulp.src(glob)
            .pipe($.jsonlint())
            .pipe($.jsonlint.reporter());
    },
    sass: function (isBuild) {
        $.util.log('Compiling SASS files...');
        var glob = path.join(config.app, 'styles/app.scss');
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub, 'styles'):path.join(config.temp, 'styles');
        return gulp.src(glob)
            // as the css is not 1-to-1, this doesn't work as expected
            // .pipe($.changed(dest, { extension: '.css' }))
            .pipe($.rubySass({
                sourcemap: true,
                style: (isBuild && config.buildEnvironment !== 'dev')?'compressed':'expanded',
                precision: 10,
                lineNumbers: (isBuild)?false:true,
                debugInfo: (isBuild)?false:true
            }))
            .pipe(gulp.dest(dest));
    },
    jade: function (isBuild) {
        $.util.log('Compiling Jade files...');
        var glob = [path.join(config.app, '**/*.jade'), path.join('!', config.app, '**/_*.jade')];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        var LOCALS = {
            DEBUG: (isBuild)?false:true,
            LOCAL: (isBuild)?false:true,
            ENV: (isBuild)?config.buildEnvironment:'local',
            GIT_REVISION: config.revision,
            VERSION: 'v' + config.pkg.version,
            DATE_STAMP: strftime('%B %d, %Y %H:%M:%S', new Date(config.today)),
            YEAR: new Date(config.today).getFullYear()
        };
        return gulp.src(glob)
            // as the jade files are not 1-to-1 this doesn't work as expected
            // .pipe($.changed(dest, { extension: '.html' }))
            .pipe($.jade({
                doctype: 'html',
                locals: LOCALS,
                pretty: (isBuild === 'dist' && config.buildEnvironment !== 'dev')?false:true
            }))
            .pipe(gulp.dest(dest));
    },
    image: function (isBuild) {
        $.util.log('Minifing image files...');
        var glob = [
            path.join(config.app, 'images/**/*.{png,jpg,jpeg}'),
            path.join(config.app, 'styles/images/**/*.{png,jpg,jpeg}'),
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob, { base: config.app })
            .pipe($.imagemin())
            .pipe(gulp.dest(dest));
    },
    _usemin: function (isBuild) {
        $.util.log('Processing usemin js blocks in HTML files...');
        var glob = path.join(config.dist, config.buildEnvironment, config.pub, '*.html');
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob)
            .pipe($.usemin({
                css: ['concat', $.rev()],
                angularJs: ['concat', $.ngAnnotate(), $.uglify(), $.rev()],
                js: ['concat', $.uglify(), $.rev()]
            }))
            .pipe(gulp.dest(dest));
    },
    _copyAssets: function (isBuild) {
        $.util.log('Copying front end assets into build destination...');
        // copy all assets relative to config.app
        var glob = [
            // copy gif files manually as they are not compressed
            path.join(config.app, 'images/**/*.gif'),
            path.join(config.app, 'styles/**/*.gif'),
            path.join(config.app, 'styles/fonts/**/*'),
            path.join(config.app, 'favicon.ico')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        return gulp.src(glob, { base: config.app })
            .pipe(gulp.dest(dest));
    },
    _copyServer: function (isBuild) {
        $.util.log('Copying node assets into build destination...');
        // copy all assets relative to config.src
        var glob = [
            path.join(config.models, '**/*'),
            path.join(config.src, 'app.js'),
            path.join(config.src, 'app.json'),
            path.join(config.src, 'datasources.json'),
            path.join(config.src, 'models.json'),
            path.join(config.src, 'views/**/*')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment):config.temp;
        return gulp.src(glob, { base: config.src })
            .pipe(gulp.dest(dest));
    },
    _copyPackage: function (isBuild) {
        $.util.log('Copying package.json into build destination...');
        // copy all assets relative to root
        var glob = 'package.json';
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment):config.temp;
        return gulp.src(glob)
            .pipe(gulp.dest(dest));
    },
    _addBanner: function (isBuild) {
        $.util.log('Add debug header to files...');
        var glob = [
            path.join(config.dist, config.buildEnvironment, config.pub, '**/*.css'),
            path.join(config.dist, config.buildEnvironment, config.pub, '**/*.js')
        ];
        var dest = (isBuild)?path.join(config.dist, config.buildEnvironment, config.pub):config.temp;
        var banner = [
            '/**',
            ' * <%= pkg.title %> - <%= pkg.description %>',
            ' * @version v<%= pkg.version %>',
            ' * @link <%= pkg.homepage %>',
            ' * @license <%= pkg.license %>',
            ' * @revision <%= revision %>',
            ' * @build <%= build %>',
            ' */',
            ''
        ].join('\n');

        return gulp.src(glob)
            .pipe($.header(banner, {
                pkg : config.pkg,
                revision: config.revision,
                build: strftime('%B %d, %Y %H:%M:%S', new Date(config.today))
            }))
            .pipe(gulp.dest(dest));
    }
};

// taskify individual build and server tasks
var taskList = Object.keys(tasks);

taskList.forEach(function (taskName) {
    if (taskName.indexOf('_') === 0) {
        return;
    }
    gulp.task(taskName, function() {
        return tasks[taskName]();
    });
    environments.forEach(function (environment) {
        gulp.task(taskName + ':' + environment, function() {
            config.buildEnvironment = environment;
            return tasks[taskName](true);
        });
    });
});

function buildProject () {
    var deferred = Q.defer();
    // List of build task function names
    var taskList = [
        'clean',
        'jshint',
        'jsonlint',
        'sass',
        'jade',
        'image',
        '_usemin',
        '_copyAssets',
        '_copyServer',
        '_copyPackage',
        '_addBanner'
    ];
    var queue = new Q();
    taskList.forEach(function (task) {
        queue = queue.then(function () {
            // cheap conversion of task stream to promise
            var taskDeferred = Q.defer();
            // call the task name with the task type dist|server
            tasks[task](true)
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    taskDeferred.resolve();
                }));
            return taskDeferred.promise;
        });
    });
    queue = queue.then(function explode() {
        deferred.resolve();
    });
    return deferred.promise;
}

function startServer () {
    var deferred = Q.defer();
    // List of build task function names
    var taskList = [
        'clean',
        'jshint',
        'jsonlint',
        'sass',
        'jade'
    ];
    var queue = new Q();
    taskList.forEach(function (task) {
        queue = queue.then(function () {
            // cheap conversion of task stream to promise
            var taskDeferred = Q.defer();
            // call the task name with the task type dist|server
            tasks[task]()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    taskDeferred.resolve();
                }));
            return taskDeferred.promise;
        });
    });
    queue = queue.then(function explode() {
        deferred.resolve();
    });
    return deferred.promise;
}

// Gulp Tasks

// use first environment as default build alias, should be dev
gulp.task('build', ['build:' + environments[0]]);
environments.forEach(function (environment) {
    gulp.task('build:' + environment, function() {
        config.buildEnvironment = environment;
        return buildProject(environment);
    });
});

// serve alias for server
gulp.task('serve', ['server']);
gulp.task('server', function() {
    var deferred = Q.defer();
    startServer().then(function () {
        var server = $.livereload();
        $.util.log('Starting local server...');
        gulp.watch(path.join(config.app, '**/*.{scss,sass}'), function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            tasks.sass()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    server.changed(file.path);
                }));

        });
        gulp.watch(path.join(config.app, '**/*.jade'), function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            tasks.jade()
                // not sure if I'm using buffer correctly but it does the trick
                .pipe($.util.buffer(function () {
                    server.changed(file.path);
                }));
        });

        var livereloadGlob = [
            path.join(config.app, 'images/**/*.{gif,png,jpg,jpeg}'),
            path.join(config.app, 'styles/images/**/*.{gif,png,jpg,jpeg}'),
            path.join(config.app, 'components/**/*.js'),
            path.join(config.app, 'controllers/**/*.js'),
            path.join(config.app, 'directives/**/*.js'),
            path.join(config.app, 'scripts/**/*.js')
        ];
        gulp.watch(livereloadGlob, function (file) {
            $.util.log('File updated', $.util.colors.magenta(file.path));
            server.changed(file.path);
        });

        nodemon({
            script: config.server,
            args: [
                '--port=' + config.port,
                '--hostname=' + config.hostname,
                '--baseurl=' + config.baseUrl,
                '--local'
            ],
            stdout: false,
            watch: [
                config.server,
                config.models
            ]
        })
            .once('start', function () {
                // delay a second, express is not always immediately ready
                setTimeout(function() {
                    openApp('http://' + config.hostname + ':' + config.port + config.baseUrl);
                }, 1000);
            })
            .on('exit', function () {
                deferred.resolve();
            })
            .on('stdout', function (msg) {
                $.util.log('Server:', $.util.colors.magenta(msg));
            });
    });
    return deferred.promise;
});

gulp.task('config.sh', function () {
    var deferred = Q.defer();
    //make .config.sh from package.json for deploy.sh, remote.sh and tail-log.sh
    var fs = require('fs');
    var configFile = [
        '#Auto-generated by grunt build from values in package.json',
        util.format('BASEDOMAIN="%s";', config.pkg.edgeplate.domain),
        util.format('HOST="%s";', config.pkg.edgeplate.host),
        util.format('SSH_PORT="%d";', config.pkg.edgeplate.ports.ssh),
        util.format('DEV_PORT="%d";', config.pkg.edgeplate.ports.dev),
        util.format('WWW_PORT="%d";', config.pkg.edgeplate.ports.www),
        util.format('DIR="%s";', config.pkg.name)
    ].join('\n');
    fs.writeFile('.config.sh', configFile, function (err) {
        if(err) {
            $.util.beep();
            $.util.log($.util.colors.yellow('Config file creation failed.', err));
        } else {
            $.util.log($.util.colors.cyan('Config file created successfully.'));
        }
        deferred.resolve();
    });
    return deferred.promise;
});
<|endoftext|>


<commit_before>/**
 * Copyright 2021 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {analyticsEventToGoogleAnalyticsEvent} from './event-type-mapping';
import {isFunction} from '../utils/types';

export class GoogleAnalyticsEventListener {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!./deps.DepsDef} deps */
    this.deps_ = deps;

    /** @private @const {!./client-event-manager.ClientEventManager} */
    this.eventManager_ = deps.eventManager();
  }

  /**
   * Start listening to client events
   */
  start() {
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   *  Listens for new events from the events manager and logs appropriate events to Google Analytics.
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @param {(!../api/client-event-manager-api.ClientEventParams|undefined)=} eventParams
   */
  handleClientEvent_(event, eventParams = undefined) {
    // Bail immediately if neither ga function (analytics.js) nor gtag function (gtag.js) exists in Window.
    if (
      !this.constructor.isGaEligible(this.deps_) &&
      !this.constructor.isGtagEligible(this.deps_)
    ) {
      return;
    }
    let subscriptionFlow = '';
    if (event.additionalParameters) {
      // additionalParameters isn't strongly typed so checking for both object and class notation.
      subscriptionFlow =
        event.additionalParameters.subscriptionFlow ||
        event.additionalParameters.getSubscriptionFlow();
    }
    let gaEvent = analyticsEventToGoogleAnalyticsEvent(
      event.eventType,
      subscriptionFlow
    );
    if (!gaEvent) {
      return;
    }

    const analyticsParams = eventParams?.googleAnalyticsParameters || {};
    gaEvent = {
      ...gaEvent,
      eventCategory: analyticsParams.event_category || gaEvent.eventCategory,
      eventLabel: analyticsParams.event_label || gaEvent.eventLabel,
    };
    // TODO(b/234825847): Remove it once universal analytics is deprecated in 2023.
    if (this.constructor.isGaEligible(this.deps_)) {
      const ga = this.deps_.win().ga || null;
      ga('send', 'event', gaEvent);
    }

    if (this.constructor.isGtagEligible(this.deps_)) {
      const gtag = this.deps_.win().gtag || null;
      const gtagEvent = {
        'event_category': gaEvent.eventCategory,
        'event_label': gaEvent.eventLabel,
        'non_interaction': gaEvent.nonInteraction,
        ...analyticsParams,
      };
      gtag('event', gaEvent.eventAction, gtagEvent);
    }
  }

  /**
   * Function to determine whether event is eligible for GA logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGaEligible(deps) {
    return isFunction(deps.win().ga || null);
  }

  /**
   * Function to determine whether event is eligible for gTag logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGtagEligible(deps) {
    return isFunction(deps.win().gtag || null);
  }
}
<commit_msg>add type annotation for win and ga/gtag<commit_after>/**
 * Copyright 2021 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {analyticsEventToGoogleAnalyticsEvent} from './event-type-mapping';
import {isFunction} from '../utils/types';

export class GoogleAnalyticsEventListener {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!./deps.DepsDef} deps */
    this.deps_ = deps;

    /** @private @const {!./client-event-manager.ClientEventManager} */
    this.eventManager_ = deps.eventManager();
  }

  /**
   * Start listening to client events
   */
  start() {
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   *  Listens for new events from the events manager and logs appropriate events to Google Analytics.
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @param {(!../api/client-event-manager-api.ClientEventParams|undefined)=} eventParams
   */
  handleClientEvent_(event, eventParams = undefined) {
    // Bail immediately if neither ga function (analytics.js) nor gtag function (gtag.js) exists in Window.
    if (
      !this.constructor.isGaEligible(this.deps_) &&
      !this.constructor.isGtagEligible(this.deps_)
    ) {
      return;
    }
    let subscriptionFlow = '';
    if (event.additionalParameters) {
      // additionalParameters isn't strongly typed so checking for both object and class notation.
      subscriptionFlow =
        event.additionalParameters.subscriptionFlow ||
        event.additionalParameters.getSubscriptionFlow();
    }
    let gaEvent = analyticsEventToGoogleAnalyticsEvent(
      event.eventType,
      subscriptionFlow
    );
    if (!gaEvent) {
      return;
    }

    const analyticsParams = eventParams?.googleAnalyticsParameters || {};
    gaEvent = {
      ...gaEvent,
      eventCategory: analyticsParams.event_category || gaEvent.eventCategory,
      eventLabel: analyticsParams.event_label || gaEvent.eventLabel,
    };

    /** @type {Window |{ga?: function(string, string, Object), gtag?: function(string, string, Object)}} */
    const win = this.deps_.win();
    // TODO(b/234825847): Remove it once universal analytics is deprecated in 2023.
    if (this.constructor.isGaEligible(this.deps_)) {
      const ga = win.ga;
      ga('send', 'event', gaEvent);
    }

    if (this.constructor.isGtagEligible(this.deps_)) {
      const gtag = win.gtag;
      const gtagEvent = {
        'event_category': gaEvent.eventCategory,
        'event_label': gaEvent.eventLabel,
        'non_interaction': gaEvent.nonInteraction,
        ...analyticsParams,
      };
      gtag('event', gaEvent.eventAction, gtagEvent);
    }
  }

  /**
   * Function to determine whether event is eligible for GA logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGaEligible(deps) {
    return isFunction(deps.win().ga || null);
  }

  /**
   * Function to determine whether event is eligible for gTag logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGtagEligible(deps) {
    return isFunction(deps.win().gtag || null);
  }
}
<|endoftext|>


<commit_before>/*global describe,it,element,by,takeScreenshot,expect,browser*/

describe("sap.m.MenuButton", function() {
	"use strict";

	browser.testrunner.currentSuite.meta.controlName = 'sap.m.MenuButton';

	it('has adequate initial width', function() {
		var oPage = element(by.id("page0")),
			oMenuButton4 = element(by.id("mb4"));

		oMenuButton4.click();

		expect(takeScreenshot(oPage)).toLookAs('menubutton_initial_width_disabled_items');
	});

	it('MenuButton correctly opens and focus a menu with enabled items', function() {
		var oPage = element(by.id("page0")),
			oMenuButton = element(by.id("mb-toolbar"));

		oMenuButton.click();

		expect(takeScreenshot(oPage)).toLookAs('menubutton_menu_items_enabled');
	});

	it('Menu button parts are visible and aligned', function() {
		var oPage = element(by.id("page0"));

		expect(takeScreenshot(oPage)).toLookAs('menu_buttons_in_page');
	});

	it('Change to Compact Mode', function() {
		var oSelect = element(by.id("density_select")),
			oCompactItem = element(by.id("item_compact")),
			oMenuButton3 = element(by.id("mb3"));

		oSelect.click();
		oCompactItem.click();
		expect(takeScreenshot(oMenuButton3)).toLookAs('menubutton_compact_initial_width');
	});

});
<commit_msg>[INTERNAL] sap.m.MenuButton: visual tests images does not shoot the same thing<commit_after>/*global describe,it,element,by,takeScreenshot,expect,browser*/

describe("sap.m.MenuButton", function() {
	"use strict";

	browser.testrunner.currentSuite.meta.controlName = 'sap.m.MenuButton';

	it('has adequate initial width', function() {
		var oPage = element(by.id("page0")),
			oMenuButton4 = element(by.id("mb4"));

		oMenuButton4.click();

		expect(takeScreenshot(oPage)).toLookAs('menubutton_initial_width_disabled_items');
	});

	it('MenuButton correctly opens and focus a menu with enabled items', function() {
		var oPage = element(by.id("page0")),
			oMenuButton = element(by.id("mb-toolbar"));

		oMenuButton.click();

		expect(takeScreenshot(oPage)).toLookAs('menubutton_menu_items_enabled');
	});

	it('Change to Compact Mode', function() {
		var oSelect = element(by.id("density_select")),
			oCompactItem = element(by.id("item_compact")),
			oMenuButton3 = element(by.id("mb3"));

		oSelect.click();
		oCompactItem.click();
		expect(takeScreenshot(oMenuButton3)).toLookAs('menubutton_compact_initial_width');
	});

});
<|endoftext|>


<commit_before>/*global describe,it,element,by,takeScreenshot,expect,browser*/

describe('sap.m.MultiInput', function() {
	"use strict";

	var bPhone = null;

	it("should load test page", function () {
		browser.executeScript(function () {
			return sap.ui.Device.system.phone;
		}).then(function (response) {
			bPhone = response;
		});

		expect(takeScreenshot()).toLookAs("initial");
	});

	//Initial Compact Mode
	it("should select Compact mode", function () {
		element(by.id("compactMode")).click();
		expect(takeScreenshot(element(by.id("page1")))).toLookAs("compact-mode");
		element(by.id("compactMode")).click();
	});

	//Single Line Mode
	it("should show on SingleLineMode", function () {
		expect(takeScreenshot(element(by.id("multiInputCustomValidator")))).toLookAs("multi-input-custom-validator");
	});

	//Single Line Mode selected
	it("should focus on SingleLineMode", function () {
		element(by.id("multiInputCustomValidator")).click();
		expect(takeScreenshot(element(by.id("multiInputCustomValidator")))).toLookAs("multi-input-custom-validator-selected");
	});

	//MultiInpuit not selected
	it("should show on multiInputCustomAsyncValidator tokens", function () {
		expect(takeScreenshot(element(by.id("multiInputCustomAsyncValidator")))).toLookAs("multi-input-custom-async-validator");
	});

	//MultiInpuit  selected
	it("should focus on multiInputCustomAsyncValidator tokens", function () {
		element(by.id("multiInputCustomAsyncValidator")).click();
		expect(takeScreenshot(element(by.id("multiInputCustomAsyncValidator")))).toLookAs("multi-input-custom-async-validator-slct");
	});

	//Multiple line enabled not  selected
	it("should show on Multi input enabled tokens", function () {
		expect(takeScreenshot(element(by.id("mI5")))).toLookAs("multi-input-line-not-selected");
	});

	//Multiple line enabled  selected
	it("should focus on Multi input enabled tokens", function () {
		element(by.id("mI5")).click();
		expect(takeScreenshot()).toLookAs("multi-input-line-selected");
		if (bPhone) {
			element(by.id("mI5-popup-closeButton")).click();
		}
	});

	//Multiinput warning
	it("should show on multiInput warning", function () {
		browser.executeScript('document.getElementById("mIWarning").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("mIWarning")))).toLookAs("multi-input-warning-not-selected");
		});
	});

	//Multiinput error
	it("should show on multiInput error", function () {
		expect(takeScreenshot(element(by.id("mIError")))).toLookAs("multi-input-error-not-selected");
	});

	//Multiinput success
	it("should show on multiInput success", function () {
		expect(takeScreenshot(element(by.id("mISuccess")))).toLookAs("multi-input-mISuccess-not-selected");
	});

	//Multiinput error selected
	it("should focus on multiInput error selected", function () {
		element(by.id("mIError")).click();
		expect(takeScreenshot(element(by.id("mIError")))).toLookAs("multi-input-error-selected");
	});

	//Multiinput error selected
	it("should show  multiInput multiInputNotEditable", function () {
		expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editalbe");
	});

	//Show multi input no placeholder
	it("should show   multiInput no placeholder", function () {
		expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editable");
	});

	//Show multi input with N-more and whole N-more label
	it("should show multiInput with N-more and reduced width", function () {
		browser.executeScript('document.getElementById("minWidthMI").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("minWidthMI")))).toLookAs("multi-input-min-width");
		});
	});

	//Multiinput read-only
	it("should show multiInput in read-only-state", function () {
		browser.executeScript('document.getElementById("multiInputReadOnlyInitial").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("multiInputReadOnlyInitial")))).toLookAs("multi-input-readonly-initial");
		});
	});

	//Show selected multi input no placeholder
	it("should show multiInput no placeholder", function () {
		browser.executeScript('document.getElementById("multiInputReadOnlyInitial").scrollIntoView()').then(function() {
			element(by.id("multiInputNotEditable")).click();
			expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editable-selected");
		});
	});

	it("multiinput should be in condensed mode", function() {
		browser.executeScript('document.getElementById("condensed-table").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("condensed-table")))).toLookAs("table-in-condensed-mode");
		});
	});
});<commit_msg>[INTERNAL] sap.m.MultiInput: Visual test adjusted<commit_after>/*global describe,it,element,by,takeScreenshot,expect,browser*/

describe('sap.m.MultiInput', function() {
	"use strict";

	var bPhone = null;

	it("should load test page", function () {
		browser.executeScript(function () {
			return sap.ui.Device.system.phone;
		}).then(function (response) {
			bPhone = response;
		});

		expect(takeScreenshot()).toLookAs("initial");
	});

	//Initial Compact Mode
	it("should select Compact mode", function () {
		element(by.id("compactMode")).click();
		expect(takeScreenshot(element(by.id("page1")))).toLookAs("compact-mode");
		element(by.id("compactMode")).click();
	});

	//Single Line Mode
	it("should show on SingleLineMode", function () {
		expect(takeScreenshot(element(by.id("multiInputCustomValidator")))).toLookAs("multi-input-custom-validator");
	});

	//Single Line Mode selected
	it("should focus on SingleLineMode", function () {
		element(by.id("multiInputCustomValidator")).click();
		expect(takeScreenshot(element(by.id("multiInputCustomValidator")))).toLookAs("multi-input-custom-validator-selected");
	});

	//MultiInpuit not selected
	it("should show on multiInputCustomAsyncValidator tokens", function () {
		expect(takeScreenshot(element(by.id("multiInputCustomAsyncValidator")))).toLookAs("multi-input-custom-async-validator");
	});

	//MultiInpuit  selected
	it("should focus on multiInputCustomAsyncValidator tokens", function () {
		element(by.id("multiInputCustomAsyncValidator")).click();
		expect(takeScreenshot(element(by.id("multiInputCustomAsyncValidator")))).toLookAs("multi-input-custom-async-validator-slct");
	});

	//Multiple line enabled not  selected
	it("should show on Multi input enabled tokens", function () {
		expect(takeScreenshot(element(by.id("mI5")))).toLookAs("multi-input-line-not-selected");
	});

	//Multiple line enabled  selected
	it("should focus on Multi input enabled tokens", function () {
		element(by.id("mI5")).click();
		expect(takeScreenshot()).toLookAs("multi-input-line-selected");
		if (bPhone) {
			element(by.id("mI5-popup-closeButton")).click();
		}
	});

	//Multiinput warning
	it("should show on multiInput warning", function () {
		browser.executeScript('document.getElementById("mIWarning").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("mIWarning")))).toLookAs("multi-input-warning-not-selected");
		});
	});

	//Multiinput error
	it("should show on multiInput error", function () {
		expect(takeScreenshot(element(by.id("mIError")))).toLookAs("multi-input-error-not-selected");
	});

	//Multiinput success
	it("should show on multiInput success", function () {
		expect(takeScreenshot(element(by.id("mISuccess")))).toLookAs("multi-input-mISuccess-not-selected");
	});

	//Multiinput error selected
	it("should focus on multiInput error selected", function () {
		element(by.id("mIError")).click();
		expect(takeScreenshot(element(by.id("mIError")))).toLookAs("multi-input-error-selected");
	});

	//Multiinput error selected
	it("should show  multiInput multiInputNotEditable", function () {
		expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editalbe");
	});

	//Show multi input no placeholder
	it("should show   multiInput no placeholder", function () {
		expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editable");
	});

	//Show multi input with N-more and whole N-more label
	it("should show multiInput with N-more and reduced width", function () {
		browser.executeScript('document.getElementById("minWidthMI").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("minWidthMI")))).toLookAs("multi-input-min-width");
		});
	});

	//Multiinput read-only
	it("should show multiInput in read-only-state", function () {
		browser.executeScript('document.getElementById("multiInputReadOnlyInitial").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("multiInputReadOnlyInitial")))).toLookAs("multi-input-readonly-initial");
		});
	});

	//Show selected multi input no placeholder
	it("should show multiInput no placeholder", function () {
		browser.executeScript('document.getElementById("multiInputNotEditable").scrollIntoView()').then(function() {
			element(by.id("multiInputNotEditable")).click();
			expect(takeScreenshot(element(by.id("multiInputNotEditable")))).toLookAs("multi-input-not-editable-selected");
		});
	});

	it("multiinput should be in condensed mode", function() {
		browser.executeScript('document.getElementById("condensed-table").scrollIntoView()').then(function() {
			expect(takeScreenshot(element(by.id("condensed-table")))).toLookAs("table-in-condensed-mode");
		});
	});
});<|endoftext|>


<commit_before>/*!
 * thought <https://github.com/nknapp/thought>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var path = require('path')

/**
 *
 * Create a spec that can be loaded with `customize` using the `load()` function.
 *
 * @param {String} workingDir the working directory of thought
 * @returns {Function} the Customize-Spec
 */
module.exports = function createSpec (workingDir) {
  return function thoughtSpec (customize) {
    return customize
      .registerEngine('handlebars', require('customize-engine-handlebars'))
      .merge({
        handlebars: {
          partials: path.join(__dirname, 'handlebars', 'partials'),
          templates: path.join(__dirname, 'handlebars', 'templates'),
          helpers: require.resolve('./handlebars/helpers.js'),
          data: {
            'package': require(path.resolve('package.json')),
            'workingDir': workingDir
          },
          preprocessor: require('./handlebars/preprocessor.js')
        }
      })
      .merge({
        handlebars: {
          partials: path.join(workingDir, '.thought', 'partials'),
          templates: path.join(workingDir, '.thought', 'templates'),
          helpers: path.join(workingDir, '.thought', 'handlebars-helpers,js')
        }
      })
      // .tap(console.log)

  }
}
<commit_msg>Load custom preprocessor from ".thought/hb-preprocessor.js" change name of custom helper file to ".thought/hb-helpers.js"<commit_after>/*!
 * thought <https://github.com/nknapp/thought>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var path = require('path')

/**
 *
 * Create a spec that can be loaded with `customize` using the `load()` function.
 *
 * @param {String} workingDir the working directory of thought
 * @returns {Function} the Customize-Spec
 */
module.exports = function createSpec (workingDir) {
  return function thoughtSpec (customize) {
    return customize
      .registerEngine('handlebars', require('customize-engine-handlebars'))
      .merge({
        handlebars: {
          partials: path.join(__dirname, 'handlebars', 'partials'),
          templates: path.join(__dirname, 'handlebars', 'templates'),
          helpers: require.resolve('./handlebars/helpers.js'),
          data: {
            'package': require(path.resolve('package.json')),
            'workingDir': workingDir
          },
          preprocessor: require('./handlebars/preprocessor.js')
        }
      })
      .merge({
        handlebars: {
          partials: path.join(workingDir, '.thought', 'partials'),
          templates: path.join(workingDir, '.thought', 'templates'),
          helpers: path.resolve(workingDir, '.thought', 'hb-helpers,js'),
          preprocessor: path.resolve(workingDir, '.thought','hb-preprocessor.js')
        }
      })
      // .tap(console.log)

  }
}
<|endoftext|>


<commit_before>'use strict';

var util = require('gulp-util');
var iff  = require('gulp-if');

exports.env = util.env.env || 'development';

exports.is = function (environments) {
  environments = Array.isArray(environments) ? environments : [].slice.call(arguments, 0);
  return environments.indexOf(exports.env) !== -1;
};

exports.isDev = exports.is('development');

exports.if = function (environments) {
  return iff.apply(null, [exports.is(environments)].concat([].slice.call(arguments, 1)));
};
exports.not = function (environments) {
  return iff.apply(null, [!exports.is(environments)].concat([].slice.call(arguments, 1)));
};
<commit_msg>documentation comment<commit_after>'use strict';

/*
utilities for acting on an --env flag
*/

var util = require('gulp-util');
var iff  = require('gulp-if');

exports.env = util.env.env || 'development';

exports.is = function (environments) {
  environments = Array.isArray(environments) ? environments : [].slice.call(arguments, 0);
  return environments.indexOf(exports.env) !== -1;
};

exports.isDev = exports.is('development');

exports.if = function (environments) {
  return iff.apply(null, [exports.is(environments)].concat([].slice.call(arguments, 1)));
};
exports.not = function (environments) {
  return iff.apply(null, [!exports.is(environments)].concat([].slice.call(arguments, 1)));
};
<|endoftext|>


<commit_before>NEWSBLUR.Welcome = Backbone.View.extend({

    el: '.NB-body-inner',
    flags: {},
    rotation: 0,

    events: {
        "click .NB-button-login" : "show_signin_form",
        "click .NB-button-tryout" : "show_tryout",
        "click .NB-welcome-header-caption" : "click_header_caption",
        "focus input" : "stop_rotation",
        "mouseenter .NB-welcome-header-caption" : "enter_header_caption",
        "mouseleave .NB-welcome-header-caption" : "leave_header_caption"
    },

    initialize: function() {
        this.start_rotation();
        this.debug_password_autocomplete();
        NEWSBLUR.reader.$s.$layout.hide();
    },

    // ==========
    // = Header =
    // ==========

    click_header_caption: function(e) {
        this.flags.on_signin = false;
        this.enter_header_caption(e);
    },

    enter_header_caption: function(e) {
        this.flags.on_header_caption = true;
        var $caption = $(e.currentTarget);

        if (this.flags.on_signin) return;

        if ($caption.hasClass('NB-welcome-header-caption-signin')) {
            this.flags.on_signin = true;
            this.show_signin_form();
        } else {
            var r = parseInt($caption.data('ss'), 10);
            this.rotate_screenshots(r);
        }
    },

    leave_header_caption: function(e) {
        var $caption = $(e.currentTarget);

        if ($caption.hasClass('NB-welcome-header-caption-signin')) {

        } else {
            this.flags.on_header_caption = false;
        }
    },

    start_rotation: function() {
        if (this.$('.NB-welcome-header-account').hasClass('NB-active')) {
            this.show_signin_form();
        }
        this.$('.NB-welcome-header-image img').eq(0).on('load', _.bind(function() {
            setInterval(_.bind(this.rotate_screenshots, this), 3000);
        }, this));
    },

    rotate_screenshots: function(force, callback) {
        if (this.flags.on_header_caption && _.isUndefined(force)) {
            return;
        }
        if (this.flags.on_signin && _.isUndefined(force)) {
            return;
        }

        var NUM_CAPTIONS = 3;
        var r = force ? force - 1 : (this.rotation + 1) % NUM_CAPTIONS;
        if (!force) {
            this.rotation += 1;
        }

        var $images = $('.NB-welcome-header-image').add('.NB-welcome-header-account');
        var $captions = $('.NB-welcome-header-caption');
        var $in_img = $images.eq(r);
        var $out_img = $images.not($in_img);
        var $in_caption = $captions.eq(r);
        var $out_caption = $captions.not($in_caption);

        $out_img.removeClass('NB-active');
        $in_img.addClass('NB-active');

        // $out_img.css({zIndex: 0}).stop(true).animate({
        //     bottom: -300,
        //     opacity: 0
        // }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400, complete: callback});
        // $in_img.css({zIndex: 1}).stop(true).animate({
        //     bottom: 0,
        //     opacity: 1
        // }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400});
        $out_caption.removeClass('NB-active');
        $in_caption.addClass('NB-active');
        callback && callback();
        if (r < 3) {
            this.$('input').blur();
        }
    },

    stop_rotation: function() {
        this.flags.on_signin = true;
    },

    show_signin_form: function() {
        var open = !NEWSBLUR.reader.flags['sidebar_closed'];
        this.hide_tryout();

        this.flags.on_header_caption = true;
        this.flags.on_signin = true;

        var add_url = $.getQueryString('add') || $.getQueryString('url');
        if (add_url) {
            this.$("input[name=next]").val("/?add=" + add_url);
        }

        this.$el.scrollTo(0, 500, {queue: false, easing: 'easeInOutQuint'});

        _.delay(_.bind(function() {
            this.rotate_screenshots(4, _.bind(function() {
                this.$('input[name=login-username]').focus();
            }, this));
        }, this), open ? 560 : 0);

    },

    show_tryout: function() {
        if (!NEWSBLUR.reader) return;

        if (!this.flags.loaded) {
            NEWSBLUR.reader.$s.$layout.layout().hide('west', true);
            NEWSBLUR.reader.$s.$layout.show();
            this.flags.loaded = true;
        }
        var open = NEWSBLUR.reader.toggle_sidebar();

        this.$('.NB-inner').animate({
            paddingLeft: open ? 240 : 0
        }, {
            queue: false,
            easing: 'easeInOutQuint',
            duration: 560
        });

        this.$('.NB-welcome-container')[open ? 'addClass' : 'removeClass']('NB-welcome-tryout');
    },

    hide_tryout: function() {
        if (!NEWSBLUR.reader) return;

        NEWSBLUR.reader.close_sidebar();

        this.$('.NB-inner').animate({
            paddingLeft: 0
        }, {
            queue: false,
            easing: 'easeInOutQuint',
            duration: 560
        });

        this.$('.NB-welcome-container').removeClass('NB-welcome-tryout');
    },

    debug_password_autocomplete: function() {
        this.$("input[name=login-username]").focus();
    }

});<commit_msg>Fixing login rotation.<commit_after>NEWSBLUR.Welcome = Backbone.View.extend({

    el: '.NB-body-inner',
    flags: {},
    rotation: 0,

    events: {
        "click .NB-button-login" : "show_signin_form",
        "click .NB-button-tryout" : "show_tryout",
        "click .NB-welcome-header-caption" : "click_header_caption",
        "focus input" : "stop_rotation",
        "mouseenter .NB-welcome-header-caption" : "enter_header_caption",
        "mouseleave .NB-welcome-header-caption" : "leave_header_caption"
    },

    initialize: function() {
        this.start_rotation();
        // this.debug_password_autocomplete();
        NEWSBLUR.reader.$s.$layout.hide();
    },

    // ==========
    // = Header =
    // ==========

    click_header_caption: function(e) {
        this.flags.on_signin = false;
        this.enter_header_caption(e);
    },

    enter_header_caption: function(e) {
        this.flags.on_header_caption = true;
        var $caption = $(e.currentTarget);

        if (this.flags.on_signin) return;

        if ($caption.hasClass('NB-welcome-header-caption-signin')) {
            this.flags.on_signin = true;
            this.show_signin_form();
        } else {
            var r = parseInt($caption.data('ss'), 10);
            this.rotate_screenshots(r);
        }
    },

    leave_header_caption: function(e) {
        var $caption = $(e.currentTarget);

        if ($caption.hasClass('NB-welcome-header-caption-signin')) {

        } else {
            this.flags.on_header_caption = false;
        }
    },

    start_rotation: function() {
        if (this.$('.NB-welcome-header-account').hasClass('NB-active')) {
            this.show_signin_form();
        }
        var $first_img = this.$('.NB-welcome-header-image img').eq(0);
        if ($first_img[0].complete) {
            setInterval(_.bind(this.rotate_screenshots, this), 3000);
        } else {
            $first_img.on('load', _.bind(function() {
                setInterval(_.bind(this.rotate_screenshots, this), 3000);
            }, this));
        }
    },

    rotate_screenshots: function(force, callback) {
        if (this.flags.on_header_caption && _.isUndefined(force)) {
            return;
        }
        if (this.flags.on_signin && _.isUndefined(force)) {
            return;
        }

        var NUM_CAPTIONS = 3;
        var r = force ? force - 1 : (this.rotation + 1) % NUM_CAPTIONS;
        if (!force) {
            this.rotation += 1;
        }

        var $images = $('.NB-welcome-header-image').add('.NB-welcome-header-account');
        var $captions = $('.NB-welcome-header-caption');
        var $in_img = $images.eq(r);
        var $out_img = $images.not($in_img);
        var $in_caption = $captions.eq(r);
        var $out_caption = $captions.not($in_caption);

        $out_img.removeClass('NB-active');
        $in_img.addClass('NB-active');

        // $out_img.css({zIndex: 0}).stop(true).animate({
        //     bottom: -300,
        //     opacity: 0
        // }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400, complete: callback});
        // $in_img.css({zIndex: 1}).stop(true).animate({
        //     bottom: 0,
        //     opacity: 1
        // }, {easing: 'easeInOutQuart', queue: false, duration: force ? 650 : 1400});
        $out_caption.removeClass('NB-active');
        $in_caption.addClass('NB-active');
        callback && callback();
        if (r < 3) {
            this.$('input').blur();
        }
    },

    stop_rotation: function() {
        this.flags.on_signin = true;
    },

    show_signin_form: function() {
        var open = !NEWSBLUR.reader.flags['sidebar_closed'];
        this.hide_tryout();

        this.flags.on_header_caption = true;
        this.flags.on_signin = true;

        var add_url = $.getQueryString('add') || $.getQueryString('url');
        if (add_url) {
            this.$("input[name=next]").val("/?add=" + add_url);
        }

        this.$el.scrollTo(0, 500, {queue: false, easing: 'easeInOutQuint'});

        _.delay(_.bind(function() {
            this.rotate_screenshots(4, _.bind(function() {
                this.$('input[name=login-username]').focus();
            }, this));
        }, this), open ? 560 : 0);

    },

    show_tryout: function() {
        if (!NEWSBLUR.reader) return;

        if (!this.flags.loaded) {
            NEWSBLUR.reader.$s.$layout.layout().hide('west', true);
            NEWSBLUR.reader.$s.$layout.show();
            this.flags.loaded = true;
        }
        var open = NEWSBLUR.reader.toggle_sidebar();

        this.$('.NB-inner').animate({
            paddingLeft: open ? 240 : 0
        }, {
            queue: false,
            easing: 'easeInOutQuint',
            duration: 560
        });

        this.$('.NB-welcome-container')[open ? 'addClass' : 'removeClass']('NB-welcome-tryout');
    },

    hide_tryout: function() {
        if (!NEWSBLUR.reader) return;

        NEWSBLUR.reader.close_sidebar();

        this.$('.NB-inner').animate({
            paddingLeft: 0
        }, {
            queue: false,
            easing: 'easeInOutQuint',
            duration: 560
        });

        this.$('.NB-welcome-container').removeClass('NB-welcome-tryout');
    },

    debug_password_autocomplete: function() {
        this.$("input[name=login-username]").focus();
    }

});<|endoftext|>


<commit_before>define({
  type: 'controller',
  definition: [
    '$scope', '$location', '$routeParams', '$log', 'tweetListHolder', '$timeout', '$filter',
    function ($scope, $location, $routeParams, $log, tweetListHolder, $timeout, $filter) {
      $scope.tweet = null;
      $scope.tweet2 = null;
      var _rotator;
      _listenerRegistered = false;

      var _setTweetVisibility = function () {
        $scope.noTweet1 = $scope.tweet == null;
        $scope.noTweet2 = $scope.tweet2 == null;
      }
      _setTweetVisibility();

      if (!_listenerRegistered) {
        _listenerRegistered = true;
        if (_rotator) {
          _rotator.stop();
        }
        $log.debug("register SearchStartListener");
        tweetListHolder.registerSearchStartListener(function () {
          $log.debug("dualTweetController SearchListener started");
          $scope.tweet = null;
          $scope.tweet2 = null;
          _setTweetVisibility();
          _rotator = new _Rotatator();
          _rotator.rotate();
        });
      }

      function _Rotatator() {
        var _enabled = true;
        var _readTime = 10000;

        this.rotate = function () {
          var rotateInternal;
          _rotateTweet1();
          $timeout(_rotateTweet2, _readTime / 2);
        };

        var _rotateTweet1 = function () {
          if (_enabled) {
            $log.debug("dualTweetController: refresh tweet1 (" + $filter('date')(new Date(), "hh:mm:ss.sss") + ")");
            $scope.tweet = tweetListHolder.getNextTweet();
            _setTweetVisibility();
            $timeout(_rotateTweet1, _readTime);
          }
        }

        var _rotateTweet2 = function () {
          if (_enabled) {
            $log.debug("dualTweetController: refresh tweet2 (" + $filter('date')(new Date(), "hh:mm:ss.sss") + ")");
            $scope.tweet2 = tweetListHolder.getNextTweet();
            _setTweetVisibility();
            $timeout(_rotateTweet2, _readTime);
          }
        }

        this.stop = function () {
          _enabled = false;
        }
      }
    }
  ]
});
<commit_msg>fixed rotator stopping function<commit_after>define({
  type: 'controller',
  definition: [
    '$scope', '$location', '$routeParams', '$log', 'tweetListHolder', '$timeout', '$filter',
    function ($scope, $location, $routeParams, $log, tweetListHolder, $timeout, $filter) {
      $scope.tweet = null;
      $scope.tweet2 = null;
      var _rotator;
      _listenerRegistered = false;

      var _setTweetVisibility = function () {
        $scope.noTweet1 = $scope.tweet == null;
        $scope.noTweet2 = $scope.tweet2 == null;
      }
      _setTweetVisibility();

      if (!_listenerRegistered) {
        _listenerRegistered = true;
        $log.debug("register SearchStartListener");
        tweetListHolder.registerSearchStartListener(function () {
          if (_rotator) {
            _rotator.stop();
          }
          $log.debug("dualTweetController SearchListener started");
          $scope.tweet = null;
          $scope.tweet2 = null;
          _setTweetVisibility();
          _rotator = new _Rotatator();
          _rotator.rotate();
        });
      }

      function _Rotatator() {
        var _enabled = true;
        var _readTime = 10000;

        this.rotate = function () {
          var rotateInternal;
          _rotateTweet1();
          $timeout(_rotateTweet2, _readTime / 2);
        };

        var _rotateTweet1 = function () {
          if (_enabled) {
            $log.debug("dualTweetController: refresh tweet1 (" + $filter('date')(new Date(), "hh:mm:ss.sss") + ")");
            $scope.tweet = tweetListHolder.getNextTweet();
            _setTweetVisibility();
            $timeout(_rotateTweet1, _readTime);
          }
        }

        var _rotateTweet2 = function () {
          if (_enabled) {
            $log.debug("dualTweetController: refresh tweet2 (" + $filter('date')(new Date(), "hh:mm:ss.sss") + ")");
            $scope.tweet2 = tweetListHolder.getNextTweet();
            _setTweetVisibility();
            $timeout(_rotateTweet2, _readTime);
          }
        }

        this.stop = function () {
          _enabled = false;
        }
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 *
 * Created by Arjun on 10/7/2014.
 */

var TIME_HIGHLIGHT = 1000;
var TIME_TRANSLATE = 1000;
var TIME_UNHIGHLIGHT = 100;
var TIME_BUCKET = 1000;
var TIME_TEXT_PER_WORD = 300;
var TIME_SET_VISIBILITY = 0;
var TIME_SWAP = 750;

function BoxedList(parent, start, nodeList) {
	this.nodeList = nodeList;
	this.nodeMap = {};
	this.parent = parent;
	this.buckets = new Set();
	this.isStart = start;

	var container = $("

");
	container.addClass(start ? "start" : "result");
	parent.elem.append(container);
	this.elem = $("

");
	container.find("table").append(this.elem);
}

BoxedList.prototype.addNode = function (node) {
	this.nodeList.append(node);
};

BoxedList.prototype.generateChildren = function () {
	for (var i = 0; i < this.nodeList.length; i++) {
		this.generateChildElement(this.nodeList[i]);
	}
};

BoxedList.prototype.generateChildElement = function (thisNode) {
	var childElem = $("	");
	this.nodeMap[thisNode.id] = childElem.find("span");
	this.nodeMap[thisNode.id].text(thisNode.value);
	this.elem.append(childElem);
};

BoxedList.prototype.animate = function (animationList, skipDelays) {
	var i = 0;
	var maxDelay = -1;
	var childEndLists = [];
	for (var j = 0; j < this.parent.children.length; j++) {
		childEndLists.push(this.parent.children[j].endList.nodeMap);
	}
	var childStartLists = [];
	for (var j = 0; j < this.parent.children.length; j++) {
		childStartLists.push(this.parent.children[j].startList.nodeMap);
	}
	var doAnim = function (boxedList) {
		// TODO: We probably shouldn't calculate this every time an animation occurs!! Maybe once when the animation starts?
		if (i >= animationList.length) {
			boxedList.animating = null;
			return;
		}
		switch (animationList[i].animationType) {
			case "highlight":
				//console.log(animationList[i]);
				var nodes = animationList[i].nodes;
				var color = animationList[i].color;
				for (var j = 0; j < nodes.length; j++) {
					for (var k = 0; k < childEndLists.length; k++) {
						if (childEndLists[k].hasOwnProperty(nodes[j].id)) {
							childEndLists[k][nodes[j].id].css("border-color", color);
						}
					}
				}
				maxDelay = Math.max(maxDelay, TIME_HIGHLIGHT);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : TIME_HIGHLIGHT, boxedList);
				break;
			case "unhighlight":
				var nodes = animationList[i].nodes;
				for (var j = 0; j < nodes.length; j++) {
					for (var k = 0; k < childEndLists.length; k++) {
						if (childEndLists[k].hasOwnProperty(nodes[j].id)) {
							childEndLists[k][nodes[j].id].css("border-color", "black");
						}
					}
				}
				maxDelay = Math.max(maxDelay, TIME_UNHIGHLIGHT);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : TIME_UNHIGHLIGHT, boxedList);
				break;
			case "translate":
				var source = animationList[i].sourceNode;
				var sourceElem = null;
				if(animationList[i].sourceCircle == -1) {
					if((animationList[i].sourceList == "start") == boxedList.isStart) {
						sourceElem = boxedList.nodeMap[source.id];
					}
					else {
						if(boxedList.isStart) {
							sourceElem = boxedList.parent.endList.nodeMap[animationList[i].sourceNode.id];
						}
						else {
							sourceElem = boxedList.parent.startList.nodeMap[source.id];
						}
					}
				}
				else {
					var search = animationList[i].sourceList == "start" ? childStartLists : childEndLists;
					if (search[animationList[i].sourceCircle].hasOwnProperty(source.id)) {
						sourceElem = search[animationList[i].sourceCircle][source.id];
					}
				}

				var dest = animationList[i].destNode;
				var destElem = null;
				if(animationList[i].destCircle == -1) {
					if((animationList[i].destList == "start") == boxedList.isStart) {
						destElem = boxedList.nodeMap[animationList[i].destNode.id];
					}
					else {
						if(boxedList.isStart) {
							destElem = boxedList.parent.endList.nodeMap[dest.id];
						}
						else {
							destElem = boxedList.parent.startList.nodeMap[dest.id];
						}
					}
				}
				else {
					var search = animationList[i].destList == "start" ? childStartLists : childEndLists;
					if (search[animationList[i].destCircle].hasOwnProperty(dest.id)) {
						destElem = search[animationList[i].destCircle][dest.id];
					}
				}

				var sourcePosition = offsetFrom(sourceElem, mainDiv);
				var ghost = $(sourceElem[0].outerHTML);
				ghost.addClass("ghost");
				ghost.css({
					position: "absolute",
					width: sourceElem.width(),
					height: sourceElem.height()
				}).css(sourcePosition);
				mainDiv.append(ghost);
				ghost.animate(offsetFrom(destElem, mainDiv), TIME_TRANSLATE, function () {
					ghost.remove();
				});

				maxDelay = Math.max(maxDelay, TIME_TRANSLATE);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : TIME_TRANSLATE, boxedList);
				break;
			case "bucket":
				animationList[i].addBuckets.forEach(function (e) {
					boxedList.buckets.add(e + "");
				});
				animationList[i].removeBuckets.forEach(function (e) {
					boxedList.buckets.delete(e + "");
				});
				boxedList.elem.find("td").removeClass("bucket start end");
				boxedList.buckets.forEach(function (e){
					var bucket = e.split(",");
					bucket[0] *= 1;
					bucket[1] *= 1;
					boxedList.elem.find("td").slice(bucket[0], bucket[1] + 1).addClass("bucket");
					boxedList.elem.find("td:nth-child(" + (1 + bucket[0]) + ")").addClass("start");
					boxedList.elem.find("td:nth-child(" + (1 + bucket[1]) + ")").addClass("end");
				});
				maxDelay = Math.max(maxDelay, TIME_BUCKET);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : TIME_BUCKET, boxedList);
				break;
			case "text":
				addConsoleCard(animationList[i].text, animationList[i].cardColor);
				var delay = TIME_TEXT_PER_WORD * animationList[i].text.split(" ").length;
				maxDelay = Math.max(maxDelay, delay);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : delay, boxedList);
				break;
			case "visibility":
				animationList[i].showRanges.forEach(function (e) {
					boxedList.elem.find("td").slice(e[0], e[1] + 1)
						.css("visibility", "initial");
				});
				animationList[i].hideRanges.forEach(function (e) {
					boxedList.elem.find("td").slice(e[0], e[1] + 1)
						.css("visibility", "hidden");
				});
				maxDelay = Math.max(maxDelay, TIME_SET_VISIBILITY);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : TIME_SET_VISIBILITY, boxedList);
				break;
			case "swap":
				var node1 = animationList[i].nodePair[0] + 1;
				var node2 = animationList[i].nodePair[1] + 1;
				if (node1 != node2) {
					var elem1 = boxedList.elem.find("td:nth-child(" + node1 + ") span");
					var elem2 = boxedList.elem.find("td:nth-child(" + node2 + ") span");
					var pos1 = offsetFrom(elem1, mainDiv);
					var pos2 = offsetFrom(elem2, mainDiv);
					var elemLeft = null, elemRight = null;
					var posLeft = null, posRight = null;
					if (pos1.left < pos2.left) {
						elemLeft = elem1;
						posLeft = pos1;
						elemRight = elem2;
						posRight = pos2;
					} else {
						elemLeft = elem2;
						posLeft = pos2;
						elemRight = elem1;
						posRight = pos1;
					}
					var ghostLeft = $(elemLeft[0].outerHTML);
					var ghostRight = $(elemRight[0].outerHTML);
					ghostLeft.addClass("ghost");
					ghostRight.addClass("ghost");
					ghostLeft.css({
						position: "absolute",
						width: elemLeft.width(),
						height: elemLeft.height()
					}).css(posLeft);
					ghostRight.css({
						position: "absolute",
						width: elemRight.width(),
						height: elemRight.height()
					}).css(posRight);
					mainDiv.append(ghostLeft);
					mainDiv.append(ghostRight);
					elemLeft.css("visibility", "hidden");
					elemRight.css("visibility", "hidden");
					var parentLeft = elemLeft.parent();
					var parentRight = elemRight.parent();
					parentLeft.append(elemRight);
					parentRight.append(elemLeft);
					ghostLeft.animate(posRight,{
						duration: TIME_SWAP,
						complete: function () {
							elemLeft.css("visibility", "initial");
							ghostLeft.remove();
						},
						step: function (now, fx) {
							ghostLeft.css("top", Math.sin(Math.PI * (now - fx.end) / (fx.start - fx.end)) * parentLeft.height() + posLeft.top);
						}
					});
					ghostRight.animate(posLeft,{
						duration: TIME_SWAP,
						complete: function () {
							elemRight.css("visibility", "initial");
							ghostRight.remove();
						},
						step: function (now, fx) {
							ghostRight.css("top", -Math.sin(Math.PI * (now - fx.end) / (fx.start - fx.end)) * parentRight.height() + posRight.top);
						}
					});
				}
				break;
			case "bundle":
				//console.log(animationList[i]);
				var delay = boxedList.animate(animationList[i].animations, true);
				//console.log(delay);
				boxedList.animating = setTimeout(doAnim, skipDelays ? 0 : delay, boxedList);
				maxDelay = Math.max(maxDelay, delay);
				break;
			default:
				break;
		}

		i++;
	};
	doAnim(this);
	return maxDelay;
};
<commit_msg>bundles are now simultaneous<commit_after>/**
 *
 * Created by Arjun on 10/7/2014.
 */

var TIME_HIGHLIGHT = 1000;
var TIME_TRANSLATE = 1000;
var TIME_UNHIGHLIGHT = 100;
var TIME_BUCKET = 1000;
var TIME_TEXT_PER_WORD = 300;
var TIME_SET_VISIBILITY = 0;
var TIME_SWAP = 750;

function BoxedList(parent, start, nodeList) {
	this.nodeList = nodeList;
	this.nodeMap = {};
	this.parent = parent;
	this.buckets = new Set();
	this.isStart = start;

	var container = $("

");
	container.addClass(start ? "start" : "result");
	parent.elem.append(container);
	this.elem = $("

");
	container.find("table").append(this.elem);
}

BoxedList.prototype.addNode = function (node) {
	this.nodeList.append(node);
};

BoxedList.prototype.generateChildren = function () {
	for (var i = 0; i < this.nodeList.length; i++) {
		this.generateChildElement(this.nodeList[i]);
	}
};

BoxedList.prototype.generateChildElement = function (thisNode) {
	var childElem = $("	");
	this.nodeMap[thisNode.id] = childElem.find("span");
	this.nodeMap[thisNode.id].text(thisNode.value);
	this.elem.append(childElem);
};

BoxedList.prototype.animate = function (animationList, skipDelays) {
	var i = 0;
	var maxDelay = -1;
	var childEndLists = [];
	for (var j = 0; j < this.parent.children.length; j++) {
		childEndLists.push(this.parent.children[j].endList.nodeMap);
	}
	var childStartLists = [];
	for (var j = 0; j < this.parent.children.length; j++) {
		childStartLists.push(this.parent.children[j].startList.nodeMap);
	}
	var doAnim = function (boxedList) {
		if (i >= animationList.length) {
			boxedList.animating = null;
			return;
		}
		var delay = 0;
		switch (animationList[i].animationType) {
			case "highlight":
				//console.log(animationList[i]);
				var nodes = animationList[i].nodes;
				var color = animationList[i].color;
				for (var j = 0; j < nodes.length; j++) {
					for (var k = 0; k < childEndLists.length; k++) {
						if (childEndLists[k].hasOwnProperty(nodes[j].id)) {
							childEndLists[k][nodes[j].id].css("border-color", color);
						}
					}
				}
				maxDelay = Math.max(maxDelay, TIME_HIGHLIGHT);
				delay = skipDelays ? 0 : TIME_HIGHLIGHT;
				break;
			case "unhighlight":
				var nodes = animationList[i].nodes;
				for (var j = 0; j < nodes.length; j++) {
					for (var k = 0; k < childEndLists.length; k++) {
						if (childEndLists[k].hasOwnProperty(nodes[j].id)) {
							childEndLists[k][nodes[j].id].css("border-color", "black");
						}
					}
				}
				maxDelay = Math.max(maxDelay, TIME_UNHIGHLIGHT);
				delay = skipDelays ? 0 : TIME_UNHIGHLIGHT;
				break;
			case "translate":
				var source = animationList[i].sourceNode;
				var sourceElem = null;
				if(animationList[i].sourceCircle == -1) {
					if((animationList[i].sourceList == "start") == boxedList.isStart) {
						sourceElem = boxedList.nodeMap[source.id];
					}
					else {
						if(boxedList.isStart) {
							sourceElem = boxedList.parent.endList.nodeMap[animationList[i].sourceNode.id];
						}
						else {
							sourceElem = boxedList.parent.startList.nodeMap[source.id];
						}
					}
				}
				else {
					var search = animationList[i].sourceList == "start" ? childStartLists : childEndLists;
					if (search[animationList[i].sourceCircle].hasOwnProperty(source.id)) {
						sourceElem = search[animationList[i].sourceCircle][source.id];
					}
				}

				var dest = animationList[i].destNode;
				var destElem = null;
				if(animationList[i].destCircle == -1) {
					if((animationList[i].destList == "start") == boxedList.isStart) {
						destElem = boxedList.nodeMap[animationList[i].destNode.id];
					}
					else {
						if(boxedList.isStart) {
							destElem = boxedList.parent.endList.nodeMap[dest.id];
						}
						else {
							destElem = boxedList.parent.startList.nodeMap[dest.id];
						}
					}
				}
				else {
					var search = animationList[i].destList == "start" ? childStartLists : childEndLists;
					if (search[animationList[i].destCircle].hasOwnProperty(dest.id)) {
						destElem = search[animationList[i].destCircle][dest.id];
					}
				}

				var sourcePosition = offsetFrom(sourceElem, mainDiv);
				var ghost = $(sourceElem[0].outerHTML);
				ghost.addClass("ghost");
				ghost.css({
					position: "absolute",
					width: sourceElem.width(),
					height: sourceElem.height()
				}).css(sourcePosition);
				mainDiv.append(ghost);
				ghost.animate(offsetFrom(destElem, mainDiv), TIME_TRANSLATE, function () {
					ghost.remove();
				});

				maxDelay = Math.max(maxDelay, TIME_TRANSLATE);
				delay = skipDelays ? 0 : TIME_TRANSLATE;
				break;
			case "bucket":
				animationList[i].addBuckets.forEach(function (e) {
					boxedList.buckets.add(e + "");
				});
				animationList[i].removeBuckets.forEach(function (e) {
					boxedList.buckets.delete(e + "");
				});
				boxedList.elem.find("td").removeClass("bucket start end");
				boxedList.buckets.forEach(function (e){
					var bucket = e.split(",");
					bucket[0] *= 1;
					bucket[1] *= 1;
					boxedList.elem.find("td").slice(bucket[0], bucket[1] + 1).addClass("bucket");
					boxedList.elem.find("td:nth-child(" + (1 + bucket[0]) + ")").addClass("start");
					boxedList.elem.find("td:nth-child(" + (1 + bucket[1]) + ")").addClass("end");
				});
				maxDelay = Math.max(maxDelay, TIME_BUCKET);
				delay = skipDelays ? 0 : TIME_BUCKET;
				break;
			case "text":
				addConsoleCard(animationList[i].text, animationList[i].cardColor);
				delay = TIME_TEXT_PER_WORD * animationList[i].text.split(" ").length;
				maxDelay = Math.max(maxDelay, delay);
				delay = skipDelays ? 0 : delay;
				break;
			case "visibility":
				animationList[i].showRanges.forEach(function (e) {
					boxedList.elem.find("td").slice(e[0], e[1] + 1)
						.css("visibility", "initial");
				});
				animationList[i].hideRanges.forEach(function (e) {
					boxedList.elem.find("td").slice(e[0], e[1] + 1)
						.css("visibility", "hidden");
				});
				maxDelay = Math.max(maxDelay, TIME_SET_VISIBILITY);
				delay = skipDelays ? 0 : TIME_SET_VISIBILITY;
				break;
			case "swap":
				var node1 = animationList[i].nodePair[0] + 1;
				var node2 = animationList[i].nodePair[1] + 1;
				if (node1 != node2) {
					var elem1 = boxedList.elem.find("td:nth-child(" + node1 + ") span");
					var elem2 = boxedList.elem.find("td:nth-child(" + node2 + ") span");
					var pos1 = offsetFrom(elem1, mainDiv);
					var pos2 = offsetFrom(elem2, mainDiv);
					var elemLeft = null, elemRight = null;
					var posLeft = null, posRight = null;
					if (pos1.left < pos2.left) {
						elemLeft = elem1;
						posLeft = pos1;
						elemRight = elem2;
						posRight = pos2;
					} else {
						elemLeft = elem2;
						posLeft = pos2;
						elemRight = elem1;
						posRight = pos1;
					}
					var ghostLeft = $(elemLeft[0].outerHTML);
					var ghostRight = $(elemRight[0].outerHTML);
					ghostLeft.addClass("ghost");
					ghostRight.addClass("ghost");
					ghostLeft.css({
						position: "absolute",
						width: elemLeft.width(),
						height: elemLeft.height()
					}).css(posLeft);
					ghostRight.css({
						position: "absolute",
						width: elemRight.width(),
						height: elemRight.height()
					}).css(posRight);
					mainDiv.append(ghostLeft);
					mainDiv.append(ghostRight);
					elemLeft.css("visibility", "hidden");
					elemRight.css("visibility", "hidden");
					var parentLeft = elemLeft.parent();
					var parentRight = elemRight.parent();
					parentLeft.append(elemRight);
					parentRight.append(elemLeft);
					ghostLeft.animate(posRight,{
						duration: TIME_SWAP,
						complete: function () {
							elemLeft.css("visibility", "initial");
							ghostLeft.remove();
						},
						step: function (now, fx) {
							ghostLeft.css("top", Math.sin(Math.PI * (now - fx.end) / (fx.start - fx.end)) * parentLeft.height() + posLeft.top);
						}
					});
					ghostRight.animate(posLeft,{
						duration: TIME_SWAP,
						complete: function () {
							elemRight.css("visibility", "initial");
							ghostRight.remove();
						},
						step: function (now, fx) {
							ghostRight.css("top", -Math.sin(Math.PI * (now - fx.end) / (fx.start - fx.end)) * parentRight.height() + posRight.top);
						}
					});
					maxDelay = Math.max(maxDelay, TIME_SWAP);
					delay = skipDelays ? 0 : TIME_SWAP;
				}
				break;
			case "bundle":
				//console.log(animationList[i]);
				delay = boxedList.animate(animationList[i].animations, true);
				//console.log(delay);
				maxDelay = Math.max(maxDelay, delay);
				delay = skipDelays ? 0 : delay;
				break;
			default:
				break;
		}
		i++;
		if (delay > 0) {
			boxedList.animating = setTimeout(doAnim, delay, boxedList);
		} else {
			doAnim(boxedList);
		}
	};
	doAnim(this);
	return maxDelay;
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DOM = {
  /** Instantiate FOAM Objects in a document. **/
  init: function(X) {
    if ( ! X.document.FOAM_OBJECTS ) X.document.FOAM_OBJECTS = {};

    var fs = X.document.querySelectorAll('foam');
    var models = [];
    for ( var i = 0 ; i < fs.length ; i++ ) {
      var e = fs[i];
      FOAM.lookup(e.getAttribute('view'), X);
      FOAM.lookup(e.getAttribute('model'), X);
      if ( e.getAttribute('view') ) models.push(arequire(e.getAttribute('view')));
      if ( e.getAttribute('model') ) models.push(arequire(e.getAttribute('model')));
    }
    for ( var key in USED_MODELS ) {
      models.push(arequire(key));
    }

    atime('DOMInit', aseq(apar.apply(null, models), function(ret) {
      for ( var i = 0 ; i < fs.length ; i++ ) {
        var e = fs[i];
        // Check that the node is still in the DOM
        // (It won't be if it was the child of another FOAM tag.)
        var node = e;
        var body = X.document.body;
        while ( node && node !== body ) node = node.parentNode;
        if ( node ) {
          this.initElement(e, X, X.document);
          e.innerHTML = '';
        }
        ret();
      }
    }.bind(this)))();
  },

  initElementChildren: function(e, X) {
    var a = [];

    for ( var i = 0 ; i < e.children.length ; i++ ) {
      var c = e.children[i];

      if ( c.tagName === 'FOAM' ) {
        a.push(DOM.initElement(c, X));
      }
    }

    return a;
  },

  /**
   * opt_document -- if supplied the object's view will be added to the document.
   **/
  initElement: function(e, X, opt_document) {
    arequire('foam.ui.FoamTagView')(function(t) {
      foam.ui.FoamTagView.create({ element: e }, X);
    });
  },

  // TODO(kgr): remove this when the FoamTagView is fully tested
  xxxinitElement: function(e, X, opt_document) {
    // If was a sub-object for an object that has already been displayed,
    // then it will no longer be in the DOM and doesn't need to be shown.
    if ( opt_document && ! opt_document.body.contains(e) ) return;

    var modelName = e.getAttribute('model');
    var model = FOAM.lookup(modelName, X);

    if ( ! model ) {
      console.error('Unknown Model: ', modelName);
      e.outerHTML = 'Unknown Model: ' + modelName;
      return;
    }

    // This is because of a bug that the model.properties isn't populated
    // with the parent model's properties until after the prototype is
    // created.  TODO: remove after FO
    model.getPrototype();

    var obj = model.create(undefined, X);
    obj.fromElement(e);

    var onLoad = e.getAttribute('oninit');
    if ( onLoad ) Function(onLoad).bind(obj)();

    if ( opt_document ) {
      var viewName = e.getAttribute('view');
      var view;
      if ( viewName ) {
        var viewModel = FOAM.lookup(viewName, X);
        view = viewModel.create({ model: model, data: obj });
      }
      else if ( View.isInstance(obj) || ( 'CView' in GLOBAL && CView.isInstance(obj) ) ) {
        view = obj;
      } else if ( obj.toView_ ) {
        view = obj.toView_();
      } else {
        var a = e.getAttribute('showActions');
        var showActions = a ?
            a.equalsIC('y') || a.equalsIC('yes') || a.equalsIC('true') || a.equalsIC('t') :
            true ;

        view = X.foam.ui.DetailView.create({ model: model, data: obj, showActions: showActions })
      }

      if ( e.id ) opt_document.FOAM_OBJECTS[e.id] = obj;
      obj.view_ = view;
      e.outerHTML = view.toHTML();
      view.initHTML();
    }

    return obj;
  },

  setClass: function(e, className, opt_enabled) {
    var oldClassName = e.className || '';
    var enabled = opt_enabled === undefined ? true : opt_enabled;
    e.className = oldClassName.replace(' ' + className, '').replace(className, '');
    if ( enabled ) e.className = e.className + ' ' + className;
  }
};

window &&
  window.addEventListener &&
  window.addEventListener('load', function() { DOM.init(X); }, false);

// TODO: document and make non-global
/** Convert a style size to an Int.  Ex. '10px' to 10. **/
function toNum(p) { return p.replace ? parseInt(p.replace('px','')) : p; };

// TODO(kgr): replace all instances of DomValue with new modelled DOMValue.
var DomValue = {
  DEFAULT_EVENT:    'change',
  DEFAULT_PROPERTY: 'value',

  create: function(element, opt_event, opt_property) {
    if ( ! element ) {
      throw "Missing Element in DomValue";
    }

    return {
      __proto__: this,
      element:   element,
      event:     opt_event    || this.DEFAULT_EVENT,
      property:  opt_property || this.DEFAULT_PROPERTY };
  },

  setElement: function ( element ) { this.element = element; },

  get: function() { return this.element[this.property]; },

  set: function(value) {
    if ( this.element[this.property] !== value )
      this.element[this.property] = value;
  },

  addListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.addEventListener(this.event, listener, false);
    } catch (x) {
    }
  },

  removeListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.removeEventListener(this.event, listener, false);
    } catch (x) {
      // could be that the element has been removed
    }
  },

  toString: function() {
    return "DomValue(" + this.event + ", " + this.property + ")";
  }
};

CLASS({
  name: 'DOMValue',

  properties: [
    {
      name: 'element',
      required: true
    },
    {
      name: 'property',
      defaultValue: 'value'
    },
    {
      name: 'event',
      defaultValue: 'change'
    },
    {
      name: 'value',
      postSet: function(_, value) { this.element[this.property] = value; }
    },
    {
      name: 'firstListener_',
      defaultValue: true
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.value = this.element[this.property];
    },

    get: function() { return this.value; },

    set: function(value) { this.value = value; },

    addListener: function(listener) {
      if ( this.firstListener_ ) {
        if ( this.event ) {
          this.element.addEventListener(
            this.event,
            function() { debugger; /* TODO */ },
            false);
        }

        this.firstListener_ = false;
      }
      this.value$.addListener(listener);
    },

    removeListener: function(listener) {
      this.value$.removeListener(listener);
    },

    toString: function() {
      return 'DOMValue(' + this.event + ', ' + this.property + ')';
    }
  }
});

CLASS({
  name: 'WindowHashValue',

  properties: [
    {
      name: 'window',
      defaultValueFn: function() { return this.X.window; }
    }
  ],

  methods: {
    get: function() { return this.window.location.hash ? this.window.location.hash.substring(1) : ''; },

    set: function(value) { this.window.location.hash = value; },

    addListener: function(listener) {
      this.window.addEventListener('hashchange', listener, false);
    },

    removeListener: function(listener) {
      this.window.removeEventListener('hashchange', listener, false);
    },

    toString: function() { return "WindowHashValue(" + this.get() + ")"; }
  }
});

X.memento = X.WindowHashValue.create();
<commit_msg>CView(1) to implements toView_() to work with FoamTagView.<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DOM = {
  /** Instantiate FOAM Objects in a document. **/
  init: function(X) {
    if ( ! X.document.FOAM_OBJECTS ) X.document.FOAM_OBJECTS = {};

    var fs = X.document.querySelectorAll('foam');
    var models = [];
    for ( var i = 0 ; i < fs.length ; i++ ) {
      var e = fs[i];
      FOAM.lookup(e.getAttribute('view'), X);
      FOAM.lookup(e.getAttribute('model'), X);
      if ( e.getAttribute('view') ) models.push(arequire(e.getAttribute('view')));
      if ( e.getAttribute('model') ) models.push(arequire(e.getAttribute('model')));
    }
    for ( var key in USED_MODELS ) {
      models.push(arequire(key));
    }

    atime('DOMInit', aseq(apar.apply(null, models), function(ret) {
      for ( var i = 0 ; i < fs.length ; i++ ) {
        var e = fs[i];
        // Check that the node is still in the DOM
        // (It won't be if it was the child of another FOAM tag.)
        var node = e;
        var body = X.document.body;
        while ( node && node !== body ) node = node.parentNode;
        if ( node ) {
          this.initElement(e, X, X.document);
          e.innerHTML = '';
        }
        ret();
      }
    }.bind(this)))();
  },

  initElementChildren: function(e, X) {
    var a = [];

    for ( var i = 0 ; i < e.children.length ; i++ ) {
      var c = e.children[i];

      if ( c.tagName === 'FOAM' ) {
        a.push(DOM.initElement(c, X));
      }
    }

    return a;
  },

  /**
   * opt_document -- if supplied the object's view will be added to the document.
   **/
  initElement: function(e, X, opt_document) {
    arequire('foam.ui.FoamTagView')(function(t) {
      foam.ui.FoamTagView.create({ element: e }, X);
    });
  },

  setClass: function(e, className, opt_enabled) {
    var oldClassName = e.className || '';
    var enabled = opt_enabled === undefined ? true : opt_enabled;
    e.className = oldClassName.replace(' ' + className, '').replace(className, '');
    if ( enabled ) e.className = e.className + ' ' + className;
  }
};

window &&
  window.addEventListener &&
  window.addEventListener('load', function() { DOM.init(X); }, false);

// TODO: document and make non-global
/** Convert a style size to an Int.  Ex. '10px' to 10. **/
function toNum(p) { return p.replace ? parseInt(p.replace('px','')) : p; };

// TODO(kgr): replace all instances of DomValue with new modelled DOMValue.
var DomValue = {
  DEFAULT_EVENT:    'change',
  DEFAULT_PROPERTY: 'value',

  create: function(element, opt_event, opt_property) {
    if ( ! element ) {
      throw "Missing Element in DomValue";
    }

    return {
      __proto__: this,
      element:   element,
      event:     opt_event    || this.DEFAULT_EVENT,
      property:  opt_property || this.DEFAULT_PROPERTY };
  },

  setElement: function ( element ) { this.element = element; },

  get: function() { return this.element[this.property]; },

  set: function(value) {
    if ( this.element[this.property] !== value )
      this.element[this.property] = value;
  },

  addListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.addEventListener(this.event, listener, false);
    } catch (x) {
    }
  },

  removeListener: function(listener) {
    if ( ! this.event ) return;
    try {
      this.element.removeEventListener(this.event, listener, false);
    } catch (x) {
      // could be that the element has been removed
    }
  },

  toString: function() {
    return "DomValue(" + this.event + ", " + this.property + ")";
  }
};

CLASS({
  name: 'DOMValue',

  properties: [
    {
      name: 'element',
      required: true
    },
    {
      name: 'property',
      defaultValue: 'value'
    },
    {
      name: 'event',
      defaultValue: 'change'
    },
    {
      name: 'value',
      postSet: function(_, value) { this.element[this.property] = value; }
    },
    {
      name: 'firstListener_',
      defaultValue: true
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.value = this.element[this.property];
    },

    get: function() { return this.value; },

    set: function(value) { this.value = value; },

    addListener: function(listener) {
      if ( this.firstListener_ ) {
        if ( this.event ) {
          this.element.addEventListener(
            this.event,
            function() { debugger; /* TODO */ },
            false);
        }

        this.firstListener_ = false;
      }
      this.value$.addListener(listener);
    },

    removeListener: function(listener) {
      this.value$.removeListener(listener);
    },

    toString: function() {
      return 'DOMValue(' + this.event + ', ' + this.property + ')';
    }
  }
});

CLASS({
  name: 'WindowHashValue',

  properties: [
    {
      name: 'window',
      defaultValueFn: function() { return this.X.window; }
    }
  ],

  methods: {
    get: function() { return this.window.location.hash ? this.window.location.hash.substring(1) : ''; },

    set: function(value) { this.window.location.hash = value; },

    addListener: function(listener) {
      this.window.addEventListener('hashchange', listener, false);
    },

    removeListener: function(listener) {
      this.window.removeEventListener('hashchange', listener, false);
    },

    toString: function() { return "WindowHashValue(" + this.get() + ")"; }
  }
});

X.memento = X.WindowHashValue.create();
<|endoftext|>