<commit_before>/*!
 * Casper is a navigation utility for PhantomJS.
 *
 * Documentation: http://casperjs.org/
 * Repository:    http://github.com/n1k0/casperjs
 *
 * Copyright (c) 2011-2012 Nicolas Perriault
 *
 * Part of source code is Copyright Joyent, Inc. and other Node contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global process, console, phantom, slimer, require:true*/
/*jshint maxstatements:34, maxcomplexity:10*/

// node check
if ('process' in this && process.title === "node") {
    console.error('CasperJS cannot be executed within a nodejs environment');
    process.exit(1);
}

// phantom check
if (!('phantom' in this)) {
    console.error('CasperJS needs to be executed in a PhantomJS environment http://phantomjs.org/');
}

// Common polyfills
if (typeof Function.prototype.bind !== "function") {
    Function.prototype.bind = function(scope) {
        "use strict";
        var _function = this;
        return function() {
            return _function.apply(scope, arguments);
        };
    };
}

// Custom base error
var CasperError = function CasperError(msg) {
    "use strict";
    Error.call(this);
    this.message = msg;
    this.name = 'CasperError';
};
CasperError.prototype = Object.getPrototypeOf(new Error());

// casperjs env initialization
(function(global, phantom){
    "use strict";
    // phantom args
    // NOTE: we can't use require('system').args here for some very obscure reason
    //       do not even attempt at using it as it creates infinite recursion
    var phantomArgs = phantom.args;

    if (phantom.casperLoaded) {
        return;
    }

    function __die(message) {
        if (message) {
            console.error(message);
        }
        phantom.exit(1);
    }

    function __terminate(message) {
        if (message) {
            console.log(message);
        }
        phantom.exit();
    }

    (function(version) {
        // required version check
        if (version.major !== 1) {
            return __die('CasperJS needs PhantomJS v1.x');
        } if (version.minor < 8) {
            return __die('CasperJS needs at least PhantomJS v1.8 or later.');
        }
        if (version.minor === 8 && version.patch < 1) {
            return __die('CasperJS needs at least PhantomJS v1.8.1 or later.');
        }
    })(phantom.version);

    // Hooks in default phantomjs error handler
    phantom.onError = function onPhantomError(msg, trace) {
        phantom.defaultErrorHandler.apply(phantom, arguments);
        // print a hint when a possible casperjs command misuse is detected
        if (msg.indexOf("ReferenceError: Can't find variable: casper") === 0) {
            console.error('Hint: you may want to use the `casperjs test` command.');
        }
        // exits on syntax error
        if (msg.indexOf('SyntaxError: Parse error') === 0) {
            __die();
        }
    };

    // Patching fs
    var fs = (function patchFs(fs) {
        if (!fs.hasOwnProperty('basename')) {
            fs.basename = function basename(path) {
                return path.replace(/.*\//, '');
            };
        }
        if (!fs.hasOwnProperty('dirname')) {
            fs.dirname = function dirname(path) {
                if (!path) return undefined;
                return path.toString().replace(/\\/g, '/').replace(/\/[^\/]*$/, '');
            };
        }
        if (!fs.hasOwnProperty('isWindows')) {
            fs.isWindows = function isWindows() {
                var testPath = arguments[0] || this.workingDirectory;
                return (/^[a-z]{1,2}:/i).test(testPath) || testPath.indexOf("\\\\") === 0;
            };
        }
        if (fs.hasOwnProperty('joinPath')) {
            fs.pathJoin = fs.joinPath;
        } else if (!fs.hasOwnProperty('pathJoin')) {
            fs.pathJoin = function pathJoin() {
                return Array.prototype.join.call(arguments, '/');
            };
        }
        return fs;
    })(require('fs'));

    // CasperJS root path
    if (!phantom.casperPath) {
        try {
            phantom.casperPath = phantom.args.map(function _map(arg) {
                var match = arg.match(/^--casper-path=(.*)/);
                if (match) {
                    return fs.absolute(match[1]);
                }
            }).filter(function _filter(path) {
                return fs.isDirectory(path);
            }).pop();
        } catch (e) {
            return __die("Couldn't find nor compute phantom.casperPath, exiting.");
        }
    }

    /**
     * Prints CasperJS help.
     */
    function printHelp() {
        var engine = phantom.casperEngine === 'slimerjs' ? slimer : phantom;
        var version = [engine.version.major, engine.version.minor, engine.version.patch].join('.');
        return __terminate([
            'CasperJS version ' + phantom.casperVersion.toString() +
            ' at ' + phantom.casperPath + ', using ' + phantom.casperEngine + ' version ' + version,
            fs.read(fs.pathJoin(phantom.casperPath, 'bin', 'usage.txt'))
        ].join('\n'))
    }

    /**
     * Patched require to allow loading of native casperjs modules.
     * Every casperjs module have to first call this function in order to
     * load a native casperjs module:
     *
     *     var require = patchRequire(require);
     *     var utils = require('utils');
     *
     * Useless for SlimerJS
     */
    function patchRequire(require) {
        if (require.patched) {
            return require;
        }
        function resolveFile(path, dir) {
            var extensions = ['js', 'coffee', 'json'];
            var basenames = [path, path + '/index'];
            var paths = [];
            extensions.forEach(function(extension) {
                basenames.forEach(function(basename) {
                    paths.push(fs.pathJoin(dir, [basename, extension].join('.')));
                });
            });
            for (var i = 0; i < paths.length; i++) {
                if (fs.isFile(paths[i])) {
                    return paths[i];
                }
            }
        }
        function getCurrentScriptRoot() {
            if ((phantom.casperScriptBaseDir || "").indexOf(fs.workingDirectory) === 0) {
                return phantom.casperScriptBaseDir;
            }
            return fs.absolute(fs.pathJoin(fs.workingDirectory, phantom.casperScriptBaseDir));
        }
        function casperBuiltinPath(path) {
            return resolveFile(path, fs.pathJoin(phantom.casperPath, 'modules'));
        }
        function nodeModulePath(path) {
            return resolveFile(path, fs.pathJoin(getCurrentScriptRoot(), 'node_modules'));
        }
        function localModulePath(path) {
            return resolveFile(path, phantom.casperScriptBaseDir || fs.workingDirectory);
        }
        var patchedRequire = function patchedRequire(path) {
            try {
                return require(casperBuiltinPath(path) ||
                               nodeModulePath(path)    ||
                               localModulePath(path)   ||
                               path);
            } catch (e) {
                throw new CasperError("Can't find module " + path);
            }
        };
        patchedRequire.cache = require.cache;
        patchedRequire.extensions = require.extensions;
        patchedRequire.stubs = require.stubs;
        patchedRequire.patched = true;
        return patchedRequire;
    }

    /**
     * Initializes the CasperJS Command Line Interface.
     */
    function initCasperCli(casperArgs) {
        /* jshint maxcomplexity:99 */
        var baseTestsPath = fs.pathJoin(phantom.casperPath, 'tests');

        function setScriptBaseDir(scriptName) {
            var dir = fs.dirname(scriptName);
            if (dir === scriptName) {
                dir = '.';
            }
            phantom.casperScriptBaseDir = dir;
        }

        if (!!casperArgs.options.version) {
            return __terminate(phantom.casperVersion.toString())
        } else if (casperArgs.get(0) === "test") {
            phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js'));
            phantom.casperTest = true;
            casperArgs.drop("test");
            setScriptBaseDir(casperArgs.get(0));
        } else if (casperArgs.get(0) === "selftest") {
            phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js'));
            phantom.casperSelfTest = phantom.casperTest = true;
            casperArgs.options.includes = fs.pathJoin(baseTestsPath, 'selftest.js');
            if (casperArgs.args.length <= 1) {
                casperArgs.args.push(fs.pathJoin(baseTestsPath, 'suites'));
            }
            casperArgs.drop("selftest");
            phantom.casperScriptBaseDir = fs.dirname(casperArgs.get(1) || fs.dirname(phantom.casperScript));
        } else if (casperArgs.args.length === 0 || !!casperArgs.options.help) {
            return printHelp();
        }

        if (!phantom.casperScript) {
            phantom.casperScript = casperArgs.get(0);
        }

        if (!fs.isFile(phantom.casperScript)) {
            return __die('Unable to open file: ' + phantom.casperScript);
        }

        if (!phantom.casperScriptBaseDir) {
            setScriptBaseDir(phantom.casperScript);
        }

        // filter out the called script name from casper args
        casperArgs.drop(phantom.casperScript);
    }

    // CasperJS version, extracted from package.json - see http://semver.org/
    phantom.casperVersion = (function getCasperVersion(path) {
        var parts, patchPart, pkg, pkgFile;
        pkgFile = fs.absolute(fs.pathJoin(path, 'package.json'));
        if (!fs.exists(pkgFile)) {
            throw new CasperError('Cannot find package.json at ' + pkgFile);
        }
        try {
            pkg = JSON.parse(require('fs').read(pkgFile));
        } catch (e) {
            throw new CasperError('Cannot read package file contents: ' + e);
        }
        parts  = pkg.version.trim().split(".");
        if (parts.length < 3) {
            throw new CasperError("Invalid version number");
        }
        patchPart = parts[2].split('-');
        return {
            major: ~~parts[0]       || 0,
            minor: ~~parts[1]       || 0,
            patch: ~~patchPart[0]   || 0,
            ident: patchPart[1]     || "",
            toString: function toString() {
                var version = [this.major, this.minor, this.patch].join('.');
                if (this.ident) {
                    version = [version, this.ident].join('-');
                }
                return version;
            }
        };
    })(phantom.casperPath);

    if ("slimer" in global) {
        // for SlimerJS, use the standard API to declare directories
        // where to search modules
        require.paths.push(fs.pathJoin(phantom.casperPath, 'modules'));
        require.paths.push(fs.workingDirectory);

        // declare a dummy patchRequire function
        require.globals.patchRequire = global.patchRequire = function(req) { return req;};
        require.globals.CasperError = CasperError;
        phantom.casperEngine = "slimerjs";
    }
    else {
        // patch require
        global.__require = require;
        global.patchRequire = patchRequire; // must be called in every casperjs module as of 1.1
        global.require = patchRequire(global.require);
        phantom.casperEngine = "phantomjs";
    }

    // casper cli args
    phantom.casperArgs = require('cli').parse(phantomArgs);

    if (true === phantom.casperArgs.get('cli')) {
        initCasperCli(phantom.casperArgs);
    }

    if ("slimer" in global && phantom.casperScriptBaseDir) {
        // initCasperCli has set casperScriptBaseDir
        // use it instead of fs.workingDirectory
        require.paths.pop();
        require.paths.push(phantom.casperScriptBaseDir);
    }

    // casper loading status flag
    phantom.casperLoaded = true;

    // passed casperjs script execution
    if (phantom.casperScript && !phantom.injectJs(phantom.casperScript)) {
        return __die('Unable to load script ' + phantom.casperScript + '; check file syntax');
    }
})(this, phantom);
<commit_msg>closes #582 - ensure BC when specifying the extension<commit_after>/*!
 * Casper is a navigation utility for PhantomJS.
 *
 * Documentation: http://casperjs.org/
 * Repository:    http://github.com/n1k0/casperjs
 *
 * Copyright (c) 2011-2012 Nicolas Perriault
 *
 * Part of source code is Copyright Joyent, Inc. and other Node contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global process, console, phantom, slimer, require:true*/
/*jshint maxstatements:34, maxcomplexity:10*/

// node check
if ('process' in this && process.title === "node") {
    console.error('CasperJS cannot be executed within a nodejs environment');
    process.exit(1);
}

// phantom check
if (!('phantom' in this)) {
    console.error('CasperJS needs to be executed in a PhantomJS environment http://phantomjs.org/');
}

// Common polyfills
if (typeof Function.prototype.bind !== "function") {
    Function.prototype.bind = function(scope) {
        "use strict";
        var _function = this;
        return function() {
            return _function.apply(scope, arguments);
        };
    };
}

// Custom base error
var CasperError = function CasperError(msg) {
    "use strict";
    Error.call(this);
    this.message = msg;
    this.name = 'CasperError';
};
CasperError.prototype = Object.getPrototypeOf(new Error());

// casperjs env initialization
(function(global, phantom){
    "use strict";
    // phantom args
    // NOTE: we can't use require('system').args here for some very obscure reason
    //       do not even attempt at using it as it creates infinite recursion
    var phantomArgs = phantom.args;

    if (phantom.casperLoaded) {
        return;
    }

    function __die(message) {
        if (message) {
            console.error(message);
        }
        phantom.exit(1);
    }

    function __terminate(message) {
        if (message) {
            console.log(message);
        }
        phantom.exit();
    }

    (function(version) {
        // required version check
        if (version.major !== 1) {
            return __die('CasperJS needs PhantomJS v1.x');
        } if (version.minor < 8) {
            return __die('CasperJS needs at least PhantomJS v1.8 or later.');
        }
        if (version.minor === 8 && version.patch < 1) {
            return __die('CasperJS needs at least PhantomJS v1.8.1 or later.');
        }
    })(phantom.version);

    // Hooks in default phantomjs error handler
    phantom.onError = function onPhantomError(msg, trace) {
        phantom.defaultErrorHandler.apply(phantom, arguments);
        // print a hint when a possible casperjs command misuse is detected
        if (msg.indexOf("ReferenceError: Can't find variable: casper") === 0) {
            console.error('Hint: you may want to use the `casperjs test` command.');
        }
        // exits on syntax error
        if (msg.indexOf('SyntaxError: Parse error') === 0) {
            __die();
        }
    };

    // Patching fs
    var fs = (function patchFs(fs) {
        if (!fs.hasOwnProperty('basename')) {
            fs.basename = function basename(path) {
                return path.replace(/.*\//, '');
            };
        }
        if (!fs.hasOwnProperty('dirname')) {
            fs.dirname = function dirname(path) {
                if (!path) return undefined;
                return path.toString().replace(/\\/g, '/').replace(/\/[^\/]*$/, '');
            };
        }
        if (!fs.hasOwnProperty('isWindows')) {
            fs.isWindows = function isWindows() {
                var testPath = arguments[0] || this.workingDirectory;
                return (/^[a-z]{1,2}:/i).test(testPath) || testPath.indexOf("\\\\") === 0;
            };
        }
        if (fs.hasOwnProperty('joinPath')) {
            fs.pathJoin = fs.joinPath;
        } else if (!fs.hasOwnProperty('pathJoin')) {
            fs.pathJoin = function pathJoin() {
                return Array.prototype.join.call(arguments, '/');
            };
        }
        return fs;
    })(require('fs'));

    // CasperJS root path
    if (!phantom.casperPath) {
        try {
            phantom.casperPath = phantom.args.map(function _map(arg) {
                var match = arg.match(/^--casper-path=(.*)/);
                if (match) {
                    return fs.absolute(match[1]);
                }
            }).filter(function _filter(path) {
                return fs.isDirectory(path);
            }).pop();
        } catch (e) {
            return __die("Couldn't find nor compute phantom.casperPath, exiting.");
        }
    }

    /**
     * Prints CasperJS help.
     */
    function printHelp() {
        var engine = phantom.casperEngine === 'slimerjs' ? slimer : phantom;
        var version = [engine.version.major, engine.version.minor, engine.version.patch].join('.');
        return __terminate([
            'CasperJS version ' + phantom.casperVersion.toString() +
            ' at ' + phantom.casperPath + ', using ' + phantom.casperEngine + ' version ' + version,
            fs.read(fs.pathJoin(phantom.casperPath, 'bin', 'usage.txt'))
        ].join('\n'))
    }

    /**
     * Patched require to allow loading of native casperjs modules.
     * Every casperjs module have to first call this function in order to
     * load a native casperjs module:
     *
     *     var require = patchRequire(require);
     *     var utils = require('utils');
     *
     * Useless for SlimerJS
     */
    function patchRequire(require) {
        if (require.patched) {
            return require;
        }
        function resolveFile(path, dir) {
            var extensions = ['js', 'coffee', 'json'];
            var basenames = [path, path + '/index'];
            var paths = [];
            extensions.forEach(function(extension) {
                basenames.forEach(function(basename) {
                    paths.push(fs.pathJoin(dir, basename));
                    paths.push(fs.pathJoin(dir, [basename, extension].join('.')));
                });
            });
            for (var i = 0; i < paths.length; i++) {
                if (fs.isFile(paths[i])) {
                    return paths[i];
                }
            }
        }
        function getCurrentScriptRoot() {
            if ((phantom.casperScriptBaseDir || "").indexOf(fs.workingDirectory) === 0) {
                return phantom.casperScriptBaseDir;
            }
            return fs.absolute(fs.pathJoin(fs.workingDirectory, phantom.casperScriptBaseDir));
        }
        function casperBuiltinPath(path) {
            return resolveFile(path, fs.pathJoin(phantom.casperPath, 'modules'));
        }
        function nodeModulePath(path) {
            return resolveFile(path, fs.pathJoin(getCurrentScriptRoot(), 'node_modules'));
        }
        function localModulePath(path) {
            return resolveFile(path, phantom.casperScriptBaseDir || fs.workingDirectory);
        }
        var patchedRequire = function patchedRequire(path) {
            try {
                return require(casperBuiltinPath(path) ||
                               nodeModulePath(path)    ||
                               localModulePath(path)   ||
                               path);
            } catch (e) {
                throw new CasperError("Can't find module " + path);
            }
        };
        patchedRequire.cache = require.cache;
        patchedRequire.extensions = require.extensions;
        patchedRequire.stubs = require.stubs;
        patchedRequire.patched = true;
        return patchedRequire;
    }

    /**
     * Initializes the CasperJS Command Line Interface.
     */
    function initCasperCli(casperArgs) {
        /* jshint maxcomplexity:99 */
        var baseTestsPath = fs.pathJoin(phantom.casperPath, 'tests');

        function setScriptBaseDir(scriptName) {
            var dir = fs.dirname(scriptName);
            if (dir === scriptName) {
                dir = '.';
            }
            phantom.casperScriptBaseDir = dir;
        }

        if (!!casperArgs.options.version) {
            return __terminate(phantom.casperVersion.toString())
        } else if (casperArgs.get(0) === "test") {
            phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js'));
            phantom.casperTest = true;
            casperArgs.drop("test");
            setScriptBaseDir(casperArgs.get(0));
        } else if (casperArgs.get(0) === "selftest") {
            phantom.casperScript = fs.absolute(fs.pathJoin(baseTestsPath, 'run.js'));
            phantom.casperSelfTest = phantom.casperTest = true;
            casperArgs.options.includes = fs.pathJoin(baseTestsPath, 'selftest.js');
            if (casperArgs.args.length <= 1) {
                casperArgs.args.push(fs.pathJoin(baseTestsPath, 'suites'));
            }
            casperArgs.drop("selftest");
            phantom.casperScriptBaseDir = fs.dirname(casperArgs.get(1) || fs.dirname(phantom.casperScript));
        } else if (casperArgs.args.length === 0 || !!casperArgs.options.help) {
            return printHelp();
        }

        if (!phantom.casperScript) {
            phantom.casperScript = casperArgs.get(0);
        }

        if (!fs.isFile(phantom.casperScript)) {
            return __die('Unable to open file: ' + phantom.casperScript);
        }

        if (!phantom.casperScriptBaseDir) {
            setScriptBaseDir(phantom.casperScript);
        }

        // filter out the called script name from casper args
        casperArgs.drop(phantom.casperScript);
    }

    // CasperJS version, extracted from package.json - see http://semver.org/
    phantom.casperVersion = (function getCasperVersion(path) {
        var parts, patchPart, pkg, pkgFile;
        pkgFile = fs.absolute(fs.pathJoin(path, 'package.json'));
        if (!fs.exists(pkgFile)) {
            throw new CasperError('Cannot find package.json at ' + pkgFile);
        }
        try {
            pkg = JSON.parse(require('fs').read(pkgFile));
        } catch (e) {
            throw new CasperError('Cannot read package file contents: ' + e);
        }
        parts  = pkg.version.trim().split(".");
        if (parts.length < 3) {
            throw new CasperError("Invalid version number");
        }
        patchPart = parts[2].split('-');
        return {
            major: ~~parts[0]       || 0,
            minor: ~~parts[1]       || 0,
            patch: ~~patchPart[0]   || 0,
            ident: patchPart[1]     || "",
            toString: function toString() {
                var version = [this.major, this.minor, this.patch].join('.');
                if (this.ident) {
                    version = [version, this.ident].join('-');
                }
                return version;
            }
        };
    })(phantom.casperPath);

    if ("slimer" in global) {
        // for SlimerJS, use the standard API to declare directories
        // where to search modules
        require.paths.push(fs.pathJoin(phantom.casperPath, 'modules'));
        require.paths.push(fs.workingDirectory);

        // declare a dummy patchRequire function
        require.globals.patchRequire = global.patchRequire = function(req) { return req;};
        require.globals.CasperError = CasperError;
        phantom.casperEngine = "slimerjs";
    }
    else {
        // patch require
        global.__require = require;
        global.patchRequire = patchRequire; // must be called in every casperjs module as of 1.1
        global.require = patchRequire(global.require);
        phantom.casperEngine = "phantomjs";
    }

    // casper cli args
    phantom.casperArgs = require('cli').parse(phantomArgs);

    if (true === phantom.casperArgs.get('cli')) {
        initCasperCli(phantom.casperArgs);
    }

    if ("slimer" in global && phantom.casperScriptBaseDir) {
        // initCasperCli has set casperScriptBaseDir
        // use it instead of fs.workingDirectory
        require.paths.pop();
        require.paths.push(phantom.casperScriptBaseDir);
    }

    // casper loading status flag
    phantom.casperLoaded = true;

    // passed casperjs script execution
    if (phantom.casperScript && !phantom.injectJs(phantom.casperScript)) {
        return __die('Unable to load script ' + phantom.casperScript + '; check file syntax');
    }
})(this, phantom);
<|endoftext|>


<commit_before>/**
 * PowerTip Core
 *
 * @fileoverview  Core variables, plugin object, and API.
 * @link          http://stevenbenner.github.com/jquery-powertip/
 * @author        Steven Benner (http://stevenbenner.com/)
 * @requires      jQuery 1.7+
 */

// useful private variables
var $document = $(window.document),
	$window = $(window),
	$body = $('body'),
	RAD2DEG = 180 / Math.PI;

/**
 * Session data
 * Private properties global to all powerTip instances
 */
var session = {
	isTipOpen: false,
	isFixedTipOpen: false,
	isClosing: false,
	tipOpenImminent: false,
	activeHover: null,
	currentX: 0,
	currentY: 0,
	previousX: 0,
	previousY: 0,
	desyncTimeout: null,
	mouseTrackingActive: false
};

/**
 * Display hover tooltips on the matched elements.
 * @param {(Object|string)} opts The options object to use for the plugin, or the
 *     name of a method to invoke on the first matched element.
 * @param {*=} [arg] Argument for an invoked method (optional).
 * @return {$} jQuery object for the matched selectors.
 */
$.fn.powerTip = function(opts, arg) {

	// don't do any work if there were no matched elements
	if (!this.length) {
		return this;
	}

	// handle api method calls on the plugin, e.g. powerTip('hide')
	if (typeof opts === 'string' && $.powerTip[opts]) {
		return $.powerTip[opts].call(this, this, arg);
	}

	// extend options
	var options = $.extend({}, $.fn.powerTip.defaults, opts),
		tipController = new TooltipController(options);

	// hook mouse tracking
	initMouseTracking();

	// setup the elements
	this.each(function elementSetup() {
		var $this = $(this),
			dataPowertip = $this.data('powertip'),
			dataElem = $this.data('powertipjq'),
			dataTarget = $this.data('powertiptarget'),
			title = $this.attr('title');

		// handle repeated powerTip calls on the same element by destroying
		// the original instance hooked to it and replacing it with this call
		if ($this.data('displayController')) {
			$.powerTip.destroy($this);
			title = $this.attr('title');
		}

		// attempt to use title attribute text if there is no data-powertip,
		// data-powertipjq or data-powertiptarget. If we do use the title
		// attribute, delete the attribute so the browser will not show it
		if (!dataPowertip && !dataTarget && !dataElem && title) {
			$this.data({powertip: title, originalTitle: title}).removeAttr('title');
		}

		// create hover controllers for each element
		$this.data(
			'displayController',
			new DisplayController($this, options, tipController)
		);
	});

	// attach events to matched elements if the manual options is not enabled
	if (!options.manual) {
		this.on({
			// mouse events
			'mouseenter.powertip': function elementMouseEnter(event) {
				elementShowAndTrack(this, event);
			},
			'mouseleave.powertip': function elementMouseLeave() {
				elementHideTip(this);
			},

			// keyboard events
			'focus.powertip': function elementFocus() {
				elementShowTip(this, true);
			},
			'blur.powertip': function elementBlur() {
				elementHideTip(this, true);
			},
			'keydown.powertip': function elementKeyDown(event) {
				// close tooltip when the escape key is pressed
				if (event.keyCode === 27) {
					elementHideTip(this, true);
				}
			}
		});
	}

	return this;

};

/**
 * Default options for the powerTip plugin.
 */
$.fn.powerTip.defaults = {
	fadeInTime: 200,
	fadeOutTime: 100,
	followMouse: false,
	popupId: 'powerTip',
	intentSensitivity: 7,
	intentPollInterval: 100,
	closeDelay: 100,
	placement: 'n',
	smartPlacement: false,
	offset: 10,
	mouseOnToPopup: false,
	manual: false
};

/**
 * Default smart placement priority lists.
 * The first item in the array is the highest priority, the last is the
 * lowest. The last item is also the default, which will be used if all
 * previous options do not fit.
 */
$.fn.powerTip.smartPlacementLists = {
	n: ['n', 'ne', 'nw', 's'],
	e: ['e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e'],
	s: ['s', 'se', 'sw', 'n'],
	w: ['w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w'],
	nw: ['nw', 'w', 'sw', 'n', 's', 'se', 'nw'],
	ne: ['ne', 'e', 'se', 'n', 's', 'sw', 'ne'],
	sw: ['sw', 'w', 'nw', 's', 'n', 'ne', 'sw'],
	se: ['se', 'e', 'ne', 's', 'n', 'nw', 'se'],
	'nw-alt': ['nw-alt', 'n', 'ne-alt', 'nw', 'ne', 'w', 'sw-alt', 's', 'se-alt', 'sw', 'se', 'e'],
	'ne-alt': ['ne-alt', 'n', 'nw-alt', 'ne', 'nw', 'e', 'se-alt', 's', 'sw-alt', 'se', 'sw', 'w'],
	'sw-alt': ['sw-alt', 's', 'se-alt', 'sw', 'se', 'w', 'nw-alt', 'n', 'ne-alt', 'nw', 'ne', 'e'],
	'se-alt': ['se-alt', 's', 'sw-alt', 'se', 'sw', 'e', 'ne-alt', 'n', 'nw-alt', 'ne', 'nw', 'w']
};

/**
 * Public API
 */
$.powerTip = {

	/**
	 * Attempts to show the tooltip for the specified element.
	 * @public
	 * @param {$} element The element that the tooltip should for.
	 * @param {$.Event=} event jQuery event for hover intent and mouse tracking (optional).
	 */
	showTip: function apiShowTip(element, event) {
		// grab only the first matched element and ask it to show its tip
		element.first().each(function() {
			if (event) {
				elementShowAndTrack(this, event);
			} else {
				elementShowTip(this, true, true);
			}
		});
		return element;
	},

	/**
	 * Repositions the tooltip on the element.
	 * @public
	 * @param {$} element The element that the tooltip is shown for.
	 */
	resetPosition: function apiResetPosition(element) {
		element.first().data('displayController').resetPosition();
		return element;
	},

	/**
	 * Attempts to close any open tooltips.
	 * @public
	 * @param {$=} element A specific element whose tip should be closed (optional).
	 * @param {boolean=} immediate Disable close delay (optional).
	 */
	closeTip: function apiCloseTip(element, immediate) {
		if (element) {
			element.first().each(function() {
				elementHideTip(this, immediate);
			});
		} else {
			$document.triggerHandler('closePowerTip');
		}
		return element;
	},

	/**
	 * Destroy and roll back any powerTip() instance on the specified element.
	 * @public
	 * @param {$} element The element with the powerTip instance.
	 */
	destroy: function apiDestroy(element) {
		return element.off('.powertip').each(function destroy() {
			var $this = $(this);

			if ($this.data('originalTitle')) {
				$this.attr('title', $this.data('originalTitle'));
			}

			$this.removeData([
				'originalTitle',
				'displayController',
				'hasActiveHover',
				'forcedOpen'
			]);
		});
	}

};

// API aliasing
$.powerTip.show = $.powerTip.showTip;
$.powerTip.hide = $.powerTip.closeTip;

// Common utility functions

/**
 * Asks the DisplayController for the specified element to show() its tooltip.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {boolean=} immediate Skip intent testing (optional).
 * @param {boolean=} forcedOpen Ignore cursor position and force tooltip to open (optional).
 */
function elementShowTip(el, immediate, forcedOpen) {
	$(el).data('displayController').show(immediate, forcedOpen);
}

/**
 * Tracks the mouse cursor position specified in the event and attempts to open
 * the tooltip for the specified element.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {$.Event} event The event with pageX and pageY info.
 */
function elementShowAndTrack(el, event) {
	trackMouse(event);
	session.previousX = event.pageX;
	session.previousY = event.pageY;
	elementShowTip(el);
}

/**
 * Asks the DisplayController for the specified element to hide() its tooltip.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {boolean=} immediate Disable close delay (optional).
 */
function elementHideTip(el, immediate) {
	$(el).data('displayController').hide(immediate);
}
<commit_msg>Removed unnecessary each() calls in API functions.<commit_after>/**
 * PowerTip Core
 *
 * @fileoverview  Core variables, plugin object, and API.
 * @link          http://stevenbenner.github.com/jquery-powertip/
 * @author        Steven Benner (http://stevenbenner.com/)
 * @requires      jQuery 1.7+
 */

// useful private variables
var $document = $(window.document),
	$window = $(window),
	$body = $('body'),
	RAD2DEG = 180 / Math.PI;

/**
 * Session data
 * Private properties global to all powerTip instances
 */
var session = {
	isTipOpen: false,
	isFixedTipOpen: false,
	isClosing: false,
	tipOpenImminent: false,
	activeHover: null,
	currentX: 0,
	currentY: 0,
	previousX: 0,
	previousY: 0,
	desyncTimeout: null,
	mouseTrackingActive: false
};

/**
 * Display hover tooltips on the matched elements.
 * @param {(Object|string)} opts The options object to use for the plugin, or the
 *     name of a method to invoke on the first matched element.
 * @param {*=} [arg] Argument for an invoked method (optional).
 * @return {$} jQuery object for the matched selectors.
 */
$.fn.powerTip = function(opts, arg) {

	// don't do any work if there were no matched elements
	if (!this.length) {
		return this;
	}

	// handle api method calls on the plugin, e.g. powerTip('hide')
	if (typeof opts === 'string' && $.powerTip[opts]) {
		return $.powerTip[opts].call(this, this, arg);
	}

	// extend options
	var options = $.extend({}, $.fn.powerTip.defaults, opts),
		tipController = new TooltipController(options);

	// hook mouse tracking
	initMouseTracking();

	// setup the elements
	this.each(function elementSetup() {
		var $this = $(this),
			dataPowertip = $this.data('powertip'),
			dataElem = $this.data('powertipjq'),
			dataTarget = $this.data('powertiptarget'),
			title = $this.attr('title');

		// handle repeated powerTip calls on the same element by destroying
		// the original instance hooked to it and replacing it with this call
		if ($this.data('displayController')) {
			$.powerTip.destroy($this);
			title = $this.attr('title');
		}

		// attempt to use title attribute text if there is no data-powertip,
		// data-powertipjq or data-powertiptarget. If we do use the title
		// attribute, delete the attribute so the browser will not show it
		if (!dataPowertip && !dataTarget && !dataElem && title) {
			$this.data({powertip: title, originalTitle: title}).removeAttr('title');
		}

		// create hover controllers for each element
		$this.data(
			'displayController',
			new DisplayController($this, options, tipController)
		);
	});

	// attach events to matched elements if the manual options is not enabled
	if (!options.manual) {
		this.on({
			// mouse events
			'mouseenter.powertip': function elementMouseEnter(event) {
				elementShowAndTrack(this, event);
			},
			'mouseleave.powertip': function elementMouseLeave() {
				elementHideTip(this);
			},

			// keyboard events
			'focus.powertip': function elementFocus() {
				elementShowTip(this, true);
			},
			'blur.powertip': function elementBlur() {
				elementHideTip(this, true);
			},
			'keydown.powertip': function elementKeyDown(event) {
				// close tooltip when the escape key is pressed
				if (event.keyCode === 27) {
					elementHideTip(this, true);
				}
			}
		});
	}

	return this;

};

/**
 * Default options for the powerTip plugin.
 */
$.fn.powerTip.defaults = {
	fadeInTime: 200,
	fadeOutTime: 100,
	followMouse: false,
	popupId: 'powerTip',
	intentSensitivity: 7,
	intentPollInterval: 100,
	closeDelay: 100,
	placement: 'n',
	smartPlacement: false,
	offset: 10,
	mouseOnToPopup: false,
	manual: false
};

/**
 * Default smart placement priority lists.
 * The first item in the array is the highest priority, the last is the
 * lowest. The last item is also the default, which will be used if all
 * previous options do not fit.
 */
$.fn.powerTip.smartPlacementLists = {
	n: ['n', 'ne', 'nw', 's'],
	e: ['e', 'ne', 'se', 'w', 'nw', 'sw', 'n', 's', 'e'],
	s: ['s', 'se', 'sw', 'n'],
	w: ['w', 'nw', 'sw', 'e', 'ne', 'se', 'n', 's', 'w'],
	nw: ['nw', 'w', 'sw', 'n', 's', 'se', 'nw'],
	ne: ['ne', 'e', 'se', 'n', 's', 'sw', 'ne'],
	sw: ['sw', 'w', 'nw', 's', 'n', 'ne', 'sw'],
	se: ['se', 'e', 'ne', 's', 'n', 'nw', 'se'],
	'nw-alt': ['nw-alt', 'n', 'ne-alt', 'nw', 'ne', 'w', 'sw-alt', 's', 'se-alt', 'sw', 'se', 'e'],
	'ne-alt': ['ne-alt', 'n', 'nw-alt', 'ne', 'nw', 'e', 'se-alt', 's', 'sw-alt', 'se', 'sw', 'w'],
	'sw-alt': ['sw-alt', 's', 'se-alt', 'sw', 'se', 'w', 'nw-alt', 'n', 'ne-alt', 'nw', 'ne', 'e'],
	'se-alt': ['se-alt', 's', 'sw-alt', 'se', 'sw', 'e', 'ne-alt', 'n', 'nw-alt', 'ne', 'nw', 'w']
};

/**
 * Public API
 */
$.powerTip = {

	/**
	 * Attempts to show the tooltip for the specified element.
	 * @public
	 * @param {$} element The element that the tooltip should for.
	 * @param {$.Event=} event jQuery event for hover intent and mouse tracking (optional).
	 */
	showTip: function apiShowTip(element, event) {
		if (event) {
			elementShowAndTrack(element.first(), event);
		} else {
			elementShowTip(element.first(), true, true);
		}
		return element;
	},

	/**
	 * Repositions the tooltip on the element.
	 * @public
	 * @param {$} element The element that the tooltip is shown for.
	 */
	resetPosition: function apiResetPosition(element) {
		element.first().data('displayController').resetPosition();
		return element;
	},

	/**
	 * Attempts to close any open tooltips.
	 * @public
	 * @param {$=} element A specific element whose tip should be closed (optional).
	 * @param {boolean=} immediate Disable close delay (optional).
	 */
	closeTip: function apiCloseTip(element, immediate) {
		if (element) {
			elementHideTip(element.first(), immediate);
		} else {
			$document.triggerHandler('closePowerTip');
		}
		return element;
	},

	/**
	 * Destroy and roll back any powerTip() instance on the specified element.
	 * @public
	 * @param {$} element The element with the powerTip instance.
	 */
	destroy: function apiDestroy(element) {
		return element.off('.powertip').each(function destroy() {
			var $this = $(this);

			if ($this.data('originalTitle')) {
				$this.attr('title', $this.data('originalTitle'));
			}

			$this.removeData([
				'originalTitle',
				'displayController',
				'hasActiveHover',
				'forcedOpen'
			]);
		});
	}

};

// API aliasing
$.powerTip.show = $.powerTip.showTip;
$.powerTip.hide = $.powerTip.closeTip;

// Common utility functions

/**
 * Asks the DisplayController for the specified element to show() its tooltip.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {boolean=} immediate Skip intent testing (optional).
 * @param {boolean=} forcedOpen Ignore cursor position and force tooltip to open (optional).
 */
function elementShowTip(el, immediate, forcedOpen) {
	$(el).data('displayController').show(immediate, forcedOpen);
}

/**
 * Tracks the mouse cursor position specified in the event and attempts to open
 * the tooltip for the specified element.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {$.Event} event The event with pageX and pageY info.
 */
function elementShowAndTrack(el, event) {
	trackMouse(event);
	session.previousX = event.pageX;
	session.previousY = event.pageY;
	elementShowTip(el);
}

/**
 * Asks the DisplayController for the specified element to hide() its tooltip.
 * @private
 * @param {$} el The element that the tooltip should be shown for.
 * @param {boolean=} immediate Disable close delay (optional).
 */
function elementHideTip(el, immediate) {
	$(el).data('displayController').hide(immediate);
}
<|endoftext|>


<commit_before>"use strict";

LocusZoom.Data = LocusZoom.Data ||  {};

LocusZoom.Data.Requester = function(sources) {

	function split_requests(fields) {
		var requests = {};
		fields.forEach(function(field) {
			var parts = field.split(/\:(.*)/);
			if (parts.length==1) {
				if (typeof requests["base"] == "undefined") {
					requests.base = [];
				}
				requests.base.push(field);
			} else {
				if (typeof requests[parts[0]] =="undefined") {
					requests[parts[0]] = [];
				}
				requests[parts[0]].push(parts[1]);
			}
		});
		return requests;
	}

	this.getData = function(state, fields) {
		var requests = split_requests(fields);
		var promises = Object.keys(requests).map(function(key) {
			return sources[key].getData(state, requests[key]);
		});
		//assume the are requested in dependent order
		//TODO: better manage dependencies
		var ret = Q.when({});
		for(var i=0; i < promises.length; i++) {
			ret = ret.then(promises[i]);
		}
		return ret;
	}
}

LocusZoom.Data.AssociationSource = function(url) {
	this.url = url;

	this.getData = function(state, fields) {
		if (fields.indexOf("position")==-1) {
			fields.unshift("position");
		}
		return function (chain) {
			var requrl = url + "results?filter=analysis in 1 " +
				"and chromosome in  '" + state.chr + "'" +
				" and position ge " + state.start +
				" and position le " + state.end;
			return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
				var records = [];
				fields.forEach(function(f) {
					if (!(f in x)) {throw "field " + f + " not found in response"}
				});
				for(var i = 0; i < x.position.length; i++) {
					var record = {};
					fields.forEach(function(f) {
						record[f] = x[f][i]
					})
					records.push(record)
				}
				var res = {header:{}, body:records}
				return res;
			});
		}
	}
}

