<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/

/* jshint node:true, bitwise:true, undef:true, trailing:true, quotmark:true,
          indent:4, unused:vars, latedef:nofunc,
          laxcomma:true
*/

var path = require('path'),
    fs = require('fs'),
    help = require('./help'),
    nopt,
    _,
    updateNotifier,
    pkg = require('../package.json'),
    telemetry = require('./telemetry'),
    Q = require('q');

var cordova_lib = require('cordova-lib'),
    CordovaError = cordova_lib.CordovaError,
    cordova = cordova_lib.cordova,
    events = cordova_lib.events,
    logger = require('cordova-common').CordovaLogger.get();

/*
 * init
 *
 * initializes nopt and underscore
 * nopt and underscore are require()d in try-catch below to print a nice error
 * message if one of them is not installed.
 */
function init() {
    try {
        nopt = require('nopt');
        _ = require('underscore');
        updateNotifier = require('update-notifier');
    } catch (e) {
        console.error(
            'Please run npm install from this directory:\n\t' +
            path.dirname(__dirname)
        );
        process.exit(2);
    }
}

function checkForUpdates() {
    try {
        // Checks for available update and returns an instance
        var notifier = updateNotifier({
            pkg: pkg
        });

        // Notify using the built-in convenience method
        notifier.notify();
    } catch (e) {
        // https://issues.apache.org/jira/browse/CB-10062
        if (e && e.message && /EACCES/.test(e.message)) {
            console.log('Update notifier was not able to access the config file.\n' +
                'You may grant permissions to the file: \'sudo chmod 744 ~/.config/configstore/update-notifier-cordova.json\'');
        } else {
            throw e;
        }
    }
}

var shouldCollectTelemetry = false;
module.exports = function (inputArgs, cb) {

    /**
     * mainly used for testing.
     */
    cb = cb || function(){};

    init();

    // If no inputArgs given, use process.argv.
    inputArgs = inputArgs || process.argv;
    var cmd = inputArgs[2]; // e.g: inputArgs= 'node cordova run ios'
    var subcommand = getSubCommand(inputArgs, cmd);
    var isTelemetryCmd = (cmd === 'telemetry');

    // ToDO: Move nopt-based parsing of args up here
    if(cmd === '--version' || cmd === '-v') {
        cmd = 'version';
    } else if(!cmd || cmd === '--help' || cmd === 'h') {
        cmd = 'help';
    }

    Q().then(function() {

        /**
         * Skip telemetry prompt if:
         * - CI environment variable is present
         * - Command is run with `--no-telemetry` flag
         * - Command ran is: `cordova telemetry on | off | ...`
         */

        if(telemetry.isCI(process.env) || telemetry.isNoTelemetryFlag(inputArgs)) {
            return Q(false);
        }

        /**
         * We shouldn't prompt for telemetry if user issues a command of the form: `cordova telemetry on | off | ...x`
         * Also, if the user has already been prompted and made a decision, use his saved answer
         */
        if(isTelemetryCmd) {
            var isOptedIn = telemetry.isOptedIn();
            return handleTelemetryCmd(subcommand, isOptedIn);
        }

        if(telemetry.hasUserOptedInOrOut()) {
            return Q(telemetry.isOptedIn());
        }

        /**
         * Otherwise, prompt user to opt-in or out
         * Note: the prompt is shown for 30 seconds. If no choice is made by that time, User is considered to have opted out.
         */
        return telemetry.showPrompt();
    }).then(function (collectTelemetry) {
        shouldCollectTelemetry = collectTelemetry;
        if(isTelemetryCmd) {
            return Q();
        }
        return cli(inputArgs);
    }).then(function () {
        if (shouldCollectTelemetry && !isTelemetryCmd) {
            telemetry.track(cmd, subcommand, 'successful');
        }
        // call cb with error as arg if something failed
        cb(null);
    }).fail(function (err) {
        if (shouldCollectTelemetry && !isTelemetryCmd) {
            telemetry.track(cmd, subcommand, 'unsuccessful');
        }
        // call cb with error as arg if something failed
        cb(err);
        throw err;
    }).done();
};

function getSubCommand(args, cmd) {
    if(cmd === 'platform' || cmd === 'platforms' || cmd === 'plugin' || cmd === 'plugins' || cmd === 'telemetry') {
        return args[3]; // e.g: args='node cordova platform rm ios', 'node cordova telemetry on'
    }
    return null;
}