LocusZoom.Data.LDSource = function(url) {
	this.url = url;

	this.getData = function(state, fields) {
		return function (chain) {
			var requrl = url + "results?find=reference::2" +
				"|chr::" + state.chr +
				"|start::" + state.start +
				"|end::" + state.end +
				"|id1::" + state.refvar +
				"&fields=chr,pos,rsquare";
			return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
				chain.touched=true;
				return chain;
			});
		};
	}
}

LocusZoom.Data.GeneSource = function(url) {
	this.url = url;

	this.getData = function(state, fields) {
		return function (chain) {
			var requrl = url + "?filter=source eq 1" +
				" and chrom eq " + state.chr +
				" and start ge " + state.start +
				" and end le " + state.end;
			return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
				return x.data;
			}, function(err) {
				console.log(err);
			})
		}
	}

}

LocusZoom.createResolvedPromise = function() {
	var response = Q.defer();
	response.resolve(Array.prototype.slice.call(arguments));
	return response.promise;
}

LocusZoom.DefaultDataSources = {
	base: new LocusZoom.Data.AssociationSource("/api/v1/single/"),
	ld: new LocusZoom.Data.LDSource("/api/v1/pair/LD/"),
	gene: new LocusZoom.Data.GeneSource("/api/v1/annotation/genes/")
}

var lzd = new LocusZoom.Data.Requester(LocusZoom.DefaultDataSources);
<commit_msg>Apply linting to locuszoom.data<commit_after>/* global LocusZoom,Q */
/* eslint-env browser */
/* eslint-disable no-console */

"use strict";

LocusZoom.Data = LocusZoom.Data ||  {};

LocusZoom.Data.Requester = function(sources) {

    function split_requests(fields) {
        var requests = {};
        fields.forEach(function(field) {
            var parts = field.split(/\:(.*)/);
            if (parts.length==1) {
                if (typeof requests["base"] == "undefined") {
                    requests.base = [];
                }
                requests.base.push(field);
            } else {
                if (typeof requests[parts[0]] =="undefined") {
                    requests[parts[0]] = [];
                }
                requests[parts[0]].push(parts[1]);
            }
        });
        return requests;
    }

    this.getData = function(state, fields) {
        var requests = split_requests(fields);
        var promises = Object.keys(requests).map(function(key) {
            return sources[key].getData(state, requests[key]);
        });
        //assume the are requested in dependent order
        //TODO: better manage dependencies
        var ret = Q.when({});
        for(var i=0; i < promises.length; i++) {
            ret = ret.then(promises[i]);
        }
        return ret;
    };
};

LocusZoom.Data.AssociationSource = function(url) {
    this.url = url;

    this.getData = function(state, fields) {
        ["id","position"].forEach(function(x) {
            if (fields.indexOf(x)==-1) {
                fields.unshift(x);
            }
        });
        return function (chain) {
            var requrl = url + "results?filter=analysis in 1 " +
                "and chromosome in  '" + state.chr + "'" +
                " and position ge " + state.start +
                " and position le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                var records = [];
                fields.forEach(function(f) {
                    if (!(f in x)) {throw "field " + f + " not found in response";}
                });
                for(var i = 0; i < x.position.length; i++) {
                    var record = {};
                    fields.forEach(function(f) {
                        record[f] = x[f][i];
                    });
                    records.push(record);
                }
                var res = {header: chain.header, body: records};
                return res;
            });
        };
    };
};