function handleTelemetryCmd(subcommand, isOptedIn) {

    if (subcommand !== 'on' && subcommand !== 'off') {
        logger.subscribe(events);
        return help(['telemetry']);
    }

    var turnOn = subcommand === 'on' ? true : false;
    var cmdSuccess = true;

    // turn telemetry on or off
    try {
        if (turnOn) {
            telemetry.turnOn();
            console.log("Thanks for opting into telemetry to help us improve cordova.");
        } else {
            telemetry.turnOff();
            console.log("You have been opted out of telemetry. To change this, run: cordova telemetry on.");
        }
    } catch (ex) {
        cmdSuccess = false;
    }

    // track or not track ?, that is the question

    if (!turnOn) {
        // Always track telemetry opt-outs (whether user opted out or not!)
        telemetry.track('telemetry', 'off', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful': 'unsuccessful');
        return Q();
    }

    if(isOptedIn) {
        telemetry.track('telemetry', 'on', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful' : 'unsuccessful');
    }

    return Q();
}

function cli(inputArgs) {
    // When changing command line arguments, update doc/help.txt accordingly.
    var knownOpts =
        { 'verbose' : Boolean
        , 'version' : Boolean
        , 'help' : Boolean
        , 'silent' : Boolean
        , 'experimental' : Boolean
        , 'noregistry' : Boolean
        , 'nohooks': Array
        , 'shrinkwrap' : Boolean
        , 'copy-from' : String
        , 'link-to' : path
        , 'searchpath' : String
        , 'variable' : Array
        , 'link': Boolean
        , 'force': Boolean
        // Flags to be passed to `cordova build/run/emulate`
        , 'debug' : Boolean
        , 'release' : Boolean
        , 'archs' : String
        , 'device' : Boolean
        , 'emulator': Boolean
        , 'target' : String
        , 'browserify': Boolean
        , 'noprepare': Boolean
        , 'fetch': Boolean
        , 'nobuild': Boolean
        , 'list': Boolean
        , 'buildConfig' : String
        , 'template' : String
        };

    var shortHands =
        { 'd' : '--verbose'
        , 'v' : '--version'
        , 'h' : '--help'
        , 'src' : '--copy-from'
        , 't' : '--template'
        };

    checkForUpdates();

    var args = nopt(knownOpts, shortHands, inputArgs);

    // For CordovaError print only the message without stack trace unless we
    // are in a verbose mode.
    process.on('uncaughtException', function(err) {
        logger.error(err);
        // Don't send exception details, just send that it happened
        if(shouldCollectTelemetry) {
            telemetry.track('uncaughtException');
        }
        process.exit(1);
    });

    logger.subscribe(events);

    if (args.silent) {
        logger.setLevel('error');
    }

    if (args.verbose) {
        logger.setLevel('verbose');
    }

    var cliVersion = require('../package').version;
    // TODO: Use semver.prerelease when it gets released
    var usingPrerelease = /-nightly|-dev$/.exec(cliVersion);
    if (args.version || usingPrerelease) {
        var libVersion = require('cordova-lib/package').version;
        var toPrint = cliVersion;
        if (cliVersion != libVersion || usingPrerelease) {
            toPrint += ' (cordova-lib@' + libVersion + ')';
        }

        if (args.version) {
            logger.results(toPrint);
            return Q();
        } else {
            // Show a warning and continue
            logger.warn('Warning: using prerelease version ' + toPrint);
        }
    }

    // If there were arguments protected from nopt with a double dash, keep
    // them in unparsedArgs. For example:
    // cordova build ios -- --verbose --whatever
    // In this case "--verbose" is not parsed by nopt and args.vergbose will be
    // false, the unparsed args after -- are kept in unparsedArgs and can be
    // passed downstream to some scripts invoked by Cordova.
    var unparsedArgs = [];
    var parseStopperIdx =  args.argv.original.indexOf('--');
    if (parseStopperIdx != -1) {
        unparsedArgs = args.argv.original.slice(parseStopperIdx + 1);
    }

    // args.argv.remain contains both the undashed args (like platform names)
    // and whatever unparsed args that were protected by " -- ".
    // "undashed" stores only the undashed args without those after " -- " .
    var remain = args.argv.remain;
    var undashed = remain.slice(0, remain.length - unparsedArgs.length);
    var cmd = undashed[0];
    var subcommand;
    var msg;
    var known_platforms = Object.keys(cordova_lib.cordova_platforms);

    if ( !cmd || cmd == 'help' || args.help ) {
        if (!args.help && remain[0] == 'help') {
            remain.shift();
        }
        return help(remain);
    }

    if ( !cordova.hasOwnProperty(cmd) ) {
        msg =
            'Cordova does not know ' + cmd + '; try `' + cordova_lib.binname +
            ' help` for a list of all the available commands.';
        throw new CordovaError(msg);
    }

    var opts = {
        platforms: [],
        options: [],
        verbose: args.verbose || false,
        silent: args.silent || false,
        browserify: args.browserify || false,
        fetch: args.fetch || false,
        nohooks: args.nohooks || [],
        searchpath : args.searchpath
    };

    if (cmd == 'emulate' || cmd == 'build' || cmd == 'prepare' || cmd == 'compile' || cmd == 'run' || cmd === 'clean') {
        // All options without dashes are assumed to be platform names
        opts.platforms = undashed.slice(1);
        var badPlatforms = _.difference(opts.platforms, known_platforms);
        if( !_.isEmpty(badPlatforms) ) {
            msg = 'Unknown platforms: ' + badPlatforms.join(', ');
            throw new CordovaError(msg);
        }

        // Pass nopt-parsed args to PlatformApi through opts.options
        opts.options = args;
        opts.options.argv = unparsedArgs;

        if (cmd === 'run' && args.list && cordova.raw.targets) {
            return cordova.raw.targets.call(null, opts);
        }

        return cordova.raw[cmd].call(null, opts);
    } else if (cmd === 'requirements') {
        // All options without dashes are assumed to be platform names
        opts.platforms = undashed.slice(1);
        var badPlatforms = _.difference(opts.platforms, known_platforms);
        if( !_.isEmpty(badPlatforms) ) {
            msg = 'Unknown platforms: ' + badPlatforms.join(', ');
            throw new CordovaError(msg);
        }

        return cordova.raw[cmd].call(null, opts.platforms)
            .then(function(platformChecks) {

                var someChecksFailed = Object.keys(platformChecks).map(function(platformName) {
                    events.emit('log', '\nRequirements check results for ' + platformName + ':');
                    var platformCheck = platformChecks[platformName];
                    if (platformCheck instanceof CordovaError) {
                        events.emit('warn', 'Check failed for ' + platformName + ' due to ' + platformCheck);
                        return true;
                    }

                    var someChecksFailed = false;
                    platformCheck.forEach(function(checkItem) {
                        var checkSummary = checkItem.name + ': ' +
                            (checkItem.installed ? 'installed ' : 'not installed ') +
                            (checkItem.metadata.version || '');
                        events.emit('log', checkSummary);
                        if (!checkItem.installed) {
                            someChecksFailed = true;
                            events.emit('warn', checkItem.metadata.reason);
                        }
                    });

                    return someChecksFailed;
                }).some(function(isCheckFailedForPlatform) {
                    return isCheckFailedForPlatform;
                });

                if (someChecksFailed) throw new CordovaError('Some of requirements check failed');
            });
    } else if (cmd == 'serve') {
        var port = undashed[1];
        return cordova.raw.serve(port);
    } else if (cmd == 'create') {
        return create();
    } else {
        // platform/plugins add/rm [target(s)]
        subcommand = undashed[1]; // sub-command like "add", "ls", "rm" etc.
        var targets = undashed.slice(2); // array of targets, either platforms or plugins
        var cli_vars = {};
        if (args.variable) {
            args.variable.forEach(function (s) {
                // CB-9171
                var eq = s.indexOf('=');
                if (eq == -1)
                    throw new CordovaError("invalid variable format: " + s);
                var key = s.substr(0, eq).toUpperCase();
                var val = s.substr(eq + 1, s.length);
                cli_vars[key] = val;
            });
        }
        var download_opts = { searchpath : args.searchpath
                            , noregistry : args.noregistry
                            , nohooks : args.nohooks
                            , cli_variables : cli_vars
                            , browserify: args.browserify || false
                            , fetch: args.fetch || false
                            , link: args.link || false
                            , save: args.save || false
                            , shrinkwrap: args.shrinkwrap || false
                            , force: args.force || false
                            };
        return cordova.raw[cmd](subcommand, targets, download_opts);
    }

    function create() {
        var cfg;            // Create config
        var customWww;      // Template path
        var wwwCfg;         // Template config

        // If we got a fourth parameter, consider it to be JSON to init the config.
        if (undashed[4])
            cfg = JSON.parse(undashed[4]);
        else
            cfg = {};

        customWww = args['copy-from'] || args['link-to'] || args.template;

        if (customWww) {
            if (!args.template && !args['copy-from'] && customWww.indexOf('http') === 0) {
                throw new CordovaError(
                    'Only local paths for custom www assets are supported for linking' + customWww
                );
            }

            // Resolve tilda
            if (customWww.substr(0,1) === '~')
                customWww = path.join(process.env.HOME,  customWww.substr(1));

            wwwCfg = {
                url: customWww,
                template: false,
                link: false
            };

            if (args['link-to']) {
                wwwCfg.link = true;
            }
            if (args.template) {
                wwwCfg.template = true;
            } else if (args['copy-from']) {
                logger.warn('Warning: --copy-from option is being deprecated. Consider using --template instead.');
                wwwCfg.template = true;
            }

            cfg.lib = cfg.lib || {};
            cfg.lib.www = wwwCfg;
        }
        return cordova.raw.create( undashed[1]  // dir to create the project in
            , undashed[2]  // App id
            , undashed[3]  // App name
            , cfg
            , events || undefined
        );
    }
}
<commit_msg>CB-11976 - Add deprecated node version warning for 0.x<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/

/* jshint node:true, bitwise:true, undef:true, trailing:true, quotmark:true,
          indent:4, unused:vars, latedef:nofunc,
          laxcomma:true
*/

var path = require('path'),
    fs = require('fs'),
    help = require('./help'),
    nopt,
    _,
    updateNotifier,
    pkg = require('../package.json'),
    telemetry = require('./telemetry'),
    Q = require('q');

var cordova_lib = require('cordova-lib'),
    CordovaError = cordova_lib.CordovaError,
    cordova = cordova_lib.cordova,
    events = cordova_lib.events,
    logger = require('cordova-common').CordovaLogger.get();

/*
 * init
 *
 * initializes nopt and underscore
 * nopt and underscore are require()d in try-catch below to print a nice error
 * message if one of them is not installed.
 */
function init() {
    try {
        nopt = require('nopt');
        _ = require('underscore');
        updateNotifier = require('update-notifier');
    } catch (e) {
        console.error(
            'Please run npm install from this directory:\n\t' +
            path.dirname(__dirname)
        );
        process.exit(2);
    }
}

function checkForUpdates() {
    try {
        // Checks for available update and returns an instance
        var notifier = updateNotifier({
            pkg: pkg
        });

        // Notify using the built-in convenience method
        notifier.notify();
    } catch (e) {
        // https://issues.apache.org/jira/browse/CB-10062
        if (e && e.message && /EACCES/.test(e.message)) {
            console.log('Update notifier was not able to access the config file.\n' +
                'You may grant permissions to the file: \'sudo chmod 744 ~/.config/configstore/update-notifier-cordova.json\'');
        } else {
            throw e;
        }
    }
}

var shouldCollectTelemetry = false;
module.exports = function (inputArgs, cb) {

    /**
     * mainly used for testing.
     */
    cb = cb || function(){};

    init();

    // If no inputArgs given, use process.argv.
    inputArgs = inputArgs || process.argv;
    var cmd = inputArgs[2]; // e.g: inputArgs= 'node cordova run ios'
    var subcommand = getSubCommand(inputArgs, cmd);
    var isTelemetryCmd = (cmd === 'telemetry');

    // ToDO: Move nopt-based parsing of args up here
    if(cmd === '--version' || cmd === '-v') {
        cmd = 'version';
    } else if(!cmd || cmd === '--help' || cmd === 'h') {
        cmd = 'help';
    }

    Q().then(function() {

        /**
         * Skip telemetry prompt if:
         * - CI environment variable is present
         * - Command is run with `--no-telemetry` flag
         * - Command ran is: `cordova telemetry on | off | ...`
         */

        if(telemetry.isCI(process.env) || telemetry.isNoTelemetryFlag(inputArgs)) {
            return Q(false);
        }

        /**
         * We shouldn't prompt for telemetry if user issues a command of the form: `cordova telemetry on | off | ...x`
         * Also, if the user has already been prompted and made a decision, use his saved answer
         */
        if(isTelemetryCmd) {
            var isOptedIn = telemetry.isOptedIn();
            return handleTelemetryCmd(subcommand, isOptedIn);
        }

        if(telemetry.hasUserOptedInOrOut()) {
            return Q(telemetry.isOptedIn());
        }

        /**
         * Otherwise, prompt user to opt-in or out
         * Note: the prompt is shown for 30 seconds. If no choice is made by that time, User is considered to have opted out.
         */
        return telemetry.showPrompt();
    }).then(function (collectTelemetry) {
        shouldCollectTelemetry = collectTelemetry;
        if(isTelemetryCmd) {
            return Q();
        }
        return cli(inputArgs);
    }).then(function () {
        if (shouldCollectTelemetry && !isTelemetryCmd) {
            telemetry.track(cmd, subcommand, 'successful');
        }
        // call cb with error as arg if something failed
        cb(null);
    }).fail(function (err) {
        if (shouldCollectTelemetry && !isTelemetryCmd) {
            telemetry.track(cmd, subcommand, 'unsuccessful');
        }
        // call cb with error as arg if something failed
        cb(err);
        throw err;
    }).done();
};

function getSubCommand(args, cmd) {
    if(cmd === 'platform' || cmd === 'platforms' || cmd === 'plugin' || cmd === 'plugins' || cmd === 'telemetry') {
        return args[3]; // e.g: args='node cordova platform rm ios', 'node cordova telemetry on'
    }
    return null;
}

function handleTelemetryCmd(subcommand, isOptedIn) {

    if (subcommand !== 'on' && subcommand !== 'off') {
        logger.subscribe(events);
        return help(['telemetry']);
    }

    var turnOn = subcommand === 'on' ? true : false;
    var cmdSuccess = true;

    // turn telemetry on or off
    try {
        if (turnOn) {
            telemetry.turnOn();
            console.log("Thanks for opting into telemetry to help us improve cordova.");
        } else {
            telemetry.turnOff();
            console.log("You have been opted out of telemetry. To change this, run: cordova telemetry on.");
        }
    } catch (ex) {
        cmdSuccess = false;
    }

    // track or not track ?, that is the question

    if (!turnOn) {
        // Always track telemetry opt-outs (whether user opted out or not!)
        telemetry.track('telemetry', 'off', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful': 'unsuccessful');
        return Q();
    }

    if(isOptedIn) {
        telemetry.track('telemetry', 'on', 'via-cordova-telemetry-cmd', cmdSuccess ? 'successful' : 'unsuccessful');
    }

    return Q();
}

function cli(inputArgs) {
    // When changing command line arguments, update doc/help.txt accordingly.
    var knownOpts =
        { 'verbose' : Boolean
        , 'version' : Boolean
        , 'help' : Boolean
        , 'silent' : Boolean
        , 'experimental' : Boolean
        , 'noregistry' : Boolean
        , 'nohooks': Array
        , 'shrinkwrap' : Boolean
        , 'copy-from' : String
        , 'link-to' : path
        , 'searchpath' : String
        , 'variable' : Array
        , 'link': Boolean
        , 'force': Boolean
        // Flags to be passed to `cordova build/run/emulate`
        , 'debug' : Boolean
        , 'release' : Boolean
        , 'archs' : String
        , 'device' : Boolean
        , 'emulator': Boolean
        , 'target' : String
        , 'browserify': Boolean
        , 'noprepare': Boolean
        , 'fetch': Boolean
        , 'nobuild': Boolean
        , 'list': Boolean
        , 'buildConfig' : String
        , 'template' : String
        };

    var shortHands =
        { 'd' : '--verbose'
        , 'v' : '--version'
        , 'h' : '--help'
        , 'src' : '--copy-from'
        , 't' : '--template'
        };

    checkForUpdates();

    var args = nopt(knownOpts, shortHands, inputArgs);

    // For CordovaError print only the message without stack trace unless we
    // are in a verbose mode.
    process.on('uncaughtException', function(err) {
        logger.error(err);
        // Don't send exception details, just send that it happened
        if(shouldCollectTelemetry) {
            telemetry.track('uncaughtException');
        }
        process.exit(1);
    });

    logger.subscribe(events);

    if (args.silent) {
        logger.setLevel('error');
    }

    if (args.verbose) {
        logger.setLevel('verbose');
    }

    var cliVersion = require('../package').version;
    // TODO: Use semver.prerelease when it gets released
    var usingPrerelease = /-nightly|-dev$/.exec(cliVersion);
    if (args.version || usingPrerelease) {
        var libVersion = require('cordova-lib/package').version;
        var toPrint = cliVersion;
        if (cliVersion != libVersion || usingPrerelease) {
            toPrint += ' (cordova-lib@' + libVersion + ')';
        }

        if (args.version) {
            logger.results(toPrint);
            return Q();
        } else {
            // Show a warning and continue
            logger.warn('Warning: using prerelease version ' + toPrint);
        }
    }

    if (/^v0.\d+[.\d+]*/.exec(process.version)) { // matches v0.*
        var msg = 'Warning: using node version ' + process.version +
                ' which has been deprecated. Please upgrade to the latest node version available (v6.x is recommended).';
        logger.warn(msg);
    }

    // If there were arguments protected from nopt with a double dash, keep
    // them in unparsedArgs. For example:
    // cordova build ios -- --verbose --whatever
    // In this case "--verbose" is not parsed by nopt and args.vergbose will be
    // false, the unparsed args after -- are kept in unparsedArgs and can be
    // passed downstream to some scripts invoked by Cordova.
    var unparsedArgs = [];
    var parseStopperIdx =  args.argv.original.indexOf('--');
    if (parseStopperIdx != -1) {
        unparsedArgs = args.argv.original.slice(parseStopperIdx + 1);
    }

    // args.argv.remain contains both the undashed args (like platform names)
    // and whatever unparsed args that were protected by " -- ".
    // "undashed" stores only the undashed args without those after " -- " .
    var remain = args.argv.remain;
    var undashed = remain.slice(0, remain.length - unparsedArgs.length);
    var cmd = undashed[0];
    var subcommand;
    var msg;
    var known_platforms = Object.keys(cordova_lib.cordova_platforms);

    if ( !cmd || cmd == 'help' || args.help ) {
        if (!args.help && remain[0] == 'help') {
            remain.shift();
        }
        return help(remain);
    }

    if ( !cordova.hasOwnProperty(cmd) ) {
        msg =
            'Cordova does not know ' + cmd + '; try `' + cordova_lib.binname +
            ' help` for a list of all the available commands.';
        throw new CordovaError(msg);
    }

    var opts = {
        platforms: [],
        options: [],
        verbose: args.verbose || false,
        silent: args.silent || false,
        browserify: args.browserify || false,
        fetch: args.fetch || false,
        nohooks: args.nohooks || [],
        searchpath : args.searchpath
    };

    if (cmd == 'emulate' || cmd == 'build' || cmd == 'prepare' || cmd == 'compile' || cmd == 'run' || cmd === 'clean') {
        // All options without dashes are assumed to be platform names
        opts.platforms = undashed.slice(1);
        var badPlatforms = _.difference(opts.platforms, known_platforms);
        if( !_.isEmpty(badPlatforms) ) {
            msg = 'Unknown platforms: ' + badPlatforms.join(', ');
            throw new CordovaError(msg);
        }

        // Pass nopt-parsed args to PlatformApi through opts.options
        opts.options = args;
        opts.options.argv = unparsedArgs;

        if (cmd === 'run' && args.list && cordova.raw.targets) {
            return cordova.raw.targets.call(null, opts);
        }

        return cordova.raw[cmd].call(null, opts);
    } else if (cmd === 'requirements') {
        // All options without dashes are assumed to be platform names
        opts.platforms = undashed.slice(1);
        var badPlatforms = _.difference(opts.platforms, known_platforms);
        if( !_.isEmpty(badPlatforms) ) {
            msg = 'Unknown platforms: ' + badPlatforms.join(', ');
            throw new CordovaError(msg);
        }

        return cordova.raw[cmd].call(null, opts.platforms)
            .then(function(platformChecks) {

                var someChecksFailed = Object.keys(platformChecks).map(function(platformName) {
                    events.emit('log', '\nRequirements check results for ' + platformName + ':');
                    var platformCheck = platformChecks[platformName];
                    if (platformCheck instanceof CordovaError) {
                        events.emit('warn', 'Check failed for ' + platformName + ' due to ' + platformCheck);
                        return true;
                    }

                    var someChecksFailed = false;
                    platformCheck.forEach(function(checkItem) {
                        var checkSummary = checkItem.name + ': ' +
                            (checkItem.installed ? 'installed ' : 'not installed ') +
                            (checkItem.metadata.version || '');
                        events.emit('log', checkSummary);
                        if (!checkItem.installed) {
                            someChecksFailed = true;
                            events.emit('warn', checkItem.metadata.reason);
                        }
                    });

                    return someChecksFailed;
                }).some(function(isCheckFailedForPlatform) {
                    return isCheckFailedForPlatform;
                });

                if (someChecksFailed) throw new CordovaError('Some of requirements check failed');
            });
    } else if (cmd == 'serve') {
        var port = undashed[1];
        return cordova.raw.serve(port);
    } else if (cmd == 'create') {
        return create();
    } else {
        // platform/plugins add/rm [target(s)]
        subcommand = undashed[1]; // sub-command like "add", "ls", "rm" etc.
        var targets = undashed.slice(2); // array of targets, either platforms or plugins
        var cli_vars = {};
        if (args.variable) {
            args.variable.forEach(function (s) {
                // CB-9171
                var eq = s.indexOf('=');
                if (eq == -1)
                    throw new CordovaError("invalid variable format: " + s);
                var key = s.substr(0, eq).toUpperCase();
                var val = s.substr(eq + 1, s.length);
                cli_vars[key] = val;
            });
        }
        var download_opts = { searchpath : args.searchpath
                            , noregistry : args.noregistry
                            , nohooks : args.nohooks
                            , cli_variables : cli_vars
                            , browserify: args.browserify || false
                            , fetch: args.fetch || false
                            , link: args.link || false
                            , save: args.save || false
                            , shrinkwrap: args.shrinkwrap || false
                            , force: args.force || false
                            };
        return cordova.raw[cmd](subcommand, targets, download_opts);
    }

    function create() {
        var cfg;            // Create config
        var customWww;      // Template path
        var wwwCfg;         // Template config

        // If we got a fourth parameter, consider it to be JSON to init the config.
        if (undashed[4])
            cfg = JSON.parse(undashed[4]);
        else
            cfg = {};

        customWww = args['copy-from'] || args['link-to'] || args.template;

        if (customWww) {
            if (!args.template && !args['copy-from'] && customWww.indexOf('http') === 0) {
                throw new CordovaError(
                    'Only local paths for custom www assets are supported for linking' + customWww
                );
            }

            // Resolve tilda
            if (customWww.substr(0,1) === '~')
                customWww = path.join(process.env.HOME,  customWww.substr(1));

            wwwCfg = {
                url: customWww,
                template: false,
                link: false
            };

            if (args['link-to']) {
                wwwCfg.link = true;
            }
            if (args.template) {
                wwwCfg.template = true;
            } else if (args['copy-from']) {
                logger.warn('Warning: --copy-from option is being deprecated. Consider using --template instead.');
                wwwCfg.template = true;
            }

            cfg.lib = cfg.lib || {};
            cfg.lib.www = wwwCfg;
        }
        return cordova.raw.create( undashed[1]  // dir to create the project in
            , undashed[2]  // App id
            , undashed[3]  // App name
            , cfg
            , events || undefined
        );
    }
}
<|endoftext|>


<commit_before>'use strict';

let program = require('commander');
import {slackHistoryExport} from './index.js';
program
  .version('0.0.1')
  .usage('[options] <file ...>')
  .option('-t, --token <value>', '[REQUIRED] Enter your slack token API, you can generate it from here https://api.slack.com/web')
  .option('-u, --username [value]', 'Enter username to download history')
  .parse(process.argv);

if (!program.token) {
  throw new Error('Slack Token must be present type "slack-history-export --help to view options"');
}

slackHistoryExport(program);
<commit_msg>add more cli commands<commit_after>'use strict';

import program from 'commander';
import {slackHistoryExport} from './index.js';
program
  .version('0.0.1')
  .usage('[options] <file ...>')
  .option('-t, --token <value>', '[REQUIRED] Enter your slack token API, you can generate it from here https://api.slack.com/web')
  .option('-u, --username [value]', 'Enter username to download history')
  .option('-d, --directory [value]', 'Directory to save generated file')
  .option('-f, --filename [value]', 'Name of generated file. Default is "<current timestamp><username || channel || group>-slack-history" e.g "1443378584156-abimbola-slack-history.json"')
  .parse(process.argv);

if (!program.token) {
  throw new Error('Slack Token must be present type "slack-history-export --help to view options"');
}

slackHistoryExport(program);
<|endoftext|>


<commit_before>const commander = require("commander");
var fs = require("fs");
const path = require("path");
const process = require("process");
const {exec, spawn} = require("cross-spawn");
const lazyMethodRequire = require("./lib/LazyMethodRequire").default(__dirname);

const newApp = lazyMethodRequire("./commands/new");
const startClient = lazyMethodRequire("./commands/start-client");
const startServer = lazyMethodRequire("./commands/start-server");
const startTest = lazyMethodRequire("./commands/test");
const generate = lazyMethodRequire("./commands/generate");
const destroy = lazyMethodRequire("./commands/destroy");
const dockerize = lazyMethodRequire("./commands/dockerize");

const updateLastVersionUsed = require("./lib/updateVersion.js");
const getVersion = require("./lib/getVersion");
const logger = require("./lib/logger");
const logsColorScheme = require("./lib/logsColorScheme");
const { highlight } = logsColorScheme;

const autoUpgrade = require("./auto-upgrade");
const chokidar = require("chokidar");

const command = process.argv[2];
const isProduction = process.env.NODE_ENV === "production";

const IS_WINDOWS = process.platform === "win32";

commander
  .version(getVersion());

commander
  .command("touch")
  .description("update project version")
  .action((options)=> updateLastVersionUsed())

commander
  .command("new")
  .description("generate a new application")
  .arguments("<app_name>")
  .action(newApp)
  .action((options)=> updateLastVersionUsed());

commander
  .command("generate <container|component|reducer>")
  .description("generate a new container")
  .arguments("<name>")
  .action((type, name) => generate(type, name, (err) => {
    if (err) console.log(chalk.red(`ERROR: ${err}`));
  }))
  .action((options)=> updateLastVersionUsed())

commander
  .command("destroy <container|component|reducer>")
  .description("destroy a generated container")
  .arguments("<name>")
  .action(destroy)
  .action((options)=> updateLastVersionUsed());

const debugOption = {
  command: "-D, --debug",
  description: "debug server side rendering with node-inspector"
};

commander
  .command("start")
  .description("start everything")
  .option("-T, --no_tests", "ignore test hook")
  .option(debugOption.command, debugOption.description)
  .action((options) => startAll(options.no_tests, options.debug))
  .action((options)=> updateLastVersionUsed());

commander
  .command("build")
  .description("create production asset build")
  .action(() => startClient(true))
  .action((options)=> updateLastVersionUsed());

commander
  .command("dockerize")
  .description("create docker image")
  .arguments("<name>")
  .action(upgradeAndDockerize)
  .action((options)=> updateLastVersionUsed());

commander
  .command("start-client", null, {noHelp: true})
  .description("start client")
  .action(() => startClient(false))
  .action((options)=> updateLastVersionUsed());

commander
  .command("start-server", null, {noHelp: true})
  .description("start server")
  .option(debugOption.command, debugOption.description)
  .action((options) => startServer(options.debug))
  .action((options)=> updateLastVersionUsed());

const firefoxOption = {
  command: "-F, --firefox",
  description: "Use Firefox with test runner"
};

commander
  .command("start-test", null, {noHelp: true})
  .option(firefoxOption.command, firefoxOption.description)
  .description("start test")
  .action((options) => startTest(options))
  .action((options)=> updateLastVersionUsed());

commander
  .command("test")
  .option(firefoxOption.command, firefoxOption.description)
  .description("start tests")
  .action(() => spawnProcess("test", process.argv.slice(3)))
  .action((options)=> updateLastVersionUsed());

// This is a catch all command. DO NOT PLACE ANY COMMANDS BELOW THIS
commander
  .command('*', null, {noHelp: true})
  .action(function(cmd){
    console.log(`Error: Command '${cmd}' not recognized`);
    commander.help();
});

commander.parse(process.argv);

function spawnProcess (type, args=[]) {
  var childProcess;
  var postFix = IS_WINDOWS ? ".cmd" : "";
  switch (type) {
    case "client":
      childProcess = spawn("gluestick" + postFix, ["start-client", ...args], {stdio: "inherit", env: Object.assign({}, process.env)});
      break;
    case "server":
      childProcess = spawn("gluestick" + postFix, ["start-server", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-server"})});
      break;
    case "test":
      childProcess = spawn("gluestick" + postFix, ["start-test", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-test"})});
      break;
  }

  childProcess.on("error", function (data) { console.log(chalk.red(JSON.stringify(arguments))) });
  return childProcess;
}

async function startAll(withoutTests=false, debug=false) {
  try {
    await autoUpgrade();
  }
  catch (e) {
    console.log(chalk.red("ERROR during auto upgrade"), e);
    process.exit();
  }

  var client = spawnProcess("client");
  var server = spawnProcess("server", (debug ? ["--debug"] : []));

  // Start tests unless they asked us not to or we are in production mode
  if (!isProduction && !withoutTests) {
    var testProcess = spawnProcess("test");
  }
}

async function upgradeAndDockerize (name) {
  await autoUpgrade();
  dockerize(name);
}
<commit_msg>fixed some merge errors dealing with logger<commit_after>const commander = require("commander");
const fs = require("fs");
const path = require("path");
const process = require("process");
const {exec, spawn} = require("cross-spawn");
const lazyMethodRequire = require("./lib/LazyMethodRequire").default(__dirname);

const newApp = lazyMethodRequire("./commands/new");
const startClient = lazyMethodRequire("./commands/start-client");
const startServer = lazyMethodRequire("./commands/start-server");
const startTest = lazyMethodRequire("./commands/test");
const generate = lazyMethodRequire("./commands/generate");
const destroy = lazyMethodRequire("./commands/destroy");
const dockerize = lazyMethodRequire("./commands/dockerize");

const updateLastVersionUsed = require("./lib/updateVersion.js");
const getVersion = require("./lib/getVersion");
const logger = require("./lib/logger");
const logsColorScheme = require("./lib/logsColorScheme");
const { highlight } = logsColorScheme;

const autoUpgrade = require("./auto-upgrade");
const chokidar = require("chokidar");

const command = process.argv[2];
const isProduction = process.env.NODE_ENV === "production";

const IS_WINDOWS = process.platform === "win32";

commander
  .version(getVersion());

commander
  .command("touch")
  .description("update project version")
  .action((options)=> updateLastVersionUsed())

commander
  .command("new")
  .description("generate a new application")
  .arguments("<app_name>")
  .action(newApp)
  .action((options)=> updateLastVersionUsed());

commander
  .command("generate <container|component|reducer>")
  .description("generate a new container")
  .arguments("<name>")
  .action((type, name) => generate(type, name, (err) => {
    if (err) logger.error(err);
  }))
  .action((options)=> updateLastVersionUsed())

commander
  .command("destroy <container|component|reducer>")
  .description("destroy a generated container")
  .arguments("<name>")
  .action(destroy)
  .action((options)=> updateLastVersionUsed());

const debugOption = {
  command: "-D, --debug",
  description: "debug server side rendering with node-inspector"
};

commander
  .command("start")
  .description("start everything")
  .option("-T, --no_tests", "ignore test hook")
  .option(debugOption.command, debugOption.description)
  .action((options) => startAll(options.no_tests, options.debug))
  .action((options)=> updateLastVersionUsed());

commander
  .command("build")
  .description("create production asset build")
  .action(() => startClient(true))
  .action((options)=> updateLastVersionUsed());

commander
  .command("dockerize")
  .description("create docker image")
  .arguments("<name>")
  .action(upgradeAndDockerize)
  .action((options)=> updateLastVersionUsed());

commander
  .command("start-client", null, {noHelp: true})
  .description("start client")
  .action(() => startClient(false))
  .action((options)=> updateLastVersionUsed());

commander
  .command("start-server", null, {noHelp: true})
  .description("start server")
  .option(debugOption.command, debugOption.description)
  .action((options) => startServer(options.debug))
  .action((options)=> updateLastVersionUsed());

const firefoxOption = {
  command: "-F, --firefox",
  description: "Use Firefox with test runner"
};

commander
  .command("start-test", null, {noHelp: true})
  .option(firefoxOption.command, firefoxOption.description)
  .description("start test")
  .action((options) => startTest(options))
  .action((options)=> updateLastVersionUsed());

commander
  .command("test")
  .option(firefoxOption.command, firefoxOption.description)
  .description("start tests")
  .action(() => spawnProcess("test", process.argv.slice(3)))
  .action((options)=> updateLastVersionUsed());

// This is a catch all command. DO NOT PLACE ANY COMMANDS BELOW THIS
commander
  .command('*', null, {noHelp: true})
  .action(function(cmd){
    logger.error(`Command '${highlight(cmd)}' not recognized`);
    commander.help();
});

commander.parse(process.argv);

function spawnProcess (type, args=[]) {
  var childProcess;
  var postFix = IS_WINDOWS ? ".cmd" : "";
  switch (type) {
    case "client":
      childProcess = spawn("gluestick" + postFix, ["start-client", ...args], {stdio: "inherit", env: Object.assign({}, process.env)});
      break;
    case "server":
      childProcess = spawn("gluestick" + postFix, ["start-server", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-server"})});
      break;
    case "test":
      childProcess = spawn("gluestick" + postFix, ["start-test", ...args], {stdio: "inherit", env: Object.assign({}, process.env, {NODE_ENV: isProduction ? "production": "development-test"})});
      break;
  }
  childProcess.on("error", function (data) { logger.error(JSON.stringify(arguments)) });
  return childProcess;
}

async function startAll(withoutTests=false, debug=false) {
  try {
    await autoUpgrade();
  }
  catch (e) {
    logger.error(`During auto upgrade: ${e}`);
    process.exit();
  }

  var client = spawnProcess("client");
  var server = spawnProcess("server", (debug ? ["--debug"] : []));

  // Start tests unless they asked us not to or we are in production mode
  if (!isProduction && !withoutTests) {
    var testProcess = spawnProcess("test");
  }
}

async function upgradeAndDockerize (name) {
  await autoUpgrade();
  dockerize(name);
}
<|endoftext|>


<commit_before>$(document).ready(function(){

	if($.fn.fancybox) {
		$.extend(true, $.fancybox.defaults, {
			hash : false
		});

		$('.magnific').fancybox();
	}

	if($.fn.validate) {
		$.validator.setDefaults({
            highlight: function(element, errorClass, validClass) {
                var $element = $(element);
                $element.addClass(errorClass).removeClass(validClass);
                if ($element.attr('type') == 'checkbox' || $element.attr('type') == 'radio') {
                    if ($element.attr('type') == 'checkbox') {
                        $element.closest('.checkbox').addClass('error-checkbox');
                    } else if ($element.attr('type') == 'radio') {
                        $element.closest('.radio').addClass('error-radio');
                    }
                } else if ($element.prop("tagName") == 'SELECT') {
                    $element.closest('.custom-select, .custom-select-multiple, .custom-select-default').addClass('error-select');
                } else if ($element.prop("tagName") == 'TEXTAREA') {
                    $element.closest('.wysiwyg-editor').addClass('error-wysiwyg');
                }
            },
            unhighlight: function(element, errorClass, validClass) {
                var $element = $(element);
                $element.removeClass(errorClass).addClass(validClass);
                if ($element.attr('type') == 'checkbox' || $element.attr('type') == 'radio') {
                    if ($element.attr('type') == 'checkbox') {
                        $element.closest('.checkbox').removeClass('error-checkbox');
                    } else if ($element.attr('type') == 'radio') {
                        $element.closest('.radio').removeClass('error-radio');
                    }
                } else if ($element.prop("tagName") == 'SELECT') {
                    $element.closest('.custom-select, .custom-select-multiple, .custom-select-default').removeClass('error-select');
                } else if ($element.prop("tagName") == 'TEXTAREA') {
                    $element.closest('.wysiwyg-editor').removeClass('error-wysiwyg');
                }
            },
            errorPlacement: function(error, element) {
                if (element.closest('.custom-select').hasClass('error-select'))
                    error.insertAfter(element.closest('.custom-select'));
                else if (element.closest('.checkbox').hasClass('error-checkbox'))
                    error.insertAfter(element.closest('.checkbox'));
                else if (element.closest('.wysiwyg-editor').hasClass('error-wysiwyg'))
                    error.insertAfter(element.closest('.wysiwyg-editor'));
                else
                    error.insertAfter(element);
            }
        });

		$('form.validate').each(function() {
			$(this).validate();
		});
	}

    if($.fn.owlCarousel) {
        $('.slider').owlCarousel({
            items: 1,
            margin: 0,
            loop: true,
            nav: true,
            dots: true,
            autoplay: true,
            autoplayTimeout: 5000,
            autoplayHoverPause: false,
            stagePadding: 0,
            navText: ['Poprzedni','Następny'],
            responsiveClass: true,
            responsive: {
                0:{
                    items: 1
                },
                600:{
                    items: 1
                },
                1000:{
                    items: 1
                }
            }
        });
    }

	if (typeof jcf == 'object') {
		jcf.setOptions('Select', {
			fakeDropInBody: false,
		    maxVisibleItems: 5,
		    wrapNative: false,
		    wrapNativeOnMobile: true,
			useCustomScroll: false
		});

		jcf.replace($('.custom-select select'));
		jcf.replace($('[type="radio"]'));
		jcf.replace($('[type="checkbox"]'));
	}

	$('.wysiwyg iframe').each(function(){
		$(this).wrap('

');
	});

	$('.wysiwyg table').each(function(){
		var mT = $(this).css('marginTop');
		var mB = $(this).css('marginBottom');
		$(this).css('marginBottom','0').css('marginTop','0').wrap('

');
	});

	$('.menu-trigger').on('click', function(){
		$('html').toggleClass('is-menu-open');
	});

	minimizeTop();
});

$(window).on('load', function(){

});

function minimizeTop()
{
	var scrollOffset = parseInt($('.top').data('minimize-offset')) > 0 ? parseInt($('.top').data('minimize-offset')) : false;
	if (scrollOffset !== false)
	{
		$(window).scroll(function(){
			if ($(window).scrollTop() >= scrollOffset)
				$('html').addClass('is-top-minimize');
			else
				$('html').removeClass('is-top-minimize');
		}).scroll();
	}
}
<commit_msg>Update main.js<commit_after>$(document).ready(function(){

	if($.fn.fancybox) {
		$.extend(true, $.fancybox.defaults, {
			hash : false
		});

		$('.magnific').fancybox();
	}

	if($.fn.validate) {
		$.validator.setDefaults({
            highlight: function(element, errorClass, validClass) {
                var $element = $(element);
                $element.addClass(errorClass).removeClass(validClass);
                if ($element.attr('type') == 'checkbox' || $element.attr('type') == 'radio') {
                    if ($element.attr('type') == 'checkbox') {
                        $element.closest('.checkbox').addClass('error-checkbox');
                    } else if ($element.attr('type') == 'radio') {
                        $element.closest('.radio').addClass('error-radio');
                    }
                } else if ($element.prop("tagName") == 'SELECT') {
                    $element.closest('.custom-select, .custom-select-multiple, .custom-select-default').addClass('error-select');
                } else if ($element.prop("tagName") == 'TEXTAREA') {
                    $element.closest('.wysiwyg-editor').addClass('error-wysiwyg');
                }
            },
            unhighlight: function(element, errorClass, validClass) {
                var $element = $(element);
                $element.removeClass(errorClass).addClass(validClass);
                if ($element.attr('type') == 'checkbox' || $element.attr('type') == 'radio') {
                    if ($element.attr('type') == 'checkbox') {
                        $element.closest('.checkbox').removeClass('error-checkbox');
                    } else if ($element.attr('type') == 'radio') {
                        $element.closest('.radio').removeClass('error-radio');
                    }
                } else if ($element.prop("tagName") == 'SELECT') {
                    $element.closest('.custom-select, .custom-select-multiple, .custom-select-default').removeClass('error-select');
                } else if ($element.prop("tagName") == 'TEXTAREA') {
                    $element.closest('.wysiwyg-editor').removeClass('error-wysiwyg');
                }
            },
            errorPlacement: function(error, element) {
                if (element.closest('.custom-select').hasClass('error-select'))
                    error.insertAfter(element.closest('.custom-select'));
                else if (element.closest('.checkbox').hasClass('error-checkbox'))
                    error.insertAfter(element.closest('.checkbox'));
                else if (element.closest('.radio').hasClass('error-radio'))
                    error.insertAfter(element.closest('.radio'));
                else if (element.closest('.wysiwyg-editor').hasClass('error-wysiwyg'))
                    error.insertAfter(element.closest('.wysiwyg-editor'));
                else
                    error.insertAfter(element);
            },
            ignore: ':hidden:not([type="hidden"]):not(select):not(.bbcode-editor)'
        });

    if($.fn.owlCarousel) {
        $('.slider').owlCarousel({
            items: 1,
            margin: 0,
            loop: true,
            nav: true,
            dots: true,
            autoplay: true,
            autoplayTimeout: 5000,
            autoplayHoverPause: false,
            stagePadding: 0,
            navText: ['Poprzedni','Następny'],
            responsiveClass: true,
            responsive: {
                0:{
                    items: 1
                },
                600:{
                    items: 1
                },
                1000:{
                    items: 1
                }
            }
        });
    }

	if (typeof jcf == 'object') {
		jcf.setOptions('Select', {
			fakeDropInBody: false,
		    maxVisibleItems: 5,
		    wrapNative: false,
		    wrapNativeOnMobile: true,
			useCustomScroll: false
		});

		jcf.replace($('.custom-select select'));
		jcf.replace($('[type="radio"]'));
		jcf.replace($('[type="checkbox"]'));
	}

	$('.wysiwyg iframe').each(function(){
		$(this).wrap('

');
	});

	$('.wysiwyg table').each(function(){
		var mT = $(this).css('marginTop');
		var mB = $(this).css('marginBottom');
		$(this).css('marginBottom','0').css('marginTop','0').wrap('

');
	});

	$('.menu-trigger').on('click', function(){
		$('html').toggleClass('is-menu-open');
	});

	minimizeTop();
});

$(window).on('load', function(){

});

function minimizeTop()
{
	var scrollOffset = parseInt($('.top').data('minimize-offset')) > 0 ? parseInt($('.top').data('minimize-offset')) : false;
	if (scrollOffset !== false)
	{
		$(window).scroll(function(){
			if ($(window).scrollTop() >= scrollOffset)
				$('html').addClass('is-top-minimize');
			else
				$('html').removeClass('is-top-minimize');
		}).scroll();
	}
}
<|endoftext|>


<commit_before>// ###########
// # imports #
// ###########

import ajaxRequest from "./helper/ajaxRequest";
import find from "./helper/find";
import reload from "./helper/reload";
import returnSearchParam from "./helper/returnSearchParam";
import themeSwitcher from "./helper/themeSwitcher";
import toggleClass from "./helper/toggleClass";

import setupTimeline from "./tools/setupTimeline";

// ###########
// # program #
// ###########

// get latest unreadItem from saved timestamp
window.lastReadTimestamps = localStorage.getItem("lastReadItems");
if (!lastReadTimestamps) {
	lastReadTimestamps = 0;
}

// load content and setupTimeline with response
ajaxRequest(
	"GET",
	"middleware.php?return=content&channel=" +
		returnSearchParam("channel") +
		"&timestamp=" +
		lastReadTimestamps,
	setupTimeline
);

// load channels into UI-Element
ajaxRequest("GET", "middleware.php?return=channels", injectChannels);
function injectChannels(response) {
	document.getElementById("channels").innerHTML = response;
}

// toggle overlays
find(".js-overlay-toggle").forEach((element) => {
	element.addEventListener(
		"click",
		() => {
			toggleClass(find(element.dataset.target), "js-hidden");
			event.preventDefault();
		},
		true
	);
});

// reload
find(".application-reload").forEach((element) => {
	element.addEventListener("click", reload, true);
});

// allow theme switching
themeSwitcher();
<commit_msg>add todo<commit_after>// ###########
// # imports #
// ###########

import ajaxRequest from "./helper/ajaxRequest";
import find from "./helper/find";
import reload from "./helper/reload";
import returnSearchParam from "./helper/returnSearchParam";
import themeSwitcher from "./helper/themeSwitcher";
import toggleClass from "./helper/toggleClass";

import setupTimeline from "./tools/setupTimeline";

// ###########
// # program #
// ###########

// get latest unreadItem from saved timestamp
window.lastReadTimestamps = localStorage.getItem("lastReadItems");
if (!lastReadTimestamps) {
	lastReadTimestamps = 0;
}

// load content and setupTimeline with response
ajaxRequest(
	"GET",
	"middleware.php?return=content&channel=" +
		returnSearchParam("channel") +
		"&timestamp=" +
		lastReadTimestamps,
	setupTimeline
);

// load channels into UI-Element
ajaxRequest("GET", "middleware.php?return=channels", injectChannels);
function injectChannels(response) {
	document.getElementById("channels").innerHTML = response;
}

// toggle overlays
find(".js-overlay-toggle").forEach((element) => {
	element.addEventListener(
		"click",
		() => {
			toggleClass(find(element.dataset.target), "js-hidden");
			event.preventDefault();
		},
		true
	);
});

// reload
find(".application-reload").forEach((element) => {
	element.addEventListener("click", reload, true);
});

// allow theme switching
// TODO: this needs refactoring alongside with the css
themeSwitcher();
<|endoftext|>


<commit_before>'use strict';
const has = Function.call.bind({}.hasOwnProperty);

function semver(input) {
  if (input instanceof semver) return input;
  // eslint-disable-next-line new-cap -- ok
  if (!(this instanceof semver)) return new semver(input);
  const match = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(input);
  if (!match) throw TypeError(`Invalid version: ${ input }`);
  const [, $major, $minor, $patch] = match;
  this.major = +$major;
  this.minor = $minor ? +$minor : 0;
  this.patch = $patch ? +$patch : 0;
}

function compare($a, operator, $b) {
  const a = semver($a);
  const b = semver($b);
  for (const component of ['major', 'minor', 'patch']) {
    if (a[component] < b[component]) return operator === '<' || operator === '<=' || operator === '!=';
    if (a[component] > b[component]) return operator === '>' || operator === '>=' || operator === '!=';
  } return operator === '==' || operator === '<=' || operator === '>=';
}

function filterOutStabilizedProposals(modules) {
  const modulesSet = new Set(modules);

  for (const $module of modulesSet) {
    if ($module.startsWith('esnext.') && modulesSet.has($module.replace(/^esnext\./, 'es.'))) {
      modulesSet.delete($module);
    }
  }

  return [...modulesSet];
}

function intersection(list, order) {
  const set = list instanceof Set ? list : new Set(list);
  return order.filter(name => set.has(name));
}

function sortObjectByKey(object, fn) {
  return Object.keys(object).sort(fn).reduce((memo, key) => {
    memo[key] = object[key];
    return memo;
  }, {});
}

module.exports = {
  compare,
  filterOutStabilizedProposals,
  has,
  intersection,
  semver,
  sortObjectByKey,
};
<commit_msg>use `Object.hasOwn` if it's available in `core-js-compat` helpers<commit_after>'use strict';
const has = Object.hasOwn || Function.call.bind({}.hasOwnProperty);

function semver(input) {
  if (input instanceof semver) return input;
  // eslint-disable-next-line new-cap -- ok
  if (!(this instanceof semver)) return new semver(input);
  const match = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(input);
  if (!match) throw TypeError(`Invalid version: ${ input }`);
  const [, $major, $minor, $patch] = match;
  this.major = +$major;
  this.minor = $minor ? +$minor : 0;
  this.patch = $patch ? +$patch : 0;
}

function compare($a, operator, $b) {
  const a = semver($a);
  const b = semver($b);
  for (const component of ['major', 'minor', 'patch']) {
    if (a[component] < b[component]) return operator === '<' || operator === '<=' || operator === '!=';
    if (a[component] > b[component]) return operator === '>' || operator === '>=' || operator === '!=';
  } return operator === '==' || operator === '<=' || operator === '>=';
}

function filterOutStabilizedProposals(modules) {
  const modulesSet = new Set(modules);

  for (const $module of modulesSet) {
    if ($module.startsWith('esnext.') && modulesSet.has($module.replace(/^esnext\./, 'es.'))) {
      modulesSet.delete($module);
    }
  }

  return [...modulesSet];
}

function intersection(list, order) {
  const set = list instanceof Set ? list : new Set(list);
  return order.filter(name => set.has(name));
}

function sortObjectByKey(object, fn) {
  return Object.keys(object).sort(fn).reduce((memo, key) => {
    memo[key] = object[key];
    return memo;
  }, {});
}

module.exports = {
  compare,
  filterOutStabilizedProposals,
  has,
  intersection,
  semver,
  sortObjectByKey,
};
<|endoftext|>


<commit_before>var sql = window.SQL;
// The db variable gets set every time a level is loaded.
var db;

// Return an HTML table as a string, given SQL.js results
var table_from_results = function(res) {
  var table_string = '
';
  if (res) {
    table_string += '
';
    for (var index in res[0].columns) {
      table_string += '' + res[0].columns[index] + '	';
    }
    table_string += '
';
    for (var row_index in res[0].values) {
      table_string += '
';
      for (var col_index in res[0].values[row_index]) {
        table_string += '' + res[0].values[row_index][col_index] + '	';
      }
      table_string += '
';
    }
  }
  table_string += '
';
  return table_string;
};

var grade_results = function(results, correct_answer) {
  if (!results) {
    return false;
  }

  return JSON.stringify(results[0].columns) == JSON.stringify(correct_answer.columns) &&
    JSON.stringify(results[0].values) == JSON.stringify(correct_answer.values);
}

var show_is_correct = function(is_correct) {
  if (is_correct) {
    is_correct_html = 'Congrats!  That is correct!
';
    if (current_level < levels.length) {
      is_correct_html += 'Next Lesson';
    } else {
      is_correct_html += 'That is currently the end of the tutorial.  Please check back later for more!';
    }
    $('#answer-correctness').html(is_correct_html);
  } else {
    $('#answer-correctness').text('That was incorrect.  Please try again.');
  }
  $('#answer-correctness').show();
};

// Onclick handler for when you click "Run SQL"
$('#sql-link').click(function() {
  var answer = levels[current_level - 1]['answer'];
  try {
    var results = db.exec($('#sql-input').val());
    $('#results').html(table_from_results(results));
    show_is_correct(grade_results(results, answer));
  } catch (err) {
    $('#results').html('');
    show_is_correct(false);
  }
  $('.expected-results-container').show();
  $('#expected-results').html(table_from_results([answer]));
  return false;
});

// Stores the prompts and answers for each level number
var levels = [{'name': 'SELECT *',
               'short_name': 'select',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                           'values': [[1, 'Dave', 'male', 'human', 28],
                                      [2, 'Mary', 'female', 'human', 27],
                                      [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In SQL, data is usually organized in various tables. For example, a sports team database might have the tables teams, players, and games. A wedding database might have tables guests, vendors, and music_playlist.

Imagine we have a table that stores family members with each member\'s name, age, species, and gender.

Let\'s start by grabbing all of the data in one table.  We have a table called family_members that is shown below.  In order to grab all of that data, please run the following command:
SELECT * FROM family_members;'},

              {'name': 'WHERE ... Equals',
               'short_name': 'where_equals',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In order to select particular rows from this table, we use the WHERE keyword.  So for example, if we wanted to grab all of the rows that correspond to humans, we would type
SELECT * FROM family_members WHERE species = \'human\';
  Note that the quotes have to be around the word human.

Can you run a query that returns all of the rows that refer to dogs?'},

              {'name': 'WHERE ... Greater than',
               'short_name': 'where_greater_than',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'If we want to only select family members based on a numerical field, we can also use the WHERE keyword.  For example, if we wanted to select family members older than 10, we would type
SELECT * FROM family_members WHERE age > 10;

  Can you run return all rows of members with age greater than 27?'},

              {'name': 'WHERE ... Greater than or equal',
               'short_name': 'where_greater_than_or_equal',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'SQL accepts various inequality symbols, including:
= "equal to"
> "greater than"
< "less than"
>= "greater than or equal to"
<= "less than or equal to"

 Can you return all rows in family_members with an age less than or equal to 27?'},

              {'name': 'SELECT specific columns',
               'short_name': 'select_columns',
               'database_type': 'family',
               'answer': {'columns': ['name', 'species'],
                          'values': [['Dave', 'human'],
                                     ['Mary', 'human'],
                                     ['Pickles', 'dog']]},
               'prompt': 'SELECT * grabs all fields (called columns) in a table. If we only wanted to see the name and age columns, we would type SELECT name, age from family_members;.

Can you return just the name and species columns?'},

              {'name': 'LIMIT # of returned rows',
               'short_name': 'limit',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'Often, tables contain millions of rows, and it can take a while to grab everything. If we just want to see a few examples of the data in a table, we can select only a few rows. If we want to select 2 rows, we would add LIMIT 2 at the end of the query.

 Can you return just the first row (and all columns) of family_members?'},

              {'name': 'COUNT(*)',
               'short_name': 'count',
               'database_type': 'family',
               'answer': {'columns': ['COUNT(*)'],
                          'values': [[3]]},
               'prompt': 'Another way to explore a table is to check the number of rows in it. For example, if we are querying a table states_of_us we\'d expect 50 rows, or 500 rows in a table called fortune_500_companies.

SELECT COUNT(*) FROM family_members; returns the total number of rows in the table family_members. Try this for yourself.'},

              {'name': 'COUNT(*) ... WHERE',
               'short_name': 'count_where',
               'database_type': 'family',
               'answer': {'columns': ['COUNT(*)'],
                          'values': [[1]]},
               'prompt': 'We can combine COUNT(*) with WHERE. For example, SELECT COUNT(*) FROM family_members WHERE species = \'human\'; returns 2.

Can you return the number of rows in family_members where the species is a dog?'},
              {'name': 'NULL',
               'short_name': 'null',
               'database_type': 'family_null',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age', 'favorite_book'],
                          'values': [[1, 'Dave', 'male', 'human', 28, 'To Kill a Mockingbird'],
                                     [2, 'Mary', 'female', 'human', 27, 'Gone with the Wind']]},
               'prompt': 'Sometimes, in a given row, there is no value at all for a given column.  For example, a dog does not have a favorite book, so in that case there is no point in putting a value in the favorite_book column, and the value is NULL.  In order to find the rows where the value for a column is or is not NULL, you would use IS NULL or IS NOT NULL.

Can you return all of the rows of family_members where favorite_book is not null?'},
              {'name': 'Date',
               'short_name': 'date',
               'database_type': 'celebs_born',
               'answer': {'columns': ['id', 'name', 'birthdate'],
                          'values': [[2, 'Justin Timberlake', '1981-01-31'],
                                     [3, 'Taylor Swift', '1989-12-13']]},
               'prompt': 'Sometimes, a column can contain a date value, which represents some specific time.  The first 4 digits represents the year, the next 2 digits represents the month, and the next 2 digits represents the day of the month.  For example, 1985-07-20 would mean July 20, 1985.

You can compare dates by using < and >.  For example, SELECT * FROM celebs_born WHERE birthdate < \'1985-08-17\'; returns a list of celebrities that were born before August 17th, 1985.

Can you return a list of celebrities that were born after September 1st, 1980?'},
              ];

// Create the SQL database
var load_database = function(db_type) {
  var database, sqlstr, results;
  switch (db_type) {
    case 'family':
      database = new sql.Database();
      sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int);";
      sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28);"
      sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27);"
      sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4);"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM family_members;");
      $('#current-tables').html(table_from_results(results));
      return database;
    case 'family_null':
      database = new sql.Database();
      sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int, favorite_book char);";
      sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28, 'To Kill a Mockingbird');"
      sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27, 'Gone with the Wind');"
      sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4, NULL);"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM family_members;");
      $('#current-tables').html(table_from_results(results));
      return database;
    case 'celebs_born':
      database = new sql.Database();
      sqlstr = "CREATE TABLE celebs_born (id int, name char, birthdate date);";
      sqlstr += "INSERT INTO celebs_born VALUES (1, 'Michael Jordan', '1963-02-17');"
      sqlstr += "INSERT INTO celebs_born VALUES (2, 'Justin Timberlake', '1981-01-31');"
      sqlstr += "INSERT INTO celebs_born VALUES (3, 'Taylor Swift', '1989-12-13');"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM celebs_born;");
      $('#current-tables').html(table_from_results(results));
      return database;
  }
};

var current_level;
var current_level_name;

var load_level = function() {
  var hash_code = window.location.hash.substr(2);
  // The current level is 1 by default, unless the hash code matches the short name for a level.
  current_level = 1;
  for (var index in levels) {
    if (hash_code == levels[index]['short_name']) {
      current_level = parseInt(index, 10) + 1;
      break;
    }
  }
  var database = load_database(levels[current_level-1]['database_type']);
  // Set text for current level
  lesson_name = levels[current_level-1]['name'];
  $('#lesson-name').text("Lesson " + current_level + ": " + lesson_name);
  $('#prompt').html(levels[current_level-1]['prompt']);

  // Add "next" and "previous" links if it makes sense.
  if (current_level > 1) {
    $('#previous-link').attr('href', '#!' + levels[current_level-2]['short_name']);
    $('#previous-link').show();
  } else {
    $('#previous-link').hide();
  }
  if (current_level < levels.length) {
    $('#next-link').attr('href', '#!' + levels[current_level]['short_name']);
    $('#next-link').show();
  } else {
    $('#next-link').hide();
  }

  // Add links to menu
  var menu_html = '';
  for (var index in levels) {
    if (index == (current_level - 1)) {
      menu_html += '';
    }
    menu_html += '' + levels[index]['name'] + '';
    if (index == (current_level - 1)) {
      menu_html += '';
    }
  }
  $('.menu').html(menu_html);

  // Clear out old data
  $('#answer-correctness').hide();
  $('#sql-input').val('');
  $('#results').html('');
  $('.expected-results-container').hide();
  return database;
};
db = load_level();

// When the URL after the # changes, we load a new level,
// and let Google Analytics know that the page has changed.
$(window).bind('hashchange', function() {
  db = load_level();
  ga('send', 'pageview', {'page': location.pathname + location.search  + location.hash});
});
<commit_msg>small tweak to copy<commit_after>var sql = window.SQL;
// The db variable gets set every time a level is loaded.
var db;

// Return an HTML table as a string, given SQL.js results
var table_from_results = function(res) {
  var table_string = '
';
  if (res) {
    table_string += '
';
    for (var index in res[0].columns) {
      table_string += '' + res[0].columns[index] + '	';
    }
    table_string += '
';
    for (var row_index in res[0].values) {
      table_string += '
';
      for (var col_index in res[0].values[row_index]) {
        table_string += '' + res[0].values[row_index][col_index] + '	';
      }
      table_string += '
';
    }
  }
  table_string += '
';
  return table_string;
};

var grade_results = function(results, correct_answer) {
  if (!results) {
    return false;
  }

  return JSON.stringify(results[0].columns) == JSON.stringify(correct_answer.columns) &&
    JSON.stringify(results[0].values) == JSON.stringify(correct_answer.values);
}

var show_is_correct = function(is_correct) {
  if (is_correct) {
    is_correct_html = 'Congrats!  That is correct!
';
    if (current_level < levels.length) {
      is_correct_html += 'Next Lesson';
    } else {
      is_correct_html += 'That is currently the end of the tutorial.  Please check back later for more!';
    }
    $('#answer-correctness').html(is_correct_html);
  } else {
    $('#answer-correctness').text('That was incorrect.  Please try again.');
  }
  $('#answer-correctness').show();
};

// Onclick handler for when you click "Run SQL"
$('#sql-link').click(function() {
  var answer = levels[current_level - 1]['answer'];
  try {
    var results = db.exec($('#sql-input').val());
    $('#results').html(table_from_results(results));
    show_is_correct(grade_results(results, answer));
  } catch (err) {
    $('#results').html('');
    show_is_correct(false);
  }
  $('.expected-results-container').show();
  $('#expected-results').html(table_from_results([answer]));
  return false;
});

// Stores the prompts and answers for each level number
var levels = [{'name': 'SELECT *',
               'short_name': 'select',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                           'values': [[1, 'Dave', 'male', 'human', 28],
                                      [2, 'Mary', 'female', 'human', 27],
                                      [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In SQL, data is usually organized in various tables. For example, a sports team database might have the tables teams, players, and games. A wedding database might have tables guests, vendors, and music_playlist.

Imagine we have a table that stores family members with each member\'s name, age, species, and gender.

Let\'s start by grabbing all of the data in one table.  We have a table called family_members that is shown below.  In order to grab all of that data, please run the following command:
SELECT * FROM family_members;'},

              {'name': 'WHERE ... Equals',
               'short_name': 'where_equals',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In order to select particular rows from this table, we use the WHERE keyword.  So for example, if we wanted to grab all of the rows that correspond to humans, we would type
SELECT * FROM family_members WHERE species = \'human\';
  Note that the quotes have to be around the word human.

Can you run a query that returns all of the rows that refer to dogs?'},

              {'name': 'WHERE ... Greater than',
               'short_name': 'where_greater_than',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'If we want to only select family members based on a numerical field, we can also use the WHERE keyword.  For example, if we wanted to select family members older than 10, we would type
SELECT * FROM family_members WHERE age > 10;

  Can you run return all rows of members with age greater than 27?'},

              {'name': 'WHERE ... Greater than or equal',
               'short_name': 'where_greater_than_or_equal',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'SQL accepts various inequality symbols, including:
= "equal to"
> "greater than"
< "less than"
>= "greater than or equal to"
<= "less than or equal to"

 Can you return all rows in family_members with an age less than or equal to 27?'},

              {'name': 'SELECT specific columns',
               'short_name': 'select_columns',
               'database_type': 'family',
               'answer': {'columns': ['name', 'species'],
                          'values': [['Dave', 'human'],
                                     ['Mary', 'human'],
                                     ['Pickles', 'dog']]},
               'prompt': 'SELECT * grabs all fields (called columns) in a table. If we only wanted to see the name and age columns, we would type SELECT name, age from family_members;.

Can you return just the name and species columns?'},

              {'name': 'LIMIT # of returned rows',
               'short_name': 'limit',
               'database_type': 'family',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'Often, tables contain millions of rows, and it can take a while to grab everything. If we just want to see a few examples of the data in a table, we can select only a few rows. If we want to select 2 rows, we would add LIMIT 2 at the end of the query.

 Can you return just the first row (and all columns) of family_members?'},

              {'name': 'COUNT(*)',
               'short_name': 'count',
               'database_type': 'family',
               'answer': {'columns': ['COUNT(*)'],
                          'values': [[3]]},
               'prompt': 'Another way to explore a table is to check the number of rows in it. For example, if we are querying a table states_of_us we\'d expect 50 rows, or 500 rows in a table called fortune_500_companies.

SELECT COUNT(*) FROM family_members; returns the total number of rows in the table family_members. Try this for yourself.'},

              {'name': 'COUNT(*) ... WHERE',
               'short_name': 'count_where',
               'database_type': 'family',
               'answer': {'columns': ['COUNT(*)'],
                          'values': [[1]]},
               'prompt': 'We can combine COUNT(*) with WHERE. For example, SELECT COUNT(*) FROM family_members WHERE species = \'human\'; returns 2.

Can you return the number of rows in family_members where the species is a dog?'},
              {'name': 'NULL',
               'short_name': 'null',
               'database_type': 'family_null',
               'answer': {'columns': ['id', 'name', 'gender', 'species', 'age', 'favorite_book'],
                          'values': [[1, 'Dave', 'male', 'human', 28, 'To Kill a Mockingbird'],
                                     [2, 'Mary', 'female', 'human', 27, 'Gone with the Wind']]},
               'prompt': 'Sometimes, in a given row, there is no value at all for a given column.  For example, a dog does not have a favorite book, so in that case there is no point in putting a value in the favorite_book column, and the value is NULL.  In order to find the rows where the value for a column is or is not NULL, you would use IS NULL or IS NOT NULL.

Can you return all of the rows of family_members where favorite_book is not null?'},
              {'name': 'Date',
               'short_name': 'date',
               'database_type': 'celebs_born',
               'answer': {'columns': ['id', 'name', 'birthdate'],
                          'values': [[2, 'Justin Timberlake', '1981-01-31'],
                                     [3, 'Taylor Swift', '1989-12-13']]},
               'prompt': 'Sometimes, a column can contain a date value.  The first 4 digits represents the year, the next 2 digits represents the month, and the next 2 digits represents the day of the month.  For example, 1985-07-20 would mean July 20, 1985.

You can compare dates by using < and >.  For example, SELECT * FROM celebs_born WHERE birthdate < \'1985-08-17\'; returns a list of celebrities that were born before August 17th, 1985.

Can you return a list of celebrities that were born after September 1st, 1980?'},
              ];

// Create the SQL database
var load_database = function(db_type) {
  var database, sqlstr, results;
  switch (db_type) {
    case 'family':
      database = new sql.Database();
      sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int);";
      sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28);"
      sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27);"
      sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4);"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM family_members;");
      $('#current-tables').html(table_from_results(results));
      return database;
    case 'family_null':
      database = new sql.Database();
      sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int, favorite_book char);";
      sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28, 'To Kill a Mockingbird');"
      sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27, 'Gone with the Wind');"
      sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4, NULL);"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM family_members;");
      $('#current-tables').html(table_from_results(results));
      return database;
    case 'celebs_born':
      database = new sql.Database();
      sqlstr = "CREATE TABLE celebs_born (id int, name char, birthdate date);";
      sqlstr += "INSERT INTO celebs_born VALUES (1, 'Michael Jordan', '1963-02-17');"
      sqlstr += "INSERT INTO celebs_born VALUES (2, 'Justin Timberlake', '1981-01-31');"
      sqlstr += "INSERT INTO celebs_born VALUES (3, 'Taylor Swift', '1989-12-13');"
      database.run(sqlstr);
      results = database.exec("SELECT * FROM celebs_born;");
      $('#current-tables').html(table_from_results(results));
      return database;
  }
};

var current_level;
var current_level_name;

var load_level = function() {
  var hash_code = window.location.hash.substr(2);
  // The current level is 1 by default, unless the hash code matches the short name for a level.
  current_level = 1;
  for (var index in levels) {
    if (hash_code == levels[index]['short_name']) {
      current_level = parseInt(index, 10) + 1;
      break;
    }
  }
  var database = load_database(levels[current_level-1]['database_type']);
  // Set text for current level
  lesson_name = levels[current_level-1]['name'];
  $('#lesson-name').text("Lesson " + current_level + ": " + lesson_name);
  $('#prompt').html(levels[current_level-1]['prompt']);

  // Add "next" and "previous" links if it makes sense.
  if (current_level > 1) {
    $('#previous-link').attr('href', '#!' + levels[current_level-2]['short_name']);
    $('#previous-link').show();
  } else {
    $('#previous-link').hide();
  }
  if (current_level < levels.length) {
    $('#next-link').attr('href', '#!' + levels[current_level]['short_name']);
    $('#next-link').show();
  } else {
    $('#next-link').hide();
  }

  // Add links to menu
  var menu_html = '';
  for (var index in levels) {
    if (index == (current_level - 1)) {
      menu_html += '';
    }
    menu_html += '' + levels[index]['name'] + '';
    if (index == (current_level - 1)) {
      menu_html += '';
    }
  }
  $('.menu').html(menu_html);

  // Clear out old data
  $('#answer-correctness').hide();
  $('#sql-input').val('');
  $('#results').html('');
  $('.expected-results-container').hide();
  return database;
};
db = load_level();

// When the URL after the # changes, we load a new level,
// and let Google Analytics know that the page has changed.
$(window).bind('hashchange', function() {
  db = load_level();
  ga('send', 'pageview', {'page': location.pathname + location.search  + location.hash});
});
<|endoftext|>