LocusZoom.Data.LDSource = function(url) {
    this.url = url;

    var findSmallestPvalue = function(x, pval) {
        pval = pval || "pvalue";
        var smVal = x[0][pval], smIdx=0;
        for(var i=1; i<x.length; i++) {
            if (x[i][pval] < smVal) {
                smVal = x[i][pval];
                smIdx = i;
            }
        }
        return smIdx;
    };

    this.getData = function(state, fields) {
        return function (chain) {
            var refVar = state.ldrefvar || chain.header.ldrefvar;
            if (!refVar) {
                refVar = chain.body[findSmallestPvalue(chain.body)].id;
            }
            var requrl = url + "results?find=reference::2" +
                "|chr::" + state.chr +
                "|start::" + state.start +
                "|end::" + state.end +
                "|id1::" + refVar +
                "&fields=chr,pos,rsquare";
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                chain.header.ldrefvar = refVar;
                console.log(x);
                return chain;
            });
        };
    };
};

LocusZoom.Data.GeneSource = function(url) {
    this.url = url;

    this.getData = function(state, fields) {
        return function (chain) {
            var requrl = url + "?filter=source eq 1" +
                " and chrom eq " + state.chr +
                " and start ge " + state.start +
                " and end le " + state.end;
            return LocusZoom.createCORSPromise("GET",requrl).then(function(x) {
                return {header: chain.header, body: x.data};
            }, function(err) {
                console.log(err);
            });
        };
    };
};

LocusZoom.createResolvedPromise = function() {
    var response = Q.defer();
    response.resolve(Array.prototype.slice.call(arguments));
    return response.promise;
};

LocusZoom.DefaultDataSources = {
    base: new LocusZoom.Data.AssociationSource("/api/v1/single/"),
    ld: new LocusZoom.Data.LDSource("/api/v1/pair/LD/"),
    gene: new LocusZoom.Data.GeneSource("/api/v1/annotation/genes/")
};

var lzd = new LocusZoom.Data.Requester(LocusZoom.DefaultDataSources);
<|endoftext|>


<commit_before>var Mutiny = window.Mutiny = {
  options: {
    initOnReady: true
  },

  widgets: {},

  util: {
    onReady: function(fn){
      if(document.readyState === 'complete'){
        fn();
      } else if(document.addEventListener){
        document.addEventListener('DOMContentLoaded', function wrapper(){
          document.removeEventListener('DOMContentLoaded', wrapper);
          fn();
        });
      } else {
        document.attachEvent('onreadystatechange', function wrapper(){
          document.detachEvent('onreadystatechange', wrapper);
          fn();
        });
      }
    },

    dasherize: function(string){
      string = string.replace(/[^a-z]+/ig, '-');
      return string.replace(/(.?)([A-Z])/g, function(match, prev, cap){
        if(prev) {
          return prev + '-' + cap.toLowerCase();
        } else {
          return cap.toLowerCase();
        }
      });
    },

    format: function(){
      var regexes = [];
      for(var i=0; i < 10; i++) {
        regexes[i] = new RegExp('\\{' + i + '\\}', 'gm');
      }

      return function() {
        var s = arguments[0];
        for(var i=1; i < arguments.length; i++) {
          s = s.replace(regexes[i-1], arguments[i]);
        }

        return s;
      };
    }(),

    isString: function(obj){
      return !!obj.substring;
    },

    isArray: function(obj){
      return obj.length !== undefined;
    }
  },

  init: function(els, namespace) {
    namespace = namespace || 'mutiny';
    var name;

    if(!els) {
      var queries = [];
      for(name in Mutiny.widgets) {
        if(Mutiny.widgets.hasOwnProperty(name)) {
          queries.push(Mutiny.util.format('[data-{0}-{1}]', namespace, Mutiny.util.dasherize(name)));
        }
      }
      els = document.querySelectorAll(queries.join(','));
    } else if(!Mutiny.util.isArray(els)) {
      els = [els];
    }

    for(var i=0; i < els.length; i++) {
      var el = els[i];
      for(name in Mutiny.widgets) {
        var attr = Mutiny.util.format('data-{0}-{1}', namespace, Mutiny.util.dasherize(name));
        if(el.hasAttribute(attr)){
          var data = el.getAttribute(attr);
          var updatedOptions = initWidget(el, name, data);
          if(updatedOptions) {
            el.setAttribute(attr, JSON.stringify(updatedOptions));
          }
        }
      }
    }
  }
};

Mutiny.util.onReady(function(){
  if(Mutiny.options.initOnReady) {
    Mutiny.init();
  }
});

function initWidget(instigator, widgetName, instanceOptions) {
  var widget = Mutiny.widgets[widgetName];
  if(widget === undefined) {
    throw Mutiny.util.format('"{0}" not found', widgetName);
  }

  try {
    instanceOptions = instanceOptions ? JSON.parse(instanceOptions) : {};
  } catch(e) {
    e.message = Mutiny.util.format('"{0}" cannot parse "{1}"', widgetName, instanceOptions);
    throw e;
  }

  if(!instanceOptions.called) {
    for(var key in widget.defaults) {
      if(widget.defaults.hasOwnProperty(key) && !instanceOptions.hasOwnProperty(key)) {
        instanceOptions[key] = widget.defaults[key];
      }
    }
    widget.init(instigator, instanceOptions);
    instanceOptions.called = true;
    return instanceOptions;
  }
}
<commit_msg>Refactored initWidget<commit_after>var Mutiny = window.Mutiny = {
  options: {
    initOnReady: true
  },

  widgets: {},

  init: function(els, namespace) {
    namespace = namespace || 'mutiny';
    var name;

    if(!els) {
      var queries = [];
      for(name in Mutiny.widgets) {
        if(Mutiny.widgets.hasOwnProperty(name)) {
          queries.push(Mutiny.util.format('[data-{0}-{1}]', namespace, Mutiny.util.dasherize(name)));
        }
      }
      els = document.querySelectorAll(queries.join(','));
    } else if(!Mutiny.util.isArray(els)) {
      els = [els];
    }

    var processed = Mutiny.util.format('data-{0}', namespace);
    for(var i=0; i < els.length; i++) {
      var el = els[i];
      if(el.hasAttribute(processed)){
        continue;
      }

      el.setAttribute(processed, 'processed');

      for(name in Mutiny.widgets) {
        var attr = Mutiny.util.format('data-{0}-{1}', namespace, Mutiny.util.dasherize(name));
        if(el.hasAttribute(attr)){
          Mutiny.util.initWidget(el, name, el.getAttribute(attr));
        }
      }
    }
  },

  util: {
    onReady: function(fn){
      if(document.readyState === 'complete'){
        fn();
      } else if(document.addEventListener){
        document.addEventListener('DOMContentLoaded', function wrapper(){
          document.removeEventListener('DOMContentLoaded', wrapper);
          fn();
        });
      } else {
        document.attachEvent('onreadystatechange', function wrapper(){
          document.detachEvent('onreadystatechange', wrapper);
          fn();
        });
      }
    },

    dasherize: function(string){
      string = string.replace(/[^a-z]+/ig, '-');
      return string.replace(/(.?)([A-Z])/g, function(match, prev, cap){
        if(prev) {
          return prev + '-' + cap.toLowerCase();
        } else {
          return cap.toLowerCase();
        }
      });
    },

    format: function(){
      var regexes = [];
      for(var i=0; i < 10; i++) {
        regexes[i] = new RegExp('\\{' + i + '\\}', 'gm');
      }

      return function() {
        var s = arguments[0];
        for(var i=1; i < arguments.length; i++) {
          s = s.replace(regexes[i-1], arguments[i]);
        }

        return s;
      };
    }(),

    isString: function(obj){
      return !!obj.substring;
    },

    isArray: function(obj){
      return obj.length !== undefined;
    },

    initWidget: function(instigator, widgetName, instanceOptions) {
      var widget = Mutiny.widgets[widgetName];
      if(widget === undefined) {
        throw Mutiny.util.format('"Mutiny.widget.{0}" not found', widgetName);
      }

      try {
        instanceOptions = instanceOptions ? JSON.parse(instanceOptions) : {};
      } catch(e) {
        e.message = Mutiny.util.format('"Mutiny.widget.{0}" cannot parse "{1}"', widgetName, instanceOptions);
        throw e;
      }

      for(var key in widget.defaults) {
        if(widget.defaults.hasOwnProperty(key) && !instanceOptions.hasOwnProperty(key)) {
          instanceOptions[key] = widget.defaults[key];
        }
      }
      widget.init(instigator, instanceOptions);
    }
  }
};

Mutiny.util.onReady(function(){
  if(Mutiny.options.initOnReady) {
    Mutiny.init();
  }
});
<|endoftext|>


<commit_before>
var ttest = require('ttest');
var summary = require('summary');

function Commonality(top) {
  this._top = top;

  this.nodelist = null;
}
module.exports = Commonality;

function attrStringify(attr) {
  var names = Object.keys(attr);
  var str = '';
  for (var i = 0, l = names.length; i < l; i++) {
    str += names[i] + '="' + attr[names[i]].slice(0, 20) + (attr[names[i]].length > 20 ? '...' : '') + '" ';
  }
  return str;
}

Commonality.prototype.collect = function (nodelist) {
  this.parent = new FeatureSummary();
  this.node = new FeatureSummary();

  this.nodelist = nodelist;

  for (var i = 0, l = nodelist.length; i < l; i++) {
    this.node.collect(nodelist[i]);

    var parent = nodelist[i];
    while(parent !== this._top && (parent = parent.parent)) {
      this.parent.collect(parent);
    }
  }
};

var ttest_options = {
  alpha: 0.025,
  alternative: "greater"
};

Commonality.prototype.tagnameReducer = function () {
  var names = Object.keys(this.node.tagname);

  // Find the tagname collection there quickly assumed to have the
  // highest density mean
  var maxmean = 0;
  var maxtagname = null;
  for (var i = 0, l = names.length; i < l; i++) {
    var mean = summary(this.node.tagname[names[i]]).mean();
    if (mean > maxmean) {
      maxtagname = names[i];
      maxmean = mean;
    }
  }
  var maxdataset = this.node.tagname[maxtagname];

  for (var n = 0, r = names.length; n < r; n++) {
    if (names[n] == maxtagname) continue;

    // If there is only two values then there is not enogth data to perfrom
    // a statical valid analysis, but if maxmean is 25% greater than the
    // alternative it should be good enogth
    if (maxdataset.length + this.node.tagname[names[n]].length === 2) {

      if (maxmean > summary(this.node.tagname[names[n]]).mean() * 1.25) {
        for (var p = 0, t = this.nodelist.length; p < t; p++) {
          if (this.nodelist[p].tagname === names[n]) {
            this.nodelist[p].remove();
            break;
          }
        }
      }
    }

    // Check if the maxdataset mean is greater than the current tagname
    else if (ttest(maxdataset, this.node.tagname[names[n]], ttest_options).valid() === false) {
      for (var p = 0, t = this.nodelist.length; p < t; p++) {
        if (this.nodelist[p].tagname === names[n]) {
          this.nodelist[p].remove();
        }
      }
    }
  }
};

//
// Collects features and bind them to the node density
//
function FeatureSummary() {
  this.type = {
    'element': [],
    'text': []
  };
  this.tagname = {
    // $tagname : [$density]
  };
  this.attribute = {
    // $attrname: { $value: [$density] }
  };
}

FeatureSummary.prototype._appendAttribute = function (attrobject, attrvalue, node) {
  if (attrobject.hasOwnProperty( attrvalue )) {
    attrobject[ attrvalue ].push(node.density);
  } else {
    attrobject[ attrvalue ] = [node.density];
  }
};

FeatureSummary.prototype.collect = function (node) {
  this.type[node.type].push(node.density);

  if (node.type === 'element') {
    if (this.tagname.hasOwnProperty(node.tagname)) {
      this.tagname[node.tagname].push(node.density);
    } else {
      this.tagname[node.tagname] = [node.density];
    }

    var attrnames = Object.keys(node.attr);
    var attrobject = null;
    var attrname = null;
    for (var i = 0, l = attrnames.length; i < l; i++) {
      attrname = attrnames[i];

      if (this.attribute.hasOwnProperty( attrname )) {
        attrobject = this.attribute[ attrname ];
      } else {
        attrobject = this.attribute[ attrname ] = {};
      }

      if (attrname === 'class') {
        for (var n = 0, r = node.classes.length; n < r; n++) {
          this._appendAttribute(attrobject, node.classes[n], node);
        }
      } else {
        this._appendAttribute(attrobject, node.attr[ attrname ], node);
      }
    }
  }
};
<commit_msg>[fix] issues when only one value exists in a data set<commit_after>
var ttest = require('ttest');
var summary = require('summary');
var domHelpers = require('../helpers-dom.js');

function Commonality(top) {
  this._top = top;

  this.nodelist = null;
}
module.exports = Commonality;

function attrStringify(attr) {
  var names = Object.keys(attr);
  var str = '';
  for (var i = 0, l = names.length; i < l; i++) {
    str += names[i] + '="' + attr[names[i]].slice(0, 20) + (attr[names[i]].length > 20 ? '...' : '') + '" ';
  }
  return str;
}

Commonality.prototype.collect = function (nodelist) {
  this.parent = new FeatureSummary();
  this.node = new FeatureSummary();

  this.nodelist = nodelist;

  for (var i = 0, l = nodelist.length; i < l; i++) {
    this.node.collect(nodelist[i]);

    var parent = nodelist[i];
    while(parent !== this._top && (parent = parent.parent)) {
      this.parent.collect(parent);
    }
  }
};

var ttest_options = {
  alpha: 0.15,
  alternative: "greater"
};

Commonality.prototype.tagnameReducer = function () {
  var names = Object.keys(this.node.tagname);

  // Find the tagname collection there quickly assumed to have the
  // highest density mean
  var maxmean = 0;
  var maxtagname = null;
  for (var i = 0, l = names.length; i < l; i++) {
    var mean = summary(this.node.tagname[names[i]]).mean();
    if (mean > maxmean) {
      maxtagname = names[i];
      maxmean = mean;
    }
  }
  var maxdataset = this.node.tagname[maxtagname];

  for (var n = 0, r = names.length; n < r; n++) {
    if (names[n] == maxtagname) continue;

    // If there is only two values then there is not enogth data to perfrom
    // a statical valid analysis, but if maxmean is 25% greater than the
    // alternative it should be good enogth
    if (maxdataset.length === 1 || this.node.tagname[names[n]].length === 1) {
      if (maxmean > summary(this.node.tagname[names[n]]).mean() * 1.25) {
        for (var p = 0, t = this.nodelist.length; p < t; p++) {
          if (this.nodelist[p].tagname === names[n]) {
            this.nodelist[p].remove();
          }
        }
      }
    }

    // Check if the maxdataset mean is greater than the current tagname
    else if (ttest(maxdataset, this.node.tagname[names[n]], ttest_options).valid() === false) {
      for (var p = 0, t = this.nodelist.length; p < t; p++) {
        if (this.nodelist[p].tagname === names[n]) {
          this.nodelist[p].remove();
        }
      }
    }
  }
};

//
// Collects features and bind them to the node density
//
function FeatureSummary() {
  this.type = {
    'element': [],
    'text': []
  };
  this.tagname = {
    // $tagname : [$density]
  };
  this.attribute = {
    // $attrname: { $value: [$density] }
  };
}

FeatureSummary.prototype._appendAttribute = function (attrobject, attrvalue, node) {
  if (attrobject.hasOwnProperty( attrvalue )) {
    attrobject[ attrvalue ].push(node.density);
  } else {
    attrobject[ attrvalue ] = [node.density];
  }
};

FeatureSummary.prototype.collect = function (node) {
  this.type[node.type].push(node.density);

  if (node.type === 'element') {
    if (this.tagname.hasOwnProperty(node.tagname)) {
      this.tagname[node.tagname].push(node.density);
    } else {
      this.tagname[node.tagname] = [node.density];
    }

    var attrnames = Object.keys(node.attr);
    var attrobject = null;
    var attrname = null;
    for (var i = 0, l = attrnames.length; i < l; i++) {
      attrname = attrnames[i];

      if (this.attribute.hasOwnProperty( attrname )) {
        attrobject = this.attribute[ attrname ];
      } else {
        attrobject = this.attribute[ attrname ] = {};
      }

      if (attrname === 'class') {
        for (var n = 0, r = node.classes.length; n < r; n++) {
          this._appendAttribute(attrobject, node.classes[n], node);
        }
      } else {
        this._appendAttribute(attrobject, node.attr[ attrname ], node);
      }
    }
  }
};
<|endoftext|>


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

var css = require("../lib/css"),
    nopt = require("nopt"),
    knownOpts = {
    },
    shortHands = {
    },
    usage = "Usage: css-smash [options] <cssfile1> <cssfile2> <cssfile3> ...\n\nThis compresses each of the specified css files and outputs the compressed results. By default css-smash outputs to stdout.\n\nWhere the options are:\n\n\t-o, --output\n\t\tPath to output compressed CSS to.\n\nExamples:\n\n\tcss-smash my.css\n\tcss-smash -o smashed.css file1.css file2.css",
    parsed = nopt(knownOpts, shortHands, process.argv);

function die(why) {
  console.warn(why);
  console.warn(usage);
  process.exit(1);
}

if (!parsed.argv.remain.length) {
  die("No files specified!");
}

console.log(parsed);
<commit_msg>Add usage output to css-smash.<commit_after>#!/usr/bin/env node

var css = require("../lib/css"),
    nopt = require("nopt"),
    knownOpts = {
      "usage" : Boolean
    },
    shortHands = {
      "u" : ["--usage"]
    },
    usage = "Usage: css-smash [options] <cssfile1> <cssfile2> <cssfile3> ...\n\nThis compresses each of the specified css files and outputs the compressed results. By default css-smash outputs to stdout.\n\nWhere the options are:\n\n\t-o, --output\n\t\tPath to output compressed CSS to.\n\n\t-u, --usage\n\t\tDisplays this usage message.\n\nExamples:\n\n\tcss-smash my.css\n\tcss-smash -o smashed.css file1.css file2.css",
    parsed = nopt(knownOpts, shortHands, process.argv);

function die(why) {
  console.warn(why, "\n");
  console.warn(usage);
  process.exit(1);
}

if (parsed.usage) {
  console.log(usage);
  process.exit(0);
}

if (!parsed.argv.remain.length) {
  die("No files specified!");
}

console.log(parsed);
<|endoftext|>


<commit_before>var addModule = (function(){
  var modules = []

  calcPositions = _.throttle(function(){
    modules.forEach(function(d){
      d.bbox     = d.sel.node().getBoundingClientRect()
      d.startPos = d.bbox.top    + pageYOffset - 1350
      d.endPos   = d.bbox.bottom + pageYOffset
    })
  }, 1000)

  d3.select(window).on('scroll.module', _.debounce(function(d){
    modules.forEach(function(d){
      d.active = d.startPos < pageYOffset && pageYOffset < d.endPos
    })
  }, 300))

  d3.select(window).on('resize.module', calcPositions)

  //content on page moves without trigging any resizing...?
  setInterval(calcPositions, 2000)

  return function(module){
    modules.push(module)
    calcPositions()
  }
})()<commit_msg>Module manager calls onload and onunload functions on module, if defined<commit_after>var addModule = (function(){
  var modules = []

  calcPositions = _.throttle(function(){
    modules.forEach(function(d){
      d.bbox     = d.sel.node().getBoundingClientRect()
      d.startPos = d.bbox.top    + pageYOffset - 1350
      d.endPos   = d.bbox.bottom + pageYOffset
    })
  }, 1000)

  d3.select(window).on('scroll.module', _.debounce(function(d){
    modules.forEach(function(d){
      var wasActive = d.active;
      d.active = d.startPos < pageYOffset && pageYOffset < d.endPos

      // if active changed, call load / unload function
      if (d.active !== wasActive) {
        d.active && d.onload ? d.onload() : null;
        !d.active && d.onunload ? d.onunload() : null;
      }
    })
  }, 300))

  d3.select(window).on('resize.module', calcPositions)

  //content on page moves without trigging any resizing...?
  setInterval(calcPositions, 2000)

  return function(module){
    modules.push(module)
    calcPositions()
  }
})()
<|endoftext|>


<commit_before>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var _hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        var _destroyed = false;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /**
        * Logs a Message
        * @param {String} msg
        */
        this.log = function(msg) {
          if(!config.silent) console.log(msg);
        }

        /**
        * Throws an Error
        * @param {String} msg
        */
        this.error = function(msg) {
          console.log("Moon ERR: " + msg);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.$dom = vdom;
        }

        /**
        * Compiles a Component
        * @param {Node} node
        */
        this.compileComponent = function(name, component) {
          var dummy = document.createElement('div');
          dummy.innerHTML = getRootElement(this.$components[name].template).outerHTML;
          component.outerHTML = dummy.outerHTML;
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          if(!_destroyed) this.build(this.$dom.children);
          if(_hooks.updated) {
            _hooks.updated();
          }
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          _methods[method]();
        }

        this.destroy = function() {
          Object.defineProperty(this, '$data', {
            set: function(value) {
              _data = value;
            }
          });
          _destroyed = true;
          if(_hooks.destroyed) _hooks.destroyed();
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // directives["m-for"] = function(el, val, vdom) {
        //   var splitVal = val.split(" in ");
        //   var alias = splitVal[0];
        //   var arr = self.get(splitVal[1]);
        //   var clone = el.cloneNode(true);
        //   var oldVal = vdom.val;
        //   var compilable = vdom.val.replace(new RegExp(alias, "gi"), splitVal[1] + '[0]');
        //   el.innerHTML = compileTemplate(compilable, self.$data);
        //   for(var i = 1; i < arr.length; i++) {
        //     var newClone = clone.cloneNode(true);
        //     var compilable = oldVal.replace(new RegExp(alias, "gi"), splitVal[1] + '[' + i + ']');
        //     newClone.innerHTML = compileTemplate(compilable, self.$data);
        //     var parent = el.parentNode;
        //     parent.appendChild(newClone);
        //   }
        //   vdom.val = el.textContent;
        //   delete vdom.props["m-for"];
        // }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  el.node.removeAttribute(prop);
                  directive(el.node, compiledProperty, el);
                }

                if(!directive) el.node.setAttribute(prop, compiledProperty);
              }
            }

            if(this.$components[el.type.toLowerCase()]) {
              this.compileComponent(el.type.toLowerCase(), el.node);
            }

            this.build(el.children);
          }
        }

        /**
        * Initializes Moon
        */
        this.init = function() {
          this.log("======= Moon =======");
          if(_hooks.created) {
            _hooks.created();
          }
          this.createVirtualDOM(this.$el);
          this.build(this.$dom.children);
          if(_hooks.mounted) {
            _hooks.mounted();
          }
        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, action) {
      components[name] = action;
    }

    /**
    * Makes an AJAX Request
    * @param {String} method
    * @param {String} url
    * @param {Object} params
    * @param {Function} cb
    */
    Moon.ajax = function(method, url, params, cb) {
      var xmlHttp = new XMLHttpRequest();
      method = method.toUpperCase();
      if(typeof params === "function") {
        cb = params;
      }
      var urlParams = "?";
      if(method === "POST") {
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        for(var param in params) {
          urlParams += param + "=" + params[param] + "&";
        }
      }
      xmlHttp.onreadystatechange = function() {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
        cb(JSON.parse(xmlHttp.responseText));
      }
      xmlHttp.open(method, url, true);
      xmlHttp.send(method === "POST" ? urlParams : null);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<commit_msg>get the props<commit_after>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var _hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        var _destroyed = false;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /**
        * Logs a Message
        * @param {String} msg
        */
        this.log = function(msg) {
          if(!config.silent) console.log(msg);
        }

        /**
        * Throws an Error
        * @param {String} msg
        */
        this.error = function(msg) {
          console.log("Moon ERR: " + msg);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.$dom = vdom;
        }

        /**
        * Compiles a Component
        * @param {Node} node
        */
        this.compileComponent = function(name, component) {
          var dummy = document.createElement('div');
          var props = component.attributes;
          dummy.innerHTML = getRootElement(this.$components[name].template).outerHTML;
          component.outerHTML = dummy.outerHTML;
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          if(!_destroyed) this.build(this.$dom.children);
          if(_hooks.updated) {
            _hooks.updated();
          }
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          _methods[method]();
        }

        this.destroy = function() {
          Object.defineProperty(this, '$data', {
            set: function(value) {
              _data = value;
            }
          });
          _destroyed = true;
          if(_hooks.destroyed) _hooks.destroyed();
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // directives["m-for"] = function(el, val, vdom) {
        //   var splitVal = val.split(" in ");
        //   var alias = splitVal[0];
        //   var arr = self.get(splitVal[1]);
        //   var clone = el.cloneNode(true);
        //   var oldVal = vdom.val;
        //   var compilable = vdom.val.replace(new RegExp(alias, "gi"), splitVal[1] + '[0]');
        //   el.innerHTML = compileTemplate(compilable, self.$data);
        //   for(var i = 1; i < arr.length; i++) {
        //     var newClone = clone.cloneNode(true);
        //     var compilable = oldVal.replace(new RegExp(alias, "gi"), splitVal[1] + '[' + i + ']');
        //     newClone.innerHTML = compileTemplate(compilable, self.$data);
        //     var parent = el.parentNode;
        //     parent.appendChild(newClone);
        //   }
        //   vdom.val = el.textContent;
        //   delete vdom.props["m-for"];
        // }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  el.node.removeAttribute(prop);
                  directive(el.node, compiledProperty, el);
                }

                if(!directive) el.node.setAttribute(prop, compiledProperty);
              }
            }

            if(this.$components[el.type.toLowerCase()]) {
              this.compileComponent(el.type.toLowerCase(), el.node);
            }

            this.build(el.children);
          }
        }

        /**
        * Initializes Moon
        */
        this.init = function() {
          this.log("======= Moon =======");
          if(_hooks.created) {
            _hooks.created();
          }
          this.createVirtualDOM(this.$el);
          this.build(this.$dom.children);
          if(_hooks.mounted) {
            _hooks.mounted();
          }
        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, action) {
      components[name] = action;
    }

    /**
    * Makes an AJAX Request
    * @param {String} method
    * @param {String} url
    * @param {Object} params
    * @param {Function} cb
    */
    Moon.ajax = function(method, url, params, cb) {
      var xmlHttp = new XMLHttpRequest();
      method = method.toUpperCase();
      if(typeof params === "function") {
        cb = params;
      }
      var urlParams = "?";
      if(method === "POST") {
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        for(var param in params) {
          urlParams += param + "=" + params[param] + "&";
        }
      }
      xmlHttp.onreadystatechange = function() {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
        cb(JSON.parse(xmlHttp.responseText));
      }
      xmlHttp.open(method, url, true);
      xmlHttp.send(method === "POST" ? urlParams : null);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<|endoftext|>


<commit_before>describe("Lessons", function() {
    jasmine.getFixtures().fixturesPath = "spec/fixtures";

    beforeEach(function() {
        QuestionBuilder.builder.reset();
    });

    describe("resetting the question", function() {
        it("should clear the container of questions", function() {
            loadFixtures("question.html");
            $("#question-container").question("Arbitrary question", {expected: "Irrelevant", answer: "Unimportant"});
            expect($("#question-container div")).toHaveId("question-0");

            QuestionBuilder.builder.reset();

            expect($("#question-container")).toBeEmpty();
        });

        it("should reset the counter", function() {
            loadFixtures("question.html");
            $("#question-container").question("Arbitrary question", {expected: "Irrelevant", answer: "Unimportant"});
            expect($("#question-container div")).toHaveId("question-0");

            QuestionBuilder.builder.reset();

            $("#question-container").question("Another question", {expected: "Irrelevant", answer: "Unimportant"});
            expect($("#question-0").size()).toEqual(1);
            expect($("#question-1").size()).toEqual(0);
        });
    });

    describe("having incorrectly answered questions", function() {
        it("should show the question, expected answer and actual answer", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 1 + 4?", {expected: 5, answer: 0});
            expect($("#question-container #question-0")).toHaveHtml("What is 1 + 4?
expected: 5
but got: 0");
        });

        it("should be styled with a red bar", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 1 + 4?", { expected: 5, answer: 0 });
            expect($("#question-0")).toHaveClass("incorrect-answer");
        });
    });

    describe("having correctly answered question", function() {
        it("should show only show the question and answer", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 3 + 5?", {expected: 8, answer: 8});
            expect($("#question-0").html()).toBe("What is 3 + 5?
Correctly answered: 8");
        });

        it("should be style with a green bar", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 3 + 5?", {expected: 8, answer: 8});
            expect($("#question-0")).toHaveClass("correct-answer");
        });
    });
});<commit_msg>Minor spec changes.<commit_after>describe("Lessons", function() {
    jasmine.getFixtures().fixturesPath = "spec/fixtures";

    beforeEach(function() {
        QuestionBuilder.builder.reset();
    });

    describe("resetting the question", function() {
        it("should remove all questions", function() {
            loadFixtures("question.html");

            $("#question-container").question("Arbitrary question", {expected: "Irrelevant", answer: "Unimportant"});

            QuestionBuilder.builder.reset();

            expect($("#question-container")).toBeEmpty();
        });

        it("should reset the counter", function() {
            loadFixtures("question.html");
            $("#question-container").question("Arbitrary question", {expected: "Irrelevant", answer: "Unimportant"});

            QuestionBuilder.builder.reset();

            $("#question-container").question("Another question", {expected: "Irrelevant", answer: "Unimportant"});
            expect($("#question-0").size()).toEqual(1);
            expect($("#question-1").size()).toEqual(0);
        });
    });

    describe("having incorrectly answered questions", function() {
        it("should show the question, expected answer and actual answer", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 1 + 4?", {expected: 5, answer: 0});
            expect($("#question-container #question-0")).toHaveHtml("What is 1 + 4?
expected: 5
but got: 0");
        });

        it("should be styled with a red bar", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 1 + 4?", { expected: 5, answer: 0 });
            expect($("#question-0")).toHaveClass("incorrect-answer");
        });
    });

    describe("having correctly answered question", function() {
        it("should show only show the question and answer", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 3 + 5?", {expected: 8, answer: 8});
            expect($("#question-0").html()).toBe("What is 3 + 5?
Correctly answered: 8");
        });

        it("should be style with a green bar", function() {
            loadFixtures("question.html");
            $("#question-container").question("What is 3 + 5?", {expected: 8, answer: 8});
            expect($("#question-0")).toHaveClass("correct-answer");
        });
    });
});<|endoftext|>